Feersum
view release on metacpan or search on metacpan
t/h2-03-edge-cases.t view on Meta::CPAN
my $body_received = 0;
$evh->request_handler(sub {
my $r = shift;
my $env = $r->env();
$body_received = $env->{CONTENT_LENGTH} || 0;
$r->send_response(200, ['Content-Type' => 'text/plain'], 'ok');
});
h2_fork_test("max_body_len H2 rejection", $port, sub {
my ($port) = @_;
my $sock = h2_connect($port);
exit(1) unless $sock;
my $headers_block = hpack_encode_headers(
[':method', 'POST'],
[':path', '/body-test'],
[':scheme', 'https'],
[':authority', "127.0.0.1:$port"],
);
# Send HEADERS without END_STREAM (body follows)
$sock->syswrite(h2_frame(H2_HEADERS, FLAG_END_HEADERS,
1, $headers_block));
# Send DATA exceeding max_body_len (200 bytes > 100 limit)
my $big_body = "X" x 200;
$sock->syswrite(h2_frame(H2_DATA, FLAG_END_STREAM, 1, $big_body));
# Expect RST_STREAM or GOAWAY (TEMPORAL_CALLBACK_FAILURE triggers reset)
# or connection close (server may close TLS after error)
my $got_error = 0;
my $deadline = time + 5 * TIMEOUT_MULT;
while (time < $deadline) {
my $f = h2_read_frame($sock, $deadline - time);
if (!$f) {
# Weak evidence on its own (h2_read_frame also returns undef on its
# deadline); the parent's $body_received assertion is the real check.
# No sysread() probe for EOF here: it steals a byte from the TLS stream.
$got_error = 1;
last;
}
if ($f->{type} == H2_RST_STREAM && $f->{stream_id} == 1) {
$got_error = 1;
last;
}
if ($f->{type} == H2_GOAWAY) {
$got_error = 1;
last;
}
}
$sock->close();
exit($got_error ? 0 : 2);
}, timeout_mult => TIMEOUT_MULT);
is $body_received, 0,
'handler never received the oversized body';
# ========================================================================
# Administrative limits produce a real HTTP status on H2, as on H1, not a
# stream reset with INTERNAL_ERROR (which RFC 9113 reserves for the endpoint's
# own faults and which leaves the client with no status at all).
# ========================================================================
$evh->max_uri_len(200);
h2_fork_test("H2 over-long :path answers 414", $port, sub {
my ($port) = @_;
my $sock = h2_connect($port) or exit(1);
my $headers_block = hpack_encode_headers(
[':method', 'GET'],
[':path', '/' . ('a' x 300)], # > max_uri_len 200
[':scheme', 'https'],
[':authority', "127.0.0.1:$port"],
);
$sock->syswrite(h2_frame(H2_HEADERS, FLAG_END_HEADERS | FLAG_END_STREAM,
1, $headers_block));
my $status;
my $deadline = time + 5 * TIMEOUT_MULT;
while (time < $deadline) {
my $f = h2_read_frame($sock, $deadline - time);
last unless $f;
exit(3) if $f->{type} == H2_RST_STREAM && $f->{stream_id} == 1;
if ($f->{type} == H2_HEADERS && $f->{stream_id} == 1) {
$status = hpack_decode_status($f->{payload});
last;
}
}
$sock->close();
exit(0) if defined $status && $status eq '414';
exit(4);
}, timeout_mult => TIMEOUT_MULT);
h2_fork_test("H2 declared over-limit content-length answers 413", $port, sub {
my ($port) = @_;
my $sock = h2_connect($port) or exit(1);
my $headers_block = hpack_encode_headers(
[':method', 'POST'],
[':path', '/big'],
[':scheme', 'https'],
[':authority', "127.0.0.1:$port"],
['content-length', '5000'], # > max_body_len 100
);
$sock->syswrite(h2_frame(H2_HEADERS, FLAG_END_HEADERS, 1, $headers_block));
# Keep sending after the refusal: the response must survive the DATA that
# follows it (the overflow path used to RST a stream already answered).
$sock->syswrite(h2_frame(H2_DATA, 0, 1, 'X' x 1000));
my $status;
my $deadline = time + 5 * TIMEOUT_MULT;
while (time < $deadline) {
my $f = h2_read_frame($sock, $deadline - time);
last unless $f;
exit(3) if $f->{type} == H2_RST_STREAM && $f->{stream_id} == 1;
if ($f->{type} == H2_HEADERS && $f->{stream_id} == 1) {
$status = hpack_decode_status($f->{payload});
last;
}
}
( run in 0.999 second using v1.01-cache-2.11-cpan-16aabcbfbcb )