httpx 1.8.1 → 1.8.4
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/doc/release_notes/1_8_2.md +30 -0
- data/doc/release_notes/1_8_3.md +9 -0
- data/doc/release_notes/1_8_4.md +13 -0
- data/lib/httpx/adapters/webmock.rb +2 -1
- data/lib/httpx/buffer.rb +5 -0
- data/lib/httpx/callbacks.rb +2 -2
- data/lib/httpx/chainable.rb +2 -2
- data/lib/httpx/connection/http1.rb +1 -1
- data/lib/httpx/connection/http2.rb +69 -17
- data/lib/httpx/connection.rb +58 -14
- data/lib/httpx/errors.rb +3 -0
- data/lib/httpx/io/ssl.rb +18 -8
- data/lib/httpx/options.rb +21 -12
- data/lib/httpx/plugins/auth.rb +6 -5
- data/lib/httpx/plugins/cookies/cookie.rb +1 -1
- data/lib/httpx/plugins/cookies.rb +2 -2
- data/lib/httpx/plugins/follow_redirects.rb +28 -14
- data/lib/httpx/plugins/grpc/call.rb +4 -4
- data/lib/httpx/plugins/grpc/grpc_encoding.rb +2 -2
- data/lib/httpx/plugins/oauth.rb +4 -2
- data/lib/httpx/plugins/persistent.rb +15 -0
- data/lib/httpx/plugins/proxy/http.rb +27 -6
- data/lib/httpx/plugins/proxy.rb +13 -7
- data/lib/httpx/plugins/retries.rb +26 -9
- data/lib/httpx/plugins/server_sent_events.rb +2 -2
- data/lib/httpx/plugins/stream.rb +2 -2
- data/lib/httpx/plugins/tracing.rb +20 -7
- data/lib/httpx/request/body.rb +8 -5
- data/lib/httpx/request.rb +8 -5
- data/lib/httpx/resolver/multi.rb +2 -2
- data/lib/httpx/resolver/native.rb +4 -0
- data/lib/httpx/resolver/resolver.rb +2 -2
- data/lib/httpx/response/body.rb +2 -2
- data/lib/httpx/response.rb +7 -7
- data/lib/httpx/selector.rb +1 -1
- data/lib/httpx/session.rb +7 -3
- data/lib/httpx/transcoder/chunker.rb +2 -2
- data/lib/httpx/transcoder/json.rb +8 -9
- data/lib/httpx/transcoder/utils/deflater.rb +1 -2
- data/lib/httpx/version.rb +1 -1
- data/sig/connection/http2.rbs +15 -1
- data/sig/connection.rbs +4 -2
- data/sig/errors.rbs +2 -2
- data/sig/io/ssl.rbs +5 -0
- data/sig/plugins/auth.rbs +3 -3
- data/sig/plugins/persistent.rbs +4 -0
- data/sig/plugins/proxy/http.rbs +2 -0
- data/sig/plugins/retries.rbs +4 -2
- data/sig/plugins/tracing.rbs +1 -1
- data/sig/request.rbs +2 -1
- metadata +8 -2
|
@@ -75,9 +75,12 @@ module HTTPX
|
|
|
75
75
|
|
|
76
76
|
max_redirects = redirect_request.max_redirects
|
|
77
77
|
|
|
78
|
-
return response unless response.is_a?(Response)
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
return response unless response.is_a?(Response) &&
|
|
79
|
+
# it's a redirect response with location
|
|
80
|
+
REDIRECT_STATUS.include?(response.status) &&
|
|
81
|
+
response.headers.key?("location") &&
|
|
82
|
+
# maximum number of redirects hasn't been reached yet
|
|
83
|
+
max_redirects.positive?
|
|
81
84
|
|
|
82
85
|
redirect_uri = __get_location_from_response(response)
|
|
83
86
|
|
|
@@ -135,9 +138,9 @@ module HTTPX
|
|
|
135
138
|
return ErrorResponse.new(request, error)
|
|
136
139
|
end
|
|
137
140
|
|
|
138
|
-
|
|
141
|
+
redirect_request = build_request(redirect_method, redirect_uri, redirect_params, options)
|
|
139
142
|
|
|
140
|
-
request.redirect_request =
|
|
143
|
+
request.redirect_request = redirect_request
|
|
141
144
|
|
|
142
145
|
redirect_after = response.headers["retry-after"]
|
|
143
146
|
|
|
@@ -151,24 +154,24 @@ module HTTPX
|
|
|
151
154
|
redirect_after = Utils.parse_retry_after(redirect_after)
|
|
152
155
|
|
|
153
156
|
retry_start = Utils.now
|
|
154
|
-
log { "redirecting after #{redirect_after} secs..." }
|
|
157
|
+
redirect_request.log { "redirecting after #{redirect_after} secs..." }
|
|
155
158
|
selector.after(redirect_after) do
|
|
156
159
|
if (response = request.response)
|
|
157
160
|
response.finish!
|
|
158
|
-
|
|
161
|
+
redirect_request.response = response
|
|
159
162
|
# request has terminated abruptly meanwhile
|
|
160
|
-
|
|
163
|
+
redirect_request.emit_response(response)
|
|
161
164
|
else
|
|
162
|
-
log { "redirecting (elapsed time: #{Utils.elapsed_time(retry_start)})!!" }
|
|
163
|
-
send_request(
|
|
165
|
+
redirect_request.log { "redirecting (elapsed time: #{Utils.elapsed_time(retry_start)})!!" }
|
|
166
|
+
send_request(redirect_request, selector, options)
|
|
164
167
|
end
|
|
165
168
|
end
|
|
166
169
|
else
|
|
167
|
-
send_request(
|
|
170
|
+
send_request(redirect_request, selector, options)
|
|
168
171
|
|
|
169
172
|
# recalling itself, in case an error was triggered by the above, and we can
|
|
170
173
|
# verify retriability again.
|
|
171
|
-
return fetch_response(
|
|
174
|
+
return fetch_response(redirect_request, selector, options)
|
|
172
175
|
end
|
|
173
176
|
nil
|
|
174
177
|
end
|
|
@@ -190,8 +193,7 @@ module HTTPX
|
|
|
190
193
|
|
|
191
194
|
# :nodoc:
|
|
192
195
|
def __get_location_from_response(response)
|
|
193
|
-
|
|
194
|
-
location_uri = URI(response.headers["location"])
|
|
196
|
+
location_uri = URI(response.headers["location"]) #: http_uri
|
|
195
197
|
location_uri = response.uri.merge(location_uri) if location_uri.relative?
|
|
196
198
|
location_uri
|
|
197
199
|
end
|
|
@@ -231,6 +233,18 @@ module HTTPX
|
|
|
231
233
|
@redirect_request.response
|
|
232
234
|
end
|
|
233
235
|
|
|
236
|
+
def response=(response)
|
|
237
|
+
return super unless @redirect_request && @response.nil? # rubocop:disable Lint/ReturnInVoidContext
|
|
238
|
+
|
|
239
|
+
@redirect_request.response = response
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def emit_response(response)
|
|
243
|
+
return super unless @redirect_request && @response.nil?
|
|
244
|
+
|
|
245
|
+
@redirect_request.emit_response(response)
|
|
246
|
+
end
|
|
247
|
+
|
|
234
248
|
def max_redirects
|
|
235
249
|
@options.max_redirects || MAX_REDIRECTS
|
|
236
250
|
end
|
|
@@ -48,12 +48,12 @@ module HTTPX
|
|
|
48
48
|
end
|
|
49
49
|
end
|
|
50
50
|
|
|
51
|
-
def respond_to_missing?(meth,
|
|
52
|
-
grpc_response.respond_to?(meth,
|
|
51
|
+
def respond_to_missing?(meth, ...)
|
|
52
|
+
grpc_response.respond_to?(meth, ...) || super
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
-
def method_missing(meth,
|
|
56
|
-
return grpc_response.__send__(meth,
|
|
55
|
+
def method_missing(meth, ...)
|
|
56
|
+
return grpc_response.__send__(meth, ...) if grpc_response.respond_to?(meth)
|
|
57
57
|
|
|
58
58
|
super
|
|
59
59
|
end
|
data/lib/httpx/plugins/oauth.rb
CHANGED
|
@@ -296,9 +296,11 @@ module HTTPX
|
|
|
296
296
|
|
|
297
297
|
private
|
|
298
298
|
|
|
299
|
-
def generate_auth_token
|
|
300
|
-
return unless @oauth_session
|
|
299
|
+
def generate_auth_token(*)
|
|
300
|
+
return super unless @oauth_session
|
|
301
301
|
|
|
302
|
+
# should_regenerate arg ignored, as there's no way to check
|
|
303
|
+
# for token expiration yet.
|
|
302
304
|
@oauth_session.fetch_access_token(self)
|
|
303
305
|
end
|
|
304
306
|
|
|
@@ -18,6 +18,12 @@ module HTTPX
|
|
|
18
18
|
# https://gitlab.com/os85/httpx/wikis/Persistent
|
|
19
19
|
#
|
|
20
20
|
module Persistent
|
|
21
|
+
SAFE_REONNECTABLE_ERRORS = [
|
|
22
|
+
PingTimeoutError,
|
|
23
|
+
Connection::HTTP2::GoawayError,
|
|
24
|
+
Connection::HTTP2::PingError,
|
|
25
|
+
].freeze
|
|
26
|
+
|
|
21
27
|
class << self
|
|
22
28
|
def load_dependencies(klass)
|
|
23
29
|
klass.plugin(:fiber_concurrency)
|
|
@@ -59,6 +65,15 @@ module HTTPX
|
|
|
59
65
|
Retries::RECONNECTABLE_ERRORS.any? { |klass| error.is_a?(klass) }
|
|
60
66
|
end
|
|
61
67
|
|
|
68
|
+
# whether the error can be safely retried without booking threshold attempts.
|
|
69
|
+
def safe_reconnectable_error?(error)
|
|
70
|
+
SAFE_REONNECTABLE_ERRORS.any? { |klass| error.is_a?(klass) }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def can_reconnect?(_, response)
|
|
74
|
+
super || (response.is_a?(ErrorResponse) && safe_reconnectable_error?(response.error))
|
|
75
|
+
end
|
|
76
|
+
|
|
62
77
|
def when_to_retry(request, response, *)
|
|
63
78
|
return super unless response.is_a?(ErrorResponse)
|
|
64
79
|
|
|
@@ -46,6 +46,11 @@ module HTTPX
|
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
module ConnectionMethods
|
|
49
|
+
def initialize(*)
|
|
50
|
+
@tunnel_parser = nil
|
|
51
|
+
super
|
|
52
|
+
end
|
|
53
|
+
|
|
49
54
|
def force_close(*)
|
|
50
55
|
if @state == :connecting
|
|
51
56
|
# proxy connect related requests should not be reenqueed
|
|
@@ -59,6 +64,12 @@ module HTTPX
|
|
|
59
64
|
|
|
60
65
|
private
|
|
61
66
|
|
|
67
|
+
def purge_after_closed
|
|
68
|
+
super
|
|
69
|
+
|
|
70
|
+
@tunnel_parser = nil
|
|
71
|
+
end
|
|
72
|
+
|
|
62
73
|
def handle_transition(nextstate)
|
|
63
74
|
return super unless @options.proxy && @options.proxy.uri.scheme == "http"
|
|
64
75
|
|
|
@@ -69,8 +80,15 @@ module HTTPX
|
|
|
69
80
|
@io.connect
|
|
70
81
|
return unless @io.connected?
|
|
71
82
|
|
|
72
|
-
@
|
|
73
|
-
@parser
|
|
83
|
+
@tunnel_parser || begin
|
|
84
|
+
if @parser
|
|
85
|
+
# in case this happens, requests from @parser need to be reenqueued for delivery.
|
|
86
|
+
enqueue_pending_requests_from_parser(@parser)
|
|
87
|
+
@parser = nil
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
@tunnel_parser = @parser = parser =
|
|
91
|
+
parser_type(@io.protocol).new(@write_buffer, @options.merge(max_concurrent_requests: 1))
|
|
74
92
|
parser.extend(ProxyParser)
|
|
75
93
|
parser.on(:response, &method(:__http_on_connect))
|
|
76
94
|
parser.on(:close) do
|
|
@@ -86,6 +104,9 @@ module HTTPX
|
|
|
86
104
|
|
|
87
105
|
initial_state = @state
|
|
88
106
|
|
|
107
|
+
# because #reset will nillify @parser...
|
|
108
|
+
tunnel_parser = @parser
|
|
109
|
+
|
|
89
110
|
reset
|
|
90
111
|
|
|
91
112
|
if @pending.empty?
|
|
@@ -98,8 +119,8 @@ module HTTPX
|
|
|
98
119
|
connect_request = parser = nil
|
|
99
120
|
|
|
100
121
|
if initial_state == :connecting
|
|
101
|
-
parser =
|
|
102
|
-
|
|
122
|
+
parser = tunnel_parser
|
|
123
|
+
parser.reset
|
|
103
124
|
if @pending.first.is_a?(ConnectRequest)
|
|
104
125
|
connect_request = @pending.shift # this happened when reenqueing
|
|
105
126
|
end
|
|
@@ -107,7 +128,7 @@ module HTTPX
|
|
|
107
128
|
|
|
108
129
|
idling
|
|
109
130
|
|
|
110
|
-
@parser = parser
|
|
131
|
+
@tunnel_parser = @parser = parser
|
|
111
132
|
if connect_request
|
|
112
133
|
@inflight += 1
|
|
113
134
|
parser.send(connect_request)
|
|
@@ -124,7 +145,7 @@ module HTTPX
|
|
|
124
145
|
case @state
|
|
125
146
|
when :connecting
|
|
126
147
|
parser = @parser
|
|
127
|
-
@parser = nil
|
|
148
|
+
@tunnel_parser = @parser = nil
|
|
128
149
|
parser.close
|
|
129
150
|
when :idle
|
|
130
151
|
@parser.callbacks.clear
|
data/lib/httpx/plugins/proxy.rb
CHANGED
|
@@ -92,16 +92,16 @@ module HTTPX
|
|
|
92
92
|
@authenticator = load_authenticator(scheme, @username, @password)
|
|
93
93
|
end
|
|
94
94
|
|
|
95
|
-
def can_authenticate?(
|
|
95
|
+
def can_authenticate?(...)
|
|
96
96
|
return false unless @authenticator && @authenticator.respond_to?(:can_authenticate?)
|
|
97
97
|
|
|
98
|
-
@authenticator.can_authenticate?(
|
|
98
|
+
@authenticator.can_authenticate?(...)
|
|
99
99
|
end
|
|
100
100
|
|
|
101
|
-
def authenticate(
|
|
101
|
+
def authenticate(...)
|
|
102
102
|
return unless @authenticator
|
|
103
103
|
|
|
104
|
-
@authenticator.authenticate(
|
|
104
|
+
@authenticator.authenticate(...)
|
|
105
105
|
end
|
|
106
106
|
|
|
107
107
|
def ==(other)
|
|
@@ -182,9 +182,11 @@ module HTTPX
|
|
|
182
182
|
if (no_proxy = proxy.no_proxy)
|
|
183
183
|
no_proxy = no_proxy.join(",") if no_proxy.is_a?(Array)
|
|
184
184
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
185
|
+
unless no_proxy != "*" && # NO_PROXY=* bypasses proxy use
|
|
186
|
+
URI::Generic.use_proxy?(request_uri.host, next_proxy.host, next_proxy.port, no_proxy)
|
|
187
|
+
# TODO: setting proxy to nil leaks the connection object in the pool
|
|
188
|
+
return super(request_uri, selector, options.merge(proxy: nil))
|
|
189
|
+
end
|
|
188
190
|
end
|
|
189
191
|
|
|
190
192
|
super(request_uri, selector, options.merge(proxy: proxy))
|
|
@@ -341,6 +343,10 @@ module HTTPX
|
|
|
341
343
|
module InstanceMethods
|
|
342
344
|
private
|
|
343
345
|
|
|
346
|
+
def proxy_error?(request, response, _)
|
|
347
|
+
super && !request.retries.positive?
|
|
348
|
+
end
|
|
349
|
+
|
|
344
350
|
def retryable_error?(ex, *)
|
|
345
351
|
super || ex.is_a?(ProxyConnectionError)
|
|
346
352
|
end
|
|
@@ -29,10 +29,23 @@ module HTTPX
|
|
|
29
29
|
Errno::ETIMEDOUT,
|
|
30
30
|
ConnectionError,
|
|
31
31
|
TLSError,
|
|
32
|
-
|
|
32
|
+
Zlib::BufError,
|
|
33
|
+
PingTimeoutError,
|
|
34
|
+
# HTTP/2 GOAWAY or RST_STREAM errors guaranteed to be retriable for all
|
|
35
|
+
# types of requests.
|
|
36
|
+
Connection::HTTP2::RefusedStreamError,
|
|
37
|
+
Connection::HTTP2::GoawayError,
|
|
38
|
+
Connection::HTTP2::PingError,
|
|
39
|
+
|
|
40
|
+
# happens if connections were somehow sent to a closed or closing
|
|
41
|
+
# HTTP2::Connection, request guaranteed to not have been processed.
|
|
42
|
+
::HTTP2::Error::ConnectionClosed,
|
|
33
43
|
].freeze
|
|
34
44
|
|
|
35
45
|
RETRYABLE_ERRORS = (RECONNECTABLE_ERRORS + [
|
|
46
|
+
# most RST_STREAM errors aren't safely retriable, as request may have
|
|
47
|
+
# been partially processed.
|
|
48
|
+
Connection::HTTP2::RstStreamError,
|
|
36
49
|
Parser::Error,
|
|
37
50
|
TimeoutError,
|
|
38
51
|
]).freeze
|
|
@@ -148,10 +161,13 @@ module HTTPX
|
|
|
148
161
|
retryable_response?(response, options)
|
|
149
162
|
try_partial_retry(request, response)
|
|
150
163
|
log { "failed to get response, #{request.retries} tries to go..." }
|
|
151
|
-
prepare_to_retry(request, response)
|
|
152
164
|
|
|
153
|
-
|
|
165
|
+
# retry-after must be calculated before prepare_to_retry, as it relies on
|
|
166
|
+
# state changed byit.
|
|
167
|
+
retry_after = when_to_retry(request, response, options)
|
|
168
|
+
prepare_to_retry(request, response)
|
|
154
169
|
|
|
170
|
+
if retry_after&.positive?
|
|
155
171
|
retry_start = Utils.now
|
|
156
172
|
log { "retrying after #{retry_after} secs..." }
|
|
157
173
|
selector.after(retry_after) do
|
|
@@ -191,13 +207,14 @@ module HTTPX
|
|
|
191
207
|
RETRYABLE_ERRORS.any? { |klass| ex.is_a?(klass) } && !ex.is_a?(TotalRequestTimeoutError)
|
|
192
208
|
end
|
|
193
209
|
|
|
194
|
-
def
|
|
195
|
-
|
|
210
|
+
def prepare_to_retry(request, response)
|
|
211
|
+
request.retries -= 1 unless can_reconnect?(request, response)
|
|
212
|
+
request.transition(:idle)
|
|
196
213
|
end
|
|
197
214
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
request.
|
|
215
|
+
# do not exhaust retries on connection liveness probes
|
|
216
|
+
def can_reconnect?(request, _)
|
|
217
|
+
request.ping?
|
|
201
218
|
end
|
|
202
219
|
|
|
203
220
|
def when_to_retry(request, response, options)
|
|
@@ -249,7 +266,7 @@ module HTTPX
|
|
|
249
266
|
attr_writer :partial_response
|
|
250
267
|
|
|
251
268
|
# initializes the request instance, sets the number of retries for the request.
|
|
252
|
-
def initialize(*
|
|
269
|
+
def initialize(*)
|
|
253
270
|
super
|
|
254
271
|
@retries = @options.max_retries
|
|
255
272
|
@partial_response = nil
|
|
@@ -40,7 +40,7 @@ module HTTPX
|
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
module InstanceMethods
|
|
43
|
-
def request(
|
|
43
|
+
def request(*, **options)
|
|
44
44
|
options[:stream] = true if options[:event_stream]
|
|
45
45
|
|
|
46
46
|
super
|
|
@@ -146,7 +146,7 @@ module HTTPX
|
|
|
146
146
|
def when_to_retry(request, *)
|
|
147
147
|
retry_after = request.last_server_sent_message&.retry_after
|
|
148
148
|
|
|
149
|
-
retry_after / 1_000.0 if retry_after # original in milliseconds
|
|
149
|
+
retry_after.to_i / 1_000.0 if retry_after # original in milliseconds
|
|
150
150
|
|
|
151
151
|
request.last_server_sent_message&.retry_after && super
|
|
152
152
|
end
|
data/lib/httpx/plugins/stream.rb
CHANGED
|
@@ -111,10 +111,10 @@ module HTTPX
|
|
|
111
111
|
end || super
|
|
112
112
|
end
|
|
113
113
|
|
|
114
|
-
def method_missing(meth,
|
|
114
|
+
def method_missing(meth, ...)
|
|
115
115
|
return super unless response.respond_to?(meth)
|
|
116
116
|
|
|
117
|
-
response.__send__(meth,
|
|
117
|
+
response.__send__(meth, ...)
|
|
118
118
|
end
|
|
119
119
|
end
|
|
120
120
|
|
|
@@ -36,9 +36,9 @@ module HTTPX::Plugins
|
|
|
36
36
|
%i[start finish reset enabled?].each do |callback|
|
|
37
37
|
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
|
38
38
|
# proxies ##{callback} calls to wrapper tracers.
|
|
39
|
-
def #{callback}(
|
|
40
|
-
@tracers.each { |t| t.#{callback}(
|
|
41
|
-
end
|
|
39
|
+
def #{callback}(...) # def start(...)
|
|
40
|
+
@tracers.each { |t| t.#{callback}(...) } # @tracers.each { |t| t.start(....) }
|
|
41
|
+
end # end
|
|
42
42
|
OUT
|
|
43
43
|
end
|
|
44
44
|
end
|
|
@@ -108,11 +108,13 @@ module HTTPX::Plugins
|
|
|
108
108
|
def initialize(*)
|
|
109
109
|
super
|
|
110
110
|
|
|
111
|
-
@init_time =
|
|
111
|
+
@init_time = nil
|
|
112
112
|
end
|
|
113
113
|
|
|
114
114
|
def send_request_to_parser(request)
|
|
115
115
|
if connecting?
|
|
116
|
+
@init_time ||= ::Time.now.utc
|
|
117
|
+
|
|
116
118
|
# request span timeframe should include the time it took to connect.
|
|
117
119
|
request.init_time ||= @init_time
|
|
118
120
|
end
|
|
@@ -123,13 +125,24 @@ module HTTPX::Plugins
|
|
|
123
125
|
def idling
|
|
124
126
|
super
|
|
125
127
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
128
|
+
@init_time = nil
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def terminate
|
|
132
|
+
super
|
|
133
|
+
|
|
134
|
+
# ensure that connections which go back to the pool reset their init time.
|
|
135
|
+
@init_time = nil
|
|
129
136
|
end
|
|
130
137
|
|
|
131
138
|
private
|
|
132
139
|
|
|
140
|
+
def connect
|
|
141
|
+
@init_time ||= ::Time.now.utc
|
|
142
|
+
|
|
143
|
+
super
|
|
144
|
+
end
|
|
145
|
+
|
|
133
146
|
def ping(request)
|
|
134
147
|
# if a connection is probed for liveness, the request timeframe should include
|
|
135
148
|
# it too.
|
data/lib/httpx/request/body.rb
CHANGED
|
@@ -4,14 +4,17 @@ module HTTPX
|
|
|
4
4
|
# Implementation of the HTTP Request body as a delegator which iterates (responds to +each+) payload chunks.
|
|
5
5
|
class Request::Body < SimpleDelegator
|
|
6
6
|
class << self
|
|
7
|
-
def new(
|
|
8
|
-
|
|
7
|
+
def new(h, options, body: nil, **params)
|
|
8
|
+
case body
|
|
9
|
+
when self
|
|
9
10
|
# request derives its options from body
|
|
10
11
|
body.options = options.merge(params)
|
|
11
|
-
|
|
12
|
+
body
|
|
13
|
+
when nil
|
|
14
|
+
super(h, options, **params)
|
|
15
|
+
else
|
|
16
|
+
super
|
|
12
17
|
end
|
|
13
|
-
|
|
14
|
-
super
|
|
15
18
|
end
|
|
16
19
|
end
|
|
17
20
|
|
data/lib/httpx/request.rb
CHANGED
|
@@ -105,9 +105,9 @@ module HTTPX
|
|
|
105
105
|
|
|
106
106
|
@state = :idle
|
|
107
107
|
@connection = @response =
|
|
108
|
-
@drainer = @peer_address =
|
|
109
|
-
|
|
110
|
-
@ping = @started = false
|
|
108
|
+
@drainer = @peer_address = @callbacks =
|
|
109
|
+
@informational_status = @on_response_arrived = nil
|
|
110
|
+
@ping = @started = @complete = false
|
|
111
111
|
@persistent = @options.persistent
|
|
112
112
|
@active_timeouts = []
|
|
113
113
|
end
|
|
@@ -115,14 +115,17 @@ module HTTPX
|
|
|
115
115
|
# dupped initialization
|
|
116
116
|
def initialize_dup(orig)
|
|
117
117
|
super
|
|
118
|
-
@uri = orig.instance_variable_get(:@uri).dup
|
|
119
118
|
@headers = orig.instance_variable_get(:@headers).dup
|
|
120
119
|
@body = orig.instance_variable_get(:@body).dup
|
|
120
|
+
@active_timeouts = orig.instance_variable_get(:@active_timeouts).dup
|
|
121
121
|
end
|
|
122
122
|
|
|
123
123
|
def complete!(response = @response)
|
|
124
|
+
return false if @complete
|
|
125
|
+
|
|
124
126
|
emit(:complete, response)
|
|
125
127
|
reset_timers(true)
|
|
128
|
+
@complete = true
|
|
126
129
|
end
|
|
127
130
|
|
|
128
131
|
# whether request has been buffered with a ping
|
|
@@ -296,7 +299,7 @@ module HTTPX
|
|
|
296
299
|
case nextstate
|
|
297
300
|
when :idle
|
|
298
301
|
@body.rewind
|
|
299
|
-
@ping = false
|
|
302
|
+
@ping = @complete = false
|
|
300
303
|
@response = @drainer = nil
|
|
301
304
|
|
|
302
305
|
# request may be sent to a different connection and will be
|
data/lib/httpx/resolver/multi.rb
CHANGED
|
@@ -539,6 +539,10 @@ module HTTPX
|
|
|
539
539
|
return unless @io.connected?
|
|
540
540
|
|
|
541
541
|
resolve if @queries.empty? && !@connections.empty?
|
|
542
|
+
|
|
543
|
+
# #resolve may have closed the resolver already as part of error handling.
|
|
544
|
+
# @fiber-switch-guard
|
|
545
|
+
return unless @io
|
|
542
546
|
when :closed
|
|
543
547
|
return if @state == :closed
|
|
544
548
|
|
data/lib/httpx/response/body.rb
CHANGED
|
@@ -69,7 +69,7 @@ module HTTPX
|
|
|
69
69
|
end
|
|
70
70
|
|
|
71
71
|
# reads a chunk from the payload (implementation of the IO reader protocol).
|
|
72
|
-
def read(
|
|
72
|
+
def read(...)
|
|
73
73
|
return unless @buffer
|
|
74
74
|
|
|
75
75
|
unless @reader
|
|
@@ -77,7 +77,7 @@ module HTTPX
|
|
|
77
77
|
@reader = @buffer
|
|
78
78
|
end
|
|
79
79
|
|
|
80
|
-
@reader.read(
|
|
80
|
+
@reader.read(...)
|
|
81
81
|
end
|
|
82
82
|
|
|
83
83
|
# size of the decoded response payload. May differ from "content-length" header if
|
data/lib/httpx/response.rb
CHANGED
|
@@ -36,10 +36,10 @@ module HTTPX
|
|
|
36
36
|
# * HTTPX::Response::Body#read
|
|
37
37
|
# * HTTPX::Response::Body#copy_to
|
|
38
38
|
# * HTTPX::Response::Body#close
|
|
39
|
-
attr_reader
|
|
39
|
+
attr_reader :body
|
|
40
40
|
|
|
41
41
|
# The HTTP protocol version used to fetch the response.
|
|
42
|
-
attr_reader
|
|
42
|
+
attr_reader :version
|
|
43
43
|
|
|
44
44
|
# returns the response body buffered in a string.
|
|
45
45
|
def_delegator :@body, :to_s
|
|
@@ -68,7 +68,7 @@ module HTTPX
|
|
|
68
68
|
@headers = @options.headers_class.new(headers)
|
|
69
69
|
@body = @options.response_body_class.new(self, @options)
|
|
70
70
|
@finished = complete?
|
|
71
|
-
@content_type = @content_length = nil
|
|
71
|
+
@callbacks = @content_type = @content_length = nil
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
# dupped initialization
|
|
@@ -176,8 +176,8 @@ module HTTPX
|
|
|
176
176
|
#
|
|
177
177
|
# response.json #≈> { "foo" => "bar" } for "{\"foo\":\"bar\"}" payload
|
|
178
178
|
# response.json(symbolize_names: true) #≈> { foo: "bar" } for "{\"foo\":\"bar\"}" payload
|
|
179
|
-
def json(
|
|
180
|
-
decode(Transcoder::JSON,
|
|
179
|
+
def json(...)
|
|
180
|
+
decode(Transcoder::JSON, ...)
|
|
181
181
|
end
|
|
182
182
|
|
|
183
183
|
# decodes the response payload into a ruby object **if** the payload is valid
|
|
@@ -200,7 +200,7 @@ module HTTPX
|
|
|
200
200
|
#
|
|
201
201
|
# +transcoder+ must implement the internal transcoder API, i.e. respond to <tt>decode(HTTPX::Response response)</tt>,
|
|
202
202
|
# which returns a decoder which responds to <tt>call(HTTPX::Response response, **kwargs)</tt>
|
|
203
|
-
def decode(transcoder,
|
|
203
|
+
def decode(transcoder, ...)
|
|
204
204
|
# TODO: check if content-type is a valid format, i.e. "application/json" for json parsing
|
|
205
205
|
|
|
206
206
|
decoder = transcoder.decode(self)
|
|
@@ -209,7 +209,7 @@ module HTTPX
|
|
|
209
209
|
|
|
210
210
|
@body.rewind
|
|
211
211
|
|
|
212
|
-
decoder.call(self,
|
|
212
|
+
decoder.call(self, ...)
|
|
213
213
|
end
|
|
214
214
|
end
|
|
215
215
|
|
data/lib/httpx/selector.rb
CHANGED
|
@@ -56,7 +56,7 @@ module HTTPX
|
|
|
56
56
|
|
|
57
57
|
begin
|
|
58
58
|
select(timeout) do |c|
|
|
59
|
-
c.log(level: 2) { "[#{c.state}] selected from selector##{object_id}
|
|
59
|
+
c.log(level: 2) { "[#{c.state}] selected from selector##{object_id}#{" after #{timeout} secs" unless timeout.nil?}..." }
|
|
60
60
|
|
|
61
61
|
c.call
|
|
62
62
|
end
|
data/lib/httpx/session.rb
CHANGED
|
@@ -237,7 +237,7 @@ module HTTPX
|
|
|
237
237
|
|
|
238
238
|
return unless response && response.finished?
|
|
239
239
|
|
|
240
|
-
log(level: 2) { "response##{response.object_id} fetched" }
|
|
240
|
+
request.log(level: 2) { "response##{response.object_id} fetched" }
|
|
241
241
|
|
|
242
242
|
response
|
|
243
243
|
end
|
|
@@ -337,6 +337,8 @@ module HTTPX
|
|
|
337
337
|
end
|
|
338
338
|
end
|
|
339
339
|
|
|
340
|
+
log(level: 2) { "waiting to receive #{pending} pending requests..." }
|
|
341
|
+
|
|
340
342
|
until pending.zero? || selector.empty?
|
|
341
343
|
# loop on selector until at least one response has been received.
|
|
342
344
|
waiting = true
|
|
@@ -353,14 +355,16 @@ module HTTPX
|
|
|
353
355
|
# making the next loop cheaper (because we're dropping).
|
|
354
356
|
next unless response
|
|
355
357
|
|
|
356
|
-
request.complete!(response)
|
|
358
|
+
next unless request.complete!(response)
|
|
359
|
+
|
|
357
360
|
responses[idx] = response
|
|
358
361
|
request.on_response_arrived = nil
|
|
359
362
|
pending -= 1
|
|
360
363
|
end
|
|
361
364
|
end
|
|
362
365
|
|
|
363
|
-
raise Error, "something went wrong, responses not found
|
|
366
|
+
raise Error, "something went wrong, #{pending} responses not found " \
|
|
367
|
+
"and requests not resent" unless pending.zero?
|
|
364
368
|
|
|
365
369
|
responses
|
|
366
370
|
end
|