webmock 3.5.1 → 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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/CI.yml +38 -0
  3. data/CHANGELOG.md +343 -2
  4. data/Gemfile +1 -1
  5. data/README.md +116 -32
  6. data/Rakefile +12 -4
  7. data/lib/webmock/http_lib_adapters/async_http_client_adapter.rb +221 -0
  8. data/lib/webmock/http_lib_adapters/curb_adapter.rb +12 -3
  9. data/lib/webmock/http_lib_adapters/em_http_request_adapter.rb +7 -4
  10. data/lib/webmock/http_lib_adapters/excon_adapter.rb +3 -0
  11. data/lib/webmock/http_lib_adapters/http_rb/client.rb +2 -1
  12. data/lib/webmock/http_lib_adapters/http_rb/response.rb +27 -3
  13. data/lib/webmock/http_lib_adapters/http_rb/streamer.rb +5 -3
  14. data/lib/webmock/http_lib_adapters/http_rb/webmock.rb +6 -2
  15. data/lib/webmock/http_lib_adapters/httpclient_adapter.rb +23 -6
  16. data/lib/webmock/http_lib_adapters/manticore_adapter.rb +33 -15
  17. data/lib/webmock/http_lib_adapters/net_http.rb +35 -103
  18. data/lib/webmock/http_lib_adapters/patron_adapter.rb +1 -1
  19. data/lib/webmock/request_body_diff.rb +1 -1
  20. data/lib/webmock/request_pattern.rb +106 -56
  21. data/lib/webmock/request_signature.rb +2 -2
  22. data/lib/webmock/request_stub.rb +15 -0
  23. data/lib/webmock/response.rb +19 -13
  24. data/lib/webmock/rspec.rb +2 -1
  25. data/lib/webmock/stub_registry.rb +26 -11
  26. data/lib/webmock/test_unit.rb +1 -3
  27. data/lib/webmock/util/query_mapper.rb +4 -2
  28. data/lib/webmock/util/uri.rb +8 -8
  29. data/lib/webmock/version.rb +1 -1
  30. data/lib/webmock/webmock.rb +20 -3
  31. data/lib/webmock.rb +1 -0
  32. data/minitest/webmock_spec.rb +1 -1
  33. data/spec/acceptance/async_http_client/async_http_client_spec.rb +375 -0
  34. data/spec/acceptance/async_http_client/async_http_client_spec_helper.rb +73 -0
  35. data/spec/acceptance/curb/curb_spec.rb +34 -5
  36. data/spec/acceptance/em_http_request/em_http_request_spec.rb +57 -1
  37. data/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +2 -2
  38. data/spec/acceptance/excon/excon_spec.rb +2 -2
  39. data/spec/acceptance/excon/excon_spec_helper.rb +2 -0
  40. data/spec/acceptance/http_rb/http_rb_spec.rb +11 -0
  41. data/spec/acceptance/manticore/manticore_spec.rb +51 -0
  42. data/spec/acceptance/net_http/net_http_shared.rb +46 -9
  43. data/spec/acceptance/net_http/net_http_spec.rb +87 -23
  44. data/spec/acceptance/net_http/real_net_http_spec.rb +1 -1
  45. data/spec/acceptance/patron/patron_spec.rb +19 -21
  46. data/spec/acceptance/patron/patron_spec_helper.rb +2 -2
  47. data/spec/acceptance/shared/allowing_and_disabling_net_connect.rb +14 -14
  48. data/spec/acceptance/shared/callbacks.rb +3 -2
  49. data/spec/acceptance/shared/complex_cross_concern_behaviors.rb +1 -1
  50. data/spec/acceptance/shared/request_expectations.rb +7 -0
  51. data/spec/acceptance/shared/returning_declared_responses.rb +36 -15
  52. data/spec/acceptance/shared/stubbing_requests.rb +40 -0
  53. data/spec/support/webmock_server.rb +1 -0
  54. data/spec/unit/request_pattern_spec.rb +201 -49
  55. data/spec/unit/request_signature_spec.rb +21 -1
  56. data/spec/unit/request_stub_spec.rb +35 -0
  57. data/spec/unit/response_spec.rb +51 -19
  58. data/spec/unit/util/query_mapper_spec.rb +7 -0
  59. data/spec/unit/util/uri_spec.rb +74 -2
  60. data/spec/unit/webmock_spec.rb +108 -5
  61. data/test/test_webmock.rb +6 -0
  62. data/webmock.gemspec +15 -7
  63. metadata +78 -35
  64. data/.travis.yml +0 -21
