void test_build_destination(void) { h2o_pathconf_t conf_not_slashed = {NULL, {H2O_STRLIT("/abc")}}, conf_slashed = {NULL, {H2O_STRLIT("/abc/")}}; h2o_req_t req; h2o_iovec_t dest; int escape; for (escape = 0; escape <= 1; escape++) { h2o_init_request(&req, NULL, NULL); note("escaping: %s", escape ? "on" : "off"); req.path_normalized = h2o_iovec_init(H2O_STRLIT("/abc/xyz")); req.query_at = req.path_normalized.len; req.input.path = req.path = h2o_concat(&req.pool, req.path_normalized, h2o_iovec_init(H2O_STRLIT("?q"))); /* basic pattern */ req.pathconf = &conf_not_slashed; dest = h2o_build_destination(&req, H2O_STRLIT("/def"), escape); ok(h2o_memis(dest.base, dest.len, H2O_STRLIT("/def/xyz?q"))); dest = h2o_build_destination(&req, H2O_STRLIT("/def/"), escape); ok(h2o_memis(dest.base, dest.len, H2O_STRLIT("/def/xyz?q"))); req.pathconf = &conf_slashed; dest = h2o_build_destination(&req, H2O_STRLIT("/def"), escape); ok(h2o_memis(dest.base, dest.len, H2O_STRLIT("/def/xyz?q"))); dest = h2o_build_destination(&req, H2O_STRLIT("/def/"), escape); ok(h2o_memis(dest.base, dest.len, H2O_STRLIT("/def/xyz?q"))); /* test wo. query */ if (escape) { req.pathconf = &conf_not_slashed; req.query_at = SIZE_MAX; dest = h2o_build_destination(&req, H2O_STRLIT("/def"), escape); ok(h2o_memis(dest.base, dest.len, H2O_STRLIT("/def/xyz"))); } /* no trailing */ req.path_normalized = h2o_iovec_init(H2O_STRLIT("/abc")); req.query_at = req.path_normalized.len; req.input.path = req.path = h2o_concat(&req.pool, req.path_normalized, h2o_iovec_init(H2O_STRLIT("?q"))); req.pathconf = &conf_not_slashed; dest = h2o_build_destination(&req, H2O_STRLIT("/def"), escape); ok(h2o_memis(dest.base, dest.len, H2O_STRLIT("/def?q"))); dest = h2o_build_destination(&req, H2O_STRLIT("/def/"), escape); ok(h2o_memis(dest.base, dest.len, H2O_STRLIT("/def/?q"))); } h2o_mem_clear_pool(&req.pool); }
static h2o_iovec_t rewrite_location(h2o_mem_pool_t *pool, const char *location, size_t location_len, h2o_proxy_location_t *upstream, h2o_iovec_t req_scheme, h2o_iovec_t req_authority, h2o_iovec_t req_basepath) { h2o_iovec_t loc_scheme, loc_host, loc_path; uint16_t loc_port; if (h2o_parse_url(location, location_len, &loc_scheme, &loc_host, &loc_port, &loc_path) != 0 || ! test_location_match(upstream, loc_scheme, loc_host, loc_port, loc_path)) return h2o_iovec_init(location, location_len); return h2o_concat(pool, req_scheme, h2o_iovec_init(H2O_STRLIT("://")), req_authority, req_basepath, h2o_iovec_init(loc_path.base + upstream->path.len, loc_path.len - upstream->path.len)); }
static int on_req(h2o_handler_t *_self, h2o_req_t *req) { h2o_redirect_handler_t *self = (void *)_self; /* build the URL */ h2o_iovec_t path = h2o_iovec_init(req->path_normalized.base + req->pathconf->path.len, req->path_normalized.len - req->pathconf->path.len); h2o_iovec_t query = req->query_at != SIZE_MAX ? h2o_iovec_init(req->path.base + req->query_at, req->path.len - req->query_at) : h2o_iovec_init(H2O_STRLIT("")); h2o_iovec_t dest = h2o_concat(&req->pool, self->prefix, path, query); if (self->internal) { redirect_internally(self, req, dest); } else { h2o_send_redirect(req, self->status, "Redirected", dest.base, dest.len); } return 0; }
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 h2o_iovec_t requests_status_final(void *priv, h2o_globalconf_t *gconf, h2o_req_t *req) { #ifndef _MSC_VER h2o_iovec_t ret = {NULL}; #else h2o_iovec_t ret = { 0 }; #endif struct st_requests_status_ctx_t *rsc = priv; if (rsc->logconf != NULL) { ret = h2o_concat(&req->pool, h2o_iovec_init(H2O_STRLIT(",\n \"requests\": [")), rsc->req_data, h2o_iovec_init(H2O_STRLIT("\n ]"))); h2o_logconf_dispose(rsc->logconf); } free(rsc->req_data.base); #ifndef _MSC_VER pthread_mutex_destroy(&rsc->mutex); #else uv_mutex_destroy(&rsc->mutex); #endif free(rsc); return ret; }
static h2o_iovec_t rewrite_location(h2o_mem_pool_t *pool, const char *location, size_t location_len, h2o_url_t *match, const h2o_url_scheme_t *req_scheme, h2o_iovec_t req_authority, h2o_iovec_t req_basepath) { h2o_url_t loc_parsed; if (h2o_url_parse(location, location_len, &loc_parsed) != 0) goto NoRewrite; if (loc_parsed.scheme != &H2O_URL_SCHEME_HTTP) goto NoRewrite; if (!h2o_lcstris(loc_parsed.host.base, loc_parsed.host.len, match->host.base, match->host.len)) goto NoRewrite; if (h2o_url_get_port(&loc_parsed) != h2o_url_get_port(match)) goto NoRewrite; if (loc_parsed.path.len < match->path.len) goto NoRewrite; if (memcmp(loc_parsed.path.base, match->path.base, match->path.len) != 0) goto NoRewrite; return h2o_concat(pool, req_scheme->name, h2o_iovec_init(H2O_STRLIT("://")), req_authority, req_basepath, h2o_iovec_init(loc_parsed.path.base + match->path.len, loc_parsed.path.len - match->path.len)); NoRewrite: return (h2o_iovec_t){}; }
static int on_req(h2o_handler_t *_self, h2o_req_t *req) { struct rp_handler_t *self = (void *)_self; h2o_req_overrides_t *overrides = h2o_mem_alloc_pool(&req->pool, sizeof(*overrides)); const h2o_url_scheme_t *scheme; h2o_iovec_t *authority; /* setup overrides */ *overrides = (h2o_req_overrides_t) {}; if (self->sockpool != NULL) { overrides->socketpool = self->sockpool; } else if (self->config.preserve_host) { overrides->hostport.host = self->upstream.host; overrides->hostport.port = h2o_url_get_port(&self->upstream); } overrides->location_rewrite.match = &self->upstream; overrides->location_rewrite.path_prefix = req->pathconf->path; overrides->client_ctx = h2o_context_get_handler_context(req->conn->ctx, &self->super); /* determine the scheme and authority */ if (self->config.preserve_host) { scheme = req->scheme; authority = &req->authority; } else { scheme = self->upstream.scheme; authority = &self->upstream.authority; } /* request reprocess */ h2o_reprocess_request(req, req->method, scheme, *authority, h2o_concat(&req->pool, self->upstream.path, h2o_iovec_init(req->path.base + req->pathconf->path.len, req->path.len - req->pathconf->path.len)), overrides, 0); return 0; }
static int on_req(h2o_handler_t *_self, h2o_req_t *req) { h2o_file_handler_t *self = (void *)_self; char *rpath; size_t rpath_len, req_path_prefix; struct st_h2o_sendfile_generator_t *generator = NULL; int is_dir; if (req->path_normalized.len < self->conf_path.len) { h2o_iovec_t dest = h2o_uri_escape(&req->pool, self->conf_path.base, self->conf_path.len, "/"); if (req->query_at != SIZE_MAX) dest = h2o_concat(&req->pool, dest, h2o_iovec_init(req->path.base + req->query_at, req->path.len - req->query_at)); h2o_send_redirect(req, 301, "Moved Permanently", dest.base, dest.len); return 0; } /* build path (still unterminated at the end of the block) */ req_path_prefix = self->conf_path.len; rpath = alloca(self->real_path.len + (req->path_normalized.len - req_path_prefix) + self->max_index_file_len + 1); rpath_len = 0; memcpy(rpath + rpath_len, self->real_path.base, self->real_path.len); rpath_len += self->real_path.len; memcpy(rpath + rpath_len, req->path_normalized.base + req_path_prefix, req->path_normalized.len - req_path_prefix); rpath_len += req->path_normalized.len - req_path_prefix; /* build generator (as well as terminating the rpath and its length upon success) */ if (rpath[rpath_len - 1] == '/') { h2o_iovec_t *index_file; for (index_file = self->index_files; index_file->base != NULL; ++index_file) { memcpy(rpath + rpath_len, index_file->base, index_file->len); rpath[rpath_len + index_file->len] = '\0'; if ((generator = create_generator(req, rpath, rpath_len + index_file->len, &is_dir, self->flags)) != NULL) { rpath_len += index_file->len; goto Opened; } if (is_dir) { /* note: apache redirects "path/" to "path/index.txt/" if index.txt is a dir */ h2o_iovec_t dest = h2o_concat(&req->pool, req->path_normalized, *index_file, h2o_iovec_init(H2O_STRLIT("/"))); dest = h2o_uri_escape(&req->pool, dest.base, dest.len, "/"); if (req->query_at != SIZE_MAX) dest = h2o_concat(&req->pool, dest, h2o_iovec_init(req->path.base + req->query_at, req->path.len - req->query_at)); h2o_send_redirect(req, 301, "Moved Permantently", dest.base, dest.len); return 0; } if (errno != ENOENT) break; } if (index_file->base == NULL && (self->flags & H2O_FILE_FLAG_DIR_LISTING) != 0) { rpath[rpath_len] = '\0'; int is_get = 0; if (h2o_memis(req->method.base, req->method.len, H2O_STRLIT("GET"))) { is_get = 1; } else if (h2o_memis(req->method.base, req->method.len, H2O_STRLIT("HEAD"))) { /* ok */ } else { send_method_not_allowed(req); return 0; } if (send_dir_listing(req, rpath, rpath_len, is_get) == 0) return 0; } } else { rpath[rpath_len] = '\0'; if ((generator = create_generator(req, rpath, rpath_len, &is_dir, self->flags)) != NULL) goto Opened; if (is_dir) { h2o_iovec_t dest = h2o_concat(&req->pool, req->path_normalized, h2o_iovec_init(H2O_STRLIT("/"))); dest = h2o_uri_escape(&req->pool, dest.base, dest.len, "/"); if (req->query_at != SIZE_MAX) dest = h2o_concat(&req->pool, dest, h2o_iovec_init(req->path.base + req->query_at, req->path.len - req->query_at)); h2o_send_redirect(req, 301, "Moved Permanently", dest.base, dest.len); return 0; } } /* failed to open */ if (errno == ENFILE || errno == EMFILE) { h2o_send_error(req, 503, "Service Unavailable", "please try again later", 0); } else { if (h2o_mimemap_has_dynamic_type(self->mimemap) && try_dynamic_request(self, req, rpath, rpath_len) == 0) return 0; if (errno == ENOENT || errno == ENOTDIR) { return -1; } else { h2o_send_error(req, 403, "Access Forbidden", "access forbidden", 0); } } return 0; Opened: return serve_with_generator(generator, req, rpath, rpath_len, h2o_mimemap_get_type_by_extension(self->mimemap, h2o_get_filext(rpath, rpath_len))); }
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; }