webmock 3.14.0 → 3.18.1

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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/CI.yml +1 -0
  3. data/CHANGELOG.md +86 -2
  4. data/README.md +12 -3
  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 +22 -115
  12. data/lib/webmock/request_pattern.rb +23 -7
  13. data/lib/webmock/request_signature.rb +2 -2
  14. data/lib/webmock/request_stub.rb +15 -0
  15. data/lib/webmock/response.rb +8 -8
  16. data/lib/webmock/version.rb +1 -1
  17. data/lib/webmock/webmock.rb +10 -0
  18. data/minitest/webmock_spec.rb +1 -1
  19. data/spec/acceptance/async_http_client/async_http_client_spec.rb +5 -5
  20. data/spec/acceptance/curb/curb_spec.rb +11 -0
  21. data/spec/acceptance/em_http_request/em_http_request_spec.rb +1 -1
  22. data/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +1 -1
  23. data/spec/acceptance/excon/excon_spec.rb +2 -2
  24. data/spec/acceptance/net_http/net_http_shared.rb +46 -9
  25. data/spec/acceptance/net_http/net_http_spec.rb +49 -23
  26. data/spec/acceptance/net_http/real_net_http_spec.rb +1 -1
  27. data/spec/acceptance/patron/patron_spec.rb +19 -21
  28. data/spec/acceptance/shared/allowing_and_disabling_net_connect.rb +14 -14
  29. data/spec/acceptance/shared/callbacks.rb +2 -2
  30. data/spec/acceptance/shared/complex_cross_concern_behaviors.rb +1 -1
  31. data/spec/unit/request_pattern_spec.rb +12 -0
  32. data/spec/unit/request_signature_spec.rb +21 -1
  33. data/spec/unit/request_stub_spec.rb +35 -0
  34. data/spec/unit/response_spec.rb +29 -1
  35. data/spec/unit/webmock_spec.rb +54 -0
  36. data/webmock.gemspec +4 -4
  37. metadata +29 -29
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a8f150fbeccca419468dfebe632e3b5d365cb8c2df9a5e2a31e65591298786fb
4
- data.tar.gz: d92950bc5ae3510e599c40545f3e7a0ebaafa54a698f79a3c82243e240d35948
3
+ metadata.gz: 3a7e964f28dd42787fd854c5fcc87dba4cd61ef214c3dee485d9bd0d71fc1cdd
4
+ data.tar.gz: f6921a9b14ccd71f18b4674aca8d4d24bf377545a4ef8c56862f95cc56b6e8a7
5
5
  SHA512:
6
- metadata.gz: f58d569f7c633d758cee725e5dafb39edacb74e0017ba9c29de0cad8fe16d1f7740cc30847a9f129f6571232d2ca8172d7fb76a1eb3383e31ee8371472f141f9
7
- data.tar.gz: 52191c4e12ac3bf79b575f36d58823ea37ab17f83c7a62602889f361eafadabfa4b0b8ffc1f466eee6de53ea77578fc0149a60e14abdd8a7459b651afbeb85d0
6
+ metadata.gz: 80e85d1bb83018bdaab80aed649134757ee8ac591b98e9ccf1b6df10c0d8930db167dd5fd3bb039dd56a955d22c0cc6b1a1227bb88e8f3e56d31d0e1c9d7708e
7
+ data.tar.gz: c67f1f8486d6ee932bd9d01dff2bfcec35d9c3e3ed937e50e1f56d34f664ca11db4c9399c51e82da9316245c1d4727c726b9e307646aef80477764609ecf95ee
@@ -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,89 @@
1
1
  # Changelog
2
2
 
