Coverage Report

Created: 2025-07-11 06:25

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