static ssize_t fixup_request(struct st_h2o_http1_conn_t *conn, struct phr_header *headers, size_t num_headers, int minor_version, h2o_iovec_t *expect) { ssize_t entity_header_index; h2o_iovec_t connection = {NULL, 0}, host = {NULL, 0}, upgrade = {NULL, 0}; expect->base = NULL; expect->len = 0; conn->req.input.scheme = conn->sock->ssl != NULL ? &H2O_URL_SCHEME_HTTPS : &H2O_URL_SCHEME_HTTP; conn->req.version = 0x100 | (minor_version != 0); /* init headers */ entity_header_index = init_headers(&conn->req.pool, &conn->req.headers, headers, num_headers, &connection, &host, &upgrade, expect); /* copy the values to pool, since the buffer pointed by the headers may get realloced */ if (entity_header_index != -1) { size_t i; conn->req.input.method = h2o_strdup(&conn->req.pool, conn->req.input.method.base, conn->req.input.method.len); conn->req.input.path = h2o_strdup(&conn->req.pool, conn->req.input.path.base, conn->req.input.path.len); for (i = 0; i != conn->req.headers.size; ++i) { h2o_header_t *header = conn->req.headers.entries + i; if (!h2o_iovec_is_token(header->name)) { *header->name = h2o_strdup(&conn->req.pool, header->name->base, header->name->len); } header->value = h2o_strdup(&conn->req.pool, header->value.base, header->value.len); } if (host.base != NULL) host = h2o_strdup(&conn->req.pool, host.base, host.len); if (upgrade.base != NULL) upgrade = h2o_strdup(&conn->req.pool, upgrade.base, upgrade.len); } /* move host header to req->authority */ if (host.base != NULL) conn->req.input.authority = host; /* setup persistent flag (and upgrade info) */ if (connection.base != NULL) { /* TODO contains_token function can be faster */ if (h2o_contains_token(connection.base, connection.len, H2O_STRLIT("keep-alive"), ',')) { conn->req.http1_is_persistent = 1; } if (upgrade.base != NULL && h2o_contains_token(connection.base, connection.len, H2O_STRLIT("upgrade"), ',')) { conn->req.upgrade = upgrade; } } else if (conn->req.version >= 0x101) { /* defaults to keep-alive if >= HTTP/1.1 */ conn->req.http1_is_persistent = 1; } /* disable keep-alive if shutdown is requested */ if (conn->req.http1_is_persistent && conn->super.ctx->shutdown_requested) conn->req.http1_is_persistent = 0; return entity_header_index; }
void h2o_rewrite_headers(h2o_mem_pool_t *pool, h2o_headers_t *headers, h2o_headers_command_t *cmd) { h2o_header_t *target; switch (cmd->cmd) { case H2O_HEADERS_CMD_ADD: goto AddHeader; case H2O_HEADERS_CMD_APPEND: if ((target = find_header(headers, cmd)) == NULL) goto AddHeader; goto AppendToken; case H2O_HEADERS_CMD_MERGE: if ((target = find_header(headers, cmd)) == NULL) goto AddHeader; if (h2o_contains_token(target->value.base, target->value.len, cmd->value.base, cmd->value.len, ',')) return; goto AppendToken; case H2O_HEADERS_CMD_SET: remove_header(headers, cmd); goto AddHeader; case H2O_HEADERS_CMD_SETIFEMPTY: if (find_header(headers, cmd) != NULL) return; goto AddHeader; case H2O_HEADERS_CMD_UNSET: remove_header(headers, cmd); return; } assert(!"FIXME"); return; AddHeader: if (h2o_iovec_is_token(cmd->name)) { h2o_add_header(pool, headers, (void *)cmd->name, NULL, cmd->value.base, cmd->value.len); } else { h2o_add_header_by_str(pool, headers, cmd->name->base, cmd->name->len, 0, NULL, cmd->value.base, cmd->value.len); } return; AppendToken: if (target->value.len != 0) { h2o_iovec_t v; v.len = target->value.len + 2 + cmd->value.len; v.base = h2o_mem_alloc_pool(pool, char, v.len); memcpy(v.base, target->value.base, target->value.len); v.base[target->value.len] = ','; v.base[target->value.len + 1] = ' '; memcpy(v.base + target->value.len + 2, cmd->value.base, cmd->value.len); target->value = v; } else {
ssize_t h2o_set_header_token(h2o_mem_pool_t *pool, h2o_headers_t *headers, const h2o_token_t *token, const char *value, size_t value_len) { size_t found = -1; size_t i; for (i = 0; i != headers->size; ++i) { if (headers->entries[i].name == &token->buf) { if (h2o_contains_token(headers->entries[i].value.base, headers->entries[i].value.len, value, value_len, ',')) return -1; found = i; } } if (found != -1) { h2o_header_t *dest = headers->entries + found; dest->value = h2o_concat(pool, dest->value, h2o_iovec_init(H2O_STRLIT(", ")), h2o_iovec_init(value, value_len)); return found; } else { return h2o_add_header(pool, headers, token, NULL, value, value_len); } }
static void on_setup_ostream(h2o_filter_t *self, h2o_req_t *req, h2o_ostream_t **slot) { gzip_encoder_t *encoder; ssize_t i; if (req->version < 0x101) goto Next; if (req->res.status != 200) goto Next; if (h2o_memis(req->input.method.base, req->input.method.len, H2O_STRLIT("HEAD"))) goto Next; if (req->res.mime_attr == NULL) h2o_req_fill_mime_attributes(req); if (!req->res.mime_attr->is_compressible) goto Next; /* 100 is a rough estimate */ if (req->res.content_length <= 100) goto Next; /* skip if no accept-encoding is set */ if ((i = h2o_find_header(&req->headers, H2O_TOKEN_ACCEPT_ENCODING, -1)) == -1) goto Next; if (!h2o_contains_token(req->headers.entries[i].value.base, req->headers.entries[i].value.len, H2O_STRLIT("gzip"), ',')) goto Next; /* skip if content-encoding header is being set (as well as obtain the location of accept-ranges */ size_t content_encoding_header_index = -1, accept_ranges_header_index = -1; for (i = 0; i != req->res.headers.size; ++i) { if (req->res.headers.entries[i].name == &H2O_TOKEN_CONTENT_ENCODING->buf) content_encoding_header_index = i; else if (req->res.headers.entries[i].name == &H2O_TOKEN_ACCEPT_RANGES->buf) accept_ranges_header_index = i; else continue; } if (content_encoding_header_index != -1) goto Next; /* adjust the response headers */ req->res.content_length = SIZE_MAX; h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_ENCODING, H2O_STRLIT("gzip")); h2o_add_header_token(&req->pool, &req->res.headers, H2O_TOKEN_VARY, H2O_STRLIT("accept-encoding")); if (accept_ranges_header_index != -1) { req->res.headers.entries[accept_ranges_header_index].value = h2o_iovec_init(H2O_STRLIT("none")); } else { h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_ACCEPT_RANGES, H2O_STRLIT("none")); } /* setup filter */ encoder = (void *)h2o_add_ostream(req, sizeof(gzip_encoder_t), slot); encoder->super.do_send = send_gzip; encoder->super.stop = stop_gzip; slot = &encoder->super.next; encoder->bufs.capacity = 0; encoder->bufs.size = 0; encoder->zstream.zalloc = gzip_encoder_alloc; encoder->zstream.zfree = gzip_encoder_free; encoder->zstream.opaque = encoder; /* adjust preferred chunk size (compress by 8192 bytes) */ if (req->preferred_chunk_size > BUF_SIZE) req->preferred_chunk_size = BUF_SIZE; Next: h2o_setup_next_ostream(req, slot); }
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 }