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 )