NAME

Net::QUIC::Connection - one QUIC connection

DESCRIPTION

A Connection is one secure QUIC relationship with a peer.

It can contain many independent Net::QUIC::Stream objects.

Application protocol code normally works with Connection and Stream. UDP socket and timer handling normally stays in Net::QUIC::Driver.

A client Driver has one Connection.

A server Driver can create many Connections.

BASIC USE

Get the client Connection:

my $connection = $driver->connection;

Wait for the handshake:

return if !$connection->ready;

Open a bidirectional Stream:

my $stream = $connection->open_bidi_stream;

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

Accept Streams opened by the peer:

while (my $stream = $connection->next_stream) {
    ...
}

Close normally:

$connection->close;

HANDSHAKE AND SAVED STATE

ready

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

Returns true after the QUIC/TLS handshake is ready for normal application work.

A server can expose a Connection before this becomes true.

client_chosen_version

my $version = $connection->client_chosen_version;

Returns 1 or 2 for the QUIC version used by the client's first Initial packet.

This can differ from "version" when Compatible Version Negotiation switches the connection to another supported version during the handshake.

version

my $version = $connection->version;

Returns the final negotiated QUIC version, 1 or 2.

It can be undef before version negotiation is complete.

Most applications do not need to branch on this value.

session_ticket

my $ticket = $connection->session_ticket;

Returns the newest opaque TLS session ticket received by the client, or undef if none is available.

A later client can pass it as:

session_ticket => $ticket

to attempt a faster resumed TLS handshake.

Treat the ticket as opaque bytes.

If it is expired or otherwise unusable, the connection falls back to a normal full handshake.

resumed

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

Returns true when the completed TLS handshake actually resumed a previous session.

address_token

my $token = $connection->address_token;

Returns the newest opaque QUIC address-validation token received by the client, or undef if none is available.

A later client can pass it as:

address_token => $token

A valid token can let a server with address validation enabled accept the new connection without another Retry round trip.

Treat the token as opaque bytes.

early_data_state

my $state = $connection->early_data_state;

Returns one opaque value that a client can save for a later 0-RTT attempt.

A later client supplies it as:

early_data => $state

0-RTT allows some application data to be sent before the new handshake completes.

0-RTT data can be replayed. Only use it for operations that are safe to repeat.

early_data_status

my $status = $connection->early_data_status;

Returns one of:

none
pending
accepted
rejected

pending means this client is attempting 0-RTT and does not yet know whether the server accepted it.

If 0-RTT is rejected, the normal TLS handshake can still complete.

Streams created for the rejected early-data attempt become invalid. Open new Streams after ready and resend only operations that are safe to repeat.

OPENING STREAMS

open_bidi_stream

my $stream = $connection->open_bidi_stream;

Opens a bidirectional Stream.

Both endpoints can send on a bidirectional Stream.

Returns undef when the peer's current bidirectional stream limit has been reached.

That is normal QUIC flow control, not a Connection failure.

open_uni_stream

my $stream = $connection->open_uni_stream;

Opens a unidirectional Stream.

Only this endpoint can send application bytes on a locally opened unidirectional Stream.

Returns undef when the peer's current unidirectional stream limit has been reached.

on_stream_available

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

Registers a callback for new local stream credit.

$type is:

bidi

or:

uni

Use this when open_bidi_stream or open_uni_stream returned undef and the application wants to try again when the peer allows another Stream.

PEER-CREATED STREAMS

next_stream

while (my $stream = $connection->next_stream) {
    ...
}

Returns the next Stream opened by the peer.

Returns undef when no new peer-created Stream is waiting.

A Stream can be bidirectional or unidirectional. Use:

$stream->can_send
$stream->can_receive

when code needs to handle either kind.

on_stream_activity

$connection->on_stream_activity(sub {
    my ($connection) = @_;
    ...
});

Registers an advanced protocol-engine wake-up callback.

