webmock 1.8.6 → 3.14.0

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 (138) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/CI.yml +37 -0
  3. data/.gitignore +6 -0
  4. data/CHANGELOG.md +1198 -0
  5. data/Gemfile +3 -15
  6. data/README.md +761 -305
  7. data/Rakefile +13 -40
  8. data/lib/webmock/api.rb +63 -17
  9. data/lib/webmock/callback_registry.rb +1 -1
  10. data/lib/webmock/config.rb +8 -0
  11. data/lib/webmock/cucumber.rb +2 -0
  12. data/lib/webmock/errors.rb +8 -24
  13. data/lib/webmock/http_lib_adapters/async_http_client_adapter.rb +216 -0
  14. data/lib/webmock/http_lib_adapters/curb_adapter.rb +148 -84
  15. data/lib/webmock/http_lib_adapters/em_http_request_adapter.rb +224 -4
  16. data/lib/webmock/http_lib_adapters/excon_adapter.rb +104 -34
  17. data/lib/webmock/http_lib_adapters/http_rb/client.rb +17 -0
  18. data/lib/webmock/http_lib_adapters/http_rb/request.rb +16 -0
  19. data/lib/webmock/http_lib_adapters/http_rb/response.rb +64 -0
  20. data/lib/webmock/http_lib_adapters/http_rb/streamer.rb +29 -0
  21. data/lib/webmock/http_lib_adapters/http_rb/webmock.rb +68 -0
  22. data/lib/webmock/http_lib_adapters/http_rb_adapter.rb +37 -0
  23. data/lib/webmock/http_lib_adapters/httpclient_adapter.rb +152 -86
  24. data/lib/webmock/http_lib_adapters/manticore_adapter.rb +145 -0
  25. data/lib/webmock/http_lib_adapters/net_http.rb +155 -46
  26. data/lib/webmock/http_lib_adapters/net_http_response.rb +1 -1
  27. data/lib/webmock/http_lib_adapters/patron_adapter.rb +16 -15
  28. data/lib/webmock/http_lib_adapters/typhoeus_hydra_adapter.rb +76 -82
  29. data/lib/webmock/matchers/any_arg_matcher.rb +13 -0
  30. data/lib/webmock/matchers/hash_argument_matcher.rb +21 -0
  31. data/lib/webmock/matchers/hash_excluding_matcher.rb +15 -0
  32. data/lib/webmock/matchers/hash_including_matcher.rb +4 -12
  33. data/lib/webmock/minitest.rb +29 -3
  34. data/lib/webmock/rack_response.rb +14 -7
  35. data/lib/webmock/request_body_diff.rb +64 -0
  36. data/lib/webmock/request_execution_verifier.rb +38 -17
  37. data/lib/webmock/request_pattern.rb +158 -38
  38. data/lib/webmock/request_registry.rb +3 -3
  39. data/lib/webmock/request_signature.rb +7 -3
  40. data/lib/webmock/request_signature_snippet.rb +61 -0
  41. data/lib/webmock/request_stub.rb +9 -6
  42. data/lib/webmock/response.rb +30 -15
  43. data/lib/webmock/rspec/matchers/request_pattern_matcher.rb +38 -2
  44. data/lib/webmock/rspec/matchers/webmock_matcher.rb +23 -2
  45. data/lib/webmock/rspec/matchers.rb +0 -1
  46. data/lib/webmock/rspec.rb +11 -2
  47. data/lib/webmock/stub_registry.rb +31 -10
  48. data/lib/webmock/stub_request_snippet.rb +14 -6
  49. data/lib/webmock/test_unit.rb +4 -4
  50. data/lib/webmock/util/hash_counter.rb +20 -6
  51. data/lib/webmock/util/hash_keys_stringifier.rb +5 -3
  52. data/lib/webmock/util/hash_validator.rb +17 -0
  53. data/lib/webmock/util/headers.rb +23 -2
  54. data/lib/webmock/util/json.rb +20 -7
  55. data/lib/webmock/util/query_mapper.rb +281 -0
  56. data/lib/webmock/util/uri.rb +29 -19
  57. data/lib/webmock/util/values_stringifier.rb +20 -0
  58. data/lib/webmock/util/version_checker.rb +40 -2
  59. data/lib/webmock/version.rb +1 -1
  60. data/lib/webmock/webmock.rb +56 -17
  61. data/lib/webmock.rb +56 -46
  62. data/minitest/test_helper.rb +8 -3
  63. data/minitest/test_webmock.rb +4 -1
  64. data/minitest/webmock_spec.rb +16 -6
  65. data/spec/acceptance/async_http_client/async_http_client_spec.rb +375 -0
  66. data/spec/acceptance/async_http_client/async_http_client_spec_helper.rb +73 -0
  67. data/spec/acceptance/curb/curb_spec.rb +227 -68
  68. data/spec/acceptance/curb/curb_spec_helper.rb +11 -8
  69. data/spec/acceptance/em_http_request/em_http_request_spec.rb +322 -28
  70. data/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +15 -10
  71. data/spec/acceptance/excon/excon_spec.rb +66 -4
  72. data/spec/acceptance/excon/excon_spec_helper.rb +21 -7
  73. data/spec/acceptance/http_rb/http_rb_spec.rb +93 -0
  74. data/spec/acceptance/http_rb/http_rb_spec_helper.rb +54 -0
  75. data/spec/acceptance/httpclient/httpclient_spec.rb +152 -11
  76. data/spec/acceptance/httpclient/httpclient_spec_helper.rb +25 -16
  77. data/spec/acceptance/manticore/manticore_spec.rb +107 -0
  78. data/spec/acceptance/manticore/manticore_spec_helper.rb +35 -0
  79. data/spec/acceptance/net_http/net_http_shared.rb +52 -24
  80. data/spec/acceptance/net_http/net_http_spec.rb +164 -50
  81. data/spec/acceptance/net_http/net_http_spec_helper.rb +19 -10
  82. data/spec/acceptance/net_http/real_net_http_spec.rb +1 -1
  83. data/spec/acceptance/patron/patron_spec.rb +29 -40
  84. data/spec/acceptance/patron/patron_spec_helper.rb +15 -11
  85. data/spec/acceptance/shared/allowing_and_disabling_net_connect.rb +229 -58
  86. data/spec/acceptance/shared/callbacks.rb +32 -30
  87. data/spec/acceptance/shared/complex_cross_concern_behaviors.rb +20 -5
  88. data/spec/acceptance/shared/enabling_and_disabling_webmock.rb +14 -14
  89. data/spec/acceptance/shared/precedence_of_stubs.rb +6 -6
  90. data/spec/acceptance/shared/request_expectations.rb +560 -296
  91. data/spec/acceptance/shared/returning_declared_responses.rb +180 -138
  92. data/spec/acceptance/shared/stubbing_requests.rb +385 -154
  93. data/spec/acceptance/typhoeus/typhoeus_hydra_spec.rb +78 -17
  94. data/spec/acceptance/typhoeus/typhoeus_hydra_spec_helper.rb +19 -15
  95. data/spec/acceptance/webmock_shared.rb +2 -2
  96. data/spec/fixtures/test.txt +1 -0
  97. data/spec/quality_spec.rb +27 -3
  98. data/spec/spec_helper.rb +11 -20
  99. data/spec/support/failures.rb +9 -0
  100. data/spec/support/my_rack_app.rb +8 -3
  101. data/spec/support/network_connection.rb +7 -13
  102. data/spec/support/webmock_server.rb +8 -3
  103. data/spec/unit/api_spec.rb +175 -0
  104. data/spec/unit/errors_spec.rb +116 -19
  105. data/spec/unit/http_lib_adapters/http_lib_adapter_registry_spec.rb +1 -1
  106. data/spec/unit/http_lib_adapters/http_lib_adapter_spec.rb +2 -2
  107. data/spec/unit/matchers/hash_excluding_matcher_spec.rb +61 -0
  108. data/spec/unit/matchers/hash_including_matcher_spec.rb +87 -0
  109. data/spec/unit/rack_response_spec.rb +54 -16
  110. data/spec/unit/request_body_diff_spec.rb +90 -0
  111. data/spec/unit/request_execution_verifier_spec.rb +147 -39
  112. data/spec/unit/request_pattern_spec.rb +462 -198
  113. data/spec/unit/request_registry_spec.rb +29 -9
  114. data/spec/unit/request_signature_snippet_spec.rb +89 -0
  115. data/spec/unit/request_signature_spec.rb +91 -49
  116. data/spec/unit/request_stub_spec.rb +71 -70
  117. data/spec/unit/response_spec.rb +100 -81
  118. data/spec/unit/stub_registry_spec.rb +37 -20
  119. data/spec/unit/stub_request_snippet_spec.rb +51 -31
  120. data/spec/unit/util/hash_counter_spec.rb +6 -6
  121. data/spec/unit/util/hash_keys_stringifier_spec.rb +4 -4
  122. data/spec/unit/util/headers_spec.rb +4 -4
  123. data/spec/unit/util/json_spec.rb +29 -3
  124. data/spec/unit/util/query_mapper_spec.rb +157 -0
  125. data/spec/unit/util/uri_spec.rb +150 -36
  126. data/spec/unit/util/version_checker_spec.rb +15 -9
  127. data/spec/unit/webmock_spec.rb +57 -4
  128. data/test/http_request.rb +3 -3
  129. data/test/shared_test.rb +45 -13
  130. data/test/test_helper.rb +1 -1
  131. data/test/test_webmock.rb +6 -0
  132. data/webmock.gemspec +30 -11
  133. metadata +308 -199
  134. data/.rvmrc +0 -1
  135. data/.travis.yml +0 -11
  136. data/Guardfile +0 -24
  137. data/lib/webmock/http_lib_adapters/em_http_request/em_http_request_0_x.rb +0 -151
  138. data/lib/webmock/http_lib_adapters/em_http_request/em_http_request_1_x.rb +0 -210
