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,216 @@
|
|
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
|
+
Async::IO::Socket.pair(Socket::AF_UNIX, Socket::SOCK_STREAM).tap do |sockets|
|
155
|
+
sockets.each do |socket|
|
156
|
+
socket.instance_variable_set :@alpn_protocol, nil
|
157
|
+
socket.instance_eval do
|
158
|
+
def alpn_protocol
|
159
|
+
nil # means HTTP11 will be used for HTTPS
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def accept_socket(socket)
|
167
|
+
server = Async::HTTP::Server.new(WebMockApplication, self)
|
168
|
+
server.accept(socket, socket.remote_address)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
module WebMockApplication
|
173
|
+
WEBMOCK_REQUEST_ID_HEADER = 'x-webmock-request-id'.freeze
|
174
|
+
|
175
|
+
class << self
|
176
|
+
def call(request)
|
177
|
+
request.read
|
178
|
+
webmock_response = get_webmock_response(request)
|
179
|
+
build_response(webmock_response)
|
180
|
+
end
|
181
|
+
|
182
|
+
def add_webmock_response(request, webmock_response)
|
183
|
+
webmock_request_id = request.object_id.to_s
|
184
|
+
request.headers.add(WEBMOCK_REQUEST_ID_HEADER, webmock_request_id)
|
185
|
+
webmock_responses[webmock_request_id] = webmock_response
|
186
|
+
end
|
187
|
+
|
188
|
+
def get_webmock_response(request)
|
189
|
+
webmock_request_id = request.headers[WEBMOCK_REQUEST_ID_HEADER][0]
|
190
|
+
webmock_responses.fetch(webmock_request_id)
|
191
|
+
end
|
192
|
+
|
193
|
+
private
|
194
|
+
|
195
|
+
def webmock_responses
|
196
|
+
@webmock_responses ||= {}
|
197
|
+
end
|
198
|
+
|
199
|
+
def build_response(webmock_response)
|
200
|
+
headers = (webmock_response.headers || {}).each_with_object([]) do |(k, value), o|
|
201
|
+
Array(value).each do |v|
|
202
|
+
o.push [k, v]
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
::Protocol::HTTP::Response[
|
207
|
+
webmock_response.status[0],
|
208
|
+
headers,
|
209
|
+
webmock_response.body
|
210
|
+
]
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
@@ -340,6 +340,10 @@ if defined?(Curl)
|
|
340
340
|
|
341
341
|
def reset
|
342
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)
|
343
347
|
super
|
344
348
|
end
|
345
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 ?
|
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 =
|
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
|
@@ -4,7 +4,10 @@ module HTTP
|
|
4
4
|
|
5
5
|
def perform(request, options)
|
6
6
|
return __perform__(request, options) unless webmock_enabled?
|
7
|
-
|
7
|
+
|
8
|
+
response = WebMockPerform.new(request) { __perform__(request, options) }.exec
|
9
|
+
options.features.each { |_name, feature| response = feature.wrap_response(response) }
|
10
|
+
response
|
8
11
|
end
|
9
12
|
|
10
13
|
def webmock_enabled?
|
@@ -11,20 +11,41 @@ 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(Streamer.new(webmock_response.body), encoding: webmock_response.body.encoding)
|
28
|
+
end
|
29
|
+
|
20
30
|
return new(status, "1.1", headers, body, uri) if HTTP::VERSION < "1.0.0"
|
21
31
|
|
32
|
+
# 5.0.0 had a breaking change to require request instead of uri.
|
33
|
+
if HTTP::VERSION < '5.0.0'
|
34
|
+
return new({
|
35
|
+
status: status,
|
36
|
+
version: "1.1",
|
37
|
+
headers: headers,
|
38
|
+
body: body,
|
39
|
+
uri: uri
|
40
|
+
})
|
41
|
+
end
|
42
|
+
|
22
43
|
new({
|
23
44
|
status: status,
|
24
45
|
version: "1.1",
|
25
46
|
headers: headers,
|
26
47
|
body: body,
|
27
|
-
|
48
|
+
request: request,
|
28
49
|
})
|
29
50
|
end
|
30
51
|
|
@@ -5,7 +5,7 @@ module HTTP
|
|
5
5
|
@io = StringIO.new str
|
6
6
|
end
|
7
7
|
|
8
|
-
def readpartial(size = nil)
|
8
|
+
def readpartial(size = nil, outbuf = nil)
|
9
9
|
unless size
|
10
10
|
if defined?(HTTP::Client::BUFFER_SIZE)
|
11
11
|
size = HTTP::Client::BUFFER_SIZE
|
@@ -14,7 +14,7 @@ module HTTP
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
-
@io.read size
|
17
|
+
@io.read size, outbuf
|
18
18
|
end
|
19
19
|
|
20
20
|
def close
|
@@ -38,7 +38,7 @@ module HTTP
|
|
38
38
|
webmock_response.raise_error_if_any
|
39
39
|
|
40
40
|
invoke_callbacks(webmock_response, real_request: false)
|
41
|
-
::HTTP::Response.from_webmock webmock_response, request_signature
|
41
|
+
::HTTP::Response.from_webmock @request, webmock_response, request_signature
|
42
42
|
end
|
43
43
|
|
44
44
|
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
|
-
|
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
|
-
|
197
|
-
|
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))
|
@@ -106,21 +112,30 @@ if defined?(Manticore)
|
|
106
112
|
end
|
107
113
|
|
108
114
|
def generate_manticore_response(webmock_response)
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
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
|
117
125
|
end
|
118
126
|
|
119
127
|
def generate_webmock_response(manticore_response)
|
120
128
|
webmock_response = WebMock::Response.new
|
121
129
|
webmock_response.status = [manticore_response.code, manticore_response.message]
|
122
|
-
webmock_response.body = manticore_response.body
|
123
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
|
+
|
124
139
|
webmock_response
|
125
140
|
end
|
126
141
|
end
|
@@ -228,9 +228,9 @@ class PatchedStringIO < StringIO #:nodoc:
|
|
228
228
|
|
229
229
|
alias_method :orig_read_nonblock, :read_nonblock
|
230
230
|
|
231
|
-
def read_nonblock(size, *args)
|
231
|
+
def read_nonblock(size, *args, **kwargs)
|
232
232
|
args.reject! {|arg| !arg.is_a?(Hash)}
|
233
|
-
orig_read_nonblock(size, *args)
|
233
|
+
orig_read_nonblock(size, *args, **kwargs)
|
234
234
|
end
|
235
235
|
|
236
236
|
end
|
@@ -252,12 +252,19 @@ class StubSocket #:nodoc:
|
|
252
252
|
def readuntil(*args)
|
253
253
|
end
|
254
254
|
|
255
|
+
def io
|
256
|
+
@io ||= StubIO.new
|
257
|
+
end
|
258
|
+
|
259
|
+
class StubIO
|
260
|
+
def setsockopt(*args); end
|
261
|
+
end
|
255
262
|
end
|
256
263
|
|
257
264
|
module Net #:nodoc: all
|
258
265
|
|
259
266
|
class WebMockNetBufferedIO < BufferedIO
|
260
|
-
def initialize(io, *args)
|
267
|
+
def initialize(io, *args, **kwargs)
|
261
268
|
io = case io
|
262
269
|
when Socket, OpenSSL::SSL::SSLSocket, IO
|
263
270
|
io
|
@@ -268,23 +275,33 @@ module Net #:nodoc: all
|
|
268
275
|
end
|
269
276
|
raise "Unable to create local socket" unless io
|
270
277
|
|
271
|
-
|
278
|
+
# Prior to 2.4.0 `BufferedIO` only takes a single argument (`io`) with no
|
279
|
+
# options. Here we pass through our full set of arguments only if we're
|
280
|
+
# on 2.4.0 or later, and use a simplified invocation otherwise.
|
281
|
+
if RUBY_VERSION >= '2.4.0'
|
282
|
+
super
|
283
|
+
else
|
284
|
+
super(io)
|
285
|
+
end
|
272
286
|
end
|
273
287
|
|
274
288
|
if RUBY_VERSION >= '2.6.0'
|
289
|
+
# https://github.com/ruby/ruby/blob/7d02441f0d6e5c9d0a73a024519eba4f69e36dce/lib/net/protocol.rb#L208
|
290
|
+
# Modified version of method from ruby, so that nil is always passed into orig_read_nonblock to avoid timeout
|
275
291
|
def rbuf_fill
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
292
|
+
case rv = @io.read_nonblock(BUFSIZE, nil, exception: false)
|
293
|
+
when String
|
294
|
+
return if rv.nil?
|
295
|
+
@rbuf << rv
|
296
|
+
rv.clear
|
297
|
+
return
|
298
|
+
when :wait_readable
|
299
|
+
@io.to_io.wait_readable(@read_timeout) or raise Net::ReadTimeout
|
300
|
+
when :wait_writable
|
301
|
+
@io.to_io.wait_writable(@read_timeout) or raise Net::ReadTimeout
|
302
|
+
when nil
|
303
|
+
raise EOFError, 'end of file reached'
|
304
|
+
end while true
|
288
305
|
end
|
289
306
|
end
|
290
307
|
end
|
@@ -296,8 +313,6 @@ module WebMock
|
|
296
313
|
module NetHTTPUtility
|
297
314
|
|
298
315
|
def self.request_signature_from_request(net_http, request, body = nil)
|
299
|
-
protocol = net_http.use_ssl? ? "https" : "http"
|
300
|
-
|
301
316
|
path = request.path
|
302
317
|
|
303
318
|
if path.respond_to?(:request_uri) #https://github.com/bblimke/webmock/issues/288
|
@@ -306,7 +321,7 @@ module WebMock
|
|
306
321
|
|
307
322
|
path = WebMock::Util::URI.heuristic_parse(path).request_uri if path =~ /^http/
|
308
323
|
|
309
|
-
uri =
|
324
|
+
uri = get_uri(net_http, path)
|
310
325
|
method = request.method.downcase.to_sym
|
311
326
|
|
312
327
|
headers = Hash[*request.to_hash.map {|k,v| [k, v]}.inject([]) {|r,x| r + x}]
|
@@ -326,6 +341,15 @@ module WebMock
|
|
326
341
|
WebMock::RequestSignature.new(method, uri, body: request.body, headers: headers)
|
327
342
|
end
|
328
343
|
|
344
|
+
def self.get_uri(net_http, path)
|
345
|
+
protocol = net_http.use_ssl? ? "https" : "http"
|
346
|
+
|
347
|
+
hostname = net_http.address
|
348
|
+
hostname = "[#{hostname}]" if /\A\[.*\]\z/ !~ hostname && /:/ =~ hostname
|
349
|
+
|
350
|
+
"#{protocol}://#{hostname}:#{net_http.port}#{path}"
|
351
|
+
end
|
352
|
+
|
329
353
|
def self.validate_headers(headers)
|
330
354
|
# For Ruby versions < 2.3.0, if you make a request with headers that are symbols
|
331
355
|
# Net::HTTP raises a NoMethodError
|