Coverage Report

Created: 2025-07-11 06:26

/src/h2o/lib/core/util.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2014-2016 DeNA Co., Ltd., Kazuho Oku, Satoh Hiroh
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 <assert.h>
23
#include <inttypes.h>
24
#include <stddef.h>
25
#include <stdio.h>
26
#include <stdlib.h>
27
#include <string.h>
28
#include <stdarg.h>
29
#include "h2o.h"
30
#include "h2o/http1.h"
31
#include "h2o/http2.h"
32
#include "h2o/hiredis_.h"
33
34
struct st_h2o_accept_data_t {
35
    h2o_accept_ctx_t *ctx;
36
    h2o_socket_t *sock;
37
    h2o_timer_t timeout;
38
    struct timeval connected_at;
39
};
40
41
struct st_h2o_memcached_resumption_accept_data_t {
42
    struct st_h2o_accept_data_t super;
43
    h2o_memcached_req_t *get_req;
44
};
45
46
struct st_h2o_redis_resumption_accept_data_t {
47
    struct st_h2o_accept_data_t super;
48
    h2o_redis_command_t *get_command;
49
};
50
51
static void on_accept_timeout(h2o_timer_t *entry);
52
static void on_redis_accept_timeout(h2o_timer_t *entry);
53
static void on_memcached_accept_timeout(h2o_timer_t *entry);
54
55
static struct {
56
    struct {
57
        h2o_memcached_context_t *ctx;
58
    } memcached;
59
    struct {
60
        h2o_iovec_t host;
61
        uint16_t port;
62
        h2o_iovec_t prefix;
63
    } redis;
64
    unsigned expiration;
65
} async_resumption_context;
66
67
static struct st_h2o_accept_data_t *create_accept_data(h2o_accept_ctx_t *ctx, h2o_socket_t *sock, struct timeval connected_at,
68
                                                       h2o_timer_cb timeout_cb, size_t sz)
69
0
{
70
0
    struct st_h2o_accept_data_t *data = h2o_mem_alloc(sz);
71
0
    data->ctx = ctx;
72
0
    data->sock = sock;
73
0
    h2o_timer_init(&data->timeout, timeout_cb);
74
0
    h2o_timer_link(ctx->ctx->loop, ctx->ctx->globalconf->handshake_timeout, &data->timeout);
75
0
    data->connected_at = connected_at;
76
0
    return data;
77
0
}
78
79
static struct st_h2o_accept_data_t *create_default_accept_data(h2o_accept_ctx_t *ctx, h2o_socket_t *sock,
80
                                                               struct timeval connected_at)
81
0
{
82
0
    struct st_h2o_accept_data_t *data =
83
0
        create_accept_data(ctx, sock, connected_at, on_accept_timeout, sizeof(struct st_h2o_accept_data_t));
84
0
    return data;
85
0
}
86
87
static struct st_h2o_accept_data_t *create_redis_accept_data(h2o_accept_ctx_t *ctx, h2o_socket_t *sock, struct timeval connected_at)
88
0
{
89
0
    struct st_h2o_redis_resumption_accept_data_t *data = (struct st_h2o_redis_resumption_accept_data_t *)create_accept_data(
90
0
        ctx, sock, connected_at, on_redis_accept_timeout, sizeof(struct st_h2o_redis_resumption_accept_data_t));
91
0
    data->get_command = NULL;
92
0
    return &data->super;
93
0
}
94
95
static struct st_h2o_accept_data_t *create_memcached_accept_data(h2o_accept_ctx_t *ctx, h2o_socket_t *sock,
96
                                                                 struct timeval connected_at)
