h2o/t/40http3-forward.t
2024-02-29 10:11:21 -08:00

110 lines
3.6 KiB
Perl

use strict;
use warnings;
use File::Temp qw(tempdir);
use Net::EmptyPort qw(wait_port);
use Test::More;
use Time::HiRes qw(time);
use t::Util;
use JSON;
plan skip_all => 'mruby support is off'
unless server_features()->{mruby};
my $client_prog = bindir() . "/h2o-httpclient";
plan skip_all => "$client_prog not found"
unless -e $client_prog;
plan skip_all => "macOS has issues https://twitter.com/kazuho/status/1298073110587949056"
if $^O eq 'darwin';
# This test utilizes two properties of BSD socket:
# * a port on a specific address can be bind(2)-ed after the same port on the ANY address in bound
# * any address within 127.0.0.0/24 is a local address that a server can accept packets
#
# Therefore, the test steps are:
# 1. create a client that connects to the server listening at 0.0.0.0 and sends a request slowly
# 2. create a new server listening at 127.0.0.1, that will forward the packets of the slow request to the original server (that
# can still receive packets at 127.0.0.2)
# 3. check that the slow request completes *after* the new server is up
my $quic_port = empty_port({ host => "0.0.0.0", proto => "udp" });
# start server1 at 0.0.0.0, check that it is up
my $server1 = spawn("*", 1);
is do {my $fh = fetch(""); local $/; join "", <$fh> }, "server=1", "server1 is up";
# initiate the slow request
my $slow_fh = fetch("-b 1200 -c 20 -i 100");
sleep 1;
# start server2 at 127.0.0.1, check that it isup
my $server2 = spawn("127.0.0.1", 2);
is do {my $fh = fetch(""); local $/; join "", <$fh> }, "server=2", "server2 is up";
# check that the slow request was served, going through $server2
my $elapsed = time;
is do {local $/; join "", <$slow_fh>}, "server=1", "slow request succeeded";
$elapsed = time - $elapsed;
cmp_ok $elapsed, '>=', 3, "slow request is so slow that the packets should have gone through server2";
# check that some packets were actually forwarded, and the event counter captured them
my $server1_port = ${server1}->{port};
my $server2_port = ${server2}->{port};
my $resp = `curl --silent -o /dev/stderr http://127.0.0.1:${server1_port}/server-status/json?show=events 2>&1 > /dev/null`;
my $jresp = decode_json("$resp");
my $num_forwarded_received = $jresp->{'http3.forwarded-packet-received'};
$resp = `curl --silent -o /dev/stderr http://127.0.0.1:${server2_port}/server-status/json?show=events 2>&1 > /dev/null`;
$jresp = decode_json("$resp");
my $num_forwarded = $jresp->{'http3.packet-forwarded'};
cmp_ok($num_forwarded, '>', 0, "some packets were forwarded");
is($num_forwarded, $num_forwarded_received, "packets forwarded == packets received");
done_testing;
sub spawn {
my ($listen_ip, $server_id) = @_;
my $host_directive = "";
if ($listen_ip ne "*") {
$host_directive = "\n host: $listen_ip";
}
my $conf = {opts => [qw(-m worker)], conf => <<"EOT"};
num-threads: 1
listen:
type: quic$host_directive
port: $quic_port
ssl:
key-file: examples/h2o/server.key
certificate-file: examples/h2o/server.crt
quic-nodes:
self: $server_id
mapping:
1: "[::1]:$quic_port" # server1 can be reached over ipv6 as well
2: "127.0.0.1:$quic_port"
ssl-session-resumption:
mode: ticket
ticket-store: file
ticket-file: t/40session-ticket/forever_ticket.yaml
hosts:
default:
paths:
"/":
mruby.handler: |
Proc.new do |env|
[200, {}, ["server=$server_id"]]
end
"/server-status":
status: ON
EOT
spawn_h2o($conf);
}
sub fetch {
my $opts = shift;
open my $fh, "-|", "$client_prog -3 100 $opts https://127.0.0.1:$quic_port/ 2> /dev/null"
or die "failed to spawn $client_prog:$!";
$fh;
}