data/Rakefile CHANGED
@@ -1,28 +1,36 @@
1
- #!/usr/bin/env rake
2
-
3
1
  require 'bundler'
4
2
  Bundler::GemHelper.install_tasks
5
3
 
6
4
  require "rspec/core/rake_task"
7
5
  RSpec::Core::RakeTask.new(:spec) do |t|
8
- t.rspec_opts = ["-c", "-f progress", "-r ./spec/spec_helper.rb"]
6
+ t.rspec_opts = %w[
7
+ --force-color
8
+ --format progress
9
+ --require ./spec/spec_helper.rb
10
+ ]
9
11
  t.pattern = 'spec/**/*_spec.rb'
10
12
  end
11
13
 
12
14
  RSpec::Core::RakeTask.new(:spec_http_without_webmock) do |t|
13
- t.rspec_opts = ["-c", "-f progress", "-r ./spec/acceptance/net_http/real_net_http_spec.rb"]
15
+ t.rspec_opts = %w[
16
+ --force-color
17
+ --format progress
18
+ --require ./spec/acceptance/net_http/real_net_http_spec.rb
19
+ ]
14
20
  t.pattern = 'spec/acceptance/net_http/real_net_http_spec.rb'
15
21
  end
16
22
 
17
23
  require 'rake/testtask'
18
24
  Rake::TestTask.new(:test) do |test|
19
25
  test.test_files = FileList["test/**/*.rb"].exclude("test/test_helper.rb")
26
+ test.options = "--use-color"
20
27
  test.verbose = false
21
28
  test.warning = false
22
29
  end
23
30
 
24
31
  Rake::TestTask.new(:minitest) do |test|
25
32
  test.test_files = FileList["minitest/**/*.rb"].exclude("test/test_helper.rb")
33
+ test.options = "--pride"
26
34
  test.verbose = false
27
35
  test.warning = false
28
36
  end
