httpx 1.6.2 → 1.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/doc/release_notes/0_11_0.md +3 -3
- data/doc/release_notes/1_6_3.md +47 -0
- data/doc/release_notes/1_7_0.md +149 -0
- data/lib/httpx/adapters/datadog.rb +1 -1
- data/lib/httpx/adapters/faraday.rb +1 -1
- data/lib/httpx/adapters/sentry.rb +1 -1
- data/lib/httpx/altsvc.rb +3 -1
- data/lib/httpx/connection/http1.rb +14 -15
- data/lib/httpx/connection/http2.rb +16 -15
- data/lib/httpx/connection.rb +118 -110
- data/lib/httpx/domain_name.rb +1 -1
- data/lib/httpx/extensions.rb +0 -14
- data/lib/httpx/headers.rb +2 -2
- data/lib/httpx/io/ssl.rb +1 -1
- data/lib/httpx/loggable.rb +14 -2
- data/lib/httpx/options.rb +60 -17
- data/lib/httpx/plugins/auth/digest.rb +44 -4
- data/lib/httpx/plugins/auth.rb +87 -4
- data/lib/httpx/plugins/aws_sdk_authentication.rb +0 -1
- data/lib/httpx/plugins/callbacks.rb +15 -1
- data/lib/httpx/plugins/cookies/cookie.rb +1 -0
- data/lib/httpx/plugins/digest_auth.rb +4 -5
- data/lib/httpx/plugins/fiber_concurrency.rb +16 -1
- data/lib/httpx/plugins/grpc/grpc_encoding.rb +1 -1
- data/lib/httpx/plugins/grpc.rb +2 -2
- data/lib/httpx/plugins/internal_telemetry.rb +1 -1
- data/lib/httpx/plugins/ntlm_auth.rb +5 -3
- data/lib/httpx/plugins/oauth.rb +162 -56
- data/lib/httpx/plugins/proxy/http.rb +37 -9
- data/lib/httpx/plugins/rate_limiter.rb +2 -2
- data/lib/httpx/plugins/response_cache/file_store.rb +1 -0
- data/lib/httpx/plugins/response_cache.rb +16 -9
- data/lib/httpx/plugins/retries.rb +55 -16
- data/lib/httpx/plugins/ssrf_filter.rb +1 -1
- data/lib/httpx/plugins/stream.rb +59 -8
- data/lib/httpx/plugins/stream_bidi.rb +87 -22
- data/lib/httpx/pool.rb +65 -21
- data/lib/httpx/request.rb +13 -14
- data/lib/httpx/resolver/https.rb +100 -34
- data/lib/httpx/resolver/multi.rb +12 -27
- data/lib/httpx/resolver/native.rb +68 -38
- data/lib/httpx/resolver/resolver.rb +46 -29
- data/lib/httpx/resolver/system.rb +63 -39
- data/lib/httpx/resolver.rb +97 -29
- data/lib/httpx/response/body.rb +2 -0
- data/lib/httpx/response.rb +22 -6
- data/lib/httpx/selector.rb +44 -20
- data/lib/httpx/session.rb +23 -33
- data/lib/httpx/transcoder/body.rb +1 -1
- data/lib/httpx/transcoder/deflate.rb +13 -8
- data/lib/httpx/transcoder/json.rb +1 -1
- data/lib/httpx/transcoder/multipart/decoder.rb +4 -4
- data/lib/httpx/transcoder/multipart/encoder.rb +1 -1
- data/lib/httpx/transcoder/multipart.rb +16 -8
- data/lib/httpx/transcoder/utils/body_reader.rb +1 -2
- data/lib/httpx/transcoder/utils/deflater.rb +1 -2
- data/lib/httpx/transcoder.rb +4 -6
- data/lib/httpx/version.rb +1 -1
- data/sig/altsvc.rbs +3 -0
- data/sig/chainable.rbs +3 -3
- data/sig/connection.rbs +13 -6
- data/sig/loggable.rbs +5 -1
- data/sig/options.rbs +6 -2
- data/sig/plugins/auth/digest.rbs +6 -0
- data/sig/plugins/auth.rbs +28 -4
- data/sig/plugins/basic_auth.rbs +3 -3
- data/sig/plugins/callbacks.rbs +3 -0
- data/sig/plugins/digest_auth.rbs +2 -4
- data/sig/plugins/fiber_concurrency.rbs +6 -0
- data/sig/plugins/ntlm_auth.rbs +2 -2
- data/sig/plugins/oauth.rbs +46 -15
- data/sig/plugins/rate_limiter.rbs +1 -1
- data/sig/plugins/response_cache/file_store.rbs +2 -0
- data/sig/plugins/response_cache.rbs +4 -0
- data/sig/plugins/retries.rbs +8 -2
- data/sig/plugins/stream.rbs +13 -3
- data/sig/plugins/stream_bidi.rbs +5 -7
- data/sig/pool.rbs +1 -1
- data/sig/resolver/https.rbs +7 -0
- data/sig/resolver/multi.rbs +2 -9
- data/sig/resolver/native.rbs +1 -1
- data/sig/resolver/resolver.rbs +9 -8
- data/sig/resolver/system.rbs +4 -2
- data/sig/resolver.rbs +12 -3
- data/sig/response.rbs +3 -0
- data/sig/selector.rbs +2 -0
- data/sig/session.rbs +8 -8
- data/sig/transcoder/multipart.rbs +4 -2
- data/sig/transcoder.rbs +5 -1
- metadata +5 -1
data/lib/httpx/connection.rb
CHANGED
|
@@ -34,8 +34,6 @@ module HTTPX
|
|
|
34
34
|
|
|
35
35
|
using URIExtensions
|
|
36
36
|
|
|
37
|
-
def_delegator :@io, :closed?
|
|
38
|
-
|
|
39
37
|
def_delegator :@write_buffer, :empty?
|
|
40
38
|
|
|
41
39
|
attr_reader :type, :io, :origin, :origins, :state, :pending, :options, :ssl_session, :sibling
|
|
@@ -48,9 +46,9 @@ module HTTPX
|
|
|
48
46
|
|
|
49
47
|
def initialize(uri, options)
|
|
50
48
|
@current_session = @current_selector =
|
|
51
|
-
@parser = @sibling = @coalesced_connection =
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
@parser = @sibling = @coalesced_connection = @altsvc_connection =
|
|
50
|
+
@family = @io = @ssl_session = @timeout =
|
|
51
|
+
@connected_at = @response_received_at = nil
|
|
54
52
|
|
|
55
53
|
@exhausted = @cloned = @main_sibling = false
|
|
56
54
|
|
|
@@ -65,7 +63,6 @@ module HTTPX
|
|
|
65
63
|
@inflight = 0
|
|
66
64
|
@keep_alive_timeout = @options.timeout[:keep_alive_timeout]
|
|
67
65
|
|
|
68
|
-
on(:error, &method(:on_error))
|
|
69
66
|
if @options.io
|
|
70
67
|
# if there's an already open IO, get its
|
|
71
68
|
# peer address, and force-initiate the parser
|
|
@@ -75,32 +72,6 @@ module HTTPX
|
|
|
75
72
|
else
|
|
76
73
|
transition(:idle)
|
|
77
74
|
end
|
|
78
|
-
on(:close) do
|
|
79
|
-
next if @exhausted # it'll reset
|
|
80
|
-
|
|
81
|
-
# may be called after ":close" above, so after the connection has been checked back in.
|
|
82
|
-
# next unless @current_session
|
|
83
|
-
|
|
84
|
-
next unless @current_session
|
|
85
|
-
|
|
86
|
-
@current_session.deselect_connection(self, @current_selector, @cloned)
|
|
87
|
-
end
|
|
88
|
-
on(:terminate) do
|
|
89
|
-
next if @exhausted # it'll reset
|
|
90
|
-
|
|
91
|
-
current_session = @current_session
|
|
92
|
-
current_selector = @current_selector
|
|
93
|
-
|
|
94
|
-
# may be called after ":close" above, so after the connection has been checked back in.
|
|
95
|
-
next unless current_session && current_selector
|
|
96
|
-
|
|
97
|
-
current_session.deselect_connection(self, current_selector)
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
on(:altsvc) do |alt_origin, origin, alt_params|
|
|
101
|
-
build_altsvc_connection(alt_origin, origin, alt_params)
|
|
102
|
-
end
|
|
103
|
-
|
|
104
75
|
self.addresses = @options.addresses if @options.addresses
|
|
105
76
|
end
|
|
106
77
|
|
|
@@ -129,14 +100,13 @@ module HTTPX
|
|
|
129
100
|
def match?(uri, options)
|
|
130
101
|
return false if !used? && (@state == :closing || @state == :closed)
|
|
131
102
|
|
|
132
|
-
(
|
|
133
|
-
@origins.include?(uri.origin) &&
|
|
103
|
+
@origins.include?(uri.origin) &&
|
|
134
104
|
# if there is more than one origin to match, it means that this connection
|
|
135
105
|
# was the result of coalescing. To prevent blind trust in the case where the
|
|
136
106
|
# origin came from an ORIGIN frame, we're going to verify the hostname with the
|
|
137
107
|
# SSL certificate
|
|
138
|
-
(@origins.size == 1 || @origin == uri.origin || (@io.is_a?(SSL) && @io.verify_hostname(uri.host)))
|
|
139
|
-
|
|
108
|
+
(@origins.size == 1 || @origin == uri.origin || (@io.is_a?(SSL) && @io.verify_hostname(uri.host))) &&
|
|
109
|
+
@options == options
|
|
140
110
|
end
|
|
141
111
|
|
|
142
112
|
def mergeable?(connection)
|
|
@@ -158,6 +128,10 @@ module HTTPX
|
|
|
158
128
|
connection.merge(self)
|
|
159
129
|
end
|
|
160
130
|
|
|
131
|
+
def coalesced?
|
|
132
|
+
@coalesced_connection
|
|
133
|
+
end
|
|
134
|
+
|
|
161
135
|
# coalescable connections need to be mergeable!
|
|
162
136
|
# but internally, #mergeable? is called before #coalescable?
|
|
163
137
|
def coalescable?(connection)
|
|
@@ -171,10 +145,6 @@ module HTTPX
|
|
|
171
145
|
end
|
|
172
146
|
end
|
|
173
147
|
|
|
174
|
-
def create_idle(options = {})
|
|
175
|
-
self.class.new(@origin, @options.merge(options))
|
|
176
|
-
end
|
|
177
|
-
|
|
178
148
|
def merge(connection)
|
|
179
149
|
@origins |= connection.instance_variable_get(:@origins)
|
|
180
150
|
if @ssl_session.nil? && connection.ssl_session
|
|
@@ -231,7 +201,7 @@ module HTTPX
|
|
|
231
201
|
|
|
232
202
|
nil
|
|
233
203
|
rescue StandardError => e
|
|
234
|
-
|
|
204
|
+
on_error(e)
|
|
235
205
|
nil
|
|
236
206
|
end
|
|
237
207
|
|
|
@@ -259,7 +229,9 @@ module HTTPX
|
|
|
259
229
|
nil
|
|
260
230
|
rescue StandardError => e
|
|
261
231
|
@write_buffer.clear
|
|
262
|
-
|
|
232
|
+
on_error(e)
|
|
233
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
234
|
+
force_close(true)
|
|
263
235
|
raise e
|
|
264
236
|
end
|
|
265
237
|
|
|
@@ -273,7 +245,7 @@ module HTTPX
|
|
|
273
245
|
case @state
|
|
274
246
|
when :idle
|
|
275
247
|
purge_after_closed
|
|
276
|
-
|
|
248
|
+
disconnect
|
|
277
249
|
when :closed
|
|
278
250
|
@connected_at = nil
|
|
279
251
|
end
|
|
@@ -281,6 +253,23 @@ module HTTPX
|
|
|
281
253
|
close
|
|
282
254
|
end
|
|
283
255
|
|
|
256
|
+
# bypasses state machine rules while setting the connection in the
|
|
257
|
+
# :closed state.
|
|
258
|
+
def force_close(delete_pending = false)
|
|
259
|
+
if delete_pending
|
|
260
|
+
@pending.clear
|
|
261
|
+
elsif (parser = @parser)
|
|
262
|
+
enqueue_pending_requests_from_parser(parser)
|
|
263
|
+
end
|
|
264
|
+
return if @state == :closed
|
|
265
|
+
|
|
266
|
+
@state = :closed
|
|
267
|
+
@write_buffer.clear
|
|
268
|
+
purge_after_closed
|
|
269
|
+
disconnect
|
|
270
|
+
emit(:force_closed, delete_pending)
|
|
271
|
+
end
|
|
272
|
+
|
|
284
273
|
# bypasses the state machine to force closing of connections still connecting.
|
|
285
274
|
# **only** used for Happy Eyeballs v2.
|
|
286
275
|
def force_reset(cloned = false)
|
|
@@ -368,18 +357,40 @@ module HTTPX
|
|
|
368
357
|
end
|
|
369
358
|
|
|
370
359
|
def handle_connect_error(error)
|
|
371
|
-
return
|
|
360
|
+
return on_error(error) unless @sibling && @sibling.connecting?
|
|
372
361
|
|
|
373
362
|
@sibling.merge(self)
|
|
374
363
|
|
|
375
364
|
force_reset(true)
|
|
376
365
|
end
|
|
377
366
|
|
|
367
|
+
# disconnects from the current session it's attached to
|
|
378
368
|
def disconnect
|
|
379
|
-
return
|
|
369
|
+
return if @exhausted # it'll reset
|
|
370
|
+
|
|
371
|
+
return unless (current_session = @current_session) && (current_selector = @current_selector)
|
|
380
372
|
|
|
381
|
-
emit(:close)
|
|
382
373
|
@current_session = @current_selector = nil
|
|
374
|
+
|
|
375
|
+
current_session.deselect_connection(self, current_selector, @cloned)
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
def on_error(error, request = nil)
|
|
379
|
+
if error.is_a?(OperationTimeoutError)
|
|
380
|
+
|
|
381
|
+
# inactive connections do not contribute to the select loop, therefore
|
|
382
|
+
# they should not fail due to such errors.
|
|
383
|
+
return if @state == :inactive
|
|
384
|
+
|
|
385
|
+
if @timeout
|
|
386
|
+
@timeout -= error.timeout
|
|
387
|
+
return unless @timeout <= 0
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
error = error.to_connection_error if connecting?
|
|
391
|
+
end
|
|
392
|
+
handle_error(error, request)
|
|
393
|
+
reset
|
|
383
394
|
end
|
|
384
395
|
|
|
385
396
|
# :nocov:
|
|
@@ -417,7 +428,11 @@ module HTTPX
|
|
|
417
428
|
# * the number of pending requests
|
|
418
429
|
# * whether the write buffer has bytes (i.e. for close handshake)
|
|
419
430
|
if @pending.empty? && @inflight.zero? && @write_buffer.empty?
|
|
420
|
-
log(level: 3) { "NO MORE REQUESTS..." }
|
|
431
|
+
log(level: 3) { "NO MORE REQUESTS..." } if @parser && @parser.pending.any?
|
|
432
|
+
|
|
433
|
+
# terminate if an altsvc connection has been established
|
|
434
|
+
terminate if @altsvc_connection
|
|
435
|
+
|
|
421
436
|
return
|
|
422
437
|
end
|
|
423
438
|
|
|
@@ -462,7 +477,14 @@ module HTTPX
|
|
|
462
477
|
break if @state == :closing || @state == :closed
|
|
463
478
|
|
|
464
479
|
# exit #consume altogether if all outstanding requests have been dealt with
|
|
465
|
-
|
|
480
|
+
if @pending.empty? && @inflight.zero? && @write_buffer.empty? # rubocop:disable Style/Next
|
|
481
|
+
log(level: 3) { "NO MORE REQUESTS..." } if @parser && @parser.pending.any?
|
|
482
|
+
|
|
483
|
+
# terminate if an altsvc connection has been established
|
|
484
|
+
terminate if @altsvc_connection
|
|
485
|
+
|
|
486
|
+
return
|
|
487
|
+
end
|
|
466
488
|
end unless ((ints = interests).nil? || ints == :w || @state == :closing) && !epiped
|
|
467
489
|
|
|
468
490
|
#
|
|
@@ -555,6 +577,17 @@ module HTTPX
|
|
|
555
577
|
request.ping!
|
|
556
578
|
end
|
|
557
579
|
|
|
580
|
+
def enqueue_pending_requests_from_parser(parser)
|
|
581
|
+
parser_pending_requests = parser.pending
|
|
582
|
+
|
|
583
|
+
return if parser_pending_requests.empty?
|
|
584
|
+
|
|
585
|
+
# the connection will be reused, so parser requests must come
|
|
586
|
+
# back to the pending list before the parser is reset.
|
|
587
|
+
@inflight -= parser_pending_requests.size
|
|
588
|
+
@pending.unshift(*parser_pending_requests)
|
|
589
|
+
end
|
|
590
|
+
|
|
558
591
|
def build_parser(protocol = @io.protocol)
|
|
559
592
|
parser = parser_type(protocol).new(@write_buffer, @options)
|
|
560
593
|
set_parser_callbacks(parser)
|
|
@@ -564,7 +597,7 @@ module HTTPX
|
|
|
564
597
|
def set_parser_callbacks(parser)
|
|
565
598
|
parser.on(:response) do |request, response|
|
|
566
599
|
AltSvc.emit(request, response) do |alt_origin, origin, alt_params|
|
|
567
|
-
|
|
600
|
+
build_altsvc_connection(alt_origin, origin, alt_params)
|
|
568
601
|
end
|
|
569
602
|
@response_received_at = Utils.now
|
|
570
603
|
@inflight -= 1
|
|
@@ -572,7 +605,7 @@ module HTTPX
|
|
|
572
605
|
request.emit(:response, response)
|
|
573
606
|
end
|
|
574
607
|
parser.on(:altsvc) do |alt_origin, origin, alt_params|
|
|
575
|
-
|
|
608
|
+
build_altsvc_connection(alt_origin, origin, alt_params)
|
|
576
609
|
end
|
|
577
610
|
|
|
578
611
|
parser.on(:pong, &method(:send_pending))
|
|
@@ -581,50 +614,31 @@ module HTTPX
|
|
|
581
614
|
request.emit(:promise, parser, stream)
|
|
582
615
|
end
|
|
583
616
|
parser.on(:exhausted) do
|
|
617
|
+
enqueue_pending_requests_from_parser(parser)
|
|
618
|
+
|
|
584
619
|
@exhausted = true
|
|
585
|
-
|
|
586
|
-
current_selector = @current_selector
|
|
587
|
-
begin
|
|
588
|
-
parser.close
|
|
589
|
-
@pending.concat(parser.pending)
|
|
590
|
-
ensure
|
|
591
|
-
@current_session = current_session
|
|
592
|
-
@current_selector = current_selector
|
|
593
|
-
end
|
|
620
|
+
parser.close
|
|
594
621
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
idling
|
|
598
|
-
@exhausted = false
|
|
599
|
-
when :closing
|
|
600
|
-
once(:closed) do
|
|
601
|
-
idling
|
|
602
|
-
@exhausted = false
|
|
603
|
-
end
|
|
604
|
-
end
|
|
622
|
+
idling
|
|
623
|
+
@exhausted = false
|
|
605
624
|
end
|
|
606
625
|
parser.on(:origin) do |origin|
|
|
607
626
|
@origins |= [origin]
|
|
608
627
|
end
|
|
609
|
-
parser.on(:close) do
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
emit(:terminate)
|
|
613
|
-
end
|
|
628
|
+
parser.on(:close) do
|
|
629
|
+
reset
|
|
630
|
+
disconnect
|
|
614
631
|
end
|
|
615
632
|
parser.on(:close_handshake) do
|
|
616
|
-
consume
|
|
633
|
+
consume unless @state == :closed
|
|
617
634
|
end
|
|
618
635
|
parser.on(:reset) do
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
current_selector = @current_selector
|
|
636
|
+
enqueue_pending_requests_from_parser(parser)
|
|
637
|
+
|
|
622
638
|
reset
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
@current_selector = current_selector
|
|
627
|
-
end
|
|
639
|
+
# :reset event only fired in http/1.1, so this guarantees
|
|
640
|
+
# that the connection will be closed here.
|
|
641
|
+
idling unless @pending.empty?
|
|
628
642
|
end
|
|
629
643
|
parser.on(:current_timeout) do
|
|
630
644
|
@current_timeout = @timeout = parser.timeout
|
|
@@ -674,16 +688,12 @@ module HTTPX
|
|
|
674
688
|
error = ConnectionError.new(e.message)
|
|
675
689
|
error.set_backtrace(e.backtrace)
|
|
676
690
|
handle_connect_error(error) if connecting?
|
|
677
|
-
|
|
678
|
-
purge_after_closed
|
|
679
|
-
disconnect
|
|
691
|
+
force_close
|
|
680
692
|
rescue TLSError, ::HTTP2::Error::ProtocolError, ::HTTP2::Error::HandshakeError => e
|
|
681
693
|
# connect errors, exit gracefully
|
|
682
694
|
handle_error(e)
|
|
683
695
|
handle_connect_error(e) if connecting?
|
|
684
|
-
|
|
685
|
-
purge_after_closed
|
|
686
|
-
disconnect
|
|
696
|
+
force_close
|
|
687
697
|
end
|
|
688
698
|
|
|
689
699
|
def handle_transition(nextstate)
|
|
@@ -711,6 +721,8 @@ module HTTPX
|
|
|
711
721
|
|
|
712
722
|
# do not deactivate connection in use
|
|
713
723
|
return if @inflight.positive? || @parser.waiting_for_ping?
|
|
724
|
+
|
|
725
|
+
disconnect
|
|
714
726
|
when :closing
|
|
715
727
|
return unless @state == :idle || @state == :open
|
|
716
728
|
|
|
@@ -785,6 +797,8 @@ module HTTPX
|
|
|
785
797
|
|
|
786
798
|
# returns an HTTPX::Connection for the negotiated Alternative Service (or none).
|
|
787
799
|
def build_altsvc_connection(alt_origin, origin, alt_params)
|
|
800
|
+
return if @altsvc_connection
|
|
801
|
+
|
|
788
802
|
# do not allow security downgrades on altsvc negotiation
|
|
789
803
|
return if @origin.scheme == "https" && alt_origin.scheme != "https"
|
|
790
804
|
|
|
@@ -802,10 +816,11 @@ module HTTPX
|
|
|
802
816
|
|
|
803
817
|
connection.extend(AltSvc::ConnectionMixin) unless connection.is_a?(AltSvc::ConnectionMixin)
|
|
804
818
|
|
|
805
|
-
|
|
819
|
+
@altsvc_connection = connection
|
|
820
|
+
|
|
821
|
+
log(level: 1) { "#{origin}: alt-svc connection##{connection.object_id} established to #{alt_origin}" }
|
|
806
822
|
|
|
807
823
|
connection.merge(self)
|
|
808
|
-
terminate
|
|
809
824
|
rescue UnsupportedSchemeError
|
|
810
825
|
altsvc["noop"] = true
|
|
811
826
|
nil
|
|
@@ -835,26 +850,8 @@ module HTTPX
|
|
|
835
850
|
end
|
|
836
851
|
end
|
|
837
852
|
|
|
838
|
-
def on_error(error, request = nil)
|
|
839
|
-
if error.is_a?(OperationTimeoutError)
|
|
840
|
-
|
|
841
|
-
# inactive connections do not contribute to the select loop, therefore
|
|
842
|
-
# they should not fail due to such errors.
|
|
843
|
-
return if @state == :inactive
|
|
844
|
-
|
|
845
|
-
if @timeout
|
|
846
|
-
@timeout -= error.timeout
|
|
847
|
-
return unless @timeout <= 0
|
|
848
|
-
end
|
|
849
|
-
|
|
850
|
-
error = error.to_connection_error if connecting?
|
|
851
|
-
end
|
|
852
|
-
handle_error(error, request)
|
|
853
|
-
reset
|
|
854
|
-
end
|
|
855
|
-
|
|
856
853
|
def handle_error(error, request = nil)
|
|
857
|
-
parser.handle_error(error, request) if @parser && parser.respond_to?(:handle_error)
|
|
854
|
+
parser.handle_error(error, request) if @parser && @parser.respond_to?(:handle_error)
|
|
858
855
|
while (req = @pending.shift)
|
|
859
856
|
next if request && req == request
|
|
860
857
|
|
|
@@ -929,6 +926,17 @@ module HTTPX
|
|
|
929
926
|
|
|
930
927
|
def set_request_timeout(label, request, timeout, start_event, finish_events, &callback)
|
|
931
928
|
request.set_timeout_callback(start_event) do
|
|
929
|
+
unless @current_selector
|
|
930
|
+
raise Error, "request has been resend to an out-of-session connection, and this " \
|
|
931
|
+
"should never happen!!! Please report this error! " \
|
|
932
|
+
"(state:#{@state}, " \
|
|
933
|
+
"parser?:#{!!@parser}, " \
|
|
934
|
+
"bytes in write buffer?:#{!@write_buffer.empty?}, " \
|
|
935
|
+
"cloned?:#{@cloned}, " \
|
|
936
|
+
"sibling?:#{!!@sibling}, " \
|
|
937
|
+
"coalesced?:#{coalesced?})"
|
|
938
|
+
end
|
|
939
|
+
|
|
932
940
|
timer = @current_selector.after(timeout, callback)
|
|
933
941
|
request.active_timeouts << label
|
|
934
942
|
|
data/lib/httpx/domain_name.rb
CHANGED
data/lib/httpx/extensions.rb
CHANGED
|
@@ -4,20 +4,6 @@ require "uri"
|
|
|
4
4
|
|
|
5
5
|
module HTTPX
|
|
6
6
|
module ArrayExtensions
|
|
7
|
-
module FilterMap
|
|
8
|
-
refine Array do
|
|
9
|
-
# Ruby 2.7 backport
|
|
10
|
-
def filter_map
|
|
11
|
-
return to_enum(:filter_map) unless block_given?
|
|
12
|
-
|
|
13
|
-
each_with_object([]) do |item, res|
|
|
14
|
-
processed = yield(item)
|
|
15
|
-
res << processed if processed
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
end unless Array.method_defined?(:filter_map)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
7
|
module Intersect
|
|
22
8
|
refine Array do
|
|
23
9
|
# Ruby 3.1 backport
|
data/lib/httpx/headers.rb
CHANGED
|
@@ -42,12 +42,12 @@ module HTTPX
|
|
|
42
42
|
# dupped initialization
|
|
43
43
|
def initialize_dup(orig)
|
|
44
44
|
super
|
|
45
|
-
@headers = orig.instance_variable_get(:@headers).dup
|
|
45
|
+
@headers = orig.instance_variable_get(:@headers).transform_values(&:dup)
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
# freezes the headers hash
|
|
49
49
|
def freeze
|
|
50
|
-
@headers.freeze
|
|
50
|
+
@headers.each_value(&:freeze).freeze
|
|
51
51
|
super
|
|
52
52
|
end
|
|
53
53
|
|
data/lib/httpx/io/ssl.rb
CHANGED
|
@@ -98,7 +98,7 @@ module HTTPX
|
|
|
98
98
|
end
|
|
99
99
|
|
|
100
100
|
unless @io.is_a?(OpenSSL::SSL::SSLSocket)
|
|
101
|
-
if (hostname_is_ip = (@ip == @sni_hostname))
|
|
101
|
+
if (hostname_is_ip = (@ip == @sni_hostname)) && @ctx.verify_hostname
|
|
102
102
|
# IPv6 address would be "[::1]", must turn to "0000:0000:0000:0000:0000:0000:0000:0001" for cert SAN check
|
|
103
103
|
@sni_hostname = @ip.to_string
|
|
104
104
|
# IP addresses in SNI is not valid per RFC 6066, section 3.
|
data/lib/httpx/loggable.rb
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "fiber" if RUBY_VERSION < "3.0.0"
|
|
4
|
+
|
|
3
5
|
module HTTPX
|
|
4
6
|
module Loggable
|
|
5
7
|
COLORS = {
|
|
@@ -34,7 +36,7 @@ module HTTPX
|
|
|
34
36
|
klass = klass.superclass
|
|
35
37
|
end
|
|
36
38
|
|
|
37
|
-
message = +"(pid:#{Process.pid}, " \
|
|
39
|
+
message = +"(time:#{Time.now.utc}, pid:#{Process.pid}, " \
|
|
38
40
|
"tid:#{Thread.current.object_id}, " \
|
|
39
41
|
"fid:#{Fiber.current.object_id}, " \
|
|
40
42
|
"self:#{class_name}##{object_id}) "
|
|
@@ -47,7 +49,17 @@ module HTTPX
|
|
|
47
49
|
log(level: level, color: color, debug_level: debug_level, debug: debug) { ex.full_message }
|
|
48
50
|
end
|
|
49
51
|
|
|
50
|
-
def
|
|
52
|
+
def log_redact_headers(text)
|
|
53
|
+
log_redact(text, @options.debug_redact == :headers)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def log_redact_body(text)
|
|
57
|
+
log_redact(text, @options.debug_redact == :body)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def log_redact(text, should_redact)
|
|
61
|
+
should_redact ||= @options.debug_redact == true
|
|
62
|
+
|
|
51
63
|
return text.to_s unless should_redact
|
|
52
64
|
|
|
53
65
|
"[REDACTED]"
|
data/lib/httpx/options.rb
CHANGED
|
@@ -13,6 +13,9 @@ module HTTPX
|
|
|
13
13
|
CONNECT_TIMEOUT = READ_TIMEOUT = WRITE_TIMEOUT = 60
|
|
14
14
|
REQUEST_TIMEOUT = OPERATION_TIMEOUT = nil
|
|
15
15
|
|
|
16
|
+
# default value used for "user-agent" header, when not overridden.
|
|
17
|
+
USER_AGENT = "httpx.rb/#{VERSION}".freeze # rubocop:disable Style/RedundantFreeze
|
|
18
|
+
|
|
16
19
|
@options_names = []
|
|
17
20
|
|
|
18
21
|
class << self
|
|
@@ -144,6 +147,7 @@ module HTTPX
|
|
|
144
147
|
instance_variable_set(:"@#{k}", value)
|
|
145
148
|
end
|
|
146
149
|
|
|
150
|
+
do_initialize
|
|
147
151
|
freeze
|
|
148
152
|
end
|
|
149
153
|
|
|
@@ -184,33 +188,40 @@ module HTTPX
|
|
|
184
188
|
end
|
|
185
189
|
|
|
186
190
|
def merge(other)
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
other.instance_variables
|
|
191
|
-
else
|
|
192
|
-
other = Hash[other] unless other.is_a?(Hash)
|
|
193
|
-
ivar_map = other.keys.to_h { |k| [:"@#{k}", k] }
|
|
194
|
-
ivar_map.keys
|
|
195
|
-
end
|
|
191
|
+
if (is_options = other.is_a?(Options))
|
|
192
|
+
|
|
193
|
+
return self if eql?(other)
|
|
196
194
|
|
|
197
|
-
|
|
195
|
+
opts_names = other.class.options_names
|
|
196
|
+
|
|
197
|
+
return self if opts_names.all? { |opt| public_send(opt) == other.public_send(opt) }
|
|
198
|
+
|
|
199
|
+
other_opts = opts_names
|
|
200
|
+
else
|
|
201
|
+
other_opts = other # : Hash[Symbol, untyped]
|
|
202
|
+
other_opts = Hash[other] unless other.is_a?(Hash)
|
|
198
203
|
|
|
199
|
-
|
|
204
|
+
return self if other_opts.empty?
|
|
205
|
+
|
|
206
|
+
return self if other_opts.all? { |opt, v| !respond_to?(opt) || public_send(opt) == v }
|
|
207
|
+
end
|
|
200
208
|
|
|
201
209
|
opts = dup
|
|
202
210
|
|
|
203
|
-
|
|
204
|
-
|
|
211
|
+
other_opts.each do |opt, v|
|
|
212
|
+
next unless respond_to?(opt)
|
|
213
|
+
|
|
214
|
+
v = other.public_send(opt) if is_options
|
|
215
|
+
ivar = :"@#{opt}"
|
|
205
216
|
|
|
206
217
|
unless v
|
|
207
218
|
opts.instance_variable_set(ivar, v)
|
|
208
219
|
next
|
|
209
220
|
end
|
|
210
221
|
|
|
211
|
-
v = opts.__send__(:"option_#{
|
|
222
|
+
v = opts.__send__(:"option_#{opt}", v)
|
|
212
223
|
|
|
213
|
-
orig_v =
|
|
224
|
+
orig_v = public_send(opt)
|
|
214
225
|
|
|
215
226
|
v = orig_v.merge(v) if orig_v.respond_to?(:merge) && v.respond_to?(:merge)
|
|
216
227
|
|
|
@@ -369,11 +380,26 @@ module HTTPX
|
|
|
369
380
|
end
|
|
370
381
|
|
|
371
382
|
def option_headers(value)
|
|
383
|
+
value = value.dup if value.frozen?
|
|
384
|
+
|
|
372
385
|
headers_class.new(value)
|
|
373
386
|
end
|
|
374
387
|
|
|
375
388
|
def option_timeout(value)
|
|
376
|
-
Hash[value]
|
|
389
|
+
timeout_hash = Hash[value]
|
|
390
|
+
|
|
391
|
+
default_timeouts = DEFAULT_OPTIONS[:timeout]
|
|
392
|
+
|
|
393
|
+
# Validate keys and values
|
|
394
|
+
timeout_hash.each do |key, val|
|
|
395
|
+
raise TypeError, "invalid timeout: :#{key}" unless default_timeouts.key?(key)
|
|
396
|
+
|
|
397
|
+
next if val.nil?
|
|
398
|
+
|
|
399
|
+
raise TypeError, ":#{key} must be numeric" unless val.is_a?(Numeric)
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
timeout_hash
|
|
377
403
|
end
|
|
378
404
|
|
|
379
405
|
def option_supported_compression_formats(value)
|
|
@@ -395,6 +421,20 @@ module HTTPX
|
|
|
395
421
|
Array(value)
|
|
396
422
|
end
|
|
397
423
|
|
|
424
|
+
# called after all options are initialized
|
|
425
|
+
def do_initialize
|
|
426
|
+
hs = @headers
|
|
427
|
+
|
|
428
|
+
# initialized default request headers
|
|
429
|
+
hs["user-agent"] = USER_AGENT unless hs.key?("user-agent")
|
|
430
|
+
hs["accept"] = "*/*" unless hs.key?("accept")
|
|
431
|
+
if hs.key?("range")
|
|
432
|
+
hs.delete("accept-encoding")
|
|
433
|
+
else
|
|
434
|
+
hs["accept-encoding"] = supported_compression_formats unless hs.key?("accept-encoding")
|
|
435
|
+
end
|
|
436
|
+
end
|
|
437
|
+
|
|
398
438
|
def access_option(obj, k, ivar_map)
|
|
399
439
|
case obj
|
|
400
440
|
when Hash
|
|
@@ -404,6 +444,8 @@ module HTTPX
|
|
|
404
444
|
end
|
|
405
445
|
end
|
|
406
446
|
|
|
447
|
+
# rubocop:disable Lint/UselessConstantScoping
|
|
448
|
+
# these really need to be defined at the end of the class
|
|
407
449
|
SET_TEMPORARY_NAME = ->(klass, pl = nil) do
|
|
408
450
|
if klass.respond_to?(:set_temporary_name) # ruby 3.4 only
|
|
409
451
|
name = klass.name || "#{klass.superclass.name}(plugin)"
|
|
@@ -458,6 +500,7 @@ module HTTPX
|
|
|
458
500
|
:pool_options => EMPTY_HASH,
|
|
459
501
|
:ip_families => nil,
|
|
460
502
|
:close_on_fork => false,
|
|
461
|
-
}.freeze
|
|
503
|
+
}.each_value(&:freeze).freeze
|
|
504
|
+
# rubocop:enable Lint/UselessConstantScoping
|
|
462
505
|
end
|
|
463
506
|
end
|