webmock 3.0.1 → 3.20.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 +5 -5
  2. data/CHANGELOG.md +558 -2
  3. data/README.md +200 -42
  4. data/lib/webmock/api.rb +14 -0
  5. data/lib/webmock/assertion_failure.rb +2 -0
  6. data/lib/webmock/callback_registry.rb +2 -0
  7. data/lib/webmock/config.rb +2 -0
  8. data/lib/webmock/cucumber.rb +2 -0
  9. data/lib/webmock/deprecation.rb +2 -0
  10. data/lib/webmock/errors.rb +2 -0
  11. data/lib/webmock/http_lib_adapters/async_http_client_adapter.rb +223 -0
  12. data/lib/webmock/http_lib_adapters/curb_adapter.rb +21 -5
  13. data/lib/webmock/http_lib_adapters/em_http_request_adapter.rb +20 -9
  14. data/lib/webmock/http_lib_adapters/excon_adapter.rb +7 -2
  15. data/lib/webmock/http_lib_adapters/http_lib_adapter.rb +2 -0
  16. data/lib/webmock/http_lib_adapters/http_lib_adapter_registry.rb +2 -0
  17. data/lib/webmock/http_lib_adapters/http_rb/client.rb +4 -1
  18. data/lib/webmock/http_lib_adapters/http_rb/request.rb +19 -1
  19. data/lib/webmock/http_lib_adapters/http_rb/response.rb +29 -3
  20. data/lib/webmock/http_lib_adapters/http_rb/streamer.rb +11 -3
  21. data/lib/webmock/http_lib_adapters/http_rb/webmock.rb +9 -3
  22. data/lib/webmock/http_lib_adapters/http_rb_adapter.rb +2 -0
  23. data/lib/webmock/http_lib_adapters/httpclient_adapter.rb +30 -9
  24. data/lib/webmock/http_lib_adapters/manticore_adapter.rb +35 -15
  25. data/lib/webmock/http_lib_adapters/net_http.rb +38 -89
  26. data/lib/webmock/http_lib_adapters/net_http_response.rb +3 -1
  27. data/lib/webmock/http_lib_adapters/patron_adapter.rb +6 -4
  28. data/lib/webmock/http_lib_adapters/typhoeus_hydra_adapter.rb +18 -2
  29. data/lib/webmock/matchers/any_arg_matcher.rb +15 -0
  30. data/lib/webmock/matchers/hash_argument_matcher.rb +23 -0
  31. data/lib/webmock/matchers/hash_excluding_matcher.rb +17 -0
  32. data/lib/webmock/matchers/hash_including_matcher.rb +6 -23
  33. data/lib/webmock/minitest.rb +2 -0
  34. data/lib/webmock/rack_response.rb +3 -1
  35. data/lib/webmock/request_body_diff.rb +3 -1
  36. data/lib/webmock/request_execution_verifier.rb +4 -3
  37. data/lib/webmock/request_pattern.rb +132 -52
  38. data/lib/webmock/request_registry.rb +3 -1
  39. data/lib/webmock/request_signature.rb +5 -3
  40. data/lib/webmock/request_signature_snippet.rb +6 -4
  41. data/lib/webmock/request_stub.rb +34 -0
  42. data/lib/webmock/response.rb +22 -14
  43. data/lib/webmock/responses_sequence.rb +2 -0
  44. data/lib/webmock/rspec/matchers/request_pattern_matcher.rb +2 -0
  45. data/lib/webmock/rspec/matchers/webmock_matcher.rb +2 -0
  46. data/lib/webmock/rspec/matchers.rb +2 -0
  47. data/lib/webmock/rspec.rb +12 -3
  48. data/lib/webmock/stub_registry.rb +28 -11
  49. data/lib/webmock/stub_request_snippet.rb +12 -6
  50. data/lib/webmock/test_unit.rb +3 -3
  51. data/lib/webmock/util/hash_counter.rb +15 -9
  52. data/lib/webmock/util/hash_keys_stringifier.rb +2 -0
  53. data/lib/webmock/util/hash_validator.rb +2 -0
  54. data/lib/webmock/util/headers.rb +39 -11
  55. data/lib/webmock/util/json.rb +3 -2
  56. data/lib/webmock/util/query_mapper.rb +11 -7
  57. data/lib/webmock/util/uri.rb +13 -11
  58. data/lib/webmock/util/values_stringifier.rb +22 -0
  59. data/lib/webmock/util/version_checker.rb +7 -5
  60. data/lib/webmock/version.rb +3 -1
  61. data/lib/webmock/webmock.rb +22 -3
  62. data/lib/webmock.rb +55 -48
  63. metadata +106 -175
  64. data/.gemtest +0 -0
  65. data/.gitignore +0 -34
  66. data/.rspec-tm +0 -2
  67. data/.travis.yml +0 -20
  68. data/Gemfile +0 -9
  69. data/Rakefile +0 -30
  70. data/minitest/test_helper.rb +0 -34
  71. data/minitest/test_webmock.rb +0 -9
  72. data/minitest/webmock_spec.rb +0 -60
  73. data/spec/acceptance/curb/curb_spec.rb +0 -466
  74. data/spec/acceptance/curb/curb_spec_helper.rb +0 -147
  75. data/spec/acceptance/em_http_request/em_http_request_spec.rb +0 -406
  76. data/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +0 -77
  77. data/spec/acceptance/excon/excon_spec.rb +0 -75
  78. data/spec/acceptance/excon/excon_spec_helper.rb +0 -50
  79. data/spec/acceptance/http_rb/http_rb_spec.rb +0 -73
  80. data/spec/acceptance/http_rb/http_rb_spec_helper.rb +0 -51
  81. data/spec/acceptance/httpclient/httpclient_spec.rb +0 -210
  82. data/spec/acceptance/httpclient/httpclient_spec_helper.rb +0 -57
  83. data/spec/acceptance/manticore/manticore_spec.rb +0 -56
  84. data/spec/acceptance/manticore/manticore_spec_helper.rb +0 -35
  85. data/spec/acceptance/net_http/net_http_shared.rb +0 -153
  86. data/spec/acceptance/net_http/net_http_spec.rb +0 -317
  87. data/spec/acceptance/net_http/net_http_spec_helper.rb +0 -64
  88. data/spec/acceptance/net_http/real_net_http_spec.rb +0 -20
  89. data/spec/acceptance/patron/patron_spec.rb +0 -118
  90. data/spec/acceptance/patron/patron_spec_helper.rb +0 -54
  91. data/spec/acceptance/shared/allowing_and_disabling_net_connect.rb +0 -313
  92. data/spec/acceptance/shared/callbacks.rb +0 -147
  93. data/spec/acceptance/shared/complex_cross_concern_behaviors.rb +0 -36
  94. data/spec/acceptance/shared/enabling_and_disabling_webmock.rb +0 -95
  95. data/spec/acceptance/shared/precedence_of_stubs.rb +0 -15
  96. data/spec/acceptance/shared/request_expectations.rb +0 -916
  97. data/spec/acceptance/shared/returning_declared_responses.rb +0 -388
  98. data/spec/acceptance/shared/stubbing_requests.rb +0 -583
  99. data/spec/acceptance/typhoeus/typhoeus_hydra_spec.rb +0 -135
  100. data/spec/acceptance/typhoeus/typhoeus_hydra_spec_helper.rb +0 -60
  101. data/spec/acceptance/webmock_shared.rb +0 -41
  102. data/spec/fixtures/test.txt +0 -1
  103. data/spec/quality_spec.rb +0 -84
  104. data/spec/spec_helper.rb +0 -48
  105. data/spec/support/example_curl_output.txt +0 -22
  106. data/spec/support/failures.rb +0 -9
  107. data/spec/support/my_rack_app.rb +0 -53
  108. data/spec/support/network_connection.rb +0 -19
  109. data/spec/support/webmock_server.rb +0 -69
  110. data/spec/unit/api_spec.rb +0 -75
  111. data/spec/unit/errors_spec.rb +0 -129
  112. data/spec/unit/http_lib_adapters/http_lib_adapter_registry_spec.rb +0 -17
  113. data/spec/unit/http_lib_adapters/http_lib_adapter_spec.rb +0 -12
  114. data/spec/unit/matchers/hash_including_matcher_spec.rb +0 -87
  115. data/spec/unit/rack_response_spec.rb +0 -112
  116. data/spec/unit/request_body_diff_spec.rb +0 -90
  117. data/spec/unit/request_execution_verifier_spec.rb +0 -208
  118. data/spec/unit/request_pattern_spec.rb +0 -590
  119. data/spec/unit/request_registry_spec.rb +0 -95
  120. data/spec/unit/request_signature_snippet_spec.rb +0 -89
  121. data/spec/unit/request_signature_spec.rb +0 -155
  122. data/spec/unit/request_stub_spec.rb +0 -199
  123. data/spec/unit/response_spec.rb +0 -282
  124. data/spec/unit/stub_registry_spec.rb +0 -103
  125. data/spec/unit/stub_request_snippet_spec.rb +0 -95
  126. data/spec/unit/util/hash_counter_spec.rb +0 -39
  127. data/spec/unit/util/hash_keys_stringifier_spec.rb +0 -27
  128. data/spec/unit/util/headers_spec.rb +0 -28
  129. data/spec/unit/util/json_spec.rb +0 -33
  130. data/spec/unit/util/query_mapper_spec.rb +0 -144
  131. data/spec/unit/util/uri_spec.rb +0 -299
  132. data/spec/unit/util/version_checker_spec.rb +0 -65
  133. data/spec/unit/webmock_spec.rb +0 -11
  134. data/test/http_request.rb +0 -24
  135. data/test/shared_test.rb +0 -95
  136. data/test/test_helper.rb +0 -23
  137. data/test/test_webmock.rb +0 -6
  138. data/webmock.gemspec +0 -46
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require 'curb'
3
5
  rescue LoadError
