Coverage Report

Created: 2023-06-07 06:21

/src/h2o/lib/core/logconf.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2014-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
#include <inttypes.h>
23
#include <stdio.h>
24
#include <stdlib.h>
25
#include "h2o.h"
26
27
enum {
28
    ELEMENT_TYPE_EMPTY,                         /* empty element (with suffix only) */
29
    ELEMENT_TYPE_LOCAL_ADDR,                    /* %A */
30
    ELEMENT_TYPE_BYTES_SENT,                    /* %b */
31
    ELEMENT_TYPE_PROTOCOL,                      /* %H */
32
    ELEMENT_TYPE_REMOTE_ADDR,                   /* %h */
33
    ELEMENT_TYPE_LOGNAME,                       /* %l */
34
    ELEMENT_TYPE_METHOD,                        /* %m */
35
    ELEMENT_TYPE_LOCAL_PORT,                    /* %p, %{local}p */
36
    ELEMENT_TYPE_REMOTE_PORT,                   /* %{remote}p */
37
    ELEMENT_TYPE_ENV_VAR,                       /* %{..}e */
38
    ELEMENT_TYPE_QUERY,                         /* %q */
39
    ELEMENT_TYPE_REQUEST_LINE,                  /* %r */
40
    ELEMENT_TYPE_STATUS,                        /* %s */
41
    ELEMENT_TYPE_TIMESTAMP,                     /* %t */
42
    ELEMENT_TYPE_TIMESTAMP_STRFTIME,            /* %{...}t */
43
    ELEMENT_TYPE_TIMESTAMP_SEC_SINCE_EPOCH,     /* %{sec}t */
44
    ELEMENT_TYPE_TIMESTAMP_MSEC_SINCE_EPOCH,    /* %{msec}t */
45
    ELEMENT_TYPE_TIMESTAMP_USEC_SINCE_EPOCH,    /* %{usec}t */
46
    ELEMENT_TYPE_TIMESTAMP_MSEC_FRAC,           /* %{msec_frac}t */
47
    ELEMENT_TYPE_TIMESTAMP_USEC_FRAC,           /* %{usec_frac}t */
48
    ELEMENT_TYPE_URL_PATH,                      /* %U */
49
    ELEMENT_TYPE_REMOTE_USER,                   /* %u */
50
    ELEMENT_TYPE_AUTHORITY,                     /* %V */
51
    ELEMENT_TYPE_HOSTCONF,                      /* %v */
52
    ELEMENT_TYPE_IN_HEADER_TOKEN,               /* %{data.header_token}i */
53
    ELEMENT_TYPE_IN_HEADER_STRING,              /* %{data.name}i */
54
    ELEMENT_TYPE_OUT_HEADER_TOKEN,              /* %{data.header_token}o */
55
    ELEMENT_TYPE_OUT_HEADER_STRING,             /* %{data.name}o */
56
    ELEMENT_TYPE_OUT_HEADER_TOKEN_CONCATENATED, /* %{data.header_token}o */
57
    ELEMENT_TYPE_EXTENDED_VAR,                  /* %{data.name}x */
58
    ELEMENT_TYPE_CONNECTION_ID,                 /* %{connection-id}x */
59
    ELEMENT_TYPE_REQUEST_ID,                    /* %{request-id}x */
60
    ELEMENT_TYPE_CONNECT_TIME,                  /* %{connect-time}x */
61
    ELEMENT_TYPE_REQUEST_HEADER_TIME,           /* %{request-header-time}x */
62
    ELEMENT_TYPE_REQUEST_BODY_TIME,             /* %{request-body-time}x */
63
    ELEMENT_TYPE_REQUEST_TOTAL_TIME,            /* %{request-total-time}x */
64
    ELEMENT_TYPE_PROCESS_TIME,                  /* %{process-time}x */
65
    ELEMENT_TYPE_RESPONSE_TIME,                 /* %{response-total-time}x */
66
    ELEMENT_TYPE_TOTAL_TIME,                    /* %{total-time}x */
67
    ELEMENT_TYPE_ERROR,                         /* %{error}x */
68
    ELEMENT_TYPE_PROTOCOL_SPECIFIC,             /* %{protocol-specific...}x */
69
    ELEMENT_TYPE_PROXY_IDLE_TIME,               /* %{proxy.idle-time}x */
70
    ELEMENT_TYPE_PROXY_CONNECT_TIME,            /* %{proxy.connect-time}x */
71
    ELEMENT_TYPE_PROXY_REQUEST_TIME,            /* %{proxy.request-time}x */
72
    ELEMENT_TYPE_PROXY_PROCESS_TIME,            /* %{proxy.process-time}x */
73
    ELEMENT_TYPE_PROXY_RESPONSE_TIME,           /* %{proxy.response-time}x */
74
    ELEMENT_TYPE_PROXY_TOTAL_TIME,              /* %{proxy.total-time}x */
75
    ELEMENT_TYPE_PROXY_REQUEST_BYTES,           /* %{proxy.request-bytes}x */
76
    ELEMENT_TYPE_PROXY_REQUEST_BYTES_HEADER,    /* %{proxy.request-bytes-header}x */
77
    ELEMENT_TYPE_PROXY_REQUEST_BYTES_BODY,      /* %{proxy.request-bytes-body}x */
78
    ELEMENT_TYPE_PROXY_RESPONSE_BYTES,          /* %{proxy.response-bytes}x */
79
    ELEMENT_TYPE_PROXY_RESPONSE_BYTES_HEADER,   /* %{proxy.response-bytes-header}x */
80
    ELEMENT_TYPE_PROXY_RESPONSE_BYTES_BODY,     /* %{proxy.response-bytes-body}x */
81
    ELEMENT_TYPE_PROXY_SSL_PROTOCOL_VERSION,    /* ${proxy.ssl.protocol-version}x */
82
    ELEMENT_TYPE_PROXY_SSL_SESSION_REUSED,      /* ${proxy.ssl.session-reused}x */
83
    ELEMENT_TYPE_PROXY_SSL_CIPHER,              /* ${proxy.ssl.cipher}x */
84
    ELEMENT_TYPE_PROXY_SSL_CIPHER_BITS,         /* ${proxy.ssl.cipher_bits}x */
85
    NUM_ELEMENT_TYPES
86
};
87
88
struct log_element_t {
89
    unsigned type;
90
    h2o_iovec_t suffix;
91
    union {
92
        const h2o_token_t *header_token;
93
        h2o_iovec_t name;
94
        size_t protocol_specific_callback_index;
95
    } data;
96
    unsigned magically_quoted_json : 1; /* whether to omit surrounding doublequotes when the output is null */
97
    unsigned original_response : 1;
98
};
99
100
struct st_h2o_logconf_t {
101
    H2O_VECTOR(struct log_element_t) elements;
102
    int escape;
103
};
104
105
static h2o_iovec_t strdup_lowercased(const char *s, size_t len)
106
0
{
107
0
    h2o_iovec_t v = h2o_strdup(NULL, s, len);
108
0
    h2o_strtolower(v.base, v.len);
109
0
    return v;
110
0
}
111
112
static int determine_magicquote_nodes(h2o_logconf_t *logconf, char *errbuf)
113
0
{
114
0
    size_t element_index;
115
0
    int quote_char = '\0'; /* the quote char being used if the state machine is within a string literal */
116
0
    int just_in = 0;       /* if we just went into the string literal */
117
118
0
    for (element_index = 0; element_index < logconf->elements.size; ++element_index) {
119
0
        h2o_iovec_t suffix = logconf->elements.entries[element_index].suffix;
120
0
        logconf->elements.entries[element_index].magically_quoted_json = just_in && suffix.len != 0 && suffix.base[0] == quote_char;
121
122
0
        just_in = 0;
123
124
0
        size_t i;
125
0
        for (i = 0; i < suffix.len; ++i) {
126
0
            just_in = 0;
127
0
            if (quote_char != '\0') {
128
0
                if (quote_char == suffix.base[i]) {
129
                    /* out of quote? */
130
0
                    size_t j, num_bs = 0;
131
0
                    for (j = i; j != 0; ++num_bs)
132
0
                        if (suffix.base[--j] != '\\')
133
0
                            break;
134
0
                    if (num_bs % 2 == 0)
135
0
                        quote_char = '\0';
136
0
                }
137
0
            } else {
138
0
                if (suffix.base[i] == '"' || suffix.base[i] == '\'') {
139
0
                    quote_char = suffix.base[i];
140
0
                    just_in = 1;
141
0
                }
142
0
            }
143
0
        }
144
0
    }
145
146
0
    return 1;
147
0
}
148
149
h2o_logconf_t *h2o_logconf_compile(const char *fmt, int escape, char *errbuf)
150
0
{
151
0
    h2o_logconf_t *logconf = h2o_mem_alloc(sizeof(*logconf));
152
0
    const char *pt = fmt;
153
0
    size_t fmt_len = strlen(fmt);
154
155
0
    *logconf = (h2o_logconf_t){{NULL}, escape};
156
157
0
#define LAST_ELEMENT() (logconf->elements.entries + logconf->elements.size - 1)
158
/* suffix buffer is always guaranteed to be larger than the fmt + (sizeof('\n') - 1) (so that they would be no buffer overruns) */
159
0
#define NEW_ELEMENT(ty)                                                                                                            \
160
0
    do {                                                                                                                           \
161
0
        h2o_vector_reserve(NULL, &logconf->elements, logconf->elements.size + 1);                                                  \
162
0
        logconf->elements.size++;                                                                                                  \
163
0
        *LAST_ELEMENT() = (struct log_element_t){0};                                                                               \
164
0
        LAST_ELEMENT()->type = ty;                                                                                                 \
165
0
        LAST_ELEMENT()->suffix.base = h2o_mem_alloc(fmt_len + 1);                                                                  \
166
0
    } while (0)
167
168
0
    while (*pt != '\0') {
169
0
        if (memcmp(pt, "%%", 2) == 0) {
170
0
            ++pt; /* emit % */
171
0
        } else if (*pt == '%') {
172
0
            ++pt;
173
            /* handle < and > */
174
0
            int log_original = 0;
175
0
            for (;; ++pt) {
176
0
                if (*pt == '<') {
177
0
                    log_original = 1;
178
0
                } else if (*pt == '>') {
179
0
                    log_original = 0;
180
0
                } else {
181
0
                    break;
182
0
                }
183
0
            }
184
            /* handle {...}n */
185
0
            if (*pt == '{') {
186
0
                const h2o_token_t *token;
187
0
                const char *quote_end = strchr(++pt, '}');
188
0
                if (quote_end == NULL) {
189
0
                    sprintf(errbuf, "failed to compile log format: unterminated header name starting at: \"%16s\"", pt);
190
0
                    goto Error;
191
0
                }
192
0
                const char modifier = quote_end[1];
193
0
                switch (modifier) {
194
0
                case 'i':
195
0
                case 'o': {
196
0
                    h2o_iovec_t name = strdup_lowercased(pt, quote_end - pt);
197
0
                    token = h2o_lookup_token(name.base, name.len);
198
0
                    if (token != NULL) {
199
0
                        free(name.base);
200
0
                        if (modifier == 'o' && token == H2O_TOKEN_SET_COOKIE) {
201
0
                            NEW_ELEMENT(ELEMENT_TYPE_OUT_HEADER_TOKEN_CONCATENATED);
202
0
                            LAST_ELEMENT()->data.header_token = token;
203
0
                        } else {
204
0
                            NEW_ELEMENT(modifier == 'i' ? ELEMENT_TYPE_IN_HEADER_TOKEN : ELEMENT_TYPE_OUT_HEADER_TOKEN);
205
0
                            LAST_ELEMENT()->data.header_token = token;
206
0
                        }
207
0
                    } else {
208
0
                        NEW_ELEMENT(modifier == 'i' ? ELEMENT_TYPE_IN_HEADER_STRING : ELEMENT_TYPE_OUT_HEADER_STRING);
209
0
                        LAST_ELEMENT()->data.name = name;
210
0
                    }
211
0
                    LAST_ELEMENT()->original_response = log_original;
212
0
                } break;
213
0
                case 'p':
214
0
                    if (h2o_memis(pt, quote_end - pt, H2O_STRLIT("local"))) {
215
0
                        NEW_ELEMENT(ELEMENT_TYPE_LOCAL_PORT);
216
0
                    } else if (h2o_memis(pt, quote_end - pt, H2O_STRLIT("remote"))) {
217
0
                        NEW_ELEMENT(ELEMENT_TYPE_REMOTE_PORT);
218
0
                    } else {
219
0
                        sprintf(errbuf, "failed to compile log format: unknown specifier for %%{...}p");
220
0
                        goto Error;
221
0
                    }
222
0
                    break;
223
0
                case 'e': {
224
0
                    h2o_iovec_t name = h2o_strdup(NULL, pt, quote_end - pt);
225
0
                    NEW_ELEMENT(ELEMENT_TYPE_ENV_VAR);
226
0
                    LAST_ELEMENT()->data.name = name;
227
0
                } break;
228
0
                case 't':
229
0
                    if (h2o_memis(pt, quote_end - pt, H2O_STRLIT("sec"))) {
230
0
                        NEW_ELEMENT(ELEMENT_TYPE_TIMESTAMP_SEC_SINCE_EPOCH);
231
0
                    } else if (h2o_memis(pt, quote_end - pt, H2O_STRLIT("msec"))) {
232
0
                        NEW_ELEMENT(ELEMENT_TYPE_TIMESTAMP_MSEC_SINCE_EPOCH);
233
0
                    } else if (h2o_memis(pt, quote_end - pt, H2O_STRLIT("usec"))) {
234
0
                        NEW_ELEMENT(ELEMENT_TYPE_TIMESTAMP_USEC_SINCE_EPOCH);
235
0
                    } else if (h2o_memis(pt, quote_end - pt, H2O_STRLIT("msec_frac"))) {
236
0
                        NEW_ELEMENT(ELEMENT_TYPE_TIMESTAMP_MSEC_FRAC);
237
0
                    } else if (h2o_memis(pt, quote_end - pt, H2O_STRLIT("usec_frac"))) {
238
0
                        NEW_ELEMENT(ELEMENT_TYPE_TIMESTAMP_USEC_FRAC);
239
0
                    } else {
240
0
                        h2o_iovec_t name = h2o_strdup(NULL, pt, quote_end - pt);
241
0
                        NEW_ELEMENT(ELEMENT_TYPE_TIMESTAMP_STRFTIME);
242
0
                        LAST_ELEMENT()->data.name = name;
243
0
                    }
244
0
                    break;
245
0
                case 'x':
246
0
#define MAP_EXT_TO_TYPE(name, id)                                                                                                  \
247
0
    if (h2o_lcstris(pt, quote_end - pt, H2O_STRLIT(name))) {                                                                       \
248
0
        NEW_ELEMENT(id);                                                                                                           \
249
0
        goto MAP_EXT_Found;                                                                                                        \
250
0
    }
251
0
#define MAP_EXT_TO_PROTO(name, cb)                                                                                                 \
252
0
    if (h2o_lcstris(pt, quote_end - pt, H2O_STRLIT(name))) {                                                                       \
253
0
        h2o_conn_callbacks_t dummy_;                                                                                               \
254
0
        NEW_ELEMENT(ELEMENT_TYPE_PROTOCOL_SPECIFIC);                                                                               \
255
0
        LAST_ELEMENT()->data.protocol_specific_callback_index = &dummy_.log_.cb - dummy_.log_.callbacks;                           \
256
0
        goto MAP_EXT_Found;                                                                                                        \
257
0
    }
258
0
                    MAP_EXT_TO_TYPE("connection-id", ELEMENT_TYPE_CONNECTION_ID);
259
0
                    MAP_EXT_TO_TYPE("connect-time", ELEMENT_TYPE_CONNECT_TIME);
260
0
                    MAP_EXT_TO_TYPE("request-id", ELEMENT_TYPE_REQUEST_ID);
261
0
                    MAP_EXT_TO_TYPE("request-total-time", ELEMENT_TYPE_REQUEST_TOTAL_TIME);
262
0
                    MAP_EXT_TO_TYPE("request-header-time", ELEMENT_TYPE_REQUEST_HEADER_TIME);
263
0
                    MAP_EXT_TO_TYPE("request-body-time", ELEMENT_TYPE_REQUEST_BODY_TIME);
264
0
                    MAP_EXT_TO_TYPE("process-time", ELEMENT_TYPE_PROCESS_TIME);
265
0
                    MAP_EXT_TO_TYPE("response-time", ELEMENT_TYPE_RESPONSE_TIME);
266
0
                    MAP_EXT_TO_TYPE("duration", ELEMENT_TYPE_TOTAL_TIME);
267
0
                    MAP_EXT_TO_TYPE("total-time", ELEMENT_TYPE_TOTAL_TIME);
268
0
                    MAP_EXT_TO_TYPE("error", ELEMENT_TYPE_ERROR);
269
0
                    MAP_EXT_TO_TYPE("proxy.idle-time", ELEMENT_TYPE_PROXY_IDLE_TIME);
270
0
                    MAP_EXT_TO_TYPE("proxy.connect-time", ELEMENT_TYPE_PROXY_CONNECT_TIME);
271
0
                    MAP_EXT_TO_TYPE("proxy.request-time", ELEMENT_TYPE_PROXY_REQUEST_TIME);
272
0
                    MAP_EXT_TO_TYPE("proxy.process-time", ELEMENT_TYPE_PROXY_PROCESS_TIME);
273
0
                    MAP_EXT_TO_TYPE("proxy.response-time", ELEMENT_TYPE_PROXY_RESPONSE_TIME);
274
0
                    MAP_EXT_TO_TYPE("proxy.total-time", ELEMENT_TYPE_PROXY_TOTAL_TIME);
275
0
                    MAP_EXT_TO_TYPE("proxy.request-bytes", ELEMENT_TYPE_PROXY_REQUEST_BYTES);
276
0
                    MAP_EXT_TO_TYPE("proxy.request-bytes-header", ELEMENT_TYPE_PROXY_REQUEST_BYTES_HEADER);
277
0
                    MAP_EXT_TO_TYPE("proxy.request-bytes-body", ELEMENT_TYPE_PROXY_REQUEST_BYTES_BODY);
278
0
                    MAP_EXT_TO_TYPE("proxy.response-bytes", ELEMENT_TYPE_PROXY_RESPONSE_BYTES);
279
0
                    MAP_EXT_TO_TYPE("proxy.response-bytes-header", ELEMENT_TYPE_PROXY_RESPONSE_BYTES_HEADER);
280
0
                    MAP_EXT_TO_TYPE("proxy.response-bytes-body", ELEMENT_TYPE_PROXY_RESPONSE_BYTES_BODY);
281
0
                    MAP_EXT_TO_TYPE("proxy.ssl.protocol-version", ELEMENT_TYPE_PROXY_SSL_PROTOCOL_VERSION);
282
0
                    MAP_EXT_TO_TYPE("proxy.ssl.session-reused", ELEMENT_TYPE_PROXY_SSL_SESSION_REUSED);
283
0
                    MAP_EXT_TO_TYPE("proxy.ssl.cipher", ELEMENT_TYPE_PROXY_SSL_CIPHER);
284
0
                    MAP_EXT_TO_TYPE("proxy.ssl.cipher-bits", ELEMENT_TYPE_PROXY_SSL_CIPHER_BITS);
285
0
                    MAP_EXT_TO_PROTO("extensible-priorities", extensible_priorities);
286
0
                    MAP_EXT_TO_PROTO("http1.request-index", http1.request_index);
287
0
                    MAP_EXT_TO_PROTO("http2.stream-id", http2.stream_id);
288
0
                    MAP_EXT_TO_PROTO("http2.priority.received", http2.priority_received);
289
0
                    MAP_EXT_TO_PROTO("http2.priority.received.exclusive", http2.priority_received_exclusive);
290
0
                    MAP_EXT_TO_PROTO("http2.priority.received.parent", http2.priority_received_parent);
291
0
                    MAP_EXT_TO_PROTO("http2.priority.received.weight", http2.priority_received_weight);
292
0
                    MAP_EXT_TO_PROTO("http2.priority.actual", http2.priority_actual);
293
0
                    MAP_EXT_TO_PROTO("http2.priority.actual.parent", http2.priority_actual_parent);
294
0
                    MAP_EXT_TO_PROTO("http2.priority.actual.weight", http2.priority_actual_weight);
295
0
                    MAP_EXT_TO_PROTO("http3.stream-id", http3.stream_id);
296
0
                    MAP_EXT_TO_PROTO("http3.quic-stats", http3.quic_stats);
297
0
                    MAP_EXT_TO_PROTO("http3.quic-version", http3.quic_version);
298
0
                    MAP_EXT_TO_PROTO("cc.name", transport.cc_name);
299
0
                    MAP_EXT_TO_PROTO("delivery-rate", transport.delivery_rate);
300
0
                    MAP_EXT_TO_PROTO("ssl.protocol-version", ssl.protocol_version);
301
0
                    MAP_EXT_TO_PROTO("ssl.session-reused", ssl.session_reused);
302
0
                    MAP_EXT_TO_PROTO("ssl.cipher", ssl.cipher);
303
0
                    MAP_EXT_TO_PROTO("ssl.cipher-bits", ssl.cipher_bits);
304
0
                    MAP_EXT_TO_PROTO("ssl.session-id", ssl.session_id);
305
0
                    MAP_EXT_TO_PROTO("ssl.server-name", ssl.server_name);
306
0
                    MAP_EXT_TO_PROTO("ssl.negotiated-protocol", ssl.negotiated_protocol);
307
0
                    MAP_EXT_TO_PROTO("ssl.ech.config-id", ssl.ech_config_id);
308
0
                    MAP_EXT_TO_PROTO("ssl.ech.kem", ssl.ech_kem);
309
0
                    MAP_EXT_TO_PROTO("ssl.ech.cipher", ssl.ech_cipher);
310
0
                    MAP_EXT_TO_PROTO("ssl.ech.cipher-bits", ssl.ech_cipher_bits);
311
0
                    MAP_EXT_TO_PROTO("ssl.backend", ssl.backend);
312
0
                    { /* not found */
313
0
                        h2o_iovec_t name = strdup_lowercased(pt, quote_end - pt);
314
0
                        NEW_ELEMENT(ELEMENT_TYPE_EXTENDED_VAR);
315
0
                        LAST_ELEMENT()->data.name = name;
316
0
                    }
317
0
                MAP_EXT_Found:
318
0
#undef MAP_EXT_TO_TYPE
319
0
#undef MAP_EXT_TO_PROTO
320
0
                    break;
321
0
                default:
322
0
                    sprintf(errbuf, "failed to compile log format: header name is not followed by either `i`, `o`, `x`, `e`");
323
0
                    goto Error;
324
0
                }
325
0
                pt = quote_end + 2;
326
0
                continue;
327
0
            } else {
328
0
                unsigned type = NUM_ELEMENT_TYPES;
329
0
                switch (*pt++) {
330
0
#define TYPE_MAP(ch, ty)                                                                                                           \
331
0
    case ch:                                                                                                                       \
332
0
        type = ty;                                                                                                                 \
333
0
        break
334
0
                    TYPE_MAP('A', ELEMENT_TYPE_LOCAL_ADDR);
335
0
                    TYPE_MAP('b', ELEMENT_TYPE_BYTES_SENT);
336
0
                    TYPE_MAP('H', ELEMENT_TYPE_PROTOCOL);
337
0
                    TYPE_MAP('h', ELEMENT_TYPE_REMOTE_ADDR);
338
0
                    TYPE_MAP('l', ELEMENT_TYPE_LOGNAME);
339
0
                    TYPE_MAP('m', ELEMENT_TYPE_METHOD);
340
0
                    TYPE_MAP('p', ELEMENT_TYPE_LOCAL_PORT);
341
0
                    TYPE_MAP('e', ELEMENT_TYPE_ENV_VAR);
342
0
                    TYPE_MAP('q', ELEMENT_TYPE_QUERY);
343
0
                    TYPE_MAP('r', ELEMENT_TYPE_REQUEST_LINE);
344
0
                    TYPE_MAP('s', ELEMENT_TYPE_STATUS);
345
0
                    TYPE_MAP('t', ELEMENT_TYPE_TIMESTAMP);
346
0
                    TYPE_MAP('U', ELEMENT_TYPE_URL_PATH);
347
0
                    TYPE_MAP('u', ELEMENT_TYPE_REMOTE_USER);
348
0
                    TYPE_MAP('V', ELEMENT_TYPE_AUTHORITY);
349
0
                    TYPE_MAP('v', ELEMENT_TYPE_HOSTCONF);
350
0
#undef TYPE_MAP
351
0
                default:
352
0
                    sprintf(errbuf, "failed to compile log format: unknown escape sequence: %%%c", pt[-1]);
353
0
                    goto Error;
354
0
                }
355
0
                NEW_ELEMENT(type);
356
0
                LAST_ELEMENT()->original_response = log_original;
357
0
                continue;
358
0
            }
359
0
        }
360
        /* emit current char */
361
0
        if (logconf->elements.size == 0)
362
0
            NEW_ELEMENT(ELEMENT_TYPE_EMPTY);
363
0
        LAST_ELEMENT()->suffix.base[LAST_ELEMENT()->suffix.len++] = *pt++;
364
0
    }
365
366
    /* emit end-of-line */
367
0
    if (logconf->elements.size == 0)
368
0
        NEW_ELEMENT(ELEMENT_TYPE_EMPTY);
369
0
    LAST_ELEMENT()->suffix.base[LAST_ELEMENT()->suffix.len++] = '\n';
370
371
0
#undef NEW_ELEMENT
372
0
#undef LAST_ELEMENT
373
374
0
    if (escape == H2O_LOGCONF_ESCAPE_JSON) {
375
0
        if (!determine_magicquote_nodes(logconf, errbuf))
376
0
            goto Error;
377
0
    }
378
379
0
    return logconf;
380
381
0
Error:
382
0
    h2o_logconf_dispose(logconf);
383
0
    return NULL;
384
0
}
385
386
void h2o_logconf_dispose(h2o_logconf_t *logconf)
387
0
{
388
0
    size_t i;
389
390
0
    for (i = 0; i != logconf->elements.size; ++i) {
391
0
        free(logconf->elements.entries[i].suffix.base);
392
0
        switch (logconf->elements.entries[i].type) {
393
0
        case ELEMENT_TYPE_EXTENDED_VAR:
394
0
        case ELEMENT_TYPE_IN_HEADER_STRING:
395
0
        case ELEMENT_TYPE_OUT_HEADER_STRING:
396
0
        case ELEMENT_TYPE_TIMESTAMP_STRFTIME:
397
0
            free(logconf->elements.entries[i].data.name.base);
398
0
            break;
399
0
        default:
400
0
            break;
401
0
        }
402
0
    }
403
0
    free(logconf->elements.entries);
404
0
    free(logconf);
405
0
}
406
407
static inline char *append_safe_string(char *pos, const char *src, size_t len)
408
0
{
409
0
    memcpy(pos, src, len);
410
0
    return pos + len;
411
0
}
412
413
static char *append_unsafe_string_apache(char *pos, const char *src, size_t len)
414
0
{
415
0
    if (len == 0)
416
0
        return pos;
417
418
0
    for (const char *src_end = src + len; src != src_end; ++src) {
419
0
        if (' ' <= *src && *src < 0x7d && *src != '"') {
420
0
            *pos++ = *src;
421
0
        } else {
422
0
            *pos++ = '\\';
423
0
            *pos++ = 'x';
424
0
            *pos++ = ("0123456789abcdef")[(*src >> 4) & 0xf];
425
0
            *pos++ = ("0123456789abcdef")[*src & 0xf];
426
0
        }
427
0
    }
428
429
0
    return pos;
430
0
}
431
432
static char *append_unsafe_string_json(char *pos, const char *src, size_t len)
433
0
{
434
0
    const char *src_end = src + len;
435
436
0
    for (; src != src_end; ++src) {
437
0
        if (' ' <= *src && *src < 0x7e) {
438
0
            if (*src == '"' || *src == '\\')
439
0
                *pos++ = '\\';
440
0
            *pos++ = *src;
441
0
        } else {
442
0
            *pos++ = '\\';
443
0
            *pos++ = 'u';
444
0
            *pos++ = '0';
445
0
            *pos++ = '0';
446
0
            *pos++ = ("0123456789abcdef")[(*src >> 4) & 0xf];
447
0
            *pos++ = ("0123456789abcdef")[*src & 0xf];
448
0
        }
449
0
    }
450
451
0
    return pos;
452
0
}
453
454
static char *append_addr(char *pos, socklen_t (*cb)(h2o_conn_t *conn, struct sockaddr *sa), h2o_conn_t *conn, h2o_iovec_t nullexpr)
455
0
{
456
0
    struct sockaddr_storage ss;
457
0
    socklen_t sslen;
458
459
0
    if ((sslen = cb(conn, (void *)&ss)) == 0)
460
0
        goto Fail;
461
0
    size_t l = h2o_socket_getnumerichost((void *)&ss, sslen, pos);
462
0
    if (l == SIZE_MAX)
463
0
        goto Fail;
464
0
    pos += l;
465
0
    return pos;
466
467
0
Fail:
468
0
    memcpy(pos, nullexpr.base, nullexpr.len);
469
0
    pos += nullexpr.len;
470
0
    return pos;
471
0
}
472
473
static char *append_port(char *pos, socklen_t (*cb)(h2o_conn_t *conn, struct sockaddr *sa), h2o_conn_t *conn, h2o_iovec_t nullexpr)
474
0
{
475
0
    struct sockaddr_storage ss;
476
0
    socklen_t sslen;
477
478
0
    if ((sslen = cb(conn, (void *)&ss)) == 0)
479
0
        goto Fail;
480
0
    int32_t port = h2o_socket_getport((void *)&ss);
481
0
    if (port == -1)
482
0
        goto Fail;
483
0
    pos += sprintf(pos, "%" PRIu16, (uint16_t)port);
484
0
    return pos;
485
486
0
Fail:
487
0
    memcpy(pos, nullexpr.base, nullexpr.len);
488
0
    pos += nullexpr.len;
489
0
    return pos;
490
0
}
491
492
#define APPEND_SAFE_STRING_WITH_LEN(pos, s, len)                                                                                   \
493
0
    do {                                                                                                                           \
494
0
        if (s == NULL)                                                                                                             \
495
0
            goto EmitNull;                                                                                                         \
496
0
        RESERVE(len);                                                                                                              \
497
0
        pos = append_safe_string(pos, s, len);                                                                                     \
498
0
    } while (0)
