Current section

Files

Jump to
erlang_h1 include h1.hrl
Raw

include/h1.hrl

%% Copyright (c) 2026 Benoit Chesneau.
%% SPDX-License-Identifier: Apache-2.0
%%
%% h1.hrl - HTTP/1.1 library common definitions
-ifndef(H1_HRL).
-define(H1_HRL, 1).
%% ----------------------------------------------------------------------------
%% Default parser limits
%% ----------------------------------------------------------------------------
-define(H1_MAX_METHOD_SIZE, 16).
-define(H1_MAX_URI_SIZE, 8192).
-define(H1_MAX_HEADER_NAME_SIZE, 256).
-define(H1_MAX_HEADER_VALUE_SIZE, 8192).
-define(H1_MAX_HEADERS, 100).
-define(H1_MAX_CHUNK_SIZE, 1048576). %% 1 MB
-define(H1_MAX_BODY_SIZE, 8388608). %% 8 MB
-define(H1_MAX_LINE_LENGTH, 16384). %% hackney default
-define(H1_MAX_HEADER_BLOCK_SIZE, 65536). %% total header-block bytes (DoS guard)
-define(H1_MAX_EMPTY_LINES, 10).
-define(H1_MAX_CHUNK_SIZE_HEX, 16). %% 64-bit max, way past any legit chunk
-define(H1_MAX_CHUNK_EXT_SIZE, 4096). %% cap chunk-extension scan (DoS guard)
%% ----------------------------------------------------------------------------
%% HTTP versions
%% ----------------------------------------------------------------------------
-define(HTTP_1_0, {1, 0}).
-define(HTTP_1_1, {1, 1}).
%% ----------------------------------------------------------------------------
%% Parser state record
%%
%% Streaming dual-mode parser (server + client). Feed bytes via
%% h1_parse_erl:execute/2 and consume events one at a time.
%% ----------------------------------------------------------------------------
-record(h1_parser, {
%% Mode
type = auto :: auto | request | response,
%% Current phase
state = on_first_line :: on_first_line | on_header | on_body
| on_trailers | on_junk | done,
%% Unconsumed input
buffer = <<>> :: binary(),
%% Parsed metadata
version = undefined :: undefined | {non_neg_integer(), non_neg_integer()},
method = undefined :: undefined | binary(),
status = undefined :: undefined | 100..599,
partial_headers = [] :: [{binary(), binary()}],
%% Fast-path header values (lowercased where noted).
%% `clen' transitions: undefined -> N (first valid value)
%% N -> N (duplicate same value)
%% N -> conflict (differing value)
%% _ -> bad_int (unparsable)
clen = undefined :: undefined | non_neg_integer() | bad_int | conflict,
te = undefined :: undefined | binary(), %% lowercased
connection = undefined :: undefined | binary(), %% lowercased
ctype = undefined :: undefined | binary(), %% lowercased
upgrade = undefined :: undefined | binary(), %% lowercased
expect = undefined :: undefined | binary(), %% lowercased
%% Framing decision computed at end-of-headers.
body_framing = undefined :: undefined | no_body
| {content_length, non_neg_integer()}
| chunked | close_delimited,
%% Body decoder state
body_state = waiting :: waiting | done
| {stream, fun(), term(), fun()},
body_read = 0 :: non_neg_integer(),
%% Limits
max_line_length = ?H1_MAX_LINE_LENGTH :: pos_integer(),
max_empty_lines = ?H1_MAX_EMPTY_LINES :: non_neg_integer(),
max_header_name_size = ?H1_MAX_HEADER_NAME_SIZE :: pos_integer(),
max_header_value_size = ?H1_MAX_HEADER_VALUE_SIZE :: pos_integer(),
max_headers = ?H1_MAX_HEADERS :: pos_integer(),
max_body_size = ?H1_MAX_BODY_SIZE :: pos_integer() | infinity,
max_header_block_size = ?H1_MAX_HEADER_BLOCK_SIZE :: pos_integer(),
%% Counters
empty_lines = 0 :: non_neg_integer(),
header_count = 0 :: non_neg_integer(),
%% Total bytes consumed in the current header (or trailer) block.
header_bytes = 0 :: non_neg_integer()
}).
-endif.