@@ -28,15 +28,23 @@ module WebMock
28
28
  Net.send(:const_set, :HTTP, OriginalNetHTTP)
29
29
  Net.send(:const_set, :HTTPSession, OriginalNetHTTP)
30
30
  Net.send(:const_set, :BufferedIO, OriginalNetBufferedIO)
31
+
32
+ #copy all constants from @webMockNetHTTP to original Net::HTTP
33
+ #in case any constants were added to @webMockNetHTTP instead of Net::HTTP
34
+ #after WebMock was enabled.
35
+ #i.e Net::HTTP::DigestAuth
36
+ @webMockNetHTTP.constants.each do |constant|
37
+ if !OriginalNetHTTP.constants.map(&:to_s).include?(constant.to_s)
38
+ OriginalNetHTTP.send(:const_set, constant, @webMockNetHTTP.const_get(constant))
39
+ end
40
+ end
31
41
  end
32
42
 
33
43
  @webMockNetHTTP = Class.new(Net::HTTP) do
34
44
  class << self
35
- def socket_type_with_webmock
45
+ def socket_type
36
46
  StubSocket
37
47
  end
38
- alias_method :socket_type_without_webmock, :socket_type
39
- alias_method :socket_type, :socket_type_with_webmock
40
48
 
41
49
  if Module.method(:const_defined?).arity == 1
