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
856k
#define HEADER_TABLE_OFFSET 62
30
37.2k
#define HEADER_TABLE_ENTRY_SIZE_OFFSET 32
31
8.71k
#define DYNAMIC_TABLE_SIZE_UPDATE_MAX_SIZE 5
32
8.71k
#define STATUS_HEADER_MAX_SIZE 5
33
#define CONTENT_LENGTH_HEADER_MAX_SIZE                                                                                             \
34
7.80k
    (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
39.9k
{
40
39.9k
    return &h2o_hpack_static_table[0].value <= value &&
41
39.9k
           value <= &h2o_hpack_static_table[sizeof(h2o_hpack_static_table) / sizeof(h2o_hpack_static_table[0]) - 1].value;
42
39.9k
}
43
44
static h2o_iovec_t *alloc_buf(h2o_mem_pool_t *pool, size_t len)
45
129k
{
46
129k
    h2o_iovec_t *buf = h2o_mem_alloc_shared(pool, sizeof(h2o_iovec_t) + len + 1, NULL);
47
129k
    buf->base = (char *)buf + sizeof(h2o_iovec_t);
48
129k
    buf->len = len;
49
129k
    return buf;
50
129k
}
51
52
int64_t h2o_hpack_decode_int(const uint8_t **src, const uint8_t *src_end, unsigned prefix_bits)
53
1.28M
{
54
1.28M
    uint64_t value;
55
1.28M
    unsigned shift;
56
1.28M
    uint8_t prefix_max = (1 << prefix_bits) - 1;
57
58
1.28M
    if (*src >= src_end)
59
5
        return H2O_HTTP2_ERROR_INCOMPLETE;
60
61
1.28M
    value = *(*src)++ & prefix_max;
62
1.28M
    if (value != prefix_max)
63
1.25M
        return (int64_t)value;
64
65
    /* decode upto 8 octets (excluding prefix), that are guaranteed not to cause overflow */
66
32.6k
    value = prefix_max;
67
48.7k
    for (shift = 0; shift < 56; shift += 7) {
68
47.9k
        if (*src == src_end)
69
182
            return H2O_HTTP2_ERROR_INCOMPLETE;
70
47.7k
        value += (uint64_t)(**src & 127) << shift;
71
47.7k
        if ((*(*src)++ & 128) == 0)
72
31.6k
            return (int64_t)value;
73
47.7k
    }
74
    /* handling the 9th octet */
75
768
    if (*src == src_end)
76
13
        return H2O_HTTP2_ERROR_INCOMPLETE;
77
755
    if ((**src & 128) != 0)
78
51
        return H2O_HTTP2_ERROR_COMPRESSION;
79
704
    value += (uint64_t)(*(*src)++ & 127) << shift;
80
704
    if (value > (uint64_t)INT64_MAX)
81
9
        return H2O_HTTP2_ERROR_COMPRESSION;
82
695
    return value;
83
704
}
84
85
static char *huffdecode4(char *dst, uint8_t in, uint8_t *state, int *maybe_eos, uint8_t *seen_char_types)
86
1.55M
{
87
1.55M
    const nghttp2_huff_decode *entry = huff_decode_table[*state] + in;
88
89
1.55M
    if ((entry->flags & NGHTTP2_HUFF_FAIL) != 0)
90
24
        return NULL;
91
1.55M
    if ((entry->flags & NGHTTP2_HUFF_SYM) != 0) {
92
1.00M
        *dst++ = entry->sym;
93
1.00M
        *seen_char_types |= (entry->flags & NGHTTP2_HUFF_INVALID_CHARS);
94
1.00M
    }
95
1.55M
    *state = entry->state;
96
1.55M
    *maybe_eos = (entry->flags & NGHTTP2_HUFF_ACCEPTED) != 0;
97
98
1.55M
    return dst;
99
1.55M
}
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
46.6k
{
108
46.6k
    char *dst = _dst;
109
46.6k
    const uint8_t *src_end = src + len;
110
46.6k
    uint8_t state = 0, seen_char_types = 0;
111
46.6k
    int maybe_eos = 1;
112
113
    /* decode */
114
826k
    for (; src < src_end; src++) {
115
779k
        if ((dst = huffdecode4(dst, *src >> 4, &state, &maybe_eos, &seen_char_types)) == NULL)
116
11
            return SIZE_MAX;
117
779k
        if ((dst = huffdecode4(dst, *src & 0xf, &state, &maybe_eos, &seen_char_types)) == NULL)
118
13
            return SIZE_MAX;
119
779k
    }
120
46.6k
    if (!maybe_eos)
121
196
        return SIZE_MAX;
122
123
    /* validate */
124
46.4k
    if (is_name) {
125
16.8k
        if (dst == _dst)
126
27
            return SIZE_MAX;
127
        /* pseudo-headers are checked later in `decode_header` */
128
16.7k
        if ((seen_char_types & NGHTTP2_HUFF_INVALID_FOR_HEADER_NAME) != 0 && _dst[0] != ':') {
129
11.7k
            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
11.7k
            } else {
133
11.7k
                *soft_errors |= H2O_HPACK_SOFT_ERROR_BIT_INVALID_NAME;
134
11.7k
            }
135
11.7k
        }
136
29.6k
    } else {
137
29.6k
        if ((seen_char_types & NGHTTP2_HUFF_INVALID_FOR_HEADER_VALUE) != 0)
138
238
            *soft_errors |= H2O_HPACK_SOFT_ERROR_BIT_INVALID_VALUE;
139
29.6k
    }
140
141
46.4k
    return dst - _dst;
142
46.4k
}
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
76.1k
{
151
    /* all printable chars, except upper case and separator characters */
152
76.1k
    static const char valid_h2_header_name_char[] = {
153
76.1k
        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
76.1k
        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
76.1k
        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
76.1k
        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
76.1k
        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
76.1k
        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
76.1k
        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
76.1k
        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
76.1k
    };
162
163
449k
    for (; len != 0; ++s, --len) {
164
373k
        unsigned char ch = (unsigned char)*s;
165
373k
        if (!valid_h2_header_name_char[ch]) {
166
104k
            if (ch - 'A' < 26U) {
167
121
                *err_desc = h2o_hpack_err_found_upper_case_in_header_name;
168
121
                return 0;
169
121
            }
170
104k
            *soft_errors |= H2O_HPACK_SOFT_ERROR_BIT_INVALID_NAME;
171
104k
        }
172
373k
    }
173
75.9k
    return 1;
174
76.1k
}
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
107k
{
179
    /* all printable chars + horizontal tab */
180
107k
    static const char valid_h2_field_value_char[] = {
181
107k
        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
107k
        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
107k
        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
107k
        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
107k
        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
107k
        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
107k
        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
107k
        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
107k
    };
190
191
449k
    for (; len != 0; ++s, --len) {
192
371k
        unsigned char ch = (unsigned char)*s;
193
371k
        if (!valid_h2_field_value_char[ch]) {
194
29.8k
            *soft_errors |= H2O_HPACK_SOFT_ERROR_BIT_INVALID_VALUE;
195
29.8k
            break;
196
29.8k
        }
197
371k
    }
198
107k
}
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
125k
{
203
125k
    h2o_iovec_t *ret;
204
125k
    int is_huffman;
205
125k
    int64_t len;
206
207
125k
    if (*src >= src_end)
208
512
        return NULL;
209
210
124k
    is_huffman = (**src & 0x80) != 0;
211
124k
    if ((len = h2o_hpack_decode_int(src, src_end, 7)) < 0)
212
27
        return NULL;
213
214
124k
    if (is_huffman) {
215
15.1k
        if (len > src_end - *src)
216
491
            return NULL;
217
14.6k
        ret = alloc_buf(pool, len * 2); /* max compression ratio is >= 0.5 */
218
14.6k
        if ((ret->len = h2o_hpack_decode_huffman(ret->base, soft_errors, *src, len, is_header_name, err_desc)) == SIZE_MAX)
219
131
            return NULL;
220
14.5k
        ret->base[ret->len] = '\0';
221
109k
    } else {
222
109k
        if (len > src_end - *src)
223
1.34k
            return NULL;
224
108k
        if (is_header_name) {
225
            /* pseudo-headers are checked later in `decode_header` */
226
47.8k
            if (**src != (uint8_t)':' && !h2o_hpack_validate_header_name(soft_errors, (char *)*src, len, err_desc))
227
59
                return NULL;
228
60.5k
        } else {
229
60.5k
            h2o_hpack_validate_header_value(soft_errors, (char *)*src, len);
230
60.5k
        }
231
108k
        ret = alloc_buf(pool, len);
232
108k
        memcpy(ret->base, *src, len);
233
108k
        ret->base[len] = '\0';
234
108k
    }
235
122k
    *src += len;
236
237
122k
    return ret;
238
124k
}
239
240
static void header_table_evict_one(h2o_hpack_header_table_t *table)
241
7.80k
{
242
7.80k
    struct st_h2o_hpack_header_table_entry_t *entry;
243
7.80k
    assert(table->num_entries != 0);
244
245
0
    entry = h2o_hpack_header_table_get(table, --table->num_entries);
246
7.80k
    table->hpack_size -= entry->name->len + entry->value->len + HEADER_TABLE_ENTRY_SIZE_OFFSET;
247
7.80k
    if (!h2o_iovec_is_token(entry->name))
248
1.66k
        h2o_mem_release_shared(entry->name);
249
7.80k
    if (!value_is_part_of_static_table(entry->value))
250
7.80k
        h2o_mem_release_shared(entry->value);
251
7.80k
    memset(entry, 0, sizeof(*entry));
252
7.80k
}
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
29.4k
{
257
    /* adjust the size */
258
32.7k
    while (table->num_entries != 0 && table->hpack_size + size_add > table->hpack_capacity)
259
3.29k
        header_table_evict_one(table);
260
29.4k
    while (max_num_entries <= table->num_entries)
261
0
        header_table_evict_one(table);
262
29.4k
    if (table->num_entries == 0) {
263
15.1k
        assert(table->hpack_size == 0);
264
15.1k
        if (size_add > table->hpack_capacity)
265
6.04k
            return NULL;
266
15.1k
    }
267
23.3k
    table->hpack_size += size_add;
268
269
    /* grow the entries if full */
270
23.3k
    if (table->num_entries == table->entry_capacity) {
271
6.98k
        size_t new_capacity = table->num_entries * 2;
272
6.98k
        if (new_capacity < 16)
273
6.78k
            new_capacity = 16;
274
6.98k
        struct st_h2o_hpack_header_table_entry_t *new_entries =
275
6.98k
            h2o_mem_alloc(new_capacity * sizeof(struct st_h2o_hpack_header_table_entry_t));
276
6.98k
        if (table->num_entries != 0) {
277
203
            size_t src_index = table->entry_start_index, dst_index = 0;
278
5.39k
            do {
279
5.39k
                new_entries[dst_index] = table->entries[src_index];
280
5.39k
                ++dst_index;
281
5.39k
                src_index = (src_index + 1) % table->entry_capacity;
282
5.39k
            } while (dst_index != table->num_entries);
283
203
        }
284
6.98k
        memset(new_entries + table->num_entries, 0, sizeof(*new_entries) * (new_capacity - table->num_entries));
285
6.98k
        free(table->entries);
286
6.98k
        table->entries = new_entries;
287
6.98k
        table->entry_capacity = new_capacity;
288
6.98k
        table->entry_start_index = 0;
289
6.98k
    }
290
291
23.3k
    ++table->num_entries;
292
23.3k
    table->entry_start_index = (table->entry_start_index + table->entry_capacity - 1) % table->entry_capacity;
293
23.3k
    return table->entries + table->entry_start_index;
294
29.4k
}
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
383k
{
299
383k
    h2o_hpack_header_table_t *hpack_header_table = _hpack_header_table;
300
383k
    h2o_iovec_t *name = NULL, *value = NULL;
301
383k
    int64_t index = 0;
302
383k
    int value_is_indexed = 0, do_index = 0;
303
304
408k
Redo:
305
408k
    if (*src >= src_end)
306
151
        return H2O_HTTP2_ERROR_COMPRESSION;
307
308
    /* determine the mode and handle accordingly */
309
408k
    if (**src >= 128) {
310
        /* indexed header field representation */
311
306k
        if ((index = h2o_hpack_decode_int(src, src_end, 7)) <= 0)
312
61
            return H2O_HTTP2_ERROR_COMPRESSION;
313
306k
        value_is_indexed = 1;
314
306k
    } else if (**src >= 64) {
315
        /* literal header field with incremental handling */
316
23.2k
        if (**src == 64) {
317
4.84k
            ++*src;
318
18.3k
        } else if ((index = h2o_hpack_decode_int(src, src_end, 6)) <= 0) {
319
27
            return H2O_HTTP2_ERROR_COMPRESSION;
320
27
        }
321
23.1k
        do_index = 1;
322
78.5k
    } else if (**src < 32) {
323
        /* literal header field without indexing / never indexed */
324
53.4k
        if ((**src & 0xf) == 0) {
325
44.6k
            ++*src;
326
44.6k
        } else if ((index = h2o_hpack_decode_int(src, src_end, 4)) <= 0) {
327
22
            return H2O_HTTP2_ERROR_COMPRESSION;
328
22
        }
329
53.4k
    } else {
330
        /* size update */
331
25.1k
        int64_t new_capacity;
332
25.1k
        if ((new_capacity = h2o_hpack_decode_int(src, src_end, 5)) < 0) {
333
23
            return H2O_HTTP2_ERROR_COMPRESSION;
334
23
        }
335
25.1k
        if (new_capacity > hpack_header_table->hpack_max_capacity) {
336
201
            return H2O_HTTP2_ERROR_COMPRESSION;
337
201
        }
338
24.9k
        hpack_header_table->hpack_capacity = (size_t)new_capacity;
339
29.4k
        while (hpack_header_table->num_entries != 0 && hpack_header_table->hpack_size > hpack_header_table->hpack_capacity) {
340
4.47k
            header_table_evict_one(hpack_header_table);
341
4.47k
        }
342
24.9k
        goto Redo;
343
25.1k
    }
344
345
    /* determine the header */
346
383k
    unsigned soft_errors = 0;
347
383k
    if (index > 0) {
348
        /* existing name (and value?) */
349
333k
        if (index < HEADER_TABLE_OFFSET) {
350
76.8k
            name = (h2o_iovec_t *)h2o_hpack_static_table[index - 1].name;
351
76.8k
            if (value_is_indexed)
352
51.1k
                value = (h2o_iovec_t *)&h2o_hpack_static_table[index - 1].value;
353
256k
        } else if (index - HEADER_TABLE_OFFSET < hpack_header_table->num_entries) {
354
255k
            struct st_h2o_hpack_header_table_entry_t *entry =
355
255k
                h2o_hpack_header_table_get(hpack_header_table, index - HEADER_TABLE_OFFSET);
356
255k
            soft_errors = entry->soft_errors;
357
255k
            name = entry->name;
358
255k
            if (!h2o_iovec_is_token(name))
359
103k
                h2o_mem_link_shared(pool, name);
360
255k
            if (value_is_indexed) {
361
254k
                value = entry->value;
362
254k
                h2o_mem_link_shared(pool, value);
363
254k
            }
364
255k
        } else {
365
847
            return H2O_HTTP2_ERROR_COMPRESSION;
366
847
        }
367
333k
    } else {
368
        /* non-existing name */
369
49.5k
        const h2o_token_t *name_token;
370
49.5k
        if ((name = decode_string(pool, &soft_errors, src, src_end, 1, err_desc)) == NULL) {
371
384
            if (*err_desc == h2o_hpack_err_found_upper_case_in_header_name)
372
59
                return H2O_HTTP2_ERROR_PROTOCOL;
373
325
            return H2O_HTTP2_ERROR_COMPRESSION;
374
384
        }
375
        /* predefined header names should be interned */
376
49.1k
        if ((name_token = h2o_lookup_token(name->base, name->len)) != NULL)
377
5.23k
            name = (h2o_iovec_t *)&name_token->buf;
378
49.1k
    }
379
380
    /* determine the value (if necessary) */
381
381k
    if (!value_is_indexed) {
382
75.9k
        soft_errors &= ~H2O_HPACK_SOFT_ERROR_BIT_INVALID_VALUE;
383
75.9k
        if ((value = decode_string(pool, &soft_errors, src, src_end, 0, err_desc)) == NULL)
384
2.17k
            return H2O_HTTP2_ERROR_COMPRESSION;
385
75.9k
    }
386
387
    /* add the decoded header to the header table if necessary */
388
379k
    if (do_index) {
389
22.0k
        struct st_h2o_hpack_header_table_entry_t *entry =
390
22.0k
            header_table_add(hpack_header_table, name->len + value->len + HEADER_TABLE_ENTRY_SIZE_OFFSET, SIZE_MAX);
391
22.0k
        if (entry != NULL) {
392
16.5k
            entry->soft_errors = soft_errors;
393
16.5k
            entry->name = name;
394
16.5k
            if (!h2o_iovec_is_token(entry->name))
395
3.63k
                h2o_mem_addref_shared(entry->name);
396
16.5k
            entry->value = value;
397
16.5k
            if (!value_is_part_of_static_table(entry->value))
398
16.5k
                h2o_mem_addref_shared(entry->value);
399
16.5k
        }
400
22.0k
    }
401
402
379k
    *_name = name;
403
379k
    *_value = *value;
404
379k
    if (soft_errors != 0) {
405
117k
        *err_desc = (soft_errors & H2O_HPACK_SOFT_ERROR_BIT_INVALID_NAME) != 0
406
117k
                        ? h2o_hpack_soft_err_found_invalid_char_in_header_name
407
117k
                        : h2o_hpack_soft_err_found_invalid_char_in_header_value;
408
117k
        return H2O_HTTP2_ERROR_INVALID_HEADER_CHAR;
409
262k
    } else {
410
262k
        return 0;
411
262k
    }
412
379k
}
413
414
static uint8_t *encode_status(uint8_t *dst, int status)
415
8.71k
{
416
    /* see also: STATUS_HEADER_MAX_SIZE */
417
418
8.71k
    assert(100 <= status && status <= 999);
419
420
0
    switch (status) {
421
0
#define COMMON_CODE(code, st)                                                                                                      \
422
8.71k
    case st:                                                                                                                       \
423
8.71k
        *dst++ = 0x80 | code;                                                                                                      \
424
8.71k
        break
425
906
        COMMON_CODE(8, 200);
426
0
        COMMON_CODE(9, 204);
427
0
        COMMON_CODE(10, 206);
428
0
        COMMON_CODE(11, 304);
429
3.32k
        COMMON_CODE(12, 400);
430
4.47k
        COMMON_CODE(13, 404);
431
0
        COMMON_CODE(14, 500);
432
0
#undef COMMON_CODE
433
1
    default:
434
        /* use literal header field without indexing - indexed name */
435
1
        *dst++ = 8;
436
1
        *dst++ = 3;
437
1
        sprintf((char *)dst, "%d", status);
438
1
        dst += 3;
439
1
        break;
440
8.71k
    }
441
442
8.71k
    return dst;
443
8.71k
}
444
445
static uint8_t *encode_content_length(uint8_t *dst, size_t value)
446
7.80k
{
447
7.80k
    char buf[32], *p = buf + sizeof(buf);
448
7.80k
    size_t l;
449
450
11.1k
    do {
451
11.1k
        *--p = '0' + value % 10;
452
11.1k
    } while ((value /= 10) != 0);
453
7.80k
    l = buf + sizeof(buf) - p;
454
7.80k
    *dst++ = 0x0f;
455
7.80k
    *dst++ = 0x0d;
456
7.80k
    *dst++ = (uint8_t)l;
457
7.80k
    memcpy(dst, p, l);
458
7.80k
    dst += l;
459
460
7.80k
    return dst;
461
7.80k
}
462
463
void h2o_hpack_dispose_header_table(h2o_hpack_header_table_t *header_table)
464
25.7k
{
465
25.7k
    if (header_table->num_entries != 0) {
466
5.88k
        size_t index = header_table->entry_start_index;
467
15.5k
        do {
468
15.5k
            struct st_h2o_hpack_header_table_entry_t *entry = header_table->entries + index;
469
15.5k
            if (!h2o_iovec_is_token(entry->name))
470
1.96k
                h2o_mem_release_shared(entry->name);
471
15.5k
            if (!value_is_part_of_static_table(entry->value))
472
15.5k
                h2o_mem_release_shared(entry->value);
473
15.5k
            index = (index + 1) % header_table->entry_capacity;
474
15.5k
        } while (--header_table->num_entries != 0);
475
5.88k
    }
476
25.7k
    free(header_table->entries);
477
25.7k
}
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
23.7k
{
484
23.7k
    const uint8_t *src_end = src + len;
485
486
23.7k
    *content_length = SIZE_MAX;
487
488
1.12M
    while (src != src_end) {
489
1.11M
        h2o_iovec_t *name, value;
490
1.11M
        const char *decode_err = NULL;
491
1.11M
        int ret = decode_cb(pool, decode_ctx, &name, &value, &src, src_end, &decode_err);
492
1.11M
        if (ret != 0) {
493
153k
            if (ret == H2O_HTTP2_ERROR_INVALID_HEADER_CHAR) {
494
                /* this is a soft error, we continue parsing, but register only the first error */
495
146k
                if (*err_desc == NULL) {
496
6.45k
                    *err_desc = decode_err;
497
6.45k
                }
498
146k
            } else {
499
7.28k
                *err_desc = decode_err;
500
7.28k
                return ret;
501
7.28k
            }
502
153k
        }
503
1.10M
        if (name->base[0] == ':') {
504
43.5k
            if (pseudo_header_exists_map != NULL) {
505
                /* FIXME validate the chars in the value (e.g. reject SP in path) */
506
43.3k
                if (name == &H2O_TOKEN_AUTHORITY->buf) {
507
9.67k
                    if (authority->base != NULL)
508
8
                        return H2O_HTTP2_ERROR_PROTOCOL;
509
9.66k
                    *authority = value;
510
9.66k
                    *pseudo_header_exists_map |= H2O_HPACK_PARSE_HEADERS_AUTHORITY_EXISTS;
511
33.6k
                } else if (name == &H2O_TOKEN_METHOD->buf) {
512
11.8k
                    if (method->base != NULL)
513
8
                        return H2O_HTTP2_ERROR_PROTOCOL;
514
11.8k
                    *method = value;
515
11.8k
                    *pseudo_header_exists_map |= H2O_HPACK_PARSE_HEADERS_METHOD_EXISTS;
516
21.7k
                } else if (name == &H2O_TOKEN_PATH->buf) {
517
10.8k
                    if (path->base != NULL)
518
8
                        return H2O_HTTP2_ERROR_PROTOCOL;
519
10.8k
                    if (value.len == 0)
520
9
                        return H2O_HTTP2_ERROR_PROTOCOL;
521
10.7k
                    *path = value;
522
10.7k
                    *pseudo_header_exists_map |= H2O_HPACK_PARSE_HEADERS_PATH_EXISTS;
523
10.9k
                } else if (name == &H2O_TOKEN_SCHEME->buf) {
524
10.9k
                    if (*scheme != NULL)
525
6
                        return H2O_HTTP2_ERROR_PROTOCOL;
526
10.9k
                    if (h2o_memis(value.base, value.len, H2O_STRLIT("https"))) {
527
1.62k
                        *scheme = &H2O_URL_SCHEME_HTTPS;
528
9.33k
                    } else if (h2o_memis(value.base, value.len, H2O_STRLIT("masque"))) {
529
1
                        *scheme = &H2O_URL_SCHEME_MASQUE;
530
9.33k
                    } else {
531
                        /* draft-16 8.1.2.3 suggests quote: ":scheme is not restricted to http and https schemed URIs" */
532
9.33k
                        *scheme = &H2O_URL_SCHEME_HTTP;
533
9.33k
                    }
534
10.9k
                    *pseudo_header_exists_map |= H2O_HPACK_PARSE_HEADERS_SCHEME_EXISTS;
535
10.9k
                } else {
536
16
                    return H2O_HTTP2_ERROR_PROTOCOL;
537
16
                }
538
43.3k
            } else {
539
235
                return H2O_HTTP2_ERROR_PROTOCOL;
540
235
            }
541
1.06M
        } else {
542
1.06M
            pseudo_header_exists_map = NULL;
543
1.06M
            if (h2o_iovec_is_token(name)) {
544
873k
                h2o_token_t *token = H2O_STRUCT_FROM_MEMBER(h2o_token_t, buf, name);
545
873k
                if (token->flags.is_hpack_special) {
546
120k
                    if (token == H2O_TOKEN_CONTENT_LENGTH) {
547
8.75k
                        if ((*content_length = h2o_strtosize(value.base, value.len)) == SIZE_MAX)
548
81
                            return H2O_HTTP2_ERROR_PROTOCOL;
549
8.67k
                        goto Next;
550
111k
                    } else if (token == H2O_TOKEN_HOST) {
551
                        /* HTTP2 allows the use of host header (in place of :authority) */
552
4.08k
                        if (authority->base == NULL)
553
875
                            *authority = value;
554
4.08k
                        goto Next;
555
107k
                    } else if (token == H2O_TOKEN_TE && h2o_lcstris(value.base, value.len, H2O_STRLIT("trailers"))) {
556
                        /* do not reject */
557
106k
                    } 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
106k
                        h2o_cache_digests_load_header(digests, value.base, value.len);
560
106k
                    } else if (token == H2O_TOKEN_DATAGRAM_FLOW_ID) {
561
361
                        if (datagram_flow_id != NULL)
562
243
                            *datagram_flow_id = value;
563
361
                        goto Next;
564
361
                    } else {
565
                        /* rest of the header fields that are marked as special are rejected */
566
71
                        return H2O_HTTP2_ERROR_PROTOCOL;
567
71
                    }
568
120k
                }
569
860k
                h2o_add_header(pool, headers, token, NULL, value.base, value.len);
570
860k
            } else {
571
187k
                h2o_add_header_by_str(pool, headers, name->base, name->len, 0, NULL, value.base, value.len);
572
187k
            }
573
1.06M
        }
574
1.10M
    Next:;
575
1.10M
    }
