# Unblock::HTTP3

[![CPAN version](https://badge.fury.io/pl/Unblock-HTTP3.svg)](https://metacpan.org/dist/Unblock-HTTP3)
[![CPANTS Kwalitee](https://cpants.cpanauthors.org/dist/Unblock-HTTP3.svg)](https://cpants.cpanauthors.org/dist/Unblock-HTTP3)
[![CI](https://github.com/haxmeister/perl-Unblock-HTTP3/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/haxmeister/perl-Unblock-HTTP3/actions/workflows/test.yml)
[![HTTP/3 interop](https://github.com/haxmeister/perl-Unblock-HTTP3/actions/workflows/interop.yml/badge.svg?branch=main)](https://github.com/haxmeister/perl-Unblock-HTTP3/actions/workflows/interop.yml)
[![License](https://img.shields.io/cpan/l/Unblock-HTTP3.svg)](https://github.com/haxmeister/perl-Unblock-HTTP3/blob/main/LICENSE)
[![Perl](https://img.shields.io/badge/perl-5.20%2B-blue.svg)](https://www.perl.org/)

Unblock::HTTP3 is a non-blocking HTTP/3 protocol engine for Perl.

It handles HTTP/3 framing, QPACK, request streams, trailers, informational
responses, Extended CONNECT, HTTP Datagrams, Capsules, priorities, graceful
shutdown, and replay-aware 0-RTT state.

It does not own UDP sockets, TLS, timers, or an event loop. Net::QUIC owns the
QUIC transport.

```text
application or HTTP library
        |
  Uniform::HTTP messages
        |
   Unblock::HTTP3
        |
      Net::QUIC
        |
        UDP
```

## Installation

From CPAN:

```text
cpanm Unblock::HTTP3
```

Unblock::HTTP3 0.13 requires:

```text
Perl            5.20+
Alien::nghttp3  0.01+
Net::QUIC       0.05+
Uniform::HTTP   0.06+
```

Version 0.13 adds bounded buffered reception and per-transaction
`on_data` / `on_finish` lifecycle callbacks for the unified Unblock::HTTP
layer while preserving standalone Client and Server use. It fixes retention
of completed callback-delivered Transactions by keeping callback
notifications weak, compacting expired weak slots without changing their
ownership, and retaining strong references for pull-only notifications.
Net::QUIC 0.05 remains the minimum dependency for credit-limited QUIC
output and targeted shared-capacity wakeups. The native consumer ABI
remains version 1.

Version 0.12 fixed NativeABI version metadata for PAUSE indexing.

Version 0.11 adds the same named-field Client request, Transaction response,
and informational response forms as the HTTP1 and HTTP2 engines. Existing
Uniform::HTTP objects remain fully supported.

Version 0.10 intentionally broke the earlier 0.03 Perl API. Client and Server
now use `new()`, final responses use `respond()`, and the old
`Connection->client()`, `Connection->server()`, and `send_response()`
entry points are removed. There are no compatibility aliases.

## Start here

The public API is built around three objects:

- `Unblock::HTTP3::Client` - one client HTTP/3 connection
- `Unblock::HTTP3::Server` - one server HTTP/3 connection
- `Unblock::HTTP3::Transaction` - one request and response exchange

The common application vocabulary intentionally matches the other Unblock HTTP
engines:

```text
Client->new
Server->new
request()
respond()
write()
end()
send_informational()
```

HTTP/3 protocol concepts keep their HTTP/3 names.

Exact canonical `Uniform::HTTP::Request` and `Uniform::HTTP::Response`
objects use the Uniform::HTTP native fast path. Uniform-compatible adapters and
subclasses use the portable Perl message contract.

## Client

```perl
use Unblock::HTTP3::Client;

my $client = Unblock::HTTP3::Client->new(
    quic => $quic,
);

$client->start;

my $transaction = $client->request(
    method    => 'GET',
    target    => '/',
    scheme    => 'https',
    authority => 'example.com',

    on_response => sub {
        my ($transaction, $response) = @_;
        print $response->status, "\n";
    },

    on_body => sub {
        my ($transaction, $response, $bytes) = @_;
        process_bytes($bytes);
    },

    on_complete => sub {
        my ($transaction) = @_;
        print "done\n";
    },
);
```

Many Transactions can be active on one Client at the same time.

The existing pull interface remains available through `next_transaction()`
and `next_informational()` for integrations that prefer polling. Pull-only
notifications retain a strong Transaction reference until retrieved.
When callbacks deliver a Transaction, its optional pull-queue notification
is weak: it remains accessible while the application or active engine holds
the Transaction, but it does not retain completed response bodies indefinitely.
Callback handlers should retain the Transaction themselves if they need it
after completion. The Server follows the same rule for `on_request`.

## Server

```perl
use Unblock::HTTP3::Server;

my $server = Unblock::HTTP3::Server->new(
    quic => $quic,

    on_request => sub {
        my ($transaction, $request) = @_;

        $transaction->respond(
            status => 200,
            body   => "hello\n",
        );
    },
);

$server->start;
```

`on_body` receives request body chunks. `on_request_end` runs after the
request body and trailers have completed.

## Event-loop integration

Use Net::QUIC::Driver to connect an event loop's UDP socket and timer to
QUIC. Unblock::HTTP3 attaches to each ready Net::QUIC::Connection; it does not
replace the Driver or accept raw TCP-style input bytes.

See [the installed integration guide](lib/Unblock/HTTP3/Integration.pm) and
[the application API guide](docs/APPLICATION-API.md). Complete Linux::Event
programs are in [server](examples/linux-event-server.pl) and
[client](examples/linux-event-client.pl). The transport examples require
Linux::Event 0.117 or later, but it is not a distribution dependency.

## Incoming body reception

Incoming request and response bodies are buffered by default, with a finite
64 MiB per-transaction limit. Configure `max_buffered_body_bytes` on the
Client or Server to reduce this limit. The complete body is installed on the
same Uniform message object before the body-completion callback; an empty
completed body is the empty string. Header callbacks run before body arrival.

Register `on_data` at request creation or during the incoming-header callback
to receive chunks without retaining a second full body. For example:

```perl
my $tx = $client->request(
    method    => 'GET',
    target    => '/',
    scheme    => 'https',
    authority => 'example.com',
    on_data   => sub {
        my ($tx, $bytes) = @_;
        process_bytes($bytes);
    },
    on_finish => sub {
        my ($tx) = @_;
        report_terminal_state($tx->state, $tx->error);
    },
);
```

On a Server, call `$tx->on_data(sub { ... })` inside `on_request` to select
streaming for that incoming request. Do not combine `on_data` with the legacy
`on_body` callback. Registration after body delivery has started is rejected.
`on_finish` runs once after the transaction reaches complete, cancelled, or
error state; it is not merely an incoming-body completion event. A receive
limit failure resets the affected HTTP/3 stream without failing unrelated
transactions. Consult the Client, Server and Transaction POD for details.

## Streaming bodies

Buffered bodies can live directly on the Uniform message object.

For a streaming client request:

```perl
my $transaction = $client->request(
    $request,
    stream_body => 1,
    on_drain => sub {
        my ($transaction) = @_;
        produce_more($transaction);
    },
);

$transaction->write($chunk);
$transaction->end($last_chunk);
```

A streaming server response uses the same Transaction API:

```perl
$transaction->respond(
    $response,
    stream_body => 1,
    on_drain => sub {
        my ($transaction) = @_;
        produce_more($transaction);
    },
);

$transaction->write($chunk);
$transaction->end;
```

For advanced HTTP/3 body control, `Body::Stream` and `Body::Reader` remain
available. They expose explicit receive credit, cancellation, and body-specific
callbacks without changing the common Transaction API.

## Informational responses

A server can send a 1xx response before the final response:

```perl
$transaction->send_informational(status => 103);

$transaction->respond($final_response);
```

HTTP/3 does not use status 101.

## Transaction state

The common lifecycle methods are:

```text
state
error
is_complete
is_cancelled
is_error
is_terminal
```

HTTP/3-specific reset and STOP_SENDING details remain available separately on
the Transaction.

## CONNECT, Capsules, and Datagrams

Ordinary CONNECT and generic Extended CONNECT are supported.

Extended CONNECT uses the Uniform `protocol` field. Higher-level modules
decide what a protocol such as WebSocket, WebTransport, or MASQUE means.

An Extended CONNECT Transaction can use the RFC 9297 Capsule Protocol through:

```perl
my $capsules = $transaction->capsules;
```

RFC 9297 HTTP Datagrams use Net::QUIC DATAGRAM support. Enable them on the
Client or Server with:

```perl
enable_http_datagrams => 1
```

A client marks a request as using Datagrams with:

```perl
my $transaction = $client->request(
    $request,
    datagrams => 1,
);
```

The Transaction provides `send_datagram()`, `next_datagram()`,
`on_datagram()`, and `max_datagram_payload_size()`.

## Priority and ORIGIN

RFC 9218 priority state is available through `Transaction->priority()`.

Servers can advertise RFC 9412 origins with the `origins` constructor option.
Clients read the received set with `peer_origins()`.

## 0-RTT

Net::QUIC owns QUIC/TLS early-data state. Unblock::HTTP3 owns the remembered
HTTP/3 SETTINGS needed to validate early requests.

Save the QUIC and HTTP/3 state from the same successful session:

```perl
my $quic_state = $quic->early_data_state;
my $h3_state   = $client->peer_settings_state;
```

An early client request must opt in explicitly:

```perl
my $transaction = $client->request(
    $request,
    early_data => 1,
);
```

0-RTT can be replayed. Unblock::HTTP3 does not automatically retry a rejected
early request.

## Native integrations

XS event frameworks and HTTP libraries can use
`Unblock::HTTP3::NativeABI`.

Native integrations can discover the installed ABI with:

```text
definition
native_include_dir
header_path
c_header
```

ABI version 1 accepts exact `Unblock::HTTP3::Client`,
`Unblock::HTTP3::Server`, and `Unblock::HTTP3::Transaction` objects.
Adapters and subclasses use the portable Perl API.

The native ABI does not expose libnghttp3 internals and does not take ownership
of the QUIC transport.

See `docs/NATIVE-ABI.md`.

## What Unblock::HTTP3 does not own

Unblock::HTTP3 does not own:

- UDP sockets
- DNS
- TLS
- QUIC packet processing
- congestion control
- retransmission
- connection migration
- timers
- event loops
- connection pools
- redirects
- cookies
- authentication policy
- retry policy
- WebSocket, WebTransport, or MASQUE semantics

Those belong to Net::QUIC, the event-loop adapter, the application, or a
higher-level protocol module.

## Testing

The normal suite uses real kernel UDP sockets, TLS, QUIC, and HTTP/3.

CI tests released dependencies on several Perl versions and validates the built
distribution. Separate interoperability tests cover both client and server
directions against independent HTTP/3 implementations.

## More documentation

- `Unblock::HTTP3::Client`
- `Unblock::HTTP3::Server`
- `Unblock::HTTP3::Transaction`
- `Unblock::HTTP3::Body::Stream`
- `Unblock::HTTP3::Body::Reader`
- `Unblock::HTTP3::NativeABI`
- `docs/ARCHITECTURE.md`
- `docs/NATIVE-ABI.md`
- `docs/RFC-COMPLIANCE.md`

## License

MIT.
