1 /*
2  * Copyright (c) 2018 Fastly, Kazuho Oku
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to
6  * deal in the Software without restriction, including without limitation the
7  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8  * sell copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20  * IN THE SOFTWARE.
21  */
22 #include <stddef.h>
23 #include <stdint.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include "picotls.h"
27 #include "h2o.h"
28 #include "h2o/hpack.h"
29 #include "h2o/qpack.h"
30 #include "h2o/http3_common.h"
31 
32 #define HEADER_ENTRY_SIZE_OFFSET 32
33 
34 /**
35  * a mem-shared object that contains the name and value of a header field
36  */
37 struct st_h2o_qpack_header_t {
38     h2o_iovec_t *name;
39     size_t value_len;
40     h2o_iovec_t _name_buf;
41     unsigned soft_errors;
42     char value[1];
43 };
44 
45 struct st_h2o_qpack_header_table_t {
46     /**
47      * pointers to the buffer structure; [buf_start, buf_end) is the memory allocated, [first, last) are the active entries
48      */
49     struct st_h2o_qpack_header_t **buf_start, **first, **last, **buf_end;
50     /**
51      * absolute index of `first`
52      */
53     int64_t base_offset;
54     /**
55      * current and maximum size
56      */
57     size_t num_bytes, max_size;
58 };
59 
60 struct st_h2o_qpack_blocked_streams_t {
61     int64_t stream_id;
62     int64_t largest_ref;
63     union {
64         struct {
65             uint8_t is_blocking;
66         } encoder_flags;
67     };
68 };
69 
70 struct st_h2o_qpack_decoder_t {
71     /**
72      *
73      */
74     struct st_h2o_qpack_header_table_t table;
75     /**
76      * maximum header table size declared by itself. Current max set by peer is available in `table.max_size`.
77      */
78     uint32_t header_table_size;
79     /**
80      *
81      */
82     uint32_t max_entries;
83     /**
84      * number of updates since last sync
85      */
86     uint32_t insert_count;
87     /**
88      *
89      */
90     uint64_t total_inserts;
91     /**
92      *
93      */
94     uint16_t max_blocked;
95     struct {
96         /**
97          * contains list of blocked streams (sorted in the ascending order of largest_ref)
98          */
99         H2O_VECTOR(struct st_h2o_qpack_blocked_streams_t) list;
100         /**
101          * number of blocked streams that are unblocked. They are evicted parse_request / response is being called.
102          */
103         size_t num_unblocked;
104     } blocked_streams;
105 };
106 
107 struct st_h2o_qpack_encoder_t {
108     /**
109      * the header table
110      */
111     struct st_h2o_qpack_header_table_t table;
112     /**
113      * maximum id of the insertion being acked (inclusive)
114      */
115     int64_t largest_known_received;
116     /**
117      * SETTINGS_QPACK_BLOCKED_STREAMS
118      */
119     uint16_t max_blocked;
120     /**
121      * number of potentially blocked HEADERS (not streams, sorry!) We count header blocks rather than streams because it is easier.
122      * Hopefully it would work well.
123      */
124     uint16_t num_blocked;
125     /**
126      * list of unacked streams
127      */
128     H2O_VECTOR(struct st_h2o_qpack_blocked_streams_t) inflight;
129 };
130 
131 struct st_h2o_qpack_flatten_context_t {
132     h2o_qpack_encoder_t *qpack;
133     h2o_mem_pool_t *pool;
134     int64_t stream_id;
135     h2o_byte_vector_t *encoder_buf;
136     h2o_byte_vector_t headers_buf;
137     int64_t base_index;
138     int64_t largest_ref;
139 };
140 
141 #define MAX_HEADER_NAME_LENGTH 128
142 #define MAX_HEADER_VALUE_LENGTH 4096
143 
144 const char *h2o_qpack_err_header_name_too_long = "header name too long";
145 const char *h2o_qpack_err_header_value_too_long = "header value too long";
146 const char *h2o_qpack_err_header_exceeds_table_size = "header exceeds table size";
147 const char *h2o_qpack_err_invalid_max_size = "invalid max size";
148 const char *h2o_qpack_err_invalid_static_reference = "invalid static reference";
149 const char *h2o_qpack_err_invalid_dynamic_reference = "invalid dynamic reference";
150 const char *h2o_qpack_err_invalid_duplicate = "invalid duplicate";
151 const char *h2o_qpack_err_invalid_pseudo_header = "invalid pseudo header";
152 
header_table_init(struct st_h2o_qpack_header_table_t * table,size_t max_size)153 static void header_table_init(struct st_h2o_qpack_header_table_t *table, size_t max_size)
154 {
155     *table = (struct st_h2o_qpack_header_table_t){NULL, NULL, NULL, NULL, 1, 0, max_size};
156 }
157 
header_table_dispose(struct st_h2o_qpack_header_table_t * table)158 static void header_table_dispose(struct st_h2o_qpack_header_table_t *table)
159 {
160     while (table->first != table->last)
161         h2o_mem_release_shared(*table->first++);
162     free(table->buf_start);
163 }
164 
header_table_evict(struct st_h2o_qpack_header_table_t * table,size_t delta)165 static void header_table_evict(struct st_h2o_qpack_header_table_t *table, size_t delta)
166 {
167     while (table->first != table->last) {
168         if (table->num_bytes + delta <= table->max_size)
169             return;
170         table->num_bytes -= (*table->first)->name->len + (*table->first)->value_len + HEADER_ENTRY_SIZE_OFFSET;
171         h2o_mem_release_shared(*table->first);
172         *table->first++ = NULL;
173         ++table->base_offset;
174     }
175     assert(table->num_bytes == 0);
176 }
177 
header_table_insert(struct st_h2o_qpack_header_table_t * table,struct st_h2o_qpack_header_t * added)178 static void header_table_insert(struct st_h2o_qpack_header_table_t *table, struct st_h2o_qpack_header_t *added)
179 {
180     header_table_evict(table, added->name->len + added->value_len + HEADER_ENTRY_SIZE_OFFSET);
181 
182     if (table->last == table->buf_end) {
183         size_t count = table->last - table->first, new_capacity = count <= 2 ? 4 : count * 2;
184         if (new_capacity > table->buf_end - table->buf_start) {
185             struct st_h2o_qpack_header_t **newbuf = h2o_mem_alloc(sizeof(*newbuf) * new_capacity);
186             memcpy(newbuf, table->first, sizeof(*newbuf) * count);
187             free(table->buf_start);
188             table->buf_start = newbuf;
189             table->first = newbuf;
190             table->last = newbuf + count;
191             table->buf_end = newbuf + new_capacity;
192         } else {
193             assert(table->buf_start != table->first);
194             memmove(table->buf_start, table->first, sizeof(*table->buf_start) * count);
195             table->first = table->buf_start;
196             table->last = table->buf_start + count;
197         }
198         memset(table->last, 0, sizeof(*table->last) * (table->buf_end - table->last));
199     }
200     *table->last++ = added;
201     table->num_bytes += added->name->len + added->value_len + HEADER_ENTRY_SIZE_OFFSET;
202 }
203 
resolve_static_abs(int64_t index,const char ** err_desc)204 static const h2o_qpack_static_table_entry_t *resolve_static_abs(int64_t index, const char **err_desc)
205 {
206     if (index >= sizeof(h2o_qpack_static_table) / sizeof(h2o_qpack_static_table[0])) {
207         *err_desc = h2o_qpack_err_invalid_static_reference;
208         return NULL;
209     }
210     return h2o_qpack_static_table + index;
211 }
212 
resolve_dynamic_abs(struct st_h2o_qpack_header_table_t * table,int64_t index,const char ** err_desc)213 static struct st_h2o_qpack_header_t *resolve_dynamic_abs(struct st_h2o_qpack_header_table_t *table, int64_t index,
214                                                          const char **err_desc)
215 {
216     if (index < table->base_offset)
217         goto Invalid;
218     index -= table->base_offset;
219     if (index >= table->last - table->first)
220         goto Invalid;
221     return table->first[index];
222 Invalid:
223     *err_desc = h2o_qpack_err_invalid_dynamic_reference;
224     return NULL;
225 }
226 
decode_int(int64_t * value,const uint8_t ** src,const uint8_t * src_end,unsigned prefix_bits)227 static int decode_int(int64_t *value, const uint8_t **src, const uint8_t *src_end, unsigned prefix_bits)
228 {
229     if ((*value = h2o_hpack_decode_int(src, src_end, prefix_bits)) < 0)
230         return *value == H2O_HTTP2_ERROR_INCOMPLETE ? H2O_HTTP3_ERROR_INCOMPLETE : H2O_HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
231     return 0;
232 }
233 
decode_value(char * outbuf,unsigned * soft_errors,int is_huff,const uint8_t * src,size_t srclen,const char ** err_desc)234 static size_t decode_value(char *outbuf, unsigned *soft_errors, int is_huff, const uint8_t *src, size_t srclen,
235                            const char **err_desc)
236 {
237     size_t outlen;
238 
239     if (is_huff) {
240         if ((outlen = h2o_hpack_decode_huffman(outbuf, soft_errors, src, srclen, 0, err_desc)) == SIZE_MAX)
241             return SIZE_MAX;
242     } else {
243         h2o_hpack_validate_header_value(soft_errors, (void *)src, srclen);
244         memcpy(outbuf, src, srclen);
245         outlen = srclen;
246     }
247     outbuf[outlen] = '\0';
248 
249     return outlen;
250 }
251 
h2o_qpack_create_decoder(uint32_t header_table_size,uint16_t max_blocked)252 h2o_qpack_decoder_t *h2o_qpack_create_decoder(uint32_t header_table_size, uint16_t max_blocked)
253 {
254     h2o_qpack_decoder_t *qpack = h2o_mem_alloc(sizeof(*qpack));
255 
256     qpack->insert_count = 0;
257     qpack->header_table_size = header_table_size;
258     qpack->max_entries = header_table_size / 32;
259     qpack->total_inserts = 0;
260     qpack->max_blocked = max_blocked;
261     header_table_init(&qpack->table, qpack->header_table_size);
262     memset(&qpack->blocked_streams, 0, sizeof(qpack->blocked_streams));
263 
264     return qpack;
265 }
266 
h2o_qpack_destroy_decoder(h2o_qpack_decoder_t * qpack)267 void h2o_qpack_destroy_decoder(h2o_qpack_decoder_t *qpack)
268 {
269     header_table_dispose(&qpack->table);
270     free(qpack->blocked_streams.list.entries);
271     free(qpack);
272 }
273 
decoder_link_blocked(h2o_qpack_decoder_t * qpack,int64_t stream_id,int64_t largest_ref)274 static void decoder_link_blocked(h2o_qpack_decoder_t *qpack, int64_t stream_id, int64_t largest_ref)
275 {
276     size_t i;
277 
278     h2o_vector_reserve(NULL, &qpack->blocked_streams.list, qpack->blocked_streams.list.size + 1);
279     for (i = qpack->blocked_streams.list.size; i != 0; --i)
280         if (qpack->blocked_streams.list.entries[i - 1].largest_ref <= largest_ref)
281             break;
282     if (i != qpack->blocked_streams.list.size)
283         memmove(qpack->blocked_streams.list.entries + i + 1, qpack->blocked_streams.list.entries + i,
284                 sizeof(qpack->blocked_streams.list.entries[0]) * (qpack->blocked_streams.list.size - i));
285     qpack->blocked_streams.list.entries[i] = (struct st_h2o_qpack_blocked_streams_t){stream_id, largest_ref};
286     ++qpack->blocked_streams.list.size;
287 }
288 
decoder_insert(h2o_qpack_decoder_t * qpack,struct st_h2o_qpack_header_t * added)289 static void decoder_insert(h2o_qpack_decoder_t *qpack, struct st_h2o_qpack_header_t *added)
290 {
291     ++qpack->insert_count;
292     ++qpack->total_inserts;
293     fprintf(stderr, "#%s:%" PRIu64 ":%.*s\t%.*s\n", __FUNCTION__, qpack->total_inserts, (int)added->name->len, added->name->base,
294             (int)added->value_len, added->value);
295     header_table_insert(&qpack->table, added);
296 }
297 
decode_value_and_insert(h2o_qpack_decoder_t * qpack,struct st_h2o_qpack_header_t * header,int is_huff,const uint8_t * qstr,size_t qstrlen,const char ** err_desc)298 static int decode_value_and_insert(h2o_qpack_decoder_t *qpack, struct st_h2o_qpack_header_t *header, int is_huff,
299                                    const uint8_t *qstr, size_t qstrlen, const char **err_desc)
300 {
301     if ((header->value_len = decode_value(header->value, &header->soft_errors, is_huff, qstr, qstrlen, err_desc)) == SIZE_MAX)
302         goto Fail;
303     if (header->name->len + header->value_len + HEADER_ENTRY_SIZE_OFFSET > qpack->table.max_size) {
304         *err_desc = h2o_qpack_err_header_exceeds_table_size;
305         goto Fail;
306     }
307     decoder_insert(qpack, header);
308     return 0;
309 Fail:
310     h2o_mem_release_shared(header);
311     return H2O_HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
312 }
313 
insert_token_header(h2o_qpack_decoder_t * qpack,const h2o_token_t * name,int value_is_huff,const uint8_t * value,size_t value_len,const char ** err_desc)314 static int insert_token_header(h2o_qpack_decoder_t *qpack, const h2o_token_t *name, int value_is_huff, const uint8_t *value,
315                                size_t value_len, const char **err_desc)
316 {
317     struct st_h2o_qpack_header_t *header =
318         h2o_mem_alloc_shared(NULL, offsetof(struct st_h2o_qpack_header_t, value) + (value_len * 2) + 1, NULL);
319 
320     header->name = (h2o_iovec_t *)&name->buf;
321     header->soft_errors = 0;
322     return decode_value_and_insert(qpack, header, value_is_huff, value, value_len, err_desc);
323 }
324 
insert_literal_header(h2o_qpack_decoder_t * qpack,const char * name,size_t name_len,int value_is_huff,const uint8_t * value,size_t value_len,unsigned soft_errors,const char ** err_desc)325 static int insert_literal_header(h2o_qpack_decoder_t *qpack, const char *name, size_t name_len, int value_is_huff,
326                                  const uint8_t *value, size_t value_len, unsigned soft_errors, const char **err_desc)
327 {
328     size_t value_capacity = (value_is_huff ? value_len * 2 : value_len) + 1;
329     struct st_h2o_qpack_header_t *header =
330         h2o_mem_alloc_shared(NULL, offsetof(struct st_h2o_qpack_header_t, value) + value_capacity + name_len + 1, NULL);
331 
332     header->_name_buf = h2o_iovec_init(header->value + value_capacity, name_len);
333     memcpy(header->_name_buf.base, name, name_len);
334     header->_name_buf.base[name_len] = '\0';
335     header->_name_buf.len = name_len;
336     header->name = &header->_name_buf;
337     header->soft_errors = soft_errors;
338 
339     return decode_value_and_insert(qpack, header, value_is_huff, value, value_len, err_desc);
340 }
341 
qpack_table_total_inserts(struct st_h2o_qpack_header_table_t * table)342 static int64_t qpack_table_total_inserts(struct st_h2o_qpack_header_table_t *table)
343 {
344     return table->base_offset + (table->last - table->first);
345 }
346 
insert_with_name_reference(h2o_qpack_decoder_t * qpack,int name_is_static,int64_t name_index,int value_is_huff,const uint8_t * value,int64_t value_len,const char ** err_desc)347 static int insert_with_name_reference(h2o_qpack_decoder_t *qpack, int name_is_static, int64_t name_index, int value_is_huff,
348                                       const uint8_t *value, int64_t value_len, const char **err_desc)
349 {
350     if (value_len >= MAX_HEADER_VALUE_LENGTH) {
351         *err_desc = h2o_qpack_err_header_value_too_long;
352         return H2O_HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
353     }
354 
355     if (name_is_static) {
356         const h2o_qpack_static_table_entry_t *ref;
357         if ((ref = resolve_static_abs(name_index, err_desc)) == NULL)
358             return H2O_HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
359         return insert_token_header(qpack, ref->name, value_is_huff, value, value_len, err_desc);
360     } else {
361         struct st_h2o_qpack_header_t *ref;
362         int64_t base_index = qpack_table_total_inserts(&qpack->table) - 1;
363         if (name_index > base_index) {
364             *err_desc = h2o_qpack_err_invalid_dynamic_reference;
365             return H2O_HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
366         }
367         if ((ref = resolve_dynamic_abs(&qpack->table, base_index - name_index, err_desc)) == NULL)
368             return H2O_HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
369         if (h2o_iovec_is_token(ref->name)) {
370             return insert_token_header(qpack, (h2o_token_t *)ref->name, value_is_huff, value, value_len, err_desc);
371         } else {
372             return insert_literal_header(qpack, ref->name->base, ref->name->len, value_is_huff, value, value_len,
373                                          ref->soft_errors & H2O_HPACK_SOFT_ERROR_BIT_INVALID_NAME, err_desc);
374         }
375     }
376 }
377 
insert_without_name_reference(h2o_qpack_decoder_t * qpack,int qnhuff,const uint8_t * qn,int64_t qnlen,int qvhuff,const uint8_t * qv,int64_t qvlen,const char ** err_desc)378 static int insert_without_name_reference(h2o_qpack_decoder_t *qpack, int qnhuff, const uint8_t *qn, int64_t qnlen, int qvhuff,
379                                          const uint8_t *qv, int64_t qvlen, const char **err_desc)
380 {
381     h2o_iovec_t name;
382     unsigned soft_errors = 0;
383 
384     if (qnlen >= MAX_HEADER_NAME_LENGTH) {
385         *err_desc = h2o_qpack_err_header_name_too_long;
386         return H2O_HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
387     }
388     if (qvlen >= MAX_HEADER_VALUE_LENGTH) {
389         *err_desc = h2o_qpack_err_header_value_too_long;
390         return H2O_HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
391     }
392 
393     if (qnhuff) {
394         name.base = alloca(qnlen * 2);
395         if ((name.len = h2o_hpack_decode_huffman(name.base, &soft_errors, qn, qnlen, 1, err_desc)) == SIZE_MAX)
396             return H2O_HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
397     } else {
398         if (!h2o_hpack_validate_header_name(&soft_errors, (void *)qn, qnlen, err_desc))
399             return H2O_HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
400         name = h2o_iovec_init(qn, qnlen);
401     }
402 
403     const h2o_token_t *token;
404     if ((token = h2o_lookup_token(name.base, name.len)) != NULL) {
405         return insert_token_header(qpack, token, qvhuff, qv, qvlen, err_desc);
406     } else {
407         return insert_literal_header(qpack, name.base, name.len, qvhuff, qv, qvlen, soft_errors, err_desc);
408     }
409 }
410 
duplicate(h2o_qpack_decoder_t * qpack,int64_t index,const char ** err_desc)411 static int duplicate(h2o_qpack_decoder_t *qpack, int64_t index, const char **err_desc)
412 {
413     if (index >= qpack->table.last - qpack->table.first) {
414         *err_desc = h2o_qpack_err_invalid_duplicate;
415         return H2O_HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
416     }
417 
418     struct st_h2o_qpack_header_t *header = qpack->table.last[-index - 1];
419     h2o_mem_addref_shared(header);
420     decoder_insert(qpack, header);
421     return 0;
422 }
423 
dynamic_table_size_update(h2o_qpack_decoder_t * qpack,int64_t max_size,const char ** err_desc)424 static int dynamic_table_size_update(h2o_qpack_decoder_t *qpack, int64_t max_size, const char **err_desc)
425 {
426     if (max_size > qpack->header_table_size) {
427         *err_desc = h2o_qpack_err_invalid_max_size;
428         return H2O_HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
429     }
430 
431     qpack->table.max_size = max_size;
432     header_table_evict(&qpack->table, 0);
433     return 0;
434 }
435 
h2o_qpack_decoder_handle_input(h2o_qpack_decoder_t * qpack,int64_t ** unblocked_stream_ids,size_t * num_unblocked,const uint8_t ** _src,const uint8_t * src_end,const char ** err_desc)436 int h2o_qpack_decoder_handle_input(h2o_qpack_decoder_t *qpack, int64_t **unblocked_stream_ids, size_t *num_unblocked,
437                                    const uint8_t **_src, const uint8_t *src_end, const char **err_desc)
438 {
439     if (qpack->blocked_streams.num_unblocked != 0) {
440         size_t remaining = qpack->blocked_streams.list.size - qpack->blocked_streams.num_unblocked;
441         if (remaining != 0)
442             memmove(qpack->blocked_streams.list.entries, qpack->blocked_streams.list.entries + remaining,
443                     sizeof(qpack->blocked_streams.list.entries[0]) * remaining);
444         qpack->blocked_streams.list.size = remaining;
445         qpack->blocked_streams.num_unblocked = 0;
446     }
447 
448     const uint8_t *src = *_src;
449     int ret = 0;
450 
451     while (src != src_end && ret == 0) {
452         switch (*src >> 5) {
453         default: /* insert with name reference */ {
454             int64_t name_index, value_len;
455             int name_is_static = (*src & 0x40) != 0;
456             if ((ret = decode_int(&name_index, &src, src_end, 6)) != 0)
457                 goto Exit;
458             if (src == src_end)
459                 goto Exit;
460             int value_is_huff = (*src & 0x80) != 0;
461             if ((ret = decode_int(&value_len, &src, src_end, 7)) != 0)
462                 goto Exit;
463             if (!(src + value_len <= src_end))
464                 goto Exit;
465             ret = insert_with_name_reference(qpack, name_is_static, name_index, value_is_huff, src, value_len, err_desc);
466             src += value_len;
467         } break;
468         case 2:
469         case 3: /* insert without name reference */ {
470             int64_t name_len, value_len;
471             int name_is_huff = (*src & 0x20) != 0;
472             if ((ret = decode_int(&name_len, &src, src_end, 5)) != 0)
473                 goto Exit;
474             if (!(src + name_len < src_end))
475                 goto Exit;
476             const uint8_t *name = src;
477             src += name_len;
478             int value_is_huff = (*src & 0x80) != 0;
479             if ((ret = decode_int(&value_len, &src, src_end, 7)) != 0)
480                 goto Exit;
481             if (!(src + value_len <= src_end))
482                 goto Exit;
483             ret = insert_without_name_reference(qpack, name_is_huff, name, name_len, value_is_huff, src, value_len, err_desc);
484             src += value_len;
485         } break;
486         case 0: /* duplicate */ {
487             int64_t index;
488             if ((ret = decode_int(&index, &src, src_end, 5)) != 0)
489                 goto Exit;
490             ret = duplicate(qpack, index, err_desc);
491         } break;
492         case 1: /* dynamic table size update */ {
493             int64_t max_size;
494             if ((ret = decode_int(&max_size, &src, src_end, 5)) != 0)
495                 goto Exit;
496             ret = dynamic_table_size_update(qpack, max_size, err_desc);
497         } break;
498         }
499         *_src = src;
500     }
501 
502 Exit:
503     if (ret == H2O_HTTP2_ERROR_INCOMPLETE)
504         ret = 0;
505     if (ret == 0) {
506         /* build list of newly unblocked streams ids reusing the memory of the blocked streams list (nasty!) */
507         *unblocked_stream_ids = &qpack->blocked_streams.list.entries[0].stream_id;
508         for (qpack->blocked_streams.num_unblocked = 0; qpack->blocked_streams.num_unblocked < qpack->blocked_streams.list.size;
509              ++qpack->blocked_streams.num_unblocked) {
510             if (qpack->blocked_streams.list.entries[qpack->blocked_streams.num_unblocked].largest_ref > qpack->total_inserts)
511                 break;
512             (*unblocked_stream_ids)[qpack->blocked_streams.num_unblocked] =
513                 qpack->blocked_streams.list.entries[qpack->blocked_streams.num_unblocked].stream_id;
514         }
515         *num_unblocked = qpack->blocked_streams.num_unblocked;
516     }
517     return (int)ret;
518 }
519 
h2o_qpack_decoder_send_state_sync(h2o_qpack_decoder_t * qpack,uint8_t * outbuf)520 size_t h2o_qpack_decoder_send_state_sync(h2o_qpack_decoder_t *qpack, uint8_t *outbuf)
521 {
522     if (qpack->insert_count == 0)
523         return 0;
524 
525     uint8_t *dst = outbuf;
526     *dst = 0;
527     dst = h2o_hpack_encode_int(dst, qpack->insert_count, 6);
528     qpack->insert_count = 0;
529 
530     return dst - outbuf;
531 }
532 
h2o_qpack_decoder_send_stream_cancel(h2o_qpack_decoder_t * qpack,uint8_t * outbuf,int64_t stream_id)533 size_t h2o_qpack_decoder_send_stream_cancel(h2o_qpack_decoder_t *qpack, uint8_t *outbuf, int64_t stream_id)
534 {
535     outbuf[0] = 0x40;
536     return h2o_hpack_encode_int(outbuf, stream_id, 6) - outbuf;
537 }
538 
resolve_static(const uint8_t ** src,const uint8_t * src_end,unsigned prefix_bits,const char ** err_desc)539 static const h2o_qpack_static_table_entry_t *resolve_static(const uint8_t **src, const uint8_t *src_end, unsigned prefix_bits,
540                                                             const char **err_desc)
541 {
542     int64_t index;
543 
544     if (decode_int(&index, src, src_end, prefix_bits) != 0)
545         goto Fail;
546     assert(index >= 0);
547     if (!(index < sizeof(h2o_qpack_static_table) / sizeof(h2o_qpack_static_table[0])))
548         goto Fail;
549     return h2o_qpack_static_table + index;
550 
551 Fail:
552     *err_desc = h2o_qpack_err_invalid_static_reference;
553     return NULL;
554 }
555 
resolve_dynamic(struct st_h2o_qpack_header_table_t * table,int64_t base_index,const uint8_t ** src,const uint8_t * src_end,unsigned prefix_bits,const char ** err_desc)556 static struct st_h2o_qpack_header_t *resolve_dynamic(struct st_h2o_qpack_header_table_t *table, int64_t base_index,
557                                                      const uint8_t **src, const uint8_t *src_end, unsigned prefix_bits,
558                                                      const char **err_desc)
559 {
560     int64_t off;
561 
562     if (decode_int(&off, src, src_end, prefix_bits) != 0 || off >= base_index) {
563         *err_desc = h2o_qpack_err_invalid_dynamic_reference;
564         return NULL;
565     }
566     return resolve_dynamic_abs(table, base_index - off, err_desc);
567 }
568 
resolve_dynamic_postbase(struct st_h2o_qpack_header_table_t * table,int64_t base_index,const uint8_t ** src,const uint8_t * src_end,unsigned prefix_bits,const char ** err_desc)569 static struct st_h2o_qpack_header_t *resolve_dynamic_postbase(struct st_h2o_qpack_header_table_t *table, int64_t base_index,
570                                                               const uint8_t **src, const uint8_t *src_end, unsigned prefix_bits,
571                                                               const char **err_desc)
572 {
573     int64_t off;
574 
575     if (decode_int(&off, src, src_end, prefix_bits) != 0 || off > INT64_MAX - base_index - 1) {
576         *err_desc = h2o_qpack_err_invalid_dynamic_reference;
577         return NULL;
578     }
579     return resolve_dynamic_abs(table, base_index + off + 1, err_desc);
580 }
581 
decode_header_name_literal(h2o_mem_pool_t * pool,unsigned * soft_errors,const uint8_t ** src,const uint8_t * src_end,unsigned prefix_bits,const char ** err_desc)582 static h2o_iovec_t *decode_header_name_literal(h2o_mem_pool_t *pool, unsigned *soft_errors, const uint8_t **src,
583                                                const uint8_t *src_end, unsigned prefix_bits, const char **err_desc)
584 {
585     h2o_iovec_t buf = {NULL};
586     const h2o_token_t *token;
587     int is_huff;
588     int64_t len;
589 
590     /* obtain flags and length */
591     is_huff = (**src >> prefix_bits) & 1;
592     if (decode_int(&len, src, src_end, prefix_bits) != 0 || len > MAX_HEADER_NAME_LENGTH) {
593         *err_desc = h2o_qpack_err_header_name_too_long;
594         goto Fail;
595     }
596     if (src_end - *src < len)
597         goto Fail;
598 
599     /* decode and convert to token (if possible) */
600     if (is_huff) {
601         buf.base = h2o_mem_alloc_pool(pool, char, len * 2 + 1);
602         if ((buf.len = h2o_hpack_decode_huffman(buf.base, soft_errors, *src, len, 1, err_desc)) == SIZE_MAX)
603             goto Fail;
604         buf.base[buf.len] = '\0';
605         token = h2o_lookup_token(buf.base, buf.len);
606     } else if ((token = h2o_lookup_token((const char *)*src, len)) != NULL) {
607         /* was an uncompressed token */
608     } else {
609         if (!h2o_hpack_validate_header_name(soft_errors, (void *)*src, len, err_desc))
610             goto Fail;
611         buf = h2o_strdup(pool, (void *)*src, len);
612     }
613     *src += len;
614 
615     /* return the result */
616     if (token != NULL)
617         return (h2o_iovec_t *)&token->buf;
618     h2o_iovec_t *ret = h2o_mem_alloc_pool(pool, h2o_iovec_t, 1);
619     *ret = buf;
620     return ret;
621 
622 Fail:
623     return NULL;
624 }
625 
decode_header_value_literal(h2o_mem_pool_t * pool,unsigned * soft_errors,const uint8_t ** src,const uint8_t * src_end,const char ** err_desc)626 static h2o_iovec_t decode_header_value_literal(h2o_mem_pool_t *pool, unsigned *soft_errors, const uint8_t **src,
627                                                const uint8_t *src_end, const char **err_desc)
628 {
629     h2o_iovec_t buf;
630     int64_t len;
631 
632     /* validate *src pointer before dereferencing it for the huffman bit check */
633     if (!(*src < src_end))
634         goto Fail;
635     int is_huff = (**src & 0x80) != 0;
636 
637     if (decode_int(&len, src, src_end, 7) != 0 || len > MAX_HEADER_VALUE_LENGTH) {
638         *err_desc = h2o_qpack_err_header_value_too_long;
639         goto Fail;
640     }
641     if (src_end - *src < len)
642         goto Fail;
643 
644     buf.base = h2o_mem_alloc_pool(pool, char, is_huff ? len * 2 + 1 : len + 1);
645     if ((buf.len = decode_value(buf.base, soft_errors, is_huff, *src, len, err_desc)) == SIZE_MAX)
646         goto Fail;
647     *src += len;
648 
649     return buf;
650 Fail:
651     return h2o_iovec_init(NULL, 0);
652 }
653 
654 struct st_h2o_qpack_decode_header_ctx_t {
655     h2o_qpack_decoder_t *qpack;
656     /**
657      * These values are non-negative.
658      */
659     int64_t req_insert_count, base_index;
660 };
661 
send_header_ack(h2o_qpack_decoder_t * qpack,struct st_h2o_qpack_decode_header_ctx_t * ctx,uint8_t * outbuf,int64_t stream_id)662 static size_t send_header_ack(h2o_qpack_decoder_t *qpack, struct st_h2o_qpack_decode_header_ctx_t *ctx, uint8_t *outbuf,
663                               int64_t stream_id)
664 {
665     if (ctx->req_insert_count == 0)
666         return 0;
667     outbuf[0] = 0x80;
668     return h2o_hpack_encode_int(outbuf, stream_id, 7) - outbuf;
669 }
670 
decode_header(h2o_mem_pool_t * pool,void * _ctx,h2o_iovec_t ** name,h2o_iovec_t * value,const uint8_t ** src,const uint8_t * src_end,const char ** err_desc)671 static int decode_header(h2o_mem_pool_t *pool, void *_ctx, h2o_iovec_t **name, h2o_iovec_t *value, const uint8_t **src,
672                          const uint8_t *src_end, const char **err_desc)
673 {
674     struct st_h2o_qpack_decode_header_ctx_t *ctx = _ctx;
675     unsigned soft_errors;
676 
677     switch (**src >> 4) {
678     case 12:
679     case 13:
680     case 14:
681     case 15: /* indexed static header field */ {
682         const h2o_qpack_static_table_entry_t *entry;
683         if ((entry = resolve_static(src, src_end, 6, err_desc)) == NULL)
684             goto Fail;
685         *name = (h2o_iovec_t *)&entry->name->buf;
686         *value = entry->value;
687         soft_errors = 0;
688     } break;
689     case 8:
690     case 9:
691     case 10:
692     case 11: /* indexed dynamic header field */ {
693         struct st_h2o_qpack_header_t *entry;
694         if ((entry = resolve_dynamic(&ctx->qpack->table, ctx->base_index, src, src_end, 6, err_desc)) == NULL)
695             goto Fail;
696         h2o_mem_link_shared(pool, entry);
697         *name = entry->name;
698         *value = h2o_iovec_init(entry->value, entry->value_len);
699         soft_errors = entry->soft_errors;
700     } break;
701     case 5:
702     case 7: /* literal header field with static name reference */ {
703         const h2o_qpack_static_table_entry_t *entry;
704         if ((entry = resolve_static(src, src_end, 4, err_desc)) == NULL)
705             goto Fail;
706         *name = (h2o_iovec_t *)&entry->name->buf;
707         soft_errors = 0;
708         if ((*value = decode_header_value_literal(pool, &soft_errors, src, src_end, err_desc)).base == NULL)
709             goto Fail;
710     } break;
711     case 4:
712     case 6: /* literal header field with dynamic name reference */ {
713         struct st_h2o_qpack_header_t *entry;
714         if ((entry = resolve_dynamic(&ctx->qpack->table, ctx->base_index, src, src_end, 4, err_desc)) == NULL)
715             goto Fail;
716         h2o_mem_link_shared(pool, entry);
717         *name = entry->name;
718         soft_errors = (entry->soft_errors) & H2O_HPACK_SOFT_ERROR_BIT_INVALID_NAME;
719         if ((*value = decode_header_value_literal(pool, &soft_errors, src, src_end, err_desc)).base == NULL)
720             goto Fail;
721     } break;
722     case 2:
723     case 3: /* literal header field without name reference */ {
724         soft_errors = 0;
725         if ((*name = decode_header_name_literal(pool, &soft_errors, src, src_end, 3, err_desc)) == NULL)
726             goto Fail;
727         if ((*value = decode_header_value_literal(pool, &soft_errors, src, src_end, err_desc)).base == NULL)
728             goto Fail;
729     } break;
730     case 1: /* indexed header field with post-base index */ {
731         struct st_h2o_qpack_header_t *entry;
732         if ((entry = resolve_dynamic_postbase(&ctx->qpack->table, ctx->base_index, src, src_end, 4, err_desc)) == NULL)
733             goto Fail;
734         h2o_mem_link_shared(pool, entry);
735         *name = entry->name;
736         *value = h2o_iovec_init(entry->value, entry->value_len);
737         soft_errors = entry->soft_errors;
738     } break;
739     case 0: /* literal header field with post-base name reference */ {
740         struct st_h2o_qpack_header_t *entry;
741         if ((entry = resolve_dynamic_postbase(&ctx->qpack->table, ctx->base_index, src, src_end, 3, err_desc)) == NULL)
742             goto Fail;
743         h2o_mem_link_shared(pool, entry);
744         *name = entry->name;
745         soft_errors = (entry->soft_errors) & H2O_HPACK_SOFT_ERROR_BIT_INVALID_NAME;
746         if ((*value = decode_header_value_literal(pool, &soft_errors, src, src_end, err_desc)).base == NULL)
747             goto Fail;
748     } break;
749     default:
750         h2o_fatal("unreachable");
751         soft_errors = 0;
752         break;
753     }
754 
755     if (soft_errors != 0) {
756         *err_desc = (soft_errors & H2O_HPACK_SOFT_ERROR_BIT_INVALID_NAME) != 0
757                         ? h2o_hpack_soft_err_found_invalid_char_in_header_name
758                         : h2o_hpack_soft_err_found_invalid_char_in_header_value;
759         return H2O_HTTP2_ERROR_INVALID_HEADER_CHAR;
760     }
761     return 0;
762 Fail:
763     return H2O_HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
764 }
765 
parse_decode_context(h2o_qpack_decoder_t * qpack,struct st_h2o_qpack_decode_header_ctx_t * ctx,int64_t stream_id,const uint8_t ** src,const uint8_t * src_end)766 static int parse_decode_context(h2o_qpack_decoder_t *qpack, struct st_h2o_qpack_decode_header_ctx_t *ctx, int64_t stream_id,
767                                 const uint8_t **src, const uint8_t *src_end)
768 {
769     ctx->qpack = qpack;
770 
771     /* largest reference */
772     if (decode_int(&ctx->req_insert_count, src, src_end, 8) != 0)
773         return H2O_HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
774     if (ctx->req_insert_count > 0) {
775         if (qpack->max_entries == 0)
776             return H2O_HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
777         const uint32_t full_range = 2 * qpack->max_entries;
778         uint64_t max_value = qpack->total_inserts + qpack->max_entries;
779         uint64_t rounded = max_value / full_range * full_range;
780         ctx->req_insert_count += rounded - 1;
781         if (ctx->req_insert_count > max_value) {
782             if (ctx->req_insert_count <= full_range)
783                 return H2O_HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
784             ctx->req_insert_count -= full_range;
785         }
786         if (ctx->req_insert_count == 0)
787             return H2O_HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
788         /* Peer cannot send no more than PTLS_QUICINT_MAX instructions. That is because one QPACK instruction is no smaller than one
789          * byte, and the maximum length of a QUIC stream (that conveys QPACK instructions) is 2^62 bytes in QUIC v1. */
790         if (ctx->req_insert_count > PTLS_QUICINT_MAX)
791             return H2O_HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
792     }
793 
794     /* sign and base index */
795     if (*src >= src_end)
796         return H2O_HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
797     int sign = (**src & 0x80) != 0;
798     int64_t delta_base;
799     if (decode_int(&delta_base, src, src_end, 7) != 0)
800         return H2O_HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
801     if (delta_base > PTLS_QUICINT_MAX)
802         return H2O_HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
803     ctx->base_index = sign == 0 ? ctx->req_insert_count + delta_base : ctx->req_insert_count - delta_base - 1;
804     if (ctx->base_index < 0) {
805         /* Reject negative base index though current QPACK specification doesn't mention such case; let's keep our eyes on
806          * https://github.com/quicwg/base-drafts/issues/4938 */
807         return H2O_HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
808     }
809 
810     /* is the stream blocked? */
811     if (ctx->req_insert_count >= qpack_table_total_inserts(&qpack->table)) {
812         if (qpack->blocked_streams.list.size + 1 >= qpack->max_blocked)
813             return H2O_HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
814         decoder_link_blocked(qpack, stream_id, ctx->req_insert_count);
815         return H2O_HTTP3_ERROR_INCOMPLETE;
816     }
817 
818     return 0;
819 }
820 
normalize_error_code(int err)821 static int normalize_error_code(int err)
822 {
823     /* convert H2 errors (except invaild_header_char) to QPACK error code */
824     if (err < 0 && err != H2O_HTTP2_ERROR_INVALID_HEADER_CHAR)
825         err = H2O_HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
826     return err;
827 }
828 
h2o_qpack_parse_request(h2o_mem_pool_t * pool,h2o_qpack_decoder_t * qpack,int64_t stream_id,h2o_iovec_t * method,const h2o_url_scheme_t ** scheme,h2o_iovec_t * authority,h2o_iovec_t * path,h2o_headers_t * headers,int * pseudo_header_exists_map,size_t * content_length,h2o_cache_digests_t ** digests,h2o_iovec_t * datagram_flow_id,uint8_t * outbuf,size_t * outbufsize,const uint8_t * _src,size_t len,const char ** err_desc)829 int h2o_qpack_parse_request(h2o_mem_pool_t *pool, h2o_qpack_decoder_t *qpack, int64_t stream_id, h2o_iovec_t *method,
830                             const h2o_url_scheme_t **scheme, h2o_iovec_t *authority, h2o_iovec_t *path, h2o_headers_t *headers,
831                             int *pseudo_header_exists_map, size_t *content_length, h2o_cache_digests_t **digests,
832                             h2o_iovec_t *datagram_flow_id, uint8_t *outbuf, size_t *outbufsize, const uint8_t *_src, size_t len,
833                             const char **err_desc)
834 {
835     struct st_h2o_qpack_decode_header_ctx_t ctx;
836     const uint8_t *src = _src, *src_end = src + len;
837     int ret;
838 
839     if ((ret = parse_decode_context(qpack, &ctx, stream_id, &src, src_end)) != 0)
840         return ret;
841     if ((ret = h2o_hpack_parse_request(pool, decode_header, &ctx, method, scheme, authority, path, headers,
842                                        pseudo_header_exists_map, content_length, digests, datagram_flow_id, src, src_end - src, err_desc)) != 0) {
843         /* bail out if the error is a hard error, otherwise build header ack then return */
844         if (ret != H2O_HTTP2_ERROR_INVALID_HEADER_CHAR)
845             return normalize_error_code(ret);
846     }
847 
848     *outbufsize = send_header_ack(qpack, &ctx, outbuf, stream_id);
849     return ret;
850 }
851 
h2o_qpack_parse_response(h2o_mem_pool_t * pool,h2o_qpack_decoder_t * qpack,int64_t stream_id,int * status,h2o_headers_t * headers,h2o_iovec_t * datagram_flow_id,uint8_t * outbuf,size_t * outbufsize,const uint8_t * _src,size_t len,const char ** err_desc)852 int h2o_qpack_parse_response(h2o_mem_pool_t *pool, h2o_qpack_decoder_t *qpack, int64_t stream_id, int *status,
853                              h2o_headers_t *headers, h2o_iovec_t *datagram_flow_id, uint8_t *outbuf, size_t *outbufsize,
854                              const uint8_t *_src, size_t len, const char **err_desc)
855 {
856     struct st_h2o_qpack_decode_header_ctx_t ctx;
857     const uint8_t *src = _src, *src_end = src + len;
858     int ret;
859 
860     if ((ret = parse_decode_context(qpack, &ctx, stream_id, &src, src_end)) != 0)
861         return ret;
862     if ((ret = h2o_hpack_parse_response(pool, decode_header, &ctx, status, headers, datagram_flow_id, src, src_end - src,
863                                         err_desc)) != 0)
864         return normalize_error_code(ret);
865 
866     *outbufsize = send_header_ack(qpack, &ctx, outbuf, stream_id);
867     return 0;
868 }
869 
h2o_qpack_create_encoder(uint32_t header_table_size,uint16_t max_blocked)870 h2o_qpack_encoder_t *h2o_qpack_create_encoder(uint32_t header_table_size, uint16_t max_blocked)
871 {
872     h2o_qpack_encoder_t *qpack = h2o_mem_alloc(sizeof(*qpack));
873     header_table_init(&qpack->table, header_table_size);
874     qpack->largest_known_received = 0;
875     qpack->max_blocked = max_blocked;
876     qpack->num_blocked = 0;
877     memset(&qpack->inflight, 0, sizeof(qpack->inflight));
878     return qpack;
879 }
880 
h2o_qpack_destroy_encoder(h2o_qpack_encoder_t * qpack)881 void h2o_qpack_destroy_encoder(h2o_qpack_encoder_t *qpack)
882 {
883     header_table_dispose(&qpack->table);
884     free(qpack->inflight.entries);
885     free(qpack);
886 }
887 
handle_table_state_synchronize(h2o_qpack_encoder_t * qpack,int64_t insert_count,const char ** err_desc)888 static int handle_table_state_synchronize(h2o_qpack_encoder_t *qpack, int64_t insert_count, const char **err_desc)
889 {
890     if (qpack == NULL || insert_count == 0)
891         goto Error;
892 
893     int64_t new_value = qpack->largest_known_received + insert_count;
894     if (new_value >= qpack_table_total_inserts(&qpack->table))
895         goto Error;
896     qpack->largest_known_received = new_value;
897 
898     return 0;
899 Error:
900     *err_desc = "Table State Synchronize: invalid argument";
901     return H2O_HTTP3_ERROR_QPACK_DECODER_STREAM;
902 }
903 
evict_inflight_by_index(h2o_qpack_encoder_t * qpack,size_t index)904 static void evict_inflight_by_index(h2o_qpack_encoder_t *qpack, size_t index)
905 {
906     if (qpack->inflight.entries[index].encoder_flags.is_blocking)
907         --qpack->num_blocked;
908     --qpack->inflight.size;
909 
910     if (qpack->inflight.size == 0) {
911         free(qpack->inflight.entries);
912         memset(&qpack->inflight, 0, sizeof(qpack->inflight));
913     } else if (index < qpack->inflight.size) {
914         memmove(qpack->inflight.entries + index, qpack->inflight.entries + index + 1, qpack->inflight.size - index);
915     }
916 }
917 
handle_header_ack(h2o_qpack_encoder_t * qpack,int64_t stream_id,const char ** err_desc)918 static int handle_header_ack(h2o_qpack_encoder_t *qpack, int64_t stream_id, const char **err_desc)
919 {
920     size_t i;
921 
922     if (qpack != NULL) {
923         for (i = 0; i < qpack->inflight.size; ++i)
924             if (qpack->inflight.entries[i].stream_id == stream_id)
925                 goto Found;
926     }
927     /* not found */
928     *err_desc = "Header Acknowledgement: invalid stream id";
929     return H2O_HTTP3_ERROR_QPACK_DECODER_STREAM;
930 
931 Found:
932     /* update largest reference */
933     if (qpack->largest_known_received < qpack->inflight.entries[i].largest_ref)
934         qpack->largest_known_received = qpack->inflight.entries[i].largest_ref;
935     /* evict the found entry */
936     evict_inflight_by_index(qpack, i);
937 
938     return 0;
939 }
940 
handle_stream_cancellation(h2o_qpack_encoder_t * qpack,int64_t stream_id,const char ** err_desc)941 static int handle_stream_cancellation(h2o_qpack_encoder_t *qpack, int64_t stream_id, const char **err_desc)
942 {
943     size_t index = 0;
944 
945     if (qpack != NULL) {
946         while (index < qpack->inflight.size) {
947             if (qpack->inflight.entries[index].stream_id == stream_id) {
948                 evict_inflight_by_index(qpack, index);
949             } else {
950                 ++index;
951             }
952         }
953     }
954 
955     return 0;
956 }
957 
h2o_qpack_encoder_handle_input(h2o_qpack_encoder_t * qpack,const uint8_t ** _src,const uint8_t * src_end,const char ** err_desc)958 int h2o_qpack_encoder_handle_input(h2o_qpack_encoder_t *qpack, const uint8_t **_src, const uint8_t *src_end, const char **err_desc)
959 {
960     const uint8_t *src = *_src;
961     int ret = 0;
962 
963     while (src != src_end && ret == 0) {
964         switch (*src >> 6) {
965         case 0: /* table state synchronize */ {
966             int64_t insert_count;
967             if ((ret = decode_int(&insert_count, &src, src_end, 6)) != 0)
968                 goto Exit;
969             ret = handle_table_state_synchronize(qpack, insert_count, err_desc);
970         } break;
971         default: /* header ack */ {
972             int64_t stream_id;
973             if ((ret = decode_int(&stream_id, &src, src_end, 7)) != 0)
974                 goto Exit;
975             ret = handle_header_ack(qpack, stream_id, err_desc);
976         } break;
977         case 1: /* stream cancellation */ {
978             int64_t stream_id;
979             if ((ret = decode_int(&stream_id, &src, src_end, 6)) != 0)
980                 goto Exit;
981             ret = handle_stream_cancellation(qpack, stream_id, err_desc);
982         } break;
983         }
984         *_src = src;
985     }
986 
987 Exit:
988     if (ret == H2O_HTTP3_ERROR_INCOMPLETE)
989         ret = 0;
990     return (int)ret;
991 }
992 
lookup_dynamic(h2o_qpack_encoder_t * qpack,const h2o_iovec_t * name,h2o_iovec_t value,int acked_only,int * is_exact)993 static int64_t lookup_dynamic(h2o_qpack_encoder_t *qpack, const h2o_iovec_t *name, h2o_iovec_t value, int acked_only, int *is_exact)
994 {
995     size_t i;
996     int64_t name_found = -1;
997 
998     for (i = acked_only ? qpack->largest_known_received : qpack_table_total_inserts(&qpack->table) - 1;
999          i >= qpack->table.base_offset; --i) {
1000         struct st_h2o_qpack_header_t *entry = qpack->table.first[i - qpack->table.base_offset];
1001         /* compare names (and continue unless they match) */
1002         if (h2o_iovec_is_token(name)) {
1003             if (name != entry->name)
1004                 continue;
1005         } else {
1006             if (!h2o_memis(name->base, name->len, entry->name->base, entry->name->len))
1007                 continue;
1008         }
1009         /* compare values */
1010         if (h2o_memis(value.base, value.len, entry->value, entry->value_len)) {
1011             *is_exact = 1;
1012             return i;
1013         }
1014         if (name_found == -1)
1015             name_found = i;
1016     }
1017 
1018     *is_exact = 0;
1019     return name_found;
1020 }
1021 
flatten_int(h2o_byte_vector_t * buf,int64_t value,unsigned prefix_bits)1022 static void flatten_int(h2o_byte_vector_t *buf, int64_t value, unsigned prefix_bits)
1023 {
1024     uint8_t *p = h2o_hpack_encode_int(buf->entries + buf->size, value, prefix_bits);
1025     buf->size = p - buf->entries;
1026 }
1027 
flatten_string(h2o_byte_vector_t * buf,const char * src,size_t len,unsigned prefix_bits,int dont_compress)1028 static void flatten_string(h2o_byte_vector_t *buf, const char *src, size_t len, unsigned prefix_bits, int dont_compress)
1029 {
1030     size_t hufflen;
1031 
1032     if (dont_compress || (hufflen = h2o_hpack_encode_huffman(buf->entries + buf->size + 1, (void *)src, len)) == SIZE_MAX) {
1033         /* uncompressed */
1034         buf->entries[buf->size] &= ~((2 << prefix_bits) - 1); /* clear huffman mark */
1035         flatten_int(buf, len, prefix_bits);
1036         memcpy(buf->entries + buf->size, src, len);
1037         buf->size += len;
1038     } else {
1039         /* build huffman header and adjust the location (if necessary) */
1040         uint8_t tmpbuf[H2O_HPACK_ENCODE_INT_MAX_LENGTH], *p = tmpbuf;
1041         *p = buf->entries[buf->size] & ~((1 << prefix_bits) - 1);
1042         *p |= (1 << prefix_bits); /* huffman mark */
1043         p = h2o_hpack_encode_int(p, hufflen, prefix_bits);
1044         if (p - tmpbuf == 1) {
1045             buf->entries[buf->size] = tmpbuf[0];
1046         } else {
1047             memmove(buf->entries + buf->size + (p - tmpbuf), buf->entries + buf->size + 1, hufflen);
1048             memcpy(buf->entries + buf->size, tmpbuf, p - tmpbuf);
1049         }
1050         buf->size += p - tmpbuf + hufflen;
1051     }
1052 }
1053 
emit_insert_with_nameref(h2o_qpack_encoder_t * qpack,h2o_mem_pool_t * pool,h2o_byte_vector_t * buf,int is_static,int64_t index,h2o_iovec_t value)1054 static void emit_insert_with_nameref(h2o_qpack_encoder_t *qpack, h2o_mem_pool_t *pool, h2o_byte_vector_t *buf, int is_static,
1055                                      int64_t index, h2o_iovec_t value)
1056 {
1057     h2o_vector_reserve(pool, buf, buf->size + (H2O_HPACK_ENCODE_INT_MAX_LENGTH * 2) + value.len);
1058     buf->entries[buf->size] = 0x80 | (is_static ? 0x40 : 0);
1059     flatten_int(buf, index, 6);
1060     flatten_string(buf, value.base, value.len, 7, 0);
1061 }
1062 
emit_insert_without_nameref(h2o_qpack_encoder_t * qpack,h2o_mem_pool_t * pool,h2o_byte_vector_t * buf,const h2o_iovec_t * name,h2o_iovec_t value)1063 static void emit_insert_without_nameref(h2o_qpack_encoder_t *qpack, h2o_mem_pool_t *pool, h2o_byte_vector_t *buf,
1064                                         const h2o_iovec_t *name, h2o_iovec_t value)
1065 {
1066     h2o_vector_reserve(pool, buf, buf->size + (H2O_HPACK_ENCODE_INT_MAX_LENGTH * 2) + name->len + value.len);
1067     buf->entries[buf->size] = 0x40;
1068     flatten_string(buf, name->base, name->len, 5, 0);
1069     flatten_string(buf, value.base, value.len, 7, 0);
1070 }
1071 
flatten_static_indexed(struct st_h2o_qpack_flatten_context_t * ctx,int32_t index)1072 static void flatten_static_indexed(struct st_h2o_qpack_flatten_context_t *ctx, int32_t index)
1073 {
1074     h2o_vector_reserve(ctx->pool, &ctx->headers_buf, ctx->headers_buf.size + H2O_HPACK_ENCODE_INT_MAX_LENGTH);
1075     ctx->headers_buf.entries[ctx->headers_buf.size] = 0xc0;
1076     flatten_int(&ctx->headers_buf, index, 6);
1077 }
1078 
flatten_dynamic_indexed(struct st_h2o_qpack_flatten_context_t * ctx,int64_t index)1079 static void flatten_dynamic_indexed(struct st_h2o_qpack_flatten_context_t *ctx, int64_t index)
1080 {
1081     h2o_vector_reserve(ctx->pool, &ctx->headers_buf, ctx->headers_buf.size + H2O_HPACK_ENCODE_INT_MAX_LENGTH);
1082 
1083     if (index > ctx->largest_ref)
1084         ctx->largest_ref = index;
1085 
1086     if (index <= ctx->base_index) {
1087         /* indexed */
1088         ctx->headers_buf.entries[ctx->headers_buf.size] = 0x80;
1089         flatten_int(&ctx->headers_buf, ctx->base_index - index, 6);
1090     } else {
1091         /* indexed (post-base) */
1092         ctx->headers_buf.entries[ctx->headers_buf.size] = 0x10;
1093         flatten_int(&ctx->headers_buf, index - ctx->base_index - 1, 4);
1094     }
1095 }
1096 
flatten_static_nameref(struct st_h2o_qpack_flatten_context_t * ctx,int32_t index,h2o_iovec_t value,int dont_compress)1097 static void flatten_static_nameref(struct st_h2o_qpack_flatten_context_t *ctx, int32_t index, h2o_iovec_t value, int dont_compress)
1098 {
1099     h2o_vector_reserve(ctx->pool, &ctx->headers_buf, ctx->headers_buf.size + H2O_HPACK_ENCODE_INT_MAX_LENGTH * 2 + value.len);
1100     ctx->headers_buf.entries[ctx->headers_buf.size] = 0x50 | (dont_compress ? 0x20 : 0);
1101     flatten_int(&ctx->headers_buf, index, 4);
1102     flatten_string(&ctx->headers_buf, value.base, value.len, 7, dont_compress);
1103 }
1104 
flatten_dynamic_nameref(struct st_h2o_qpack_flatten_context_t * ctx,int64_t index,h2o_iovec_t value,int dont_compress)1105 static void flatten_dynamic_nameref(struct st_h2o_qpack_flatten_context_t *ctx, int64_t index, h2o_iovec_t value, int dont_compress)
1106 {
1107     if (index > ctx->largest_ref)
1108         ctx->largest_ref = index;
1109 
1110     h2o_vector_reserve(ctx->pool, &ctx->headers_buf, ctx->headers_buf.size + H2O_HPACK_ENCODE_INT_MAX_LENGTH * 2 + value.len);
1111     if (index <= ctx->base_index) {
1112         ctx->headers_buf.entries[ctx->headers_buf.size] = 0x40 | (dont_compress ? 0x20 : 0);
1113         flatten_int(&ctx->headers_buf, ctx->base_index - index, 4);
1114     } else {
1115         ctx->headers_buf.entries[ctx->headers_buf.size] = dont_compress ? 0x8 : 0;
1116         flatten_int(&ctx->headers_buf, index - ctx->base_index - 1, 3);
1117     }
1118     flatten_string(&ctx->headers_buf, value.base, value.len, 7, dont_compress);
1119 }
1120 
flatten_without_nameref(struct st_h2o_qpack_flatten_context_t * ctx,const h2o_iovec_t * name,h2o_iovec_t value,int dont_compress)1121 static void flatten_without_nameref(struct st_h2o_qpack_flatten_context_t *ctx, const h2o_iovec_t *name, h2o_iovec_t value,
1122                                     int dont_compress)
1123 {
1124     h2o_vector_reserve(ctx->pool, &ctx->headers_buf,
1125                        ctx->headers_buf.size + H2O_HPACK_ENCODE_INT_MAX_LENGTH * 2 + name->len + value.len);
1126     ctx->headers_buf.entries[ctx->headers_buf.size] = 0x20 | (dont_compress ? 0x10 : 0);
1127     flatten_string(&ctx->headers_buf, name->base, name->len, 3, 0);
1128     flatten_string(&ctx->headers_buf, value.base, value.len, 7, dont_compress);
1129 }
1130 
do_flatten_header(struct st_h2o_qpack_flatten_context_t * ctx,int32_t static_index,int is_exact,int likely_to_repeat,const h2o_iovec_t * name,h2o_iovec_t value,h2o_header_flags_t flags)1131 static void do_flatten_header(struct st_h2o_qpack_flatten_context_t *ctx, int32_t static_index, int is_exact, int likely_to_repeat,
1132                               const h2o_iovec_t *name, h2o_iovec_t value, h2o_header_flags_t flags)
1133 {
1134     int64_t dynamic_index;
1135 
1136     if (static_index >= 0 && is_exact) {
1137         flatten_static_indexed(ctx, static_index);
1138         return;
1139     }
1140 
1141     if (ctx->qpack != NULL) {
1142         /* try dynamic indexed */
1143         if ((dynamic_index = lookup_dynamic(ctx->qpack, name, value, ctx->encoder_buf == NULL, &is_exact)) >= 0 && is_exact) {
1144             flatten_dynamic_indexed(ctx, dynamic_index);
1145             return;
1146         }
1147         /* emit to encoder buf and dynamic index?
1148          * At the moment the strategy is dumb; we emit encoder stream data until the table becomes full. Never triggers eviction.
1149          */
1150         if (likely_to_repeat && ctx->encoder_buf != NULL && ((static_index < 0 && dynamic_index < 0) || value.len >= 8) &&
1151             name->len + value.len + HEADER_ENTRY_SIZE_OFFSET <= ctx->qpack->table.max_size - ctx->qpack->table.num_bytes) {
1152             /* emit instruction to decoder stream */
1153             if (static_index >= 0) {
1154                 emit_insert_with_nameref(ctx->qpack, ctx->pool, ctx->encoder_buf, 1, static_index, value);
1155             } else if (dynamic_index >= 0) {
1156                 emit_insert_with_nameref(ctx->qpack, ctx->pool, ctx->encoder_buf, 0, dynamic_index, value);
1157             } else {
1158                 emit_insert_without_nameref(ctx->qpack, ctx->pool, ctx->encoder_buf, name, value);
1159             }
1160             /* register the entry to table */
1161             struct st_h2o_qpack_header_t *added;
1162             if (h2o_iovec_is_token(name)) {
1163                 added = h2o_mem_alloc_shared(NULL, offsetof(struct st_h2o_qpack_header_t, value) + value.len + 1, NULL);
1164                 added->name = (h2o_iovec_t *)name;
1165             } else {
1166                 added =
1167                     h2o_mem_alloc_shared(NULL, offsetof(struct st_h2o_qpack_header_t, value) + name->len + 1 + value.len + 1, NULL);
1168                 added->name = &added->_name_buf;
1169                 added->_name_buf = h2o_iovec_init(added->value + added->value_len + 1, name->len);
1170                 memcpy(added->_name_buf.base, name->base, name->len);
1171                 added->_name_buf.base[name->len] = '\0';
1172             }
1173             added->value_len = value.len;
1174             memcpy(added->value, value.base, value.len);
1175             added->value[value.len] = '\0';
1176             header_table_insert(&ctx->qpack->table, added);
1177             /* emit header field to headers block */
1178             flatten_dynamic_indexed(ctx, qpack_table_total_inserts(&ctx->qpack->table) - 1);
1179             return;
1180         }
1181     } else {
1182         dynamic_index = -1;
1183     }
1184 
1185     if (static_index >= 0) {
1186         flatten_static_nameref(ctx, static_index, value, flags.dont_compress);
1187     } else if (dynamic_index >= 0) {
1188         flatten_dynamic_nameref(ctx, dynamic_index, value, flags.dont_compress);
1189     } else {
1190         flatten_without_nameref(ctx, name, value, flags.dont_compress);
1191     }
1192 }
1193 
flatten_header(struct st_h2o_qpack_flatten_context_t * ctx,const h2o_header_t * header)1194 static void flatten_header(struct st_h2o_qpack_flatten_context_t *ctx, const h2o_header_t *header)
1195 {
1196     int32_t static_index = -1;
1197     int is_exact = 0, likely_to_repeat = 0;
1198 
1199     /* obtain static index if possible */
1200     if (h2o_iovec_is_token(header->name)) {
1201         const h2o_token_t *token = H2O_STRUCT_FROM_MEMBER(h2o_token_t, buf, header->name);
1202         static_index = h2o_qpack_lookup_static[token - h2o__tokens](header->value, &is_exact);
1203         likely_to_repeat = token->flags.likely_to_repeat;
1204     }
1205 
1206     return do_flatten_header(ctx, static_index, is_exact, likely_to_repeat, header->name, header->value, header->flags);
1207 }
1208 
flatten_known_header_with_static_lookup(struct st_h2o_qpack_flatten_context_t * ctx,h2o_qpack_lookup_static_cb lookup_cb,const h2o_token_t * name,h2o_iovec_t value)1209 static void flatten_known_header_with_static_lookup(struct st_h2o_qpack_flatten_context_t *ctx,
1210                                                     h2o_qpack_lookup_static_cb lookup_cb, const h2o_token_t *name,
1211                                                     h2o_iovec_t value)
1212 {
1213     int is_exact;
1214     int32_t static_index = lookup_cb(value, &is_exact);
1215     assert(index >= 0);
1216 
1217     do_flatten_header(ctx, static_index, is_exact, name->flags.likely_to_repeat, &name->buf, value, (h2o_header_flags_t){0});
1218 }
1219 
1220 /* header of the qpack message that are written afterwards */
1221 static const size_t PREFIX_CAPACITY =
1222     1 /* frame header */ + 8 /* frame payload len */ + H2O_HPACK_ENCODE_INT_MAX_LENGTH + H2O_HPACK_ENCODE_INT_MAX_LENGTH;
1223 
prepare_flatten(struct st_h2o_qpack_flatten_context_t * ctx,h2o_qpack_encoder_t * qpack,h2o_mem_pool_t * pool,int64_t stream_id,h2o_byte_vector_t * encoder_buf)1224 static void prepare_flatten(struct st_h2o_qpack_flatten_context_t *ctx, h2o_qpack_encoder_t *qpack, h2o_mem_pool_t *pool,
1225                             int64_t stream_id, h2o_byte_vector_t *encoder_buf)
1226 {
1227     ctx->qpack = qpack;
1228     ctx->pool = pool;
1229     ctx->stream_id = stream_id;
1230     ctx->encoder_buf = qpack != NULL && qpack->num_blocked < qpack->max_blocked ? encoder_buf : NULL;
1231     ctx->headers_buf = (h2o_byte_vector_t){NULL};
1232     ctx->base_index = qpack != NULL ? qpack_table_total_inserts(&qpack->table) - 1 : 0;
1233     ctx->largest_ref = 0;
1234 
1235     /* allocate some space, hoping to avoid realloc, but not wasting too much */
1236     h2o_vector_reserve(ctx->pool, &ctx->headers_buf, PREFIX_CAPACITY + 100);
1237     ctx->headers_buf.size = PREFIX_CAPACITY;
1238 }
1239 
finalize_flatten(struct st_h2o_qpack_flatten_context_t * ctx)1240 static h2o_iovec_t finalize_flatten(struct st_h2o_qpack_flatten_context_t *ctx)
1241 {
1242     if (ctx->largest_ref == 0) {
1243         ctx->base_index = 0;
1244     } else {
1245         int is_blocking = 0;
1246         /* adjust largest reference to achieve more compact representation on wire without risking blocking */
1247         if (ctx->largest_ref < ctx->qpack->largest_known_received) {
1248             ctx->largest_ref = ctx->qpack->largest_known_received;
1249         } else if (ctx->largest_ref > ctx->qpack->largest_known_received) {
1250             assert(ctx->qpack->num_blocked < ctx->qpack->max_blocked);
1251             ++ctx->qpack->num_blocked;
1252             is_blocking = 1;
1253         }
1254         /* mark as inflight */
1255         h2o_vector_reserve(NULL, &ctx->qpack->inflight, ctx->qpack->inflight.size + 1);
1256         ctx->qpack->inflight.entries[ctx->qpack->inflight.size++] =
1257             (struct st_h2o_qpack_blocked_streams_t){ctx->stream_id, ctx->largest_ref, {{is_blocking}}};
1258     }
1259 
1260     size_t start_off = PREFIX_CAPACITY;
1261 
1262     { /* prepend largest ref and delta base index */
1263         uint8_t buf[H2O_HPACK_ENCODE_INT_MAX_LENGTH * 2], *p = buf;
1264         /* largest_ref */
1265         *p = 0;
1266         p = h2o_hpack_encode_int(p, ctx->largest_ref != 0 ? ctx->largest_ref + 1 : 0, 8);
1267         /* delta base index */
1268         if (ctx->largest_ref <= ctx->base_index) {
1269             *p = 0;
1270             p = h2o_hpack_encode_int(p, ctx->base_index - ctx->largest_ref, 7);
1271         } else {
1272             *p = 0x80;
1273             p = h2o_hpack_encode_int(p, ctx->largest_ref - ctx->base_index - 1, 7);
1274         }
1275         memcpy(ctx->headers_buf.entries + start_off - (p - buf), buf, p - buf);
1276         start_off -= p - buf;
1277     }
1278 
1279     /* prepend frame header */
1280     size_t len_len = quicly_encodev_capacity(ctx->headers_buf.size - start_off);
1281     quicly_encodev(ctx->headers_buf.entries + start_off - len_len, ctx->headers_buf.size - start_off);
1282     start_off -= len_len;
1283     ctx->headers_buf.entries[--start_off] = H2O_HTTP3_FRAME_TYPE_HEADERS;
1284 
1285     return h2o_iovec_init(ctx->headers_buf.entries + start_off, ctx->headers_buf.size - start_off);
1286 }
1287 
h2o_qpack_flatten_request(h2o_qpack_encoder_t * _qpack,h2o_mem_pool_t * _pool,int64_t _stream_id,h2o_byte_vector_t * _encoder_buf,h2o_iovec_t method,const h2o_url_scheme_t * scheme,h2o_iovec_t authority,h2o_iovec_t path,const h2o_header_t * headers,size_t num_headers,h2o_iovec_t datagram_flow_id)1288 h2o_iovec_t h2o_qpack_flatten_request(h2o_qpack_encoder_t *_qpack, h2o_mem_pool_t *_pool, int64_t _stream_id,
1289                                       h2o_byte_vector_t *_encoder_buf, h2o_iovec_t method, const h2o_url_scheme_t *scheme,
1290                                       h2o_iovec_t authority, h2o_iovec_t path, const h2o_header_t *headers, size_t num_headers,
1291                                       h2o_iovec_t datagram_flow_id)
1292 {
1293     struct st_h2o_qpack_flatten_context_t ctx;
1294 
1295     prepare_flatten(&ctx, _qpack, _pool, _stream_id, _encoder_buf);
1296 
1297     /* pseudo headers */
1298     flatten_known_header_with_static_lookup(&ctx, h2o_qpack_lookup_method, H2O_TOKEN_METHOD, method);
1299     int is_connect = h2o_memis(method.base, method.len, H2O_STRLIT("CONNECT"));
1300     if (!is_connect) {
1301         if (scheme == &H2O_URL_SCHEME_HTTP) {
1302             flatten_static_indexed(&ctx, 22);
1303         } else if (scheme == &H2O_URL_SCHEME_HTTPS) {
1304             flatten_static_indexed(&ctx, 23);
1305         } else {
1306             flatten_known_header_with_static_lookup(&ctx, h2o_qpack_lookup_scheme, H2O_TOKEN_SCHEME, scheme->name);
1307         }
1308     }
1309     flatten_known_header_with_static_lookup(&ctx, h2o_qpack_lookup_authority, H2O_TOKEN_AUTHORITY, authority);
1310     if (!is_connect)
1311         flatten_known_header_with_static_lookup(&ctx, h2o_qpack_lookup_path, H2O_TOKEN_PATH, path);
1312 
1313     /* flatten headers */
1314     size_t i;
1315     for (i = 0; i != num_headers; ++i)
1316         flatten_header(&ctx, headers + i);
1317 
1318     if (datagram_flow_id.base != NULL)
1319         flatten_known_header_with_static_lookup(&ctx, h2o_qpack_lookup_datagram_flow_id, H2O_TOKEN_DATAGRAM_FLOW_ID,
1320                                                 datagram_flow_id);
1321 
1322     return finalize_flatten(&ctx);
1323 }
1324 
h2o_qpack_flatten_response(h2o_qpack_encoder_t * _qpack,h2o_mem_pool_t * _pool,int64_t _stream_id,h2o_byte_vector_t * _encoder_buf,int status,const h2o_header_t * headers,size_t num_headers,const h2o_iovec_t * server_name,size_t content_length,h2o_iovec_t datagram_flow_id)1325 h2o_iovec_t h2o_qpack_flatten_response(h2o_qpack_encoder_t *_qpack, h2o_mem_pool_t *_pool, int64_t _stream_id,
1326                                        h2o_byte_vector_t *_encoder_buf, int status, const h2o_header_t *headers, size_t num_headers,
1327                                        const h2o_iovec_t *server_name, size_t content_length, h2o_iovec_t datagram_flow_id)
1328 {
1329     struct st_h2o_qpack_flatten_context_t ctx;
1330 
1331     prepare_flatten(&ctx, _qpack, _pool, _stream_id, _encoder_buf);
1332 
1333     /* pseudo headers */
1334     switch (status) {
1335 #define INDEXED_STATUS(cp, st)                                                                                                     \
1336     case st:                                                                                                                       \
1337         flatten_static_indexed(&ctx, cp);                                                                                          \
1338         break
1339         INDEXED_STATUS(24, 103);
1340         INDEXED_STATUS(25, 200);
1341         INDEXED_STATUS(26, 304);
1342         INDEXED_STATUS(27, 404);
1343         INDEXED_STATUS(28, 503);
1344         INDEXED_STATUS(63, 100);
1345         INDEXED_STATUS(64, 204);
1346         INDEXED_STATUS(65, 206);
1347         INDEXED_STATUS(66, 302);
1348         INDEXED_STATUS(67, 400);
1349         INDEXED_STATUS(68, 403);
1350         INDEXED_STATUS(69, 421);
1351         INDEXED_STATUS(70, 425);
1352         INDEXED_STATUS(71, 500);
1353 #undef INDEXED_STATUS
1354     default: {
1355         char status_str[sizeof(H2O_UINT16_LONGEST_STR)];
1356         sprintf(status_str, "%" PRIu16, (uint16_t)status);
1357         do_flatten_header(&ctx, 24, 0, H2O_TOKEN_STATUS->flags.likely_to_repeat, &H2O_TOKEN_STATUS->buf,
1358                           h2o_iovec_init(status_str, strlen(status_str)), (h2o_header_flags_t){0});
1359     } break;
1360     }
1361 
1362     /* TODO keep some kind of reference to the indexed Server header, and reuse it */
1363     if (server_name != NULL && server_name->len != 0)
1364         do_flatten_header(&ctx, 92, 0, H2O_TOKEN_SERVER->flags.likely_to_repeat, &H2O_TOKEN_SERVER->buf, *server_name,
1365                           (h2o_header_flags_t){0});
1366 
1367     /* content-length */
1368     if (content_length != SIZE_MAX) {
1369         if (content_length == 0) {
1370             flatten_static_indexed(&ctx, 4);
1371         } else {
1372             char cl_str[sizeof(H2O_SIZE_T_LONGEST_STR)];
1373             size_t cl_len = (size_t)sprintf(cl_str, "%zu", content_length);
1374             do_flatten_header(&ctx, 4, 0, H2O_TOKEN_CONTENT_LENGTH->flags.likely_to_repeat, &H2O_TOKEN_CONTENT_LENGTH->buf,
1375                               h2o_iovec_init(cl_str, cl_len), (h2o_header_flags_t){0});
1376         }
1377     }
1378 
1379     /* flatten headers */
1380     size_t i;
1381     for (i = 0; i != num_headers; ++i)
1382         flatten_header(&ctx, headers + i);
1383 
1384     if (datagram_flow_id.base != NULL)
1385         flatten_known_header_with_static_lookup(&ctx, h2o_qpack_lookup_datagram_flow_id, H2O_TOKEN_DATAGRAM_FLOW_ID,
1386                                                 datagram_flow_id);
1387 
1388     return finalize_flatten(&ctx);
1389 }
1390