/src/h2o/lib/handler/h2olog.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2022 Fastly, Inc, Goro Fuji, Kazuho Oku |
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 "h2o.h" |
23 | | |
24 | | static int on_req(h2o_handler_t *_self, h2o_req_t *req) |
25 | 0 | { |
26 | 0 | struct sockaddr_storage local; |
27 | 0 | float sample_ratio = 1; |
28 | 0 | char *trace = h2o_mem_alloc(req->path.len + 2 /* should be enough */), *trace_tail = trace; |
29 | 0 | char *sni = h2o_mem_alloc(req->path.len + 2 /* should be enough */), *sni_tail = sni; |
30 | 0 | char *address = h2o_mem_alloc(req->path.len + 2 /* should be enough */), *address_tail = address; |
31 | 0 | int appdata = 0, ret; |
32 | 0 | h2o_socket_t *sock; |
33 | 0 | h2o_socket_export_t export_info; |
34 | | |
35 | | /* delegate the request to the next handler unless the request is accepted on a UNIX socket */ |
36 | 0 | if (!(req->conn->callbacks->get_sockname(req->conn, (struct sockaddr *)&local) > 0 && local.ss_family == AF_UNIX)) { |
37 | 0 | ret = -1; |
38 | 0 | goto Exit; |
39 | 0 | } |
40 | | |
41 | | /* parse params */ |
42 | 0 | if (req->query_at != SIZE_MAX) { |
43 | 0 | h2o_iovec_t iter = h2o_iovec_init(req->path.base + req->query_at + 1, req->path.len - (req->query_at + 1)), value; |
44 | 0 | const char *name; |
45 | 0 | size_t name_len; |
46 | 0 | while ((name = h2o_next_token(&iter, '&', '&', &name_len, &value)) != NULL) { |
47 | 0 | if (h2o_memis(name, name_len, H2O_STRLIT("sample-ratio"))) { |
48 | 0 | if (sscanf(h2o_uri_unescape(&req->pool, value.base, value.len).base, "%f", &sample_ratio) != 1 || |
49 | 0 | !(0 <= sample_ratio && sample_ratio <= 1)) { |
50 | 0 | h2o_send_error_400(req, "Bad Request", "sample-rate must be a number between 0 and 1", 0); |
51 | 0 | ret = 0; |
52 | 0 | goto Exit; |
53 | 0 | } |
54 | 0 | } else if (h2o_memis(name, name_len, H2O_STRLIT("trace"))) { |
55 | 0 | h2o_iovec_t unescaped = h2o_uri_unescape(&req->pool, value.base, value.len); |
56 | 0 | h2o_memcpy(trace_tail, unescaped.base, unescaped.len); |
57 | 0 | trace_tail += unescaped.len; |
58 | 0 | *trace_tail++ = '\0'; |
59 | 0 | } else if (h2o_memis(name, name_len, H2O_STRLIT("sni"))) { |
60 | 0 | h2o_iovec_t unescaped = h2o_uri_unescape(&req->pool, value.base, value.len); |
61 | 0 | h2o_memcpy(sni_tail, unescaped.base, unescaped.len); |
62 | 0 | sni_tail += unescaped.len; |
63 | 0 | *sni_tail++ = '\0'; |
64 | 0 | } else if (h2o_memis(name, name_len, H2O_STRLIT("address"))) { |
65 | 0 | h2o_iovec_t unescaped = h2o_uri_unescape(&req->pool, value.base, value.len); |
66 | 0 | h2o_memcpy(address_tail, unescaped.base, unescaped.len); |
67 | 0 | address_tail += unescaped.len; |
68 | 0 | *address_tail++ = '\0'; |
69 | 0 | } else if (h2o_memis(name, name_len, H2O_STRLIT("application-data"))) { |
70 | 0 | appdata = 1; |
71 | 0 | } |
72 | 0 | } |
73 | 0 | } |
74 | | |
75 | | /* termninate lists */ |
76 | 0 | *trace_tail = '\0'; |
77 | 0 | *sni_tail = '\0'; |
78 | 0 | *address_tail = '\0'; |
79 | | |
80 | | /* steal the socket (NOTE: request, including `req->pool` is dipsosed of here, do we want to delay?) */ |
81 | 0 | if (req->conn->callbacks->steal_socket == NULL || (sock = req->conn->callbacks->steal_socket(req->conn)) == NULL) { |
82 | 0 | h2o_send_error_400(req, "Bad Request", "h2olog is available only for cleartext HTTP/1", 0); |
83 | 0 | ret = 0; |
84 | 0 | goto Exit; |
85 | 0 | } |
86 | | |
87 | 0 | if (h2o_socket_export(sock, &export_info) != 0) |
88 | 0 | h2o_fatal("h2o_socket_export failed"); |
89 | | |
90 | 0 | (void)write(export_info.fd, H2O_STRLIT("HTTP/1.1 200 OK\r\n\r\n")); |
91 | | |
92 | | /* register log fd after writing HTTP response, as log is written by multiple threads */ |
93 | 0 | if (ptls_log_add_fd(export_info.fd, sample_ratio, trace, sni, address, appdata) != 0) |
94 | 0 | h2o_fatal("failed to add fd to h2olog"); |
95 | | |
96 | 0 | ret = 0; |
97 | |
|
98 | 0 | Exit: |
99 | 0 | free(trace); |
100 | 0 | free(sni); |
101 | 0 | free(address); |
102 | 0 | return ret; |
103 | 0 | } |
104 | | |
105 | | void h2o_log_register(h2o_hostconf_t *hostconf) |
106 | 0 | { |
107 | 0 | h2o_pathconf_t *pathconf = h2o_config_register_path(hostconf, H2O_LOG_URI_PATH, 0); |
108 | 0 | h2o_handler_t *self = h2o_create_handler(pathconf, sizeof(*self)); |
109 | 0 | self->on_req = on_req; |
110 | 0 | } |