@@ -5,7 +7,7 @@ rescue LoadError
5
7
  end
6
8
 
7
9
  if defined?(Curl)
8
- WebMock::VersionChecker.new('Curb', Curl::CURB_VERSION, '0.7.16', '0.9.1', ['0.8.7']).check_version!
10
+ WebMock::VersionChecker.new('Curb', Curl::CURB_VERSION, '0.7.16', '1.0.1', ['0.8.7']).check_version!
9
11
 
10
12
  module WebMock
11
13
  module HttpLibAdapters
@@ -54,7 +56,6 @@ if defined?(Curl)
54
56
 
55
57
  module Curl
56
58
  class WebMockCurlEasy < Curl::Easy
57
-
58
59
  def curb_or_webmock
59
60
  request_signature = build_request_signature
60
61
  WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
@@ -129,7 +130,7 @@ if defined?(Curl)
129
130
  def headers_as_hash(headers)
130
131
  if headers.is_a?(Array)
131
132
  headers.inject({}) {|hash, header|
132
- name, value = header.split(":").map(&:strip)
133
+ name, value = header.split(":", 2).map(&:strip)
133
134
  hash[name] = value
134
135
  hash
135
136
  }
@@ -153,7 +154,7 @@ if defined?(Curl)
153
154
  @body_str = webmock_response.body
