webmock 3.0.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.
- checksums.yaml +5 -5
- data/.github/workflows/CI.yml +38 -0
- data/CHANGELOG.md +496 -2
- data/Gemfile +1 -1
- data/README.md +169 -34
- data/Rakefile +12 -4
- data/lib/webmock/api.rb +12 -0
- data/lib/webmock/http_lib_adapters/async_http_client_adapter.rb +221 -0
- data/lib/webmock/http_lib_adapters/curb_adapter.rb +19 -5
- data/lib/webmock/http_lib_adapters/em_http_request_adapter.rb +7 -4
- data/lib/webmock/http_lib_adapters/excon_adapter.rb +5 -2
- data/lib/webmock/http_lib_adapters/http_rb/client.rb +2 -1
- data/lib/webmock/http_lib_adapters/http_rb/request.rb +7 -1
- data/lib/webmock/http_lib_adapters/http_rb/response.rb +27 -3
- data/lib/webmock/http_lib_adapters/http_rb/streamer.rb +9 -3
- data/lib/webmock/http_lib_adapters/http_rb/webmock.rb +7 -3
- data/lib/webmock/http_lib_adapters/httpclient_adapter.rb +28 -9
- data/lib/webmock/http_lib_adapters/manticore_adapter.rb +33 -15
- data/lib/webmock/http_lib_adapters/net_http.rb +36 -89
- data/lib/webmock/http_lib_adapters/net_http_response.rb +1 -1
- data/lib/webmock/http_lib_adapters/patron_adapter.rb +4 -4
- data/lib/webmock/matchers/any_arg_matcher.rb +13 -0
- data/lib/webmock/matchers/hash_argument_matcher.rb +21 -0
- data/lib/webmock/matchers/hash_excluding_matcher.rb +15 -0
- data/lib/webmock/matchers/hash_including_matcher.rb +4 -23
- data/lib/webmock/rack_response.rb +1 -1
- data/lib/webmock/request_body_diff.rb +1 -1
- data/lib/webmock/request_execution_verifier.rb +2 -3
- data/lib/webmock/request_pattern.rb +129 -51
- data/lib/webmock/request_registry.rb +1 -1
- data/lib/webmock/request_signature.rb +3 -3
- data/lib/webmock/request_signature_snippet.rb +4 -4
- data/lib/webmock/request_stub.rb +15 -0
- data/lib/webmock/response.rb +19 -13
- data/lib/webmock/rspec.rb +10 -3
- data/lib/webmock/stub_registry.rb +26 -11
- data/lib/webmock/stub_request_snippet.rb +10 -6
- data/lib/webmock/test_unit.rb +1 -3
- data/lib/webmock/util/hash_counter.rb +3 -3
- data/lib/webmock/util/headers.rb +17 -2
- data/lib/webmock/util/json.rb +1 -2
- data/lib/webmock/util/query_mapper.rb +9 -7
- data/lib/webmock/util/uri.rb +10 -10
- data/lib/webmock/util/values_stringifier.rb +20 -0
- data/lib/webmock/version.rb +1 -1
- data/lib/webmock/webmock.rb +20 -3
- data/lib/webmock.rb +53 -48
- data/minitest/webmock_spec.rb +3 -3
- data/spec/acceptance/async_http_client/async_http_client_spec.rb +375 -0
- data/spec/acceptance/async_http_client/async_http_client_spec_helper.rb +73 -0
- data/spec/acceptance/curb/curb_spec.rb +44 -0
- data/spec/acceptance/em_http_request/em_http_request_spec.rb +57 -1
- data/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +2 -2
- data/spec/acceptance/excon/excon_spec.rb +4 -2
- data/spec/acceptance/excon/excon_spec_helper.rb +2 -0
- data/spec/acceptance/http_rb/http_rb_spec.rb +20 -0
- data/spec/acceptance/http_rb/http_rb_spec_helper.rb +5 -2
- data/spec/acceptance/httpclient/httpclient_spec.rb +8 -1
- data/spec/acceptance/manticore/manticore_spec.rb +51 -0
- data/spec/acceptance/net_http/net_http_shared.rb +47 -10
- data/spec/acceptance/net_http/net_http_spec.rb +102 -24
- data/spec/acceptance/net_http/real_net_http_spec.rb +1 -1
- data/spec/acceptance/patron/patron_spec.rb +26 -21
- data/spec/acceptance/patron/patron_spec_helper.rb +3 -3
- data/spec/acceptance/shared/allowing_and_disabling_net_connect.rb +14 -14
- data/spec/acceptance/shared/callbacks.rb +3 -2
- data/spec/acceptance/shared/complex_cross_concern_behaviors.rb +1 -1
- data/spec/acceptance/shared/request_expectations.rb +14 -0
- data/spec/acceptance/shared/returning_declared_responses.rb +36 -15
- data/spec/acceptance/shared/stubbing_requests.rb +95 -0
- data/spec/acceptance/typhoeus/typhoeus_hydra_spec.rb +1 -1
- data/spec/acceptance/typhoeus/typhoeus_hydra_spec_helper.rb +1 -1
- data/spec/support/webmock_server.rb +1 -0
- data/spec/unit/api_spec.rb +103 -3
- data/spec/unit/matchers/hash_excluding_matcher_spec.rb +61 -0
- data/spec/unit/request_execution_verifier_spec.rb +12 -12
- data/spec/unit/request_pattern_spec.rb +207 -49
- data/spec/unit/request_signature_snippet_spec.rb +2 -2
- data/spec/unit/request_signature_spec.rb +21 -1
- data/spec/unit/request_stub_spec.rb +35 -0
- data/spec/unit/response_spec.rb +51 -19
- data/spec/unit/stub_request_snippet_spec.rb +30 -10
- data/spec/unit/util/query_mapper_spec.rb +13 -0
- data/spec/unit/util/uri_spec.rb +74 -2
- data/spec/unit/webmock_spec.rb +108 -5
- data/test/shared_test.rb +15 -2
- data/test/test_webmock.rb +6 -0
- data/webmock.gemspec +15 -7
- metadata +86 -37
- data/.travis.yml +0 -20
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
require 'spec_helper'
|
|
3
|
+
require 'acceptance/webmock_shared'
|
|
4
|
+
require_relative './async_http_client_spec_helper'
|
|
5
|
+
|
|
6
|
+
require 'protocol/http/body/file'
|
|
7
|
+
|
|
8
|
+
Async.logger.debug! if ENV['ASYNC_LOGGER_DEBUG']
|
|
9
|
+
|
|
10
|
+
unless RUBY_PLATFORM =~ /java/
|
|
11
|
+
describe 'Async::HTTP::Client' do
|
|
12
|
+
include AsyncHttpClientSpecHelper
|
|
13
|
+
|
|
14
|
+
include_context "with WebMock",
|
|
15
|
+
:no_status_message,
|
|
16
|
+
:no_url_auth,
|
|
17
|
+
:no_content_length_header
|
|
18
|
+
|
|
19
|
+
it 'works' do
|
|
20
|
+
stub_request(:get, 'http://www.example.com')
|
|
21
|
+
expect(make_request(:get, 'http://www.example.com')).to eq(
|
|
22
|
+
status: 200,
|
|
23
|
+
headers: {},
|
|
24
|
+
body: nil
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it 'works with request path' do
|
|
29
|
+
stub_request(:get, 'http://www.example.com/foo')
|
|
30
|
+
expect(make_request(:get, 'http://www.example.com/foo')).to eq(
|
|
31
|
+
status: 200,
|
|
32
|
+
headers: {},
|
|
33
|
+
body: nil
|
|
34
|
+
)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'works with request query' do
|
|
38
|
+
stub_request(:get, 'http://www.example.com/').with(
|
|
39
|
+
query: {
|
|
40
|
+
'foo' => 'bar'
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
expect(make_request(:get, 'http://www.example.com/?foo=bar')).to eq(
|
|
44
|
+
status: 200,
|
|
45
|
+
headers: {},
|
|
46
|
+
body: nil
|
|
47
|
+
)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it 'works with request headers' do
|
|
51
|
+
stub_request(:get, 'http://www.example.com').with(
|
|
52
|
+
headers: {
|
|
53
|
+
'X-Token' => 'Token'
|
|
54
|
+
}
|
|
55
|
+
)
|
|
56
|
+
expect(
|
|
57
|
+
make_request :get, 'http://www.example.com',
|
|
58
|
+
headers: {
|
|
59
|
+
'X-Token' => 'Token'
|
|
60
|
+
}
|
|
61
|
+
).to eq(
|
|
62
|
+
status: 200,
|
|
63
|
+
headers: {},
|
|
64
|
+
body: nil
|
|
65
|
+
)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it 'works with request body as text' do
|
|
69
|
+
stub_request(:post, 'http://www.example.com').with(
|
|
70
|
+
body: 'x'*10_000
|
|
71
|
+
)
|
|
72
|
+
expect(
|
|
73
|
+
make_request :post, 'http://www.example.com',
|
|
74
|
+
body: 'x'*10_000
|
|
75
|
+
).to eq(
|
|
76
|
+
status: 200,
|
|
77
|
+
headers: {},
|
|
78
|
+
body: nil
|
|
79
|
+
)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it 'works with request body as file' do
|
|
83
|
+
stub_request(:post, "www.example.com").with(
|
|
84
|
+
body: File.read(__FILE__)
|
|
85
|
+
)
|
|
86
|
+
expect(
|
|
87
|
+
make_request :post, "http://www.example.com",
|
|
88
|
+
body: ::Protocol::HTTP::Body::File.open(__FILE__, block_size: 32)
|
|
89
|
+
).to eq(
|
|
90
|
+
status: 200,
|
|
91
|
+
headers: {},
|
|
92
|
+
body: nil
|
|
93
|
+
)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
it 'works with response status' do
|
|
97
|
+
stub_request(:get, 'http://www.example.com').to_return(
|
|
98
|
+
status: 400
|
|
99
|
+
)
|
|
100
|
+
expect(make_request(:get, 'http://www.example.com')).to eq(
|
|
101
|
+
status: 400,
|
|
102
|
+
headers: {},
|
|
103
|
+
body: nil
|
|
104
|
+
)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it 'works with response headers' do
|
|
108
|
+
stub_request(:get, 'http://www.example.com').to_return(
|
|
109
|
+
headers: {
|
|
110
|
+
'X-Token' => 'TOKEN'
|
|
111
|
+
}
|
|
112
|
+
)
|
|
113
|
+
expect(make_request(:get, 'http://www.example.com')).to eq(
|
|
114
|
+
status: 200,
|
|
115
|
+
headers: {
|
|
116
|
+
'x-token' => ['TOKEN']
|
|
117
|
+
},
|
|
118
|
+
body: nil
|
|
119
|
+
)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
it 'works with response body' do
|
|
123
|
+
stub_request(:get, 'http://www.example.com').to_return(
|
|
124
|
+
body: 'abc'
|
|
125
|
+
)
|
|
126
|
+
expect(make_request(:get, 'http://www.example.com')).to eq(
|
|
127
|
+
status: 200,
|
|
128
|
+
headers: {},
|
|
129
|
+
body: 'abc'
|
|
130
|
+
)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
it 'works with to_timeout' do
|
|
134
|
+
stub_request(:get, 'http://www.example.com').to_timeout
|
|
135
|
+
expect { make_request(:get, 'http://www.example.com') }.to raise_error Async::TimeoutError
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it 'does not invoke "after real request" callbacks for stubbed requests' do
|
|
139
|
+
WebMock.allow_net_connect!
|
|
140
|
+
stub_request(:get, 'http://www.example.com').to_return(body: 'abc')
|
|
141
|
+
|
|
142
|
+
callback_invoked = false
|
|
143
|
+
WebMock.after_request(real_requests_only: true) { |_| callback_invoked = true }
|
|
144
|
+
|
|
145
|
+
make_request(:get, 'http://www.example.com')
|
|
146
|
+
expect(callback_invoked).to eq(false)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
it 'does invoke "after request" callbacks for stubbed requests' do
|
|
150
|
+
WebMock.allow_net_connect!
|
|
151
|
+
stub_request(:get, 'http://www.example.com').to_return(body: 'abc')
|
|
152
|
+
|
|
153
|
+
callback_invoked = false
|
|
154
|
+
WebMock.after_request(real_requests_only: false) { |_| callback_invoked = true }
|
|
155
|
+
|
|
156
|
+
make_request(:get, 'http://www.example.com')
|
|
157
|
+
expect(callback_invoked).to eq(true)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
context 'scheme and protocol' do
|
|
161
|
+
let(:default_response_headers) { {} }
|
|
162
|
+
|
|
163
|
+
before do
|
|
164
|
+
stub_request(
|
|
165
|
+
:get, "#{scheme}://www.example.com"
|
|
166
|
+
).and_return(
|
|
167
|
+
body: 'BODY'
|
|
168
|
+
)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
subject do
|
|
172
|
+
make_request(:get, "#{scheme}://www.example.com", protocol: protocol)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
shared_examples :common do
|
|
176
|
+
specify do
|
|
177
|
+
expect(subject).to eq(
|
|
178
|
+
status: 200,
|
|
179
|
+
headers: default_response_headers,
|
|
180
|
+
body: 'BODY'
|
|
181
|
+
)
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
context 'http scheme' do
|
|
186
|
+
let(:scheme) { 'http' }
|
|
187
|
+
|
|
188
|
+
context 'default protocol' do
|
|
189
|
+
let(:protocol) { nil }
|
|
190
|
+
|
|
191
|
+
include_examples :common
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
context 'HTTP10 protocol' do
|
|
195
|
+
let(:protocol) { Async::HTTP::Protocol::HTTP10 }
|
|
196
|
+
let(:default_response_headers) { {"connection"=>["keep-alive"]} }
|
|
197
|
+
|
|
198
|
+
include_examples :common
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
context 'HTTP11 protocol' do
|
|
202
|
+
let(:protocol) { Async::HTTP::Protocol::HTTP11 }
|
|
203
|
+
|
|
204
|
+
include_examples :common
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
context 'HTTP2 protocol' do
|
|
208
|
+
let(:protocol) { Async::HTTP::Protocol::HTTP2 }
|
|
209
|
+
|
|
210
|
+
include_examples :common
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
context 'https scheme' do
|
|
215
|
+
let(:scheme) { 'https' }
|
|
216
|
+
|
|
217
|
+
context 'default protocol' do
|
|
218
|
+
let(:protocol) { nil }
|
|
219
|
+
|
|
220
|
+
include_examples :common
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
context 'HTTP10 protocol' do
|
|
224
|
+
let(:protocol) { Async::HTTP::Protocol::HTTP10 }
|
|
225
|
+
let(:default_response_headers) { {"connection"=>["keep-alive"]} }
|
|
226
|
+
|
|
227
|
+
include_examples :common
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
context 'HTTP11 protocol' do
|
|
231
|
+
let(:protocol) { Async::HTTP::Protocol::HTTP11 }
|
|
232
|
+
|
|
233
|
+
include_examples :common
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
context 'HTTP2 protocol' do
|
|
237
|
+
let(:protocol) { Async::HTTP::Protocol::HTTP2 }
|
|
238
|
+
|
|
239
|
+
include_examples :common
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
context 'HTTPS protocol' do
|
|
243
|
+
let(:protocol) { Async::HTTP::Protocol::HTTPS }
|
|
244
|
+
|
|
245
|
+
include_examples :common
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
context 'multiple requests' do
|
|
251
|
+
let!(:endpoint) { Async::HTTP::Endpoint.parse('http://www.example.com') }
|
|
252
|
+
let(:requests_count) { 3 }
|
|
253
|
+
|
|
254
|
+
shared_examples :common do
|
|
255
|
+
before do
|
|
256
|
+
requests_count.times do |index|
|
|
257
|
+
stub_request(
|
|
258
|
+
:get, "http://www.example.com/foo#{index}"
|
|
259
|
+
).to_return(
|
|
260
|
+
status: 200 + index,
|
|
261
|
+
headers: {'X-Token' => "foo#{index}"},
|
|
262
|
+
body: "FOO#{index}"
|
|
263
|
+
)
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
specify do
|
|
268
|
+
expect(subject).to eq(
|
|
269
|
+
0 => {
|
|
270
|
+
status: 200,
|
|
271
|
+
headers: {'x-token' => ['foo0']},
|
|
272
|
+
body: 'FOO0'
|
|
273
|
+
},
|
|
274
|
+
1 => {
|
|
275
|
+
status: 201,
|
|
276
|
+
headers: {'x-token' => ['foo1']},
|
|
277
|
+
body: 'FOO1'
|
|
278
|
+
},
|
|
279
|
+
2 => {
|
|
280
|
+
status: 202,
|
|
281
|
+
headers: {'x-token' => ['foo2']},
|
|
282
|
+
body: 'FOO2'
|
|
283
|
+
}
|
|
284
|
+
)
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
context 'sequential' do
|
|
289
|
+
subject do
|
|
290
|
+
responses = {}
|
|
291
|
+
Async do |task|
|
|
292
|
+
Async::HTTP::Client.open(endpoint, protocol) do |client|
|
|
293
|
+
requests_count.times do |index|
|
|
294
|
+
response = client.get "/foo#{index}"
|
|
295
|
+
responses[index] = response_to_hash(response)
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
responses
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
context 'HTTP1 protocol' do
|
|
303
|
+
let!(:protocol) { Async::HTTP::Protocol::HTTP1 }
|
|
304
|
+
|
|
305
|
+
include_examples :common
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
context 'HTTP2 protocol' do
|
|
309
|
+
let!(:protocol) { Async::HTTP::Protocol::HTTP2 }
|
|
310
|
+
|
|
311
|
+
include_examples :common
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
context 'asynchronous' do
|
|
316
|
+
subject do
|
|
317
|
+
responses = {}
|
|
318
|
+
Async do |task|
|
|
319
|
+
Async::HTTP::Client.open(endpoint, protocol) do |client|
|
|
320
|
+
tasks = requests_count.times.map do |index|
|
|
321
|
+
task.async do
|
|
322
|
+
response = client.get "/foo#{index}"
|
|
323
|
+
responses[index] = response_to_hash(response)
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
tasks.map(&:wait)
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
responses
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
context 'HTTP1 protocol' do
|
|
334
|
+
let!(:protocol) { Async::HTTP::Protocol::HTTP1 }
|
|
335
|
+
|
|
336
|
+
include_examples :common
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
context 'HTTP2 protocol' do
|
|
340
|
+
let!(:protocol) { Async::HTTP::Protocol::HTTP2 }
|
|
341
|
+
|
|
342
|
+
include_examples :common
|
|
343
|
+
end
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
def make_request(method, url, protocol: nil, headers: {}, body: nil)
|
|
348
|
+
Async do
|
|
349
|
+
endpoint = Async::HTTP::Endpoint.parse(url)
|
|
350
|
+
|
|
351
|
+
begin
|
|
352
|
+
Async::HTTP::Client.open(endpoint, protocol || endpoint.protocol) do |client|
|
|
353
|
+
response = client.send(
|
|
354
|
+
method,
|
|
355
|
+
endpoint.path,
|
|
356
|
+
headers,
|
|
357
|
+
body
|
|
358
|
+
)
|
|
359
|
+
response_to_hash(response)
|
|
360
|
+
end
|
|
361
|
+
rescue Async::TimeoutError => e
|
|
362
|
+
e
|
|
363
|
+
end
|
|
364
|
+
end.wait
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
def response_to_hash(response)
|
|
368
|
+
{
|
|
369
|
+
status: response.status,
|
|
370
|
+
headers: response.headers.to_h,
|
|
371
|
+
body: response.read
|
|
372
|
+
}
|
|
373
|
+
end
|
|
374
|
+
end
|
|
375
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
module AsyncHttpClientSpecHelper
|
|
2
|
+
def http_request(method, url, options = {}, &block)
|
|
3
|
+
endpoint = Async::HTTP::Endpoint.parse(url)
|
|
4
|
+
|
|
5
|
+
path = endpoint.path
|
|
6
|
+
path = path + "?" + options[:query] if options[:query]
|
|
7
|
+
|
|
8
|
+
headers = (options[:headers] || {}).each_with_object([]) do |(k, v), o|
|
|
9
|
+
Array(v).each do |v|
|
|
10
|
+
o.push [k, v]
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
headers.push(
|
|
14
|
+
['authorization', 'Basic ' + Base64.strict_encode64(options[:basic_auth].join(':'))]
|
|
15
|
+
) if options[:basic_auth]
|
|
16
|
+
|
|
17
|
+
body = options[:body]
|
|
18
|
+
|
|
19
|
+
Async do
|
|
20
|
+
begin
|
|
21
|
+
Async::HTTP::Client.open(endpoint) do |client|
|
|
22
|
+
response = client.send(
|
|
23
|
+
method,
|
|
24
|
+
path,
|
|
25
|
+
headers,
|
|
26
|
+
body
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
OpenStruct.new(
|
|
30
|
+
build_hash_response(response)
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
rescue Exception => e
|
|
34
|
+
e
|
|
35
|
+
end
|
|
36
|
+
end.wait
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def client_timeout_exception_class
|
|
40
|
+
Async::TimeoutError
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def connection_refused_exception_class
|
|
44
|
+
Errno::ECONNREFUSED
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def http_library
|
|
48
|
+
:async_http_client
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def build_hash_response(response)
|
|
54
|
+
{
|
|
55
|
+
|
|
56
|
+
status: response.status.to_s,
|
|
57
|
+
message: Protocol::HTTP1::Reason::DESCRIPTIONS[response.status],
|
|
58
|
+
headers: build_response_headers(response),
|
|
59
|
+
body: response.read
|
|
60
|
+
}
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def build_response_headers(response)
|
|
64
|
+
response.headers.each.each_with_object({}) do |(k, v), o|
|
|
65
|
+
o[k] ||= []
|
|
66
|
+
o[k] << v
|
|
67
|
+
end.tap do |o|
|
|
68
|
+
o.each do |k, v|
|
|
69
|
+
o[k] = v.join(', ')
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -383,6 +383,17 @@ unless RUBY_PLATFORM =~ /java/
|
|
|
383
383
|
expect(c.body).to eq("abc")
|
|
384
384
|
end
|
|
385
385
|
|
|
386
|
+
it "supports headers containing the ':' character" do
|
|
387
|
+
stub_request(:get, "www.example.com").with(headers: {'Referer'=>'http://www.example.com'}).to_return(body: "abc")
|
|
388
|
+
|
|
389
|
+
c = Curl::Easy.new
|
|
390
|
+
c.url = "http://www.example.com"
|
|
391
|
+
c.headers = ["Referer: http://www.example.com"]
|
|
392
|
+
c.http(:GET)
|
|
393
|
+
expect(c.body).to eq("abc")
|
|
394
|
+
expect(c.headers).to eq(["Referer: http://www.example.com"])
|
|
395
|
+
end
|
|
396
|
+
|
|
386
397
|
describe 'match request body' do
|
|
387
398
|
it 'for post' do
|
|
388
399
|
stub_request(:post, "www.example.com").with(body: 'foo=nhe').to_return(body: "abc")
|
|
@@ -411,6 +422,17 @@ unless RUBY_PLATFORM =~ /java/
|
|
|
411
422
|
it_should_behave_like "Curb"
|
|
412
423
|
include CurbSpecHelper::NamedHttp
|
|
413
424
|
|
|
425
|
+
it "should reset @webmock_method after each call" do
|
|
426
|
+
stub_request(:post, "www.example.com").with(body: "01234")
|
|
427
|
+
c = Curl::Easy.new
|
|
428
|
+
c.url = "http://www.example.com"
|
|
429
|
+
c.post_body = "01234"
|
|
430
|
+
c.http_post
|
|
431
|
+
expect {
|
|
432
|
+
c.perform
|
|
433
|
+
}.to raise_error(WebMock::NetConnectNotAllowedError, %r(Real HTTP connections are disabled. Unregistered request: GET http://www.example.com))
|
|
434
|
+
end
|
|
435
|
+
|
|
414
436
|
it "should work with blank arguments for post" do
|
|
415
437
|
stub_request(:post, "www.example.com").with(body: "01234")
|
|
416
438
|
c = Curl::Easy.new
|
|
@@ -462,5 +484,27 @@ unless RUBY_PLATFORM =~ /java/
|
|
|
462
484
|
it_should_behave_like "Curb"
|
|
463
485
|
include CurbSpecHelper::ClassPerform
|
|
464
486
|
end
|
|
487
|
+
|
|
488
|
+
describe "using #reset" do
|
|
489
|
+
before do
|
|
490
|
+
@curl = Curl::Easy.new
|
|
491
|
+
@curl.url = "http://example.com"
|
|
492
|
+
stub_request(:any, "example.com").
|
|
493
|
+
to_return(body: "abc",
|
|
494
|
+
headers: { "Content-Type" => "application/json" })
|
|
495
|
+
@curl.http_get
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
it "should clear all memoized response fields" do
|
|
499
|
+
@curl.reset
|
|
500
|
+
expect(@curl).to have_attributes(
|
|
501
|
+
body_str: nil,
|
|
502
|
+
content_type: nil,
|
|
503
|
+
header_str: nil,
|
|
504
|
+
last_effective_url: nil,
|
|
505
|
+
response_code: 0,
|
|
506
|
+
)
|
|
507
|
+
end
|
|
508
|
+
end
|
|
465
509
|
end
|
|
466
510
|
end
|
|
@@ -22,7 +22,7 @@ unless RUBY_PLATFORM =~ /java/
|
|
|
22
22
|
|
|
23
23
|
def make_request
|
|
24
24
|
EM.run do
|
|
25
|
-
request = EM::HttpRequest.new(http_url).get(redirects: 1)
|
|
25
|
+
request = EM::HttpRequest.new(http_url, ssl: {verify_peer: true}).get(redirects: 1)
|
|
26
26
|
request.callback { EM.stop }
|
|
27
27
|
end
|
|
28
28
|
end
|
|
@@ -71,6 +71,35 @@ unless RUBY_PLATFORM =~ /java/
|
|
|
71
71
|
end
|
|
72
72
|
end
|
|
73
73
|
|
|
74
|
+
it "only calls request middleware once" do
|
|
75
|
+
stub_request(:get, "www.example.com")
|
|
76
|
+
|
|
77
|
+
middleware = Class.new do
|
|
78
|
+
def self.called!
|
|
79
|
+
@called = called + 1
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def self.called
|
|
83
|
+
@called || 0
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def request(client, head, body)
|
|
87
|
+
self.class.called!
|
|
88
|
+
[head, body]
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
EM.run do
|
|
93
|
+
conn = EventMachine::HttpRequest.new('http://www.example.com/')
|
|
94
|
+
conn.use middleware
|
|
95
|
+
http = conn.get
|
|
96
|
+
http.callback do
|
|
97
|
+
expect(middleware.called).to eq(1)
|
|
98
|
+
EM.stop
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
74
103
|
let(:response_middleware) do
|
|
75
104
|
Class.new do
|
|
76
105
|
def response(resp)
|
|
@@ -119,6 +148,33 @@ unless RUBY_PLATFORM =~ /java/
|
|
|
119
148
|
context 'making a real request', net_connect: true do
|
|
120
149
|
before { WebMock.allow_net_connect! }
|
|
121
150
|
include_examples "em-http-request middleware/after_request hook integration"
|
|
151
|
+
|
|
152
|
+
it "only calls request middleware once" do
|
|
153
|
+
middleware = Class.new do
|
|
154
|
+
def self.called!
|
|
155
|
+
@called = called + 1
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def self.called
|
|
159
|
+
@called || 0
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def request(client, head, body)
|
|
163
|
+
self.class.called!
|
|
164
|
+
[head, body]
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
EM.run do
|
|
169
|
+
conn = EventMachine::HttpRequest.new(webmock_server_url)
|
|
170
|
+
conn.use middleware
|
|
171
|
+
http = conn.get
|
|
172
|
+
http.callback do
|
|
173
|
+
expect(middleware.called).to eq(1)
|
|
174
|
+
EM.stop
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
122
178
|
end
|
|
123
179
|
|
|
124
180
|
context 'when the request is stubbed' do
|
|
@@ -16,7 +16,7 @@ module EMHttpRequestSpecHelper
|
|
|
16
16
|
error_set = false
|
|
17
17
|
uri = Addressable::URI.heuristic_parse(uri)
|
|
18
18
|
EventMachine.run {
|
|
19
|
-
request = EventMachine::HttpRequest.new("#{uri.normalize.to_s}")
|
|
19
|
+
request = EventMachine::HttpRequest.new("#{uri.normalize.to_s}", ssl: {verify_peer: true})
|
|
20
20
|
http = request.send(method, {
|
|
21
21
|
timeout: 30,
|
|
22
22
|
body: options[:body],
|
|
@@ -50,7 +50,7 @@ module EMHttpRequestSpecHelper
|
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
def client_timeout_exception_class
|
|
53
|
-
|
|
53
|
+
'Errno::ETIMEDOUT'
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
def connection_refused_exception_class
|
|
@@ -20,7 +20,8 @@ describe "Excon" do
|
|
|
20
20
|
it "should support excon response_block for real requests", net_connect: true do
|
|
21
21
|
a = []
|
|
22
22
|
WebMock.allow_net_connect!
|
|
23
|
-
r = Excon.new('
|
|
23
|
+
r = Excon.new('https://httpstat.us/200', headers: { "Accept" => "*" }).
|
|
24
|
+
get(response_block: lambda {|e, remaining, total| a << e}, chunk_size: 1)
|
|
24
25
|
expect(a).to eq(["2", "0", "0", " ", "O", "K"])
|
|
25
26
|
expect(r.body).to eq("")
|
|
26
27
|
end
|
|
@@ -40,7 +41,8 @@ describe "Excon" do
|
|
|
40
41
|
WebMock.after_request { |_, res|
|
|
41
42
|
response = res
|
|
42
43
|
}
|
|
43
|
-
r = Excon.new('
|
|
44
|
+
r = Excon.new('https://httpstat.us/200', headers: { "Accept" => "*" }).
|
|
45
|
+
get(response_block: lambda {|e, remaining, total| a << e}, chunk_size: 1)
|
|
44
46
|
expect(response.body).to eq("200 OK")
|
|
45
47
|
expect(a).to eq(["2", "0", "0", " ", "O", "K"])
|
|
46
48
|
expect(r.body).to eq("")
|
|
@@ -70,4 +70,24 @@ describe "HTTP.rb" do
|
|
|
70
70
|
expect(response.uri.to_s).to eq "http://example.com/foo"
|
|
71
71
|
end
|
|
72
72
|
end
|
|
73
|
+
|
|
74
|
+
context "streamer" do
|
|
75
|
+
it "can be read to a provided buffer" do
|
|
76
|
+
stub_request(:get, "example.com/foo")
|
|
77
|
+
.to_return(status: 200, body: "Hello world! ")
|
|
78
|
+
response = HTTP.get "http://example.com/foo"
|
|
79
|
+
|
|
80
|
+
buffer = ""
|
|
81
|
+
response.body.readpartial(1024, buffer)
|
|
82
|
+
|
|
83
|
+
expect(buffer).to eq "Hello world! "
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it "can be closed" do
|
|
87
|
+
stub_request :get, "example.com/foo"
|
|
88
|
+
response = HTTP.get "http://example.com/foo"
|
|
89
|
+
|
|
90
|
+
response.connection.close
|
|
91
|
+
end
|
|
92
|
+
end
|
|
73
93
|
end
|
|
@@ -8,7 +8,10 @@ module HttpRbSpecHelper
|
|
|
8
8
|
chain = chain.basic_auth(user: basic_auth[0], pass: basic_auth[1])
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
ssl_ctx = OpenSSL::SSL::SSLContext.new
|
|
12
|
+
ssl_ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
13
|
+
|
|
14
|
+
response = chain.request(method, normalize_uri(uri), options.merge(ssl_context: ssl_ctx))
|
|
12
15
|
|
|
13
16
|
OpenStruct.new({
|
|
14
17
|
body: response.body.to_s,
|
|
@@ -20,7 +23,7 @@ module HttpRbSpecHelper
|
|
|
20
23
|
|
|
21
24
|
def client_timeout_exception_class
|
|
22
25
|
return Errno::ETIMEDOUT if HTTP::VERSION < "1.0.0"
|
|
23
|
-
HTTP::
|
|
26
|
+
HTTP::TimeoutError
|
|
24
27
|
end
|
|
25
28
|
|
|
26
29
|
def connection_refused_exception_class
|