Coverage Report

Created: 2023-06-07 06:21

/src/h2o/lib/http2/hpack.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2014-2016 DeNA Co., Ltd., Kazuho Oku, Fastly, Inc.
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 "h2o/hpack.h"
27
#include "h2o/http2_common.h"
28
29
738k
#define HEADER_TABLE_OFFSET 62
30
25.6k
#define HEADER_TABLE_ENTRY_SIZE_OFFSET 32
31
4.23k
#define DYNAMIC_TABLE_SIZE_UPDATE_MAX_SIZE 5
32
4.23k
#define STATUS_HEADER_MAX_SIZE 5
33
#define CONTENT_LENGTH_HEADER_MAX_SIZE                                                                                             \
34
3.55k
    (3 + sizeof(H2O_SIZE_T_LONGEST_STR) - 1) /* uses Literal Header Field without Indexing (RFC7541 6.2.2) */
35
36
#include "hpack_huffman_table.h"
37
38
static inline int value_is_part_of_static_table(const h2o_iovec_t *value)
39
27.5k
{
40
27.5k
    return &h2o_hpack_static_table[0].value <= value &&
41
27.5k
           value <= &h2o_hpack_static_table[sizeof(h2o_hpack_static_table) / sizeof(h2o_hpack_static_table[0]) - 1].value;
42
27.5k
}
43
44
static h2o_iovec_t *alloc_buf(h2o_mem_pool_t *pool, size_t len)
45
107k
{
46
107k
    h2o_iovec_t *buf = h2o_mem_alloc_shared(pool, sizeof(h2o_iovec_t) + len + 1, NULL);
47
107k
    buf->base = (char *)buf + sizeof(h2o_iovec_t);
48
107k
    buf->len = len;
49
107k
    return buf;
50
107k
}
51
52
int64_t h2o_hpack_decode_int(const uint8_t **src, const uint8_t *src_end, unsigned prefix_bits)
53
408k
{
54
408k
    uint64_t value;
55
408k
    unsigned shift;
56
408k
    uint8_t prefix_max = (1 << prefix_bits) - 1;
57
58
408k
    if (*src >= src_end)
59
0
        return H2O_HTTP2_ERROR_INCOMPLETE;
60
61
408k
    value = *(*src)++ & prefix_max;
62
408k
    if (value != prefix_max)
63
404k
        return (int64_t)value;
64
65
    /* decode upto 8 octets (excluding prefix), that are guaranteed not to cause overflow */
66
4.08k
    value = prefix_max;
67
7.67k
    for (shift = 0; shift < 56; shift += 7) {
68
7.51k
        if (*src == src_end)
69
72
            return H2O_HTTP2_ERROR_INCOMPLETE;
70
7.44k
        value += (uint64_t)(**src & 127) << shift;
71
7.44k
        if ((*(*src)++ & 128) == 0)
72
3.85k
            return (int64_t)value;
73
7.44k
    }
74
    /* handling the 9th octet */
75
161
    if (*src == src_end)
76
5
        return H2O_HTTP2_ERROR_INCOMPLETE;
77
156
    if ((**src & 128) != 0)
78
28
        return H2O_HTTP2_ERROR_COMPRESSION;
79
128
    value += (uint64_t)(*(*src)++ & 127) << shift;
80
128
    if (value > (uint64_t)INT64_MAX)
81
2
        return H2O_HTTP2_ERROR_COMPRESSION;
82
126
    return value;
83
128
}
84
85
static char *huffdecode4(char *dst, uint8_t in, uint8_t *state, int *maybe_eos, uint8_t *seen_char_types)
86
334k
{
87
334k
    const nghttp2_huff_decode *entry = huff_decode_table[*state] + in;
88
89
334k
    if ((entry->flags & NGHTTP2_HUFF_FAIL) != 0)
90
8
        return NULL;
91
334k
    if ((entry->flags & NGHTTP2_HUFF_SYM) != 0) {
92
229k
        *dst++ = entry->sym;
93
229k
        *seen_char_types |= (entry->flags & NGHTTP2_HUFF_INVALID_CHARS);
94
229k
    }
95
334k
    *state = entry->state;
96
334k
    *maybe_eos = (entry->flags & NGHTTP2_HUFF_ACCEPTED) != 0;
97
98
334k
    return dst;
99
334k
}
100
101
const char h2o_hpack_err_found_upper_case_in_header_name[] = "found an upper-case letter in header name";
102
const char h2o_hpack_soft_err_found_invalid_char_in_header_name[] = "found an invalid character in header name";
103
const char h2o_hpack_soft_err_found_invalid_char_in_header_value[] = "found an invalid character in header value";
104
105
size_t h2o_hpack_decode_huffman(char *_dst, unsigned *soft_errors, const uint8_t *src, size_t len, int is_name,
106
                                const char **err_desc)
107
8.84k
{
108
8.84k
    char *dst = _dst;
109
8.84k
    const uint8_t *src_end = src + len;
110
8.84k
    uint8_t state = 0, seen_char_types = 0;
111
8.84k
    int maybe_eos = 1;
112
113
    /* decode */
114
175k
    for (; src < src_end; src++) {
115
167k
        if ((dst = huffdecode4(dst, *src >> 4, &state, &maybe_eos, &seen_char_types)) == NULL)
116
3
            return SIZE_MAX;
117
167k
        if ((dst = huffdecode4(dst, *src & 0xf, &state, &maybe_eos, &seen_char_types)) == NULL)
118
5
            return SIZE_MAX;
119
167k
    }
120
8.83k
    if (!maybe_eos)
121
80
        return SIZE_MAX;
122
123
    /* validate */
124
8.75k
    if (is_name) {
125
1.22k
        if (dst == _dst)
126
9
            return SIZE_MAX;
127
        /* pseudo-headers are checked later in `decode_header` */
128
1.21k
        if ((seen_char_types & NGHTTP2_HUFF_INVALID_FOR_HEADER_NAME) != 0 && _dst[0] != ':') {
129
853
            if ((seen_char_types & NGHTTP2_HUFF_UPPER_CASE_CHAR) != 0) {
130
0
                *err_desc = h2o_hpack_err_found_upper_case_in_header_name;
131
0
                return SIZE_MAX;
132
853
            } else {
133
853
                *soft_errors |= H2O_HPACK_SOFT_ERROR_BIT_INVALID_NAME;
134
853
            }
135
853
        }
136
7.53k
    } else {
137
7.53k
        if ((seen_char_types & NGHTTP2_HUFF_INVALID_FOR_HEADER_VALUE) != 0)
138
34
            *soft_errors |= H2O_HPACK_SOFT_ERROR_BIT_INVALID_VALUE;
139
7.53k
    }
140
141
8.74k
    return dst - _dst;
142
8.75k
}
143
144
/* validate a header name against https://tools.ietf.org/html/rfc7230#section-3.2,
145
 * in addition to that, we disallow upper case chars as well.
146
 * This sets @err_desc for all invalid characters, but only returns true
147
 * for upper case characters, this is because we return a protocol error
148
 * in that case. */
