NAME

Unblock::HTTP1::Transaction - One HTTP/1 request/response exchange

SYNOPSIS

Server side:

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

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

Client side:

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

RECEIVE AND FINISH CALLBACKS

$tx->on_data(sub {
    my ($tx, $bytes) = @_;
    consume_chunk($bytes);
});

Register during the incoming-header callback (server on_request or client on_response). This selects streaming for this transaction. Only decoded HTTP body bytes are passed; content encodings such as gzip are not decoded. No full body is accumulated. Registering after body delivery starts or ends, registering twice, or combining this callback with on_body is an error. Client request() also accepts on_data before the request is dispatched.

$tx->on_finish(sub {
    my ($tx) = @_;
    record_outcome($tx->state, $tx->error);
});

Runs once after the applicable completion or error callbacks for a terminal complete, cancelled, or error transaction. The state is committed before the callback. Cancellation alone does not call on_error. A completed incoming request can still have an unfinished response; it is not terminal until both sides required by the engine have finished.

Client request() also accepts on_finish. Register it before the transaction becomes terminal. Duplicate and late registration are errors. An exception from this terminal observer is ignored so cleanup and other transactions can continue. Earlier callbacks keep the engine's exception policy, but cannot suppress this notification.

DESCRIPTION

A Transaction represents one HTTP/1 request and its response.

Applications do not construct Transaction objects directly. A client gets one from $client->request(). A server receives one in on_request.

The same class is used on both sides:

Client Transaction

Owns one outgoing request and the incoming response.

For a streaming request body, write() and end() send request body bytes.

Server Transaction

Owns one incoming request and the outgoing response.

respond() starts the final response. For a streaming response body, write() and end() send response body bytes.

A Transaction does not own the socket or event loop. It delegates wire work to its owning Client or Server connection.

MESSAGE ACCESSORS

request

my $request = $tx->request;

Returns the canonical Uniform::HTTP::Request for this exchange.

On the client this is the request supplied to, or constructed by, Client->request().

On the server this is the parsed request delivered by on_request.

response

my $response = $tx->response;

Returns the canonical Uniform::HTTP::Response once a final response exists.

Before that point it returns undef.

On a client, the response becomes available when the final response head is received.

On a server, it becomes available after respond() starts the final response.

Informational responses do not replace the final response() value.

SERVER RESPONSE METHODS

These methods are meaningful on a server Transaction.

respond

Concise form:

$tx->respond(
    status  => 200,
    headers => [
        [ 'Content-Type' => 'text/plain' ],
    ],
    body => "hello\n",
);

Existing-object form:

$tx->respond($response);

Starts the final response and returns the Transaction.

The concise form constructs a canonical Uniform::HTTP::Response internally.

respond() may be called before the complete request body has arrived. This is useful for early rejection or any response which does not depend on the full request body.

A Transaction can have only one final response.

Informational status codes other than 101 must use send_informational() instead.

For a streaming response:

$tx->respond(
    status      => 200,
    stream_body => 1,
    on_drain    => sub {
        produce_more();
    },
);

The response head is sent immediately and body data is supplied later with write() and end().

on_drain is valid only with stream_body => 1.

Calling respond() on a client Transaction is an error.

send_informational

Concise form:

$tx->send_informational(
    status  => 103,
    headers => [
        [ Link => '</style.css>; rel=preload' ],
    ],
);

Existing-object form:

$tx->send_informational($response);

Sends one informational response and returns the Transaction.

The status must be from 100 through 199, except 101. A 101 response is a final protocol-switch response and uses respond().

More than one informational response may be sent before the final response.

HTTP/1.0 clients cannot receive informational responses through this method.

Calling send_informational() on a client Transaction is an error.

STREAMING BODY METHODS

write

my $ok = $tx->write($bytes);

Adds one chunk of a streaming body.

On a client Transaction, this writes an outgoing request body.

On a server Transaction, this writes an outgoing response body after respond(stream_body => 1).

The supplied bytes are accepted even when the return value is false.

