h2o/t/40tls13-early-data.t
2023-04-25 09:13:29 +09:00

182 lines
5.6 KiB
Perl

use strict;
use warnings;
use File::Temp qw(tempdir);
use Net::EmptyPort qw(check_port);
use Test::Requires qw(Plack::Runner Starlet);
use Test::More;
use Time::HiRes qw(sleep);
use t::Util;
my $tempdir = tempdir(CLEANUP => 1);
my $upstream_port = empty_port();
subtest "http/1" => sub {
my $fetch = sub {
my ($server, $path, $send_count) = @_;
my $cmd = "exec @{[bindir]}/picotls/cli -I -s $tempdir/session -e 127.0.0.1 $server->{tls_port} > $tempdir/resp.txt";
open my $fh, "|-", $cmd
or die "failed to invoke command:$cmd:$!";
autoflush $fh 1;
for (my $i = 0; $i < $send_count; ++$i) {
sleep 0.5
if $i != 0;
print $fh <<"EOT";
GET $path HTTP/1.1\r
Host: 127.0.0.1:$server->{tls_port}\r
Connection: @{[$i + 1 == $send_count ? "close" : "keep-alive"]}\r
\r
EOT
}
close $fh;
open $fh, "<", "$tempdir/resp.txt"
or die "failed to open file:$tempdir/resp.txt:$!";
do { local $/; <$fh> };
};
run_tests(sub {
my ($server, $path) = @_;
my $resp = $fetch->($server, $path, 1);
like $resp, qr{^HTTP/[^ ]* 200 .*\r\n\r\ncount:1$}s;
$resp = $fetch->($server, $path, 1);
like $resp, qr{^HTTP/[^ ]* 425 .*\r\n\r\ncount:2$}s;
$resp = $fetch->($server, $path, 2);
like $resp, qr{^HTTP/[^ ]* 425 .*\r\n\r\ncount:3HTTP/[^ ]* 200 .*\r\n\r\ncount:4$}s;
});
};
subtest "http/2" => sub {
my $fetch = sub {
my ($server, $path) = @_;
my $cmd = "exec @{[bindir]}/picotls/cli -I -s $tempdir/session -e 127.0.0.1 $server->{tls_port} > $tempdir/resp.txt";
my $pid = open my $child_in, "|-", $cmd
or die "failed to invoke command:$cmd:$!";
autoflush $child_in 1;
# send request
my $hpack_str = sub { chr(length $_[0]) . $_[0] };
my $hpack_hdr = sub { "\x10" . $hpack_str->($_[0]) . $hpack_str->($_[1]) };
my $hpack = join("",
$hpack_hdr->(":method", "GET"),
$hpack_hdr->(":scheme", "https"),
$hpack_hdr->(":authority", "127.0.0.1"),
$hpack_hdr->(":path", $path),
);
syswrite $child_in, join '', (
"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", # preface
"\x00\x00\x00\x04\x00\x00\x00\x00\x00", # SETTINGS
"\x00\x00@{[chr length $hpack]}\x01\x05\x00\x00\x00\x01$hpack", # HEADERS
);
while (waitpid($pid, 0) != $pid) {}
open my $fh, "<", "$tempdir/resp.txt"
or die "failed to open file:$tempdir/resp.txt:$!";
do { local $/; <$fh> };
};
run_tests(sub {
my ($server, $path) = @_;
my $resp = $fetch->($server, $path);
# HEADERS for stream zero starting with :status:200 followed by DATA frame carrying the expected content
like $resp, qr{\x01\x04\x00\x00\x00\x01\x88.*\x00[\x00\x01]\x00\x00\x00\x01count:1}is;
$resp = $fetch->($server, $path);
like $resp, qr{\x01\x04\x00\x00\x00\x01\x88.*\x00[\x00\x01]\x00\x00\x00\x01count:3}is;
});
};
sub run_tests {
my $do_test = shift;
# spawn server
my $server = spawn_h2o(<< "EOT");
http2-idle-timeout: 1
num-threads: 1
hosts:
default:
paths:
/proxy/:
proxy.reverse.url: http://127.0.0.1:$upstream_port
/fcgi/:
fastcgi.connect:
host: 127.0.0.1
port: $upstream_port
type: tcp
@{[server_features()->{mruby} ? << "EOT2" : ""
/mruby/:
mruby.handler: |
num_reqs = 0
Proc.new do |req|
num_reqs += 1
[req["HTTP_EARLY_DATA"] ? 425 : 200, {}, ["count:#{num_reqs}"]]
end
EOT2
]}
EOT
# give some time to h2o to setup the session ticket encryption key
sleep 1;
# run tests
subtest "proxy" => sub {
for my $sleep (0, 1) {
subtest "sleep=$sleep" => sub {
unlink "$tempdir/session";
my $guard = spawn_starlet($sleep);
$do_test->($server, "/proxy/");
};
}
};
subtest "fcgi" => sub {
for my $sleep (0, 1) {
subtest "sleep=$sleep" => sub {
unlink "$tempdir/session";
my $guard = spawn_fcgi($sleep);
$do_test->($server, "/fcgi/");
};
}
};
subtest "mruby" => sub {
unlink "$tempdir/session";
plan skip_all => "mruby is off"
unless server_features()->{mruby};
$do_test->($server, "/mruby/");
};
}
sub spawn_plack_server {
my ($sleep_secs, $port, @options) = @_;
# the logic should be same as the mruby handler
my $app = sub {
my $num_reqs = 0;
sub {
my $env = shift;
sleep $sleep_secs;
++$num_reqs;
my $body = "count:$num_reqs";
return [$env->{HTTP_EARLY_DATA} ? 425 : 200, ["content-length" => length $body], [$body]];
};
}->();
my $upstream_pid = fork;
die "fork failed:$!"
unless defined $upstream_pid;
if ($upstream_pid == 0) {
my $runner = Plack::Runner->new;
$runner->parse_options(@options, "--listen", "127.0.0.1:$port");
$runner->run($app);
exit 0;
}
while (!check_port($port)) {
sleep 0.1;
}
make_guard(sub {
kill 'TERM', $upstream_pid;
while (waitpid($upstream_pid, 0) != $upstream_pid) {}
});
}
sub spawn_starlet {
spawn_plack_server(@_, $upstream_port, qw(-s Starlet --max-workers=1 --access-log /dev/null));
}
sub spawn_fcgi {
spawn_plack_server(@_, $upstream_port, qw(-s FCGI --nproc 1 --access-log /dev/null));
}
done_testing;