19H3Stream *h3_stream_get(H3Conn *h3, uint64_t
id,
bool create)
21 H3Stream *free_slot =
nullptr;
24 if (h3->streams[i].role != H3StreamRole::H3_ROLE_FREE && h3->streams[i].id ==
id)
25 return &h3->streams[i];
26 if (!free_slot && h3->streams[i].role == H3StreamRole::H3_ROLE_FREE)
27 free_slot = &h3->streams[i];
29 if (!create || !free_slot)
31 memset(free_slot, 0,
sizeof(*free_slot));
37void set_field(
char *dst,
size_t cap,
const char *src,
size_t len)
41 memcpy(dst, src, len);
50bool req_emit(
void *ctx,
const char *name,
size_t nlen,
const char *value,
size_t vlen)
52 H3Stream *st = ((ReqEmit *)ctx)->st;
53 if (nlen == 7 && memcmp(name,
":method", 7) == 0)
54 set_field(st->method,
sizeof(st->method), value, vlen);
55 else if (nlen == 5 && memcmp(name,
":path", 5) == 0)
56 set_field(st->path,
sizeof(st->path), value, vlen);
57 else if (nlen == 10 && memcmp(name,
":authority", 10) == 0)
58 set_field(st->authority,
sizeof(st->authority), value, vlen);
63void dispatch_request(H3Conn *h3, H3Stream *st)
65 static uint8_t body[DETWS_H3_STREAM_BUF];
66 static char scratch[DETWS_H3_PATH_LEN + DETWS_H3_AUTHORITY_LEN + 64];
70 while (off < st->buf_len)
73 if (!h3_frame_parse(st->buf + off, st->buf_len - off, &fr))
75 size_t payload = off + fr.header_len;
76 if (payload + fr.length > st->buf_len)
78 const uint8_t *fp = st->buf + payload;
79 if (fr.type == H3FrameType::H3_HEADERS)
82 qpack_decode(fp, (
size_t)fr.length, scratch,
sizeof(scratch), req_emit, &e);
83 st->have_headers =
true;
85 else if (fr.type == H3FrameType::H3_DATA)
89 size_t room = (body_len <
sizeof(body)) ?
sizeof(body) - body_len : 0;
90 size_t take = (size_t)fr.length;
94 memcpy(body + body_len, fp, take);
97 off = payload + (size_t)fr.length;
100 if (st->have_headers && h3->on_request)
101 h3->on_request(h3->app, h3, st->id, st->method, st->path, st->authority, body, body_len);
104void append(H3Stream *st,
const uint8_t *data,
size_t len)
106 if (len >
sizeof(st->buf) - st->buf_len)
107 len =
sizeof(st->buf) - st->buf_len;
108 memcpy(st->buf + st->buf_len, data, len);
114bool h3_classify_uni_stream(H3Stream *st)
118 if (!quic_varint_decode(st->buf, st->buf_len, &type, &c))
120 st->type_read =
true;
122 st->role = H3StreamRole::H3_ROLE_CONTROL;
123 else if (type == 0x02)
124 st->role = H3StreamRole::H3_ROLE_QPACK_ENC;
125 else if (type == 0x03)
126 st->role = H3StreamRole::H3_ROLE_QPACK_DEC;
128 st->role = H3StreamRole::H3_ROLE_OTHER_UNI;
129 memmove(st->buf, st->buf + c, st->buf_len - c);
135void h3_consume_control(H3Conn *h3, H3Stream *st)
138 while (off < st->buf_len)
141 if (!h3_frame_parse(st->buf + off, st->buf_len - off, &fr))
143 if (off + fr.header_len + fr.length > st->buf_len)
145 if (fr.type == H3FrameType::H3_SETTINGS)
147 h3_settings_defaults(&h3->peer_settings);
148 h3_parse_settings(st->buf + off + fr.header_len, (
size_t)fr.length, &h3->peer_settings);
150 off += fr.header_len + (size_t)fr.length;
152 memmove(st->buf, st->buf + off, st->buf_len - off);
156void on_stream_data(
void *app, QuicConn *, uint64_t stream_id,
const uint8_t *data,
size_t len,
bool fin)
158 H3Conn *h3 = (H3Conn *)app;
159 H3Stream *st = h3_stream_get(h3, stream_id,
true);
163 if (st->role == H3StreamRole::H3_ROLE_FREE)
164 st->role = (stream_id & 0x03) == 0x00 ? H3StreamRole::H3_ROLE_REQUEST : H3StreamRole::H3_ROLE_OTHER_UNI;
166 append(st, data, len);
169 if (st->role != H3StreamRole::H3_ROLE_REQUEST && !st->type_read && st->buf_len >= 1 && !h3_classify_uni_stream(st))
172 if (st->role == H3StreamRole::H3_ROLE_CONTROL)
174 h3_consume_control(h3, st);
177 if (st->role != H3StreamRole::H3_ROLE_REQUEST)
184 dispatch_request(h3, st);
187void on_handshake_done(
void *app, QuicConn *qc)
189 H3Conn *h3 = (H3Conn *)app;
190 if (h3->control_opened)
192 h3->control_opened =
true;
196 size_t p = quic_varint_encode(buf,
sizeof(buf), 0x00);
197 const uint64_t ids[] = {H3Setting::H3_SETTINGS_QPACK_MAX_TABLE_CAPACITY,
198 H3Setting::H3_SETTINGS_QPACK_BLOCKED_STREAMS};
199 const uint64_t vals[] = {0, 0};
200 p += h3_build_settings(buf + p,
sizeof(buf) - p, ids, vals, 2);
201 quic_conn_stream_send(qc, 3, buf, p,
false);
205 size_t n = quic_varint_encode(&t, 1, 0x02);
206 quic_conn_stream_send(qc, 7, &t, n,
false);
207 n = quic_varint_encode(&t, 1, 0x03);
208 quic_conn_stream_send(qc, 11, &t, n,
false);
209 h3->next_uni_id = 15;
213void h3_conn_init(H3Conn *h3, QuicConn *qc, H3RequestFn on_request,
void *app)
215 memset(h3, 0,
sizeof(*h3));
217 h3->on_request = on_request;
221 h3->streams[i].id = UINT64_MAX;
222 h3_settings_defaults(&h3->peer_settings);
224 QuicConnCallbacks cb = {on_stream_data, on_handshake_done, h3};
228bool h3_conn_respond(H3Conn *h3, uint64_t stream_id,
int status,
const char *content_type,
const uint8_t *body,
231 H3Stream *st = h3_stream_get(h3, stream_id,
false);
233 st->responded =
true;
237 size_t bp = qpack_encode_prefix(block,
sizeof(block));
239 st3[0] = (char)(
'0' + (status / 100) % 10);
240 st3[1] = (char)(
'0' + (status / 10) % 10);
241 st3[2] = (char)(
'0' + status % 10);
243 bp += qpack_encode_header(block + bp,
sizeof(block) - bp,
":status", 7, st3, 3);
248 bp += qpack_encode_header(block + bp,
sizeof(block) - bp,
"content-type", 12, content_type,
249 strnlen(content_type,
sizeof(block) * 2));
259 tmp[n++] = (char)(
'0' + v % 10);
263 clen[cl++] = tmp[--n];
265 bp += qpack_encode_header(block + bp,
sizeof(block) - bp,
"content-length", 14, clen, cl);
268 uint8_t out[DETWS_H3_STREAM_BUF];
269 size_t op = h3_build_headers(out,
sizeof(out), block, bp);
274 size_t dn = h3_build_data(out + op,
sizeof(out) - op, body, body_len);
279 return quic_conn_stream_send(h3->qc, stream_id, out, op,
true) == op;
#define DETWS_H3_MAX_STREAMS
Maximum concurrent request streams per HTTP/3 connection.
HTTP/3 application engine over QUIC streams (RFC 9114).
QPACK field-section compression for HTTP/3 (RFC 9204).
QUIC variable-length integer coding (RFC 9000 sec 16).