httpx 1.6.1 → 1.6.3
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_6_2.md +11 -0
- data/doc/release_notes/1_6_3.md +47 -0
- data/lib/httpx/adapters/datadog.rb +15 -11
- data/lib/httpx/adapters/sentry.rb +1 -1
- data/lib/httpx/connection/http1.rb +9 -9
- data/lib/httpx/connection/http2.rb +14 -15
- data/lib/httpx/connection.rb +119 -102
- data/lib/httpx/extensions.rb +0 -14
- data/lib/httpx/io/ssl.rb +1 -1
- data/lib/httpx/loggable.rb +12 -2
- data/lib/httpx/options.rb +20 -0
- data/lib/httpx/plugins/callbacks.rb +15 -1
- data/lib/httpx/plugins/digest_auth.rb +1 -1
- data/lib/httpx/plugins/proxy/http.rb +37 -9
- data/lib/httpx/plugins/response_cache/file_store.rb +1 -0
- data/lib/httpx/plugins/response_cache.rb +13 -2
- data/lib/httpx/plugins/stream_bidi.rb +15 -6
- data/lib/httpx/pool.rb +53 -19
- data/lib/httpx/request.rb +3 -13
- data/lib/httpx/resolver/https.rb +35 -19
- data/lib/httpx/resolver/multi.rb +9 -32
- data/lib/httpx/resolver/native.rb +46 -38
- data/lib/httpx/resolver/resolver.rb +45 -28
- data/lib/httpx/resolver/system.rb +63 -39
- data/lib/httpx/selector.rb +35 -20
- data/lib/httpx/session.rb +18 -28
- data/lib/httpx/transcoder/deflate.rb +13 -8
- data/lib/httpx/transcoder/utils/body_reader.rb +1 -2
- data/lib/httpx/transcoder/utils/deflater.rb +1 -2
- data/lib/httpx/version.rb +1 -1
- data/sig/connection.rbs +12 -3
- data/sig/loggable.rbs +5 -1
- data/sig/options.rbs +5 -1
- data/sig/plugins/callbacks.rbs +3 -0
- data/sig/plugins/stream_bidi.rbs +3 -5
- data/sig/resolver/https.rbs +2 -0
- data/sig/resolver/multi.rbs +0 -9
- data/sig/resolver/native.rbs +0 -2
- data/sig/resolver/resolver.rbs +9 -8
- data/sig/resolver/system.rbs +4 -2
- data/sig/selector.rbs +2 -0
- data/sig/session.rbs +5 -3
- 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
|
|
|
@@ -158,6 +129,10 @@ module HTTPX
|
|
|
158
129
|
connection.merge(self)
|
|
159
130
|
end
|
|
160
131
|
|
|
132
|
+
def coalesced?
|
|
133
|
+
@coalesced_connection
|
|
134
|
+
end
|
|
135
|
+
|
|
161
136
|
# coalescable connections need to be mergeable!
|
|
162
137
|
# but internally, #mergeable? is called before #coalescable?
|
|
163
138
|
def coalescable?(connection)
|
|
@@ -231,7 +206,7 @@ module HTTPX
|
|
|
231
206
|
|
|
232
207
|
nil
|
|
233
208
|
rescue StandardError => e
|
|
234
|
-
|
|
209
|
+
on_error(e)
|
|
235
210
|
nil
|
|
236
211
|
end
|
|
237
212
|
|
|
@@ -243,6 +218,10 @@ module HTTPX
|
|
|
243
218
|
case @state
|
|
244
219
|
when :idle
|
|
245
220
|
connect
|
|
221
|
+
|
|
222
|
+
# when opening the tcp or ssl socket fails
|
|
223
|
+
return if @state == :closed
|
|
224
|
+
|
|
246
225
|
consume
|
|
247
226
|
when :closed
|
|
248
227
|
return
|
|
@@ -255,7 +234,9 @@ module HTTPX
|
|
|
255
234
|
nil
|
|
256
235
|
rescue StandardError => e
|
|
257
236
|
@write_buffer.clear
|
|
258
|
-
|
|
237
|
+
on_error(e)
|
|
238
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
239
|
+
force_close(true)
|
|
259
240
|
raise e
|
|
260
241
|
end
|
|
261
242
|
|
|
@@ -269,7 +250,7 @@ module HTTPX
|
|
|
269
250
|
case @state
|
|
270
251
|
when :idle
|
|
271
252
|
purge_after_closed
|
|
272
|
-
|
|
253
|
+
disconnect
|
|
273
254
|
when :closed
|
|
274
255
|
@connected_at = nil
|
|
275
256
|
end
|
|
@@ -277,6 +258,23 @@ module HTTPX
|
|
|
277
258
|
close
|
|
278
259
|
end
|
|
279
260
|
|
|
261
|
+
# bypasses state machine rules while setting the connection in the
|
|
262
|
+
# :closed state.
|
|
263
|
+
def force_close(delete_pending = false)
|
|
264
|
+
if delete_pending
|
|
265
|
+
@pending.clear
|
|
266
|
+
elsif (parser = @parser)
|
|
267
|
+
enqueue_pending_requests_from_parser(parser)
|
|
268
|
+
end
|
|
269
|
+
return if @state == :closed
|
|
270
|
+
|
|
271
|
+
@state = :closed
|
|
272
|
+
@write_buffer.clear
|
|
273
|
+
purge_after_closed
|
|
274
|
+
disconnect
|
|
275
|
+
emit(:force_closed, delete_pending)
|
|
276
|
+
end
|
|
277
|
+
|
|
280
278
|
# bypasses the state machine to force closing of connections still connecting.
|
|
281
279
|
# **only** used for Happy Eyeballs v2.
|
|
282
280
|
def force_reset(cloned = false)
|
|
@@ -364,18 +362,40 @@ module HTTPX
|
|
|
364
362
|
end
|
|
365
363
|
|
|
366
364
|
def handle_connect_error(error)
|
|
367
|
-
return
|
|
365
|
+
return on_error(error) unless @sibling && @sibling.connecting?
|
|
368
366
|
|
|
369
367
|
@sibling.merge(self)
|
|
370
368
|
|
|
371
369
|
force_reset(true)
|
|
372
370
|
end
|
|
373
371
|
|
|
372
|
+
# disconnects from the current session it's attached to
|
|
374
373
|
def disconnect
|
|
375
|
-
return
|
|
374
|
+
return if @exhausted # it'll reset
|
|
375
|
+
|
|
376
|
+
return unless (current_session = @current_session) && (current_selector = @current_selector)
|
|
376
377
|
|
|
377
|
-
emit(:close)
|
|
378
378
|
@current_session = @current_selector = nil
|
|
379
|
+
|
|
380
|
+
current_session.deselect_connection(self, current_selector, @cloned)
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
def on_error(error, request = nil)
|
|
384
|
+
if error.is_a?(OperationTimeoutError)
|
|
385
|
+
|
|
386
|
+
# inactive connections do not contribute to the select loop, therefore
|
|
387
|
+
# they should not fail due to such errors.
|
|
388
|
+
return if @state == :inactive
|
|
389
|
+
|
|
390
|
+
if @timeout
|
|
391
|
+
@timeout -= error.timeout
|
|
392
|
+
return unless @timeout <= 0
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
error = error.to_connection_error if connecting?
|
|
396
|
+
end
|
|
397
|
+
handle_error(error, request)
|
|
398
|
+
reset
|
|
379
399
|
end
|
|
380
400
|
|
|
381
401
|
# :nocov:
|
|
@@ -413,7 +433,11 @@ module HTTPX
|
|
|
413
433
|
# * the number of pending requests
|
|
414
434
|
# * whether the write buffer has bytes (i.e. for close handshake)
|
|
415
435
|
if @pending.empty? && @inflight.zero? && @write_buffer.empty?
|
|
416
|
-
log(level: 3) { "NO MORE REQUESTS..." }
|
|
436
|
+
log(level: 3) { "NO MORE REQUESTS..." } if @parser && @parser.pending.any?
|
|
437
|
+
|
|
438
|
+
# terminate if an altsvc connection has been established
|
|
439
|
+
terminate if @altsvc_connection
|
|
440
|
+
|
|
417
441
|
return
|
|
418
442
|
end
|
|
419
443
|
|
|
@@ -458,7 +482,14 @@ module HTTPX
|
|
|
458
482
|
break if @state == :closing || @state == :closed
|
|
459
483
|
|
|
460
484
|
# exit #consume altogether if all outstanding requests have been dealt with
|
|
461
|
-
|
|
485
|
+
if @pending.empty? && @inflight.zero? && @write_buffer.empty? # rubocop:disable Style/Next
|
|
486
|
+
log(level: 3) { "NO MORE REQUESTS..." } if @parser && @parser.pending.any?
|
|
487
|
+
|
|
488
|
+
# terminate if an altsvc connection has been established
|
|
489
|
+
terminate if @altsvc_connection
|
|
490
|
+
|
|
491
|
+
return
|
|
492
|
+
end
|
|
462
493
|
end unless ((ints = interests).nil? || ints == :w || @state == :closing) && !epiped
|
|
463
494
|
|
|
464
495
|
#
|
|
@@ -551,6 +582,17 @@ module HTTPX
|
|
|
551
582
|
request.ping!
|
|
552
583
|
end
|
|
553
584
|
|
|
585
|
+
def enqueue_pending_requests_from_parser(parser)
|
|
586
|
+
parser_pending_requests = parser.pending
|
|
587
|
+
|
|
588
|
+
return if parser_pending_requests.empty?
|
|
589
|
+
|
|
590
|
+
# the connection will be reused, so parser requests must come
|
|
591
|
+
# back to the pending list before the parser is reset.
|
|
592
|
+
@inflight -= parser_pending_requests.size
|
|
593
|
+
@pending.unshift(*parser_pending_requests)
|
|
594
|
+
end
|
|
595
|
+
|
|
554
596
|
def build_parser(protocol = @io.protocol)
|
|
555
597
|
parser = parser_type(protocol).new(@write_buffer, @options)
|
|
556
598
|
set_parser_callbacks(parser)
|
|
@@ -560,7 +602,7 @@ module HTTPX
|
|
|
560
602
|
def set_parser_callbacks(parser)
|
|
561
603
|
parser.on(:response) do |request, response|
|
|
562
604
|
AltSvc.emit(request, response) do |alt_origin, origin, alt_params|
|
|
563
|
-
|
|
605
|
+
build_altsvc_connection(alt_origin, origin, alt_params)
|
|
564
606
|
end
|
|
565
607
|
@response_received_at = Utils.now
|
|
566
608
|
@inflight -= 1
|
|
@@ -568,7 +610,7 @@ module HTTPX
|
|
|
568
610
|
request.emit(:response, response)
|
|
569
611
|
end
|
|
570
612
|
parser.on(:altsvc) do |alt_origin, origin, alt_params|
|
|
571
|
-
|
|
613
|
+
build_altsvc_connection(alt_origin, origin, alt_params)
|
|
572
614
|
end
|
|
573
615
|
|
|
574
616
|
parser.on(:pong, &method(:send_pending))
|
|
@@ -577,50 +619,31 @@ module HTTPX
|
|
|
577
619
|
request.emit(:promise, parser, stream)
|
|
578
620
|
end
|
|
579
621
|
parser.on(:exhausted) do
|
|
622
|
+
enqueue_pending_requests_from_parser(parser)
|
|
623
|
+
|
|
580
624
|
@exhausted = true
|
|
581
|
-
|
|
582
|
-
current_selector = @current_selector
|
|
583
|
-
begin
|
|
584
|
-
parser.close
|
|
585
|
-
@pending.concat(parser.pending)
|
|
586
|
-
ensure
|
|
587
|
-
@current_session = current_session
|
|
588
|
-
@current_selector = current_selector
|
|
589
|
-
end
|
|
625
|
+
parser.close
|
|
590
626
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
idling
|
|
594
|
-
@exhausted = false
|
|
595
|
-
when :closing
|
|
596
|
-
once(:closed) do
|
|
597
|
-
idling
|
|
598
|
-
@exhausted = false
|
|
599
|
-
end
|
|
600
|
-
end
|
|
627
|
+
idling
|
|
628
|
+
@exhausted = false
|
|
601
629
|
end
|
|
602
630
|
parser.on(:origin) do |origin|
|
|
603
631
|
@origins |= [origin]
|
|
604
632
|
end
|
|
605
|
-
parser.on(:close) do
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
emit(:terminate)
|
|
609
|
-
end
|
|
633
|
+
parser.on(:close) do
|
|
634
|
+
reset
|
|
635
|
+
disconnect
|
|
610
636
|
end
|
|
611
637
|
parser.on(:close_handshake) do
|
|
612
|
-
consume
|
|
638
|
+
consume unless @state == :closed
|
|
613
639
|
end
|
|
614
640
|
parser.on(:reset) do
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
current_selector = @current_selector
|
|
641
|
+
enqueue_pending_requests_from_parser(parser)
|
|
642
|
+
|
|
618
643
|
reset
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
@current_selector = current_selector
|
|
623
|
-
end
|
|
644
|
+
# :reset event only fired in http/1.1, so this guarantees
|
|
645
|
+
# that the connection will be closed here.
|
|
646
|
+
idling unless @pending.empty?
|
|
624
647
|
end
|
|
625
648
|
parser.on(:current_timeout) do
|
|
626
649
|
@current_timeout = @timeout = parser.timeout
|
|
@@ -670,16 +693,12 @@ module HTTPX
|
|
|
670
693
|
error = ConnectionError.new(e.message)
|
|
671
694
|
error.set_backtrace(e.backtrace)
|
|
672
695
|
handle_connect_error(error) if connecting?
|
|
673
|
-
|
|
674
|
-
purge_after_closed
|
|
675
|
-
disconnect
|
|
696
|
+
force_close
|
|
676
697
|
rescue TLSError, ::HTTP2::Error::ProtocolError, ::HTTP2::Error::HandshakeError => e
|
|
677
698
|
# connect errors, exit gracefully
|
|
678
699
|
handle_error(e)
|
|
679
700
|
handle_connect_error(e) if connecting?
|
|
680
|
-
|
|
681
|
-
purge_after_closed
|
|
682
|
-
disconnect
|
|
701
|
+
force_close
|
|
683
702
|
end
|
|
684
703
|
|
|
685
704
|
def handle_transition(nextstate)
|
|
@@ -707,6 +726,8 @@ module HTTPX
|
|
|
707
726
|
|
|
708
727
|
# do not deactivate connection in use
|
|
709
728
|
return if @inflight.positive? || @parser.waiting_for_ping?
|
|
729
|
+
|
|
730
|
+
disconnect
|
|
710
731
|
when :closing
|
|
711
732
|
return unless @state == :idle || @state == :open
|
|
712
733
|
|
|
@@ -781,6 +802,8 @@ module HTTPX
|
|
|
781
802
|
|
|
782
803
|
# returns an HTTPX::Connection for the negotiated Alternative Service (or none).
|
|
783
804
|
def build_altsvc_connection(alt_origin, origin, alt_params)
|
|
805
|
+
return if @altsvc_connection
|
|
806
|
+
|
|
784
807
|
# do not allow security downgrades on altsvc negotiation
|
|
785
808
|
return if @origin.scheme == "https" && alt_origin.scheme != "https"
|
|
786
809
|
|
|
@@ -798,10 +821,11 @@ module HTTPX
|
|
|
798
821
|
|
|
799
822
|
connection.extend(AltSvc::ConnectionMixin) unless connection.is_a?(AltSvc::ConnectionMixin)
|
|
800
823
|
|
|
801
|
-
|
|
824
|
+
@altsvc_connection = connection
|
|
825
|
+
|
|
826
|
+
log(level: 1) { "#{origin}: alt-svc connection##{connection.object_id} established to #{alt_origin}" }
|
|
802
827
|
|
|
803
828
|
connection.merge(self)
|
|
804
|
-
terminate
|
|
805
829
|
rescue UnsupportedSchemeError
|
|
806
830
|
altsvc["noop"] = true
|
|
807
831
|
nil
|
|
@@ -831,26 +855,8 @@ module HTTPX
|
|
|
831
855
|
end
|
|
832
856
|
end
|
|
833
857
|
|
|
834
|
-
def on_error(error, request = nil)
|
|
835
|
-
if error.is_a?(OperationTimeoutError)
|
|
836
|
-
|
|
837
|
-
# inactive connections do not contribute to the select loop, therefore
|
|
838
|
-
# they should not fail due to such errors.
|
|
839
|
-
return if @state == :inactive
|
|
840
|
-
|
|
841
|
-
if @timeout
|
|
842
|
-
@timeout -= error.timeout
|
|
843
|
-
return unless @timeout <= 0
|
|
844
|
-
end
|
|
845
|
-
|
|
846
|
-
error = error.to_connection_error if connecting?
|
|
847
|
-
end
|
|
848
|
-
handle_error(error, request)
|
|
849
|
-
reset
|
|
850
|
-
end
|
|
851
|
-
|
|
852
858
|
def handle_error(error, request = nil)
|
|
853
|
-
parser.handle_error(error, request) if @parser && parser.respond_to?(:handle_error)
|
|
859
|
+
parser.handle_error(error, request) if @parser && @parser.respond_to?(:handle_error)
|
|
854
860
|
while (req = @pending.shift)
|
|
855
861
|
next if request && req == request
|
|
856
862
|
|
|
@@ -925,6 +931,17 @@ module HTTPX
|
|
|
925
931
|
|
|
926
932
|
def set_request_timeout(label, request, timeout, start_event, finish_events, &callback)
|
|
927
933
|
request.set_timeout_callback(start_event) do
|
|
934
|
+
unless @current_selector
|
|
935
|
+
raise Error, "request has been resend to an out-of-session connection, and this " \
|
|
936
|
+
"should never happen!!! Please report this error! " \
|
|
937
|
+
"(state:#{@state}, " \
|
|
938
|
+
"parser?:#{!!@parser}, " \
|
|
939
|
+
"bytes in write buffer?:#{!@write_buffer.empty?}, " \
|
|
940
|
+
"cloned?:#{@cloned}, " \
|
|
941
|
+
"sibling?:#{!!@sibling}, " \
|
|
942
|
+
"coalesced?:#{coalesced?})"
|
|
943
|
+
end
|
|
944
|
+
|
|
928
945
|
timer = @current_selector.after(timeout, callback)
|
|
929
946
|
request.active_timeouts << label
|
|
930
947
|
|
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/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
|
@@ -34,7 +34,7 @@ module HTTPX
|
|
|
34
34
|
klass = klass.superclass
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
message = +"(pid:#{Process.pid}, " \
|
|
37
|
+
message = +"(time:#{Time.now.utc}, pid:#{Process.pid}, " \
|
|
38
38
|
"tid:#{Thread.current.object_id}, " \
|
|
39
39
|
"fid:#{Fiber.current.object_id}, " \
|
|
40
40
|
"self:#{class_name}##{object_id}) "
|
|
@@ -47,7 +47,17 @@ module HTTPX
|
|
|
47
47
|
log(level: level, color: color, debug_level: debug_level, debug: debug) { ex.full_message }
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
-
def
|
|
50
|
+
def log_redact_headers(text)
|
|
51
|
+
log_redact(text, @options.debug_redact == :headers)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def log_redact_body(text)
|
|
55
|
+
log_redact(text, @options.debug_redact == :body)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def log_redact(text, should_redact)
|
|
59
|
+
should_redact ||= @options.debug_redact == true
|
|
60
|
+
|
|
51
61
|
return text.to_s unless should_redact
|
|
52
62
|
|
|
53
63
|
"[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
|
|
|
@@ -369,6 +373,8 @@ module HTTPX
|
|
|
369
373
|
end
|
|
370
374
|
|
|
371
375
|
def option_headers(value)
|
|
376
|
+
value = value.dup if value.frozen?
|
|
377
|
+
|
|
372
378
|
headers_class.new(value)
|
|
373
379
|
end
|
|
374
380
|
|
|
@@ -395,6 +401,20 @@ module HTTPX
|
|
|
395
401
|
Array(value)
|
|
396
402
|
end
|
|
397
403
|
|
|
404
|
+
# called after all options are initialized
|
|
405
|
+
def do_initialize
|
|
406
|
+
hs = @headers
|
|
407
|
+
|
|
408
|
+
# initialized default request headers
|
|
409
|
+
hs["user-agent"] = USER_AGENT unless hs.key?("user-agent")
|
|
410
|
+
hs["accept"] = "*/*" unless hs.key?("accept")
|
|
411
|
+
if hs.key?("range")
|
|
412
|
+
hs.delete("accept-encoding")
|
|
413
|
+
else
|
|
414
|
+
hs["accept-encoding"] = supported_compression_formats unless hs.key?("accept-encoding")
|
|
415
|
+
end
|
|
416
|
+
end
|
|
417
|
+
|
|
398
418
|
def access_option(obj, k, ivar_map)
|
|
399
419
|
case obj
|
|
400
420
|
when Hash
|
|
@@ -64,7 +64,7 @@ module HTTPX
|
|
|
64
64
|
|
|
65
65
|
emit_or_callback_error(:connection_opened, connection.origin, connection.io.socket)
|
|
66
66
|
end
|
|
67
|
-
connection.on(:
|
|
67
|
+
connection.on(:callback_connection_closed) do
|
|
68
68
|
next unless connection.current_session == self
|
|
69
69
|
|
|
70
70
|
emit_or_callback_error(:connection_closed, connection.origin) if connection.used?
|
|
@@ -121,6 +121,20 @@ module HTTPX
|
|
|
121
121
|
raise e.cause
|
|
122
122
|
end
|
|
123
123
|
end
|
|
124
|
+
|
|
125
|
+
module ConnectionMethods
|
|
126
|
+
private
|
|
127
|
+
|
|
128
|
+
def disconnect
|
|
129
|
+
return if @exhausted
|
|
130
|
+
|
|
131
|
+
return unless @current_session && @current_selector
|
|
132
|
+
|
|
133
|
+
emit(:callback_connection_closed)
|
|
134
|
+
|
|
135
|
+
super
|
|
136
|
+
end
|
|
137
|
+
end
|
|
124
138
|
end
|
|
125
139
|
register_plugin :callbacks, Callbacks
|
|
126
140
|
end
|
|
@@ -48,7 +48,7 @@ module HTTPX
|
|
|
48
48
|
|
|
49
49
|
probe_response = wrap { super(request).first }
|
|
50
50
|
|
|
51
|
-
return probe_response unless probe_response.is_a?(Response)
|
|
51
|
+
return ([probe_response] * requests.size) unless probe_response.is_a?(Response)
|
|
52
52
|
|
|
53
53
|
if probe_response.status == 401 && digest.can_authenticate?(probe_response.headers["www-authenticate"])
|
|
54
54
|
request.transition(:idle)
|
|
@@ -47,6 +47,17 @@ module HTTPX
|
|
|
47
47
|
super || @state == :connecting || @state == :connected
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
+
def force_close(*)
|
|
51
|
+
if @state == :connecting
|
|
52
|
+
# proxy connect related requests should not be reenqueed
|
|
53
|
+
@parser.reset!
|
|
54
|
+
@inflight -= @parser.pending.size
|
|
55
|
+
@parser.pending.clear
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
super
|
|
59
|
+
end
|
|
60
|
+
|
|
50
61
|
private
|
|
51
62
|
|
|
52
63
|
def handle_transition(nextstate)
|
|
@@ -64,23 +75,40 @@ module HTTPX
|
|
|
64
75
|
parser = @parser
|
|
65
76
|
parser.extend(ProxyParser)
|
|
66
77
|
parser.on(:response, &method(:__http_on_connect))
|
|
67
|
-
parser.on(:close) do
|
|
78
|
+
parser.on(:close) do
|
|
68
79
|
next unless @parser
|
|
69
80
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
emit(:terminate)
|
|
73
|
-
end
|
|
81
|
+
reset
|
|
82
|
+
disconnect
|
|
74
83
|
end
|
|
75
84
|
parser.on(:reset) do
|
|
76
85
|
if parser.empty?
|
|
77
86
|
reset
|
|
78
87
|
else
|
|
79
|
-
|
|
80
|
-
|
|
88
|
+
enqueue_pending_requests_from_parser(parser)
|
|
89
|
+
|
|
90
|
+
initial_state = @state
|
|
91
|
+
|
|
92
|
+
reset
|
|
93
|
+
|
|
94
|
+
if @pending.empty?
|
|
95
|
+
@parser = nil
|
|
96
|
+
next
|
|
97
|
+
end
|
|
98
|
+
# keep parser state around due to proxy auth protocol;
|
|
99
|
+
# intermediate authenticated request is already inside
|
|
100
|
+
# the parser
|
|
101
|
+
parser = nil
|
|
102
|
+
|
|
103
|
+
if initial_state == :connecting
|
|
104
|
+
parser = @parser
|
|
105
|
+
@parser.reset
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
idling
|
|
109
|
+
|
|
110
|
+
@parser = parser
|
|
81
111
|
|
|
82
|
-
parser.reset if @parser
|
|
83
|
-
transition(:idle)
|
|
84
112
|
transition(:connecting)
|
|
85
113
|
end
|
|
86
114
|
end
|
|
@@ -130,6 +130,7 @@ module HTTPX::Plugins
|
|
|
130
130
|
response = request.options.response_class.new(request, status, version, response_headers)
|
|
131
131
|
response.original_request = original_request
|
|
132
132
|
response.finish!
|
|
133
|
+
response.mark_as_cached!
|
|
133
134
|
|
|
134
135
|
IO.copy_stream(f, response.body)
|
|
135
136
|
|
|
@@ -118,7 +118,10 @@ module HTTPX
|
|
|
118
118
|
|
|
119
119
|
response.copy_from_cached!
|
|
120
120
|
elsif request.cacheable_verb? && ResponseCache.cacheable_response?(response)
|
|
121
|
-
|
|
121
|
+
unless response.cached?
|
|
122
|
+
log { "caching response for #{request.uri}..." }
|
|
123
|
+
request.options.response_cache_store.set(request, response)
|
|
124
|
+
end
|
|
122
125
|
end
|
|
123
126
|
|
|
124
127
|
response
|
|
@@ -204,7 +207,7 @@ module HTTPX
|
|
|
204
207
|
# returns a unique cache key as a String identifying this request
|
|
205
208
|
def response_cache_key
|
|
206
209
|
@response_cache_key ||= begin
|
|
207
|
-
keys = [@verb, @uri]
|
|
210
|
+
keys = [@verb, @uri.merge(path)]
|
|
208
211
|
|
|
209
212
|
@options.supported_vary_headers.each do |field|
|
|
210
213
|
value = @headers[field]
|
|
@@ -327,6 +330,14 @@ module HTTPX
|
|
|
327
330
|
Time.now
|
|
328
331
|
end
|
|
329
332
|
end
|
|
333
|
+
|
|
334
|
+
module ResponseBodyMethods
|
|
335
|
+
def decode_chunk(chunk)
|
|
336
|
+
return chunk if @response.cached?
|
|
337
|
+
|
|
338
|
+
super
|
|
339
|
+
end
|
|
340
|
+
end
|
|
330
341
|
end
|
|
331
342
|
register_plugin :response_cache, ResponseCache
|
|
332
343
|
end
|