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