diff --git a/src/h2load.cc b/src/h2load.cc index 10dbd20f..1c9bed26 100644 --- a/src/h2load.cc +++ b/src/h2load.cc @@ -94,6 +94,7 @@ Config::Config() log_fd(-1), port(0), default_port(0), + connect_to_port(0), verbose(false), timing_script(false), base_uri_unix(false), @@ -1565,9 +1566,11 @@ void resolve_host() { const auto &resolve_host = config.connect_to_host.empty() ? config.host : config.connect_to_host; + auto port = + config.connect_to_port == 0 ? config.port : config.connect_to_port; - rv = getaddrinfo(resolve_host.c_str(), util::utos(config.port).c_str(), - &hints, &res); + rv = + getaddrinfo(resolve_host.c_str(), util::utos(port).c_str(), &hints, &res); if (rv != 0) { std::cerr << "getaddrinfo() failed: " << gai_strerror(rv) << std::endl; exit(EXIT_FAILURE); @@ -1982,8 +1985,9 @@ Options: response time when using one worker thread, but may appear slightly out of order with multiple threads due to buffering. Status code is -1 for failed streams. - --connect-to= - Host to connect instead of using the host in . + --connect-to=[:] + Host and port to connect instead of using the authority + in . -v, --verbose Output debug information. --version Display version information and exit. @@ -2270,11 +2274,20 @@ int main(int argc, char **argv) { // --log-file logfile = optarg; break; - case 11: + case 11: { // --connect-to - config.connect_to_host = optarg; + auto p = util::split_hostport(StringRef{optarg}); + int64_t port = 0; + if (p.first.empty() || + (!p.second.empty() && (port = util::parse_uint(p.second)) == -1)) { + std::cerr << "--connect-to: Invalid value " << optarg << std::endl; + exit(EXIT_FAILURE); + } + config.connect_to_host = p.first.str(); + config.connect_to_port = port; break; } + } break; default: break; diff --git a/src/h2load.h b/src/h2load.h index c3ed12ee..ca689976 100644 --- a/src/h2load.h +++ b/src/h2load.h @@ -102,6 +102,7 @@ struct Config { int log_fd; uint16_t port; uint16_t default_port; + uint16_t connect_to_port; bool verbose; bool timing_script; std::string base_uri; diff --git a/src/shrpx-unittest.cc b/src/shrpx-unittest.cc index dd4fadf1..3cf38163 100644 --- a/src/shrpx-unittest.cc +++ b/src/shrpx-unittest.cc @@ -195,6 +195,8 @@ int main(int argc, char *argv[]) { !CU_add_test(pSuite, "util_decode_hex", shrpx::test_util_decode_hex) || !CU_add_test(pSuite, "util_extract_host", shrpx::test_util_extract_host) || + !CU_add_test(pSuite, "util_split_hostport", + shrpx::test_util_split_hostport) || !CU_add_test(pSuite, "gzip_inflate", test_nghttp2_gzip_inflate) || !CU_add_test(pSuite, "buffer_write", nghttp2::test_buffer_write) || !CU_add_test(pSuite, "pool_recycle", nghttp2::test_pool_recycle) || diff --git a/src/util.cc b/src/util.cc index bb30fcd3..187fd3a8 100644 --- a/src/util.cc +++ b/src/util.cc @@ -1537,6 +1537,41 @@ StringRef extract_host(const StringRef &hostport) { return StringRef{std::begin(hostport), p}; } +std::pair split_hostport(const StringRef &hostport) { + if (hostport.empty()) { + return {}; + } + if (hostport[0] == '[') { + // assume this is IPv6 numeric address + auto p = std::find(std::begin(hostport), std::end(hostport), ']'); + if (p == std::end(hostport)) { + return {}; + } + if (p + 1 == std::end(hostport)) { + return {StringRef{std::begin(hostport) + 1, p}, {}}; + } + if (*(p + 1) != ':' || p + 2 == std::end(hostport)) { + return {}; + } + return {StringRef{std::begin(hostport) + 1, p}, + StringRef{p + 2, std::end(hostport)}}; + } + + auto p = std::find(std::begin(hostport), std::end(hostport), ':'); + if (p == std::begin(hostport)) { + return {}; + } + if (p == std::end(hostport)) { + return {StringRef{std::begin(hostport), p}, {}}; + } + if (p + 1 == std::end(hostport)) { + return {}; + } + + return {StringRef{std::begin(hostport), p}, + StringRef{p + 1, std::end(hostport)}}; +} + std::mt19937 make_mt19937() { std::random_device rd; return std::mt19937(rd()); diff --git a/src/util.h b/src/util.h index 38761e08..3443fe05 100644 --- a/src/util.h +++ b/src/util.h @@ -769,6 +769,13 @@ int sha1(uint8_t *buf, const StringRef &s); // NULL-terminated. StringRef extract_host(const StringRef &hostport); +// split_hostport splits host and port in |hostport|. Unlike +// extract_host, square brackets enclosing host name is stripped. If +// port is not available, it returns empty string in the second +// string. The returned string might not be NULL-terminated. On any +// error, it returns a pair which has empty strings. +std::pair split_hostport(const StringRef &hostport); + // Returns new std::mt19937 object. std::mt19937 make_mt19937(); diff --git a/src/util_test.cc b/src/util_test.cc index d375b437..6ad313d2 100644 --- a/src/util_test.cc +++ b/src/util_test.cc @@ -628,4 +628,28 @@ void test_util_extract_host(void) { CU_ASSERT(util::extract_host(StringRef{}).empty()); } +void test_util_split_hostport(void) { + CU_ASSERT(std::make_pair(StringRef::from_lit("foo"), StringRef{}) == + util::split_hostport(StringRef::from_lit("foo"))); + CU_ASSERT( + std::make_pair(StringRef::from_lit("foo"), StringRef::from_lit("80")) == + util::split_hostport(StringRef::from_lit("foo:80"))); + CU_ASSERT( + std::make_pair(StringRef::from_lit("::1"), StringRef::from_lit("80")) == + util::split_hostport(StringRef::from_lit("[::1]:80"))); + CU_ASSERT(std::make_pair(StringRef::from_lit("::1"), StringRef{}) == + util::split_hostport(StringRef::from_lit("[::1]"))); + + CU_ASSERT(std::make_pair(StringRef{}, StringRef{}) == + util::split_hostport(StringRef{})); + CU_ASSERT(std::make_pair(StringRef{}, StringRef{}) == + util::split_hostport(StringRef::from_lit("[::1]:"))); + CU_ASSERT(std::make_pair(StringRef{}, StringRef{}) == + util::split_hostport(StringRef::from_lit("foo:"))); + CU_ASSERT(std::make_pair(StringRef{}, StringRef{}) == + util::split_hostport(StringRef::from_lit("[::1:"))); + CU_ASSERT(std::make_pair(StringRef{}, StringRef{}) == + util::split_hostport(StringRef::from_lit("[::1]80"))); +} + } // namespace shrpx diff --git a/src/util_test.h b/src/util_test.h index 41ba9fe2..cc34f7d2 100644 --- a/src/util_test.h +++ b/src/util_test.h @@ -67,6 +67,7 @@ void test_util_format_hex(void); void test_util_is_hex_string(void); void test_util_decode_hex(void); void test_util_extract_host(void); +void test_util_split_hostport(void); } // namespace shrpx