webmock 3.14.0 → 3.16.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/CI.yml +1 -0
  3. data/CHANGELOG.md +42 -2
  4. data/README.md +7 -2
  5. data/lib/webmock/http_lib_adapters/async_http_client_adapter.rb +6 -1
  6. data/lib/webmock/http_lib_adapters/curb_adapter.rb +2 -2
  7. data/lib/webmock/http_lib_adapters/http_rb/client.rb +1 -3
  8. data/lib/webmock/http_lib_adapters/http_rb/response.rb +4 -1
  9. data/lib/webmock/http_lib_adapters/http_rb/streamer.rb +4 -2
  10. data/lib/webmock/http_lib_adapters/http_rb/webmock.rb +6 -2
  11. data/lib/webmock/http_lib_adapters/net_http.rb +11 -18
  12. data/lib/webmock/request_signature.rb +2 -2
  13. data/lib/webmock/request_stub.rb +15 -0
  14. data/lib/webmock/version.rb +1 -1
  15. data/lib/webmock/webmock.rb +10 -0
  16. data/minitest/webmock_spec.rb +1 -1
  17. data/spec/acceptance/async_http_client/async_http_client_spec.rb +5 -5
  18. data/spec/acceptance/curb/curb_spec.rb +11 -0
  19. data/spec/acceptance/em_http_request/em_http_request_spec.rb +1 -1
  20. data/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +1 -1
  21. data/spec/acceptance/excon/excon_spec.rb +2 -2
  22. data/spec/acceptance/net_http/net_http_shared.rb +46 -9
  23. data/spec/acceptance/net_http/net_http_spec.rb +27 -0
  24. data/spec/acceptance/net_http/real_net_http_spec.rb +1 -1
  25. data/spec/acceptance/shared/allowing_and_disabling_net_connect.rb +14 -14
  26. data/spec/acceptance/shared/callbacks.rb +2 -2
  27. data/spec/acceptance/shared/complex_cross_concern_behaviors.rb +1 -1
  28. data/spec/unit/request_signature_spec.rb +21 -1
  29. data/spec/unit/request_stub_spec.rb +35 -0
  30. data/spec/unit/response_spec.rb +1 -1
  31. data/spec/unit/webmock_spec.rb +54 -0
  32. data/webmock.gemspec +2 -2
  33. metadata +27 -27
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a8f150fbeccca419468dfebe632e3b5d365cb8c2df9a5e2a31e65591298786fb
4
- data.tar.gz: d92950bc5ae3510e599c40545f3e7a0ebaafa54a698f79a3c82243e240d35948
3
+ metadata.gz: 438dab0f90920381d01009b8c75892ac83e46043e170c17e1dba5e46ca755c61
4
+ data.tar.gz: a760f4b2399f465c62cf03268c13a7ae973900945ed0c4631e146c44f688a97d
5
5
  SHA512:
6
- metadata.gz: f58d569f7c633d758cee725e5dafb39edacb74e0017ba9c29de0cad8fe16d1f7740cc30847a9f129f6571232d2ca8172d7fb76a1eb3383e31ee8371472f141f9
7
- data.tar.gz: 52191c4e12ac3bf79b575f36d58823ea37ab17f83c7a62602889f361eafadabfa4b0b8ffc1f466eee6de53ea77578fc0149a60e14abdd8a7459b651afbeb85d0
6
+ metadata.gz: c8033597431b3e8ce43e60837d31f18d1ffa52c35079827e122a4616587f29b480b2340786c841bbe9f6b9390c5df08e9b84e933160412c8dc1d5c8a9b214f8b
7
+ data.tar.gz: e0580a7b6b712073edf7d6c38fbf7b6df00a294aa9910f1de0e2de61535de67d07315b5d10f141d7e3387bf91aaee7ce6bb204d40190b5357b2120dc0d7d4427
@@ -14,6 +14,7 @@ jobs:
14
14
  matrix:
15
15
  ruby:
16
16
  - head
17
+ - '3.1'
17
18
  - '3.0'
18
19
  - '2.7'
19
20
  - '2.6'
data/CHANGELOG.md CHANGED
@@ -1,5 +1,45 @@
1
1
  # Changelog
2
2
 