42
50
  def const_defined?(name)
@@ -58,12 +66,12 @@ module WebMock
58
66
 
59
67
  if Module.method(:constants).arity != 0
60
68
  def constants(inherit=true)
61
- super + self.superclass.constants(inherit)
69
+ (super + self.superclass.constants(inherit)).uniq
62
70
  end
63
71
  end
64
72
  end
65
73
 
66
- def request_with_webmock(request, body = nil, &block)
74
+ def request(request, body = nil, &block)
67
75
  request_signature = WebMock::NetHTTPUtility.request_signature_from_request(self, request, body)
68
76
 
69
77
  WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
@@ -71,7 +79,7 @@ module WebMock
71
79
  if webmock_response = WebMock::StubRegistry.instance.response_for_request(request_signature)
72
80
  @socket = Net::HTTP.socket_type.new
73
81
  WebMock::CallbackRegistry.invoke_callbacks(
74
- {:lib => :net_http}, request_signature, webmock_response)
82
+ {lib: :net_http}, request_signature, webmock_response)
75
83
  build_net_http_response(webmock_response, &block)
76
84
  elsif WebMock.net_connect_allowed?(request_signature.uri)
77
85
  check_right_http_connection
@@ -79,28 +87,33 @@ module WebMock
79
87
  if WebMock::CallbackRegistry.any_callbacks?
80
88
  webmock_response = build_webmock_response(response)
81
89
  WebMock::CallbackRegistry.invoke_callbacks(
82
- {:lib => :net_http, :real_request => true}, request_signature, webmock_response)
90
+ {lib: :net_http, real_request: true}, request_signature, webmock_response)
83
91
  end
