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"},
127bool decode_str7(
const uint8_t *block,
size_t len,
size_t *pos,
char *out,
size_t cap,
size_t *out_len)
131 bool huff = (block[*pos] & 0x80) != 0;
134 if (!hpack_decode_int(block + *pos, len - *pos, 7, &c, &slen))
137 if (*pos + slen > len)
141 if (!hpack_huff_decode(block + *pos, slen, out, cap, out_len))
148 memcpy(out, block + *pos, slen);
156size_t encode_str7(uint8_t *out,
size_t cap,
const char *s,
size_t n)
158 size_t hl = hpack_huff_len(s, n);
161 size_t hdr = hpack_encode_int(out, cap, 7, 0x80, (uint32_t)hl);
164 size_t body = hpack_huff_encode(out + hdr, cap - hdr, s, n);
169 size_t hdr = hpack_encode_int(out, cap, 7, 0x00, (uint32_t)n);
170 if (!hdr || hdr + n > cap)
172 memcpy(out + hdr, s, n);
178size_t qpack_encode_prefix(uint8_t *out,
size_t cap)
187size_t qpack_encode_header(uint8_t *out,
size_t cap,
const char *name,
size_t name_len,
const char *value,
190 int name_idx = -1, full_idx = -1;
191 for (
int i = 0; i < 99; i++)
193 if (strnlen(QPACK_STATIC[i][0], name_len + 1) == name_len && memcmp(QPACK_STATIC[i][0], name, name_len) == 0)
197 if (strnlen(QPACK_STATIC[i][1], value_len + 1) == value_len &&
198 memcmp(QPACK_STATIC[i][1], value, value_len) == 0)
206 return hpack_encode_int(out, cap, 6, 0xC0, (uint32_t)full_idx);
210 size_t o = hpack_encode_int(out, cap, 4, 0x50, (uint32_t)name_idx);
213 size_t vs = encode_str7(out + o, cap - o, value, value_len);
220 size_t hl = hpack_huff_len(name, name_len);
221 bool huff = hl < name_len;
222 size_t nbytes = huff ? hl : name_len;
223 size_t o = hpack_encode_int(out, cap, 3, (uint8_t)(0x20 | (huff ? 0x08 : 0x00)), (uint32_t)nbytes);
228 size_t body = hpack_huff_encode(out + o, cap - o, name, name_len);
235 if (o + name_len > cap)
237 memcpy(out + o, name, name_len);
240 size_t vs = encode_str7(out + o, cap - o, value, value_len);
246bool qpack_decode(
const uint8_t *block,
size_t len,
char *scratch,
size_t scratch_cap, QpackEmitFn emit,
void *ctx)
252 if (!hpack_decode_int(block + pos, len - pos, 8, &c, &ric))
258 if (!hpack_decode_int(block + pos, len - pos, 7, &c, &base))
264 uint8_t b = block[pos];
270 if (!hpack_decode_int(block + pos, len - pos, 6, &c, &idx) || idx >= 99)
273 const char *nm = QPACK_STATIC[idx][0];
274 const char *vl = QPACK_STATIC[idx][1];
275 if (!emit(ctx, nm, strnlen(nm, scratch_cap + 1), vl, strnlen(vl, scratch_cap + 1)))
278 else if ((b & 0xC0) == 0x40)
280 bool is_static = (b & 0x10) != 0;
282 if (!hpack_decode_int(block + pos, len - pos, 4, &c, &idx))
285 if (!is_static || idx >= 99)
287 const char *nm = QPACK_STATIC[idx][0];
288 size_t nlen = strnlen(nm, scratch_cap + 1);
289 if (nlen > scratch_cap)
291 memcpy(scratch, nm, nlen);
293 if (!decode_str7(block, len, &pos, scratch + nlen, scratch_cap - nlen, &vlen))
295 if (!emit(ctx, scratch, nlen, scratch + nlen, vlen))
298 else if ((b & 0xE0) == 0x20)
300 bool huff = (b & 0x08) != 0;
302 if (!hpack_decode_int(block + pos, len - pos, 3, &c, &nlen32))
305 if (pos + nlen32 > len)
310 if (!hpack_huff_decode(block + pos, nlen32, scratch, scratch_cap, &nlen))
315 if (nlen32 > scratch_cap)
317 memcpy(scratch, block + pos, nlen32);
322 if (!decode_str7(block, len, &pos, scratch + nlen, scratch_cap - nlen, &vlen))
324 if (!emit(ctx, scratch, nlen, scratch + nlen, vlen))
Low-level field-coding primitives shared by HPACK and QPACK.
QPACK field-section compression for HTTP/3 (RFC 9204).