# Net::QUIC

[![CPAN version](https://badge.fury.io/pl/Net-QUIC.svg)](https://metacpan.org/dist/Net-QUIC)
[![CPANTS Kwalitee](https://cpants.cpanauthors.org/dist/Net-QUIC.svg)](https://cpants.cpanauthors.org/dist/Net-QUIC)
[![CI](https://github.com/haxmeister/perl-Net-QUIC/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/haxmeister/perl-Net-QUIC/actions/workflows/test.yml)
[![License](https://img.shields.io/cpan/l/Net-QUIC.svg)](https://github.com/haxmeister/perl-Net-QUIC/blob/main/LICENSE)
[![Perl](https://img.shields.io/badge/perl-5.20%2B-blue.svg)](https://www.perl.org/)
[![ngtcp2](https://img.shields.io/badge/ngtcp2-1.25.0-blue.svg)](https://github.com/ngtcp2/ngtcp2)
[![QUIC](https://img.shields.io/badge/QUIC-v1%20%2B%20v2-blue.svg)](https://www.rfc-editor.org/rfc/rfc9000)


Net::QUIC is a QUIC transport library for Perl.

QUIC is a secure network transport built on UDP. A QUIC connection can carry
many independent byte streams at the same time.

If you are new to QUIC, the useful mental model is:

```text
one QUIC connection
    |
    +-- stream
    +-- stream
    +-- stream
```

Each stream is a reliable ordered sequence of bytes.

Net::QUIC handles the difficult transport work:

- QUIC packets and connection state
- TLS 1.3 encryption
- certificate verification
- streams and flow control
- retransmission and loss recovery
- connection IDs
- timers
- migration between network paths
- QUIC v1 and QUIC v2

Your event loop still owns the UDP socket.

You do not need to know ngtcp2 to use Net::QUIC. It is an internal native
dependency.

Net::QUIC is not HTTP/3 and is not a web framework. It gives applications
connections and byte streams. Your application decides what the bytes mean.

## Installation

From CPAN:

```text
cpanm Net::QUIC
```

Net::QUIC uses `Alien::ngtcp2` to provide its native dependencies. A normal
installation does not require you to find or configure ngtcp2 yourself.

The event-loop modules used in `examples/` are optional. Net::QUIC itself
does not require Linux::Event, AnyEvent, IO::Async, Mojolicious, or EV.

## Start here

Most application code only needs three classes:

```text
Net::QUIC::Driver
        |
        +-- Net::QUIC::Connection
                    |
                    +-- Net::QUIC::Stream
```

Use:

- `Net::QUIC::Driver` to connect Net::QUIC to UDP and a timer
- `Net::QUIC::Connection` to represent one QUIC connection
- `Net::QUIC::Stream` to send and receive application bytes

There is also a lower-level `Net::QUIC::Endpoint`. Most applications should
use Driver instead.

## What application code looks like

Once a connection is ready, application code is simple.

Open a stream:

```perl
my $stream = $connection->open_bidi_stream;

if ($stream) {
    $stream->send("hello\n");
    $stream->finish;
}
```

Accept streams opened by the peer:

```perl
while (my $stream = $connection->next_stream) {
    while (defined(my $bytes = $stream->next_data)) {
        handle_bytes($bytes);
    }
}
```

Close the connection:

```perl
$connection->close;
```

The UDP socket and QUIC timer normally stay in your event-loop adapter rather
than in the application protocol code.

## QUIC streams are byte streams

A QUIC stream is an ordered sequence of bytes.

It is not a sequence of messages.

This:

```perl
$stream->send("one");
$stream->send("two");
```

does not guarantee that the peer receives exactly two `next_data` results.

If your application needs messages, add its own framing. For example, it could
use newline-delimited messages, fixed-size records, or a length prefix.

QUIC gives each stream its own ordering and flow control. A blocked or lost
packet on one stream does not turn all other streams into one shared byte
stream.

## Bidirectional and unidirectional streams

A bidirectional stream allows both endpoints to send:

```perl
my $stream = $connection->open_bidi_stream;
```

A unidirectional stream allows only the endpoint that created it to send:

```perl
my $stream = $connection->open_uni_stream;
```

A stream can also tell you what this endpoint is allowed to do:

```perl
$stream->can_send;
$stream->can_receive;
```

## Waiting for the connection

A new QUIC connection performs a TLS handshake before normal application work
begins.

Check:

```perl
if ($connection->ready) {
    ...
}
```

A server can receive a new Connection object before its handshake has
completed. This is normal.

## A client

A client Driver needs:

- `local` - the packed local UDP socket address
- `peer` - the packed server UDP address
- `alpn` - the name of the application protocol carried over QUIC
- `server_name` - the name expected in the server certificate
- `send` - a callback that transmits one UDP datagram
- `set_timeout` - a callback that replaces the QUIC timer

For example:

```perl
use Net::QUIC::Driver;

my $driver = Net::QUIC::Driver->client(
    local       => $packed_local_address,
    peer        => $packed_peer_address,
    alpn        => 'my-protocol',
    server_name => 'example.com',

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

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

my $connection = $driver->connection;

$driver->start;
```

`start` tells Driver that the UDP transport is ready. The client can then
produce its first QUIC packet.

### What is ALPN?

ALPN is simply a short protocol name agreed on by the client and server.

For a custom protocol you might use:

```perl
alpn => 'my-protocol'
```

It prevents two unrelated protocols from accidentally using the same QUIC
connection.

### What is server_name?

`server_name` is the DNS name or IP address that the server certificate must
represent.

For example, the UDP peer can be a numeric address:

```text
192.0.2.20:4433
```

while certificate verification uses:

```perl
server_name => 'service.example.com'
```

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

$driver->start;
```

Feed received UDP packets into Driver:

```perl
$driver->receive($bytes, $local, $peer);
```

Pull newly created connections with:

```perl
while (my $connection = $driver->next_connection) {
    ...
}
```

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

## The event-loop contract

Net::QUIC deliberately does not choose an event loop.

The event loop owns:

- UDP I/O
- one replaceable one-shot timer

Driver needs two callbacks from the adapter:

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

    # Send one complete UDP datagram.
    #
    # Return true if another datagram can be accepted immediately.
    # Return false if this datagram was accepted but output is now blocked.
},

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

    # Replace the current one-shot QUIC timer.
    # undef means cancel it.
},
```

The event loop reports four events to Driver:

```perl
$driver->start;
$driver->receive($bytes, $local, $peer);
$driver->timeout;
$driver->writable;
```

That is the normal Driver contract.

There is no application-visible QUIC pump loop.

Driver automatically drains pending QUIC output, updates the timer, pauses for
UDP backpressure, and resumes after `writable`.

Application operations such as:

```perl
$stream->send(...);
$stream->finish;
$stream->reset(...);
$connection->close;
```

also notify Driver automatically when transport work is needed.

## UDP packet wrapper: Net::QUIC::Datagram

The Driver `send` callback receives a `Net::QUIC::Datagram`.

This class is an outbound UDP packet wrapper for the event-loop adapter. It is
not an RFC 9221 application DATAGRAM carried inside QUIC.

Useful fields are:

```perl
$datagram->data;   # complete UDP payload
$datagram->local;  # packed local source address
$datagram->peer;   # packed destination address
$datagram->ecn;    # ECN codepoint for the IP header
```

Do not split `data`. It is one complete UDP datagram.

For the common case of a socket bound to one concrete local address, the socket
already supplies the correct source address.

The `local` value becomes especially important for wildcard sockets and
connection migration.

## Local addresses

QUIC needs to know the actual network path used by each packet.

The `local` value passed to Net::QUIC must therefore be a concrete IPv4 or
IPv6 address.

These are wildcard bind addresses, not concrete QUIC paths:

```text
0.0.0.0
::
```

A client normally avoids this problem by connecting its UDP socket and using
`getsockname` after the kernel chooses a local address.

A server can still bind to a wildcard address, but its adapter must discover
the actual destination address of each received packet and pass that concrete
address to:

```perl
$driver->receive($bytes, $local, $peer);
```

On Linux this is commonly done with packet-info ancillary data and
`recvmsg` / `sendmsg`.

If an event system cannot report the actual destination address, bind the QUIC
socket to one concrete local address instead.

## UDP backpressure

A UDP send can temporarily be unable to accept another packet.

The Driver `send` callback uses its return value to report that condition:

- true - another datagram can be accepted immediately
- false - this datagram was accepted, but stop sending more for now

When output becomes writable again, call:

```perl
$driver->writable;
```

Driver then continues where it stopped.

## Bounded reliable Stream sending

Net::QUIC lets applications impose an optional hard bound on the bytes it
retains in its reliable Stream transmit queue:

```perl
$connection->send_buffer_limit(8192);
my $accepted = $stream->send_some($bytes);
```

The default `send_some` behavior is unchanged: it can queue bytes beyond
the peer's current QUIC flow-control window, provided the shared buffer
has space. It returns how many prefix bytes were accepted. The application
must never resend the accepted prefix just because the result was short.

When many streams share a small buffer, advanced protocol engines can opt
into credit-aware admission **per call**:

```perl
my $accepted = $stream->send_some(
    $bytes, limit_to_credit => 1,
);
```

This limits new accepted bytes to currently available **stream and
connection peer receive credit**, after subtracting bytes already queued
for transport. Bytes belonging to a peer-blocked stream will not fill the
shared queue while other streams can make progress. Ordinary callers do
not need to opt in.

When fewer bytes are accepted, pause writing that Stream. The existing
`$connection->on_stream_activity(sub { ... })` callback wakes advanced
integrations when its flow credit increases or ACKs on other Streams return
shared transmit capacity. Use `next_active_stream_id` to identify which
Streams to retry. These callbacks are independent of HTTP and the event
loop; they are notifications, not instructions to spin until writable.

Neither mode bounds kernel buffers or TLS/QUIC internal packet allocations.
The configured limit concerns Net::QUIC's retained Stream transmit data.

## QUIC DATAGRAM (RFC 9221)

QUIC DATAGRAM carries unreliable, unordered application bytes inside a QUIC
connection.

It is optional and directional. To advertise that this endpoint can receive
DATAGRAM frames:

```perl
transport => {
    max_datagram_frame_size => 65535,
}
```

The default is zero, so existing applications do not enable DATAGRAM
implicitly.

After negotiation:

```perl
if ($connection->can_send_datagram) {
    my $accepted = $connection->send_datagram($bytes);
}
```

`send_datagram` uses one bounded pending slot. It returns false if that slot
is already occupied rather than building an unbounded queue. A true result
means Net::QUIC accepted the bytes for QUIC transmission; it does not mean the
peer received them. Lost DATAGRAMs are not retransmitted.

Pull received payloads with:

```perl
while (defined(my $bytes = $connection->next_received_datagram)) {
    ...
}
```

or register:

```perl
$connection->on_datagram(sub {
    my ($connection, $bytes, $early_data) = @_;
    ...
});
```

The receive fallback queue is bounded. Excess unreliable DATAGRAMs are dropped
and can be observed with `datagram_receive_drops`.

For the current safe payload ceiling:

```perl
my $bytes = $connection->max_datagram_payload_size;
```

That value reflects both the peer's DATAGRAM limit and the current QUIC path
capacity. Higher protocols must subtract their own framing overhead.

A returning client with saved `early_data` state can use QUIC DATAGRAM in
0-RTT when the remembered peer transport parameters permit it. As with all
0-RTT, the application must treat the operation as replayable.

This is raw RFC 9221 transport only. HTTP/3 DATAGRAM flow identifiers,
SETTINGS_H3_DATAGRAM, Capsules, WebTransport, and other application-protocol
semantics belong above Net::QUIC.

## TLS and certificate verification

QUIC always uses TLS 1.3.

Clients verify server certificates by default.

`server_name` is the name checked against the certificate:

```perl
server_name => 'example.com'
```

OpenSSL's normal trust locations are used.

For a private or test CA:

```perl
ca_file => '/path/to/private-ca.pem'
```

adds that PEM file to the trust store.

Net::QUIC does not provide an insecure "skip certificate verification" switch.

Server certificate and key files are loaded when the server is created and
shared by connections accepted by that server.

## Closing streams

A normal stream finish is:

```perl
$stream->finish;
```

This means "I am done sending after the bytes already queued."

On a bidirectional stream, the peer can still send data back.

Abort this endpoint's send side:

```perl
$stream->reset($application_error_code);
```

Stop receiving and ask the peer to stop its send side:

```perl
$stream->stop_sending($application_error_code);
```

The send and receive directions are independent.

For normal code, `finish` is usually what you want. `reset` and
`stop_sending` are abrupt error/abort operations.

## Stream limits

QUIC limits how many streams can be open at once.

Therefore:

```perl
my $stream = $connection->open_bidi_stream;
```

can return `undef`.

That does not mean the connection failed. It means the peer has not currently
given this endpoint permission to open another stream.

To wait for more stream credit:

```perl
$connection->on_stream_available(sub {
    my ($connection, $type) = @_;

    return if $type ne 'bidi';

    my $stream = $connection->open_bidi_stream;
    return if !defined $stream;

    ...
});
```

## Advanced protocol-engine integration

Ordinary applications do not need these APIs.

A protocol engine that needs tighter control over receive consumption,
acknowledgement progress, Stream wake-ups, or transmit memory can use:

```text
Connection:
    on_stream_activity
    next_active_stream_id
    send_buffer_limit
    send_buffered_bytes

Stream:
    next_data_chunk
    consume
    acked_offset
    send_some
    send_buffered_bytes
```

The ordinary `send`, `finish`, and `next_data` API remains unchanged.

These are transport primitives only. Net::QUIC does not add HTTP/3 or other
application-protocol semantics.

See the `Net::QUIC::Connection` and `Net::QUIC::Stream` POD for the exact
contracts.

## Closing a connection

Start a normal application close with:

```perl
$connection->close;
```

or:

```perl
$connection->close($application_error_code);
```

QUIC does not destroy the connection immediately. It has a short
closing/draining period so late packets can still be handled correctly.

`$connection->closed` becomes true when the connection no longer needs UDP or
timer service.

## Connection errors

Remote errors and normal close conditions are stored on the Connection rather
than thrown as ordinary Perl exceptions.

Inspect them with:

```perl
my $info = $connection->close_info;
```

For example:

```perl
{
    type      => 'application',
    initiator => 'peer',
    code      => 0,
}
```

Possible `type` values include:

```text
application
transport
tls
certificate
handshake
idle
drop
```

Programming mistakes, invalid local configuration, allocation failures, and
internal failures still throw Perl exceptions.

## QUIC v1 and v2

Net::QUIC supports QUIC v1 and QUIC v2.

Clients use v1 by default:

```perl
version => 1
```

To start directly with v2:

```perl
version => 2
```

A server can prefer v2:

```perl
preferred_version => 2
```

while still accepting compatible v1 clients.

Normally you do not need to care which version was used.

For diagnostics:

```perl
$connection->client_chosen_version;
$connection->version;
```

## Session resumption

A completed client connection can receive an opaque TLS session ticket:

```perl
my $ticket = $connection->session_ticket;
```

A later connection can offer it:

```perl
session_ticket => $ticket
```

After the handshake:

```perl
if ($connection->resumed) {
    ...
}
```

reports whether TLS actually resumed the old session.

Treat the ticket as opaque bytes. Net::QUIC remembers the QUIC version inside
the opaque value.

If the ticket is expired or no longer valid, the connection falls back to a
normal full handshake.

## 0-RTT / early data

0-RTT lets a returning client send some application data before the new
handshake has completed.

It is optional because early data can be replayed by the network.

Only use 0-RTT for operations that are safe to repeat.

Save the opaque state:

```perl
my $state = $connection->early_data_state;
```

Use it on a later client:

```perl
early_data => $state
```

The server must also allow it:

```perl
accept_early_data => 1
```

Check what happened:

```perl
$connection->early_data_status;
```

Possible values are:

```text
none
pending
accepted
rejected
```

If early data is rejected, the ordinary handshake can still succeed. Open new
streams after the connection becomes ready and resend only operations that are
safe to repeat.

On a server, a received stream can be checked with:

```perl
$stream->early_data;
```

## Retry and NEW_TOKEN

A server can require clients to prove that they can receive packets at their
source address before it allocates full connection state:

```perl
validate_address => 1
```

A new client may then receive a QUIC Retry packet and repeat its Initial.

After a validated connection succeeds, the server can give the client a
NEW_TOKEN.

The client exposes that opaque token as:

```perl
my $token = $connection->address_token;
```

A later connection can reuse it:

```perl
address_token => $token
```

A valid token can avoid another Retry round trip.

Most applications can simply cache this opaque value alongside the session
ticket if they want faster returning connections.

## Network migration

QUIC can keep a connection alive when the client's local network path changes.

For example, an application can move an established connection to another
local address:

```perl
$connection->migrate($new_packed_local_address);
```

Net::QUIC tests the new path before switching to it.

Check progress with:

```perl
$connection->path_validation_status;
```

Possible values are:

```text
none
validating
succeeded
failed
aborted
```

If validation fails, the old working path remains active.

The current path is available from:

```perl
my $path = $connection->path;
```

## Preferred server address

A server can tell the client that another server address is preferred:

```perl
preferred_address => $packed_server_address
```

The client tests that path before switching.

The UDP adapter must actually be able to send and receive on the advertised
address.

## PMTU discovery

Different network paths can safely carry different UDP packet sizes.

Net::QUIC automatically discovers a useful packet size for the current path.

For diagnostics:

```perl
my $bytes = $connection->path_max_udp_payload_size;
```

A new path starts at QUIC's safe 1200-byte baseline. Discovery can raise that
value when larger packets work.

Most applications do not need to manage this themselves.

## ECN

ECN is an IP feature that can report congestion without requiring a packet to
be dropped.

ECN support is optional at the adapter boundary.

An ECN-aware adapter can pass the two-bit codepoint from the received IP packet:

```perl
$driver->receive($bytes, $local, $peer, $ecn);
```

For outgoing traffic it applies:

```perl
$datagram->ecn;
```

to the IP header.

The wire values are:

```text
0   Not-ECT
1   ECT(1)
2   ECT(0)
3   CE
```

If the adapter does not provide ECN metadata, the normal three-argument
`receive` form remains valid.

Net::QUIC tests whether ECN works correctly on the path and automatically
stops using it when necessary.

## Lower-level Endpoint API

`Net::QUIC::Endpoint` is the engine underneath Driver.

It exposes:

```text
receive_datagram
next_datagram
timeout_after
handle_timeout
```

Use Endpoint directly only when you want to own output draining,
backpressure handling, and timer replacement yourself.

Most event-loop integrations are simpler with Driver.

## Examples

The `examples/` directory contains complete integrations for:

- Linux::Event
- AnyEvent
- IO::Async
- IO::Async with Future::AsyncAwait
- Mojo::IOLoop
- EV
- a small IO::Select echo server

All client examples implement the same Driver contract.

See `examples/README.md` for commands and notes.

## What is not part of Net::QUIC

Net::QUIC is the base QUIC transport.

It does not define:

- HTTP/3
- an RPC protocol
- an application message format
- a web framework

Raw RFC 9221 QUIC DATAGRAM transport is supported, but HTTP/3 DATAGRAM,
Capsules, WebTransport, and other application-protocol meanings are not defined
by Net::QUIC.

qlog and advanced congestion-control configuration remain separate tooling or
advanced configuration work.

## Native implementation

Net::QUIC uses ngtcp2 for the native QUIC transport and Picotls/OpenSSL for
QUIC TLS.

Most application code does not need to know those APIs.

The important public boundary remains:

```text
event loop / UDP
        |
        v
Net::QUIC::Driver
        |
        v
Net::QUIC::Connection
        |
        v
Net::QUIC::Stream
```

## License

MIT
