NAME

Uniform::HTTP::Message - Common HTTP message behavior

SYNOPSIS

use Uniform::HTTP::Message;

my $message = Uniform::HTTP::Message->new(
    version => '1.1',
    headers => [
        [ 'Content-Type', 'text/plain' ],
        [ 'Set-Cookie',   'a=1' ],
        [ 'Set-Cookie',   'b=2' ],
    ],
    body => "hello\n",
);

DESCRIPTION

Uniform::HTTP::Message is the common base for Uniform::HTTP::Request and Uniform::HTTP::Response.

It stores HTTP version, headers, trailers, and an optional buffered body. It does not parse HTTP, send data, read streams, or perform network I/O.

Headers and trailers are separate lists preserving duplicate fields, order, original field-name spelling, and value bytes.

Most applications will create Request or Response objects rather than using Message directly.

CONSTRUCTOR

new

my $message = Uniform::HTTP::Message->new(
    version => '1.1',
    headers => [
        [ 'Content-Type', 'text/plain' ],
    ],
    body => 'hello',
);

All arguments are optional.

headers and trailers must be array references containing [ name, value ] pairs. Both default to empty lists.

HEADERS

my $value = $message->header('Content-Type');

Returns the first matching value. Header names are matched case-insensitively.

Set or replace a header with:

$message->header('Content-Type', 'application/json');

When duplicate fields already exist, the setter replaces them with one field.

header_values

my $values = $message->header_values('Set-Cookie');

Returns an array reference containing every matching value in order.

add_header

$message->add_header('Set-Cookie', 'c=3');

Appends one new field without replacing existing fields.

remove_header

$message->remove_header('X-Debug');

Removes every matching field.

header_count

Returns the number of header field occurrences.

header_name

my $name = $message->header_name($index);

Returns the original field name at a zero-based index.

header_value

my $value = $message->header_value($index);

Returns the field value at a zero-based index.

headers_are_lossless

Returns true for canonical Uniform messages because duplicate fields, order, and original field-name spelling are preserved.

Adapters may return false when their native framework cannot preserve all of those details.

TRAILERS

Trailers use the same field rules as headers, but never appear in header lookups. They may be supplied to the constructor:

trailers => [ [ 'Content-Digest', $digest_field_value ] ],

trailer

my $first = $message->trailer('Content-Digest');
$message->trailer('Content-Digest', $digest_field_value);

The getter returns the first matching value or undef. The setter replaces all matches at the first matching position, or appends when absent.

trailer_values

Returns an array reference of all matching values in order, or an empty array when none are present. Values are never comma-joined.

add_trailer

$message->add_trailer('X-Metric', '42');

Appends one field without replacing earlier occurrences.

remove_trailer

Removes every occurrence of the named field.

trailer_count

Returns the number of currently represented trailer field occurrences.

trailer_name

Returns the original field name at the supplied zero-based index, or undef when out of range.

trailer_value

Returns the value at the supplied zero-based index, or undef when out of range. Negative or noninteger indexes throw.

has_trailers

Returns true when at least one trailer field is represented. Omitted trailers and an explicit empty list both return false. On an incomplete message, false does not mean no fields can arrive later.

trailers_are_lossless

Returns true for canonical objects. Adapters report false when trailer fields, order, spelling, or value bytes were lost. This is independent of header fidelity.

Adapters with unavailable trailers return undef from all trailer getters, including trailer_count, has_trailers, and trailer_values; they return false from trailers_are_lossless and trailers_are_mutable. This differs from a known empty section.

The HTTP sender is responsible for checking which fields may be trailers and whether its selected framing supports them. Uniform checks generic field syntax without interpreting field-specific rules.

BODY

body

my $bytes = $message->body;

Returns the buffered body, or undef when no buffered body is present.

Set a buffered body with:

$message->body($bytes);

Calling body() never reads a socket, filehandle, callback, or streaming source.

has_buffered_body

Returns true when body() contains a buffered body. An empty string still counts as a buffered body.

VERSION

version

my $version = $message->version;

Returns values such as 1.1, 2, or 3, without an HTTP/ prefix. undef is suitable for an application-created neutral message. A sender can choose a version separately without changing or unfreezing that object. Received messages should report their actual version when known.

Set or clear it with:

$message->version('2');
$message->version(undef);

MESSAGE STATE

is_mutable

Returns true when at least one section can be changed; false means all data mutations are forbidden. Canonical objects begin fully mutable. After a section freeze, use the specific capability before changing that section.

initial_is_mutable

Reports whether initial headers, version, and Request/Response metadata can be changed. False after freeze_initial() or freeze().

body_is_mutable

Reports whether a complete buffered body can be supplied or replaced. False after freeze(). Adapters may report false for a streaming-only body.

trailers_are_mutable

Reports whether trailer fields can be changed. False after freeze_trailers() or freeze().

freeze_initial

$message->mark_incomplete->freeze_initial;
# External receipt continues; Uniform itself performs no I/O.
$message->add_trailer('Content-Digest', $digest_field_value);
$message->mark_complete->freeze;

Locks only initial headers, version, and Request/Response metadata. Body and trailers remain editable. It is idempotent and never thaws a fully frozen object.

freeze_trailers

Locks just the trailer fields, including an empty section. It is idempotent.

freeze

$message->freeze;

Freezes all data in a canonical Uniform object, including body and trailers. After this, all data setters throw an exception.

freeze() only changes the local object. It does not send headers, commit a framework response, or perform I/O.

is_complete

Returns true when the whole message is known to be complete, including any trailers. A buffered body alone does not establish completeness.

Canonical objects begin complete. An adapter may return undef when its framework cannot determine completeness yet.

mark_incomplete

Marks a canonical message incomplete.

mark_complete

Marks a canonical message complete.

These two helpers are useful when a detached Uniform object is following externally managed streaming progress. They change only completeness, even after full freeze. Neither helper freezes nor thaws any section. Complete objects remain editable unless explicitly frozen.

Freeze and completeness helpers belong to canonical objects. Adapters need only report native state; they are not required to provide these helpers.

BYTE STRINGS

Message values are byte strings. Uniform::HTTP does not guess a character encoding.

Header and trailer names must be valid HTTP tokens. Field values reject prohibited control bytes. Body bytes remain opaque.

SEE ALSO

Uniform::HTTP, Uniform::HTTP::Request, Uniform::HTTP::Response.

The full adapter contract is documented in docs/MESSAGE-SPEC.md.

AUTHOR

Joshua S. Day <HAX@cpan.org>

LICENSE

This software is available under the MIT License.