Coverage Report

Created: 2025-07-18 06:42

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