diff --git a/google/cloud/internal/curl_impl.cc b/google/cloud/internal/curl_impl.cc index ba47998b2df4e..89d4091bb2829 100644 --- a/google/cloud/internal/curl_impl.cc +++ b/google/cloud/internal/curl_impl.cc @@ -23,6 +23,7 @@ #include "google/cloud/internal/rest_options.h" #include "google/cloud/internal/user_agent_prefix.h" #include "google/cloud/log.h" +#include "google/cloud/rest_options.h" #include "absl/strings/match.h" #include "absl/strings/strip.h" #include @@ -196,6 +197,8 @@ CurlImpl::CurlImpl(CurlHandle handle, proxy_ = CurlOptProxy(options); proxy_username_ = CurlOptProxyUsername(options); proxy_password_ = CurlOptProxyPassword(options); + + interface_ = CurlOptInterface(options); } CurlImpl::~CurlImpl() { @@ -319,6 +322,10 @@ Status CurlImpl::MakeRequest(HttpMethod method, RestContext& context, status = handle_.SetOption(CURLOPT_PROXYPASSWORD, proxy_password_->c_str()); if (!status.ok()) return OnTransferError(context, std::move(status)); } + if (interface_) { + status = handle_.SetOption(CURLOPT_INTERFACE, interface_->c_str()); + if (!status.ok()) return OnTransferError(context, std::move(status)); + } if (method == HttpMethod::kGet) { status = handle_.SetOption(CURLOPT_NOPROGRESS, 1L); @@ -786,6 +793,12 @@ absl::optional CurlOptProxyPassword(Options const& options) { return cfg.password(); } +absl::optional CurlOptInterface(Options const& options) { + auto const& cfg = options.get(); + if (cfg.empty()) return absl::nullopt; + return cfg; +} + GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END } // namespace rest_internal } // namespace cloud diff --git a/google/cloud/internal/curl_impl.h b/google/cloud/internal/curl_impl.h index 0faca01a2a096..a27318d9a175b 100644 --- a/google/cloud/internal/curl_impl.h +++ b/google/cloud/internal/curl_impl.h @@ -143,6 +143,8 @@ class CurlImpl { absl::optional proxy_username_; absl::optional proxy_password_; + absl::optional interface_; + CurlReceivedHeaders received_headers_; std::string url_; HttpStatusCode http_code_; @@ -192,6 +194,9 @@ absl::optional CurlOptProxyUsername(Options const& options); /// Compute the CURLOPT_PROXYPASSWORD setting from @p options. absl::optional CurlOptProxyPassword(Options const& options); +/// Compute the CURLOPT_INTERFACE setting from @p options. +absl::optional CurlOptInterface(Options const& options); + GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END } // namespace rest_internal } // namespace cloud diff --git a/google/cloud/internal/curl_impl_test.cc b/google/cloud/internal/curl_impl_test.cc index 611611af50cc7..da6e581b6ec5c 100644 --- a/google/cloud/internal/curl_impl_test.cc +++ b/google/cloud/internal/curl_impl_test.cc @@ -14,6 +14,7 @@ #include "google/cloud/internal/curl_impl.h" #include "google/cloud/common_options.h" +#include "google/cloud/rest_options.h" #include "google/cloud/testing_util/status_matchers.h" #include #include @@ -182,6 +183,12 @@ TEST_F(CurlImplTest, CurlOptProxyPassword) { absl::make_optional(std::string("password"))); } +TEST_F(CurlImplTest, CurlOptInterface) { + EXPECT_EQ(CurlOptInterface(Options{}), absl::nullopt); + EXPECT_EQ(CurlOptInterface(Options{}.set("interface")), + absl::make_optional(std::string("interface"))); +} + } // namespace GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END } // namespace rest_internal diff --git a/google/cloud/rest_options.h b/google/cloud/rest_options.h index e12f21f636e68..cdca49e084516 100644 --- a/google/cloud/rest_options.h +++ b/google/cloud/rest_options.h @@ -45,10 +45,24 @@ struct RestTracingOptionsOption { using Type = TracingOptions; }; +/** + * Sets the interface name to use as outgoing network interface. The + * name can be an interface name, IP address, or hostname. To + * utilize one of these use the following special prefixes: + * + * if![name] for interface name, host![name] for IP address or hostname, + * ifhost![interface]![host] for interface name and IP address or hostname. + * + * The default is to use whatever the TCP stack finds suitable. + */ +struct Interface { + using Type = std::string; +}; + /// The complete list of options accepted by `CurlRestClient` using RestOptionList = ::google::cloud::OptionList; + ServerTimeoutOption, UserIpOption, Interface>; GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END } // namespace cloud