97
0
{
98
0
    struct st_h2o_memcached_resumption_accept_data_t *data = (struct st_h2o_memcached_resumption_accept_data_t *)create_accept_data(
99
0
        ctx, sock, connected_at, on_memcached_accept_timeout, sizeof(struct st_h2o_memcached_resumption_accept_data_t));
100
0
    data->get_req = NULL;
101
0
    return &data->super;
102
0
}
103
104
static void destroy_accept_data(struct st_h2o_accept_data_t *data)
105
0
{
106
0
    h2o_timer_unlink(&data->timeout);
107
0
    free(data);
108
0
}
109
110
static void destroy_default_accept_data(struct st_h2o_accept_data_t *_accept_data)
111
0
{
112
0
    destroy_accept_data(_accept_data);
113
0
}
114
115
static void destroy_redis_accept_data(struct st_h2o_accept_data_t *_accept_data)
116
0
{
117
0
    struct st_h2o_redis_resumption_accept_data_t *accept_data = (struct st_h2o_redis_resumption_accept_data_t *)_accept_data;
118
0
    assert(accept_data->get_command == NULL);
119
0
    destroy_accept_data(&accept_data->super);
120
0
}
121
122
static void destroy_memcached_accept_data(struct st_h2o_accept_data_t *_accept_data)
123
0
{
124
0
    struct st_h2o_memcached_resumption_accept_data_t *accept_data =
125
0
        (struct st_h2o_memcached_resumption_accept_data_t *)_accept_data;
126
0
    assert(accept_data->get_req == NULL);
127
0
    destroy_accept_data(&accept_data->super);
128
0
}
129
130
static struct {
131
    struct st_h2o_accept_data_t *(*create)(h2o_accept_ctx_t *ctx, h2o_socket_t *sock, struct timeval connected_at);
132
    void (*destroy)(struct st_h2o_accept_data_t *accept_data);
133
} accept_data_callbacks = {
134
    create_default_accept_data,
135
    destroy_default_accept_data,
136
};
137
138
static void memcached_resumption_on_get(h2o_iovec_t session_data, void *_accept_data)
139
0
{
140
0
    struct st_h2o_memcached_resumption_accept_data_t *accept_data = _accept_data;
141
0
    accept_data->get_req = NULL;
142
0
    h2o_socket_ssl_resume_server_handshake(accept_data->super.sock, session_data);
143
0
}
144
145
static void memcached_resumption_get(h2o_socket_t *sock, h2o_iovec_t session_id)
146
0
{
147
0
    struct st_h2o_memcached_resumption_accept_data_t *data = sock->data;
148
149
0
    data->get_req = h2o_memcached_get(async_resumption_context.memcached.ctx, data->super.ctx->libmemcached_receiver, session_id,
150
0
                                      memcached_resumption_on_get, data, H2O_MEMCACHED_ENCODE_KEY | H2O_MEMCACHED_ENCODE_VALUE);
151
0
}
152
153
static void memcached_resumption_new(h2o_socket_t *sock, h2o_iovec_t session_id, h2o_iovec_t session_data)
154
0
{
155
0
    h2o_memcached_set(async_resumption_context.memcached.ctx, session_id, session_data,
156
0
                      (uint32_t)time(NULL) + async_resumption_context.expiration,
157
0
                      H2O_MEMCACHED_ENCODE_KEY | H2O_MEMCACHED_ENCODE_VALUE);
158
0
}
159
160
void h2o_accept_setup_memcached_ssl_resumption(h2o_memcached_context_t *memc, unsigned expiration)
161
0
{
162
0
    async_resumption_context.memcached.ctx = memc;
163
0
    async_resumption_context.expiration = expiration;
164
0
    h2o_socket_ssl_async_resumption_init(memcached_resumption_get, memcached_resumption_new);
165
0
    accept_data_callbacks.create = create_memcached_accept_data;
166
0
    accept_data_callbacks.destroy = destroy_memcached_accept_data;
167
0
}
168
169
static void on_redis_connect(void)
170
0
{
171
0
    h2o_error_printf("connected to redis at %s:%" PRIu16 "\n", async_resumption_context.redis.host.base,
172
0
                     async_resumption_context.redis.port);
173
0
}
174
175
static void on_redis_close(const char *errstr)
176
0
{
177
0
    if (errstr == NULL) {
178
0
        h2o_error_printf("disconnected from redis at %s:%" PRIu16 "\n", async_resumption_context.redis.host.base,
179
0
                         async_resumption_context.redis.port);
180
0
    } else {
181
0
        h2o_error_printf("redis connection failure: %s\n", errstr);
182
0
    }
183
0
}
184
185
static void dispose_redis_connection(void *client)
186
0
{
187
0
    h2o_redis_free((h2o_redis_client_t *)client);
188
0
}
189
190
static h2o_redis_client_t *get_redis_client(h2o_context_t *ctx)
191
0
{
192
0
    static size_t key = SIZE_MAX;
193
0
    h2o_redis_client_t **client = (h2o_redis_client_t **)h2o_context_get_storage(ctx, &key, dispose_redis_connection);
194
0
    if (*client == NULL) {
195
0
        *client = h2o_redis_create_client(ctx->loop, sizeof(h2o_redis_client_t));
196
0
        (*client)->on_connect = on_redis_connect;
197
0
        (*client)->on_close = on_redis_close;
198
0
    }
199
0
    return *client;
200
0
}
201
202
0
#define BASE64_LENGTH(len) (((len) + 2) / 3 * 4 + 1)
203
204
static h2o_iovec_t build_redis_key(h2o_iovec_t session_id, h2o_iovec_t prefix)
205
0
{
206
0
    h2o_iovec_t key;
207
0
    key.base = h2o_mem_alloc(prefix.len + BASE64_LENGTH(session_id.len));
208
0
    if (prefix.len != 0) {
209
0
        memcpy(key.base, prefix.base, prefix.len);
210
0
    }
211
0
    key.len = prefix.len;
212
0
    key.len += h2o_base64_encode(key.base + key.len, session_id.base, session_id.len, 1);
213
0
    return key;
214
0
}
215
216
static h2o_iovec_t build_redis_value(h2o_iovec_t session_data)
217
0
{
218
0
    h2o_iovec_t value;
219
0
    value.base = h2o_mem_alloc(BASE64_LENGTH(session_data.len));
220
0
    value.len = h2o_base64_encode(value.base, session_data.base, session_data.len, 1);
221
0
    return value;
222
0
}
223
224
#undef BASE64_LENGTH
225
226
static void redis_resumption_on_get(redisReply *reply, void *_accept_data, const char *errstr)
227
0
{
228
0
    struct st_h2o_redis_resumption_accept_data_t *accept_data = _accept_data;
229
0
    accept_data->get_command = NULL;
230
231
0
    h2o_iovec_t session_data;
232
0
    if (reply != NULL && reply->type == REDIS_REPLY_STRING) {
233
0
        session_data = h2o_decode_base64url(NULL, reply->str, reply->len);
234
0
    } else {
235
0
        session_data = h2o_iovec_init(NULL, 0);
236
0
    }
237
238
0
    h2o_socket_ssl_resume_server_handshake(accept_data->super.sock, session_data);
239
240
0
    if (session_data.base != NULL)
241
0
        free(session_data.base);
242
0
}
243
244
static void on_redis_resumption_get_failed(h2o_timer_t *timeout_entry)
245
0
{
246
0
    struct st_h2o_redis_resumption_accept_data_t *accept_data =
247
0
        H2O_STRUCT_FROM_MEMBER(struct st_h2o_redis_resumption_accept_data_t, super.timeout, timeout_entry);
248
0
    accept_data->get_command = NULL;
249
0
    h2o_socket_ssl_resume_server_handshake(accept_data->super.sock, h2o_iovec_init(NULL, 0));
250
0
    h2o_timer_unlink(timeout_entry);
251
0
}
252
253
static void redis_resumption_get(h2o_socket_t *sock, h2o_iovec_t session_id)
254
0
{
255
0
    struct st_h2o_redis_resumption_accept_data_t *accept_data = sock->data;
256
0
    h2o_redis_client_t *client = get_redis_client(accept_data->super.ctx->ctx);
257
258
0
    if (client->state == H2O_REDIS_CONNECTION_STATE_CONNECTED) {
259
0
        h2o_iovec_t key = build_redis_key(session_id, async_resumption_context.redis.prefix);
260
0
        accept_data->get_command = h2o_redis_command(client, redis_resumption_on_get, accept_data, "GET %s", key.base);
261
0
        free(key.base);
262
0
    } else {
263
0
        if (client->state == H2O_REDIS_CONNECTION_STATE_CLOSED) {
264
            // try to connect
265
0
            h2o_redis_connect(client, async_resumption_context.redis.host.base, async_resumption_context.redis.port);
266
0
        }
267
        // abort resumption
268
0
        h2o_timer_unlink(&accept_data->super.timeout);
269
0
        accept_data->super.timeout.cb = on_redis_resumption_get_failed;
270
0
        h2o_timer_link(accept_data->super.ctx->ctx->loop, 0, &accept_data->super.timeout);
271
0
    }
272
0
}
273
274
static void redis_resumption_new(h2o_socket_t *sock, h2o_iovec_t session_id, h2o_iovec_t session_data)
275
0
{
276
0
    struct st_h2o_redis_resumption_accept_data_t *accept_data = sock->data;
277
0
    h2o_redis_client_t *client = get_redis_client(accept_data->super.ctx->ctx);
278
279
0
    if (client->state == H2O_REDIS_CONNECTION_STATE_CLOSED) {
280
        // try to connect
281
0
        h2o_redis_connect(client, async_resumption_context.redis.host.base, async_resumption_context.redis.port);
282
0
    }
283
284
0
    h2o_iovec_t key = build_redis_key(session_id, async_resumption_context.redis.prefix);
285
0
    h2o_iovec_t value = build_redis_value(session_data);
286
0
    h2o_redis_command(client, NULL, NULL, "SETEX %s %d %s", key.base, async_resumption_context.expiration * 10, value.base);
287
0
    free(key.base);
288
0
    free(value.base);
289
0
}
290
291
void h2o_accept_setup_redis_ssl_resumption(const char *host, uint16_t port, unsigned expiration, const char *prefix)
292
0
{
293
0
    async_resumption_context.redis.host = h2o_strdup(NULL, host, SIZE_MAX);
294
0
    async_resumption_context.redis.port = port;
295
0
    async_resumption_context.redis.prefix = h2o_strdup(NULL, prefix, SIZE_MAX);
296
0
    async_resumption_context.expiration = expiration;
297
298
0
    h2o_socket_ssl_async_resumption_init(redis_resumption_get, redis_resumption_new);
299
300
0
    accept_data_callbacks.create = create_redis_accept_data;
301
0
    accept_data_callbacks.destroy = destroy_redis_accept_data;
302
0
}
303
304
static void accept_timeout(struct st_h2o_accept_data_t *data)
305
0
{
306
    /* TODO log */
307
0
    h2o_socket_t *sock = data->sock;
308
0
    accept_data_callbacks.destroy(data);
309
0
    h2o_socket_close(sock);
310
0
}
311
312
static void on_accept_timeout(h2o_timer_t *entry)
313
0
{
314
0
    struct st_h2o_accept_data_t *data = H2O_STRUCT_FROM_MEMBER(struct st_h2o_accept_data_t, timeout, entry);
315
0
    accept_timeout(data);
316
0
}
317
318
static void on_redis_accept_timeout(h2o_timer_t *entry)
319
0
{
320
0
    struct st_h2o_redis_resumption_accept_data_t *data =
321
0
        H2O_STRUCT_FROM_MEMBER(struct st_h2o_redis_resumption_accept_data_t, super.timeout, entry);
322
0
    if (data->get_command != NULL) {
323
0
        data->get_command->cb = NULL;
324
0
        data->get_command = NULL;
325
0
    }
326
0
    accept_timeout(&data->super);
327
0
}
328
329
static void on_memcached_accept_timeout(h2o_timer_t *entry)
330
0
{
331
0
    struct st_h2o_memcached_resumption_accept_data_t *data =
332
0
        H2O_STRUCT_FROM_MEMBER(struct st_h2o_memcached_resumption_accept_data_t, super.timeout, entry);
333
0
    if (data->get_req != NULL) {
334
0
        h2o_memcached_cancel_get(async_resumption_context.memcached.ctx, data->get_req);
335
0
        data->get_req = NULL;
336
0
    }
337
0
    accept_timeout(&data->super);
338
0
}
339
340
static void on_ssl_handshake_complete(h2o_socket_t *sock, const char *err)
341
0
{
342
0
    struct st_h2o_accept_data_t *data = sock->data;
343
0
    sock->data = NULL;
344
345
0
    if (err != NULL) {
346
0
        ++data->ctx->ctx->ssl.errors;
347
0
        h2o_socket_close(sock);
348
0
        goto Exit;
349
0
    }
350
351
    /* stats for handshake */
352
0
    struct timeval handshake_completed_at = h2o_gettimeofday(data->ctx->ctx->loop);
353
0
    int64_t handshake_time = h2o_timeval_subtract(&data->connected_at, &handshake_completed_at);
354
0
    if (h2o_socket_get_ssl_session_reused(sock)) {
355
0
        ++data->ctx->ctx->ssl.handshake_resume;
356
0
        data->ctx->ctx->ssl.handshake_accum_time_resume += handshake_time;
357
0
    } else {
358
0
        ++data->ctx->ctx->ssl.handshake_full;
359
0
        data->ctx->ctx->ssl.handshake_accum_time_full += handshake_time;
360
0
    }
361
362
0
    h2o_iovec_t proto = h2o_socket_ssl_get_selected_protocol(sock);
363
0
    const h2o_iovec_t *ident;
364
0
    for (ident = h2o_http2_alpn_protocols; ident->len != 0; ++ident) {
365
0
        if (proto.len == ident->len && memcmp(proto.base, ident->base, proto.len) == 0) {
366
            /* connect as http2 */
367
0
            ++data->ctx->ctx->ssl.alpn_h2;
368
0
            h2o_http2_accept(data->ctx, sock, data->connected_at);
369
0
            goto Exit;
370
0
        }
371
0
    }
372
    /* connect as http1 */
373
0
    if (proto.len != 0)
374
0
        ++data->ctx->ctx->ssl.alpn_h1;
375
0
    h2o_http1_accept(data->ctx, sock, data->connected_at);
376
377
0
Exit:
378
0
    accept_data_callbacks.destroy(data);
379
0
}
380
381
static ssize_t parse_proxy_line(char *src, size_t len, struct sockaddr *sa, socklen_t *salen)
382
0
{
383
0
#define CHECK_EOF()                                                                                                                \
384
0
    if (p == end)                                                                                                                  \
385
0
    return -2
386
0
#define EXPECT_CHAR(ch)                                                                                                            \
387
0
    do {                                                                                                                           \
388
0
        CHECK_EOF();                                                                                                               \
389
0
        if (*p++ != ch)                                                                                                            \
390
0
            return -1;                                                                                                             \
391
0
    } while (0)
392
0
#define SKIP_TO_WS()                                                                                                               \
393
0
    do {                                                                                                                           \
394
0
        do {                                                                                                                       \
395
0
            CHECK_EOF();                                                                                                           \
396
0
        } while (*p++ != ' ');                                                                                                     \
397
0
        --p;                                                                                                                       \
398
0
    } while (0)
399
400
0
    char *p = src, *end = p + len;
401
0
    void *addr;
402
0
    in_port_t *port;
403
404
    /* "PROXY "*/
405
0
    EXPECT_CHAR('P');
406
0
    EXPECT_CHAR('R');
407
0
    EXPECT_CHAR('O');
408
0
    EXPECT_CHAR('X');
409
0
    EXPECT_CHAR('Y');
410
0
    EXPECT_CHAR(' ');
411
412
    /* "TCP[46] " */
413
0
    CHECK_EOF();
414
0
    if (*p++ != 'T') {
415
0
        *salen = 0; /* indicate that no data has been obtained */
416
0
        goto SkipToEOL;
417
0
    }
418
0
    EXPECT_CHAR('C');
419
0
    EXPECT_CHAR('P');
420
0
    CHECK_EOF();
421
0
    switch (*p++) {
422
0
    case '4':
423
0
        *salen = sizeof(struct sockaddr_in);
424
0
        memset(sa, 0, sizeof(struct sockaddr_in));
425
0
        sa->sa_family = AF_INET;
426
0
        addr = &((struct sockaddr_in *)sa)->sin_addr;
427
0
        port = &((struct sockaddr_in *)sa)->sin_port;
428
0
        break;
429
0
    case '6':
430
0
        *salen = sizeof(struct sockaddr_in6);
431
0
        memset(sa, 0, sizeof(struct sockaddr_in6));
432
0
        sa->sa_family = AF_INET6;
433
0
        addr = &((struct sockaddr_in6 *)sa)->sin6_addr;
434
0
        port = &((struct sockaddr_in6 *)sa)->sin6_port;
435
0
        break;
436
0
    default:
437
0
        return -1;
438
0
    }
439
0
    EXPECT_CHAR(' ');
440
441
    /* parse peer address */
442
0
    char *addr_start = p;
443
0
    SKIP_TO_WS();
444
0
    *p = '\0';
445
0
    if (inet_pton(sa->sa_family, addr_start, addr) != 1)
446
0
        return -1;
447
0
    *p++ = ' ';
448
449
    /* skip local address */
450
0
    SKIP_TO_WS();
451
0
    ++p;
452
453
    /* parse peer port */
454
0
    char *port_start = p;
455
0
    SKIP_TO_WS();
456
0
    *p = '\0';
457
0
    unsigned short usval;
458
0
    if (sscanf(port_start, "%hu", &usval) != 1)
459
0
        return -1;
460
0
    *port = htons(usval);
461
0
    *p++ = ' ';
462
463
0
SkipToEOL:
464
0
    do {
465
0
        CHECK_EOF();
466
0
    } while (*p++ != '\r');
467
0
    CHECK_EOF();
468
0
    if (*p++ != '\n')
469
0
        return -2;
470
0
    return p - src;
471
472
0
#undef CHECK_EOF
473
0
#undef EXPECT_CHAR
474
0
#undef SKIP_TO_WS
475
0
}
476
477
static void on_read_proxy_line(h2o_socket_t *sock, const char *err)
478
0
{
479
0
    struct st_h2o_accept_data_t *data = sock->data;
480
481
0
    if (err != NULL) {
482
0
        accept_data_callbacks.destroy(data);
483
0
        h2o_socket_close(sock);
484
0
        return;
485
0
    }
486
487
0
    struct sockaddr_storage addr;
488
0
    socklen_t addrlen;
489
0
    ssize_t r = parse_proxy_line(sock->input->bytes, sock->input->size, (void *)&addr, &addrlen);
490
0
    switch (r) {
491
0
    case -1: /* error, just pass the input to the next handler */
492
0
        break;
493
0
    case -2: /* incomplete */
494
0
        return;
495
0
    default:
496
0
        h2o_buffer_consume(&sock->input, r);
497
0
        if (addrlen != 0)
498
0
            h2o_socket_setpeername(sock, (void *)&addr, addrlen);
499
0
        break;
500
0
    }
501
502
0
    if (data->ctx->ssl_ctx != NULL) {
503
0
        h2o_socket_ssl_handshake(sock, data->ctx->ssl_ctx, NULL, h2o_iovec_init(NULL, 0), on_ssl_handshake_complete);
504
0
    } else {
505
0
        struct st_h2o_accept_data_t *data = sock->data;
506
0
        sock->data = NULL;
507
0
        h2o_http1_accept(data->ctx, sock, data->connected_at);
508
0
        accept_data_callbacks.destroy(data);
509
0
    }
510
0
}
511
512
void h2o_accept(h2o_accept_ctx_t *ctx, h2o_socket_t *sock)
513
0
{
514
0
    struct timeval connected_at = h2o_gettimeofday(ctx->ctx->loop);
515
516
0
    if (ctx->expect_proxy_line || ctx->ssl_ctx != NULL) {
517
0
        sock->data = accept_data_callbacks.create(ctx, sock, connected_at);
518
0
        if (ctx->expect_proxy_line) {
519
0
            h2o_socket_read_start(sock, on_read_proxy_line);
520
0
        } else {
521
0
            h2o_socket_ssl_handshake(sock, ctx->ssl_ctx, NULL, h2o_iovec_init(NULL, 0), on_ssl_handshake_complete);
522
0
        }
523
0
    } else {
524
0
        h2o_http1_accept(ctx, sock, connected_at);
525
0
    }
526
0
}
527
528
size_t h2o_stringify_protocol_version(char *dst, int version)
529
0
{
530
0
    char *p = dst;
531
532
0
    if (version < 0x200) {
533
0
        assert(version <= 0x109);
534
0
#define PREFIX "HTTP/1."
535
0
        memcpy(p, PREFIX, sizeof(PREFIX) - 1);
536
0
        p += sizeof(PREFIX) - 1;
537
0
#undef PREFIX
538
0
        *p++ = '0' + (version & 0xff);
539
0
    } else {
540
0
#define PREFIX "HTTP/"
541
0
        memcpy(p, PREFIX, sizeof(PREFIX) - 1);
542
0
        p += sizeof(PREFIX) - 1;
543
0
#undef PREFIX
544
0
        *p++ = (version >> 8) + '0';
545
0
    }
546
547
0
    *p = '\0';
548
0
    return p - dst;
549
0
}
550
551
size_t h2o_stringify_proxy_header(h2o_conn_t *conn, char *buf)
552
0
{
553
0
    struct sockaddr_storage ss;
554
0
    socklen_t sslen;
555
0
    size_t strlen;
556
0
    uint16_t peerport;
557
0
    char *dst = buf;
558
559
0
    if ((sslen = conn->callbacks->get_peername(conn, (void *)&ss)) == 0)
560
0
        goto Unknown;
561
0
    switch (ss.ss_family) {
562
0
    case AF_INET:
563
0
        memcpy(dst, "PROXY TCP4 ", 11);
564
0
        dst += 11;
565
0
        break;
566
0
    case AF_INET6:
567
0
        memcpy(dst, "PROXY TCP6 ", 11);
568
0
        dst += 11;
569
0
        break;
570
0
    default:
571
0
        goto Unknown;
572
0
    }
573
0
    if ((strlen = h2o_socket_getnumerichost((void *)&ss, sslen, dst)) == SIZE_MAX)
574
0
        goto Unknown;
575
0
    dst += strlen;
576
0
    *dst++ = ' ';
577
578
0
    peerport = h2o_socket_getport((void *)&ss);
579
580
0
    if ((sslen = conn->callbacks->get_sockname(conn, (void *)&ss)) == 0)
581
0
        goto Unknown;
582
0
    if ((strlen = h2o_socket_getnumerichost((void *)&ss, sslen, dst)) == SIZE_MAX)
583
0
        goto Unknown;
584
0
    dst += strlen;
585
0
    *dst++ = ' ';
586
587
0
    dst += sprintf(dst, "%" PRIu16 " %" PRIu16 "\r\n", peerport, (uint16_t)h2o_socket_getport((void *)&ss));
588
589
0
    return dst - buf;
590
591
0
Unknown:
592
0
    memcpy(buf, "PROXY UNKNOWN\r\n", 15);
593
0
    return 15;
594
0
}
595
596
static h2o_iovec_t to_push_path(h2o_mem_pool_t *pool, h2o_iovec_t url, h2o_iovec_t base_path, const h2o_url_scheme_t *input_scheme,
597
                                h2o_iovec_t input_authority, const h2o_url_scheme_t *base_scheme, h2o_iovec_t *base_authority,
598
                                int allow_cross_origin_push)
