void h2o_set_header_by_str(h2o_mempool_t *pool, h2o_headers_t *headers, const char *name, size_t name_len, int maybe_token, const char *value, size_t value_len, int overwrite_if_exists) { ssize_t cursor; if (maybe_token) { const h2o_token_t *token = h2o_lookup_token(name, name_len); if (token != NULL) { h2o_set_header(pool, headers, token ,value, value_len, overwrite_if_exists); return; } } cursor = h2o_find_header_by_str(headers, name, name_len, -1); if (cursor != -1) { if (overwrite_if_exists) { h2o_buf_t *slot = &headers->entries[cursor].value; slot->base = (char*)value; slot->len = value_len; } } else { h2o_buf_t *name_buf = h2o_mempool_alloc(pool, sizeof(h2o_buf_t)); name_buf->base = (char*)name; name_buf->len = name_len; add_header(pool, headers, name_buf, value, value_len); } }
ssize_t h2o_set_header_by_str(h2o_mem_pool_t *pool, h2o_headers_t *headers, const char *name, size_t name_len, int maybe_token, const char *value, size_t value_len, int overwrite_if_exists) { ssize_t cursor; if (maybe_token) { const h2o_token_t *token = h2o_lookup_token(name, name_len); if (token != NULL) { return h2o_set_header(pool, headers, token, value, value_len, overwrite_if_exists); } } cursor = h2o_find_header_by_str(headers, name, name_len, -1); if (cursor != -1) { if (overwrite_if_exists) { h2o_iovec_t *slot = &headers->entries[cursor].value; slot->base = (char *)value; slot->len = value_len; } return cursor; } else { h2o_iovec_t *name_buf = h2o_mem_alloc_pool(pool, *name_buf, 1); name_buf->base = (char *)name; name_buf->len = name_len; return add_header(pool, headers, name_buf, NULL, value, value_len, (h2o_header_flags_t){0}); } }
static h2o_http1client_body_cb on_head(h2o_http1client_t *client, const char *errstr, int minor_version, int status, h2o_iovec_t msg, struct phr_header *headers, size_t num_headers) { struct rp_generator_t *self = client->data; size_t i; if (errstr != NULL && errstr != h2o_http1client_error_is_eos) { self->client = NULL; h2o_send_error(self->src_req, 502, "Gateway Error", errstr, 0); return NULL; } /* copy the response */ self->src_req->res.status = status; self->src_req->res.reason = h2o_strdup(&self->src_req->pool, msg.base, msg.len).base; for (i = 0; i != num_headers; ++i) { const h2o_token_t *token = h2o_lookup_token(headers[i].name, headers[i].name_len); h2o_iovec_t value; if (token != NULL) { if (token->is_connection_specific) { goto Skip; } if (token == H2O_TOKEN_CONTENT_LENGTH) { if (self->src_req->res.content_length != SIZE_MAX || (self->src_req->res.content_length = h2o_strtosize(headers[i].value, headers[i].value_len)) == SIZE_MAX) { self->client = NULL; h2o_send_error(self->src_req, 502, "Gateway Error", "invalid response from upstream", 0); return NULL; } goto Skip; } else if (token == H2O_TOKEN_LOCATION) { value = rewrite_location(&self->src_req->pool, headers[i].value, headers[i].value_len, self->upstream, self->src_req->scheme, self->src_req->authority, self->src_req->pathconf->path); goto AddHeader; } /* default behaviour, transfer the header downstream */ value = h2o_strdup(&self->src_req->pool, headers[i].value, headers[i].value_len); AddHeader: h2o_add_header(&self->src_req->pool, &self->src_req->res.headers, token, value.base, value.len); Skip: ; } else { h2o_iovec_t name = h2o_strdup(&self->src_req->pool, headers[i].name, headers[i].name_len); h2o_iovec_t value = h2o_strdup(&self->src_req->pool, headers[i].value, headers[i].value_len); h2o_add_header_by_str(&self->src_req->pool, &self->src_req->res.headers, name.base, name.len, 0, value.base, value.len); } } /* declare the start of the response */ h2o_start_response(self->src_req, &self->super); if (errstr == h2o_http1client_error_is_eos) { self->client = NULL; h2o_send(self->src_req, NULL, 0, 1); return NULL; } return on_body; }
static ssize_t init_headers(h2o_mem_pool_t *pool, h2o_headers_t *headers, const struct phr_header *src, size_t len, h2o_iovec_t *connection, h2o_iovec_t *host, h2o_iovec_t *upgrade, h2o_iovec_t *expect) { ssize_t entity_header_index = -1; assert(headers->size == 0); /* setup */ if (len != 0) { size_t i; h2o_vector_reserve(pool, headers, len); for (i = 0; i != len; ++i) { const h2o_token_t *name_token; char orig_case[src[i].name_len]; /* preserve the original case */ memcpy(orig_case, src[i].name, src[i].name_len); /* convert to lower-case in-place */ h2o_strtolower((char *)src[i].name, src[i].name_len); if ((name_token = h2o_lookup_token(src[i].name, src[i].name_len)) != NULL) { if (name_token->is_init_header_special) { if (name_token == H2O_TOKEN_HOST) { host->base = (char *)src[i].value; host->len = src[i].value_len; } else if (name_token == H2O_TOKEN_CONTENT_LENGTH) { if (entity_header_index == -1) entity_header_index = i; } else if (name_token == H2O_TOKEN_TRANSFER_ENCODING) { entity_header_index = i; } else if (name_token == H2O_TOKEN_EXPECT) { expect->base = (char *)src[i].value; expect->len = src[i].value_len; } else if (name_token == H2O_TOKEN_UPGRADE) { upgrade->base = (char *)src[i].value; upgrade->len = src[i].value_len; } else { assert(!"logic flaw"); } } else { h2o_add_header(pool, headers, name_token, orig_case, src[i].value, src[i].value_len); if (name_token == H2O_TOKEN_CONNECTION) *connection = headers->entries[headers->size - 1].value; } } else { h2o_add_header_by_str(pool, headers, src[i].name, src[i].name_len, 0, orig_case, src[i].value, src[i].value_len); } } } return entity_header_index; }
void h2o_add_header_by_str(h2o_mempool_t *pool, h2o_headers_t *headers, const char *name, size_t name_len, int maybe_token, const char *value, size_t value_len) { h2o_buf_t *name_buf; if (maybe_token) { const h2o_token_t *token = h2o_lookup_token(name, name_len); if (token != NULL) { add_header(pool, headers, (h2o_buf_t*)token, value, value_len); return; } } name_buf = h2o_mempool_alloc(pool, sizeof(h2o_buf_t)); name_buf->base = (char*)name; name_buf->len = name_len; add_header(pool, headers, name_buf, value, value_len); }
ssize_t h2o_add_header_by_str(h2o_mem_pool_t *pool, h2o_headers_t *headers, const char *name, size_t name_len, int maybe_token, const char *orig_name, const char *value, size_t value_len) { h2o_iovec_t *name_buf; if (maybe_token) { const h2o_token_t *token = h2o_lookup_token(name, name_len); if (token != NULL) { return add_header(pool, headers, (h2o_iovec_t *)token, orig_name, value, value_len, (h2o_header_flags_t){0}); } } name_buf = h2o_mem_alloc_pool(pool, *name_buf, 1); name_buf->base = (char *)name; name_buf->len = name_len; return add_header(pool, headers, name_buf, orig_name, value, value_len, (h2o_header_flags_t){0}); }
static int extract_name(const char *src, size_t len, h2o_iovec_t **_name) { h2o_iovec_t name; const h2o_token_t *name_token; name = h2o_str_stripws(src, len); if (name.len == 0) return -1; name = h2o_strdup(NULL, name.base, name.len); h2o_strtolower(name.base, name.len); if ((name_token = h2o_lookup_token(name.base, name.len)) != NULL) { *_name = (h2o_iovec_t *)&name_token->buf; free(name.base); } else { *_name = h2o_mem_alloc(sizeof(**_name)); **_name = name; } return 0; }
ssize_t h2o_init_headers(h2o_mempool_t *pool, h2o_headers_t *headers, const struct phr_header *src, size_t len, h2o_buf_t *connection, h2o_buf_t *host, h2o_buf_t *upgrade) { ssize_t entity_header_index = -1; assert(headers->size == 0); /* setup */ if (len != 0) { size_t i; h2o_vector_reserve(pool, (h2o_vector_t*)headers, sizeof(h2o_header_t), len); for (i = 0; i != len; ++i) { const h2o_token_t *name_token = h2o_lookup_token(src[i].name, src[i].name_len); if (name_token != NULL) { if (name_token == H2O_TOKEN_HOST) { host->base = (char*)src[i].value; host->len = src[i].value_len; } else if (name_token == H2O_TOKEN_UPGRADE) { upgrade->base = (char*)src[i].value; upgrade->len = src[i].value_len; } else if (name_token == H2O_TOKEN_CONTENT_LENGTH) { if (entity_header_index == -1) entity_header_index = i; } else if (name_token == H2O_TOKEN_TRANSFER_ENCODING) { entity_header_index = i; } else { h2o_header_t *added = add_header(pool, headers, (h2o_buf_t*)name_token, src[i].value, src[i].value_len); if (name_token == H2O_TOKEN_CONNECTION) *connection = added->value; } } else { h2o_add_header_by_str(pool, headers, src[i].name, src[i].name_len, 0, src[i].value, src[i].value_len); } } } return entity_header_index; }
static h2o_http1client_body_cb on_head(h2o_http1client_t *client, const char *errstr, int minor_version, int status, h2o_iovec_t msg, struct phr_header *headers, size_t num_headers) { struct rp_generator_t *self = client->data; h2o_req_t *req = self->src_req; size_t i; if (errstr != NULL && errstr != h2o_http1client_error_is_eos) { self->client = NULL; h2o_req_log_error(req, "lib/core/proxy.c", "%s", errstr); h2o_send_error(req, 502, "Gateway Error", errstr, 0); return NULL; } /* copy the response (note: all the headers must be copied; http1client discards the input once we return from this callback) */ req->res.status = status; req->res.reason = h2o_strdup(&req->pool, msg.base, msg.len).base; for (i = 0; i != num_headers; ++i) { const h2o_token_t *token = h2o_lookup_token(headers[i].name, headers[i].name_len); h2o_iovec_t value; if (token != NULL) { if (token->proxy_should_drop) { goto Skip; } if (token == H2O_TOKEN_CONTENT_LENGTH) { if (req->res.content_length != SIZE_MAX || (req->res.content_length = h2o_strtosize(headers[i].value, headers[i].value_len)) == SIZE_MAX) { self->client = NULL; h2o_req_log_error(req, "lib/core/proxy.c", "%s", "invalid response from upstream (malformed content-length)"); h2o_send_error(req, 502, "Gateway Error", "invalid response from upstream", 0); return NULL; } goto Skip; } else if (token == H2O_TOKEN_LOCATION) { if (req->res_is_delegated && (300 <= status && status <= 399) && status != 304) { self->client = NULL; h2o_iovec_t method = h2o_get_redirect_method(req->method, status); h2o_send_redirect_internal(req, method, headers[i].value, headers[i].value_len, 1); return NULL; } if (req->overrides != NULL && req->overrides->location_rewrite.match != NULL) { value = rewrite_location(&req->pool, headers[i].value, headers[i].value_len, req->overrides->location_rewrite.match, req->input.scheme, req->input.authority, req->overrides->location_rewrite.path_prefix); if (value.base != NULL) goto AddHeader; } goto AddHeaderDuped; } else if (token == H2O_TOKEN_LINK) { h2o_puth_path_in_link_header(req, headers[i].value, headers[i].value_len); } /* default behaviour, transfer the header downstream */ AddHeaderDuped: value = h2o_strdup(&req->pool, headers[i].value, headers[i].value_len); AddHeader: h2o_add_header(&req->pool, &req->res.headers, token, value.base, value.len); Skip: ; } else { h2o_iovec_t name = h2o_strdup(&req->pool, headers[i].name, headers[i].name_len); h2o_iovec_t value = h2o_strdup(&req->pool, headers[i].value, headers[i].value_len); h2o_add_header_by_str(&req->pool, &req->res.headers, name.base, name.len, 0, value.base, value.len); } } if (self->is_websocket_handshake && req->res.status == 101) { h2o_http1client_ctx_t *client_ctx = get_client_ctx(req); assert(client_ctx->websocket_timeout != NULL); h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_UPGRADE, H2O_STRLIT("websocket")); on_websocket_upgrade(self, client_ctx->websocket_timeout); self->client = NULL; return NULL; } /* declare the start of the response */ h2o_start_response(req, &self->super); if (errstr == h2o_http1client_error_is_eos) { self->client = NULL; h2o_send(req, NULL, 0, 1); return NULL; } return on_body; }
static struct log_element_t *compile_log_format(const char *fmt, size_t *_num_elements) { struct log_element_t *elements = NULL; size_t fmt_len = strlen(fmt), num_elements = 0; const char *pt = fmt; /* suffix buffer is always guaranteed to be larger than the fmt + (sizeof('\n') - 1) (so that they would be no buffer overruns) */ #define NEW_ELEMENT(ty) \ do { \ elements = h2o_mem_realloc(elements, sizeof(*elements) * (num_elements + 1)); \ elements[num_elements].type = ty; \ elements[num_elements].suffix = h2o_iovec_init(h2o_mem_alloc(fmt_len + 1), 0); \ ++num_elements; \ } while (0) while (*pt != '\0') { if (*pt == '%') { ++pt; if (*pt == '%') { /* skip */ } else if (*pt == '{') { const h2o_token_t *token; const char *quote_end = strchr(++pt, '}'); if (quote_end == NULL) { fprintf(stderr, "failed to compile log format: unterminated header name starting at: \"%16s\"\n", pt); goto Error; } h2o_iovec_t name = strdup_lowercased(pt, quote_end - pt); token = h2o_lookup_token(name.base, name.len); switch (quote_end[1]) { case 'i': if (token != NULL) { free(name.base); NEW_ELEMENT(ELEMENT_TYPE_IN_HEADER_TOKEN); elements[num_elements - 1].data.header_token = token; } else { NEW_ELEMENT(ELEMENT_TYPE_IN_HEADER_STRING); elements[num_elements - 1].data.header_string = name; } break; case 'o': if (token != NULL) { free(name.base); NEW_ELEMENT(ELEMENT_TYPE_OUT_HEADER_TOKEN); elements[num_elements - 1].data.header_token = token; } else { NEW_ELEMENT(ELEMENT_TYPE_OUT_HEADER_STRING); elements[num_elements - 1].data.header_string = name; } break; default: free(name.base); fprintf(stderr, "failed to compile log format: header name is not followed by either `i` or `o`\n"); goto Error; } pt = quote_end + 2; continue; } else { unsigned type = NUM_ELEMENT_TYPES; switch (*pt++) { #define TYPE_MAP(ch, ty) \ case ch: \ type = ty; \ break TYPE_MAP('b', ELEMENT_TYPE_BYTES_SENT); TYPE_MAP('H', ELEMENT_TYPE_PROTOCOL); TYPE_MAP('h', ELEMENT_TYPE_REMOTE_ADDR); TYPE_MAP('l', ELEMENT_TYPE_LOGNAME); TYPE_MAP('m', ELEMENT_TYPE_METHOD); TYPE_MAP('q', ELEMENT_TYPE_QUERY); TYPE_MAP('r', ELEMENT_TYPE_REQUEST_LINE); TYPE_MAP('s', ELEMENT_TYPE_STATUS); TYPE_MAP('t', ELEMENT_TYPE_TIMESTAMP); TYPE_MAP('U', ELEMENT_TYPE_URL_PATH); TYPE_MAP('u', ELEMENT_TYPE_REMOTE_USER); TYPE_MAP('V', ELEMENT_TYPE_AUTHORITY); TYPE_MAP('v', ELEMENT_TYPE_HOSTCONF); #undef TYPE_MAP default: fprintf(stderr, "failed to compile log format: unknown escape sequence: %%%c\n", pt[-1]); goto Error; } NEW_ELEMENT(type); continue; } } /* emit current char */ if (elements == NULL) NEW_ELEMENT(ELEMENT_TYPE_EMPTY); elements[num_elements - 1].suffix.base[elements[num_elements - 1].suffix.len++] = *pt++; } /* emit end-of-line */ if (elements == NULL) NEW_ELEMENT(ELEMENT_TYPE_EMPTY); elements[num_elements - 1].suffix.base[elements[num_elements - 1].suffix.len++] = '\n'; #undef NEW_ELEMENT *_num_elements = num_elements; return elements; Error: free(elements); return NULL; }
static void on_head(h2o_socket_t *sock, const char *err) { struct st_h2o_http1client_t *client = sock->data; int minor_version, version, http_status, rlen, is_eos; const char *msg; #define MAX_HEADERS 100 h2o_header_t *headers; h2o_iovec_t *header_names; size_t msg_len, num_headers, i; h2o_socket_cb reader; h2o_timer_unlink(&client->super._timeout); if (err != NULL) { on_error_before_head(client, "I/O error (head)"); return; } headers = h2o_mem_alloc_pool(client->super.pool, *headers, MAX_HEADERS); header_names = h2o_mem_alloc_pool(client->super.pool, *header_names, MAX_HEADERS); /* continue parsing the responses until we see a final one */ while (1) { /* parse response */ struct phr_header src_headers[MAX_HEADERS]; num_headers = MAX_HEADERS; rlen = phr_parse_response(sock->input->bytes, sock->input->size, &minor_version, &http_status, &msg, &msg_len, src_headers, &num_headers, 0); switch (rlen) { case -1: /* error */ on_error_before_head(client, "failed to parse the response"); return; case -2: /* incomplete */ h2o_timer_link(client->super.ctx->loop, client->super.ctx->io_timeout, &client->super._timeout); return; } version = 0x100 | (minor_version != 0); /* fill-in the headers */ for (i = 0; i != num_headers; ++i) { const h2o_token_t *token; char *orig_name = h2o_strdup(client->super.pool, src_headers[i].name, src_headers[i].name_len).base; h2o_strtolower((char *)src_headers[i].name, src_headers[i].name_len); token = h2o_lookup_token(src_headers[i].name, src_headers[i].name_len); if (token != NULL) { headers[i].name = (h2o_iovec_t *)&token->buf; } else { header_names[i] = h2o_iovec_init(src_headers[i].name, src_headers[i].name_len); headers[i].name = &header_names[i]; } headers[i].value = h2o_iovec_init(src_headers[i].value, src_headers[i].value_len); headers[i].orig_name = orig_name; headers[i].flags = (h2o_header_flags_t){0}; } if (!(100 <= http_status && http_status <= 199 && http_status != 101)) break; if (client->super.informational_cb != NULL && client->super.informational_cb(&client->super, version, http_status, h2o_iovec_init(msg, msg_len), headers, num_headers) != 0) { close_client(client); return; } h2o_buffer_consume(&client->sock->input, rlen); if (client->sock->input->size == 0) { h2o_timer_link(client->super.ctx->loop, client->super.ctx->io_timeout, &client->super._timeout); return; } } client->super.timings.response_start_at = h2o_gettimeofday(client->super.ctx->loop); /* parse the headers */ reader = on_body_until_close; client->_do_keepalive = minor_version >= 1; for (i = 0; i != num_headers; ++i) { if (headers[i].name == &H2O_TOKEN_CONNECTION->buf) { if (h2o_contains_token(headers[i].value.base, headers[i].value.len, H2O_STRLIT("keep-alive"), ',')) { client->_do_keepalive = 1; } else { client->_do_keepalive = 0; } } else if (headers[i].name == &H2O_TOKEN_TRANSFER_ENCODING->buf) { if (h2o_memis(headers[i].value.base, headers[i].value.len, H2O_STRLIT("chunked"))) { /* precond: _body_decoder.chunked is zero-filled */ client->_body_decoder.chunked.decoder.consume_trailer = 1; reader = on_req_chunked; } else if (h2o_memis(headers[i].value.base, headers[i].value.len, H2O_STRLIT("identity"))) { /* continue */ } else { on_error_before_head(client, "unexpected type of transfer-encoding"); return; } } else if (headers[i].name == &H2O_TOKEN_CONTENT_LENGTH->buf) { if ((client->_body_decoder.content_length.bytesleft = h2o_strtosize(headers[i].value.base, headers[i].value.len)) == SIZE_MAX) { on_error_before_head(client, "invalid content-length"); return; } if (reader != on_req_chunked) reader = on_body_content_length; } } /* RFC 2616 4.4 */ if (client->_method_is_head || http_status == 101 || http_status == 204 || http_status == 304) { is_eos = 1; client->super.timings.response_end_at = h2o_gettimeofday(client->super.ctx->loop); } else { is_eos = 0; /* close the connection if impossible to determine the end of the response (RFC 7230 3.3.3) */ if (reader == on_body_until_close) client->_do_keepalive = 0; } /* call the callback. sock may be stealed */ client->bytes_to_consume = rlen; client->super._cb.on_body = client->super._cb.on_head(&client->super, is_eos ? h2o_httpclient_error_is_eos : NULL, version, http_status, h2o_iovec_init(msg, msg_len), headers, num_headers, 1); if (is_eos) { close_client(client); return; } else if (client->super._cb.on_body == NULL) { client->_do_keepalive = 0; close_client(client); return; } h2o_buffer_consume(&sock->input, client->bytes_to_consume); client->bytes_to_consume = 0; client->sock->bytes_read = client->sock->input->size; client->super._timeout.cb = on_body_timeout; h2o_socket_read_start(sock, reader); reader(client->sock, 0); #undef MAX_HEADERS }