httpx 0.13.0 → 0.14.2
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/0_10_1.md +1 -1
- data/doc/release_notes/0_13_0.md +2 -2
- data/doc/release_notes/0_13_1.md +5 -0
- data/doc/release_notes/0_13_2.md +9 -0
- data/doc/release_notes/0_14_0.md +79 -0
- data/doc/release_notes/0_14_1.md +7 -0
- data/doc/release_notes/0_14_2.md +6 -0
- data/lib/httpx.rb +1 -2
- data/lib/httpx/callbacks.rb +12 -3
- data/lib/httpx/connection.rb +12 -9
- data/lib/httpx/connection/http1.rb +32 -14
- data/lib/httpx/connection/http2.rb +61 -15
- data/lib/httpx/headers.rb +7 -3
- data/lib/httpx/io/tcp.rb +3 -1
- data/lib/httpx/io/udp.rb +31 -7
- data/lib/httpx/options.rb +91 -56
- data/lib/httpx/plugins/aws_sdk_authentication.rb +5 -2
- data/lib/httpx/plugins/aws_sigv4.rb +5 -4
- data/lib/httpx/plugins/basic_authentication.rb +8 -3
- data/lib/httpx/plugins/compression.rb +8 -8
- data/lib/httpx/plugins/compression/brotli.rb +4 -3
- data/lib/httpx/plugins/compression/deflate.rb +4 -3
- data/lib/httpx/plugins/compression/gzip.rb +2 -1
- data/lib/httpx/plugins/cookies.rb +3 -7
- data/lib/httpx/plugins/digest_authentication.rb +4 -4
- data/lib/httpx/plugins/expect.rb +6 -6
- data/lib/httpx/plugins/follow_redirects.rb +3 -3
- data/lib/httpx/plugins/grpc.rb +247 -0
- data/lib/httpx/plugins/grpc/call.rb +62 -0
- data/lib/httpx/plugins/grpc/message.rb +85 -0
- data/lib/httpx/plugins/multipart/part.rb +2 -2
- data/lib/httpx/plugins/proxy.rb +3 -7
- data/lib/httpx/plugins/proxy/http.rb +5 -4
- data/lib/httpx/plugins/proxy/ssh.rb +3 -3
- data/lib/httpx/plugins/rate_limiter.rb +1 -1
- data/lib/httpx/plugins/retries.rb +13 -14
- data/lib/httpx/plugins/stream.rb +96 -74
- data/lib/httpx/plugins/upgrade.rb +6 -5
- data/lib/httpx/request.rb +25 -2
- data/lib/httpx/resolver/native.rb +7 -3
- data/lib/httpx/response.rb +4 -0
- data/lib/httpx/session.rb +17 -7
- data/lib/httpx/transcoder/chunker.rb +1 -1
- data/lib/httpx/version.rb +1 -1
- data/sig/callbacks.rbs +2 -0
- data/sig/connection/http1.rbs +5 -1
- data/sig/connection/http2.rbs +6 -2
- data/sig/headers.rbs +2 -2
- data/sig/options.rbs +9 -2
- data/sig/plugins/aws_sdk_authentication.rbs +2 -0
- data/sig/plugins/basic_authentication.rbs +2 -0
- data/sig/plugins/compression.rbs +2 -2
- data/sig/plugins/multipart.rbs +1 -1
- data/sig/plugins/stream.rbs +17 -16
- data/sig/request.rbs +7 -2
- data/sig/response.rbs +1 -0
- data/sig/session.rbs +4 -0
- metadata +18 -7
- data/lib/httpx/timeout.rb +0 -67
- data/sig/timeout.rbs +0 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5049a0cfb698c15800ea618a2775831183765f24ccb946c2adfb6b067c976c4d
|
4
|
+
data.tar.gz: 498a626981251e0ffbff3dde9d63fcc57e0b3183d29fafbc1ecd921d3bfe8172
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6fb23f5a45bd521bf30c25b8e30af3b35e4359093a470fb29f9ab6c8f5d0f09674a08b28ba2c20f92c51fe46702f4f3751b855b6b31f202be01728f56273d6a2
|
7
|
+
data.tar.gz: f64d1057e2483502dc3751a74489cfec0e7672d906f5901a81f98227c98ee907dc6be0b8d0ad7f4c4688546a2a7c22886f437807e28ba5b2a5e09c6b588de023
|
data/doc/release_notes/0_10_1.md
CHANGED
@@ -26,7 +26,7 @@ From now on, both headers and the responnse payload will also appear, so expecte
|
|
26
26
|
## Bugfixes
|
27
27
|
|
28
28
|
* HTTP/2 and HTTP/1.1 exhausted connections now get properly migrated into a new connection;
|
29
|
-
* HTTP/2 421 responses will now correctly migrate the connection and
|
29
|
+
* HTTP/2 421 responses will now correctly migrate the connection and pending requests to HTTP/1.1 (a hanging loop was being caused);
|
30
30
|
* HTTP/2 connection failed with a GOAWAY settings timeout will now return error responses (instead of hanging indefinitely);
|
31
31
|
* Non-IP proxy name-resolving errors will now move on to the next available proxy in the list (instead of hanging indefinitely);
|
32
32
|
* Non-IP DNS resolve errors for `native` and `https` variants will now return the appropriate error response (instead of hanging indefinitely);
|
data/doc/release_notes/0_13_0.md
CHANGED
@@ -41,7 +41,7 @@ The `:transport_options` are therefore deprecated, and will be moved in a major
|
|
41
41
|
|
42
42
|
## Improvements
|
43
43
|
|
44
|
-
Some internal improvements that allow
|
44
|
+
Some internal improvements that allow certain plugins not to "leak" globally, such as the `:compression` plugin, which used to enable compression for all the `httpx` sessions from the same process. It doesn't anymore.
|
45
45
|
|
46
46
|
Using exceptionless nonblocking connect calls in the supported rubies.
|
47
47
|
|
@@ -55,4 +55,4 @@ When passing open IO objects for origins (the `:io` option), `httpx` was still t
|
|
55
55
|
|
56
56
|
Fixed usage of `:io` option when passed an "authority/io" hash.
|
57
57
|
|
58
|
-
Fixing some issues around trying to connnect to the next available IPAddress when the previous one was unreachable or ETIMEDOUT.
|
58
|
+
Fixing some issues around trying to connnect to the next available IPAddress when the previous one was unreachable or ETIMEDOUT.
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# 0.13.1
|
2
|
+
|
3
|
+
## Improvements
|
4
|
+
|
5
|
+
`UDPSocket#sendmsg_nonblock` is now used in the native resolver.
|
6
|
+
|
7
|
+
## Bugfixes
|
8
|
+
|
9
|
+
Usage in Windows was buggy, resulting in `Errno::EINVAL` during DNS resolving, when using the native resolver. This was due to a discrepancy between `recvfrom` behaviour in WS Sockets and Linux Sockets. This was fixed by making we the UDP socket never tries to receive before a DNS query has been actually sent.
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# 0.14.0
|
2
|
+
|
3
|
+
## Features
|
4
|
+
|
5
|
+
### GRPC plugin
|
6
|
+
|
7
|
+
A new plugin, `:grpc`, is now available. This plugin provides a simple DSL to build GRPC services and performing calls using `httpx` under the hood.
|
8
|
+
|
9
|
+
Example:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
require "httpx"
|
13
|
+
|
14
|
+
grpc = HTTPX.plugin(:grpc)
|
15
|
+
helloworld_stub = grpc.build_stub("localhost:4545")
|
16
|
+
helloworld_svc = helloworld_stub.rpc(:SayHello, HelloRequest, HelloReply)
|
17
|
+
result = helloworld_svc.say_hello(HelloRequest.new(name: "Jack")) #=> HelloReply: "Hello Jack"
|
18
|
+
```
|
19
|
+
|
20
|
+
You can read more about the `:grpc` plugin in the [wiki](https://honeyryderchuck.gitlab.io/httpx/wiki/GRPC).
|
21
|
+
|
22
|
+
### :origin
|
23
|
+
|
24
|
+
A new `:origin` option is available. You can use it for setting a base URL for subsequent relative paths on that session:
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
HTTPX.get("/httpbin/get") #=> HTTPX::Error: invalid URI: /httpbin/get
|
28
|
+
|
29
|
+
httpbin = HTTPX.with(origin: "https://nghttp2.org")
|
30
|
+
httpbin.get("/httpbin/get") #=> #<Response:5420 HTTP/2.0 @status=200 ....
|
31
|
+
```
|
32
|
+
|
33
|
+
**Note!** The origin is **not** for setting base paths, i.e. if you pass it a relative path, it'll be filtered out in subsequent requests (`HTTPX.with(origin: "https://nghttp2.org/httpbin")` will still use only `"https://nghttp2.org"`).
|
34
|
+
|
35
|
+
## Improvements
|
36
|
+
|
37
|
+
* setting an unexpected option will now raise an `HTTPX::Error` with an helpful message, instead of a confusing `NoMethodError`:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
HTTPX.with(foo: "bar")
|
41
|
+
# before
|
42
|
+
#=> NoMethodError
|
43
|
+
# after
|
44
|
+
#=> HTTPX::Error: unknown option: foo
|
45
|
+
|
46
|
+
```
|
47
|
+
|
48
|
+
* `HTTPX::Options#def_option` (which can be used for setting custom plugin options) can now be passed a full body string (where the argument is `value`), although it still support the block form. This is the recommended approach, as the block form is based on `define_method`, which would make clients unusable inside ractors.
|
49
|
+
|
50
|
+
* Added support for `:wait_for_handshake` under the `http2_settings` option (`false` by default). HTTP/2 connections complete the protocol handshake before requests are sent. When this option is `true`, requests get send in the initial payload, before the HTTP/2 connection is fully acknowledged.
|
51
|
+
|
52
|
+
* 441716a5ac0f7707211ebe0048f568cf0b759c3f: The `:stream` plugin has been improved to start streaming the real response as methods are called (instead of a completely separate synchronous one, which is definitely not good):
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
session = HTTPX.plugin(:stream)
|
56
|
+
response = session.get(build_uri("/stream/3"), stream: true)
|
57
|
+
|
58
|
+
# before
|
59
|
+
response.status # this could block indefinitely, if the request truly streams infinitely.
|
60
|
+
|
61
|
+
# after
|
62
|
+
response.status # sends the request, and starts streaming the response until status is available.
|
63
|
+
response.each {|chunk|...} # and now you can start yielding the chunks...
|
64
|
+
```
|
65
|
+
|
66
|
+
|
67
|
+
## Bugfixes
|
68
|
+
|
69
|
+
* fixed usage of the `:multipart` if `pathname` isn't loaded.
|
70
|
+
* fixed HTTP/2 trailers.
|
71
|
+
* fixed connection merges with the same origin, which was causing them to be duplicated and breaking further usage. (#125)
|
72
|
+
* fixed repeated session callbacks on a connection, by ensure they're set only once.
|
73
|
+
* fixed calculation of `content-length` for streaming or chunked compressed requests.
|
74
|
+
|
75
|
+
|
76
|
+
## Chore
|
77
|
+
|
78
|
+
* using ruby base container images in CI instead.
|
79
|
+
* using truffleruby official container image.
|
@@ -0,0 +1,7 @@
|
|
1
|
+
# 0.14.1
|
2
|
+
|
3
|
+
|
4
|
+
## Bugfixes
|
5
|
+
|
6
|
+
* fixed: HTTP/2-specific headers were being reused on insecure redirects, thereby creating an invalid request (#128);
|
7
|
+
* fixed: multipart request parts weren't using explicity set `:content_type`, instead using file mime type or "text/plain";
|
data/lib/httpx.rb
CHANGED
@@ -12,12 +12,11 @@ require "httpx/callbacks"
|
|
12
12
|
require "httpx/loggable"
|
13
13
|
require "httpx/registry"
|
14
14
|
require "httpx/transcoder"
|
15
|
-
require "httpx/options"
|
16
|
-
require "httpx/timeout"
|
17
15
|
require "httpx/pool"
|
18
16
|
require "httpx/headers"
|
19
17
|
require "httpx/request"
|
20
18
|
require "httpx/response"
|
19
|
+
require "httpx/options"
|
21
20
|
require "httpx/chainable"
|
22
21
|
|
23
22
|
# Top-Level Namespace
|
data/lib/httpx/callbacks.rb
CHANGED
@@ -6,19 +6,28 @@ module HTTPX
|
|
6
6
|
callbacks(type) << action
|
7
7
|
end
|
8
8
|
|
9
|
-
def once(
|
10
|
-
on(
|
9
|
+
def once(type, &block)
|
10
|
+
on(type) do |*args, &callback|
|
11
11
|
block.call(*args, &callback)
|
12
12
|
:delete
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
+
def only(type, &block)
|
17
|
+
callbacks(type).clear
|
18
|
+
on(type, &block)
|
19
|
+
end
|
20
|
+
|
16
21
|
def emit(type, *args)
|
17
|
-
callbacks(type).delete_if { |pr| pr[*args]
|
22
|
+
callbacks(type).delete_if { |pr| :delete == pr[*args] } # rubocop:disable Style/YodaCondition
|
18
23
|
end
|
19
24
|
|
20
25
|
protected
|
21
26
|
|
27
|
+
def callbacks_for?(type)
|
28
|
+
@callbacks.key?(type) && !@callbacks[type].empty?
|
29
|
+
end
|
30
|
+
|
22
31
|
def callbacks(type = nil)
|
23
32
|
return @callbacks unless type
|
24
33
|
|
data/lib/httpx/connection.rb
CHANGED
@@ -69,7 +69,7 @@ module HTTPX
|
|
69
69
|
end
|
70
70
|
|
71
71
|
@inflight = 0
|
72
|
-
@keep_alive_timeout = options.timeout
|
72
|
+
@keep_alive_timeout = options.timeout[:keep_alive_timeout]
|
73
73
|
@keep_alive_timer = nil
|
74
74
|
|
75
75
|
self.addresses = options.addresses if options.addresses
|
@@ -129,7 +129,7 @@ module HTTPX
|
|
129
129
|
end
|
130
130
|
|
131
131
|
def merge(connection)
|
132
|
-
@origins
|
132
|
+
@origins |= connection.instance_variable_get(:@origins)
|
133
133
|
connection.purge_pending do |req|
|
134
134
|
send(req)
|
135
135
|
end
|
@@ -238,9 +238,9 @@ module HTTPX
|
|
238
238
|
def timeout
|
239
239
|
return @timeout if defined?(@timeout)
|
240
240
|
|
241
|
-
return @options.timeout
|
241
|
+
return @options.timeout[:connect_timeout] if @state == :idle
|
242
242
|
|
243
|
-
@options.timeout
|
243
|
+
@options.timeout[:operation_timeout]
|
244
244
|
end
|
245
245
|
|
246
246
|
private
|
@@ -413,7 +413,7 @@ module HTTPX
|
|
413
413
|
emit(:exhausted)
|
414
414
|
end
|
415
415
|
parser.on(:origin) do |origin|
|
416
|
-
@origins
|
416
|
+
@origins |= [origin]
|
417
417
|
end
|
418
418
|
parser.on(:close) do |force|
|
419
419
|
transition(:closing)
|
@@ -443,6 +443,7 @@ module HTTPX
|
|
443
443
|
emit(:misdirected, request)
|
444
444
|
else
|
445
445
|
response = ErrorResponse.new(request, ex, @options)
|
446
|
+
request.response = response
|
446
447
|
request.emit(:response, response)
|
447
448
|
end
|
448
449
|
end
|
@@ -451,7 +452,7 @@ module HTTPX
|
|
451
452
|
def transition(nextstate)
|
452
453
|
case nextstate
|
453
454
|
when :idle
|
454
|
-
@timeout = @current_timeout = @options.timeout
|
455
|
+
@timeout = @current_timeout = @options.timeout[:connect_timeout]
|
455
456
|
|
456
457
|
when :open
|
457
458
|
return if @state == :closed
|
@@ -463,7 +464,7 @@ module HTTPX
|
|
463
464
|
|
464
465
|
send_pending
|
465
466
|
|
466
|
-
@timeout = @current_timeout = @options.timeout
|
467
|
+
@timeout = @current_timeout = @options.timeout[:operation_timeout]
|
467
468
|
emit(:open)
|
468
469
|
when :closing
|
469
470
|
return unless @state == :open
|
@@ -542,12 +543,14 @@ module HTTPX
|
|
542
543
|
def handle_error(error)
|
543
544
|
parser.handle_error(error) if @parser && parser.respond_to?(:handle_error)
|
544
545
|
while (request = @pending.shift)
|
545
|
-
|
546
|
+
response = ErrorResponse.new(request, error, @options)
|
547
|
+
request.response = response
|
548
|
+
request.emit(:response, response)
|
546
549
|
end
|
547
550
|
end
|
548
551
|
|
549
552
|
def total_timeout
|
550
|
-
total = @options.timeout
|
553
|
+
total = @options.timeout[:total_timeout]
|
551
554
|
|
552
555
|
return unless total
|
553
556
|
|
@@ -254,12 +254,15 @@ module HTTPX
|
|
254
254
|
end
|
255
255
|
|
256
256
|
def set_protocol_headers(request)
|
257
|
-
request.headers["host"] ||= request.authority
|
258
|
-
request.headers["connection"] ||= request.options.persistent ? "keep-alive" : "close"
|
259
257
|
if !request.headers.key?("content-length") &&
|
260
258
|
request.body.bytesize == Float::INFINITY
|
261
259
|
request.chunk!
|
262
260
|
end
|
261
|
+
|
262
|
+
{
|
263
|
+
"host" => (request.headers["host"] || request.authority),
|
264
|
+
"connection" => (request.headers["connection"] || (request.options.persistent ? "keep-alive" : "close")),
|
265
|
+
}
|
263
266
|
end
|
264
267
|
|
265
268
|
def headline_uri(request)
|
@@ -272,23 +275,18 @@ module HTTPX
|
|
272
275
|
join_headers(request) if request.state == :headers
|
273
276
|
request.transition(:body)
|
274
277
|
join_body(request) if request.state == :body
|
278
|
+
request.transition(:trailers)
|
279
|
+
# HTTP/1.1 trailers should only work for chunked encoding
|
280
|
+
join_trailers(request) if request.body.chunked? && request.state == :trailers
|
275
281
|
request.transition(:done)
|
276
282
|
end
|
277
283
|
end
|
278
284
|
|
279
285
|
def join_headers(request)
|
280
|
-
buffer
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
buffer.clear
|
285
|
-
set_protocol_headers(request)
|
286
|
-
request.headers.each do |field, value|
|
287
|
-
buffer << "#{capitalized(field)}: #{value}" << CRLF
|
288
|
-
log(color: :yellow) { "<- HEADER: #{buffer.chomp}" }
|
289
|
-
@buffer << buffer
|
290
|
-
buffer.clear
|
291
|
-
end
|
286
|
+
@buffer << "#{request.verb.to_s.upcase} #{headline_uri(request)} HTTP/#{@version.join(".")}" << CRLF
|
287
|
+
log(color: :yellow) { "<- HEADLINE: #{@buffer.to_s.chomp.inspect}" }
|
288
|
+
extra_headers = set_protocol_headers(request)
|
289
|
+
join_headers2(request.headers.each(extra_headers))
|
292
290
|
log { "<- " }
|
293
291
|
@buffer << CRLF
|
294
292
|
end
|
@@ -302,6 +300,26 @@ module HTTPX
|
|
302
300
|
@buffer << chunk
|
303
301
|
throw(:buffer_full, request) if @buffer.full?
|
304
302
|
end
|
303
|
+
|
304
|
+
raise request.drain_error if request.drain_error
|
305
|
+
end
|
306
|
+
|
307
|
+
def join_trailers(request)
|
308
|
+
return unless request.trailers? && request.callbacks_for?(:trailers)
|
309
|
+
|
310
|
+
join_headers2(request.trailers)
|
311
|
+
log { "<- " }
|
312
|
+
@buffer << CRLF
|
313
|
+
end
|
314
|
+
|
315
|
+
def join_headers2(headers)
|
316
|
+
buffer = "".b
|
317
|
+
headers.each do |field, value|
|
318
|
+
buffer << "#{capitalized(field)}: #{value}" << CRLF
|
319
|
+
log(color: :yellow) { "<- HEADER: #{buffer.chomp}" }
|
320
|
+
@buffer << buffer
|
321
|
+
buffer.clear
|
322
|
+
end
|
305
323
|
end
|
306
324
|
|
307
325
|
UPCASED = {
|
@@ -21,14 +21,16 @@ module HTTPX
|
|
21
21
|
|
22
22
|
def initialize(buffer, options)
|
23
23
|
@options = Options.new(options)
|
24
|
-
@
|
25
|
-
@max_requests = @options.max_requests || 0
|
24
|
+
@settings = @options.http2_settings
|
26
25
|
@pending = []
|
27
26
|
@streams = {}
|
28
27
|
@drains = {}
|
29
28
|
@pings = []
|
30
29
|
@buffer = buffer
|
31
30
|
@handshake_completed = false
|
31
|
+
@wait_for_handshake = @settings.key?(:wait_for_handshake) ? @settings.delete(:wait_for_handshake) : true
|
32
|
+
@max_concurrent_requests = @options.max_concurrent_requests || MAX_CONCURRENT_REQUESTS
|
33
|
+
@max_requests = @options.max_requests || 0
|
32
34
|
init_connection
|
33
35
|
end
|
34
36
|
|
@@ -36,7 +38,11 @@ module HTTPX
|
|
36
38
|
# waiting for WINDOW_UPDATE frames
|
37
39
|
return :r if @buffer.full?
|
38
40
|
|
39
|
-
|
41
|
+
if @connection.state == :closed
|
42
|
+
return unless @handshake_completed
|
43
|
+
|
44
|
+
return :w
|
45
|
+
end
|
40
46
|
|
41
47
|
unless (@connection.state == :connected && @handshake_completed)
|
42
48
|
return @buffer.empty? ? :r : :rw
|
@@ -75,9 +81,13 @@ module HTTPX
|
|
75
81
|
end
|
76
82
|
|
77
83
|
def can_buffer_more_requests?
|
78
|
-
@handshake_completed
|
84
|
+
if @handshake_completed
|
79
85
|
@streams.size < @max_concurrent_requests &&
|
80
|
-
|
86
|
+
@streams.size < @max_requests
|
87
|
+
else
|
88
|
+
!@wait_for_handshake &&
|
89
|
+
@streams.size < @max_concurrent_requests
|
90
|
+
end
|
81
91
|
end
|
82
92
|
|
83
93
|
def send(request)
|
@@ -140,12 +150,14 @@ module HTTPX
|
|
140
150
|
join_headers(stream, request) if request.state == :headers
|
141
151
|
request.transition(:body)
|
142
152
|
join_body(stream, request) if request.state == :body
|
153
|
+
request.transition(:trailers)
|
154
|
+
join_trailers(stream, request) if request.state == :trailers && !request.body.empty?
|
143
155
|
request.transition(:done)
|
144
156
|
end
|
145
157
|
end
|
146
158
|
|
147
159
|
def init_connection
|
148
|
-
@connection = HTTP2Next::Client.new(@
|
160
|
+
@connection = HTTP2Next::Client.new(@settings)
|
149
161
|
@connection.max_streams = @max_requests if @connection.respond_to?(:max_streams=) && @max_requests.positive?
|
150
162
|
@connection.on(:frame, &method(:on_frame))
|
151
163
|
@connection.on(:frame_sent, &method(:on_frame_sent))
|
@@ -169,6 +181,7 @@ module HTTPX
|
|
169
181
|
public :reset
|
170
182
|
|
171
183
|
def handle_stream(stream, request)
|
184
|
+
request.on(:refuse, &method(:on_stream_refuse).curry(3)[stream, request])
|
172
185
|
stream.on(:close, &method(:on_stream_close).curry(3)[stream, request])
|
173
186
|
stream.on(:half_close) do
|
174
187
|
log(level: 2) { "#{stream.id}: waiting for response..." }
|
@@ -179,18 +192,32 @@ module HTTPX
|
|
179
192
|
end
|
180
193
|
|
181
194
|
def set_protocol_headers(request)
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
195
|
+
{
|
196
|
+
":scheme" => request.scheme,
|
197
|
+
":method" => request.verb.to_s.upcase,
|
198
|
+
":path" => headline_uri(request),
|
199
|
+
":authority" => request.authority,
|
200
|
+
}
|
186
201
|
end
|
187
202
|
|
188
203
|
def join_headers(stream, request)
|
189
|
-
set_protocol_headers(request)
|
204
|
+
extra_headers = set_protocol_headers(request)
|
205
|
+
log(level: 1, color: :yellow) do
|
206
|
+
request.headers.merge(extra_headers).each.map { |k, v| "#{stream.id}: -> HEADER: #{k}: #{v}" }.join("\n")
|
207
|
+
end
|
208
|
+
stream.headers(request.headers.each(extra_headers), end_stream: request.empty?)
|
209
|
+
end
|
210
|
+
|
211
|
+
def join_trailers(stream, request)
|
212
|
+
unless request.trailers?
|
213
|
+
stream.data("", end_stream: true) if request.callbacks_for?(:trailers)
|
214
|
+
return
|
215
|
+
end
|
216
|
+
|
190
217
|
log(level: 1, color: :yellow) do
|
191
|
-
request.
|
218
|
+
request.trailers.each.map { |k, v| "#{stream.id}: -> HEADER: #{k}: #{v}" }.join("\n")
|
192
219
|
end
|
193
|
-
stream.headers(request.
|
220
|
+
stream.headers(request.trailers.each, end_stream: true)
|
194
221
|
end
|
195
222
|
|
196
223
|
def join_body(stream, request)
|
@@ -201,13 +228,15 @@ module HTTPX
|
|
201
228
|
next_chunk = request.drain_body
|
202
229
|
log(level: 1, color: :green) { "#{stream.id}: -> DATA: #{chunk.bytesize} bytes..." }
|
203
230
|
log(level: 2, color: :green) { "#{stream.id}: -> #{chunk.inspect}" }
|
204
|
-
stream.data(chunk, end_stream: !next_chunk)
|
205
|
-
if next_chunk && @buffer.full?
|
231
|
+
stream.data(chunk, end_stream: !(next_chunk || request.trailers? || request.callbacks_for?(:trailers)))
|
232
|
+
if next_chunk && (@buffer.full? || request.body.unbounded_body?)
|
206
233
|
@drains[request] = next_chunk
|
207
234
|
throw(:buffer_full)
|
208
235
|
end
|
209
236
|
chunk = next_chunk
|
210
237
|
end
|
238
|
+
|
239
|
+
on_stream_refuse(stream, request, request.drain_error) if request.drain_error
|
211
240
|
end
|
212
241
|
|
213
242
|
######
|
@@ -215,6 +244,11 @@ module HTTPX
|
|
215
244
|
######
|
216
245
|
|
217
246
|
def on_stream_headers(stream, request, h)
|
247
|
+
if request.response && request.response.version == "2.0"
|
248
|
+
on_stream_trailers(stream, request, h)
|
249
|
+
return
|
250
|
+
end
|
251
|
+
|
218
252
|
log(color: :yellow) do
|
219
253
|
h.map { |k, v| "#{stream.id}: <- HEADER: #{k}: #{v}" }.join("\n")
|
220
254
|
end
|
@@ -227,12 +261,24 @@ module HTTPX
|
|
227
261
|
handle(request, stream) if request.expects?
|
228
262
|
end
|
229
263
|
|
264
|
+
def on_stream_trailers(stream, request, h)
|
265
|
+
log(color: :yellow) do
|
266
|
+
h.map { |k, v| "#{stream.id}: <- HEADER: #{k}: #{v}" }.join("\n")
|
267
|
+
end
|
268
|
+
request.response.merge_headers(h)
|
269
|
+
end
|
270
|
+
|
230
271
|
def on_stream_data(stream, request, data)
|
231
272
|
log(level: 1, color: :green) { "#{stream.id}: <- DATA: #{data.bytesize} bytes..." }
|
232
273
|
log(level: 2, color: :green) { "#{stream.id}: <- #{data.inspect}" }
|
233
274
|
request.response << data
|
234
275
|
end
|
235
276
|
|
277
|
+
def on_stream_refuse(stream, request, error)
|
278
|
+
stream.close
|
279
|
+
on_stream_close(stream, request, error)
|
280
|
+
end
|
281
|
+
|
236
282
|
def on_stream_close(stream, request, error)
|
237
283
|
log(level: 2) { "#{stream.id}: closing stream" }
|
238
284
|
@drains.delete(request)
|