@@ -0,0 +1,221 @@
1
+ begin
2
+ require 'async'
3
+ require 'async/http'
4
+ rescue LoadError
5
+ # async-http not found
6
+ end
7
+
8
+ if defined?(Async::HTTP)
9
+ module WebMock
10
+ module HttpLibAdapters
11
+ class AsyncHttpClientAdapter < HttpLibAdapter
12
+ adapter_for :async_http_client
13
+
14
+ OriginalAsyncHttpClient = Async::HTTP::Client unless const_defined?(:OriginalAsyncHttpClient)
15
+
16
+ class << self
17
+ def enable!
18
+ Async::HTTP.send(:remove_const, :Client)
19
+ Async::HTTP.send(:const_set, :Client, Async::HTTP::WebMockClientWrapper)
20
+ end
21
+
22
+ def disable!
23
+ Async::HTTP.send(:remove_const, :Client)
24
+ Async::HTTP.send(:const_set, :Client, OriginalAsyncHttpClient)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ module Async
32
+ module HTTP
33
+ class WebMockClientWrapper < Client
34
+ def initialize(
35
+ endpoint,
36
+ protocol = endpoint.protocol,
37
+ scheme = endpoint.scheme,
38
+ authority = endpoint.authority,
39
+ **options
40
+ )
41
+ webmock_endpoint = WebMockEndpoint.new(scheme, authority, protocol)
42
+
43
+ @network_client = WebMockClient.new(endpoint, **options)
44
+ @webmock_client = WebMockClient.new(webmock_endpoint, **options)
45
+
46
+ @scheme = scheme
47
+ @authority = authority
48
+ end
49
+
50
+ def call(request)
51
+ request.scheme ||= self.scheme
52
+ request.authority ||= self.authority
53
+
54
+ request_signature = build_request_signature(request)
55
+ WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
56
+ webmock_response = WebMock::StubRegistry.instance.response_for_request(request_signature)
57
+ net_connect_allowed = WebMock.net_connect_allowed?(request_signature.uri)
58
+ real_request = false
59
+
60
+ if webmock_response
61
+ webmock_response.raise_error_if_any
62
+ raise Async::TimeoutError, 'WebMock timeout error' if webmock_response.should_timeout
63
+ WebMockApplication.add_webmock_response(request, webmock_response)
64
+ response = @webmock_client.call(request)
65
+ elsif net_connect_allowed
66
+ response = @network_client.call(request)
67
+ real_request = true
68
+ else
69
+ raise WebMock::NetConnectNotAllowedError.new(request_signature) unless webmock_response
70
+ end
71
+
72
+ if WebMock::CallbackRegistry.any_callbacks?
73
+ webmock_response ||= build_webmock_response(response)
74
+ WebMock::CallbackRegistry.invoke_callbacks(
75
+ {
76
+ lib: :async_http_client,
77
+ real_request: real_request
78
+ },
79
+ request_signature,
80
+ webmock_response
81
+ )
82
+ end
83
+
84
+ response
85
+ end
86
+
87
+ def close
88
+ @network_client.close
89
+ @webmock_client.close
90
+ end
91
+
92
+ private
93
+
94
+ def build_request_signature(request)
95
+ body = request.read
96
+ request.body = ::Protocol::HTTP::Body::Buffered.wrap(body)
97
+ WebMock::RequestSignature.new(
98
+ request.method.downcase.to_sym,
99
+ "#{request.scheme}://#{request.authority}#{request.path}",
100
+ headers: request.headers.to_h,
101
+ body: body
102
+ )
103
+ end
104
+
105
+ def build_webmock_response(response)
106
+ body = response.read
107
+ response.body = ::Protocol::HTTP::Body::Buffered.wrap(body)
108
+
109
+ webmock_response = WebMock::Response.new
110
+ webmock_response.status = [
111
+ response.status,
112
+ ::Protocol::HTTP1::Reason::DESCRIPTIONS[response.status]
113
+ ]
114
+ webmock_response.headers = build_webmock_response_headers(response)
115
+ webmock_response.body = body
116
+ webmock_response
117
+ end
118
+
119
+ def build_webmock_response_headers(response)
120
+ response.headers.each.each_with_object({}) do |(k, v), o|
121
+ o[k] ||= []
122
+ o[k] << v
123
+ end
124
+ end
125
+ end
126
+
127
+ class WebMockClient < Client
128
+ end
129
+
130
+ class WebMockEndpoint
131
+ def initialize(scheme, authority, protocol)
132
+ @scheme = scheme
133
+ @authority = authority
134
+ @protocol = protocol
135
+ end
136
+
137
+ attr :scheme, :authority, :protocol
138
+
139
+ def connect
140
+ server_socket, client_socket = create_connected_sockets
141
+ Async(transient: true) do
142
+ accept_socket(server_socket)
143
+ end
144
+ client_socket
145
+ end
146
+
147
+ def inspect
148
+ "\#<#{self.class}> #{scheme}://#{authority} protocol=#{protocol}"
149
+ end
150
+
151
+ private
152
+
153
+ def create_connected_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|
160
+ sockets.each do |socket|
161
+ socket.instance_variable_set :@alpn_protocol, nil
162
+ socket.instance_eval do
163
+ def alpn_protocol
164
+ nil # means HTTP11 will be used for HTTPS
165
+ end
166
+ end
167
+ end
168
+ end
169
+ end
170
+
171
+ def accept_socket(socket)
172
+ server = Async::HTTP::Server.new(WebMockApplication, self)
173
+ server.accept(socket, socket.remote_address)
174
+ end
175
+ end
176
+
177
+ module WebMockApplication
178
+ WEBMOCK_REQUEST_ID_HEADER = 'x-webmock-request-id'.freeze
179
+
180
+ class << self
181
+ def call(request)
182
+ request.read
183
+ webmock_response = get_webmock_response(request)
184
+ build_response(webmock_response)
185
+ end
186
+
187
+ def add_webmock_response(request, webmock_response)
188
+ webmock_request_id = request.object_id.to_s
189
+ request.headers.add(WEBMOCK_REQUEST_ID_HEADER, webmock_request_id)
190
+ webmock_responses[webmock_request_id] = webmock_response
191
+ end
192
+
193
+ def get_webmock_response(request)
194
+ webmock_request_id = request.headers[WEBMOCK_REQUEST_ID_HEADER][0]
195
+ webmock_responses.fetch(webmock_request_id)
196
+ end
197
+
198
+ private
199
+
200
+ def webmock_responses
201
+ @webmock_responses ||= {}
202
+ end
203
+
204
+ def build_response(webmock_response)
205
+ headers = (webmock_response.headers || {}).each_with_object([]) do |(k, value), o|
206
+ Array(value).each do |v|
207
+ o.push [k, v]
208
+ end
209
+ end
210
+
211
+ ::Protocol::HTTP::Response[
212
+ webmock_response.status[0],
213
+ headers,
214
+ webmock_response.body
215
+ ]
216
+ end
217
+ end
218
+ end
219
+ end
220
+ end
221
+ end
@@ -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
@@ -54,7 +54,6 @@ if defined?(Curl)
54
54
 
