Unblock::HTTP1

Tests CPAN License

Unblock::HTTP1 is a non-blocking HTTP/1.0 and HTTP/1.1 protocol engine for Perl.

It parses requests, serializes responses, handles HTTP message boundaries, streaming bodies, keep-alive, trailers, Upgrade, and CONNECT.

It does not open sockets, do DNS or TLS, run an event loop, or manage connection pools. It can be hosted by IO::Async, AnyEvent, Linux::Event, Mojolicious, native transport code, or another event framework.

Installation

cpanm Unblock::HTTP1

Version 0.11 requires Perl 5.16 and Uniform::HTTP 0.06 or newer. Linux::Event, IO::Async and AnyEvent are optional host frameworks.

Start here

The three application classes are:

A transaction uses the shared Unblock vocabulary: respond(), write(), end(), send_informational(), state(), error(), is_complete(), is_cancelled(), is_error(), and is_terminal().

The easiest server response:

$tx->respond(
    status => 200,
    body   => "Hello World!\n",
);

The easiest client request:

$client->request(
    method    => 'GET',
    target    => '/',
    authority => 'example.test',
    on_response => sub {
        my ($tx, $response) = @_;
        print $response->status, "\n";
    },
);

These calls still create and use canonical Uniform::HTTP::Request and Uniform::HTTP::Response messages internally. Existing Uniform objects continue to work directly:

$client->request($request);
$tx->respond($response);

Connect to an event framework

Create one Client or Server per framework connection:

$self->{http1} = Unblock::HTTP1::Server->new(
    transport  => $self,
    on_request => $on_request,
);

The framework object implements only three host methods:

unblock_send($bytes)      # accept complete output into framework queue
unblock_finish()          # graceful close after queued output
unblock_abort($reason)    # immediate failure close

The framework feeds incoming events to the engine:

$http->input($bytes);
$http->input_eof;
$http->transport_error($reason);

The engine delivers output automatically. A delayed response from a timer or database callback also triggers output without another framework read event.

An integration that exposes congestion returns false from unblock_send() AFTER accepting the whole buffer, and later calls $http->resume_output. If the framework simply queues all output, it may return undef.

The engine holds a weak reference to the host. The framework owns its sockets, output queue, and loop.

Full working server and client examples:

The examples separate adapter code from application code. The Linux::Event server demonstrates the same host API using a Stream subclass and a separate application subclass. Linux::Event is not a required dependency.

For complete instructions, read:

perldoc Unblock::HTTP1::Integration

The repository also includes docs/INTEGRATION.md and docs/COOKBOOK.md.

Connection and message behavior

A Client queues requests serially on one connection; it does not silently enable HTTP/1 pipelining. A Server can receive pipelined request bytes and process them in HTTP order.

A server's on_request runs after the request head has arrived. on_body delivers decoded request body fragments. on_request_end runs when the full request, including trailers, is complete.

For a streaming response:

$tx->respond(status => 200, stream_body => 1);
$tx->write($chunk);
$tx->end($final_bytes);

write() and end() accept bytes even when they return false for backpressure. Use on_drain to resume producing data.

A clean read EOF is not the same as fatal transport failure. After a fully received request, the server can still send a delayed response, then close gracefully.

A 101 Upgrade response or successful CONNECT switches away from HTTP. take_remainder() returns already-received bytes belonging to the next protocol. An attached server invokes on_switch after the full outgoing HTTP handshake has been accepted into the framework's write queue.

Manual and native integration

For advanced consumers, the original low-level output interface remains:

$http->input($bytes);
while ($http->want_write) {
    $transport->write($http->output);
}

This is for engines built WITHOUT an attached transport. Do not mix manual output draining and automatic output ownership.

XS-backed transports can use Unblock::HTTP1::NativeABI v1 to pass borrowed native input. The ABI is input-side and remains transport neutral. It supports the same HTTP callbacks and can construct canonical Uniform messages through Uniform::HTTP 0.06's native FastPath.

The installed C header is:

Unblock/HTTP1/NativeABI/unblock_http1_native_abi.h

ABI discovery:

Unblock::HTTP1::NativeABI::definition()
Unblock::HTTP1::NativeABI::native_include_dir()
Unblock::HTTP1::NativeABI::header_path()
Unblock::HTTP1::NativeABI::c_header()

Limits

Defaults:

max_head_size              65536 bytes
max_headers                100
max_chunk_extension_size   16384 bytes
high_water                 65536 bytes
low_water                  32768 bytes

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

License

MIT License.