84
92
  response.extend Net::WebMockHTTPResponse
85
93
  block.call response if block
86
94
  response
87
95
  end
88
- response = if (started? && !WebMock::Config.instance.net_http_connect_on_start) || !started?
89
- @started = false #otherwise start_with_connect wouldn't execute and connect
96
+ super_with_after_request = lambda {
97
+ response = super(request, nil, &nil)
98
+ after_request.call(response)
99
+ }
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
108
+ else
90
109
  start_with_connect {
91
- response = request_without_webmock(request, nil)
92
- after_request.call(response)
110
+ super_with_after_request.call
93
111
  }
94
- else
95
- response = request_without_webmock(request, nil)
96
- after_request.call(response)
97
112
  end
98
113
  else
99
114
  raise WebMock::NetConnectNotAllowedError.new(request_signature)
100
115
  end
101
116
  end
102
- alias_method :request_without_webmock, :request
103
- alias_method :request, :request_with_webmock
104
117
 
105
118
  def start_without_connect
106
119
  raise IOError, 'HTTP session already opened' if @started
@@ -116,19 +129,34 @@ module WebMock
116
129
  self
117
130
  end
118
131
 
119
- def start_with_conditional_connect(&block)
132
+
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
142
+ end
143
+
144
+ alias_method :start_with_connect, :start
145
+
146
+ def start(&block)
120
147
  if WebMock::Config.instance.net_http_connect_on_start
121
- start_with_connect(&block)
148
+ super(&block)
122
149
  else
123
150
  start_without_connect(&block)
124
151
  end
125
152
  end
126
- alias_method :start_with_connect, :start
127
- alias_method :start, :start_with_conditional_connect
128
153
 
129
154
  def build_net_http_response(webmock_response, &block)
130
155
  response = Net::HTTPResponse.send(:response_class, webmock_response.status[0].to_s).new("1.0", webmock_response.status[0].to_s, webmock_response.status[1])
131
- response.instance_variable_set(:@body, webmock_response.body)
156
+ body = webmock_response.body
157
+ body = nil if webmock_response.status[0].to_s == '204'
158
+
159
+ response.instance_variable_set(:@body, body)
132
160
  webmock_response.headers.to_a.each do |name, values|
133
161
  values = [values] unless values.is_a?(Array)
134
162
  values.each do |value|
@@ -140,7 +168,9 @@ module WebMock
140
168
 
141
169
  response.extend Net::WebMockHTTPResponse
142
170
 
143
- raise Timeout::Error, "execution expired" if webmock_response.should_timeout
171
+ if webmock_response.should_timeout
172
+ raise timeout_exception, "execution expired"
173
+ end
144
174
 
145
175
  webmock_response.raise_error_if_any
146
176
 
@@ -149,6 +179,16 @@ module WebMock
149
179
  response
150
180
  end
151
181
 
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
+
152
192
  def build_webmock_response(net_http_response)
153
193
  webmock_response = WebMock::Response.new
