httpx 1.3.3 → 1.4.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/1_3_3.md +1 -1
- data/doc/release_notes/1_3_4.md +6 -0
- data/doc/release_notes/1_4_0.md +43 -0
- data/lib/httpx/adapters/faraday.rb +2 -0
- data/lib/httpx/adapters/webmock.rb +11 -5
- data/lib/httpx/callbacks.rb +0 -5
- data/lib/httpx/chainable.rb +3 -1
- data/lib/httpx/connection/http2.rb +11 -7
- data/lib/httpx/connection.rb +128 -16
- data/lib/httpx/errors.rb +12 -0
- data/lib/httpx/loggable.rb +5 -5
- data/lib/httpx/options.rb +26 -16
- data/lib/httpx/plugins/aws_sigv4.rb +31 -16
- data/lib/httpx/plugins/callbacks.rb +12 -2
- data/lib/httpx/plugins/circuit_breaker.rb +0 -5
- data/lib/httpx/plugins/content_digest.rb +202 -0
- data/lib/httpx/plugins/expect.rb +4 -3
- data/lib/httpx/plugins/follow_redirects.rb +7 -8
- data/lib/httpx/plugins/h2c.rb +23 -20
- data/lib/httpx/plugins/internal_telemetry.rb +27 -0
- data/lib/httpx/plugins/persistent.rb +16 -0
- data/lib/httpx/plugins/proxy/http.rb +17 -19
- data/lib/httpx/plugins/proxy.rb +91 -93
- data/lib/httpx/plugins/retries.rb +5 -8
- data/lib/httpx/plugins/upgrade.rb +5 -10
- data/lib/httpx/plugins/webdav.rb +6 -0
- data/lib/httpx/plugins/xml.rb +76 -0
- data/lib/httpx/pool.rb +73 -244
- data/lib/httpx/request/body.rb +16 -12
- data/lib/httpx/request.rb +1 -1
- data/lib/httpx/resolver/https.rb +12 -19
- data/lib/httpx/resolver/multi.rb +34 -16
- data/lib/httpx/resolver/native.rb +36 -13
- data/lib/httpx/resolver/resolver.rb +49 -11
- data/lib/httpx/resolver/system.rb +29 -11
- data/lib/httpx/resolver.rb +21 -14
- data/lib/httpx/response/body.rb +12 -1
- data/lib/httpx/response.rb +5 -3
- data/lib/httpx/selector.rb +164 -95
- data/lib/httpx/session.rb +296 -139
- data/lib/httpx/transcoder/gzip.rb +0 -3
- data/lib/httpx/transcoder/json.rb +14 -2
- data/lib/httpx/transcoder/multipart/encoder.rb +3 -1
- data/lib/httpx/transcoder/utils/deflater.rb +7 -4
- data/lib/httpx/transcoder/utils/inflater.rb +2 -0
- data/lib/httpx/transcoder.rb +0 -1
- data/lib/httpx/version.rb +1 -1
- data/lib/httpx.rb +19 -20
- data/sig/callbacks.rbs +0 -1
- data/sig/chainable.rbs +4 -0
- data/sig/connection/http2.rbs +1 -1
- data/sig/connection.rbs +14 -3
- data/sig/errors.rbs +6 -0
- data/sig/loggable.rbs +2 -0
- data/sig/options.rbs +7 -0
- data/sig/plugins/aws_sigv4.rbs +8 -2
- data/sig/plugins/content_digest.rbs +51 -0
- data/sig/plugins/cookies/cookie.rbs +9 -0
- data/sig/plugins/grpc/call.rbs +4 -0
- data/sig/plugins/persistent.rbs +4 -1
- data/sig/plugins/proxy/socks5.rbs +11 -3
- data/sig/plugins/proxy.rbs +18 -11
- data/sig/plugins/push_promise.rbs +3 -0
- data/sig/plugins/rate_limiter.rbs +2 -0
- data/sig/plugins/retries.rbs +1 -1
- data/sig/plugins/ssrf_filter.rbs +26 -0
- data/sig/plugins/webdav.rbs +23 -0
- data/sig/plugins/xml.rbs +37 -0
- data/sig/pool.rbs +25 -33
- data/sig/request/body.rbs +5 -1
- data/sig/resolver/multi.rbs +26 -1
- data/sig/resolver/native.rbs +0 -2
- data/sig/resolver/resolver.rbs +21 -2
- data/sig/resolver.rbs +5 -1
- data/sig/response/body.rbs +2 -2
- data/sig/response/buffer.rbs +2 -2
- data/sig/selector.rbs +30 -4
- data/sig/session.rbs +45 -18
- data/sig/transcoder/body.rbs +1 -1
- data/sig/transcoder/chunker.rbs +1 -1
- data/sig/transcoder/deflate.rbs +1 -0
- data/sig/transcoder/form.rbs +8 -0
- data/sig/transcoder/gzip.rbs +4 -1
- data/sig/transcoder/multipart.rbs +3 -3
- data/sig/transcoder/utils/body_reader.rbs +2 -2
- data/sig/transcoder/utils/deflater.rbs +2 -2
- metadata +12 -4
- data/lib/httpx/transcoder/xml.rb +0 -52
- data/sig/transcoder/xml.rbs +0 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7edc783221b5d919a1c788bf360f0cc2d43ee1556eb893c6b5d377d1bd3b4241
|
4
|
+
data.tar.gz: 14dc4593ae5c46acb33a4f242a0058e75eddec70bd22fdf75c05496240de3abc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 181bc9e6155708c2d1cdaee3dc7c5c15cd5e2b1ba8d7911b6dd5a79e1cebe38558766ec89d8446483d434c7387e330874a70633b2ed5fb8f6a9d8a3401a4abf4
|
7
|
+
data.tar.gz: 49f57c406a2a5c5be83c018c2151ef2e842c266e93d9609dff15684e662bb25580e7c84e2e96a32d1a45094c9baf8ad18f89dbab1e8235127f4d40429f733572
|
data/doc/release_notes/1_3_3.md
CHANGED
@@ -0,0 +1,43 @@
|
|
1
|
+
# 1.4.0
|
2
|
+
|
3
|
+
## Features
|
4
|
+
|
5
|
+
### `:content_digest` plugin
|
6
|
+
|
7
|
+
The `:content_digest` can be used to calculate the digest of request payloads and set them in the `"content-digest"` header; it can also validate the integrity of responses which declare the same `"content-digest"` header.
|
8
|
+
|
9
|
+
More info under https://honeyryderchuck.gitlab.io/httpx/wiki/Content-Digest
|
10
|
+
|
11
|
+
## Per-session connection pools
|
12
|
+
|
13
|
+
This architectural changes moves away from per-thread shared connection pools, and into per-session (also thread-safe) connection pools. Unlike before, this enables connections from a session to be reused across threads, as well as limiting the number of connections that can be open on a given origin peer. This fixes long-standing issues, such as reusing connections under a fiber scheduler loop (such as the one from the gem `async`).
|
14
|
+
|
15
|
+
A new `:pool_options` option is introduced, which can be passed an hash with the following sub-options:
|
16
|
+
|
17
|
+
* `:max_connections_per_origin`: maximum number of connections a pool allows (unbounded by default, for backwards compatibility).
|
18
|
+
* `:pool_timeout`: the number of seconds a session will wait for a connection to be checked out (default: 5)
|
19
|
+
|
20
|
+
More info under https://honeyryderchuck.gitlab.io/httpx/wiki/Connection-Pools
|
21
|
+
|
22
|
+
|
23
|
+
## Improvements
|
24
|
+
|
25
|
+
* `:aws_sigv4` plugin: improved digest calculation on compressed request bodies by buffering content to a tempfile.
|
26
|
+
* `HTTPX::Response#json` will parse payload from extended json MIME types (like `application/ld+json`, `application/hal+json`, ...).
|
27
|
+
|
28
|
+
## Bugfixes
|
29
|
+
|
30
|
+
* `:aws_sigv4` plugin: do not try to rewind a request body which yields chunks.
|
31
|
+
* fixed request encoding when `:json` param is passed, and the `oj` gem is used (by using the `:compat` flag).
|
32
|
+
* native resolver: on message truncation, bubble up tcp handshake errors as resolve errors.
|
33
|
+
* allow `HTTPX::Response#json` to accept extended JSON mime types (such as responses with `content-type: application/ld+json`)
|
34
|
+
|
35
|
+
## Chore
|
36
|
+
|
37
|
+
* default options are now fully frozen (in case anyone relies on overriding them).
|
38
|
+
|
39
|
+
### `:xml` plugin
|
40
|
+
|
41
|
+
XML encoding/decoding (via `:xml` request param, and `HTTPX::Response#xml`) is now available via the `:xml` plugin.
|
42
|
+
|
43
|
+
Using `HTTPX::Response#xml` without the plugin will issue a deprecation warning.
|
@@ -52,16 +52,18 @@ module WebMock
|
|
52
52
|
end
|
53
53
|
|
54
54
|
module InstanceMethods
|
55
|
-
|
56
|
-
|
55
|
+
private
|
56
|
+
|
57
|
+
def do_init_connection(connection, selector)
|
58
|
+
super
|
59
|
+
|
57
60
|
connection.once(:unmock_connection) do
|
58
61
|
unless connection.addresses
|
59
62
|
connection.__send__(:callbacks)[:connect_error].clear
|
60
|
-
|
63
|
+
deselect_connection(connection, selector)
|
61
64
|
end
|
62
|
-
|
65
|
+
resolve_connection(connection, selector)
|
63
66
|
end
|
64
|
-
connection
|
65
67
|
end
|
66
68
|
end
|
67
69
|
|
@@ -100,6 +102,10 @@ module WebMock
|
|
100
102
|
super
|
101
103
|
end
|
102
104
|
|
105
|
+
def terminate
|
106
|
+
force_reset
|
107
|
+
end
|
108
|
+
|
103
109
|
def send(request)
|
104
110
|
request_signature = Plugin.build_webmock_request_signature(request)
|
105
111
|
WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
|
data/lib/httpx/callbacks.rb
CHANGED
data/lib/httpx/chainable.rb
CHANGED
@@ -73,7 +73,7 @@ module HTTPX
|
|
73
73
|
].include?(callback)
|
74
74
|
|
75
75
|
warn "DEPRECATION WARNING: calling `.#{meth}` on plain HTTPX sessions is deprecated. " \
|
76
|
-
"Use HTTPX.plugin(:callbacks).#{meth} instead."
|
76
|
+
"Use `HTTPX.plugin(:callbacks).#{meth}` instead."
|
77
77
|
|
78
78
|
plugin(:callbacks).__send__(meth, *args, **options, &blk)
|
79
79
|
else
|
@@ -101,4 +101,6 @@ module HTTPX
|
|
101
101
|
end
|
102
102
|
end
|
103
103
|
end
|
104
|
+
|
105
|
+
extend Chainable
|
104
106
|
end
|
@@ -98,10 +98,10 @@ module HTTPX
|
|
98
98
|
@streams.size < @max_requests
|
99
99
|
end
|
100
100
|
|
101
|
-
def send(request)
|
101
|
+
def send(request, head = false)
|
102
102
|
unless can_buffer_more_requests?
|
103
|
-
@pending << request
|
104
|
-
return
|
103
|
+
head ? @pending.unshift(request) : @pending << request
|
104
|
+
return false
|
105
105
|
end
|
106
106
|
unless (stream = @streams[request])
|
107
107
|
stream = @connection.new_stream
|
@@ -113,6 +113,7 @@ module HTTPX
|
|
113
113
|
true
|
114
114
|
rescue ::HTTP2::Error::StreamLimitExceeded
|
115
115
|
@pending.unshift(request)
|
116
|
+
false
|
116
117
|
end
|
117
118
|
|
118
119
|
def consume
|
@@ -154,8 +155,7 @@ module HTTPX
|
|
154
155
|
|
155
156
|
def send_pending
|
156
157
|
while (request = @pending.shift)
|
157
|
-
|
158
|
-
break unless send(request)
|
158
|
+
break unless send(request, true)
|
159
159
|
end
|
160
160
|
end
|
161
161
|
|
@@ -327,10 +327,14 @@ module HTTPX
|
|
327
327
|
end
|
328
328
|
end
|
329
329
|
send(@pending.shift) unless @pending.empty?
|
330
|
+
|
330
331
|
return unless @streams.empty? && exhausted?
|
331
332
|
|
332
|
-
|
333
|
-
|
333
|
+
if @pending.empty?
|
334
|
+
close
|
335
|
+
else
|
336
|
+
emit(:exhausted)
|
337
|
+
end
|
334
338
|
end
|
335
339
|
|
336
340
|
def on_frame(bytes)
|
data/lib/httpx/connection.rb
CHANGED
@@ -43,13 +43,14 @@ module HTTPX
|
|
43
43
|
|
44
44
|
attr_reader :type, :io, :origin, :origins, :state, :pending, :options, :ssl_session
|
45
45
|
|
46
|
-
attr_writer :
|
46
|
+
attr_writer :current_selector, :coalesced_connection
|
47
47
|
|
48
|
-
attr_accessor :family
|
48
|
+
attr_accessor :current_session, :family
|
49
49
|
|
50
50
|
def initialize(uri, options)
|
51
|
-
@
|
52
|
-
@
|
51
|
+
@current_session = @current_selector = @coalesced_connection = nil
|
52
|
+
@exhausted = @cloned = false
|
53
|
+
|
53
54
|
@options = Options.new(options)
|
54
55
|
@type = initialize_type(uri, @options)
|
55
56
|
@origins = [uri.origin]
|
@@ -58,6 +59,7 @@ module HTTPX
|
|
58
59
|
@read_buffer = Buffer.new(@options.buffer_size)
|
59
60
|
@write_buffer = Buffer.new(@options.buffer_size)
|
60
61
|
@pending = []
|
62
|
+
|
61
63
|
on(:error, &method(:on_error))
|
62
64
|
if @options.io
|
63
65
|
# if there's an already open IO, get its
|
@@ -68,6 +70,31 @@ module HTTPX
|
|
68
70
|
else
|
69
71
|
transition(:idle)
|
70
72
|
end
|
73
|
+
on(:activate) do
|
74
|
+
@current_session.select_connection(self, @current_selector)
|
75
|
+
end
|
76
|
+
on(:close) do
|
77
|
+
next if @exhausted # it'll reset
|
78
|
+
|
79
|
+
# may be called after ":close" above, so after the connection has been checked back in.
|
80
|
+
# next unless @current_session
|
81
|
+
|
82
|
+
next unless @current_session
|
83
|
+
|
84
|
+
@current_session.deselect_connection(self, @current_selector, @cloned)
|
85
|
+
end
|
86
|
+
on(:terminate) do
|
87
|
+
next if @exhausted # it'll reset
|
88
|
+
|
89
|
+
# may be called after ":close" above, so after the connection has been checked back in.
|
90
|
+
next unless @current_session
|
91
|
+
|
92
|
+
@current_session.deselect_connection(self, @current_selector)
|
93
|
+
end
|
94
|
+
|
95
|
+
on(:altsvc) do |alt_origin, origin, alt_params|
|
96
|
+
build_altsvc_connection(alt_origin, origin, alt_params)
|
97
|
+
end
|
71
98
|
|
72
99
|
@inflight = 0
|
73
100
|
@keep_alive_timeout = @options.timeout[:keep_alive_timeout]
|
@@ -77,6 +104,10 @@ module HTTPX
|
|
77
104
|
self.addresses = @options.addresses if @options.addresses
|
78
105
|
end
|
79
106
|
|
107
|
+
def peer
|
108
|
+
@origin
|
109
|
+
end
|
110
|
+
|
80
111
|
# this is a semi-private method, to be used by the resolver
|
81
112
|
# to initiate the io object.
|
82
113
|
def addresses=(addrs)
|
@@ -168,7 +199,12 @@ module HTTPX
|
|
168
199
|
end
|
169
200
|
|
170
201
|
def inflight?
|
171
|
-
@parser &&
|
202
|
+
@parser && (
|
203
|
+
# parser may be dealing with other requests (possibly started from a different fiber)
|
204
|
+
!@parser.empty? ||
|
205
|
+
# connection may be doing connection termination handshake
|
206
|
+
!@write_buffer.empty?
|
207
|
+
)
|
172
208
|
end
|
173
209
|
|
174
210
|
def interests
|
@@ -184,6 +220,9 @@ module HTTPX
|
|
184
220
|
|
185
221
|
return @parser.interests if @parser
|
186
222
|
|
223
|
+
nil
|
224
|
+
rescue StandardError => e
|
225
|
+
emit(:error, e)
|
187
226
|
nil
|
188
227
|
end
|
189
228
|
|
@@ -205,6 +244,9 @@ module HTTPX
|
|
205
244
|
consume
|
206
245
|
end
|
207
246
|
nil
|
247
|
+
rescue StandardError => e
|
248
|
+
emit(:error, e)
|
249
|
+
raise e
|
208
250
|
end
|
209
251
|
|
210
252
|
def close
|
@@ -221,8 +263,9 @@ module HTTPX
|
|
221
263
|
|
222
264
|
# bypasses the state machine to force closing of connections still connecting.
|
223
265
|
# **only** used for Happy Eyeballs v2.
|
224
|
-
def force_reset
|
266
|
+
def force_reset(cloned = false)
|
225
267
|
@state = :closing
|
268
|
+
@cloned = cloned
|
226
269
|
transition(:closed)
|
227
270
|
end
|
228
271
|
|
@@ -235,6 +278,8 @@ module HTTPX
|
|
235
278
|
end
|
236
279
|
|
237
280
|
def send(request)
|
281
|
+
return @coalesced_connection.send(request) if @coalesced_connection
|
282
|
+
|
238
283
|
if @parser && !@write_buffer.full?
|
239
284
|
if @response_received_at && @keep_alive_timeout &&
|
240
285
|
Utils.elapsed_time(@response_received_at) > @keep_alive_timeout
|
@@ -255,6 +300,8 @@ module HTTPX
|
|
255
300
|
end
|
256
301
|
|
257
302
|
def timeout
|
303
|
+
return if @state == :closed || @state == :inactive
|
304
|
+
|
258
305
|
return @timeout if @timeout
|
259
306
|
|
260
307
|
return @options.timeout[:connect_timeout] if @state == :idle
|
@@ -301,6 +348,12 @@ module HTTPX
|
|
301
348
|
transition(:open)
|
302
349
|
end
|
303
350
|
|
351
|
+
def disconnect
|
352
|
+
emit(:close)
|
353
|
+
@current_session = nil
|
354
|
+
@current_selector = nil
|
355
|
+
end
|
356
|
+
|
304
357
|
def consume
|
305
358
|
return unless @io
|
306
359
|
|
@@ -475,8 +528,25 @@ module HTTPX
|
|
475
528
|
request.emit(:promise, parser, stream)
|
476
529
|
end
|
477
530
|
parser.on(:exhausted) do
|
531
|
+
@exhausted = true
|
532
|
+
current_session = @current_session
|
533
|
+
current_selector = @current_selector
|
534
|
+
parser.close
|
478
535
|
@pending.concat(parser.pending)
|
479
|
-
|
536
|
+
case @state
|
537
|
+
when :closed
|
538
|
+
idling
|
539
|
+
@exhausted = false
|
540
|
+
@current_session = current_session
|
541
|
+
@current_selector = current_selector
|
542
|
+
when :closing
|
543
|
+
once(:close) do
|
544
|
+
idling
|
545
|
+
@exhausted = false
|
546
|
+
@current_session = current_session
|
547
|
+
@current_selector = current_selector
|
548
|
+
end
|
549
|
+
end
|
480
550
|
end
|
481
551
|
parser.on(:origin) do |origin|
|
482
552
|
@origins |= [origin]
|
@@ -492,8 +562,14 @@ module HTTPX
|
|
492
562
|
end
|
493
563
|
parser.on(:reset) do
|
494
564
|
@pending.concat(parser.pending) unless parser.empty?
|
565
|
+
current_session = @current_session
|
566
|
+
current_selector = @current_selector
|
495
567
|
reset
|
496
|
-
|
568
|
+
unless @pending.empty?
|
569
|
+
idling
|
570
|
+
@current_session = current_session
|
571
|
+
@current_selector = current_selector
|
572
|
+
end
|
497
573
|
end
|
498
574
|
parser.on(:current_timeout) do
|
499
575
|
@current_timeout = @timeout = parser.timeout
|
@@ -504,7 +580,15 @@ module HTTPX
|
|
504
580
|
parser.on(:error) do |request, ex|
|
505
581
|
case ex
|
506
582
|
when MisdirectedRequestError
|
507
|
-
|
583
|
+
current_session = @current_session
|
584
|
+
current_selector = @current_selector
|
585
|
+
parser.close
|
586
|
+
|
587
|
+
other_connection = current_session.find_connection(@origin, current_selector,
|
588
|
+
@options.merge(ssl: { alpn_protocols: %w[http/1.1] }))
|
589
|
+
other_connection.merge(self)
|
590
|
+
request.transition(:idle)
|
591
|
+
other_connection.send(request)
|
508
592
|
else
|
509
593
|
response = ErrorResponse.new(request, ex)
|
510
594
|
request.response = response
|
@@ -531,13 +615,13 @@ module HTTPX
|
|
531
615
|
error.set_backtrace(e.backtrace)
|
532
616
|
connecting? && callbacks_for?(:connect_error) ? emit(:connect_error, error) : handle_error(error)
|
533
617
|
@state = :closed
|
534
|
-
|
618
|
+
disconnect
|
535
619
|
rescue TLSError, ::HTTP2::Error::ProtocolError, ::HTTP2::Error::HandshakeError => e
|
536
620
|
# connect errors, exit gracefully
|
537
621
|
handle_error(e)
|
538
622
|
connecting? && callbacks_for?(:connect_error) ? emit(:connect_error, e) : handle_error(e)
|
539
623
|
@state = :closed
|
540
|
-
|
624
|
+
disconnect
|
541
625
|
end
|
542
626
|
|
543
627
|
def handle_transition(nextstate)
|
@@ -582,7 +666,7 @@ module HTTPX
|
|
582
666
|
return unless @write_buffer.empty?
|
583
667
|
|
584
668
|
purge_after_closed
|
585
|
-
|
669
|
+
disconnect if @pending.empty?
|
586
670
|
when :already_open
|
587
671
|
nextstate = :open
|
588
672
|
# the first check for given io readiness must still use a timeout.
|
@@ -617,12 +701,40 @@ module HTTPX
|
|
617
701
|
end
|
618
702
|
end
|
619
703
|
|
704
|
+
# returns an HTTPX::Connection for the negotiated Alternative Service (or none).
|
705
|
+
def build_altsvc_connection(alt_origin, origin, alt_params)
|
706
|
+
# do not allow security downgrades on altsvc negotiation
|
707
|
+
return if @origin.scheme == "https" && alt_origin.scheme != "https"
|
708
|
+
|
709
|
+
altsvc = AltSvc.cached_altsvc_set(origin, alt_params.merge("origin" => alt_origin))
|
710
|
+
|
711
|
+
# altsvc already exists, somehow it wasn't advertised, probably noop
|
712
|
+
return unless altsvc
|
713
|
+
|
714
|
+
alt_options = @options.merge(ssl: @options.ssl.merge(hostname: URI(origin).host))
|
715
|
+
|
716
|
+
connection = @current_session.find_connection(alt_origin, @current_selector, alt_options)
|
717
|
+
|
718
|
+
# advertised altsvc is the same origin being used, ignore
|
719
|
+
return if connection == self
|
720
|
+
|
721
|
+
connection.extend(AltSvc::ConnectionMixin) unless connection.is_a?(AltSvc::ConnectionMixin)
|
722
|
+
|
723
|
+
log(level: 1) { "#{origin} alt-svc: #{alt_origin}" }
|
724
|
+
|
725
|
+
connection.merge(self)
|
726
|
+
terminate
|
727
|
+
rescue UnsupportedSchemeError
|
728
|
+
altsvc["noop"] = true
|
729
|
+
nil
|
730
|
+
end
|
731
|
+
|
620
732
|
def build_socket(addrs = nil)
|
621
733
|
case @type
|
622
734
|
when "tcp"
|
623
|
-
TCP.new(
|
735
|
+
TCP.new(peer, addrs, @options)
|
624
736
|
when "ssl"
|
625
|
-
SSL.new(
|
737
|
+
SSL.new(peer, addrs, @options) do |sock|
|
626
738
|
sock.ssl_session = @ssl_session
|
627
739
|
sock.session_new_cb do |sess|
|
628
740
|
@ssl_session = sess
|
@@ -635,7 +747,7 @@ module HTTPX
|
|
635
747
|
|
636
748
|
path = String(path) if path
|
637
749
|
|
638
|
-
UNIX.new(
|
750
|
+
UNIX.new(peer, path, @options)
|
639
751
|
else
|
640
752
|
raise Error, "unsupported transport (#{@type})"
|
641
753
|
end
|
@@ -734,7 +846,7 @@ module HTTPX
|
|
734
846
|
|
735
847
|
def set_request_timeout(request, timeout, start_event, finish_events, &callback)
|
736
848
|
request.once(start_event) do
|
737
|
-
interval = @
|
849
|
+
interval = @current_selector.after(timeout, callback)
|
738
850
|
|
739
851
|
Array(finish_events).each do |event|
|
740
852
|
# clean up request timeouts if the connection errors out
|
data/lib/httpx/errors.rb
CHANGED
@@ -29,6 +29,18 @@ module HTTPX
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
+
# Raise when it can't acquire a connection for a given origin.
|
33
|
+
class PoolTimeoutError < TimeoutError
|
34
|
+
attr_reader :origin
|
35
|
+
|
36
|
+
# initializes the +origin+ it refers to, and the
|
37
|
+
# +timeout+ causing the error.
|
38
|
+
def initialize(origin, timeout)
|
39
|
+
@origin = origin
|
40
|
+
super(timeout, "Timed out after #{timeout} seconds while waiting for a connection to #{origin}")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
32
44
|
# Error raised when there was a timeout establishing the connection to a server.
|
33
45
|
# This may be raised due to timeouts during TCP and TLS (when applicable) connection
|
34
46
|
# establishment.
|
data/lib/httpx/loggable.rb
CHANGED
@@ -13,11 +13,14 @@ module HTTPX
|
|
13
13
|
white: 37,
|
14
14
|
}.freeze
|
15
15
|
|
16
|
+
USE_DEBUG_LOG = ENV.key?("HTTPX_DEBUG")
|
17
|
+
|
16
18
|
def log(level: @options.debug_level, color: nil, &msg)
|
17
|
-
return unless @options.debug
|
18
19
|
return unless @options.debug_level >= level
|
19
20
|
|
20
|
-
debug_stream = @options.debug
|
21
|
+
debug_stream = @options.debug || ($stderr if USE_DEBUG_LOG)
|
22
|
+
|
23
|
+
return unless debug_stream
|
21
24
|
|
22
25
|
message = (+"" << msg.call << "\n")
|
23
26
|
message = "\e[#{COLORS[color]}m#{message}\e[0m" if color && debug_stream.respond_to?(:isatty) && debug_stream.isatty
|
@@ -25,9 +28,6 @@ module HTTPX
|
|
25
28
|
end
|
26
29
|
|
27
30
|
def log_exception(ex, level: @options.debug_level, color: nil)
|
28
|
-
return unless @options.debug
|
29
|
-
return unless @options.debug_level >= level
|
30
|
-
|
31
31
|
log(level: level, color: color) { ex.full_message }
|
32
32
|
end
|
33
33
|
end
|
data/lib/httpx/options.rb
CHANGED
@@ -25,14 +25,14 @@ module HTTPX
|
|
25
25
|
end
|
26
26
|
rescue NotImplementedError
|
27
27
|
[Socket::AF_INET]
|
28
|
-
end
|
28
|
+
end.freeze
|
29
29
|
|
30
30
|
DEFAULT_OPTIONS = {
|
31
31
|
:max_requests => Float::INFINITY,
|
32
|
-
:debug =>
|
32
|
+
:debug => nil,
|
33
33
|
:debug_level => (ENV["HTTPX_DEBUG"] || 1).to_i,
|
34
|
-
:ssl =>
|
35
|
-
:http2_settings => { settings_enable_push: 0 },
|
34
|
+
:ssl => EMPTY_HASH,
|
35
|
+
:http2_settings => { settings_enable_push: 0 }.freeze,
|
36
36
|
:fallback_protocol => "http/1.1",
|
37
37
|
:supported_compression_formats => %w[gzip deflate],
|
38
38
|
:decompress_response_body => true,
|
@@ -56,13 +56,15 @@ module HTTPX
|
|
56
56
|
:response_class => Class.new(Response),
|
57
57
|
:request_body_class => Class.new(Request::Body),
|
58
58
|
:response_body_class => Class.new(Response::Body),
|
59
|
+
:pool_class => Class.new(Pool),
|
59
60
|
:connection_class => Class.new(Connection),
|
60
61
|
:options_class => Class.new(self),
|
61
62
|
:transport => nil,
|
62
63
|
:addresses => nil,
|
63
64
|
:persistent => false,
|
64
65
|
:resolver_class => (ENV["HTTPX_RESOLVER"] || :native).to_sym,
|
65
|
-
:resolver_options => { cache: true },
|
66
|
+
:resolver_options => { cache: true }.freeze,
|
67
|
+
:pool_options => EMPTY_HASH,
|
66
68
|
:ip_families => ip_address_families,
|
67
69
|
}.freeze
|
68
70
|
|
@@ -110,6 +112,7 @@ module HTTPX
|
|
110
112
|
# :request_body_class :: class used to instantiate a request body
|
111
113
|
# :response_body_class :: class used to instantiate a response body
|
112
114
|
# :connection_class :: class used to instantiate connections
|
115
|
+
# :pool_class :: class used to instantiate the session connection pool
|
113
116
|
# :options_class :: class used to instantiate options
|
114
117
|
# :transport :: type of transport to use (set to "unix" for UNIX sockets)
|
115
118
|
# :addresses :: bucket of peer addresses (can be a list of IP addresses, a hash of domain to list of adddresses;
|
@@ -118,7 +121,8 @@ module HTTPX
|
|
118
121
|
# :persistent :: whether to persist connections in between requests (defaults to <tt>true</tt>)
|
119
122
|
# :resolver_class :: which resolver to use (defaults to <tt>:native</tt>, can also be <tt>:system<tt> for
|
120
123
|
# using getaddrinfo or <tt>:https</tt> for DoH resolver, or a custom class)
|
121
|
-
# :resolver_options :: hash of options passed to the resolver
|
124
|
+
# :resolver_options :: hash of options passed to the resolver. Accepted keys depend on the resolver type.
|
125
|
+
# :pool_options :: hash of options passed to the connection pool (See Pool#initialize).
|
122
126
|
# :ip_families :: which socket families are supported (system-dependent)
|
123
127
|
# :origin :: HTTP origin to set on requests with relative path (ex: "https://api.serv.com")
|
124
128
|
# :base_path :: path to prefix given relative paths with (ex: "/v2")
|
@@ -215,6 +219,7 @@ module HTTPX
|
|
215
219
|
ssl http2_settings
|
216
220
|
request_class response_class headers_class request_body_class
|
217
221
|
response_body_class connection_class options_class
|
222
|
+
pool_class pool_options
|
218
223
|
io fallback_protocol debug debug_level resolver_class resolver_options
|
219
224
|
compress_request_body decompress_response_body
|
220
225
|
persistent
|
@@ -246,14 +251,6 @@ module HTTPX
|
|
246
251
|
end
|
247
252
|
end
|
248
253
|
|
249
|
-
OTHER_LOOKUP = ->(obj, k, ivar_map) {
|
250
|
-
case obj
|
251
|
-
when Hash
|
252
|
-
obj[ivar_map[k]]
|
253
|
-
else
|
254
|
-
obj.instance_variable_get(k)
|
255
|
-
end
|
256
|
-
}
|
257
254
|
def merge(other)
|
258
255
|
ivar_map = nil
|
259
256
|
other_ivars = case other
|
@@ -266,12 +263,12 @@ module HTTPX
|
|
266
263
|
|
267
264
|
return self if other_ivars.empty?
|
268
265
|
|
269
|
-
return self if other_ivars.all? { |ivar| instance_variable_get(ivar) ==
|
266
|
+
return self if other_ivars.all? { |ivar| instance_variable_get(ivar) == access_option(other, ivar, ivar_map) }
|
270
267
|
|
271
268
|
opts = dup
|
272
269
|
|
273
270
|
other_ivars.each do |ivar|
|
274
|
-
v =
|
271
|
+
v = access_option(other, ivar, ivar_map)
|
275
272
|
|
276
273
|
unless v
|
277
274
|
opts.instance_variable_set(ivar, v)
|
@@ -322,6 +319,10 @@ module HTTPX
|
|
322
319
|
@response_body_class.__send__(:include, pl::ResponseBodyMethods) if defined?(pl::ResponseBodyMethods)
|
323
320
|
@response_body_class.extend(pl::ResponseBodyClassMethods) if defined?(pl::ResponseBodyClassMethods)
|
324
321
|
end
|
322
|
+
if defined?(pl::PoolMethods)
|
323
|
+
@pool_class = @pool_class.dup
|
324
|
+
@pool_class.__send__(:include, pl::PoolMethods)
|
325
|
+
end
|
325
326
|
if defined?(pl::ConnectionMethods)
|
326
327
|
@connection_class = @connection_class.dup
|
327
328
|
@connection_class.__send__(:include, pl::ConnectionMethods)
|
@@ -346,5 +347,14 @@ module HTTPX
|
|
346
347
|
instance_variable_set(:"@#{k}", value)
|
347
348
|
end
|
348
349
|
end
|
350
|
+
|
351
|
+
def access_option(obj, k, ivar_map)
|
352
|
+
case obj
|
353
|
+
when Hash
|
354
|
+
obj[ivar_map[k]]
|
355
|
+
else
|
356
|
+
obj.instance_variable_get(k)
|
357
|
+
end
|
358
|
+
end
|
349
359
|
end
|
350
360
|
end
|