154
155
  @response_code = webmock_response.status[0]
155
156
 
156
- @header_str = "HTTP/1.1 #{webmock_response.status[0]} #{webmock_response.status[1]}\r\n"
157
+ @header_str = "HTTP/1.1 #{webmock_response.status[0]} #{webmock_response.status[1]}\r\n".dup
157
158
 
158
159
  @on_debug.call(@header_str, 1) if defined?( @on_debug )
159
160
 
@@ -183,7 +184,7 @@ if defined?(Curl)
183
184
  self.url = location
184
185
 
185
186
  curb_or_webmock do
186
- send( "http_#{@webmock_method}_without_webmock" )
187
+ send( :http, {'method' => @webmock_method} )
187
188
  end
188
189
 
189
190
  self.url = first_url
@@ -271,6 +272,8 @@ if defined?(Curl)
271
272
  def perform
272
273
  @webmock_method ||= :get
273
274
  curb_or_webmock { super }
275
+ ensure
276
+ reset_webmock_method
274
277
  end
275
278
 
276
279
  def put_data= data
@@ -332,6 +335,19 @@ if defined?(Curl)
332
335
  end
333
336
  METHOD
334
337
  end
338
+
339
+ def reset_webmock_method
340
+ @webmock_method = :get
341
+ end
342
+
343
+ def reset
344
+ instance_variable_set(:@body_str, nil)
345
+ instance_variable_set(:@content_type, nil)
346
+ instance_variable_set(:@header_str, nil)
347
+ instance_variable_set(:@last_effective_url, nil)
348
+ instance_variable_set(:@response_code, nil)
349
+ super
350
+ end
335
351
  end