3
+ # 3.18.1
4
+
5
+ * Reverted simplified connection handing in Net::HTTP adapter due to https://github.com/bblimke/webmock/issues/999
6
+
7
+ # 3.18.0
8
+
9
+ * Net::BufferedIO is not replaced anymore.
10
+
11
+ Thanks to [Ray Zane](https://github.com/rzane)
12
+
13
+ * Simplified connection handing in Net::HTTP adapter.
14
+
15
+ Thanks to [Ray Zane](https://github.com/rzane)
16
+
17
+ # 3.17.1
18
+
19
+ * Fixed Syntax Error
20
+
21
+ Thanks to [Mark Spangler](https://github.com/mspangler)
22
+
23
+ # 3.17.0
24
+
25
+ * Minimum required Ruby version is 2.3
26
+
27
+ Thanks to [Go Sueyoshi](https://github.com/sue445)
28
+
29
+ * When using Net::HTTP, stubbed socket StubSocket#close and StubSocket#closed? behave more like the real sockets.
30
+
31
+ Thanks to [Ray Zane](https://github.com/rzane)
32
+
33
+ * Added `peeraddr`, `ssl_version` and `cipher` methods to stubbed sockets used by Net::HTTP.
34
+
35
+ Thanks to [Ray Zane](https://github.com/rzane)
36
+
37
+ * Added support for matching top-level array in JSON request body.
38
+
39
+ E.g.
40
+
41
+ ````
42
+ stub_request(:post, 'www.example.com').with(body: [{a: 1}])
43
+ ````
44
+
45
+ Thanks to [Cedric Sohrauer](https://github.com/cedrics)
46
+
47
+ # 3.16.2
48
+
49
+ * Minimum required Ruby version is 2.0.
50
+
51
+ # 3.16.0 (yanked)
52
+
53
+ * Fix leaky file descriptors and reuse socket for persistent connections.
54
+
55
+ Thanks to [Ray Zane](https://github.com/rzane)
56
+
57
+ * Allow specifying for what URIs or hosts, Net::HTTP should connect on start.
58
+
59
+ Thanks to [Ray Zane](https://github.com/rzane)
60
+
61
+ # 3.15.2
62
+
63
+ * Minimum required Ruby version is 2.0.
64
+
65
+ # 3.15.0 (yanked)
66
+
67
+ * fixed async-http adapter on Windows
68
+
69
+ Thanks to [Pavel Rosický](https://github.com/ahorek)
70
+
71
+ * Support for http.rb >= 5.0.2
72
+
73
+ Thanks to [ojab](https://github.com/ojab)
74
+
75
+ * Curb adapter supports headers with `:` character in the header value
76
+
77
+ Thanks to [Giorgio Gambino](https://github.com/mrbuzz)
78
+
79
+ * Support for matching body of JSON or application/x-www-form-urlencoded requests with content type header including charset.
80
+
81
+ Thanks to [Timmitry](https://github.com/Timmitry)
82
+
83
+ * Prevent double-wrapping http.rb features on non-stubbed requests
84
+
85
+ Thanks to [Michael Fairley](https://github.com/michaelfairley)
86
+
3
87
  # 3.14.0
4
88
 
5
89
  * Bump Addressable from 2.3.6 to 2.8.0
@@ -605,9 +689,9 @@
605
689
  * `WebMock.disable_net_connect` accepts `:allow` option with an object that responds to `#call`, receiving a `URI` object and returning a boolean:
606
690
 
607
691
 
608
- blacklist = ['google.com', 'facebook.com', 'apple.com']
692
+ denylist = ['google.com', 'facebook.com', 'apple.com']
609
693
  allowed_sites = lambda{|uri|
610
- blacklist.none?{|site| uri.host.include?(site) }
694
+ denylist.none?{|site| uri.host.include?(site) }
611
695
  }
612
696
  WebMock.disable_net_connect!(:allow => allowed_sites)
613
697
 
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,13 +1161,22 @@ 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
1169
+ * Go Sueyoshi
1170
+ * Cedric Sohrauer
1171
+ * Akira Matsuda
1172
+ * Mark Spangler
1164
1173
 
1165
1174
  For a full list of contributors you can visit the
1166
1175
  [contributors](https://github.com/bblimke/webmock/contributors) page.
1167
1176
 
1168
1177
  ## Background
1169
1178
 
1170
- Thank you Fakeweb! This library was inspired by [FakeWeb](http://fakeweb.rubyforge.org).
1179
+ Thank you Fakeweb! This library was inspired by [FakeWeb](https://github.com/chrisk/fakeweb).
1171
1180
  I imported some solutions from that project to WebMock. I also copied some code i.e Net:HTTP adapter.
1172
1181
  Fakeweb architecture unfortunately didn't allow me to extend it easily with the features I needed.
1173
1182
  I also preferred some things to work differently i.e request stub precedence.
@@ -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
@@ -10,24 +10,19 @@ module WebMock
10
10
  adapter_for :net_http
11
11
 
12
12
  OriginalNetHTTP = Net::HTTP unless const_defined?(:OriginalNetHTTP)
13
- OriginalNetBufferedIO = Net::BufferedIO unless const_defined?(:OriginalNetBufferedIO)
14
13
 
15
14
  def self.enable!
16
- Net.send(:remove_const, :BufferedIO)
17
15
  Net.send(:remove_const, :HTTP)
18
16
  Net.send(:remove_const, :HTTPSession)
19
17
  Net.send(:const_set, :HTTP, @webMockNetHTTP)
20
18
  Net.send(:const_set, :HTTPSession, @webMockNetHTTP)
21
- Net.send(:const_set, :BufferedIO, Net::WebMockNetBufferedIO)
22
19
  end
23
20
 
24
21
  def self.disable!
25
- Net.send(:remove_const, :BufferedIO)
26
22
  Net.send(:remove_const, :HTTP)
27
23
  Net.send(:remove_const, :HTTPSession)
28
24
  Net.send(:const_set, :HTTP, OriginalNetHTTP)
29
25
  Net.send(:const_set, :HTTPSession, OriginalNetHTTP)
30
- Net.send(:const_set, :BufferedIO, OriginalNetBufferedIO)
31
26
 
32
27
  #copy all constants from @webMockNetHTTP to original Net::HTTP
33
28
  #in case any constants were added to @webMockNetHTTP instead of Net::HTTP
@@ -98,13 +93,8 @@ module WebMock
98
93
  after_request.call(response)
99
94
  }
100
95
  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
96
+ ensure_actual_connection
97
+ super_with_after_request.call
108
98
  else
109
99
  start_with_connect {
110
100
  super_with_after_request.call
@@ -119,32 +109,33 @@ module WebMock
119
109
  raise IOError, 'HTTP session already opened' if @started
120
110
  if block_given?
121
111
  begin
112
+ @socket = Net::HTTP.socket_type.new
122
113
  @started = true
123
114
  return yield(self)
124
115
  ensure
125
116
  do_finish
126
117
  end
127
118
  end
119
+ @socket = Net::HTTP.socket_type.new
128
120
  @started = true
129
121
  self
130
122
  end
131
123
 
132
124
 
133
- def start_with_connect_without_finish # :yield: http
134
- if block_given?
135
- begin
136
- do_start
137
- return yield(self)
138
- end
125
+ def ensure_actual_connection
126
+ if @socket.is_a?(StubSocket)
127
+ @socket&.close
128
+ @socket = nil
129
+ do_start
139
130
  end
140
- do_start
141
- self
142
131
  end
143
132
 
144
133
  alias_method :start_with_connect, :start
145
134
 
146
135
  def start(&block)
147
- if WebMock::Config.instance.net_http_connect_on_start
136
+ uri = Addressable::URI.parse(WebMock::NetHTTPUtility.get_uri(self))
137
+
138
+ if WebMock.net_http_connect_on_start?(uri)
148
139
  super(&block)
149
140
  else
150
141
  start_without_connect(&block)
@@ -169,7 +160,7 @@ module WebMock
169
160
  response.extend Net::WebMockHTTPResponse
170
161
 
171
162
  if webmock_response.should_timeout
172
- raise timeout_exception, "execution expired"
163
+ raise Net::OpenTimeout, "execution expired"
173
164
  end
174
165
 
175
166
  webmock_response.raise_error_if_any
@@ -179,16 +170,6 @@ module WebMock
179
170
  response
180
171
  end
181
172
 
182
- def timeout_exception
183
- if defined?(Net::OpenTimeout)
184
- # Ruby 2.x
185
- Net::OpenTimeout
186
- else
187
- # Fallback, if things change
188
- Timeout::Error
189
- end
190
- end
191
-
192
173
  def build_webmock_response(net_http_response)
193
174
  webmock_response = WebMock::Response.new
194
175
  webmock_response.status = [
@@ -222,31 +203,21 @@ module WebMock
222
203
  end
223
204
  end
224
205
 
225
- # patch for StringIO behavior in Ruby 2.2.3
226
- # https://github.com/bblimke/webmock/issues/558
227
- class PatchedStringIO < StringIO #:nodoc:
228
-
229
- alias_method :orig_read_nonblock, :read_nonblock
230
-
231
- def read_nonblock(size, *args, **kwargs)
232
- args.reject! {|arg| !arg.is_a?(Hash)}
233
- orig_read_nonblock(size, *args, **kwargs)
234
- end
235
-
236
- end
237
-
238
206
  class StubSocket #:nodoc:
239
207
 
240
208
  attr_accessor :read_timeout, :continue_timeout, :write_timeout
241
209
 
242
210
  def initialize(*args)
211
+ @closed = false
243
212
  end
244
213
 
245
214
  def closed?
246
- @closed ||= true
215
+ @closed
247
216
  end
248
217
 
249
218
  def close
219
+ @closed = true
220
+ nil
250
221
  end
251
222
 
252
223
  def readuntil(*args)
@@ -258,57 +229,13 @@ class StubSocket #:nodoc:
258
229
 
259
230
  class StubIO
260
231
  def setsockopt(*args); end
232
+ def peer_cert; end
233
+ def peeraddr; ["AF_INET", 443, "127.0.0.1", "127.0.0.1"] end
234
+ def ssl_version; "TLSv1.3" end
235
+ def cipher; ["TLS_AES_128_GCM_SHA256", "TLSv1.3", 128, 128] end
261
236
  end
262
237
  end
263
238
 
264
- module Net #:nodoc: all
265
-
266
- class WebMockNetBufferedIO < BufferedIO
267
- def initialize(io, *args, **kwargs)
268
- io = case io
269
- when Socket, OpenSSL::SSL::SSLSocket, IO
270
- io
271
- when StringIO
272
- PatchedStringIO.new(io.string)
273
- when String
274
- PatchedStringIO.new(io)
275
- end
276
- raise "Unable to create local socket" unless io
277
-
278
- # Prior to 2.4.0 `BufferedIO` only takes a single argument (`io`) with no
279
- # options. Here we pass through our full set of arguments only if we're
280
- # on 2.4.0 or later, and use a simplified invocation otherwise.
281
- if RUBY_VERSION >= '2.4.0'
282
- super
283
- else
284
- super(io)
285
- end
286
- end
287
-
288
- if RUBY_VERSION >= '2.6.0'
289
- # https://github.com/ruby/ruby/blob/7d02441f0d6e5c9d0a73a024519eba4f69e36dce/lib/net/protocol.rb#L208
290
- # Modified version of method from ruby, so that nil is always passed into orig_read_nonblock to avoid timeout
291
- def rbuf_fill
292
- case rv = @io.read_nonblock(BUFSIZE, nil, exception: false)
293
- when String
294
- return if rv.nil?
295
- @rbuf << rv
296
- rv.clear
297
- return
298
- when :wait_readable
299
- @io.to_io.wait_readable(@read_timeout) or raise Net::ReadTimeout
300
- when :wait_writable
301
- @io.to_io.wait_writable(@read_timeout) or raise Net::ReadTimeout
302
- when nil
303
- raise EOFError, 'end of file reached'
304
- end while true
305
- end
306
- end
307
- end
308
-
309
- end
310
-
311
-
312
239
  module WebMock
313
240
  module NetHTTPUtility
314
241
 
@@ -325,7 +252,6 @@ module WebMock
325
252
  method = request.method.downcase.to_sym
326
253
 
327
254
  headers = Hash[*request.to_hash.map {|k,v| [k, v]}.inject([]) {|r,x| r + x}]
328
- validate_headers(headers)
329
255
 
330
256
  if request.body_stream
331
257
  body = request.body_stream.read
@@ -341,7 +267,7 @@ module WebMock
341
267
  WebMock::RequestSignature.new(method, uri, body: request.body, headers: headers)
342
268
  end
343
269
 
344
- def self.get_uri(net_http, path)
270
+ def self.get_uri(net_http, path = nil)
345
271
  protocol = net_http.use_ssl? ? "https" : "http"
346
272
 
347
273
  hostname = net_http.address
@@ -350,25 +276,6 @@ module WebMock
350
276
  "#{protocol}://#{hostname}:#{net_http.port}#{path}"
351
277
  end
352
278
 
353
- def self.validate_headers(headers)
354
- # For Ruby versions < 2.3.0, if you make a request with headers that are symbols
355
- # Net::HTTP raises a NoMethodError
356
- #
357
- # WebMock normalizes headers when creating a RequestSignature,
358
- # and will update all headers from symbols to strings.
359
- #
360
- # This could create a false positive in a test suite with WebMock.
361
- #
362
- # So before this point, WebMock raises an ArgumentError if any of the headers are symbols
363
- # instead of the cryptic NoMethodError "undefined method `split' ...` from Net::HTTP
364
- if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new('2.3.0')
365
- header_as_symbol = headers.keys.find {|header| header.is_a? Symbol}
366
- if header_as_symbol
367
- raise ArgumentError.new("Net:HTTP does not accept headers as symbols")
368
- end
369
- end
370
- end
371
-
372
279
  def self.check_right_http_connection
373
280
  @was_right_http_connection_loaded = defined?(RightHttpConnection)
374
281
  end
@@ -281,6 +281,8 @@ module WebMock
281
281
  if (@pattern).is_a?(Hash)
282
282
  return true if @pattern.empty?
283
283
  matching_body_hashes?(body_as_hash(body, content_type), @pattern, content_type)
284
+ elsif (@pattern).is_a?(Array)
285
+ matching_body_array?(body_as_hash(body, content_type), @pattern, content_type)
284
286
  elsif (@pattern).is_a?(WebMock::Matchers::HashIncludingMatcher)
285
287
  @pattern == body_as_hash(body, content_type)
286
288
  else
@@ -344,19 +346,33 @@ module WebMock
344
346
  def matching_body_hashes?(query_parameters, pattern, content_type)
345
347
  return false unless query_parameters.is_a?(Hash)
346
348
  return false unless query_parameters.keys.sort == pattern.keys.sort
347
- query_parameters.each do |key, actual|
349
+
350
+ query_parameters.all? do |key, actual|
348
351
  expected = pattern[key]
352
+ matching_values(actual, expected, content_type)
353
+ end
354
+ end
349
355
 
350
- if actual.is_a?(Hash) && expected.is_a?(Hash)
351
- return false unless matching_body_hashes?(actual, expected, content_type)
352
- else
353
- expected = WebMock::Util::ValuesStringifier.stringify_values(expected) if url_encoded_body?(content_type)
354
- return false unless expected === actual
355
- end
356
+ def matching_body_array?(query_parameters, pattern, content_type)
357
+ return false unless query_parameters.is_a?(Array)
358
+ return false unless query_parameters.length == pattern.length
359
+
360
+ query_parameters.each_with_index do |actual, index|
361
+ expected = pattern[index]
362
+ return false unless matching_values(actual, expected, content_type)
356
363
  end
364
+
357
365
  true
358
366
  end
359
367
 
368
+ def matching_values(actual, expected, content_type)
369
+ return matching_body_hashes?(actual, expected, content_type) if actual.is_a?(Hash) && expected.is_a?(Hash)
370
+ return matching_body_array?(actual, expected, content_type) if actual.is_a?(Array) && expected.is_a?(Array)
371
+
372
+ expected = WebMock::Util::ValuesStringifier.stringify_values(expected) if url_encoded_body?(content_type)
373
+ expected === actual
374
+ end
375
+
360
376
  def empty_string?(string)
361
377
  string.nil? || string == ""
362
378
  end
@@ -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&.fetch('Content-Type', nil)&.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&.fetch('Content-Type', nil)&.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
@@ -14,8 +14,11 @@ module WebMock
14
14
 
15
15
  class Response
16
16
  def initialize(options = {})
17
- if options.is_a?(IO) || options.is_a?(String)
17
+ case options
18
+ when IO, StringIO
18
19
  self.options = read_raw_response(options)
20
+ when String
21
+ self.options = read_raw_response(StringIO.new(options))
19
22
  else
20
23
  self.options = options
21
24
  end
@@ -120,13 +123,8 @@ module WebMock
120
123
  end
121
124
  end
122
125
 
123
- def read_raw_response(raw_response)
124
- if raw_response.is_a?(IO)
125
- string = raw_response.read
126
- raw_response.close
127
- raw_response = string
128
- end
129
- socket = ::Net::BufferedIO.new(raw_response)
126
+ def read_raw_response(io)
127
+ socket = ::Net::BufferedIO.new(io)
130
128
  response = ::Net::HTTPResponse.read_new(socket)
131
129
  transfer_encoding = response.delete('transfer-encoding') #chunks were already read by curl
132
130
  response.reading_body(socket, true) {}
@@ -138,6 +136,8 @@ module WebMock
138
136
  options[:body] = response.read_body
139
137
  options[:status] = [response.code.to_i, response.message]
140
138
  options
139
+ ensure
140
+ socket.close
141
141
  end
142
142
 
143
143
  InvalidBody = Class.new(StandardError)
@@ -1,3 +1,3 @@
1
1
  module WebMock
2
- VERSION = '3.14.0' unless defined?(::WebMock::VERSION)
2
+ VERSION = '3.18.1' 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