149
int h2o_hpack_validate_header_name(unsigned *soft_errors, const char *s, size_t len, const char **err_desc)
150
42.2k
{
151
    /* all printable chars, except upper case and separator characters */
152
42.2k
    static const char valid_h2_header_name_char[] = {
153
42.2k
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*    0-31 */
154
42.2k
        0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /*   32-63 */
155
42.2k
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, /*   64-95 */
156
42.2k
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, /*  96-127 */
157
42.2k
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 128-159 */
158
42.2k
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 160-191 */
159
42.2k
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 192-223 */
160
42.2k
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 224-255 */
161
42.2k
    };
162
163
244k
    for (; len != 0; ++s, --len) {
164
202k
        unsigned char ch = (unsigned char)*s;
165
202k
        if (!valid_h2_header_name_char[ch]) {
166
58.1k
            if (ch - 'A' < 26U) {
167
52
                *err_desc = h2o_hpack_err_found_upper_case_in_header_name;
168
52
                return 0;
169
52
            }
170
58.0k
            *soft_errors |= H2O_HPACK_SOFT_ERROR_BIT_INVALID_NAME;
171
58.0k
        }
172
202k
    }
173
42.2k
    return 1;
174
42.2k
}
175
176
/* validate a header value against https://tools.ietf.org/html/rfc7230#section-3.2 */
177
void h2o_hpack_validate_header_value(unsigned *soft_errors, const char *s, size_t len)
178
52.0k
{
179
    /* all printable chars + horizontal tab */
180
52.0k
    static const char valid_h2_field_value_char[] = {
181
52.0k
        0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*    0-31 */
182
52.0k
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*   32-63 */
183
52.0k
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*   64-95 */
184
52.0k
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /*  96-127 */
185
52.0k
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 128-159 */
186
52.0k
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 160-191 */
187
52.0k
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 192-223 */
188
52.0k
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 224-255 */
189
52.0k
    };
190
191
199k
    for (; len != 0; ++s, --len) {
192
163k
        unsigned char ch = (unsigned char)*s;
193
163k
        if (!valid_h2_field_value_char[ch]) {
194
15.8k
            *soft_errors |= H2O_HPACK_SOFT_ERROR_BIT_INVALID_VALUE;
195
15.8k
            break;
196
15.8k
        }
197
163k
    }
198
52.0k
}
199
200
static h2o_iovec_t *decode_string(h2o_mem_pool_t *pool, unsigned *soft_errors, const uint8_t **src, const uint8_t *src_end,
201
                                  int is_header_name, const char **err_desc)
202
105k
{
203
105k
    h2o_iovec_t *ret;
204
105k
    int is_huffman;
205
105k
    int64_t len;
206
207
105k
    if (*src >= src_end)
208
412
        return NULL;
209
210
104k
    is_huffman = (**src & 0x80) != 0;
211
104k
    if ((len = h2o_hpack_decode_int(src, src_end, 7)) < 0)
212
22
        return NULL;
213
214
104k
    if (is_huffman) {
215
9.19k
        if (len > src_end - *src)
216
352
            return NULL;
217
8.84k
        ret = alloc_buf(pool, len * 2); /* max compression ratio is >= 0.5 */
218
8.84k
        if ((ret->len = h2o_hpack_decode_huffman(ret->base, soft_errors, *src, len, is_header_name, err_desc)) == SIZE_MAX)
219
97
            return NULL;
220
8.74k
        ret->base[ret->len] = '\0';
221
95.4k
    } else {
222
95.4k
        if (len > src_end - *src)
223
1.11k
            return NULL;
224
94.3k
        if (is_header_name) {
225
            /* pseudo-headers are checked later in `decode_header` */
226
42.3k
            if (**src != (uint8_t)':' && !h2o_hpack_validate_header_name(soft_errors, (char *)*src, len, err_desc))
227
52
                return NULL;
228
52.0k
        } else {
229
52.0k
            h2o_hpack_validate_header_value(soft_errors, (char *)*src, len);
230
52.0k
        }
231
94.2k
        ret = alloc_buf(pool, len);
232
94.2k
        memcpy(ret->base, *src, len);
233
94.2k
        ret->base[len] = '\0';
234
94.2k
    }
235
103k
    *src += len;
236
237
103k
    return ret;
238
104k
}
239
240
static void header_table_evict_one(h2o_hpack_header_table_t *table)
241
5.11k
{
242
5.11k
    struct st_h2o_hpack_header_table_entry_t *entry;
243
5.11k
    assert(table->num_entries != 0);
244
245
0
    entry = h2o_hpack_header_table_get(table, --table->num_entries);
246
5.11k
    table->hpack_size -= entry->name->len + entry->value->len + HEADER_TABLE_ENTRY_SIZE_OFFSET;
247
5.11k
    if (!h2o_iovec_is_token(entry->name))
248
1.21k
        h2o_mem_release_shared(entry->name);
249
5.11k
    if (!value_is_part_of_static_table(entry->value))
250
5.11k
        h2o_mem_release_shared(entry->value);
251
5.11k
    memset(entry, 0, sizeof(*entry));
252
5.11k
}
253
254
static struct st_h2o_hpack_header_table_entry_t *header_table_add(h2o_hpack_header_table_t *table, size_t size_add,
255
                                                                  size_t max_num_entries)
256
20.5k
{
257
    /* adjust the size */
258
22.2k
    while (table->num_entries != 0 && table->hpack_size + size_add > table->hpack_capacity)
259
1.70k
        header_table_evict_one(table);
260
20.5k
    while (max_num_entries <= table->num_entries)
261
0
        header_table_evict_one(table);
262
20.5k
    if (table->num_entries == 0) {
263
10.2k
        assert(table->hpack_size == 0);
264
10.2k
        if (size_add > table->hpack_capacity)
265
4.76k
            return NULL;
266
10.2k
    }
267
15.7k
    table->hpack_size += size_add;
268
269
    /* grow the entries if full */
270
15.7k
    if (table->num_entries == table->entry_capacity) {
271
4.66k
        size_t new_capacity = table->num_entries * 2;
272
4.66k
        if (new_capacity < 16)
273
4.51k
            new_capacity = 16;
274
4.66k
        struct st_h2o_hpack_header_table_entry_t *new_entries =
275
4.66k
            h2o_mem_alloc(new_capacity * sizeof(struct st_h2o_hpack_header_table_entry_t));
276
4.66k
        if (table->num_entries != 0) {
277
153
            size_t src_index = table->entry_start_index, dst_index = 0;
278
3.92k
            do {
279
3.92k
                new_entries[dst_index] = table->entries[src_index];
280
3.92k
                ++dst_index;
281
3.92k
                src_index = (src_index + 1) % table->entry_capacity;
282
3.92k
            } while (dst_index != table->num_entries);
283
153
        }
284
4.66k
        memset(new_entries + table->num_entries, 0, sizeof(*new_entries) * (new_capacity - table->num_entries));
285
4.66k
        free(table->entries);
286
4.66k
        table->entries = new_entries;
287
4.66k
        table->entry_capacity = new_capacity;
288
4.66k
        table->entry_start_index = 0;
289
4.66k
    }
290
291
15.7k
    ++table->num_entries;
292
15.7k
    table->entry_start_index = (table->entry_start_index + table->entry_capacity - 1) % table->entry_capacity;
293
15.7k
    return table->entries + table->entry_start_index;
294
20.5k
}
295
296
int h2o_hpack_decode_header(h2o_mem_pool_t *pool, void *_hpack_header_table, h2o_iovec_t **_name, h2o_iovec_t *_value,
297
                            const uint8_t **const src, const uint8_t *src_end, const char **err_desc)
