webmock 3.5.0 → 3.25.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 (142) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +476 -2
  3. data/README.md +166 -42
  4. data/lib/webmock/api.rb +2 -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 +228 -0
  12. data/lib/webmock/http_lib_adapters/curb_adapter.rb +14 -3
  13. data/lib/webmock/http_lib_adapters/em_http_request_adapter.rb +22 -9
  14. data/lib/webmock/http_lib_adapters/excon_adapter.rb +5 -0
  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 +17 -5
  19. data/lib/webmock/http_lib_adapters/http_rb/response.rb +49 -5
  20. data/lib/webmock/http_lib_adapters/http_rb/streamer.rb +11 -3
  21. data/lib/webmock/http_lib_adapters/http_rb/webmock.rb +8 -2
  22. data/lib/webmock/http_lib_adapters/http_rb_adapter.rb +7 -5
  23. data/lib/webmock/http_lib_adapters/httpclient_adapter.rb +25 -7
  24. data/lib/webmock/http_lib_adapters/manticore_adapter.rb +35 -15
  25. data/lib/webmock/http_lib_adapters/net_http.rb +59 -112
  26. data/lib/webmock/http_lib_adapters/net_http_response.rb +3 -1
  27. data/lib/webmock/http_lib_adapters/patron_adapter.rb +4 -2
  28. data/lib/webmock/http_lib_adapters/typhoeus_hydra_adapter.rb +18 -2
  29. data/lib/webmock/matchers/any_arg_matcher.rb +2 -0
  30. data/lib/webmock/matchers/hash_argument_matcher.rb +2 -0
  31. data/lib/webmock/matchers/hash_excluding_matcher.rb +2 -0
  32. data/lib/webmock/matchers/hash_including_matcher.rb +2 -0
  33. data/lib/webmock/minitest.rb +2 -0
  34. data/lib/webmock/rack_response.rb +5 -1
  35. data/lib/webmock/request_body_diff.rb +3 -1
  36. data/lib/webmock/request_execution_verifier.rb +2 -0
  37. data/lib/webmock/request_pattern.rb +118 -61
  38. data/lib/webmock/request_registry.rb +2 -0
  39. data/lib/webmock/request_signature.rb +4 -2
  40. data/lib/webmock/request_signature_snippet.rb +2 -0
  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 +4 -1
  48. data/lib/webmock/stub_registry.rb +28 -11
  49. data/lib/webmock/stub_request_snippet.rb +2 -0
  50. data/lib/webmock/test_unit.rb +3 -3
  51. data/lib/webmock/util/hash_counter.rb +12 -6
  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 +23 -10
  55. data/lib/webmock/util/parsers/json.rb +72 -0
  56. data/lib/webmock/util/parsers/parse_error.rb +7 -0
  57. data/lib/webmock/util/parsers/xml.rb +16 -0
  58. data/lib/webmock/util/query_mapper.rb +6 -2
  59. data/lib/webmock/util/uri.rb +11 -9
  60. data/lib/webmock/util/values_stringifier.rb +2 -0
  61. data/lib/webmock/util/version_checker.rb +7 -5
  62. data/lib/webmock/version.rb +3 -1
  63. data/lib/webmock/webmock.rb +22 -3
  64. data/lib/webmock.rb +5 -2
  65. metadata +99 -175
  66. data/.gemtest +0 -0
  67. data/.gitignore +0 -34
  68. data/.rspec-tm +0 -2
  69. data/.travis.yml +0 -21
  70. data/Gemfile +0 -9
  71. data/Rakefile +0 -30
  72. data/lib/webmock/util/json.rb +0 -67
  73. data/minitest/test_helper.rb +0 -34
  74. data/minitest/test_webmock.rb +0 -9
  75. data/minitest/webmock_spec.rb +0 -60
  76. data/spec/acceptance/curb/curb_spec.rb +0 -481
  77. data/spec/acceptance/curb/curb_spec_helper.rb +0 -147
  78. data/spec/acceptance/em_http_request/em_http_request_spec.rb +0 -406
  79. data/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +0 -77
  80. data/spec/acceptance/excon/excon_spec.rb +0 -77
  81. data/spec/acceptance/excon/excon_spec_helper.rb +0 -50
  82. data/spec/acceptance/http_rb/http_rb_spec.rb +0 -82
  83. data/spec/acceptance/http_rb/http_rb_spec_helper.rb +0 -54
  84. data/spec/acceptance/httpclient/httpclient_spec.rb +0 -217
  85. data/spec/acceptance/httpclient/httpclient_spec_helper.rb +0 -57
  86. data/spec/acceptance/manticore/manticore_spec.rb +0 -56
  87. data/spec/acceptance/manticore/manticore_spec_helper.rb +0 -35
  88. data/spec/acceptance/net_http/net_http_shared.rb +0 -153
  89. data/spec/acceptance/net_http/net_http_spec.rb +0 -331
  90. data/spec/acceptance/net_http/net_http_spec_helper.rb +0 -64
  91. data/spec/acceptance/net_http/real_net_http_spec.rb +0 -20
  92. data/spec/acceptance/patron/patron_spec.rb +0 -125
  93. data/spec/acceptance/patron/patron_spec_helper.rb +0 -54
  94. data/spec/acceptance/shared/allowing_and_disabling_net_connect.rb +0 -313
  95. data/spec/acceptance/shared/callbacks.rb +0 -147
  96. data/spec/acceptance/shared/complex_cross_concern_behaviors.rb +0 -36
  97. data/spec/acceptance/shared/enabling_and_disabling_webmock.rb +0 -95
  98. data/spec/acceptance/shared/precedence_of_stubs.rb +0 -15
  99. data/spec/acceptance/shared/request_expectations.rb +0 -923
  100. data/spec/acceptance/shared/returning_declared_responses.rb +0 -388
  101. data/spec/acceptance/shared/stubbing_requests.rb +0 -638
  102. data/spec/acceptance/typhoeus/typhoeus_hydra_spec.rb +0 -135
  103. data/spec/acceptance/typhoeus/typhoeus_hydra_spec_helper.rb +0 -60
  104. data/spec/acceptance/webmock_shared.rb +0 -41
  105. data/spec/fixtures/test.txt +0 -1
  106. data/spec/quality_spec.rb +0 -84
  107. data/spec/spec_helper.rb +0 -48
  108. data/spec/support/example_curl_output.txt +0 -22
  109. data/spec/support/failures.rb +0 -9
  110. data/spec/support/my_rack_app.rb +0 -53
  111. data/spec/support/network_connection.rb +0 -19
  112. data/spec/support/webmock_server.rb +0 -69
  113. data/spec/unit/api_spec.rb +0 -175
  114. data/spec/unit/errors_spec.rb +0 -129
  115. data/spec/unit/http_lib_adapters/http_lib_adapter_registry_spec.rb +0 -17
  116. data/spec/unit/http_lib_adapters/http_lib_adapter_spec.rb +0 -12
  117. data/spec/unit/matchers/hash_excluding_matcher_spec.rb +0 -61
  118. data/spec/unit/matchers/hash_including_matcher_spec.rb +0 -87
  119. data/spec/unit/rack_response_spec.rb +0 -112
  120. data/spec/unit/request_body_diff_spec.rb +0 -90
  121. data/spec/unit/request_execution_verifier_spec.rb +0 -208
  122. data/spec/unit/request_pattern_spec.rb +0 -596
  123. data/spec/unit/request_registry_spec.rb +0 -95
  124. data/spec/unit/request_signature_snippet_spec.rb +0 -89
  125. data/spec/unit/request_signature_spec.rb +0 -155
  126. data/spec/unit/request_stub_spec.rb +0 -199
  127. data/spec/unit/response_spec.rb +0 -282
  128. data/spec/unit/stub_registry_spec.rb +0 -103
  129. data/spec/unit/stub_request_snippet_spec.rb +0 -115
  130. data/spec/unit/util/hash_counter_spec.rb +0 -39
  131. data/spec/unit/util/hash_keys_stringifier_spec.rb +0 -27
  132. data/spec/unit/util/headers_spec.rb +0 -28
  133. data/spec/unit/util/json_spec.rb +0 -33
  134. data/spec/unit/util/query_mapper_spec.rb +0 -150
  135. data/spec/unit/util/uri_spec.rb +0 -299
  136. data/spec/unit/util/version_checker_spec.rb +0 -65
  137. data/spec/unit/webmock_spec.rb +0 -11
  138. data/test/http_request.rb +0 -24
  139. data/test/shared_test.rb +0 -108
  140. data/test/test_helper.rb +0 -23
  141. data/test/test_webmock.rb +0 -6
  142. data/webmock.gemspec +0 -46
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ return if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.4.0')
4
+
1
5
  begin