154
194
  webmock_response.status = [
@@ -182,9 +222,22 @@ module WebMock
182
222
  end
183
223
  end
184
224
 
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
+
185
238
  class StubSocket #:nodoc:
186
239
 
187
- attr_accessor :read_timeout
240
+ attr_accessor :read_timeout, :continue_timeout, :write_timeout
188
241
 
189
242
  def initialize(*args)
190
243
  end
@@ -193,29 +246,64 @@ class StubSocket #:nodoc:
193
246
  @closed ||= true
194
247
  end
195
248
 
249
+ def close
250
+ end
251
+
196
252
  def readuntil(*args)
197
253
  end
198
254
 
255
+ def io
256
+ @io ||= StubIO.new
257
+ end
258
+
259
+ class StubIO
260
+ def setsockopt(*args); end
261
+ end
199
262
  end
200
263
 
201
264
  module Net #:nodoc: all
202
265
 
203
266
  class WebMockNetBufferedIO < BufferedIO
204
- def initialize_with_webmock(io, debug_output = nil)
205
- @read_timeout = 60
206
- @rbuf = ''
207
- @debug_output = debug_output
208
-
209
- @io = case io
267
+ def initialize(io, *args, **kwargs)
268
+ io = case io
210
269
  when Socket, OpenSSL::SSL::SSLSocket, IO
211
270
  io
271
+ when StringIO
272
+ PatchedStringIO.new(io.string)
212
273
  when String
213
- StringIO.new(io)
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
214
305
  end
215
- raise "Unable to create local socket" unless @io
216
306
  end
217
- alias_method :initialize_without_webmock, :initialize
218
- alias_method :initialize, :initialize_with_webmock
219
307
  end
220
308
 
221
309
  end
@@ -225,25 +313,19 @@ module WebMock
225
313
  module NetHTTPUtility
226
314
 
227
315
  def self.request_signature_from_request(net_http, request, body = nil)
228
- protocol = net_http.use_ssl? ? "https" : "http"
229
-
230
316
  path = request.path
231
- path = WebMock::Util::URI.heuristic_parse(request.path).request_uri if request.path =~ /^http/
232
317
 
233
- if request["authorization"] =~ /^Basic /
234
- userinfo = WebMock::Util::Headers.decode_userinfo_from_header(request["authorization"])
235
- userinfo = WebMock::Util::URI.encode_unsafe_chars_in_userinfo(userinfo) + "@"
236
- else
237
- userinfo = ""
318
+ if path.respond_to?(:request_uri) #https://github.com/bblimke/webmock/issues/288
319
+ path = path.request_uri
238
320
  end
239
321
 
240
- uri = "#{protocol}://#{userinfo}#{net_http.address}:#{net_http.port}#{path}"
322
+ path = WebMock::Util::URI.heuristic_parse(path).request_uri if path =~ /^http/
323
+
324
+ uri = get_uri(net_http, path)
241
325
  method = request.method.downcase.to_sym
242
326
 
243
327
  headers = Hash[*request.to_hash.map {|k,v| [k, v]}.inject([]) {|r,x| r + x}]
244
-
245
- headers.reject! {|k,v| k =~ /[Aa]uthorization/ && v.first =~ /^Basic / } #we added it to url userinfo
246
-
328
+ validate_headers(headers)
247
329
 
248
330
  if request.body_stream
249
331
  body = request.body_stream.read
@@ -256,9 +338,36 @@ module WebMock
256
338
  request.set_body_internal body
257
339
  end
258
340
 
259
- WebMock::RequestSignature.new(method, uri, :body => request.body, :headers => headers)
341
+ WebMock::RequestSignature.new(method, uri, body: request.body, headers: headers)
342
+ end
343
+
344
+ def self.get_uri(net_http, path)
345
+ protocol = net_http.use_ssl? ? "https" : "http"
346
+
347
+ hostname = net_http.address
348
+ hostname = "[#{hostname}]" if /\A\[.*\]\z/ !~ hostname && /:/ =~ hostname
349
+
350
+ "#{protocol}://#{hostname}:#{net_http.port}#{path}"
260
351
  end
261
352
 
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
262
371
 
263
372
  def self.check_right_http_connection
264
373
  @was_right_http_connection_loaded = defined?(RightHttpConnection)
@@ -23,7 +23,7 @@ module Net
23
23
  return nil if @body.nil?
24
24
 
25
25
  dest ||= ::Net::ReadAdapter.new(block)
26
- dest << @body
26
+ dest << @body.dup
27
27
  @body = dest
28
28
  ensure
29
29
  # allow subsequent calls to #read_body to proceed as normal, without our hack...
@@ -13,7 +13,7 @@ if defined?(::Patron)
13
13
  OriginalPatronSession = ::Patron::Session unless const_defined?(:OriginalPatronSession)
14
14
 
15
15
  class WebMockPatronSession < ::Patron::Session
16
- def handle_request_with_webmock(req)
16
+ def handle_request(req)
17
17
  request_signature =
18
18
  WebMock::HttpLibAdapters::PatronAdapter.build_request_signature(req)
19
19
 
@@ -25,15 +25,15 @@ if defined?(::Patron)
25
25
  res = WebMock::HttpLibAdapters::PatronAdapter.
26
26
  build_patron_response(webmock_response, default_response_charset)
27
27
  WebMock::CallbackRegistry.invoke_callbacks(
28
- {:lib => :patron}, request_signature, webmock_response)
28
+ {lib: :patron}, request_signature, webmock_response)
29
29
  res
30
30
  elsif WebMock.net_connect_allowed?(request_signature.uri)
31
- res = handle_request_without_webmock(req)
31
+ res = super
32
32
  if WebMock::CallbackRegistry.any_callbacks?