298
324k
{
299
324k
    h2o_hpack_header_table_t *hpack_header_table = _hpack_header_table;
300
324k
    h2o_iovec_t *name = NULL, *value = NULL;
301
324k
    int64_t index = 0;
302
324k
    int value_is_indexed = 0, do_index = 0;
303
304
347k
Redo:
305
347k
    if (*src >= src_end)
306
88
        return H2O_HTTP2_ERROR_COMPRESSION;
307
308
    /* determine the mode and handle accordingly */
309
347k
    if (**src >= 128) {
310
        /* indexed header field representation */
311
262k
        if ((index = h2o_hpack_decode_int(src, src_end, 7)) <= 0)
312
41
            return H2O_HTTP2_ERROR_COMPRESSION;
313
262k
        value_is_indexed = 1;
314
262k
    } else if (**src >= 64) {
315
        /* literal header field with incremental handling */
316
17.2k
        if (**src == 64) {
317
3.62k
            ++*src;
318
13.6k
        } else if ((index = h2o_hpack_decode_int(src, src_end, 6)) <= 0) {
319
24
            return H2O_HTTP2_ERROR_COMPRESSION;
320
24
        }
321
17.2k
        do_index = 1;
322
67.4k
    } else if (**src < 32) {
323
        /* literal header field without indexing / never indexed */
324
44.5k
        if ((**src & 0xf) == 0) {
325
40.1k
            ++*src;
326
40.1k
        } else if ((index = h2o_hpack_decode_int(src, src_end, 4)) <= 0) {
327
12
            return H2O_HTTP2_ERROR_COMPRESSION;
328
12
        }
329
44.5k
    } else {
330
        /* size update */
331
22.9k
        int64_t new_capacity;
332
22.9k
        if ((new_capacity = h2o_hpack_decode_int(src, src_end, 5)) < 0) {
333
14
            return H2O_HTTP2_ERROR_COMPRESSION;
334
14
        }
335
22.9k
        if (new_capacity > hpack_header_table->hpack_max_capacity) {
336
105
            return H2O_HTTP2_ERROR_COMPRESSION;
337
105
        }
338
22.8k
        hpack_header_table->hpack_capacity = (size_t)new_capacity;
339
26.2k
        while (hpack_header_table->num_entries != 0 && hpack_header_table->hpack_size > hpack_header_table->hpack_capacity) {
340
3.39k
            header_table_evict_one(hpack_header_table);
341
3.39k
        }
342
22.8k
        goto Redo;
343
22.9k
    }
344
345
    /* determine the header */
346
324k
    unsigned soft_errors = 0;
347
324k
    if (index > 0) {
348
        /* existing name (and value?) */
349
280k
        if (index < HEADER_TABLE_OFFSET) {
350
53.6k
            name = (h2o_iovec_t *)h2o_hpack_static_table[index - 1].name;
351
53.6k
            if (value_is_indexed)
352
36.4k
                value = (h2o_iovec_t *)&h2o_hpack_static_table[index - 1].value;
353
227k
        } else if (index - HEADER_TABLE_OFFSET < hpack_header_table->num_entries) {
354
226k
            struct st_h2o_hpack_header_table_entry_t *entry =
355
226k
                h2o_hpack_header_table_get(hpack_header_table, index - HEADER_TABLE_OFFSET);
356
226k
            soft_errors = entry->soft_errors;
357
226k
            name = entry->name;
358
226k
            if (!h2o_iovec_is_token(name))
359
92.3k
                h2o_mem_link_shared(pool, name);
360
226k
            if (value_is_indexed) {
361
226k
                value = entry->value;
362
226k
                h2o_mem_link_shared(pool, value);
363
226k
            }
364
226k
        } else {
365
502
            return H2O_HTTP2_ERROR_COMPRESSION;
366
502
        }
367
280k
    } else {
368
        /* non-existing name */
369
43.7k
        const h2o_token_t *name_token;
370
43.7k
        if ((name = decode_string(pool, &soft_errors, src, src_end, 1, err_desc)) == NULL) {
371
280
            if (*err_desc == h2o_hpack_err_found_upper_case_in_header_name)
372
52
                return H2O_HTTP2_ERROR_PROTOCOL;
373
228
            return H2O_HTTP2_ERROR_COMPRESSION;
374
280
        }
375
        /* predefined header names should be interned */
376
43.5k
        if ((name_token = h2o_lookup_token(name->base, name->len)) != NULL)
377
4.72k
            name = (h2o_iovec_t *)&name_token->buf;
378
43.5k
    }
379
380
    /* determine the value (if necessary) */
381
323k
    if (!value_is_indexed) {
382
61.3k
        soft_errors &= ~H2O_HPACK_SOFT_ERROR_BIT_INVALID_VALUE;
383
61.3k
        if ((value = decode_string(pool, &soft_errors, src, src_end, 0, err_desc)) == NULL)
384
1.76k
            return H2O_HTTP2_ERROR_COMPRESSION;
385
61.3k
    }
386
387
    /* add the decoded header to the header table if necessary */
388
321k
    if (do_index) {
389
16.4k
        struct st_h2o_hpack_header_table_entry_t *entry =
390
16.4k
            header_table_add(hpack_header_table, name->len + value->len + HEADER_TABLE_ENTRY_SIZE_OFFSET, SIZE_MAX);
391
16.4k
        if (entry != NULL) {
392
11.8k
            entry->soft_errors = soft_errors;
393
11.8k
            entry->name = name;
394
11.8k
            if (!h2o_iovec_is_token(entry->name))
395
2.48k
                h2o_mem_addref_shared(entry->name);
396
11.8k
            entry->value = value;
397
11.8k
            if (!value_is_part_of_static_table(entry->value))
398
11.8k
                h2o_mem_addref_shared(entry->value);
399
11.8k
        }
400
16.4k
    }
401
402
321k
    *_name = name;
403
321k
    *_value = *value;
404
321k
    if (soft_errors != 0) {
405
109k
        *err_desc = (soft_errors & H2O_HPACK_SOFT_ERROR_BIT_INVALID_NAME) != 0
406
109k
                        ? h2o_hpack_soft_err_found_invalid_char_in_header_name
407
109k
                        : h2o_hpack_soft_err_found_invalid_char_in_header_value;
408
109k
        return H2O_HTTP2_ERROR_INVALID_HEADER_CHAR;
409
212k
    } else {
410
212k
        return 0;
411
212k
    }
412
321k
}
413
414
static uint8_t *encode_status(uint8_t *dst, int status)
415
4.23k
{
416
    /* see also: STATUS_HEADER_MAX_SIZE */
417
418
4.23k
    assert(100 <= status && status <= 999);
419
420
0
    switch (status) {
421
0
#define COMMON_CODE(code, st)                                                                                                      \
422
4.23k
    case st:                                                                                                                       \
423
4.23k
        *dst++ = 0x80 | code;                                                                                                      \
424
4.23k
        break
425
684
        COMMON_CODE(8, 200);
426
0
        COMMON_CODE(9, 204);
427
0
        COMMON_CODE(10, 206);
428
0
        COMMON_CODE(11, 304);
429
1.90k
        COMMON_CODE(12, 400);
430
1.64k
        COMMON_CODE(13, 404);
431
0
        COMMON_CODE(14, 500);
432
0
#undef COMMON_CODE
433
0
    default:
434
        /* use literal header field without indexing - indexed name */
435
0
        *dst++ = 8;
436
0
        *dst++ = 3;
437
0
        sprintf((char *)dst, "%d", status);
438
0
        dst += 3;
439
0
        break;
440
4.23k
    }
441
442
4.23k
    return dst;
443
4.23k
}
444
445
static uint8_t *encode_content_length(uint8_t *dst, size_t value)
446
3.55k
{
447
3.55k
    char buf[32], *p = buf + sizeof(buf);
448
3.55k
    size_t l;
449
450
5.45k
    do {
451
5.45k
        *--p = '0' + value % 10;
452
5.45k
    } while ((value /= 10) != 0);
453
3.55k
    l = buf + sizeof(buf) - p;
454
3.55k
    *dst++ = 0x0f;
455
3.55k
    *dst++ = 0x0d;
456
3.55k
    *dst++ = (uint8_t)l;
457
3.55k
    memcpy(dst, p, l);
458
3.55k
    dst += l;
459
460
3.55k
    return dst;
461
3.55k
}
462
463
void h2o_hpack_dispose_header_table(h2o_hpack_header_table_t *header_table)
464
16.6k
{
465
16.6k
    if (header_table->num_entries != 0) {
466
3.87k
        size_t index = header_table->entry_start_index;
467
10.6k
        do {
468
10.6k
            struct st_h2o_hpack_header_table_entry_t *entry = header_table->entries + index;
469
10.6k
            if (!h2o_iovec_is_token(entry->name))
470
1.26k
                h2o_mem_release_shared(entry->name);
471
10.6k
            if (!value_is_part_of_static_table(entry->value))
472
10.6k
                h2o_mem_release_shared(entry->value);
473
10.6k
            index = (index + 1) % header_table->entry_capacity;
474
10.6k
        } while (--header_table->num_entries != 0);
475
3.87k
    }
476
16.6k
    free(header_table->entries);
477
16.6k
}
478
479
int h2o_hpack_parse_request(h2o_mem_pool_t *pool, h2o_hpack_decode_header_cb decode_cb, void *decode_ctx, h2o_iovec_t *method,
480
                            const h2o_url_scheme_t **scheme, h2o_iovec_t *authority, h2o_iovec_t *path, h2o_headers_t *headers,
481
                            int *pseudo_header_exists_map, size_t *content_length, h2o_cache_digests_t **digests,
482
                            h2o_iovec_t *datagram_flow_id, const uint8_t *src, size_t len, const char **err_desc)
