httpx 1.8.2 → 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_3.md +9 -0
- data/doc/release_notes/1_8_4.md +13 -0
- data/lib/httpx/callbacks.rb +2 -2
- data/lib/httpx/chainable.rb +2 -2
- data/lib/httpx/connection/http2.rb +64 -13
- data/lib/httpx/connection.rb +16 -2
- data/lib/httpx/options.rb +17 -12
- data/lib/httpx/plugins/cookies/cookie.rb +1 -1
- data/lib/httpx/plugins/cookies.rb +2 -2
- data/lib/httpx/plugins/follow_redirects.rb +7 -5
- data/lib/httpx/plugins/grpc/call.rb +4 -4
- data/lib/httpx/plugins/grpc/grpc_encoding.rb +2 -2
- data/lib/httpx/plugins/proxy/http.rb +27 -6
- data/lib/httpx/plugins/proxy.rb +4 -4
- data/lib/httpx/plugins/retries.rb +11 -1
- data/lib/httpx/plugins/server_sent_events.rb +1 -1
- data/lib/httpx/plugins/stream.rb +2 -2
- data/lib/httpx/plugins/tracing.rb +3 -3
- data/lib/httpx/request.rb +5 -2
- data/lib/httpx/resolver/multi.rb +2 -2
- data/lib/httpx/resolver/resolver.rb +2 -2
- data/lib/httpx/response/body.rb +2 -2
- data/lib/httpx/response.rb +4 -4
- data/lib/httpx/session.rb +2 -1
- data/lib/httpx/transcoder/chunker.rb +2 -2
- data/lib/httpx/transcoder/json.rb +7 -7
- data/lib/httpx/version.rb +1 -1
- data/sig/connection/http2.rbs +15 -1
- data/sig/connection.rbs +1 -0
- data/sig/plugins/proxy/http.rbs +2 -0
- data/sig/request.rbs +2 -1
- metadata +5 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: daca70f040a0f0afcc413a6eb3b2e586ae56a5405477c0045cf226aaddf5b49c
|
|
4
|
+
data.tar.gz: 53910e19b4bf264a2d3cf4aed4298de57c4c6373f868e3d9e187140c15f2ed63
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8b19e3219fb672bfbb45c4e5427c68b9f7ba715003f5b39796a2314bd8b991b58c5bc185767e303557159f04d61d84c19523f25b953b604a74753e55859defb8
|
|
7
|
+
data.tar.gz: 844bf656335bdbc5599003d6ee9724ff54b05478bfa9de37d5e251acff788154d4d7cea4b866b56c7d4c42d07b1afa8ece6982b02c5236d981843f092ee25ceb
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# 1.8.3
|
|
2
|
+
|
|
3
|
+
## Bugfixes
|
|
4
|
+
|
|
5
|
+
options print a warning instead of raising an error on unknown option
|
|
6
|
+
|
|
7
|
+
the approach introduced in the previous version, of erroring on something people have been accidentally relying on, is too strict in hindsight for a patch version. This was reverted, and instead, a deprecation warning will be printed, to give time for folks to migrate.
|
|
8
|
+
|
|
9
|
+
the format of options messages (exceptions and warnings) also slightly changes to wrap the option in quotes, for proper highlighting.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# 1.8.4
|
|
2
|
+
|
|
3
|
+
## Improvements
|
|
4
|
+
|
|
5
|
+
* send requests which were not sent, or were not processed by the server, when the server sends back a GOAWAY frame (which is deemed recoverable, like when there's no error), after forcing a reconnection (instead of bubbling up the GOAWAY error for all of them).
|
|
6
|
+
|
|
7
|
+
## Bugfixes
|
|
8
|
+
|
|
9
|
+
* `:retries` plugin: also retry requests which failed due to a RST_STREAM frame with REFUSED_STREAM error code.
|
|
10
|
+
* `:retries` plugin: resend requests which were piped to a closed connection internally.
|
|
11
|
+
* `:proxy` plugin: fix issue related with reconnecting inactive connections shut down by the peer, when used with the `:persistent` plugin, where the CONNECT request wasn't being sent to establish the tunnel.
|
|
12
|
+
* fixed compatibility issue starting with the `json` gem 3.0.0, when calling `HTTPX::Response#json`.
|
|
13
|
+
* avoid bookkepping issue in session, where the `on_response_arrived` on a request may be called more than once (which it can happen in the `:follow_redirects` plugin, where a request may have completed due to an error, while the request is waiting to be resent due to `retry-after`).
|
data/lib/httpx/callbacks.rb
CHANGED
|
@@ -14,9 +14,9 @@ module HTTPX
|
|
|
14
14
|
end
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
def emit(type,
|
|
17
|
+
def emit(type, ...)
|
|
18
18
|
log { "emit #{type.inspect} callbacks" } if respond_to?(:log)
|
|
19
|
-
callbacks(type).delete_if { |pr| :delete == pr.call(
|
|
19
|
+
callbacks(type).delete_if { |pr| :delete == pr.call(...) } # rubocop:disable Style/YodaCondition
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def callbacks_for?(type)
|
data/lib/httpx/chainable.rb
CHANGED
|
@@ -13,8 +13,8 @@ module HTTPX
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
# delegates to the default session (see HTTPX::Session#request).
|
|
16
|
-
def request(
|
|
17
|
-
branch(default_options).request(
|
|
16
|
+
def request(...)
|
|
17
|
+
branch(default_options).request(...)
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def accept(type)
|
|
@@ -23,9 +23,29 @@ module HTTPX
|
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
class GoawayError < Error
|
|
26
|
-
|
|
26
|
+
UNRECOVERABLE_ERRORS = %i[settings_timeout inadequate_security].freeze
|
|
27
|
+
|
|
28
|
+
def initialize(code, last_stream_id)
|
|
29
|
+
@code = code
|
|
30
|
+
@last_stream_id = last_stream_id
|
|
27
31
|
super(0, code)
|
|
28
32
|
end
|
|
33
|
+
|
|
34
|
+
def last_stream_id
|
|
35
|
+
return Float::INFINITY if unrecoverable?
|
|
36
|
+
|
|
37
|
+
@last_stream_id
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def unrecoverable?
|
|
41
|
+
UNRECOVERABLE_ERRORS.include?(@code)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
class RstStreamError < Error
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
class RefusedStreamError < RstStreamError
|
|
29
49
|
end
|
|
30
50
|
|
|
31
51
|
attr_reader :streams, :pending
|
|
@@ -149,18 +169,43 @@ module HTTPX
|
|
|
149
169
|
end
|
|
150
170
|
|
|
151
171
|
def handle_error(ex, request = nil)
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
172
|
+
last_stream_id = Float::INFINITY
|
|
173
|
+
case ex
|
|
174
|
+
when OperationTimeoutError
|
|
175
|
+
if !@handshake_completed && @connection.state != :closed
|
|
176
|
+
@connection.goaway(:settings_timeout, "closing due to settings timeout")
|
|
177
|
+
emit(:close_handshake)
|
|
178
|
+
settings_ex = SettingsTimeoutError.new(ex.timeout, ex.message)
|
|
179
|
+
settings_ex.set_backtrace(ex.backtrace)
|
|
180
|
+
ex = settings_ex
|
|
181
|
+
end
|
|
182
|
+
when GoawayError
|
|
183
|
+
last_stream_id = ex.last_stream_id
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
inflight_unprocessed_requests = [] #: Array[Request]
|
|
187
|
+
|
|
188
|
+
while (req, stream = @streams.shift)
|
|
160
189
|
next if request && request == req
|
|
161
190
|
|
|
191
|
+
if stream.id > last_stream_id
|
|
192
|
+
req.transition(:idle)
|
|
193
|
+
# unprocessed request
|
|
194
|
+
inflight_unprocessed_requests << req
|
|
195
|
+
|
|
196
|
+
next
|
|
197
|
+
end
|
|
198
|
+
|
|
162
199
|
emit(:error, req, ex)
|
|
163
200
|
end
|
|
201
|
+
|
|
202
|
+
if ex.is_a?(GoawayError) && !ex.unrecoverable?
|
|
203
|
+
# resend unprocessed requests on a different connection
|
|
204
|
+
@pending.unshift(*inflight_unprocessed_requests) if inflight_unprocessed_requests.any?
|
|
205
|
+
emit(:exhausted, ex) if @pending.any?
|
|
206
|
+
return
|
|
207
|
+
end
|
|
208
|
+
|
|
164
209
|
while (req = @pending.shift)
|
|
165
210
|
next if request && request == req
|
|
166
211
|
|
|
@@ -395,7 +440,14 @@ module HTTPX
|
|
|
395
440
|
when :http_1_1_required
|
|
396
441
|
emit(:error, request, error)
|
|
397
442
|
else
|
|
398
|
-
ex =
|
|
443
|
+
ex =
|
|
444
|
+
case error
|
|
445
|
+
when :refused_stream
|
|
446
|
+
RefusedStreamError.new(stream.id, error)
|
|
447
|
+
else
|
|
448
|
+
RstStreamError.new(stream.id, error)
|
|
449
|
+
end
|
|
450
|
+
|
|
399
451
|
ex.set_backtrace(caller)
|
|
400
452
|
response = ErrorResponse.new(request, ex)
|
|
401
453
|
request.response = response
|
|
@@ -432,7 +484,7 @@ module HTTPX
|
|
|
432
484
|
send_pending
|
|
433
485
|
end
|
|
434
486
|
|
|
435
|
-
def on_close(
|
|
487
|
+
def on_close(last_stream_id, error, _payload)
|
|
436
488
|
is_connection_closed = @connection.closed?
|
|
437
489
|
if error
|
|
438
490
|
@buffer.clear if is_connection_closed
|
|
@@ -442,12 +494,11 @@ module HTTPX
|
|
|
442
494
|
emit(:error, request, error)
|
|
443
495
|
end
|
|
444
496
|
else
|
|
445
|
-
ex = GoawayError.new(error)
|
|
497
|
+
ex = GoawayError.new(error, last_stream_id)
|
|
446
498
|
ex.set_backtrace(caller)
|
|
447
499
|
|
|
448
500
|
handle_error(ex)
|
|
449
501
|
teardown
|
|
450
|
-
|
|
451
502
|
end
|
|
452
503
|
end
|
|
453
504
|
return unless is_connection_closed && @streams.empty?
|
data/lib/httpx/connection.rb
CHANGED
|
@@ -51,7 +51,10 @@ module HTTPX
|
|
|
51
51
|
@callbacks = @ping_timer = @family =
|
|
52
52
|
@io = @ssl_session = @timeout = @connected_at = @response_received_at = nil
|
|
53
53
|
|
|
54
|
-
@exhausted = @cloned = @main_sibling =
|
|
54
|
+
@exhausted = @cloned = @main_sibling =
|
|
55
|
+
# variable used to gate against a potential endless loop where the peer continuously closes the connection with
|
|
56
|
+
# GOAWAY frames without ever processing a request.
|
|
57
|
+
@previously_exhausted_with_error = false
|
|
55
58
|
|
|
56
59
|
@options = Options.new(options)
|
|
57
60
|
@type = initialize_type(uri, @options)
|
|
@@ -711,6 +714,7 @@ module HTTPX
|
|
|
711
714
|
AltSvc.emit(request, response) do |alt_origin, origin, alt_params|
|
|
712
715
|
build_altsvc_connection(alt_origin, origin, alt_params)
|
|
713
716
|
end
|
|
717
|
+
@previously_exhausted_with_error = false
|
|
714
718
|
@response_received_at = Utils.now
|
|
715
719
|
@no_more_requests_counter = 0
|
|
716
720
|
@inflight -= 1
|
|
@@ -726,9 +730,19 @@ module HTTPX
|
|
|
726
730
|
parser.on(:promise) do |request, stream|
|
|
727
731
|
request.emit(:promise, parser, stream)
|
|
728
732
|
end
|
|
729
|
-
parser.on(:exhausted) do
|
|
733
|
+
parser.on(:exhausted) do |error|
|
|
730
734
|
enqueue_pending_requests_from_parser(parser)
|
|
731
735
|
|
|
736
|
+
if error
|
|
737
|
+
if @previously_exhausted_with_error
|
|
738
|
+
@previously_exhausted_with_error = false
|
|
739
|
+
on_error(error)
|
|
740
|
+
next
|
|
741
|
+
else
|
|
742
|
+
@previously_exhausted_with_error = true
|
|
743
|
+
end
|
|
744
|
+
end
|
|
745
|
+
|
|
732
746
|
@exhausted = true
|
|
733
747
|
parser.close
|
|
734
748
|
|
data/lib/httpx/options.rb
CHANGED
|
@@ -133,12 +133,12 @@ module HTTPX
|
|
|
133
133
|
when Options
|
|
134
134
|
unknown_options = options.class.options_names - options_names
|
|
135
135
|
|
|
136
|
-
raise Error, "unknown option:
|
|
136
|
+
raise Error, "unknown option: `:#{unknown_options.first}`" unless unknown_options.empty?
|
|
137
137
|
|
|
138
138
|
DEFAULT_OPTIONS.merge(options)
|
|
139
139
|
else
|
|
140
140
|
options.each_key do |k|
|
|
141
|
-
raise Error, "unknown option:
|
|
141
|
+
raise Error, "unknown option: `:#{k}`" unless options_names.include?(k)
|
|
142
142
|
end
|
|
143
143
|
|
|
144
144
|
options.empty? ? DEFAULT_OPTIONS : DEFAULT_OPTIONS.merge(options)
|
|
@@ -184,7 +184,7 @@ module HTTPX
|
|
|
184
184
|
cache_type.respond_to?(:get) &&
|
|
185
185
|
cache_type.respond_to?(:set) &&
|
|
186
186
|
cache_type.respond_to?(:evict)
|
|
187
|
-
raise TypeError, "
|
|
187
|
+
raise TypeError, "`:resolver_cache` must be a compatible resolver cache and implement `#get`, `#set` and `#evict`"
|
|
188
188
|
end
|
|
189
189
|
|
|
190
190
|
cache_type #: Object & Resolver::_Cache
|
|
@@ -263,9 +263,14 @@ module HTTPX
|
|
|
263
263
|
return self if other_opts.empty?
|
|
264
264
|
|
|
265
265
|
return self if other_opts.all? do |opt, v|
|
|
266
|
-
|
|
266
|
+
unless respond_to?(opt)
|
|
267
|
+
# TODO: do this instead in a major or minor version bump
|
|
268
|
+
# raise Error, "unknown option: #{opt}" unless respond_to?(opt)
|
|
269
|
+
warn "DEPRECATION WARNING: unknown option: `:#{opt}`. an exception will be raised in a future version."
|
|
270
|
+
next(true)
|
|
271
|
+
end
|
|
267
272
|
|
|
268
|
-
public_send(opt) == v
|
|
273
|
+
!respond_to?(opt) || public_send(opt) == v
|
|
269
274
|
end
|
|
270
275
|
end
|
|
271
276
|
|
|
@@ -404,7 +409,7 @@ module HTTPX
|
|
|
404
409
|
# converts +v+ into an Integer before setting the +#{option}+ option.
|
|
405
410
|
private def option_#{option}(value) # private def option_max_requests(v)
|
|
406
411
|
value = Integer(value) unless value.respond_to?(:infinite?) && value.infinite?
|
|
407
|
-
raise TypeError, "
|
|
412
|
+
raise TypeError, "`:#{option}` must be positive" unless value.positive? # raise TypeError, ":max_requests must be positive" unless value.positive?
|
|
408
413
|
|
|
409
414
|
value
|
|
410
415
|
end
|
|
@@ -456,11 +461,11 @@ module HTTPX
|
|
|
456
461
|
|
|
457
462
|
# Validate keys and values
|
|
458
463
|
timeout_hash.each do |key, val|
|
|
459
|
-
raise TypeError, "invalid timeout:
|
|
464
|
+
raise TypeError, "invalid timeout: `:#{key}`" unless default_timeouts.key?(key)
|
|
460
465
|
|
|
461
466
|
next if val.nil?
|
|
462
467
|
|
|
463
|
-
raise TypeError, "
|
|
468
|
+
raise TypeError, "`:#{key}` must be numeric" unless val.is_a?(Numeric)
|
|
464
469
|
end
|
|
465
470
|
|
|
466
471
|
timeout_hash
|
|
@@ -490,15 +495,15 @@ module HTTPX
|
|
|
490
495
|
when Symbol
|
|
491
496
|
meth = :"resolver_#{resolver_type}_class"
|
|
492
497
|
|
|
493
|
-
raise TypeError, "
|
|
498
|
+
raise TypeError, "`:resolver_class` must be a supported type" unless respond_to?(meth)
|
|
494
499
|
|
|
495
500
|
resolver_type
|
|
496
501
|
when Class
|
|
497
|
-
raise TypeError, "
|
|
502
|
+
raise TypeError, "`:resolver_class` must be a subclass of `#{Resolver::Resolver}`" unless resolver_type < Resolver::Resolver
|
|
498
503
|
|
|
499
504
|
resolver_type
|
|
500
505
|
else
|
|
501
|
-
raise TypeError, "
|
|
506
|
+
raise TypeError, "`:resolver_class` must be a supported type"
|
|
502
507
|
end
|
|
503
508
|
end
|
|
504
509
|
|
|
@@ -513,7 +518,7 @@ module HTTPX
|
|
|
513
518
|
cache_type.respond_to?(:get) &&
|
|
514
519
|
cache_type.respond_to?(:set) &&
|
|
515
520
|
cache_type.respond_to?(:evict)
|
|
516
|
-
raise TypeError, "
|
|
521
|
+
raise TypeError, "`:resolver_cache` must be a compatible resolver cache and implement `#resolve`, `#get`, `#set` and `#evict`"
|
|
517
522
|
end
|
|
518
523
|
end
|
|
519
524
|
|
|
@@ -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
|
|
|
@@ -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
|
|
@@ -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
|
|
@@ -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)
|
|
@@ -31,11 +31,21 @@ module HTTPX
|
|
|
31
31
|
TLSError,
|
|
32
32
|
Zlib::BufError,
|
|
33
33
|
PingTimeoutError,
|
|
34
|
+
# HTTP/2 GOAWAY or RST_STREAM errors guaranteed to be retriable for all
|
|
35
|
+
# types of requests.
|
|
36
|
+
Connection::HTTP2::RefusedStreamError,
|
|
34
37
|
Connection::HTTP2::GoawayError,
|
|
35
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,
|
|
36
43
|
].freeze
|
|
37
44
|
|
|
38
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,
|
|
39
49
|
Parser::Error,
|
|
40
50
|
TimeoutError,
|
|
41
51
|
]).freeze
|
|
@@ -256,7 +266,7 @@ module HTTPX
|
|
|
256
266
|
attr_writer :partial_response
|
|
257
267
|
|
|
258
268
|
# initializes the request instance, sets the number of retries for the request.
|
|
259
|
-
def initialize(*
|
|
269
|
+
def initialize(*)
|
|
260
270
|
super
|
|
261
271
|
@retries = @options.max_retries
|
|
262
272
|
@partial_response = nil
|
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
|
data/lib/httpx/request.rb
CHANGED
|
@@ -107,7 +107,7 @@ module HTTPX
|
|
|
107
107
|
@connection = @response =
|
|
108
108
|
@drainer = @peer_address = @callbacks =
|
|
109
109
|
@informational_status = @on_response_arrived = nil
|
|
110
|
-
@ping = @started = false
|
|
110
|
+
@ping = @started = @complete = false
|
|
111
111
|
@persistent = @options.persistent
|
|
112
112
|
@active_timeouts = []
|
|
113
113
|
end
|
|
@@ -121,8 +121,11 @@ module HTTPX
|
|
|
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
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
|
@@ -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/session.rb
CHANGED
|
@@ -355,7 +355,8 @@ module HTTPX
|
|
|
355
355
|
# making the next loop cheaper (because we're dropping).
|
|
356
356
|
next unless response
|
|
357
357
|
|
|
358
|
-
request.complete!(response)
|
|
358
|
+
next unless request.complete!(response)
|
|
359
|
+
|
|
359
360
|
responses[idx] = response
|
|
360
361
|
request.on_response_arrived = nil
|
|
361
362
|
pending -= 1
|
|
@@ -53,18 +53,18 @@ module HTTPX::Transcoder
|
|
|
53
53
|
|
|
54
54
|
# rubocop:disable-next Style/SingleLineMethods
|
|
55
55
|
if defined?(MultiJson)
|
|
56
|
-
def json_load(
|
|
57
|
-
def json_dump(
|
|
56
|
+
def json_load(...); MultiJson.load(...); end
|
|
57
|
+
def json_dump(...); MultiJson.dump(...); end
|
|
58
58
|
elsif defined?(Oj)
|
|
59
|
-
def json_load(response,
|
|
59
|
+
def json_load(response, ...); Oj.load(response.to_s, ...); end
|
|
60
60
|
def json_dump(obj, options = {}); Oj.dump(obj, { mode: :compat }.merge(options)); end
|
|
61
61
|
elsif defined?(Yajl)
|
|
62
|
-
def json_load(response,
|
|
63
|
-
def json_dump(
|
|
62
|
+
def json_load(response, ...); Yajl::Parser.new(...).parse(response.to_s); end
|
|
63
|
+
def json_dump(...); Yajl::Encoder.encode(...); end
|
|
64
64
|
else
|
|
65
65
|
require "json"
|
|
66
|
-
def json_load(
|
|
67
|
-
def json_dump(
|
|
66
|
+
def json_load(...); ::JSON.parse(...); end
|
|
67
|
+
def json_dump(...); ::JSON.generate(...); end
|
|
68
68
|
end
|
|
69
69
|
end
|
|
70
70
|
end
|
data/lib/httpx/version.rb
CHANGED
data/sig/connection/http2.rbs
CHANGED
|
@@ -116,11 +116,25 @@ module HTTPX
|
|
|
116
116
|
end
|
|
117
117
|
|
|
118
118
|
class GoawayError < Error
|
|
119
|
-
|
|
119
|
+
UNRECOVERABLE_ERRORS: Array[Symbol]
|
|
120
|
+
|
|
121
|
+
@code: Symbol
|
|
122
|
+
|
|
123
|
+
def initialize: (Symbol code, Integer last_stream_id) -> void
|
|
124
|
+
|
|
125
|
+
def last_stream_id: () -> Numeric
|
|
126
|
+
|
|
127
|
+
def unrecoverable?: () -> bool
|
|
120
128
|
end
|
|
121
129
|
|
|
122
130
|
class PingError < Error
|
|
123
131
|
def initialize: () -> void
|
|
124
132
|
end
|
|
133
|
+
|
|
134
|
+
class RstStreamError < Error
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
class RefusedStreamError < Error
|
|
138
|
+
end
|
|
125
139
|
end
|
|
126
140
|
end
|
data/sig/connection.rbs
CHANGED
data/sig/plugins/proxy/http.rbs
CHANGED
data/sig/request.rbs
CHANGED
|
@@ -32,10 +32,11 @@ module HTTPX
|
|
|
32
32
|
@query: String?
|
|
33
33
|
@drainer: Enumerator[String, void]?
|
|
34
34
|
@started: bool
|
|
35
|
+
@complete: bool
|
|
35
36
|
|
|
36
37
|
def initialize: (Symbol | String verb, generic_uri uri, Options options, ?request_params params) -> untyped
|
|
37
38
|
|
|
38
|
-
def complete!: (?response response) ->
|
|
39
|
+
def complete!: (?response response) -> bool
|
|
39
40
|
|
|
40
41
|
def started?: () -> bool
|
|
41
42
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: httpx
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.8.
|
|
4
|
+
version: 1.8.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tiago Cardoso
|
|
@@ -171,6 +171,8 @@ extra_rdoc_files:
|
|
|
171
171
|
- doc/release_notes/1_8_0.md
|
|
172
172
|
- doc/release_notes/1_8_1.md
|
|
173
173
|
- doc/release_notes/1_8_2.md
|
|
174
|
+
- doc/release_notes/1_8_3.md
|
|
175
|
+
- doc/release_notes/1_8_4.md
|
|
174
176
|
files:
|
|
175
177
|
- LICENSE.txt
|
|
176
178
|
- README.md
|
|
@@ -314,6 +316,8 @@ files:
|
|
|
314
316
|
- doc/release_notes/1_8_0.md
|
|
315
317
|
- doc/release_notes/1_8_1.md
|
|
316
318
|
- doc/release_notes/1_8_2.md
|
|
319
|
+
- doc/release_notes/1_8_3.md
|
|
320
|
+
- doc/release_notes/1_8_4.md
|
|
317
321
|
- lib/httpx.rb
|
|
318
322
|
- lib/httpx/adapters/datadog.rb
|
|
319
323
|
- lib/httpx/adapters/faraday.rb
|