33
33
  webmock_response = WebMock::HttpLibAdapters::PatronAdapter.
34
34
  build_webmock_response(res)
35
35
  WebMock::CallbackRegistry.invoke_callbacks(
36
- {:lib => :patron, :real_request => true}, request_signature,
36
+ {lib: :patron, real_request: true}, request_signature,
37
37
  webmock_response)
38
38
  end
39
39
  res
@@ -41,9 +41,6 @@ if defined?(::Patron)
41
41
  raise WebMock::NetConnectNotAllowedError.new(request_signature)
42
42
  end
43
43
  end
44
-
45
- alias_method :handle_request_without_webmock, :handle_request
46
- alias_method :handle_request, :handle_request_with_webmock
47
44
  end
48
45
 
49
46
  def self.enable!
@@ -71,10 +68,8 @@ if defined?(::Patron)
71
68
  def self.build_request_signature(req)
72
69
  uri = WebMock::Util::URI.heuristic_parse(req.url)
73
70
  uri.path = uri.normalized_path.gsub("[^:]//","/")
74
- uri.user = req.username
75
- uri.password = req.password
76
71
 
77
- if [:put, :post].include?(req.action)
72
+ if [:put, :post, :patch].include?(req.action)
78
73
  if req.file_name
79
74
  if !File.exist?(req.file_name) || !File.readable?(req.file_name)
80
75
  raise ArgumentError.new("Unable to open specified file.")
@@ -87,11 +82,17 @@ if defined?(::Patron)
87
82
  end
88
83
  end
89
84
 
85
+ headers = req.headers
86
+
87
+ if req.credentials
88
+ headers['Authorization'] = WebMock::Util::Headers.basic_auth_header(req.credentials)
89
+ end
90
+
90
91
  request_signature = WebMock::RequestSignature.new(
91
92
  req.action,
92
93
  uri.to_s,
93
- :body => request_body,
94
- :headers => req.headers
94
+ body: request_body,
95
+ headers: headers
95
96
  )
96
97
  request_signature
97
98
  end
@@ -105,11 +106,11 @@ if defined?(::Patron)
105
106
  header_data = ([status_line] + header_fields).join("\r\n")
106
107
 
107
108
  ::Patron::Response.new(
108
- "",
109
+ "".dup,
109
110
  webmock_response.status[0],
110
111
  0,
111
112
  header_data,
112
- webmock_response.body,
113
+ webmock_response.body.dup,
113
114
  default_response_charset
114
115
  )
115
116
  end
@@ -117,7 +118,7 @@ if defined?(::Patron)
117
118
  def self.build_webmock_response(patron_response)
118
119
  webmock_response = WebMock::Response.new
119
120
  reason = patron_response.status_line.
120
- scan(%r(\AHTTP/(\d+\.\d+)\s+(\d\d\d)\s*([^\r\n]+)?))[0][2]
121
+ scan(%r(\AHTTP/(\d+(?:\.\d+)?)\s+(\d\d\d)\s*([^\r\n]+)?))[0][2]
121
122
  webmock_response.status = [patron_response.status, reason]
122
123
  webmock_response.body = patron_response.body
123
124
  webmock_response.headers = patron_response.headers
@@ -14,53 +14,63 @@ if defined?(Typhoeus)
14
14
 
15
15
  def self.enable!
16
16
  @disabled = false
17
+ add_before_callback
17
18
  add_after_request_callback
18
- ::Typhoeus::Hydra.allow_net_connect = true
19
+ ::Typhoeus::Config.block_connection = true
19
20
  end
20
21
 
21
22
  def self.disable!
22
23
  @disabled = true
23
24
  remove_after_request_callback
24
- ::Typhoeus::Hydra.allow_net_connect = true
25
+ remove_before_callback
26
+ ::Typhoeus::Config.block_connection = false
25
27
  end
26
28
 
27
29
  def self.disabled?
28
30
  !!@disabled
29
31
  end
30
32
 
33
+ def self.add_before_callback
34
+ unless Typhoeus.before.include?(BEFORE_CALLBACK)
35
+ Typhoeus.before << BEFORE_CALLBACK
36
+ end
37
+ end
38
+
39
+ def self.remove_before_callback
40
+ Typhoeus.before.delete_if {|v| v == BEFORE_CALLBACK }
41
+ end
42
+
31
43
  def self.add_after_request_callback