483
10.6k
{
484
10.6k
    const uint8_t *src_end = src + len;
485
486
10.6k
    *content_length = SIZE_MAX;
487
488
332k
    while (src != src_end) {
489
324k
        h2o_iovec_t *name, value;
490
324k
        const char *decode_err = NULL;
491
324k
        int ret = decode_cb(pool, decode_ctx, &name, &value, &src, src_end, &decode_err);
492
324k
        if (ret != 0) {
493
112k
            if (ret == H2O_HTTP2_ERROR_INVALID_HEADER_CHAR) {
494
                /* this is a soft error, we continue parsing, but register only the first error */
495
109k
                if (*err_desc == NULL) {
496
3.43k
                    *err_desc = decode_err;
497
3.43k
                }
498
109k
            } else {
499
2.83k
                *err_desc = decode_err;
500
2.83k
                return ret;
501
2.83k
            }
502
112k
        }
503
321k
        if (name->base[0] == ':') {
504
18.4k
            if (pseudo_header_exists_map != NULL) {
505
                /* FIXME validate the chars in the value (e.g. reject SP in path) */
506
18.3k
                if (name == &H2O_TOKEN_AUTHORITY->buf) {
507
3.66k
                    if (authority->base != NULL)
508
4
                        return H2O_HTTP2_ERROR_PROTOCOL;
509
3.66k
                    *authority = value;
510
3.66k
                    *pseudo_header_exists_map |= H2O_HPACK_PARSE_HEADERS_AUTHORITY_EXISTS;
511
14.6k
                } else if (name == &H2O_TOKEN_METHOD->buf) {
512
4.93k
                    if (method->base != NULL)
513
4
                        return H2O_HTTP2_ERROR_PROTOCOL;
514
4.93k
                    *method = value;
515
4.93k
                    *pseudo_header_exists_map |= H2O_HPACK_PARSE_HEADERS_METHOD_EXISTS;
516
9.71k
                } else if (name == &H2O_TOKEN_PATH->buf) {
517
4.87k
                    if (path->base != NULL)
518
4
                        return H2O_HTTP2_ERROR_PROTOCOL;
519
4.87k
                    if (value.len == 0)
520
1
                        return H2O_HTTP2_ERROR_PROTOCOL;
521
4.87k
                    *path = value;
522
4.87k
                    *pseudo_header_exists_map |= H2O_HPACK_PARSE_HEADERS_PATH_EXISTS;
523
4.87k
                } else if (name == &H2O_TOKEN_SCHEME->buf) {
524
4.82k
                    if (*scheme != NULL)
525
2
                        return H2O_HTTP2_ERROR_PROTOCOL;
526
4.82k
                    if (h2o_memis(value.base, value.len, H2O_STRLIT("https"))) {
527
342
                        *scheme = &H2O_URL_SCHEME_HTTPS;
528
4.48k
                    } else if (h2o_memis(value.base, value.len, H2O_STRLIT("masque"))) {
529
0
                        *scheme = &H2O_URL_SCHEME_MASQUE;
530
4.48k
                    } else {
531
                        /* draft-16 8.1.2.3 suggests quote: ":scheme is not restricted to http and https schemed URIs" */
532
4.48k
                        *scheme = &H2O_URL_SCHEME_HTTP;
533
4.48k
                    }
534
4.82k
                    *pseudo_header_exists_map |= H2O_HPACK_PARSE_HEADERS_SCHEME_EXISTS;
535
4.82k
                } else {
536
7
                    return H2O_HTTP2_ERROR_PROTOCOL;
537
7
                }
538
18.3k
            } else {
539
169
                return H2O_HTTP2_ERROR_PROTOCOL;
540
169
            }
541
303k
        } else {
542
303k
            pseudo_header_exists_map = NULL;
543
303k
            if (h2o_iovec_is_token(name)) {
544
173k
                h2o_token_t *token = H2O_STRUCT_FROM_MEMBER(h2o_token_t, buf, name);
545
173k
                if (token->flags.is_hpack_special) {
546
106k
                    if (token == H2O_TOKEN_CONTENT_LENGTH) {
547
995
                        if ((*content_length = h2o_strtosize(value.base, value.len)) == SIZE_MAX)
548
34
                            return H2O_HTTP2_ERROR_PROTOCOL;
549
961
                        goto Next;
550
105k
                    } else if (token == H2O_TOKEN_HOST) {
551
                        /* HTTP2 allows the use of host header (in place of :authority) */
552
2.98k
                        if (authority->base == NULL)
553
237
                            *authority = value;
554
2.98k
                        goto Next;
555
102k
                    } else if (token == H2O_TOKEN_TE && h2o_lcstris(value.base, value.len, H2O_STRLIT("trailers"))) {
556
                        /* do not reject */
557
101k
                    } else if (token == H2O_TOKEN_CACHE_DIGEST && digests != NULL) {
558
                        /* TODO cache the decoded result in HPACK, as well as delay the decoding of the digest until being used */
559
101k
                        h2o_cache_digests_load_header(digests, value.base, value.len);
560
101k
                    } else if (token == H2O_TOKEN_DATAGRAM_FLOW_ID) {
561
118
                        if (datagram_flow_id != NULL)
562
0
                            *datagram_flow_id = value;
563
118
                        goto Next;
564
118
                    } else {
565
                        /* rest of the header fields that are marked as special are rejected */
566
28
                        return H2O_HTTP2_ERROR_PROTOCOL;
567
28
                    }
568
106k
                }
569
169k
                h2o_add_header(pool, headers, token, NULL, value.base, value.len);
570
169k
            } else {
571
130k
                h2o_add_header_by_str(pool, headers, name->base, name->len, 0, NULL, value.base, value.len);
572
130k
            }
573
303k
        }
574
321k
    Next:;
575
321k
    }
576
577
7.57k
    if (*err_desc != NULL)
578
2.22k
        return H2O_HTTP2_ERROR_INVALID_HEADER_CHAR;
579
5.35k
    return 0;
580
7.57k
}
581
582
int h2o_hpack_parse_response(h2o_mem_pool_t *pool, h2o_hpack_decode_header_cb decode_cb, void *decode_ctx, int *status,
583
                             h2o_headers_t *headers, h2o_iovec_t *datagram_flow_id, const uint8_t *src, size_t len,
584
                             const char **err_desc)
