22#define HPACK_BYTES DETWS_HPACK_TABLE_BYTES
23#define HPACK_ENTS DETWS_HPACK_MAX_ENTRIES
26const char *
const STATIC[62][2] = {
32 {
":path",
"/index.html"},
42 {
"accept-charset",
""},
43 {
"accept-encoding",
"gzip, deflate"},
44 {
"accept-language",
""},
45 {
"accept-ranges",
""},
47 {
"access-control-allow-origin",
""},
50 {
"authorization",
""},
51 {
"cache-control",
""},
52 {
"content-disposition",
""},
53 {
"content-encoding",
""},
54 {
"content-language",
""},
55 {
"content-length",
""},
56 {
"content-location",
""},
57 {
"content-range",
""},
67 {
"if-modified-since",
""},
68 {
"if-none-match",
""},
70 {
"if-unmodified-since",
""},
71 {
"last-modified",
""},
75 {
"proxy-authenticate",
""},
76 {
"proxy-authorization",
""},
83 {
"strict-transport-security",
""},
84 {
"transfer-encoding",
""},
88 {
"www-authenticate",
""},
93void ring_write(HpackDynTable *t, uint16_t pos,
const char *src,
size_t n)
95 for (
size_t i = 0; i < n; i++)
96 t->ring[(pos + i) % HPACK_BYTES] = src[i];
98void ring_read(
const HpackDynTable *t, uint16_t pos,
char *dst,
size_t n)
100 for (
size_t i = 0; i < n; i++)
101 dst[i] = t->ring[(pos + i) % HPACK_BYTES];
105const HpackEntry *dyn_entry(
const HpackDynTable *t, uint32_t k)
107 if (k < 1 || k > t->ecount)
109 uint16_t di = (uint16_t)((t->ehead + HPACK_ENTS - k) % HPACK_ENTS);
113void dyn_evict_oldest(HpackDynTable *t)
117 uint16_t oi = (uint16_t)((t->ehead + HPACK_ENTS - t->ecount) % HPACK_ENTS);
118 const HpackEntry *e = &t->ent[oi];
119 uint16_t bytes = (uint16_t)(e->name_len + e->val_len);
120 t->rtail = (uint16_t)((t->rtail + bytes) % HPACK_BYTES);
121 t->rused = (uint16_t)(t->rused - bytes);
122 t->used -= (uint32_t)e->name_len + e->val_len + 32;
126void dyn_set_max(HpackDynTable *t, uint32_t new_max)
128 if (new_max > HPACK_BYTES)
129 new_max = HPACK_BYTES;
130 t->max_size = new_max;
131 while (t->used > t->max_size && t->ecount > 0)
135void dyn_insert(HpackDynTable *t,
const char *name,
size_t nlen,
const char *val,
size_t vlen)
137 uint32_t entry_size = (uint32_t)nlen + (uint32_t)vlen + 32;
138 if (entry_size > t->max_size)
147 while ((t->used + entry_size > t->max_size || t->ecount >= HPACK_ENTS) && t->ecount > 0)
149 uint16_t rpos = (uint16_t)((t->rtail + t->rused) % HPACK_BYTES);
150 HpackEntry *e = &t->ent[t->ehead];
151 e->name_len = (uint16_t)nlen;
152 e->val_len = (uint16_t)vlen;
154 ring_write(t, rpos, name, nlen);
155 ring_write(t, (uint16_t)((rpos + nlen) % HPACK_BYTES), val, vlen);
156 t->rused = (uint16_t)(t->rused + nlen + vlen);
157 t->ehead = (uint16_t)((t->ehead + 1) % HPACK_ENTS);
159 t->used += entry_size;
165bool decode_string(
const uint8_t *block,
size_t len,
size_t *pos,
char *out,
size_t cap,
size_t *out_len)
169 bool huff = (block[*pos] & 0x80) != 0;
172 if (!hpack_decode_int(block + *pos, len - *pos, 7, &c, &slen))
175 if (*pos + slen > len)
179 if (!hpack_huff_decode(block + *pos, slen, out, cap, out_len))
186 memcpy(out, block + *pos, slen);
193size_t encode_string(uint8_t *out,
size_t cap,
const char *s,
size_t n)
195 size_t hl = hpack_huff_len(s, n);
198 size_t hdr = hpack_encode_int(out, cap, 7, 0x80, (uint32_t)hl);
201 size_t body = hpack_huff_encode(out + hdr, cap - hdr, s, n);
206 size_t hdr = hpack_encode_int(out, cap, 7, 0x00, (uint32_t)n);
207 if (!hdr || hdr + n > cap)
209 memcpy(out + hdr, s, n);
214bool resolve_name(
const HpackDynTable *t, uint32_t idx,
char *out,
size_t cap,
size_t *out_len)
216 if (idx >= 1 && idx <= 61)
218 size_t nl = strnlen(STATIC[idx][0], cap + 1);
221 memcpy(out, STATIC[idx][0], nl);
225 const HpackEntry *e = dyn_entry(t, idx - 61);
226 if (!e || e->name_len > cap)
228 ring_read(t, e->ring_pos, out, e->name_len);
229 *out_len = e->name_len;
234bool emit_indexed(HpackDynTable *t, uint32_t idx,
char *scratch,
size_t cap, HpackEmitFn emit,
void *ctx)
238 if (idx >= 1 && idx <= 61)
240 nl = strnlen(STATIC[idx][0], cap + 1);
241 vl = strnlen(STATIC[idx][1], cap + 1);
244 memcpy(scratch, STATIC[idx][0], nl);
245 memcpy(scratch + nl, STATIC[idx][1], vl);
249 const HpackEntry *e = dyn_entry(t, idx - 61);
256 ring_read(t, e->ring_pos, scratch, nl);
257 ring_read(t, (uint16_t)((e->ring_pos + nl) % HPACK_BYTES), scratch + nl, vl);
259 return emit(ctx, scratch, nl, scratch + nl, vl);
263bool decode_literal(HpackDynTable *t,
const uint8_t *block,
size_t len,
size_t *pos, uint8_t prefix_bits,
bool do_index,
264 char *scratch,
size_t cap, HpackEmitFn emit,
void *ctx)
267 uint32_t name_idx = 0;
268 if (!hpack_decode_int(block + *pos, len - *pos, prefix_bits, &c, &name_idx))
274 if (!decode_string(block, len, pos, scratch, cap, &name_len))
277 else if (!resolve_name(t, name_idx, scratch, cap, &name_len))
282 if (!decode_string(block, len, pos, scratch + name_len, cap - name_len, &val_len))
285 dyn_insert(t, scratch, name_len, scratch + name_len, val_len);
286 return emit(ctx, scratch, name_len, scratch + name_len, val_len);
293void hpack_dyn_init(HpackDynTable *t, uint32_t max_bytes)
295 memset(t, 0,
sizeof(*t));
296 t->max_size = max_bytes ? max_bytes : (uint32_t)HPACK_BYTES;
297 if (t->max_size > HPACK_BYTES)
298 t->max_size = HPACK_BYTES;
301bool hpack_decode(HpackDynTable *t,
const uint8_t *block,
size_t len,
char *scratch,
size_t scratch_cap,
302 HpackEmitFn emit,
void *ctx)
307 uint8_t b = block[pos];
312 if (!hpack_decode_int(block + pos, len - pos, 7, &c, &idx) || idx == 0)
315 if (!emit_indexed(t, idx, scratch, scratch_cap, emit, ctx))
320 if (!decode_literal(t, block, len, &pos, 6,
true, scratch, scratch_cap, emit, ctx))
323 else if ((b & 0xE0) == 0x20)
327 if (!hpack_decode_int(block + pos, len - pos, 5, &c, &nm))
334 if (!decode_literal(t, block, len, &pos, 4,
false, scratch, scratch_cap, emit, ctx))
341size_t hpack_encode_header(uint8_t *out,
size_t cap,
const char *name,
size_t name_len,
const char *value,
346 for (
int i = 1; i <= 61; i++)
348 if (strnlen(STATIC[i][0], name_len + 1) == name_len && memcmp(STATIC[i][0], name, name_len) == 0)
352 if (strnlen(STATIC[i][1], value_len + 1) == value_len && memcmp(STATIC[i][1], value, value_len) == 0)
360 return hpack_encode_int(out, cap, 7, 0x80, (uint32_t)full_idx);
362 size_t o = hpack_encode_int(out, cap, 4, 0x00, (uint32_t)name_idx);
367 size_t ns = encode_string(out + o, cap - o, name, name_len);
372 size_t vs = encode_string(out + o, cap - o, value, value_len);
HPACK header compression for HTTP/2 (RFC 7541).
Low-level field-coding primitives shared by HPACK and QPACK.