32
- unless Typhoeus::Hydra.
33
- global_hooks[:after_request_before_on_complete].
34
- include?(AFTER_REQUEST_CALLBACK)
35
- Typhoeus::Hydra.
36
- global_hooks[:after_request_before_on_complete] << AFTER_REQUEST_CALLBACK
44
+ unless Typhoeus.on_complete.include?(AFTER_REQUEST_CALLBACK)
45
+ Typhoeus.on_complete << AFTER_REQUEST_CALLBACK
37
46
  end
38
47
  end
39
48
 
40
49
  def self.remove_after_request_callback
41
- Typhoeus::Hydra.global_hooks[:after_request_before_on_complete].
42
- delete_if {|v| v == AFTER_REQUEST_CALLBACK }
50
+ Typhoeus.on_complete.delete_if {|v| v == AFTER_REQUEST_CALLBACK }
43
51
  end
44
52
 
45
53
  def self.build_request_signature(req)
46
54
  uri = WebMock::Util::URI.heuristic_parse(req.url)
47
55
  uri.path = uri.normalized_path.gsub("[^:]//","/")
48
- if req.username
49
- uri.user = req.username
50
- uri.password = req.password
56
+
57
+ headers = req.options[:headers]
58
+
59
+ if req.options[:userpwd]
60
+ headers['Authorization'] = WebMock::Util::Headers.basic_auth_header(req.options[:userpwd])
51
61
  end
52
62
 
53
- body = req.body
63
+ body = req.options[:body]
54
64
 
55
- if req.params && req.method == :post
56
- body = request_body_for_post_request_with_params(req)
65
+ if body.is_a?(Hash)
66
+ body = WebMock::Util::QueryMapper.values_to_query(body)
57
67
  end
58
68
 
59
69
  request_signature = WebMock::RequestSignature.new(
60
- req.method,
70
+ req.options[:method] || :get,
61
71
  uri.to_s,
62
- :body => body,
63
- :headers => req.headers
72
+ body: body,
73
+ headers: headers
64
74
  )
65
75
 
66
76
  req.instance_variable_set(:@__webmock_request_signature, request_signature)
@@ -68,45 +78,35 @@ if defined?(Typhoeus)
68
78
  request_signature
69
79
  end
70
80
 
71
- def self.request_body_for_post_request_with_params(req)
72
- params = req.params
73
- form = Typhoeus::Form.new(params)
74
- form.process!
75
- form.to_s
76
- end
77
81
 
78
82
  def self.build_webmock_response(typhoeus_response)
79
83
  webmock_response = WebMock::Response.new
80
84
  webmock_response.status = [typhoeus_response.code, typhoeus_response.status_message]
81
85
  webmock_response.body = typhoeus_response.body
82
- webmock_response.headers = typhoeus_response.headers_hash
86
+ webmock_response.headers = typhoeus_response.headers
83
87
  webmock_response
84
88
  end
85
89
 
86
- def self.stub_typhoeus(request_signature, webmock_response, typhoeus)
90
+ def self.generate_typhoeus_response(request_signature, webmock_response)
87
91
  response = if webmock_response.should_timeout
88
92
  ::Typhoeus::Response.new(
89
- :code => 0,
90
- :status_message => "",
91
- :body => "",
92
- :headers_hash => {},
93
- :curl_return_code => 28
93
+ code: 0,
94
+ status_message: "",
95
+ body: "",
96
+ headers: {},
97
+ return_code: :operation_timedout
94
98
  )
95
99
  else
96
100
  ::Typhoeus::Response.new(
97
- :code => webmock_response.status[0],
98
- :status_message => webmock_response.status[1],
99
- :body => webmock_response.body,
100
- :headers_hash => webmock_response.headers
101
+ code: webmock_response.status[0],
102
+ status_message: webmock_response.status[1],
103
+ body: webmock_response.body,
104
+ headers: webmock_response.headers,
105
+ effective_url: request_signature.uri
101
106
  )
102
107
  end
103
-
104
-
105
- typhoeus.stub(
106
- request_signature.method || :any,
107
- /.*/,
108
- :webmock_stub => true
109
- ).and_return(response)
108
+ response.mock = :webmock
109
+ response
110
110
  end
111
111
 
112
112
  def self.request_hash(request_signature)
@@ -118,62 +118,56 @@ if defined?(Typhoeus)
118
118
  hash
119
119
  end
120
120
 