599
0
{
600
0
    h2o_url_t parsed, resolved;
601
602
    /* check the authority, and extract absolute path */
603
0
    if (h2o_url_parse_relative(pool, url.base, url.len, &parsed) != 0)
604
0
        goto Invalid;
605
606
    /* fast-path for abspath form */
607
0
    if (base_scheme == NULL && parsed.scheme == NULL && parsed.authority.base == NULL && url.len != 0 && url.base[0] == '/') {
608
0
        return h2o_strdup(pool, url.base, url.len);
609
0
    }
610
611
    /* check scheme and authority if given URL contains either of the two, or if base is specified */
612
0
    h2o_url_t base = {input_scheme, input_authority, {NULL}, base_path, 65535};
613
0
    if (base_scheme != NULL) {
614
0
        base.scheme = base_scheme;
615
0
        base.authority = *base_authority;
616
0
    }
617
0
    h2o_url_resolve(pool, &base, &parsed, &resolved);
618
0
    if (input_scheme != resolved.scheme)
619
0
        goto Invalid;
620
0
    if (!allow_cross_origin_push &&
621
0
        !h2o_lcstris(input_authority.base, input_authority.len, resolved.authority.base, resolved.authority.len))
622
0
        goto Invalid;
623
624
0
    return resolved.path;
625
626
0
Invalid:
627
0
    return h2o_iovec_init(NULL, 0);
628
0
}
629
630
void h2o_extract_push_path_from_link_header(h2o_mem_pool_t *pool, const char *value, size_t value_len, h2o_iovec_t base_path,
631
                                            const h2o_url_scheme_t *input_scheme, h2o_iovec_t input_authority,
632
                                            const h2o_url_scheme_t *base_scheme, h2o_iovec_t *base_authority,
633
                                            void (*cb)(void *ctx, const char *path, size_t path_len, int is_critical), void *cb_ctx,
634
                                            h2o_iovec_t *filtered_value, int allow_cross_origin_push)
