Net-QUIC

 view release on metacpan or  search on metacpan

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


sub pump_pair {
    my ($client, $server, $client_local, $server_local) = @_;
    my $progress = 0;

    while (my $datagram = $server->next_datagram) {
        ++$progress;
        $client->receive_datagram(
            $datagram->data,
            $client_local,
            $server_local,
        );
    }

    while (my $datagram = $client->next_datagram) {
        ++$progress;
        $server->receive_datagram(
            $datagram->data,
            $server_local,
            $client_local,
        );
    }

    my $server_after = $server->timeout_after;
    if (defined($server_after) && $server_after <= 0) {
        ++$progress;
        $server->handle_timeout;
    }

    my $client_after = $client->timeout_after;
    if (defined($client_after) && $client_after <= 0) {
        ++$progress;
        $client->handle_timeout;
    }

    if (!$progress) {
        my @wait = sort { $a <=> $b }
            grep { defined($_) && $_ > 0 }
            ($server_after, $client_after);

        if (@wait) {
            my $nap = $wait[0] > 0.01 ? 0.01 : $wait[0] + 0.001;
            sleep($nap);
            ++$progress;
        }
    }

    return $progress;
}

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,
    );

    my $accepted;

    for (1 .. 1000) {
        pump_pair($client, $server, $client_local, $server_local);
        $accepted ||= $server->next_connection;

        last if $accepted
            && $client->connection->ready
            && $accepted->ready;
    }

    die "lifecycle test handshake did not complete"
        if !$accepted
        || !$client->connection->ready
        || !$accepted->ready;

    return (
        $client,
        $server,
        $accepted,
        $client_local,
        $server_local,
    );
}

subtest 'object destruction order' => sub {
    my ($client, $server, $accepted, $client_local, $server_local) =
        make_ready_pair(4460, 'drop-order-one');

    my @discarded;
    my @timeouts;

    my $driver = Net::QUIC::Driver->new(
        endpoint => $client,

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

        set_timeout => sub {
            push @timeouts, $_[0];
            return;
        },
    );

    my $connection = $driver->connection;
    my $stream = $connection->open_uni_stream;

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

            $server->receive_datagram(
                $datagram->data,
                $server_local,
                $locals->[$i],
            );
        }
    }

    while (my $datagram = $server->next_datagram) {
        my $client = $client_for{$datagram->peer};
        die "server produced datagram for unknown lifecycle client"
            if !defined $client;

        ++$progress;
        $client->receive_datagram(
            $datagram->data,
            $datagram->peer,
            $server_local,
        );
    }

    my $server_after = $server->timeout_after;
    if (defined($server_after) && $server_after <= 0) {
        ++$progress;
        $server->handle_timeout;
    }

    my @client_after;
    for my $client (@$clients) {
        my $after = $client->timeout_after;
        push @client_after, $after;

        if (defined($after) && $after <= 0) {
            ++$progress;
            $client->handle_timeout;
        }
    }

    if (!$progress) {
        my @wait = sort { $a <=> $b }
            grep { defined($_) && $_ > 0 }
            ($server_after, @client_after);

        if (@wait) {
            my $nap = $wait[0] > 0.01 ? 0.01 : $wait[0] + 0.001;
            sleep($nap);
            ++$progress;
        }
    }

    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,
            alpn        => $alpn,
            server_name => 'localhost',
            ca_file     => $cert_file,
        )
    } 0 .. 2;

    my @accepted;

    for (1 .. 2000) {
        pump_many($server, \@clients, \@locals, $server_local);

        while (my $connection = $server->next_connection) {
            push @accepted, $connection;
        }

        last if @accepted == 3
            && !(grep { !$_->connection->ready } @clients)
            && !(grep { !$_->ready } @accepted);
    }

    is(scalar(@accepted), 3, 'server accepts all three lifecycle clients');
    ok(!(grep { !$_->connection->ready } @clients), 'all clients are ready');
    ok(!(grep { !$_->ready } @accepted), 'all server Connections are ready');
    is($server->_managed_connection_count, 3, 'server manages three live Connections');
    ok($server->_route_count >= 3, 'live Connections have CID routes');

    my @client_streams;
    for my $i (0 .. 2) {
        my $stream = $clients[$i]->connection->open_bidi_stream;
        $stream->send("lifecycle-client-$i\n");
        $stream->finish;
        push @client_streams, $stream;
    }

    my @server_for;
    my %server_stream_for;
    my %server_finished;
    my %received_for;

    for (1 .. 2000) {
        pump_many($server, \@clients, \@locals, $server_local);

        for my $connection (@accepted) {
            my $key = refaddr($connection);
            my $stream = $server_stream_for{$key};

            if (!$stream) {
                $stream = $connection->next_stream;
                $server_stream_for{$key} = $stream if $stream;
            }



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