diff --git a/Changes b/Changes index 86dfbfd..c018c45 100644 --- a/Changes +++ b/Changes @@ -1,6 +1,7 @@ Revision history for Cro::HTTP {{NEXT}} + - Fix content-length header in HTTP/2 - Support link generation - Make http function accept a list of http methods - Catch URI parse errors during routing diff --git a/lib/Cro/HTTP2/RequestSerializer.rakumod b/lib/Cro/HTTP2/RequestSerializer.rakumod index f1a39d4..29edab8 100644 --- a/lib/Cro/HTTP2/RequestSerializer.rakumod +++ b/lib/Cro/HTTP2/RequestSerializer.rakumod @@ -12,6 +12,11 @@ class Cro::HTTP2::RequestSerializer does Cro::Transform { whenever $in -> Cro::HTTP::Request $req { my $encoder = HTTP::HPACK::Encoder.new; + my $body-byte-stream; + if $req.has-body { + $body-byte-stream = $req.body-byte-stream; + } + my $host; my @headers = $req.headers.map: { my $name = .name.lc; @@ -46,7 +51,7 @@ class Cro::HTTP2::RequestSerializer does Cro::Transform { if $req.has-body { with $req.header('Content-Length') { my $counter = $_; - whenever $req.body-byte-stream { + whenever $body-byte-stream { $counter -= .elems; die 'Content-Length settings is incorrect: too small' if $counter < 0; emit Cro::HTTP2::Frame::Data.new( @@ -60,7 +65,7 @@ class Cro::HTTP2::RequestSerializer does Cro::Transform { } } else { - whenever $req.body-byte-stream { + whenever $body-byte-stream { emit Cro::HTTP2::Frame::Data.new( flags => 0, stream-identifier => $req.http2-stream-id, diff --git a/t/http2-request-serializer.rakutest b/t/http2-request-serializer.rakutest index b8fbd05..85c7e0d 100644 --- a/t/http2-request-serializer.rakutest +++ b/t/http2-request-serializer.rakutest @@ -1,4 +1,5 @@ use Cro::HTTP2::Frame; +use Cro::HTTP2::RequestParser; use Cro::HTTP2::RequestSerializer; use Cro::HTTP::Request; use Cro::HTTP::Response; @@ -118,4 +119,38 @@ test $req, 3, 'Header + Data with unknown Content-Length', (*.stream-identifier == 5), (*.data eq Buf.new)]]; +# Round-trip test: a POST request with set-body should be fully recoverable after +# serialization and parsing. In particular, the content-length header must be present +# and correct so the parser can reconstruct the body. +{ + my $body-text = 'hello world'; + $req = Cro::HTTP::Request.new(:method, :target, :5http2-stream-id); + $req.append-header('host' => 'example.org'); + $req.set-body($body-text); + + my $done = Promise.new; + my $serializer = Cro::HTTP2::RequestSerializer; + my $parser = Cro::HTTP2::RequestParser.new; + my $frames-in = Supplier.new; + my $connection-state = Cro::HTTP2::ConnectionState.new; + + $parser.transformer($serializer.transformer($frames-in.Supply), :$connection-state).tap: + -> $parsed { + start { + is $parsed.method, 'POST', 'round-trip: method'; + is $parsed.target, '/resource', 'round-trip: target'; + is $parsed.header('Content-length'), $body-text.encode.bytes.Str, + 'round-trip: content-length header present and correct'; + is await($parsed.body-text), $body-text, 'round-trip: body text'; + $done.keep; + CATCH { default { $done.break($_) } } + } + }, + quit => { $done.break }; + + start { $frames-in.emit($req); $frames-in.done } + await Promise.anyof($done, Promise.in(5)); + ok $done.status ~~ Kept, 'POST with set-body round-trips correctly over HTTP/2'; +} + done-testing;