635
0
{
636
0
    h2o_iovec_t iter = h2o_iovec_init(value, value_len), token_value;
637
0
    const char *token;
638
0
    size_t token_len;
639
0
    *filtered_value = h2o_iovec_init(NULL, 0);
640
641
0
#define PUSH_FILTERED_VALUE(s, e)                                                                                                  \
642
0
    do {                                                                                                                           \
643
0
        if (filtered_value->len != 0) {                                                                                            \
644
0
            memcpy(filtered_value->base + filtered_value->len, ", ", 2);                                                           \
645
0
            filtered_value->len += 2;                                                                                              \
646
0
        }                                                                                                                          \
647
0
        memcpy(filtered_value->base + filtered_value->len, (s), (e) - (s));                                                        \
648
0
        filtered_value->len += (e) - (s);                                                                                          \
649
0
    } while (0)
650
651
    /* extract URL values from Link: </pushed.css>; rel=preload */
652
0
    do {
653
0
        if ((token = h2o_next_token(&iter, ';', ',', &token_len, NULL)) == NULL)
654
0
            break;
655
        /* first element should be <URL> */
656
0
        if (!(token_len >= 2 && token[0] == '<' && token[token_len - 1] == '>'))
657
0
            break;
658
0
        h2o_iovec_t url_with_brackets = h2o_iovec_init(token, token_len);
659
        /* find rel=preload */
660
0
        int preload = 0, nopush = 0, push_only = 0, critical = 0;
661
0
        while ((token = h2o_next_token(&iter, ';', ',', &token_len, &token_value)) != NULL &&
662
0
               !h2o_memis(token, token_len, H2O_STRLIT(","))) {
663
0
            if (h2o_lcstris(token, token_len, H2O_STRLIT("rel")) &&
664
0
                h2o_lcstris(token_value.base, token_value.len, H2O_STRLIT("preload"))) {
665
0
                preload = 1;
666
0
            } else if (h2o_lcstris(token, token_len, H2O_STRLIT("nopush"))) {
667
0
                nopush = 1;
668
0
            } else if (h2o_lcstris(token, token_len, H2O_STRLIT("x-http2-push-only"))) {
669
0
                push_only = 1;
670
0
            } else if (h2o_lcstris(token, token_len, H2O_STRLIT("critical"))) {
671
0
                critical = 1;
672
0
            }
673
0
        }
674
        /* push the path */
675
0
        if (!nopush && preload) {
676
0
            h2o_iovec_t path = to_push_path(pool, h2o_iovec_init(url_with_brackets.base + 1, url_with_brackets.len - 2), base_path,
677
0
                                            input_scheme, input_authority, base_scheme, base_authority, allow_cross_origin_push);
678
0
            if (path.len != 0)
679
0
                (*cb)(cb_ctx, path.base, path.len, critical);
680
0
        }
681
        /* store the elements that needs to be preserved to filtered_value */
682
0
        if (push_only) {
683
0
            if (filtered_value->base == NULL) {
684
                /* the max. size of filtered_value would be x2 in the worst case, when "," is converted to ", " */
685
0
                filtered_value->base = h2o_mem_alloc_pool(pool, char, value_len * 2);
686
0
                const char *prev_comma = h2o_memrchr(value, ',', url_with_brackets.base - value);
687
0
                if (prev_comma != NULL)
688
0
                    PUSH_FILTERED_VALUE(value, prev_comma);
689
0
            }
690
0
        } else if (filtered_value->base != NULL) {
691
0
            PUSH_FILTERED_VALUE(url_with_brackets.base, token != NULL ? token : value + value_len);
692
0
        }
693
0
    } while (token != NULL);
694
695
0
    if (filtered_value->base != NULL) {
696
0
        if (token != NULL)
697
0
            PUSH_FILTERED_VALUE(token, value + value_len);
698
0
    } else {
699
0
        *filtered_value = h2o_iovec_init(value, value_len);
700
0
    }
701
702
0
#undef PUSH_FILTERED_VALUE
703
0
}
704
705
int h2o_get_compressible_types(const h2o_headers_t *headers)
706
0
{
707
0
    size_t header_index;
708
0
    int compressible_types = 0;
709
710
0
    for (header_index = 0; header_index != headers->size; ++header_index) {
711
0
        const h2o_header_t *header = headers->entries + header_index;
712
0
        if (H2O_UNLIKELY(header->name == &H2O_TOKEN_ACCEPT_ENCODING->buf)) {
713
0
            h2o_iovec_t iter = h2o_iovec_init(header->value.base, header->value.len);
714
0
            const char *token = NULL;
715
0
            size_t token_len = 0;
716
0
            while ((token = h2o_next_token(&iter, ',', ',', &token_len, NULL)) != NULL) {
717
0
                if (h2o_lcstris(token, token_len, H2O_STRLIT("gzip")))
718
0
                    compressible_types |= H2O_COMPRESSIBLE_GZIP;
719
0
                else if (h2o_lcstris(token, token_len, H2O_STRLIT("br")))
720
0
                    compressible_types |= H2O_COMPRESSIBLE_BROTLI;
721
0
                else if (h2o_lcstris(token, token_len, H2O_STRLIT("zstd")))
722
0
                    compressible_types |= H2O_COMPRESSIBLE_ZSTD;
723
0
            }
724
0
        }
725
0
    }
726
727
0
    return compressible_types;
728
0
}
729
730
h2o_iovec_t h2o_build_destination(h2o_req_t *req, const char *prefix, size_t prefix_len, int use_path_normalized)
731
3.60k
{
732
3.60k
    h2o_iovec_t parts[4];
733
3.60k
    size_t num_parts = 0;
734
3.60k
    int conf_ends_with_slash = req->pathconf->path.base[req->pathconf->path.len - 1] == '/', prefix_ends_with_slash;
735
736
    /* destination starts with given prefix, if any */
737
3.60k
    if (prefix_len != 0) {
738
0
        parts[num_parts++] = h2o_iovec_init(prefix, prefix_len);
739
0
        prefix_ends_with_slash = prefix[prefix_len - 1] == '/';
740
3.60k
    } else {
741
3.60k
        prefix_ends_with_slash = 0;
742
3.60k
    }
743
744
    /* make adjustments depending on the trailing slashes */
745
3.60k
    if (conf_ends_with_slash != prefix_ends_with_slash) {
746
0
        if (conf_ends_with_slash) {
747
0
            parts[num_parts++] = h2o_iovec_init(H2O_STRLIT("/"));
748
0
        } else {
749
0
            if (req->path_normalized.len != req->pathconf->path.len)
750
0
                parts[num_parts - 1].len -= 1;
751
0
        }
752
0
    }
753
754
    /* append suffix path and query */
755
756
3.60k
    if (use_path_normalized) {
757
0
        parts[num_parts++] = h2o_uri_escape(&req->pool, req->path_normalized.base + req->pathconf->path.len,
758
0
                                            req->path_normalized.len - req->pathconf->path.len, "/@:");
759
0
        if (req->query_at != SIZE_MAX) {
760
0
            parts[num_parts++] = h2o_iovec_init(req->path.base + req->query_at, req->path.len - req->query_at);
761
0
        }
762
3.60k
    } else {
763
3.60k
        if (req->path.len > 1) {
764
            /*
765
             * When proxying, we want to modify the input URL as little
766
             * as possible. We use norm_indexes to find the start of
767
             * the path we want to forward.
768
             */
769
3.60k
            size_t next_unnormalized;
770
3.60k
            if (req->norm_indexes && req->pathconf->path.len > 1) {
771
2.73k
                next_unnormalized = req->norm_indexes[req->pathconf->path.len - 1];
772
2.73k
            } else {
773
872
                next_unnormalized = req->pathconf->path.len;
774
872
            }
775
776
            /*
777
             * Special case: the input path didn't have any '/' including the first,
778
             * so the first character is actually found at '0'
779
             */
780
3.60k
            if (req->path.base[0] != '/' && next_unnormalized == 1) {
781
0
                next_unnormalized = 0;
782
0
            }
783
3.60k
            parts[num_parts++] = (h2o_iovec_t){req->path.base + next_unnormalized, req->path.len - next_unnormalized};
784
3.60k
        }
785
3.60k
    }
786
787
3.60k
    return h2o_concat_list(&req->pool, parts, num_parts);
788
3.60k
}
789
790
0
#define SERVER_TIMING_DURATION_LONGEST_STR "dur=" H2O_INT32_LONGEST_STR ".000"
791
792
size_t stringify_duration(char *buf, int64_t usec)
793
0
{
794
0
    int32_t msec = (int32_t)(usec / 1000);
795
0
    usec -= ((int64_t)msec * 1000);
796
0
    char *pos = buf;
797
0
    pos += sprintf(pos, "dur=%" PRId32, msec);
798
0
    if (usec != 0) {
799
0
        *pos++ = '.';
800
0
        int denom;
801
0
        for (denom = 100; denom != 0; denom /= 10) {
802
0
            int d = (int)usec / denom;
803
0
            *pos++ = '0' + d;
804
0
            usec -= d * denom;
805
0
            if (usec == 0)
806
0
                break;
807
0
        }
808
0
    }
809
0
    return pos - buf;
810
0
}
811
812
#define DELIMITER ", "
813
0
#define ELEMENT_LONGEST_STR(name) name "; " SERVER_TIMING_DURATION_LONGEST_STR
814
815
static void emit_server_timing_element(h2o_req_t *req, h2o_iovec_t *dst, const char *name,
816
                                       int (*compute_func)(h2o_req_t *, int64_t *), size_t max_len)
