Coverage Report

Created: 2026-10-17 07:02

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