18uint32_t rd32(
const uint8_t *p)
20 return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | ((uint32_t)p[2] << 8) | p[3];
23void wr(H2Conn *c,
const uint8_t *data,
size_t len)
26 c->cb.write(c->cb.io, data, len);
29H2Stream *find_stream(H2Conn *c, uint32_t
id)
32 if (c->streams[i].id ==
id &&
id != 0)
33 return &c->streams[i];
37H2Stream *alloc_stream(H2Conn *c, uint32_t
id)
40 if (c->streams[i].id == 0)
42 c->streams[i].id = id;
43 c->streams[i].state = H2StreamState::H2_ST_OPEN;
44 c->streams[i].send_window = (int32_t)c->peer.initial_window_size;
45 return &c->streams[i];
50void send_our_settings(H2Conn *c)
52 const uint16_t ids[4] = {H2Setting::H2_SETTINGS_ENABLE_PUSH, H2Setting::H2_SETTINGS_MAX_CONCURRENT_STREAMS,
53 H2Setting::H2_SETTINGS_INITIAL_WINDOW_SIZE, H2Setting::H2_SETTINGS_MAX_FRAME_SIZE};
55 uint8_t buf[H2_FRAME_HEADER_LEN + 4 * 6];
56 size_t n = h2_build_settings(buf,
sizeof buf, ids, vals, 4);
60void send_control(H2Conn *c,
size_t (*build)(uint8_t *,
size_t))
62 uint8_t buf[H2_FRAME_HEADER_LEN + 16];
63 size_t n = build(buf,
sizeof buf);
74bool emit_header(
void *ctx,
const char *name,
size_t nl,
const char *val,
size_t vl)
76 EmitCtx *e = (EmitCtx *)ctx;
77 if (e->c->cb.on_header)
78 e->c->cb.on_header(e->c->cb.app, e->stream_id, name, nl, val, vl);
83bool decode_block(H2Conn *c, uint32_t stream_id,
const uint8_t *block,
size_t len,
bool end_stream)
85 EmitCtx e = {c, stream_id};
86 if (!hpack_decode(&c->hdec, block, len, c->hscratch,
sizeof c->hscratch, emit_header, &e))
88 if (c->cb.on_headers_end)
89 c->cb.on_headers_end(c->cb.app, stream_id, end_stream);
90 H2Stream *s = find_stream(c, stream_id);
92 s->state = end_stream ? H2StreamState::H2_ST_HALF_CLOSED : H2StreamState::H2_ST_OPEN;
96bool handle_headers(H2Conn *c,
const H2FrameHeader *h,
const uint8_t *payload)
98 if (h->stream_id == 0 || (h->stream_id & 1) == 0)
100 const uint8_t *p = payload;
101 size_t plen = h->length;
103 if (h->flags & H2_FLAG_PADDED)
111 if (h->flags & H2_FLAG_PRIORITY)
122 bool end_stream = (h->flags & H2_FLAG_END_STREAM) != 0;
123 if (h->stream_id <= c->last_peer_stream)
125 c->last_peer_stream = h->stream_id;
126 if (!alloc_stream(c, h->stream_id))
128 send_control(c, [](uint8_t *b,
size_t cap) {
return h2_build_rst_stream(b, cap, 0, 0); });
132 if (h->flags & H2_FLAG_END_HEADERS)
133 return decode_block(c, h->stream_id, p, plen, end_stream);
135 if (plen >
sizeof c->hblock)
137 memcpy(c->hblock, p, plen);
138 c->hblock_len = plen;
139 c->hblock_stream = h->stream_id;
140 c->hblock_end_stream = end_stream;
141 c->in_header_block =
true;
145bool handle_continuation(H2Conn *c,
const H2FrameHeader *h,
const uint8_t *payload)
147 if (!c->in_header_block || h->stream_id != c->hblock_stream)
149 if (c->hblock_len + h->length >
sizeof c->hblock)
151 memcpy(c->hblock + c->hblock_len, payload, h->length);
152 c->hblock_len += h->length;
153 if (h->flags & H2_FLAG_END_HEADERS)
155 c->in_header_block =
false;
156 return decode_block(c, c->hblock_stream, c->hblock, c->hblock_len, c->hblock_end_stream);
161bool handle_data(H2Conn *c,
const H2FrameHeader *h,
const uint8_t *payload)
163 if (h->stream_id == 0)
165 const uint8_t *p = payload;
166 size_t plen = h->length;
168 if (h->flags & H2_FLAG_PADDED)
180 bool end_stream = (h->flags & H2_FLAG_END_STREAM) != 0;
182 c->cb.on_data(c->cb.app, h->stream_id, p, plen, end_stream);
183 H2Stream *s = find_stream(c, h->stream_id);
185 s->state = H2StreamState::H2_ST_HALF_CLOSED;
189 uint8_t wu[H2_FRAME_HEADER_LEN + 4];
190 size_t n = h2_build_window_update(wu,
sizeof wu, 0, h->length);
192 n = h2_build_window_update(wu,
sizeof wu, h->stream_id, h->length);
198bool process_frame(H2Conn *c)
201 h2_parse_header(c->fbuf, H2_FRAME_HEADER_LEN, &h);
202 const uint8_t *payload = c->fbuf + H2_FRAME_HEADER_LEN;
205 if (c->in_header_block && h.type != H2FrameType::H2_CONTINUATION)
210 case H2FrameType::H2_SETTINGS:
211 if (h.flags & H2_FLAG_ACK)
212 return h.length == 0;
213 if (!h2_parse_settings(payload, h.length, &c->peer))
215 send_control(c, h2_build_settings_ack);
217 case H2FrameType::H2_PING:
218 if (h.flags & H2_FLAG_ACK)
223 uint8_t pg[H2_FRAME_HEADER_LEN + 8];
224 size_t n = h2_build_ping_ack(pg,
sizeof pg, payload);
228 case H2FrameType::H2_WINDOW_UPDATE: {
231 uint32_t inc = rd32(payload) & 0x7FFFFFFF;
232 if (h.stream_id == 0)
233 c->conn_send_window += (int32_t)inc;
236 H2Stream *s = find_stream(c, h.stream_id);
238 s->send_window += (int32_t)inc;
242 case H2FrameType::H2_HEADERS:
243 return handle_headers(c, &h, payload);
244 case H2FrameType::H2_CONTINUATION:
245 return handle_continuation(c, &h, payload);
246 case H2FrameType::H2_DATA:
247 return handle_data(c, &h, payload);
248 case H2FrameType::H2_RST_STREAM: {
249 H2Stream *s = find_stream(c, h.stream_id);
254 case H2FrameType::H2_PRIORITY:
256 case H2FrameType::H2_GOAWAY:
259 case H2FrameType::H2_PUSH_PROMISE:
267void h2_conn_init(H2Conn *c,
const H2Callbacks *cb)
269 memset(c, 0,
sizeof(*c));
272 h2_settings_defaults(&c->peer);
273 c->conn_send_window = 65535;
275 send_our_settings(c);
278bool h2_conn_recv(H2Conn *c,
const uint8_t *data,
size_t len)
283 while (off < len && c->pre < H2_PREFACE_LEN)
285 if (data[off] != (uint8_t)H2_PREFACE[c->pre])
290 if (c->pre < H2_PREFACE_LEN)
299 if (c->fhave < H2_FRAME_HEADER_LEN)
301 size_t take = H2_FRAME_HEADER_LEN - c->fhave;
302 if (take > len - off)
304 memcpy(c->fbuf + c->fhave, data + off, take);
307 if (c->fhave < H2_FRAME_HEADER_LEN)
310 uint32_t plen = ((uint32_t)c->fbuf[0] << 16) | ((uint32_t)c->fbuf[1] << 8) | c->fbuf[2];
313 size_t total = H2_FRAME_HEADER_LEN + plen;
314 size_t take = total - c->fhave;
315 if (take > len - off)
317 memcpy(c->fbuf + c->fhave, data + off, take);
320 if (c->fhave < total)
322 if (!process_frame(c))
329bool h2_conn_respond(H2Conn *c, uint32_t stream_id,
int status,
const char *content_type,
const char *body,
332 H2Stream *s = find_stream(c, stream_id);
340 int nl = snprintf(num,
sizeof num,
"%d", status);
341 size_t w = hpack_encode_header(block + bo,
sizeof block - bo,
":status", 7, num, (
size_t)nl);
352 w = hpack_encode_header(block + bo,
sizeof block - bo,
"content-type", 12, content_type,
353 strnlen(content_type,
sizeof block * 2));
358 int cl = snprintf(num,
sizeof num,
"%u", (
unsigned)body_len);
359 w = hpack_encode_header(block + bo,
sizeof block - bo,
"content-length", 14, num, (
size_t)cl);
366 uint8_t frame[H2_FRAME_HEADER_LEN +
sizeof block];
367 size_t n = h2_build_headers(frame,
sizeof frame, stream_id, block, bo, body_len == 0);
376 uint32_t chunk_max = c->peer.max_frame_size ? c->peer.max_frame_size : 16384;
377 while (sent < body_len)
379 size_t chunk = body_len - sent;
380 if (chunk > chunk_max)
382 bool last = (sent + chunk == body_len);
383 uint8_t dh[H2_FRAME_HEADER_LEN];
384 size_t hn = h2_write_header(dh,
sizeof dh, (uint32_t)chunk, H2FrameType::H2_DATA, last ? H2_FLAG_END_STREAM : 0,
391 wr(c, (
const uint8_t *)(body + sent), chunk);
392 c->conn_send_window -= (int32_t)chunk;
393 s->send_window -= (int32_t)chunk;
400void h2_conn_goaway(H2Conn *c, uint32_t error)
402 uint8_t buf[H2_FRAME_HEADER_LEN + 8];
403 size_t n = h2_build_goaway(buf,
sizeof buf, c->last_peer_stream, error);
#define DETWS_HPACK_TABLE_BYTES
Per-connection HPACK dynamic-table size in bytes (our decoder; advertised to the peer as SETTINGS_HEA...
#define DETWS_H2_MAX_FRAME
Largest HTTP/2 frame we accept, in bytes (advertised as SETTINGS_MAX_FRAME_SIZE). RFC 9113 requires a...
#define DETWS_H2_MAX_STREAMS
Max concurrent HTTP/2 streams per connection (advertised as MAX_CONCURRENT_STREAMS).
HTTP/2 connection + stream engine (RFC 9113) over the HPACK + frame layers.