336
352
  end
337
353
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require 'em-http-request'
3
5
  rescue LoadError
@@ -99,6 +101,11 @@ if defined?(EventMachine::HttpClient)
99
101
  end
100
102
  end
101
103
 
104
+ def connection_completed
105
+ @state = :response_header
106
+ send_request(*headers_and_body_processed_by_middleware)
107
+ end
108
+
102
109
  def send_request(head, body)
103
110
  WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
104
111
 
@@ -107,7 +114,7 @@ if defined?(EventMachine::HttpClient)
107
114
  @uri ||= nil
108
115
  EM.next_tick {
109
116
  setup(make_raw_response(stubbed_webmock_response), @uri,
110
- stubbed_webmock_response.should_timeout ? "WebMock timeout error" : nil)
117
+ stubbed_webmock_response.should_timeout ? Errno::ETIMEDOUT : nil)
111
118
  }
112
119
  self
113
120
  elsif WebMock.net_connect_allowed?(request_signature.uri)
@@ -149,7 +156,7 @@ if defined?(EventMachine::HttpClient)
149
156
  raw_cookie = response_header.cookie
150
157
  raw_cookie = [raw_cookie] if raw_cookie.is_a? String
151
158
 
152
- cookie = raw_cookie.select { |c| c.start_with? name }.first
159
+ cookie = raw_cookie.detect { |c| c.start_with? name }
153
160
  cookie and cookie.split('=', 2)[1]
154
161
  end
155
162
 
@@ -163,12 +170,18 @@ if defined?(EventMachine::HttpClient)
163
170
  webmock_response
164
171
  end
165
172
 
166
- def build_request_signature
167
- headers, body = @req.headers, @req.body
168
-
169
- @conn.middleware.select {|m| m.respond_to?(:request) }.each do |m|
170
- headers, body = m.request(self, headers, body)
173
+ def headers_and_body_processed_by_middleware
174
+ @headers_and_body_processed_by_middleware ||= begin
175
+ head, body = build_request, @req.body
176
+ @conn.middleware.each do |m|
177
+ head, body = m.request(self, head, body) if m.respond_to?(:request)
178
+ end
179
+ [head, body]
171
180
  end
181
+ end
182
+
183
+ def build_request_signature
184
+ headers, body = headers_and_body_processed_by_middleware
172
185
 
173
186
  method = @req.method
174
187
  uri = @req.uri.clone
@@ -178,8 +191,6 @@ if defined?(EventMachine::HttpClient)
178
191
 
179
192
  body = form_encode_body(body) if body.is_a?(Hash)
180
193
 
181
- headers = @req.headers
182
-
183
194
  if headers['authorization'] && headers['authorization'].is_a?(Array)
184
195
  headers['Authorization'] = WebMock::Util::Headers.basic_auth_header(headers.delete('authorization'))
185
196
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require 'excon'
3
5
  rescue LoadError
@@ -92,7 +94,7 @@ if defined?(Excon)
92
94
  end
93
95
 
94
96
  def self.to_query(hash)
95
- string = ""
97
+ string = "".dup
96
98
  for key, values in hash
97
99
  if values.nil?
98
100
  string << key.to_s << '&'
@@ -152,11 +154,14 @@ if defined?(Excon)
152
154
  end
153
155
 
154
156
  Excon::Connection.class_eval do
155
- def self.new(args)
157
+ def self.new(args = {})
156
158
  args.delete(:__construction_args)
157
159
  super(args).tap do |instance|
158
160
  instance.data[:__construction_args] = args
159
161
  end
160
162
  end
161
163
  end
164
+
165
+ # Suppresses Excon connection argument validation warning
166
+ Excon::VALID_CONNECTION_KEYS << :__construction_args
162
167
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WebMock
2
4
  class HttpLibAdapter
3
5
  def self.adapter_for(lib)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WebMock
2
4
  class HttpLibAdapterRegistry
3
5
  include Singleton
@@ -1,10 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module HTTP
2
4
  class Client
3
5
  alias_method :__perform__, :perform
4
6
 
5
7
  def perform(request, options)
6
8
  return __perform__(request, options) unless webmock_enabled?
