Http module

Parser

structSource

An incremental HTTP/1.x parser for one direction of a connection. Start it with parser_init, feed it with parse in pieces of any size, and release it with parser_free. The result does not depend on how the bytes were split.

The head (head), the trailers and the error are valid as the events say, and the strings in them point into the parser's own buffers, so copy what must outlive the next message. One parser handles the messages of one connection one after another; it is not safe to use from two threads. The buffers come from context.allocator. The fields under Internal are the parser's state and are not for callers.

ExampleOpen in playground ↗
parser: Parser;
parser_init(*parser, .REQUEST);
defer parser_free(*parser);

text := "POST /items HTTP/1.1\r\nHost: example.com\r\nContent-Length: 2\r\n\r\nhi";
data: [] u8 = .{ text.count, text.data };

body: [..] u8;
defer array_free(body);

while true {
    used, event := parse(*parser, data);
    data = advance(data, used);

    if event.kind == .HEAD {
        assert(parser.head.method == .POST);
        assert(parser.head.target == "/items");
        assert(parser.head.content_length == 2);
    }

    if event.kind == .BODY array_add(*body, ..event.data);
    if event.kind == .END || event.kind == .NEED_MORE || event.kind == .ERROR break;
}

assert(string.{ body.count, body.data } == "hi");

Fields

limits: Limits
response_to: Method
For a response parser: the method of the request being answered. HEAD and CONNECT change how the response body is framed (RFC 9112 6.3), so set it before the response head arrives.
head: Head
valid after a HEAD event, until the next message begins
trailers: [] Header_Field
valid after END of a chunked body, until the next message begins
started: bool
Internal.
in_headers: bool
the start line has been read
bytes: [..] u8
the head: start line and header lines, CRLFs included
line_start: int
spans: [..] Span
list: [..] Header_Field
skipped_lines: int
method_start: int
The start line, as offsets into bytes.
method_end: int
target_start: int
target_end: int
reason_start: int
reason_end: int
version: Version
status: int
method: Method
content_length_seen: bool
What the headers seen so far say (note_header).
content_length: s64
encoding_seen: bool
chunked: bool
other_coding: bool
host_count: int
close: bool
keep_alive_token: bool
upgrade_token: bool
upgrade_header: bool
expect_continue: bool
remaining: s64
bytes left in the Content-Length body or the current chunk
total: s64
decoded body bytes so far
chunk_size: s64
chunk_digits: int
chunk_ext: int
bytes of the extension so far
ext_token: int
bytes of the name or value being read
offset: int
bytes of this message consumed before the current call
tbytes: [..] u8
trailers
tline_start: int
tspans: [..] Span
tlist: [..] Header_Field

See also