585
0
{
586
0
    *status = 0;
587
588
0
    const uint8_t *src_end = src + len;
589
590
    /* the response MUST contain a :status header as the first element */
591
0
    if (src == src_end)
592
0
        return H2O_HTTP2_ERROR_PROTOCOL;
593
594
0
    do {
595
0
        h2o_iovec_t *name, value;
596
0
        const char *decode_err = NULL;
597
0
        int ret = decode_cb(pool, decode_ctx, &name, &value, &src, src_end, &decode_err);
598
0
        if (ret != 0) {
599
0
            if (ret == H2O_HTTP2_ERROR_INVALID_HEADER_CHAR) {
600
                /* this is a soft error, we continue parsing, but register only the first error */
601
0
                if (*err_desc == NULL) {
602
0
                    *err_desc = decode_err;
603
0
                }
604
0
            } else {
605
0
                *err_desc = decode_err;
606
0
                return ret;
607
0
            }
608
0
        }
609
0
        if (name->base[0] == ':') {
610
0
            if (name != &H2O_TOKEN_STATUS->buf)
611
0
                return H2O_HTTP2_ERROR_PROTOCOL;
612
0
            if (*status != 0)
613
0
                return H2O_HTTP2_ERROR_PROTOCOL;
614
            /* parse status */
615
0
            if (value.len != 3)
616
0
                return H2O_HTTP2_ERROR_PROTOCOL;
617
0
            char *c = value.base;
618
0
#define PARSE_DIGIT(mul, min_digit)                                                                                                \
619
0
    do {                                                                                                                           \
620
0
        if (*c < '0' + (min_digit) || '9' < *c)                                                                                    \
621
0
            return H2O_HTTP2_ERROR_PROTOCOL;                                                                                       \
622
0
        *status += (*c - '0') * mul;                                                                                               \
623
0
        ++c;                                                                                                                       \
624
0
    } while (0)
625
0
            PARSE_DIGIT(100, 1);
626
0
            PARSE_DIGIT(10, 0);
627
0
            PARSE_DIGIT(1, 0);
628
0
#undef PARSE_DIGIT
629
0
        } else {
630
0
            if (*status == 0)
631
0
                return H2O_HTTP2_ERROR_PROTOCOL;
632
0
            if (h2o_iovec_is_token(name)) {
633
0
                h2o_token_t *token = H2O_STRUCT_FROM_MEMBER(h2o_token_t, buf, name);
634
                /* reject headers as defined in draft-16 8.1.2.2 */
635
0
                if (token->flags.is_hpack_special) {
636
0
                    if (token == H2O_TOKEN_CONTENT_LENGTH || token == H2O_TOKEN_CACHE_DIGEST) {
637
                        /* pass them through when found in response headers (TODO reconsider?) */
638
0
                    } else if (token == H2O_TOKEN_DATAGRAM_FLOW_ID) {
639
0
                        if (datagram_flow_id != NULL)
640
0
                            *datagram_flow_id = value;
641
0
                        goto Next;
642
0
                    } else {
643
0
                        return H2O_HTTP2_ERROR_PROTOCOL;
644
0
                    }
645
0
                }
646
0
                h2o_add_header(pool, headers, token, NULL, value.base, value.len);
647
0
            } else {
648
0
                h2o_add_header_by_str(pool, headers, name->base, name->len, 0, NULL, value.base, value.len);
649
0
            }
650
0
        }
651
0
    Next:;
652
0
    } while (src != src_end);
653
654
0
    if (*err_desc) {
655
0
        return H2O_HTTP2_ERROR_INVALID_HEADER_CHAR;
656
0
    }
657
0
    return 0;
658
0
}
659
660
static inline int encode_int_is_onebyte(int64_t value, unsigned prefix_bits)
661
12.6k
{
662
12.6k
    return value < (1 << prefix_bits) - 1;
663
12.6k
}
664
665
uint8_t *h2o_hpack_encode_int(uint8_t *dst, int64_t value, unsigned prefix_bits)
666
8.53k
{
667
8.53k
    if (encode_int_is_onebyte(value, prefix_bits)) {
668
8.48k
        *dst++ |= value;
669
8.48k
    } else {
670
        /* see also: MAX_ENCODE_INT_LENGTH */
671
49
        assert(value >= 0);
672
0
        value -= (1 << prefix_bits) - 1;
673
49
        *dst++ |= (1 << prefix_bits) - 1;
674
65
        for (; value >= 128; value >>= 7) {
675
16
            *dst++ = 0x80 | value;
676
16
        }
677
49
        *dst++ = value;
678
49
    }
679
0
    return dst;
680
8.53k
}
681
682
size_t h2o_hpack_encode_huffman(uint8_t *_dst, const uint8_t *src, size_t len)
683
4.10k
{
684
4.10k
    uint8_t *dst = _dst, *dst_end = dst + len;
685
4.10k
    const uint8_t *src_end = src + len;
686
4.10k
    uint64_t bits = 0;
687
4.10k
    int bits_left = 40;
688
689
104k
    while (src != src_end) {
690
99.9k
        const nghttp2_huff_sym *sym = huff_sym_table + *src++;
691
99.9k
        bits |= (uint64_t)sym->code << (bits_left - sym->nbits);
692
99.9k
        bits_left -= sym->nbits;
693
171k
        while (bits_left <= 32) {
694
71.2k
            *dst++ = bits >> 32;
695
71.2k
            bits <<= 8;
696
71.2k
            bits_left += 8;
697
71.2k
            if (dst == dst_end) {
698
0
                return SIZE_MAX;
699
0
            }
700
71.2k
        }
701
99.9k
    }
702
703
4.10k
    if (bits_left != 40) {
704
4.09k
        bits |= ((uint64_t)1 << bits_left) - 1;
705
4.09k
        *dst++ = bits >> 32;
706
4.09k
    }
707
4.10k
    if (dst == dst_end) {
708
0
        return SIZE_MAX;
709
0
    }
710
711
4.10k
    return dst - _dst;
712
4.10k
}
713
714
static size_t encode_as_is(uint8_t *dst, const char *s, size_t len)
715
0
{
716
0
    uint8_t *start = dst;
717
0
    *dst = '\0';
718
0
    dst = h2o_hpack_encode_int(dst, len, 7);
719
0
    memcpy(dst, s, len);
720
0
    dst += len;
721
0
    return dst - start;
722
0
}
723
724
size_t h2o_hpack_encode_string(uint8_t *dst, const char *s, size_t len)
725
4.10k
{
726
4.10k
    if (H2O_LIKELY(len != 0)) {
727
        /* try to encode using huffman */
728
4.10k
        size_t hufflen = h2o_hpack_encode_huffman(dst + 1, (const uint8_t *)s, len);
729
4.10k
        if (H2O_LIKELY(hufflen != SIZE_MAX)) {
730
4.10k
            size_t head_len;
731
4.10k
            if (H2O_LIKELY(encode_int_is_onebyte((uint32_t)hufflen, 7))) {
732
4.10k
                dst[0] = (uint8_t)(0x80 | hufflen);
733
4.10k
                head_len = 1;
734
4.10k
            } else {
735
0
                uint8_t head[8];
736
0
                head[0] = '\x80';
737
0
                head_len = h2o_hpack_encode_int(head, hufflen, 7) - head;
738
0
                memmove(dst + head_len, dst + 1, hufflen);
739
0
                memcpy(dst, head, head_len);
740
0
            }
741
4.10k
            return head_len + hufflen;
742
4.10k
        }
743
4.10k
    }
744
0
    return encode_as_is(dst, s, len);
745
4.10k
}
746
747
static uint8_t *header_table_adjust_size(h2o_hpack_header_table_t *table, uint32_t new_capacity, uint8_t *dst)
748
4.23k
{
749
    /* Do nothing if user-supplied value is greater than the current value. Because we never allow the peer to increase the table
750
     * size here, there is no need to worry about using excess memory. */
751
4.23k
    if (new_capacity >= table->hpack_capacity)
752
4.17k
        return dst;
753
754
    /* update state */
755
63
    table->hpack_capacity = new_capacity;
756
77
    while (table->num_entries != 0 && table->hpack_size > table->hpack_capacity)
757
14
        header_table_evict_one(table);
758
759
    /* encode Dynamic Table Size Update */
760
63
    *dst = 0x20;
761
63
    dst = h2o_hpack_encode_int(dst, table->hpack_capacity, 5);
762
763
63
    return dst;
764
4.23k
}
765
766
static uint8_t *do_encode_header(h2o_hpack_header_table_t *header_table, uint8_t *dst, const h2o_iovec_t *name,
767
                                 const h2o_iovec_t *value, int dont_compress)
