Linux::Event::HTTP

Linux::Event::HTTP integrates Linux::Event's native sockets, TLS and timers with the event-loop-neutral Unblock::HTTP protocol engines. Server and Client expose Unblock transactions and Uniform messages. There is no compatibility layer for the previous Linux::Event::HTTP API.

The distribution provides HTTP/1 servers and clients over TCP and TLS, real Unblock transactions and Uniform messages, bounded buffering, streaming reception/responses, pooling, cancellation, timers and native borrowed input. Queued streaming uploads and graceful HTTP/1 service shutdown are implemented. Optional HTTP/2 over TLS uses ALPN, multiplexing, flow control and GOAWAY. HTTP/3 Client and combined TCP/UDP Server integration is verified with real QUIC/TLS loopback, timeout, cancellation, congestion and multi-connection tests. HTTP/1 Upgrade and HTTP/2/3 WebSocket and generic CONNECT handoff are verified. HTTP/2 strict h2spec conformance passes with Unblock::HTTP2 0.13 or newer. HTTP/1 throughput was measured against the previous implementation; its trade-offs are documented, not an open requirement for identical requests-per-second performance.

Server

use v5.36;
use Linux::Event::Loop;
use Linux::Event::HTTP::Server;

my $loop = Linux::Event::Loop->new;
my $server = Linux::Event::HTTP::Server->new(
    loop => $loop,
    port => 8080,
    on_request => sub ($tx, $request) {
        $tx->respond(status => 200, body => "Hello\n");
    },
);
$loop->run;

on_headers($tx, $request) runs at headers. on_request runs once after the whole body and trailers complete successfully. To stream an incoming body, register $tx->on_data(sub ($tx, $bytes) { ... }) during on_headers. Otherwise Unblock buffers up to 1 MiB by default. Set http1 => { max_buffered_body_bytes => $bytes } to change that bound.

Stream a response using $tx->respond(status => 200, stream_body => 1, on_drain => sub ($tx) { ... }), followed by write and end. A false write return means the chunk was accepted but the producer must pause until on_drain; never resend that chunk.

loop, listener, host, port, connection_count, pause, resume and close expose service controls. close closes TCP admission; with HTTP/3 enabled it also starts QUIC GOAWAY draining. It does not immediately discard admitted H3 streams. shutdown(grace => 30, on_complete => sub ($server) { ... }) stops accepting, completes admitted requests and responses, and closes any remaining owned sockets after the grace deadline. Completion runs once after all owned connections and transactions finish. It may run synchronously if idle.

Client

use Linux::Event::HTTP::Client;

my $client = Linux::Event::HTTP::Client->new(loop => $loop);
my $op = $client->get('https://example.com/',
    timeout => 15,
    on_response => sub ($tx, $response) {
        say $response->status;
        say $response->body;
    },
    on_error => sub ($object, $error, @protocol_details) { warn "$error\n" },
);
$loop->run;

Without on_data, on_response receives the complete bounded response. Supplying on_data selects streaming automatically: headers trigger on_response, chunks trigger on_data($tx, $bytes), and successful body completion triggers on_end($tx). on_finish runs on terminal cleanup.

The return value is the real Unblock operation. It supports cancel, state, error, is_terminal and transaction; the transaction appears after admission. Timeouts include queueing, connection establishment and reception.

Unblock owns the pools and their limits. pool_stats, pending_count, network_changed and shutdown retain Unblock's semantics. Client shutdown cancels queued work, lets active work finish and retires connections. Stream an upload with stream_body => 1 and on_transaction => sub ($tx) { $tx->write(...); $tx->end }. The callback gets the real transaction when queued work is admitted. It can run synchronously on an available connection; use its argument rather than an operation variable that may not yet have been assigned. There is no second body queue.

HTTPS verifies certificates and the original hostname by default. Use tls => { ca_file => $path } for a private trust root. The Client owns TLS hostname and ALPN selection. Server TLS options are passed as tls => { cert_file => $cert, key_file => $key }.

HTTP/3

Install Net::QUIC >= 0.06 and Unblock::HTTP3 >= 0.14, then enable http3 => 1. A strict Client request can use http_version => '3'. A client may pass quic => { ca_file => $path } for a private CA and uses Linux::Event's asynchronous UDP hostname resolution and Net::QUIC's verification of the original HTTPS origin. The existing Unblock pool owns the operation and H3 multiplexing.

