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 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))); }