768
8.47k
{
769
8.47k
    int is_token = h2o_iovec_is_token(name);
770
8.47k
    int name_index = is_token ? ((const h2o_token_t *)name)->flags.http2_static_table_name_index : 0;
771
772
    /* try to send as indexed */
773
8.47k
    {
774
8.47k
        size_t header_table_index = header_table->entry_start_index, n;
775
13.0k
        for (n = header_table->num_entries; n != 0; --n) {
776
8.95k
            struct st_h2o_hpack_header_table_entry_t *entry = header_table->entries + header_table_index;
777
8.95k
            if (is_token) {
778
8.95k
                if (name != entry->name)
779
4.57k
                    goto Next;
780
8.95k
            } else {
781
0
                if (!h2o_memis(name->base, name->len, entry->name->base, entry->name->len))
782
0
                    goto Next;
783
0
                if (name_index == 0)
784
0
                    name_index = (int)(header_table->num_entries - n + HEADER_TABLE_OFFSET);
785
0
            }
786
            /* name matched! */
787
4.38k
            if (!h2o_memis(value->base, value->len, entry->value->base, entry->value->len))
788
15
                goto Next;
789
            /* name and value matched! */
790
4.36k
            *dst = 0x80;
791
4.36k
            dst = h2o_hpack_encode_int(dst, header_table->num_entries - n + HEADER_TABLE_OFFSET, 7);
792
4.36k
            return dst;
793
4.58k
        Next:
794
4.58k
            ++header_table_index;
795
4.58k
            if (header_table_index == header_table->entry_capacity)
796
1.82k
                header_table_index = 0;
797
4.58k
        }
798
8.47k
    }
799
800
4.10k
    if (!dont_compress && is_token)
801
4.10k
        dont_compress = ((const h2o_token_t *)name)->flags.dont_compress;
802
4.10k
    if (dont_compress)
803
0
        dont_compress = value->len < 20;
804
805
4.10k
    if (name_index != 0) {
806
        /* literal header field with indexing (indexed name). */
807
4.10k
        if (dont_compress == 1) {
808
            /* mark the field as 'never indexed' */
809
0
            *dst = 0x10;
810
0
            dst = h2o_hpack_encode_int(dst, name_index, 4);
811
4.10k
        } else {
812
4.10k
            *dst = 0x40;
813
4.10k
            dst = h2o_hpack_encode_int(dst, name_index, 6);
814
4.10k
        }
815
4.10k
    } else {
816
        /* literal header field with indexing (new name) */
817
0
        *dst++ = 0x40;
818
0
        dst += h2o_hpack_encode_string(dst, name->base, name->len);
819
0
    }
820
4.10k
    if (dont_compress == 1) {
821
        /* bypass huffman encoding */
822
0
        dst += encode_as_is(dst, value->base, value->len);
823
4.10k
    } else {
824
        /* add to header table (maximum number of entries in output header table is limited to 32 so that the search (see above)
825
           would
826
           not take too long) */
827
4.10k
        dst += h2o_hpack_encode_string(dst, value->base, value->len);
828
4.10k
        struct st_h2o_hpack_header_table_entry_t *entry =
829
4.10k
            header_table_add(header_table, name->len + value->len + HEADER_TABLE_ENTRY_SIZE_OFFSET, 32);
830
4.10k
        if (entry != NULL) {
831
3.98k
            if (is_token) {
832
3.98k
                entry->name = (h2o_iovec_t *)name;
833
3.98k
            } else {
834
0
                entry->name = alloc_buf(NULL, name->len);
835
0
                entry->name->base[name->len] = '\0';
836
0
                memcpy(entry->name->base, name->base, name->len);
837
0
            }
838
3.98k
            entry->value = alloc_buf(NULL, value->len);
839
3.98k
            entry->value->base[value->len] = '\0';
840
3.98k
            memcpy(entry->value->base, value->base, value->len);
841
3.98k
        }
842
4.10k
    }
843
844
4.10k
    return dst;
845
8.47k
}
846
847
static uint8_t *encode_header(h2o_hpack_header_table_t *header_table, uint8_t *dst, const h2o_header_t *header)
848
4.23k
{
849
4.23k
    return do_encode_header(header_table, dst, header->name, &header->value, header->flags.dont_compress);
850
4.23k
}
851
852
static uint8_t *encode_header_token(h2o_hpack_header_table_t *header_table, uint8_t *dst, const h2o_token_t *token,
853
                                    const h2o_iovec_t *value)