2
6
  require 'em-http-request'
3
7
  rescue LoadError
@@ -99,6 +103,11 @@ if defined?(EventMachine::HttpClient)
99
103
  end
100
104
  end
101
105
 
106
+ def connection_completed
107
+ @state = :response_header
108
+ send_request(*headers_and_body_processed_by_middleware)
109
+ end
110
+
102
111
  def send_request(head, body)
103
112
  WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
104
113
 
@@ -107,7 +116,7 @@ if defined?(EventMachine::HttpClient)
107
116
  @uri ||= nil
108
117
  EM.next_tick {
109
118
  setup(make_raw_response(stubbed_webmock_response), @uri,
110
- stubbed_webmock_response.should_timeout ? "WebMock timeout error" : nil)
119
+ stubbed_webmock_response.should_timeout ? Errno::ETIMEDOUT : nil)
111
120
  }
112
121
  self
113
122
  elsif WebMock.net_connect_allowed?(request_signature.uri)
@@ -149,7 +158,7 @@ if defined?(EventMachine::HttpClient)
149
158
  raw_cookie = response_header.cookie
150
159
  raw_cookie = [raw_cookie] if raw_cookie.is_a? String
151
160
 
152
- cookie = raw_cookie.select { |c| c.start_with? name }.first
161
+ cookie = raw_cookie.detect { |c| c.start_with? name }
153
162
  cookie and cookie.split('=', 2)[1]
