ssize_t h2o_find_header_by_str(const h2o_headers_t *headers, const char *name, size_t name_len, ssize_t cursor) { for (++cursor; cursor < headers->size; ++cursor) { h2o_header_t *t = headers->entries + cursor; if (h2o_memis(t->name->base, t->name->len, name, name_len)) { return cursor; } } return -1; }
void h2o_config_unsetenv(h2o_envconf_t *envconf, const char *name) { size_t i, name_len = strlen(name); /* do nothing if already set */ for (i = 0; i != envconf->unsets.size; ++i) if (h2o_memis(envconf->unsets.entries[i].base, envconf->unsets.entries[i].len, name, name_len)) return; /* register */ h2o_vector_reserve(NULL, &envconf->unsets, envconf->unsets.size + 1); envconf->unsets.entries[envconf->unsets.size++] = h2o_strdup_shared(NULL, name, name_len); }
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; }
static void check_flatten(h2o_hpack_header_table_t *header_table, h2o_res_t *res, const char *expected, size_t expected_len) { h2o_buffer_t *buf; h2o_http2_frame_t frame; h2o_buffer_init(&buf, &h2o_socket_buffer_prototype); h2o_hpack_flatten_headers(&buf, header_table, 1, H2O_HTTP2_SETTINGS_DEFAULT.max_frame_size, res, NULL, NULL); ok(h2o_http2_decode_frame(&frame, (uint8_t*)buf->bytes, buf->size, &H2O_HTTP2_SETTINGS_DEFAULT) > 0); ok(h2o_memis(frame.payload, frame.length, expected, expected_len)); h2o_buffer_dispose(&buf); }
static int on_1xx(h2o_http1client_t *client, int minor_version, int status, h2o_iovec_t msg, h2o_http1client_header_t *headers, size_t num_headers) { struct rp_generator_t *self = client->data; size_t i; for (i = 0; i != num_headers; ++i) { if (h2o_memis(headers[i].name, headers[i].name_len, H2O_STRLIT("link"))) h2o_push_path_in_link_header(self->src_req, headers[i].value, headers[i].value_len); } return 0; }
static int test_location_match(h2o_proxy_location_t *location, h2o_iovec_t scheme, h2o_iovec_t host, uint16_t port, h2o_iovec_t path) { if (! h2o_memis(scheme.base, scheme.len, H2O_STRLIT("http"))) return 0; if (! h2o_lcstris(host.base, host.len, location->host.base, location->host.len)) return 0; if (port != location->port) return 0; if (path.len < location->path.len) return 0; if (memcmp(path.base, location->path.base, location->path.len) != 0) return 0; return 1; }
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 }
static void bind_conf(h2o_req_t *req) { h2o_context_t *ctx; h2o_hostconf_t *hostconf; h2o_pathconf_t *pathconf; if (req->pathconf != NULL) { /* already bound */ return; } ctx = req->conn->ctx; h2o_get_timestamp(ctx, &req->pool, &req->processed_at); /* find the host context */ if (req->authority.base != NULL) { h2o_hostconf_t *end = ctx->globalconf->hosts.entries + ctx->globalconf->hosts.size; for (hostconf = ctx->globalconf->hosts.entries; hostconf != end; ++hostconf) { if (h2o_memis(req->authority.base, req->authority.len, hostconf->hostname.base, hostconf->hostname.len)) goto HostFound; } hostconf = ctx->globalconf->hosts.entries; HostFound: ; } else { /* set the authority name to the default one */ hostconf = ctx->globalconf->hosts.entries; req->authority = hostconf->hostname; } /* find the path context (as well as building path_normalized) */ if (hostconf->paths.size != 0) { size_t i = 0; req->path_normalized = h2o_normalize_path(&req->pool, req->path.base, req->path.len); do { pathconf = hostconf->paths.entries + i; if (req->path.len >= pathconf->path.len && memcmp(req->path.base, pathconf->path.base, pathconf->path.len) == 0) goto PathFound; } while (++i != hostconf->paths.size); pathconf = &hostconf->fallback_path; PathFound: ; } else { pathconf = &hostconf->fallback_path; } req->pathconf = pathconf; }
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); self->super.proceed = do_proceed; self->super.stop = do_close; self->src_req = req; self->up_req.bufs[0] = build_request(req, 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_doublebuffer_init(&self->sending, &h2o_socket_buffer_prototype); return self; }
static void test_next_token3(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("</foo.css>; rel=preload; xxx=,</bar.js>, </zzz.js>")); NEXT(); ok(h2o_memis(name, name_len, H2O_STRLIT("</foo.css>"))); ok(value.base == NULL); ok(value.len == 0); NEXT(); ok(h2o_memis(name, name_len, H2O_STRLIT("rel"))); ok(h2o_memis(value.base, value.len, H2O_STRLIT("preload"))); NEXT(); ok(h2o_memis(name, name_len, H2O_STRLIT("xxx"))); ok(value.base != NULL); /* xxx _has_ a value! */ ok(value.len == 0); NEXT(); ok(h2o_memis(name, name_len, H2O_STRLIT(","))); ok(value.base == NULL); ok(value.len == 0); NEXT(); ok(h2o_memis(name, name_len, H2O_STRLIT("</bar.js>"))); ok(value.base == NULL); ok(value.len == 0); NEXT(); ok(h2o_memis(name, name_len, H2O_STRLIT(","))); ok(value.base == NULL); ok(value.len == 0); NEXT(); ok(h2o_memis(name, name_len, H2O_STRLIT("</zzz.js>"))); ok(value.base == NULL); ok(value.len == 0); name = h2o_next_token(&iter, ',', &name_len, &value); ok(name == NULL); #undef NEXT }
static int chunked_test(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("GET"))) return -1; h2o_iovec_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; }
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); }
h2o_hostconf_t *h2o_config_register_host(h2o_globalconf_t *config, h2o_iovec_t host, uint16_t port) { h2o_hostconf_t *hostconf = NULL; h2o_iovec_t host_lc; assert(host.len != 0); /* convert hostname to lowercase */ host_lc = h2o_strdup(NULL, host.base, host.len); h2o_strtolower(host_lc.base, host_lc.len); { /* return NULL if given authority is already registered */ h2o_hostconf_t **p; for (p = config->hosts; *p != NULL; ++p) if (h2o_memis((*p)->authority.host.base, (*p)->authority.host.len, host_lc.base, host_lc.len) && (*p)->authority.port == port) goto Exit; } /* create hostconf */ hostconf = create_hostconf(config); hostconf->authority.host = host_lc; host_lc = (h2o_iovec_t){}; hostconf->authority.port = port; if (hostconf->authority.port == 65535) { hostconf->authority.hostport = hostconf->authority.host; } else { hostconf->authority.hostport.base = h2o_mem_alloc(hostconf->authority.host.len + sizeof("[]:65535")); if (strchr(hostconf->authority.host.base, ':') != NULL) { hostconf->authority.hostport.len = sprintf(hostconf->authority.hostport.base, "[%s]:%" PRIu16, hostconf->authority.host.base, port); } else { hostconf->authority.hostport.len = sprintf(hostconf->authority.hostport.base, "%s:%" PRIu16, hostconf->authority.host.base, port); } } /* append to the list */ h2o_append_to_null_terminated_list((void *)&config->hosts, hostconf); Exit: free(host_lc.base); return hostconf; }
static int check_params(h2o_iovec_t *vecs, size_t *index, uint16_t request_id, const char *expected, size_t expected_len) { #define DECODE_UINT16(p) (((unsigned char *)&p)[0] << 8 | ((unsigned char *)&p)[1]) char buf[4096]; size_t offset = 0; while (1) { if (vecs[*index].len != FCGI_RECORD_HEADER_SIZE) { fprintf(stderr, "record too short (index: %zu)\n", *index); return 0; } struct st_fcgi_record_header_t *header = (void *)vecs[*index].base; if (header->version != FCGI_VERSION_1 || header->type != FCGI_PARAMS || header->paddingLength != 0 || header->reserved != 0) { fprintf(stderr, "header is corrupt (index: %zu)\n", *index); return 0; } if (DECODE_UINT16(header->requestId) != request_id) { fprintf(stderr, "unexpected request id (index: %zu)\n", *index); return 0; } ++*index; if (DECODE_UINT16(header->contentLength) == 0) break; if (vecs[*index].len != DECODE_UINT16(header->contentLength)) { fprintf(stderr, "unexpected body size (index: %zu)\n", *index); return 0; } memcpy(buf + offset, vecs[*index].base, vecs[*index].len); offset += vecs[*index].len; ++*index; } if (!h2o_memis(buf, offset, expected, expected_len)) { fprintf(stderr, "PARAMS content mistach\n"); return 0; } return 1; #undef DECODE_UINT16 }
static void remove_header(h2o_headers_t *headers, h2o_headers_command_t *cmd) { size_t src, dst = 0; for (src = 0; src != headers->size; ++src) { if (h2o_iovec_is_token(cmd->name)) { if (headers->entries[src].name == cmd->name) continue; } else { if (h2o_memis(headers->entries[src].name->base, headers->entries[src].name->len, cmd->name->base, cmd->name->len)) continue; } /* not matched */ if (dst != src) headers->entries[dst] = headers->entries[src]; ++dst; } headers->size = dst; }
static void check(const h2o_url_scheme_t *scheme, const char *host, const char *expected) { 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.scheme = scheme; conn->req.input.authority = h2o_iovec_init(host, strlen(host)); conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/")); h2o_loopback_run_loop(conn); ok(conn->req.res.status == 200); size_t index = h2o_find_header_by_str(&conn->req.res.headers, H2O_STRLIT("x-authority"), SIZE_MAX); ok(index != SIZE_MAX); if (index != SIZE_MAX) { ok(h2o_memis(conn->req.res.headers.entries[index].value.base, conn->req.res.headers.entries[index].value.len, expected, strlen(expected))); } h2o_loopback_destroy(conn); }
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; }
void h2o_cache_digests_load_header(h2o_cache_digests_t **digests, const char *value, size_t len) { h2o_iovec_t iter = h2o_iovec_init(value, len); const char *token; size_t token_len; do { const char *gcs_base64; size_t gcs_base64_len; int reset = 0, validators = 0, complete = 0, skip = 0; h2o_iovec_t token_value; if ((gcs_base64 = h2o_next_token(&iter, ';', &gcs_base64_len, NULL)) == NULL) return; while ((token = h2o_next_token(&iter, ';', &token_len, &token_value)) != NULL && !h2o_memis(token, token_len, H2O_STRLIT(","))) { if (h2o_lcstris(token, token_len, H2O_STRLIT("reset"))) { reset = 1; } else if (h2o_lcstris(token, token_len, H2O_STRLIT("validators"))) { validators = 1; } else if (h2o_lcstris(token, token_len, H2O_STRLIT("complete"))) { complete = 1; } else { skip = 1; } } if (reset && *digests != NULL) { h2o_cache_digests_destroy(*digests); *digests = NULL; } if (skip) { /* not supported for the time being */ } else { load_digest(digests, gcs_base64, gcs_base64_len, validators, complete); } } while (token != NULL); }
static void test_next_token(void) { h2o_iovec_t iter; const char *token; size_t token_len; #define NEXT() \ if ((token = h2o_next_token(&iter, ',', &token_len, NULL)) == NULL) { \ ok(0); \ return; \ } iter = h2o_iovec_init(H2O_STRLIT("public, max-age=86400, must-revalidate")); NEXT(); ok(h2o_memis(token, token_len, H2O_STRLIT("public"))); NEXT(); ok(h2o_memis(token, token_len, H2O_STRLIT("max-age=86400"))); NEXT(); ok(h2o_memis(token, token_len, H2O_STRLIT("must-revalidate"))); token = h2o_next_token(&iter, ',', &token_len, NULL); ok(token == NULL); iter = h2o_iovec_init(H2O_STRLIT(" public ,max-age=86400 ,")); NEXT(); ok(h2o_memis(token, token_len, H2O_STRLIT("public"))); NEXT(); ok(h2o_memis(token, token_len, H2O_STRLIT("max-age=86400"))); token = h2o_next_token(&iter, ',', &token_len, NULL); ok(token == NULL); iter = h2o_iovec_init(H2O_STRLIT("")); token = h2o_next_token(&iter, ',', &token_len, NULL); ok(token == NULL); iter = h2o_iovec_init(H2O_STRLIT(", ,a, ")); NEXT(); ok(token_len == 0); NEXT(); ok(token_len == 0); NEXT(); ok(h2o_memis(token, token_len, H2O_STRLIT("a"))); token = h2o_next_token(&iter, ',', &token_len, NULL); ok(token == NULL); #undef NEXT }
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(""))); }
mrb_value h2o_mruby_send_chunked_init(h2o_mruby_generator_t *generator, mrb_value body) { h2o_mruby_chunked_t *chunked = h2o_mem_alloc_pool(&generator->req->pool, sizeof(*chunked)); h2o_doublebuffer_init(&chunked->sending, &h2o_socket_buffer_prototype); chunked->bytes_left = h2o_memis(generator->req->method.base, generator->req->method.len, H2O_STRLIT("HEAD")) ? 0 : generator->req->res.content_length; generator->super.proceed = do_proceed; generator->chunked = chunked; if ((chunked->shortcut.client = h2o_mruby_http_set_shortcut(generator->ctx->shared->mrb, body, on_shortcut_notify)) != NULL) { chunked->type = H2O_MRUBY_CHUNKED_TYPE_SHORTCUT; chunked->shortcut.remaining = NULL; on_shortcut_notify(generator); return mrb_nil_value(); } else { chunked->type = H2O_MRUBY_CHUNKED_TYPE_CALLBACK; h2o_buffer_init(&chunked->callback.receiving, &h2o_socket_buffer_prototype); mrb_gc_register(generator->ctx->shared->mrb, body); chunked->callback.body_obj = body; return mrb_ary_entry(generator->ctx->shared->constants, H2O_MRUBY_CHUNKED_PROC_EACH_TO_FIBER); } }
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 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; }
static void test_parse(void) { h2o_url_t parsed; int ret; ret = h2o_url_parse("http://example.com/abc", SIZE_MAX, &parsed); ok(ret == 0); ok(parsed.scheme == &H2O_URL_SCHEME_HTTP); ok(h2o_memis(parsed.authority.base, parsed.authority.len, H2O_STRLIT("example.com"))); ok(h2o_memis(parsed.host.base, parsed.host.len, H2O_STRLIT("example.com"))); ok(parsed._port == 65535); ok(h2o_url_get_port(&parsed) == 80); ok(h2o_memis(parsed.path.base, parsed.path.len, H2O_STRLIT("/abc"))); ret = h2o_url_parse("http://example.com", SIZE_MAX, &parsed); ok(ret == 0); ok(parsed.scheme == &H2O_URL_SCHEME_HTTP); ok(h2o_memis(parsed.authority.base, parsed.authority.len, H2O_STRLIT("example.com"))); ok(h2o_memis(parsed.host.base, parsed.host.len, H2O_STRLIT("example.com"))); ok(parsed._port == 65535); ok(h2o_url_get_port(&parsed) == 80); ok(h2o_memis(parsed.path.base, parsed.path.len, H2O_STRLIT("/"))); ret = h2o_url_parse("http://example.com:81/abc", SIZE_MAX, &parsed); ok(ret == 0); ok(parsed.scheme == &H2O_URL_SCHEME_HTTP); ok(h2o_memis(parsed.authority.base, parsed.authority.len, H2O_STRLIT("example.com:81"))); ok(h2o_memis(parsed.host.base, parsed.host.len, H2O_STRLIT("example.com"))); ok(parsed._port == 81); ok(h2o_url_get_port(&parsed) == 81); ok(h2o_memis(parsed.path.base, parsed.path.len, H2O_STRLIT("/abc"))); ret = h2o_url_parse("http://example.com:81", SIZE_MAX, &parsed); ok(ret == 0); ok(parsed.scheme == &H2O_URL_SCHEME_HTTP); ok(h2o_memis(parsed.authority.base, parsed.authority.len, H2O_STRLIT("example.com:81"))); ok(h2o_memis(parsed.host.base, parsed.host.len, H2O_STRLIT("example.com"))); ok(parsed._port == 81); ok(h2o_url_get_port(&parsed) == 81); ok(h2o_memis(parsed.path.base, parsed.path.len, H2O_STRLIT("/"))); ret = h2o_url_parse("https://example.com/abc", SIZE_MAX, &parsed); ok(ret == 0); ok(parsed.scheme == &H2O_URL_SCHEME_HTTPS); ok(h2o_memis(parsed.authority.base, parsed.authority.len, H2O_STRLIT("example.com"))); ok(h2o_memis(parsed.host.base, parsed.host.len, H2O_STRLIT("example.com"))); ok(parsed._port == 65535); ok(h2o_url_get_port(&parsed) == 443); ok(h2o_memis(parsed.path.base, parsed.path.len, H2O_STRLIT("/abc"))); ret = h2o_url_parse("http:/abc", SIZE_MAX, &parsed); ok(ret != 0); ret = h2o_url_parse("ftp://example.com/abc", SIZE_MAX, &parsed); ok(ret != 0); ret = h2o_url_parse("http://abc:111111/def", SIZE_MAX, &parsed); ok(ret != 0); ret = h2o_url_parse("http://[::ffff:192.0.2.128]", SIZE_MAX, &parsed); ok(ret == 0); ok(parsed.scheme == &H2O_URL_SCHEME_HTTP); ok(h2o_memis(parsed.authority.base, parsed.authority.len, H2O_STRLIT("[::ffff:192.0.2.128]"))); ok(h2o_memis(parsed.host.base, parsed.host.len, H2O_STRLIT("::ffff:192.0.2.128"))); ok(parsed._port == 65535); ok(h2o_url_get_port(&parsed) == 80); ok(h2o_memis(parsed.path.base, parsed.path.len, H2O_STRLIT("/"))); ret = h2o_url_parse("https://[::ffff:192.0.2.128]/abc", SIZE_MAX, &parsed); ok(ret == 0); ok(parsed.scheme == &H2O_URL_SCHEME_HTTPS); ok(h2o_memis(parsed.authority.base, parsed.authority.len, H2O_STRLIT("[::ffff:192.0.2.128]"))); ok(h2o_memis(parsed.host.base, parsed.host.len, H2O_STRLIT("::ffff:192.0.2.128"))); ok(parsed._port == 65535); ok(h2o_url_get_port(&parsed) == 443); ok(h2o_memis(parsed.path.base, parsed.path.len, H2O_STRLIT("/abc"))); ret = h2o_url_parse("https://[::ffff:192.0.2.128]:111/abc", SIZE_MAX, &parsed); ok(ret == 0); ok(parsed.scheme == &H2O_URL_SCHEME_HTTPS); ok(h2o_memis(parsed.authority.base, parsed.authority.len, H2O_STRLIT("[::ffff:192.0.2.128]:111"))); ok(h2o_memis(parsed.host.base, parsed.host.len, H2O_STRLIT("::ffff:192.0.2.128"))); ok(parsed._port == 111); ok(h2o_url_get_port(&parsed) == 111); ok(h2o_memis(parsed.path.base, parsed.path.len, H2O_STRLIT("/abc"))); }
static void test_parse_relative(void) { h2o_url_t parsed; int ret; memset(&parsed, 0x55, sizeof(parsed)); ret = h2o_url_parse_relative("abc", SIZE_MAX, &parsed); ok(ret == 0); ok(parsed.scheme == NULL); ok(parsed.authority.base == NULL); ok(parsed.host.base == NULL); ok(parsed._port == 65535); ok(h2o_memis(parsed.path.base, parsed.path.len, H2O_STRLIT("abc"))); memset(&parsed, 0x55, sizeof(parsed)); ret = h2o_url_parse_relative("/abc", SIZE_MAX, &parsed); ok(ret == 0); ok(parsed.scheme == NULL); ok(parsed.authority.base == NULL); ok(parsed.host.base == NULL); ok(parsed._port == 65535); ok(h2o_memis(parsed.path.base, parsed.path.len, H2O_STRLIT("/abc"))); memset(&parsed, 0x55, sizeof(parsed)); ret = h2o_url_parse_relative("http:abc", SIZE_MAX, &parsed); ok(ret == 0); ok(parsed.scheme == &H2O_URL_SCHEME_HTTP); ok(parsed.authority.base == NULL); ok(parsed.host.base == NULL); ok(parsed._port == 65535); ok(h2o_memis(parsed.path.base, parsed.path.len, H2O_STRLIT("abc"))); memset(&parsed, 0x55, sizeof(parsed)); ret = h2o_url_parse_relative("//host", SIZE_MAX, &parsed); ok(ret == 0); ok(parsed.scheme == NULL); ok(h2o_memis(parsed.authority.base, parsed.authority.len, H2O_STRLIT("host"))); ok(h2o_memis(parsed.host.base, parsed.host.len, H2O_STRLIT("host"))); ok(parsed._port == 65535); ok(h2o_memis(parsed.path.base, parsed.path.len, H2O_STRLIT("/"))); memset(&parsed, 0x55, sizeof(parsed)); ret = h2o_url_parse_relative("//host:12345/path", SIZE_MAX, &parsed); ok(ret == 0); ok(parsed.scheme == NULL); ok(h2o_memis(parsed.authority.base, parsed.authority.len, H2O_STRLIT("host:12345"))); ok(h2o_memis(parsed.host.base, parsed.host.len, H2O_STRLIT("host"))); ok(parsed._port == 12345); ok(h2o_memis(parsed.path.base, parsed.path.len, H2O_STRLIT("/path"))); memset(&parsed, 0x55, sizeof(parsed)); ret = h2o_url_parse_relative("https://host:12345/path", SIZE_MAX, &parsed); ok(ret == 0); ok(parsed.scheme == &H2O_URL_SCHEME_HTTPS); ok(h2o_memis(parsed.authority.base, parsed.authority.len, H2O_STRLIT("host:12345"))); ok(h2o_memis(parsed.host.base, parsed.host.len, H2O_STRLIT("host"))); ok(parsed._port == 12345); ok(h2o_memis(parsed.path.base, parsed.path.len, H2O_STRLIT("/path"))); }
h2o_mem_pool_t pool; h2o_url_t base, relative, resolved; h2o_iovec_t final; int ret; h2o_mem_init_pool(&pool); ret = h2o_url_parse("http://example.com/dir/index.html", SIZE_MAX, &base); ok(ret == 0); ret = h2o_url_parse_relative("../assets/jquery.js", SIZE_MAX, &relative); ok(ret == 0); final = h2o_url_resolve(&pool, &base, &relative, &resolved); ok(h2o_memis(final.base, final.len, H2O_STRLIT("http://example.com/assets/jquery.js"))); ok(resolved.scheme == &H2O_URL_SCHEME_HTTP); ok(h2o_memis(resolved.authority.base, resolved.authority.len, H2O_STRLIT("example.com"))); ok(h2o_memis(resolved.host.base, resolved.host.len, H2O_STRLIT("example.com"))); ok(resolved._port == 65535); ok(h2o_url_get_port(&resolved) == 80); ok(h2o_memis(resolved.path.base, resolved.path.len, H2O_STRLIT("/assets/jquery.js"))); ret = h2o_url_parse_relative("foo.html", SIZE_MAX, &relative); ok(ret == 0); final = h2o_url_resolve(&pool, &base, &relative, &resolved); ok(h2o_memis(final.base, final.len, H2O_STRLIT("http://example.com/dir/foo.html"))); ok(resolved.scheme == &H2O_URL_SCHEME_HTTP); ok(h2o_memis(resolved.authority.base, resolved.authority.len, H2O_STRLIT("example.com"))); ok(h2o_memis(resolved.host.base, resolved.host.len, H2O_STRLIT("example.com"))); ok(resolved._port == 65535); ok(h2o_url_get_port(&resolved) == 80); ok(h2o_memis(resolved.path.base, resolved.path.len, H2O_STRLIT("/dir/foo.html")));
static int on_req(h2o_handler_t *_self, h2o_req_t *req) { h2o_file_handler_t *self = (void*)_self; h2o_buf_t vpath, mime_type; char *rpath; size_t rpath_len; struct st_h2o_sendfile_generator_t *generator = NULL; size_t if_modified_since_header_index, if_none_match_header_index; int is_dir; /* only accept GET (TODO accept HEAD as well) */ if (! h2o_memis(req->method.base, req->method.len, H2O_STRLIT("GET"))) return -1; /* prefix match */ if (req->path.len < self->virtual_path.len || memcmp(req->path.base, self->virtual_path.base, self->virtual_path.len) != 0) return -1; /* normalize path */ vpath = h2o_normalize_path(&req->pool, req->path.base + self->virtual_path.len - 1, req->path.len - self->virtual_path.len + 1); if (vpath.len > PATH_MAX) return -1; /* build path (still unterminated at the end of the block) */ rpath = alloca( self->real_path.len + (vpath.len - 1) /* exclude "/" at the head */ + 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, vpath.base + 1, vpath.len - 1); rpath_len += vpath.len - 1; /* build generator (as well as terminating the rpath and its length upon success) */ if (rpath[rpath_len - 1] == '/') { h2o_buf_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->pool, rpath, &is_dir)) != NULL) { rpath_len += index_file->len; break; } if (is_dir) { /* note: apache redirects "path/" to "path/index.txt/" if index.txt is a dir */ char *path = alloca(req->path.len + index_file->len + 1); size_t path_len = sprintf(path, "%.*s%.*s", (int)req->path.len, req->path.base, (int)index_file->len, index_file->base); return redirect_to_dir(req, path, path_len); } if (errno != ENOENT) break; } } else { rpath[rpath_len] = '\0'; generator = create_generator(&req->pool, rpath, &is_dir); if (generator == NULL && is_dir) return redirect_to_dir(req, req->path.base, req->path.len); } /* return error if failed */ if (generator == NULL) { if (errno == ENOENT) { h2o_send_error(req, 404, "File Not Found", "file not found"); } else { h2o_send_error(req, 403, "Access Forbidden", "access forbidden"); } return 0; } if ((if_none_match_header_index = h2o_find_header(&req->headers, H2O_TOKEN_IF_NONE_MATCH, SIZE_MAX)) != -1) { h2o_buf_t *if_none_match = &req->headers.entries[if_none_match_header_index].value; if (h2o_memis(if_none_match->base, if_none_match->len, generator->etag_buf, generator->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_buf_t *if_modified_since = &req->headers.entries[if_modified_since_header_index].value; if (h2o_memis(if_modified_since->base, if_modified_since->len, generator->last_modified_buf, H2O_TIMESTR_RFC1123_LEN)) goto NotModified; } /* obtain mime type */ mime_type = h2o_mimemap_get_type(self->mimemap, h2o_get_filext(rpath, rpath_len)); /* return file */ do_send_file(generator, req, 200, "OK", mime_type); return 0; NotModified: req->res.status = 304; req->res.reason = "Not Modified"; h2o_send_inline(req, NULL, 0); do_close(&generator->super, req); return 0; }
void test_lib__file_c() { h2o_globalconf_t globalconf; h2o_hostconf_t *hostconf; h2o_context_t ctx; h2o_config_init(&globalconf); hostconf = h2o_config_register_host(&globalconf, "default"); h2o_file_register(hostconf, "/", "t/00unit/file", NULL, NULL); h2o_context_init(&ctx, test_loop, &globalconf); { h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx); conn->req.method = h2o_buf_init(H2O_STRLIT("GET")); conn->req.path = h2o_buf_init(H2O_STRLIT("/")); h2o_loopback_run_loop(conn); ok(conn->req.res.status == 200); ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_TYPE, "text/html")); ok(h2o_memis(conn->body->bytes, conn->body->size, H2O_STRLIT("hello html\n"))); h2o_loopback_destroy(conn); } { h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx); conn->req.method = h2o_buf_init(H2O_STRLIT("GET")); conn->req.path = h2o_buf_init(H2O_STRLIT("/index.html")); h2o_loopback_run_loop(conn); ok(conn->req.res.status == 200); ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_TYPE, "text/html")); ok(h2o_memis(conn->body->bytes, conn->body->size, H2O_STRLIT("hello html\n"))); h2o_loopback_destroy(conn); } { h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx); conn->req.method = h2o_buf_init(H2O_STRLIT("GET")); conn->req.path = h2o_buf_init(H2O_STRLIT("/1000.txt")); h2o_loopback_run_loop(conn); ok(conn->req.res.status == 200); ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_TYPE, "text/plain")); ok(conn->body->size == 1000); ok(strcmp(sha1sum(conn->body->bytes, conn->body->size), "dfd3ae1f5c475555fad62efe42e07309fa45f2ed") == 0); h2o_loopback_destroy(conn); } { h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx); conn->req.method = h2o_buf_init(H2O_STRLIT("GET")); conn->req.path = h2o_buf_init(H2O_STRLIT("/1000000.txt")); h2o_loopback_run_loop(conn); ok(conn->req.res.status == 200); ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_TYPE, "text/plain")); ok(conn->body->size == 1000000); ok(strcmp(sha1sum(conn->body->bytes, conn->body->size), "00c8ab71d0914dce6a1ec2eaa0fda0df7044b2a2") == 0); h2o_loopback_destroy(conn); } { h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx); conn->req.method = h2o_buf_init(H2O_STRLIT("GET")); conn->req.path = h2o_buf_init(H2O_STRLIT("/index_txt/")); h2o_loopback_run_loop(conn); ok(conn->req.res.status == 200); ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_TYPE, "text/plain")); ok(h2o_memis(conn->body->bytes, conn->body->size, H2O_STRLIT("hello text\n"))); h2o_loopback_destroy(conn); } { h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx); conn->req.method = h2o_buf_init(H2O_STRLIT("GET")); conn->req.path = h2o_buf_init(H2O_STRLIT("/index_txt")); h2o_loopback_run_loop(conn); ok(conn->req.res.status == 301); ok(check_header(&conn->req.res, H2O_TOKEN_LOCATION, "http://default/index_txt/")); h2o_loopback_destroy(conn); } { h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx); conn->req.method = h2o_buf_init(H2O_STRLIT("GET")); conn->req.path = h2o_buf_init(H2O_STRLIT("/index_txt_as_dir/")); h2o_loopback_run_loop(conn); ok(conn->req.res.status == 301); ok(check_header(&conn->req.res, H2O_TOKEN_LOCATION, "http://default/index_txt_as_dir/index.txt/")); h2o_loopback_destroy(conn); } h2o_context_dispose(&ctx); h2o_config_dispose(&globalconf); }