854
4.23k
{
855
4.23k
    return do_encode_header(header_table, dst, &token->buf, value, token->flags.dont_compress);
856
4.23k
}
857
858
static uint8_t *encode_method(h2o_hpack_header_table_t *header_table, uint8_t *dst, h2o_iovec_t value)
859
0
{
860
0
    if (h2o_memis(value.base, value.len, H2O_STRLIT("GET"))) {
861
0
        *dst++ = 0x82;
862
0
        return dst;
863
0
    }
864
0
    if (h2o_memis(value.base, value.len, H2O_STRLIT("POST"))) {
865
0
        *dst++ = 0x83;
866
0
        return dst;
867
0
    }
868
0
    return encode_header_token(header_table, dst, H2O_TOKEN_METHOD, &value);
869
0
}
870
871
static uint8_t *encode_scheme(h2o_hpack_header_table_t *header_table, uint8_t *dst, const h2o_url_scheme_t *scheme)
872
0
{
873
0
    if (scheme == &H2O_URL_SCHEME_HTTPS) {
874
0
        *dst++ = 0x87;
875
0
        return dst;
876
0
    }
877
0
    if (scheme == &H2O_URL_SCHEME_HTTP) {
878
0
        *dst++ = 0x86;
879
0
        return dst;
880
0
    }
881
0
    return encode_header_token(header_table, dst, H2O_TOKEN_SCHEME, &scheme->name);
882
0
}
883
884
static uint8_t *encode_path(h2o_hpack_header_table_t *header_table, uint8_t *dst, h2o_iovec_t value)
885
0
{
886
0
    if (h2o_memis(value.base, value.len, H2O_STRLIT("/"))) {
887
0
        *dst++ = 0x84;
888
0
        return dst;
889
0
    }
890
0
    if (h2o_memis(value.base, value.len, H2O_STRLIT("/index.html"))) {
891
0
        *dst++ = 0x85;
892
0
        return dst;
893
0
    }
894
0
    return encode_header_token(header_table, dst, H2O_TOKEN_PATH, &value);
895
0
}
896
897
static uint8_t *encode_literal_header_without_indexing(uint8_t *dst, const h2o_iovec_t *name, const h2o_iovec_t *value)
898
0
{
899
0
    /* literal header field without indexing / never indexed */
900
0
    *dst++ = 0;
901
0
    dst += h2o_hpack_encode_string(dst, name->base, name->len);
902
0
    dst += h2o_hpack_encode_string(dst, value->base, value->len);
903
0
    return dst;
904
0
}
905
906
static size_t calc_capacity(size_t name_len, size_t value_len)
907
4.23k
{
908
4.23k
    return name_len + value_len + 1 + H2O_HPACK_ENCODE_INT_MAX_LENGTH * 2;
909
4.23k
}
910
911
static size_t calc_headers_capacity(const h2o_header_t *headers, size_t num_headers)
912
4.23k
{
913
4.23k
    const h2o_header_t *header;
914
4.23k
    size_t capacity = 0;
915
8.47k
    for (header = headers; num_headers != 0; ++header, --num_headers)
916
4.23k
        capacity += calc_capacity(header->name->len, header->value.len);
917
4.23k
    return capacity;
918
4.23k
}
919
920
static void fixup_frame_headers(h2o_buffer_t **buf, size_t start_at, uint8_t type, uint32_t stream_id, size_t max_frame_size,
921
                                int flags)
922
4.23k
{
923
    /* try to fit all data into single frame, using the preallocated space for the frame header */
924
4.23k
    size_t payload_size = (*buf)->size - start_at - H2O_HTTP2_FRAME_HEADER_SIZE;
925
4.23k
    if (payload_size <= max_frame_size) {
926
4.23k
        h2o_http2_encode_frame_header((uint8_t *)((*buf)->bytes + start_at), payload_size, type,
927
4.23k
                                      H2O_HTTP2_FRAME_FLAG_END_HEADERS | flags, stream_id);
928
4.23k
        return;
929
4.23k
    }
930
931
    /* need to setup continuation frames */
932
0
    size_t off;
933
0
    h2o_http2_encode_frame_header((uint8_t *)((*buf)->bytes + start_at), max_frame_size, type, flags, stream_id);
934
0
    off = start_at + H2O_HTTP2_FRAME_HEADER_SIZE + max_frame_size;
935
0
    while (1) {
936
0
        size_t left = (*buf)->size - off;
937
0
        h2o_buffer_reserve(buf, H2O_HTTP2_FRAME_HEADER_SIZE);
938
0
        memmove((*buf)->bytes + off + H2O_HTTP2_FRAME_HEADER_SIZE, (*buf)->bytes + off, left);
939
0
        (*buf)->size += H2O_HTTP2_FRAME_HEADER_SIZE;
940
0
        if (left <= max_frame_size) {
941
0
            h2o_http2_encode_frame_header((uint8_t *)((*buf)->bytes + off), left, H2O_HTTP2_FRAME_TYPE_CONTINUATION,
942
0
                                          H2O_HTTP2_FRAME_FLAG_END_HEADERS, stream_id);
943
0
            break;
944
0
        } else {
945
0
            h2o_http2_encode_frame_header((uint8_t *)((*buf)->bytes + off), max_frame_size, H2O_HTTP2_FRAME_TYPE_CONTINUATION, 0,
946
0
                                          stream_id);
947
0
            off += H2O_HTTP2_FRAME_HEADER_SIZE + max_frame_size;
948
0
        }
949
0
    }
950
0
}
951
952
void h2o_hpack_flatten_request(h2o_buffer_t **buf, h2o_hpack_header_table_t *header_table, uint32_t hpack_capacity,
953
                               uint32_t stream_id, size_t max_frame_size, h2o_iovec_t method, h2o_url_t *url,
954
                               const h2o_header_t *headers, size_t num_headers, int is_end_stream)
955
0
{
956
0
    int is_connect = h2o_memis(method.base, method.len, H2O_STRLIT("CONNECT"));
957
958
0
    size_t capacity = calc_headers_capacity(headers, num_headers);
959
0
    capacity += H2O_HTTP2_FRAME_HEADER_SIZE;
960
0
    capacity += DYNAMIC_TABLE_SIZE_UPDATE_MAX_SIZE;
961
0
    capacity += calc_capacity(H2O_TOKEN_METHOD->buf.len, method.len);
962
0
    if (!is_connect)
963
0
        capacity += calc_capacity(H2O_TOKEN_SCHEME->buf.len, url->scheme->name.len);
964
0
    capacity += calc_capacity(H2O_TOKEN_AUTHORITY->buf.len, url->authority.len);
965
0
    if (!is_connect)
966
0
        capacity += calc_capacity(H2O_TOKEN_PATH->buf.len, url->path.len);
967
968
0
    size_t start_at = (*buf)->size;
969
0
    uint8_t *dst = (void *)(h2o_buffer_reserve(buf, capacity).base + H2O_HTTP2_FRAME_HEADER_SIZE);
970
971
    /* encode */
972
0
    dst = header_table_adjust_size(header_table, hpack_capacity, dst);
973
0
    dst = encode_method(header_table, dst, method);
974
0
    if (!is_connect)
975
0
        dst = encode_scheme(header_table, dst, url->scheme);
976
0
    dst = encode_header_token(header_table, dst, H2O_TOKEN_AUTHORITY, &url->authority);
977
0
    if (!is_connect)
978
0
        dst = encode_path(header_table, dst, url->path);
979
0
    for (size_t i = 0; i != num_headers; ++i) {
980
0
        const h2o_header_t *header = headers + i;
981
0
        if (header->name == &H2O_TOKEN_ACCEPT_ENCODING->buf &&
982
0
            h2o_memis(header->value.base, header->value.len, H2O_STRLIT("gzip, deflate"))) {
983
0
            *dst++ = 0x90;
984
0
        } else {
985
0
            dst = encode_header(header_table, dst, header);
986
0
        }
987
0
    }
988
0
    (*buf)->size = (char *)dst - (*buf)->bytes;
989
990
    /* setup the frame headers */
991
0
    fixup_frame_headers(buf, start_at, H2O_HTTP2_FRAME_TYPE_HEADERS, stream_id, max_frame_size,
992
0
                        is_end_stream ? H2O_HTTP2_FRAME_FLAG_END_STREAM : 0);
993
0
}
994
995
void h2o_hpack_flatten_push_promise(h2o_buffer_t **buf, h2o_hpack_header_table_t *header_table, uint32_t hpack_capacity,
996
                                    uint32_t stream_id, size_t max_frame_size, const h2o_url_scheme_t *scheme,
997
                                    h2o_iovec_t authority, h2o_iovec_t method, h2o_iovec_t path, const h2o_header_t *headers,
998
                                    size_t num_headers, uint32_t parent_stream_id)
