h2o_socketpool_target_t *h2o_socketpool_create_target(h2o_url_t *origin, h2o_socketpool_target_conf_t *lb_target_conf) { struct sockaddr_storage sa; socklen_t salen; h2o_socketpool_target_t *target = h2o_mem_alloc(sizeof(*target)); h2o_url_copy(NULL, &target->url, origin); assert(target->url.host.base[target->url.host.len] == '\0'); /* needs to be null-terminated in order to be used in SNI */ target->type = detect_target_type(origin, &sa, &salen); if (!(target->type == H2O_SOCKETPOOL_TYPE_SOCKADDR && sa.ss_family == AF_UNIX)) { h2o_strtolower(target->url.authority.base, target->url.authority.len); h2o_strtolower(target->url.host.base, target->url.host.len); } switch (target->type) { case H2O_SOCKETPOOL_TYPE_NAMED: target->peer.named_serv.base = h2o_mem_alloc(sizeof(H2O_UINT16_LONGEST_STR)); target->peer.named_serv.len = sprintf(target->peer.named_serv.base, "%u", (unsigned)h2o_url_get_port(&target->url)); break; case H2O_SOCKETPOOL_TYPE_SOCKADDR: assert(salen <= sizeof(target->peer.sockaddr.bytes)); memcpy(&target->peer.sockaddr.bytes, &sa, salen); target->peer.sockaddr.len = salen; break; } target->_shared.leased_count = 0; if (lb_target_conf != NULL) target->conf.weight_m1 = lb_target_conf->weight_m1; else { target->conf.weight_m1 = 0; } h2o_linklist_init_anchor(&target->_shared.sockets); return target; }
void h2o_proxy_register_reverse_proxy(h2o_pathconf_t *pathconf, h2o_url_t *upstream, h2o_proxy_config_vars_t *config) { struct rp_handler_t *self = (void *)h2o_create_handler(pathconf, sizeof(*self)); self->super.on_context_init = on_context_init; self->super.on_context_dispose = on_context_dispose; self->super.dispose = on_handler_dispose; self->super.on_req = on_req; if (config->keepalive_timeout != 0) { self->sockpool = h2o_mem_alloc(sizeof(*self->sockpool)); struct sockaddr_un sa; const char *to_sa_err; int is_ssl = upstream->scheme == &H2O_URL_SCHEME_HTTPS; if ((to_sa_err = h2o_url_host_to_sun(upstream->host, &sa)) == h2o_url_host_to_sun_err_is_not_unix_socket) { h2o_socketpool_init_by_hostport(self->sockpool, upstream->host, h2o_url_get_port(upstream), is_ssl, SIZE_MAX /* FIXME */); } else { assert(to_sa_err == NULL); h2o_socketpool_init_by_address(self->sockpool, (void *)&sa, sizeof(sa), is_ssl, SIZE_MAX /* FIXME */); } } h2o_url_copy(NULL, &self->upstream, upstream); h2o_strtolower(self->upstream.host.base, self->upstream.host.len); self->config = *config; if (self->config.ssl_ctx != NULL) CRYPTO_add(&self->config.ssl_ctx->references, 1, CRYPTO_LOCK_SSL_CTX); }
h2o_hostconf_t *h2o_config_register_host(h2o_globalconf_t *config, h2o_iovec_t host, uint16_t port) { h2o_hostconf_t *hostconf; assert(host.len != 0); /* create hostconf */ hostconf = create_hostconf(config); hostconf->authority.host = h2o_strdup(NULL, host.base, host.len); h2o_strtolower(hostconf->authority.host.base, hostconf->authority.host.len); hostconf->authority.port = port; if (hostconf->authority.port == 65535) { hostconf->authority.hostport = hostconf->authority.host; } else { hostconf->authority.hostport.base = h2o_mem_alloc(hostconf->authority.host.len + sizeof("[]:65535")); if (strchr(hostconf->authority.host.base, ':') != NULL) { hostconf->authority.hostport.len = sprintf(hostconf->authority.hostport.base, "[%s]:%" PRIu16, hostconf->authority.host.base, port); } else { hostconf->authority.hostport.len = sprintf(hostconf->authority.hostport.base, "%s:%" PRIu16, hostconf->authority.host.base, port); } } /* append to the list */ h2o_append_to_null_terminated_list((void *)&config->hosts, hostconf); return hostconf; }
static ssize_t init_headers(h2o_mem_pool_t *pool, h2o_headers_t *headers, const struct phr_header *src, size_t len, h2o_iovec_t *connection, h2o_iovec_t *host, h2o_iovec_t *upgrade, h2o_iovec_t *expect) { ssize_t entity_header_index = -1; assert(headers->size == 0); /* setup */ if (len != 0) { size_t i; h2o_vector_reserve(pool, headers, len); for (i = 0; i != len; ++i) { const h2o_token_t *name_token; char orig_case[src[i].name_len]; /* preserve the original case */ memcpy(orig_case, src[i].name, src[i].name_len); /* convert to lower-case in-place */ h2o_strtolower((char *)src[i].name, src[i].name_len); if ((name_token = h2o_lookup_token(src[i].name, src[i].name_len)) != NULL) { if (name_token->is_init_header_special) { if (name_token == H2O_TOKEN_HOST) { host->base = (char *)src[i].value; host->len = src[i].value_len; } else if (name_token == H2O_TOKEN_CONTENT_LENGTH) { if (entity_header_index == -1) entity_header_index = i; } else if (name_token == H2O_TOKEN_TRANSFER_ENCODING) { entity_header_index = i; } else if (name_token == H2O_TOKEN_EXPECT) { expect->base = (char *)src[i].value; expect->len = src[i].value_len; } else if (name_token == H2O_TOKEN_UPGRADE) { upgrade->base = (char *)src[i].value; upgrade->len = src[i].value_len; } else { assert(!"logic flaw"); } } else { h2o_add_header(pool, headers, name_token, orig_case, src[i].value, src[i].value_len); if (name_token == H2O_TOKEN_CONNECTION) *connection = headers->entries[headers->size - 1].value; } } else { h2o_add_header_by_str(pool, headers, src[i].name, src[i].name_len, 0, orig_case, src[i].value, src[i].value_len); } } } return entity_header_index; }
h2o_hostconf_t *h2o_config_register_host(h2o_globalconf_t *config, h2o_iovec_t host, uint16_t port) { h2o_hostconf_t *hostconf = NULL; h2o_iovec_t host_lc; assert(host.len != 0); /* convert hostname to lowercase */ host_lc = h2o_strdup(NULL, host.base, host.len); h2o_strtolower(host_lc.base, host_lc.len); { /* return NULL if given authority is already registered */ h2o_hostconf_t **p; for (p = config->hosts; *p != NULL; ++p) if (h2o_memis((*p)->authority.host.base, (*p)->authority.host.len, host_lc.base, host_lc.len) && (*p)->authority.port == port) goto Exit; } /* create hostconf */ hostconf = create_hostconf(config); hostconf->authority.host = host_lc; host_lc = (h2o_iovec_t){}; hostconf->authority.port = port; if (hostconf->authority.port == 65535) { hostconf->authority.hostport = hostconf->authority.host; } else { hostconf->authority.hostport.base = h2o_mem_alloc(hostconf->authority.host.len + sizeof("[]:65535")); if (strchr(hostconf->authority.host.base, ':') != NULL) { hostconf->authority.hostport.len = sprintf(hostconf->authority.hostport.base, "[%s]:%" PRIu16, hostconf->authority.host.base, port); } else { hostconf->authority.hostport.len = sprintf(hostconf->authority.hostport.base, "%s:%" PRIu16, hostconf->authority.host.base, port); } } /* append to the list */ h2o_append_to_null_terminated_list((void *)&config->hosts, hostconf); Exit: free(host_lc.base); return hostconf; }
static int extract_name(const char *src, size_t len, h2o_iovec_t **_name) { h2o_iovec_t name; const h2o_token_t *name_token; name = h2o_str_stripws(src, len); if (name.len == 0) return -1; name = h2o_strdup(NULL, name.base, name.len); h2o_strtolower(name.base, name.len); if ((name_token = h2o_lookup_token(name.base, name.len)) != NULL) { *_name = (h2o_iovec_t *)&name_token->buf; free(name.base); } else { *_name = h2o_mem_alloc(sizeof(**_name)); **_name = name; } return 0; }
static h2o_iovec_t strdup_lowercased(const char *s, size_t len) { h2o_iovec_t v = h2o_strdup(NULL, s, len); h2o_strtolower(v.base, v.len); return v; }
static void on_head(h2o_socket_t *sock, const char *err) { struct st_h2o_http1client_t *client = sock->data; int minor_version, version, http_status, rlen, is_eos; const char *msg; #define MAX_HEADERS 100 h2o_header_t *headers; h2o_iovec_t *header_names; size_t msg_len, num_headers, i; h2o_socket_cb reader; h2o_timer_unlink(&client->super._timeout); if (err != NULL) { on_error_before_head(client, "I/O error (head)"); return; } headers = h2o_mem_alloc_pool(client->super.pool, *headers, MAX_HEADERS); header_names = h2o_mem_alloc_pool(client->super.pool, *header_names, MAX_HEADERS); /* continue parsing the responses until we see a final one */ while (1) { /* parse response */ struct phr_header src_headers[MAX_HEADERS]; num_headers = MAX_HEADERS; rlen = phr_parse_response(sock->input->bytes, sock->input->size, &minor_version, &http_status, &msg, &msg_len, src_headers, &num_headers, 0); switch (rlen) { case -1: /* error */ on_error_before_head(client, "failed to parse the response"); return; case -2: /* incomplete */ h2o_timer_link(client->super.ctx->loop, client->super.ctx->io_timeout, &client->super._timeout); return; } version = 0x100 | (minor_version != 0); /* fill-in the headers */ for (i = 0; i != num_headers; ++i) { const h2o_token_t *token; char *orig_name = h2o_strdup(client->super.pool, src_headers[i].name, src_headers[i].name_len).base; h2o_strtolower((char *)src_headers[i].name, src_headers[i].name_len); token = h2o_lookup_token(src_headers[i].name, src_headers[i].name_len); if (token != NULL) { headers[i].name = (h2o_iovec_t *)&token->buf; } else { header_names[i] = h2o_iovec_init(src_headers[i].name, src_headers[i].name_len); headers[i].name = &header_names[i]; } headers[i].value = h2o_iovec_init(src_headers[i].value, src_headers[i].value_len); headers[i].orig_name = orig_name; headers[i].flags = (h2o_header_flags_t){0}; } if (!(100 <= http_status && http_status <= 199 && http_status != 101)) break; if (client->super.informational_cb != NULL && client->super.informational_cb(&client->super, version, http_status, h2o_iovec_init(msg, msg_len), headers, num_headers) != 0) { close_client(client); return; } h2o_buffer_consume(&client->sock->input, rlen); if (client->sock->input->size == 0) { h2o_timer_link(client->super.ctx->loop, client->super.ctx->io_timeout, &client->super._timeout); return; } } client->super.timings.response_start_at = h2o_gettimeofday(client->super.ctx->loop); /* parse the headers */ reader = on_body_until_close; client->_do_keepalive = minor_version >= 1; for (i = 0; i != num_headers; ++i) { if (headers[i].name == &H2O_TOKEN_CONNECTION->buf) { if (h2o_contains_token(headers[i].value.base, headers[i].value.len, H2O_STRLIT("keep-alive"), ',')) { client->_do_keepalive = 1; } else { client->_do_keepalive = 0; } } else if (headers[i].name == &H2O_TOKEN_TRANSFER_ENCODING->buf) { if (h2o_memis(headers[i].value.base, headers[i].value.len, H2O_STRLIT("chunked"))) { /* precond: _body_decoder.chunked is zero-filled */ client->_body_decoder.chunked.decoder.consume_trailer = 1; reader = on_req_chunked; } else if (h2o_memis(headers[i].value.base, headers[i].value.len, H2O_STRLIT("identity"))) { /* continue */ } else { on_error_before_head(client, "unexpected type of transfer-encoding"); return; } } else if (headers[i].name == &H2O_TOKEN_CONTENT_LENGTH->buf) { if ((client->_body_decoder.content_length.bytesleft = h2o_strtosize(headers[i].value.base, headers[i].value.len)) == SIZE_MAX) { on_error_before_head(client, "invalid content-length"); return; } if (reader != on_req_chunked) reader = on_body_content_length; } } /* RFC 2616 4.4 */ if (client->_method_is_head || http_status == 101 || http_status == 204 || http_status == 304) { is_eos = 1; client->super.timings.response_end_at = h2o_gettimeofday(client->super.ctx->loop); } else { is_eos = 0; /* close the connection if impossible to determine the end of the response (RFC 7230 3.3.3) */ if (reader == on_body_until_close) client->_do_keepalive = 0; } /* call the callback. sock may be stealed */ client->bytes_to_consume = rlen; client->super._cb.on_body = client->super._cb.on_head(&client->super, is_eos ? h2o_httpclient_error_is_eos : NULL, version, http_status, h2o_iovec_init(msg, msg_len), headers, num_headers, 1); if (is_eos) { close_client(client); return; } else if (client->super._cb.on_body == NULL) { client->_do_keepalive = 0; close_client(client); return; } h2o_buffer_consume(&sock->input, client->bytes_to_consume); client->bytes_to_consume = 0; client->sock->bytes_read = client->sock->input->size; client->super._timeout.cb = on_body_timeout; h2o_socket_read_start(sock, reader); reader(client->sock, 0); #undef MAX_HEADERS }