webmock 3.6.2 → 3.7.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/CHANGELOG.md +6 -0
- data/README.md +2 -2
- data/lib/webmock.rb +1 -0
- data/lib/webmock/http_lib_adapters/async_http_client_adapter.rb +214 -0
- data/lib/webmock/version.rb +1 -1
- data/spec/acceptance/async_http_client/async_http_client_spec.rb +349 -0
- data/spec/acceptance/async_http_client/async_http_client_spec_helper.rb +73 -0
- data/spec/acceptance/shared/callbacks.rb +2 -1
- data/spec/acceptance/shared/returning_declared_responses.rb +36 -15
- data/webmock.gemspec +1 -0
- metadata +21 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e385b4f3b881b29e228d1b6517f4f7a119fbde52757c0076a1be4ad97fb7c860
|
4
|
+
data.tar.gz: ed890699362b956dd2f3e8707d33377069064183355015a6bc7d4fdfbc1d94b9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 93519997dde799f24eb9f64412b5c49ff2b1444fe0158ead76cbf9d340d0273f63aa6790e3acaf5b621df08df621d85d7b3c6a50b55f0d82494869fdac5e491a
|
7
|
+
data.tar.gz: 8668e895d819594f52963336a376c3b0a6b857eccc557d42f5d0d71757ed64af02efd10275c494b2c64b577b81e7f454017ecd5a5b50f4946e817979162521ef
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -33,6 +33,7 @@ Supported HTTP libraries
|
|
33
33
|
* Excon
|
34
34
|
* HTTP Gem
|
35
35
|
* Manticore
|
36
|
+
* Async::HTTP::Client
|
36
37
|
|
37
38
|
Supported Ruby Interpreters
|
38
39
|
---------------------------
|
@@ -1105,8 +1106,7 @@ People who submitted patches and new features or suggested improvements. Many th
|
|
1105
1106
|
* Csaba Apagyi
|
1106
1107
|
* Frederick Cheung
|
1107
1108
|
* Fábio D. Batista
|
1108
|
-
|
1109
|
-
|
1109
|
+
* Andriy Yanko
|
1110
1110
|
|
1111
1111
|
|
1112
1112
|
For a full list of contributors you can visit the
|
data/lib/webmock.rb
CHANGED
@@ -54,5 +54,6 @@ require_relative 'webmock/http_lib_adapters/em_http_request_adapter'
|
|
54
54
|
require_relative 'webmock/http_lib_adapters/typhoeus_hydra_adapter'
|
55
55
|
require_relative 'webmock/http_lib_adapters/excon_adapter'
|
56
56
|
require_relative 'webmock/http_lib_adapters/manticore_adapter'
|
57
|
+
require_relative 'webmock/http_lib_adapters/async_http_client_adapter'
|
57
58
|
|
58
59
|
require_relative 'webmock/webmock'
|
@@ -0,0 +1,214 @@
|
|
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, protocol, scheme, authority, options)
|
44
|
+
@webmock_client = WebMockClient.new(webmock_endpoint, protocol, scheme, authority, 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
|
+
|
59
|
+
if webmock_response
|
60
|
+
webmock_response.raise_error_if_any
|
61
|
+
raise Async::TimeoutError, 'WebMock timeout error' if webmock_response.should_timeout
|
62
|
+
WebMockApplication.add_webmock_response(request, webmock_response)
|
63
|
+
response = @webmock_client.call(request)
|
64
|
+
elsif net_connect_allowed
|
65
|
+
response = @network_client.call(request)
|
66
|
+
else
|
67
|
+
raise WebMock::NetConnectNotAllowedError.new(request_signature) unless webmock_response
|
68
|
+
end
|
69
|
+
|
70
|
+
if WebMock::CallbackRegistry.any_callbacks?
|
71
|
+
webmock_response ||= build_webmock_response(response)
|
72
|
+
WebMock::CallbackRegistry.invoke_callbacks(
|
73
|
+
{
|
74
|
+
lib: :async_http_client,
|
75
|
+
real_request: net_connect_allowed
|
76
|
+
},
|
77
|
+
request_signature,
|
78
|
+
webmock_response
|
79
|
+
)
|
80
|
+
end
|
81
|
+
|
82
|
+
response
|
83
|
+
end
|
84
|
+
|
85
|
+
def close
|
86
|
+
@network_client.close
|
87
|
+
@webmock_client.close
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def build_request_signature(request)
|
93
|
+
body = request.read
|
94
|
+
request.body = ::Protocol::HTTP::Body::Buffered.wrap(body)
|
95
|
+
WebMock::RequestSignature.new(
|
96
|
+
request.method.downcase.to_sym,
|
97
|
+
"#{request.scheme}://#{request.authority}#{request.path}",
|
98
|
+
headers: request.headers.to_h,
|
99
|
+
body: body
|
100
|
+
)
|
101
|
+
end
|
102
|
+
|
103
|
+
def build_webmock_response(response)
|
104
|
+
body = response.read
|
105
|
+
response.body = ::Protocol::HTTP::Body::Buffered.wrap(body)
|
106
|
+
|
107
|
+
webmock_response = WebMock::Response.new
|
108
|
+
webmock_response.status = [
|
109
|
+
response.status,
|
110
|
+
::Protocol::HTTP1::Reason::DESCRIPTIONS[response.status]
|
111
|
+
]
|
112
|
+
webmock_response.headers = build_webmock_response_headers(response)
|
113
|
+
webmock_response.body = body
|
114
|
+
webmock_response
|
115
|
+
end
|
116
|
+
|
117
|
+
def build_webmock_response_headers(response)
|
118
|
+
response.headers.each.each_with_object({}) do |(k, v), o|
|
119
|
+
o[k] ||= []
|
120
|
+
o[k] << v
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
class WebMockClient < Client
|
126
|
+
end
|
127
|
+
|
128
|
+
class WebMockEndpoint
|
129
|
+
def initialize(scheme, authority, protocol)
|
130
|
+
@scheme = scheme
|
131
|
+
@authority = authority
|
132
|
+
@protocol = protocol
|
133
|
+
end
|
134
|
+
|
135
|
+
attr :scheme, :authority, :protocol
|
136
|
+
|
137
|
+
def connect
|
138
|
+
server_socket, client_socket = create_connected_sockets
|
139
|
+
Async do
|
140
|
+
accept_socket(server_socket)
|
141
|
+
end
|
142
|
+
client_socket
|
143
|
+
end
|
144
|
+
|
145
|
+
def inspect
|
146
|
+
"\#<#{self.class}> #{scheme}://#{authority} protocol=#{protocol}"
|
147
|
+
end
|
148
|
+
|
149
|
+
private
|
150
|
+
|
151
|
+
def create_connected_sockets
|
152
|
+
Async::IO::Socket.pair(Socket::AF_UNIX, Socket::SOCK_STREAM).tap do |sockets|
|
153
|
+
sockets.each do |socket|
|
154
|
+
socket.instance_variable_set :@alpn_protocol, @alpn_protocol
|
155
|
+
socket.instance_eval do
|
156
|
+
def alpn_protocol
|
157
|
+
nil # means HTTP11 will be used for HTTPS
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def accept_socket(socket)
|
165
|
+
server = Async::HTTP::Server.new(WebMockApplication, self)
|
166
|
+
server.accept(socket, socket.remote_address)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
module WebMockApplication
|
171
|
+
WEBMOCK_REQUEST_ID_HEADER = 'x-webmock-request-id'.freeze
|
172
|
+
|
173
|
+
class << self
|
174
|
+
def call(request)
|
175
|
+
request.read
|
176
|
+
webmock_response = get_webmock_response(request)
|
177
|
+
build_response(webmock_response)
|
178
|
+
end
|
179
|
+
|
180
|
+
def add_webmock_response(request, webmock_response)
|
181
|
+
webmock_request_id = request.object_id.to_s
|
182
|
+
request.headers.add(WEBMOCK_REQUEST_ID_HEADER, webmock_request_id)
|
183
|
+
webmock_responses[webmock_request_id] = webmock_response
|
184
|
+
end
|
185
|
+
|
186
|
+
def get_webmock_response(request)
|
187
|
+
webmock_request_id = request.headers[WEBMOCK_REQUEST_ID_HEADER][0]
|
188
|
+
webmock_responses.fetch(webmock_request_id)
|
189
|
+
end
|
190
|
+
|
191
|
+
private
|
192
|
+
|
193
|
+
def webmock_responses
|
194
|
+
@webmock_responses ||= {}
|
195
|
+
end
|
196
|
+
|
197
|
+
def build_response(webmock_response)
|
198
|
+
headers = (webmock_response.headers || {}).each_with_object([]) do |(k, v), o|
|
199
|
+
Array(v).each do |v|
|
200
|
+
o.push [k, v]
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
::Protocol::HTTP::Response[
|
205
|
+
webmock_response.status[0],
|
206
|
+
headers,
|
207
|
+
webmock_response.body
|
208
|
+
]
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
data/lib/webmock/version.rb
CHANGED
@@ -0,0 +1,349 @@
|
|
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
|
+
context 'scheme and protocol' do
|
139
|
+
before do
|
140
|
+
stub_request(
|
141
|
+
:get, "#{scheme}://www.example.com"
|
142
|
+
).and_return(
|
143
|
+
body: 'BODY'
|
144
|
+
)
|
145
|
+
end
|
146
|
+
|
147
|
+
subject do
|
148
|
+
make_request(:get, "#{scheme}://www.example.com", protocol: protocol)
|
149
|
+
end
|
150
|
+
|
151
|
+
shared_examples :common do
|
152
|
+
specify do
|
153
|
+
expect(subject).to eq(
|
154
|
+
status: 200,
|
155
|
+
headers: {},
|
156
|
+
body: 'BODY'
|
157
|
+
)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
context 'http scheme' do
|
162
|
+
let(:scheme) { 'http' }
|
163
|
+
|
164
|
+
context 'default protocol' do
|
165
|
+
let(:protocol) { nil }
|
166
|
+
|
167
|
+
include_examples :common
|
168
|
+
end
|
169
|
+
|
170
|
+
context 'HTTP10 protocol' do
|
171
|
+
let(:protocol) { Async::HTTP::Protocol::HTTP10 }
|
172
|
+
|
173
|
+
include_examples :common
|
174
|
+
end
|
175
|
+
|
176
|
+
context 'HTTP11 protocol' do
|
177
|
+
let(:protocol) { Async::HTTP::Protocol::HTTP11 }
|
178
|
+
|
179
|
+
include_examples :common
|
180
|
+
end
|
181
|
+
|
182
|
+
context 'HTTP2 protocol' do
|
183
|
+
let(:protocol) { Async::HTTP::Protocol::HTTP2 }
|
184
|
+
|
185
|
+
include_examples :common
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
context 'https scheme' do
|
190
|
+
let(:scheme) { 'https' }
|
191
|
+
|
192
|
+
context 'default protocol' do
|
193
|
+
let(:protocol) { nil }
|
194
|
+
|
195
|
+
include_examples :common
|
196
|
+
end
|
197
|
+
|
198
|
+
context 'HTTP10 protocol' do
|
199
|
+
let(:protocol) { Async::HTTP::Protocol::HTTP10 }
|
200
|
+
|
201
|
+
include_examples :common
|
202
|
+
end
|
203
|
+
|
204
|
+
context 'HTTP11 protocol' do
|
205
|
+
let(:protocol) { Async::HTTP::Protocol::HTTP11 }
|
206
|
+
|
207
|
+
include_examples :common
|
208
|
+
end
|
209
|
+
|
210
|
+
context 'HTTP2 protocol' do
|
211
|
+
let(:protocol) { Async::HTTP::Protocol::HTTP2 }
|
212
|
+
|
213
|
+
include_examples :common
|
214
|
+
end
|
215
|
+
|
216
|
+
context 'HTTPS protocol' do
|
217
|
+
let(:protocol) { Async::HTTP::Protocol::HTTPS }
|
218
|
+
|
219
|
+
include_examples :common
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
context 'multiple requests' do
|
225
|
+
let(:endpoint) { Async::HTTP::Endpoint.parse('http://www.example.com') }
|
226
|
+
let(:requests_count) { 3 }
|
227
|
+
|
228
|
+
shared_examples :common do
|
229
|
+
before do
|
230
|
+
requests_count.times do |index|
|
231
|
+
stub_request(
|
232
|
+
:get, "http://www.example.com/foo#{index}"
|
233
|
+
).to_return(
|
234
|
+
status: 200 + index,
|
235
|
+
headers: {'X-Token' => "foo#{index}"},
|
236
|
+
body: "FOO#{index}"
|
237
|
+
)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
specify do
|
242
|
+
expect(subject).to eq(
|
243
|
+
0 => {
|
244
|
+
status: 200,
|
245
|
+
headers: {'x-token' => ['foo0']},
|
246
|
+
body: 'FOO0'
|
247
|
+
},
|
248
|
+
1 => {
|
249
|
+
status: 201,
|
250
|
+
headers: {'x-token' => ['foo1']},
|
251
|
+
body: 'FOO1'
|
252
|
+
},
|
253
|
+
2 => {
|
254
|
+
status: 202,
|
255
|
+
headers: {'x-token' => ['foo2']},
|
256
|
+
body: 'FOO2'
|
257
|
+
}
|
258
|
+
)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
context 'sequential' do
|
263
|
+
subject do
|
264
|
+
responses = {}
|
265
|
+
Async do |task|
|
266
|
+
Async::HTTP::Client.open(endpoint, protocol) do |client|
|
267
|
+
requests_count.times do |index|
|
268
|
+
response = client.get "/foo#{index}"
|
269
|
+
responses[index] = response_to_hash(response)
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
responses
|
274
|
+
end
|
275
|
+
|
276
|
+
context 'HTTP1 protocol' do
|
277
|
+
let(:protocol) { Async::HTTP::Protocol::HTTP1 }
|
278
|
+
|
279
|
+
include_examples :common
|
280
|
+
end
|
281
|
+
|
282
|
+
context 'HTTP2 protocol' do
|
283
|
+
let(:protocol) { Async::HTTP::Protocol::HTTP2 }
|
284
|
+
|
285
|
+
include_examples :common
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
context 'asynchronous' do
|
290
|
+
subject do
|
291
|
+
responses = {}
|
292
|
+
Async do |task|
|
293
|
+
Async::HTTP::Client.open(endpoint, protocol) do |client|
|
294
|
+
tasks = requests_count.times.map do |index|
|
295
|
+
task.async do
|
296
|
+
response = client.get "/foo#{index}"
|
297
|
+
responses[index] = response_to_hash(response)
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
tasks.map(&:wait)
|
302
|
+
end
|
303
|
+
end
|
304
|
+
responses
|
305
|
+
end
|
306
|
+
|
307
|
+
context 'HTTP1 protocol' do
|
308
|
+
let(:protocol) { Async::HTTP::Protocol::HTTP1 }
|
309
|
+
|
310
|
+
include_examples :common
|
311
|
+
end
|
312
|
+
|
313
|
+
context 'HTTP2 protocol' do
|
314
|
+
let(:protocol) { Async::HTTP::Protocol::HTTP2 }
|
315
|
+
|
316
|
+
include_examples :common
|
317
|
+
end
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
def make_request(method, url, protocol: nil, headers: {}, body: nil)
|
322
|
+
Async do
|
323
|
+
endpoint = Async::HTTP::Endpoint.parse(url)
|
324
|
+
|
325
|
+
begin
|
326
|
+
Async::HTTP::Client.open(endpoint, protocol || endpoint.protocol) do |client|
|
327
|
+
response = client.send(
|
328
|
+
method,
|
329
|
+
endpoint.path,
|
330
|
+
headers,
|
331
|
+
body
|
332
|
+
)
|
333
|
+
response_to_hash(response)
|
334
|
+
end
|
335
|
+
rescue Async::TimeoutError => e
|
336
|
+
e
|
337
|
+
end
|
338
|
+
end.wait
|
339
|
+
end
|
340
|
+
|
341
|
+
def response_to_hash(response)
|
342
|
+
{
|
343
|
+
status: response.status,
|
344
|
+
headers: response.headers.to_h,
|
345
|
+
body: response.read
|
346
|
+
}
|
347
|
+
end
|
348
|
+
end
|
349
|
+
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
|
@@ -111,7 +111,8 @@ shared_context "callbacks" do |*adapter_info|
|
|
111
111
|
end
|
112
112
|
|
113
113
|
it "should pass real response to callback with headers" do
|
114
|
-
expect(@response.headers["
|
114
|
+
expect(@response.headers["X-Powered-By"]).to eq( "ASP.NET")
|
115
|
+
expect(@response.headers["Content-Length"]).to eq("11") unless adapter_info.include?(:no_content_length_header)
|
115
116
|
end
|
116
117
|
|
117
118
|
it "should pass response to callback with body" do
|
@@ -64,7 +64,10 @@ shared_context "declared responses" do |*adapter_info|
|
|
64
64
|
it "should return response with declared headers" do
|
65
65
|
stub_request(:get, "www.example.com").to_return(headers: SAMPLE_HEADERS)
|
66
66
|
response = http_request(:get, "http://www.example.com/")
|
67
|
-
expect(response.headers["
|
67
|
+
expect(response.headers["Accept"]).to eq("application/json")
|
68
|
+
unless adapter_info.include?(:no_content_length_header)
|
69
|
+
expect(response.headers["Content-Length"]).to eq("8888")
|
70
|
+
end
|
68
71
|
end
|
69
72
|
|
70
73
|
it "should return response with declared headers even if there are multiple headers with the same key" do
|
@@ -171,13 +174,22 @@ shared_context "declared responses" do |*adapter_info|
|
|
171
174
|
end
|
172
175
|
|
173
176
|
it "should return recorded headers" do
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
177
|
+
if adapter_info.include?(:no_content_length_header)
|
178
|
+
expect(@response.headers).to eq({
|
179
|
+
"Date"=>"Sat, 23 Jan 2010 01:01:05 GMT",
|
180
|
+
"Content-Type"=>"text/html; charset=UTF-8",
|
181
|
+
"Connection"=>"Keep-Alive",
|
182
|
+
"Accept"=>"image/jpeg, image/png"
|
183
|
+
})
|
184
|
+
else
|
185
|
+
expect(@response.headers).to eq({
|
186
|
+
"Date"=>"Sat, 23 Jan 2010 01:01:05 GMT",
|
187
|
+
"Content-Type"=>"text/html; charset=UTF-8",
|
188
|
+
"Content-Length"=>"419",
|
189
|
+
"Connection"=>"Keep-Alive",
|
190
|
+
"Accept"=>"image/jpeg, image/png"
|
191
|
+
})
|
192
|
+
end
|
181
193
|
end
|
182
194
|
|
183
195
|
it "should return recorded body" do
|
@@ -205,13 +217,22 @@ shared_context "declared responses" do |*adapter_info|
|
|
205
217
|
end
|
206
218
|
|
207
219
|
it "should return recorded headers" do
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
220
|
+
if adapter_info.include?(:no_content_length_header)
|
221
|
+
expect(@response.headers).to eq({
|
222
|
+
"Date"=>"Sat, 23 Jan 2010 01:01:05 GMT",
|
223
|
+
"Content-Type"=>"text/html; charset=UTF-8",
|
224
|
+
"Connection"=>"Keep-Alive",
|
225
|
+
"Accept"=>"image/jpeg, image/png"
|
226
|
+
})
|
227
|
+
else
|
228
|
+
expect(@response.headers).to eq({
|
229
|
+
"Date"=>"Sat, 23 Jan 2010 01:01:05 GMT",
|
230
|
+
"Content-Type"=>"text/html; charset=UTF-8",
|
231
|
+
"Content-Length"=>"419",
|
232
|
+
"Connection"=>"Keep-Alive",
|
233
|
+
"Accept"=>"image/jpeg, image/png"
|
234
|
+
})
|
235
|
+
end
|
215
236
|
end
|
216
237
|
|
217
238
|
it "should return recorded body" do
|
data/webmock.gemspec
CHANGED
@@ -33,6 +33,7 @@ Gem::Specification.new do |s|
|
|
33
33
|
s.add_development_dependency 'em-http-request', '>= 1.0.2'
|
34
34
|
s.add_development_dependency 'em-synchrony', '>= 1.0.0'
|
35
35
|
s.add_development_dependency 'excon', '>= 0.27.5'
|
36
|
+
s.add_development_dependency 'async-http', '>= 0.48.0'
|
36
37
|
s.add_development_dependency 'minitest', '>= 5.0.0'
|
37
38
|
s.add_development_dependency 'test-unit', '>= 3.0.0'
|
38
39
|
s.add_development_dependency 'rdoc', '> 3.5.0'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: webmock
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bartosz Blimke
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-08-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|
@@ -198,6 +198,20 @@ dependencies:
|
|
198
198
|
- - ">="
|
199
199
|
- !ruby/object:Gem::Version
|
200
200
|
version: 0.27.5
|
201
|
+
- !ruby/object:Gem::Dependency
|
202
|
+
name: async-http
|
203
|
+
requirement: !ruby/object:Gem::Requirement
|
204
|
+
requirements:
|
205
|
+
- - ">="
|
206
|
+
- !ruby/object:Gem::Version
|
207
|
+
version: 0.48.0
|
208
|
+
type: :development
|
209
|
+
prerelease: false
|
210
|
+
version_requirements: !ruby/object:Gem::Requirement
|
211
|
+
requirements:
|
212
|
+
- - ">="
|
213
|
+
- !ruby/object:Gem::Version
|
214
|
+
version: 0.48.0
|
201
215
|
- !ruby/object:Gem::Dependency
|
202
216
|
name: minitest
|
203
217
|
requirement: !ruby/object:Gem::Requirement
|
@@ -265,6 +279,7 @@ files:
|
|
265
279
|
- lib/webmock/cucumber.rb
|
266
280
|
- lib/webmock/deprecation.rb
|
267
281
|
- lib/webmock/errors.rb
|
282
|
+
- lib/webmock/http_lib_adapters/async_http_client_adapter.rb
|
268
283
|
- lib/webmock/http_lib_adapters/curb_adapter.rb
|
269
284
|
- lib/webmock/http_lib_adapters/em_http_request_adapter.rb
|
270
285
|
- lib/webmock/http_lib_adapters/excon_adapter.rb
|
@@ -318,6 +333,8 @@ files:
|
|
318
333
|
- minitest/test_helper.rb
|
319
334
|
- minitest/test_webmock.rb
|
320
335
|
- minitest/webmock_spec.rb
|
336
|
+
- spec/acceptance/async_http_client/async_http_client_spec.rb
|
337
|
+
- spec/acceptance/async_http_client/async_http_client_spec_helper.rb
|
321
338
|
- spec/acceptance/curb/curb_spec.rb
|
322
339
|
- spec/acceptance/curb/curb_spec_helper.rb
|
323
340
|
- spec/acceptance/em_http_request/em_http_request_spec.rb
|
@@ -409,6 +426,8 @@ signing_key:
|
|
409
426
|
specification_version: 4
|
410
427
|
summary: Library for stubbing HTTP requests in Ruby.
|
411
428
|
test_files:
|
429
|
+
- spec/acceptance/async_http_client/async_http_client_spec.rb
|
430
|
+
- spec/acceptance/async_http_client/async_http_client_spec_helper.rb
|
412
431
|
- spec/acceptance/curb/curb_spec.rb
|
413
432
|
- spec/acceptance/curb/curb_spec_helper.rb
|
414
433
|
- spec/acceptance/em_http_request/em_http_request_spec.rb
|