154
163
  end
155
164
 
@@ -163,12 +172,18 @@ if defined?(EventMachine::HttpClient)
163
172
  webmock_response
164
173
  end
165
174
 
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)
175
+ def headers_and_body_processed_by_middleware
176
+ @headers_and_body_processed_by_middleware ||= begin
177
+ head, body = build_request, @req.body
178
+ @conn.middleware.each do |m|
179
+ head, body = m.request(self, head, body) if m.respond_to?(:request)
180
+ end
181
+ [head, body]
171
182
  end
183
+ end
184
+
185
+ def build_request_signature
186
+ headers, body = headers_and_body_processed_by_middleware
172
187
 
173
188
  method = @req.method
174
189
  uri = @req.uri.clone
@@ -178,8 +193,6 @@ if defined?(EventMachine::HttpClient)
178
193
 
179
194
  body = form_encode_body(body) if body.is_a?(Hash)
180
195
 
181
- headers = @req.headers
182
-
183
196
  if headers['authorization'] && headers['authorization'].is_a?(Array)
184
197
  headers['Authorization'] = WebMock::Util::Headers.basic_auth_header(headers.delete('authorization'))
185
198
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require 'excon'
3
5
  rescue LoadError
@@ -159,4 +161,7 @@ if defined?(Excon)
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,11 +1,23 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module HTTP
2
4
  class Request
3
5
  def webmock_signature
4
- request_body = if defined?(HTTP::Request::Body)
5
- ''.tap { |string| body.each { |part| string << part } }
6
- else
7
- body
8
- end
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
9
21
 
