httpx 0.15.3 → 0.17.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/doc/release_notes/0_15_4.md +5 -0
- data/doc/release_notes/0_16_0.md +93 -0
- data/doc/release_notes/0_16_1.md +5 -0
- data/doc/release_notes/0_17_0.md +49 -0
- data/lib/httpx/adapters/faraday.rb +3 -11
- data/lib/httpx/adapters/webmock.rb +2 -2
- data/lib/httpx/buffer.rb +1 -1
- data/lib/httpx/callbacks.rb +1 -1
- data/lib/httpx/chainable.rb +15 -8
- data/lib/httpx/connection/http1.rb +18 -10
- data/lib/httpx/connection/http2.rb +14 -21
- data/lib/httpx/connection.rb +6 -7
- data/lib/httpx/errors.rb +11 -11
- data/lib/httpx/headers.rb +1 -1
- data/lib/httpx/io/ssl.rb +2 -2
- data/lib/httpx/io/tls.rb +1 -1
- data/lib/httpx/options.rb +108 -81
- data/lib/httpx/parser/http1.rb +11 -7
- data/lib/httpx/plugins/aws_sigv4.rb +10 -9
- data/lib/httpx/plugins/compression.rb +12 -11
- data/lib/httpx/plugins/cookies/cookie.rb +4 -2
- data/lib/httpx/plugins/cookies/jar.rb +20 -1
- data/lib/httpx/plugins/cookies.rb +20 -7
- data/lib/httpx/plugins/digest_authentication.rb +19 -15
- data/lib/httpx/plugins/expect.rb +19 -15
- data/lib/httpx/plugins/follow_redirects.rb +9 -9
- data/lib/httpx/plugins/grpc/call.rb +4 -1
- data/lib/httpx/plugins/grpc.rb +73 -47
- data/lib/httpx/plugins/h2c.rb +7 -3
- data/lib/httpx/plugins/multipart/decoder.rb +187 -0
- data/lib/httpx/plugins/multipart/mime_type_detector.rb +3 -3
- data/lib/httpx/plugins/multipart/part.rb +2 -2
- data/lib/httpx/plugins/multipart.rb +14 -0
- data/lib/httpx/plugins/ntlm_authentication.rb +12 -10
- data/lib/httpx/plugins/proxy/socks4.rb +2 -1
- data/lib/httpx/plugins/proxy/socks5.rb +2 -1
- data/lib/httpx/plugins/proxy/ssh.rb +20 -13
- data/lib/httpx/plugins/proxy.rb +10 -10
- data/lib/httpx/plugins/retries.rb +25 -21
- data/lib/httpx/plugins/stream.rb +2 -3
- data/lib/httpx/plugins/upgrade.rb +7 -6
- data/lib/httpx/registry.rb +2 -2
- data/lib/httpx/request.rb +10 -19
- data/lib/httpx/resolver/https.rb +0 -2
- data/lib/httpx/resolver/native.rb +15 -3
- data/lib/httpx/resolver/resolver_mixin.rb +2 -1
- data/lib/httpx/response.rb +72 -38
- data/lib/httpx/selector.rb +6 -7
- data/lib/httpx/session.rb +34 -21
- data/lib/httpx/session2.rb +23 -0
- data/lib/httpx/transcoder/body.rb +1 -1
- data/lib/httpx/transcoder/chunker.rb +2 -1
- data/lib/httpx/transcoder/form.rb +20 -0
- data/lib/httpx/transcoder/json.rb +12 -0
- data/lib/httpx/transcoder.rb +62 -1
- data/lib/httpx/utils.rb +2 -2
- data/lib/httpx/version.rb +1 -1
- data/lib/httpx.rb +6 -3
- data/sig/buffer.rbs +3 -1
- data/sig/chainable.rbs +30 -29
- data/sig/connection/http1.rbs +11 -5
- data/sig/connection/http2.rbs +16 -5
- data/sig/connection.rbs +23 -11
- data/sig/errors.rbs +35 -1
- data/sig/headers.rbs +20 -19
- data/sig/httpx.rbs +4 -1
- data/sig/loggable.rbs +3 -1
- data/sig/options.rbs +45 -34
- data/sig/parser/http1.rbs +3 -3
- data/sig/plugins/authentication.rbs +1 -1
- data/sig/plugins/aws_sdk_authentication.rbs +5 -1
- data/sig/plugins/aws_sigv4.rbs +13 -5
- data/sig/plugins/basic_authentication.rbs +1 -1
- data/sig/plugins/compression.rbs +4 -6
- data/sig/plugins/cookies/cookie.rbs +5 -7
- data/sig/plugins/cookies/jar.rbs +9 -10
- data/sig/plugins/cookies.rbs +4 -5
- data/sig/plugins/digest_authentication.rbs +2 -3
- data/sig/plugins/expect.rbs +2 -4
- data/sig/plugins/follow_redirects.rbs +3 -5
- data/sig/plugins/grpc.rbs +4 -7
- data/sig/plugins/h2c.rbs +0 -2
- data/sig/plugins/multipart.rbs +64 -10
- data/sig/plugins/ntlm_authentication.rbs +2 -3
- data/sig/plugins/persistent.rbs +3 -8
- data/sig/plugins/proxy/ssh.rbs +4 -4
- data/sig/plugins/proxy.rbs +13 -13
- data/sig/plugins/push_promise.rbs +0 -2
- data/sig/plugins/retries.rbs +4 -8
- data/sig/plugins/stream.rbs +1 -1
- data/sig/plugins/upgrade.rbs +2 -3
- data/sig/pool.rbs +1 -2
- data/sig/registry.rbs +1 -1
- data/sig/request.rbs +11 -8
- data/sig/resolver/native.rbs +12 -6
- data/sig/resolver/resolver_mixin.rbs +4 -5
- data/sig/resolver/system.rbs +2 -0
- data/sig/resolver.rbs +7 -0
- data/sig/response.rbs +24 -12
- data/sig/selector.rbs +11 -9
- data/sig/session.rbs +22 -23
- data/sig/transcoder/body.rbs +6 -1
- data/sig/transcoder/chunker.rbs +8 -2
- data/sig/transcoder/form.rbs +3 -1
- data/sig/transcoder/json.rbs +2 -0
- data/sig/transcoder.rbs +13 -5
- data/sig/utils.rbs +2 -0
- metadata +12 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f6b2befec2e4b0093acd45f7ef9ef448ad41516510710616aded46934f1e3981
|
4
|
+
data.tar.gz: a9858adfacbdc27e1097b98b958f7dd1614f1207c74ec4290a54268df6270f69
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 86276d59efaf3a15efe0a27fbd59bff2d005bb3bab83ee9917599854bf46eba1bfbb016b9df55172c41799d1fe82195e4bb7d82c008c1996814ec2e393be71b3
|
7
|
+
data.tar.gz: bd113e65cf5700f231992bb485f6c59511f114372d53078ea53c019a654e449e5012b2a1a19f889d79cee1eecd24345d3e03f3f9fa50aa6c69060138327e1d09
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# 0.16.0
|
2
|
+
|
3
|
+
## Features
|
4
|
+
|
5
|
+
### Response::Body#to_s does not clear the internal buffer
|
6
|
+
|
7
|
+
It's well documented that the response body should be treated as a file, and that calling `.to_s` on a response should be done only once, and the user should not expect the same call to return the same response body again, while suggesting that the first call should be cached in a variable in case it's needed:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
response = HTTPX.get("https://google.com")
|
11
|
+
body = response.body.to_s #=> "<html ...."
|
12
|
+
response.body.to_s #=> ""
|
13
|
+
|
14
|
+
# thankfully,it's cached in the body var there.
|
15
|
+
```
|
16
|
+
|
17
|
+
The justification for this behaviour probably had to do with avoiding keeping huge payloads around, but it got a bit lost in git history. It became a feature, not a bug.
|
18
|
+
|
19
|
+
However, I got an [issue report](https://gitlab.com/honeyryderchuck/httpx/-/issues/143) that made me change my mind about this behaviour (tl;dr: it broke pattern matching when matching against response bodies more than once).
|
20
|
+
|
21
|
+
So now, you can call `.to_s` how many times you want!
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
response = HTTPX.get("https://google.com")
|
25
|
+
body = response.body.to_s #=> "<html ...."
|
26
|
+
response.body.to_s #=> "<html ....", still here!
|
27
|
+
```
|
28
|
+
|
29
|
+
Some optimizations were done around how the body is carried forward, and bodies buffered in files will now get properly garbage collected and not leak descriptors behind when users forget to call `.close`.
|
30
|
+
|
31
|
+
### grpc plugin improvements
|
32
|
+
|
33
|
+
##### build fully-enabled stub from grpc service
|
34
|
+
|
35
|
+
The `:grpc` plugin can now build fully-loaded stubs from existing GRPC generic services.
|
36
|
+
|
37
|
+
GRPC stubs could be a bit tedious to write when compared to what the `grpc` gem offers, which is, auto-generation from ruby service stubs from protobuf definitions:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
# service generated from the command:
|
41
|
+
#
|
42
|
+
# > grpc_tools_ruby_protoc -I ../../protos --ruby_out=../lib --grpc_out=../lib ../../protos/route_guide.proto
|
43
|
+
#
|
44
|
+
require "route_guide_services_pb.rb"
|
45
|
+
|
46
|
+
# with httpx, before 0.16
|
47
|
+
stub = HTTPX.plugin(:grpc).build_stub("localhost:#{server_port}", service: "RouteGuide")
|
48
|
+
.rpc(:GetFeature, Point, Feature)
|
49
|
+
.rpc(:ListFeatures, # ... and so on, all hand stitched
|
50
|
+
|
51
|
+
stub.get_feature(# ...
|
52
|
+
|
53
|
+
# with httpx 0.16
|
54
|
+
stub = HTTPX.plugin(:grpc).build_stub("localhost:#{server_port}", service: RouteGuide)
|
55
|
+
# that's it!
|
56
|
+
stub.get_feature(# ...
|
57
|
+
```
|
58
|
+
|
59
|
+
#### no google/protobuf direct dependency
|
60
|
+
|
61
|
+
`"google/protobuf"` is no longer assumed when using the plugin, i.e. you can use other protobuf serializers, such as https://github.com/ruby-protobuf/protobuf , which supports `jruby` (unlike the former).
|
62
|
+
|
63
|
+
### OptionsMethods for plugins
|
64
|
+
|
65
|
+
https://gitlab.com/honeyryderchuck/httpx/-/wikis/Custom-Plugins
|
66
|
+
|
67
|
+
You can now define an `OptionsMethods` module under your custom plugin to define your own methods. The tl;dr is, that, given the following module below, a new `:bar` option will be available (and the method will be used to set it):
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
module CustomPlugin
|
71
|
+
module OptionsMethods
|
72
|
+
def option_bar(x) ; x; end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
HTTPX.plugin(CustomPlugin).with(bar: 2)
|
77
|
+
```
|
78
|
+
|
79
|
+
### cookies plugin: improved jar management
|
80
|
+
|
81
|
+
The behaviour of the cookies jar from the `:cookies` plugin was a bit unpredictable in certain conditions, for instance if a "Cookie" header would be passed directly via `.with(headers: {"Cookie" => "a=1"})` and there'd be a value for it already (in same cases, it'd be fully ignored). This would even get worse, if the session had a jar, and a specific set of cookies would be passed to a request(i.e.: `session_with_cookies.get("http://url.get", headers: {"Cookies" => "..."}`).
|
82
|
+
|
83
|
+
The behaviour was fixed, and is now specced under https://gitlab.com/honeyryderchuck/httpx/-/blob/master/test/support/requests/plugins/cookies.rb .
|
84
|
+
|
85
|
+
## Bugfixes
|
86
|
+
|
87
|
+
* Cookies sorting in the `:cookies` plugin jar was fixed for truffleruby;
|
88
|
+
|
89
|
+
## Chore
|
90
|
+
|
91
|
+
* errors when setting options nnow raise `TypeError` instead of `HTTPX::Error`.
|
92
|
+
* options are now internally frozen by default, which should protect the internals against accidentally updating them;
|
93
|
+
* Fixed optimization around options initialization, to prevent needless allocations;
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# 0.17.0
|
2
|
+
|
3
|
+
## Features
|
4
|
+
|
5
|
+
### Response mime type decoders (#json, #form)
|
6
|
+
|
7
|
+
https://gitlab.com/honeyryderchuck/httpx/-/wikis/Response-Handling#response-decoding
|
8
|
+
|
9
|
+
Two new methods, `#json` and `#form`, were added to `HTTPX::Response`. As the name implies, they'll decode the raw payload into ruby objects you can work with.
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
# after HTTPX.get("https://api.smth/endpoint-returning-json")
|
13
|
+
response.json # same as JSON.dump(response.to_s)
|
14
|
+
```
|
15
|
+
|
16
|
+
Although not yet documented, integrating custom decoders is also possible (i.e. parsing HTML with `nokogiri` or something similar).
|
17
|
+
|
18
|
+
## Improvements
|
19
|
+
|
20
|
+
### Connection: reduce interest calculations
|
21
|
+
|
22
|
+
Due to it being an intensive task, internal interest calculation in connections was reduce to the bare minimum.
|
23
|
+
|
24
|
+
### Immutable Options, internal recycling of instances, improves memory usage in the happy path
|
25
|
+
|
26
|
+
A lot of effort went into avoiding generating options objects internally whenever necessary. This means, when sending several requests with the same set of options (the most common case in `httpx` usage), internally only one object is passed around. For that, the following improvements were done:
|
27
|
+
|
28
|
+
* `Options#merge` returns the same options the the options being merged are a subset of the current set of options (b126938a6547e09b726dd64298fb488891d938e9).
|
29
|
+
* `Session#build_request` bypasses instantiation of options if it receives an `Options` object (which happens internally in the happy path, if users don't call `#build_request` directly) (3d549817cb41d4b904102fdc61afe3ecd9170893).
|
30
|
+
* Improving internal `Session` APIs to not pass around options, and instead rely on accessing request options.
|
31
|
+
* `Options#to_hash` does not build internal garbage arrays anymore (cc02679b804f63798f5d2136a039be1624e96ab6).
|
32
|
+
|
33
|
+
### Reduce regexp operations in the HTTP/1 parser
|
34
|
+
|
35
|
+
Some code paths in the HTTP/1 parser still using regular expressions were replaced by string operations accomplishing the same.
|
36
|
+
|
37
|
+
### HTTP/1 improvements on the complexity of connection accounting calculations
|
38
|
+
|
39
|
+
Managing open HTTP/1 connections relies on operations calculating whether there are requests waiting for completion. This relied on traversing all requests for that connectionn (O(n)); it now only checks the completion state of the first and last request of that connection, given that all requests in HTTP/1 are sequential (O(1)); this optimization brings a big improvement to persistent and pipelined requests (65261217b1270913e4bb93717e8b8dcfa775565a).
|
40
|
+
|
41
|
+
## Bugfixes
|
42
|
+
|
43
|
+
* fixing HTTP/1 protocol uncompliant exposing multiple values for the "Host" header (e435dd0534314508262184fb03d83124d89d2079).
|
44
|
+
|
45
|
+
* Custom response finalizer introduced in 0.16.0 has been reverted. It was brought to my attention that `Tempfile` implementation already takes care of the file on GC (and `httpx` was duplicating), and the approach taken in `httpx` was buggy in several ways (not tolerant to forks, never recycled finalizers...) (aa3be21c890f92a41afcc7931f01dd24cc801f7c).
|
46
|
+
|
47
|
+
## Chore
|
48
|
+
|
49
|
+
RBS Typing improvements based on latest stdlib signatures additions, such as `openssl`, `digest`, `socket` and others.
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "delegate"
|
3
4
|
require "httpx"
|
4
5
|
require "faraday"
|
5
6
|
|
@@ -91,11 +92,12 @@ module Faraday
|
|
91
92
|
end
|
92
93
|
|
93
94
|
class ParallelManager
|
94
|
-
class ResponseHandler
|
95
|
+
class ResponseHandler < SimpleDelegator
|
95
96
|
attr_reader :env
|
96
97
|
|
97
98
|
def initialize(env)
|
98
99
|
@env = env
|
100
|
+
super
|
99
101
|
end
|
100
102
|
|
101
103
|
def on_response(&blk)
|
@@ -117,16 +119,6 @@ module Faraday
|
|
117
119
|
@on_complete
|
118
120
|
end
|
119
121
|
end
|
120
|
-
|
121
|
-
def respond_to_missing?(meth)
|
122
|
-
@env.respond_to?(meth) || super
|
123
|
-
end
|
124
|
-
|
125
|
-
def method_missing(meth, *args, &blk)
|
126
|
-
return super unless @env && @env.respond_to?(meth)
|
127
|
-
|
128
|
-
@env.__send__(meth, *args, &blk)
|
129
|
-
end
|
130
122
|
end
|
131
123
|
|
132
124
|
include RequestMixin
|
@@ -19,7 +19,7 @@ module WebMock
|
|
19
19
|
module InstanceMethods
|
20
20
|
private
|
21
21
|
|
22
|
-
def send_requests(*requests
|
22
|
+
def send_requests(*requests)
|
23
23
|
request_signatures = requests.map do |request|
|
24
24
|
request_signature = _build_webmock_request_signature(request)
|
25
25
|
WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
|
@@ -47,7 +47,7 @@ module WebMock
|
|
47
47
|
|
48
48
|
unless real_requests.empty?
|
49
49
|
reqs = real_requests.keys
|
50
|
-
reqs.zip(super(*reqs
|
50
|
+
reqs.zip(super(*reqs)).each do |req, res|
|
51
51
|
idx = real_requests[req]
|
52
52
|
|
53
53
|
if WebMock::CallbackRegistry.any_callbacks?
|
data/lib/httpx/buffer.rb
CHANGED
data/lib/httpx/callbacks.rb
CHANGED
@@ -19,7 +19,7 @@ module HTTPX
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def emit(type, *args)
|
22
|
-
callbacks(type).delete_if { |pr| :delete == pr
|
22
|
+
callbacks(type).delete_if { |pr| :delete == pr.call(*args) } # rubocop:disable Style/YodaCondition
|
23
23
|
end
|
24
24
|
|
25
25
|
protected
|
data/lib/httpx/chainable.rb
CHANGED
@@ -34,21 +34,21 @@ module HTTPX
|
|
34
34
|
branch(default_options).wrap(&blk)
|
35
35
|
end
|
36
36
|
|
37
|
-
def plugin(
|
37
|
+
def plugin(pl, options = nil, &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(
|
41
|
+
klass.plugin(pl, options, &blk).new
|
42
42
|
end
|
43
43
|
|
44
44
|
# deprecated
|
45
45
|
# :nocov:
|
46
|
-
def plugins(
|
46
|
+
def plugins(pls)
|
47
47
|
warn ":#{__method__} is deprecated, use :plugin instead"
|
48
48
|
klass = is_a?(Session) ? self.class : Session
|
49
49
|
klass = Class.new(klass)
|
50
50
|
klass.instance_variable_set(:@default_options, klass.default_options.merge(default_options))
|
51
|
-
klass.plugins(
|
51
|
+
klass.plugins(pls).new
|
52
52
|
end
|
53
53
|
# :nocov:
|
54
54
|
|
@@ -71,12 +71,19 @@ module HTTPX
|
|
71
71
|
def method_missing(meth, *args, **options)
|
72
72
|
return super unless meth =~ /\Awith_(.+)/
|
73
73
|
|
74
|
-
option = Regexp.last_match(1)
|
75
|
-
|
74
|
+
option = Regexp.last_match(1)
|
75
|
+
|
76
|
+
return super unless option
|
77
|
+
|
78
|
+
with(option.to_sym => (args.first || options))
|
76
79
|
end
|
77
80
|
|
78
|
-
def respond_to_missing?(meth, *
|
79
|
-
|
81
|
+
def respond_to_missing?(meth, *)
|
82
|
+
return super unless meth =~ /\Awith_(.+)/
|
83
|
+
|
84
|
+
option = Regexp.last_match(1)
|
85
|
+
|
86
|
+
default_options.respond_to?(option) || super
|
80
87
|
end
|
81
88
|
end
|
82
89
|
end
|
@@ -59,7 +59,12 @@ module HTTPX
|
|
59
59
|
def empty?
|
60
60
|
# this means that for every request there's an available
|
61
61
|
# partial response, so there are no in-flight requests waiting.
|
62
|
-
@requests.empty? ||
|
62
|
+
@requests.empty? || (
|
63
|
+
# checking all responses can be time-consuming. Alas, as in HTTP/1, responses
|
64
|
+
# do not come out of order, we can get away with checking first and last.
|
65
|
+
!@requests.first.response.nil? &&
|
66
|
+
(@requests.size == 1 || !@requests.last.response.nil?)
|
67
|
+
)
|
63
68
|
end
|
64
69
|
|
65
70
|
def <<(data)
|
@@ -260,10 +265,12 @@ module HTTPX
|
|
260
265
|
def set_protocol_headers(request)
|
261
266
|
if !request.headers.key?("content-length") &&
|
262
267
|
request.body.bytesize == Float::INFINITY
|
263
|
-
request.chunk!
|
268
|
+
request.body.chunk!
|
264
269
|
end
|
265
270
|
|
266
|
-
connection =
|
271
|
+
connection = request.headers["connection"]
|
272
|
+
|
273
|
+
connection ||= if request.options.persistent
|
267
274
|
# when in a persistent connection, the request can't be at
|
268
275
|
# the edge of a renegotiation
|
269
276
|
if @requests.index(request) + 1 < @max_requests
|
@@ -283,10 +290,9 @@ module HTTPX
|
|
283
290
|
end
|
284
291
|
end
|
285
292
|
|
286
|
-
{
|
287
|
-
|
288
|
-
|
289
|
-
}
|
293
|
+
extra_headers = { "connection" => connection }
|
294
|
+
extra_headers["host"] = request.authority unless request.headers.key?("host")
|
295
|
+
extra_headers
|
290
296
|
end
|
291
297
|
|
292
298
|
def headline_uri(request)
|
@@ -316,7 +322,7 @@ module HTTPX
|
|
316
322
|
end
|
317
323
|
|
318
324
|
def join_body(request)
|
319
|
-
return if request.empty?
|
325
|
+
return if request.body.empty?
|
320
326
|
|
321
327
|
while (chunk = request.drain_body)
|
322
328
|
log(color: :green) { "<- DATA: #{chunk.bytesize} bytes..." }
|
@@ -325,7 +331,9 @@ module HTTPX
|
|
325
331
|
throw(:buffer_full, request) if @buffer.full?
|
326
332
|
end
|
327
333
|
|
328
|
-
|
334
|
+
return unless (error = request.drain_error)
|
335
|
+
|
336
|
+
raise error
|
329
337
|
end
|
330
338
|
|
331
339
|
def join_trailers(request)
|
@@ -352,7 +360,7 @@ module HTTPX
|
|
352
360
|
}.freeze
|
353
361
|
|
354
362
|
def capitalized(field)
|
355
|
-
UPCASED[field] || field.
|
363
|
+
UPCASED[field] || field.split("-").map(&:capitalize).join("-")
|
356
364
|
end
|
357
365
|
end
|
358
366
|
Connection.register "http/1.1", Connection::HTTP1
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "securerandom"
|
4
|
-
require "io/wait"
|
5
4
|
require "http/2/next"
|
6
5
|
|
7
6
|
module HTTPX
|
@@ -11,7 +10,7 @@ module HTTPX
|
|
11
10
|
|
12
11
|
MAX_CONCURRENT_REQUESTS = HTTP2Next::DEFAULT_MAX_CONCURRENT_STREAMS
|
13
12
|
|
14
|
-
Error
|
13
|
+
class Error < Error
|
15
14
|
def initialize(id, code)
|
16
15
|
super("stream #{id} closed with error: #{code}")
|
17
16
|
end
|
@@ -56,7 +55,7 @@ module HTTPX
|
|
56
55
|
|
57
56
|
return :w if !@pending.empty? && can_buffer_more_requests?
|
58
57
|
|
59
|
-
return :w
|
58
|
+
return :w unless @drains.empty?
|
60
59
|
|
61
60
|
if @buffer.empty?
|
62
61
|
return if @streams.empty? && @pings.empty?
|
@@ -218,7 +217,7 @@ module HTTPX
|
|
218
217
|
log(level: 1, color: :yellow) do
|
219
218
|
request.headers.merge(extra_headers).each.map { |k, v| "#{stream.id}: -> HEADER: #{k}: #{v}" }.join("\n")
|
220
219
|
end
|
221
|
-
stream.headers(request.headers.each(extra_headers), end_stream: request.empty?)
|
220
|
+
stream.headers(request.headers.each(extra_headers), end_stream: request.body.empty?)
|
222
221
|
end
|
223
222
|
|
224
223
|
def join_trailers(stream, request)
|
@@ -234,7 +233,7 @@ module HTTPX
|
|
234
233
|
end
|
235
234
|
|
236
235
|
def join_body(stream, request)
|
237
|
-
return if request.empty?
|
236
|
+
return if request.body.empty?
|
238
237
|
|
239
238
|
chunk = @drains.delete(request) || request.drain_body
|
240
239
|
while chunk
|
@@ -249,7 +248,9 @@ module HTTPX
|
|
249
248
|
chunk = next_chunk
|
250
249
|
end
|
251
250
|
|
252
|
-
|
251
|
+
return unless (error = request.drain_error)
|
252
|
+
|
253
|
+
on_stream_refuse(stream, request, error)
|
253
254
|
end
|
254
255
|
|
255
256
|
######
|
@@ -257,8 +258,10 @@ module HTTPX
|
|
257
258
|
######
|
258
259
|
|
259
260
|
def on_stream_headers(stream, request, h)
|
260
|
-
|
261
|
-
|
261
|
+
response = request.response
|
262
|
+
|
263
|
+
if response.is_a?(Response) && response.version == "2.0"
|
264
|
+
on_stream_trailers(stream, response, h)
|
262
265
|
return
|
263
266
|
end
|
264
267
|
|
@@ -274,11 +277,11 @@ module HTTPX
|
|
274
277
|
handle(request, stream) if request.expects?
|
275
278
|
end
|
276
279
|
|
277
|
-
def on_stream_trailers(stream,
|
280
|
+
def on_stream_trailers(stream, response, h)
|
278
281
|
log(color: :yellow) do
|
279
282
|
h.map { |k, v| "#{stream.id}: <- HEADER: #{k}: #{v}" }.join("\n")
|
280
283
|
end
|
281
|
-
|
284
|
+
response.merge_headers(h)
|
282
285
|
end
|
283
286
|
|
284
287
|
def on_stream_data(stream, request, data)
|
@@ -304,7 +307,7 @@ module HTTPX
|
|
304
307
|
emit(:response, request, response)
|
305
308
|
else
|
306
309
|
response = request.response
|
307
|
-
if response.status == 421
|
310
|
+
if response && response.status == 421
|
308
311
|
ex = MisdirectedRequestError.new(response)
|
309
312
|
ex.set_backtrace(caller)
|
310
313
|
emit(:error, request, ex)
|
@@ -391,16 +394,6 @@ module HTTPX
|
|
391
394
|
emit(:pong)
|
392
395
|
end
|
393
396
|
end
|
394
|
-
|
395
|
-
def respond_to_missing?(meth, *args)
|
396
|
-
@connection.respond_to?(meth, *args) || super
|
397
|
-
end
|
398
|
-
|
399
|
-
def method_missing(meth, *args, &blk)
|
400
|
-
return super unless @connection.respond_to?(meth)
|
401
|
-
|
402
|
-
@connection.__send__(meth, *args, &blk)
|
403
|
-
end
|
404
397
|
end
|
405
398
|
Connection.register "h2", Connection::HTTP2
|
406
399
|
end
|