Unblock::HTTP1

Tests CPAN License

Portable, non-blocking HTTP/1 for Perl.

Unblock::HTTP1 is an HTTP/1 protocol engine. It handles HTTP bytes and message boundaries, but it does not open sockets or choose an event loop.

The same engine can be used with any reliable ordered byte stream.

The normal API accepts Perl byte strings. XS-backed transports can optionally use Unblock::HTTP1::NativeABI to feed borrowed native buffers directly. The transport keeps ownership of the buffer and Unblock reports how much of it was consumed. On this path, received requests and responses are constructed as canonical Uniform::HTTP objects through the Uniform::HTTP 0.06 native FastPath.

What it does

Unblock::HTTP1 provides:

It does not provide sockets, DNS, TLS, connection pools, redirects, cookies, authentication, proxy policy, WebSocket framing, HTTP/2, or HTTP/3.

Install

cpan Unblock::HTTP1

Client

use Uniform::HTTP::Request;
use Unblock::HTTP1::Client;

my $client = Unblock::HTTP1::Client->new;

$client->request(
    Uniform::HTTP::Request->new(
        method    => 'GET',
        target    => '/',
        authority => 'example.com',
    ),
    on_response => sub {
        my ($tx, $response) = @_;
        print $response->status, "\n";
    },
    on_body => sub {
        my ($tx, $response, $bytes) = @_;
        print $bytes;
    },
);

Feed bytes received from your transport:

$client->input($bytes);

Take generated bytes and send them through your transport:

while ($client->want_write) {
    my $bytes = $client->output;
    $transport->write($bytes);
}

When the transport reaches EOF:

$client->input_eof;

A Client serializes requests on one connection. It does not silently enable HTTP/1 pipelining.

Server

use Uniform::HTTP::Response;
use Unblock::HTTP1::Server;

my $server = Unblock::HTTP1::Server->new(
    on_request => sub {
        my ($tx, $request) = @_;

        $tx->respond(
            Uniform::HTTP::Response->new(
                status => 200,
                body   => "hello\n",
            )
        );
    },
);

Feed received bytes with input() and drain generated bytes with output(), just as with the client.

Request bodies arrive through on_body. on_request_end runs after the complete request body and any trailers have arrived.

Streaming bodies

Pass stream_body when the body length is not known yet:

my $tx = $client->request(
    $request,
    stream_body => 1,
);

$tx->write($chunk);
$tx->end($last_chunk);

HTTP/1.1 uses chunked framing when needed. HTTP/1.0 streaming requires an explicit Content-Length.

The same Transaction write()/end() API is used for streaming server responses.

Upgrade and CONNECT

A 101 response or successful CONNECT ends HTTP framing on the connection.

The engine then reports:

$engine->is_switched

Bytes already read after the HTTP boundary are preserved:

my $bytes = $engine->take_remainder;

The caller can pass those bytes to the next protocol implementation.

Backpressure

The engine has configurable high-water and low-water output limits.

write() returns false when the output queue reaches the high-water mark. on_drain fires after it falls below the low-water mark.

Limits

Default limits are:

maximum HTTP head:             65536 bytes
maximum header fields:         100
maximum chunk extension bytes: 16384 per message
output high water:             65536 bytes
output low water:              32768 bytes

The limits can be changed when constructing a Client or Server.

Message objects

Unblock::HTTP1 uses Uniform::HTTP directly:

Uniform::HTTP::Request
Uniform::HTTP::Response

It does not define competing HTTP message classes.

Received messages preserve ordered duplicate fields, trailers, exact request targets, and the received HTTP version.

Integration

Unblock::HTTP1 does not require a particular event loop or framework.

An adapter only needs to:

  1. pass received bytes to input()
  2. drain output() while want_write() is true
  3. call input_eof() when the transport closes
  4. stop feeding HTTP bytes when is_switched() becomes true

See docs/INTEGRATION.md for the transport boundary.

Protocol status

The HTTP/1 protocol engine is complete for its declared scope and has cross-platform CI coverage on Linux, macOS, and Windows, including Perl 5.16.

See docs/PROTOCOL_STATUS.md for the detailed protocol checklist.

License

MIT License.