static void do_send_file(struct st_h2o_sendfile_generator_t *self, h2o_req_t *req, int status, const char *reason, h2o_buf_t mime_type) { /* link the request */ self->req = req; /* setup response */ req->res.status = status; req->res.reason = reason; req->res.content_length = self->bytesleft; h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, mime_type.base, mime_type.len); h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_LAST_MODIFIED, self->last_modified_buf, H2O_TIMESTR_RFC1123_LEN); h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_ETAG, self->etag_buf, self->etag_len); /* send data */ h2o_start_response(req, &self->super); if (req->_ostr_top->start_pull != NULL) { req->_ostr_top->start_pull(req->_ostr_top, do_pull); } else { size_t bufsz = MAX_BUF_SIZE; if (self->bytesleft < bufsz) bufsz = self->bytesleft; self->buf = h2o_mempool_alloc(&req->pool, bufsz); do_proceed(&self->super, req); } }
h2o_websocket_conn_t *h2o_upgrade_to_websocket(h2o_req_t *req, const char *client_key, void *data, h2o_websocket_msg_callback cb) { h2o_websocket_conn_t *conn = h2o_mem_alloc(sizeof(*conn)); char accept_key[29]; /* only for http1 connection */ assert(req->version < 0x200); /* setup the context */ memset(conn, 0, sizeof(*conn)); // conn->sock = sock; set by on_complete conn->ws_callbacks.recv_callback = recv_callback; conn->ws_callbacks.send_callback = send_callback; conn->ws_callbacks.on_msg_recv_callback = on_msg_callback; conn->data = data; conn->cb = cb; wslay_event_context_server_init(&conn->ws_ctx, &conn->ws_callbacks, conn); /* build response */ create_accept_key(accept_key, client_key); req->res.status = 101; req->res.reason = "Switching Protocols"; h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_UPGRADE, NULL, H2O_STRLIT("websocket")); h2o_add_header_by_str(&req->pool, &req->res.headers, H2O_STRLIT("sec-websocket-accept"), 0, NULL, accept_key, strlen(accept_key)); /* send */ h2o_http1_upgrade(req, NULL, 0, on_complete, conn); return conn; }
static int send_dir_listing(h2o_req_t *req, const char *path, size_t path_len, int is_get) { static h2o_generator_t generator = {NULL, NULL}; DIR *dp; h2o_buffer_t *body; h2o_iovec_t bodyvec; /* build html */ if ((dp = opendir(path)) == NULL) return -1; body = build_dir_listing_html(&req->pool, req->path_normalized, dp); closedir(dp); bodyvec = h2o_iovec_init(body->bytes, body->size); h2o_buffer_link_to_pool(body, &req->pool); /* send response */ req->res.status = 200; req->res.reason = "OK"; h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, H2O_STRLIT("text/html; charset=utf-8")); /* send headers */ if (!is_get) { h2o_send_inline(req, NULL, 0); return 0; } /* send data */ h2o_start_response(req, &generator); h2o_send(req, &bodyvec, 1, 1); return 0; }
static void on_setup_ostream(h2o_filter_t *self, h2o_req_t *req, h2o_ostream_t **slot) { chunked_encoder_t *encoder; /* do nothing if content-length is known */ if (req->res.content_length != SIZE_MAX) goto Next; /* RFC 2616 4.4 states that the following status codes (and response to a HEAD method) should not include message body */ if ((100 <= req->res.status && req->res.status <= 199) || req->res.status == 204 || req->res.status == 304) goto Next; else if (h2o_memis(req->method.base, req->method.len, H2O_STRLIT("HEAD")) == 0) goto Next; /* we cannot handle certain responses (like 101 switching protocols) */ if (req->res.status != 200) { req->http1_is_persistent = 0; goto Next; } /* skip if content-encoding header is being set */ if (h2o_find_header(&req->res.headers, H2O_TOKEN_TRANSFER_ENCODING, -1) != -1) goto Next; /* set content-encoding header */ h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_TRANSFER_ENCODING, H2O_STRLIT("chunked")); /* setup filter */ encoder = (void*)h2o_add_ostream(req, sizeof(chunked_encoder_t), slot); encoder->super.do_send = send_chunk; slot = &encoder->super.next; Next: h2o_setup_next_ostream(self, req, slot); }
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 int reproxy_test(h2o_handler_t *self, h2o_req_t *req) { if (!h2o_memis(req->method.base, req->method.len, H2O_STRLIT("GET"))) return -1; req->res.status = 200; req->res.reason = "OK"; h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_X_REPROXY_URL, H2O_STRLIT("http://example.com:81/bar")); h2o_send_inline(req, H2O_STRLIT("you should never see this!\n")); return 0; }
void h2o_send_error(h2o_req_t *req, int status, const char *reason, const char *body) { /* FIXME setup host_config, since this function may get called before h2o_process_request is invoked */ req->http1_is_persistent = 0; req->res.status = status; req->res.reason = reason; memset(&req->res.headers, 0, sizeof(req->res.headers)); h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, H2O_STRLIT("text/plain; charset=utf-8")); h2o_send_inline(req, body, SIZE_MAX); }
static int redirect_to_dir(h2o_req_t *req, const char *path, size_t path_len) { static h2o_generator_t generator = { NULL, NULL }; static const h2o_buf_t body_prefix = { H2O_STRLIT("<!DOCTYPE html><TITLE>301 Moved Permanently</TITLE><P>The document has moved <A HREF=\"") }; static const h2o_buf_t body_suffix = { H2O_STRLIT("\">here</A>") }; h2o_buf_t url; size_t alloc_size; h2o_buf_t bufs[3]; /* determine the size of the memory needed */ alloc_size = sizeof(":///") + req->scheme.len + req->authority.len + path_len; /* allocate and build url */ url.base = h2o_mempool_alloc(&req->pool, alloc_size); url.len = sprintf(url.base, "%.*s://%.*s%.*s/", (int)req->scheme.len, req->scheme.base, (int)req->authority.len, req->authority.base, (int)path_len, path); assert(url.len + 1 == alloc_size); /* build response header */ req->res.status = 301; req->res.reason = "Moved Permanently"; memset(&req->res.headers, 0, sizeof(req->res.headers)); h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_LOCATION, url.base, url.len); h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, H2O_STRLIT("text/html; charset=utf-8")); /* build response */ bufs[0] = body_prefix; bufs[1] = h2o_htmlescape(&req->pool, url.base, url.len); bufs[2] = body_suffix; /* send */ h2o_start_response(req, &generator); h2o_send(req, bufs, 3, 1); return 0; }
static void add_headers_unconditional(struct st_h2o_sendfile_generator_t *self, h2o_req_t *req) { /* RFC 7232 4.1: The server generating a 304 response MUST generate any of the following header fields that would have been sent * in a 200 (OK) response to the same request: Cache-Control, Content-Location, Date, ETag, Expires, and Vary (snip) a sender * SHOULD NOT generate representation metadata other than the above listed fields unless said metadata exists for the purpose of * guiding cache updates. */ if (self->send_etag) { size_t etag_len = h2o_filecache_get_etag(self->file.ref, self->header_bufs.etag); h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_ETAG, self->header_bufs.etag, etag_len); } if (self->send_vary) h2o_set_header_token(&req->pool, &req->res.headers, H2O_TOKEN_VARY, H2O_STRLIT("accept-encoding")); }
void h2o_set_header(h2o_mempool_t *pool, h2o_headers_t *headers, const h2o_token_t *token, const char *value, size_t value_len, int overwrite_if_exists) { ssize_t cursor = h2o_find_header(headers, token, -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_add_header(pool, headers, token, value, value_len); } }
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_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 {
void h2o_send_error(h2o_req_t *req, int status, const char *reason, const char *body, int flags) { bind_conf(req); if ((flags & H2O_SEND_ERROR_HTTP1_CLOSE_CONNECTION) != 0) req->http1_is_persistent = 0; req->res.status = status; req->res.reason = reason; req->res.content_length = strlen(body); memset(&req->res.headers, 0, sizeof(req->res.headers)); h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, H2O_STRLIT("text/plain; charset=utf-8")); h2o_send_inline(req, body, SIZE_MAX); }
static int post_test(h2o_handler_t *self, h2o_req_t *req) { if (h2o_memis(req->method.base, req->method.len, H2O_STRLIT("POST")) && h2o_memis(req->path.base, req->path.len, H2O_STRLIT("/post-test"))) { static h2o_generator_t generator = { NULL, NULL }; req->res.status = 200; req->res.reason = "OK"; h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, H2O_STRLIT("text/plain; charset=utf-8")); h2o_start_response(req, &generator); h2o_send(req, &req->entity, 1, 1); return 0; } return -1; }
static int chunked_test(h2o_handler_t *self, h2o_req_t *req) { if (h2o_memis(req->method.base, req->method.len, H2O_STRLIT("GET")) && h2o_memis(req->path.base, req->path.len, H2O_STRLIT("/chunked-test"))) { static h2o_generator_t generator = { NULL, NULL }; h2o_buf_t body = h2o_strdup(&req->pool, "hello world\n", SIZE_MAX); req->res.status = 200; req->res.reason = "OK"; h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, H2O_STRLIT("text/plain")); h2o_start_response(req, &generator); h2o_send(req, &body, 1, 1); return 0; } return -1; }
static mrb_value h2o_mrb_req_send_file(mrb_state *mrb, mrb_value self) { h2o_mruby_internal_context_t *mruby_ctx = (h2o_mruby_internal_context_t *)mrb->ud; char *fn; int status; h2o_iovec_t content_type; int content_type_header_removed = 0; if (mruby_ctx->state != H2O_MRUBY_STATE_UNDETERMINED) mrb_raise(mrb, E_RUNTIME_ERROR, "response already sent"); mrb_get_args(mrb, "z", &fn); /* determine status and reason to be used */ if ((status = mruby_ctx->req->res.status) == 0) status = 200; { /* determine content-type (removing existing header, since it is added by h2o_file_send) */ ssize_t header_index; if ((header_index = h2o_find_header(&mruby_ctx->req->res.headers, H2O_TOKEN_CONTENT_TYPE, -1)) != -1) { content_type = mruby_ctx->req->res.headers.entries[header_index].value; h2o_delete_header(&mruby_ctx->req->res.headers, header_index); content_type_header_removed = 1; } else { const char *ext = h2o_get_filext(fn, strlen(fn)); h2o_mimemap_type_t *m = h2o_mimemap_get_type_by_extension(mruby_ctx->req->pathconf->mimemap, ext); if (m == NULL || m->type != H2O_MIMEMAP_TYPE_MIMETYPE) { m = h2o_mimemap_get_default_type(mruby_ctx->req->pathconf->mimemap); assert(m->type == H2O_MIMEMAP_TYPE_MIMETYPE); } content_type = m->data.mimetype; } } if (h2o_file_send(mruby_ctx->req, status, mruby_ctx->req->res.reason, fn, content_type, H2O_FILE_FLAG_SEND_GZIP) == 0) { /* succeeded, return true */ mruby_ctx->state = H2O_MRUBY_STATE_RESPONSE_SENT; return mrb_true_value(); } else { /* failed, restore content-type header and return false */ if (content_type_header_removed) h2o_add_header(&mruby_ctx->req->pool, &mruby_ctx->req->res.headers, H2O_TOKEN_CONTENT_TYPE, content_type.base, content_type.len); return mrb_false_value(); } }
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) { struct st_compress_filter_t *self = (void *)_self; struct st_compress_encoder_t *encoder; int compressible_types; h2o_compress_context_t *compressor; 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; if (req->res.content_length < self->args.min_size) goto Next; /* skip if failed to gather the list of compressible types */ if ((compressible_types = h2o_get_compressible_types(&req->headers)) == 0) 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; /* open the compressor */ #ifndef _MSC_VER #else #define H2O_USE_BROTLI 0 #endif #if H2O_USE_BROTLI if (self->args.brotli.quality != -1 && (compressible_types & H2O_COMPRESSIBLE_BROTLI) != 0) { compressor = h2o_compress_brotli_open(&req->pool, self->args.brotli.quality, req->res.content_length); } else #endif if (self->args.gzip.quality != -1 && (compressible_types & H2O_COMPRESSIBLE_GZIP) != 0) { compressor = h2o_compress_gzip_open(&req->pool, self->args.gzip.quality); } else { goto Next; } /* adjust the response headers */ req->res.content_length = SIZE_MAX; h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_ENCODING, compressor->name.base, compressor->name.len); h2o_set_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(*encoder), slot); encoder->super.do_send = do_send; slot = &encoder->super.next; encoder->compressor = compressor; /* 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_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 int serve_with_generator(struct st_h2o_sendfile_generator_t *generator, h2o_req_t *req, const char *rpath, size_t rpath_len, h2o_mimemap_type_t *mime_type) { enum { METHOD_IS_GET, METHOD_IS_HEAD, METHOD_IS_OTHER } method_type; size_t if_modified_since_header_index, if_none_match_header_index; size_t range_header_index; /* determine the method */ if (h2o_memis(req->method.base, req->method.len, H2O_STRLIT("GET"))) { method_type = METHOD_IS_GET; } else if (h2o_memis(req->method.base, req->method.len, H2O_STRLIT("HEAD"))) { method_type = METHOD_IS_HEAD; } else { method_type = METHOD_IS_OTHER; } /* if-non-match and if-modified-since */ if ((if_none_match_header_index = h2o_find_header(&req->headers, H2O_TOKEN_IF_NONE_MATCH, SIZE_MAX)) != -1) { h2o_iovec_t *if_none_match = &req->headers.entries[if_none_match_header_index].value; char etag[H2O_FILECACHE_ETAG_MAXLEN + 1]; size_t etag_len = h2o_filecache_get_etag(generator->file.ref, etag); if (h2o_memis(if_none_match->base, if_none_match->len, etag, etag_len)) goto NotModified; } else if ((if_modified_since_header_index = h2o_find_header(&req->headers, H2O_TOKEN_IF_MODIFIED_SINCE, SIZE_MAX)) != -1) { h2o_iovec_t *ims_vec = &req->headers.entries[if_modified_since_header_index].value; struct tm ims_tm, *last_modified_tm; if (h2o_time_parse_rfc1123(ims_vec->base, ims_vec->len, &ims_tm) == 0) { last_modified_tm = h2o_filecache_get_last_modified(generator->file.ref, NULL); if (!tm_is_lessthan(&ims_tm, last_modified_tm)) goto NotModified; } } /* obtain mime type */ if (mime_type->type == H2O_MIMEMAP_TYPE_DYNAMIC) { do_close(&generator->super, req); return delegate_dynamic_request(req, req->path_normalized.len, rpath, rpath_len, mime_type); } assert(mime_type->type == H2O_MIMEMAP_TYPE_MIMETYPE); /* only allow GET or POST for static files */ if (method_type == METHOD_IS_OTHER) { do_close(&generator->super, req); send_method_not_allowed(req); return 0; } /* if-range */ if ((range_header_index = h2o_find_header(&req->headers, H2O_TOKEN_RANGE, SIZE_MAX)) != -1) { h2o_iovec_t *range = &req->headers.entries[range_header_index].value; size_t *range_infos, range_count; range_infos = process_range(&req->pool, range, generator->bytesleft, &range_count); if (range_infos == NULL) { h2o_iovec_t content_range; content_range.base = h2o_mem_alloc_pool(&req->pool, 32); content_range.len = sprintf(content_range.base, "bytes */%zu", generator->bytesleft); h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_RANGE, content_range.base, content_range.len); h2o_send_error(req, 416, "Request Range Not Satisfiable", "requested range not satisfiable", H2O_SEND_ERROR_KEEP_HEADERS); goto Close; } generator->ranged.range_count = range_count; generator->ranged.range_infos = range_infos; generator->ranged.current_range = 0; generator->ranged.filesize = generator->bytesleft; /* set content-length according to range */ if (range_count == 1) generator->bytesleft = range_infos[1]; else { generator->ranged.mimetype = h2o_strdup(&req->pool, mime_type->data.mimetype.base, mime_type->data.mimetype.len); size_t final_content_len = 0, size_tmp = 0, size_fixed_each_part, i; generator->ranged.boundary.base = h2o_mem_alloc_pool(&req->pool, BOUNDARY_SIZE + 1); generator->ranged.boundary.len = BOUNDARY_SIZE; gen_rand_string(&generator->ranged.boundary); i = generator->bytesleft; while (i) { i /= 10; size_tmp++; } size_fixed_each_part = FIXED_PART_SIZE + mime_type->data.mimetype.len + size_tmp; for (i = 0; i < range_count; i++) { size_tmp = *range_infos++; if (size_tmp == 0) final_content_len++; while (size_tmp) { size_tmp /= 10; final_content_len++; } size_tmp = *(range_infos - 1); final_content_len += *range_infos; size_tmp += *range_infos++ - 1; if (size_tmp == 0) final_content_len++; while (size_tmp) { size_tmp /= 10; final_content_len++; } } final_content_len += sizeof("\r\n--") - 1 + BOUNDARY_SIZE + sizeof("--\r\n") - 1 + size_fixed_each_part * range_count - (sizeof("\r\n") - 1); generator->bytesleft = final_content_len; } do_send_file(generator, req, 206, "Partial Content", mime_type->data.mimetype, &h2o_mime_attributes_as_is, method_type == METHOD_IS_GET); return 0; } /* return file */ do_send_file(generator, req, 200, "OK", mime_type->data.mimetype, &mime_type->data.attr, method_type == METHOD_IS_GET); return 0; NotModified: req->res.status = 304; req->res.reason = "Not Modified"; add_headers_unconditional(generator, req); h2o_send_inline(req, NULL, 0); Close: do_close(&generator->super, req); return 0; }
static void send_method_not_allowed(h2o_req_t *req) { h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_ALLOW, H2O_STRLIT("GET, HEAD")); h2o_send_error(req, 405, "Method Not Allowed", "method not allowed", H2O_SEND_ERROR_KEEP_HEADERS); }
static void do_send_file(struct st_h2o_sendfile_generator_t *self, h2o_req_t *req, int status, const char *reason, h2o_iovec_t mime_type, h2o_mime_attributes_t *mime_attr, int is_get) { /* link the request */ self->req = req; /* setup response */ req->res.status = status; req->res.reason = reason; req->res.content_length = self->bytesleft; req->res.mime_attr = mime_attr; if (self->ranged.range_count > 1) { mime_type.base = h2o_mem_alloc_pool(&req->pool, 52); mime_type.len = sprintf(mime_type.base, "multipart/byteranges; boundary=%s", self->ranged.boundary.base); } h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, mime_type.base, mime_type.len); h2o_filecache_get_last_modified(self->file.ref, self->header_bufs.last_modified); h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_LAST_MODIFIED, self->header_bufs.last_modified, H2O_TIMESTR_RFC1123_LEN); add_headers_unconditional(self, req); if (self->content_encoding.base != NULL) h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_ENCODING, self->content_encoding.base, self->content_encoding.len); if (self->ranged.range_count == 0) h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_ACCEPT_RANGES, H2O_STRLIT("bytes")); else if (self->ranged.range_count == 1) { h2o_iovec_t content_range; content_range.base = h2o_mem_alloc_pool(&req->pool, 128); content_range.len = sprintf(content_range.base, "bytes %zd-%zd/%zd", self->ranged.range_infos[0], self->ranged.range_infos[0] + self->ranged.range_infos[1] - 1, self->ranged.filesize); h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_RANGE, content_range.base, content_range.len); } /* special path for cases where we do not need to send any data */ if (!is_get || self->bytesleft == 0) { static h2o_generator_t generator = {NULL, NULL}; h2o_start_response(req, &generator); h2o_send(req, NULL, 0, 1); do_close(&self->super, req); return; } /* send data */ h2o_start_response(req, &self->super); if (self->ranged.range_count == 1) self->file.off = self->ranged.range_infos[0]; if (req->_ostr_top->start_pull != NULL && self->ranged.range_count < 2) { req->_ostr_top->start_pull(req->_ostr_top, do_pull); } else { size_t bufsz = MAX_BUF_SIZE; if (self->bytesleft < bufsz) bufsz = self->bytesleft; self->buf = h2o_mem_alloc_pool(&req->pool, bufsz); if (self->ranged.range_count < 2) do_proceed(&self->super, req); else { self->bytesleft = 0; self->super.proceed = do_multirange_proceed; do_multirange_proceed(&self->super, req); } } }
static int send_headers(h2o_http2_conn_t *conn, h2o_http2_stream_t *stream) { h2o_timestamp_t ts; size_t num_casper_entries_before_push = 0; h2o_get_timestamp(conn->super.ctx, &stream->req.pool, &ts); /* cancel push with an error response */ if (h2o_http2_stream_is_push(stream->stream_id)) { if (400 <= stream->req.res.status) goto CancelPush; h2o_add_header_by_str(&stream->req.pool, &stream->req.res.headers, H2O_STRLIT("x-http2-pushed"), 0, H2O_STRLIT("1")); } if (stream->req.hostconf->http2.casper.capacity_bits != 0) { /* extract the client-side cache fingerprint */ if (conn->casper == NULL) h2o_http2_conn_init_casper(conn, stream->req.hostconf->http2.casper.capacity_bits); size_t header_index = -1; while ((header_index = h2o_find_header(&stream->req.headers, H2O_TOKEN_COOKIE, header_index)) != -1) { h2o_header_t *header = stream->req.headers.entries + header_index; h2o_http2_casper_consume_cookie(conn->casper, header->value.base, header->value.len); } num_casper_entries_before_push = h2o_http2_casper_num_entries(conn->casper); /* update casper if necessary */ if (stream->req.hostconf->http2.casper.track_all_types || is_blocking_asset(&stream->req)) { ssize_t etag_index = h2o_find_header(&stream->req.headers, H2O_TOKEN_ETAG, -1); h2o_iovec_t etag = etag_index != -1 ? stream->req.headers.entries[etag_index].value : (h2o_iovec_t){}; if (h2o_http2_casper_lookup(conn->casper, stream->req.path.base, stream->req.path.len, etag.base, etag.len, 1)) { /* cancel if the pushed resource is already marked as cached */ if (h2o_http2_stream_is_push(stream->stream_id)) goto CancelPush; } } } if (h2o_http2_stream_is_push(stream->stream_id)) { /* for push, send the push promise */ if (!stream->push.promise_sent) h2o_http2_stream_send_push_promise(conn, stream); /* send ASAP if it is a blocking asset (even in case of Firefox we can't wait 1RTT for it to reprioritize the asset) */ if (is_blocking_asset(&stream->req)) h2o_http2_scheduler_rebind(&stream->_refs.scheduler, &conn->scheduler, 257, 0); } else { /* for pull, push things requested, as well as send the casper cookie if modified */ if (conn->peer_settings.enable_push) { size_t i; for (i = 0; i != stream->req.http2_push_paths.size; ++i) h2o_http2_conn_push_path(conn, stream->req.http2_push_paths.entries[i], stream); /* send casper cookie if it has been altered (due to the __stream itself__ or by some of the pushes) */ if (conn->casper != NULL && num_casper_entries_before_push != h2o_http2_casper_num_entries(conn->casper)) { h2o_iovec_t cookie = h2o_http2_casper_build_cookie(conn->casper, &stream->req.pool); h2o_add_header(&stream->req.pool, &stream->req.res.headers, H2O_TOKEN_SET_COOKIE, cookie.base, cookie.len); } } /* raise the priority of asset files that block rendering to highest if the user-agent is _not_ using dependency-based * prioritization (e.g. that of Firefox) */ if (conn->num_streams.open_priority == 0 && stream->req.hostconf->http2.reprioritize_blocking_assets && h2o_http2_scheduler_get_parent(&stream->_refs.scheduler) == &conn->scheduler && is_blocking_asset(&stream->req)) h2o_http2_scheduler_rebind(&stream->_refs.scheduler, &conn->scheduler, 257, 0); } /* send HEADERS, as well as start sending body */ h2o_hpack_flatten_response(&conn->_write.buf, &conn->_output_header_table, stream->stream_id, conn->peer_settings.max_frame_size, &stream->req.res, &ts, &conn->super.ctx->globalconf->server_name); h2o_http2_conn_request_write(conn); h2o_http2_stream_set_state(conn, stream, H2O_HTTP2_STREAM_STATE_SEND_BODY); return 0; CancelPush: h2o_http2_stream_set_state(conn, stream, H2O_HTTP2_STREAM_STATE_END_STREAM); h2o_linklist_insert(&conn->_write.streams_to_proceed, &stream->_refs.link); if (stream->push.promise_sent) { h2o_http2_encode_rst_stream_frame(&conn->_write.buf, stream->stream_id, H2O_HTTP2_ERROR_INTERNAL); h2o_http2_conn_request_write(conn); } return -1; }
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 void test_build_request(void) { h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts); h2o_fastcgi_config_vars_t config = {5000, 0, {}}; iovec_vector_t vecs; size_t vec_index; conn->req.input.method = conn->req.method = h2o_iovec_init(H2O_STRLIT("GET")); conn->req.input.scheme = conn->req.scheme = &H2O_URL_SCHEME_HTTP; conn->req.input.authority = conn->req.authority = h2o_iovec_init(H2O_STRLIT("localhost")); conn->req.input.path = conn->req.path = h2o_iovec_init(H2O_STRLIT("/")); conn->req.path_normalized = conn->req.path; conn->req.query_at = SIZE_MAX; conn->req.version = 0x101; conn->req.hostconf = *ctx.globalconf->hosts; conn->req.pathconf = conn->req.hostconf->paths.entries; h2o_add_header(&conn->req.pool, &conn->req.headers, H2O_TOKEN_COOKIE, H2O_STRLIT("foo=bar")); h2o_add_header(&conn->req.pool, &conn->req.headers, H2O_TOKEN_USER_AGENT, H2O_STRLIT("Mozilla/5.0 (X11; Linux) KHTML/4.9.1 (like Gecko) Konqueror/4.9")); /* build with max_record_size=65535 */ build_request(&conn->req, &vecs, 0x1234, 65535, &config); ok(h2o_memis(vecs.entries[0].base, vecs.entries[0].len, H2O_STRLIT("\x01\x01\x12\x34\x00\x08\x00\x00" "\x00\x01\0\0\0\0\0\0"))); vec_index = 1; ok(check_params(vecs.entries, &vec_index, 0x1234, H2O_STRLIT("\x0b\x00SCRIPT_NAME" /* */ "\x09\x01PATH_INFO/" /* */ "\x0c\x00QUERY_STRING" /* */ "\x0b\x09REMOTE_ADDR127.0.0.1" /* */ "\x0b\x05REMOTE_PORT55555" /* */ "\x0e\x03REQUEST_METHODGET" /* */ "\x09\x09HTTP_HOSTlocalhost" /* */ "\x0b\x01REQUEST_URI/" /* */ "\x0b\x09SERVER_ADDR127.0.0.1" /* */ "\x0b\x02SERVER_PORT80" /* */ "\x0b\x07SERVER_NAMEdefault" /* */ "\x0f\x08SERVER_PROTOCOLHTTP/1.1" /* */ "\x0f\x10SERVER_SOFTWAREh2o/1.2.1-alpha1" /* */ "\x0f\x3fHTTP_USER_AGENTMozilla/5.0 (X11; Linux) KHTML/4.9.1 (like Gecko) Konqueror/4.9" /* */ "\x0b\x07HTTP_COOKIEfoo=bar" /* */ ))); ok(h2o_memis(vecs.entries[vec_index].base, vecs.entries[vec_index].len, H2O_STRLIT("\x01\x05\x12\x34\x00\x00\x00\x00"))); ++vec_index; ok(vec_index == vecs.size); /* build with max_record_size=64, DOCUMENT_ROOT, additional cookie, and content */ config.document_root = h2o_iovec_init(H2O_STRLIT("/var/www/htdocs")); h2o_add_header(&conn->req.pool, &conn->req.headers, H2O_TOKEN_COOKIE, H2O_STRLIT("hoge=fuga")); conn->req.entity = h2o_iovec_init(H2O_STRLIT("The above copyright notice and this permission notice shall be included in all " "copies or substantial portions of the Software.")); build_request(&conn->req, &vecs, 0x1234, 64, &config); ok(h2o_memis(vecs.entries[0].base, vecs.entries[0].len, H2O_STRLIT("\x01\x01\x12\x34\x00\x08\x00\x00" "\x00\x01\0\0\0\0\0\0"))); vec_index = 1; ok(check_params(vecs.entries, &vec_index, 0x1234, H2O_STRLIT("\x0e\x03" "CONTENT_LENGTH126" /* */ "\x0b\x00SCRIPT_NAME" /* */ "\x09\x01PATH_INFO/" /* */ "\x0d\x0f" "DOCUMENT_ROOT/var/www/htdocs" /* */ "\x0f\x10PATH_TRANSLATED/var/www/htdocs/" /* */ "\x0c\x00QUERY_STRING" /* */ "\x0b\x09REMOTE_ADDR127.0.0.1" /* */ "\x0b\x05REMOTE_PORT55555" /* */ "\x0e\x03REQUEST_METHODGET" /* */ "\x09\x09HTTP_HOSTlocalhost" /* */ "\x0b\x01REQUEST_URI/" /* */ "\x0b\x09SERVER_ADDR127.0.0.1" /* */ "\x0b\x02SERVER_PORT80" /* */ "\x0b\x07SERVER_NAMEdefault" /* */ "\x0f\x08SERVER_PROTOCOLHTTP/1.1" /* */ "\x0f\x10SERVER_SOFTWAREh2o/1.2.1-alpha1" /* */ "\x0f\x3fHTTP_USER_AGENTMozilla/5.0 (X11; Linux) KHTML/4.9.1 (like Gecko) Konqueror/4.9" /* */ "\x0b\x11HTTP_COOKIEfoo=bar;hoge=fuga" /* */ ))); ok(h2o_memis(vecs.entries[vec_index].base, vecs.entries[vec_index].len, H2O_STRLIT("\x01\x05\x12\x34\x00\x40\x00\x00"))); ++vec_index; ok(h2o_memis(vecs.entries[vec_index].base, vecs.entries[vec_index].len, H2O_STRLIT("The above copyright notice and this permission notice shall be i"))); ++vec_index; ok(h2o_memis(vecs.entries[vec_index].base, vecs.entries[vec_index].len, H2O_STRLIT("\x01\x05\x12\x34\x00\x3e\x00\x00"))); ++vec_index; ok(h2o_memis(vecs.entries[vec_index].base, vecs.entries[vec_index].len, H2O_STRLIT("ncluded in all copies or substantial portions of the Software."))); ++vec_index; ok(h2o_memis(vecs.entries[vec_index].base, vecs.entries[vec_index].len, H2O_STRLIT("\x01\x05\x12\x34\x00\x00\x00\x00"))); ++vec_index; ok(vec_index == vecs.size); h2o_loopback_destroy(conn); }
void test_lib__http2__hpack(void) { h2o_mem_pool_t pool; h2o_mem_init_pool(&pool); note("decode_int"); { h2o_iovec_t in; const uint8_t *p; int32_t out; #define TEST(input, output) \ in = h2o_iovec_init(H2O_STRLIT(input)); \ p = (const uint8_t*)in.base; \ out = decode_int(&p, p + in.len, 7); \ ok(out == output); \ ok(p == (const uint8_t*)in.base + in.len); TEST("\x00", 0); TEST("\x03", 3); TEST("\x81", 1); TEST("\x7f\x00", 127); TEST("\x7f\x01", 128); TEST("\x7f\x7f", 254); TEST("\x7f\x81\x00", 128); TEST("\x7f\x80\x01", 255); TEST("\x7f\xff\xff\xff\x7f", 0xfffffff + 127); /* failures */ TEST("", -1); TEST("\x7f", -1); TEST("\x7f\xff", -1); TEST("\x7f\xff\xff\xff\xff", -1); #undef TEST } note("decode_huffman"); { h2o_iovec_t huffcode = { H2O_STRLIT("\xf1\xe3\xc2\xe5\xf2\x3a\x6b\xa0\xab\x90\xf4\xff") }; h2o_iovec_t *decoded = decode_huffman(&pool, (const uint8_t*)huffcode.base, huffcode.len); ok(decoded->len == sizeof("www.example.com") -1); ok(strcmp(decoded->base, "www.example.com") == 0); } h2o_mem_clear_pool(&pool); note("decode_header (literal header field with indexing)"); { struct st_h2o_decode_header_result_t result; h2o_hpack_header_table_t header_table; h2o_iovec_t in; int r; memset(&header_table, 0, sizeof(header_table)); header_table.hpack_capacity = 4096; in = h2o_iovec_init(H2O_STRLIT("\x40\x0a\x63\x75\x73\x74\x6f\x6d\x2d\x6b\x65\x79\x0d\x63\x75\x73\x74\x6f\x6d\x2d\x68\x65\x61\x64\x65\x72")); const uint8_t *p = (const uint8_t*)in.base; r = decode_header(&pool, &result, &header_table, &p, p + in.len); ok(r == 0); ok(result.name->len == 10); ok(strcmp(result.name->base, "custom-key") == 0); ok(result.value->len == 13); ok(strcmp(result.value->base, "custom-header") == 0); ok(header_table.hpack_size == 55); } h2o_mem_clear_pool(&pool); note("decode_header (literal header field without indexing)"); { struct st_h2o_decode_header_result_t result; h2o_hpack_header_table_t header_table; h2o_iovec_t in; int r; memset(&header_table, 0, sizeof(header_table)); header_table.hpack_capacity = 4096; in = h2o_iovec_init(H2O_STRLIT("\x04\x0c\x2f\x73\x61\x6d\x70\x6c\x65\x2f\x70\x61\x74\x68")); const uint8_t *p = (const uint8_t*)in.base; r = decode_header(&pool, &result, &header_table, &p, p + in.len); ok(r == 0); ok(result.name == &H2O_TOKEN_PATH->buf); ok(result.value->len == 12); ok(strcmp(result.value->base, "/sample/path") == 0); ok(header_table.hpack_size == 0); } h2o_mem_clear_pool(&pool); note("decode_header (literal header field never indexed)"); { struct st_h2o_decode_header_result_t result; h2o_hpack_header_table_t header_table; h2o_iovec_t in; int r; memset(&header_table, 0, sizeof(header_table)); header_table.hpack_capacity = 4096; in = h2o_iovec_init(H2O_STRLIT("\x10\x08\x70\x61\x73\x73\x77\x6f\x72\x64\x06\x73\x65\x63\x72\x65\x74")); const uint8_t *p = (const uint8_t*)in.base; r = decode_header(&pool, &result, &header_table, &p, p + in.len); ok(r == 0); ok(result.name->len == 8); ok(strcmp(result.name->base, "password") == 0); ok(result.value->len == 6); ok(strcmp(result.value->base, "secret") == 0); ok(header_table.hpack_size == 0); } h2o_mem_clear_pool(&pool); note("decode_header (indexed header field)"); { struct st_h2o_decode_header_result_t result; h2o_hpack_header_table_t header_table; h2o_iovec_t in; int r; memset(&header_table, 0, sizeof(header_table)); header_table.hpack_capacity = 4096; in = h2o_iovec_init(H2O_STRLIT("\x82")); const uint8_t *p = (const uint8_t*)in.base; r = decode_header(&pool, &result, &header_table, &p, p + in.len); ok(r == 0); ok(result.name == &H2O_TOKEN_METHOD->buf); ok(result.value->len == 3); ok(strcmp(result.value->base, "GET") == 0); ok(header_table.hpack_size == 0); } h2o_mem_clear_pool(&pool); note("request examples without huffman coding"); test_request( h2o_iovec_init(H2O_STRLIT("\x82\x86\x84\x41\x0f\x77\x77\x77\x2e\x65\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d")), h2o_iovec_init(H2O_STRLIT("\x82\x86\x84\xbe\x58\x08\x6e\x6f\x2d\x63\x61\x63\x68\x65")), h2o_iovec_init(H2O_STRLIT("\x82\x87\x85\xbf\x40\x0a\x63\x75\x73\x74\x6f\x6d\x2d\x6b\x65\x79\x0c\x63\x75\x73\x74\x6f\x6d\x2d\x76\x61\x6c\x75\x65"))); note("request examples with huffman coding"); test_request( h2o_iovec_init(H2O_STRLIT("\x82\x86\x84\x41\x8c\xf1\xe3\xc2\xe5\xf2\x3a\x6b\xa0\xab\x90\xf4\xff")), h2o_iovec_init(H2O_STRLIT("\x82\x86\x84\xbe\x58\x86\xa8\xeb\x10\x64\x9c\xbf")), h2o_iovec_init(H2O_STRLIT("\x82\x87\x85\xbf\x40\x88\x25\xa8\x49\xe9\x5b\xa9\x7d\x7f\x89\x25\xa8\x49\xe9\x5b\xb8\xe8\xb4\xbf"))); note("encode_huffman"); { h2o_iovec_t huffcode = { H2O_STRLIT("\xf1\xe3\xc2\xe5\xf2\x3a\x6b\xa0\xab\x90\xf4\xff") }; char buf[sizeof("www.example.com")]; size_t l = encode_huffman((uint8_t*)buf, (uint8_t*)H2O_STRLIT("www.example.com")); ok(l == huffcode.len); ok(memcmp(buf, huffcode.base, huffcode.len) == 0); } note("response examples with huffmann coding"); { h2o_hpack_header_table_t header_table; h2o_res_t res; memset(&header_table, 0, sizeof(header_table)); header_table.hpack_capacity = 256; memset(&res, 0, sizeof(res)); res.status = 302; res.reason = "Found"; h2o_add_header(&pool, &res.headers, H2O_TOKEN_CACHE_CONTROL, H2O_STRLIT("private")); h2o_add_header(&pool, &res.headers, H2O_TOKEN_DATE, H2O_STRLIT("Mon, 21 Oct 2013 20:13:21 GMT")); h2o_add_header(&pool, &res.headers, H2O_TOKEN_LOCATION, H2O_STRLIT("https://www.example.com")); check_flatten(&header_table, &res, H2O_STRLIT("\x08\x03\x33\x30\x32\x58\x85\xae\xc3\x77\x1a\x4b\x61\x96\xd0\x7a\xbe\x94\x10\x54\xd4\x44\xa8\x20\x05\x95\x04\x0b\x81\x66\xe0\x82\xa6\x2d\x1b\xff\x6e\x91\x9d\x29\xad\x17\x18\x63\xc7\x8f\x0b\x97\xc8\xe9\xae\x82\xae\x43\xd3")); memset(&res, 0, sizeof(res)); res.status = 307; res.reason = "Temporary Redirect"; h2o_add_header(&pool, &res.headers, H2O_TOKEN_CACHE_CONTROL, H2O_STRLIT("private")); h2o_add_header(&pool, &res.headers, H2O_TOKEN_DATE, H2O_STRLIT("Mon, 21 Oct 2013 20:13:21 GMT")); h2o_add_header(&pool, &res.headers, H2O_TOKEN_LOCATION, H2O_STRLIT("https://www.example.com")); check_flatten(&header_table, &res, H2O_STRLIT("\x08\x03\x33\x30\x37\xc0\xbf\xbe")); #if 0 h2o_iovec_init(H2O_STRLIT("\x48\x03\x33\x30\x37\xc1\xc0\xbf")), h2o_iovec_init(H2O_STRLIT("\x88\xc1\x61\x1d\x4d\x6f\x6e\x2c\x20\x32\x31\x20\x4f\x63\x74\x20\x32\x30\x31\x33\x20\x32\x30\x3a\x31\x33\x3a\x32\x32\x20\x47\x4d\x54\xc0\x5a\x04\x67\x7a\x69\x70\x77\x38\x66\x6f\x6f\x3d\x41\x53\x44\x4a\x4b\x48\x51\x4b\x42\x5a\x58\x4f\x51\x57\x45\x4f\x50\x49\x55\x41\x58\x51\x57\x45\x4f\x49\x55\x3b\x20\x6d\x61\x78\x2d\x61\x67\x65\x3d\x33\x36\x30\x30\x3b\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x31"))); #endif } h2o_mem_clear_pool(&pool); }
/** HTTP POST entry point for receiving entries from client * Provide the user with an ID */ static int __http_get_id(h2o_handler_t *self, h2o_req_t *req) { static h2o_generator_t generator = { NULL, NULL }; if (!h2o_memis(req->method.base, req->method.len, H2O_STRLIT("POST"))) return -1; /* redirect to leader if needed */ int leader = raft_get_current_leader(sv->raft); if (-1 == leader) { return h2oh_respond_with_error(req, 503, "Leader unavailable"); } else if (leader != sv->node_idx) { raft_node_t* node = raft_get_node(sv->raft, leader); peer_connection_t* leader_conn = raft_node_get_udata(node); char leader_url[LEADER_URL_LEN]; static h2o_generator_t generator = { NULL, NULL }; static h2o_iovec_t body = { .base = "", .len = 0 }; req->res.status = 301; req->res.reason = "Moved Permanently"; h2o_start_response(req, &generator); snprintf(leader_url, LEADER_URL_LEN, "http://%s:%d/", inet_ntoa(leader_conn->addr.sin_addr), leader_conn->http_port); h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_LOCATION, leader_url, strlen(leader_url)); h2o_send(req, &body, 1, 1); return 0; } int e; unsigned int ticket = __generate_ticket(); msg_entry_t entry; entry.id = rand(); entry.data.buf = (void*)&ticket; entry.data.len = sizeof(ticket); uv_mutex_lock(&sv->raft_lock); msg_entry_response_t r; e = raft_recv_entry(sv->raft, sv->node_idx, &entry, &r); if (0 != e) return h2oh_respond_with_error(req, 500, "BAD"); /* block until the entry is committed */ int done = 0; do { uv_cond_wait(&sv->appendentries_received, &sv->raft_lock); e = raft_msg_entry_response_committed(sv->raft, &r); switch (e) { case 0: /* not committed yet */ break; case 1: done = 1; uv_mutex_unlock(&sv->raft_lock); break; case -1: uv_mutex_unlock(&sv->raft_lock); return h2oh_respond_with_error(req, 400, "TRY AGAIN"); } } while (!done); /* serialize ID */ char id_str[100]; h2o_iovec_t body; sprintf(id_str, "%d", entry.id); body = h2o_iovec_init(id_str, strlen(id_str)); req->res.status = 200; req->res.reason = "OK"; h2o_start_response(req, &generator); h2o_send(req, &body, 1, 1); return 0; }
static int send_headers(h2o_http2_conn_t *conn, h2o_http2_stream_t *stream) { h2o_timestamp_t ts; h2o_get_timestamp(conn->super.ctx, &stream->req.pool, &ts); /* cancel push with an error response */ if (h2o_http2_stream_is_push(stream->stream_id)) { if (400 <= stream->req.res.status) goto CancelPush; if (stream->cache_digests != NULL) { ssize_t etag_index = h2o_find_header(&stream->req.headers, H2O_TOKEN_ETAG, -1); if (etag_index != -1) { h2o_iovec_t url = h2o_concat(&stream->req.pool, stream->req.input.scheme->name, h2o_iovec_init(H2O_STRLIT("://")), stream->req.input.authority, stream->req.input.path); h2o_iovec_t *etag = &stream->req.headers.entries[etag_index].value; if (h2o_cache_digests_lookup_by_url_and_etag(stream->cache_digests, url.base, url.len, etag->base, etag->len) == H2O_CACHE_DIGESTS_STATE_FRESH) goto CancelPush; } } } /* reset casper cookie in case cache-digests exist */ if (stream->cache_digests != NULL && stream->req.hostconf->http2.casper.capacity_bits != 0) { h2o_add_header(&stream->req.pool, &stream->req.res.headers, H2O_TOKEN_SET_COOKIE, H2O_STRLIT("h2o_casper=; Path=/; Expires=Sat, 01 Jan 2000 00:00:00 GMT")); } /* CASPER */ if (conn->casper != NULL) { /* update casper if necessary */ if (stream->req.hostconf->http2.casper.track_all_types || is_blocking_asset(&stream->req)) { if (h2o_http2_casper_lookup(conn->casper, stream->req.path.base, stream->req.path.len, 1)) { /* cancel if the pushed resource is already marked as cached */ if (h2o_http2_stream_is_push(stream->stream_id)) goto CancelPush; } } if (stream->cache_digests != NULL) goto SkipCookie; /* browsers might ignore push responses, or they may process the responses in a different order than they were pushed. * Therefore H2O tries to include casper cookie only in the last stream that may be received by the client, or when the * value become stable; see also: https://github.com/h2o/h2o/issues/421 */ if (h2o_http2_stream_is_push(stream->stream_id)) { if (!(conn->num_streams.pull.open == 0 && (conn->num_streams.push.half_closed - conn->num_streams.push.send_body) == 1)) goto SkipCookie; } else { if (conn->num_streams.push.half_closed - conn->num_streams.push.send_body != 0) goto SkipCookie; } h2o_iovec_t cookie = h2o_http2_casper_get_cookie(conn->casper); h2o_add_header(&stream->req.pool, &stream->req.res.headers, H2O_TOKEN_SET_COOKIE, cookie.base, cookie.len); SkipCookie:; } if (h2o_http2_stream_is_push(stream->stream_id)) { /* for push, send the push promise */ if (!stream->push.promise_sent) h2o_http2_stream_send_push_promise(conn, stream); /* send ASAP if it is a blocking asset (even in case of Firefox we can't wait 1RTT for it to reprioritize the asset) */ if (is_blocking_asset(&stream->req)) h2o_http2_scheduler_rebind(&stream->_refs.scheduler, &conn->scheduler, 257, 0); } else { /* raise the priority of asset files that block rendering to highest if the user-agent is _not_ using dependency-based * prioritization (e.g. that of Firefox) */ if (conn->num_streams.priority.open == 0 && stream->req.hostconf->http2.reprioritize_blocking_assets && h2o_http2_scheduler_get_parent(&stream->_refs.scheduler) == &conn->scheduler && is_blocking_asset(&stream->req)) h2o_http2_scheduler_rebind(&stream->_refs.scheduler, &conn->scheduler, 257, 0); } /* send HEADERS, as well as start sending body */ if (h2o_http2_stream_is_push(stream->stream_id)) h2o_add_header_by_str(&stream->req.pool, &stream->req.res.headers, H2O_STRLIT("x-http2-push"), 0, H2O_STRLIT("pushed")); h2o_hpack_flatten_response(&conn->_write.buf, &conn->_output_header_table, stream->stream_id, conn->peer_settings.max_frame_size, &stream->req.res, &ts, &conn->super.ctx->globalconf->server_name, stream->req.res.content_length); h2o_http2_conn_request_write(conn); h2o_http2_stream_set_state(conn, stream, H2O_HTTP2_STREAM_STATE_SEND_BODY); return 0; CancelPush: h2o_add_header_by_str(&stream->req.pool, &stream->req.res.headers, H2O_STRLIT("x-http2-push"), 0, H2O_STRLIT("cancelled")); h2o_http2_stream_set_state(conn, stream, H2O_HTTP2_STREAM_STATE_END_STREAM); h2o_linklist_insert(&conn->_write.streams_to_proceed, &stream->_refs.link); if (stream->push.promise_sent) { #ifndef _MSC_VER h2o_http2_encode_rst_stream_frame(&conn->_write.buf, stream->stream_id, -H2O_HTTP2_ERROR_INTERNAL); #else h2o_http2_encode_rst_stream_frame(&conn->_write.buf, stream->stream_id, H2O_HTTP2_ERROR_INTERNAL); #endif h2o_http2_conn_request_write(conn); } return -1; }