3
+ # 3.16.2
4
+
5
+ * Minimum required Ruby version is 2.0 again.
6
+
7
+ # 3.16.1
8
+
9
+ * Minimum required Ruby version is 2.3
10
+
11
+ # 3.16.0
12
+
13
+ * Fix leaky file descriptors and reuse socket for persistent connections.
14
+
15
+ Thanks to [Ray Zane](https://github.com/rzane)
16
+
17
+ * Allow specifying for what URIs or hosts, Net::HTTP should connect on start.
18
+
19
+ Thanks to [Ray Zane](https://github.com/rzane)
20
+
21
+ # 3.15.0
22
+
23
+ * fixed async-http adapter on Windows
24
+
25
+ Thanks to [Pavel Rosický](https://github.com/ahorek)
26
+
27
+ * Support for http.rb >= 5.0.2
28
+
29
+ Thanks to [ojab](https://github.com/ojab)
30
+
31
+ * Curb adapter supports headers with `:` character in the header value
32
+
33
+ Thanks to [Giorgio Gambino](https://github.com/mrbuzz)
34
+
35
+ * Support for matching body of JSON or application/x-www-form-urlencoded requests with content type header including charset.
36
+
37
+ Thanks to [Timmitry](https://github.com/Timmitry)
38
+
39
+ * Prevent double-wrapping http.rb features on non-stubbed requests
40
+
41
+ Thanks to [Michael Fairley](https://github.com/michaelfairley)
42
+
3
43
  # 3.14.0
4
44
 
5
45
  * Bump Addressable from 2.3.6 to 2.8.0
@@ -605,9 +645,9 @@
605
645
  * `WebMock.disable_net_connect` accepts `:allow` option with an object that responds to `#call`, receiving a `URI` object and returning a boolean:
606
646
 
607
647
 
608
- blacklist = ['google.com', 'facebook.com', 'apple.com']
648
+ denylist = ['google.com', 'facebook.com', 'apple.com']
609
649
  allowed_sites = lambda{|uri|
610
- blacklist.none?{|site| uri.host.include?(site) }
650
+ denylist.none?{|site| uri.host.include?(site) }
611
651
  }
612
652
  WebMock.disable_net_connect!(:allow => allowed_sites)
613
653
 
data/README.md CHANGED
@@ -550,9 +550,9 @@ RestClient.get('sample.org', '/bar') # ===> Failure
550
550
  With an object that responds to `#call`, receiving a `URI` object and returning a boolean:
551
551
 
552
552
  ```ruby
553
- blacklist = ['google.com', 'facebook.com', 'apple.com']
553
+ denylist = ['google.com', 'facebook.com', 'apple.com']
554
554
  allowed_sites = lambda{|uri|
555
- blacklist.none?{|site| uri.host.include?(site) }
555
+ denylist.none?{|site| uri.host.include?(site) }
556
556
  }
557
557
  WebMock.disable_net_connect!(allow: allowed_sites)
558
558
 
@@ -1161,6 +1161,11 @@ People who submitted patches and new features or suggested improvements. Many th
1161
1161
  * Alex Vondrak
1162
1162
  * Will Storey
1163
1163
  * Eduardo Hernandez
1164
+ * ojab
1165
+ * Giorgio Gambino
1166
+ * Timmitry
1167
+ * Michael Fairley
1168
+ * Ray Zane
1164
1169
 
1165
1170
  For a full list of contributors you can visit the
1166
1171
  [contributors](https://github.com/bblimke/webmock/contributors) page.
@@ -151,7 +151,12 @@ if defined?(Async::HTTP)
151
151
  private
152
152
 
153
153
  def create_connected_sockets
154
- Async::IO::Socket.pair(Socket::AF_UNIX, Socket::SOCK_STREAM).tap do |sockets|
154
+ pair = begin
155
+ Async::IO::Socket.pair(Socket::AF_UNIX, Socket::SOCK_STREAM)
156
+ rescue Errno::EAFNOSUPPORT
157
+ Async::IO::Socket.pair(Socket::AF_INET, Socket::SOCK_STREAM)
158
+ end
159
+ pair.tap do |sockets|
155
160
  sockets.each do |socket|
156
161
  socket.instance_variable_set :@alpn_protocol, nil
157
162
  socket.instance_eval do
@@ -5,7 +5,7 @@ rescue LoadError
5
5
  end
6
6
 
7
7
  if defined?(Curl)
8
- WebMock::VersionChecker.new('Curb', Curl::CURB_VERSION, '0.7.16', '0.9.1', ['0.8.7']).check_version!
8
+ WebMock::VersionChecker.new('Curb', Curl::CURB_VERSION, '0.7.16', '1.0.1', ['0.8.7']).check_version!
9
9
 
10
10
  module WebMock
11
11
  module HttpLibAdapters
@@ -128,7 +128,7 @@ if defined?(Curl)
128
128
  def headers_as_hash(headers)
129
129
  if headers.is_a?(Array)
130
130
  headers.inject({}) {|hash, header|
131
- name, value = header.split(":").map(&:strip)
131
+ name, value = header.split(":", 2).map(&:strip)
132
132
  hash[name] = value
133
133
  hash
134
134
  }
@@ -5,9 +5,7 @@ module HTTP
5
5
  def perform(request, options)
6
6
  return __perform__(request, options) unless webmock_enabled?
7
7
 
8
- response = WebMockPerform.new(request) { __perform__(request, options) }.exec
9
- options.features.each { |_name, feature| response = feature.wrap_response(response) }
10
- response
8
+ WebMockPerform.new(request, options) { __perform__(request, options) }.exec
11
9
  end
12
10
 
13
11
  def webmock_enabled?
@@ -24,7 +24,10 @@ module HTTP
24
24
  elsif HTTP::VERSION < "3.0.0"
25
25
  Body.new(Streamer.new(webmock_response.body), webmock_response.body.encoding)
26
26
  else
27
- Body.new(Streamer.new(webmock_response.body), encoding: webmock_response.body.encoding)
27
+ Body.new(
28
+ Streamer.new(webmock_response.body, encoding: webmock_response.body.encoding),
29
+ encoding: webmock_response.body.encoding
30
+ )
28
31
  end
29
32
 
30
33
  return new(status, "1.1", headers, body, uri) if HTTP::VERSION < "1.0.0"
@@ -1,8 +1,9 @@
1
1
  module HTTP
2
2
  class Response
3
3
  class Streamer
4
- def initialize(str)
4
+ def initialize(str, encoding: Encoding::BINARY)
5
5
  @io = StringIO.new str
6
+ @encoding = encoding
6
7
  end
7
8
 
8
9
  def readpartial(size = nil, outbuf = nil)
@@ -14,7 +15,8 @@ module HTTP
14
15
  end
15
16
  end
16
17
 
17
- @io.read size, outbuf
18
+ chunk = @io.read size, outbuf
19
+ chunk.force_encoding(@encoding) if chunk
18
20
  end
19
21
 
20
22
  def close
@@ -1,7 +1,8 @@
1
1
  module HTTP
2
2
  class WebMockPerform
3
- def initialize(request, &perform)
3
+ def initialize(request, options, &perform)
4
4
  @request = request
5
+ @options = options
5
6
  @perform = perform
6
7
  @request_signature = nil
7
8
  end
@@ -38,7 +39,10 @@ module HTTP
38
39
  webmock_response.raise_error_if_any
39
40
 
40
41
  invoke_callbacks(webmock_response, real_request: false)
41
- ::HTTP::Response.from_webmock @request, webmock_response, request_signature
42
+ response = ::HTTP::Response.from_webmock @request, webmock_response, request_signature
43
+
44
+ @options.features.each { |_name, feature| response = feature.wrap_response(response) }
45
+ response
42
46
  end
43
47
 
44
48
  def raise_timeout_error
@@ -98,13 +98,8 @@ module WebMock
98
98
  after_request.call(response)
99
99
  }
100
100
  if started?
101
- if WebMock::Config.instance.net_http_connect_on_start
102
- super_with_after_request.call
103
- else
104
- start_with_connect_without_finish {
105
- super_with_after_request.call
106
- }
107
- end
101
+ ensure_actual_connection
102
+ super_with_after_request.call
108
103
  else
109
104
  start_with_connect {
110
105
  super_with_after_request.call
@@ -119,32 +114,29 @@ module WebMock
119
114
  raise IOError, 'HTTP session already opened' if @started
120
115
  if block_given?
121
116
  begin
117
+ @socket = Net::HTTP.socket_type.new
122
118
  @started = true
123
119
  return yield(self)
124
120
  ensure
125
121
  do_finish
126
122
  end
127
123
  end
124
+ @socket = Net::HTTP.socket_type.new
128
125
  @started = true
129
126
  self
130
127
  end
131
128
 
132
129
 
133
- def start_with_connect_without_finish # :yield: http
134
- if block_given?
135
- begin
136
- do_start
137
- return yield(self)
138
- end
139
- end
140
- do_start
141
- self
130
+ def ensure_actual_connection
131
+ do_start if @socket.is_a?(StubSocket)
142
132
  end
143
133
 
144
134
  alias_method :start_with_connect, :start
145
135
 
146
136
  def start(&block)
147
- if WebMock::Config.instance.net_http_connect_on_start
137
+ uri = Addressable::URI.parse(WebMock::NetHTTPUtility.get_uri(self))
138
+
139
+ if WebMock.net_http_connect_on_start?(uri)
148
140
  super(&block)
149
141
  else
150
142
  start_without_connect(&block)
@@ -258,6 +250,7 @@ class StubSocket #:nodoc:
258
250
 
259
251
  class StubIO
260
252
  def setsockopt(*args); end
253
+ def peer_cert; end
261
254
  end
262
255
  end
263
256
 
@@ -341,7 +334,7 @@ module WebMock
341
334
  WebMock::RequestSignature.new(method, uri, body: request.body, headers: headers)
342
335
  end
343
336
 
344
- def self.get_uri(net_http, path)
337
+ def self.get_uri(net_http, path = nil)
345
338
  protocol = net_http.use_ssl? ? "https" : "http"
346
339
 
347
340
  hostname = net_http.address
@@ -35,11 +35,11 @@ module WebMock
35
35
  alias == eql?
36
36
 
37
37
  def url_encoded?
38
- !!(headers && headers['Content-Type'] == 'application/x-www-form-urlencoded')
38
+ !!(headers && headers.fetch('Content-Type', "").start_with?('application/x-www-form-urlencoded'))
39
39
  end
40
40
 
41
41
  def json_headers?
42
- !!(headers && headers['Content-Type'] == 'application/json')
42
+ !!(headers && headers.fetch('Content-Type', "").start_with?('application/json'))
43
43
  end
44
44
 
45
45
  private
@@ -24,6 +24,21 @@ module WebMock
24
24
  end
25
25
  alias_method :and_return, :to_return
26
26
 
27
+ def to_return_json(*response_hashes)
28
+ raise ArgumentError, '#to_return_json does not support passing a block' if block_given?
29
+
30
+ json_response_hashes = [*response_hashes].flatten.map do |resp_h|
31
+ headers, body = resp_h.values_at(:headers, :body)
32
+ resp_h.merge(
33
+ headers: {content_type: 'application/json'}.merge(headers.to_h),
34
+ body: body.is_a?(Hash) ? body.to_json : body
35
+ )
36
+ end
37
+
38
+ to_return(json_response_hashes)
39
+ end
40
+ alias_method :and_return_json, :to_return_json
41
+
27
42
  def to_rack(app, options={})
28
43
  @responses_sequences << ResponsesSequence.new([RackResponse.new(app)])
29
44
  end
@@ -1,3 +1,3 @@
1
1
  module WebMock
2
- VERSION = '3.14.0' unless defined?(::WebMock::VERSION)
2
+ VERSION = '3.16.2' unless defined?(::WebMock::VERSION)
3
3
  end
@@ -70,6 +70,16 @@ module WebMock
70
70
  Config.instance.allow && net_connect_explicit_allowed?(Config.instance.allow, uri) )
71
71
  end
72
72
 
73
+ def self.net_http_connect_on_start?(uri)
74
+ allowed = Config.instance.net_http_connect_on_start || false
75
+
76
+ if [true, false].include?(allowed)
77
+ allowed
78
+ else
79
+ net_connect_explicit_allowed?(allowed, uri)
80
+ end
81
+ end
82
+
73
83
  def self.net_connect_explicit_allowed?(allowed, uri=nil)
74
84
  case allowed
75
85
  when Array
@@ -20,7 +20,7 @@ require File.expand_path(File.dirname(__FILE__) + '/test_helper')
20
20
  end
21
21
 
22
22
  it "should raise error on non stubbed request" do
23
- lambda { http_request(:get, "http://www.example.net/") }.must_raise(WebMock::NetConnectNotAllowedError)
23
+ expect { http_request(:get, "http://www.example.net/") }.must_raise(WebMock::NetConnectNotAllowedError)
24
24
  end
25
25
 
26
26
  it "should verify that expected request occured" do
@@ -248,7 +248,7 @@ unless RUBY_PLATFORM =~ /java/
248
248
  end
249
249
 
250
250
  context 'multiple requests' do
251
- let(:endpoint) { Async::HTTP::Endpoint.parse('http://www.example.com') }
251
+ let!(:endpoint) { Async::HTTP::Endpoint.parse('http://www.example.com') }
252
252
  let(:requests_count) { 3 }
253
253
 
254
254
  shared_examples :common do
@@ -300,13 +300,13 @@ unless RUBY_PLATFORM =~ /java/
300
300
  end
301
301
 
302
302
  context 'HTTP1 protocol' do
303
- let(:protocol) { Async::HTTP::Protocol::HTTP1 }
303
+ let!(:protocol) { Async::HTTP::Protocol::HTTP1 }
304
304
 
305
305
  include_examples :common
306
306
  end
307
307
 
308
308
  context 'HTTP2 protocol' do
309
- let(:protocol) { Async::HTTP::Protocol::HTTP2 }
309
+ let!(:protocol) { Async::HTTP::Protocol::HTTP2 }
310
310
 
311
311
  include_examples :common
312
312
  end
@@ -331,13 +331,13 @@ unless RUBY_PLATFORM =~ /java/
331
331
  end
332
332
 
333
333
  context 'HTTP1 protocol' do
334
- let(:protocol) { Async::HTTP::Protocol::HTTP1 }
334
+ let!(:protocol) { Async::HTTP::Protocol::HTTP1 }
335
335
 
336
336
  include_examples :common
337
337
  end
338
338
 
339
339
  context 'HTTP2 protocol' do
340
- let(:protocol) { Async::HTTP::Protocol::HTTP2 }
340
+ let!(:protocol) { Async::HTTP::Protocol::HTTP2 }
341
341
 
342
342
  include_examples :common
343
343
  end
@@ -383,6 +383,17 @@ unless RUBY_PLATFORM =~ /java/
383
383
  expect(c.body).to eq("abc")
384
384
  end
385
385
 
386
+ it "supports headers containing the ':' character" do
387
+ stub_request(:get, "www.example.com").with(headers: {'Referer'=>'http://www.example.com'}).to_return(body: "abc")
388
+
389
+ c = Curl::Easy.new
390
+ c.url = "http://www.example.com"
391
+ c.headers = ["Referer: http://www.example.com"]
392
+ c.http(:GET)
393
+ expect(c.body).to eq("abc")
394
+ expect(c.headers).to eq(["Referer: http://www.example.com"])
395
+ end
396
+
386
397
  describe 'match request body' do
387
398
  it 'for post' do
388
399
  stub_request(:post, "www.example.com").with(body: 'foo=nhe').to_return(body: "abc")
@@ -22,7 +22,7 @@ unless RUBY_PLATFORM =~ /java/
22
22
 
23
23
  def make_request
24
24
  EM.run do
25
- request = EM::HttpRequest.new(http_url).get(redirects: 1)
25
+ request = EM::HttpRequest.new(http_url, ssl: {verify_peer: true}).get(redirects: 1)
26
26
  request.callback { EM.stop }
27
27
  end
28
28
  end
@@ -16,7 +16,7 @@ module EMHttpRequestSpecHelper
16
16
  error_set = false
17
17
  uri = Addressable::URI.heuristic_parse(uri)
18
18
  EventMachine.run {
19
- request = EventMachine::HttpRequest.new("#{uri.normalize.to_s}")
19
+ request = EventMachine::HttpRequest.new("#{uri.normalize.to_s}", ssl: {verify_peer: true})
20
20
  http = request.send(method, {
21
21
  timeout: 30,
22
22
  body: options[:body],
@@ -20,7 +20,7 @@ describe "Excon" do
20
20
  it "should support excon response_block for real requests", net_connect: true do
21
21
  a = []
22
22
  WebMock.allow_net_connect!
23
- r = Excon.new('http://httpstat.us/200', headers: { "Accept" => "*" }).
23
+ r = Excon.new('https://httpstat.us/200', headers: { "Accept" => "*" }).
24
24
  get(response_block: lambda {|e, remaining, total| a << e}, chunk_size: 1)
25
25
  expect(a).to eq(["2", "0", "0", " ", "O", "K"])
26
26
  expect(r.body).to eq("")
@@ -41,7 +41,7 @@ describe "Excon" do
41
41
  WebMock.after_request { |_, res|
42
42
  response = res
43
43
  }
44
- r = Excon.new('http://httpstat.us/200', headers: { "Accept" => "*" }).
44
+ r = Excon.new('https://httpstat.us/200', headers: { "Accept" => "*" }).
45
45
  get(response_block: lambda {|e, remaining, total| a << e}, chunk_size: 1)
46
46
  expect(response.body).to eq("200 OK")
47
47
  expect(a).to eq(["2", "0", "0", " ", "O", "K"])
@@ -1,3 +1,5 @@
1
+ require 'set'
2
+
1
3
  shared_examples_for "Net::HTTP" do
2
4
  describe "when making real requests", net_connect: true do
3
5
  let(:port){ WebMockServer.instance.port }
@@ -26,21 +28,56 @@ shared_examples_for "Net::HTTP" do
26
28
 
27
29
  it "should connect only once when connected on start", net_connect: true do
28
30
  @http = Net::HTTP.new('localhost', port)
29
- socket_id_before_request = socket_id_after_request = nil
31
+ socket_before_request = socket_after_request = nil
30
32
  @http.start {|conn|
31
- socket_id_before_request = conn.instance_variable_get(:@socket).object_id
33
+ socket_before_request = conn.instance_variable_get(:@socket)
32
34
  conn.request(Net::HTTP::Get.new("/"))
33
- socket_id_after_request = conn.instance_variable_get(:@socket).object_id
35
+ socket_after_request = conn.instance_variable_get(:@socket)
34
36
  }
35
37
 
36
- if !defined?(WebMock::Config) || WebMock::Config.instance.net_http_connect_on_start
37
- expect(socket_id_before_request).not_to eq(nil.object_id)
38
- expect(socket_id_after_request).not_to eq(nil.object_id)
39
- expect(socket_id_after_request).to eq(socket_id_before_request)
38
+ if !defined?(WebMock::NetHTTPUtility) || WebMock::Config.instance.net_http_connect_on_start
39
+ expect(socket_before_request).to be_a(Net::BufferedIO)
40
+ expect(socket_after_request).to be_a(Net::BufferedIO)
41
+ expect(socket_after_request).to be(socket_before_request)
42
+ else
43
+ expect(socket_before_request).to be_a(StubSocket)
44
+ expect(socket_after_request).to be_a(Net::BufferedIO)
45
+ end
46
+ end
47
+
48
+ it "should allow sending multiple requests when persisted", net_connect: true do
49
+ @http = Net::HTTP.new('example.org')
50
+ @http.start
51
+ expect(@http.get("/")).to be_a(Net::HTTPSuccess)
52
+ expect(@http.get("/")).to be_a(Net::HTTPSuccess)
53
+ expect(@http.get("/")).to be_a(Net::HTTPSuccess)
54
+ @http.finish
55
+ end
56
+
57
+ it "should not leak file descriptors", net_connect: true do
58
+ sockets = Set.new
59
+
60
+ @http = Net::HTTP.new('example.org')
61
+ @http.start
62
+ sockets << @http.instance_variable_get(:@socket)
63
+ @http.get("/")
64
+ sockets << @http.instance_variable_get(:@socket)
65
+ @http.get("/")
66
+ sockets << @http.instance_variable_get(:@socket)
67
+ @http.get("/")
68
+ sockets << @http.instance_variable_get(:@socket)
69
+ @http.finish
70
+
71
+ if !defined?(WebMock::NetHTTPUtility) || WebMock.net_http_connect_on_start?(Addressable::URI.parse("http://example.com/"))
72
+ expect(sockets.length).to eq(1)
73
+ expect(sockets.to_a[0]).to be_a(Net::BufferedIO)
40
74
  else
41
- expect(socket_id_before_request).to eq(nil.object_id)
42
- expect(socket_id_after_request).not_to eq(nil.object_id)
75
+ expect(sockets.length).to eq(2)
76
+ expect(sockets.to_a[0]).to be_a(StubSocket)
77
+ expect(sockets.to_a[1]).to be_a(Net::BufferedIO)
43
78
  end
79
+
80
+ expect(sockets.all?(&:closed?)).to be(true)
44
81
  end
45
82
 
46
83
  it "should pass the read_timeout value on", net_connect: true do
@@ -254,6 +254,21 @@ describe "Net:HTTP" do
254
254
  }
255
255
  end
256
256
 
257
+ it "should connect to the server on start when allowlisted", net_connect: true do
258
+ WebMock.disable_net_connect!(allow: "www.google.com", net_http_connect_on_start: "www.google.com")
259
+ @http.start {|conn|
260
+ cert = OpenSSL::X509::Certificate.new conn.peer_cert
261
+ expect(cert).to be_a(OpenSSL::X509::Certificate)
262
+ }
263
+ end
264
+
265
+ it "should not connect to the server on start when not allowlisted", net_connect: true do
266
+ WebMock.disable_net_connect!(allow: "www.google.com", net_http_connect_on_start: "www.yahoo.com")
267
+ @http.start {|conn|
268
+ expect(conn.peer_cert).to be_nil
269
+ }
270
+ end
271
+
257
272
  it "should connect to the server if the URI matches an regex", net_connect: true do
258
273
  WebMock.disable_net_connect!(allow: /google.com/)
259
274
  Net::HTTP.get('www.google.com','/')
@@ -282,6 +297,13 @@ describe "Net:HTTP" do
282
297
  it_should_behave_like "Net::HTTP"
283
298
  end
284
299
 
300
+ describe "when net_http_connect_on_start is a specific host" do
301
+ before(:each) do
302
+ WebMock.allow_net_connect!(net_http_connect_on_start: "localhost")
303
+ end
304
+ it_should_behave_like "Net::HTTP"
305
+ end
306
+
285
307
  describe 'after_request callback support', net_connect: true do
286
308
  let(:expected_body_regex) { /hello world/ }
287
309
 
@@ -365,5 +387,10 @@ describe "Net:HTTP" do
365
387
  path = '/example.jpg'
366
388
  expect(WebMock::NetHTTPUtility.get_uri(net_http, path)).to eq('http://www.example.com:80/example.jpg')
367
389
  end
390
+
391
+ it "does not require a path" do
392
+ net_http = Net::HTTP.new('www.example.com', 80)
393
+ expect(WebMock::NetHTTPUtility.get_uri(net_http)).to eq('http://www.example.com:80')
394
+ end
368
395
  end
369
396
  end
@@ -17,4 +17,4 @@ describe "Real Net:HTTP without webmock", without_webmock: true do
17
17
  end
18
18
 
19
19
  it_should_behave_like "Net::HTTP"
20
- end
20
+ end
@@ -91,7 +91,7 @@ shared_context "allowing and disabling net connect" do |*adapter_info|
91
91
  describe "is not allowed, with exceptions" do
92
92
  describe "allowing by host string" do
93
93
  before :each do
94
- WebMock.disable_net_connect!(allow: 'httpstat.us')
94
+ WebMock.disable_net_connect!(allow: 'https://httpstat.us')
95
95
  end
96
96
 
97
97
  context "when the host is not allowed" do
@@ -109,13 +109,13 @@ shared_context "allowing and disabling net connect" do |*adapter_info|
109
109
 
110
110
  context "when the host is allowed" do
111
111
  it "should return stubbed response if request was stubbed" do
112
- stub_request(:get, 'httpstat.us/200').to_return(body: "abc")
113
- expect(http_request(:get, "http://httpstat.us/200").body).to eq("abc")
112
+ stub_request(:get, 'https://httpstat.us/200').to_return(body: "abc")
113
+ expect(http_request(:get, "https://httpstat.us/200").body).to eq("abc")
114
114
  end
115
115
 
116
116
  # WARNING: this makes a real HTTP request!
117
117
  it "should make a real request to allowed host", net_connect: true do
118
- expect(http_request(:get, "http://httpstat.us/200").status).to eq('200')
118
+ expect(http_request(:get, "https://httpstat.us/200").status).to eq('200')
119
119
  end
120
120
  end
121
121
  end
@@ -229,13 +229,13 @@ shared_context "allowing and disabling net connect" do |*adapter_info|
229
229
 
230
230
  context "when the host is allowed" do
231
231
  it "should return stubbed response if request was stubbed" do
232
- stub_request(:get, 'httpstat.us/200').to_return(body: "abc")
233
- expect(http_request(:get, "http://httpstat.us/200").body).to eq("abc")
232
+ stub_request(:get, 'https://httpstat.us/200').to_return(body: "abc")
233
+ expect(http_request(:get, "https://httpstat.us/200").body).to eq("abc")
234
234
  end
235
235
 
236
236
  # WARNING: this makes a real HTTP request!
237
237
  it "should make a real request to allowed host", net_connect: true do
238
- expect(http_request(:get, "http://httpstat.us/200").status).to eq('200')
238
+ expect(http_request(:get, "https://httpstat.us/200").status).to eq('200')
239
239
  end
240
240
 
241
241
  it "should make a real request if request is allowed by path regexp and url contains default port", net_connect: true do
@@ -266,20 +266,20 @@ shared_context "allowing and disabling net connect" do |*adapter_info|
266
266
 
267
267
  context "when the host is allowed" do
268
268
  it "should return stubbed response if request was stubbed" do
269
- stub_request(:get, 'httpstat.us/200').to_return(body: "abc")
270
- expect(http_request(:get, "http://httpstat.us/200").body).to eq("abc")
269
+ stub_request(:get, 'https://httpstat.us/200').to_return(body: "abc")
270
+ expect(http_request(:get, "https://httpstat.us/200").body).to eq("abc")
271
271
  end
272
272
 
273
273
  # WARNING: this makes a real HTTP request!
274
274
  it "should make a real request to allowed host", net_connect: true do
275
- expect(http_request(:get, "http://httpstat.us/200").status).to eq('200')
275
+ expect(http_request(:get, "https://httpstat.us/200").status).to eq('200')
276
276
  end
277
277
  end
278
278
  end
279
279
 
280
280
  describe "allowing by a list of the above" do
281
281
  before :each do
282
- WebMock.disable_net_connect!(allow: [lambda{|_| false }, %r{foobar}, 'httpstat.us'])
282
+ WebMock.disable_net_connect!(allow: [lambda{|_| false }, %r{foobar}, 'https://httpstat.us'])
283
283
  end
284
284
 
285
285
  context "when the host is not allowed" do
@@ -297,13 +297,13 @@ shared_context "allowing and disabling net connect" do |*adapter_info|
297
297
 
298
298
  context "when the host is allowed" do
299
299
  it "should return stubbed response if request was stubbed" do
300
- stub_request(:get, 'httpstat.us/200').to_return(body: "abc")
301
- expect(http_request(:get, "http://httpstat.us/200").body).to eq("abc")
300
+ stub_request(:get, 'https://httpstat.us/200').to_return(body: "abc")
301
+ expect(http_request(:get, "https://httpstat.us/200").body).to eq("abc")
302
302
  end
303
303
 
304
304
  # WARNING: this makes a real HTTP request!
305
305
  it "should make a real request to allowed host", net_connect: true do
306
- expect(http_request(:get, "http://httpstat.us/200").status).to eq('200')
306
+ expect(http_request(:get, "https://httpstat.us/200").status).to eq('200')
307
307
  end
308
308
  end
309
309
  end
@@ -102,7 +102,7 @@ shared_context "callbacks" do |*adapter_info|
102
102
  WebMock.after_request(except: [:other_lib]) do |_, response|
103
103
  @response = response
104
104
  end
105
- http_request(:get, "http://httpstat.us/201", headers: { "Accept" => "*" })
105
+ http_request(:get, "https://httpstat.us/201", headers: { "Accept" => "*" })
106
106
  end
107
107
 
108
108
  it "should pass real response to callback with status and message" do
@@ -111,7 +111,7 @@ shared_context "callbacks" do |*adapter_info|
111
111
  end
112
112
 
113
113
  it "should pass real response to callback with headers" do
114
- expect(@response.headers["X-Powered-By"]).to eq( "ASP.NET")
114
+ expect(@response.headers["Server"]).to eq( "Kestrel")
115
115
  expect(@response.headers["Content-Length"]).to eq("11") unless adapter_info.include?(:no_content_length_header)
116
116
  end
117
117
 
@@ -18,7 +18,7 @@ shared_context "complex cross-concern behaviors" do |*adapter_info|
18
18
  expect(played_back_response).to eq(real_response)
19
19
  end
20
20
 
21
- let(:no_content_url) { 'http://httpstat.us/204' }
21
+ let(:no_content_url) { 'https://httpstat.us/204' }
22
22
  [nil, ''].each do |stub_val|
23
23
  it "returns the same value (nil or "") for a request stubbed as #{stub_val.inspect} that a real empty response has", net_connect: true do
24
24
  unless http_library == :curb
@@ -18,7 +18,7 @@ describe WebMock::RequestSignature do
18
18
  end
19
19
 
20
20
  it "assigns normalized headers" do
21
- expect(WebMock::Util::Headers).to receive(:normalize_headers).with('A' => 'a').and_return('B' => 'b')
21
+ allow(WebMock::Util::Headers).to receive(:normalize_headers).with({'A' => 'a'}.freeze).and_return('B' => 'b')
22
22
  expect(
23
23
  WebMock::RequestSignature.new(:get, "www.example.com", headers: {'A' => 'a'}).headers
24
24
  ).to eq({'B' => 'b'})
@@ -125,11 +125,21 @@ describe WebMock::RequestSignature do
125
125
  expect(subject.url_encoded?).to be true
126
126
  end
127
127
 
128
+ it "returns true if the headers are urlencoded with a specified charset" do
129
+ subject.headers = { "Content-Type" => "application/x-www-form-urlencoded; charset=UTF-8" }
130
+ expect(subject.url_encoded?).to be true
131
+ end
132
+
128
133
  it "returns false if the headers are NOT urlencoded" do
129
134
  subject.headers = { "Content-Type" => "application/made-up-format" }
130
135
  expect(subject.url_encoded?).to be false
131
136
  end
132
137
 
138
+ it "returns false when no content type header is present" do
139
+ subject.headers = { "Some-Header" => "some-value" }
140
+ expect(subject.url_encoded?).to be false
141
+ end
142
+
133
143
  it "returns false when no headers are set" do
134
144
  subject.headers = nil
135
145
  expect(subject.url_encoded?).to be false
@@ -142,11 +152,21 @@ describe WebMock::RequestSignature do
142
152
  expect(subject.json_headers?).to be true
143
153
  end
144
154
 
155
+ it "returns true if the headers are json with a specified charset" do
156
+ subject.headers = { "Content-Type" => "application/json; charset=UTF-8" }
157
+ expect(subject.json_headers?).to be true
158
+ end
159
+
145
160
  it "returns false if the headers are NOT json" do
146
161
  subject.headers = { "Content-Type" => "application/made-up-format" }
147
162
  expect(subject.json_headers?).to be false
148
163
  end
149
164
 
165
+ it "returns false when no content type header is present" do
166
+ subject.headers = { "Some-Header" => "some-value" }
167
+ expect(subject.json_headers?).to be false
168
+ end
169
+
150
170
  it "returns false when no headers are set" do
151
171
  subject.headers = nil
152
172
  expect(subject.json_headers?).to be false
@@ -50,6 +50,41 @@ describe WebMock::RequestStub do
50
50
 
51
51
  end
52
52
 
53
+ describe "to_return_json" do
54
+
55
+ it "should raise if a block is given" do
56
+ expect {
57
+ @request_stub.to_return_json(body: "abc", status: 500) { puts "don't call me" }
58
+ }.to raise_error(ArgumentError, '#to_return_json does not support passing a block')
59
+ end
60
+
61
+ it "should assign responses normally" do
62
+ @request_stub.to_return_json([{body: "abc"}, {body: "def"}])
63
+ expect([@request_stub.response.body, @request_stub.response.body]).to eq(["abc", "def"])
64
+ end
65
+
66
+ it "should json-ify a Hash body" do
67
+ @request_stub.to_return_json(body: {abc: "def"}, status: 500)
68
+ expect(@request_stub.response.body).to eq({abc: "def"}.to_json)
69
+ expect(@request_stub.response.status).to eq([500, ""])
70
+ end
71
+
72
+ it "should apply the content_type header" do
73
+ @request_stub.to_return_json(body: {abc: "def"}, status: 500)
74
+ expect(@request_stub.response.headers).to eq({"Content-Type"=>"application/json"})
75
+ end
76
+
77
+ it "should preserve existing headers" do
78
+ @request_stub.to_return_json(headers: {"A" => "a"}, body: "")
79
+ expect(@request_stub.response.headers).to eq({"A"=>"a", "Content-Type"=>"application/json"})
80
+ end
81
+
82
+ it "should allow callsites to override content_type header" do
83
+ @request_stub.to_return_json(headers: {content_type: 'application/super-special-json'})
84
+ expect(@request_stub.response.headers).to eq({"Content-Type"=>"application/super-special-json"})
85
+ end
86
+ end
87
+
53
88
  describe "then" do
54
89
  it "should return stub without any modifications, acting as syntactic sugar" do
55
90
  expect(@request_stub.then).to eq(@request_stub)
@@ -31,7 +31,7 @@ describe WebMock::Response do
31
31
  end
32
32
 
33
33
  it "should report normalized headers" do
34
- expect(WebMock::Util::Headers).to receive(:normalize_headers).with('A' => 'a').and_return('B' => 'b')
34
+ allow(WebMock::Util::Headers).to receive(:normalize_headers).with({'A' => 'a'}.freeze).and_return('B' => 'b')
35
35
  @response = WebMock::Response.new(headers: {'A' => 'a'})
36
36
  expect(@response.headers).to eq({'B' => 'b'})
37
37
  end
@@ -57,4 +57,58 @@ describe "WebMock" do
57
57
  end
58
58
  end
59
59
  end
60
+
61
+ describe ".net_http_connect_on_start?" do
62
+ let(:uri) { Addressable::URI.parse("http://example.org:5432") }
63
+
64
+ it "will not connect on start when false" do
65
+ WebMock.disable_net_connect!
66
+ expect(WebMock.net_http_connect_on_start?(uri)).to be(false)
67
+ end
68
+
69
+ it "will connect on start when true" do
70
+ WebMock.disable_net_connect!(net_http_connect_on_start: true)
71
+ expect(WebMock.net_http_connect_on_start?(uri)).to be(true)
72
+ end
73
+
74
+ it "will connect on start when regexp matches" do
75
+ WebMock.disable_net_connect!(net_http_connect_on_start: /example/)
76
+ expect(WebMock.net_http_connect_on_start?(uri)).to be(true)
77
+ end
78
+
79
+ it "will not connect on start when regexp does not match" do
80
+ WebMock.disable_net_connect!(net_http_connect_on_start: /nope/)
81
+ expect(WebMock.net_http_connect_on_start?(uri)).to be(false)
82
+ end
83
+
84
+ it "will connect on start when host matches" do
85
+ WebMock.disable_net_connect!(net_http_connect_on_start: "example.org")
86
+ expect(WebMock.net_http_connect_on_start?(uri)).to be(true)
87
+ end
88
+
89
+ it "will not connect on start when host does not match" do
90
+ WebMock.disable_net_connect!(net_http_connect_on_start: "localhost")
91
+ expect(WebMock.net_http_connect_on_start?(uri)).to be(false)
92
+ end
93
+
94
+ it "will connect on start when host + port matches" do
95
+ WebMock.disable_net_connect!(net_http_connect_on_start: "example.org:5432")
96
+ expect(WebMock.net_http_connect_on_start?(uri)).to be(true)
97
+ end
98
+
99
+ it "will not connect on start when host + port does not match" do
100
+ WebMock.disable_net_connect!(net_http_connect_on_start: "example.org:80")
101
+ expect(WebMock.net_http_connect_on_start?(uri)).to be(false)
102
+ end
103
+
104
+ it "will connect on start when scheme + host + port matches" do
105
+ WebMock.disable_net_connect!(net_http_connect_on_start: "http://example.org:5432")
106
+ expect(WebMock.net_http_connect_on_start?(uri)).to be(true)
107
+ end
108
+
109
+ it "will not connect on start when scheme + host + port does not match" do
110
+ WebMock.disable_net_connect!(net_http_connect_on_start: "https://example.org:5432")
111
+ expect(WebMock.net_http_connect_on_start?(uri)).to be(false)
112
+ end
113
+ end
60
114
  end
data/webmock.gemspec CHANGED
@@ -31,6 +31,8 @@ Gem::Specification.new do |s|
31
31
  s.add_development_dependency 'patron', '>= 0.4.18'
32
32
  s.add_development_dependency 'curb', '>= 0.7.16'
33
33
  s.add_development_dependency 'typhoeus', '>= 0.5.0'
34
+ s.add_development_dependency 'em-http-request', '>= 1.0.2'
35
+ s.add_development_dependency 'em-synchrony', '>= 1.0.0'
34
36
  end
35
37
 
36
38
  s.add_development_dependency 'http', '>= 0.8.0'
@@ -38,8 +40,6 @@ Gem::Specification.new do |s|
38
40
  s.add_development_dependency 'rack', ((RUBY_VERSION < '2.2.2') ? '1.6.0' : '> 1.6')
39
41
  s.add_development_dependency 'rspec', '>= 3.1.0'
40
42
  s.add_development_dependency 'httpclient', '>= 2.2.4'
41
- s.add_development_dependency 'em-http-request', '>= 1.0.2'
42
- s.add_development_dependency 'em-synchrony', '>= 1.0.0'
43
43
  s.add_development_dependency 'excon', '>= 0.27.5'
44
44
  s.add_development_dependency 'async-http', '>= 0.48.0'
45
45
  s.add_development_dependency 'minitest', '>= 5.0.0'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: webmock
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.14.0
4
+ version: 3.16.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bartosz Blimke
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-08-05 00:00:00.000000000 Z
11
+ date: 2022-08-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -101,89 +101,89 @@ dependencies:
101
101
  - !ruby/object:Gem::Version
102
102
  version: 0.5.0
103
103
  - !ruby/object:Gem::Dependency
104
- name: http
104
+ name: em-http-request
105
105
  requirement: !ruby/object:Gem::Requirement
106
106
  requirements:
107
107
  - - ">="
108
108
  - !ruby/object:Gem::Version
109
- version: 0.8.0
109
+ version: 1.0.2
110
110
  type: :development
111
111
  prerelease: false
112
112
  version_requirements: !ruby/object:Gem::Requirement
113
113
  requirements:
114
114
  - - ">="
115
115
  - !ruby/object:Gem::Version
116
- version: 0.8.0
116
+ version: 1.0.2
117
117
  - !ruby/object:Gem::Dependency
118
- name: rack
118
+ name: em-synchrony
119
119
  requirement: !ruby/object:Gem::Requirement
120
120
  requirements:
121
- - - ">"
121
+ - - ">="
122
122
  - !ruby/object:Gem::Version
123
- version: '1.6'
123
+ version: 1.0.0
124
124
  type: :development
125
125
  prerelease: false
126
126
  version_requirements: !ruby/object:Gem::Requirement
127
127
  requirements:
128
- - - ">"
128
+ - - ">="
129
129
  - !ruby/object:Gem::Version
130
- version: '1.6'
130
+ version: 1.0.0
131
131
  - !ruby/object:Gem::Dependency
132
- name: rspec
132
+ name: http
133
133
  requirement: !ruby/object:Gem::Requirement
134
134
  requirements:
135
135
  - - ">="
136
136
  - !ruby/object:Gem::Version
137
- version: 3.1.0
137
+ version: 0.8.0
138
138
  type: :development
139
139
  prerelease: false
140
140
  version_requirements: !ruby/object:Gem::Requirement
141
141
  requirements:
142
142
  - - ">="
143
143
  - !ruby/object:Gem::Version
144
- version: 3.1.0
144
+ version: 0.8.0
145
145
  - !ruby/object:Gem::Dependency
146
- name: httpclient
146
+ name: rack
147
147
  requirement: !ruby/object:Gem::Requirement
148
148
  requirements:
149
- - - ">="
149
+ - - ">"
150
150
  - !ruby/object:Gem::Version
151
- version: 2.2.4
151
+ version: '1.6'
152
152
  type: :development
153
153
  prerelease: false
154
154
  version_requirements: !ruby/object:Gem::Requirement
155
155
  requirements:
156
- - - ">="
156
+ - - ">"
157
157
  - !ruby/object:Gem::Version
158
- version: 2.2.4
158
+ version: '1.6'
159
159
  - !ruby/object:Gem::Dependency
160
- name: em-http-request
160
+ name: rspec
161
161
  requirement: !ruby/object:Gem::Requirement
162
162
  requirements:
163
163
  - - ">="
164
164
  - !ruby/object:Gem::Version
165
- version: 1.0.2
165
+ version: 3.1.0
166
166
  type: :development
167
167
  prerelease: false
168
168
  version_requirements: !ruby/object:Gem::Requirement
169
169
  requirements:
170
170
  - - ">="
171
171
  - !ruby/object:Gem::Version
172
- version: 1.0.2
172
+ version: 3.1.0
173
173
  - !ruby/object:Gem::Dependency
174
- name: em-synchrony
174
+ name: httpclient
175
175
  requirement: !ruby/object:Gem::Requirement
176
176
  requirements:
177
177
  - - ">="
178
178
  - !ruby/object:Gem::Version
179
- version: 1.0.0
179
+ version: 2.2.4
180
180
  type: :development
181
181
  prerelease: false
182
182
  version_requirements: !ruby/object:Gem::Requirement
183
183
  requirements:
184
184
  - - ">="
185
185
  - !ruby/object:Gem::Version
186
- version: 1.0.0
186
+ version: 2.2.4
187
187
  - !ruby/object:Gem::Dependency
188
188
  name: excon
189
189
  requirement: !ruby/object:Gem::Requirement
@@ -421,9 +421,9 @@ licenses:
421
421
  - MIT
422
422
  metadata:
423
423
  bug_tracker_uri: https://github.com/bblimke/webmock/issues
424
- changelog_uri: https://github.com/bblimke/webmock/blob/v3.14.0/CHANGELOG.md
425
- documentation_uri: https://www.rubydoc.info/gems/webmock/3.14.0
426
- source_code_uri: https://github.com/bblimke/webmock/tree/v3.14.0
424
+ changelog_uri: https://github.com/bblimke/webmock/blob/v3.16.2/CHANGELOG.md
425
+ documentation_uri: https://www.rubydoc.info/gems/webmock/3.16.2
426
+ source_code_uri: https://github.com/bblimke/webmock/tree/v3.16.2
427
427
  wiki_uri: https://github.com/bblimke/webmock/wiki
428
428
  post_install_message:
429
429
  rdoc_options: []