817
0
{
818
0
    int64_t usec;
819
0
    if (compute_func(req, &usec) == 0)
820
0
        return;
821
0
    if (dst->len == 0) {
822
0
        if (max_len != SIZE_MAX)
823
0
            dst->base = h2o_mem_alloc_pool(&req->pool, *dst->base, max_len);
824
0
    } else {
825
0
        dst->base[dst->len++] = ',';
826
0
        dst->base[dst->len++] = ' ';
827
0
    }
828
0
    size_t name_len = strlen(name);
829
0
    memcpy(dst->base + dst->len, name, name_len);
830
0
    dst->len += name_len;
831
0
    dst->base[dst->len++] = ';';
832
0
    dst->base[dst->len++] = ' ';
833
0
    dst->len += stringify_duration(dst->base + dst->len, usec);
834
0
}
835
836
void h2o_add_server_timing_header(h2o_req_t *req, int uses_trailer)
837
0
{
838
    /* caller needs to make sure that trailers can be used */
839
0
    if (0x101 <= req->version && req->version < 0x200)
840
0
        assert(req->content_length == SIZE_MAX);
841
842
    /* emit timings */
843
0
    h2o_iovec_t dst = {NULL};
844
845
0
#define LONGEST_STR                                                                                                                \
846
0
    ELEMENT_LONGEST_STR("connect")                                                                                                 \
847
0
    DELIMITER ELEMENT_LONGEST_STR("request-header") DELIMITER ELEMENT_LONGEST_STR("request-body")                                  \
848
0
        DELIMITER ELEMENT_LONGEST_STR("request-total") DELIMITER ELEMENT_LONGEST_STR("process")                                    \
849
0
            DELIMITER ELEMENT_LONGEST_STR("proxy.idle") DELIMITER ELEMENT_LONGEST_STR("proxy.connect")                             \
850
0
                DELIMITER ELEMENT_LONGEST_STR("proxy.request") DELIMITER ELEMENT_LONGEST_STR("proxy.process")
851
0
    size_t max_len = sizeof(LONGEST_STR) - 1;
852
853
0
    if ((req->send_server_timing & H2O_SEND_SERVER_TIMING_BASIC) != 0) {
854
0
        emit_server_timing_element(req, &dst, "connect", h2o_time_compute_connect_time, max_len);
855
0
        emit_server_timing_element(req, &dst, "request-header", h2o_time_compute_header_time, max_len);
856
0
        emit_server_timing_element(req, &dst, "request-body", h2o_time_compute_body_time, max_len);
857
0
        emit_server_timing_element(req, &dst, "request-total", h2o_time_compute_request_total_time, max_len);
858
0
        emit_server_timing_element(req, &dst, "process", h2o_time_compute_process_time, max_len);
859
0
    }
860
0
    if ((req->send_server_timing & H2O_SEND_SERVER_TIMING_PROXY) != 0) {
861
0
        emit_server_timing_element(req, &dst, "proxy.idle", h2o_time_compute_proxy_idle_time, max_len);
862
0
        emit_server_timing_element(req, &dst, "proxy.connect", h2o_time_compute_proxy_connect_time, max_len);
863
0
        emit_server_timing_element(req, &dst, "proxy.request", h2o_time_compute_proxy_request_time, max_len);
864
0
        emit_server_timing_element(req, &dst, "proxy.process", h2o_time_compute_proxy_process_time, max_len);
865
0
    }
866
867
0
#undef LONGEST_STR
868
869
0
    if (uses_trailer)
870
0
        h2o_add_header_by_str(&req->pool, &req->res.headers, H2O_STRLIT("trailer"), 0, NULL, H2O_STRLIT("server-timing"));
871
0
    if (dst.len != 0)
872
0
        h2o_add_header_by_str(&req->pool, &req->res.headers, H2O_STRLIT("server-timing"), 0, NULL, dst.base, dst.len);
873
0
}
874
875
h2o_iovec_t h2o_build_server_timing_trailer(h2o_req_t *req, const char *prefix, size_t prefix_len, const char *suffix,
876
                                            size_t suffix_len)