576
577
16.0k
    if (*err_desc != NULL)
578
4.00k
        return H2O_HTTP2_ERROR_INVALID_HEADER_CHAR;
579
12.0k
    return 0;
580
16.0k
}
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
40.5k
{
662
40.5k
    return value < (1 << prefix_bits) - 1;
663
40.5k
}
664
665
uint8_t *h2o_hpack_encode_int(uint8_t *dst, int64_t value, unsigned prefix_bits)
666
33.2k
{
667
33.2k
    if (encode_int_is_onebyte(value, prefix_bits)) {
668
29.5k
        *dst++ |= value;
669
29.5k
    } else {
670
        /* see also: MAX_ENCODE_INT_LENGTH */
671
3.68k
        assert(value >= 0);
672
0
        value -= (1 << prefix_bits) - 1;
673
3.68k
        *dst++ |= (1 << prefix_bits) - 1;
674
3.71k
        for (; value >= 128; value >>= 7) {
675
30
            *dst++ = 0x80 | value;
676
30
        }
677
3.68k
        *dst++ = value;
678
3.68k
    }
679
0
    return dst;
680
33.2k
}
681
682
size_t h2o_hpack_encode_huffman(uint8_t *_dst, const uint8_t *src, size_t len)
683
12.4k
{
684
12.4k
    uint8_t *dst = _dst, *dst_end = dst + len;
685
12.4k
    const uint8_t *src_end = src + len;
686
12.4k
    uint64_t bits = 0;
687
12.4k
    int bits_left = 40;
688
689
283k
    while (src != src_end) {
690
271k
        const nghttp2_huff_sym *sym = huff_sym_table + *src++;
691
271k
        bits |= (uint64_t)sym->code << (bits_left - sym->nbits);
692
271k
        bits_left -= sym->nbits;
693
464k
        while (bits_left <= 32) {
694
192k
            *dst++ = bits >> 32;
695
192k
            bits <<= 8;
696
192k
            bits_left += 8;
697
192k
            if (dst == dst_end) {
698
0
                return SIZE_MAX;
699
0
            }
700
192k
        }
701
271k
    }
702
703
12.4k
    if (bits_left != 40) {
704
12.4k
        bits |= ((uint64_t)1 << bits_left) - 1;
705
12.4k
        *dst++ = bits >> 32;
706
12.4k
    }
707
12.4k
    if (dst == dst_end) {
708
1.41k
        return SIZE_MAX;
709
1.41k
    }
710
711
11.0k
    return dst - _dst;
712
12.4k
}
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
7.31k
{
726
7.31k
    if (H2O_LIKELY(len != 0)) {
727
        /* try to encode using huffman */
728
7.31k
        size_t hufflen = h2o_hpack_encode_huffman(dst + 1, (const uint8_t *)s, len);
729
7.31k
        if (H2O_LIKELY(hufflen != SIZE_MAX)) {
730
7.31k
            size_t head_len;
731
7.31k
            if (H2O_LIKELY(encode_int_is_onebyte((uint32_t)hufflen, 7))) {
732
7.31k
                dst[0] = (uint8_t)(0x80 | hufflen);
733
7.31k
                head_len = 1;
734
7.31k
            } 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
7.31k
            return head_len + hufflen;
742
7.31k
        }
743
7.31k
    }
744
0
    return encode_as_is(dst, s, len);
745
7.31k
}
746
747
static uint8_t *header_table_adjust_size(h2o_hpack_header_table_t *table, uint32_t new_capacity, uint8_t *dst)
748
8.71k
{
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
8.71k
    if (new_capacity >= table->hpack_capacity)
752
8.58k
        return dst;
753
754
    /* update state */
755
124
    table->hpack_capacity = new_capacity;
756
151
    while (table->num_entries != 0 && table->hpack_size > table->hpack_capacity)
757
27
        header_table_evict_one(table);
758
759
    /* encode Dynamic Table Size Update */
760
124
    *dst = 0x20;
761
124
    dst = h2o_hpack_encode_int(dst, table->hpack_capacity, 5);
762
763
124
    return dst;
764
8.71k
}
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
17.4k
{
769
17.4k
    int is_token = h2o_iovec_is_token(name);
770
17.4k
    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
17.4k
    {
774
17.4k
        size_t header_table_index = header_table->entry_start_index, n;
775
27.1k
        for (n = header_table->num_entries; n != 0; --n) {
776
19.8k
            struct st_h2o_hpack_header_table_entry_t *entry = header_table->entries + header_table_index;
777
19.8k
            if (is_token) {
778
19.8k
                if (name != entry->name)
779
9.67k
                    goto Next;
780
19.8k
            } 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
10.1k
            if (!h2o_memis(value->base, value->len, entry->value->base, entry->value->len))
788
28
                goto Next;
789
            /* name and value matched! */
790
10.1k
            *dst = 0x80;
791
10.1k
            dst = h2o_hpack_encode_int(dst, header_table->num_entries - n + HEADER_TABLE_OFFSET, 7);
792
10.1k
            return dst;
793
9.70k
        Next:
794
9.70k
            ++header_table_index;
795
9.70k
            if (header_table_index == header_table->entry_capacity)
796
2.76k
                header_table_index = 0;
797
9.70k
        }
798
17.4k
    }
799
800
7.31k
    if (!dont_compress && is_token)
801
7.31k
        dont_compress = ((const h2o_token_t *)name)->flags.dont_compress;
802
7.31k
    if (dont_compress)
803
0
        dont_compress = value->len < 20;
804
805
7.31k
    if (name_index != 0) {
806
        /* literal header field with indexing (indexed name). */
807
7.31k
        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
7.31k
        } else {
812
7.31k
            *dst = 0x40;
813
7.31k
            dst = h2o_hpack_encode_int(dst, name_index, 6);
814
7.31k
        }
815
7.31k
    } 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
