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 void register_authority(h2o_globalconf_t *globalconf, h2o_iovec_t host, uint16_t port) { static h2o_iovec_t x_authority = {H2O_STRLIT("x-authority")}; h2o_hostconf_t *hostconf = h2o_config_register_host(globalconf, host, port); h2o_pathconf_t *pathconf = h2o_config_register_path(hostconf, "/"); h2o_file_register(pathconf, "t/00unit/assets", NULL, NULL, 0); char *authority = h2o_mem_alloc(host.len + sizeof(":65535")); sprintf(authority, "%.*s:%" PRIu16, (int)host.len, host.base, port); h2o_headers_command_t *cmds = h2o_mem_alloc(sizeof(*cmds) * 2); cmds[0] = (h2o_headers_command_t){H2O_HEADERS_CMD_ADD, &x_authority, {authority, strlen(authority)}}; cmds[1] = (h2o_headers_command_t){H2O_HEADERS_CMD_NULL}; h2o_headers_register(pathconf, cmds); }
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 void test_decode_base64(void) { h2o_mem_pool_t pool; char buf[256]; h2o_mem_init_pool(&pool); h2o_iovec_t src = {H2O_STRLIT("The quick brown fox jumps over the lazy dog.")}, decoded; h2o_base64_encode(buf, (const uint8_t *)src.base, src.len, 1); ok(strcmp(buf, "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZy4") == 0); decoded = h2o_decode_base64url(&pool, buf, strlen(buf)); ok(src.len == decoded.len); ok(strcmp(decoded.base, src.base) == 0); h2o_mem_clear_pool(&pool); }
static struct rp_generator_t *proxy_send_prepare(h2o_req_t *req, h2o_proxy_location_t *upstream, int keepalive) { struct rp_generator_t *self = h2o_mem_alloc_shared(&req->pool, sizeof(*self), on_generator_dispose); self->super.proceed = do_proceed; self->super.stop = do_close; self->upstream = upstream; self->src_req = req; self->up_req.bufs[0] = build_request(req, upstream, keepalive); self->up_req.bufs[1] = req->entity; self->up_req.is_head = h2o_memis(req->method.base, req->method.len, H2O_STRLIT("HEAD")); h2o_buffer_init(&self->last_content_before_send, &h2o_socket_buffer_prototype); h2o_buffer_init(&self->buf_sending, &h2o_socket_buffer_prototype); return self; }
h2o_compress_context_t *h2o_compress_gzip_open(h2o_mem_pool_t *pool, int quality) { struct st_gzip_context_t *self = h2o_mem_alloc_shared(pool, sizeof(*self), do_free); self->super.name = h2o_iovec_init(H2O_STRLIT("gzip")); self->super.compress = do_compress; self->zs.zalloc = alloc_cb; self->zs.zfree = free_cb; self->zs.opaque = NULL; /* Z_BEST_SPEED for on-the-fly compression, memlevel set to 8 as suggested by the manual */ deflateInit2(&self->zs, quality, Z_DEFLATED, WINDOW_BITS, 8, Z_DEFAULT_STRATEGY); self->zs_is_open = 1; self->bufs = (iovec_vector_t){}; expand_buf(&self->bufs); return &self->super; }
static void redirect_internally(h2o_redirect_handler_t *self, h2o_req_t *req, h2o_iovec_t dest) { h2o_iovec_t method; h2o_url_t input, resolved; /* resolve the URL */ if (h2o_url_parse_relative(dest.base, dest.len, &input) != 0) { h2o_req_log_error(req, MODULE_NAME, "invalid destination:%.*s", (int)dest.len, dest.base); goto SendInternalError; } if (input.scheme != NULL && input.authority.base != NULL) { resolved = input; } else { h2o_url_t base; /* we MUST to set authority to that of hostconf, or internal redirect might create a TCP connection */ if (h2o_url_init(&base, req->scheme, req->hostconf->authority.hostport, req->path) != 0) { h2o_req_log_error(req, MODULE_NAME, "failed to parse current authority:%.*s", (int)req->authority.len, req->authority.base); goto SendInternalError; } h2o_url_resolve(&req->pool, &base, &input, &resolved); } /* determine the method */ switch (self->status) { case 307: case 308: method = req->method; break; default: method = h2o_iovec_init(H2O_STRLIT("GET")); #ifndef _MSC_VER req->entity = (h2o_iovec_t){NULL}; #else req->entity = (h2o_iovec_t) { 0 }; #endif break; } h2o_reprocess_request_deferred(req, method, resolved.scheme, resolved.authority, resolved.path, NULL, 1); return; SendInternalError: h2o_send_error_503(req, "Internal Server Error", "internal server error", 0); }
int main(int argc, char **argv) { h2o_hostconf_t *hostconf; signal(SIGPIPE, SIG_IGN); h2o_config_init(&config); hostconf = h2o_config_register_host(&config, h2o_iovec_init(H2O_STRLIT("default")), 65535); register_handler(hostconf, "/post-test", post_test); register_handler(hostconf, "/chunked-test", chunked_test); h2o_reproxy_register(register_handler(hostconf, "/reproxy-test", reproxy_test)); h2o_file_register(h2o_config_register_path(hostconf, "/"), "examples/doc_root", NULL, NULL, 0); #if H2O_USE_LIBUV uv_loop_t loop; uv_loop_init(&loop); h2o_context_init(&ctx, &loop, &config); #else h2o_context_init(&ctx, h2o_evloop_create(), &config); #endif /* disabled by default: uncomment the block below to use HTTPS instead of HTTP */ /* if (setup_ssl("server.crt", "server.key") != 0) goto Error; */ /* disabled by default: uncomment the line below to enable access logging */ /* h2o_access_log_register(&config.default_host, "/dev/stdout", NULL); */ if (create_listener() != 0) { fprintf(stderr, "failed to listen to 127.0.0.1:7890:%s\n", strerror(errno)); goto Error; } #if H2O_USE_LIBUV uv_run(ctx.loop, UV_RUN_DEFAULT); #else while (h2o_evloop_run(ctx.loop) == 0) ; #endif Error: return 1; }
void test_lib__handler__redirect_c() { h2o_globalconf_t globalconf; h2o_hostconf_t *hostconf; h2o_pathconf_t *pathconf; h2o_config_init(&globalconf); hostconf = h2o_config_register_host(&globalconf, h2o_iovec_init(H2O_STRLIT("default")), 65535); pathconf = h2o_config_register_path(hostconf, "/", 0); h2o_redirect_register(pathconf, 0, 301, "https://example.com/bar/"); h2o_context_init(&ctx, test_loop, &globalconf); { h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts); conn->req.input.method = h2o_iovec_init(H2O_STRLIT("GET")); conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/")); h2o_loopback_run_loop(conn); ok(conn->req.res.status == 301); ok(check_header(&conn->req.res, H2O_TOKEN_LOCATION, "https://example.com/bar/")); ok(conn->body->size != 0); h2o_loopback_destroy(conn); } { h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts); conn->req.input.method = h2o_iovec_init(H2O_STRLIT("GET")); conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/abc")); h2o_loopback_run_loop(conn); ok(conn->req.res.status == 301); ok(check_header(&conn->req.res, H2O_TOKEN_LOCATION, "https://example.com/bar/abc")); ok(conn->body->size != 0); h2o_loopback_destroy(conn); } { h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts); conn->req.input.method = h2o_iovec_init(H2O_STRLIT("HEAD")); conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/")); h2o_loopback_run_loop(conn); ok(conn->req.res.status == 301); ok(check_header(&conn->req.res, H2O_TOKEN_LOCATION, "https://example.com/bar/")); ok(conn->body->size == 0); h2o_loopback_destroy(conn); } h2o_context_dispose(&ctx); h2o_config_dispose(&globalconf); }
static void test_parse_proxy_line(void) { char in[256]; struct sockaddr_storage sa; socklen_t salen; ssize_t ret; strcpy(in, ""); ret = parse_proxy_line(in, strlen(in), (void *)&sa, &salen); ok(ret == -2); strcpy(in, "PROXY TCP4 192.168.0.1 192.168.0.11 56324 443\r\nabc"); ret = parse_proxy_line(in, strlen(in), (void *)&sa, &salen); ok(ret == strlen(in) - 3); ok(salen == sizeof(struct sockaddr_in)); ok(sa.ss_family == AF_INET); ok(((struct sockaddr_in *)&sa)->sin_addr.s_addr == htonl(0xc0a80001)); ok(((struct sockaddr_in *)&sa)->sin_port == htons(56324)); strcpy(in, "PROXY TCP4 192.168.0.1 192.168.0.11 56324 443\r"); ret = parse_proxy_line(in, strlen(in), (void *)&sa, &salen); ok(ret == -2); strcpy(in, "PROXY TCP5"); ret = parse_proxy_line(in, strlen(in), (void *)&sa, &salen); ok(ret == -1); strcpy(in, "PROXY UNKNOWN"); ret = parse_proxy_line(in, strlen(in), (void *)&sa, &salen); ok(ret == -2); strcpy(in, "PROXY UNKNOWN\r\nabc"); ret = parse_proxy_line(in, strlen(in), (void *)&sa, &salen); ok(ret == strlen(in) - 3); ok(salen == 0); strcpy(in, "PROXY TCP6 ::1 ::1 56324 443\r\n"); ret = parse_proxy_line(in, strlen(in), (void *)&sa, &salen); ok(ret == strlen(in)); ok(salen == sizeof(struct sockaddr_in6)); ok(sa.ss_family == AF_INET6); ok(memcmp(&((struct sockaddr_in6 *)&sa)->sin6_addr, H2O_STRLIT("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1")) == 0); ok(((struct sockaddr_in6 *)&sa)->sin6_port == htons(56324)); }
void test_issues293() { h2o_globalconf_t globalconf; h2o_config_init(&globalconf); /* register two hosts, using 80 and 443 */ register_authority(&globalconf, h2o_iovec_init(H2O_STRLIT("default")), 65535); register_authority(&globalconf, h2o_iovec_init(H2O_STRLIT("host1")), 80); register_authority(&globalconf, h2o_iovec_init(H2O_STRLIT("host1")), 443); register_authority(&globalconf, h2o_iovec_init(H2O_STRLIT("host2")), 80); register_authority(&globalconf, h2o_iovec_init(H2O_STRLIT("host2")), 443); register_authority(&globalconf, h2o_iovec_init(H2O_STRLIT("host3")), 65535); h2o_context_init(&ctx, test_loop, &globalconf); /* run the tests */ check(&H2O_URL_SCHEME_HTTP, "host1", "host1:80"); check(&H2O_URL_SCHEME_HTTPS, "host1", "host1:443"); check(&H2O_URL_SCHEME_HTTP, "host2", "host2:80"); check(&H2O_URL_SCHEME_HTTPS, "host2", "host2:443"); /* supplied port number in the Host header must be preferred */ check(&H2O_URL_SCHEME_HTTP, "host1:80", "host1:80"); check(&H2O_URL_SCHEME_HTTP, "host1:443", "host1:443"); check(&H2O_URL_SCHEME_HTTPS, "host1:80", "host1:80"); check(&H2O_URL_SCHEME_HTTPS, "host1:443", "host1:443"); check(&H2O_URL_SCHEME_HTTP, "host2:80", "host2:80"); check(&H2O_URL_SCHEME_HTTP, "host2:443", "host2:443"); check(&H2O_URL_SCHEME_HTTPS, "host2:80", "host2:80"); check(&H2O_URL_SCHEME_HTTPS, "host2:443", "host2:443"); /* host-level conf without default port */ check(&H2O_URL_SCHEME_HTTP, "host3", "host3:65535"); check(&H2O_URL_SCHEME_HTTPS, "host3", "host3:65535"); check(&H2O_URL_SCHEME_HTTP, "host3", "host3:65535"); check(&H2O_URL_SCHEME_HTTPS, "host3", "host3:65535"); check(&H2O_URL_SCHEME_HTTP, "host3:80", "host3:65535"); check(&H2O_URL_SCHEME_HTTPS, "host3:80", "default:65535"); check(&H2O_URL_SCHEME_HTTP, "host3:443", "default:65535"); check(&H2O_URL_SCHEME_HTTPS, "host3:443", "host3:65535"); /* upper-case */ check(&H2O_URL_SCHEME_HTTP, "HoST1", "host1:80"); check(&H2O_URL_SCHEME_HTTP, "HoST1:80", "host1:80"); check(&H2O_URL_SCHEME_HTTPS, "HoST1", "host1:443"); check(&H2O_URL_SCHEME_HTTPS, "HoST1:443", "host1:443"); h2o_context_dispose(&ctx); h2o_config_dispose(&globalconf); }
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; }
int main(int argc, char **argv) { h2o_hostconf_t *hostconf; h2o_pathconf_t *pathconf; h2o_config_init(&config); hostconf = h2o_config_register_host(&config, h2o_iovec_init(H2O_STRLIT("default")), 65535); pathconf = h2o_config_register_path(hostconf, "/", 0); h2o_create_handler(pathconf, sizeof(h2o_handler_t))->on_req = on_req; #if H2O_USE_LIBUV uv_loop_t loop; uv_loop_init(&loop); h2o_context_init(&ctx, &loop, &config); #else h2o_context_init(&ctx, h2o_evloop_create(), &config); #endif /* disabled by default: uncomment the block below to use HTTPS instead of HTTP */ /* if (setup_ssl("server.crt", "server.key") != 0) goto Error; */ accept_ctx.ctx = &ctx; accept_ctx.hosts = config.hosts; if (create_listener() != 0) { fprintf(stderr, "failed to listen to 127.0.0.1:7890:%s\n", strerror(errno)); goto Error; } #if H2O_USE_LIBUV uv_run(ctx.loop, UV_RUN_DEFAULT); #else while (h2o_evloop_run(ctx.loop, INT32_MAX) == 0) ; #endif Error: return 1; }
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 struct rp_generator_t *proxy_send_prepare(h2o_req_t *req, int keepalive) { struct rp_generator_t *self = h2o_mem_alloc_shared(&req->pool, sizeof(*self), on_generator_dispose); h2o_http1client_ctx_t *client_ctx = get_client_ctx(req); self->super.proceed = do_proceed; self->super.stop = do_close; self->src_req = req; if (client_ctx->websocket_timeout != NULL && h2o_lcstris(req->upgrade.base, req->upgrade.len, H2O_STRLIT("websocket"))) { self->is_websocket_handshake = 1; } else { self->is_websocket_handshake = 0; } self->up_req.bufs[0] = build_request(req, keepalive, self->is_websocket_handshake); self->up_req.bufs[1] = req->entity; self->up_req.is_head = h2o_memis(req->method.base, req->method.len, H2O_STRLIT("HEAD")); h2o_buffer_init(&self->last_content_before_send, &h2o_socket_buffer_prototype); h2o_doublebuffer_init(&self->sending, &h2o_socket_buffer_prototype); return self; }
static void send_chunk(h2o_ostream_t *_self, h2o_req_t *req, h2o_buf_t *inbufs, size_t inbufcnt, int is_final) { struct rproxy_t *self = (void*)_self; const char *host, *path; uint16_t port; /* throw away all data */ if (! is_final) { h2o_ostream_send_next(&self->super, req, NULL, 0, 0); return; } /* end of the original stream, start retreiving the data from the reproxy-url */ if (! parse_url(&req->pool, self->reproxy_url, &host, &port, &path)) { host = NULL; path = NULL; port = 0; } /* NOT IMPLEMENTED!!! */ h2o_buf_t body = h2o_sprintf( &req->pool, "reproxy request to URL: %s\n" " host: %s\n" " port: %u\n" " path: %s\n", self->reproxy_url, host, (int)port, path); req->res.status = 200; req->res.reason = "Internal Server Error"; req->res.content_length = SIZE_MAX; h2o_set_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, H2O_STRLIT("text/plain; charset=utf-8"), 1); h2o_setup_next_ostream(self->filter, req, &self->super.next); assert(is_final); h2o_ostream_send_next(&self->super, req, &body, 1, is_final); }
static void test_aton(void) { struct in_addr addr; memset(&addr, 0x55, sizeof(addr)); #ifndef _MSC_VER ok(h2o_hostinfo_aton((h2o_iovec_t){H2O_STRLIT("127.0.0.1")}, &addr) == 0); #else ok(h2o_hostinfo_aton((h2o_iovec_t) { H2O_MY_STRLIT("127.0.0.1") }, &addr) == 0); #endif ok(ntohl(addr.s_addr) == 0x7f000001); memset(&addr, 0x55, sizeof(addr)); #ifndef _MSC_VER ok(h2o_hostinfo_aton((h2o_iovec_t){"127.0.0.12", sizeof("127.0.0.1") - 1}, &addr) == 0); #else ok(h2o_hostinfo_aton((h2o_iovec_t) { sizeof("127.0.0.1") - 1 , "127.0.0.12" }, &addr) == 0); #endif ok(ntohl(addr.s_addr) == 0x7f000001); memset(&addr, 0x55, sizeof(addr)); #ifndef _MSC_VER ok(h2o_hostinfo_aton((h2o_iovec_t){H2O_STRLIT("255.001.002.128")}, &addr) == 0); #else ok(h2o_hostinfo_aton((h2o_iovec_t) { H2O_MY_STRLIT("255.001.002.128") }, &addr) == 0); #endif ok(ntohl(addr.s_addr) == 0xff010280); #ifndef _MSC_VER ok(h2o_hostinfo_aton((h2o_iovec_t){H2O_STRLIT("127.0.0.z")}, &addr) != 0); ok(h2o_hostinfo_aton((h2o_iovec_t){H2O_STRLIT("256.0.0.0")}, &addr) != 0); ok(h2o_hostinfo_aton((h2o_iovec_t){H2O_STRLIT("0001.0.0.0")}, &addr) != 0); ok(h2o_hostinfo_aton((h2o_iovec_t){H2O_STRLIT("0.0..1")}, &addr) != 0); ok(h2o_hostinfo_aton((h2o_iovec_t){H2O_STRLIT("1.0.0.0.")}, &addr) != 0); #else ok(h2o_hostinfo_aton((h2o_iovec_t) { H2O_MY_STRLIT("127.0.0.z") }, &addr) != 0); ok(h2o_hostinfo_aton((h2o_iovec_t) { H2O_MY_STRLIT("256.0.0.0") }, &addr) != 0); ok(h2o_hostinfo_aton((h2o_iovec_t) { H2O_MY_STRLIT("0001.0.0.0") }, &addr) != 0); ok(h2o_hostinfo_aton((h2o_iovec_t) { H2O_MY_STRLIT("0.0..1") }, &addr) != 0); ok(h2o_hostinfo_aton((h2o_iovec_t) { H2O_MY_STRLIT("1.0.0.0.") }, &addr) != 0); #endif }
void h2o_config_init(h2o_globalconf_t *config) { memset(config, 0, sizeof(*config)); config->hosts = h2o_mem_alloc(sizeof(config->hosts[0])); config->hosts[0] = NULL; h2o_linklist_init_anchor(&config->configurators); config->server_name = h2o_iovec_init(H2O_STRLIT("h2o/" H2O_VERSION)); config->max_request_entity_size = H2O_DEFAULT_MAX_REQUEST_ENTITY_SIZE; config->max_delegations = H2O_DEFAULT_MAX_DELEGATIONS; config->handshake_timeout = H2O_DEFAULT_HANDSHAKE_TIMEOUT; config->http1.req_timeout = H2O_DEFAULT_HTTP1_REQ_TIMEOUT; config->http1.upgrade_to_http2 = H2O_DEFAULT_HTTP1_UPGRADE_TO_HTTP2; config->http1.callbacks = H2O_HTTP1_CALLBACKS; config->http2.idle_timeout = H2O_DEFAULT_HTTP2_IDLE_TIMEOUT; config->proxy.io_timeout = H2O_DEFAULT_PROXY_IO_TIMEOUT; config->http2.max_concurrent_requests_per_connection = H2O_HTTP2_SETTINGS_HOST.max_concurrent_streams; config->http2.max_streams_for_priority = 16; config->http2.callbacks = H2O_HTTP2_CALLBACKS; config->mimemap = h2o_mimemap_create(); h2o_configurator__init_core(config); }
static void test_decode(void) { h2o_cache_digests_t *digests = NULL; h2o_cache_digests_load_header(&digests, H2O_STRLIT("AeLA")); ok(digests != NULL); if (digests == NULL) return; ok(digests->fresh.url_only.size == 1); ok(digests->fresh.url_and_etag.size == 0); ok(digests->fresh.url_only.entries[0].capacity_bits == 7); ok(digests->fresh.url_only.entries[0].keys.size == 1); ok(digests->fresh.url_only.entries[0].keys.entries[0] == 0x0b); ok(!digests->fresh.complete); ok(h2o_cache_digests_lookup_by_url(digests, H2O_STRLIT("https://127.0.0.1.xip.io:8081/cache-digests.cgi/hello.js")) == H2O_CACHE_DIGESTS_STATE_FRESH); ok(h2o_cache_digests_lookup_by_url(digests, H2O_STRLIT("https://127.0.0.1.xip.io:8081/notfound.js")) == H2O_CACHE_DIGESTS_STATE_UNKNOWN); h2o_cache_digests_load_header(&digests, H2O_STRLIT("FOO; stale, AcA; validators; complete")); ok(digests->fresh.url_only.size == 1); ok(digests->fresh.url_and_etag.size == 1); ok(digests->fresh.url_and_etag.entries[0].capacity_bits == 7); ok(digests->fresh.url_and_etag.entries[0].keys.size == 0); ok(digests->fresh.complete); ok(h2o_cache_digests_lookup_by_url(digests, H2O_STRLIT("https://127.0.0.1.xip.io:8081/notfound.js")) == H2O_CACHE_DIGESTS_STATE_NOT_CACHED); ok(h2o_cache_digests_lookup_by_url(digests, H2O_STRLIT("https://127.0.0.1.xip.io:8081/cache-digests.cgi/hello.js")) == H2O_CACHE_DIGESTS_STATE_FRESH); h2o_cache_digests_load_header(&digests, H2O_STRLIT("AcA; reset")); ok(digests->fresh.url_only.size == 1); ok(digests->fresh.url_and_etag.size == 0); ok(digests->fresh.url_only.entries[0].capacity_bits == 7); ok(digests->fresh.url_only.entries[0].keys.size == 0); ok(!digests->fresh.complete); h2o_cache_digests_destroy(digests); }
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 void on_send_request(h2o_socket_t *sock, const char *err) { struct st_h2o_http1client_t *client = sock->data; h2o_timer_unlink(&client->super._timeout); if (err != NULL) { on_error_before_head(client, "I/O error (send request)"); return; } if (client->_is_chunked) { client->_is_chunked = 0; h2o_iovec_t last = h2o_iovec_init(H2O_STRLIT("0\r\n")); h2o_socket_write(client->sock, &last, 1, on_send_request); return; } client->super.timings.request_end_at = h2o_gettimeofday(client->super.ctx->loop); h2o_socket_read_start(client->sock, on_head); client->super._timeout.cb = on_head_timeout; h2o_timer_link(client->super.ctx->loop, client->super.ctx->first_byte_timeout, &client->super._timeout); }
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); }
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; } #ifndef _MSC_VER h2o_status_handler_t requests_status_handler = { {H2O_STRLIT("requests")}, requests_status_init, requests_status_per_thread, requests_status_final, }; #else h2o_status_handler_t requests_status_handler = { { H2O_MY_STRLIT("requests") }, requests_status_init, requests_status_per_thread, requests_status_final, }; #endif
static void test_stripws(void) { h2o_iovec_t t; t = h2o_str_stripws(H2O_STRLIT("")); ok(h2o_memis(t.base, t.len, H2O_STRLIT(""))); t = h2o_str_stripws(H2O_STRLIT("hello world")); ok(h2o_memis(t.base, t.len, H2O_STRLIT("hello world"))); t = h2o_str_stripws(H2O_STRLIT(" hello world")); ok(h2o_memis(t.base, t.len, H2O_STRLIT("hello world"))); t = h2o_str_stripws(H2O_STRLIT("hello world ")); ok(h2o_memis(t.base, t.len, H2O_STRLIT("hello world"))); t = h2o_str_stripws(H2O_STRLIT(" hello world ")); ok(h2o_memis(t.base, t.len, H2O_STRLIT("hello world"))); t = h2o_str_stripws(H2O_STRLIT(" ")); ok(h2o_memis(t.base, t.len, H2O_STRLIT(""))); }
static void test_at_position(void) { char buf[160]; int ret; /* normal cases */ ret = h2o_str_at_position(buf, H2O_STRLIT("hello\nworld\n"), 1, 1); ok(ret == 0); ok(strcmp(buf, "hello\n^\n") == 0); ret = h2o_str_at_position(buf, H2O_STRLIT("hello\nworld\n"), 1, 5); ok(ret == 0); ok(strcmp(buf, "hello\n ^\n") == 0); ret = h2o_str_at_position(buf, H2O_STRLIT("hello\nworld\n"), 1, 6); ok(ret == 0); ok(strcmp(buf, "hello\n ^\n") == 0); ret = h2o_str_at_position(buf, H2O_STRLIT("hello\nworld\n"), 1, 7); ok(ret == 0); ok(strcmp(buf, "hello\n ^\n") == 0); ret = h2o_str_at_position(buf, H2O_STRLIT("hello\nworld\n"), 2, 1); ok(ret == 0); ok(strcmp(buf, "world\n^\n") == 0); ret = h2o_str_at_position(buf, H2O_STRLIT("hello\nworld\n"), 2, 5); ok(ret == 0); ok(strcmp(buf, "world\n ^\n") == 0); ret = h2o_str_at_position(buf, H2O_STRLIT("hello\nworld\n"), 1, 7); ok(ret == 0); ok(strcmp(buf, "hello\n ^\n") == 0); ret = h2o_str_at_position( buf, H2O_STRLIT("_________1_________2_________3_________4_________5_________6_________7_________\nworld\n"), 1, 5); ok(ret == 0); ok(strcmp(buf, "_________1_________2_________3_________4_________5_________6_________7______\n ^\n") == 0); ret = h2o_str_at_position( buf, H2O_STRLIT("_________1_________2_________3_________4_________5_________6_________7_________\nworld\n"), 1, 60); ok(ret == 0); ok(strcmp(buf, "_________3_________4_________5_________6_________7_________\n ^\n") == 0); ret = h2o_str_at_position(buf, H2O_STRLIT("hello"), 1, 20); ok(ret == 0); ok(strcmp(buf, "hello\n ^\n") == 0); /* error cases */ ret = h2o_str_at_position(buf, H2O_STRLIT("hello\nworld\n"), 0, 1); ok(ret != 0); ret = h2o_str_at_position(buf, H2O_STRLIT("hello\nworld\n"), 1, 0); ok(ret != 0); ret = h2o_str_at_position(buf, H2O_STRLIT("hello\nworld\n"), 4, 1); ok(ret != 0); }
static void test_next_token2(void) { h2o_iovec_t iter, value; const char *name; size_t name_len; #define NEXT() \ if ((name = h2o_next_token(&iter, ',', &name_len, &value)) == NULL) { \ ok(0); \ return; \ } iter = h2o_iovec_init(H2O_STRLIT("public, max-age=86400, must-revalidate")); NEXT(); ok(h2o_memis(name, name_len, H2O_STRLIT("public"))); ok(value.base == NULL); ok(value.len == 0); NEXT(); ok(h2o_memis(name, name_len, H2O_STRLIT("max-age"))); ok(h2o_memis(value.base, value.len, H2O_STRLIT("86400"))); NEXT(); ok(h2o_memis(name, name_len, H2O_STRLIT("must-revalidate"))); ok(value.base == NULL); ok(value.len == 0); name = h2o_next_token(&iter, ',', &name_len, &value); ok(name == NULL); iter = h2o_iovec_init(H2O_STRLIT("public, max-age = 86400 = c , must-revalidate=")); NEXT(); ok(h2o_memis(name, name_len, H2O_STRLIT("public"))); ok(value.base == NULL); ok(value.len == 0); NEXT(); ok(h2o_memis(name, name_len, H2O_STRLIT("max-age"))); ok(h2o_memis(value.base, value.len, H2O_STRLIT("86400 = c"))); NEXT(); ok(h2o_memis(name, name_len, H2O_STRLIT("must-revalidate"))); name = h2o_next_token(&iter, ',', &name_len, &value); ok(h2o_memis(value.base, value.len, H2O_STRLIT(""))); #undef NEXT }
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include <stdio.h> #include <stdlib.h> #include "h2o.h" #include "h2o/http1.h" #include "h2o/http2.h" #include "h2o/http2_internal.h" static const h2o_iovec_t CONNECTION_PREFACE = {H2O_STRLIT("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")}; /* h2-14 and h2-16 are kept for backwards compatibility, as they are often used */ static const h2o_iovec_t alpn_protocols[] = {{H2O_STRLIT("h2")}, {H2O_STRLIT("h2-16")}, {H2O_STRLIT("h2-14")}, {NULL, 0}}; const h2o_iovec_t *h2o_http2_alpn_protocols = alpn_protocols; /* npn defs should match the definition of alpn_protocols */ const char *h2o_http2_npn_protocols = "\x02" "h2" "\x05" "h2-16" "\x05" "h2-14"; const h2o_http2_priority_t h2o_http2_default_priority = { 0, /* exclusive */ 0, /* dependency */
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 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; }