PAGI-Server

 view release on metacpan or  search on metacpan

t/10-http-compliance.t  view on Meta::CPAN

            quiet           => 1,
            max_header_size => 1024,  # Custom max header size
        );

        $loop->add($server);
        await $server->listen;

        my $port = $server->port;

        # Create a stream and add to loop for proper async I/O
        my $socket = IO::Socket::INET->new(
            PeerAddr => '127.0.0.1',
            PeerPort => $port,
            Proto    => 'tcp',
            Blocking => 0,
        ) or die "Cannot connect: $!";

        my $response = '';
        my $done = $loop->new_future;

        my $stream = IO::Async::Stream->new(
            handle => $socket,
            on_read => sub  {
        my ($s, $buffref, $eof) = @_;
                $response .= $$buffref;
                $$buffref = '';
                if ($eof || $response =~ /\r\n\r\n/) {
                    $done->done unless $done->is_ready;
                }
                return 0;
            },
            on_closed => sub {
                $done->done unless $done->is_ready;
            },
        );

        $loop->add($stream);

        # Send request with headers exceeding 1024 bytes
        my $large_header = "X-Large: " . ("x" x 2000);
        $stream->write("GET / HTTP/1.1\r\nHost: localhost\r\n$large_header\r\n\r\n");

        # Wait with timeout
        my $timeout = $loop->delay_future(after => 3)->then(sub { $done->done });
        await Future->wait_any($done, $timeout);

        like($response, qr/HTTP\/1\.1 431/, '431 response for headers exceeding max_header_size');

        $loop->remove($stream);
        await $server->shutdown;
        $loop->remove($server);
    })->()->get;
};

# Test 18: send() after disconnect is a no-op per spec
# This tests the unit behavior of the send function when connection is closed
subtest 'send() after disconnect returns completed Future' => sub {
    # Test the _create_send implementation directly by verifying
    # that it returns immediately when closed flag is set

    # First verify by examining the send() return behavior in a streaming test
    # where the client disconnects mid-stream
    my $app_completed = 0;
    my $error_during_send = 0;

    my $test_app = async sub  {
        my ($scope, $receive, $send) = @_;
        # Handle lifespan scope
        if ($scope->{type} eq 'lifespan') {
            while (1) {
                my $event = await $receive->();
                if ($event->{type} eq 'lifespan.startup') {
                    await $send->({ type => 'lifespan.startup.complete' });
                }
                elsif ($event->{type} eq 'lifespan.shutdown') {
                    await $send->({ type => 'lifespan.shutdown.complete' });
                    last;
                }
            }
            return;
        }

        die "Unsupported scope type: $scope->{type}" unless $scope->{type} eq 'http';

        # Start response
        await $send->({
            type    => 'http.response.start',
            status  => 200,
            headers => [['content-type', 'text/plain']],
        });

        # Send some content - force close by sending more => 0
        await $send->({
            type => 'http.response.body',
            body => 'Hello',
            more => 0,
        });

        # Now try to send AFTER response is complete
        # This should be a no-op (not throw an error)
        eval {
            await $send->({
                type => 'http.response.body',
                body => 'This should be ignored',
                more => 0,
            });
        };

        if ($@) {
            $error_during_send = 1;
        }

        $app_completed = 1;
    };

    my $server = PAGI::Server->new(
        app   => $test_app,
        host  => '127.0.0.1',
        port  => 0,
        quiet => 1,
    );



( run in 0.968 second using v1.01-cache-2.11-cpan-995e09ba956 )