7.31k
    if (dont_compress == 1) {
821
        /* bypass huffman encoding */
822
0
        dst += encode_as_is(dst, value->base, value->len);
823
7.31k
    } 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
7.31k
        dst += h2o_hpack_encode_string(dst, value->base, value->len);
828
7.31k
        struct st_h2o_hpack_header_table_entry_t *entry =
829
7.31k
            header_table_add(header_table, name->len + value->len + HEADER_TABLE_ENTRY_SIZE_OFFSET, 32);
830
7.31k
        if (entry != NULL) {
831
6.82k
            if (is_token) {
832
6.82k
                entry->name = (h2o_iovec_t *)name;
833
6.82k
            } 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
6.82k
            entry->value = alloc_buf(NULL, value->len);
839
6.82k
            entry->value->base[value->len] = '\0';
840
6.82k
            memcpy(entry->value->base, value->base, value->len);
841
6.82k
        }
842
7.31k
    }
843
844
7.31k
    return dst;
845
17.4k
}
846
847
static uint8_t *encode_header(h2o_hpack_header_table_t *header_table, uint8_t *dst, const h2o_header_t *header)
848
8.71k
{
849
8.71k
    return do_encode_header(header_table, dst, header->name, &header->value, header->flags.dont_compress);
850
8.71k
}
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
8.71k
{
855
8.71k
    return do_encode_header(header_table, dst, &token->buf, value, token->flags.dont_compress);
856
8.71k
}
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
8.71k
{
908
8.71k
    return name_len + value_len + 1 + H2O_HPACK_ENCODE_INT_MAX_LENGTH * 2;
909
8.71k
}
910
911
static size_t calc_headers_capacity(const h2o_header_t *headers, size_t num_headers)
912
8.71k
{
913
8.71k
    const h2o_header_t *header;
914
8.71k
    size_t capacity = 0;
915
17.4k
    for (header = headers; num_headers != 0; ++header, --num_headers)
916
8.71k
        capacity += calc_capacity(header->name->len, header->value.len);
917
8.71k
    return capacity;
918
8.71k
}
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
8.71k
{
923
    /* try to fit all data into single frame, using the preallocated space for the frame header */
924
8.71k
    size_t payload_size = (*buf)->size - start_at - H2O_HTTP2_FRAME_HEADER_SIZE;
925
8.71k
    if (payload_size <= max_frame_size) {
926
8.71k
        h2o_http2_encode_frame_header((uint8_t *)((*buf)->bytes + start_at), payload_size, type,
927
8.71k
                                      H2O_HTTP2_FRAME_FLAG_END_HEADERS | flags, stream_id);
928
8.71k
        return;
929
8.71k
    }
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
8.71k
{
1038
8.71k
    size_t capacity = calc_headers_capacity(headers, num_headers);
1039
8.71k
    capacity += H2O_HTTP2_FRAME_HEADER_SIZE; /* for the first header */
1040
8.71k
    capacity += DYNAMIC_TABLE_SIZE_UPDATE_MAX_SIZE;
1041
8.71k
    capacity += STATUS_HEADER_MAX_SIZE; /* for :status: */
1042
8.71k
#ifndef H2O_UNITTEST
1043
8.71k
    if (server_name != NULL && server_name->len) {
1044
8.71k
        capacity += 5 + server_name->len; /* for Server: */
1045
8.71k
    }
1046
8.71k
#endif
1047
8.71k
    if (content_length != SIZE_MAX)
1048
7.80k
        capacity += CONTENT_LENGTH_HEADER_MAX_SIZE; /* for content-length: UINT64_MAX (with huffman compression applied) */
1049
1050
8.71k
    size_t start_at = (*buf)->size;
1051
8.71k
    uint8_t *dst = (void *)(h2o_buffer_reserve(buf, capacity).base + H2O_HTTP2_FRAME_HEADER_SIZE); /* skip frame header */
1052
1053
    /* encode */
1054
8.71k
    dst = header_table_adjust_size(header_table, hpack_capacity, dst);
1055
8.71k
    dst = encode_status(dst, status);
1056
8.71k
#ifndef H2O_UNITTEST
1057
    /* TODO keep some kind of reference to the indexed Server header, and reuse it */
1058
8.71k
    if (server_name != NULL && server_name->len) {
1059
8.71k
        dst = encode_header_token(header_table, dst, H2O_TOKEN_SERVER, server_name);
1060
8.71k
    }
1061
8.71k
#endif
1062
17.4k
    for (size_t i = 0; i != num_headers; ++i)
1063
8.71k
        dst = encode_header(header_table, dst, headers + i);
1064
8.71k
    if (content_length != SIZE_MAX)
1065
7.80k
        dst = encode_content_length(dst, content_length);
1066
8.71k
    (*buf)->size = (char *)dst - (*buf)->bytes;
1067
1068
    /* setup the frame headers */
1069
8.71k
    fixup_frame_headers(buf, start_at, H2O_HTTP2_FRAME_TYPE_HEADERS, stream_id, max_frame_size, 0);
1070
8.71k
}
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
}