55
55
  module Curl
56
56
  class WebMockCurlEasy < Curl::Easy
57
-
58
57
  def curb_or_webmock
59
58
  request_signature = build_request_signature
60
59
  WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
@@ -129,7 +128,7 @@ if defined?(Curl)
129
128
  def headers_as_hash(headers)
130
129
  if headers.is_a?(Array)
131
130
  headers.inject({}) {|hash, header|
132
- name, value = header.split(":").map(&:strip)
131
+ name, value = header.split(":", 2).map(&:strip)
133
132
  hash[name] = value
134
133
  hash
135
134
  }
@@ -271,6 +270,8 @@ if defined?(Curl)
271
270
  def perform
272
271
  @webmock_method ||= :get
273
272
  curb_or_webmock { super }
273
+ ensure
274
+ reset_webmock_method
274
275
  end
275
276
 
276
277
  def put_data= data
@@ -333,8 +334,16 @@ if defined?(Curl)
333
334
  METHOD
334
335
  end
335
336
 
337
+ def reset_webmock_method
338
+ @webmock_method = :get
339
+ end
340
+
336
341
  def reset
337
342
  instance_variable_set(:@body_str, nil)
343
+ instance_variable_set(:@content_type, nil)
344
+ instance_variable_set(:@header_str, nil)
345
+ instance_variable_set(:@last_effective_url, nil)
346
+ instance_variable_set(:@response_code, nil)
338
347
  super
339
348
  end
340
349
  end
@@ -99,6 +99,11 @@ if defined?(EventMachine::HttpClient)
99
99
  end
100
100
  end
101
101
 
102
+ def connection_completed
103
+ @state = :response_header
104
+ send_request(request_signature.headers, request_signature.body)
105
+ end
106
+
102
107
  def send_request(head, body)
103
108
  WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
104
109
 
@@ -107,7 +112,7 @@ if defined?(EventMachine::HttpClient)
107
112
  @uri ||= nil
108
113
  EM.next_tick {
109
114
  setup(make_raw_response(stubbed_webmock_response), @uri,
110
- stubbed_webmock_response.should_timeout ? "WebMock timeout error" : nil)
115
+ stubbed_webmock_response.should_timeout ? Errno::ETIMEDOUT : nil)
111
116
  }
112
117
  self
113
118
  elsif WebMock.net_connect_allowed?(request_signature.uri)
@@ -164,7 +169,7 @@ if defined?(EventMachine::HttpClient)
164
169
  end
165
170
 
166
171
  def build_request_signature
167
- headers, body = @req.headers, @req.body
172
+ headers, body = build_request, @req.body
168
173
 
169
174
  @conn.middleware.select {|m| m.respond_to?(:request) }.each do |m|
170
175
  headers, body = m.request(self, headers, body)
@@ -178,8 +183,6 @@ if defined?(EventMachine::HttpClient)
178
183
 
179
184
  body = form_encode_body(body) if body.is_a?(Hash)
180
185
 
181
- headers = @req.headers
182
-
183
186
  if headers['authorization'] && headers['authorization'].is_a?(Array)
184
187
  headers['Authorization'] = WebMock::Util::Headers.basic_auth_header(headers.delete('authorization'))
185
188
  end
@@ -159,4 +159,7 @@ if defined?(Excon)
159
159
  end
160
160
  end
161
161
  end
162
+
163
+ # Suppresses Excon connection argument validation warning
164
+ Excon::VALID_CONNECTION_KEYS << :__construction_args
162
165
  end
@@ -4,7 +4,8 @@ module HTTP
4
4
 
5
5
  def perform(request, options)
6
6
  return __perform__(request, options) unless webmock_enabled?
7
- WebMockPerform.new(request) { __perform__(request, options) }.exec
7
+
8
+ WebMockPerform.new(request, options) { __perform__(request, options) }.exec
8
9
  end
9
10
 
