static int try_dynamic_request(h2o_file_handler_t *self, h2o_req_t *req, char *rpath, size_t rpath_len) { /* we have full local path in {rpath,rpath_len}, and need to split it into name and path_info */ struct stat st; size_t slash_at = self->real_path.len; while (1) { /* find the next slash (or return -1 if failed) */ for (++slash_at;; ++slash_at) { if (slash_at >= rpath_len) return -1; if (rpath[slash_at] == '/') break; } /* change the slash to '\0', and check if the file exists */ rpath[slash_at] = '\0'; if (stat(rpath, &st) != 0) return -1; if (!S_ISDIR(st.st_mode)) break; /* restore slash, and continue the search */ rpath[slash_at] = '/'; } /* file found! */ h2o_mimemap_type_t *mime_type = h2o_mimemap_get_type_by_extension(self->mimemap, h2o_get_filext(rpath, slash_at)); switch (mime_type->type) { case H2O_MIMEMAP_TYPE_MIMETYPE: return -1; case H2O_MIMEMAP_TYPE_DYNAMIC: return delegate_dynamic_request(req, self->conf_path.len + slash_at - self->real_path.len, rpath, slash_at, mime_type); } fprintf(stderr, "unknown h2o_miemmap_type_t::type (%d)\n", (int)mime_type->type); abort(); }
static int on_config_file(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) { struct st_h2o_file_configurator_t *self = (void *)cmd->configurator; h2o_mimemap_type_t *mime_type = h2o_mimemap_get_type_by_extension(*ctx->mimemap, h2o_get_filext(node->data.scalar, strlen(node->data.scalar))); h2o_file_register_file(ctx->pathconf, node->data.scalar, mime_type, self->vars->flags); return 0; }
static mrb_value h2o_mrb_req_send_file(mrb_state *mrb, mrb_value self) { h2o_mruby_internal_context_t *mruby_ctx = (h2o_mruby_internal_context_t *)mrb->ud; char *fn; int status; h2o_iovec_t content_type; int content_type_header_removed = 0; if (mruby_ctx->state != H2O_MRUBY_STATE_UNDETERMINED) mrb_raise(mrb, E_RUNTIME_ERROR, "response already sent"); mrb_get_args(mrb, "z", &fn); /* determine status and reason to be used */ if ((status = mruby_ctx->req->res.status) == 0) status = 200; { /* determine content-type (removing existing header, since it is added by h2o_file_send) */ ssize_t header_index; if ((header_index = h2o_find_header(&mruby_ctx->req->res.headers, H2O_TOKEN_CONTENT_TYPE, -1)) != -1) { content_type = mruby_ctx->req->res.headers.entries[header_index].value; h2o_delete_header(&mruby_ctx->req->res.headers, header_index); content_type_header_removed = 1; } else { const char *ext = h2o_get_filext(fn, strlen(fn)); h2o_mimemap_type_t *m = h2o_mimemap_get_type_by_extension(mruby_ctx->req->pathconf->mimemap, ext); if (m == NULL || m->type != H2O_MIMEMAP_TYPE_MIMETYPE) { m = h2o_mimemap_get_default_type(mruby_ctx->req->pathconf->mimemap); assert(m->type == H2O_MIMEMAP_TYPE_MIMETYPE); } content_type = m->data.mimetype; } } if (h2o_file_send(mruby_ctx->req, status, mruby_ctx->req->res.reason, fn, content_type, H2O_FILE_FLAG_SEND_GZIP) == 0) { /* succeeded, return true */ mruby_ctx->state = H2O_MRUBY_STATE_RESPONSE_SENT; return mrb_true_value(); } else { /* failed, restore content-type header and return false */ if (content_type_header_removed) h2o_add_header(&mruby_ctx->req->pool, &mruby_ctx->req->res.headers, H2O_TOKEN_CONTENT_TYPE, content_type.base, content_type.len); return mrb_false_value(); } }
static void test_get_filext(void) { h2o_iovec_t ext; ext = h2o_get_filext(H2O_STRLIT("/abc.txt")); ok(h2o_memis(ext.base, ext.len, H2O_STRLIT("txt"))); ext = h2o_get_filext(H2O_STRLIT("/abc.txt.gz")); ok(h2o_memis(ext.base, ext.len, H2O_STRLIT("gz"))); ext = h2o_get_filext(H2O_STRLIT("/abc.")); ok(h2o_memis(ext.base, ext.len, H2O_STRLIT(""))); ext = h2o_get_filext(H2O_STRLIT("/abc")); ok(ext.base == NULL); ext = h2o_get_filext(H2O_STRLIT("/abc.def/abc")); ok(ext.base == NULL); ext = h2o_get_filext(H2O_STRLIT("/abc.def/")); ok(ext.base == NULL); }
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 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; }