diff --git a/src/shrpx_config.cc b/src/shrpx_config.cc index 6196e6bf..dc1de8ba 100644 --- a/src/shrpx_config.cc +++ b/src/shrpx_config.cc @@ -804,7 +804,7 @@ namespace { // as catch-all. We also parse protocol specified in |src_proto|. // // This function returns 0 if it succeeds, or -1. -int parse_mapping(Config *config, DownstreamAddrConfig addr, +int parse_mapping(Config *config, DownstreamAddrConfig &addr, const StringRef &src_pattern, const StringRef &src_params) { // This returns at least 1 element (it could be empty string). We // will append '/' to all patterns, so it becomes catch-all pattern. @@ -824,7 +824,7 @@ int parse_mapping(Config *config, DownstreamAddrConfig addr, addr.rise = params.rise; addr.proto = params.proto; addr.tls = params.tls; - addr.sni = ImmutableString{std::begin(params.sni), std::end(params.sni)}; + addr.sni = make_string_ref(downstreamconf.balloc, params.sni); auto &routerconf = downstreamconf.router; auto &router = routerconf.router; @@ -833,18 +833,31 @@ int parse_mapping(Config *config, DownstreamAddrConfig addr, for (const auto &raw_pattern : mapping) { auto done = false; - std::string pattern; + StringRef pattern; auto slash = std::find(std::begin(raw_pattern), std::end(raw_pattern), '/'); if (slash == std::end(raw_pattern)) { - // This effectively makes empty pattern to "/". - pattern.assign(std::begin(raw_pattern), std::end(raw_pattern)); - util::inp_strlower(pattern); - pattern += '/'; + // This effectively makes empty pattern to "/". 2 for '/' and + // terminal NULL character. + auto iov = make_byte_ref(downstreamconf.balloc, raw_pattern.size() + 2); + auto p = iov.base; + p = std::copy(std::begin(raw_pattern), std::end(raw_pattern), p); + util::inp_strlower(iov.base, p); + *p++ = '/'; + *p = '\0'; + pattern = StringRef{iov.base, p}; } else { - pattern.assign(std::begin(raw_pattern), slash); - util::inp_strlower(pattern); - pattern += http2::normalize_path(StringRef{slash, std::end(raw_pattern)}, - StringRef{}); + auto path = http2::normalize_path(downstreamconf.balloc, + StringRef{slash, std::end(raw_pattern)}, + StringRef{}); + auto iov = make_byte_ref(downstreamconf.balloc, + std::distance(std::begin(raw_pattern), slash) + + path.size() + 1); + auto p = iov.base; + p = std::copy(std::begin(raw_pattern), slash, p); + util::inp_strlower(iov.base, p); + p = std::copy(std::begin(path), std::end(path), p); + *p = '\0'; + pattern = StringRef{iov.base, p}; } for (auto &g : addr_groups) { if (g.pattern == pattern) { @@ -863,7 +876,7 @@ int parse_mapping(Config *config, DownstreamAddrConfig addr, } auto idx = addr_groups.size(); - addr_groups.emplace_back(StringRef{pattern}); + addr_groups.emplace_back(pattern); auto &g = addr_groups.back(); g.addrs.push_back(addr); g.affinity = params.affinity; @@ -886,10 +899,13 @@ int parse_mapping(Config *config, DownstreamAddrConfig addr, auto &router = wildcard_patterns.back().router; router.add_route(path, idx); - auto rev_host = host.str(); - std::reverse(std::begin(rev_host), std::end(rev_host)); + auto iov = make_byte_ref(downstreamconf.balloc, host.size() + 1); + auto p = iov.base; + p = std::reverse_copy(std::begin(host), std::end(host), p); + *p = '\0'; + auto rev_host = StringRef{iov.base, p}; - rw_router.add_route(StringRef{rev_host}, wildcard_patterns.size() - 1); + rw_router.add_route(rev_host, wildcard_patterns.size() - 1); } else { (*it).router.add_route(path, idx); } @@ -897,7 +913,7 @@ int parse_mapping(Config *config, DownstreamAddrConfig addr, continue; } - router.add_route(StringRef{g.pattern}, idx); + router.add_route(g.pattern, idx); } return 0; } @@ -1804,12 +1820,14 @@ int parse_config(Config *config, int optid, const StringRef &opt, switch (optid) { case SHRPX_OPTID_BACKEND: { + auto &downstreamconf = *config->conn.downstream; auto addr_end = std::find(std::begin(optarg), std::end(optarg), ';'); DownstreamAddrConfig addr{}; if (util::istarts_with(optarg, SHRPX_UNIX_PATH_PREFIX)) { auto path = std::begin(optarg) + SHRPX_UNIX_PATH_PREFIX.size(); - addr.host = ImmutableString(path, addr_end); + addr.host = + make_string_ref(downstreamconf.balloc, StringRef{path, addr_end}); addr.host_unix = true; } else { if (split_host_port(host, sizeof(host), &port, @@ -1817,7 +1835,7 @@ int parse_config(Config *config, int optid, const StringRef &opt, return -1; } - addr.host = ImmutableString(host); + addr.host = make_string_ref(downstreamconf.balloc, StringRef{host}); addr.port = port; } @@ -3071,13 +3089,13 @@ int configure_downstream_group(Config *config, bool http2_proxy, if (addr_groups.empty()) { DownstreamAddrConfig addr{}; - addr.host = ImmutableString::from_lit(DEFAULT_DOWNSTREAM_HOST); + addr.host = StringRef::from_lit(DEFAULT_DOWNSTREAM_HOST); addr.port = DEFAULT_DOWNSTREAM_PORT; addr.proto = PROTO_HTTP1; DownstreamAddrGroupConfig g(StringRef::from_lit("/")); g.addrs.push_back(std::move(addr)); - router.add_route(StringRef{g.pattern}, addr_groups.size()); + router.add_route(g.pattern, addr_groups.size()); addr_groups.push_back(std::move(g)); } else if (http2_proxy) { // We don't support host mapping in these cases. Move all @@ -3090,7 +3108,7 @@ int configure_downstream_group(Config *config, bool http2_proxy, std::vector().swap(addr_groups); // maybe not necessary? routerconf = RouterConfig{}; - router.add_route(StringRef{catch_all.pattern}, addr_groups.size()); + router.add_route(catch_all.pattern, addr_groups.size()); addr_groups.push_back(std::move(catch_all)); } @@ -3100,7 +3118,7 @@ int configure_downstream_group(Config *config, bool http2_proxy, auto &sni = tlsconf.backend_sni_name; for (auto &addr_group : addr_groups) { for (auto &addr : addr_group.addrs) { - addr.sni = ImmutableString{sni}; + addr.sni = StringRef{sni}; } } } @@ -3147,7 +3165,7 @@ int configure_downstream_group(Config *config, bool http2_proxy, // for AF_UNIX socket, we use "localhost" as host for backend // hostport. This is used as Host header field to backend and // not going to be passed to any syscalls. - addr.hostport = ImmutableString::from_lit("localhost"); + addr.hostport = StringRef::from_lit("localhost"); auto path = addr.host.c_str(); auto pathlen = addr.host.size(); @@ -3171,10 +3189,11 @@ int configure_downstream_group(Config *config, bool http2_proxy, continue; } - addr.hostport = ImmutableString( - util::make_http_hostport(StringRef(addr.host), addr.port)); + addr.hostport = + util::make_http_hostport(downstreamconf.balloc, addr.host, addr.port); - auto hostport = util::make_hostport(StringRef{addr.host}, addr.port); + auto hostport = + util::make_hostport(downstreamconf.balloc, addr.host, addr.port); if (resolve_hostname(&addr.addr, addr.host.c_str(), addr.port, downstreamconf.family, resolve_flags) == -1) { diff --git a/src/shrpx_config.h b/src/shrpx_config.h index a36210c0..8387dfe7 100644 --- a/src/shrpx_config.h +++ b/src/shrpx_config.h @@ -54,6 +54,7 @@ #include "template.h" #include "http2.h" #include "network.h" +#include "allocator.h" using namespace nghttp2; @@ -375,13 +376,13 @@ struct UpstreamAddr { struct DownstreamAddrConfig { Address addr; // backend address. If |host_unix| is true, this is UNIX domain - // socket path. - ImmutableString host; + // socket path. This must be NULL terminated string. + StringRef host; // :. This does not treat 80 and 443 specially. If // |host_unix| is true, this is "localhost". - ImmutableString hostport; + StringRef hostport; // hostname sent as SNI field - ImmutableString sni; + StringRef sni; size_t fall; size_t rise; // Application protocol used in this group @@ -404,9 +405,9 @@ struct AffinityHash { struct DownstreamAddrGroupConfig { DownstreamAddrGroupConfig(const StringRef &pattern) - : pattern(pattern.c_str(), pattern.size()), affinity(AFFINITY_NONE) {} + : pattern(pattern), affinity(AFFINITY_NONE) {} - ImmutableString pattern; + StringRef pattern; std::vector addrs; // Bunch of session affinity hash. Only used if affinity == // AFFINITY_IP. @@ -655,10 +656,11 @@ struct RateLimitConfig { // field. router includes all path patterns sharing the same wildcard // host. struct WildcardPattern { - WildcardPattern(const StringRef &host) - : host(std::begin(host), std::end(host)) {} + WildcardPattern(const StringRef &host) : host(host) {} - ImmutableString host; + // This may not be NULL terminated. Currently it is only used for + // comparison. + StringRef host; Router router; }; @@ -676,7 +678,8 @@ struct RouterConfig { struct DownstreamConfig { DownstreamConfig() - : timeout{}, + : balloc(1024, 1024), + timeout{}, addr_group_catch_all{0}, connections_per_host{0}, connections_per_frontend{0}, @@ -684,6 +687,12 @@ struct DownstreamConfig { response_buffer_size{0}, family{0} {} + DownstreamConfig(const DownstreamConfig &) = delete; + DownstreamConfig(DownstreamConfig &&) = delete; + DownstreamConfig &operator=(const DownstreamConfig &) = delete; + DownstreamConfig &operator=(DownstreamConfig &&) = delete; + + BlockAllocator balloc; struct { ev_tstamp read; ev_tstamp write; diff --git a/src/shrpx_worker.cc b/src/shrpx_worker.cc index 3ea416d6..2b9ae1b2 100644 --- a/src/shrpx_worker.cc +++ b/src/shrpx_worker.cc @@ -182,7 +182,8 @@ void Worker::replace_downstream_config( auto &dst = downstream_addr_groups_[i]; dst = std::make_shared(); - dst->pattern = src.pattern; + dst->pattern = + ImmutableString{std::begin(src.pattern), std::end(src.pattern)}; auto shared_addr = std::make_shared(); @@ -198,16 +199,14 @@ void Worker::replace_downstream_config( auto &dst_addr = shared_addr->addrs[j]; dst_addr.addr = src_addr.addr; - dst_addr.host = - make_string_ref(shared_addr->balloc, StringRef{src_addr.host}); + dst_addr.host = make_string_ref(shared_addr->balloc, src_addr.host); dst_addr.hostport = - make_string_ref(shared_addr->balloc, StringRef{src_addr.hostport}); + make_string_ref(shared_addr->balloc, src_addr.hostport); dst_addr.port = src_addr.port; dst_addr.host_unix = src_addr.host_unix; dst_addr.proto = src_addr.proto; dst_addr.tls = src_addr.tls; - dst_addr.sni = - make_string_ref(shared_addr->balloc, StringRef{src_addr.sni}); + dst_addr.sni = make_string_ref(shared_addr->balloc, src_addr.sni); dst_addr.fall = src_addr.fall; dst_addr.rise = src_addr.rise; diff --git a/src/util.cc b/src/util.cc index 91f469c2..c6515c25 100644 --- a/src/util.cc +++ b/src/util.cc @@ -1172,6 +1172,32 @@ std::string make_http_hostport(const StringRef &host, uint16_t port) { return hostport; } +StringRef make_http_hostport(BlockAllocator &balloc, const StringRef &host, + uint16_t port) { + if (port != 80 && port != 443) { + return make_hostport(balloc, host, port); + } + + auto ipv6 = ipv6_numeric_addr(host.c_str()); + + auto iov = make_byte_ref(balloc, host.size() + (ipv6 ? 2 : 0) + 1); + auto p = iov.base; + + if (ipv6) { + *p++ = '['; + } + + p = std::copy(std::begin(host), std::end(host), p); + + if (ipv6) { + *p++ = ']'; + } + + *p = '\0'; + + return StringRef{iov.base, p}; +} + std::string make_hostport(const StringRef &host, uint16_t port) { auto ipv6 = ipv6_numeric_addr(host.c_str()); auto serv = utos(port); @@ -1197,6 +1223,34 @@ std::string make_hostport(const StringRef &host, uint16_t port) { return hostport; } +StringRef make_hostport(BlockAllocator &balloc, const StringRef &host, + uint16_t port) { + auto ipv6 = ipv6_numeric_addr(host.c_str()); + auto serv = utos(port); + + auto iov = + make_byte_ref(balloc, host.size() + (ipv6 ? 2 : 0) + 1 + serv.size()); + auto p = iov.base; + + if (ipv6) { + *p++ = '['; + } + + p = std::copy(std::begin(host), std::end(host), p); + + if (ipv6) { + *p++ = ']'; + } + + *p++ = ':'; + + std::copy(std::begin(serv), std::end(serv), p); + + *p = '\0'; + + return StringRef{iov.base, p}; +} + namespace { void hexdump8(FILE *out, const uint8_t *first, const uint8_t *last) { auto stop = std::min(first + 8, last); diff --git a/src/util.h b/src/util.h index 28172c7c..95239ab8 100644 --- a/src/util.h +++ b/src/util.h @@ -635,10 +635,16 @@ std::string format_duration(double t); // and "]". If |port| is 80 or 443, port part is omitted. std::string make_http_hostport(const StringRef &host, uint16_t port); +StringRef make_http_hostport(BlockAllocator &balloc, const StringRef &host, + uint16_t port); + // Just like make_http_hostport(), but doesn't treat 80 and 443 // specially. std::string make_hostport(const StringRef &host, uint16_t port); +StringRef make_hostport(BlockAllocator &balloc, const StringRef &host, + uint16_t port); + // Dumps |src| of length |len| in the format similar to `hexdump -C`. void hexdump(FILE *out, const uint8_t *src, size_t len);