webmock 3.6.2 → 3.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/CI.yml +37 -0
- data/CHANGELOG.md +214 -0
- data/Gemfile +1 -1
- data/README.md +85 -32
- data/Rakefile +12 -2
- data/lib/webmock/http_lib_adapters/async_http_client_adapter.rb +216 -0
- data/lib/webmock/http_lib_adapters/curb_adapter.rb +4 -0
- data/lib/webmock/http_lib_adapters/em_http_request_adapter.rb +7 -4
- data/lib/webmock/http_lib_adapters/excon_adapter.rb +3 -0
- data/lib/webmock/http_lib_adapters/http_rb/client.rb +4 -1
- data/lib/webmock/http_lib_adapters/http_rb/response.rb +24 -3
- data/lib/webmock/http_lib_adapters/http_rb/streamer.rb +2 -2
- data/lib/webmock/http_lib_adapters/http_rb/webmock.rb +1 -1
- data/lib/webmock/http_lib_adapters/httpclient_adapter.rb +23 -6
- data/lib/webmock/http_lib_adapters/manticore_adapter.rb +24 -9
- data/lib/webmock/http_lib_adapters/net_http.rb +43 -19
- data/lib/webmock/request_pattern.rb +82 -47
- data/lib/webmock/response.rb +11 -5
- data/lib/webmock/rspec.rb +2 -1
- data/lib/webmock/stub_registry.rb +26 -11
- data/lib/webmock/test_unit.rb +1 -3
- data/lib/webmock/util/uri.rb +5 -4
- data/lib/webmock/version.rb +1 -1
- data/lib/webmock/webmock.rb +5 -3
- data/lib/webmock.rb +1 -0
- 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 +12 -5
- data/spec/acceptance/em_http_request/em_http_request_spec.rb +56 -0
- data/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +1 -1
- data/spec/acceptance/excon/excon_spec_helper.rb +2 -0
- data/spec/acceptance/http_rb/http_rb_spec.rb +11 -0
- data/spec/acceptance/manticore/manticore_spec.rb +51 -0
- data/spec/acceptance/net_http/net_http_spec.rb +38 -0
- data/spec/acceptance/patron/patron_spec_helper.rb +2 -2
- data/spec/acceptance/shared/callbacks.rb +2 -1
- data/spec/acceptance/shared/returning_declared_responses.rb +36 -15
- data/spec/acceptance/shared/stubbing_requests.rb +35 -0
- data/spec/unit/request_pattern_spec.rb +183 -48
- data/spec/unit/response_spec.rb +22 -18
- data/spec/unit/util/uri_spec.rb +10 -0
- data/spec/unit/webmock_spec.rb +52 -11
- data/test/test_webmock.rb +6 -0
- data/webmock.gemspec +11 -1
- metadata +48 -10
- data/.travis.yml +0 -19
@@ -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
|
@@ -474,18 +474,25 @@ unless RUBY_PLATFORM =~ /java/
|
|
474
474
|
include CurbSpecHelper::ClassPerform
|
475
475
|
end
|
476
476
|
|
477
|
-
describe "using
|
477
|
+
describe "using #reset" do
|
478
478
|
before do
|
479
479
|
@curl = Curl::Easy.new
|
480
480
|
@curl.url = "http://example.com"
|
481
|
-
|
482
|
-
|
481
|
+
stub_request(:any, "example.com").
|
482
|
+
to_return(body: "abc",
|
483
|
+
headers: { "Content-Type" => "application/json" })
|
483
484
|
@curl.http_get
|
484
485
|
end
|
485
486
|
|
486
|
-
it "should clear
|
487
|
+
it "should clear all memoized response fields" do
|
487
488
|
@curl.reset
|
488
|
-
expect(@curl
|
489
|
+
expect(@curl).to have_attributes(
|
490
|
+
body_str: nil,
|
491
|
+
content_type: nil,
|
492
|
+
header_str: nil,
|
493
|
+
last_effective_url: nil,
|
494
|
+
response_code: 0,
|
495
|
+
)
|
489
496
|
end
|
490
497
|
end
|
491
498
|
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
|
@@ -72,6 +72,17 @@ describe "HTTP.rb" do
|
|
72
72
|
end
|
73
73
|
|
74
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
|
+
|
75
86
|
it "can be closed" do
|
76
87
|
stub_request :get, "example.com/foo"
|
77
88
|
response = HTTP.get "http://example.com/foo"
|
@@ -51,6 +51,57 @@ if RUBY_PLATFORM =~ /java/
|
|
51
51
|
response = Manticore.head("http://example-foo.com")
|
52
52
|
expect(response.code).to eq(204)
|
53
53
|
end
|
54
|
+
|
55
|
+
context "when a custom failure handler is defined" do
|
56
|
+
let(:failure_handler) { proc {} }
|
57
|
+
|
58
|
+
before do
|
59
|
+
allow(failure_handler).to receive(:call).with(kind_of(Manticore::Timeout)) do |ex|
|
60
|
+
raise ex
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
it "handles timeouts by invoking the failure handler" do
|
65
|
+
stub_request(:get, "http://example-foo.com").to_timeout
|
66
|
+
request = Manticore.get("http://example-foo.com").tap do |req|
|
67
|
+
req.on_failure(&failure_handler)
|
68
|
+
end
|
69
|
+
expect { request.call }.to raise_error(Manticore::Timeout)
|
70
|
+
expect(failure_handler).to have_received(:call)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'when used in a streaming mode' do
|
75
|
+
let(:webmock_server_url) {"http://#{WebMockServer.instance.host_with_port}/"}
|
76
|
+
let(:result_chunks) { [] }
|
77
|
+
|
78
|
+
def manticore_streaming_get
|
79
|
+
Manticore.get(webmock_server_url).tap do |req|
|
80
|
+
req.on_success do |response|
|
81
|
+
response.body do |chunk|
|
82
|
+
result_chunks << chunk
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'when connections are allowed' do
|
89
|
+
it 'works' do
|
90
|
+
WebMock.allow_net_connect!
|
91
|
+
expect { manticore_streaming_get.call }.to_not raise_error
|
92
|
+
expect(result_chunks).to_not be_empty
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context 'when stubbed' do
|
97
|
+
it 'works' do
|
98
|
+
stub_body = 'hello!'
|
99
|
+
stub_request(:get, webmock_server_url).to_return(body: stub_body)
|
100
|
+
expect { manticore_streaming_get.call }.to_not raise_error
|
101
|
+
expect(result_chunks).to eq [stub_body]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
54
105
|
end
|
55
106
|
end
|
56
107
|
end
|
@@ -201,6 +201,18 @@ describe "Net:HTTP" do
|
|
201
201
|
expect(Net::HTTP.get_response(Addressable::URI.parse('http://www.example.com/hello?a=1')).body).to eq("abc")
|
202
202
|
end
|
203
203
|
|
204
|
+
it "should support method calls on stubbed socket" do
|
205
|
+
WebMock.allow_net_connect!
|
206
|
+
stub_request(:get, 'www.google.com')#.with(headers: {"My-Header" => 99})
|
207
|
+
req = Net::HTTP::Get.new('/')
|
208
|
+
Net::HTTP.start('www.google.com') do |http|
|
209
|
+
http.request(req, '')
|
210
|
+
socket = http.instance_variable_get(:@socket)
|
211
|
+
expect(socket).to be_a(StubSocket)
|
212
|
+
expect { socket.io.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) }.to_not raise_error
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
204
216
|
describe "connecting on Net::HTTP.start" do
|
205
217
|
before(:each) do
|
206
218
|
@http = Net::HTTP.new('www.google.com', 443)
|
@@ -328,4 +340,30 @@ describe "Net:HTTP" do
|
|
328
340
|
http.request(req, '')
|
329
341
|
end
|
330
342
|
end
|
343
|
+
|
344
|
+
describe "hostname handling" do
|
345
|
+
it "should set brackets around the hostname if it is an IPv6 address" do
|
346
|
+
net_http = Net::HTTP.new('b2dc:5bdf:4f0d::3014:e0ca', 80)
|
347
|
+
path = '/example.jpg'
|
348
|
+
expect(WebMock::NetHTTPUtility.get_uri(net_http, path)).to eq('http://[b2dc:5bdf:4f0d::3014:e0ca]:80/example.jpg')
|
349
|
+
end
|
350
|
+
|
351
|
+
it "should not set brackets around the hostname if it is already wrapped by brackets" do
|
352
|
+
net_http = Net::HTTP.new('[b2dc:5bdf:4f0d::3014:e0ca]', 80)
|
353
|
+
path = '/example.jpg'
|
354
|
+
expect(WebMock::NetHTTPUtility.get_uri(net_http, path)).to eq('http://[b2dc:5bdf:4f0d::3014:e0ca]:80/example.jpg')
|
355
|
+
end
|
356
|
+
|
357
|
+
it "should not set brackets around the hostname if it is an IPv4 address" do
|
358
|
+
net_http = Net::HTTP.new('181.152.137.168', 80)
|
359
|
+
path = '/example.jpg'
|
360
|
+
expect(WebMock::NetHTTPUtility.get_uri(net_http, path)).to eq('http://181.152.137.168:80/example.jpg')
|
361
|
+
end
|
362
|
+
|
363
|
+
it "should not set brackets around the hostname if it is a domain" do
|
364
|
+
net_http = Net::HTTP.new('www.example.com', 80)
|
365
|
+
path = '/example.jpg'
|
366
|
+
expect(WebMock::NetHTTPUtility.get_uri(net_http, path)).to eq('http://www.example.com:80/example.jpg')
|
367
|
+
end
|
368
|
+
end
|
331
369
|
end
|
@@ -28,8 +28,8 @@ module PatronSpecHelper
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
-
status_line_pattern = %r(\AHTTP/(\d
|
32
|
-
message = response.status_line.match(status_line_pattern)[
|
31
|
+
status_line_pattern = %r(\AHTTP/(\d+(\.\d+)?)\s+(\d\d\d)\s*([^\r\n]+)?)
|
32
|
+
message = response.status_line.match(status_line_pattern)[4] || ""
|
33
33
|
|
34
34
|
OpenStruct.new({
|
35
35
|
body: response.body,
|