static void do_send_file(struct st_h2o_sendfile_generator_t *self, h2o_req_t *req, int status, const char *reason, h2o_buf_t mime_type) { /* link the request */ self->req = req; /* setup response */ req->res.status = status; req->res.reason = reason; req->res.content_length = self->bytesleft; h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, mime_type.base, mime_type.len); h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_LAST_MODIFIED, self->last_modified_buf, H2O_TIMESTR_RFC1123_LEN); h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_ETAG, self->etag_buf, self->etag_len); /* send data */ h2o_start_response(req, &self->super); if (req->_ostr_top->start_pull != NULL) { req->_ostr_top->start_pull(req->_ostr_top, do_pull); } else { size_t bufsz = MAX_BUF_SIZE; if (self->bytesleft < bufsz) bufsz = self->bytesleft; self->buf = h2o_mempool_alloc(&req->pool, bufsz); do_proceed(&self->super, req); } }
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 h2o_http1client_body_cb on_head(h2o_http1client_t *client, const char *errstr, int minor_version, int status, h2o_iovec_t msg, struct phr_header *headers, size_t num_headers) { struct rp_generator_t *self = client->data; size_t i; if (errstr != NULL && errstr != h2o_http1client_error_is_eos) { self->client = NULL; h2o_send_error(self->src_req, 502, "Gateway Error", errstr, 0); return NULL; } /* copy the response */ self->src_req->res.status = status; self->src_req->res.reason = h2o_strdup(&self->src_req->pool, msg.base, msg.len).base; for (i = 0; i != num_headers; ++i) { const h2o_token_t *token = h2o_lookup_token(headers[i].name, headers[i].name_len); h2o_iovec_t value; if (token != NULL) { if (token->is_connection_specific) { goto Skip; } if (token == H2O_TOKEN_CONTENT_LENGTH) { if (self->src_req->res.content_length != SIZE_MAX || (self->src_req->res.content_length = h2o_strtosize(headers[i].value, headers[i].value_len)) == SIZE_MAX) { self->client = NULL; h2o_send_error(self->src_req, 502, "Gateway Error", "invalid response from upstream", 0); return NULL; } goto Skip; } else if (token == H2O_TOKEN_LOCATION) { value = rewrite_location(&self->src_req->pool, headers[i].value, headers[i].value_len, self->upstream, self->src_req->scheme, self->src_req->authority, self->src_req->pathconf->path); goto AddHeader; } /* default behaviour, transfer the header downstream */ value = h2o_strdup(&self->src_req->pool, headers[i].value, headers[i].value_len); AddHeader: h2o_add_header(&self->src_req->pool, &self->src_req->res.headers, token, value.base, value.len); Skip: ; } else { h2o_iovec_t name = h2o_strdup(&self->src_req->pool, headers[i].name, headers[i].name_len); h2o_iovec_t value = h2o_strdup(&self->src_req->pool, headers[i].value, headers[i].value_len); h2o_add_header_by_str(&self->src_req->pool, &self->src_req->res.headers, name.base, name.len, 0, value.base, value.len); } } /* declare the start of the response */ h2o_start_response(self->src_req, &self->super); if (errstr == h2o_http1client_error_is_eos) { self->client = NULL; h2o_send(self->src_req, NULL, 0, 1); return NULL; } return on_body; }
void h2o_send_inline(h2o_req_t *req, const char *body, size_t len) { static h2o_generator_t generator = { NULL, NULL }; h2o_iovec_t buf = h2o_strdup(&req->pool, body, len); /* the function intentionally does not set the content length, since it may be used for generating 304 response, etc. */ /* req->res.content_length = buf.len; */ h2o_start_response(req, &generator); h2o_send(req, &buf, 1, 1); }
static int post_test(h2o_handler_t *self, h2o_req_t *req) { if (h2o_memis(req->method.base, req->method.len, H2O_STRLIT("POST")) && h2o_memis(req->path.base, req->path.len, H2O_STRLIT("/post-test"))) { static h2o_generator_t generator = { NULL, NULL }; req->res.status = 200; req->res.reason = "OK"; h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, H2O_STRLIT("text/plain; charset=utf-8")); h2o_start_response(req, &generator); h2o_send(req, &req->entity, 1, 1); return 0; } return -1; }
static int chunked_test(h2o_handler_t *self, h2o_req_t *req) { if (h2o_memis(req->method.base, req->method.len, H2O_STRLIT("GET")) && h2o_memis(req->path.base, req->path.len, H2O_STRLIT("/chunked-test"))) { static h2o_generator_t generator = { NULL, NULL }; h2o_buf_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; } return -1; }
static int redirect_to_dir(h2o_req_t *req, const char *path, size_t path_len) { static h2o_generator_t generator = { NULL, NULL }; static const h2o_buf_t body_prefix = { H2O_STRLIT("<!DOCTYPE html><TITLE>301 Moved Permanently</TITLE><P>The document has moved <A HREF=\"") }; static const h2o_buf_t body_suffix = { H2O_STRLIT("\">here</A>") }; h2o_buf_t url; size_t alloc_size; h2o_buf_t bufs[3]; /* determine the size of the memory needed */ alloc_size = sizeof(":///") + req->scheme.len + req->authority.len + path_len; /* allocate and build url */ url.base = h2o_mempool_alloc(&req->pool, alloc_size); url.len = sprintf(url.base, "%.*s://%.*s%.*s/", (int)req->scheme.len, req->scheme.base, (int)req->authority.len, req->authority.base, (int)path_len, path); assert(url.len + 1 == alloc_size); /* build response header */ req->res.status = 301; req->res.reason = "Moved Permanently"; memset(&req->res.headers, 0, sizeof(req->res.headers)); h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_LOCATION, url.base, url.len); h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, H2O_STRLIT("text/html; charset=utf-8")); /* build response */ bufs[0] = body_prefix; bufs[1] = h2o_htmlescape(&req->pool, url.base, url.len); bufs[2] = body_suffix; /* send */ h2o_start_response(req, &generator); h2o_send(req, bufs, 3, 1); return 0; }
/** Get a list of document keys where the key prefix matches the provided key */ static int __get_keys(h2o_req_t *req, kstr_t* key) { get_keys_generator_t *gen = h2o_mem_alloc_pool(&req->pool, sizeof(*gen)); gen->super.proceed = __get_keys_proceed; gen->super.stop = __get_keys_close; gen->req = req; gen->key = key; req->res.status = 200; req->res.reason = "OK"; h2o_start_response(req, &gen->super); int e; e = mdb_txn_begin(sv->db_env, NULL, MDB_RDONLY, &gen->txn); if (0 != e) mdb_fatal(e); e = mdb_cursor_open(gen->txn, sv->docs, &gen->curs); if (0 != e) mdb_fatal(e); MDB_val k = { .mv_size = key->len, .mv_data = key->s }, v;
mrb_value h2o_mruby_send_chunked_init(h2o_mruby_generator_t *generator, mrb_value body) { mrb_state *mrb = generator->ctx->shared->mrb; h2o_mruby_http_request_context_t *client = h2o_mruby_http_set_shortcut(mrb, body, on_shortcut_notify, generator); if (mrb->exc != NULL) { return mrb_nil_value(); } h2o_mruby_chunked_t *chunked = h2o_mem_alloc_pool(&generator->req->pool, *chunked, 1); 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; mrb_value ret; h2o_start_response(generator->req, &generator->super); if (client != NULL) { chunked->type = H2O_MRUBY_CHUNKED_TYPE_SHORTCUT; chunked->shortcut.client = client; chunked->shortcut.remaining = NULL; on_shortcut_notify(generator); ret = mrb_nil_value(); } else { chunked->type = H2O_MRUBY_CHUNKED_TYPE_CALLBACK; h2o_buffer_init(&chunked->callback.receiving, &h2o_socket_buffer_prototype); ret = mrb_ary_entry(generator->ctx->shared->constants, H2O_MRUBY_CHUNKED_PROC_EACH_TO_FIBER); } mrb_gc_register(generator->ctx->shared->mrb, body); chunked->body_obj = body; return ret; }
static void do_send_file(struct st_h2o_sendfile_generator_t *self, h2o_req_t *req, int status, const char *reason, h2o_iovec_t mime_type, h2o_mime_attributes_t *mime_attr, int is_get) { /* link the request */ self->req = req; /* setup response */ req->res.status = status; req->res.reason = reason; req->res.content_length = self->bytesleft; req->res.mime_attr = mime_attr; if (self->ranged.range_count > 1) { mime_type.base = h2o_mem_alloc_pool(&req->pool, 52); mime_type.len = sprintf(mime_type.base, "multipart/byteranges; boundary=%s", self->ranged.boundary.base); } h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_TYPE, mime_type.base, mime_type.len); h2o_filecache_get_last_modified(self->file.ref, self->header_bufs.last_modified); h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_LAST_MODIFIED, self->header_bufs.last_modified, H2O_TIMESTR_RFC1123_LEN); add_headers_unconditional(self, req); if (self->content_encoding.base != NULL) h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_ENCODING, self->content_encoding.base, self->content_encoding.len); if (self->ranged.range_count == 0) h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_ACCEPT_RANGES, H2O_STRLIT("bytes")); else if (self->ranged.range_count == 1) { h2o_iovec_t content_range; content_range.base = h2o_mem_alloc_pool(&req->pool, 128); content_range.len = sprintf(content_range.base, "bytes %zd-%zd/%zd", self->ranged.range_infos[0], self->ranged.range_infos[0] + self->ranged.range_infos[1] - 1, self->ranged.filesize); h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_CONTENT_RANGE, content_range.base, content_range.len); } /* special path for cases where we do not need to send any data */ if (!is_get || self->bytesleft == 0) { static h2o_generator_t generator = {NULL, NULL}; h2o_start_response(req, &generator); h2o_send(req, NULL, 0, 1); do_close(&self->super, req); return; } /* send data */ h2o_start_response(req, &self->super); if (self->ranged.range_count == 1) self->file.off = self->ranged.range_infos[0]; if (req->_ostr_top->start_pull != NULL && self->ranged.range_count < 2) { req->_ostr_top->start_pull(req->_ostr_top, do_pull); } else { size_t bufsz = MAX_BUF_SIZE; if (self->bytesleft < bufsz) bufsz = self->bytesleft; self->buf = h2o_mem_alloc_pool(&req->pool, bufsz); if (self->ranged.range_count < 2) do_proceed(&self->super, req); else { self->bytesleft = 0; self->super.proceed = do_multirange_proceed; do_multirange_proceed(&self->super, req); } } }
static h2o_http1client_body_cb on_head(h2o_http1client_t *client, const char *errstr, int minor_version, int status, h2o_iovec_t msg, struct phr_header *headers, size_t num_headers) { struct rp_generator_t *self = client->data; h2o_req_t *req = self->src_req; size_t i; if (errstr != NULL && errstr != h2o_http1client_error_is_eos) { self->client = NULL; h2o_req_log_error(req, "lib/core/proxy.c", "%s", errstr); h2o_send_error(req, 502, "Gateway Error", errstr, 0); return NULL; } /* copy the response (note: all the headers must be copied; http1client discards the input once we return from this callback) */ req->res.status = status; req->res.reason = h2o_strdup(&req->pool, msg.base, msg.len).base; for (i = 0; i != num_headers; ++i) { const h2o_token_t *token = h2o_lookup_token(headers[i].name, headers[i].name_len); h2o_iovec_t value; if (token != NULL) { if (token->proxy_should_drop) { goto Skip; } if (token == H2O_TOKEN_CONTENT_LENGTH) { if (req->res.content_length != SIZE_MAX || (req->res.content_length = h2o_strtosize(headers[i].value, headers[i].value_len)) == SIZE_MAX) { self->client = NULL; h2o_req_log_error(req, "lib/core/proxy.c", "%s", "invalid response from upstream (malformed content-length)"); h2o_send_error(req, 502, "Gateway Error", "invalid response from upstream", 0); return NULL; } goto Skip; } else if (token == H2O_TOKEN_LOCATION) { if (req->res_is_delegated && (300 <= status && status <= 399) && status != 304) { self->client = NULL; h2o_iovec_t method = h2o_get_redirect_method(req->method, status); h2o_send_redirect_internal(req, method, headers[i].value, headers[i].value_len, 1); return NULL; } if (req->overrides != NULL && req->overrides->location_rewrite.match != NULL) { value = rewrite_location(&req->pool, headers[i].value, headers[i].value_len, req->overrides->location_rewrite.match, req->input.scheme, req->input.authority, req->overrides->location_rewrite.path_prefix); if (value.base != NULL) goto AddHeader; } goto AddHeaderDuped; } else if (token == H2O_TOKEN_LINK) { h2o_puth_path_in_link_header(req, headers[i].value, headers[i].value_len); } /* default behaviour, transfer the header downstream */ AddHeaderDuped: value = h2o_strdup(&req->pool, headers[i].value, headers[i].value_len); AddHeader: h2o_add_header(&req->pool, &req->res.headers, token, value.base, value.len); Skip: ; } else { h2o_iovec_t name = h2o_strdup(&req->pool, headers[i].name, headers[i].name_len); h2o_iovec_t value = h2o_strdup(&req->pool, headers[i].value, headers[i].value_len); h2o_add_header_by_str(&req->pool, &req->res.headers, name.base, name.len, 0, value.base, value.len); } } if (self->is_websocket_handshake && req->res.status == 101) { h2o_http1client_ctx_t *client_ctx = get_client_ctx(req); assert(client_ctx->websocket_timeout != NULL); h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_UPGRADE, H2O_STRLIT("websocket")); on_websocket_upgrade(self, client_ctx->websocket_timeout); self->client = NULL; return NULL; } /* declare the start of the response */ h2o_start_response(req, &self->super); if (errstr == h2o_http1client_error_is_eos) { self->client = NULL; h2o_send(req, NULL, 0, 1); return NULL; } return on_body; }
/** HTTP POST entry point for receiving entries from client * Provide the user with an ID */ static int __http_get_id(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("POST"))) return -1; /* redirect to leader if needed */ int leader = raft_get_current_leader(sv->raft); if (-1 == leader) { return h2oh_respond_with_error(req, 503, "Leader unavailable"); } else if (leader != sv->node_idx) { raft_node_t* node = raft_get_node(sv->raft, leader); peer_connection_t* leader_conn = raft_node_get_udata(node); char leader_url[LEADER_URL_LEN]; static h2o_generator_t generator = { NULL, NULL }; static h2o_iovec_t body = { .base = "", .len = 0 }; req->res.status = 301; req->res.reason = "Moved Permanently"; h2o_start_response(req, &generator); snprintf(leader_url, LEADER_URL_LEN, "http://%s:%d/", inet_ntoa(leader_conn->addr.sin_addr), leader_conn->http_port); h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_LOCATION, leader_url, strlen(leader_url)); h2o_send(req, &body, 1, 1); return 0; } int e; unsigned int ticket = __generate_ticket(); msg_entry_t entry; entry.id = rand(); entry.data.buf = (void*)&ticket; entry.data.len = sizeof(ticket); uv_mutex_lock(&sv->raft_lock); msg_entry_response_t r; e = raft_recv_entry(sv->raft, sv->node_idx, &entry, &r); if (0 != e) return h2oh_respond_with_error(req, 500, "BAD"); /* block until the entry is committed */ int done = 0; do { uv_cond_wait(&sv->appendentries_received, &sv->raft_lock); e = raft_msg_entry_response_committed(sv->raft, &r); switch (e) { case 0: /* not committed yet */ break; case 1: done = 1; uv_mutex_unlock(&sv->raft_lock); break; case -1: uv_mutex_unlock(&sv->raft_lock); return h2oh_respond_with_error(req, 400, "TRY AGAIN"); } } while (!done); /* serialize ID */ char id_str[100]; h2o_iovec_t body; sprintf(id_str, "%d", entry.id); body = h2o_iovec_init(id_str, strlen(id_str)); req->res.status = 200; req->res.reason = "OK"; h2o_start_response(req, &generator); h2o_send(req, &body, 1, 1); return 0; }
static result_return_t on_fortune_result(db_query_param_t *param, PGresult *result) { fortune_ctx_t * const fortune_ctx = H2O_STRUCT_FROM_MEMBER(fortune_ctx_t, param, param); int ret = DONE; const ExecStatusType status = PQresultStatus(result); if (status == PGRES_TUPLES_OK) { const size_t num_rows = PQntuples(result); ret = SUCCESS; for (size_t i = 0; i < num_rows; i++) { fortune_t * const fortune = h2o_mem_alloc_pool(&fortune_ctx->req->pool, sizeof(*fortune)); if (fortune) { memset(fortune, 0, sizeof(*fortune)); fortune->id.base = PQgetvalue(result, i, 0); fortune->id.len = PQgetlength(result, i, 0); fortune->message = h2o_htmlescape(&fortune_ctx->req->pool, PQgetvalue(result, i, 1), PQgetlength(result, i, 1)); fortune->l.next = fortune_ctx->result; fortune_ctx->result = &fortune->l; fortune_ctx->num_result++; if (!i) fortune->data = result; } else { send_error(INTERNAL_SERVER_ERROR, REQ_ERROR, fortune_ctx->req); ret = DONE; if (!i) PQclear(result); break; } } } else if (result) { LIBRARY_ERROR("PQresultStatus", PQresultErrorMessage(result)); send_error(BAD_GATEWAY, DB_ERROR, fortune_ctx->req); PQclear(result); } else { mustache_api_t api = {.sectget = on_fortune_section, .varget = on_fortune_variable, .write = add_iovec}; thread_context_t * const ctx = H2O_STRUCT_FROM_MEMBER(thread_context_t, event_loop.h2o_ctx, fortune_ctx->req->conn->ctx); const size_t iovcnt = MIN(MAX_IOVEC, fortune_ctx->num_result * 5 + 2); const size_t sz = offsetof(iovec_list_t, iov) + iovcnt * sizeof(h2o_iovec_t); char _Alignas(iovec_list_t) mem[sz]; iovec_list_t * const restrict iovec_list = (iovec_list_t *) mem; memset(iovec_list, 0, offsetof(iovec_list_t, iov)); iovec_list->max_iovcnt = iovcnt; fortune_ctx->iovec_list_iter = iovec_list; fortune_ctx->result = sort_fortunes(fortune_ctx->result); if (mustache_render(&api, fortune_ctx, ctx->global_data->fortunes_template)) { fortune_ctx->iovec_list = iovec_list->l.next; set_default_response_param(HTML, fortune_ctx->content_length, fortune_ctx->req); h2o_start_response(fortune_ctx->req, &fortune_ctx->generator); const h2o_send_state_t state = fortune_ctx->iovec_list ? H2O_SEND_STATE_IN_PROGRESS : H2O_SEND_STATE_FINAL; h2o_send(fortune_ctx->req, iovec_list->iov, iovec_list->iovcnt, state); } else send_error(INTERNAL_SERVER_ERROR, REQ_ERROR, fortune_ctx->req); } return ret; }