PAGI-Server
view release on metacpan or search on metacpan
t/http2/21-http-date-header.t view on Meta::CPAN
my ($conn, $stream_io, $client_sock, $server) = create_h2c_connection(app => $app);
my %headers;
my $client = create_client(
on_header => sub { my ($sid, $n, $v) = @_; $headers{lc $n} = $v; return 0 },
);
$client->send_connection_preface;
$client_sock->syswrite($client->mem_send);
pump($client, $client_sock);
$client->submit_request(
method => 'GET', path => '/events', scheme => 'http', authority => 'localhost',
headers => [['accept', 'text/event-stream']],
);
$client_sock->syswrite($client->mem_send);
pump($client, $client_sock, sub { defined $headers{date} });
ok(defined $headers{date}, 'HTTP/2 SSE response carries a Date header');
$stream_io->close_now;
$loop->remove($server);
};
# ============================================================
# Design 13.1: Date must be added on the server-generated response paths
# too, not just the app-driven ones above -- 413 (both the content-length
# precheck and the body-overrun enforcement), the synthesized 500, and the
# WebSocket bare-403 fallback build their header lists entirely on the
# server side.
# ============================================================
subtest 'HTTP/2 413 (content-length precheck) includes a Date header' => sub {
my $app_called = 0;
my $app = async sub {
my ($scope, $receive, $send) = @_;
$app_called = 1;
await $receive->();
await $send->({ type => 'http.response.start', status => 200, headers => [] });
await $send->({ type => 'http.response.body', body => 'ok', more => 0 });
};
my ($conn, $stream_io, $client_sock, $server) = create_h2c_connection(
app => $app, max_body_size => 100,
);
my %headers;
my $stream_closed = 0;
my $client = create_client(
on_header => sub { my ($sid, $n, $v) = @_; $headers{lc $n} = $v; return 0 },
on_stream_close => sub { $stream_closed = 1; return 0 },
);
$client->send_connection_preface;
$client_sock->syswrite($client->mem_send);
pump($client, $client_sock);
$client->submit_request(
method => 'POST', path => '/upload', scheme => 'http', authority => 'localhost',
headers => [['content-type', 'application/octet-stream'], ['content-length', '50000']],
body => sub { return undef }, # streaming: keep open
);
$client_sock->syswrite($client->mem_send);
pump($client, $client_sock, sub { $stream_closed });
is($headers{':status'}, '413', 'content-length precheck rejected with 413');
ok(!$app_called, 'app was never called');
ok(defined $headers{date}, 'HTTP/2 413 (precheck) response carries a Date header');
$stream_io->close_now;
$loop->remove($server);
};
subtest 'HTTP/2 413 (body overrun) includes a Date header' => sub {
my $app_saw_body = 0;
my $app = async sub {
my ($scope, $receive, $send) = @_;
my $event = await $receive->();
$app_saw_body = 1 if $event->{type} eq 'http.request';
await $send->({ type => 'http.response.start', status => 200, headers => [] });
await $send->({ type => 'http.response.body', body => 'ok', more => 0 });
};
my ($conn, $stream_io, $client_sock, $server) = create_h2c_connection(
app => $app, max_body_size => 100,
);
my %headers;
my $stream_closed = 0;
my $client = create_client(
on_header => sub { my ($sid, $n, $v) = @_; $headers{lc $n} = $v; return 0 },
on_stream_close => sub { $stream_closed = 1; return 0 },
);
$client->send_connection_preface;
$client_sock->syswrite($client->mem_send);
pump($client, $client_sock);
$client->submit_request(
method => 'POST', path => '/upload', scheme => 'http', authority => 'localhost',
headers => [['content-type', 'application/octet-stream']],
body => 'X' x 200,
);
$client_sock->syswrite($client->mem_send);
pump($client, $client_sock, sub { $stream_closed });
is($headers{':status'}, '413', 'body-overrun rejected with 413');
ok(!$app_saw_body, 'app never saw the request body');
ok(defined $headers{date}, 'HTTP/2 413 (overrun) response carries a Date header');
$stream_io->close_now;
$loop->remove($server);
};
subtest 'HTTP/2 synthesized 500 includes a Date header' => sub {
my $app = async sub {
my ($scope, $receive, $send) = @_;
await $receive->();
die "app blew up\n";
};
my ($conn, $stream_io, $client_sock, $server) = create_h2c_connection(app => $app);
my %headers;
my $stream_closed = 0;
local $SIG{__WARN__} = sub { }; # the app error is expected; keep test output pristine
my $client = create_client(
on_header => sub { my ($sid, $n, $v) = @_; $headers{lc $n} = $v; return 0 },
on_stream_close => sub { $stream_closed = 1; return 0 },
);
$client->send_connection_preface;
$client_sock->syswrite($client->mem_send);
pump($client, $client_sock);
$client->submit_request(
method => 'GET', path => '/', scheme => 'http', authority => 'localhost',
);
$client_sock->syswrite($client->mem_send);
pump($client, $client_sock, sub { $stream_closed });
is($headers{':status'}, '500', 'app exception synthesizes a 500');
ok(defined $headers{date}, 'HTTP/2 synthesized 500 response carries a Date header');
$stream_io->close_now;
$loop->remove($server);
};
subtest 'HTTP/2 WebSocket denial response includes a Date header' => sub {
my $app = async sub {
my ($scope, $receive, $send) = @_;
await $receive->(); # websocket.connect
await $send->({
type => 'websocket.http.response.start', status => 401,
headers => [['x-deny', 'auth']],
});
await $send->({ type => 'websocket.http.response.body', body => 'nope' });
return;
};
my ($conn, $stream_io, $client_sock, $server) = create_h2c_connection(app => $app);
my %headers;
my $client = create_client(
on_header => sub { my ($sid, $n, $v) = @_; $headers{lc $n} = $v; return 0 },
);
$client->send_connection_preface;
$client_sock->syswrite($client->mem_send);
pump($client, $client_sock);
$client->submit_request(
method => 'CONNECT', path => '/ws/test', scheme => 'https', authority => 'localhost',
headers => [[':protocol', 'websocket'], ['sec-websocket-version', '13']],
body => sub { return undef }, # streaming: keep open
);
$client_sock->syswrite($client->mem_send);
pump($client, $client_sock, sub { defined $headers{':status'} && $headers{':status'} eq '401' });
is($headers{':status'}, '401', 'custom denial status used');
ok(defined $headers{date}, 'HTTP/2 WebSocket denial response carries a Date header');
$stream_io->close_now;
$loop->remove($server);
};
subtest 'HTTP/2 WebSocket bare-403 fallback includes a Date header' => sub {
my $app = async sub {
my ($scope, $receive, $send) = @_;
await $receive->(); # websocket.connect
await $send->({ type => 'websocket.close' });
return;
};
my ($conn, $stream_io, $client_sock, $server) = create_h2c_connection(app => $app);
my %headers;
my $client = create_client(
on_header => sub { my ($sid, $n, $v) = @_; $headers{lc $n} = $v; return 0 },
);
$client->send_connection_preface;
$client_sock->syswrite($client->mem_send);
pump($client, $client_sock);
$client->submit_request(
method => 'CONNECT', path => '/ws/reject', scheme => 'https', authority => 'localhost',
headers => [[':protocol', 'websocket'], ['sec-websocket-version', '13']],
body => sub { return undef },
);
$client_sock->syswrite($client->mem_send);
pump($client, $client_sock, sub { defined $headers{':status'} && $headers{':status'} eq '403' });
is($headers{':status'}, '403', 'bare websocket.close still gives 403');
ok(defined $headers{date}, 'HTTP/2 WebSocket bare-403 response carries a Date header');
$stream_io->close_now;
$loop->remove($server);
};
subtest 'HTTP/2 SSE decline response includes a Date header' => sub {
my $app = async sub {
my ($scope, $receive, $send) = @_;
await $send->({
type => 'sse.http.response.start', status => 404,
headers => [['content-type', 'text/plain']],
});
await $send->({ type => 'sse.http.response.body', body => 'No such stream', more => 0 });
};
my ($conn, $stream_io, $client_sock, $server) = create_h2c_connection(app => $app);
my %headers;
my $client = create_client(
on_header => sub { my ($sid, $n, $v) = @_; $headers{lc $n} = $v; return 0 },
( run in 1.435 second using v1.01-cache-2.11-cpan-5c0b1e786e0 )