999
0
{
1000
0
    size_t capacity = calc_headers_capacity(headers, num_headers);
1001
    capacity += H2O_HTTP2_FRAME_HEADER_SIZE /* first frame header */
1002
0
                + 4;                        /* promised stream id */
1003
0
    capacity += DYNAMIC_TABLE_SIZE_UPDATE_MAX_SIZE;
1004
0
    capacity += calc_capacity(H2O_TOKEN_METHOD->buf.len, method.len);
1005
0
    capacity += calc_capacity(H2O_TOKEN_SCHEME->buf.len, scheme->name.len);
1006
0
    capacity += calc_capacity(H2O_TOKEN_AUTHORITY->buf.len, authority.len);
1007
0
    capacity += calc_capacity(H2O_TOKEN_PATH->buf.len, path.len);
1008
1009
0
    size_t start_at = (*buf)->size;
1010
0
    uint8_t *dst = (void *)(h2o_buffer_reserve(buf, capacity).base + H2O_HTTP2_FRAME_HEADER_SIZE);
1011
1012
    /* encode */
1013
0
    dst = h2o_http2_encode32u(dst, stream_id);
1014
0
    dst = header_table_adjust_size(header_table, hpack_capacity, dst);
1015
0
    dst = encode_method(header_table, dst, method);
1016
0
    dst = encode_scheme(header_table, dst, scheme);
1017
0
    dst = encode_header_token(header_table, dst, H2O_TOKEN_AUTHORITY, &authority);
1018
0
    dst = encode_path(header_table, dst, path);
1019
0
    for (size_t i = 0; i != num_headers; ++i) {
1020
0
        const h2o_header_t *header = headers + i;
1021
0
        if (header->name == &H2O_TOKEN_ACCEPT_ENCODING->buf &&
1022
0
            h2o_memis(header->value.base, header->value.len, H2O_STRLIT("gzip, deflate"))) {
1023
0
            *dst++ = 0x90;
1024
0
        } else {
1025
0
            dst = encode_header(header_table, dst, header);
1026
0
        }
1027
0
    }
1028
0
    (*buf)->size = (char *)dst - (*buf)->bytes;
1029
1030
    /* setup the frame headers */
1031
0
    fixup_frame_headers(buf, start_at, H2O_HTTP2_FRAME_TYPE_PUSH_PROMISE, parent_stream_id, max_frame_size, 0);
1032
0
}
1033
1034
void h2o_hpack_flatten_response(h2o_buffer_t **buf, h2o_hpack_header_table_t *header_table, uint32_t hpack_capacity,
1035
                                uint32_t stream_id, size_t max_frame_size, int status, const h2o_header_t *headers,
1036
                                size_t num_headers, const h2o_iovec_t *server_name, size_t content_length)
1037
4.23k
{
1038
4.23k
    size_t capacity = calc_headers_capacity(headers, num_headers);
1039
4.23k
    capacity += H2O_HTTP2_FRAME_HEADER_SIZE; /* for the first header */
1040
4.23k
    capacity += DYNAMIC_TABLE_SIZE_UPDATE_MAX_SIZE;
1041
4.23k
    capacity += STATUS_HEADER_MAX_SIZE; /* for :status: */
1042
4.23k
#ifndef H2O_UNITTEST
1043
4.23k
    if (server_name != NULL && server_name->len) {
1044
4.23k
        capacity += 5 + server_name->len; /* for Server: */
1045
4.23k
    }
1046
4.23k
#endif
1047
4.23k
    if (content_length != SIZE_MAX)
1048
3.55k
        capacity += CONTENT_LENGTH_HEADER_MAX_SIZE; /* for content-length: UINT64_MAX (with huffman compression applied) */
1049
1050
4.23k
    size_t start_at = (*buf)->size;
1051
4.23k
    uint8_t *dst = (void *)(h2o_buffer_reserve(buf, capacity).base + H2O_HTTP2_FRAME_HEADER_SIZE); /* skip frame header */
1052
1053
    /* encode */
1054
4.23k
    dst = header_table_adjust_size(header_table, hpack_capacity, dst);
1055
4.23k
    dst = encode_status(dst, status);
1056
4.23k
#ifndef H2O_UNITTEST
1057
    /* TODO keep some kind of reference to the indexed Server header, and reuse it */
1058
4.23k
    if (server_name != NULL && server_name->len) {
1059
4.23k
        dst = encode_header_token(header_table, dst, H2O_TOKEN_SERVER, server_name);
1060
4.23k
    }
1061
4.23k
#endif
1062
8.47k
    for (size_t i = 0; i != num_headers; ++i)
1063
4.23k
        dst = encode_header(header_table, dst, headers + i);
1064
4.23k
    if (content_length != SIZE_MAX)
1065
3.55k
        dst = encode_content_length(dst, content_length);
1066
4.23k
    (*buf)->size = (char *)dst - (*buf)->bytes;
1067
1068
    /* setup the frame headers */
1069
4.23k
    fixup_frame_headers(buf, start_at, H2O_HTTP2_FRAME_TYPE_HEADERS, stream_id, max_frame_size, 0);
1070
4.23k
}
1071
1072
void h2o_hpack_flatten_trailers(h2o_buffer_t **buf, h2o_hpack_header_table_t *header_table, uint32_t hpack_capacity,
1073
                                uint32_t stream_id, size_t max_frame_size, const h2o_header_t *headers, size_t num_headers)
1074
0
{
1075
0
    size_t capacity = calc_headers_capacity(headers, num_headers);
1076
0
    capacity += H2O_HTTP2_FRAME_HEADER_SIZE;
1077
0
    capacity += DYNAMIC_TABLE_SIZE_UPDATE_MAX_SIZE;
1078
1079
0
    size_t start_at = (*buf)->size;
1080
0
    uint8_t *dst = (void *)(h2o_buffer_reserve(buf, capacity).base + H2O_HTTP2_FRAME_HEADER_SIZE); /* skip frame header */
1081
1082
0
    dst = header_table_adjust_size(header_table, hpack_capacity, dst);
1083
0
    for (size_t i = 0; i != num_headers; ++i)
1084
0
        dst = encode_header(header_table, dst, headers + i);
1085
0
    (*buf)->size = (char *)dst - (*buf)->bytes;
1086
1087
    /* setup the frame headers */
1088
0
    fixup_frame_headers(buf, start_at, H2O_HTTP2_FRAME_TYPE_HEADERS, stream_id, max_frame_size, H2O_HTTP2_FRAME_FLAG_END_STREAM);
1089
0
}