10
11
  def webmock_enabled?
@@ -11,20 +11,44 @@ module HTTP
11
11
  end
12
12
 
13
13
  class << self
14
- def from_webmock(webmock_response, request_signature = nil)
14
+ def from_webmock(request, webmock_response, request_signature = nil)
15
15
  status = Status.new(webmock_response.status.first)
16
16
  headers = webmock_response.headers || {}
17
- body = Body.new Streamer.new webmock_response.body
18
17
  uri = normalize_uri(request_signature && request_signature.uri)
19
18
 
19
+ # HTTP.rb 3.0+ uses a keyword argument to pass the encoding, but 1.x
20
+ # and 2.x use a positional argument, and 0.x don't support supplying
21
+ # the encoding.
22
+ body = if HTTP::VERSION < "1.0.0"
23
+ Body.new(Streamer.new(webmock_response.body))
24
+ elsif HTTP::VERSION < "3.0.0"
25
+ Body.new(Streamer.new(webmock_response.body), webmock_response.body.encoding)
26
+ else
27
+ Body.new(
28
+ Streamer.new(webmock_response.body, encoding: webmock_response.body.encoding),
29
+ encoding: webmock_response.body.encoding
30
+ )
31
+ end
32
+
20
33
  return new(status, "1.1", headers, body, uri) if HTTP::VERSION < "1.0.0"
21
34
 
35
+ # 5.0.0 had a breaking change to require request instead of uri.
36
+ if HTTP::VERSION < '5.0.0'
37
+ return new({
38
+ status: status,
39
+ version: "1.1",
40
+ headers: headers,
41
+ body: body,
42
+ uri: uri
43
+ })
44
+ end
45
+
22
46
  new({
23
47
  status: status,
24
48
  version: "1.1",
25
49
  headers: headers,
26
50
  body: body,
27
- uri: uri
51
+ request: request,
28
52
  })
29
53
  end
30
54
 
@@ -1,11 +1,12 @@
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
- def readpartial(size = nil)
9
+ def readpartial(size = nil, outbuf = nil)
9
10
  unless size
10
11
  if defined?(HTTP::Client::BUFFER_SIZE)
11
12
  size = HTTP::Client::BUFFER_SIZE
@@ -14,7 +15,8 @@ module HTTP
14
15
  end
15
16
  end
16
17
 
17
- @io.read size
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 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
@@ -43,6 +43,9 @@ if defined?(::HTTPClient)
43
43
  end
44
44
 
45
45
  module WebMockHTTPClients
46
+
47
+ REQUEST_RESPONSE_LOCK = Mutex.new
48
+
46
49
  def do_get_block(req, proxy, conn, &block)
47
50
  do_get(req, proxy, conn, false, &block)
48
51
  end
@@ -57,7 +60,7 @@ if defined?(::HTTPClient)
57
60
  WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
58
61
 
59
62
  if webmock_responses[request_signature]
60
- webmock_response = webmock_responses.delete(request_signature)
63
+ webmock_response = synchronize_request_response { webmock_responses.delete(request_signature) }
61
64
  response = build_httpclient_response(webmock_response, stream, req.header, &block)
62
65
  @request_filter.each do |filter|
63
66
  filter.filter_response(req, response)
@@ -68,7 +71,7 @@ if defined?(::HTTPClient)
68
71
  res
69
72
  elsif WebMock.net_connect_allowed?(request_signature.uri)
70
73
  # in case there is a nil entry in the hash...
71
- webmock_responses.delete(request_signature)
74
+ synchronize_request_response { webmock_responses.delete(request_signature) }
72
75
 
73
76
  res = if stream
74
77
  do_get_stream_without_webmock(req, proxy, conn, &block)
@@ -100,7 +103,7 @@ if defined?(::HTTPClient)
100
103
  def do_request_async(method, uri, query, body, extheader)
101
104
  req = create_request(method, uri, query, body, extheader)
102
105
  request_signature = build_request_signature(req)
103
- webmock_request_signatures << request_signature
106
+ synchronize_request_response { webmock_request_signatures << request_signature }
104
107
 
105
108
  if webmock_responses[request_signature] || WebMock.net_connect_allowed?(request_signature.uri)
106
109
  super
@@ -184,7 +187,9 @@ if defined?(::HTTPClient)
184
187
 
185
188
  def webmock_responses
186
189
  @webmock_responses ||= Hash.new do |hash, request_signature|