A true return means the output path remains writable.

A false return means backpressure is active. The producer should stop creating more body data and resume from its on_drain callback.

write() is valid only for a transaction created or started with stream_body => 1.

The engine applies HTTP transfer framing. Applications provide body bytes, not chunk framing.

When Content-Length is explicitly declared, writing more than that length is an error.

end

my $ok = $tx->end;
my $ok = $tx->end($last_bytes);

Finishes a streaming body.

$last_bytes, when supplied, are accepted as the final body bytes before the stream is ended.

On a client this finishes the request body.

On a server this finishes the response body.

For chunked HTTP/1.1 output, end() writes the final chunk and any configured trailers.

For an explicit Content-Length, ending before exactly that many bytes have been written is an error.

After end() the local streaming body is complete and additional write() or end() calls are invalid.

The boolean return has the same backpressure meaning as write().

CANCELLATION

cancel

$tx->cancel;

Cancels the Transaction and returns it.

HTTP/1 transactions share connection ordering and framing state, so cancellation closes the HTTP/1 connection rather than attempting to skip an arbitrary exchange in place.

On a client, queued requests which depended on that connection are failed.

Calling cancel() again after the Transaction is terminal is harmless.

Cancellation is distinct from an error. A cancelled Transaction has state cancelled and error() remains undef.

STATE

state

my $state = $tx->state;

Returns one of:

active
complete
cancelled
error

active means the exchange is still in progress.

complete means both sides required by the exchange have finished.

cancelled means cancel() terminated the exchange.

error means the exchange failed.

error

my $error = $tx->error;

Returns the transaction error string when state() is error.

Returns undef for active, complete, and cancelled Transactions.

is_complete

if ($tx->is_complete) { ... }

True only when state() is complete.

is_cancelled

if ($tx->is_cancelled) { ... }

True only when state() is cancelled.

is_error

if ($tx->is_error) { ... }

True only when state() is error.

is_terminal

if ($tx->is_terminal) { ... }

True for complete, cancelled, or error.

False only while the Transaction is active.

CLIENT LIFECYCLE

For a normal buffered client request:

  1. $client->request() creates the Transaction and queues or sends the request.

  2. on_informational may run zero or more times.

  3. on_response runs when the final response head arrives.

  4. on_body runs zero or more times.

  5. The response completes, and on_complete runs once the local request side is also complete.

For stream_body => 1, the local side remains incomplete until end() or until an early final response makes further request-body output impossible.

SERVER LIFECYCLE

For a normal server request:

  1. The Transaction is created and passed to on_request.

  2. on_body may run zero or more times as request body bytes arrive.

  3. on_request_end runs when the full request and trailers are complete.

  4. The application starts the final response with respond().

  5. If the response streams, write() and end() finish the local response side.

  6. The Transaction becomes complete when both the request and response sides are done.

The response may begin before steps 2 and 3 finish.

BACKPRESSURE

Backpressure is advisory to the producer, not rejection of the bytes passed to write() or end().

When either the HTTP queue or an attached transport becomes congested:

my $ok = $tx->write($bytes);

returns false.

The producer should stop until on_drain runs.

With a manual integration, draining Client->output() or Server->output() below the low-water mark can cause on_drain.

With an attached transport, the adapter calls $http->resume_output() when the transport becomes writable again.

PROTOCOL SWITCH

A server Transaction can cause a switch by sending a valid 101 response or a successful CONNECT response.

A client Transaction can receive the corresponding switch response.

The Transaction completes its HTTP lifecycle before the connection reports is_switched(). With an attached transport, a server's on_switch is delayed until the complete outgoing handshake has been accepted by the host.

Bytes after the HTTP boundary belong to the next protocol and are obtained from the owning Client or Server with take_remainder().

SEE ALSO

Unblock::HTTP1::Integration, Unblock::HTTP1, Unblock::HTTP1::Client, Unblock::HTTP1::Server, Uniform::HTTP::Request, Uniform::HTTP::Response