499
0
#define APPEND_SAFE_STRING(pos, s) APPEND_SAFE_STRING_WITH_LEN(pos, s, strlen(s))
500
#define APPEND_DURATION(pos, name)                                                                                                 \
501
0
    do {                                                                                                                           \
502
0
        int64_t delta_usec;                                                                                                        \
503
0
        if (!h2o_time_compute_##name(req, &delta_usec))                                                                            \
504
0
            goto EmitNull;                                                                                                         \
505
0
        int32_t delta_sec = (int32_t)(delta_usec / (1000 * 1000));                                                                 \
506
0
        delta_usec -= ((int64_t)delta_sec * (1000 * 1000));                                                                        \
507
0
        RESERVE(sizeof(H2O_INT32_LONGEST_STR ".999999") - 1);                                                                      \
508
0
        pos += sprintf(pos, "%" PRId32, delta_sec);                                                                                \
509
0
        if (delta_usec != 0) {                                                                                                     \
510
0
            int i;                                                                                                                 \
511
0
            *pos++ = '.';                                                                                                          \
512
0
            for (i = 5; i >= 0; --i) {                                                                                             \
513
0
                pos[i] = '0' + delta_usec % 10;                                                                                    \
514
0
                delta_usec /= 10;                                                                                                  \
515
0
            }                                                                                                                      \
516
0
            pos += 6;                                                                                                              \
517
0
        }                                                                                                                          \
518
0
    } while (0)
519
520
static char *expand_line_buf(char *line, size_t *cur_size, size_t required, int should_realloc)
521
0
{
522
0
    size_t new_size = *cur_size;
523
524
    /* determine the new size */
525
0
    do {
526
0
        new_size *= 2;
527
0
    } while (new_size < required);
528
529
    /* reallocate */
530
0
    if (!should_realloc) {
531
0
        char *newpt = h2o_mem_alloc(new_size);
532
0
        memcpy(newpt, line, *cur_size);
533
0
        line = newpt;
534
0
    } else {
535
0
        line = h2o_mem_realloc(line, new_size);
536
0
    }
537
0
    *cur_size = new_size;
538
539
0
    return line;
540
0
}
541
542
char *h2o_log_request(h2o_logconf_t *logconf, h2o_req_t *req, size_t *len, char *buf)
543
0
{
544
0
    char *line = buf, *pos = line, *line_end = line + *len;
545
0
    h2o_iovec_t nullexpr;
546
0
    char *(*append_unsafe_string)(char *pos, const char *src, size_t len);
547
0
    size_t element_index, unsafe_factor;
548
0
    struct tm localt = {0};
549
0
    int should_realloc_on_expand = 0;
550
551
0
    switch (logconf->escape) {
552
0
    case H2O_LOGCONF_ESCAPE_APACHE:
553
0
        nullexpr = h2o_iovec_init(H2O_STRLIT("-"));
554
0
        append_unsafe_string = append_unsafe_string_apache;
555
0
        unsafe_factor = 4;
556
0
        break;
557
0
    case H2O_LOGCONF_ESCAPE_JSON:
558
0
        nullexpr = h2o_iovec_init(H2O_STRLIT("null"));
559
0
        append_unsafe_string = append_unsafe_string_json;
560
0
        unsafe_factor = 6;
561
0
        break;
562
0
    default:
563
0
        h2o_fatal("unexpected escape mode");
564
0
        break;
565
0
    }
566
567
0
    for (element_index = 0; element_index != logconf->elements.size; ++element_index) {
568
0
        struct log_element_t *element = logconf->elements.entries + element_index;
569
570
/* reserve capacity + suffix.len */
571
0
#define RESERVE(capacity)                                                                                                          \
572
0
    do {                                                                                                                           \
573
0
        if ((capacity) + element->suffix.len > line_end - pos) {                                                                   \
574
0
            size_t off = pos - line;                                                                                               \
575
0
            size_t size = line_end - line;                                                                                         \
576
0
            line = expand_line_buf(line, &size, off + (capacity) + element->suffix.len, should_realloc_on_expand);                 \
577
0
            pos = line + off;                                                                                                      \
578
0
            line_end = line + size;                                                                                                \
579
0
            should_realloc_on_expand = 1;                                                                                          \
580
0
        }                                                                                                                          \
581
0
    } while (0)
582
583
0
        switch (element->type) {
584
0
        case ELEMENT_TYPE_EMPTY:
585
0
            RESERVE(0);
586
0
            break;
587
0
        case ELEMENT_TYPE_LOCAL_ADDR: /* %A */
588
0
            RESERVE(NI_MAXHOST);
589
0
            pos = append_addr(pos, req->conn->callbacks->get_sockname, req->conn, nullexpr);
590
0
            break;
591
0
        case ELEMENT_TYPE_BYTES_SENT: /* %b */
592
0
            RESERVE(sizeof(H2O_UINT64_LONGEST_STR) - 1);
593
0
            pos += sprintf(pos, "%" PRIu64, req->bytes_sent);
594
0
            break;
595
0
        case ELEMENT_TYPE_PROTOCOL: /* %H */
596
0
            if (req->version == 0)
597
0
                goto EmitNull;
598
0
            RESERVE(sizeof("HTTP/1.1"));
599
0
            pos += h2o_stringify_protocol_version(pos, req->version);
600
0
            break;
601
0
        case ELEMENT_TYPE_REMOTE_ADDR: /* %h */
602
0
            RESERVE(NI_MAXHOST);
603
0
            pos = append_addr(pos, req->conn->callbacks->get_peername, req->conn, nullexpr);
604
0
            break;
605
0
        case ELEMENT_TYPE_METHOD: /* %m */
606
0
            if (req->input.method.len == 0)
607
0
                goto EmitNull;
608
0
            RESERVE(req->input.method.len * unsafe_factor);
609
0
            pos = append_unsafe_string(pos, req->input.method.base, req->input.method.len);
610
0
            break;
611
0
        case ELEMENT_TYPE_LOCAL_PORT: /* %p */
612
0
            RESERVE(sizeof(H2O_UINT16_LONGEST_STR) - 1);
613
0
            pos = append_port(pos, req->conn->callbacks->get_sockname, req->conn, nullexpr);
614
0
            break;
615
0
        case ELEMENT_TYPE_REMOTE_PORT: /* %{remote}p */
616
0
            RESERVE(sizeof(H2O_UINT16_LONGEST_STR) - 1);
617
0
            pos = append_port(pos, req->conn->callbacks->get_peername, req->conn, nullexpr);
618
0
            break;
619
0
        case ELEMENT_TYPE_ENV_VAR: /* %{..}e */ {
620
0
            h2o_iovec_t *env_var = h2o_req_getenv(req, element->data.name.base, element->data.name.len, 0);
621
0
            if (env_var == NULL)
622
0
                goto EmitNull;
623
0
            RESERVE(env_var->len * unsafe_factor);
624
0
            pos = append_safe_string(pos, env_var->base, env_var->len);
625
0
        } break;
626
0
        case ELEMENT_TYPE_QUERY: /* %q */
627
0
            if (req->input.query_at != SIZE_MAX) {
628
0
                size_t len = req->input.path.len - req->input.query_at;
629
0
                RESERVE(len * unsafe_factor);
630
0
                pos = append_unsafe_string(pos, req->input.path.base + req->input.query_at, len);
631
0
            }
632
0
            break;
633
0
        case ELEMENT_TYPE_REQUEST_LINE: /* %r */
634
0
            if (req->version == 0)
635
0
                goto EmitNull;
636
0
            RESERVE((req->input.method.len + req->input.path.len) * unsafe_factor + sizeof("  HTTP/1.1"));
637
0
            pos = append_unsafe_string(pos, req->input.method.base, req->input.method.len);
638
0
            *pos++ = ' ';
639
0
            pos = append_unsafe_string(pos, req->input.path.base, req->input.path.len);
640
0
            *pos++ = ' ';
641
0
            pos += h2o_stringify_protocol_version(pos, req->version);
642
0
            break;
643
0
        case ELEMENT_TYPE_STATUS: /* %s */
644
0
            if (req->res.status == 0)
645
0
                goto EmitNull;
646
0
            RESERVE(sizeof(H2O_INT32_LONGEST_STR) - 1);
647
0
            pos += sprintf(pos, "%" PRId32, (int32_t)(element->original_response ? req->res.original.status : req->res.status));
648
0
            break;
649
0
        case ELEMENT_TYPE_TIMESTAMP: /* %t */
650
0
            if (h2o_timeval_is_null(&req->processed_at.at))
651
0
                goto EmitNull;
652
0
            RESERVE(H2O_TIMESTR_LOG_LEN + 2);
653
0
            *pos++ = '[';
654
0
            pos = append_safe_string(pos, req->processed_at.str->log, H2O_TIMESTR_LOG_LEN);
655
0
            *pos++ = ']';
656
0
            break;
657
0
        case ELEMENT_TYPE_TIMESTAMP_STRFTIME: /* %{...}t */
658
0
            if (h2o_timeval_is_null(&req->processed_at.at))
659
0
                goto EmitNull;
660
0
            {
661
0
                size_t bufsz, len;
662
0
                if (localt.tm_year == 0)
663
0
                    localtime_r(&req->processed_at.at.tv_sec, &localt);
664
0
                for (bufsz = 128;; bufsz *= 2) {
665
0
                    RESERVE(bufsz);
666
0
                    if ((len = strftime(pos, bufsz, element->data.name.base, &localt)) != 0)
667
0
                        break;
668
0
                }
669
0
                pos += len;
670
0
            }
671
0
            break;
672
0
        case ELEMENT_TYPE_TIMESTAMP_SEC_SINCE_EPOCH: /* %{sec}t */
673
0
            if (h2o_timeval_is_null(&req->processed_at.at))
674
0
                goto EmitNull;
675
0
            RESERVE(sizeof(H2O_UINT32_LONGEST_STR) - 1);
676
0
            pos += sprintf(pos, "%" PRIu32, (uint32_t)req->processed_at.at.tv_sec);
677
0
            break;
678
0
        case ELEMENT_TYPE_TIMESTAMP_MSEC_SINCE_EPOCH: /* %{msec}t */
679
0
            if (h2o_timeval_is_null(&req->processed_at.at))
680
0
                goto EmitNull;
681
0
            RESERVE(sizeof(H2O_UINT64_LONGEST_STR) - 1);
682
0
            pos += sprintf(pos, "%" PRIu64,
683
0
                           (uint64_t)req->processed_at.at.tv_sec * 1000 + (uint64_t)req->processed_at.at.tv_usec / 1000);
684
0
            break;
685
0
        case ELEMENT_TYPE_TIMESTAMP_USEC_SINCE_EPOCH: /* %{usec}t */
686
0
            if (h2o_timeval_is_null(&req->processed_at.at))
687
0
                goto EmitNull;
688
0
            RESERVE(sizeof(H2O_UINT64_LONGEST_STR) - 1);
689
0
            pos +=
690
0
                sprintf(pos, "%" PRIu64, (uint64_t)req->processed_at.at.tv_sec * 1000000 + (uint64_t)req->processed_at.at.tv_usec);
691
0
            break;
692
0
        case ELEMENT_TYPE_TIMESTAMP_MSEC_FRAC: /* %{msec_frac}t */
693
0
            if (h2o_timeval_is_null(&req->processed_at.at))
694
0
                goto EmitNull;
695
0
            RESERVE(3);
696
0
            pos += sprintf(pos, "%03u", (unsigned)(req->processed_at.at.tv_usec / 1000));
697
0
            break;
698
0
        case ELEMENT_TYPE_TIMESTAMP_USEC_FRAC: /* %{usec_frac}t */
699
0
            if (h2o_timeval_is_null(&req->processed_at.at))
700
0
                goto EmitNull;
701
0
            RESERVE(6);
702
0
            pos += sprintf(pos, "%06u", (unsigned)req->processed_at.at.tv_usec);
703
0
            break;
704
0
        case ELEMENT_TYPE_URL_PATH: /* %U */ {
705
0
            if (req->input.path.len == 0)
706
0
                goto EmitNull;
707
0
            size_t path_len = req->input.query_at == SIZE_MAX ? req->input.path.len : req->input.query_at;
708
0
            RESERVE(path_len * unsafe_factor);
709
0
            pos = append_unsafe_string(pos, req->input.path.base, path_len);
710
0
        } break;
711
0
        case ELEMENT_TYPE_REMOTE_USER: /* %u */ {
712
0
            h2o_iovec_t *remote_user = h2o_req_getenv(req, H2O_STRLIT("REMOTE_USER"), 0);
713
0
            if (remote_user == NULL)
714
0
                goto EmitNull;
715
0
            RESERVE(remote_user->len * unsafe_factor);
716
0
            pos = append_unsafe_string(pos, remote_user->base, remote_user->len);
717
0
        } break;
718
0
        case ELEMENT_TYPE_AUTHORITY: /* %V */
719
0
            RESERVE(req->input.authority.len * unsafe_factor);
720
0
            pos = append_unsafe_string(pos, req->input.authority.base, req->input.authority.len);
721
0
            break;
722
0
        case ELEMENT_TYPE_HOSTCONF: /* %v */
723
0
            RESERVE(req->hostconf->authority.hostport.len * unsafe_factor);
724
0
            pos = append_unsafe_string(pos, req->hostconf->authority.hostport.base, req->hostconf->authority.hostport.len);
725
0
            break;
726
727
0
#define EMIT_HEADER(__headers, concat, findfunc, ...)                                                                              \
728
0
    do {                                                                                                                           \
729
0
        h2o_headers_t *headers = (__headers);                                                                                      \
730
0
        ssize_t index = -1;                                                                                                        \
731
0
        int found = 0;                                                                                                             \
732
0
        while ((index = (findfunc)(headers, __VA_ARGS__, index)) != -1) {                                                          \
733
0
            if (found) {                                                                                                           \
734
0
                RESERVE(2);                                                                                                        \
735
0
                *pos++ = ',';                                                                                                      \
736
0
                *pos++ = ' ';                                                                                                      \
737
0
            } else {                                                                                                               \
738
0
                found = 1;                                                                                                         \
739
0
            }                                                                                                                      \
740
0
            const h2o_header_t *header = headers->entries + index;                                                                 \
741
0
            RESERVE(header->value.len *unsafe_factor);                                                                             \
742
0
            pos = append_unsafe_string(pos, header->value.base, header->value.len);                                                \
743
0
            if (!concat)                                                                                                           \
744
0
                break;                                                                                                             \
745
0
        }                                                                                                                          \
746
0
        if (!found)                                                                                                                \
747
0
            goto EmitNull;                                                                                                         \
748
0
    } while (0)
749
750
0
        case ELEMENT_TYPE_IN_HEADER_TOKEN:
751
0
            EMIT_HEADER(&req->headers, 0, h2o_find_header, element->data.header_token);
752
0
            break;
753
0
        case ELEMENT_TYPE_IN_HEADER_STRING:
754
0
            EMIT_HEADER(&req->headers, 0, h2o_find_header_by_str, element->data.name.base, element->data.name.len);
755
0
            break;
756
0
        case ELEMENT_TYPE_OUT_HEADER_TOKEN:
757
0
            EMIT_HEADER(element->original_response ? &req->res.original.headers : &req->res.headers, 0, h2o_find_header,
758
0
                        element->data.header_token);
759
0
            break;
760
0
        case ELEMENT_TYPE_OUT_HEADER_STRING:
761
0
            EMIT_HEADER(element->original_response ? &req->res.original.headers : &req->res.headers, 0, h2o_find_header_by_str,
762
0
                        element->data.name.base, element->data.name.len);
763
0
            break;
764
0
        case ELEMENT_TYPE_OUT_HEADER_TOKEN_CONCATENATED:
765
0
            EMIT_HEADER(element->original_response ? &req->res.original.headers : &req->res.headers, 1, h2o_find_header,
766
0
                        element->data.header_token);
767
0
            break;
768
769
0
#undef EMIT_HEADER
770
771
0
        case ELEMENT_TYPE_CONNECTION_ID:
772
0
            RESERVE(sizeof(H2O_UINT64_LONGEST_STR) - 1);
773
0
            pos += sprintf(pos, "%" PRIu64, req->conn->id);
774
0
            break;
775
776
0
        case ELEMENT_TYPE_CONNECT_TIME:
777
0
            APPEND_DURATION(pos, connect_time);
778
0
            break;
779
780
0
        case ELEMENT_TYPE_REQUEST_ID:
781
0
            RESERVE(sizeof(H2O_UINT64_LONGEST_STR) - 1);
782
0
            pos += sprintf(pos, "%" PRIu64, req->conn->callbacks->get_req_id(req));
783
0
            break;
784
785
0
        case ELEMENT_TYPE_REQUEST_HEADER_TIME:
786
0
            APPEND_DURATION(pos, header_time);
787
0
            break;
788
789
0
        case ELEMENT_TYPE_REQUEST_BODY_TIME:
790
0
            APPEND_DURATION(pos, body_time);
791
0
            break;
792
793
0
        case ELEMENT_TYPE_REQUEST_TOTAL_TIME:
794
0
            APPEND_DURATION(pos, request_total_time);
795
0
            break;
796
797
0
        case ELEMENT_TYPE_PROCESS_TIME:
798
0
            APPEND_DURATION(pos, process_time);
799
0
            break;
800
801
0
        case ELEMENT_TYPE_RESPONSE_TIME:
802
0
            APPEND_DURATION(pos, response_time);
803
0
            break;
804
805
0
        case ELEMENT_TYPE_TOTAL_TIME:
806
0
            APPEND_DURATION(pos, total_time);
807
0
            break;
808
809
0
        case ELEMENT_TYPE_ERROR:
810
0
            if (req->error_logs != NULL)
811
0
                pos = append_unsafe_string(pos, req->error_logs->bytes, req->error_logs->size);
812
0
            break;
813
814
0
        case ELEMENT_TYPE_PROXY_IDLE_TIME:
815
0
            APPEND_DURATION(pos, proxy_idle_time);
816
0
            break;
817
818
0
        case ELEMENT_TYPE_PROXY_CONNECT_TIME:
819
0
            APPEND_DURATION(pos, proxy_connect_time);
820
0
            break;
821
822
0
        case ELEMENT_TYPE_PROXY_REQUEST_TIME:
823
0
            APPEND_DURATION(pos, proxy_request_time);
824
0
            break;
825
826
0
        case ELEMENT_TYPE_PROXY_PROCESS_TIME:
827
0
            APPEND_DURATION(pos, proxy_process_time);
828
0
            break;
829
830
0
        case ELEMENT_TYPE_PROXY_RESPONSE_TIME:
831
0
            APPEND_DURATION(pos, proxy_response_time);
832
0
            break;
833
834
0
        case ELEMENT_TYPE_PROXY_TOTAL_TIME:
835
0
            APPEND_DURATION(pos, proxy_total_time);
836
0
            break;
837
838
0
        case ELEMENT_TYPE_PROXY_REQUEST_BYTES:
839
0
            RESERVE(sizeof(H2O_UINT64_LONGEST_STR) - 1);
840
0
            pos += sprintf(pos, "%" PRIu64, req->proxy_stats.bytes_written.total);
841
0
            break;
842
843
0
        case ELEMENT_TYPE_PROXY_REQUEST_BYTES_HEADER:
844
0
            RESERVE(sizeof(H2O_UINT64_LONGEST_STR) - 1);
845
0
            pos += sprintf(pos, "%" PRIu64, req->proxy_stats.bytes_written.header);
846
0
            break;
847
848
0
        case ELEMENT_TYPE_PROXY_REQUEST_BYTES_BODY:
849
0
            RESERVE(sizeof(H2O_UINT64_LONGEST_STR) - 1);
850
0
            pos += sprintf(pos, "%" PRIu64, req->proxy_stats.bytes_written.body);
851
0
            break;
852
853
0
        case ELEMENT_TYPE_PROXY_RESPONSE_BYTES:
854
0
            RESERVE(sizeof(H2O_UINT64_LONGEST_STR) - 1);
855
0
            pos += sprintf(pos, "%" PRIu64, req->proxy_stats.bytes_read.total);
856
0
            break;
857
858
0
        case ELEMENT_TYPE_PROXY_RESPONSE_BYTES_HEADER:
859
0
            RESERVE(sizeof(H2O_UINT64_LONGEST_STR) - 1);
860
0
            pos += sprintf(pos, "%" PRIu64, req->proxy_stats.bytes_read.header);
861
0
            break;
862
863
0
        case ELEMENT_TYPE_PROXY_RESPONSE_BYTES_BODY:
864
0
            RESERVE(sizeof(H2O_UINT64_LONGEST_STR) - 1);
865
0
            pos += sprintf(pos, "%" PRIu64, req->proxy_stats.bytes_read.body);
866
0
            break;
867
0
        case ELEMENT_TYPE_PROXY_SSL_SESSION_REUSED:
868
0
            RESERVE(1);
869
0
            *pos++ = (req->proxy_stats.conn.ssl.session_reused) ? '1' : '0';
870
0
            break;
871
0
        case ELEMENT_TYPE_PROXY_SSL_CIPHER_BITS:
872
0
            if (req->proxy_stats.conn.ssl.cipher_bits == 0)
873
0
                goto EmitNull;
874
0
            RESERVE(sizeof(H2O_INT16_LONGEST_STR));
875
0
            pos += sprintf(pos, "%" PRIu16, (uint16_t)req->proxy_stats.conn.ssl.cipher_bits);
876
0
            break;
877
0
        case ELEMENT_TYPE_PROXY_SSL_PROTOCOL_VERSION:
878
0
            APPEND_SAFE_STRING(pos, req->proxy_stats.conn.ssl.protocol_version);
879
0
            break;
880
0
        case ELEMENT_TYPE_PROXY_SSL_CIPHER:
881
0
            APPEND_SAFE_STRING(pos, req->proxy_stats.conn.ssl.cipher);
882
0
            break;
883
0
        case ELEMENT_TYPE_PROTOCOL_SPECIFIC: {
884
0
            h2o_iovec_t (*cb)(h2o_req_t *) = req->conn->callbacks->log_.callbacks[element->data.protocol_specific_callback_index];
885
0
            if (cb == NULL)
886
0
                goto EmitNull;
887
0
            h2o_iovec_t s = cb(req);
888
0
            APPEND_SAFE_STRING_WITH_LEN(pos, s.base, s.len);
889
0
        } break;
890
891
0
        case ELEMENT_TYPE_LOGNAME:      /* %l */
892
0
        case ELEMENT_TYPE_EXTENDED_VAR: /* %{...}x */
893
0
        EmitNull:
894
0
            RESERVE(nullexpr.len);
895
            /* special case that trims surrounding quotes */
896
0
            if (element->magically_quoted_json) {
897
0
                --pos;
898
0
                pos = append_safe_string(pos, nullexpr.base, nullexpr.len);
899
0
                pos = append_safe_string(pos, element->suffix.base + 1, element->suffix.len - 1);
900
0
                continue;
901
0
            }
902
0
            pos = append_safe_string(pos, nullexpr.base, nullexpr.len);
903
0
            break;
904
905
0
        default:
906
0
            assert(!"unknown type");
907
0
            break;
908
0
        }
909
910
0
#undef RESERVE
911
912
0
        pos = append_safe_string(pos, element->suffix.base, element->suffix.len);
913
0
    }
914
915
0
    *len = pos - line;
916
0
    return line;
917
0
}