7
- WebMockPerform.new(request) { __perform__(request, options) }.exec
9
+
10
+ WebMockPerform.new(request, options) { __perform__(request, options) }.exec
8
11
  end
9
12
 
10
13
  def webmock_enabled?
@@ -1,9 +1,27 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module HTTP
2
4
  class Request
3
5
  def webmock_signature
6
+ request_body = nil
7
+
8
+ if defined?(HTTP::Request::Body)
9
+ request_body = String.new
10
+ first_chunk_encoding = nil
11
+ body.each do |part|
12
+ request_body << part
13
+ first_chunk_encoding ||= part.encoding
14
+ end
15
+
16
+ request_body.force_encoding(first_chunk_encoding) if first_chunk_encoding
17
+ request_body
18
+ else
19
+ request_body = body
20
+ end
21
+
4
22
  ::WebMock::RequestSignature.new(verb, uri.to_s, {
5
23
  headers: headers.to_h,
6
- body: body
24
+ body: request_body
7
25
  })
8
26
  end
9
27
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module HTTP
2
4
  class Response
3
5
  def to_webmock
@@ -11,20 +13,44 @@ module HTTP
11
13
  end
12
14
 
13
15
  class << self
14
- def from_webmock(webmock_response, request_signature = nil)
16
+ def from_webmock(request, webmock_response, request_signature = nil)
15
17
  status = Status.new(webmock_response.status.first)
16
18
  headers = webmock_response.headers || {}
17
- body = Body.new Streamer.new webmock_response.body
18
19
  uri = normalize_uri(request_signature && request_signature.uri)
19
20
 
21
+ # HTTP.rb 3.0+ uses a keyword argument to pass the encoding, but 1.x
22
+ # and 2.x use a positional argument, and 0.x don't support supplying
23
+ # the encoding.
24
+ body = if HTTP::VERSION < "1.0.0"
25
+ Body.new(Streamer.new(webmock_response.body))
26
+ elsif HTTP::VERSION < "3.0.0"
27
+ Body.new(Streamer.new(webmock_response.body), webmock_response.body.encoding)
28
+ else
29
+ Body.new(
30
+ Streamer.new(webmock_response.body, encoding: webmock_response.body.encoding),
31
+ encoding: webmock_response.body.encoding
32
+ )
33
+ end
34
+
20
35
  return new(status, "1.1", headers, body, uri) if HTTP::VERSION < "1.0.0"
21
36
 
37
+ # 5.0.0 had a breaking change to require request instead of uri.
38
+ if HTTP::VERSION < '5.0.0'
39
+ return new({
40
+ status: status,
41
+ version: "1.1",
42
+ headers: headers,
43
+ body: body,
44
+ uri: uri
45
+ })
46
+ end
47
+
22
48
  new({
23
49
  status: status,
24
50
  version: "1.1",
25
51
  headers: headers,
26
52
  body: body,
27
- uri: uri
53
+ request: request,
28
54
  })
29
55
  end
30
56
 
@@ -1,11 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module HTTP
2
4
  class Response
3
5
  class Streamer
4
- def initialize(str)
6
+ def initialize(str, encoding: Encoding::BINARY)
5
7
  @io = StringIO.new str
8
+ @encoding = encoding
6
9
  end
7
10
 
8
- def readpartial(size = nil)
11
+ def readpartial(size = nil, outbuf = nil)
9
12
  unless size
10
13
  if defined?(HTTP::Client::BUFFER_SIZE)
11
14
  size = HTTP::Client::BUFFER_SIZE
@@ -14,7 +17,12 @@ module HTTP
14
17
  end
15
18
  end
16
19
 
17
- @io.read size
20
+ chunk = @io.read size, outbuf
21
+ chunk.force_encoding(@encoding) if chunk
22
+ end
23
+
24
+ def close
25
+ @io.close
18
26
  end
19
27
 
20
28
  def sequence_id
@@ -1,7 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module HTTP
2
4
  class WebMockPerform
3
- def initialize(request, &perform)
5
+ def initialize(request, options, &perform)
4
6
  @request = request
7
+ @options = options
5
8
  @perform = perform
6
9
  @request_signature = nil
7
10
  end
@@ -38,12 +41,15 @@ module HTTP
38
41
  webmock_response.raise_error_if_any
39
42
 
40
43
  invoke_callbacks(webmock_response, real_request: false)
