DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
h2_frame.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 h2_frame.h
6 * @brief HTTP/2 binary framing (RFC 9113 sec 4 + sec 6).
7 *
8 * Every HTTP/2 frame is a 9-byte header (24-bit length, 8-bit type, 8-bit flags, 1 reserved bit
9 * + 31-bit stream id) followed by a type-specific payload. This module parses that header and
10 * builds the frames the server sends (SETTINGS + its ACK, WINDOW_UPDATE, RST_STREAM, GOAWAY,
11 * PING ACK, HEADERS, DATA) and reads a SETTINGS payload. Pure and host-tested; no I/O.
12 *
13 * @author Douglas Quigg (dstroy0)
14 * @date 2026
15 */
16
17#ifndef DETERMINISTICESPASYNCWEBSERVER_H2_FRAME_H
18#define DETERMINISTICESPASYNCWEBSERVER_H2_FRAME_H
19
20#include "ServerConfig.h"
21
22#if DETWS_ENABLE_HTTP2
23
24#include <stddef.h>
25#include <stdint.h>
26
27/** @brief The client connection preface that opens every HTTP/2 connection (RFC 9113 sec 3.4). */
28#define H2_PREFACE "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
29#define H2_PREFACE_LEN 24
30#define H2_FRAME_HEADER_LEN 9
31
32/** @brief Frame types (RFC 9113 sec 6). */
33// Frame type octet (RFC 9113 sec 6): wire values compared against a parsed type byte, so integer
34// constants in a namespacing struct.
35struct H2FrameType
36{
37 static constexpr uint8_t H2_DATA = 0x0;
38 static constexpr uint8_t H2_HEADERS = 0x1;
39 static constexpr uint8_t H2_PRIORITY = 0x2;
40 static constexpr uint8_t H2_RST_STREAM = 0x3;
41 static constexpr uint8_t H2_SETTINGS = 0x4;
42 static constexpr uint8_t H2_PUSH_PROMISE = 0x5;
43 static constexpr uint8_t H2_PING = 0x6;
44 static constexpr uint8_t H2_GOAWAY = 0x7;
45 static constexpr uint8_t H2_WINDOW_UPDATE = 0x8;
46 static constexpr uint8_t H2_CONTINUATION = 0x9;
47};
48
49/** @brief Frame flags (meaning is per-type; RFC 9113 sec 6). */
50#define H2_FLAG_END_STREAM 0x01 ///< DATA / HEADERS
51#define H2_FLAG_ACK 0x01 ///< SETTINGS / PING
52#define H2_FLAG_END_HEADERS 0x04 ///< HEADERS / CONTINUATION / PUSH_PROMISE
53#define H2_FLAG_PADDED 0x08 ///< DATA / HEADERS / PUSH_PROMISE
54#define H2_FLAG_PRIORITY 0x20 ///< HEADERS
55
56/** @brief SETTINGS parameter identifiers (RFC 9113 sec 6.5.2; the 16-bit wire id). */
57struct H2Setting
58{
59 static constexpr uint16_t H2_SETTINGS_HEADER_TABLE_SIZE = 0x1;
60 static constexpr uint16_t H2_SETTINGS_ENABLE_PUSH = 0x2;
61 static constexpr uint16_t H2_SETTINGS_MAX_CONCURRENT_STREAMS = 0x3;
62 static constexpr uint16_t H2_SETTINGS_INITIAL_WINDOW_SIZE = 0x4;
63 static constexpr uint16_t H2_SETTINGS_MAX_FRAME_SIZE = 0x5;
64 static constexpr uint16_t H2_SETTINGS_MAX_HEADER_LIST_SIZE = 0x6;
65};
66
67/** @brief Error codes (RFC 9113 sec 7; the 32-bit wire code). */
68struct H2Error
69{
70 static constexpr uint32_t H2_NO_ERROR = 0x0;
71 static constexpr uint32_t H2_PROTOCOL_ERROR = 0x1;
72 static constexpr uint32_t H2_INTERNAL_ERROR = 0x2;
73 static constexpr uint32_t H2_FLOW_CONTROL_ERROR = 0x3;
74 static constexpr uint32_t H2_SETTINGS_TIMEOUT = 0x4;
75 static constexpr uint32_t H2_STREAM_CLOSED = 0x5;
76 static constexpr uint32_t H2_FRAME_SIZE_ERROR = 0x6;
77 static constexpr uint32_t H2_REFUSED_STREAM = 0x7;
78 static constexpr uint32_t H2_CANCEL = 0x8;
79 static constexpr uint32_t H2_COMPRESSION_ERROR = 0x9;
80 static constexpr uint32_t H2_CONNECT_ERROR = 0xa;
81 static constexpr uint32_t H2_ENHANCE_YOUR_CALM = 0xb;
82 static constexpr uint32_t H2_INADEQUATE_SECURITY = 0xc;
83 static constexpr uint32_t H2_HTTP_1_1_REQUIRED = 0xd;
84};
85
86/** @brief A parsed frame header. */
87struct H2FrameHeader
88{
89 uint32_t length; ///< payload length (24-bit)
90 uint8_t type; ///< frame type
91 uint8_t flags; ///< frame flags
92 uint32_t stream_id; ///< stream identifier (reserved bit cleared)
93};
94
95/** @brief The six settings we track, with RFC defaults after h2_settings_defaults(). */
96struct H2Settings
97{
98 uint32_t header_table_size; ///< default 4096
99 uint32_t enable_push; ///< default 1
100 uint32_t max_concurrent_streams; ///< default "unlimited" (0xFFFFFFFF here)
101 uint32_t initial_window_size; ///< default 65535
102 uint32_t max_frame_size; ///< default 16384
103 uint32_t max_header_list_size; ///< default "unlimited" (0xFFFFFFFF here)
104};
105
106/** @brief Parse the 9-byte frame header at @p buf (needs >= 9 bytes). */
107bool h2_parse_header(const uint8_t *buf, size_t len, H2FrameHeader *out);
108
109/** @brief Write a 9-byte frame header. @return 9, or 0 on overflow / length too large. */
110size_t h2_write_header(uint8_t *out, size_t cap, uint32_t length, uint8_t type, uint8_t flags, uint32_t stream_id);
111
112/** @brief Fill @p s with the RFC 9113 default settings values. */
113void h2_settings_defaults(H2Settings *s);
114/** @brief Apply a SETTINGS payload (list of id:16 + value:32) to @p s. @return false if malformed. */
115bool h2_parse_settings(const uint8_t *payload, size_t len, H2Settings *s);
116
117// --- Frame builders (write a complete frame including its header) -----------------------------
118
119/** @brief SETTINGS frame from @p n (id, value) pairs (stream 0). */
120size_t h2_build_settings(uint8_t *out, size_t cap, const uint16_t *ids, const uint32_t *vals, size_t n);
121/** @brief Empty SETTINGS with the ACK flag (stream 0). */
122size_t h2_build_settings_ack(uint8_t *out, size_t cap);
123/** @brief WINDOW_UPDATE with a 31-bit @p increment on @p stream_id. */
124size_t h2_build_window_update(uint8_t *out, size_t cap, uint32_t stream_id, uint32_t increment);
125/** @brief RST_STREAM with @p error on @p stream_id. */
126size_t h2_build_rst_stream(uint8_t *out, size_t cap, uint32_t stream_id, uint32_t error);
127/** @brief GOAWAY (stream 0) with @p last_stream_id and @p error (no debug data). */
128size_t h2_build_goaway(uint8_t *out, size_t cap, uint32_t last_stream_id, uint32_t error);
129/** @brief PING with the ACK flag echoing the 8 opaque bytes (stream 0). */
130size_t h2_build_ping_ack(uint8_t *out, size_t cap, const uint8_t opaque[8]);
131/** @brief HEADERS frame carrying an HPACK @p block on @p stream_id (END_HEADERS always set). */
132size_t h2_build_headers(uint8_t *out, size_t cap, uint32_t stream_id, const uint8_t *block, size_t block_len,
133 bool end_stream);
134/** @brief DATA frame carrying @p data on @p stream_id. */
135size_t h2_build_data(uint8_t *out, size_t cap, uint32_t stream_id, const uint8_t *data, size_t data_len,
136 bool end_stream);
137
138#endif // DETWS_ENABLE_HTTP2
139#endif // DETERMINISTICESPASYNCWEBSERVER_H2_FRAME_H
User-facing configuration for DeterministicESPAsyncWebServer.