Skip to content

Commit e584d4c

Browse files
committed
Better handling of trailers.
1 parent af3806b commit e584d4c

File tree

7 files changed

+20
-11
lines changed

7 files changed

+20
-11
lines changed

async-http.gemspec

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ Gem::Specification.new do |spec|
2929
spec.add_dependency "io-endpoint", "~> 0.14"
3030
spec.add_dependency "io-stream", "~> 0.6"
3131
spec.add_dependency "metrics", "~> 0.12"
32-
spec.add_dependency "protocol-http", "~> 0.49"
33-
spec.add_dependency "protocol-http1", "~> 0.30"
32+
spec.add_dependency "protocol-http", "~> 0.58"
33+
spec.add_dependency "protocol-http1", "~> 0.36"
3434
spec.add_dependency "protocol-http2", "~> 0.22"
3535
spec.add_dependency "protocol-url", "~> 0.2"
3636
spec.add_dependency "traces", "~> 0.10"

lib/async/http/protocol/http1/client.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ def closed(error = nil)
3232
def call(request, task: Task.current)
3333
# Mark the start of the trailers:
3434
trailer = request.headers.trailer!
35+
headers = request.headers.header
3536

3637
# We carefully interpret https://tools.ietf.org/html/rfc7230#section-6.3.1 to implement this correctly.
3738
begin
@@ -44,7 +45,7 @@ def call(request, task: Task.current)
4445
authority = nil
4546
end
4647

47-
write_request(authority, request.method, target, @version, request.headers)
48+
write_request(authority, request.method, target, @version, headers)
4849
rescue
4950
# If we fail to fully write the request and body, we can retry this request.
5051
raise RequestFailed

lib/async/http/protocol/http1/server.rb

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,13 @@ def each(task: Task.current)
8282
# If a response was generated, send it:
8383
if response
8484
trailer = response.headers.trailer!
85+
headers = response.headers.header
8586

8687
# Some operations in this method are long running, that is, it's expected that `body.call(stream)` could literally run indefinitely. In order to facilitate garbage collection, we want to nullify as many local variables before calling the streaming body. This ensures that the garbage collection can clean up as much state as possible during the long running operation, so we don't retain objects that are no longer needed.
8788

8889
if body and protocol = response.protocol
8990
# We force a 101 response if the protocol is upgraded - HTTP/2 CONNECT will return 200 for success, but this won't be understood by HTTP/1 clients:
90-
write_response(@version, 101, response.headers)
91+
write_response(@version, 101, headers)
9192

9293
# At this point, the request body is hijacked, so we don't want to call #finish below.
9394
request = nil
@@ -100,7 +101,7 @@ def each(task: Task.current)
100101
end
101102
elsif response.status == 101
102103
# This code path is to support legacy behavior where the response status is set to 101, but the protocol is not upgraded. This may not be a valid use case, but it is supported for compatibility. We expect the response headers to contain the `upgrade` header.
103-
write_response(@version, response.status, response.headers)
104+
write_response(@version, response.status, headers)
104105

105106
# Same as above:
106107
request = nil
@@ -112,7 +113,7 @@ def each(task: Task.current)
112113
write_tunnel_body(version, body)
113114
end
114115
else
115-
write_response(@version, response.status, response.headers)
116+
write_response(@version, response.status, headers)
116117

117118
if request.connect? and response.success?
118119
# Same as above:

lib/async/http/protocol/http2/request.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,10 @@ def send_response(response)
123123
protocol_headers << [CONTENT_LENGTH, length]
124124
end
125125

126-
headers = ::Protocol::HTTP::Headers::Merged.new(protocol_headers, response.headers)
126+
headers = ::Protocol::HTTP::Headers::Merged.new(
127+
protocol_headers,
128+
response.headers.header
129+
)
127130

128131
if body = response.body and !self.head?
129132
# This function informs the headers object that any subsequent headers are going to be trailer. Therefore, it must be called *before* sending the headers, to avoid any race conditions.

lib/async/http/protocol/http2/response.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ def send_request(request)
221221

222222
headers = ::Protocol::HTTP::Headers::Merged.new(
223223
pseudo_headers,
224-
request.headers
224+
request.headers.header
225225
)
226226

227227
if request.body.nil?

lib/async/http/protocol/http2/stream.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,21 +36,21 @@ def initialize(*)
3636

3737
attr :input
3838

39-
def add_header(key, value)
39+
def add_header(key, value, trailer: false)
4040
if key == CONNECTION
4141
raise ::Protocol::HTTP2::HeaderError, "Connection header is not allowed!"
4242
elsif key.start_with? ":"
4343
raise ::Protocol::HTTP2::HeaderError, "Invalid pseudo-header #{key}!"
4444
elsif key =~ /[A-Z]/
4545
raise ::Protocol::HTTP2::HeaderError, "Invalid upper-case characters in header #{key}!"
4646
else
47-
@headers.add(key, value)
47+
@headers.add(key, value, trailer: trailer)
4848
end
4949
end
5050

5151
def receive_trailing_headers(headers, end_stream)
5252
headers.each do |key, value|
53-
add_header(key, value)
53+
add_header(key, value, trailer: true)
5454
end
5555
end
5656

releases.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Releases
22

3+
## Unreleased
4+
5+
- Better handling of trailers.
6+
37
## v0.92.0
48

59
- **Breaking**: Remove `Async::HTTP::Reference`. Use `Protocol::URL::Reference` directly instead.

0 commit comments

Comments
 (0)