static void do_proceed(h2o_generator_t *_self, h2o_req_t *req) { struct st_h2o_sendfile_generator_t *self = (void *)_self; size_t rlen; ssize_t rret; h2o_iovec_t vec; int is_final; /* read the file */ rlen = self->bytesleft; if (rlen > MAX_BUF_SIZE) rlen = MAX_BUF_SIZE; while ((rret = pread(self->file.ref->fd, self->buf, rlen, self->file.off)) == -1 && errno == EINTR) ; if (rret == -1) { req->http1_is_persistent = 0; /* FIXME need a better interface to dispose an errored response w. content-length */ h2o_send(req, NULL, 0, 1); do_close(&self->super, req); return; } self->file.off += rret; self->bytesleft -= rret; is_final = self->bytesleft == 0; /* send (and close if done) */ vec.base = self->buf; vec.len = rret; h2o_send(req, &vec, 1, is_final); if (is_final) do_close(&self->super, req); }
static void do_multirange_proceed(h2o_generator_t *_self, h2o_req_t *req) { struct st_h2o_sendfile_generator_t *self = (void *)_self; size_t rlen, used_buf = 0; ssize_t rret, vecarrsize; h2o_iovec_t vec[2]; int is_finished; if (self->bytesleft == 0) { size_t *range_cur = self->ranged.range_infos + 2 * self->ranged.current_range; size_t range_end = *range_cur + *(range_cur + 1) - 1; if (H2O_LIKELY(self->ranged.current_range != 0)) used_buf = sprintf(self->buf, "\r\n--%s\r\nContent-Type: %s\r\nContent-Range: bytes %zd-%zd/%zd\r\n\r\n", self->ranged.boundary.base, self->ranged.mimetype.base, *range_cur, range_end, self->ranged.filesize); else used_buf = sprintf(self->buf, "--%s\r\nContent-Type: %s\r\nContent-Range: bytes %zd-%zd/%zd\r\n\r\n", self->ranged.boundary.base, self->ranged.mimetype.base, *range_cur, range_end, self->ranged.filesize); self->ranged.current_range++; self->file.off = *range_cur; self->bytesleft = *++range_cur; } rlen = self->bytesleft; if (rlen + used_buf > MAX_BUF_SIZE) rlen = MAX_BUF_SIZE - used_buf; while ((rret = pread(self->file.ref->fd, self->buf + used_buf, rlen, self->file.off)) == -1 && errno == EINTR) ; if (rret == -1) goto Error; self->file.off += rret; self->bytesleft -= rret; vec[0].base = self->buf; vec[0].len = rret + used_buf; if (self->ranged.current_range == self->ranged.range_count && self->bytesleft == 0) { vec[1].base = h2o_mem_alloc_pool(&req->pool, sizeof("\r\n--") - 1 + BOUNDARY_SIZE + sizeof("--\r\n")); vec[1].len = sprintf(vec[1].base, "\r\n--%s--\r\n", self->ranged.boundary.base); vecarrsize = 2; is_finished = 1; } else { vecarrsize = 1; is_finished = 0; } h2o_send(req, vec, vecarrsize, is_finished); if (is_finished) do_close(&self->super, req); return; Error: req->http1_is_persistent = 0; h2o_send(req, NULL, 0, 1); do_close(&self->super, req); return; }
static void do_send(struct rp_generator_t *self) { assert(self->buf_sending->size == 0); swap_buffer( &self->buf_sending, self->client != NULL ? &self->client->sock->input : &self->last_content_before_send); if (self->buf_sending->size != 0) { h2o_iovec_t buf = h2o_iovec_init(self->buf_sending->bytes, self->buf_sending->size); h2o_send(self->src_req, &buf, 1, self->client == NULL); } else if (self->client == NULL) { h2o_send(self->src_req, NULL, 0, 1); } }
static void do_send(struct rp_generator_t *self) { h2o_iovec_t vecs[1]; size_t veccnt; h2o_send_state_t ststate; assert(self->sending.bytes_inflight == 0); vecs[0] = h2o_doublebuffer_prepare(&self->sending, self->client != NULL ? &self->client->sock->input : &self->last_content_before_send, self->src_req->preferred_chunk_size); if (self->client == NULL && vecs[0].len == self->sending.buf->size && self->last_content_before_send->size == 0) { veccnt = vecs[0].len != 0 ? 1 : 0; ststate = H2O_SEND_STATE_FINAL; } else { if (vecs[0].len == 0) return; veccnt = 1; ststate = H2O_SEND_STATE_IN_PROGRESS; } if (self->had_body_error) ststate = H2O_SEND_STATE_ERROR; h2o_send(self->src_req, vecs, veccnt, ststate); }
static void do_send(h2o_mruby_generator_t *generator, h2o_buffer_t **input, int is_final) { h2o_mruby_chunked_t *chunked = generator->chunked; assert(!chunked->sending.inflight); h2o_iovec_t buf = h2o_doublebuffer_prepare(&chunked->sending, input, generator->req->preferred_chunk_size); size_t bufcnt = 1; h2o_send_state_t send_state; if (is_final && buf.len == chunked->sending.buf->size && (*input)->size == 0) { if (buf.len == 0) --bufcnt; /* send error if the length of content served is smaller than content-length header value */ if (chunked->bytes_left == 0 || chunked->bytes_left == SIZE_MAX) { send_state = H2O_SEND_STATE_FINAL; } else { send_state = H2O_SEND_STATE_ERROR; } } else { if (buf.len == 0) return; send_state = H2O_SEND_STATE_IN_PROGRESS; } h2o_send(generator->req, &buf, bufcnt, send_state); }
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; }
static void __get_keys_send(get_keys_generator_t *self, int e, MDB_val* k, h2o_req_t *req) { if (0 == e && 0 >= strncmp(k->mv_data, self->key->s, min(self->key->len, k->mv_size))) { #define SEND_BUFS 2 h2o_iovec_t body[SEND_BUFS]; body[0] = h2o_iovec_init(k->mv_data, k->mv_size); body[1] = h2o_iovec_init("\n", 1); h2o_send(req, body, SEND_BUFS, 0); } else { h2o_send(req, NULL, 0, 1); __get_keys_close((h2o_generator_t*)self, req); } }
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 void complete_fortunes(struct st_h2o_generator_t *self, h2o_req_t *req) { fortune_ctx_t * const fortune_ctx = H2O_STRUCT_FROM_MEMBER(fortune_ctx_t, generator, self); iovec_list_t * const iovec_list = H2O_STRUCT_FROM_MEMBER(iovec_list_t, l, fortune_ctx->iovec_list); fortune_ctx->iovec_list = iovec_list->l.next; const h2o_send_state_t state = fortune_ctx->iovec_list ? H2O_SEND_STATE_IN_PROGRESS : H2O_SEND_STATE_FINAL; h2o_send(req, iovec_list->iov, iovec_list->iovcnt, state); }
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; }
static void do_send(h2o_mruby_generator_t *generator, h2o_buffer_t **input, int is_final) { h2o_mruby_chunked_t *chunked = generator->chunked; assert(chunked->sending.bytes_inflight == 0); h2o_iovec_t buf = h2o_doublebuffer_prepare(&chunked->sending, input, generator->req->preferred_chunk_size); size_t bufcnt = 1; if (is_final && buf.len == chunked->sending.buf->size && (*input)->size == 0) { if (buf.len == 0) --bufcnt; /* terminate the H1 connection if the length of content served did not match the value sent in content-length header */ if (chunked->bytes_left != SIZE_MAX && chunked->bytes_left != 0) generator->req->http1_is_persistent = 0; } else { if (buf.len == 0) return; is_final = 0; } h2o_send(generator->req, &buf, bufcnt, is_final ? H2O_SEND_STATE_FINAL : H2O_SEND_STATE_IN_PROGRESS); }
static void do_send(struct rp_generator_t *self) { h2o_iovec_t vecs[1]; size_t veccnt; int is_eos; assert(self->sending.bytes_inflight == 0); vecs[0] = h2o_doublebuffer_prepare(&self->sending, self->client != NULL ? &self->client->sock->input : &self->last_content_before_send, self->src_req->preferred_chunk_size); if (self->client == NULL && vecs[0].len == self->sending.buf->size && self->last_content_before_send->size == 0) { veccnt = vecs[0].len != 0 ? 1 : 0; is_eos = 1; } else { if (vecs[0].len == 0) return; veccnt = 1; is_eos = 0; } h2o_send(self->src_req, vecs, veccnt, is_eos); }
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 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; }
/** 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 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; }