The callback runs when one or more Streams have meaningful new activity, such as received data, FIN, acknowledgement progress, RESET_STREAM, STOP_SENDING, stream close, or a newly peer-created Stream.

The callback does not receive one event object per transport event. Activity is coalesced by Stream.

Drain the changed Stream IDs with "next_active_stream_id".

Pass undef to disable activity tracking:

$connection->on_stream_activity(undef);

Activity tracking is opt-in so ordinary applications pay no queueing cost.

next_active_stream_id

while (defined(my $id = $connection->next_active_stream_id)) {
    ...
}

Returns the next Stream ID with coalesced activity.

Returns undef when the activity queue is empty.

A protocol engine should normally drain this queue when "on_stream_activity" wakes it. State such as received data, acknowledgement offsets, reset codes, and STOP_SENDING codes remains available on the corresponding Stream object.

QUIC DATAGRAM

RFC 9221 QUIC DATAGRAM carries unreliable application bytes inside a Connection.

This is different from Net::QUIC::Datagram. That class represents one UDP packet that the event-loop adapter must send. The methods in this section represent application DATAGRAM frames carried inside QUIC.

DATAGRAM support is directional.

This endpoint advertises its receive limit with the Endpoint transport option:

transport => {
    max_datagram_frame_size => 65535,
}

The default is zero, which disables receiving QUIC DATAGRAM.

can_send_datagram

if ($connection->can_send_datagram) {
    ...
}

Returns true when the peer advertised a nonzero QUIC DATAGRAM receive limit.

can_receive_datagram

if ($connection->can_receive_datagram) {
    ...
}

Returns true when this endpoint advertised a nonzero QUIC DATAGRAM receive limit.

peer_max_datagram_frame_size

my $bytes = $connection->peer_max_datagram_frame_size;

Returns the peer's advertised max_datagram_frame_size transport parameter.

It can be undef before peer transport parameters are available.

local_max_datagram_frame_size

my $bytes = $connection->local_max_datagram_frame_size;

Returns this endpoint's advertised max_datagram_frame_size.

max_datagram_payload_size

my $bytes = $connection->max_datagram_payload_size;

Returns a conservative maximum application payload that can currently fit in one QUIC DATAGRAM on the active path.

It accounts for both the peer's DATAGRAM frame limit and the current QUIC path UDP payload ceiling. The value can change after PMTU discovery or path migration.

A protocol layered above QUIC must subtract its own framing bytes from this value.

send_datagram

my $accepted = $connection->send_datagram($bytes);

Queues one unreliable QUIC DATAGRAM.

Returns true when Net::QUIC copied the payload into its bounded pending slot. Returns false when that one pending slot is already occupied.

A true return value does not mean the peer received the DATAGRAM. Lost QUIC DATAGRAMs are not retransmitted.

The method throws if the peer did not negotiate DATAGRAM receive support, the payload is too large, the Connection is closing, or the handshake is not ready and no saved 0-RTT state is active.

A client using saved "early_data_state" may send DATAGRAM in 0-RTT when the saved peer transport parameters permit it. 0-RTT data can be replayed, so only send operations that are safe to repeat and check "early_data_status" after the handshake.

next_received_datagram

my $bytes = $connection->next_received_datagram;

or:

my ($bytes, $early_data) = $connection->next_received_datagram;

Returns the next received QUIC DATAGRAM, or undef when none is waiting.

In scalar context it returns only the payload bytes. In list context it also returns a boolean that is true when the DATAGRAM was received as 0-RTT early data.

A zero-length DATAGRAM is returned as an empty string. Test the result with defined, not truth.

Received DATAGRAMs are kept in a bounded native fallback queue until pulled or dispatched. Additional unreliable DATAGRAMs are dropped when that queue is full.

on_datagram

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

Registers a callback for received QUIC DATAGRAM payloads.

Callbacks run after native QUIC packet processing returns. They are not called from inside the ngtcp2 receive callback.