41
- ::HTTP::Response.from_webmock webmock_response, request_signature
44
+ response = ::HTTP::Response.from_webmock @request, webmock_response, request_signature
45
+
46
+ @options.features.each { |_name, feature| response = feature.wrap_response(response) }
47
+ response
42
48
  end
43
49
 
44
50
  def raise_timeout_error
45
51
  raise Errno::ETIMEDOUT if HTTP::VERSION < "1.0.0"
46
- raise HTTP::ConnectionError, "connection error: #{Errno::ETIMEDOUT.new}"
52
+ raise HTTP::TimeoutError, "connection error: #{Errno::ETIMEDOUT.new}"
47
53
  end
48
54
 
49
55
  def perform
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require "http"
3
5
  rescue LoadError
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require 'httpclient'
3
5
  require 'jsonclient' # defined in 'httpclient' gem as well
@@ -43,6 +45,9 @@ if defined?(::HTTPClient)
43
45
  end
44
46
 
45
47
  module WebMockHTTPClients
48
+
49
+ REQUEST_RESPONSE_LOCK = Mutex.new
50
+
46
51
  def do_get_block(req, proxy, conn, &block)
47
52
  do_get(req, proxy, conn, false, &block)
48
53
  end
@@ -57,7 +62,7 @@ if defined?(::HTTPClient)
57
62
  WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
58
63
 
59
64
  if webmock_responses[request_signature]
60
- webmock_response = webmock_responses.delete(request_signature)
65
+ webmock_response = synchronize_request_response { webmock_responses.delete(request_signature) }
61
66
  response = build_httpclient_response(webmock_response, stream, req.header, &block)
62
67
  @request_filter.each do |filter|
63
68
  filter.filter_response(req, response)
@@ -68,15 +73,17 @@ if defined?(::HTTPClient)
68
73
  res
69
74
  elsif WebMock.net_connect_allowed?(request_signature.uri)
70
75
  # in case there is a nil entry in the hash...
71
- webmock_responses.delete(request_signature)
76
+ synchronize_request_response { webmock_responses.delete(request_signature) }
72
77
 
73
78
  res = if stream
74
79
  do_get_stream_without_webmock(req, proxy, conn, &block)
75
80
  elsif block
76
81
  body = ''
77
82
  do_get_block_without_webmock(req, proxy, conn) do |http_res, chunk|
78
- body += chunk
79
- block.call(http_res, chunk)
83
+ if chunk && chunk.bytesize > 0
84
+ body += chunk
85
+ block.call(http_res, chunk)
86
+ end
80
87
  end
81
88
  else
82
89
  do_get_block_without_webmock(req, proxy, conn)
@@ -98,7 +105,7 @@ if defined?(::HTTPClient)
98
105
  def do_request_async(method, uri, query, body, extheader)
99
106
  req = create_request(method, uri, query, body, extheader)
100
107
  request_signature = build_request_signature(req)
101
- webmock_request_signatures << request_signature
108
+ synchronize_request_response { webmock_request_signatures << request_signature }
102
109
 
103
110
  if webmock_responses[request_signature] || WebMock.net_connect_allowed?(request_signature.uri)
104
111
  super
@@ -117,7 +124,7 @@ if defined?(::HTTPClient)
117
124
  raise HTTPClient::TimeoutError if webmock_response.should_timeout
118
125
  webmock_response.raise_error_if_any
119
126
 
120
- block.call(response, body) if block
127
+ block.call(response, body) if block && body && body.bytesize > 0
121
128
 
122
129
  response
123
130
  end
@@ -182,7 +189,9 @@ if defined?(::HTTPClient)
182
189
 
183
190
  def webmock_responses
184
191
  @webmock_responses ||= Hash.new do |hash, request_signature|
185
- hash[request_signature] = WebMock::StubRegistry.instance.response_for_request(request_signature)
192
+ synchronize_request_response do
193
+ hash[request_signature] = WebMock::StubRegistry.instance.response_for_request(request_signature)
194
+ end
186
195
  end
187
196
  end
188
197
 
@@ -191,8 +200,10 @@ if defined?(::HTTPClient)
191
200
  end
192
201
 
193
202
  def previous_signature_for(signature)
194
- return nil unless index = webmock_request_signatures.index(signature)
195
- webmock_request_signatures.delete_at(index)
203
+ synchronize_request_response do
204
+ return nil unless index = webmock_request_signatures.index(signature)
205
+ webmock_request_signatures.delete_at(index)
206
+ end
196
207
  end
