httpx 1.5.1 → 1.6.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_6_0.md +50 -0
- data/lib/httpx/adapters/datadog.rb +23 -13
- data/lib/httpx/adapters/faraday.rb +14 -9
- data/lib/httpx/adapters/webmock.rb +1 -1
- data/lib/httpx/callbacks.rb +1 -1
- data/lib/httpx/connection/http1.rb +5 -6
- data/lib/httpx/connection/http2.rb +30 -12
- data/lib/httpx/connection.rb +17 -24
- data/lib/httpx/errors.rb +3 -1
- data/lib/httpx/io/ssl.rb +1 -4
- data/lib/httpx/io/tcp.rb +25 -16
- data/lib/httpx/io/unix.rb +4 -3
- data/lib/httpx/loggable.rb +4 -1
- data/lib/httpx/options.rb +252 -158
- data/lib/httpx/plugins/aws_sdk_authentication.rb +2 -0
- data/lib/httpx/plugins/aws_sigv4.rb +2 -0
- data/lib/httpx/plugins/callbacks.rb +13 -1
- data/lib/httpx/plugins/circuit_breaker.rb +2 -0
- data/lib/httpx/plugins/content_digest.rb +2 -0
- data/lib/httpx/plugins/cookies.rb +2 -2
- data/lib/httpx/plugins/digest_auth.rb +2 -0
- data/lib/httpx/plugins/expect.rb +2 -0
- data/lib/httpx/plugins/fiber_concurrency.rb +195 -0
- data/lib/httpx/plugins/follow_redirects.rb +2 -0
- data/lib/httpx/plugins/grpc.rb +2 -0
- data/lib/httpx/plugins/h2c.rb +26 -16
- data/lib/httpx/plugins/internal_telemetry.rb +0 -49
- data/lib/httpx/plugins/ntlm_auth.rb +2 -0
- data/lib/httpx/plugins/oauth.rb +2 -0
- data/lib/httpx/plugins/persistent.rb +27 -18
- data/lib/httpx/plugins/proxy/socks4.rb +1 -1
- data/lib/httpx/plugins/proxy/socks5.rb +1 -1
- data/lib/httpx/plugins/proxy/ssh.rb +2 -0
- data/lib/httpx/plugins/proxy.rb +61 -20
- data/lib/httpx/plugins/response_cache/file_store.rb +2 -2
- data/lib/httpx/plugins/response_cache.rb +2 -0
- data/lib/httpx/plugins/retries.rb +2 -0
- data/lib/httpx/plugins/ssrf_filter.rb +2 -2
- data/lib/httpx/plugins/stream_bidi.rb +3 -3
- data/lib/httpx/plugins/upgrade/h2.rb +11 -1
- data/lib/httpx/plugins/upgrade.rb +8 -0
- data/lib/httpx/pool.rb +15 -10
- data/lib/httpx/request/body.rb +8 -3
- data/lib/httpx/request.rb +22 -11
- data/lib/httpx/resolver/entry.rb +30 -0
- data/lib/httpx/resolver/https.rb +3 -1
- data/lib/httpx/resolver/multi.rb +5 -2
- data/lib/httpx/resolver/native.rb +15 -6
- data/lib/httpx/resolver/resolver.rb +3 -5
- data/lib/httpx/resolver/system.rb +1 -1
- data/lib/httpx/resolver.rb +34 -21
- data/lib/httpx/response/body.rb +1 -1
- data/lib/httpx/response/buffer.rb +13 -18
- data/lib/httpx/selector.rb +92 -34
- data/lib/httpx/session.rb +89 -30
- data/lib/httpx/session_extensions.rb +3 -2
- data/lib/httpx/transcoder/form.rb +1 -13
- data/lib/httpx/transcoder/multipart/mime_type_detector.rb +1 -1
- data/lib/httpx/transcoder/multipart.rb +14 -0
- data/lib/httpx/transcoder/utils/deflater.rb +1 -1
- data/lib/httpx/version.rb +1 -1
- data/sig/callbacks.rbs +1 -1
- data/sig/chainable.rbs +1 -0
- data/sig/connection/http1.rbs +2 -0
- data/sig/connection/http2.rbs +4 -0
- data/sig/connection.rbs +6 -6
- data/sig/errors.rbs +3 -1
- data/sig/io/ssl.rbs +1 -1
- data/sig/io/tcp.rbs +13 -7
- data/sig/io/udp.rbs +7 -2
- data/sig/io/unix.rbs +0 -1
- data/sig/io.rbs +0 -3
- data/sig/options.rbs +63 -10
- data/sig/plugins/fiber_concurrency.rbs +51 -0
- data/sig/plugins/h2c.rbs +5 -1
- data/sig/plugins/persistent.rbs +1 -1
- data/sig/plugins/proxy/socks4.rbs +1 -1
- data/sig/plugins/proxy/socks5.rbs +1 -1
- data/sig/plugins/proxy.rbs +5 -2
- data/sig/plugins/ssrf_filter.rbs +1 -1
- data/sig/plugins/stream_bidi.rbs +2 -2
- data/sig/request.rbs +4 -1
- data/sig/resolver/entry.rbs +13 -0
- data/sig/resolver/native.rbs +1 -0
- data/sig/resolver/resolver.rbs +2 -3
- data/sig/resolver/system.rbs +2 -2
- data/sig/resolver.rbs +10 -11
- data/sig/response.rbs +2 -2
- data/sig/selector.rbs +18 -10
- data/sig/session.rbs +4 -0
- data/sig/transcoder/form.rbs +3 -3
- data/sig/transcoder/multipart.rbs +9 -3
- metadata +9 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 69e60280aa195822960c54a6e90e29a2dfc56cd400332fb39fa9747e14028595
|
4
|
+
data.tar.gz: 4395c88ede0b567c52c1cb67afcb11535cf4c0f797210b05b3771e884cb77d69
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f0bdc947b9b401a5fdecd664ca857d75a36f4837d945cd7d4aff723e4ba2397c48faf5fb16b8a36265c8b82caaa772ad08776771ce48078000fc8e882ffe296a
|
7
|
+
data.tar.gz: 953d4930cdb8939af913fbdc16c034ffa2cd53e59c95bae61210e1b1a8e6afb82e3e0f83d590141acdecb1161e2cd067e9d0fde43bab696e222e3b955659a8b3
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# 1.6.0
|
2
|
+
|
3
|
+
## Features
|
4
|
+
|
5
|
+
### `:fiber_concurrency` plugin
|
6
|
+
|
7
|
+
While simple use cases of `httpx` being used inside a fiber scheduler (such as [async](github.com/socketry/async)) worked, a serious of issues were identified when connections were reused across fibers for multiple requests (such as when using the `:persistent` plugin). The `:fiber_concurrency` plugin fixes that, by bookkeeping which requests are being used in which fibers in order to avoid spurious wakeups and busy loops.
|
8
|
+
|
9
|
+
This plugin is loaded by default when using the `:persistent` plugin, and by extension the `faraday` adapter.
|
10
|
+
|
11
|
+
You can read more about it in https://honeyryderchuck.gitlab.io/httpx/wiki/Fiber-Concurrency
|
12
|
+
|
13
|
+
## Improvements
|
14
|
+
|
15
|
+
* proxy errors are now retriable (when using the `:retries` plugin alongside the `:proxy` plugin).
|
16
|
+
* several options improvements:
|
17
|
+
* improve initialization by caching options names in the class, which facilitates predictable option ivar initialization, which avoids "too many shapes" performance penalty.
|
18
|
+
* when using `HTTPX::Options#merge`, enforce usage of Hash or `HTTPX::Options` object as the argument to merge with, instead of silently ignoring when none of the former.
|
19
|
+
* `option_` setter methods are now private.
|
20
|
+
* all options ivars are frozen on initialize, which enforces the immutability guarantees (**note**: in case you were relying on it not being truly mutable, this may break your code. Try either passing immutable values to options, or instead use procs).
|
21
|
+
* several selector loop improvements:
|
22
|
+
* move selectable checks a layer above to avoid calling `IO.select` with a single socket at all costs.
|
23
|
+
* Improve connection interest calculation to reduce spurious wakeups.
|
24
|
+
* skip early when finding closed selectables
|
25
|
+
* improve connection initialization to avoid "too many shapes" performance penalty.
|
26
|
+
* Plugins are now able to extend the functionality of both HTTP1 and HTTP2 connections, as well as resolvers.
|
27
|
+
* see instructions in the [custom lugins wiki](https://gitlab.com/os85/httpx/-/wikis/Custom-Plugins).
|
28
|
+
* IP addresses which have expired their TTL (from the respective DNS answer) will be invalidated and force a new name resolution on operations requiring it, such as reconnections or cache lookups.
|
29
|
+
* connections are removed from errors which store it internally (for internal purposes) before returning the respective error response (so that it can be garbage collected).
|
30
|
+
|
31
|
+
## Bugfixes
|
32
|
+
|
33
|
+
* remove check for non-unique local ipv6 which was preventing Happy Eyeballs v2 from kicking in.
|
34
|
+
* recover from "network unreachable" errors triggered by using a cached IP.
|
35
|
+
* dealing with requests which are rerouted for retries in the main session response handling loop.
|
36
|
+
* `datadog` adapter: compatibility with support versions under 1.13.0 is working again.
|
37
|
+
* http2 connection: fix calculation when connection closes and there's no termination handshake
|
38
|
+
* `callback_for`: check for existence of ivar `@callbacks` first.
|
39
|
+
* native resolver: do not buffer DNS query if waiting on a previous reply (even across fibers).
|
40
|
+
* http2: do not allow deactivating connections which finished all requests but are still waiting for the ack of the PING frame (which can happen in a multi-fiber usage scenario).
|
41
|
+
* pool: fix when connection is acquired after waiting on it; return it immediately, instead of bookkeeping on `:max_connections` (when defined).
|
42
|
+
* session: fix deactivation flow, when connections get deregistered from selector and removed from the array being iterated on.
|
43
|
+
* `:proxy` plugin: fix ssl reconnection such as, on close, the IO downgrades to the respective tcp socket, so that reconnection handshake starts from scratch with the original IO.
|
44
|
+
* `:persistent` plugin: calling `HTTPX::Session#close` will close selectors from other threads (instead of just the thread it was called on).
|
45
|
+
* `:callbacks` plugin: propagate callbacks to "derived-via `.plugin`" sessions.
|
46
|
+
* `:callbacks` plugin: do not trigger the `on_response_completed` callback if there was an error (as that's what the `on_request_error` callback is for).
|
47
|
+
|
48
|
+
## Chore
|
49
|
+
|
50
|
+
* decoupled form and multipart transcoders, moved "form or multipart" check to request body.
|
@@ -13,7 +13,11 @@ module Datadog::Tracing
|
|
13
13
|
|
14
14
|
TYPE_OUTBOUND = Datadog::Tracing::Metadata::Ext::HTTP::TYPE_OUTBOUND
|
15
15
|
|
16
|
-
TAG_BASE_SERVICE =
|
16
|
+
TAG_BASE_SERVICE = if Gem::Version.new(DATADOG_VERSION::STRING) < Gem::Version.new("1.15.0")
|
17
|
+
"_dd.base_service"
|
18
|
+
else
|
19
|
+
Datadog::Tracing::Contrib::Ext::Metadata::TAG_BASE_SERVICE
|
20
|
+
end
|
17
21
|
TAG_PEER_HOSTNAME = Datadog::Tracing::Metadata::Ext::TAG_PEER_HOSTNAME
|
18
22
|
|
19
23
|
TAG_KIND = Datadog::Tracing::Metadata::Ext::TAG_KIND
|
@@ -64,16 +68,7 @@ module Datadog::Tracing
|
|
64
68
|
end
|
65
69
|
|
66
70
|
request.on(:response) do |response|
|
67
|
-
|
68
|
-
next unless response.is_a?(::HTTPX::ErrorResponse) && response.error.respond_to?(:connection)
|
69
|
-
|
70
|
-
# handles the case when the +error+ happened during name resolution, which means
|
71
|
-
# that the tracing start point hasn't been triggered yet; in such cases, the approximate
|
72
|
-
# initial resolving time is collected from the connection, and used as span start time,
|
73
|
-
# and the tracing object in inserted before the on response callback is called.
|
74
|
-
span = initialize_span(request, response.error.connection.init_time)
|
75
|
-
|
76
|
-
end
|
71
|
+
span = initialize_span(request, request.init_time) if !span && request.init_time
|
77
72
|
|
78
73
|
finish(response, span)
|
79
74
|
end
|
@@ -89,7 +84,7 @@ module Datadog::Tracing
|
|
89
84
|
|
90
85
|
span.set_tags(
|
91
86
|
Datadog.configuration.tracing.header_tags.response_tags(response.headers.to_h)
|
92
|
-
)
|
87
|
+
) if Datadog.configuration.tracing.respond_to?(:header_tags)
|
93
88
|
end
|
94
89
|
|
95
90
|
span.finish
|
@@ -139,7 +134,7 @@ module Datadog::Tracing
|
|
139
134
|
|
140
135
|
span.set_tags(
|
141
136
|
Datadog.configuration.tracing.header_tags.request_tags(request.headers.to_h)
|
142
|
-
)
|
137
|
+
) if Datadog.configuration.tracing.respond_to?(:header_tags)
|
143
138
|
|
144
139
|
span
|
145
140
|
rescue StandardError => e
|
@@ -185,14 +180,29 @@ module Datadog::Tracing
|
|
185
180
|
end
|
186
181
|
|
187
182
|
module RequestMethods
|
183
|
+
attr_reader :init_time
|
184
|
+
|
188
185
|
# intercepts request initialization to inject the tracing logic.
|
189
186
|
def initialize(*)
|
190
187
|
super
|
191
188
|
|
189
|
+
@init_time = nil
|
190
|
+
|
192
191
|
return unless Datadog::Tracing.enabled?
|
193
192
|
|
194
193
|
RequestTracer.call(self)
|
195
194
|
end
|
195
|
+
|
196
|
+
def response=(response)
|
197
|
+
if response.is_a?(::HTTPX::ErrorResponse) && response.error.respond_to?(:connection)
|
198
|
+
# handles the case when the +error+ happened during name resolution, which means
|
199
|
+
# that the tracing start point hasn't been triggered yet; in such cases, the approximate
|
200
|
+
# initial resolving time is collected from the connection, and used as span start time,
|
201
|
+
# and the tracing object in inserted before the on response callback is called.
|
202
|
+
@init_time = response.error.connection.init_time
|
203
|
+
end
|
204
|
+
super
|
205
|
+
end
|
196
206
|
end
|
197
207
|
|
198
208
|
module ConnectionMethods
|
@@ -7,9 +7,14 @@ require "faraday"
|
|
7
7
|
module Faraday
|
8
8
|
class Adapter
|
9
9
|
class HTTPX < Faraday::Adapter
|
10
|
+
def initialize(app = nil, opts = {}, &block)
|
11
|
+
@connection = @bind = nil
|
12
|
+
super(app, opts, &block)
|
13
|
+
end
|
14
|
+
|
10
15
|
module RequestMixin
|
11
16
|
def build_connection(env)
|
12
|
-
return @connection if
|
17
|
+
return @connection if @connection
|
13
18
|
|
14
19
|
@connection = ::HTTPX.plugin(:persistent).plugin(ReasonPlugin)
|
15
20
|
@connection = @connection.with(@connection_options) unless @connection_options.empty?
|
@@ -54,6 +59,8 @@ module Faraday
|
|
54
59
|
Errno::EPIPE,
|
55
60
|
::HTTPX::ConnectionError => e
|
56
61
|
raise Faraday::ConnectionFailed, e
|
62
|
+
rescue ::HTTPX::TimeoutError => e
|
63
|
+
raise Faraday::TimeoutError, e
|
57
64
|
end
|
58
65
|
|
59
66
|
def build_request(env)
|
@@ -125,9 +132,11 @@ module Faraday
|
|
125
132
|
def response=(response)
|
126
133
|
super
|
127
134
|
|
128
|
-
return
|
135
|
+
return unless @response
|
136
|
+
|
137
|
+
return if @response.is_a?(::HTTPX::ErrorResponse)
|
129
138
|
|
130
|
-
response.body.on_data = @response_on_data
|
139
|
+
@response.body.on_data = @response_on_data
|
131
140
|
end
|
132
141
|
end
|
133
142
|
|
@@ -218,10 +227,10 @@ module Faraday
|
|
218
227
|
handler.on_complete.call(handler.env) if handler.on_complete
|
219
228
|
end
|
220
229
|
end
|
221
|
-
rescue ::HTTPX::TimeoutError => e
|
222
|
-
raise Faraday::TimeoutError, e
|
223
230
|
end
|
224
231
|
|
232
|
+
private
|
233
|
+
|
225
234
|
# from Faraday::Adapter#connection
|
226
235
|
def connection(env)
|
227
236
|
conn = build_connection(env)
|
@@ -230,8 +239,6 @@ module Faraday
|
|
230
239
|
yield conn
|
231
240
|
end
|
232
241
|
|
233
|
-
private
|
234
|
-
|
235
242
|
# from Faraday::Adapter#request_timeout
|
236
243
|
def request_timeout(type, options)
|
237
244
|
key = Faraday::Adapter::TIMEOUT_KEYS[type]
|
@@ -284,8 +291,6 @@ module Faraday
|
|
284
291
|
response.raise_for_status unless response.is_a?(::HTTPX::Response)
|
285
292
|
response
|
286
293
|
end
|
287
|
-
rescue ::HTTPX::TimeoutError => e
|
288
|
-
raise Faraday::TimeoutError, e
|
289
294
|
end
|
290
295
|
|
291
296
|
def parallel?(env)
|
data/lib/httpx/callbacks.rb
CHANGED
@@ -31,12 +31,7 @@ module HTTPX
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def interests
|
34
|
-
|
35
|
-
return :r if @request
|
36
|
-
|
37
|
-
return if @requests.empty?
|
38
|
-
|
39
|
-
request = @requests.first
|
34
|
+
request = @request || @requests.first
|
40
35
|
|
41
36
|
return unless request
|
42
37
|
|
@@ -229,6 +224,10 @@ module HTTPX
|
|
229
224
|
emit(:exhausted)
|
230
225
|
end
|
231
226
|
|
227
|
+
def waiting_for_ping?
|
228
|
+
false
|
229
|
+
end
|
230
|
+
|
232
231
|
private
|
233
232
|
|
234
233
|
def manage_connection(request, response)
|
@@ -35,7 +35,7 @@ module HTTPX
|
|
35
35
|
@settings = @options.http2_settings
|
36
36
|
@pending = []
|
37
37
|
@streams = {}
|
38
|
-
@drains
|
38
|
+
@drains = {}
|
39
39
|
@pings = []
|
40
40
|
@buffer = buffer
|
41
41
|
@handshake_completed = false
|
@@ -52,12 +52,11 @@ module HTTPX
|
|
52
52
|
end
|
53
53
|
|
54
54
|
def interests
|
55
|
-
# waiting for WINDOW_UPDATE frames
|
56
|
-
return :r if @buffer.full?
|
57
|
-
|
58
55
|
if @connection.state == :closed
|
59
56
|
return unless @handshake_completed
|
60
57
|
|
58
|
+
return if @buffer.empty?
|
59
|
+
|
61
60
|
return :w
|
62
61
|
end
|
63
62
|
|
@@ -65,6 +64,13 @@ module HTTPX
|
|
65
64
|
return @buffer.empty? ? :r : :rw
|
66
65
|
end
|
67
66
|
|
67
|
+
unless @connection.send_buffer.empty?
|
68
|
+
return :rw unless @buffer.empty?
|
69
|
+
|
70
|
+
# waiting for WINDOW_UPDATE frames
|
71
|
+
return :r
|
72
|
+
end
|
73
|
+
|
68
74
|
return :w if !@pending.empty? && can_buffer_more_requests?
|
69
75
|
|
70
76
|
return :w unless @drains.empty?
|
@@ -72,10 +78,10 @@ module HTTPX
|
|
72
78
|
if @buffer.empty?
|
73
79
|
return if @streams.empty? && @pings.empty?
|
74
80
|
|
75
|
-
|
81
|
+
:r
|
82
|
+
else
|
83
|
+
:w
|
76
84
|
end
|
77
|
-
|
78
|
-
:rw
|
79
85
|
end
|
80
86
|
|
81
87
|
def close
|
@@ -151,6 +157,10 @@ module HTTPX
|
|
151
157
|
@pings << ping
|
152
158
|
end
|
153
159
|
|
160
|
+
def waiting_for_ping?
|
161
|
+
@pings.any?
|
162
|
+
end
|
163
|
+
|
154
164
|
private
|
155
165
|
|
156
166
|
def can_buffer_more_requests?
|
@@ -229,7 +239,7 @@ module HTTPX
|
|
229
239
|
end
|
230
240
|
|
231
241
|
log(level: 1, color: :yellow) do
|
232
|
-
request.headers.merge(extra_headers).each.map { |k, v| "#{stream.id}: -> HEADER: #{k}: #{log_redact(v)}" }.join("\n")
|
242
|
+
"\n#{request.headers.merge(extra_headers).each.map { |k, v| "#{stream.id}: -> HEADER: #{k}: #{log_redact(v)}" }.join("\n")}"
|
233
243
|
end
|
234
244
|
stream.headers(request.headers.each(extra_headers), end_stream: request.body.empty?)
|
235
245
|
end
|
@@ -323,8 +333,7 @@ module HTTPX
|
|
323
333
|
return if error == :stream_closed && !@streams.key?(request)
|
324
334
|
|
325
335
|
log(level: 2) { "#{stream.id}: closing stream" }
|
326
|
-
|
327
|
-
@streams.delete(request)
|
336
|
+
teardown(request)
|
328
337
|
|
329
338
|
if error
|
330
339
|
case error
|
@@ -379,8 +388,7 @@ module HTTPX
|
|
379
388
|
when :no_error
|
380
389
|
ex = GoawayError.new
|
381
390
|
@pending.unshift(*@streams.keys)
|
382
|
-
|
383
|
-
@streams.clear
|
391
|
+
teardown
|
384
392
|
else
|
385
393
|
ex = Error.new(0, error)
|
386
394
|
end
|
@@ -448,5 +456,15 @@ module HTTPX
|
|
448
456
|
|
449
457
|
emit(:pong)
|
450
458
|
end
|
459
|
+
|
460
|
+
def teardown(request = nil)
|
461
|
+
if request
|
462
|
+
@drains.delete(request)
|
463
|
+
@streams.delete(request)
|
464
|
+
else
|
465
|
+
@drains.clear
|
466
|
+
@streams.clear
|
467
|
+
end
|
468
|
+
end
|
451
469
|
end
|
452
470
|
end
|
data/lib/httpx/connection.rb
CHANGED
@@ -34,9 +34,6 @@ module HTTPX
|
|
34
34
|
|
35
35
|
using URIExtensions
|
36
36
|
|
37
|
-
require "httpx/connection/http2"
|
38
|
-
require "httpx/connection/http1"
|
39
|
-
|
40
37
|
def_delegator :@io, :closed?
|
41
38
|
|
42
39
|
def_delegator :@write_buffer, :empty?
|
@@ -50,7 +47,11 @@ module HTTPX
|
|
50
47
|
protected :sibling
|
51
48
|
|
52
49
|
def initialize(uri, options)
|
53
|
-
@current_session = @current_selector =
|
50
|
+
@current_session = @current_selector =
|
51
|
+
@parser = @sibling = @coalesced_connection =
|
52
|
+
@family = @io = @ssl_session = @timeout =
|
53
|
+
@connected_at = @response_received_at = nil
|
54
|
+
|
54
55
|
@exhausted = @cloned = @main_sibling = false
|
55
56
|
|
56
57
|
@options = Options.new(options)
|
@@ -61,6 +62,8 @@ module HTTPX
|
|
61
62
|
@read_buffer = Buffer.new(@options.buffer_size)
|
62
63
|
@write_buffer = Buffer.new(@options.buffer_size)
|
63
64
|
@pending = []
|
65
|
+
@inflight = 0
|
66
|
+
@keep_alive_timeout = @options.timeout[:keep_alive_timeout]
|
64
67
|
|
65
68
|
on(:error, &method(:on_error))
|
66
69
|
if @options.io
|
@@ -98,9 +101,6 @@ module HTTPX
|
|
98
101
|
build_altsvc_connection(alt_origin, origin, alt_params)
|
99
102
|
end
|
100
103
|
|
101
|
-
@inflight = 0
|
102
|
-
@keep_alive_timeout = @options.timeout[:keep_alive_timeout]
|
103
|
-
|
104
104
|
self.addresses = @options.addresses if @options.addresses
|
105
105
|
end
|
106
106
|
|
@@ -122,6 +122,10 @@ module HTTPX
|
|
122
122
|
@io && @io.addresses
|
123
123
|
end
|
124
124
|
|
125
|
+
def addresses?
|
126
|
+
@io && @io.addresses?
|
127
|
+
end
|
128
|
+
|
125
129
|
def match?(uri, options)
|
126
130
|
return false if !used? && (@state == :closing || @state == :closed)
|
127
131
|
|
@@ -135,12 +139,6 @@ module HTTPX
|
|
135
139
|
) && @options == options
|
136
140
|
end
|
137
141
|
|
138
|
-
def expired?
|
139
|
-
return false unless @io
|
140
|
-
|
141
|
-
@io.expired?
|
142
|
-
end
|
143
|
-
|
144
142
|
def mergeable?(connection)
|
145
143
|
return false if @state == :closing || @state == :closed || !@io
|
146
144
|
|
@@ -229,9 +227,6 @@ module HTTPX
|
|
229
227
|
return @io.interests if connecting?
|
230
228
|
end
|
231
229
|
|
232
|
-
# if the write buffer is full, we drain it
|
233
|
-
return :w unless @write_buffer.empty?
|
234
|
-
|
235
230
|
return @parser.interests if @parser
|
236
231
|
|
237
232
|
nil
|
@@ -369,8 +364,6 @@ module HTTPX
|
|
369
364
|
end
|
370
365
|
|
371
366
|
def handle_connect_error(error)
|
372
|
-
@connect_error = error
|
373
|
-
|
374
367
|
return handle_error(error) unless @sibling && @sibling.connecting?
|
375
368
|
|
376
369
|
@sibling.merge(self)
|
@@ -382,8 +375,7 @@ module HTTPX
|
|
382
375
|
return unless @current_session && @current_selector
|
383
376
|
|
384
377
|
emit(:close)
|
385
|
-
@current_session = nil
|
386
|
-
@current_selector = nil
|
378
|
+
@current_session = @current_selector = nil
|
387
379
|
end
|
388
380
|
|
389
381
|
# :nocov:
|
@@ -545,7 +537,7 @@ module HTTPX
|
|
545
537
|
|
546
538
|
def send_request_to_parser(request)
|
547
539
|
@inflight += 1
|
548
|
-
request.peer_address = @io.ip
|
540
|
+
request.peer_address = @io.ip && @io.ip.address
|
549
541
|
set_request_timeouts(request)
|
550
542
|
|
551
543
|
parser.send(request)
|
@@ -714,7 +706,7 @@ module HTTPX
|
|
714
706
|
return unless @state == :open
|
715
707
|
|
716
708
|
# do not deactivate connection in use
|
717
|
-
return if @inflight.positive?
|
709
|
+
return if @inflight.positive? || @parser.waiting_for_ping?
|
718
710
|
when :closing
|
719
711
|
return unless @state == :idle || @state == :open
|
720
712
|
|
@@ -748,6 +740,7 @@ module HTTPX
|
|
748
740
|
# activate
|
749
741
|
@current_session.select_connection(self, @current_selector)
|
750
742
|
end
|
743
|
+
log(level: 3) { "#{@state} -> #{nextstate}" }
|
751
744
|
@state = nextstate
|
752
745
|
end
|
753
746
|
|
@@ -947,8 +940,8 @@ module HTTPX
|
|
947
940
|
|
948
941
|
def parser_type(protocol)
|
949
942
|
case protocol
|
950
|
-
when "h2" then
|
951
|
-
when "http/1.1" then
|
943
|
+
when "h2" then @options.http2_class
|
944
|
+
when "http/1.1" then @options.http1_class
|
952
945
|
else
|
953
946
|
raise Error, "unsupported protocol (##{protocol})"
|
954
947
|
end
|
data/lib/httpx/errors.rb
CHANGED
@@ -77,7 +77,9 @@ module HTTPX
|
|
77
77
|
# Error raised when there was an error while resolving a domain to an IP
|
78
78
|
# using a HTTPX::Resolver::Native resolver.
|
79
79
|
class NativeResolveError < ResolveError
|
80
|
-
attr_reader :
|
80
|
+
attr_reader :host
|
81
|
+
|
82
|
+
attr_accessor :connection
|
81
83
|
|
82
84
|
# initializes the exception with the +connection+ it refers to, the +host+ domain
|
83
85
|
# which failed to resolve, and the error +message+.
|
data/lib/httpx/io/ssl.rb
CHANGED
@@ -19,6 +19,7 @@ module HTTPX
|
|
19
19
|
def initialize(_, _, options)
|
20
20
|
super
|
21
21
|
|
22
|
+
@ssl_session = nil
|
22
23
|
ctx_options = TLS_OPTIONS.merge(options.ssl)
|
23
24
|
@sni_hostname = ctx_options.delete(:hostname) || @hostname
|
24
25
|
|
@@ -84,10 +85,6 @@ module HTTPX
|
|
84
85
|
@state == :negotiated
|
85
86
|
end
|
86
87
|
|
87
|
-
def expired?
|
88
|
-
super || ssl_session_expired?
|
89
|
-
end
|
90
|
-
|
91
88
|
def ssl_session_expired?
|
92
89
|
@ssl_session.nil? || Process.clock_gettime(Process::CLOCK_REALTIME) >= (@ssl_session.time.to_f + @ssl_session.timeout)
|
93
90
|
end
|
data/lib/httpx/io/tcp.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "resolv"
|
4
|
-
require "ipaddr"
|
5
4
|
|
6
5
|
module HTTPX
|
7
6
|
class TCP
|
@@ -30,7 +29,8 @@ module HTTPX
|
|
30
29
|
end
|
31
30
|
raise Error, "Given IO objects do not match the request authority" unless @io
|
32
31
|
|
33
|
-
_, _, _,
|
32
|
+
_, _, _, ip = @io.addr
|
33
|
+
@ip = Resolver::Entry.new(ip)
|
34
34
|
@addresses << @ip
|
35
35
|
@keep_open = true
|
36
36
|
@state = :connected
|
@@ -47,8 +47,6 @@ module HTTPX
|
|
47
47
|
def add_addresses(addrs)
|
48
48
|
return if addrs.empty?
|
49
49
|
|
50
|
-
addrs = addrs.map { |addr| addr.is_a?(IPAddr) ? addr : IPAddr.new(addr) }
|
51
|
-
|
52
50
|
ip_index = @ip_index || (@addresses.size - 1)
|
53
51
|
if addrs.first.ipv6?
|
54
52
|
# should be the next in line
|
@@ -59,6 +57,19 @@ module HTTPX
|
|
59
57
|
end
|
60
58
|
end
|
61
59
|
|
60
|
+
# eliminates expired entries and returns whether there are still any left.
|
61
|
+
def addresses?
|
62
|
+
prev_addr_size = @addresses.size
|
63
|
+
|
64
|
+
@addresses.delete_if(&:expired?)
|
65
|
+
|
66
|
+
unless (decr = prev_addr_size - @addresses.size).zero?
|
67
|
+
@ip_index = @addresses.size - decr
|
68
|
+
end
|
69
|
+
|
70
|
+
@addresses.any?
|
71
|
+
end
|
72
|
+
|
62
73
|
def to_io
|
63
74
|
@io.to_io
|
64
75
|
end
|
@@ -75,9 +86,18 @@ module HTTPX
|
|
75
86
|
@io = build_socket
|
76
87
|
end
|
77
88
|
try_connect
|
89
|
+
rescue Errno::EHOSTUNREACH,
|
90
|
+
Errno::ENETUNREACH => e
|
91
|
+
raise e if @ip_index <= 0
|
92
|
+
|
93
|
+
log { "failed connecting to #{@ip} (#{e.message}), evict from cache and trying next..." }
|
94
|
+
Resolver.cached_lookup_evict(@hostname, @ip)
|
95
|
+
|
96
|
+
@ip_index -= 1
|
97
|
+
@io = build_socket
|
98
|
+
retry
|
78
99
|
rescue Errno::ECONNREFUSED,
|
79
100
|
Errno::EADDRNOTAVAIL,
|
80
|
-
Errno::EHOSTUNREACH,
|
81
101
|
SocketError,
|
82
102
|
IOError => e
|
83
103
|
raise e if @ip_index <= 0
|
@@ -154,17 +174,6 @@ module HTTPX
|
|
154
174
|
@state == :idle || @state == :closed
|
155
175
|
end
|
156
176
|
|
157
|
-
def expired?
|
158
|
-
# do not mess with external sockets
|
159
|
-
return false if @options.io
|
160
|
-
|
161
|
-
return true unless @addresses
|
162
|
-
|
163
|
-
resolver_addresses = Resolver.nolookup_resolve(@hostname)
|
164
|
-
|
165
|
-
(Array(resolver_addresses) & @addresses).empty?
|
166
|
-
end
|
167
|
-
|
168
177
|
# :nocov:
|
169
178
|
def inspect
|
170
179
|
"#<#{self.class}:#{object_id} " \
|
data/lib/httpx/io/unix.rb
CHANGED
@@ -48,11 +48,12 @@ module HTTPX
|
|
48
48
|
transition(:connected)
|
49
49
|
rescue Errno::EINPROGRESS,
|
50
50
|
Errno::EALREADY,
|
51
|
-
|
51
|
+
IO::WaitReadable
|
52
52
|
end
|
53
53
|
|
54
|
-
|
55
|
-
|
54
|
+
# the path is always explicitly passed, so no point in resolving.
|
55
|
+
def addresses?
|
56
|
+
true
|
56
57
|
end
|
57
58
|
|
58
59
|
# :nocov:
|
data/lib/httpx/loggable.rb
CHANGED
@@ -34,7 +34,10 @@ module HTTPX
|
|
34
34
|
klass = klass.superclass
|
35
35
|
end
|
36
36
|
|
37
|
-
message = +"(pid:#{Process.pid}
|
37
|
+
message = +"(pid:#{Process.pid}, " \
|
38
|
+
"tid:#{Thread.current.object_id}, " \
|
39
|
+
"fid:#{Fiber.current.object_id}, " \
|
40
|
+
"self:#{class_name}##{object_id}) "
|
38
41
|
message << msg.call << "\n"
|
39
42
|
message = "\e[#{COLORS[color]}m#{message}\e[0m" if color && debug_stream.respond_to?(:isatty) && debug_stream.isatty
|
40
43
|
debug_stream << message
|