121
- AFTER_REQUEST_CALLBACK = Proc.new do |request|
121
+ AFTER_REQUEST_CALLBACK = Proc.new do |response|
122
+ request = response.request
122
123
  request_signature = request.instance_variable_get(:@__webmock_request_signature)
123
124
  webmock_response =
124
125
  ::WebMock::HttpLibAdapters::TyphoeusAdapter.
125
- build_webmock_response(request.response)
126
- if request.response.mock?
126
+ build_webmock_response(response)
127
+ if response.mock
127
128
  WebMock::CallbackRegistry.invoke_callbacks(
128
- {:lib => :typhoeus},
129
+ {lib: :typhoeus},
129
130
  request_signature,
130
131
  webmock_response
131
132
  )
132
133
  else
133
134
  WebMock::CallbackRegistry.invoke_callbacks(
134
- {:lib => :typhoeus, :real_request => true},
135
+ {lib: :typhoeus, real_request: true},
135
136
  request_signature,
136
137
  webmock_response
137
138
  )
138
139
  end
139
140
  end
140
- end
141
- end
142
- end
143
-
144
-
145
- module Typhoeus
146
- class Hydra
147
- def queue_with_webmock(request)
148
- self.clear_webmock_stubs
149
-
150
- if WebMock::HttpLibAdapters::TyphoeusAdapter.disabled?
151
- return queue_without_webmock(request)
152
- end
153
-
154
- request_signature =
155
- ::WebMock::HttpLibAdapters::TyphoeusAdapter.build_request_signature(request)
156
141
 
157
- ::WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
158
-
159
- if webmock_response = ::WebMock::StubRegistry.instance.response_for_request(request_signature)
160
- ::WebMock::HttpLibAdapters::TyphoeusAdapter.
161
- stub_typhoeus(request_signature, webmock_response, self)
162
- webmock_response.raise_error_if_any
163
- elsif !WebMock.net_connect_allowed?(request_signature.uri)
164
- raise WebMock::NetConnectNotAllowedError.new(request_signature)
142
+ BEFORE_CALLBACK = Proc.new do |request|
143
+ Typhoeus::Expectation.all.delete_if {|e| e.from == :webmock }
144
+ res = true
145
+
146
+ unless WebMock::HttpLibAdapters::TyphoeusAdapter.disabled?
147
+ request_signature = ::WebMock::HttpLibAdapters::TyphoeusAdapter.build_request_signature(request)
148
+ request.block_connection = false;
149
+
150
+ ::WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
151
+
152
+ if webmock_response = ::WebMock::StubRegistry.instance.response_for_request(request_signature)
153
+ # ::WebMock::HttpLibAdapters::TyphoeusAdapter.stub_typhoeus(request_signature, webmock_response, self)
154
+ response = ::WebMock::HttpLibAdapters::TyphoeusAdapter.generate_typhoeus_response(request_signature, webmock_response)
155
+ if request.respond_to?(:on_headers)
156
+ request.execute_headers_callbacks(response)
157
+ end
158
+ if request.respond_to?(:streaming?) && request.streaming?
159
+ response.options[:response_body] = ""
160
+ request.on_body.each { |callback| callback.call(webmock_response.body, response) }
161
+ end
162
+ request.finish(response)
163
+ webmock_response.raise_error_if_any
164
+ res = false
165
+ elsif !WebMock.net_connect_allowed?(request_signature.uri)
166
+ raise WebMock::NetConnectNotAllowedError.new(request_signature)
167
+ end
168
+ end
169
+ res
165
170
  end
166
- queue_without_webmock(request)
167
- end
168
-
169
- alias_method :queue_without_webmock, :queue
170
- alias_method :queue, :queue_with_webmock
171
-
172
- def clear_webmock_stubs
173
- self.stubs = [] unless self.stubs
174
- self.stubs.delete_if {|s|
175
- s.instance_variable_get(:@options)[:webmock_stub]
176
- }
177
171
  end
178
172
  end
179
173
  end
@@ -0,0 +1,13 @@
1
+ module WebMock
2
+ module Matchers
3
+ # this is a based on RSpec::Mocks::ArgumentMatchers::AnyArgMatcher
4
+ class AnyArgMatcher
5
+ def initialize(ignore)
6
+ end
7
+
8
+ def ==(other)
9
+ true
10
+ end
11
+ end
12
+ end
13
+ end