877
0
{
878
0
    h2o_iovec_t value;
879
880
0
#define LONGEST_STR                                                                                                                \
881
0
    ELEMENT_LONGEST_STR("response")                                                                                                \
882
0
    DELIMITER ELEMENT_LONGEST_STR("total") DELIMITER ELEMENT_LONGEST_STR("proxy.response")                                         \
883
0
        DELIMITER ELEMENT_LONGEST_STR("proxy.total")
884
885
0
    value.base = h2o_mem_alloc_pool(&req->pool, *value.base, prefix_len + suffix_len + sizeof(LONGEST_STR) - 1);
886
0
    value.len = 0;
887
888
0
    if (prefix_len != 0) {
889
0
        memcpy(value.base + value.len, prefix, prefix_len);
890
0
        value.len += prefix_len;
891
0
    }
892
893
0
    h2o_iovec_t dst = h2o_iovec_init(value.base + value.len, 0);
894
895
0
    if ((req->send_server_timing & H2O_SEND_SERVER_TIMING_BASIC) != 0) {
896
0
        emit_server_timing_element(req, &dst, "response", h2o_time_compute_response_time, SIZE_MAX);
897
0
        emit_server_timing_element(req, &dst, "total", h2o_time_compute_total_time, SIZE_MAX);
898
0
    }
899
0
    if ((req->send_server_timing & H2O_SEND_SERVER_TIMING_PROXY) != 0) {
900
0
        emit_server_timing_element(req, &dst, "proxy.response", h2o_time_compute_proxy_response_time, SIZE_MAX);
901
0
        emit_server_timing_element(req, &dst, "proxy.total", h2o_time_compute_proxy_total_time, SIZE_MAX);
902
0
    }
903
904
0
    if (dst.len == 0)
905
0
        return h2o_iovec_init(NULL, 0);
906
0
    value.len += dst.len;
907
908
0
    if (suffix_len != 0) {
909
0
        memcpy(value.base + value.len, suffix, suffix_len);
910
0
        value.len += suffix_len;
911
0
    }
912
913
0
    return value;
914
915
0
#undef LONGEST_STR
916
0
}
917
918
#undef ELEMENT_LONGEST_STR
919
#undef DELIMITER
920
921
/* h2-14 and h2-16 are kept for backwards compatibility, as they are often used */
922
#define ALPN_ENTRY(s) {H2O_STRLIT(s)}
923
#define ALPN_PROTOCOLS_CORE ALPN_ENTRY("h2"), ALPN_ENTRY("h2-16"), ALPN_ENTRY("h2-14")
924
#define NPN_PROTOCOLS_CORE                                                                                                         \
925
    "\x02"                                                                                                                         \
