static int send_dir_listing(h2o_req_t *req, const char *path, size_t path_len, int is_get) { static h2o_generator_t generator = {NULL, NULL}; DIR *dp; h2o_buffer_t *body; h2o_iovec_t bodyvec; /* build html */ if ((dp = opendir(path)) == NULL) return -1; body = build_dir_listing_html(&req->pool, req->path_normalized, dp); closedir(dp); bodyvec = h2o_iovec_init(body->bytes, body->size); h2o_buffer_link_to_pool(body, &req->pool); /* send response */ req->res.status = 200; req->res.reason = "OK"; h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, H2O_STRLIT("text/html; charset=utf-8")); /* send headers */ if (!is_get) { h2o_send_inline(req, NULL, 0); return 0; } /* send data */ h2o_start_response(req, &generator); h2o_send(req, &bodyvec, 1, 1); return 0; }
static int reproxy_test(h2o_handler_t *self, h2o_req_t *req) { if (!h2o_memis(req->method.base, req->method.len, H2O_STRLIT("GET"))) return -1; req->res.status = 200; req->res.reason = "OK"; h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_X_REPROXY_URL, H2O_STRLIT("http://example.com:81/bar")); h2o_send_inline(req, H2O_STRLIT("you should never see this!\n")); return 0; }
void h2o_send_error(h2o_req_t *req, int status, const char *reason, const char *body) { /* FIXME setup host_config, since this function may get called before h2o_process_request is invoked */ req->http1_is_persistent = 0; req->res.status = status; req->res.reason = reason; memset(&req->res.headers, 0, sizeof(req->res.headers)); h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, H2O_STRLIT("text/plain; charset=utf-8")); h2o_send_inline(req, body, SIZE_MAX); }
void h2o_send_error(h2o_req_t *req, int status, const char *reason, const char *body, int flags) { bind_conf(req); if ((flags & H2O_SEND_ERROR_HTTP1_CLOSE_CONNECTION) != 0) req->http1_is_persistent = 0; req->res.status = status; req->res.reason = reason; req->res.content_length = strlen(body); memset(&req->res.headers, 0, sizeof(req->res.headers)); h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, H2O_STRLIT("text/plain; charset=utf-8")); h2o_send_inline(req, body, SIZE_MAX); }
static int 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 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; }