197
208
 
198
209
  private
@@ -207,6 +218,16 @@ if defined?(::HTTPClient)
207
218
  hdrs
208
219
  end
209
220
  end
221
+
222
+ def synchronize_request_response
223
+ if REQUEST_RESPONSE_LOCK.owned?
224
+ yield
225
+ else
226
+ REQUEST_RESPONSE_LOCK.synchronize do
227
+ yield
228
+ end
229
+ end
230
+ end
210
231
  end
211
232
 
212
233
  class WebMockHTTPClient < HTTPClient
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require 'manticore'
3
5
  rescue LoadError
@@ -24,6 +26,12 @@ if defined?(Manticore)
24
26
  Manticore.instance_variable_set(:@manticore_facade, OriginalManticoreClient.new)
25
27
  end
26
28
 
29
+ class StubbedTimeoutResponse < Manticore::StubbedResponse
30
+ def call
31
+ @handlers[:failure].call(Manticore::ConnectTimeout.new("Too slow (mocked timeout)"))
32
+ end
33
+ end
34
+
27
35
  class WebMockManticoreClient < Manticore::Client
28
36
  def request(klass, url, options={}, &block)
29
37
  super(klass, WebMock::Util::URI.normalize_uri(url).to_s, format_options(options))
@@ -50,19 +58,22 @@ if defined?(Manticore)
50
58
 
51
59
  if webmock_response = registered_response_for(request_signature)
52
60
  webmock_response.raise_error_if_any
53
- manticore_response = generate_manticore_response(webmock_response).call
54
- real_request = false
61
+ manticore_response = generate_manticore_response(webmock_response)
62
+ manticore_response.on_success do
63
+ WebMock::CallbackRegistry.invoke_callbacks({lib: :manticore, real_request: false}, request_signature, webmock_response)
64
+ end
55
65
 
56
66
  elsif real_request_allowed?(request_signature.uri)
57
- manticore_response = Manticore::Response.new(self, request, context, &block).call
58
- webmock_response = generate_webmock_response(manticore_response)
59
- real_request = true
67
+ manticore_response = Manticore::Response.new(self, request, context, &block)
68
+ manticore_response.on_complete do |completed_response|
69
+ webmock_response = generate_webmock_response(completed_response)
70
+ WebMock::CallbackRegistry.invoke_callbacks({lib: :manticore, real_request: true}, request_signature, webmock_response)
71
+ end
60
72
 
61
73
  else
62
74
  raise WebMock::NetConnectNotAllowedError.new(request_signature)
63
75
  end
64
76
 
65
- WebMock::CallbackRegistry.invoke_callbacks({lib: :manticore, real_request: real_request}, request_signature, webmock_response)
66
77
  manticore_response
67
78
  end
68
79
 
@@ -103,21 +114,30 @@ if defined?(Manticore)
103
114
  end
104
115
 
105
116
  def generate_manticore_response(webmock_response)
106
- raise Manticore::ConnectTimeout if webmock_response.should_timeout
107
-
108
- Manticore::StubbedResponse.stub(
109
- code: webmock_response.status[0],
110
- body: webmock_response.body,
111
- headers: webmock_response.headers,
112
- cookies: {}
113
- )
117
+ if webmock_response.should_timeout
118
+ StubbedTimeoutResponse.new
119
+ else
120
+ Manticore::StubbedResponse.stub(
121
+ code: webmock_response.status[0],
122
+ body: webmock_response.body,
123
+ headers: webmock_response.headers,
124
+ cookies: {}
125
+ )
126
+ end
114
127
  end
115
128
 
116
129
  def generate_webmock_response(manticore_response)
117
130
  webmock_response = WebMock::Response.new
118
131
  webmock_response.status = [manticore_response.code, manticore_response.message]
119
- webmock_response.body = manticore_response.body
120
132
  webmock_response.headers = manticore_response.headers
133
+
134
+ # The attempt to read the body could fail if manticore is used in a streaming mode
135
+ webmock_response.body = begin
136
+ manticore_response.body
137
+ rescue ::Manticore::StreamClosedException
138
+ nil
139
+ end
140
+
121
141
  webmock_response
122
142
  end
123
143
  end