926
    "h2"                                                                                                                           \
927
    "\x05"                                                                                                                         \
928
    "h2-16"                                                                                                                        \
929
    "\x05"                                                                                                                         \
930
    "h2-14"
931
932
const h2o_iovec_t h2o_http2_alpn_protocols[] = {ALPN_PROTOCOLS_CORE, {NULL}};
933
const h2o_iovec_t h2o_alpn_protocols[] = {ALPN_PROTOCOLS_CORE, ALPN_ENTRY("http/1.1"), {NULL}};
934
935
const char h2o_http2_npn_protocols[] = NPN_PROTOCOLS_CORE;
936
const char h2o_npn_protocols[] = NPN_PROTOCOLS_CORE "\x08"
937
                                                    "http/1.1";
938
939
uint64_t h2o_connection_id = 0;
940
941
uint32_t h2o_cleanup_thread(uint64_t now, h2o_context_t *ctx_optional)
942
0
{
943
    /* File descriptor cache is cleared fully per event loop and it is sufficient to do so, because:
944
     * * if the file handler opens one file only once per event loop, then calling open (2) is relatively lightweight compared to
945
     *   other stuff such as connection establishment, and
946
     * * if a file is large enough that it is not served in one event loop, the file descriptor remains open within the cache. */
947
0
    if (ctx_optional != NULL)
948
0
        h2o_filecache_clear(ctx_optional->filecache);
949
950
    /* recycle either fully, or partially if at least 1 second has elasped since previous gc */
951
0
    static __thread uint64_t next_gc_at;
952
0
    if (now >= next_gc_at) {
953
0
        int full = now == 0;
954
0
        h2o_buffer_clear_recycle(full);
955
0
        h2o_socket_clear_recycle(full);
956
0
        h2o_mem_clear_recycle(&h2o_mem_pool_allocator, full);
957
0
        next_gc_at = now + 1000;
958
0
    }
959
960
    /* if all the recyclers are empty, we can sleep forever; otherwise request to be invoked again within no more than one second */
961
0
    if (h2o_buffer_recycle_is_empty() && h2o_socket_recycle_is_empty() && h2o_mem_recycle_is_empty(&h2o_mem_pool_allocator)) {
962
0
        return INT32_MAX;
963
0
    } else {
964
0
        return 1000;
965
0
    }
966
0
}