diff --git a/src/http2.h b/src/http2.h index 8f9628c3..a96a0fa0 100644 --- a/src/http2.h +++ b/src/http2.h @@ -151,6 +151,12 @@ nghttp2_nv make_nv_ls_nocopy(const char(&name)[N], const std::string &value) { NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE}; } +template +nghttp2_nv make_nv_ls_nocopy(const char(&name)[N], const StringAdaptor &value) { + return {(uint8_t *)name, (uint8_t *)value.c_str(), N - 1, value.size(), + NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE}; +} + // Appends headers in |headers| to |nv|. |headers| must be indexed // before this call (its element's token field is assigned). Certain // headers, including disallowed headers in HTTP/2 spec and headers diff --git a/src/shrpx.cc b/src/shrpx.cc index 0484f910..4a2103fa 100644 --- a/src/shrpx.cc +++ b/src/shrpx.cc @@ -2474,7 +2474,7 @@ int main(int argc, char **argv) { if (get_config()->downstream_addr_groups.empty()) { DownstreamAddr addr; - addr.host = strcopy(DEFAULT_DOWNSTREAM_HOST); + addr.host = VString(DEFAULT_DOWNSTREAM_HOST); addr.port = DEFAULT_DOWNSTREAM_PORT; DownstreamAddrGroup g("/"); @@ -2513,7 +2513,7 @@ int main(int argc, char **argv) { LOG(INFO) << "Host-path pattern: group " << i << ": '" << g.pattern.get() << "'"; for (auto &addr : g.addrs) { - LOG(INFO) << "group " << i << " -> " << addr.host.get() + LOG(INFO) << "group " << i << " -> " << addr.host.c_str() << (addr.host_unix ? "" : ":" + util::utos(addr.port)); } } @@ -2537,10 +2537,10 @@ int main(int argc, char **argv) { // hostport. This is used as Host header field to backend and // not going to be passed to any syscalls. addr.hostport = - strcopy(util::make_hostport("localhost", get_config()->port)); + VString(util::make_hostport("localhost", get_config()->port)); - auto path = addr.host.get(); - auto pathlen = strlen(path); + auto path = addr.host.c_str(); + auto pathlen = addr.host.size(); if (pathlen + 1 > sizeof(addr.addr.su.un.sun_path)) { LOG(FATAL) << "UNIX domain socket path " << path << " is too long > " @@ -2559,10 +2559,11 @@ int main(int argc, char **argv) { continue; } - addr.hostport = strcopy(util::make_hostport(addr.host.get(), addr.port)); + addr.hostport = + VString(util::make_hostport(addr.host.c_str(), addr.port)); if (resolve_hostname( - &addr.addr, addr.host.get(), addr.port, + &addr.addr, addr.host.c_str(), addr.port, get_config()->backend_ipv4 ? AF_INET : (get_config()->backend_ipv6 ? AF_INET6 : AF_UNSPEC)) == -1) { diff --git a/src/shrpx_config.cc b/src/shrpx_config.cc index feaed202..46027f94 100644 --- a/src/shrpx_config.cc +++ b/src/shrpx_config.cc @@ -79,9 +79,8 @@ TicketKeys::~TicketKeys() { } DownstreamAddr::DownstreamAddr(const DownstreamAddr &other) - : addr(other.addr), host(strcopy(other.host)), - hostport(strcopy(other.hostport)), port(other.port), - host_unix(other.host_unix) {} + : addr(other.addr), host(other.host), hostport(other.hostport), + port(other.port), host_unix(other.host_unix) {} DownstreamAddr &DownstreamAddr::operator=(const DownstreamAddr &other) { if (this == &other) { @@ -89,8 +88,8 @@ DownstreamAddr &DownstreamAddr::operator=(const DownstreamAddr &other) { } addr = other.addr; - host = strcopy(other.host); - hostport = strcopy(other.hostport); + host = other.host; + hostport = other.hostport; port = other.port; host_unix = other.host_unix; @@ -1394,7 +1393,7 @@ int parse_config(const char *opt, const char *optarg, DownstreamAddr addr; if (util::istarts_with(optarg, SHRPX_UNIX_PATH_PREFIX)) { auto path = optarg + str_size(SHRPX_UNIX_PATH_PREFIX); - addr.host = strcopy(path, pat_delim); + addr.host = VString(path, pat_delim); addr.host_unix = true; } else { if (split_host_port(host, sizeof(host), &port, optarg, @@ -1402,7 +1401,7 @@ int parse_config(const char *opt, const char *optarg, return -1; } - addr.host = strcopy(host); + addr.host = VString(host); addr.port = port; } diff --git a/src/shrpx_config.h b/src/shrpx_config.h index a25205fe..8d5a4e72 100644 --- a/src/shrpx_config.h +++ b/src/shrpx_config.h @@ -245,8 +245,8 @@ struct DownstreamAddr { Address addr; // backend address. If |host_unix| is true, this is UNIX domain // socket path. - std::unique_ptr host; - std::unique_ptr hostport; + VString host; + VString hostport; // backend port. 0 if |host_unix| is true. uint16_t port; // true if |host| contains UNIX domain socket path. diff --git a/src/shrpx_http2_downstream_connection.cc b/src/shrpx_http2_downstream_connection.cc index 730370cc..350d618a 100644 --- a/src/shrpx_http2_downstream_connection.cc +++ b/src/shrpx_http2_downstream_connection.cc @@ -264,19 +264,18 @@ int Http2DownstreamConnection::push_request_headers() { // addr_idx here. auto addr_idx = http2session_->get_addr_idx(); auto group = http2session_->get_group(); - auto downstream_hostport = get_config() - ->downstream_addr_groups[group] - .addrs[addr_idx] - .hostport.get(); + const auto &downstream_hostport = + get_config()->downstream_addr_groups[group].addrs[addr_idx].hostport; // For HTTP/1.0 request, there is no authority in request. In that // case, we use backend server's host nonetheless. - const char *authority = downstream_hostport; + auto authority = StringAdaptor(downstream_hostport); + if (no_host_rewrite && !req.authority.empty()) { - authority = req.authority.c_str(); + authority = req.authority; } - downstream_->set_request_downstream_host(authority); + downstream_->set_request_downstream_host(authority.str()); size_t num_cookies = 0; if (!get_config()->http2_no_cookie_crumbling) { @@ -312,12 +311,12 @@ int Http2DownstreamConnection::push_request_headers() { } if (!req.no_authority) { - nva.push_back(http2::make_nv_lc_nocopy(":authority", authority)); + nva.push_back(http2::make_nv_ls_nocopy(":authority", authority)); } else { - nva.push_back(http2::make_nv_lc_nocopy("host", authority)); + nva.push_back(http2::make_nv_ls_nocopy("host", authority)); } } else { - nva.push_back(http2::make_nv_lc_nocopy(":authority", authority)); + nva.push_back(http2::make_nv_ls_nocopy(":authority", authority)); } http2::copy_headers_to_nva_nocopy(nva, req.fs.headers()); diff --git a/src/shrpx_http2_session.cc b/src/shrpx_http2_session.cc index 391e5ce0..accd20d6 100644 --- a/src/shrpx_http2_session.cc +++ b/src/shrpx_http2_session.cc @@ -338,7 +338,7 @@ int Http2Session::initiate_connection() { if (get_config()->backend_tls_sni_name) { sni_name = get_config()->backend_tls_sni_name.get(); } else { - sni_name = downstream_addr.host.get(); + sni_name = downstream_addr.host.c_str(); } if (sni_name && !util::numeric_host(sni_name)) { @@ -518,13 +518,13 @@ int Http2Session::downstream_connect_proxy() { get_config()->downstream_addr_groups[group_].addrs[addr_idx_]; std::string req = "CONNECT "; - req += downstream_addr.hostport.get(); + req.append(downstream_addr.hostport.c_str(), downstream_addr.hostport.size()); if (downstream_addr.port == 80 || downstream_addr.port == 443) { req += ':'; req += util::utos(downstream_addr.port); } req += " HTTP/1.1\r\nHost: "; - req += downstream_addr.host.get(); + req.append(downstream_addr.host.c_str(), downstream_addr.host.size()); req += "\r\n"; if (get_config()->downstream_http_proxy_userinfo) { req += "Proxy-Authorization: Basic "; diff --git a/src/shrpx_http_downstream_connection.cc b/src/shrpx_http_downstream_connection.cc index 4cee09e9..9c3fc42b 100644 --- a/src/shrpx_http_downstream_connection.cc +++ b/src/shrpx_http_downstream_connection.cc @@ -211,27 +211,24 @@ int HttpDownstreamConnection::attach_downstream(Downstream *downstream) { } int HttpDownstreamConnection::push_request_headers() { - auto downstream_hostport = get_config() - ->downstream_addr_groups[group_] - .addrs[addr_idx_] - .hostport.get(); + const auto &downstream_hostport = + get_config()->downstream_addr_groups[group_].addrs[addr_idx_].hostport; const auto &req = downstream_->request(); auto connect_method = req.method == HTTP_CONNECT; // For HTTP/1.0 request, there is no authority in request. In that // case, we use backend server's host nonetheless. - const char *authority = downstream_hostport; + auto authority = StringAdaptor(downstream_hostport); auto no_host_rewrite = get_config()->no_host_rewrite || get_config()->http2_proxy || get_config()->client_proxy || connect_method; if (no_host_rewrite && !req.authority.empty()) { - authority = req.authority.c_str(); + authority = StringAdaptor(req.authority); } - auto authoritylen = strlen(authority); - downstream_->set_request_downstream_host(authority); + downstream_->set_request_downstream_host(authority.str()); auto buf = downstream_->get_request_buf(); @@ -241,14 +238,14 @@ int HttpDownstreamConnection::push_request_headers() { buf->append(" "); if (connect_method) { - buf->append(authority, authoritylen); + buf->append(authority.c_str(), authority.size()); } else if (get_config()->http2_proxy || get_config()->client_proxy) { // Construct absolute-form request target because we are going to // send a request to a HTTP/1 proxy. assert(!req.scheme.empty()); buf->append(req.scheme); buf->append("://"); - buf->append(authority, authoritylen); + buf->append(authority.c_str(), authority.size()); buf->append(req.path); } else if (req.method == HTTP_OPTIONS && req.path.empty()) { // Server-wide OPTIONS @@ -257,7 +254,7 @@ int HttpDownstreamConnection::push_request_headers() { buf->append(req.path); } buf->append(" HTTP/1.1\r\nHost: "); - buf->append(authority, authoritylen); + buf->append(authority.c_str(), authority.size()); buf->append("\r\n"); http2::build_http1_headers_from_headers(buf, req.fs.headers()); diff --git a/src/shrpx_ssl.cc b/src/shrpx_ssl.cc index 890484f2..8da8db36 100644 --- a/src/shrpx_ssl.cc +++ b/src/shrpx_ssl.cc @@ -973,7 +973,7 @@ int check_cert(SSL *ssl, const DownstreamAddr *addr) { } auto hostname = get_config()->backend_tls_sni_name ? get_config()->backend_tls_sni_name.get() - : addr->host.get(); + : addr->host.c_str(); if (verify_hostname(cert, hostname, strlen(hostname), &addr->addr) != 0) { LOG(ERROR) << "Certificate verification failed: hostname does not match"; return -1; diff --git a/src/template.h b/src/template.h index 3f92bfac..5ce17bae 100644 --- a/src/template.h +++ b/src/template.h @@ -199,6 +199,47 @@ inline std::unique_ptr strcopy(const std::unique_ptr &val) { return strcopy(val.get()); } +struct VString { + VString() : len(0) {} + VString(const char *s, size_t slen) : base(strcopy(s)), len(slen) {} + VString(const char *s) : base(strcopy(s)), len(strlen(s)) {} + VString(const std::string &s) : base(strcopy(s)), len(s.size()) {} + template + VString(InputIt first, InputIt last) + : base(strcopy(first, last)), len(std::distance(first, last)) {} + VString(const VString &other) : base(strcopy(other.base)), len(other.len) {} + VString(VString &&) = default; + VString &operator=(const VString &other) { + if (this == &other) { + return *this; + } + base = strcopy(other.base); + len = other.len; + return *this; + } + VString &operator=(VString &&other) = default; + + const char *c_str() const { return base.get(); } + size_t size() const { return len; } + + std::unique_ptr base; + size_t len; +}; + +struct StringAdaptor { + template + StringAdaptor(const T &s) + : base(s.c_str()), len(s.size()) {} + + const char *c_str() const { return base; } + size_t size() const { return len; } + + std::string str() const { return std::string(base, len); } + + const char *base; + size_t len; +}; + inline int run_app(std::function app, int argc, char **argv) { try {