187
- hash[request_signature] = WebMock::StubRegistry.instance.response_for_request(request_signature)
190
+ synchronize_request_response do
191
+ hash[request_signature] = WebMock::StubRegistry.instance.response_for_request(request_signature)
192
+ end
188
193
  end
189
194
  end
190
195
 
@@ -193,8 +198,10 @@ if defined?(::HTTPClient)
193
198
  end
194
199
 
195
200
  def previous_signature_for(signature)
196
- return nil unless index = webmock_request_signatures.index(signature)
197
- webmock_request_signatures.delete_at(index)
201
+ synchronize_request_response do
202
+ return nil unless index = webmock_request_signatures.index(signature)
203
+ webmock_request_signatures.delete_at(index)
204
+ end
198
205
  end
199
206
 
200
207
  private
@@ -209,6 +216,16 @@ if defined?(::HTTPClient)
209
216
  hdrs
210
217
  end
211
218
  end
219
+
220
+ def synchronize_request_response
221
+ if REQUEST_RESPONSE_LOCK.owned?
222
+ yield
223
+ else
224
+ REQUEST_RESPONSE_LOCK.synchronize do
225
+ yield
226
+ end
227
+ end
228
+ end
212
229
  end
213
230
 
214
231
  class WebMockHTTPClient < HTTPClient
@@ -24,6 +24,12 @@ if defined?(Manticore)
24
24
  Manticore.instance_variable_set(:@manticore_facade, OriginalManticoreClient.new)
25
25
  end
26
26
 
27
+ class StubbedTimeoutResponse < Manticore::StubbedResponse
28
+ def call
29
+ @handlers[:failure].call(Manticore::ConnectTimeout.new("Too slow (mocked timeout)"))
30
+ end
31
+ end
32
+
27
33
  class WebMockManticoreClient < Manticore::Client
28
34
  def request(klass, url, options={}, &block)
29
35
  super(klass, WebMock::Util::URI.normalize_uri(url).to_s, format_options(options))
@@ -50,19 +56,22 @@ if defined?(Manticore)
50
56
 
51
57
  if webmock_response = registered_response_for(request_signature)
52
58
  webmock_response.raise_error_if_any
53
- manticore_response = generate_manticore_response(webmock_response).call
54
- real_request = false
59
+ manticore_response = generate_manticore_response(webmock_response)
60
+ manticore_response.on_success do
61
+ WebMock::CallbackRegistry.invoke_callbacks({lib: :manticore, real_request: false}, request_signature, webmock_response)
62
+ end
55
63
 
56
64
  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
65
+ manticore_response = Manticore::Response.new(self, request, context, &block)
66
+ manticore_response.on_complete do |completed_response|
67
+ webmock_response = generate_webmock_response(completed_response)
68
+ WebMock::CallbackRegistry.invoke_callbacks({lib: :manticore, real_request: true}, request_signature, webmock_response)
69
+ end
60
70
 
61
71
  else
62
72
  raise WebMock::NetConnectNotAllowedError.new(request_signature)
63
73
  end
64
74
 
65
- WebMock::CallbackRegistry.invoke_callbacks({lib: :manticore, real_request: real_request}, request_signature, webmock_response)
66
75
  manticore_response
67
76
  end
68
77
 
@@ -103,21 +112,30 @@ if defined?(Manticore)
103
112
  end
104
113
 
105
114
  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
- )
115
+ if webmock_response.should_timeout
116
+ StubbedTimeoutResponse.new
117
+ else
118
+ Manticore::StubbedResponse.stub(
119
+ code: webmock_response.status[0],
120
+ body: webmock_response.body,
121
+ headers: webmock_response.headers,
122
+ cookies: {}
123
+ )
124
+ end
114
125
  end
115
126
 
116
127
  def generate_webmock_response(manticore_response)
117
128
  webmock_response = WebMock::Response.new
118
129
  webmock_response.status = [manticore_response.code, manticore_response.message]
119
- webmock_response.body = manticore_response.body
120
130
  webmock_response.headers = manticore_response.headers
131
+
132
+ # The attempt to read the body could fail if manticore is used in a streaming mode
133
+ webmock_response.body = begin
134
+ manticore_response.body
135
+ rescue ::Manticore::StreamClosedException
136
+ nil
137
+ end
138
+
121
139
  webmock_response
122
140
  end
123
141
  end