httpx 0.17.0 → 0.18.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -3
- data/doc/release_notes/0_18_0.md +69 -0
- data/doc/release_notes/0_18_1.md +12 -0
- data/doc/release_notes/0_18_2.md +10 -0
- data/doc/release_notes/0_18_3.md +7 -0
- data/lib/httpx/adapters/datadog.rb +1 -1
- data/lib/httpx/adapters/faraday.rb +5 -3
- data/lib/httpx/adapters/webmock.rb +7 -1
- data/lib/httpx/altsvc.rb +2 -2
- data/lib/httpx/chainable.rb +3 -3
- data/lib/httpx/connection/http1.rb +8 -5
- data/lib/httpx/connection/http2.rb +22 -7
- data/lib/httpx/connection.rb +70 -71
- data/lib/httpx/domain_name.rb +1 -1
- data/lib/httpx/extensions.rb +50 -4
- data/lib/httpx/io/ssl.rb +5 -1
- data/lib/httpx/io/tls.rb +7 -7
- data/lib/httpx/loggable.rb +5 -5
- data/lib/httpx/options.rb +7 -7
- data/lib/httpx/plugins/aws_sdk_authentication.rb +42 -18
- data/lib/httpx/plugins/aws_sigv4.rb +9 -11
- data/lib/httpx/plugins/compression.rb +5 -3
- data/lib/httpx/plugins/cookies/jar.rb +1 -1
- data/lib/httpx/plugins/expect.rb +7 -3
- data/lib/httpx/plugins/grpc/message.rb +2 -2
- data/lib/httpx/plugins/grpc.rb +3 -3
- data/lib/httpx/plugins/internal_telemetry.rb +8 -8
- data/lib/httpx/plugins/multipart.rb +2 -2
- data/lib/httpx/plugins/response_cache/store.rb +55 -0
- data/lib/httpx/plugins/response_cache.rb +88 -0
- data/lib/httpx/plugins/retries.rb +36 -14
- data/lib/httpx/plugins/stream.rb +1 -1
- data/lib/httpx/pool.rb +39 -13
- data/lib/httpx/request.rb +7 -7
- data/lib/httpx/resolver/https.rb +5 -7
- data/lib/httpx/resolver/native.rb +4 -2
- data/lib/httpx/resolver/system.rb +2 -0
- data/lib/httpx/resolver.rb +2 -2
- data/lib/httpx/response.rb +23 -14
- data/lib/httpx/selector.rb +12 -17
- data/lib/httpx/session.rb +7 -2
- data/lib/httpx/session2.rb +1 -1
- data/lib/httpx/timers.rb +84 -0
- data/lib/httpx/transcoder/body.rb +2 -1
- data/lib/httpx/transcoder/form.rb +1 -1
- data/lib/httpx/transcoder/json.rb +1 -1
- data/lib/httpx/utils.rb +8 -0
- data/lib/httpx/version.rb +1 -1
- data/lib/httpx.rb +1 -0
- data/sig/chainable.rbs +1 -0
- data/sig/connection/http1.rbs +5 -0
- data/sig/connection/http2.rbs +3 -0
- data/sig/connection.rbs +12 -6
- data/sig/plugins/aws_sdk_authentication.rbs +22 -4
- data/sig/plugins/response_cache.rbs +35 -0
- data/sig/plugins/retries.rbs +3 -0
- data/sig/pool.rbs +6 -0
- data/sig/resolver/native.rbs +3 -4
- data/sig/resolver/system.rbs +2 -0
- data/sig/response.rbs +3 -2
- data/sig/timers.rbs +32 -0
- data/sig/utils.rbs +4 -0
- metadata +17 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 81b7e14071e0e1978c864c63b0c8146044f255a9b2f7d9d3e917b564fb301ffb
|
4
|
+
data.tar.gz: 7492d34e586d86e373f62f151ceb6962719fcec156597ff34dae4c10b8347072
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5c5930cd68b2ba0f42ea60e6dc5a76ff24b2643f16c300b778e8af62f0f04d8c01baadbf0163e2b925275bbfdf44d8dd73bbf78e06dd2c86b2e54d0eb99cbbdc
|
7
|
+
data.tar.gz: 20a70c6ffc8895c7b54f84f9832bc8831942048ba37dc83907e0b4fef83484d1f6797a0792b075539cb985a8df65798085f3089e6746d1246e9d27ef0cb63d96
|
data/README.md
CHANGED
@@ -45,7 +45,7 @@ response = HTTPX.get("https://nghttp2.org")
|
|
45
45
|
puts response.status #=> 200
|
46
46
|
body = response.body
|
47
47
|
puts body #=> #<HTTPX::Response ...
|
48
|
-
```
|
48
|
+
```
|
49
49
|
|
50
50
|
You can also send as many requests as you want simultaneously:
|
51
51
|
|
@@ -79,7 +79,7 @@ In Ruby, HTTP client implementations are a known cheap commodity. Why this one?
|
|
79
79
|
|
80
80
|
### Concurrency
|
81
81
|
|
82
|
-
This library supports HTTP/2 seamlessly (which means, if the request is secure, and the server support ALPN negotiation AND HTTP/2, the request will be made through HTTP/2). If you pass multiple URIs, and they can utilize the same connection, they will run concurrently in it.
|
82
|
+
This library supports HTTP/2 seamlessly (which means, if the request is secure, and the server support ALPN negotiation AND HTTP/2, the request will be made through HTTP/2). If you pass multiple URIs, and they can utilize the same connection, they will run concurrently in it.
|
83
83
|
|
84
84
|
However if the server supports HTTP/1.1, it will try to use HTTP pipelining, falling back to 1 request at a time if the server doesn't support it (if the server support Keep-Alive connections, it will reuse the same connection).
|
85
85
|
|
@@ -137,7 +137,8 @@ In order to use HTTP/2 under JRuby, [check this link](https://gitlab.com/honeyry
|
|
137
137
|
|
138
138
|
### Known bugs
|
139
139
|
|
140
|
-
Doesn't work with ruby 2.4.0 for Windows (see [#36](https://gitlab.com/honeyryderchuck/httpx/issues/36)).
|
140
|
+
* Doesn't work with ruby 2.4.0 for Windows (see [#36](https://gitlab.com/honeyryderchuck/httpx/issues/36)).
|
141
|
+
* Using `total_timeout` along with the `:persistent` plugin [does not work as you might expect](https://gitlab.com/honeyryderchuck/httpx/-/wikis/Timeouts#total_timeout).
|
141
142
|
|
142
143
|
## Contributing
|
143
144
|
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# 0.18.0
|
2
|
+
|
3
|
+
## Features
|
4
|
+
|
5
|
+
### Response Cache
|
6
|
+
|
7
|
+
https://gitlab.com/honeyryderchuck/httpx/-/wikis/Response-Cache
|
8
|
+
|
9
|
+
The `:response_cache` plugin handles transparent usage of HTTP caching and conditional requests to improve performance and bandwidth usage.
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
client = HTTPX.plugin(:response_cache)
|
13
|
+
r1 = client.get("https://nghttp2.org/httpbin/cache")
|
14
|
+
r2 = client.get("https://nghttp2.org/httpbin/cache")
|
15
|
+
|
16
|
+
r1.status #=> 200
|
17
|
+
r2.status #=> 304
|
18
|
+
r1.body == r2.body #=> true
|
19
|
+
```
|
20
|
+
|
21
|
+
### jitter on "retry-after"
|
22
|
+
|
23
|
+
On the `:retries` plugin, jitter calculation is now applied to the value in seconds defined by user after which a request should be retried (i.e. if `:retry_after` option is set to `2`, the retry interval may be `1.5422312` seconds, for example). This is important to avoid cases of synchronized "thundering herd", where server rejects requests, but they all get retried at the same time because the retry interval is exactly the same.
|
24
|
+
|
25
|
+
You can override the jitter calculation function by using the [:retry_jitter](https://gitlab.com/honeyryderchuck/httpx/-/wikis/Retries#retry_jitter) option:
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
HTTPX.plugin(:retries, retry_after: 2, retry_jitter: ->(interval) { interval + rand }) # interval is 3
|
29
|
+
```
|
30
|
+
|
31
|
+
You can opt out of this by setting `HTTPX_NO_JITTER=1` environment variable.
|
32
|
+
|
33
|
+
### Response#error
|
34
|
+
|
35
|
+
`HTTPX::Response#error` was added, to match `HTTPX::Response#error`. It returns an exception for 4xx/5xx responses (`HTTPX::HTTPError`), `nil` otherwise. It allows for end users to write such code:
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
if (response = HTTPX.get(uri)).error
|
39
|
+
# success
|
40
|
+
else
|
41
|
+
#error
|
42
|
+
end
|
43
|
+
```
|
44
|
+
|
45
|
+
## Improvements
|
46
|
+
|
47
|
+
* `webmock` adapter: added support for "stub_http_request#to_timeout" (https://gitlab.com/honeyryderchuck/httpx/-/merge_requests/165).
|
48
|
+
|
49
|
+
## timers not a dependency
|
50
|
+
|
51
|
+
The functionality provided by the `timers` gem was replaced by a simpler custom implementation. Although powerful, its complexity was somewhat unnecessary for `httpx`'s simpler event loop, where user-defined timeouts are usually the same for a given batch of requests. The removal of `timers` reduces the number of dependencies to 1, which is `http-2-next` and is maintained by me.
|
52
|
+
|
53
|
+
## AWS plugins
|
54
|
+
|
55
|
+
* `aws_sdk_authentication` plugin: removed implementation relying on `aws-sdk-s3`, replacing it with an `aws-sdk-core` relying implementation, which only uses credentials strategies and region discovery (the whole point of this SDK is to use a minimal subset of AWS SDK).
|
56
|
+
|
57
|
+
|
58
|
+
## Bugfixes
|
59
|
+
|
60
|
+
* Fixed Error class declaration on response decoders when mime type is invalid (https://gitlab.com/honeyryderchuck/httpx/-/merge_requests/166).
|
61
|
+
* `ErrorResponse#to_s` now removes ANSI escape sequences from error backtraces.
|
62
|
+
* Persistent connections were kept around both in the pool and in the selector; the first is necessary, but the second caused busy loop scenarios all over; they are now removed when no requests are being handled.
|
63
|
+
* Connections which failed connection handshake were removed from the pool, but not from the selector list, causing busy loop scenarios in a few cases; this has been fixed.
|
64
|
+
* Fixed issue where HTTP/2 streams were being closed twice (and signaling it also twice), messing connection accounting in the pool.
|
65
|
+
* DoH resolver was always subscribed to the default thread "connection pool", which broke scenarios were session was patched to use its custom pool; it now ensures that it subscribes to the pool it was created in.
|
66
|
+
* `:aws_sigv4` plugin: removed require of `aws-sdk-s3`, left there by mistake (the whole point of the plugin is to run without the AWS SDK).
|
67
|
+
## Chore
|
68
|
+
|
69
|
+
* `HTTPX::ErrorResponse#status` is now deprecated.
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# 0.18.1
|
2
|
+
|
3
|
+
## Bugfixes
|
4
|
+
|
5
|
+
* HTTP/1.1 pipelining logs were logging the previously-buffered requests all together for each triggered request, which created some confusion for users when reporting errors. This has been fixed.
|
6
|
+
* HTTP/2 coalescing is now skipped when performing TLS connections with VERIFY_NONE.
|
7
|
+
* HTTP/2 peer GOAWAY frames will now result in a (retryable) connection error, instead of being ignored and leaving a "ghost" connection behind.
|
8
|
+
* fixed total timeout call which was not raising the exception.
|
9
|
+
|
10
|
+
## Chore
|
11
|
+
|
12
|
+
This gem now requires MFA-based gem releases.
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# 0.18.2
|
2
|
+
|
3
|
+
## Bugfixes
|
4
|
+
|
5
|
+
* A bug was reported and fixed, whereby a persistent connection with a `:total_timeout` set was triggering the timeout and leaving the process looping indefinitely.
|
6
|
+
|
7
|
+
|
8
|
+
## Chore
|
9
|
+
|
10
|
+
The quirk of using the `:persistent` plugin with `:total_timeout` has been documented: https://gitlab.com/honeyryderchuck/httpx/-/wikis/Timeouts#total_timeout.
|
@@ -0,0 +1,7 @@
|
|
1
|
+
# 0.18.3
|
2
|
+
|
3
|
+
## Bugfixes
|
4
|
+
|
5
|
+
* request bodies eager-loaded from enumerables yield duped partial chunks.
|
6
|
+
|
7
|
+
An error was observed while looking at webmock integration, where requests formed via the multipart plugin where returning an empty string as body. The issue was caused by an optimization on multipart encoder, which reuses the same buffer when reading chunks. Unfortunately, these cannot be yielded the same way via IO.copy_stream, as the same (cleared) buffer will be used to generate the eager-loaded body chunks.
|
@@ -22,6 +22,8 @@ module Faraday
|
|
22
22
|
# :nocov:
|
23
23
|
|
24
24
|
module RequestMixin
|
25
|
+
using ::HTTPX::HashExtensions
|
26
|
+
|
25
27
|
private
|
26
28
|
|
27
29
|
def build_request(env)
|
@@ -38,7 +40,7 @@ module Faraday
|
|
38
40
|
timeout_options = {
|
39
41
|
connect_timeout: env.request.open_timeout,
|
40
42
|
operation_timeout: env.request.timeout,
|
41
|
-
}.
|
43
|
+
}.compact
|
42
44
|
|
43
45
|
options = {
|
44
46
|
ssl: {},
|
@@ -101,7 +103,7 @@ module Faraday
|
|
101
103
|
end
|
102
104
|
|
103
105
|
def on_response(&blk)
|
104
|
-
if
|
106
|
+
if blk
|
105
107
|
@on_response = lambda do |response|
|
106
108
|
blk.call(response)
|
107
109
|
end
|
@@ -112,7 +114,7 @@ module Faraday
|
|
112
114
|
end
|
113
115
|
|
114
116
|
def on_complete(&blk)
|
115
|
-
if
|
117
|
+
if blk
|
116
118
|
@on_complete = blk
|
117
119
|
self
|
118
120
|
else
|
@@ -86,7 +86,9 @@ module WebMock
|
|
86
86
|
end
|
87
87
|
|
88
88
|
def _build_from_webmock_response(request, webmock_response)
|
89
|
-
return
|
89
|
+
return _build_error_response(request, HTTPX::TimeoutError.new(1, "Timed out")) if webmock_response.should_timeout
|
90
|
+
|
91
|
+
return _build_error_response(request, webmock_response.exception) if webmock_response.exception
|
90
92
|
|
91
93
|
response = request.options.response_class.new(request,
|
92
94
|
webmock_response.status[0],
|
@@ -95,6 +97,10 @@ module WebMock
|
|
95
97
|
response << webmock_response.body.dup
|
96
98
|
response
|
97
99
|
end
|
100
|
+
|
101
|
+
def _build_error_response(request, exception)
|
102
|
+
HTTPX::ErrorResponse.new(request, exception, request.options)
|
103
|
+
end
|
98
104
|
end
|
99
105
|
end
|
100
106
|
|
data/lib/httpx/altsvc.rb
CHANGED
@@ -10,14 +10,14 @@ module HTTPX
|
|
10
10
|
module_function
|
11
11
|
|
12
12
|
def cached_altsvc(origin)
|
13
|
-
now =
|
13
|
+
now = Utils.now
|
14
14
|
@altsvc_mutex.synchronize do
|
15
15
|
lookup(origin, now)
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
19
|
def cached_altsvc_set(origin, entry)
|
20
|
-
now =
|
20
|
+
now = Utils.now
|
21
21
|
@altsvc_mutex.synchronize do
|
22
22
|
return if @altsvcs[origin].any? { |altsvc| altsvc["origin"] == entry["origin"] }
|
23
23
|
|
data/lib/httpx/chainable.rb
CHANGED
@@ -4,9 +4,9 @@ module HTTPX
|
|
4
4
|
module Chainable
|
5
5
|
%i[head get post put delete trace options connect patch].each do |meth|
|
6
6
|
class_eval(<<-MOD, __FILE__, __LINE__ + 1)
|
7
|
-
def #{meth}(*uri, **options)
|
8
|
-
request(:#{meth}, uri, **options)
|
9
|
-
end
|
7
|
+
def #{meth}(*uri, **options) # def get(*uri, **options)
|
8
|
+
request(:#{meth}, uri, **options) # request(:get, uri, **options)
|
9
|
+
end # end
|
10
10
|
MOD
|
11
11
|
end
|
12
12
|
|
@@ -36,6 +36,8 @@ module HTTPX
|
|
36
36
|
|
37
37
|
request = @requests.first
|
38
38
|
|
39
|
+
return unless request
|
40
|
+
|
39
41
|
return :w if request.interests == :w || !@buffer.empty?
|
40
42
|
|
41
43
|
:r
|
@@ -283,10 +285,10 @@ module HTTPX
|
|
283
285
|
# on the last request of the possible batch (either allowed max requests,
|
284
286
|
# or if smaller, the size of the batch itself)
|
285
287
|
requests_limit = [@max_requests, @requests.size].min
|
286
|
-
if request
|
287
|
-
"keep-alive"
|
288
|
-
else
|
288
|
+
if request == @requests[requests_limit - 1]
|
289
289
|
"close"
|
290
|
+
else
|
291
|
+
"keep-alive"
|
290
292
|
end
|
291
293
|
end
|
292
294
|
|
@@ -313,8 +315,9 @@ module HTTPX
|
|
313
315
|
end
|
314
316
|
|
315
317
|
def join_headers(request)
|
316
|
-
|
317
|
-
|
318
|
+
headline = "#{request.verb.to_s.upcase} #{headline_uri(request)} HTTP/#{@version.join(".")}"
|
319
|
+
@buffer << headline << CRLF
|
320
|
+
log(color: :yellow) { "<- HEADLINE: #{headline.chomp.inspect}" }
|
318
321
|
extra_headers = set_protocol_headers(request)
|
319
322
|
join_headers2(request.headers.each(extra_headers))
|
320
323
|
log { "<- " }
|
@@ -16,6 +16,12 @@ module HTTPX
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
+
class GoawayError < Error
|
20
|
+
def initialize
|
21
|
+
super(0, :no_error)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
19
25
|
attr_reader :streams, :pending
|
20
26
|
|
21
27
|
def initialize(buffer, options)
|
@@ -291,16 +297,18 @@ module HTTPX
|
|
291
297
|
end
|
292
298
|
|
293
299
|
def on_stream_refuse(stream, request, error)
|
294
|
-
stream.close
|
295
300
|
on_stream_close(stream, request, error)
|
301
|
+
stream.close
|
296
302
|
end
|
297
303
|
|
298
304
|
def on_stream_close(stream, request, error)
|
305
|
+
return if error == :stream_closed && !@streams.key?(request)
|
306
|
+
|
299
307
|
log(level: 2) { "#{stream.id}: closing stream" }
|
300
308
|
@drains.delete(request)
|
301
309
|
@streams.delete(request)
|
302
310
|
|
303
|
-
if error
|
311
|
+
if error
|
304
312
|
ex = Error.new(stream.id, error)
|
305
313
|
ex.set_backtrace(caller)
|
306
314
|
response = ErrorResponse.new(request, ex, request.options)
|
@@ -342,9 +350,16 @@ module HTTPX
|
|
342
350
|
|
343
351
|
def on_close(_last_frame, error, _payload)
|
344
352
|
is_connection_closed = @connection.state == :closed
|
345
|
-
if error
|
353
|
+
if error
|
346
354
|
@buffer.clear if is_connection_closed
|
347
|
-
|
355
|
+
if error == :no_error
|
356
|
+
ex = GoawayError.new
|
357
|
+
@pending.unshift(*@streams.keys)
|
358
|
+
@drains.clear
|
359
|
+
@streams.clear
|
360
|
+
else
|
361
|
+
ex = Error.new(0, error)
|
362
|
+
end
|
348
363
|
ex.set_backtrace(caller)
|
349
364
|
handle_error(ex)
|
350
365
|
end
|
@@ -388,10 +403,10 @@ module HTTPX
|
|
388
403
|
end
|
389
404
|
|
390
405
|
def on_pong(ping)
|
391
|
-
if
|
392
|
-
close(:protocol_error, "ping payload did not match")
|
393
|
-
else
|
406
|
+
if @pings.delete(ping.to_s)
|
394
407
|
emit(:pong)
|
408
|
+
else
|
409
|
+
close(:protocol_error, "ping payload did not match")
|
395
410
|
end
|
396
411
|
end
|
397
412
|
end
|
data/lib/httpx/connection.rb
CHANGED
@@ -70,7 +70,7 @@ module HTTPX
|
|
70
70
|
|
71
71
|
@inflight = 0
|
72
72
|
@keep_alive_timeout = @options.timeout[:keep_alive_timeout]
|
73
|
-
@
|
73
|
+
@total_timeout = @options.timeout[:total_timeout]
|
74
74
|
|
75
75
|
self.addresses = @options.addresses if @options.addresses
|
76
76
|
end
|
@@ -117,7 +117,8 @@ module HTTPX
|
|
117
117
|
def coalescable?(connection)
|
118
118
|
if @io.protocol == "h2" &&
|
119
119
|
@origin.scheme == "https" &&
|
120
|
-
connection.origin.scheme == "https"
|
120
|
+
connection.origin.scheme == "https" &&
|
121
|
+
@io.can_verify_peer?
|
121
122
|
@io.verify_hostname(connection.origin.host)
|
122
123
|
else
|
123
124
|
@origin == connection.origin
|
@@ -200,11 +201,9 @@ module HTTPX
|
|
200
201
|
end
|
201
202
|
|
202
203
|
def close
|
203
|
-
|
204
|
-
return unless @keep_alive_timer
|
204
|
+
transition(:active) if @state == :inactive
|
205
205
|
|
206
|
-
@
|
207
|
-
remove_instance_variable(:@keep_alive_timer)
|
206
|
+
@parser.close if @parser
|
208
207
|
end
|
209
208
|
|
210
209
|
def reset
|
@@ -216,26 +215,40 @@ module HTTPX
|
|
216
215
|
def send(request)
|
217
216
|
if @parser && !@write_buffer.full?
|
218
217
|
request.headers["alt-used"] = @origin.authority if match_altsvcs?(request.uri)
|
219
|
-
|
218
|
+
|
219
|
+
if @response_received_at && @keep_alive_timeout &&
|
220
|
+
Utils.elapsed_time(@response_received_at) > @keep_alive_timeout
|
220
221
|
# when pushing a request into an existing connection, we have to check whether there
|
221
222
|
# is the possibility that the connection might have extended the keep alive timeout.
|
222
223
|
# for such cases, we want to ping for availability before deciding to shovel requests.
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
end
|
228
|
-
|
229
|
-
@keep_alive_timer.pause
|
224
|
+
@pending << request
|
225
|
+
parser.ping
|
226
|
+
transition(:active) if @state == :inactive
|
227
|
+
return
|
230
228
|
end
|
231
|
-
|
232
|
-
|
229
|
+
|
230
|
+
send_request_to_parser(request)
|
233
231
|
else
|
234
232
|
@pending << request
|
235
233
|
end
|
236
234
|
end
|
237
235
|
|
238
236
|
def timeout
|
237
|
+
if @total_timeout
|
238
|
+
return @total_timeout unless @connected_at
|
239
|
+
|
240
|
+
elapsed_time = @total_timeout - Utils.elapsed_time(@connected_at)
|
241
|
+
|
242
|
+
if elapsed_time.negative?
|
243
|
+
ex = TotalTimeoutError.new(@total_timeout, "Timed out after #{@total_timeout} seconds")
|
244
|
+
ex.set_backtrace(caller)
|
245
|
+
on_error(ex)
|
246
|
+
return
|
247
|
+
end
|
248
|
+
|
249
|
+
return elapsed_time
|
250
|
+
end
|
251
|
+
|
239
252
|
return @timeout if defined?(@timeout)
|
240
253
|
|
241
254
|
return @options.timeout[:connect_timeout] if @state == :idle
|
@@ -243,6 +256,14 @@ module HTTPX
|
|
243
256
|
@options.timeout[:operation_timeout]
|
244
257
|
end
|
245
258
|
|
259
|
+
def deactivate
|
260
|
+
transition(:inactive)
|
261
|
+
end
|
262
|
+
|
263
|
+
def open?
|
264
|
+
@state == :open || @state == :inactive
|
265
|
+
end
|
266
|
+
|
246
267
|
private
|
247
268
|
|
248
269
|
def connect
|
@@ -379,9 +400,7 @@ module HTTPX
|
|
379
400
|
|
380
401
|
def send_pending
|
381
402
|
while !@write_buffer.full? && (request = @pending.shift)
|
382
|
-
|
383
|
-
@keep_alive_timer.pause if @keep_alive_timer
|
384
|
-
parser.send(request)
|
403
|
+
send_request_to_parser(request)
|
385
404
|
end
|
386
405
|
end
|
387
406
|
|
@@ -389,6 +408,15 @@ module HTTPX
|
|
389
408
|
@parser ||= build_parser
|
390
409
|
end
|
391
410
|
|
411
|
+
def send_request_to_parser(request)
|
412
|
+
@inflight += 1
|
413
|
+
parser.send(request)
|
414
|
+
|
415
|
+
return unless @state == :inactive
|
416
|
+
|
417
|
+
transition(:active)
|
418
|
+
end
|
419
|
+
|
392
420
|
def build_parser(protocol = @io.protocol)
|
393
421
|
parser = registry(protocol).new(@write_buffer, @options)
|
394
422
|
set_parser_callbacks(parser)
|
@@ -400,7 +428,8 @@ module HTTPX
|
|
400
428
|
AltSvc.emit(request, response) do |alt_origin, origin, alt_params|
|
401
429
|
emit(:altsvc, alt_origin, origin, alt_params)
|
402
430
|
end
|
403
|
-
|
431
|
+
@response_received_at = Utils.now
|
432
|
+
@inflight -= 1
|
404
433
|
request.emit(:response, response)
|
405
434
|
end
|
406
435
|
parser.on(:altsvc) do |alt_origin, origin, alt_params|
|
@@ -420,7 +449,7 @@ module HTTPX
|
|
420
449
|
end
|
421
450
|
parser.on(:close) do |force|
|
422
451
|
transition(:closing)
|
423
|
-
if force
|
452
|
+
if force || @state == :idle
|
424
453
|
transition(:closed)
|
425
454
|
emit(:close)
|
426
455
|
end
|
@@ -435,6 +464,7 @@ module HTTPX
|
|
435
464
|
transition(:closing)
|
436
465
|
transition(:closed)
|
437
466
|
emit(:reset)
|
467
|
+
|
438
468
|
@parser.reset if @parser
|
439
469
|
transition(:idle)
|
440
470
|
transition(:open)
|
@@ -466,15 +496,17 @@ module HTTPX
|
|
466
496
|
when :open
|
467
497
|
return if @state == :closed
|
468
498
|
|
469
|
-
total_timeout
|
470
|
-
|
471
499
|
@io.connect
|
472
500
|
return unless @io.connected?
|
473
501
|
|
502
|
+
@connected_at = Utils.now
|
503
|
+
|
474
504
|
send_pending
|
475
505
|
|
476
506
|
@timeout = @current_timeout = parser.timeout
|
477
507
|
emit(:open)
|
508
|
+
when :inactive
|
509
|
+
return unless @state == :open
|
478
510
|
when :closing
|
479
511
|
return unless @state == :open
|
480
512
|
|
@@ -482,15 +514,15 @@ module HTTPX
|
|
482
514
|
return unless @state == :closing
|
483
515
|
return unless @write_buffer.empty?
|
484
516
|
|
485
|
-
if @total_timeout
|
486
|
-
@total_timeout.cancel
|
487
|
-
remove_instance_variable(:@total_timeout)
|
488
|
-
end
|
489
|
-
|
490
517
|
purge_after_closed
|
491
518
|
when :already_open
|
492
519
|
nextstate = :open
|
493
520
|
send_pending
|
521
|
+
when :active
|
522
|
+
return unless @state == :inactive
|
523
|
+
|
524
|
+
nextstate = :open
|
525
|
+
emit(:activate)
|
494
526
|
end
|
495
527
|
@state = nextstate
|
496
528
|
rescue Errno::ECONNREFUSED,
|
@@ -506,44 +538,24 @@ module HTTPX
|
|
506
538
|
def purge_after_closed
|
507
539
|
@io.close if @io
|
508
540
|
@read_buffer.clear
|
509
|
-
if @keep_alive_timer
|
510
|
-
@keep_alive_timer.cancel
|
511
|
-
remove_instance_variable(:@keep_alive_timer)
|
512
|
-
end
|
513
|
-
|
514
541
|
remove_instance_variable(:@timeout) if defined?(@timeout)
|
515
542
|
end
|
516
543
|
|
517
|
-
def handle_response
|
518
|
-
@inflight -= 1
|
519
|
-
return unless @inflight.zero?
|
520
|
-
|
521
|
-
if @keep_alive_timer
|
522
|
-
@keep_alive_timer.resume
|
523
|
-
@keep_alive_timer.reset
|
524
|
-
else
|
525
|
-
@keep_alive_timer = @timers.after(@keep_alive_timeout) do
|
526
|
-
unless @inflight.zero?
|
527
|
-
log { "(#{@origin}): keep alive timeout expired" }
|
528
|
-
parser.ping
|
529
|
-
end
|
530
|
-
end
|
531
|
-
end
|
532
|
-
end
|
533
|
-
|
534
544
|
def on_error(error)
|
535
545
|
if error.instance_of?(TimeoutError)
|
536
|
-
if @timeout
|
537
|
-
@timeout -= error.timeout
|
538
|
-
return unless @timeout <= 0
|
539
|
-
end
|
540
546
|
|
541
|
-
if @total_timeout && @
|
542
|
-
|
547
|
+
if @total_timeout && @connected_at &&
|
548
|
+
Utils.elapsed_time(@connected_at) > @total_timeout
|
549
|
+
ex = TotalTimeoutError.new(@total_timeout, "Timed out after #{@total_timeout} seconds")
|
543
550
|
ex.set_backtrace(error.backtrace)
|
544
551
|
error = ex
|
545
|
-
|
546
|
-
|
552
|
+
else
|
553
|
+
if @timeout
|
554
|
+
@timeout -= error.timeout
|
555
|
+
return unless @timeout <= 0
|
556
|
+
end
|
557
|
+
|
558
|
+
error = error.to_connection_error if connecting?
|
547
559
|
end
|
548
560
|
end
|
549
561
|
handle_error(error)
|
@@ -558,18 +570,5 @@ module HTTPX
|
|
558
570
|
request.emit(:response, response)
|
559
571
|
end
|
560
572
|
end
|
561
|
-
|
562
|
-
def total_timeout
|
563
|
-
total = @options.timeout[:total_timeout]
|
564
|
-
|
565
|
-
return unless total
|
566
|
-
|
567
|
-
@total_timeout ||= @timers.after(total) do
|
568
|
-
ex = TotalTimeoutError.new(total, "Timed out after #{total} seconds")
|
569
|
-
ex.set_backtrace(caller)
|
570
|
-
on_error(ex)
|
571
|
-
@parser.close if @parser
|
572
|
-
end
|
573
|
-
end
|
574
573
|
end
|
575
574
|
end
|
data/lib/httpx/domain_name.rb
CHANGED