static void log_access(h2o_logger_t *_self, h2o_req_t *req) { struct st_h2o_access_logger_t *self = (struct st_h2o_access_logger_t*)_self; char *line, *pos, *line_end; size_t element_index; line = alloca(LOG_ALLOCA_SIZE); pos = line; line_end = line + LOG_ALLOCA_SIZE; for (element_index = 0; element_index != self->num_elements; ++element_index) { struct log_element_t *element = self->elements + element_index; /* reserve capacity + suffix.len */ #define RESERVE(capacity) \ do { \ if ((capacity) + element->suffix.len > line_end - pos) { \ size_t off = pos - line; \ line = expand_line_buf(line, line_end - line, off + (capacity) + element->suffix.len); \ pos = line + off; \ } \ } while (0) switch (element->type) { case ELEMENT_TYPE_EMPTY: RESERVE(0); break; case ELEMENT_TYPE_REMOTE_ADDR: if (req->conn->peername != NULL && req->conn->peername->sa_family == AF_INET) { uint32_t addr; RESERVE(sizeof("255.255.255.255") - 1); addr = htonl(((struct sockaddr_in*)req->conn->peername)->sin_addr.s_addr); pos += sprintf(pos, "%d.%d.%d.%d", addr >> 24, (addr >> 16) & 255, (addr >> 8) & 255, addr & 255); } else { RESERVE(1); *pos++ = '-'; } break; case ELEMENT_TYPE_LOGNAME: case ELEMENT_TYPE_REMOTE_USER: RESERVE(1); *pos++ = '-'; break; case ELEMENT_TYPE_TIMESTAMP: RESERVE(H2O_TIMESTR_LOG_LEN + 2); *pos++ = '['; pos = append_safe_string(pos, req->processed_at.str->log, H2O_TIMESTR_LOG_LEN); *pos++ = ']'; break; case ELEMENT_TYPE_REQUEST_LINE: RESERVE((req->method.len + req->path.len) * 4 + sizeof(" HTTP/1.2147483647") - 1); pos = append_unsafe_string(pos, req->method.base, req->method.len); *pos++ = ' '; pos = append_unsafe_string(pos, req->path.base, req->path.len); *pos++ = ' '; if (req->version < 0x200) { pos = append_safe_string(pos, H2O_STRLIT("HTTP/1.")); if ((req->version & 0xff) <= 9) { *pos++ = '0' + (req->version & 0xff); } else { pos += sprintf(pos, "%d", req->version); } } else { pos = append_safe_string(pos, H2O_STRLIT("HTTP/2")); } break; case ELEMENT_TYPE_STATUS: RESERVE(sizeof("2147483647") - 1); pos += sprintf(pos, "%d", req->res.status); break; case ELEMENT_TYPE_BYTES_SENT: RESERVE(sizeof("18446744073709551615") - 1); pos += sprintf(pos, "%llu", (unsigned long long)req->bytes_sent); break; default: assert(!"unknown type"); break; }
static void log_access(h2o_logger_t *_self, h2o_req_t *req) { struct st_h2o_access_logger_t *self = (struct st_h2o_access_logger_t *)_self; h2o_access_log_filehandle_t *fh = self->fh; char *line, *pos, *line_end; size_t element_index; /* note: LOG_ALLOCA_SIZE should be much greater than NI_MAXHOST to avoid unnecessary reallocations */ line = alloca(LOG_ALLOCA_SIZE); pos = line; line_end = line + LOG_ALLOCA_SIZE; for (element_index = 0; element_index != fh->num_elements; ++element_index) { struct log_element_t *element = fh->elements + element_index; /* reserve capacity + suffix.len */ #define RESERVE(capacity) \ do { \ if ((capacity) + element->suffix.len > line_end - pos) { \ size_t off = pos - line; \ line = expand_line_buf(line, line_end - line, off + (capacity) + element->suffix.len); \ pos = line + off; \ } \ } while (0) switch (element->type) { case ELEMENT_TYPE_EMPTY: RESERVE(0); break; case ELEMENT_TYPE_BYTES_SENT: /* %b */ RESERVE(sizeof("18446744073709551615") - 1); pos += sprintf(pos, "%llu", (unsigned long long)req->bytes_sent); break; case ELEMENT_TYPE_PROTOCOL: /* %H */ RESERVE(sizeof("HTTP/1.1")); pos += h2o_stringify_protocol_version(pos, req->version); break; case ELEMENT_TYPE_REMOTE_ADDR: /* %h */ { struct sockaddr_storage ss; socklen_t sslen; if ((sslen = req->conn->get_peername(req->conn, (void *)&ss)) != 0) { RESERVE(NI_MAXHOST); size_t l = h2o_socket_getnumerichost((void *)&ss, sslen, pos); if (l != SIZE_MAX) pos += l; else *pos++ = '-'; } else { RESERVE(1); *pos++ = '-'; } } break; case ELEMENT_TYPE_METHOD: /* %m */ RESERVE(req->input.method.len * 4); pos = append_unsafe_string(pos, req->input.method.base, req->input.method.len); break; case ELEMENT_TYPE_QUERY: /* %q */ if (req->input.query_at != SIZE_MAX) { size_t len = req->input.path.len - req->input.query_at; RESERVE(len * 4); pos = append_unsafe_string(pos, req->input.path.base + req->input.query_at, len); } break; case ELEMENT_TYPE_REQUEST_LINE: /* %r */ RESERVE((req->input.method.len + req->input.path.len) * 4 + sizeof(" HTTP/1.1")); pos = append_unsafe_string(pos, req->input.method.base, req->input.method.len); *pos++ = ' '; pos = append_unsafe_string(pos, req->input.path.base, req->input.path.len); *pos++ = ' '; pos += h2o_stringify_protocol_version(pos, req->version); break; case ELEMENT_TYPE_STATUS: /* %s */ RESERVE(sizeof("2147483647") - 1); pos += sprintf(pos, "%d", req->res.status); break; case ELEMENT_TYPE_TIMESTAMP: /* %t */ RESERVE(H2O_TIMESTR_LOG_LEN + 2); *pos++ = '['; pos = append_safe_string(pos, req->processed_at.str->log, H2O_TIMESTR_LOG_LEN); *pos++ = ']'; break; case ELEMENT_TYPE_URL_PATH: /* %U */ { size_t path_len = req->input.query_at == SIZE_MAX ? req->input.path.len : req->input.query_at; RESERVE(req->input.scheme->name.len + (sizeof("://") - 1) + (req->input.authority.len + path_len) * 4); pos = append_safe_string(pos, req->input.scheme->name.base, req->input.scheme->name.len); pos = append_safe_string(pos, H2O_STRLIT("://")); pos = append_unsafe_string(pos, req->input.authority.base, req->input.authority.len); pos = append_unsafe_string(pos, req->input.path.base, path_len); } break; case ELEMENT_TYPE_AUTHORITY: /* %V */ RESERVE(req->input.authority.len * 4); pos = append_unsafe_string(pos, req->input.authority.base, req->input.authority.len); break; case ELEMENT_TYPE_HOSTCONF: /* %v */ RESERVE(req->hostconf->authority.hostport.len * 4); pos = append_unsafe_string(pos, req->hostconf->authority.hostport.base, req->hostconf->authority.hostport.len); break; case ELEMENT_TYPE_LOGNAME: /* %l */ case ELEMENT_TYPE_REMOTE_USER: /* %u */ RESERVE(1); *pos++ = '-'; break; #define EMIT_HEADER(headers, _index) \ do { \ ssize_t index = (_index); \ if (index != -1) { \ const h2o_header_t *header = (headers)->entries + index; \ RESERVE(header->value.len * 4); \ pos = append_unsafe_string(pos, header->value.base, header->value.len); \ } else { \ RESERVE(1); \ *pos++ = '-'; \ } \ } while (0) case ELEMENT_TYPE_IN_HEADER_TOKEN: EMIT_HEADER(&req->headers, h2o_find_header(&req->headers, element->data.header_token, SIZE_MAX)); break; case ELEMENT_TYPE_IN_HEADER_STRING: EMIT_HEADER(&req->headers, h2o_find_header_by_str(&req->headers, element->data.header_string.base, element->data.header_string.len, SIZE_MAX)); break; case ELEMENT_TYPE_OUT_HEADER_TOKEN: EMIT_HEADER(&req->res.headers, h2o_find_header(&req->res.headers, element->data.header_token, SIZE_MAX)); break; case ELEMENT_TYPE_OUT_HEADER_STRING: EMIT_HEADER(&req->res.headers, h2o_find_header_by_str(&req->res.headers, element->data.header_string.base, element->data.header_string.len, SIZE_MAX)); break; #undef EMIT_HEADER default: assert(!"unknown type"); break; } #undef RESERVE pos = append_safe_string(pos, element->suffix.base, element->suffix.len); } write(fh->fd, line, pos - line); if (line_end - line != LOG_ALLOCA_SIZE) free(line); }
case ELEMENT_TYPE_STATUS: RESERVE(sizeof("2147483647") - 1); pos += sprintf(pos, "%d", req->res.status); break; case ELEMENT_TYPE_BYTES_SENT: RESERVE(sizeof("18446744073709551615") - 1); pos += sprintf(pos, "%llu", (unsigned long long)req->bytes_sent); break; default: assert(!"unknown type"); break; } #undef RESERVE pos = append_safe_string(pos, element->suffix.base, element->suffix.len); } write(self->fd, line, pos - line); if (line_end - line != LOG_ALLOCA_SIZE) free(line); } static void destroy(h2o_logger_t *_self) { struct st_h2o_access_logger_t *self = (void*)_self; size_t i; for (i = 0; i != self->num_elements; ++i) free(self->elements[i].suffix.base);