DeterministicESPAsyncWebServer 1.2.0
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
http_parser.h
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file http_parser.h
6 * @brief Standalone HTTP/1.1 request parser — no transport dependency.
7 *
8 * The parser is a pure byte-stream state machine. It has no knowledge of
9 * ring buffers, TCP PCBs, or FreeRTOS. Feed it bytes one at a time via
10 * `http_parser_feed()` and inspect `HttpReq::parse_state` to know when the
11 * request is ready.
12 *
13 * **State machine**
14 * ```
15 * PARSE_METHOD ──space──────► PARSE_PATH
16 * PARSE_PATH ──space──────► PARSE_VERSION
17 * PARSE_PATH ──'?'────────► PARSE_QUERY
18 * PARSE_QUERY ──space──────► PARSE_VERSION (calls parse_query_params)
19 * PARSE_VERSION ──CR─────────► PARSE_EXPECT_LF
20 * PARSE_EXPECT_LF ──LF─────────► PARSE_HEADER_KEY
21 * PARSE_HEADER_KEY ──':'────────► PARSE_HEADER_VAL
22 * PARSE_HEADER_KEY ──CR─────────► PARSE_EXPECT_BODY_LF (blank line)
23 * PARSE_HEADER_VAL ──CR─────────► PARSE_EXPECT_LF (stores header)
24 * PARSE_EXPECT_BODY_LF ──LF (CL=0)──► PARSE_COMPLETE
25 * PARSE_EXPECT_BODY_LF ──LF (CL>BUF)► PARSE_ENTITY_TOO_LARGE (→ 413)
26 * PARSE_EXPECT_BODY_LF ──LF (else)──► PARSE_BODY
27 * PARSE_BODY ──(all read)──► PARSE_COMPLETE
28 * PARSE_PATH (overflow) ───────────► PARSE_URI_TOO_LONG (→ 414)
29 * Any state + protocol error ──────► PARSE_ERROR (→ 400)
30 * ```
31 *
32 * @author Douglas Quigg (dstroy0)
33 * @date 2026
34 */
35
36#ifndef DETERMINISTICESPASYNCWEBSERVER_HTTP_PARSER_H
37#define DETERMINISTICESPASYNCWEBSERVER_HTTP_PARSER_H
38
39#include "DetWebServerConfig.h"
40#include <Arduino.h>
41
42// ---------------------------------------------------------------------------
43// Parser state enumeration
44// ---------------------------------------------------------------------------
45
46/**
47 * @brief States of the HTTP/1.1 request parser.
48 *
49 * Advance via http_parser_feed(). The application layer inspects this
50 * after each feed call or after draining a complete chunk.
51 */
53{
54 PARSE_METHOD, ///< Reading the HTTP method (GET, POST, …).
55 PARSE_PATH, ///< Reading the URL path component.
56 PARSE_QUERY, ///< Reading the raw query string (after `?`).
57 PARSE_VERSION, ///< Accumulating `HTTP/1.x` — hashed for validation.
58 PARSE_HEADER_KEY, ///< Reading a header field name.
59 PARSE_HEADER_VAL, ///< Reading a header field value.
60 PARSE_EXPECT_LF, ///< Consuming the LF of a header-line CRLF pair.
61 PARSE_EXPECT_BODY_LF, ///< Consuming the LF of the blank-line CRLF.
62 PARSE_BODY, ///< Reading the request body.
63 PARSE_COMPLETE, ///< Full request parsed; ready for dispatch.
64 PARSE_ERROR, ///< Unrecoverable parse failure → 400.
65 PARSE_ENTITY_TOO_LARGE, ///< Content-Length > BODY_BUF_SIZE → 413.
66 PARSE_URI_TOO_LONG ///< Path exceeds MAX_PATH_LEN → 414.
67};
68
69/**
70 * @brief Parsed HTTP protocol version.
71 *
72 * Populated from the request line (`HTTP/1.0` or `HTTP/1.1`) using an FNV-1a
73 * hash accumulated during `PARSE_VERSION`. The application layer may use
74 * this to drive keep-alive semantics: HTTP/1.1 defaults to persistent
75 * connections; HTTP/1.0 defaults to close.
76 */
78{
79 HTTP_UNKNOWN = 0, ///< Version string did not match any known token.
80 HTTP_10, ///< HTTP/1.0 — close semantics by default.
81 HTTP_11 ///< HTTP/1.1 — persistent connection by default.
82};
83
84// ---------------------------------------------------------------------------
85// Data types
86// ---------------------------------------------------------------------------
87
88/** @brief A single HTTP header field (key: value). */
89struct Header
90{
91 char key[MAX_KEY_LEN]; ///< Field name, null-terminated.
92 char val[MAX_VAL_LEN]; ///< Field value, null-terminated.
93};
94
95/** @brief A single parsed query-string parameter. */
97{
98 char key[QUERY_KEY_LEN]; ///< Parameter name, null-terminated.
99 char val[QUERY_VAL_LEN]; ///< Parameter value (empty string if absent).
100};
101
102/**
103 * @brief Fully-parsed HTTP/1.1 request.
104 *
105 * Populated incrementally by http_parser_feed(). Valid for dispatch
106 * only when `parse_state == PARSE_COMPLETE`.
107 *
108 * Call http_parser_reset() to recycle this struct for the next request.
109 */
111{
112 uint8_t slot_id; ///< Transport slot index (set by presentation layer).
113 ParseState parse_state; ///< Current parser state.
114 HttpVersion version; ///< Protocol version parsed from the request line.
115 uint32_t _version_hash; ///< FNV-1a accumulator for version validation (internal).
116
117 char method[8]; ///< HTTP method, null-terminated (max 7: OPTIONS).
118 char path[MAX_PATH_LEN]; ///< URL path, null-terminated; no query string.
119 size_t path_idx; ///< Write cursor into path[].
120
121 char query[MAX_QUERY_LEN]; ///< Raw query string (after `?`).
122 size_t query_idx; ///< Write cursor into query[].
123 QueryParam query_params[MAX_QUERY_PARAMS]; ///< Parsed key=value pairs.
124 uint8_t query_count; ///< Valid entries in query_params[].
125
126 Header headers[MAX_HEADERS]; ///< Captured header fields.
127 uint8_t header_count; ///< Valid entries in headers[].
128 size_t current_token_idx; ///< Write cursor shared by key/value sub-states.
129
130 size_t content_length; ///< Value of Content-Length header (0 if absent).
131 size_t body_bytes_read; ///< Body bytes received (may exceed BODY_BUF_SIZE).
132
133 uint8_t body[BODY_BUF_SIZE + 1]; ///< Stored body bytes, always null-terminated.
134 size_t body_len; ///< Bytes stored in body[] (≤ BODY_BUF_SIZE).
135};
136
137/** @brief Pool of parser contexts, one per transport slot. */
139
140// ---------------------------------------------------------------------------
141// Parser API
142// ---------------------------------------------------------------------------
143
144/**
145 * @brief Reset a parser context to the initial (PARSE_METHOD) state.
146 *
147 * Zeroes all fields and sets `parse_state = PARSE_METHOD`. Call before the
148 * first use, after each completed or failed request, and on connection events.
149 *
150 * @param req Parser context to reset. Must not be null.
151 */
152void http_parser_reset(HttpReq *req);
153
154/**
155 * @brief Feed one byte to the parser state machine.
156 *
157 * Returns immediately without modifying state when `parse_state` is already
158 * `PARSE_COMPLETE`, `PARSE_ERROR`, `PARSE_ENTITY_TOO_LARGE`, or `PARSE_URI_TOO_LONG`.
159 *
160 * @param req Parser context for this request.
161 * @param byte Next byte from the HTTP stream.
162 */
163void http_parser_feed(HttpReq *req, uint8_t byte);
164
165/**
166 * @brief Look up a header value by name (case-insensitive).
167 *
168 * @param req Parsed request.
169 * @param key Header field name (e.g. `"Content-Type"`).
170 * @return Pointer to the null-terminated value, or `nullptr` if not found.
171 */
172const char *http_get_header(const HttpReq *req, const char *key);
173
174/**
175 * @brief Look up a query parameter value by name (case-sensitive).
176 *
177 * @param req Parsed request.
178 * @param key Parameter name.
179 * @return Pointer to the null-terminated value (empty string if `key=` with
180 * no value), or `nullptr` if the key is absent.
181 */
182const char *http_get_query(const HttpReq *req, const char *key);
183
184#endif
User-facing configuration for DeterministicESPAsyncWebServer.
#define MAX_VAL_LEN
Maximum header field-value length.
#define MAX_QUERY_PARAMS
Maximum number of parsed query-string parameters.
#define MAX_QUERY_LEN
Maximum raw query-string length (everything after ?).
#define QUERY_VAL_LEN
Maximum query-parameter value length.
#define BODY_BUF_SIZE
Maximum request body bytes stored in HttpReq::body.
#define MAX_HEADERS
Maximum HTTP headers stored per request.
#define MAX_PATH_LEN
Maximum URL path length (including leading /).
#define MAX_CONNS
Maximum simultaneous TCP connections.
#define QUERY_KEY_LEN
Maximum query-parameter key length.
#define MAX_KEY_LEN
Maximum header field-name length (e.g. "Content-Type").
void http_parser_reset(HttpReq *req)
Reset a parser context to the initial (PARSE_METHOD) state.
const char * http_get_header(const HttpReq *req, const char *key)
Look up a header value by name (case-insensitive).
void http_parser_feed(HttpReq *req, uint8_t byte)
Feed one byte to the parser state machine.
ParseState
States of the HTTP/1.1 request parser.
Definition http_parser.h:53
@ PARSE_BODY
Reading the request body.
Definition http_parser.h:62
@ PARSE_HEADER_VAL
Reading a header field value.
Definition http_parser.h:59
@ PARSE_QUERY
Reading the raw query string (after ?).
Definition http_parser.h:56
@ PARSE_COMPLETE
Full request parsed; ready for dispatch.
Definition http_parser.h:63
@ PARSE_HEADER_KEY
Reading a header field name.
Definition http_parser.h:58
@ PARSE_EXPECT_BODY_LF
Consuming the LF of the blank-line CRLF.
Definition http_parser.h:61
@ PARSE_VERSION
Accumulating HTTP/1.x — hashed for validation.
Definition http_parser.h:57
@ PARSE_URI_TOO_LONG
Path exceeds MAX_PATH_LEN → 414.
Definition http_parser.h:66
@ PARSE_EXPECT_LF
Consuming the LF of a header-line CRLF pair.
Definition http_parser.h:60
@ PARSE_ENTITY_TOO_LARGE
Content-Length > BODY_BUF_SIZE → 413.
Definition http_parser.h:65
@ PARSE_PATH
Reading the URL path component.
Definition http_parser.h:55
@ PARSE_ERROR
Unrecoverable parse failure → 400.
Definition http_parser.h:64
@ PARSE_METHOD
Reading the HTTP method (GET, POST, …).
Definition http_parser.h:54
HttpVersion
Parsed HTTP protocol version.
Definition http_parser.h:78
@ HTTP_UNKNOWN
Version string did not match any known token.
Definition http_parser.h:79
@ HTTP_11
HTTP/1.1 — persistent connection by default.
Definition http_parser.h:81
@ HTTP_10
HTTP/1.0 — close semantics by default.
Definition http_parser.h:80
const char * http_get_query(const HttpReq *req, const char *key)
Look up a query parameter value by name (case-sensitive).
HttpReq http_pool[MAX_CONNS]
Pool of parser contexts, one per transport slot.
A single HTTP header field (key: value).
Definition http_parser.h:90
char val[MAX_VAL_LEN]
Field value, null-terminated.
Definition http_parser.h:92
char key[MAX_KEY_LEN]
Field name, null-terminated.
Definition http_parser.h:91
Fully-parsed HTTP/1.1 request.
QueryParam query_params[MAX_QUERY_PARAMS]
Parsed key=value pairs.
Header headers[MAX_HEADERS]
Captured header fields.
char query[MAX_QUERY_LEN]
Raw query string (after ?).
uint32_t _version_hash
FNV-1a accumulator for version validation (internal).
uint8_t body[BODY_BUF_SIZE+1]
Stored body bytes, always null-terminated.
uint8_t header_count
Valid entries in headers[].
size_t current_token_idx
Write cursor shared by key/value sub-states.
ParseState parse_state
Current parser state.
size_t path_idx
Write cursor into path[].
size_t content_length
Value of Content-Length header (0 if absent).
uint8_t slot_id
Transport slot index (set by presentation layer).
size_t body_len
Bytes stored in body[] (≤ BODY_BUF_SIZE).
HttpVersion version
Protocol version parsed from the request line.
char method[8]
HTTP method, null-terminated (max 7: OPTIONS).
char path[MAX_PATH_LEN]
URL path, null-terminated; no query string.
uint8_t query_count
Valid entries in query_params[].
size_t body_bytes_read
Body bytes received (may exceed BODY_BUF_SIZE).
size_t query_idx
Write cursor into query[].
A single parsed query-string parameter.
Definition http_parser.h:97
char val[QUERY_VAL_LEN]
Parameter value (empty string if absent).
Definition http_parser.h:99
char key[QUERY_KEY_LEN]
Parameter name, null-terminated.
Definition http_parser.h:98