DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
h2_frame.cpp
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.cpp
6 * @brief HTTP/2 binary framing - implementation. See h2_frame.h.
7 */
8
10
11#if DETWS_ENABLE_HTTP2
12
13#include <string.h>
14
15namespace
16{
17void wr32(uint8_t *p, uint32_t v)
18{
19 p[0] = (uint8_t)(v >> 24);
20 p[1] = (uint8_t)(v >> 16);
21 p[2] = (uint8_t)(v >> 8);
22 p[3] = (uint8_t)v;
23}
24uint32_t rd32(const uint8_t *p)
25{
26 return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | ((uint32_t)p[2] << 8) | p[3];
27}
28} // namespace
29
30bool h2_parse_header(const uint8_t *buf, size_t len, H2FrameHeader *out)
31{
32 if (len < H2_FRAME_HEADER_LEN)
33 return false;
34 out->length = ((uint32_t)buf[0] << 16) | ((uint32_t)buf[1] << 8) | buf[2];
35 out->type = buf[3];
36 out->flags = buf[4];
37 out->stream_id = ((uint32_t)(buf[5] & 0x7f) << 24) | ((uint32_t)buf[6] << 16) | ((uint32_t)buf[7] << 8) |
38 buf[8]; // clear the reserved bit
39 return true;
40}
41
42size_t h2_write_header(uint8_t *out, size_t cap, uint32_t length, uint8_t type, uint8_t flags, uint32_t stream_id)
43{
44 if (cap < H2_FRAME_HEADER_LEN || length > 0xFFFFFF)
45 return 0;
46 out[0] = (uint8_t)(length >> 16);
47 out[1] = (uint8_t)(length >> 8);
48 out[2] = (uint8_t)length;
49 out[3] = type;
50 out[4] = flags;
51 out[5] = (uint8_t)((stream_id >> 24) & 0x7f); // reserved bit stays 0
52 out[6] = (uint8_t)(stream_id >> 16);
53 out[7] = (uint8_t)(stream_id >> 8);
54 out[8] = (uint8_t)stream_id;
55 return H2_FRAME_HEADER_LEN;
56}
57
58void h2_settings_defaults(H2Settings *s)
59{
60 s->header_table_size = 4096;
61 s->enable_push = 1;
62 s->max_concurrent_streams = 0xFFFFFFFFu;
63 s->initial_window_size = 65535;
64 s->max_frame_size = 16384;
65 s->max_header_list_size = 0xFFFFFFFFu;
66}
67
68bool h2_parse_settings(const uint8_t *payload, size_t len, H2Settings *s)
69{
70 if (len % 6 != 0) // each entry is 2-byte id + 4-byte value
71 return false;
72 for (size_t i = 0; i < len; i += 6)
73 {
74 uint16_t id = (uint16_t)(((uint16_t)payload[i] << 8) | payload[i + 1]);
75 uint32_t val = rd32(payload + i + 2);
76 switch (id)
77 {
78 case H2Setting::H2_SETTINGS_HEADER_TABLE_SIZE:
79 s->header_table_size = val;
80 break;
81 case H2Setting::H2_SETTINGS_ENABLE_PUSH:
82 if (val > 1)
83 return false; // RFC 9113 sec 6.5.2: must be 0 or 1
84 s->enable_push = val;
85 break;
86 case H2Setting::H2_SETTINGS_MAX_CONCURRENT_STREAMS:
87 s->max_concurrent_streams = val;
88 break;
89 case H2Setting::H2_SETTINGS_INITIAL_WINDOW_SIZE:
90 if (val > 0x7FFFFFFF)
91 return false; // must not exceed 2^31-1
92 s->initial_window_size = val;
93 break;
94 case H2Setting::H2_SETTINGS_MAX_FRAME_SIZE:
95 if (val < 16384 || val > 16777215)
96 return false; // must be in [2^14, 2^24-1]
97 s->max_frame_size = val;
98 break;
99 case H2Setting::H2_SETTINGS_MAX_HEADER_LIST_SIZE:
100 s->max_header_list_size = val;
101 break;
102 default:
103 break; // unknown settings are ignored
104 }
105 }
106 return true;
107}
108
109size_t h2_build_settings(uint8_t *out, size_t cap, const uint16_t *ids, const uint32_t *vals, size_t n)
110{
111 size_t payload = n * 6;
112 if (cap < H2_FRAME_HEADER_LEN + payload)
113 return 0;
114 h2_write_header(out, cap, (uint32_t)payload, H2FrameType::H2_SETTINGS, 0, 0);
115 uint8_t *p = out + H2_FRAME_HEADER_LEN;
116 for (size_t i = 0; i < n; i++)
117 {
118 p[0] = (uint8_t)(ids[i] >> 8);
119 p[1] = (uint8_t)ids[i];
120 wr32(p + 2, vals[i]);
121 p += 6;
122 }
123 return H2_FRAME_HEADER_LEN + payload;
124}
125
126size_t h2_build_settings_ack(uint8_t *out, size_t cap)
127{
128 return h2_write_header(out, cap, 0, H2FrameType::H2_SETTINGS, H2_FLAG_ACK, 0);
129}
130
131size_t h2_build_window_update(uint8_t *out, size_t cap, uint32_t stream_id, uint32_t increment)
132{
133 if (cap < H2_FRAME_HEADER_LEN + 4)
134 return 0;
135 h2_write_header(out, cap, 4, H2FrameType::H2_WINDOW_UPDATE, 0, stream_id);
136 wr32(out + H2_FRAME_HEADER_LEN, increment & 0x7FFFFFFF); // 31-bit, reserved bit 0
137 return H2_FRAME_HEADER_LEN + 4;
138}
139
140size_t h2_build_rst_stream(uint8_t *out, size_t cap, uint32_t stream_id, uint32_t error)
141{
142 if (cap < H2_FRAME_HEADER_LEN + 4)
143 return 0;
144 h2_write_header(out, cap, 4, H2FrameType::H2_RST_STREAM, 0, stream_id);
145 wr32(out + H2_FRAME_HEADER_LEN, error);
146 return H2_FRAME_HEADER_LEN + 4;
147}
148
149size_t h2_build_goaway(uint8_t *out, size_t cap, uint32_t last_stream_id, uint32_t error)
150{
151 if (cap < H2_FRAME_HEADER_LEN + 8)
152 return 0;
153 h2_write_header(out, cap, 8, H2FrameType::H2_GOAWAY, 0, 0);
154 wr32(out + H2_FRAME_HEADER_LEN, last_stream_id & 0x7FFFFFFF);
155 wr32(out + H2_FRAME_HEADER_LEN + 4, error);
156 return H2_FRAME_HEADER_LEN + 8;
157}
158
159size_t h2_build_ping_ack(uint8_t *out, size_t cap, const uint8_t opaque[8])
160{
161 if (cap < H2_FRAME_HEADER_LEN + 8)
162 return 0;
163 h2_write_header(out, cap, 8, H2FrameType::H2_PING, H2_FLAG_ACK, 0);
164 memcpy(out + H2_FRAME_HEADER_LEN, opaque, 8);
165 return H2_FRAME_HEADER_LEN + 8;
166}
167
168size_t h2_build_headers(uint8_t *out, size_t cap, uint32_t stream_id, const uint8_t *block, size_t block_len,
169 bool end_stream)
170{
171 if (cap < H2_FRAME_HEADER_LEN + block_len)
172 return 0;
173 uint8_t flags = H2_FLAG_END_HEADERS | (end_stream ? H2_FLAG_END_STREAM : 0);
174 h2_write_header(out, cap, (uint32_t)block_len, H2FrameType::H2_HEADERS, flags, stream_id);
175 memcpy(out + H2_FRAME_HEADER_LEN, block, block_len);
176 return H2_FRAME_HEADER_LEN + block_len;
177}
178
179size_t h2_build_data(uint8_t *out, size_t cap, uint32_t stream_id, const uint8_t *data, size_t data_len,
180 bool end_stream)
181{
182 if (cap < H2_FRAME_HEADER_LEN + data_len)
183 return 0;
184 h2_write_header(out, cap, (uint32_t)data_len, H2FrameType::H2_DATA, end_stream ? H2_FLAG_END_STREAM : 0, stream_id);
185 if (data_len)
186 memcpy(out + H2_FRAME_HEADER_LEN, data, data_len);
187 return H2_FRAME_HEADER_LEN + data_len;
188}
189
190#endif // DETWS_ENABLE_HTTP2
HTTP/2 binary framing (RFC 9113 sec 4 + sec 6).