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 | } |