httpx 0.8.1 → 0.10.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE.txt +48 -0
- data/README.md +9 -5
- data/doc/release_notes/0_0_1.md +7 -0
- data/doc/release_notes/0_0_2.md +9 -0
- data/doc/release_notes/0_0_3.md +9 -0
- data/doc/release_notes/0_0_4.md +7 -0
- data/doc/release_notes/0_0_5.md +5 -0
- data/doc/release_notes/0_10_0.md +66 -0
- data/doc/release_notes/0_10_1.md +37 -0
- data/doc/release_notes/0_10_2.md +5 -0
- data/doc/release_notes/0_1_0.md +9 -0
- data/doc/release_notes/0_2_0.md +5 -0
- data/doc/release_notes/0_2_1.md +16 -0
- data/doc/release_notes/0_3_0.md +12 -0
- data/doc/release_notes/0_3_1.md +6 -0
- data/doc/release_notes/0_4_0.md +51 -0
- data/doc/release_notes/0_4_1.md +3 -0
- data/doc/release_notes/0_5_0.md +15 -0
- data/doc/release_notes/0_5_1.md +14 -0
- data/doc/release_notes/0_6_0.md +5 -0
- data/doc/release_notes/0_6_1.md +6 -0
- data/doc/release_notes/0_6_2.md +6 -0
- data/doc/release_notes/0_6_3.md +13 -0
- data/doc/release_notes/0_6_4.md +21 -0
- data/doc/release_notes/0_6_5.md +22 -0
- data/doc/release_notes/0_6_6.md +19 -0
- data/doc/release_notes/0_6_7.md +5 -0
- data/doc/release_notes/0_7_0.md +46 -0
- data/doc/release_notes/0_8_0.md +27 -0
- data/doc/release_notes/0_8_1.md +8 -0
- data/doc/release_notes/0_8_2.md +7 -0
- data/doc/release_notes/0_9_0.md +38 -0
- data/lib/httpx.rb +2 -0
- data/lib/httpx/adapters/faraday.rb +1 -1
- data/lib/httpx/chainable.rb +11 -11
- data/lib/httpx/connection.rb +23 -31
- data/lib/httpx/connection/http1.rb +30 -4
- data/lib/httpx/connection/http2.rb +26 -9
- data/lib/httpx/domain_name.rb +440 -0
- data/lib/httpx/errors.rb +2 -1
- data/lib/httpx/extensions.rb +22 -2
- data/lib/httpx/headers.rb +1 -1
- data/lib/httpx/io/ssl.rb +0 -1
- data/lib/httpx/io/tcp.rb +6 -5
- data/lib/httpx/io/udp.rb +4 -1
- data/lib/httpx/options.rb +5 -1
- data/lib/httpx/parser/http1.rb +14 -17
- data/lib/httpx/plugins/compression.rb +46 -65
- data/lib/httpx/plugins/compression/brotli.rb +10 -14
- data/lib/httpx/plugins/compression/deflate.rb +7 -6
- data/lib/httpx/plugins/compression/gzip.rb +23 -5
- data/lib/httpx/plugins/cookies.rb +21 -60
- data/lib/httpx/plugins/cookies/cookie.rb +173 -0
- data/lib/httpx/plugins/cookies/jar.rb +74 -0
- data/lib/httpx/plugins/cookies/set_cookie_parser.rb +142 -0
- data/lib/httpx/plugins/expect.rb +12 -1
- data/lib/httpx/plugins/follow_redirects.rb +20 -2
- data/lib/httpx/plugins/h2c.rb +1 -1
- data/lib/httpx/plugins/multipart.rb +12 -6
- data/lib/httpx/plugins/persistent.rb +6 -1
- data/lib/httpx/plugins/proxy.rb +16 -2
- data/lib/httpx/plugins/proxy/socks4.rb +14 -14
- data/lib/httpx/plugins/rate_limiter.rb +51 -0
- data/lib/httpx/plugins/retries.rb +3 -2
- data/lib/httpx/plugins/stream.rb +109 -13
- data/lib/httpx/pool.rb +14 -20
- data/lib/httpx/request.rb +8 -20
- data/lib/httpx/resolver.rb +7 -10
- data/lib/httpx/resolver/https.rb +22 -24
- data/lib/httpx/resolver/native.rb +19 -16
- data/lib/httpx/resolver/resolver_mixin.rb +4 -2
- data/lib/httpx/resolver/system.rb +2 -2
- data/lib/httpx/response.rb +16 -25
- data/lib/httpx/selector.rb +11 -18
- data/lib/httpx/session.rb +40 -26
- data/lib/httpx/transcoder.rb +18 -0
- data/lib/httpx/transcoder/chunker.rb +0 -2
- data/lib/httpx/transcoder/form.rb +9 -7
- data/lib/httpx/transcoder/json.rb +0 -4
- data/lib/httpx/utils.rb +45 -0
- data/lib/httpx/version.rb +1 -1
- data/sig/buffer.rbs +24 -0
- data/sig/callbacks.rbs +14 -0
- data/sig/chainable.rbs +37 -0
- data/sig/connection.rbs +85 -0
- data/sig/connection/http1.rbs +66 -0
- data/sig/connection/http2.rbs +78 -0
- data/sig/domain_name.rbs +17 -0
- data/sig/errors.rbs +3 -0
- data/sig/headers.rbs +42 -0
- data/sig/httpx.rbs +15 -0
- data/sig/loggable.rbs +11 -0
- data/sig/missing.rbs +12 -0
- data/sig/options.rbs +118 -0
- data/sig/parser/http1.rbs +50 -0
- data/sig/plugins/authentication.rbs +11 -0
- data/sig/plugins/basic_authentication.rbs +13 -0
- data/sig/plugins/compression.rbs +55 -0
- data/sig/plugins/compression/brotli.rbs +21 -0
- data/sig/plugins/compression/deflate.rbs +17 -0
- data/sig/plugins/compression/gzip.rbs +29 -0
- data/sig/plugins/cookies.rbs +26 -0
- data/sig/plugins/cookies/cookie.rbs +50 -0
- data/sig/plugins/cookies/jar.rbs +27 -0
- data/sig/plugins/digest_authentication.rbs +33 -0
- data/sig/plugins/expect.rbs +19 -0
- data/sig/plugins/follow_redirects.rbs +37 -0
- data/sig/plugins/h2c.rbs +26 -0
- data/sig/plugins/multipart.rbs +21 -0
- data/sig/plugins/persistent.rbs +17 -0
- data/sig/plugins/proxy.rbs +47 -0
- data/sig/plugins/proxy/http.rbs +14 -0
- data/sig/plugins/proxy/socks4.rbs +33 -0
- data/sig/plugins/proxy/socks5.rbs +36 -0
- data/sig/plugins/proxy/ssh.rbs +18 -0
- data/sig/plugins/push_promise.rbs +22 -0
- data/sig/plugins/rate_limiter.rbs +11 -0
- data/sig/plugins/retries.rbs +48 -0
- data/sig/plugins/stream.rbs +39 -0
- data/sig/pool.rbs +36 -0
- data/sig/registry.rbs +9 -0
- data/sig/request.rbs +61 -0
- data/sig/resolver.rbs +26 -0
- data/sig/resolver/https.rbs +49 -0
- data/sig/resolver/native.rbs +60 -0
- data/sig/resolver/resolver_mixin.rbs +27 -0
- data/sig/resolver/system.rbs +17 -0
- data/sig/response.rbs +87 -0
- data/sig/selector.rbs +20 -0
- data/sig/session.rbs +49 -0
- data/sig/timeout.rbs +29 -0
- data/sig/transcoder.rbs +18 -0
- data/sig/transcoder/body.rbs +18 -0
- data/sig/transcoder/chunker.rbs +32 -0
- data/sig/transcoder/form.rbs +16 -0
- data/sig/transcoder/json.rbs +14 -0
- metadata +130 -22
- data/lib/httpx/resolver/options.rb +0 -25
@@ -0,0 +1,22 @@
|
|
1
|
+
# 0.6.5
|
2
|
+
|
3
|
+
This release fixes important bugs, and automates the PKI for the test suite.
|
4
|
+
|
5
|
+
## Features
|
6
|
+
|
7
|
+
* `resolver_options` can now receive a `:cache` flag (default: `true`). This bypasses caching and forces the lookup;
|
8
|
+
|
9
|
+
## Improvements
|
10
|
+
|
11
|
+
* Building the TLS certs necessary for the test suite has been scripted, after the initial certs expired and brought the CI to a halt;
|
12
|
+
* All DNS resolvers have a functional test, both for the happy as well as the error case;
|
13
|
+
* Added functional tests for HTTP and HTTPS proxy with authentication, making all proxy options now tested with authentication;
|
14
|
+
|
15
|
+
|
16
|
+
## Bugfixes
|
17
|
+
|
18
|
+
* native and https DNS resolvers weren't usable after a resolving error;
|
19
|
+
* system DNS resolver could halt the system after a dns resolving error;
|
20
|
+
* fixed system halt on HTTP proxy authentication error;
|
21
|
+
|
22
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# 0.6.6
|
2
|
+
|
3
|
+
## Features
|
4
|
+
|
5
|
+
* The `retries` plugin receives two new options:
|
6
|
+
* `retry_on`: a callable that receives the failed response as an argument; the return value will determine whether there'll be a retried request.
|
7
|
+
* `retry_after`: time (in seconds) after which there request will be retried. Can be an integer or a callable that receives the request and returns an integer (one can do exponential back-off like that, for example).
|
8
|
+
* Added support for DNS-over-HTTPS GET requests as per the latest spec.
|
9
|
+
|
10
|
+
## Improvements
|
11
|
+
|
12
|
+
* `HTTPX.plugins` got deprecated; basically, it's great until you have to pass options to a plugin, and then it just works (not). The recommended way to load multiple plugins is `HTTPX.plugin(...).plugin(...)`.
|
13
|
+
|
14
|
+
|
15
|
+
## Bugfixes
|
16
|
+
|
17
|
+
* fixed a proxy bug where an `Alt-Svc` response header would make the client try to connect. Just like connection coalescing and the ORIGIN frame, it ignores it when going through a proxy.
|
18
|
+
|
19
|
+
|
@@ -0,0 +1,5 @@
|
|
1
|
+
# 0.6.7
|
2
|
+
|
3
|
+
## Bugfixes
|
4
|
+
|
5
|
+
* An error was reported when using the follow plugin allowing insecure redirects: if the insecure redirect would be for the same host, and the original request was performed with HTTP/2, the library would try to coalesce the request, and blocks the reactor. A check was made to ensure that connection only coalesces if both are https connections.
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# 0.7.0
|
2
|
+
|
3
|
+
|
4
|
+
## Features
|
5
|
+
|
6
|
+
New option: `:max_requests`. This is a connection-level option signalizing how many requests can be performed on a connection. Although the HTTP/1 parser defined this well, in HTTP/2 this wasn't very clear, so: by definition, the remote MAX_CONCURRENT_STREAMS setting will be used to define it, unless the user explicitly passed the option. You can also pass `:max_requests => Float::INFINITY` if you know that the server allows more requests than that on a connection.
|
7
|
+
|
8
|
+
New plugin: `:expect`.
|
9
|
+
|
10
|
+
Although there was support for `expect: 100-continue` header already when passed, this plugin can:
|
11
|
+
|
12
|
+
* automatically set the header on requests with body;
|
13
|
+
* execute the flow;
|
14
|
+
* recover from 417 status errors (i.e. try again without it);
|
15
|
+
* send body after X seconds if no 100 response came;
|
16
|
+
|
17
|
+
Suport for `with_` methods for the session. As long as the suffix is a valid attribute, it's just like that:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
HTTPX.with_timeout(...).with_ssl(...)
|
21
|
+
# same as:
|
22
|
+
# HTTPX.with(timeout: ..., ssl: ...)
|
23
|
+
```
|
24
|
+
|
25
|
+
## Improvements
|
26
|
+
|
27
|
+
### Connections
|
28
|
+
|
29
|
+
The following improvements make the `persistent` plugin way more resilient:
|
30
|
+
|
31
|
+
* Better balancing of HTTP/2 connections by distributing requests among X connections depending of how many requests they can process.
|
32
|
+
* Exhausted connections can off-load to a new same-origin connection (such as, when the server negotiates less `MAX_CONCURRENT_STREAMS` than what's expected).
|
33
|
+
|
34
|
+
### Timeouts
|
35
|
+
|
36
|
+
(Timeouts will be one of the main improvements from the 0.7.x series)
|
37
|
+
|
38
|
+
`:total_timeout` is now a connection-level directive, which means that this feature will actually make more sense and account for all requests in a block at the same time, instead of one-by-one.
|
39
|
+
|
40
|
+
### Options
|
41
|
+
|
42
|
+
Option setters were being bypassed, therefore a lot of the type-checks defined there weren't effectively being picked upon, which could have led to weird user errors.
|
43
|
+
|
44
|
+
## Bugfixes
|
45
|
+
|
46
|
+
* fixed the `push_promise` plugin integration (wasn't working well since `http-2-next` was adopted);
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# 0.8.0
|
2
|
+
|
3
|
+
|
4
|
+
## Features
|
5
|
+
|
6
|
+
* `keep_alive_timeout`: for persistent connections, the keep alive timeout will set the connection to be closed if not reused for a request **after** the last received response;
|
7
|
+
|
8
|
+
## Improvements
|
9
|
+
|
10
|
+
* using `max_requests` for HTTP/1 pipelining as well;
|
11
|
+
* `retries` plugin now works with plain HTTP responses (not just error responses);
|
12
|
+
* reduced the number of string allocations from log labels;
|
13
|
+
* performance: a lot of improvements were made to optimize the "waiting for IO events" phase, which dramatically reduced the CPU usage and make the performance of the library more on-par with other ruby HTTP gems for the 1-shot request scenario.
|
14
|
+
|
15
|
+
|
16
|
+
## Bugfixes
|
17
|
+
|
18
|
+
* fixed `HTTPX::Response#copy_to`;
|
19
|
+
* fixed `compression` plugin not properly compressing request bodies using `gzip`;
|
20
|
+
* fixed `compression` plugin not handling `content-encoding: identity` payloads;
|
21
|
+
* do not overwrite user-defined `max_requests`on HTTP2 connection handshake;
|
22
|
+
* `retries` plugin: connection was blocking when a request with body was retried;
|
23
|
+
* `alt-svc: clear` response header was causing the process to hang;
|
24
|
+
|
25
|
+
## Tests
|
26
|
+
|
27
|
+
* Code coverage improved to 91%;
|
@@ -0,0 +1,7 @@
|
|
1
|
+
# 0.8.2
|
2
|
+
|
3
|
+
## Features
|
4
|
+
|
5
|
+
* `:expect` plugin now supports a new option, `:expect_threshold_size`, meaning: the byte size threshold below which no `expect` header will be sent with requests with payload.
|
6
|
+
* `:compression` plugin now supports a new option, `:compression_threshold_size`, meaning: the bytesize threshold below which request payload won't be compressed before being sent.
|
7
|
+
* for HTTP/2 connections, when `keep_alive_timeout` expires, a `PING` frame is used to check connection availability; if successful, the connection will be reused.
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# 0.9.0
|
2
|
+
|
3
|
+
## Features
|
4
|
+
|
5
|
+
### Multiple requests with specific options
|
6
|
+
|
7
|
+
You can now pass a third element to the "request element" of an array to `.request`.
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
requests = [
|
11
|
+
[:post, "https://url/post", { form: { foo: "bar" } }],
|
12
|
+
[:post, "https://url/post", { form: { foo: "bar2" } }]
|
13
|
+
]
|
14
|
+
HTTPX.request(requests)
|
15
|
+
# or, if you want to pass options common to all requests
|
16
|
+
HTTPX.request(requests, max_concurrent_requests: 1)
|
17
|
+
```
|
18
|
+
|
19
|
+
|
20
|
+
### HTTPX::Session#build_request
|
21
|
+
|
22
|
+
`HTTPX::Session::build_request` is now public API from a session. You can now build requests before you send them. These request objects are still considered somewhat "internal", so consider them immutable and **do not rely on its API**. Just pass them forward.
|
23
|
+
|
24
|
+
Note: this API is only available for instantiated session, so there is no `HTTPX.build_request`.
|
25
|
+
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
|
29
|
+
HTTPX.wrap do |http|
|
30
|
+
requests = [
|
31
|
+
http.build_request(:post, "https://url/post", { form: { foo: "bar" } }),
|
32
|
+
http.build_request(:post, "https://url/post", { form: { foo: "bar2" } })
|
33
|
+
]
|
34
|
+
http.request(requests)
|
35
|
+
# or, if you want to pass options common to all requests
|
36
|
+
http.request(requests, max_concurrent_requests: 1)
|
37
|
+
end
|
38
|
+
```
|
data/lib/httpx.rb
CHANGED
data/lib/httpx/chainable.rb
CHANGED
@@ -10,8 +10,8 @@ module HTTPX
|
|
10
10
|
MOD
|
11
11
|
end
|
12
12
|
|
13
|
-
def request(
|
14
|
-
branch(default_options).request(
|
13
|
+
def request(*args, **options)
|
14
|
+
branch(default_options).request(*args, **options)
|
15
15
|
end
|
16
16
|
|
17
17
|
# :nocov:
|
@@ -34,20 +34,23 @@ module HTTPX
|
|
34
34
|
branch(default_options).wrap(&blk)
|
35
35
|
end
|
36
36
|
|
37
|
-
def plugin(*args, **opts)
|
37
|
+
def plugin(*args, **opts, &blk)
|
38
38
|
klass = is_a?(Session) ? self.class : Session
|
39
39
|
klass = Class.new(klass)
|
40
40
|
klass.instance_variable_set(:@default_options, klass.default_options.merge(default_options))
|
41
|
-
klass.plugin(*args, **opts).new
|
41
|
+
klass.plugin(*args, **opts, &blk).new
|
42
42
|
end
|
43
43
|
|
44
44
|
# deprecated
|
45
|
+
# :nocov:
|
45
46
|
def plugins(*args, **opts)
|
47
|
+
warn ":#{__method__} is deprecated, use :plugin instead"
|
46
48
|
klass = is_a?(Session) ? self.class : Session
|
47
49
|
klass = Class.new(klass)
|
48
50
|
klass.instance_variable_set(:@default_options, klass.default_options.merge(default_options))
|
49
51
|
klass.plugins(*args, **opts).new
|
50
52
|
end
|
53
|
+
# :nocov:
|
51
54
|
|
52
55
|
def with(options, &blk)
|
53
56
|
branch(default_options.merge(options), &blk)
|
@@ -59,7 +62,6 @@ module HTTPX
|
|
59
62
|
@options || Options.new
|
60
63
|
end
|
61
64
|
|
62
|
-
# :nodoc:
|
63
65
|
def branch(options, &blk)
|
64
66
|
return self.class.new(options, &blk) if is_a?(Session)
|
65
67
|
|
@@ -67,12 +69,10 @@ module HTTPX
|
|
67
69
|
end
|
68
70
|
|
69
71
|
def method_missing(meth, *args, **options)
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
super
|
75
|
-
end
|
72
|
+
return super unless meth =~ /\Awith_(.+)/
|
73
|
+
|
74
|
+
option = Regexp.last_match(1).to_sym
|
75
|
+
with(option => (args.first || options))
|
76
76
|
end
|
77
77
|
|
78
78
|
def respond_to_missing?(meth, *args)
|
data/lib/httpx/connection.rb
CHANGED
@@ -51,7 +51,7 @@ module HTTPX
|
|
51
51
|
def initialize(type, uri, options)
|
52
52
|
@type = type
|
53
53
|
@origins = [uri.origin]
|
54
|
-
@origin =
|
54
|
+
@origin = Utils.uri(uri.origin)
|
55
55
|
@options = Options.new(options)
|
56
56
|
@window_size = @options.window_size
|
57
57
|
@read_buffer = Buffer.new(BUFFER_SIZE)
|
@@ -88,8 +88,6 @@ module HTTPX
|
|
88
88
|
|
89
89
|
return false if exhausted?
|
90
90
|
|
91
|
-
return false if @keep_alive_timer && @keep_alive_timer.fires_in.negative?
|
92
|
-
|
93
91
|
(
|
94
92
|
(
|
95
93
|
@origins.include?(uri.origin) &&
|
@@ -107,8 +105,6 @@ module HTTPX
|
|
107
105
|
|
108
106
|
return false if exhausted?
|
109
107
|
|
110
|
-
return false if @keep_alive_timer && @keep_alive_timer.fires_in.negative?
|
111
|
-
|
112
108
|
!(@io.addresses & connection.addresses).empty? && @options == connection.options
|
113
109
|
end
|
114
110
|
|
@@ -124,8 +120,8 @@ module HTTPX
|
|
124
120
|
end
|
125
121
|
end
|
126
122
|
|
127
|
-
def create_idle
|
128
|
-
self.class.new(@type, @origin, @options)
|
123
|
+
def create_idle(options = {})
|
124
|
+
self.class.new(@type, @origin, @options.merge(options))
|
129
125
|
end
|
130
126
|
|
131
127
|
def merge(connection)
|
@@ -135,18 +131,7 @@ module HTTPX
|
|
135
131
|
end
|
136
132
|
end
|
137
133
|
|
138
|
-
def
|
139
|
-
@origins -= connection.instance_variable_get(:@origins)
|
140
|
-
purge_pending do |request|
|
141
|
-
request.uri.origin == connection.origin && begin
|
142
|
-
request.transition(:idle)
|
143
|
-
connection.send(request)
|
144
|
-
true
|
145
|
-
end
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
def purge_pending
|
134
|
+
def purge_pending(&block)
|
150
135
|
pendings = []
|
151
136
|
if @parser
|
152
137
|
@inflight -= @parser.pending.size
|
@@ -154,9 +139,7 @@ module HTTPX
|
|
154
139
|
end
|
155
140
|
pendings << @pending
|
156
141
|
pendings.each do |pending|
|
157
|
-
pending.reject!
|
158
|
-
yield request
|
159
|
-
end
|
142
|
+
pending.reject!(&block)
|
160
143
|
end
|
161
144
|
end
|
162
145
|
|
@@ -229,8 +212,19 @@ module HTTPX
|
|
229
212
|
def send(request)
|
230
213
|
if @parser && !@write_buffer.full?
|
231
214
|
request.headers["alt-used"] = @origin.authority if match_altsvcs?(request.uri)
|
215
|
+
if @keep_alive_timer
|
216
|
+
# when pushing a request into an existing connection, we have to check whether there
|
217
|
+
# is the possibility that the connection might have extended the keep alive timeout.
|
218
|
+
# for such cases, we want to ping for availability before deciding to shovel requests.
|
219
|
+
if @keep_alive_timer.fires_in.negative?
|
220
|
+
@pending << request
|
221
|
+
parser.ping
|
222
|
+
return
|
223
|
+
end
|
224
|
+
|
225
|
+
@keep_alive_timer.pause
|
226
|
+
end
|
232
227
|
@inflight += 1
|
233
|
-
@keep_alive_timer.pause if @keep_alive_timer
|
234
228
|
parser.send(request)
|
235
229
|
else
|
236
230
|
@pending << request
|
@@ -286,8 +280,6 @@ module HTTPX
|
|
286
280
|
break
|
287
281
|
end
|
288
282
|
|
289
|
-
log { "READ: #{siz} bytes..." }
|
290
|
-
|
291
283
|
parser << @read_buffer.to_s
|
292
284
|
|
293
285
|
break if @state == :closing || @state == :closed
|
@@ -310,7 +302,6 @@ module HTTPX
|
|
310
302
|
on_error(ex)
|
311
303
|
return
|
312
304
|
end
|
313
|
-
log { "WRITE: #{siz} bytes..." }
|
314
305
|
|
315
306
|
if siz.zero?
|
316
307
|
write_drained = !@write_buffer.empty?
|
@@ -361,6 +352,8 @@ module HTTPX
|
|
361
352
|
emit(:altsvc, alt_origin, origin, alt_params)
|
362
353
|
end
|
363
354
|
|
355
|
+
parser.on(:pong, &method(:send_pending))
|
356
|
+
|
364
357
|
parser.on(:promise) do |request, stream|
|
365
358
|
request.emit(:promise, parser, stream)
|
366
359
|
end
|
@@ -395,7 +388,7 @@ module HTTPX
|
|
395
388
|
parser.on(:error) do |request, ex|
|
396
389
|
case ex
|
397
390
|
when MisdirectedRequestError
|
398
|
-
emit(:
|
391
|
+
emit(:misdirected, request)
|
399
392
|
else
|
400
393
|
response = ErrorResponse.new(request, ex, @options)
|
401
394
|
request.emit(:response, response)
|
@@ -431,7 +424,7 @@ module HTTPX
|
|
431
424
|
remove_instance_variable(:@total_timeout)
|
432
425
|
end
|
433
426
|
|
434
|
-
@io.close
|
427
|
+
@io.close if @io
|
435
428
|
@read_buffer.clear
|
436
429
|
if @keep_alive_timer
|
437
430
|
@keep_alive_timer.cancel
|
@@ -451,7 +444,6 @@ module HTTPX
|
|
451
444
|
throw(:jump_tick)
|
452
445
|
rescue Errno::ECONNREFUSED,
|
453
446
|
Errno::EADDRNOTAVAIL,
|
454
|
-
Errno::EHOSTUNREACH,
|
455
447
|
OpenSSL::SSL::SSLError => e
|
456
448
|
# connect errors, exit gracefully
|
457
449
|
handle_error(e)
|
@@ -469,8 +461,8 @@ module HTTPX
|
|
469
461
|
else
|
470
462
|
@keep_alive_timer = @timers.after(@keep_alive_timeout) do
|
471
463
|
unless @inflight.zero?
|
472
|
-
log { "(#{
|
473
|
-
|
464
|
+
log { "(#{@origin}): keep alive timeout expired" }
|
465
|
+
parser.ping
|
474
466
|
end
|
475
467
|
end
|
476
468
|
end
|
@@ -21,6 +21,7 @@ module HTTPX
|
|
21
21
|
@version = [1, 1]
|
22
22
|
@pending = []
|
23
23
|
@requests = []
|
24
|
+
@handshake_completed = false
|
24
25
|
end
|
25
26
|
|
26
27
|
def interests
|
@@ -78,6 +79,7 @@ module HTTPX
|
|
78
79
|
break if idx >= requests_limit
|
79
80
|
next if request.state == :done
|
80
81
|
|
82
|
+
request.headers["connection"] ||= request.options.persistent || idx < requests_limit - 1 ? "keep-alive" : "close"
|
81
83
|
handle(request)
|
82
84
|
end
|
83
85
|
end
|
@@ -154,6 +156,7 @@ module HTTPX
|
|
154
156
|
@parser.reset!
|
155
157
|
@max_requests -= 1
|
156
158
|
manage_connection(response)
|
159
|
+
|
157
160
|
send(@pending.shift) unless @pending.empty?
|
158
161
|
end
|
159
162
|
|
@@ -170,23 +173,39 @@ module HTTPX
|
|
170
173
|
end
|
171
174
|
end
|
172
175
|
|
176
|
+
def ping
|
177
|
+
emit(:reset)
|
178
|
+
emit(:exhausted)
|
179
|
+
end
|
180
|
+
|
173
181
|
private
|
174
182
|
|
175
183
|
def manage_connection(response)
|
176
184
|
connection = response.headers["connection"]
|
177
185
|
case connection
|
178
|
-
when /keep
|
186
|
+
when /keep-alive/i
|
187
|
+
if @handshake_completed
|
188
|
+
if @max_requests.zero?
|
189
|
+
@pending.concat(@requests)
|
190
|
+
@requests.clear
|
191
|
+
emit(:exhausted)
|
192
|
+
end
|
193
|
+
return
|
194
|
+
end
|
195
|
+
|
179
196
|
keep_alive = response.headers["keep-alive"]
|
180
197
|
return unless keep_alive
|
181
198
|
|
182
199
|
parameters = Hash[keep_alive.split(/ *, */).map do |pair|
|
183
200
|
pair.split(/ *= */)
|
184
201
|
end]
|
185
|
-
@max_requests = parameters["max"].to_i if parameters.key?("max")
|
202
|
+
@max_requests = parameters["max"].to_i - 1 if parameters.key?("max")
|
203
|
+
|
186
204
|
if parameters.key?("timeout")
|
187
205
|
keep_alive_timeout = parameters["timeout"].to_i
|
188
206
|
emit(:timeout, keep_alive_timeout)
|
189
207
|
end
|
208
|
+
@handshake_completed = true
|
190
209
|
when /close/i
|
191
210
|
disable
|
192
211
|
when nil
|
@@ -206,7 +225,14 @@ module HTTPX
|
|
206
225
|
def disable_pipelining
|
207
226
|
return if @requests.empty?
|
208
227
|
|
209
|
-
@requests.each
|
228
|
+
@requests.each do |r|
|
229
|
+
r.transition(:idle)
|
230
|
+
|
231
|
+
# when we disable pipelining, we still want to try keep-alive.
|
232
|
+
# only when keep-alive with one request fails, do we fallback to
|
233
|
+
# connection: close.
|
234
|
+
r.headers["connection"] = "close" if @max_concurrent_requests == 1
|
235
|
+
end
|
210
236
|
# server doesn't handle pipelining, and probably
|
211
237
|
# doesn't support keep-alive. Fallback to send only
|
212
238
|
# 1 keep alive request.
|
@@ -216,7 +242,7 @@ module HTTPX
|
|
216
242
|
|
217
243
|
def set_request_headers(request)
|
218
244
|
request.headers["host"] ||= request.authority
|
219
|
-
request.headers["connection"] ||= "keep-alive"
|
245
|
+
request.headers["connection"] ||= request.options.persistent ? "keep-alive" : "close"
|
220
246
|
if !request.headers.key?("content-length") &&
|
221
247
|
request.body.bytesize == Float::INFINITY
|
222
248
|
request.chunk!
|