A Server with HTTP3 enabled binds a UDP socket on the same concrete host and port as its TCP listener, using tls => { cert_file => ..., key_file => ... }. quic => { transport => { ... } } forwards QUIC policy to Net::QUIC. The same on_headers and on_request callbacks receive actual H3 transactions and Uniform messages.

Current limits: wildcard UDP binding, H3-only listeners, comprehensive QUIC migration and packet-origin metadata are not supported yet. Graceful H3 shutdown, failure, cancellation, congestion and multi-connection coverage pass their required Stage D integration regressions. See the release notes for supported features and current limitations.

HTTP/2

Install Unblock::HTTP2 >= 0.13 and set http2 => 1 on Client and Server. Server HTTP/2 requires tls => { cert_file => ..., key_file => ... }; ALPN selects HTTP/2 while HTTP/1 stays available by default. Set http1 => 0 to disable it. Cleartext HTTP/2 prior knowledge and h2c are not implemented.

The same request callbacks receive actual HTTP2 Transactions and Uniform messages. Protocol-specific engine tuning belongs in http2 => { ... }. Client http_version => '2' is strict; ordinary requests can negotiate HTTP/1 fallback. Cancelled H2 streams leave sibling streams active. Server shutdown sends GOAWAY and drains admitted streams before its grace deadline.

on_error retains the engine callback arguments; HTTP/2 can supply a third protocol error-code argument. With Perl signatures, use an optional argument or @protocol_details as in the example above.

Implementation and dependencies

Native borrowed HTTP/1 and HTTP/2 input is the default. native_input => 0 selects the ordinary Perl input path for comparisons and diagnostics. Both paths share lifecycle callbacks; the native path adapts the two distinct ABIs and retains the host across callbacks that can close the stream. It contains no HTTP parser. HTTP/2 native input uses its own checked ABI table; output uses the ordinary attached host, respecting congestion until on_drain. Malformed protocol input closes the affected connection without unwinding the reactor.

Current direct runtime requirements: Linux, Perl 5.36+, Linux::Event 0.117, Unblock::HTTP 0.03, Unblock::HTTP1 0.13 and Uniform::HTTP 0.06. These versions are published on CPAN; no development dependency checkout is needed. HTTP2, HTTP3/Net::QUIC and WebSocket remain optional and are loaded only when enabled. Enabling an unavailable engine produces an explicit error. Server construction also checks HTTP1's graceful drain capability before binding a listener. HTTP::CookieJar and Uniform::HTTP::Auth are not direct dependencies of this integration layer. The superseded private HTTP parsers, executors and public Request/Response wrappers have been removed. The earlier implementation remains available from the 0.003 release.

WebSocket compatibility: the separate Linux::Event::WebSocket 0.002 distribution uses the removed HTTP internals and cannot run with HTTP 0.004. Its coordinated 0.003 compatibility release instead uses Unblock::HTTP1 directly, preserves the existing WebSocket client/server API and does not depend on this distribution.

For a dedicated WebSocket application, use Linux::Event::WebSocket 0.003. For WebSocket endpoints within an HTTP application, use this distribution's on_websocket or connect_websocket convenience. Neither requires choosing a WebSocket framing engine. Publish WebSocket 0.003 first, then HTTP 0.004, to avoid breaking existing WebSocket installations.

In a shared-client, single-execution-slot HTTP/1.1 comparison using Benchmark::Web, this rebuild exceeded Mojolicious throughput but trailed Feersum, Hyperman and the original Linux::Event::HTTP significantly. The documented HTTP/1 performance trade-off is accepted rather than treated as an indefinite release blocker; see docs/BENCHMARKING.md for matched workloads and caveats. See release notes and architecture provide the supported contract. Internal development handoffs are kept in Git, not in the packaged distribution.

Development checks

perl Makefile.PL
make
make test
REBUILD_NATIVE_INPUT=0 make test

make test runs t/rebuild/*.t, the new API contract's suite. The prior implementation and its tests are preserved in the 0.003 release.

CI covers Perl 5.36, 5.38, 5.40, 5.42, 5.44, latest and latest threaded. The release notes describe the tested scope. GitHub and CPAN publication are separate maintainer actions.