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.
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
- kind: Parser_Kind
- 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
- error: Parse_Error
- state: Parser_State
- 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: Ext_State
- 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