From 7f9ffd1fa3488000d8f1a6853fc8d2b8e933e8b0 Mon Sep 17 00:00:00 2001 From: Brian Duggan Date: Thu, 19 Feb 2026 10:49:10 -0500 Subject: [PATCH 1/3] Fix content-length setting for HTTP/2 The content length header is not set for http/2 requests, as described in https://github.com/croservices/cro-http/issues/73. This fixes that by calling has-body before setting the headers, which has a side effect of setting the body serializer and the content-length header. --- lib/Cro/HTTP2/RequestSerializer.rakumod | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/Cro/HTTP2/RequestSerializer.rakumod b/lib/Cro/HTTP2/RequestSerializer.rakumod index f1a39d49..29edab86 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, From 07edf52cf2b35d2ab601a903db63ce9c97a27375 Mon Sep 17 00:00:00 2001 From: Brian Duggan Date: Thu, 19 Feb 2026 10:53:11 -0500 Subject: [PATCH 2/3] changes --- Changes | 1 + 1 file changed, 1 insertion(+) diff --git a/Changes b/Changes index 86dfbfde..c018c455 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 From cebcc31ce53b775ffdad1a15a33f3028fc141e56 Mon Sep 17 00:00:00 2001 From: Brian Duggan Date: Thu, 19 Feb 2026 19:50:21 -0500 Subject: [PATCH 3/3] test for content-length --- t/http2-request-serializer.rakutest | 35 +++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/t/http2-request-serializer.rakutest b/t/http2-request-serializer.rakutest index b8fbd052..85c7e0d7 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;