The callback drains the same queue used by "next_received_datagram".

Disable it with:

$connection->on_datagram(undef);

datagram_receive_drops

my $count = $connection->datagram_receive_drops;

Returns the number of received QUIC DATAGRAM payloads dropped because the bounded receive queue was full.

BOUNDED TRANSMIT BUFFERING

These methods are for advanced producers that need a hard bound on Stream data retained by Net::QUIC.

send_buffer_limit

$connection->send_buffer_limit(4 * 1024 * 1024);

Enables a connection-wide limit on retained Stream transmit bytes.

The limit counts Stream data that is queued for sending plus data already sent but still retained until peer acknowledgement.

Use:

my $limit = $connection->send_buffer_limit;

to read the current limit.

It returns undef when bounded transmit mode is disabled.

Disable the limit with:

$connection->send_buffer_limit(undef);

A new limit cannot be smaller than the amount of Stream data already retained.

When a limit is enabled, ordinary "send" in Net::QUIC::Stream remains all-or-nothing. It throws instead of exceeding the configured bound. Advanced producers should use "send_some" in Net::QUIC::Stream.

send_buffered_bytes

my $bytes = $connection->send_buffered_bytes;

Returns the total number of Stream data bytes currently retained for transmit across this Connection.

This includes sent data that still has to remain available until it is acknowledged.

NETWORK PATH

A QUIC Connection can survive some network-address changes without being recreated.

Most applications do not need to inspect path state during ordinary use.

path

my $path = $connection->path;

Returns the active network path:

{
    local => $packed_local_address,
    peer  => $packed_peer_address,
}

migrate

$connection->migrate($new_packed_local_address);

Client only.

Starts migration to another local address while keeping the same QUIC Connection.

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

If validation fails, the previous working path stays active.

path_validation_status

my $status = $connection->path_validation_status;

Returns:

none
validating
succeeded
failed
aborted

This is the simple path-validation view.

path_validation

my $info = $connection->path_validation;

Returns the detailed current or most recent path-validation state.

The hash includes at least:

status

and, when a validation has occurred:

local
peer

It also reports whether the validation was associated with a server preferred address or a fresh address-validation token.

path_max_udp_payload_size

my $bytes = $connection->path_max_udp_payload_size;

Returns the currently discovered maximum UDP payload size for the active path.

A new path starts at QUIC's safe 1200-byte baseline. The native QUIC engine can raise this value when larger packets work.

This is mainly diagnostic. Applications normally do not need to manage PMTU discovery themselves.

CLOSING

close

$connection->close;

or:

$connection->close($application_error_code);

Starts a normal application-level QUIC close.

The default application error code is zero.

Closing is not immediate destruction. QUIC has a short closing/draining period so late packets can still be handled correctly.

closed

if ($connection->closed) {
    ...
}

Returns true when the Connection no longer needs network or timer service.

CLOSE AND ERROR INFORMATION

close_info

my $info = $connection->close_info;

Returns undef while no close or failure has been recorded.

Otherwise it returns a hash describing the outcome.

For example, a normal peer application close can look like:

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

type can be:

application
transport
tls
certificate
handshake
idle
drop

initiator is:

local
peer

code is the application error code, QUIC transport error code, or TLS alert code as appropriate.

A peer transport error can also include frame_type.

A locally detected native transport failure can include native_error.

Remote protocol errors, certificate failures, handshake failures, idle timeout, and normal closes are Connection outcomes rather than ordinary Perl exceptions.

Local programming mistakes, invalid configuration, allocation failure, and internal implementation failures still throw Perl exceptions.

DRIVER NOTIFICATION

Connections obtained through Net::QUIC::Driver automatically notify Driver when application operations create new transport work.

For example:

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

do not require a separate service or pump call.

SEE ALSO

Net::QUIC

Net::QUIC::Driver

Net::QUIC::Stream

Net::QUIC::Endpoint