Coverage Report

Created: 2025-08-29 06:21

/src/h2o/lib/handler/status/requests.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2016 DeNA Co., Ltd., 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
23
#include "h2o.h"
24
25
struct st_requests_status_ctx_t {
26
    h2o_logconf_t *logconf;
27
    h2o_iovec_t req_data;
28
    pthread_mutex_t mutex;
29
};
30
31
struct st_collect_req_status_cbdata_t {
32
    h2o_logconf_t *logconf;
33
    h2o_buffer_t *buffer;
34
};
35
36
static int collect_req_status(h2o_req_t *req, void *_cbdata)
37
0
{
38
0
    struct st_collect_req_status_cbdata_t *cbdata = _cbdata;
39
40
    /* collect log */
41
0
    char buf[4096];
42
0
    size_t len = sizeof(buf);
43
0
    char *logline = h2o_log_request(cbdata->logconf, req, &len, buf);
44
0
    assert(len != 0);
45
0
    --len; /* omit trailing LF */
46
47
    /* append to buffer */
48
0
    if ((h2o_buffer_try_reserve(&cbdata->buffer, len + 3)).base == NULL) {
49
0
        if (logline != buf)
50
0
            free(logline);
51
0
        return -1;
52
0
    }
53
0
    memcpy(cbdata->buffer->bytes + cbdata->buffer->size, logline, len);
54
0
    cbdata->buffer->size += len;
55
56
0
    if (logline != buf)
57
0
        free(logline);
58
59
0
    return 0;
60
0
}
61
62
static void requests_status_per_thread(void *priv, h2o_context_t *ctx)
63
0
{
64
0
    struct st_requests_status_ctx_t *rsc = priv;
65
0
    struct st_collect_req_status_cbdata_t cbdata = {rsc->logconf};
66
67
    /* we encountered an error at init() time, return early */
68
0
    if (rsc->logconf == NULL)
69
0
        return;
70
71
0
    h2o_buffer_init(&cbdata.buffer, &h2o_socket_buffer_prototype);
72
73
0
    H2O_CONN_LIST_FOREACH(h2o_conn_t * conn, ({&ctx->_conns.active, &ctx->_conns.idle, &ctx->_conns.shutdown}), {
74
0
        if (conn->callbacks->foreach_request(conn, collect_req_status, &cbdata) != 0) {
75
0
            h2o_buffer_dispose(&cbdata.buffer);
76
0
            return;
77
0
        }
78
0
    });
79
80
    /* concat JSON elements */
81
0
    if (cbdata.buffer->size != 0) {
82
0
        pthread_mutex_lock(&rsc->mutex);
83
0
        if (rsc->req_data.len == 0)
84
0
            h2o_buffer_consume(&cbdata.buffer, 1); /* skip preceeding comma */
85
0
        rsc->req_data.base = h2o_mem_realloc(rsc->req_data.base, rsc->req_data.len + cbdata.buffer->size);
86
0
        memcpy(rsc->req_data.base + rsc->req_data.len, cbdata.buffer->bytes, cbdata.buffer->size);
87
0
        rsc->req_data.len += cbdata.buffer->size;
88
0
        pthread_mutex_unlock(&rsc->mutex);
89
0
    }
90
91
0
    h2o_buffer_dispose(&cbdata.buffer);
92
0
}
93
94
static void *requests_status_init(void)
95
0
{
96
0
    struct st_requests_status_ctx_t *rsc = h2o_mem_alloc(sizeof(*rsc));
97
0
    char errbuf[256];
98
99
0
#define ELEMENT(key, expr) "\"" key "\": \"" expr "\""
100
0
#define X_ELEMENT(id) ELEMENT(id, "%{" id "}x")
101
0
#define SEPARATOR ", "
102
0
    const char *fmt = ",\n  {"
103
        /* combined_log */
104
0
        ELEMENT("host", "%h") SEPARATOR ELEMENT("user", "%u") SEPARATOR ELEMENT("at", "%{%Y%m%dT%H%M%S}t.%{usec_frac}t%{%z}t")
105
0
            SEPARATOR ELEMENT("method", "%m") SEPARATOR ELEMENT("path", "%U") SEPARATOR ELEMENT("query", "%q")
106
0
                SEPARATOR ELEMENT("protocol", "%H") SEPARATOR ELEMENT("referer", "%{Referer}i")
107
0
                    SEPARATOR ELEMENT("user-agent", "%{User-agent}i") SEPARATOR
108
                        /* time */
109
0
                        X_ELEMENT("connect-time") SEPARATOR X_ELEMENT("request-header-time")
110
0
                            SEPARATOR X_ELEMENT("request-body-time") SEPARATOR X_ELEMENT("request-total-time")
111
0
                                SEPARATOR X_ELEMENT("process-time") SEPARATOR X_ELEMENT("response-time") SEPARATOR
112
                                    /* connection */
113
0
                                    X_ELEMENT("connection-id") SEPARATOR X_ELEMENT("ssl.protocol-version")
114
0
                                        SEPARATOR X_ELEMENT("ssl.session-reused") SEPARATOR X_ELEMENT("ssl.cipher")
115
0
                                            SEPARATOR X_ELEMENT("ssl.cipher-bits") SEPARATOR X_ELEMENT("ssl.session-ticket")
116
0
                                                SEPARATOR X_ELEMENT("ssl.server-name") SEPARATOR
117
                                                    /* http1 */
118
0
                                                    X_ELEMENT("http1.request-index") SEPARATOR
119
                                                        /* http2 */
120
0
                                                        X_ELEMENT("http2.stream-id")
121
0
                                                            SEPARATOR X_ELEMENT("http2.priority.received.exclusive")
122
0
                                                                SEPARATOR X_ELEMENT("http2.priority.received.parent")
123
0
                                                                    SEPARATOR X_ELEMENT("http2.priority.received.weight")
124
0
                                                                        SEPARATOR X_ELEMENT("http2.priority.actual.parent")
125
0
                                                                            SEPARATOR X_ELEMENT("http2.priority.actual.weight")
126
0
                                                                                SEPARATOR
127
                                                                                    /* misc */
128
0
                                                                                    ELEMENT("authority", "%V")
129
        /* end */
130
0
        "}";
131
0
#undef ELEMENT
132
0
#undef X_ELEMENT
133
0
#undef SEPARATOR
134
135
    /* compile logconf */
136
0
    if ((rsc->logconf = h2o_logconf_compile(fmt, H2O_LOGCONF_ESCAPE_JSON, errbuf)) == NULL)
137
        /* log format compilation error is an internal logic flaw, therefore we need not send the details to the client */
138
0
        h2o_error_printf("[lib/handler/status/requests.c] failed to compile log format: %s", errbuf);
139
140
0
    rsc->req_data = (h2o_iovec_t){NULL};
141
0
    pthread_mutex_init(&rsc->mutex, NULL);
142
143
0
    return rsc;
144
0
}
145
146
static h2o_iovec_t requests_status_final(void *priv, h2o_globalconf_t *gconf, h2o_req_t *req)
147
0
{
148
0
    h2o_iovec_t ret = {NULL};
149
0
    struct st_requests_status_ctx_t *rsc = priv;
150
151
0
    if (rsc->logconf != NULL) {
152
0
        ret = h2o_concat(&req->pool, h2o_iovec_init(H2O_STRLIT(",\n \"requests\": [")), rsc->req_data,
153
0
                         h2o_iovec_init(H2O_STRLIT("\n ]")));
154
0
        h2o_logconf_dispose(rsc->logconf);
155
0
    }
156
0
    free(rsc->req_data.base);
157
0
    pthread_mutex_destroy(&rsc->mutex);
158
159
0
    free(rsc);
160
0
    return ret;
161
0
}
162
163
h2o_status_handler_t h2o_requests_status_handler = {
164
    {H2O_STRLIT("requests")}, requests_status_final, requests_status_init, requests_status_per_thread};