Net-QUIC

 view release on metacpan or  search on metacpan

README.md  view on Meta::CPAN

```

## A server

A server Driver uses the same UDP/timer contract:

```perl
my $driver = Net::QUIC::Driver->server(
    alpn             => 'my-protocol',
    certificate_file => 'server-cert.pem',
    private_key_file => 'server-key.pem',

    send => sub {
        my ($datagram) = @_;
        send_udp($datagram);
        return 1;
    },

    set_timeout => sub {
        my ($seconds) = @_;
        replace_timer($seconds);

examples/io-select-echo-server.pl  view on Meta::CPAN

            1;
        }
    } @connections;

    return;
}

$driver = Net::QUIC::Driver->server(
    alpn             => $alpn,
    certificate_file => $cert_file,
    private_key_file => $key_file,

    send => sub {
        my ($datagram) = @_;

        push @udp_out, $datagram;

        if (!flush_udp()) {
            $writable->add($socket);
            return 0;
        }

lib/Net/QUIC/Driver.pm  view on Meta::CPAN


Other client options are passed to L<Net::QUIC::Endpoint/client>. This includes
session resumption, 0-RTT, address-token reuse, QUIC version selection, and
transport limits.

=head2 server

    my $driver = Net::QUIC::Driver->server(
        alpn             => 'my-protocol',
        certificate_file => 'server-cert.pem',
        private_key_file => 'server-key.pem',
        send              => sub { ... },
        set_timeout       => sub { ... },
    );

Creates a server Driver.

One server Driver can manage many QUIC Connections on one UDP socket.

Pull newly created Connections with L</next_connection>.

lib/Net/QUIC/Endpoint.pm  view on Meta::CPAN


    return bless {
        mode       => 'client',
        connection => $connection,
    }, $class;
}

sub server {
    my ($class, %args) = @_;

    for my $name (qw(alpn certificate_file private_key_file)) {
        croak "missing required $name argument"
            if !defined $args{$name};
    }

    my $server_tls = Net::QUIC::_ServerTLS->_new(
        $args{certificate_file},
        $args{private_key_file},
        $args{accept_early_data} ? 1 : 0,
    );

    my $transport = $class->_transport_config(delete $args{transport});
    my $preferred_address = delete $args{preferred_address};
    my $preferred_version = delete $args{preferred_version};

    $preferred_version = $class->_quic_version(
        $preferred_version,
        'preferred_version',

lib/Net/QUIC/Endpoint.pm  view on Meta::CPAN

See L<Net::QUIC::Connection/QUIC DATAGRAM>.

The Stream values are flow-control starting values, not lifetime byte or
stream limits.

=head2 server

    my $endpoint = Net::QUIC::Endpoint->server(
        alpn             => 'my-protocol',
        certificate_file => 'server-cert.pem',
        private_key_file => 'server-key.pem',
    );

Creates a server Endpoint.

One server Endpoint can manage many QUIC Connections.

Required options are:

    alpn
    certificate_file
    private_key_file

=head3 Address validation

To require a new client to prove that it can receive packets at its source
address:

    validate_address => 1

A new client may receive QUIC Retry before a full Connection is created.

t/06-server-endpoint.t  view on Meta::CPAN

    pack_sockaddr_in(40003, inet_aton('127.0.0.1')),
    pack_sockaddr_in(40004, inet_aton('127.0.0.1')),
);
my $alpn = 'net-quic-server-endpoint-test';
my $cert_file = "$FindBin::Bin/data/server-cert.pem";
my $key_file = "$FindBin::Bin/data/server-key.pem";

my $server = Net::QUIC::Endpoint->server(
    alpn             => $alpn,
    certificate_file => $cert_file,
    private_key_file => $key_file,
);

$server->receive_datagram(
    "not a QUIC packet",
    $server_local,
    $client_local[0],
);
ok(!defined($server->next_connection), 'invalid datagram does not create a connection');

my @client = map {

t/07-server-front-door.t  view on Meta::CPAN

        peer        => $server_local,
        alpn        => $alpn,
        server_name => 'localhost',
        ca_file     => $cert_file,
    );
}

my $vn_server = Net::QUIC::Endpoint->server(
    alpn             => $alpn,
    certificate_file => $cert_file,
    private_key_file => $key_file,
);

my $vn_client = make_client();
my $initial = $vn_client->next_datagram;
ok(defined($initial), 'client produces Initial for version negotiation test');

my $unsupported = $initial->data;
substr($unsupported, 1, 4, pack('N', 0x1a2a3a4a));

$vn_server->receive_datagram(

t/07-server-front-door.t  view on Meta::CPAN

    'Version Negotiation advertises QUIC v1',
);
ok(
    !defined($vn_server->next_datagram),
    'Version Negotiation response is drained from stateless queue',
);

my $server = Net::QUIC::Endpoint->server(
    alpn             => $alpn,
    certificate_file => $cert_file,
    private_key_file => $key_file,
    validate_address => 1,
);
my $client = make_client();

$initial = $client->next_datagram;
ok(defined($initial), 'client produces Initial for Retry test');

$server->receive_datagram(
    $initial->data,
    $server_local,

t/08-certificate-verification.t  view on Meta::CPAN

    qr/unable to load client CA file/,
    'bad CA file is rejected during client construction',
);

sub run_handshake {
    my (%args) = @_;

    my $server = Net::QUIC::Endpoint->server(
        alpn             => defined($args{server_alpn}) ? $args{server_alpn} : $alpn,
        certificate_file => $cert_file,
        private_key_file => $key_file,
    );

    my %client_args = (
        local       => $client_local,
        peer        => $server_local,
        alpn        => defined($args{client_alpn}) ? $args{client_alpn} : $alpn,
        server_name => $args{server_name},
    );
    $client_args{ca_file} = $args{ca_file}
        if defined $args{ca_file};

t/09-connection-retirement.t  view on Meta::CPAN


my $server_local = pack_sockaddr_in(4439, inet_aton('127.0.0.1'));
my $client_local = pack_sockaddr_in(40008, inet_aton('127.0.0.1'));
my $alpn = 'net-quic-connection-retirement-test';
my $cert_file = "$FindBin::Bin/data/server-cert.pem";
my $key_file = "$FindBin::Bin/data/server-key.pem";

my $server = Net::QUIC::Endpoint->server(
    alpn             => $alpn,
    certificate_file => $cert_file,
    private_key_file => $key_file,
);

my $client = Net::QUIC::Endpoint->client(
    local       => $client_local,
    peer        => $server_local,
    alpn        => $alpn,
    server_name => 'localhost',
    ca_file     => $cert_file,
);

t/10-shared-server-tls.t  view on Meta::CPAN

my $dir = tempdir(CLEANUP => 1);
my $cert_file = "$dir/server-cert.pem";
my $key_file = "$dir/server-key.pem";

copy_file($source_cert, $cert_file);
copy_file($source_key, $key_file);

my $server = Net::QUIC::Endpoint->server(
    alpn             => $alpn,
    certificate_file => $cert_file,
    private_key_file => $key_file,
);

ok(unlink($cert_file), 'certificate file can be removed after server construction');
ok(unlink($key_file), 'private key file can be removed after server construction');

my @client = map {
    Net::QUIC::Endpoint->client(
        local       => $_,
        peer        => $server_local,
        alpn        => $alpn,

t/10-shared-server-tls.t  view on Meta::CPAN

ok($client[0]->connection->ready, 'first client handshake completes');
ok($client[1]->connection->ready, 'second client handshake completes');
ok($accepted[0]->ready, 'first server connection uses shared TLS context');
ok($accepted[1]->ready, 'second server connection uses shared TLS context');

like(
    dies {
        Net::QUIC::Endpoint->server(
            alpn             => $alpn,
            certificate_file => "$dir/missing-cert.pem",
            private_key_file => "$dir/missing-key.pem",
        );
    },
    qr/unable to load Picotls server certificate/,
    'missing certificate fails when Endpoint is constructed',
);

my $valid_cert = "$dir/valid-cert.pem";
copy_file($source_cert, $valid_cert);

like(
    dies {
        Net::QUIC::Endpoint->server(
            alpn             => $alpn,
            certificate_file => $valid_cert,
            private_key_file => "$dir/missing-key.pem",
        );
    },
    qr/unable to open Picotls server private key/,
    'missing private key fails when Endpoint is constructed',
);

done_testing;

t/11-stateless-reset.t  view on Meta::CPAN

my $server_local = pack_sockaddr_in(4441, inet_aton('127.0.0.1'));
my $client_local = pack_sockaddr_in(40010, inet_aton('127.0.0.1'));
my $alpn = 'net-quic-stateless-reset-test';
my $cert_file = "$FindBin::Bin/data/server-cert.pem";
my $key_file = "$FindBin::Bin/data/server-key.pem";

sub make_server {
    return Net::QUIC::Endpoint->server(
        alpn             => $alpn,
        certificate_file => $cert_file,
        private_key_file => $key_file,
    );
}

sub make_client {
    return Net::QUIC::Endpoint->client(
        local       => $client_local,
        peer        => $server_local,
        alpn        => $alpn,
        server_name => 'localhost',
        ca_file     => $cert_file,

t/15-driver-loopback.t  view on Meta::CPAN

ok(defined($server_local), 'server UDP socket has a local address');
ok(defined($client_local), 'client UDP socket has a local address');

my ($server_deadline, $client_deadline);
my ($server_tx, $client_tx) = (0, 0);
my ($server_rx, $client_rx) = (0, 0);

my $server = Net::QUIC::Driver->server(
    alpn             => $alpn,
    certificate_file => $cert_file,
    private_key_file => $key_file,

    send => sub {
        my ($datagram) = @_;
        return send_datagram($server_socket, $datagram, \$server_tx);
    },

    set_timeout => sub {
        my ($after) = @_;
        $server_deadline = defined($after)
            ? time() + $after

t/16-stream-limits.t  view on Meta::CPAN


my $server_local = pack_sockaddr_in(4450, inet_aton('127.0.0.1'));
my $client_local = pack_sockaddr_in(40050, inet_aton('127.0.0.1'));
my $alpn = 'net-quic-stream-limit-test';
my $cert_file = "$FindBin::Bin/data/server-cert.pem";
my $key_file = "$FindBin::Bin/data/server-key.pem";

my $server = Net::QUIC::Endpoint->server(
    alpn             => $alpn,
    certificate_file => $cert_file,
    private_key_file => $key_file,
);

my $client = Net::QUIC::Endpoint->client(
    local       => $client_local,
    peer        => $server_local,
    alpn        => $alpn,
    server_name => 'localhost',
    ca_file     => $cert_file,
);

t/17-transport-tuning.t  view on Meta::CPAN


is(
    $short_handshake->connection->close_info->{type},
    'handshake',
    'configured handshake timeout becomes a handshake close outcome',
);

my $server = Net::QUIC::Endpoint->server(
    alpn             => $alpn,
    certificate_file => $cert_file,
    private_key_file => $key_file,
    transport        => {
        max_bidi_streams => 1,
        max_uni_streams  => 1,
    },
);

my $client = make_client();
my $accepted;

sub pump_pair {

t/18-destructive-lifecycle.t  view on Meta::CPAN

sub make_ready_pair {
    my ($port, $name) = @_;

    my $server_local = pack_sockaddr_in($port, inet_aton('127.0.0.1'));
    my $client_local = pack_sockaddr_in($port + 10000, inet_aton('127.0.0.1'));
    my $alpn = "net-quic-lifecycle-$name";

    my $server = Net::QUIC::Endpoint->server(
        alpn             => $alpn,
        certificate_file => $cert_file,
        private_key_file => $key_file,
    );

    my $client = Net::QUIC::Endpoint->client(
        local       => $client_local,
        peer        => $server_local,
        alpn        => $alpn,
        server_name => 'localhost',
        ca_file     => $cert_file,
    );

t/18-destructive-lifecycle.t  view on Meta::CPAN

    return $progress;
}

subtest 'staggered multi-connection retirement' => sub {
    my $server_local = pack_sockaddr_in(4463, inet_aton('127.0.0.1'));
    my $alpn = 'net-quic-lifecycle-many';

    my $server = Net::QUIC::Endpoint->server(
        alpn             => $alpn,
        certificate_file => $cert_file,
        private_key_file => $key_file,
    );

    my @locals = map {
        pack_sockaddr_in(41000 + $_, inet_aton('127.0.0.1'))
    } 0 .. 2;

    my @clients = map {
        Net::QUIC::Endpoint->client(
            local       => $locals[$_],
            peer        => $server_local,

t/19-local-address.t  view on Meta::CPAN

            ca_file     => $cert_file,
        );
    },
    qr/local must be a concrete IPv4 or IPv6 address/,
    'client rejects an IPv4 wildcard as its QUIC local path',
);

my $server = Net::QUIC::Endpoint->server(
    alpn             => 'local-address-test',
    certificate_file => $cert_file,
    private_key_file => $key_file,
);

like(
    dies {
        $server->receive_datagram(
            "not a QUIC packet",
            $wildcard_v4,
            $loopback_v4,
        );
    },

t/19-local-address.t  view on Meta::CPAN

            $loopback_v4,
            $loopback_v4,
        );
    },
    'server receive accepts a concrete IPv4 local path',
);

my $driver = Net::QUIC::Driver->server(
    alpn             => 'local-address-test',
    certificate_file => $cert_file,
    private_key_file => $key_file,
    send             => sub { 1 },
    set_timeout      => sub { return },
);

$driver->start;

like(
    dies {
        $driver->receive(
            "not a QUIC packet",

t/19-local-address.t  view on Meta::CPAN

        },
        qr/local must be a concrete IPv4 or IPv6 address/,
        'client rejects an IPv6 wildcard as its QUIC local path',
    );

    ok(
        !dies {
            my $ipv6_server = Net::QUIC::Endpoint->server(
                alpn             => 'local-address-test',
                certificate_file => $cert_file,
                private_key_file => $key_file,
            );

            $ipv6_server->receive_datagram(
                "not a QUIC packet",
                $loopback_v6,
                $loopback_v6,
            );
        },
        'server receive accepts a concrete IPv6 local path',
    );

t/24-preferred-address.t  view on Meta::CPAN

my $alpn = 'net-quic-preferred-address-test';
my $cert_file = "$FindBin::Bin/data/server-cert.pem";
my $key_file = "$FindBin::Bin/data/server-key.pem";

my $saw_client_to_preferred = 0;
my $saw_server_from_preferred = 0;

my $server = Net::QUIC::Endpoint->server(
    alpn              => $alpn,
    certificate_file  => $cert_file,
    private_key_file  => $key_file,
    preferred_address => $server_preferred,
);

my $client = Net::QUIC::Endpoint->client(
    local       => $client_local,
    peer        => $server_initial,
    alpn        => $alpn,
    server_name => 'localhost',
    ca_file     => $cert_file,
);

t/25-new-token.t  view on Meta::CPAN

my $client_a = pack_sockaddr_in(40200, inet_aton('127.0.0.1'));
my $client_b = pack_sockaddr_in(40201, inet_aton('127.0.0.1'));
my $client_wrong_ip = pack_sockaddr_in(40202, inet_aton('127.0.0.2'));
my $alpn = 'net-quic-new-token-test';
my $cert_file = "$FindBin::Bin/data/server-cert.pem";
my $key_file = "$FindBin::Bin/data/server-key.pem";

my $server = Net::QUIC::Endpoint->server(
    alpn             => $alpn,
    certificate_file => $cert_file,
    private_key_file => $key_file,
    validate_address => 1,
);

my %client_for_local;
my @clients;

sub make_client {
    my ($local, $token) = @_;

    my %args = (

t/27-quic-v2-cvn.t  view on Meta::CPAN

    my ($datagram) = @_;
    return unpack('N', substr($datagram->data, 1, 4));
}

sub make_server {
    my (%extra) = @_;

    return Net::QUIC::Endpoint->server(
        alpn             => $alpn,
        certificate_file => $cert_file,
        private_key_file => $key_file,
        %extra,
    );
}

sub make_client {
    my ($local, %extra) = @_;

    return Net::QUIC::Endpoint->client(
        local       => $local,
        peer        => $server_local,

t/28-ecn.t  view on Meta::CPAN

        inet_aton('127.0.0.1'),
    );
    my $server_local = pack_sockaddr_in(
        $server_port,
        inet_aton('127.0.0.1'),
    );

    my $server = Net::QUIC::Endpoint->server(
        alpn             => $alpn,
        certificate_file => $cert_file,
        private_key_file => $key_file,
    );

    my $client = Net::QUIC::Endpoint->client(
        local       => $client_local,
        peer        => $server_local,
        alpn        => $alpn,
        server_name => 'localhost',
        ca_file     => $cert_file,
    );

t/29-protocol-rx.t  view on Meta::CPAN


my $server_local = pack_sockaddr_in(4470, inet_aton('127.0.0.1'));
my $client_local = pack_sockaddr_in(4070, inet_aton('127.0.0.1'));
my $alpn = 'net-quic-protocol-rx-test';
my $cert_file = "$FindBin::Bin/data/server-cert.pem";
my $key_file = "$FindBin::Bin/data/server-key.pem";

my $server = Net::QUIC::Endpoint->server(
    alpn             => $alpn,
    certificate_file => $cert_file,
    private_key_file => $key_file,
    transport        => {
        connection_window => 64 * 1024,
        stream_window     => 2048,
    },
);

my $client = Net::QUIC::Endpoint->client(
    local       => $client_local,
    peer        => $server_local,
    alpn        => $alpn,

t/30-acked-offset.t  view on Meta::CPAN


my $server_local = pack_sockaddr_in(4471, inet_aton('127.0.0.1'));
my $client_local = pack_sockaddr_in(4071, inet_aton('127.0.0.1'));
my $alpn = 'net-quic-acked-offset-test';
my $cert_file = "$FindBin::Bin/data/server-cert.pem";
my $key_file = "$FindBin::Bin/data/server-key.pem";

my $server = Net::QUIC::Endpoint->server(
    alpn             => $alpn,
    certificate_file => $cert_file,
    private_key_file => $key_file,
);

my $client = Net::QUIC::Endpoint->client(
    local       => $client_local,
    peer        => $server_local,
    alpn        => $alpn,
    server_name => 'localhost',
    ca_file     => $cert_file,
);

t/31-stream-activity.t  view on Meta::CPAN


my $server_local = pack_sockaddr_in(4472, inet_aton('127.0.0.1'));
my $client_local = pack_sockaddr_in(4072, inet_aton('127.0.0.1'));
my $alpn = 'net-quic-stream-activity-test';
my $cert_file = "$FindBin::Bin/data/server-cert.pem";
my $key_file = "$FindBin::Bin/data/server-key.pem";

my $server = Net::QUIC::Endpoint->server(
    alpn             => $alpn,
    certificate_file => $cert_file,
    private_key_file => $key_file,
);

my $client = Net::QUIC::Endpoint->client(
    local       => $client_local,
    peer        => $server_local,
    alpn        => $alpn,
    server_name => 'localhost',
    ca_file     => $cert_file,
);

t/32-bounded-tx.t  view on Meta::CPAN


my $server_local = pack_sockaddr_in(4473, inet_aton('127.0.0.1'));
my $client_local = pack_sockaddr_in(4073, inet_aton('127.0.0.1'));
my $alpn = 'net-quic-bounded-tx-test';
my $cert_file = "$FindBin::Bin/data/server-cert.pem";
my $key_file = "$FindBin::Bin/data/server-key.pem";

my $server = Net::QUIC::Endpoint->server(
    alpn             => $alpn,
    certificate_file => $cert_file,
    private_key_file => $key_file,
    transport        => {
        connection_window => 64 * 1024,
        stream_window     => 1024,
    },
);

my $client = Net::QUIC::Endpoint->client(
    local       => $client_local,
    peer        => $server_local,
    alpn        => $alpn,

t/33-rx-reclaim-credit.t  view on Meta::CPAN


my $server_local = pack_sockaddr_in(4474, inet_aton('127.0.0.1'));
my $client_local = pack_sockaddr_in(4074, inet_aton('127.0.0.1'));
my $alpn = 'net-quic-rx-reclaim-credit-test';
my $cert_file = "$FindBin::Bin/data/server-cert.pem";
my $key_file = "$FindBin::Bin/data/server-key.pem";

my $server = Net::QUIC::Endpoint->server(
    alpn             => $alpn,
    certificate_file => $cert_file,
    private_key_file => $key_file,
    transport        => {
        connection_window => 1024,
        stream_window     => 1024,
    },
);

my $client = Net::QUIC::Endpoint->client(
    local       => $client_local,
    peer        => $server_local,
    alpn        => $alpn,

t/34-protocol-engine-v2.t  view on Meta::CPAN


my $server_local = pack_sockaddr_in(4475, inet_aton('127.0.0.1'));
my $client_local = pack_sockaddr_in(4075, inet_aton('127.0.0.1'));
my $alpn = 'net-quic-protocol-engine-v2-test';
my $cert_file = "$FindBin::Bin/data/server-cert.pem";
my $key_file = "$FindBin::Bin/data/server-key.pem";

my $server = Net::QUIC::Endpoint->server(
    alpn             => $alpn,
    certificate_file => $cert_file,
    private_key_file => $key_file,
    transport        => {
        connection_window => 64 * 1024,
        stream_window     => 1024,
    },
);

my $client = Net::QUIC::Endpoint->client(
    local       => $client_local,
    peer        => $server_local,
    alpn        => $alpn,

t/35-stream-index.t  view on Meta::CPAN


my $server_local = pack_sockaddr_in(4476, inet_aton('127.0.0.1'));
my $client_local = pack_sockaddr_in(4076, inet_aton('127.0.0.1'));
my $alpn = 'net-quic-stream-index-test';
my $cert_file = "$FindBin::Bin/data/server-cert.pem";
my $key_file = "$FindBin::Bin/data/server-key.pem";

my $server = Net::QUIC::Endpoint->server(
    alpn             => $alpn,
    certificate_file => $cert_file,
    private_key_file => $key_file,
    transport        => {
        max_bidi_streams => 2500,
        max_uni_streams  => 2500,
    },
);

my $client = Net::QUIC::Endpoint->client(
    local       => $client_local,
    peer        => $server_local,
    alpn        => $alpn,

t/36-quic-datagram.t  view on Meta::CPAN

        inet_aton('127.0.0.1'),
    );
    my $server_local = pack_sockaddr_in(
        $args{server_port},
        inet_aton('127.0.0.1'),
    );

    my $server = Net::QUIC::Endpoint->server(
        alpn             => $alpn,
        certificate_file => $cert_file,
        private_key_file => $key_file,
        transport        => {
            max_datagram_frame_size => $args{server_datagram_size} // 65535,
        },
        (defined($args{version})
            ? (preferred_version => $args{version})
            : ()),
    );

    my $client = Net::QUIC::Endpoint->client(
        local       => $client_local,

t/36-quic-datagram.t  view on Meta::CPAN

        'v2-datagram',
        'QUIC v2 DATAGRAM is delivered',
    );
};

like(
    dies {
        Net::QUIC::Endpoint->server(
            alpn             => $alpn,
            certificate_file => $cert_file,
            private_key_file => $key_file,
            transport        => {
                max_datagram_frame_size => 65536,
            },
        );
    },
    qr/max_datagram_frame_size cannot exceed 65535/,
    'impossible DATAGRAM frame size is rejected',
);

done_testing;



( run in 1.406 second using v1.01-cache-2.11-cpan-036bef1c656 )