10
22
  ::WebMock::RequestSignature.new(verb, uri.to_s, {
11
23
  headers: headers.to_h,
@@ -1,34 +1,72 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module HTTP
2
4
  class Response
3
5
  def to_webmock
4
6
  webmock_response = ::WebMock::Response.new
5
7
 
6
8
  webmock_response.status = [status.to_i, reason]
9
+
7
10
  webmock_response.body = body.to_s
8
- webmock_response.headers = headers.to_h
11
+ # This call is used to reset the body of the response to enable it to be streamed if necessary.
12
+ # The `body.to_s` call above reads the body, which allows WebMock to trigger any registered callbacks.
13
+ # However, once the body is read to_s, it cannot be streamed again and attempting to do so
14
+ # will raise a "HTTP::StateError: body has already been consumed" error.
15
+ # To avoid this error, we replace the original body with a new one.
16
+ # The new body has its @stream attribute set to new Streamer, instead of the original Connection.
17
+ # Unfortunately, it's not possible to reset the original body to its initial streaming state.
18
+ # Therefore, this replacement is the best workaround currently available.
19
+ reset_body_to_allow_it_to_be_streamed!(webmock_response)
9
20
 
21
+ webmock_response.headers = headers.to_h
10
22
  webmock_response
11
23
  end
12
24
 
13
25
  class << self
14
- def from_webmock(webmock_response, request_signature = nil)
26
+ def from_webmock(request, webmock_response, request_signature = nil)
15
27
  status = Status.new(webmock_response.status.first)
16
28
  headers = webmock_response.headers || {}
17
- body = Body.new Streamer.new webmock_response.body
18
29
  uri = normalize_uri(request_signature && request_signature.uri)
19
30
 
31
+ # HTTP.rb 3.0+ uses a keyword argument to pass the encoding, but 1.x
32
+ # and 2.x use a positional argument, and 0.x don't support supplying
33
+ # the encoding.
34
+ body = build_http_rb_response_body_from_webmock_response(webmock_response)
35
+
20
36
  return new(status, "1.1", headers, body, uri) if HTTP::VERSION < "1.0.0"
21
37
 
38
+ # 5.0.0 had a breaking change to require request instead of uri.
39
+ if HTTP::VERSION < '5.0.0'
40
+ return new({
41
+ status: status,
42
+ version: "1.1",
43
+ headers: headers,
44
+ body: body,
45
+ uri: uri
46
+ })
47
+ end
48
+
22
49
  new({
23
50
  status: status,
24
51
  version: "1.1",
25
52
  headers: headers,
26
53
  body: body,
27
- uri: uri
54
+ request: request,
28
55
  })
29
56
  end
30
57
 
31
- private
58
+ def build_http_rb_response_body_from_webmock_response(webmock_response)
59
+ if HTTP::VERSION < "1.0.0"
60
+ Body.new(Streamer.new(webmock_response.body))
61
+ elsif HTTP::VERSION < "3.0.0"
62
+ Body.new(Streamer.new(webmock_response.body), webmock_response.body.encoding)
63
+ else
64
+ Body.new(
65
+ Streamer.new(webmock_response.body, encoding: webmock_response.body.encoding),
66
+ encoding: webmock_response.body.encoding
67
+ )
68
+ end
69
+ end
32
70
 
33
71
  def normalize_uri(uri)
34
72
  return unless uri
@@ -39,5 +77,11 @@ module HTTP
39
77
  uri
40
78
  end
41
79
  end
80
+
81
+ private
82
+
83
+ def reset_body_to_allow_it_to_be_streamed!(webmock_response)
84
+ @body = self.class.build_http_rb_response_body_from_webmock_response(webmock_response)
85
+ end
42
86
  end
43
87
  end
@@ -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,13 +17,18 @@ 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
18
22
  end
19
23
 
20
24
  def close
21
25
  @io.close
22
26
  end
23
27
 
28
+ def finished_request?
29
+ @io.eof?
30
+ end
31
+
24
32
  def sequence_id
25
33
  -1
26
34
  end
@@ -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,7 +41,10 @@ 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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require "http"
3
5
  rescue LoadError
@@ -29,9 +31,9 @@ if defined?(HTTP) && defined?(HTTP::VERSION)
29
31
  end
30
32
  end
31
33
 
32
- require "webmock/http_lib_adapters/http_rb/client"
33
- require "webmock/http_lib_adapters/http_rb/request"
34
- require "webmock/http_lib_adapters/http_rb/response"
35
- require "webmock/http_lib_adapters/http_rb/streamer"
36
- require "webmock/http_lib_adapters/http_rb/webmock"
34
+ require_relative "http_rb/client"
35
+ require_relative "http_rb/request"
36
+ require_relative "http_rb/response"
37
+ require_relative "http_rb/streamer"
38
+ require_relative "http_rb/webmock"
37
39
  end
@@ -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,11 @@ if defined?(::HTTPClient)
43
45
  end
44
46
 
45
47
  module WebMockHTTPClients
48
+ WEBMOCK_HTTPCLIENT_RESPONSES = :webmock_httpclient_responses
49
+ WEBMOCK_HTTPCLIENT_REQUEST_SIGNATURES = :webmock_httpclient_request_signatures
50
+
51
+ REQUEST_RESPONSE_LOCK = Mutex.new
52
+
46
53
  def do_get_block(req, proxy, conn, &block)
47
54
  do_get(req, proxy, conn, false, &block)
48
55
  end
@@ -52,6 +59,8 @@ if defined?(::HTTPClient)
52
59
  end
53
60
 
54
61
  def do_get(req, proxy, conn, stream = false, &block)
62
+ clear_thread_variables unless conn.async_thread
63
+
55
64
  request_signature = build_request_signature(req, :reuse_existing)
56
65
 
57
66
  WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
@@ -98,12 +107,16 @@ if defined?(::HTTPClient)
98
107
  end
99
108
 
100
109
  def do_request_async(method, uri, query, body, extheader)
110
+ clear_thread_variables
101
111
  req = create_request(method, uri, query, body, extheader)
102
112
  request_signature = build_request_signature(req)
103
113
  webmock_request_signatures << request_signature
104
114
 
105
115
  if webmock_responses[request_signature] || WebMock.net_connect_allowed?(request_signature.uri)
106
- super
116
+ conn = super
117
+ conn.async_thread[WEBMOCK_HTTPCLIENT_REQUEST_SIGNATURES] = Thread.current[WEBMOCK_HTTPCLIENT_REQUEST_SIGNATURES]
118
+ conn.async_thread[WEBMOCK_HTTPCLIENT_RESPONSES] = Thread.current[WEBMOCK_HTTPCLIENT_RESPONSES]
119
+ conn
107
120
  else
108
121
  raise WebMock::NetConnectNotAllowedError.new(request_signature)
109
122
  end
@@ -152,14 +165,14 @@ if defined?(::HTTPClient)
152
165
  end
153
166
 
154
167
  def build_request_signature(req, reuse_existing = false)
155
- uri = WebMock::Util::URI.heuristic_parse(req.header.request_uri.to_s)
156
- uri.query = WebMock::Util::QueryMapper.values_to_query(req.header.request_query, notation: WebMock::Config.instance.query_values_notation) if req.header.request_query
157
- uri.port = req.header.request_uri.port
158
-
159
168
  @request_filter.each do |filter|
160
169
  filter.filter_request(req)
161
170
  end
162
171
 
172
+ uri = WebMock::Util::URI.heuristic_parse(req.header.request_uri.to_s)
173
+ uri.query = WebMock::Util::QueryMapper.values_to_query(req.header.request_query, notation: WebMock::Config.instance.query_values_notation) if req.header.request_query
174
+ uri.port = req.header.request_uri.port
175
+
163
176
  headers = req.header.all.inject({}) do |hdrs, header|
164
177
  hdrs[header[0]] ||= []
165
178
  hdrs[header[0]] << header[1]
@@ -183,13 +196,13 @@ if defined?(::HTTPClient)
183
196
  end
184
197
 
185
198
  def webmock_responses
186
- @webmock_responses ||= Hash.new do |hash, request_signature|
199
+ Thread.current[WEBMOCK_HTTPCLIENT_RESPONSES] ||= Hash.new do |hash, request_signature|
187
200
  hash[request_signature] = WebMock::StubRegistry.instance.response_for_request(request_signature)
188
201
  end
189
202
  end
190
203
 
191
204
  def webmock_request_signatures
192
- @webmock_request_signatures ||= []
205
+ Thread.current[WEBMOCK_HTTPCLIENT_REQUEST_SIGNATURES] ||= []
193
206
  end
194
207
 
195
208
  def previous_signature_for(signature)
@@ -209,6 +222,11 @@ if defined?(::HTTPClient)
209
222
  hdrs
210
223
  end
211
224
  end
225
+
226
+ def clear_thread_variables
227
+ Thread.current[WEBMOCK_HTTPCLIENT_REQUEST_SIGNATURES] = nil
228
+ Thread.current[WEBMOCK_HTTPCLIENT_RESPONSES] = nil
229
+ end
212
230
  end
213
231
 
214
232
  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