PAGI-Server
view release on metacpan or search on metacpan
t/10-http-compliance.t view on Meta::CPAN
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 the response is complete raises, per mandatory
# sequencing (PAGI spec compliance) -- not the genuine post-disconnect no-op,
# which lives in t/52-mandatory-validation.t's /post-close case.
subtest 'send() after response completion raises "response already complete"' => sub {
# Exercise the _create_send implementation directly by verifying that a
# send after the response has completed fails the returned Future.
# 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 (same connection, no
# disconnect involved). Mandatory sequencing now rejects this: a
# send after the response reaches 'complete' state must croak
# rather than be silently ignored (PAGI spec compliance).
eval {
await $send->({
type => 'http.response.body',
body => 'This should be rejected',
more => 0,
});
};
if ($@) {
$error_during_send = $@;
}
$app_completed = 1;
};
my $server = PAGI::Server->new(
app => $test_app,
host => '127.0.0.1',
port => 0,
( run in 0.820 second using v1.01-cache-2.11-cpan-5c0b1e786e0 )