NAME
Net::QUIC - QUIC transport for Perl
DESCRIPTION
Net::QUIC gives Perl applications secure QUIC connections and byte streams.
If QUIC is new to you, the useful model is:
one Connection
|
+-- Stream
+-- Stream
+-- Stream
A Connection is one secure relationship with a peer.
A Stream is one reliable ordered sequence of bytes inside that Connection.
Net::QUIC handles the QUIC protocol, TLS 1.3, retransmission, flow control, timers, connection IDs, migration, and the other transport details.
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 provides connections and byte streams. Your application decides what the bytes mean.
START HERE
Most applications use three classes:
Net::QUIC::Driver
|
+-- Net::QUIC::Connection
|
+-- Net::QUIC::Stream
Net::QUIC::Driver connects Net::QUIC to UDP and a timer.
Net::QUIC::Connection represents one QUIC connection.
Net::QUIC::Stream sends and receives application bytes.
Net::QUIC::Endpoint is the lower-level engine under Driver. Most applications do not need to use Endpoint directly.
SYNOPSIS
Create a client Driver:
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) = @_;
...
},
set_timeout => sub {
my ($seconds) = @_;
...
},
);
my $connection = $driver->connection;
$driver->start;
After the handshake is ready:
return if !$connection->ready;
my $stream = $connection->open_bidi_stream;
if ($stream) {
$stream->send("hello\n");
$stream->finish;
}
Read streams opened by the peer:
while (my $stream = $connection->next_stream) {
while (defined(my $bytes = $stream->next_data)) {
handle_bytes($bytes);
}
}
QUIC STREAMS ARE BYTE STREAMS
A QUIC Stream is an ordered sequence of bytes.
It is not a sequence of application messages.
One call to:
$stream->send($message);
does not guarantee one matching next_data result on the peer.
If the application needs messages, add framing above the Stream, such as a newline, fixed record size, or length prefix.
WHAT DRIVER NEEDS
Driver is the recommended event-loop integration layer.
The event loop owns:
UDP I/O
one replaceable one-shot timer
The adapter supplies two callbacks:
send
set_timeout
and reports four events:
start
receive
timeout
writable
Conceptually:
UDP transport ready
-> $driver->start
UDP packet received
-> $driver->receive($bytes, $local, $peer)
requested timer fired
-> $driver->timeout
UDP output became writable again
-> $driver->writable
That is the ordinary Driver contract.
There is no application-visible QUIC pump loop.
Driver automatically drains pending output, updates the timer, pauses for UDP backpressure, and resumes after writable.
The optional fourth argument to receive is the packet's ECN codepoint:
$driver->receive($bytes, $local, $peer, $ecn);
Adapters that do not support ECN can keep using the three-argument form.
See Net::QUIC::Driver for the full adapter contract.
LOCAL AND PEER ADDRESSES
local and peer are packed IPv4 or IPv6 socket addresses.
local must be the actual local address used by that packet.
Wildcard bind addresses such as:
0.0.0.0
::
are not concrete QUIC paths.
A server may bind its UDP socket to a wildcard address, but the adapter must recover the real destination address for each received packet.
If the event system cannot do that, bind the QUIC socket to one concrete local address instead.
CONNECTIONS
A client Driver has one Connection:
my $connection = $driver->connection;
A server Driver can create many:
while (my $connection = $driver->next_connection) {
...
}
Wait for the handshake before ordinary application work:
if ($connection->ready) {
...
}
Open streams with:
$connection->open_bidi_stream;
$connection->open_uni_stream;
These methods can return undef when the peer's current stream limit has been reached. That is normal flow control, not a failed Connection.
CLOSING AND ERRORS
Start a normal close with:
$connection->close;
Remote close conditions and protocol/TLS outcomes are available through:
my $info = $connection->close_info;
Local programming mistakes and invalid local configuration still throw Perl exceptions.
TLS
QUIC always uses TLS 1.3.
Clients verify server certificates by default.
server_name is the DNS name or IP address expected in the certificate.
For a private CA, use:
ca_file => '/path/to/private-ca.pem'
Net::QUIC does not provide an insecure skip-verification switch.
ADVANCED QUIC FEATURES
Net::QUIC also supports:
QUIC v1 and QUIC v2
session resumption
optional 0-RTT early data
Retry and NEW_TOKEN address validation
active connection migration
server preferred addresses
PMTU discovery
ECN
These features are documented in Net::QUIC::Connection, Net::QUIC::Endpoint, and the main README.
Applications that only need ordinary reliable streams do not need to use most of them directly.
INSTALLATION
Install from CPAN:
cpanm Net::QUIC
Net::QUIC uses Alien::ngtcp2 for its native dependencies.
A normal installation does not require you to separately configure ngtcp2, Picotls, or OpenSSL.
The event-loop modules shown in examples/ are optional example dependencies.
EXAMPLES
The distribution includes examples for:
Linux::Event
AnyEvent
IO::Async
Mojo::IOLoop
EV
There is also a small IO::Select echo server.
See examples/README.md.
LOW-LEVEL ENDPOINT
Net::QUIC::Endpoint is available when an integration deliberately wants to manage the lower-level cycle itself:
receive_datagram
next_datagram
timeout_after
handle_timeout
Most event-loop adapters are simpler with Driver.
NATIVE INFORMATION
These methods are mainly diagnostic.
ngtcp2_version
my $version = Net::QUIC::ngtcp2_version();
Returns the version string reported by the linked ngtcp2 library.
ngtcp2_version_num
my $version_num = Net::QUIC::ngtcp2_version_num();
Returns ngtcp2's numeric version value.
crypto_backend
my $backend = Net::QUIC::crypto_backend();
Returns picotls.
Application code normally does not need to branch on the native TLS implementation.
NATIVE DEPENDENCY
Net::QUIC requires Alien::ngtcp2 0.03 or newer.
Alien::ngtcp2 supplies the tested ngtcp2 and Picotls build.
SEE ALSO
AUTHOR
Joshua S. Day
COPYRIGHT AND LICENSE
This software is Copyright (c) 2026 by Joshua S. Day.
This is free software, licensed under:
The MIT (X11) License