From 23adf3a61aa4b9a0129362a184e91a2c4bbfcf70 Mon Sep 17 00:00:00 2001 From: David Li Date: Tue, 3 Nov 2020 18:38:37 -0500 Subject: [PATCH] ARROW-10475: [C++][FlightRPC] handle IPv6 hosts --- cpp/src/arrow/flight/client.cc | 3 ++- cpp/src/arrow/flight/flight_test.cc | 17 +++++++++++++++++ cpp/src/arrow/flight/server.cc | 3 ++- cpp/src/arrow/util/uri.cc | 13 +++++++++++++ cpp/src/arrow/util/uri.h | 5 +++++ cpp/src/arrow/util/uri_test.cc | 6 ++++++ 6 files changed, 45 insertions(+), 2 deletions(-) diff --git a/cpp/src/arrow/flight/client.cc b/cpp/src/arrow/flight/client.cc index 821da214a59e..cdffa7f06567 100644 --- a/cpp/src/arrow/flight/client.cc +++ b/cpp/src/arrow/flight/client.cc @@ -876,7 +876,8 @@ class FlightClient::FlightClientImpl { std::stringstream grpc_uri; std::shared_ptr creds; if (scheme == kSchemeGrpc || scheme == kSchemeGrpcTcp || scheme == kSchemeGrpcTls) { - grpc_uri << location.uri_->host() << ":" << location.uri_->port_text(); + grpc_uri << arrow::internal::UriEncodeHost(location.uri_->host()) << ':' + << location.uri_->port_text(); if (scheme == kSchemeGrpcTls) { if (options.disable_server_verification) { diff --git a/cpp/src/arrow/flight/flight_test.cc b/cpp/src/arrow/flight/flight_test.cc index a5fdfe8184e1..8d7e944bcddd 100644 --- a/cpp/src/arrow/flight/flight_test.cc +++ b/cpp/src/arrow/flight/flight_test.cc @@ -289,6 +289,23 @@ TEST(TestFlight, GetPort) { ASSERT_GT(server->port(), 0); } +// CI environments don't have an IPv6 interface configured +TEST(TestFlight, DISABLED_IpV6Port) { + Location location, location2; + std::unique_ptr server = ExampleTestServer(); + + ASSERT_OK(Location::ForGrpcTcp("[::1]", 0, &location)); + FlightServerOptions options(location); + ASSERT_OK(server->Init(options)); + ASSERT_GT(server->port(), 0); + + ASSERT_OK(Location::ForGrpcTcp("[::1]", server->port(), &location2)); + std::unique_ptr client; + ASSERT_OK(FlightClient::Connect(location2, &client)); + std::unique_ptr listing; + ASSERT_OK(client->ListFlights(&listing)); +} + TEST(TestFlight, BuilderHook) { Location location; std::unique_ptr server = ExampleTestServer(); diff --git a/cpp/src/arrow/flight/server.cc b/cpp/src/arrow/flight/server.cc index 0cdd3b43f516..87c96ce49102 100644 --- a/cpp/src/arrow/flight/server.cc +++ b/cpp/src/arrow/flight/server.cc @@ -822,7 +822,8 @@ Status FlightServerBase::Init(const FlightServerOptions& options) { const std::string scheme = location.scheme(); if (scheme == kSchemeGrpc || scheme == kSchemeGrpcTcp || scheme == kSchemeGrpcTls) { std::stringstream address; - address << location.uri_->host() << ':' << location.uri_->port_text(); + address << arrow::internal::UriEncodeHost(location.uri_->host()) << ':' + << location.uri_->port_text(); std::shared_ptr creds; if (scheme == kSchemeGrpcTls) { diff --git a/cpp/src/arrow/util/uri.cc b/cpp/src/arrow/util/uri.cc index 1261607b6c14..f48a8cb6ed17 100644 --- a/cpp/src/arrow/util/uri.cc +++ b/cpp/src/arrow/util/uri.cc @@ -80,6 +80,19 @@ std::string UriEscape(const std::string& s) { return escaped; } +std::string UriEncodeHost(const std::string& host) { + // Fairly naive check: if it contains a ':', it's IPv6 and needs + // brackets, else it's OK + if (host.find(":") != std::string::npos) { + std::string result = "["; + result += host; + result += ']'; + return result; + } else { + return host; + } +} + struct Uri::Impl { Impl() : string_rep_(""), port_(-1) { memset(&uri_, 0, sizeof(uri_)); } diff --git a/cpp/src/arrow/util/uri.h b/cpp/src/arrow/util/uri.h index 6ef54900020a..480e35474a31 100644 --- a/cpp/src/arrow/util/uri.h +++ b/cpp/src/arrow/util/uri.h @@ -91,5 +91,10 @@ class ARROW_EXPORT Uri { ARROW_EXPORT std::string UriEscape(const std::string& s); +/// Encode a host for use within a URI, such as "localhost", +/// "127.0.0.1", or "[::1]". +ARROW_EXPORT +std::string UriEncodeHost(const std::string& host); + } // namespace internal } // namespace arrow diff --git a/cpp/src/arrow/util/uri_test.cc b/cpp/src/arrow/util/uri_test.cc index 44804704c436..169e9c81b36b 100644 --- a/cpp/src/arrow/util/uri_test.cc +++ b/cpp/src/arrow/util/uri_test.cc @@ -35,6 +35,12 @@ TEST(UriEscape, Basics) { ASSERT_EQ(UriEscape("/El NiƱo/"), "%2FEl%20Ni%C3%B1o%2F"); } +TEST(UriEncodeHost, Basics) { + ASSERT_EQ(UriEncodeHost("::1"), "[::1]"); + ASSERT_EQ(UriEscape("arrow.apache.org"), "arrow.apache.org"); + ASSERT_EQ(UriEscape("192.168.1.1"), "192.168.1.1"); +} + TEST(Uri, Empty) { Uri uri; ASSERT_EQ(uri.scheme(), "");