24const char *
const QPACK_STATIC[99][2] = {
28 {
"content-disposition",
""},
29 {
"content-length",
"0"},
33 {
"if-modified-since",
""},
34 {
"if-none-match",
""},
35 {
"last-modified",
""},
40 {
":method",
"CONNECT"},
41 {
":method",
"DELETE"},
44 {
":method",
"OPTIONS"},
55 {
"accept",
"application/dns-message"},
56 {
"accept-encoding",
"gzip, deflate, br"},
57 {
"accept-ranges",
"bytes"},
58 {
"access-control-allow-headers",
"cache-control"},
59 {
"access-control-allow-headers",
"content-type"},
60 {
"access-control-allow-origin",
"*"},
61 {
"cache-control",
"max-age=0"},
62 {
"cache-control",
"max-age=2592000"},
63 {
"cache-control",
"max-age=604800"},
64 {
"cache-control",
"no-cache"},
65 {
"cache-control",
"no-store"},
66 {
"cache-control",
"public, max-age=31536000"},
67 {
"content-encoding",
"br"},
68 {
"content-encoding",
"gzip"},
69 {
"content-type",
"application/dns-message"},
70 {
"content-type",
"application/javascript"},
71 {
"content-type",
"application/json"},
72 {
"content-type",
"application/x-www-form-urlencoded"},
73 {
"content-type",
"image/gif"},
74 {
"content-type",
"image/jpeg"},
75 {
"content-type",
"image/png"},
76 {
"content-type",
"text/css"},
77 {
"content-type",
"text/html;charset=utf-8"},
78 {
"content-type",
"text/plain"},
79 {
"content-type",
"text/plain;charset=utf-8"},
80 {
"range",
"bytes=0-"},
81 {
"strict-transport-security",
"max-age=31536000"},
82 {
"strict-transport-security",
"max-age=31536000;includesubdomains"},
83 {
"strict-transport-security",
"max-age=31536000;includesubdomains;preload"},
84 {
"vary",
"accept-encoding"},
86 {
"x-content-type-options",
"nosniff"},
87 {
"x-xss-protection",
"1; mode=block"},
97 {
"accept-language",
""},
98 {
"access-control-allow-credentials",
"FALSE"},
99 {
"access-control-allow-credentials",
"TRUE"},
100 {
"access-control-allow-headers",
"*"},
101 {
"access-control-allow-methods",
"get"},
102 {
"access-control-allow-methods",
"get, post, options"},
103 {
"access-control-allow-methods",
"options"},
104 {
"access-control-expose-headers",
"content-length"},
105 {
"access-control-request-headers",
"content-type"},
106 {
"access-control-request-method",
"get"},
107 {
"access-control-request-method",
"post"},
108 {
"alt-svc",
"clear"},
109 {
"authorization",
""},
110 {
"content-security-policy",
"script-src 'none';object-src 'none';base-uri 'none'"},
116 {
"purpose",
"prefetch"},
118 {
"timing-allow-origin",
"*"},
119 {
"upgrade-insecure-requests",
"1"},
121 {
"x-forwarded-for",
""},
122 {
"x-frame-options",
"deny"},
123 {
"x-frame-options",
"sameorigin"},
128size_t pc_qpack_encode_prefix(uint8_t *out,
size_t cap)
139size_t pc_qpack_encode_header(uint8_t *out,
size_t cap,
const char *name,
size_t name_len,
const char *value,
142 int name_idx = -1, full_idx = -1;
143 for (
int i = 0; i < 99; i++)
145 if (strnlen(QPACK_STATIC[i][0], name_len + 1) == name_len && memcmp(QPACK_STATIC[i][0], name, name_len) == 0)
151 if (strnlen(QPACK_STATIC[i][1], value_len + 1) == value_len &&
152 memcmp(QPACK_STATIC[i][1], value, value_len) == 0)
161 return pc_hpack_encode_int(out, cap, 6, 0xC0, (uint32_t)full_idx);
166 size_t o = pc_hpack_encode_int(out, cap, 4, 0x50, (uint32_t)name_idx);
171 size_t vs = pc_hpack_encode_str(out + o, cap - o, value, value_len);
180 size_t hl = pc_hpack_huff_len(name, name_len);
181 bool huff = hl < name_len;
182 size_t nbytes = huff ? hl : name_len;
183 size_t o = pc_hpack_encode_int(out, cap, 3, (uint8_t)(0x20 | (huff ? 0x08 : 0x00)), (uint32_t)nbytes);
190 size_t body = pc_hpack_huff_encode(out + o, cap - o, name, name_len);
199 if (o + name_len > cap)
203 memcpy(out + o, name, name_len);
206 size_t vs = pc_hpack_encode_str(out + o, cap - o, value, value_len);
214bool pc_qpack_decode(
const uint8_t *block,
size_t len,
char *scratch,
size_t scratch_cap, QpackEmitFn emit,
void *ctx)
220 if (!pc_hpack_decode_int(block + pos, len - pos, 8, &c, &ric))
230 if (!pc_hpack_decode_int(block + pos, len - pos, 7, &c, &base))
238 uint8_t b = block[pos];
246 if (!pc_hpack_decode_int(block + pos, len - pos, 6, &c, &idx) || idx >= 99)
251 const char *nm = QPACK_STATIC[idx][0];
252 const char *vl = QPACK_STATIC[idx][1];
253 if (!emit(ctx, nm, strnlen(nm, scratch_cap + 1), vl, strnlen(vl, scratch_cap + 1)))
258 else if ((b & 0xC0) == 0x40)
260 bool is_static = (b & 0x10) != 0;
262 if (!pc_hpack_decode_int(block + pos, len - pos, 4, &c, &idx))
267 if (!is_static || idx >= 99)
271 const char *nm = QPACK_STATIC[idx][0];
272 size_t nlen = strnlen(nm, scratch_cap + 1);
273 if (nlen > scratch_cap)
277 memcpy(scratch, nm, nlen);
279 if (!pc_hpack_decode_str(block, len, &pos, scratch + nlen, scratch_cap - nlen, &vlen))
283 if (!emit(ctx, scratch, nlen, scratch + nlen, vlen))
288 else if ((b & 0xE0) == 0x20)
290 bool huff = (b & 0x08) != 0;
292 if (!pc_hpack_decode_int(block + pos, len - pos, 3, &c, &nlen32))
297 if (pos + nlen32 > len)
304 if (!pc_hpack_huff_decode(block + pos, nlen32, scratch, scratch_cap, &nlen))
311 if (nlen32 > scratch_cap)
315 memcpy(scratch, block + pos, nlen32);
320 if (!pc_hpack_decode_str(block, len, &pos, scratch + nlen, scratch_cap - nlen, &vlen))
324 if (!emit(ctx, scratch, nlen, scratch + nlen, vlen))
QPACK field-section compression for HTTP/3 (RFC 9204).