create
This commit is contained in:
147
Pods/gRPC-Core/src/core/resolver/binder/binder_resolver.cc
generated
Normal file
147
Pods/gRPC-Core/src/core/resolver/binder/binder_resolver.cc
generated
Normal file
@@ -0,0 +1,147 @@
|
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
|
||||
#include "src/core/lib/gprpp/status_helper.h"
|
||||
#include "src/core/lib/iomgr/port.h" // IWYU pragma: keep
|
||||
|
||||
#ifdef GRPC_HAVE_UNIX_SOCKET
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/strings/strip.h"
|
||||
|
||||
#include <grpc/support/log.h>
|
||||
|
||||
#include "src/core/lib/channel/channel_args.h"
|
||||
#include "src/core/lib/config/core_configuration.h"
|
||||
#include "src/core/lib/gprpp/orphanable.h"
|
||||
#include "src/core/lib/iomgr/error.h"
|
||||
#include "src/core/lib/iomgr/resolved_address.h"
|
||||
#include "src/core/resolver/endpoint_addresses.h"
|
||||
#include "src/core/resolver/resolver.h"
|
||||
#include "src/core/resolver/resolver_factory.h"
|
||||
#include "src/core/lib/uri/uri_parser.h"
|
||||
|
||||
namespace grpc_core {
|
||||
namespace {
|
||||
|
||||
class BinderResolver : public Resolver {
|
||||
public:
|
||||
BinderResolver(EndpointAddressesList addresses, ResolverArgs args)
|
||||
: result_handler_(std::move(args.result_handler)),
|
||||
addresses_(std::move(addresses)),
|
||||
channel_args_(std::move(args.args)) {}
|
||||
|
||||
void StartLocked() override {
|
||||
Result result;
|
||||
result.addresses = std::move(addresses_);
|
||||
result.args = channel_args_;
|
||||
channel_args_ = ChannelArgs();
|
||||
result_handler_->ReportResult(std::move(result));
|
||||
}
|
||||
|
||||
void ShutdownLocked() override {}
|
||||
|
||||
private:
|
||||
std::unique_ptr<ResultHandler> result_handler_;
|
||||
EndpointAddressesList addresses_;
|
||||
ChannelArgs channel_args_;
|
||||
};
|
||||
|
||||
class BinderResolverFactory : public ResolverFactory {
|
||||
public:
|
||||
absl::string_view scheme() const override { return "binder"; }
|
||||
|
||||
bool IsValidUri(const URI& uri) const override {
|
||||
return ParseUri(uri, nullptr);
|
||||
}
|
||||
|
||||
OrphanablePtr<Resolver> CreateResolver(ResolverArgs args) const override {
|
||||
EndpointAddressesList addresses;
|
||||
if (!ParseUri(args.uri, &addresses)) return nullptr;
|
||||
return MakeOrphanable<BinderResolver>(std::move(addresses),
|
||||
std::move(args));
|
||||
}
|
||||
|
||||
private:
|
||||
static grpc_error_handle BinderAddrPopulate(
|
||||
absl::string_view path, grpc_resolved_address* resolved_addr) {
|
||||
path = absl::StripPrefix(path, "/");
|
||||
if (path.empty()) {
|
||||
return GRPC_ERROR_CREATE("path is empty");
|
||||
}
|
||||
// Store parsed path in a unix socket so it can be reinterpreted as
|
||||
// sockaddr. An invalid address family (AF_MAX) is set to make sure it won't
|
||||
// be accidentally used.
|
||||
memset(resolved_addr, 0, sizeof(*resolved_addr));
|
||||
struct sockaddr_un* un =
|
||||
reinterpret_cast<struct sockaddr_un*>(resolved_addr->addr);
|
||||
un->sun_family = AF_MAX;
|
||||
static_assert(sizeof(un->sun_path) >= 101,
|
||||
"unix socket path size is unexpectedly short");
|
||||
if (path.size() + 1 > sizeof(un->sun_path)) {
|
||||
return GRPC_ERROR_CREATE(
|
||||
absl::StrCat(path, " is too long to be handled"));
|
||||
}
|
||||
// `un` has already be set to zero, no need to append null after the string
|
||||
memcpy(un->sun_path, path.data(), path.size());
|
||||
resolved_addr->len =
|
||||
static_cast<socklen_t>(sizeof(un->sun_family) + path.size() + 1);
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
static bool ParseUri(const URI& uri, EndpointAddressesList* addresses) {
|
||||
grpc_resolved_address addr;
|
||||
{
|
||||
if (!uri.authority().empty()) {
|
||||
gpr_log(GPR_ERROR, "authority is not supported in binder scheme");
|
||||
return false;
|
||||
}
|
||||
grpc_error_handle error = BinderAddrPopulate(uri.path(), &addr);
|
||||
if (!error.ok()) {
|
||||
gpr_log(GPR_ERROR, "%s", StatusToString(error).c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (addresses != nullptr) {
|
||||
addresses->emplace_back(addr, ChannelArgs());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void RegisterBinderResolver(CoreConfiguration::Builder* builder) {
|
||||
builder->resolver_registry()->RegisterResolverFactory(
|
||||
std::make_unique<BinderResolverFactory>());
|
||||
}
|
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif
|
||||
758
Pods/gRPC-Core/src/core/resolver/dns/c_ares/dns_resolver_ares.cc
generated
Normal file
758
Pods/gRPC-Core/src/core/resolver/dns/c_ares/dns_resolver_ares.cc
generated
Normal file
@@ -0,0 +1,758 @@
|
||||
//
|
||||
// Copyright 2015 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/base/thread_annotations.h"
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/match.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/strings/strip.h"
|
||||
#include "absl/types/optional.h"
|
||||
|
||||
#include <grpc/impl/channel_arg_names.h>
|
||||
#include <grpc/support/alloc.h>
|
||||
#include <grpc/support/log.h>
|
||||
|
||||
#include "src/core/resolver/dns/event_engine/service_config_helper.h"
|
||||
#include "src/core/lib/config/core_configuration.h"
|
||||
#include "src/core/lib/debug/trace.h"
|
||||
#include "src/core/lib/gprpp/debug_location.h"
|
||||
#include "src/core/lib/gprpp/orphanable.h"
|
||||
#include "src/core/lib/gprpp/ref_counted_ptr.h"
|
||||
#include "src/core/lib/gprpp/status_helper.h"
|
||||
#include "src/core/lib/gprpp/sync.h"
|
||||
#include "src/core/lib/gprpp/time.h"
|
||||
#include "src/core/lib/iomgr/closure.h"
|
||||
#include "src/core/lib/iomgr/error.h"
|
||||
#include "src/core/lib/iomgr/iomgr_fwd.h"
|
||||
#include "src/core/lib/iomgr/pollset_set.h"
|
||||
#include "src/core/lib/iomgr/resolved_address.h"
|
||||
#include "src/core/resolver/resolver.h"
|
||||
#include "src/core/resolver/resolver_factory.h"
|
||||
#include "src/core/service_config/service_config.h"
|
||||
#include "src/core/lib/uri/uri_parser.h"
|
||||
|
||||
#if GRPC_ARES == 1
|
||||
|
||||
#include <address_sorting/address_sorting.h>
|
||||
|
||||
#include "absl/strings/str_cat.h"
|
||||
|
||||
#include "src/core/lib/backoff/backoff.h"
|
||||
#include "src/core/lib/channel/channel_args.h"
|
||||
#include "src/core/lib/config/config_vars.h"
|
||||
#include "src/core/lib/iomgr/resolve_address.h"
|
||||
#include "src/core/resolver/endpoint_addresses.h"
|
||||
#include "src/core/service_config/service_config_impl.h"
|
||||
#include "src/core/lib/transport/error_utils.h"
|
||||
#include "src/core/load_balancing/grpclb/grpclb_balancer_addresses.h"
|
||||
#include "src/core/resolver/dns/c_ares/grpc_ares_wrapper.h"
|
||||
#include "src/core/resolver/polling_resolver.h"
|
||||
|
||||
#define GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS 1
|
||||
#define GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER 1.6
|
||||
#define GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS 120
|
||||
#define GRPC_DNS_RECONNECT_JITTER 0.2
|
||||
|
||||
namespace grpc_core {
|
||||
|
||||
namespace {
|
||||
|
||||
class AresClientChannelDNSResolver : public PollingResolver {
|
||||
public:
|
||||
AresClientChannelDNSResolver(ResolverArgs args,
|
||||
Duration min_time_between_resolutions);
|
||||
|
||||
OrphanablePtr<Orphanable> StartRequest() override;
|
||||
|
||||
private:
|
||||
class AresRequestWrapper : public InternallyRefCounted<AresRequestWrapper> {
|
||||
public:
|
||||
explicit AresRequestWrapper(
|
||||
RefCountedPtr<AresClientChannelDNSResolver> resolver)
|
||||
: resolver_(std::move(resolver)) {
|
||||
// TODO(hork): replace this callback bookkeeping with promises.
|
||||
// Locking to prevent completion before all records are queried
|
||||
MutexLock lock(&on_resolved_mu_);
|
||||
Ref(DEBUG_LOCATION, "OnHostnameResolved").release();
|
||||
GRPC_CLOSURE_INIT(&on_hostname_resolved_, OnHostnameResolved, this,
|
||||
nullptr);
|
||||
hostname_request_.reset(grpc_dns_lookup_hostname_ares(
|
||||
resolver_->authority().c_str(), resolver_->name_to_resolve().c_str(),
|
||||
kDefaultSecurePort, resolver_->interested_parties(),
|
||||
&on_hostname_resolved_, &addresses_, resolver_->query_timeout_ms_));
|
||||
GRPC_CARES_TRACE_LOG(
|
||||
"resolver:%p Started resolving hostnames. hostname_request_:%p",
|
||||
resolver_.get(), hostname_request_.get());
|
||||
if (resolver_->enable_srv_queries_) {
|
||||
Ref(DEBUG_LOCATION, "OnSRVResolved").release();
|
||||
GRPC_CLOSURE_INIT(&on_srv_resolved_, OnSRVResolved, this, nullptr);
|
||||
srv_request_.reset(grpc_dns_lookup_srv_ares(
|
||||
resolver_->authority().c_str(),
|
||||
resolver_->name_to_resolve().c_str(),
|
||||
resolver_->interested_parties(), &on_srv_resolved_,
|
||||
&balancer_addresses_, resolver_->query_timeout_ms_));
|
||||
GRPC_CARES_TRACE_LOG(
|
||||
"resolver:%p Started resolving SRV records. srv_request_:%p",
|
||||
resolver_.get(), srv_request_.get());
|
||||
}
|
||||
if (resolver_->request_service_config_) {
|
||||
Ref(DEBUG_LOCATION, "OnTXTResolved").release();
|
||||
GRPC_CLOSURE_INIT(&on_txt_resolved_, OnTXTResolved, this, nullptr);
|
||||
txt_request_.reset(grpc_dns_lookup_txt_ares(
|
||||
resolver_->authority().c_str(),
|
||||
resolver_->name_to_resolve().c_str(),
|
||||
resolver_->interested_parties(), &on_txt_resolved_,
|
||||
&service_config_json_, resolver_->query_timeout_ms_));
|
||||
GRPC_CARES_TRACE_LOG(
|
||||
"resolver:%p Started resolving TXT records. txt_request_:%p",
|
||||
resolver_.get(), txt_request_.get());
|
||||
}
|
||||
}
|
||||
|
||||
~AresRequestWrapper() override {
|
||||
gpr_free(service_config_json_);
|
||||
resolver_.reset(DEBUG_LOCATION, "dns-resolving");
|
||||
}
|
||||
|
||||
// Note that thread safety cannot be analyzed due to this being invoked from
|
||||
// OrphanablePtr<>, and there's no way to pass the lock annotation through
|
||||
// there.
|
||||
void Orphan() override ABSL_NO_THREAD_SAFETY_ANALYSIS {
|
||||
{
|
||||
MutexLock lock(&on_resolved_mu_);
|
||||
if (hostname_request_ != nullptr) {
|
||||
grpc_cancel_ares_request(hostname_request_.get());
|
||||
}
|
||||
if (srv_request_ != nullptr) {
|
||||
grpc_cancel_ares_request(srv_request_.get());
|
||||
}
|
||||
if (txt_request_ != nullptr) {
|
||||
grpc_cancel_ares_request(txt_request_.get());
|
||||
}
|
||||
}
|
||||
Unref(DEBUG_LOCATION, "Orphan");
|
||||
}
|
||||
|
||||
private:
|
||||
static void OnHostnameResolved(void* arg, grpc_error_handle error);
|
||||
static void OnSRVResolved(void* arg, grpc_error_handle error);
|
||||
static void OnTXTResolved(void* arg, grpc_error_handle error);
|
||||
absl::optional<Result> OnResolvedLocked(grpc_error_handle error)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(on_resolved_mu_);
|
||||
|
||||
Mutex on_resolved_mu_;
|
||||
RefCountedPtr<AresClientChannelDNSResolver> resolver_;
|
||||
grpc_closure on_hostname_resolved_;
|
||||
std::unique_ptr<grpc_ares_request> hostname_request_
|
||||
ABSL_GUARDED_BY(on_resolved_mu_);
|
||||
grpc_closure on_srv_resolved_;
|
||||
std::unique_ptr<grpc_ares_request> srv_request_
|
||||
ABSL_GUARDED_BY(on_resolved_mu_);
|
||||
grpc_closure on_txt_resolved_;
|
||||
std::unique_ptr<grpc_ares_request> txt_request_
|
||||
ABSL_GUARDED_BY(on_resolved_mu_);
|
||||
// Output fields from ares request.
|
||||
std::unique_ptr<EndpointAddressesList> addresses_
|
||||
ABSL_GUARDED_BY(on_resolved_mu_);
|
||||
std::unique_ptr<EndpointAddressesList> balancer_addresses_
|
||||
ABSL_GUARDED_BY(on_resolved_mu_);
|
||||
char* service_config_json_ ABSL_GUARDED_BY(on_resolved_mu_) = nullptr;
|
||||
};
|
||||
|
||||
~AresClientChannelDNSResolver() override;
|
||||
|
||||
/// whether to request the service config
|
||||
const bool request_service_config_;
|
||||
// whether or not to enable SRV DNS queries
|
||||
const bool enable_srv_queries_;
|
||||
// timeout in milliseconds for active DNS queries
|
||||
const int query_timeout_ms_;
|
||||
};
|
||||
|
||||
AresClientChannelDNSResolver::AresClientChannelDNSResolver(
|
||||
ResolverArgs args, Duration min_time_between_resolutions)
|
||||
: PollingResolver(std::move(args), min_time_between_resolutions,
|
||||
BackOff::Options()
|
||||
.set_initial_backoff(Duration::Milliseconds(
|
||||
GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS * 1000))
|
||||
.set_multiplier(GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER)
|
||||
.set_jitter(GRPC_DNS_RECONNECT_JITTER)
|
||||
.set_max_backoff(Duration::Milliseconds(
|
||||
GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS * 1000)),
|
||||
&grpc_trace_cares_resolver),
|
||||
request_service_config_(
|
||||
!channel_args()
|
||||
.GetBool(GRPC_ARG_SERVICE_CONFIG_DISABLE_RESOLUTION)
|
||||
.value_or(true)),
|
||||
enable_srv_queries_(channel_args()
|
||||
.GetBool(GRPC_ARG_DNS_ENABLE_SRV_QUERIES)
|
||||
.value_or(false)),
|
||||
query_timeout_ms_(
|
||||
std::max(0, channel_args()
|
||||
.GetInt(GRPC_ARG_DNS_ARES_QUERY_TIMEOUT_MS)
|
||||
.value_or(GRPC_DNS_ARES_DEFAULT_QUERY_TIMEOUT_MS))) {}
|
||||
|
||||
AresClientChannelDNSResolver::~AresClientChannelDNSResolver() {
|
||||
GRPC_CARES_TRACE_LOG("resolver:%p destroying AresClientChannelDNSResolver",
|
||||
this);
|
||||
}
|
||||
|
||||
OrphanablePtr<Orphanable> AresClientChannelDNSResolver::StartRequest() {
|
||||
return MakeOrphanable<AresRequestWrapper>(
|
||||
RefAsSubclass<AresClientChannelDNSResolver>(DEBUG_LOCATION,
|
||||
"dns-resolving"));
|
||||
}
|
||||
|
||||
void AresClientChannelDNSResolver::AresRequestWrapper::OnHostnameResolved(
|
||||
void* arg, grpc_error_handle error) {
|
||||
auto* self = static_cast<AresRequestWrapper*>(arg);
|
||||
absl::optional<Result> result;
|
||||
{
|
||||
MutexLock lock(&self->on_resolved_mu_);
|
||||
self->hostname_request_.reset();
|
||||
result = self->OnResolvedLocked(error);
|
||||
}
|
||||
if (result.has_value()) {
|
||||
self->resolver_->OnRequestComplete(std::move(*result));
|
||||
}
|
||||
self->Unref(DEBUG_LOCATION, "OnHostnameResolved");
|
||||
}
|
||||
|
||||
void AresClientChannelDNSResolver::AresRequestWrapper::OnSRVResolved(
|
||||
void* arg, grpc_error_handle error) {
|
||||
auto* self = static_cast<AresRequestWrapper*>(arg);
|
||||
absl::optional<Result> result;
|
||||
{
|
||||
MutexLock lock(&self->on_resolved_mu_);
|
||||
self->srv_request_.reset();
|
||||
result = self->OnResolvedLocked(error);
|
||||
}
|
||||
if (result.has_value()) {
|
||||
self->resolver_->OnRequestComplete(std::move(*result));
|
||||
}
|
||||
self->Unref(DEBUG_LOCATION, "OnSRVResolved");
|
||||
}
|
||||
|
||||
void AresClientChannelDNSResolver::AresRequestWrapper::OnTXTResolved(
|
||||
void* arg, grpc_error_handle error) {
|
||||
auto* self = static_cast<AresRequestWrapper*>(arg);
|
||||
absl::optional<Result> result;
|
||||
{
|
||||
MutexLock lock(&self->on_resolved_mu_);
|
||||
self->txt_request_.reset();
|
||||
result = self->OnResolvedLocked(error);
|
||||
}
|
||||
if (result.has_value()) {
|
||||
self->resolver_->OnRequestComplete(std::move(*result));
|
||||
}
|
||||
self->Unref(DEBUG_LOCATION, "OnTXTResolved");
|
||||
}
|
||||
|
||||
// Returns a Result if resolution is complete.
|
||||
// callers must release the lock and call OnRequestComplete if a Result is
|
||||
// returned. This is because OnRequestComplete may Orphan the resolver, which
|
||||
// requires taking the lock.
|
||||
absl::optional<AresClientChannelDNSResolver::Result>
|
||||
AresClientChannelDNSResolver::AresRequestWrapper::OnResolvedLocked(
|
||||
grpc_error_handle error) ABSL_EXCLUSIVE_LOCKS_REQUIRED(on_resolved_mu_) {
|
||||
if (hostname_request_ != nullptr || srv_request_ != nullptr ||
|
||||
txt_request_ != nullptr) {
|
||||
GRPC_CARES_TRACE_LOG(
|
||||
"resolver:%p OnResolved() waiting for results (hostname: %s, srv: %s, "
|
||||
"txt: %s)",
|
||||
this, hostname_request_ != nullptr ? "waiting" : "done",
|
||||
srv_request_ != nullptr ? "waiting" : "done",
|
||||
txt_request_ != nullptr ? "waiting" : "done");
|
||||
return absl::nullopt;
|
||||
}
|
||||
GRPC_CARES_TRACE_LOG("resolver:%p OnResolved() proceeding", this);
|
||||
Result result;
|
||||
result.args = resolver_->channel_args();
|
||||
// TODO(roth): Change logic to be able to report failures for addresses
|
||||
// and service config independently of each other.
|
||||
if (addresses_ != nullptr || balancer_addresses_ != nullptr) {
|
||||
if (addresses_ != nullptr) {
|
||||
result.addresses = std::move(*addresses_);
|
||||
} else {
|
||||
result.addresses.emplace();
|
||||
}
|
||||
if (service_config_json_ != nullptr) {
|
||||
auto service_config_string = ChooseServiceConfig(service_config_json_);
|
||||
if (!service_config_string.ok()) {
|
||||
result.service_config = absl::UnavailableError(
|
||||
absl::StrCat("failed to parse service config: ",
|
||||
StatusToString(service_config_string.status())));
|
||||
} else if (!service_config_string->empty()) {
|
||||
GRPC_CARES_TRACE_LOG("resolver:%p selected service config choice: %s",
|
||||
this, service_config_string->c_str());
|
||||
result.service_config = ServiceConfigImpl::Create(
|
||||
resolver_->channel_args(), *service_config_string);
|
||||
if (!result.service_config.ok()) {
|
||||
result.service_config = absl::UnavailableError(
|
||||
absl::StrCat("failed to parse service config: ",
|
||||
result.service_config.status().message()));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (balancer_addresses_ != nullptr) {
|
||||
result.args =
|
||||
SetGrpcLbBalancerAddresses(result.args, *balancer_addresses_);
|
||||
}
|
||||
} else {
|
||||
GRPC_CARES_TRACE_LOG("resolver:%p dns resolution failed: %s", this,
|
||||
StatusToString(error).c_str());
|
||||
std::string error_message;
|
||||
grpc_error_get_str(error, StatusStrProperty::kDescription, &error_message);
|
||||
absl::Status status = absl::UnavailableError(
|
||||
absl::StrCat("DNS resolution failed for ", resolver_->name_to_resolve(),
|
||||
": ", error_message));
|
||||
result.addresses = status;
|
||||
result.service_config = status;
|
||||
}
|
||||
|
||||
return std::move(result);
|
||||
}
|
||||
|
||||
//
|
||||
// Factory
|
||||
//
|
||||
|
||||
class AresClientChannelDNSResolverFactory : public ResolverFactory {
|
||||
public:
|
||||
absl::string_view scheme() const override { return "dns"; }
|
||||
|
||||
bool IsValidUri(const URI& uri) const override {
|
||||
if (absl::StripPrefix(uri.path(), "/").empty()) {
|
||||
gpr_log(GPR_ERROR, "no server name supplied in dns URI");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
OrphanablePtr<Resolver> CreateResolver(ResolverArgs args) const override {
|
||||
Duration min_time_between_resolutions = std::max(
|
||||
Duration::Zero(), args.args
|
||||
.GetDurationFromIntMillis(
|
||||
GRPC_ARG_DNS_MIN_TIME_BETWEEN_RESOLUTIONS_MS)
|
||||
.value_or(Duration::Seconds(30)));
|
||||
return MakeOrphanable<AresClientChannelDNSResolver>(
|
||||
std::move(args), min_time_between_resolutions);
|
||||
}
|
||||
};
|
||||
|
||||
class AresDNSResolver : public DNSResolver {
|
||||
public:
|
||||
// Abstract class that centralizes common request handling logic via the
|
||||
// template method pattern.
|
||||
// This requires a two-phase initialization, where 1) a request is created via
|
||||
// a subclass constructor, and 2) the request is initiated via Run()
|
||||
class AresRequest {
|
||||
public:
|
||||
virtual ~AresRequest() {
|
||||
GRPC_CARES_TRACE_LOG("AresRequest:%p dtor ares_request_:%p", this,
|
||||
grpc_ares_request_.get());
|
||||
resolver_->UnregisterRequest(task_handle());
|
||||
grpc_pollset_set_destroy(pollset_set_);
|
||||
}
|
||||
|
||||
// Initiates the low-level c-ares request and returns its handle.
|
||||
virtual std::unique_ptr<grpc_ares_request> MakeRequestLocked() = 0;
|
||||
// Called on ares resolution, but not upon cancellation.
|
||||
// After execution, the AresRequest will perform any final cleanup and
|
||||
// delete itself.
|
||||
virtual void OnComplete(grpc_error_handle error) = 0;
|
||||
|
||||
// Called to initiate the request.
|
||||
void Run() {
|
||||
MutexLock lock(&mu_);
|
||||
grpc_ares_request_ = MakeRequestLocked();
|
||||
}
|
||||
|
||||
bool Cancel() {
|
||||
MutexLock lock(&mu_);
|
||||
if (grpc_ares_request_ != nullptr) {
|
||||
GRPC_CARES_TRACE_LOG("AresRequest:%p Cancel ares_request_:%p", this,
|
||||
grpc_ares_request_.get());
|
||||
if (completed_) return false;
|
||||
// OnDnsLookupDone will still be run
|
||||
completed_ = true;
|
||||
grpc_cancel_ares_request(grpc_ares_request_.get());
|
||||
} else {
|
||||
completed_ = true;
|
||||
OnDnsLookupDone(this, absl::CancelledError());
|
||||
}
|
||||
grpc_pollset_set_del_pollset_set(pollset_set_, interested_parties_);
|
||||
return true;
|
||||
}
|
||||
|
||||
TaskHandle task_handle() {
|
||||
return {reinterpret_cast<intptr_t>(this), aba_token_};
|
||||
}
|
||||
|
||||
protected:
|
||||
AresRequest(absl::string_view name, absl::string_view name_server,
|
||||
Duration timeout, grpc_pollset_set* interested_parties,
|
||||
AresDNSResolver* resolver, intptr_t aba_token)
|
||||
: name_(name),
|
||||
name_server_(name_server),
|
||||
timeout_(timeout),
|
||||
interested_parties_(interested_parties),
|
||||
completed_(false),
|
||||
resolver_(resolver),
|
||||
aba_token_(aba_token),
|
||||
pollset_set_(grpc_pollset_set_create()) {
|
||||
GRPC_CLOSURE_INIT(&on_dns_lookup_done_, OnDnsLookupDone, this,
|
||||
grpc_schedule_on_exec_ctx);
|
||||
grpc_pollset_set_add_pollset_set(pollset_set_, interested_parties_);
|
||||
}
|
||||
|
||||
grpc_pollset_set* pollset_set() { return pollset_set_; };
|
||||
grpc_closure* on_dns_lookup_done() { return &on_dns_lookup_done_; };
|
||||
const std::string& name() { return name_; }
|
||||
const std::string& name_server() { return name_server_; }
|
||||
const Duration& timeout() { return timeout_; }
|
||||
|
||||
private:
|
||||
// Called by ares when lookup has completed or when cancelled. It is always
|
||||
// called exactly once, and it triggers self-deletion.
|
||||
static void OnDnsLookupDone(void* arg, grpc_error_handle error) {
|
||||
AresRequest* r = static_cast<AresRequest*>(arg);
|
||||
auto deleter = std::unique_ptr<AresRequest>(r);
|
||||
{
|
||||
MutexLock lock(&r->mu_);
|
||||
grpc_pollset_set_del_pollset_set(r->pollset_set_,
|
||||
r->interested_parties_);
|
||||
if (r->completed_) {
|
||||
return;
|
||||
}
|
||||
r->completed_ = true;
|
||||
}
|
||||
r->OnComplete(error);
|
||||
}
|
||||
|
||||
// the name to resolve
|
||||
const std::string name_;
|
||||
// the name server to query
|
||||
const std::string name_server_;
|
||||
// request-specific timeout
|
||||
Duration timeout_;
|
||||
// mutex to synchronize access to this object (but not to the ares_request
|
||||
// object itself).
|
||||
Mutex mu_;
|
||||
// parties interested in our I/O
|
||||
grpc_pollset_set* const interested_parties_;
|
||||
// underlying cares_request that the query is performed on
|
||||
std::unique_ptr<grpc_ares_request> grpc_ares_request_ ABSL_GUARDED_BY(mu_);
|
||||
// Set when the callback is either cancelled or executed.
|
||||
// It is not the subclasses' responsibility to set this flag.
|
||||
bool completed_ ABSL_GUARDED_BY(mu_);
|
||||
// Parent resolver that created this request
|
||||
AresDNSResolver* resolver_;
|
||||
// Unique token to help distinguish this request from others that may later
|
||||
// be created in the same memory location.
|
||||
intptr_t aba_token_;
|
||||
// closure to call when the ares resolution request completes. Subclasses
|
||||
// should use this as the ares callback in MakeRequestLocked()
|
||||
grpc_closure on_dns_lookup_done_ ABSL_GUARDED_BY(mu_);
|
||||
// locally owned pollset_set, required to support cancellation of requests
|
||||
// while ares still needs a valid pollset_set. Subclasses should give this
|
||||
// pollset to ares in MakeRequestLocked();
|
||||
grpc_pollset_set* pollset_set_;
|
||||
};
|
||||
|
||||
class AresHostnameRequest : public AresRequest {
|
||||
public:
|
||||
AresHostnameRequest(
|
||||
absl::string_view name, absl::string_view default_port,
|
||||
absl::string_view name_server, Duration timeout,
|
||||
grpc_pollset_set* interested_parties,
|
||||
std::function<void(absl::StatusOr<std::vector<grpc_resolved_address>>)>
|
||||
on_resolve_address_done,
|
||||
AresDNSResolver* resolver, intptr_t aba_token)
|
||||
: AresRequest(name, name_server, timeout, interested_parties, resolver,
|
||||
aba_token),
|
||||
default_port_(default_port),
|
||||
on_resolve_address_done_(std::move(on_resolve_address_done)) {
|
||||
GRPC_CARES_TRACE_LOG("AresHostnameRequest:%p ctor", this);
|
||||
}
|
||||
|
||||
std::unique_ptr<grpc_ares_request> MakeRequestLocked() override {
|
||||
auto ares_request =
|
||||
std::unique_ptr<grpc_ares_request>(grpc_dns_lookup_hostname_ares(
|
||||
name_server().c_str(), name().c_str(), default_port_.c_str(),
|
||||
pollset_set(), on_dns_lookup_done(), &addresses_,
|
||||
timeout().millis()));
|
||||
GRPC_CARES_TRACE_LOG("AresHostnameRequest:%p Start ares_request_:%p",
|
||||
this, ares_request.get());
|
||||
return ares_request;
|
||||
}
|
||||
|
||||
void OnComplete(grpc_error_handle error) override {
|
||||
GRPC_CARES_TRACE_LOG("AresHostnameRequest:%p OnComplete", this);
|
||||
if (!error.ok()) {
|
||||
on_resolve_address_done_(grpc_error_to_absl_status(error));
|
||||
return;
|
||||
}
|
||||
std::vector<grpc_resolved_address> resolved_addresses;
|
||||
if (addresses_ != nullptr) {
|
||||
resolved_addresses.reserve(addresses_->size());
|
||||
for (const auto& server_address : *addresses_) {
|
||||
resolved_addresses.push_back(server_address.address());
|
||||
}
|
||||
}
|
||||
on_resolve_address_done_(std::move(resolved_addresses));
|
||||
}
|
||||
|
||||
// the default port to use if name doesn't have one
|
||||
const std::string default_port_;
|
||||
// user-provided completion callback
|
||||
const std::function<void(
|
||||
absl::StatusOr<std::vector<grpc_resolved_address>>)>
|
||||
on_resolve_address_done_;
|
||||
// currently resolving addresses
|
||||
std::unique_ptr<EndpointAddressesList> addresses_;
|
||||
};
|
||||
|
||||
class AresSRVRequest : public AresRequest {
|
||||
public:
|
||||
AresSRVRequest(
|
||||
absl::string_view name, absl::string_view name_server, Duration timeout,
|
||||
grpc_pollset_set* interested_parties,
|
||||
std::function<void(absl::StatusOr<std::vector<grpc_resolved_address>>)>
|
||||
on_resolve_address_done,
|
||||
AresDNSResolver* resolver, intptr_t aba_token)
|
||||
: AresRequest(name, name_server, timeout, interested_parties, resolver,
|
||||
aba_token),
|
||||
on_resolve_address_done_(std::move(on_resolve_address_done)) {
|
||||
GRPC_CARES_TRACE_LOG("AresSRVRequest:%p ctor", this);
|
||||
}
|
||||
|
||||
std::unique_ptr<grpc_ares_request> MakeRequestLocked() override {
|
||||
auto ares_request =
|
||||
std::unique_ptr<grpc_ares_request>(grpc_dns_lookup_srv_ares(
|
||||
name_server().c_str(), name().c_str(), pollset_set(),
|
||||
on_dns_lookup_done(), &balancer_addresses_, timeout().millis()));
|
||||
GRPC_CARES_TRACE_LOG("AresSRVRequest:%p Start ares_request_:%p", this,
|
||||
ares_request.get());
|
||||
return ares_request;
|
||||
}
|
||||
|
||||
void OnComplete(grpc_error_handle error) override {
|
||||
GRPC_CARES_TRACE_LOG("AresSRVRequest:%p OnComplete", this);
|
||||
if (!error.ok()) {
|
||||
on_resolve_address_done_(grpc_error_to_absl_status(error));
|
||||
return;
|
||||
}
|
||||
std::vector<grpc_resolved_address> resolved_addresses;
|
||||
if (balancer_addresses_ != nullptr) {
|
||||
resolved_addresses.reserve(balancer_addresses_->size());
|
||||
for (const auto& server_address : *balancer_addresses_) {
|
||||
resolved_addresses.push_back(server_address.address());
|
||||
}
|
||||
}
|
||||
on_resolve_address_done_(std::move(resolved_addresses));
|
||||
}
|
||||
|
||||
// user-provided completion callback
|
||||
const std::function<void(
|
||||
absl::StatusOr<std::vector<grpc_resolved_address>>)>
|
||||
on_resolve_address_done_;
|
||||
// currently resolving addresses
|
||||
std::unique_ptr<EndpointAddressesList> balancer_addresses_;
|
||||
};
|
||||
|
||||
class AresTXTRequest : public AresRequest {
|
||||
public:
|
||||
AresTXTRequest(absl::string_view name, absl::string_view name_server,
|
||||
Duration timeout, grpc_pollset_set* interested_parties,
|
||||
std::function<void(absl::StatusOr<std::string>)> on_resolved,
|
||||
AresDNSResolver* resolver, intptr_t aba_token)
|
||||
: AresRequest(name, name_server, timeout, interested_parties, resolver,
|
||||
aba_token),
|
||||
on_resolved_(std::move(on_resolved)) {
|
||||
GRPC_CARES_TRACE_LOG("AresTXTRequest:%p ctor", this);
|
||||
}
|
||||
|
||||
~AresTXTRequest() override { gpr_free(service_config_json_); }
|
||||
|
||||
std::unique_ptr<grpc_ares_request> MakeRequestLocked() override {
|
||||
auto ares_request =
|
||||
std::unique_ptr<grpc_ares_request>(grpc_dns_lookup_txt_ares(
|
||||
name_server().c_str(), name().c_str(), pollset_set(),
|
||||
on_dns_lookup_done(), &service_config_json_, timeout().millis()));
|
||||
GRPC_CARES_TRACE_LOG("AresSRVRequest:%p Start ares_request_:%p", this,
|
||||
ares_request.get());
|
||||
return ares_request;
|
||||
}
|
||||
|
||||
void OnComplete(grpc_error_handle error) override {
|
||||
GRPC_CARES_TRACE_LOG("AresSRVRequest:%p OnComplete", this);
|
||||
if (!error.ok()) {
|
||||
on_resolved_(grpc_error_to_absl_status(error));
|
||||
return;
|
||||
}
|
||||
on_resolved_(service_config_json_);
|
||||
}
|
||||
|
||||
// service config from the TXT record
|
||||
char* service_config_json_ = nullptr;
|
||||
// user-provided completion callback
|
||||
const std::function<void(absl::StatusOr<std::string>)> on_resolved_;
|
||||
};
|
||||
|
||||
TaskHandle LookupHostname(
|
||||
std::function<void(absl::StatusOr<std::vector<grpc_resolved_address>>)>
|
||||
on_resolved,
|
||||
absl::string_view name, absl::string_view default_port, Duration timeout,
|
||||
grpc_pollset_set* interested_parties,
|
||||
absl::string_view name_server) override {
|
||||
MutexLock lock(&mu_);
|
||||
auto* request = new AresHostnameRequest(
|
||||
name, default_port, name_server, timeout, interested_parties,
|
||||
std::move(on_resolved), this, aba_token_++);
|
||||
request->Run();
|
||||
auto handle = request->task_handle();
|
||||
open_requests_.insert(handle);
|
||||
return handle;
|
||||
}
|
||||
|
||||
absl::StatusOr<std::vector<grpc_resolved_address>> LookupHostnameBlocking(
|
||||
absl::string_view name, absl::string_view default_port) override {
|
||||
// TODO(apolcyn): change this to wrap the async version of the c-ares
|
||||
// API with a promise, and remove the reference to the previous resolver.
|
||||
return default_resolver_->LookupHostnameBlocking(name, default_port);
|
||||
}
|
||||
|
||||
TaskHandle LookupSRV(
|
||||
std::function<void(absl::StatusOr<std::vector<grpc_resolved_address>>)>
|
||||
on_resolved,
|
||||
absl::string_view name, Duration timeout,
|
||||
grpc_pollset_set* interested_parties,
|
||||
absl::string_view name_server) override {
|
||||
MutexLock lock(&mu_);
|
||||
auto* request =
|
||||
new AresSRVRequest(name, name_server, timeout, interested_parties,
|
||||
std::move(on_resolved), this, aba_token_++);
|
||||
request->Run();
|
||||
auto handle = request->task_handle();
|
||||
open_requests_.insert(handle);
|
||||
return handle;
|
||||
};
|
||||
|
||||
TaskHandle LookupTXT(
|
||||
std::function<void(absl::StatusOr<std::string>)> on_resolved,
|
||||
absl::string_view name, Duration timeout,
|
||||
grpc_pollset_set* interested_parties,
|
||||
absl::string_view name_server) override {
|
||||
MutexLock lock(&mu_);
|
||||
auto* request =
|
||||
new AresTXTRequest(name, name_server, timeout, interested_parties,
|
||||
std::move(on_resolved), this, aba_token_++);
|
||||
request->Run();
|
||||
auto handle = request->task_handle();
|
||||
open_requests_.insert(handle);
|
||||
return handle;
|
||||
};
|
||||
|
||||
bool Cancel(TaskHandle handle) override {
|
||||
MutexLock lock(&mu_);
|
||||
if (!open_requests_.contains(handle)) {
|
||||
// Unknown request, possibly completed already, or an invalid handle.
|
||||
GRPC_CARES_TRACE_LOG(
|
||||
"AresDNSResolver:%p attempt to cancel unknown TaskHandle:%s", this,
|
||||
HandleToString(handle).c_str());
|
||||
return false;
|
||||
}
|
||||
auto* request = reinterpret_cast<AresRequest*>(handle.keys[0]);
|
||||
GRPC_CARES_TRACE_LOG("AresDNSResolver:%p cancel ares_request:%p", this,
|
||||
request);
|
||||
return request->Cancel();
|
||||
}
|
||||
|
||||
private:
|
||||
// Called exclusively from the AresRequest destructor.
|
||||
void UnregisterRequest(TaskHandle handle) {
|
||||
MutexLock lock(&mu_);
|
||||
open_requests_.erase(handle);
|
||||
}
|
||||
|
||||
// the previous default DNS resolver, used to delegate blocking DNS calls to
|
||||
std::shared_ptr<DNSResolver> default_resolver_ = GetDNSResolver();
|
||||
Mutex mu_;
|
||||
TaskHandleSet open_requests_ ABSL_GUARDED_BY(mu_);
|
||||
intptr_t aba_token_ ABSL_GUARDED_BY(mu_) = 0;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
bool ShouldUseAresDnsResolver(absl::string_view resolver_env) {
|
||||
return resolver_env.empty() || absl::EqualsIgnoreCase(resolver_env, "ares");
|
||||
}
|
||||
|
||||
void RegisterAresDnsResolver(CoreConfiguration::Builder* builder) {
|
||||
builder->resolver_registry()->RegisterResolverFactory(
|
||||
std::make_unique<AresClientChannelDNSResolverFactory>());
|
||||
}
|
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
void grpc_resolver_dns_ares_init() {
|
||||
if (grpc_core::ShouldUseAresDnsResolver(
|
||||
grpc_core::ConfigVars::Get().DnsResolver())) {
|
||||
address_sorting_init();
|
||||
grpc_error_handle error = grpc_ares_init();
|
||||
if (!error.ok()) {
|
||||
GRPC_LOG_IF_ERROR("grpc_ares_init() failed", error);
|
||||
return;
|
||||
}
|
||||
grpc_core::ResetDNSResolver(std::make_unique<grpc_core::AresDNSResolver>());
|
||||
}
|
||||
}
|
||||
|
||||
void grpc_resolver_dns_ares_shutdown() {
|
||||
if (grpc_core::ShouldUseAresDnsResolver(
|
||||
grpc_core::ConfigVars::Get().DnsResolver())) {
|
||||
address_sorting_shutdown();
|
||||
grpc_ares_cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
#else // GRPC_ARES == 1
|
||||
|
||||
namespace grpc_core {
|
||||
bool ShouldUseAresDnsResolver(absl::string_view /* resolver_env */) {
|
||||
return false;
|
||||
}
|
||||
void RegisterAresDnsResolver(CoreConfiguration::Builder*) {}
|
||||
} // namespace grpc_core
|
||||
|
||||
void grpc_resolver_dns_ares_init() {}
|
||||
|
||||
void grpc_resolver_dns_ares_shutdown() {}
|
||||
|
||||
#endif // GRPC_ARES == 1
|
||||
30
Pods/gRPC-Core/src/core/resolver/dns/c_ares/dns_resolver_ares.h
generated
Normal file
30
Pods/gRPC-Core/src/core/resolver/dns/c_ares/dns_resolver_ares.h
generated
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright 2022 The gRPC Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#ifndef GRPC_SRC_CORE_RESOLVER_DNS_C_ARES_DNS_RESOLVER_ARES_H
|
||||
#define GRPC_SRC_CORE_RESOLVER_DNS_C_ARES_DNS_RESOLVER_ARES_H
|
||||
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
#include "src/core/lib/config/core_configuration.h"
|
||||
|
||||
namespace grpc_core {
|
||||
|
||||
bool ShouldUseAresDnsResolver(absl::string_view resolver_env);
|
||||
void RegisterAresDnsResolver(CoreConfiguration::Builder*);
|
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_SRC_CORE_RESOLVER_DNS_C_ARES_DNS_RESOLVER_ARES_H
|
||||
90
Pods/gRPC-Core/src/core/resolver/dns/c_ares/grpc_ares_ev_driver.h
generated
Normal file
90
Pods/gRPC-Core/src/core/resolver/dns/c_ares/grpc_ares_ev_driver.h
generated
Normal file
@@ -0,0 +1,90 @@
|
||||
//
|
||||
//
|
||||
// Copyright 2016 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
//
|
||||
|
||||
#ifndef GRPC_SRC_CORE_RESOLVER_DNS_C_ARES_GRPC_ARES_EV_DRIVER_H
|
||||
#define GRPC_SRC_CORE_RESOLVER_DNS_C_ARES_GRPC_ARES_EV_DRIVER_H
|
||||
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <ares.h>
|
||||
|
||||
#include "absl/base/thread_annotations.h"
|
||||
|
||||
#include "src/core/resolver/dns/c_ares/grpc_ares_wrapper.h"
|
||||
#include "src/core/lib/gprpp/sync.h"
|
||||
#include "src/core/lib/iomgr/closure.h"
|
||||
#include "src/core/lib/iomgr/error.h"
|
||||
#include "src/core/lib/iomgr/iomgr_fwd.h"
|
||||
|
||||
namespace grpc_core {
|
||||
|
||||
// A wrapped fd that integrates with the grpc iomgr of the current platform.
|
||||
// A GrpcPolledFd knows how to create grpc platform-specific iomgr endpoints
|
||||
// from "ares_socket_t" sockets, and then sign up for readability/writeability
|
||||
// with that poller, and do shutdown and destruction.
|
||||
class GrpcPolledFd {
|
||||
public:
|
||||
virtual ~GrpcPolledFd() {}
|
||||
// Called when c-ares library is interested and there's no pending callback
|
||||
virtual void RegisterForOnReadableLocked(grpc_closure* read_closure)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&grpc_ares_request::mu) = 0;
|
||||
// Called when c-ares library is interested and there's no pending callback
|
||||
virtual void RegisterForOnWriteableLocked(grpc_closure* write_closure)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&grpc_ares_request::mu) = 0;
|
||||
// Indicates if there is data left even after just being read from
|
||||
virtual bool IsFdStillReadableLocked()
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&grpc_ares_request::mu) = 0;
|
||||
// Called once and only once. Must cause cancellation of any pending
|
||||
// read/write callbacks.
|
||||
virtual void ShutdownLocked(grpc_error_handle error)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&grpc_ares_request::mu) = 0;
|
||||
// Get the underlying ares_socket_t that this was created from
|
||||
virtual ares_socket_t GetWrappedAresSocketLocked()
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&grpc_ares_request::mu) = 0;
|
||||
// A unique name, for logging
|
||||
virtual const char* GetName() const = 0;
|
||||
};
|
||||
|
||||
// A GrpcPolledFdFactory is 1-to-1 with and owned by the
|
||||
// ares event driver. It knows how to create GrpcPolledFd's
|
||||
// for the current platform, and the ares driver uses it for all of
|
||||
// its fd's.
|
||||
class GrpcPolledFdFactory {
|
||||
public:
|
||||
virtual ~GrpcPolledFdFactory() {}
|
||||
// Creates a new wrapped fd for the current platform
|
||||
virtual GrpcPolledFd* NewGrpcPolledFdLocked(
|
||||
ares_socket_t as, grpc_pollset_set* driver_pollset_set)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&grpc_ares_request::mu) = 0;
|
||||
// Optionally configures the ares channel after creation
|
||||
virtual void ConfigureAresChannelLocked(ares_channel channel)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&grpc_ares_request::mu) = 0;
|
||||
};
|
||||
|
||||
// Creates a new polled fd factory.
|
||||
// Note that even though ownership of mu is not transferred, the mu
|
||||
// parameter is guaranteed to be alive for the the whole lifetime of
|
||||
// the resulting GrpcPolledFdFactory as well as any GrpcPolledFd
|
||||
// returned by the factory.
|
||||
std::unique_ptr<GrpcPolledFdFactory> NewGrpcPolledFdFactory(Mutex* mu);
|
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_SRC_CORE_RESOLVER_DNS_C_ARES_GRPC_ARES_EV_DRIVER_H
|
||||
206
Pods/gRPC-Core/src/core/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc
generated
Normal file
206
Pods/gRPC-Core/src/core/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc
generated
Normal file
@@ -0,0 +1,206 @@
|
||||
//
|
||||
//
|
||||
// Copyright 2016 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
//
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include "src/core/lib/iomgr/port.h"
|
||||
|
||||
#if GRPC_ARES == 1 && defined(GRPC_POSIX_SOCKET_ARES_EV_DRIVER)
|
||||
|
||||
// IWYU pragma: no_include <ares_build.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
#include <ares.h>
|
||||
|
||||
#include "absl/base/thread_annotations.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
|
||||
#include <grpc/support/log.h>
|
||||
|
||||
#include "src/core/resolver/dns/c_ares/grpc_ares_ev_driver.h"
|
||||
#include "src/core/resolver/dns/c_ares/grpc_ares_wrapper.h"
|
||||
#include "src/core/lib/gprpp/sync.h"
|
||||
#include "src/core/lib/iomgr/closure.h"
|
||||
#include "src/core/lib/iomgr/error.h"
|
||||
#include "src/core/lib/iomgr/ev_posix.h"
|
||||
#include "src/core/lib/iomgr/iomgr_fwd.h"
|
||||
#include "src/core/lib/iomgr/socket_utils_posix.h"
|
||||
|
||||
namespace grpc_core {
|
||||
|
||||
class GrpcPolledFdPosix : public GrpcPolledFd {
|
||||
public:
|
||||
GrpcPolledFdPosix(ares_socket_t as, grpc_pollset_set* driver_pollset_set)
|
||||
: name_(absl::StrCat("c-ares fd: ", static_cast<int>(as))), as_(as) {
|
||||
fd_ = grpc_fd_create(static_cast<int>(as), name_.c_str(), false);
|
||||
driver_pollset_set_ = driver_pollset_set;
|
||||
grpc_pollset_set_add_fd(driver_pollset_set_, fd_);
|
||||
}
|
||||
|
||||
~GrpcPolledFdPosix() override {
|
||||
grpc_pollset_set_del_fd(driver_pollset_set_, fd_);
|
||||
// c-ares library will close the fd inside grpc_fd. This fd may be picked up
|
||||
// immediately by another thread, and should not be closed by the following
|
||||
// grpc_fd_orphan.
|
||||
int phony_release_fd;
|
||||
grpc_fd_orphan(fd_, nullptr, &phony_release_fd, "c-ares query finished");
|
||||
}
|
||||
|
||||
void RegisterForOnReadableLocked(grpc_closure* read_closure)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&grpc_ares_request::mu) override {
|
||||
grpc_fd_notify_on_read(fd_, read_closure);
|
||||
}
|
||||
|
||||
void RegisterForOnWriteableLocked(grpc_closure* write_closure)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&grpc_ares_request::mu) override {
|
||||
grpc_fd_notify_on_write(fd_, write_closure);
|
||||
}
|
||||
|
||||
bool IsFdStillReadableLocked()
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&grpc_ares_request::mu) override {
|
||||
size_t bytes_available = 0;
|
||||
return ioctl(grpc_fd_wrapped_fd(fd_), FIONREAD, &bytes_available) == 0 &&
|
||||
bytes_available > 0;
|
||||
}
|
||||
|
||||
void ShutdownLocked(grpc_error_handle error)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&grpc_ares_request::mu) override {
|
||||
grpc_fd_shutdown(fd_, error);
|
||||
}
|
||||
|
||||
ares_socket_t GetWrappedAresSocketLocked()
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&grpc_ares_request::mu) override {
|
||||
return as_;
|
||||
}
|
||||
|
||||
const char* GetName() const override { return name_.c_str(); }
|
||||
|
||||
private:
|
||||
const std::string name_;
|
||||
const ares_socket_t as_;
|
||||
grpc_fd* fd_ ABSL_GUARDED_BY(&grpc_ares_request::mu);
|
||||
grpc_pollset_set* driver_pollset_set_ ABSL_GUARDED_BY(&grpc_ares_request::mu);
|
||||
};
|
||||
|
||||
class GrpcPolledFdFactoryPosix : public GrpcPolledFdFactory {
|
||||
public:
|
||||
~GrpcPolledFdFactoryPosix() override {
|
||||
for (auto& fd : owned_fds_) {
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
GrpcPolledFd* NewGrpcPolledFdLocked(
|
||||
ares_socket_t as, grpc_pollset_set* driver_pollset_set) override {
|
||||
auto insert_result = owned_fds_.insert(as);
|
||||
GPR_ASSERT(insert_result.second);
|
||||
return new GrpcPolledFdPosix(as, driver_pollset_set);
|
||||
}
|
||||
|
||||
void ConfigureAresChannelLocked(ares_channel channel) override {
|
||||
ares_set_socket_functions(channel, &kSockFuncs, this);
|
||||
ares_set_socket_configure_callback(
|
||||
channel, &GrpcPolledFdFactoryPosix::ConfigureSocket, nullptr);
|
||||
}
|
||||
|
||||
private:
|
||||
/// Overridden socket API for c-ares
|
||||
static ares_socket_t Socket(int af, int type, int protocol,
|
||||
void* /*user_data*/) {
|
||||
return socket(af, type, protocol);
|
||||
}
|
||||
|
||||
/// Overridden connect API for c-ares
|
||||
static int Connect(ares_socket_t as, const struct sockaddr* target,
|
||||
ares_socklen_t target_len, void* /*user_data*/) {
|
||||
return connect(as, target, target_len);
|
||||
}
|
||||
|
||||
/// Overridden writev API for c-ares
|
||||
static ares_ssize_t WriteV(ares_socket_t as, const struct iovec* iov,
|
||||
int iovec_count, void* /*user_data*/) {
|
||||
return writev(as, iov, iovec_count);
|
||||
}
|
||||
|
||||
/// Overridden recvfrom API for c-ares
|
||||
static ares_ssize_t RecvFrom(ares_socket_t as, void* data, size_t data_len,
|
||||
int flags, struct sockaddr* from,
|
||||
ares_socklen_t* from_len, void* /*user_data*/) {
|
||||
return recvfrom(as, data, data_len, flags, from, from_len);
|
||||
}
|
||||
|
||||
/// Overridden close API for c-ares
|
||||
static int Close(ares_socket_t as, void* user_data) {
|
||||
GrpcPolledFdFactoryPosix* self =
|
||||
static_cast<GrpcPolledFdFactoryPosix*>(user_data);
|
||||
if (self->owned_fds_.find(as) == self->owned_fds_.end()) {
|
||||
// c-ares owns this fd, grpc has never seen it
|
||||
return close(as);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Because we're using socket API overrides, c-ares won't
|
||||
/// perform its typical configuration on the socket. See
|
||||
/// https://github.com/c-ares/c-ares/blob/bad62225b7f6b278b92e8e85a255600b629ef517/src/lib/ares_process.c#L1018.
|
||||
/// So we use the configure socket callback override and copy default
|
||||
/// settings that c-ares would normally apply on posix platforms:
|
||||
/// - non-blocking
|
||||
/// - cloexec flag
|
||||
/// - disable nagle */
|
||||
static int ConfigureSocket(ares_socket_t fd, int type, void* /*user_data*/) {
|
||||
grpc_error_handle err;
|
||||
err = grpc_set_socket_nonblocking(fd, true);
|
||||
if (!err.ok()) return -1;
|
||||
err = grpc_set_socket_cloexec(fd, true);
|
||||
if (!err.ok()) return -1;
|
||||
if (type == SOCK_STREAM) {
|
||||
err = grpc_set_socket_low_latency(fd, true);
|
||||
if (!err.ok()) return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct ares_socket_functions kSockFuncs = {
|
||||
&GrpcPolledFdFactoryPosix::Socket /* socket */,
|
||||
&GrpcPolledFdFactoryPosix::Close /* close */,
|
||||
&GrpcPolledFdFactoryPosix::Connect /* connect */,
|
||||
&GrpcPolledFdFactoryPosix::RecvFrom /* recvfrom */,
|
||||
&GrpcPolledFdFactoryPosix::WriteV /* writev */,
|
||||
};
|
||||
|
||||
// fds that are used/owned by grpc - we (grpc) will close them rather than
|
||||
// c-ares
|
||||
std::unordered_set<ares_socket_t> owned_fds_;
|
||||
};
|
||||
|
||||
std::unique_ptr<GrpcPolledFdFactory> NewGrpcPolledFdFactory(Mutex* /* mu */) {
|
||||
return std::make_unique<GrpcPolledFdFactoryPosix>();
|
||||
}
|
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_ARES == 1 && defined(GRPC_POSIX_SOCKET_ARES_EV_DRIVER)
|
||||
818
Pods/gRPC-Core/src/core/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc
generated
Normal file
818
Pods/gRPC-Core/src/core/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc
generated
Normal file
@@ -0,0 +1,818 @@
|
||||
//
|
||||
//
|
||||
// Copyright 2016 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
//
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include "src/core/lib/iomgr/port.h" // IWYU pragma: keep
|
||||
#if GRPC_ARES == 1 && defined(GRPC_WINDOWS_SOCKET_ARES_EV_DRIVER)
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <ares.h>
|
||||
|
||||
#include "absl/functional/any_invocable.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
|
||||
#include <grpc/support/alloc.h>
|
||||
#include <grpc/support/log.h>
|
||||
#include <grpc/support/log_windows.h>
|
||||
#include <grpc/support/string_util.h>
|
||||
#include <grpc/support/time.h>
|
||||
|
||||
#include "src/core/resolver/dns/c_ares/grpc_ares_ev_driver.h"
|
||||
#include "src/core/resolver/dns/c_ares/grpc_ares_wrapper.h"
|
||||
#include "src/core/lib/address_utils/sockaddr_utils.h"
|
||||
#include "src/core/lib/gpr/string.h"
|
||||
#include "src/core/lib/gprpp/crash.h"
|
||||
#include "src/core/lib/gprpp/memory.h"
|
||||
#include "src/core/lib/iomgr/iocp_windows.h"
|
||||
#include "src/core/lib/iomgr/sockaddr_windows.h"
|
||||
#include "src/core/lib/iomgr/socket_windows.h"
|
||||
#include "src/core/lib/iomgr/tcp_windows.h"
|
||||
#include "src/core/lib/slice/slice.h"
|
||||
#include "src/core/lib/slice/slice_internal.h"
|
||||
|
||||
// TODO(apolcyn): remove this hack after fixing upstream.
|
||||
// Our grpc/c-ares code on Windows uses the ares_set_socket_functions API,
|
||||
// which uses "struct iovec" type, which on Windows is defined inside of
|
||||
// a c-ares header that is not public.
|
||||
// See https://github.com/c-ares/c-ares/issues/206.
|
||||
struct iovec {
|
||||
void* iov_base;
|
||||
size_t iov_len;
|
||||
};
|
||||
|
||||
namespace grpc_core {
|
||||
|
||||
namespace {
|
||||
|
||||
// c-ares reads and takes action on the error codes of the
|
||||
// "virtual socket operations" in this file, via the WSAGetLastError
|
||||
// APIs. If code in this file wants to set a specific WSA error that
|
||||
// c-ares should read, it must do so by calling SetWSAError() on the
|
||||
// WSAErrorContext instance passed to it. A WSAErrorContext must only be
|
||||
// instantiated at the top of the virtual socket function callstack.
|
||||
class WSAErrorContext {
|
||||
public:
|
||||
explicit WSAErrorContext(){};
|
||||
|
||||
~WSAErrorContext() {
|
||||
if (error_ != 0) {
|
||||
WSASetLastError(error_);
|
||||
}
|
||||
}
|
||||
|
||||
// Disallow copy and assignment operators
|
||||
WSAErrorContext(const WSAErrorContext&) = delete;
|
||||
WSAErrorContext& operator=(const WSAErrorContext&) = delete;
|
||||
|
||||
void SetWSAError(int error) { error_ = error; }
|
||||
|
||||
private:
|
||||
int error_ = 0;
|
||||
};
|
||||
|
||||
// c-ares creates its own sockets and is meant to read them when readable and
|
||||
// write them when writeable. To fit this socket usage model into the grpc
|
||||
// windows poller (which gives notifications when attempted reads and writes are
|
||||
// actually fulfilled rather than possible), this GrpcPolledFdWindows class
|
||||
// takes advantage of the ares_set_socket_functions API and acts as a virtual
|
||||
// socket. It holds its own read and write buffers which are written to and read
|
||||
// from c-ares and are used with the grpc windows poller, and it, e.g.,
|
||||
// manufactures virtual socket error codes when it e.g. needs to tell the c-ares
|
||||
// library to wait for an async read.
|
||||
class GrpcPolledFdWindows : public GrpcPolledFd {
|
||||
public:
|
||||
enum WriteState {
|
||||
WRITE_IDLE,
|
||||
WRITE_REQUESTED,
|
||||
WRITE_PENDING,
|
||||
WRITE_WAITING_FOR_VERIFICATION_UPON_RETRY,
|
||||
};
|
||||
|
||||
GrpcPolledFdWindows(ares_socket_t as, Mutex* mu, int address_family,
|
||||
int socket_type,
|
||||
absl::AnyInvocable<void()> on_shutdown_locked)
|
||||
: mu_(mu),
|
||||
read_buf_(grpc_empty_slice()),
|
||||
write_buf_(grpc_empty_slice()),
|
||||
name_(absl::StrFormat("c-ares socket: %" PRIdPTR, as)),
|
||||
address_family_(address_family),
|
||||
socket_type_(socket_type),
|
||||
on_shutdown_locked_(std::move(on_shutdown_locked)) {
|
||||
// Closure Initialization
|
||||
GRPC_CLOSURE_INIT(&outer_read_closure_,
|
||||
&GrpcPolledFdWindows::OnIocpReadable, this,
|
||||
grpc_schedule_on_exec_ctx);
|
||||
GRPC_CLOSURE_INIT(&outer_write_closure_,
|
||||
&GrpcPolledFdWindows::OnIocpWriteable, this,
|
||||
grpc_schedule_on_exec_ctx);
|
||||
GRPC_CLOSURE_INIT(&on_tcp_connect_locked_,
|
||||
&GrpcPolledFdWindows::OnTcpConnect, this,
|
||||
grpc_schedule_on_exec_ctx);
|
||||
winsocket_ = grpc_winsocket_create(as, name_.c_str());
|
||||
}
|
||||
|
||||
~GrpcPolledFdWindows() override {
|
||||
GRPC_CARES_TRACE_LOG("fd:|%s| ~GrpcPolledFdWindows shutdown_called_: %d ",
|
||||
GetName(), shutdown_called_);
|
||||
CSliceUnref(read_buf_);
|
||||
CSliceUnref(write_buf_);
|
||||
GPR_ASSERT(read_closure_ == nullptr);
|
||||
GPR_ASSERT(write_closure_ == nullptr);
|
||||
if (!shutdown_called_) {
|
||||
// This can happen if the socket was never seen by grpc ares wrapper
|
||||
// code, i.e. if we never started I/O polling on it.
|
||||
grpc_winsocket_shutdown(winsocket_);
|
||||
}
|
||||
grpc_winsocket_destroy(winsocket_);
|
||||
}
|
||||
|
||||
void ScheduleAndNullReadClosure(grpc_error_handle error) {
|
||||
ExecCtx::Run(DEBUG_LOCATION, read_closure_, error);
|
||||
read_closure_ = nullptr;
|
||||
}
|
||||
|
||||
void ScheduleAndNullWriteClosure(grpc_error_handle error) {
|
||||
ExecCtx::Run(DEBUG_LOCATION, write_closure_, error);
|
||||
write_closure_ = nullptr;
|
||||
}
|
||||
|
||||
void RegisterForOnReadableLocked(grpc_closure* read_closure) override {
|
||||
GPR_ASSERT(read_closure_ == nullptr);
|
||||
read_closure_ = read_closure;
|
||||
GPR_ASSERT(GRPC_SLICE_LENGTH(read_buf_) == 0);
|
||||
CSliceUnref(read_buf_);
|
||||
GPR_ASSERT(!read_buf_has_data_);
|
||||
read_buf_ = GRPC_SLICE_MALLOC(4192);
|
||||
if (connect_done_) {
|
||||
ContinueRegisterForOnReadableLocked();
|
||||
} else {
|
||||
GPR_ASSERT(pending_continue_register_for_on_readable_locked_ == false);
|
||||
pending_continue_register_for_on_readable_locked_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ContinueRegisterForOnReadableLocked() {
|
||||
GRPC_CARES_TRACE_LOG(
|
||||
"fd:|%s| ContinueRegisterForOnReadableLocked "
|
||||
"wsa_connect_error_:%d",
|
||||
GetName(), wsa_connect_error_);
|
||||
GPR_ASSERT(connect_done_);
|
||||
if (wsa_connect_error_ != 0) {
|
||||
ScheduleAndNullReadClosure(GRPC_WSA_ERROR(wsa_connect_error_, "connect"));
|
||||
return;
|
||||
}
|
||||
WSABUF buffer;
|
||||
buffer.buf = (char*)GRPC_SLICE_START_PTR(read_buf_);
|
||||
buffer.len = GRPC_SLICE_LENGTH(read_buf_);
|
||||
memset(&winsocket_->read_info.overlapped, 0, sizeof(OVERLAPPED));
|
||||
recv_from_source_addr_len_ = sizeof(recv_from_source_addr_);
|
||||
DWORD flags = 0;
|
||||
if (WSARecvFrom(grpc_winsocket_wrapped_socket(winsocket_), &buffer, 1,
|
||||
nullptr, &flags, (sockaddr*)recv_from_source_addr_,
|
||||
&recv_from_source_addr_len_,
|
||||
&winsocket_->read_info.overlapped, nullptr)) {
|
||||
int wsa_last_error = WSAGetLastError();
|
||||
char* msg = gpr_format_message(wsa_last_error);
|
||||
GRPC_CARES_TRACE_LOG(
|
||||
"fd:|%s| RegisterForOnReadableLocked WSARecvFrom error code:|%d| "
|
||||
"msg:|%s|",
|
||||
GetName(), wsa_last_error, msg);
|
||||
gpr_free(msg);
|
||||
if (wsa_last_error != WSA_IO_PENDING) {
|
||||
ScheduleAndNullReadClosure(
|
||||
GRPC_WSA_ERROR(wsa_last_error, "WSARecvFrom"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
grpc_socket_notify_on_read(winsocket_, &outer_read_closure_);
|
||||
}
|
||||
|
||||
void RegisterForOnWriteableLocked(grpc_closure* write_closure) override {
|
||||
if (socket_type_ == SOCK_DGRAM) {
|
||||
GRPC_CARES_TRACE_LOG("fd:|%s| RegisterForOnWriteableLocked called",
|
||||
GetName());
|
||||
} else {
|
||||
GPR_ASSERT(socket_type_ == SOCK_STREAM);
|
||||
GRPC_CARES_TRACE_LOG(
|
||||
"fd:|%s| RegisterForOnWriteableLocked called tcp_write_state_: %d "
|
||||
"connect_done_: %d",
|
||||
GetName(), tcp_write_state_, connect_done_);
|
||||
}
|
||||
GPR_ASSERT(write_closure_ == nullptr);
|
||||
write_closure_ = write_closure;
|
||||
if (!connect_done_) {
|
||||
GPR_ASSERT(!pending_continue_register_for_on_writeable_locked_);
|
||||
pending_continue_register_for_on_writeable_locked_ = true;
|
||||
// Register an async OnTcpConnect callback here rather than when the
|
||||
// connect was initiated, since we are now guaranteed to hold a ref of the
|
||||
// c-ares wrapper before write_closure_ is called.
|
||||
grpc_socket_notify_on_write(winsocket_, &on_tcp_connect_locked_);
|
||||
} else {
|
||||
ContinueRegisterForOnWriteableLocked();
|
||||
}
|
||||
}
|
||||
|
||||
void ContinueRegisterForOnWriteableLocked() {
|
||||
GRPC_CARES_TRACE_LOG(
|
||||
"fd:|%s| ContinueRegisterForOnWriteableLocked "
|
||||
"wsa_connect_error_:%d",
|
||||
GetName(), wsa_connect_error_);
|
||||
GPR_ASSERT(connect_done_);
|
||||
if (wsa_connect_error_ != 0) {
|
||||
ScheduleAndNullWriteClosure(
|
||||
GRPC_WSA_ERROR(wsa_connect_error_, "connect"));
|
||||
return;
|
||||
}
|
||||
if (socket_type_ == SOCK_DGRAM) {
|
||||
ScheduleAndNullWriteClosure(absl::OkStatus());
|
||||
} else {
|
||||
GPR_ASSERT(socket_type_ == SOCK_STREAM);
|
||||
int wsa_error_code = 0;
|
||||
switch (tcp_write_state_) {
|
||||
case WRITE_IDLE:
|
||||
ScheduleAndNullWriteClosure(absl::OkStatus());
|
||||
break;
|
||||
case WRITE_REQUESTED:
|
||||
tcp_write_state_ = WRITE_PENDING;
|
||||
if (SendWriteBuf(nullptr, &winsocket_->write_info.overlapped,
|
||||
&wsa_error_code) != 0) {
|
||||
ScheduleAndNullWriteClosure(
|
||||
GRPC_WSA_ERROR(wsa_error_code, "WSASend (overlapped)"));
|
||||
} else {
|
||||
grpc_socket_notify_on_write(winsocket_, &outer_write_closure_);
|
||||
}
|
||||
break;
|
||||
case WRITE_PENDING:
|
||||
case WRITE_WAITING_FOR_VERIFICATION_UPON_RETRY:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool IsFdStillReadableLocked() override { return read_buf_has_data_; }
|
||||
|
||||
void ShutdownLocked(grpc_error_handle /* error */) override {
|
||||
GPR_ASSERT(!shutdown_called_);
|
||||
shutdown_called_ = true;
|
||||
on_shutdown_locked_();
|
||||
grpc_winsocket_shutdown(winsocket_);
|
||||
}
|
||||
|
||||
ares_socket_t GetWrappedAresSocketLocked() override {
|
||||
return grpc_winsocket_wrapped_socket(winsocket_);
|
||||
}
|
||||
|
||||
const char* GetName() const override { return name_.c_str(); }
|
||||
|
||||
ares_ssize_t RecvFrom(WSAErrorContext* wsa_error_ctx, void* data,
|
||||
ares_socket_t data_len, int /* flags */,
|
||||
struct sockaddr* from, ares_socklen_t* from_len) {
|
||||
GRPC_CARES_TRACE_LOG(
|
||||
"fd:|%s| RecvFrom called read_buf_has_data:%d Current read buf "
|
||||
"length:|%d|",
|
||||
GetName(), read_buf_has_data_, GRPC_SLICE_LENGTH(read_buf_));
|
||||
if (!read_buf_has_data_) {
|
||||
wsa_error_ctx->SetWSAError(WSAEWOULDBLOCK);
|
||||
return -1;
|
||||
}
|
||||
ares_ssize_t bytes_read = 0;
|
||||
for (size_t i = 0; i < GRPC_SLICE_LENGTH(read_buf_) && i < data_len; i++) {
|
||||
((char*)data)[i] = GRPC_SLICE_START_PTR(read_buf_)[i];
|
||||
bytes_read++;
|
||||
}
|
||||
read_buf_ = grpc_slice_sub_no_ref(read_buf_, bytes_read,
|
||||
GRPC_SLICE_LENGTH(read_buf_));
|
||||
if (GRPC_SLICE_LENGTH(read_buf_) == 0) {
|
||||
read_buf_has_data_ = false;
|
||||
}
|
||||
// c-ares overloads this recv_from virtual socket function to receive
|
||||
// data on both UDP and TCP sockets, and from is nullptr for TCP.
|
||||
if (from != nullptr) {
|
||||
GPR_ASSERT(*from_len <= recv_from_source_addr_len_);
|
||||
memcpy(from, &recv_from_source_addr_, recv_from_source_addr_len_);
|
||||
*from_len = recv_from_source_addr_len_;
|
||||
}
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
grpc_slice FlattenIovec(const struct iovec* iov, int iov_count) {
|
||||
int total = 0;
|
||||
for (int i = 0; i < iov_count; i++) {
|
||||
total += iov[i].iov_len;
|
||||
}
|
||||
grpc_slice out = GRPC_SLICE_MALLOC(total);
|
||||
size_t cur = 0;
|
||||
for (int i = 0; i < iov_count; i++) {
|
||||
for (size_t k = 0; k < iov[i].iov_len; k++) {
|
||||
GRPC_SLICE_START_PTR(out)[cur++] = ((char*)iov[i].iov_base)[k];
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
int SendWriteBuf(LPDWORD bytes_sent_ptr, LPWSAOVERLAPPED overlapped,
|
||||
int* wsa_error_code) {
|
||||
WSABUF buf;
|
||||
buf.len = GRPC_SLICE_LENGTH(write_buf_);
|
||||
buf.buf = (char*)GRPC_SLICE_START_PTR(write_buf_);
|
||||
DWORD flags = 0;
|
||||
int out = WSASend(grpc_winsocket_wrapped_socket(winsocket_), &buf, 1,
|
||||
bytes_sent_ptr, flags, overlapped, nullptr);
|
||||
*wsa_error_code = WSAGetLastError();
|
||||
GRPC_CARES_TRACE_LOG(
|
||||
"fd:|%s| SendWriteBuf WSASend buf.len:%d *bytes_sent_ptr:%d "
|
||||
"overlapped:%p "
|
||||
"return:%d *wsa_error_code:%d",
|
||||
GetName(), buf.len, bytes_sent_ptr != nullptr ? *bytes_sent_ptr : 0,
|
||||
overlapped, out, *wsa_error_code);
|
||||
return out;
|
||||
}
|
||||
|
||||
ares_ssize_t SendV(WSAErrorContext* wsa_error_ctx, const struct iovec* iov,
|
||||
int iov_count) {
|
||||
GRPC_CARES_TRACE_LOG(
|
||||
"fd:|%s| SendV called connect_done_:%d wsa_connect_error_:%d",
|
||||
GetName(), connect_done_, wsa_connect_error_);
|
||||
if (!connect_done_) {
|
||||
wsa_error_ctx->SetWSAError(WSAEWOULDBLOCK);
|
||||
return -1;
|
||||
}
|
||||
if (wsa_connect_error_ != 0) {
|
||||
wsa_error_ctx->SetWSAError(wsa_connect_error_);
|
||||
return -1;
|
||||
}
|
||||
switch (socket_type_) {
|
||||
case SOCK_DGRAM:
|
||||
return SendVUDP(wsa_error_ctx, iov, iov_count);
|
||||
case SOCK_STREAM:
|
||||
return SendVTCP(wsa_error_ctx, iov, iov_count);
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
ares_ssize_t SendVUDP(WSAErrorContext* wsa_error_ctx, const struct iovec* iov,
|
||||
int iov_count) {
|
||||
// c-ares doesn't handle retryable errors on writes of UDP sockets.
|
||||
// Therefore, the sendv handler for UDP sockets must only attempt
|
||||
// to write everything inline.
|
||||
GRPC_CARES_TRACE_LOG("fd:|%s| SendVUDP called", GetName());
|
||||
GPR_ASSERT(GRPC_SLICE_LENGTH(write_buf_) == 0);
|
||||
CSliceUnref(write_buf_);
|
||||
write_buf_ = FlattenIovec(iov, iov_count);
|
||||
DWORD bytes_sent = 0;
|
||||
int wsa_error_code = 0;
|
||||
if (SendWriteBuf(&bytes_sent, nullptr, &wsa_error_code) != 0) {
|
||||
CSliceUnref(write_buf_);
|
||||
write_buf_ = grpc_empty_slice();
|
||||
wsa_error_ctx->SetWSAError(wsa_error_code);
|
||||
char* msg = gpr_format_message(wsa_error_code);
|
||||
GRPC_CARES_TRACE_LOG(
|
||||
"fd:|%s| SendVUDP SendWriteBuf error code:%d msg:|%s|", GetName(),
|
||||
wsa_error_code, msg);
|
||||
gpr_free(msg);
|
||||
return -1;
|
||||
}
|
||||
write_buf_ = grpc_slice_sub_no_ref(write_buf_, bytes_sent,
|
||||
GRPC_SLICE_LENGTH(write_buf_));
|
||||
return bytes_sent;
|
||||
}
|
||||
|
||||
ares_ssize_t SendVTCP(WSAErrorContext* wsa_error_ctx, const struct iovec* iov,
|
||||
int iov_count) {
|
||||
// The "sendv" handler on TCP sockets buffers up write
|
||||
// requests and returns an artificial WSAEWOULDBLOCK. Writing that buffer
|
||||
// out in the background, and making further send progress in general, will
|
||||
// happen as long as c-ares continues to show interest in writeability on
|
||||
// this fd.
|
||||
GRPC_CARES_TRACE_LOG("fd:|%s| SendVTCP called tcp_write_state_:%d",
|
||||
GetName(), tcp_write_state_);
|
||||
switch (tcp_write_state_) {
|
||||
case WRITE_IDLE:
|
||||
tcp_write_state_ = WRITE_REQUESTED;
|
||||
GPR_ASSERT(GRPC_SLICE_LENGTH(write_buf_) == 0);
|
||||
CSliceUnref(write_buf_);
|
||||
write_buf_ = FlattenIovec(iov, iov_count);
|
||||
wsa_error_ctx->SetWSAError(WSAEWOULDBLOCK);
|
||||
return -1;
|
||||
case WRITE_REQUESTED:
|
||||
case WRITE_PENDING:
|
||||
wsa_error_ctx->SetWSAError(WSAEWOULDBLOCK);
|
||||
return -1;
|
||||
case WRITE_WAITING_FOR_VERIFICATION_UPON_RETRY:
|
||||
// c-ares is retrying a send on data that we previously returned
|
||||
// WSAEWOULDBLOCK for, but then subsequently wrote out in the
|
||||
// background. Right now, we assume that c-ares is retrying the same
|
||||
// send again. If c-ares still needs to send even more data, we'll get
|
||||
// to it eventually.
|
||||
grpc_slice currently_attempted = FlattenIovec(iov, iov_count);
|
||||
GPR_ASSERT(GRPC_SLICE_LENGTH(currently_attempted) >=
|
||||
GRPC_SLICE_LENGTH(write_buf_));
|
||||
ares_ssize_t total_sent = 0;
|
||||
for (size_t i = 0; i < GRPC_SLICE_LENGTH(write_buf_); i++) {
|
||||
GPR_ASSERT(GRPC_SLICE_START_PTR(currently_attempted)[i] ==
|
||||
GRPC_SLICE_START_PTR(write_buf_)[i]);
|
||||
total_sent++;
|
||||
}
|
||||
CSliceUnref(currently_attempted);
|
||||
tcp_write_state_ = WRITE_IDLE;
|
||||
return total_sent;
|
||||
}
|
||||
abort();
|
||||
}
|
||||
|
||||
static void OnTcpConnect(void* arg, grpc_error_handle error) {
|
||||
GrpcPolledFdWindows* grpc_polled_fd =
|
||||
static_cast<GrpcPolledFdWindows*>(arg);
|
||||
MutexLock lock(grpc_polled_fd->mu_);
|
||||
grpc_polled_fd->OnTcpConnectLocked(error);
|
||||
}
|
||||
|
||||
void OnTcpConnectLocked(grpc_error_handle error) {
|
||||
GRPC_CARES_TRACE_LOG(
|
||||
"fd:%s InnerOnTcpConnectLocked error:|%s| "
|
||||
"pending_register_for_readable:%d"
|
||||
" pending_register_for_writeable:%d",
|
||||
GetName(), StatusToString(error).c_str(),
|
||||
pending_continue_register_for_on_readable_locked_,
|
||||
pending_continue_register_for_on_writeable_locked_);
|
||||
GPR_ASSERT(!connect_done_);
|
||||
connect_done_ = true;
|
||||
GPR_ASSERT(wsa_connect_error_ == 0);
|
||||
if (!error.ok() || shutdown_called_) {
|
||||
wsa_connect_error_ = WSA_OPERATION_ABORTED;
|
||||
} else {
|
||||
DWORD transferred_bytes = 0;
|
||||
DWORD flags;
|
||||
BOOL wsa_success =
|
||||
WSAGetOverlappedResult(grpc_winsocket_wrapped_socket(winsocket_),
|
||||
&winsocket_->write_info.overlapped,
|
||||
&transferred_bytes, FALSE, &flags);
|
||||
GPR_ASSERT(transferred_bytes == 0);
|
||||
if (!wsa_success) {
|
||||
wsa_connect_error_ = WSAGetLastError();
|
||||
char* msg = gpr_format_message(wsa_connect_error_);
|
||||
GRPC_CARES_TRACE_LOG(
|
||||
"fd:%s InnerOnTcpConnectLocked WSA overlapped result code:%d "
|
||||
"msg:|%s|",
|
||||
GetName(), wsa_connect_error_, msg);
|
||||
gpr_free(msg);
|
||||
}
|
||||
}
|
||||
if (pending_continue_register_for_on_readable_locked_) {
|
||||
ContinueRegisterForOnReadableLocked();
|
||||
}
|
||||
if (pending_continue_register_for_on_writeable_locked_) {
|
||||
ContinueRegisterForOnWriteableLocked();
|
||||
}
|
||||
}
|
||||
|
||||
int Connect(WSAErrorContext* wsa_error_ctx, const struct sockaddr* target,
|
||||
ares_socklen_t target_len) {
|
||||
switch (socket_type_) {
|
||||
case SOCK_DGRAM:
|
||||
return ConnectUDP(wsa_error_ctx, target, target_len);
|
||||
case SOCK_STREAM:
|
||||
return ConnectTCP(wsa_error_ctx, target, target_len);
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
int ConnectUDP(WSAErrorContext* wsa_error_ctx, const struct sockaddr* target,
|
||||
ares_socklen_t target_len) {
|
||||
GRPC_CARES_TRACE_LOG("fd:%s ConnectUDP", GetName());
|
||||
GPR_ASSERT(!connect_done_);
|
||||
GPR_ASSERT(wsa_connect_error_ == 0);
|
||||
SOCKET s = grpc_winsocket_wrapped_socket(winsocket_);
|
||||
int out =
|
||||
WSAConnect(s, target, target_len, nullptr, nullptr, nullptr, nullptr);
|
||||
wsa_connect_error_ = WSAGetLastError();
|
||||
wsa_error_ctx->SetWSAError(wsa_connect_error_);
|
||||
connect_done_ = true;
|
||||
char* msg = gpr_format_message(wsa_connect_error_);
|
||||
GRPC_CARES_TRACE_LOG("fd:%s WSAConnect error code:|%d| msg:|%s|", GetName(),
|
||||
wsa_connect_error_, msg);
|
||||
gpr_free(msg);
|
||||
// c-ares expects a posix-style connect API
|
||||
return out == 0 ? 0 : -1;
|
||||
}
|
||||
|
||||
int ConnectTCP(WSAErrorContext* wsa_error_ctx, const struct sockaddr* target,
|
||||
ares_socklen_t target_len) {
|
||||
GRPC_CARES_TRACE_LOG("fd:%s ConnectTCP", GetName());
|
||||
LPFN_CONNECTEX ConnectEx;
|
||||
GUID guid = WSAID_CONNECTEX;
|
||||
DWORD ioctl_num_bytes;
|
||||
SOCKET s = grpc_winsocket_wrapped_socket(winsocket_);
|
||||
if (WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid),
|
||||
&ConnectEx, sizeof(ConnectEx), &ioctl_num_bytes, nullptr,
|
||||
nullptr) != 0) {
|
||||
int wsa_last_error = WSAGetLastError();
|
||||
wsa_error_ctx->SetWSAError(wsa_last_error);
|
||||
char* msg = gpr_format_message(wsa_last_error);
|
||||
GRPC_CARES_TRACE_LOG(
|
||||
"fd:%s WSAIoctl(SIO_GET_EXTENSION_FUNCTION_POINTER) error code:%d "
|
||||
"msg:|%s|",
|
||||
GetName(), wsa_last_error, msg);
|
||||
gpr_free(msg);
|
||||
connect_done_ = true;
|
||||
wsa_connect_error_ = wsa_last_error;
|
||||
return -1;
|
||||
}
|
||||
grpc_resolved_address wildcard4_addr;
|
||||
grpc_resolved_address wildcard6_addr;
|
||||
grpc_sockaddr_make_wildcards(0, &wildcard4_addr, &wildcard6_addr);
|
||||
grpc_resolved_address* local_address = nullptr;
|
||||
if (address_family_ == AF_INET) {
|
||||
local_address = &wildcard4_addr;
|
||||
} else {
|
||||
local_address = &wildcard6_addr;
|
||||
}
|
||||
if (bind(s, (struct sockaddr*)local_address->addr,
|
||||
(int)local_address->len) != 0) {
|
||||
int wsa_last_error = WSAGetLastError();
|
||||
wsa_error_ctx->SetWSAError(wsa_last_error);
|
||||
char* msg = gpr_format_message(wsa_last_error);
|
||||
GRPC_CARES_TRACE_LOG("fd:%s bind error code:%d msg:|%s|", GetName(),
|
||||
wsa_last_error, msg);
|
||||
gpr_free(msg);
|
||||
connect_done_ = true;
|
||||
wsa_connect_error_ = wsa_last_error;
|
||||
return -1;
|
||||
}
|
||||
int out = 0;
|
||||
if (ConnectEx(s, target, target_len, nullptr, 0, nullptr,
|
||||
&winsocket_->write_info.overlapped) == 0) {
|
||||
out = -1;
|
||||
int wsa_last_error = WSAGetLastError();
|
||||
wsa_error_ctx->SetWSAError(wsa_last_error);
|
||||
char* msg = gpr_format_message(wsa_last_error);
|
||||
GRPC_CARES_TRACE_LOG("fd:%s ConnectEx error code:%d msg:|%s|", GetName(),
|
||||
wsa_last_error, msg);
|
||||
gpr_free(msg);
|
||||
if (wsa_last_error == WSA_IO_PENDING) {
|
||||
// c-ares only understands WSAEINPROGRESS and EWOULDBLOCK error codes on
|
||||
// connect, but an async connect on IOCP socket will give
|
||||
// WSA_IO_PENDING, so we need to convert.
|
||||
wsa_error_ctx->SetWSAError(WSAEWOULDBLOCK);
|
||||
} else {
|
||||
// By returning a non-retryable error to c-ares at this point,
|
||||
// we're aborting the possibility of any future operations on this fd.
|
||||
connect_done_ = true;
|
||||
wsa_connect_error_ = wsa_last_error;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
// RegisterForOnWriteable will register for an async notification
|
||||
return out;
|
||||
}
|
||||
|
||||
static void OnIocpReadable(void* arg, grpc_error_handle error) {
|
||||
GrpcPolledFdWindows* polled_fd = static_cast<GrpcPolledFdWindows*>(arg);
|
||||
MutexLock lock(polled_fd->mu_);
|
||||
polled_fd->OnIocpReadableLocked(error);
|
||||
}
|
||||
|
||||
// TODO(apolcyn): improve this error handling to be less conversative.
|
||||
// An e.g. ECONNRESET error here should result in errors when
|
||||
// c-ares reads from this socket later, but it shouldn't necessarily cancel
|
||||
// the entire resolution attempt. Doing so will allow the "inject broken
|
||||
// nameserver list" test to pass on Windows.
|
||||
void OnIocpReadableLocked(grpc_error_handle error) {
|
||||
if (error.ok()) {
|
||||
if (winsocket_->read_info.wsa_error != 0) {
|
||||
// WSAEMSGSIZE would be due to receiving more data
|
||||
// than our read buffer's fixed capacity. Assume that
|
||||
// the connection is TCP and read the leftovers
|
||||
// in subsequent c-ares reads.
|
||||
if (winsocket_->read_info.wsa_error != WSAEMSGSIZE) {
|
||||
error = GRPC_WSA_ERROR(winsocket_->read_info.wsa_error,
|
||||
"OnIocpReadableInner");
|
||||
GRPC_CARES_TRACE_LOG(
|
||||
"fd:|%s| OnIocpReadableInner winsocket_->read_info.wsa_error "
|
||||
"code:|%d| msg:|%s|",
|
||||
GetName(), winsocket_->read_info.wsa_error,
|
||||
StatusToString(error).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (error.ok()) {
|
||||
read_buf_ = grpc_slice_sub_no_ref(
|
||||
read_buf_, 0, winsocket_->read_info.bytes_transferred);
|
||||
read_buf_has_data_ = true;
|
||||
} else {
|
||||
CSliceUnref(read_buf_);
|
||||
read_buf_ = grpc_empty_slice();
|
||||
}
|
||||
GRPC_CARES_TRACE_LOG(
|
||||
"fd:|%s| OnIocpReadable finishing. read buf length now:|%d|", GetName(),
|
||||
GRPC_SLICE_LENGTH(read_buf_));
|
||||
ScheduleAndNullReadClosure(error);
|
||||
}
|
||||
|
||||
static void OnIocpWriteable(void* arg, grpc_error_handle error) {
|
||||
GrpcPolledFdWindows* polled_fd = static_cast<GrpcPolledFdWindows*>(arg);
|
||||
MutexLock lock(polled_fd->mu_);
|
||||
polled_fd->OnIocpWriteableLocked(error);
|
||||
}
|
||||
|
||||
void OnIocpWriteableLocked(grpc_error_handle error) {
|
||||
GRPC_CARES_TRACE_LOG("OnIocpWriteableInner. fd:|%s|", GetName());
|
||||
GPR_ASSERT(socket_type_ == SOCK_STREAM);
|
||||
if (error.ok()) {
|
||||
if (winsocket_->write_info.wsa_error != 0) {
|
||||
error = GRPC_WSA_ERROR(winsocket_->write_info.wsa_error,
|
||||
"OnIocpWriteableInner");
|
||||
GRPC_CARES_TRACE_LOG(
|
||||
"fd:|%s| OnIocpWriteableInner. winsocket_->write_info.wsa_error "
|
||||
"code:|%d| msg:|%s|",
|
||||
GetName(), winsocket_->write_info.wsa_error,
|
||||
StatusToString(error).c_str());
|
||||
}
|
||||
}
|
||||
GPR_ASSERT(tcp_write_state_ == WRITE_PENDING);
|
||||
if (error.ok()) {
|
||||
tcp_write_state_ = WRITE_WAITING_FOR_VERIFICATION_UPON_RETRY;
|
||||
write_buf_ = grpc_slice_sub_no_ref(
|
||||
write_buf_, 0, winsocket_->write_info.bytes_transferred);
|
||||
GRPC_CARES_TRACE_LOG("fd:|%s| OnIocpWriteableInner. bytes transferred:%d",
|
||||
GetName(), winsocket_->write_info.bytes_transferred);
|
||||
} else {
|
||||
CSliceUnref(write_buf_);
|
||||
write_buf_ = grpc_empty_slice();
|
||||
}
|
||||
ScheduleAndNullWriteClosure(error);
|
||||
}
|
||||
|
||||
private:
|
||||
Mutex* mu_;
|
||||
char recv_from_source_addr_[200];
|
||||
ares_socklen_t recv_from_source_addr_len_;
|
||||
grpc_slice read_buf_;
|
||||
bool read_buf_has_data_ = false;
|
||||
grpc_slice write_buf_;
|
||||
grpc_closure* read_closure_ = nullptr;
|
||||
grpc_closure* write_closure_ = nullptr;
|
||||
grpc_closure outer_read_closure_;
|
||||
grpc_closure outer_write_closure_;
|
||||
grpc_winsocket* winsocket_;
|
||||
const std::string name_;
|
||||
bool shutdown_called_ = false;
|
||||
int address_family_;
|
||||
int socket_type_;
|
||||
// State related to TCP sockets
|
||||
grpc_closure on_tcp_connect_locked_;
|
||||
bool connect_done_ = false;
|
||||
int wsa_connect_error_ = 0;
|
||||
WriteState tcp_write_state_ = WRITE_IDLE;
|
||||
// We don't run register_for_{readable,writeable} logic until
|
||||
// a socket is connected. In the interim, we queue readable/writeable
|
||||
// registrations with the following state.
|
||||
bool pending_continue_register_for_on_readable_locked_ = false;
|
||||
bool pending_continue_register_for_on_writeable_locked_ = false;
|
||||
absl::AnyInvocable<void()> on_shutdown_locked_;
|
||||
};
|
||||
|
||||
class GrpcPolledFdFactoryWindows : public GrpcPolledFdFactory {
|
||||
public:
|
||||
explicit GrpcPolledFdFactoryWindows(Mutex* mu) : mu_(mu) {}
|
||||
|
||||
~GrpcPolledFdFactoryWindows() override {
|
||||
// We might still have a socket -> polled fd mappings if the socket
|
||||
// was never seen by the grpc ares wrapper code, i.e. if we never
|
||||
// initiated I/O polling for them.
|
||||
for (auto& it : sockets_) {
|
||||
delete it.second;
|
||||
}
|
||||
}
|
||||
|
||||
GrpcPolledFd* NewGrpcPolledFdLocked(
|
||||
ares_socket_t as, grpc_pollset_set* /* driver_pollset_set */) override {
|
||||
auto it = sockets_.find(as);
|
||||
GPR_ASSERT(it != sockets_.end());
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void ConfigureAresChannelLocked(ares_channel channel) override {
|
||||
ares_set_socket_functions(channel, &kCustomSockFuncs, this);
|
||||
}
|
||||
|
||||
private:
|
||||
// These virtual socket functions are called from within the c-ares
|
||||
// library. These methods generally dispatch those socket calls to the
|
||||
// appropriate methods. The virtual "socket" and "close" methods are
|
||||
// special and instead create/add and remove/destroy GrpcPolledFdWindows
|
||||
// objects.
|
||||
//
|
||||
static ares_socket_t Socket(int af, int type, int protocol, void* user_data) {
|
||||
if (type != SOCK_DGRAM && type != SOCK_STREAM) {
|
||||
GRPC_CARES_TRACE_LOG("Socket called with invalid socket type:%d", type);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
GrpcPolledFdFactoryWindows* self =
|
||||
static_cast<GrpcPolledFdFactoryWindows*>(user_data);
|
||||
SOCKET s = WSASocket(af, type, protocol, nullptr, 0,
|
||||
grpc_get_default_wsa_socket_flags());
|
||||
if (s == INVALID_SOCKET) {
|
||||
GRPC_CARES_TRACE_LOG(
|
||||
"WSASocket failed with params af:%d type:%d protocol:%d", af, type,
|
||||
protocol);
|
||||
return s;
|
||||
}
|
||||
grpc_error_handle error = grpc_tcp_set_non_block(s);
|
||||
if (!error.ok()) {
|
||||
GRPC_CARES_TRACE_LOG("WSAIoctl failed with error: %s",
|
||||
StatusToString(error).c_str());
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
auto on_shutdown_locked = [self, s]() {
|
||||
// grpc_winsocket_shutdown calls closesocket which invalidates our
|
||||
// socket -> polled_fd mapping because the socket handle can be henceforth
|
||||
// reused.
|
||||
self->sockets_.erase(s);
|
||||
};
|
||||
auto polled_fd = new GrpcPolledFdWindows(s, self->mu_, af, type,
|
||||
std::move(on_shutdown_locked));
|
||||
GRPC_CARES_TRACE_LOG(
|
||||
"fd:|%s| created with params af:%d type:%d protocol:%d",
|
||||
polled_fd->GetName(), af, type, protocol);
|
||||
GPR_ASSERT(self->sockets_.insert({s, polled_fd}).second);
|
||||
return s;
|
||||
}
|
||||
|
||||
static int Connect(ares_socket_t as, const struct sockaddr* target,
|
||||
ares_socklen_t target_len, void* user_data) {
|
||||
WSAErrorContext wsa_error_ctx;
|
||||
GrpcPolledFdFactoryWindows* self =
|
||||
static_cast<GrpcPolledFdFactoryWindows*>(user_data);
|
||||
auto it = self->sockets_.find(as);
|
||||
GPR_ASSERT(it != self->sockets_.end());
|
||||
return it->second->Connect(&wsa_error_ctx, target, target_len);
|
||||
}
|
||||
|
||||
static ares_ssize_t SendV(ares_socket_t as, const struct iovec* iov,
|
||||
int iovec_count, void* user_data) {
|
||||
WSAErrorContext wsa_error_ctx;
|
||||
GrpcPolledFdFactoryWindows* self =
|
||||
static_cast<GrpcPolledFdFactoryWindows*>(user_data);
|
||||
auto it = self->sockets_.find(as);
|
||||
GPR_ASSERT(it != self->sockets_.end());
|
||||
return it->second->SendV(&wsa_error_ctx, iov, iovec_count);
|
||||
}
|
||||
|
||||
static ares_ssize_t RecvFrom(ares_socket_t as, void* data, size_t data_len,
|
||||
int flags, struct sockaddr* from,
|
||||
ares_socklen_t* from_len, void* user_data) {
|
||||
WSAErrorContext wsa_error_ctx;
|
||||
GrpcPolledFdFactoryWindows* self =
|
||||
static_cast<GrpcPolledFdFactoryWindows*>(user_data);
|
||||
auto it = self->sockets_.find(as);
|
||||
GPR_ASSERT(it != self->sockets_.end());
|
||||
return it->second->RecvFrom(&wsa_error_ctx, data, data_len, flags, from,
|
||||
from_len);
|
||||
}
|
||||
|
||||
static int CloseSocket(SOCKET /* s */, void* /* user_data */) { return 0; }
|
||||
|
||||
const struct ares_socket_functions kCustomSockFuncs = {
|
||||
&GrpcPolledFdFactoryWindows::Socket /* socket */,
|
||||
&GrpcPolledFdFactoryWindows::CloseSocket /* close */,
|
||||
&GrpcPolledFdFactoryWindows::Connect /* connect */,
|
||||
&GrpcPolledFdFactoryWindows::RecvFrom /* recvfrom */,
|
||||
&GrpcPolledFdFactoryWindows::SendV /* sendv */,
|
||||
};
|
||||
|
||||
Mutex* mu_;
|
||||
std::map<SOCKET, GrpcPolledFdWindows*> sockets_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<GrpcPolledFdFactory> NewGrpcPolledFdFactory(Mutex* mu) {
|
||||
return std::make_unique<GrpcPolledFdFactoryWindows>(mu);
|
||||
}
|
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_ARES == 1 && defined(GPR_WINDOWS)
|
||||
1219
Pods/gRPC-Core/src/core/resolver/dns/c_ares/grpc_ares_wrapper.cc
generated
Normal file
1219
Pods/gRPC-Core/src/core/resolver/dns/c_ares/grpc_ares_wrapper.cc
generated
Normal file
@@ -0,0 +1,1219 @@
|
||||
//
|
||||
//
|
||||
// Copyright 2016 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
//
|
||||
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
#include <grpc/impl/channel_arg_names.h>
|
||||
|
||||
#include "src/core/lib/gprpp/status_helper.h"
|
||||
#include "src/core/lib/iomgr/sockaddr.h"
|
||||
|
||||
// IWYU pragma: no_include <arpa/inet.h>
|
||||
// IWYU pragma: no_include <arpa/nameser.h>
|
||||
// IWYU pragma: no_include <inttypes.h>
|
||||
// IWYU pragma: no_include <netdb.h>
|
||||
// IWYU pragma: no_include <netinet/in.h>
|
||||
// IWYU pragma: no_include <stdlib.h>
|
||||
// IWYU pragma: no_include <sys/socket.h>
|
||||
|
||||
#if GRPC_ARES == 1
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/types.h> // IWYU pragma: keep
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <address_sorting/address_sorting.h>
|
||||
#include <ares.h>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
|
||||
#include <grpc/support/alloc.h>
|
||||
#include <grpc/support/log.h>
|
||||
#include <grpc/support/string_util.h>
|
||||
#include <grpc/support/sync.h>
|
||||
|
||||
#include "src/core/resolver/dns/c_ares/grpc_ares_ev_driver.h"
|
||||
#include "src/core/resolver/dns/c_ares/grpc_ares_wrapper.h"
|
||||
#include "src/core/lib/address_utils/parse_address.h"
|
||||
#include "src/core/lib/address_utils/sockaddr_utils.h"
|
||||
#include "src/core/lib/channel/channel_args.h"
|
||||
#include "src/core/lib/gpr/string.h"
|
||||
#include "src/core/lib/gprpp/debug_location.h"
|
||||
#include "src/core/lib/gprpp/host_port.h"
|
||||
#include "src/core/lib/gprpp/time.h"
|
||||
#include "src/core/lib/iomgr/error.h"
|
||||
#include "src/core/lib/iomgr/exec_ctx.h"
|
||||
#include "src/core/lib/iomgr/nameser.h" // IWYU pragma: keep
|
||||
#include "src/core/lib/iomgr/resolved_address.h"
|
||||
#include "src/core/lib/iomgr/timer.h"
|
||||
|
||||
using grpc_core::EndpointAddresses;
|
||||
using grpc_core::EndpointAddressesList;
|
||||
|
||||
grpc_core::TraceFlag grpc_trace_cares_address_sorting(false,
|
||||
"cares_address_sorting");
|
||||
|
||||
grpc_core::TraceFlag grpc_trace_cares_resolver(false, "cares_resolver");
|
||||
|
||||
typedef struct fd_node {
|
||||
// default constructor exists only for linked list manipulation
|
||||
fd_node() : ev_driver(nullptr) {}
|
||||
|
||||
explicit fd_node(grpc_ares_ev_driver* ev_driver) : ev_driver(ev_driver) {}
|
||||
|
||||
/// the owner of this fd node
|
||||
grpc_ares_ev_driver* const ev_driver;
|
||||
/// a closure wrapping on_readable_locked, which should be
|
||||
/// invoked when the grpc_fd in this node becomes readable.
|
||||
grpc_closure read_closure ABSL_GUARDED_BY(&grpc_ares_request::mu);
|
||||
/// a closure wrapping on_writable_locked, which should be
|
||||
/// invoked when the grpc_fd in this node becomes writable.
|
||||
grpc_closure write_closure ABSL_GUARDED_BY(&grpc_ares_request::mu);
|
||||
/// next fd node in the list
|
||||
struct fd_node* next ABSL_GUARDED_BY(&grpc_ares_request::mu);
|
||||
|
||||
/// wrapped fd that's polled by grpc's poller for the current platform
|
||||
grpc_core::GrpcPolledFd* grpc_polled_fd
|
||||
ABSL_GUARDED_BY(&grpc_ares_request::mu);
|
||||
/// if the readable closure has been registered
|
||||
bool readable_registered ABSL_GUARDED_BY(&grpc_ares_request::mu);
|
||||
/// if the writable closure has been registered
|
||||
bool writable_registered ABSL_GUARDED_BY(&grpc_ares_request::mu);
|
||||
/// if the fd has been shutdown yet from grpc iomgr perspective
|
||||
bool already_shutdown ABSL_GUARDED_BY(&grpc_ares_request::mu);
|
||||
} fd_node;
|
||||
|
||||
struct grpc_ares_ev_driver {
|
||||
explicit grpc_ares_ev_driver(grpc_ares_request* request) : request(request) {}
|
||||
|
||||
/// the ares_channel owned by this event driver
|
||||
ares_channel channel ABSL_GUARDED_BY(&grpc_ares_request::mu);
|
||||
/// pollset set for driving the IO events of the channel
|
||||
grpc_pollset_set* pollset_set ABSL_GUARDED_BY(&grpc_ares_request::mu);
|
||||
/// refcount of the event driver
|
||||
gpr_refcount refs;
|
||||
|
||||
/// a list of grpc_fd that this event driver is currently using.
|
||||
fd_node* fds ABSL_GUARDED_BY(&grpc_ares_request::mu);
|
||||
/// is this event driver being shut down
|
||||
bool shutting_down ABSL_GUARDED_BY(&grpc_ares_request::mu);
|
||||
/// request object that's using this ev driver
|
||||
grpc_ares_request* const request;
|
||||
/// Owned by the ev_driver. Creates new GrpcPolledFd's
|
||||
std::unique_ptr<grpc_core::GrpcPolledFdFactory> polled_fd_factory
|
||||
ABSL_GUARDED_BY(&grpc_ares_request::mu);
|
||||
/// query timeout in milliseconds
|
||||
int query_timeout_ms ABSL_GUARDED_BY(&grpc_ares_request::mu);
|
||||
/// alarm to cancel active queries
|
||||
grpc_timer query_timeout ABSL_GUARDED_BY(&grpc_ares_request::mu);
|
||||
/// cancels queries on a timeout
|
||||
grpc_closure on_timeout_locked ABSL_GUARDED_BY(&grpc_ares_request::mu);
|
||||
/// alarm to poll ares_process on in case fd events don't happen
|
||||
grpc_timer ares_backup_poll_alarm ABSL_GUARDED_BY(&grpc_ares_request::mu);
|
||||
/// polls ares_process on a periodic timer
|
||||
grpc_closure on_ares_backup_poll_alarm_locked
|
||||
ABSL_GUARDED_BY(&grpc_ares_request::mu);
|
||||
};
|
||||
|
||||
// TODO(apolcyn): make grpc_ares_hostbyname_request a sub-class
|
||||
// of GrpcAresQuery.
|
||||
typedef struct grpc_ares_hostbyname_request {
|
||||
/// following members are set in create_hostbyname_request_locked
|
||||
///
|
||||
/// the top-level request instance
|
||||
grpc_ares_request* parent_request;
|
||||
/// host to resolve, parsed from the name to resolve
|
||||
char* host;
|
||||
/// port to fill in sockaddr_in, parsed from the name to resolve
|
||||
uint16_t port;
|
||||
/// is it a grpclb address
|
||||
bool is_balancer;
|
||||
/// for logging and errors: the query type ("A" or "AAAA")
|
||||
const char* qtype;
|
||||
} grpc_ares_hostbyname_request;
|
||||
|
||||
static void grpc_ares_request_ref_locked(grpc_ares_request* r)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(r->mu);
|
||||
static void grpc_ares_request_unref_locked(grpc_ares_request* r)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(r->mu);
|
||||
|
||||
// TODO(apolcyn): as a part of C++-ification, find a way to
|
||||
// organize per-query and per-resolution information in such a way
|
||||
// that doesn't involve allocating a number of different data
|
||||
// structures.
|
||||
class GrpcAresQuery {
|
||||
public:
|
||||
explicit GrpcAresQuery(grpc_ares_request* r, const std::string& name)
|
||||
: r_(r), name_(name) {
|
||||
grpc_ares_request_ref_locked(r_);
|
||||
}
|
||||
|
||||
~GrpcAresQuery() { grpc_ares_request_unref_locked(r_); }
|
||||
|
||||
grpc_ares_request* parent_request() { return r_; }
|
||||
|
||||
const std::string& name() { return name_; }
|
||||
|
||||
private:
|
||||
// the top level request instance
|
||||
grpc_ares_request* r_;
|
||||
/// for logging and errors
|
||||
const std::string name_;
|
||||
};
|
||||
|
||||
static grpc_ares_ev_driver* grpc_ares_ev_driver_ref(
|
||||
grpc_ares_ev_driver* ev_driver)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&grpc_ares_request::mu) {
|
||||
GRPC_CARES_TRACE_LOG("request:%p Ref ev_driver %p", ev_driver->request,
|
||||
ev_driver);
|
||||
gpr_ref(&ev_driver->refs);
|
||||
return ev_driver;
|
||||
}
|
||||
|
||||
static void grpc_ares_complete_request_locked(grpc_ares_request* r)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(r->mu);
|
||||
|
||||
static void grpc_ares_ev_driver_unref(grpc_ares_ev_driver* ev_driver)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&grpc_ares_request::mu) {
|
||||
GRPC_CARES_TRACE_LOG("request:%p Unref ev_driver %p", ev_driver->request,
|
||||
ev_driver);
|
||||
if (gpr_unref(&ev_driver->refs)) {
|
||||
GRPC_CARES_TRACE_LOG("request:%p destroy ev_driver %p", ev_driver->request,
|
||||
ev_driver);
|
||||
GPR_ASSERT(ev_driver->fds == nullptr);
|
||||
ares_destroy(ev_driver->channel);
|
||||
grpc_ares_complete_request_locked(ev_driver->request);
|
||||
delete ev_driver;
|
||||
}
|
||||
}
|
||||
|
||||
static void fd_node_destroy_locked(fd_node* fdn)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&grpc_ares_request::mu) {
|
||||
GRPC_CARES_TRACE_LOG("request:%p delete fd: %s", fdn->ev_driver->request,
|
||||
fdn->grpc_polled_fd->GetName());
|
||||
GPR_ASSERT(!fdn->readable_registered);
|
||||
GPR_ASSERT(!fdn->writable_registered);
|
||||
GPR_ASSERT(fdn->already_shutdown);
|
||||
delete fdn->grpc_polled_fd;
|
||||
delete fdn;
|
||||
}
|
||||
|
||||
static void fd_node_shutdown_locked(fd_node* fdn, const char* reason)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&grpc_ares_request::mu) {
|
||||
if (!fdn->already_shutdown) {
|
||||
fdn->already_shutdown = true;
|
||||
fdn->grpc_polled_fd->ShutdownLocked(GRPC_ERROR_CREATE(reason));
|
||||
}
|
||||
}
|
||||
|
||||
void grpc_ares_ev_driver_on_queries_complete_locked(
|
||||
grpc_ares_ev_driver* ev_driver)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&grpc_ares_request::mu) {
|
||||
// We mark the event driver as being shut down.
|
||||
// grpc_ares_notify_on_event_locked will shut down any remaining
|
||||
// fds.
|
||||
ev_driver->shutting_down = true;
|
||||
grpc_timer_cancel(&ev_driver->query_timeout);
|
||||
grpc_timer_cancel(&ev_driver->ares_backup_poll_alarm);
|
||||
grpc_ares_ev_driver_unref(ev_driver);
|
||||
}
|
||||
|
||||
void grpc_ares_ev_driver_shutdown_locked(grpc_ares_ev_driver* ev_driver)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&grpc_ares_request::mu) {
|
||||
ev_driver->shutting_down = true;
|
||||
fd_node* fn = ev_driver->fds;
|
||||
while (fn != nullptr) {
|
||||
fd_node_shutdown_locked(fn, "grpc_ares_ev_driver_shutdown");
|
||||
fn = fn->next;
|
||||
}
|
||||
}
|
||||
|
||||
// Search fd in the fd_node list head. This is an O(n) search, the max possible
|
||||
// value of n is ARES_GETSOCK_MAXNUM (16). n is typically 1 - 2 in our tests.
|
||||
static fd_node* pop_fd_node_locked(fd_node** head, ares_socket_t as)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&grpc_ares_request::mu) {
|
||||
fd_node phony_head;
|
||||
phony_head.next = *head;
|
||||
fd_node* node = &phony_head;
|
||||
while (node->next != nullptr) {
|
||||
if (node->next->grpc_polled_fd->GetWrappedAresSocketLocked() == as) {
|
||||
fd_node* ret = node->next;
|
||||
node->next = node->next->next;
|
||||
*head = phony_head.next;
|
||||
return ret;
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static grpc_core::Timestamp calculate_next_ares_backup_poll_alarm(
|
||||
grpc_ares_ev_driver* driver)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&grpc_ares_request::mu) {
|
||||
// An alternative here could be to use ares_timeout to try to be more
|
||||
// accurate, but that would require using "struct timeval"'s, which just makes
|
||||
// things a bit more complicated. So just poll every second, as suggested
|
||||
// by the c-ares code comments.
|
||||
grpc_core::Duration until_next_ares_backup_poll_alarm =
|
||||
grpc_core::Duration::Seconds(1);
|
||||
GRPC_CARES_TRACE_LOG(
|
||||
"request:%p ev_driver=%p. next ares process poll time in "
|
||||
"%" PRId64 " ms",
|
||||
driver->request, driver, until_next_ares_backup_poll_alarm.millis());
|
||||
return grpc_core::Timestamp::Now() + until_next_ares_backup_poll_alarm;
|
||||
}
|
||||
|
||||
static void on_timeout(void* arg, grpc_error_handle error) {
|
||||
grpc_ares_ev_driver* driver = static_cast<grpc_ares_ev_driver*>(arg);
|
||||
grpc_core::MutexLock lock(&driver->request->mu);
|
||||
GRPC_CARES_TRACE_LOG(
|
||||
"request:%p ev_driver=%p on_timeout_locked. driver->shutting_down=%d. "
|
||||
"err=%s",
|
||||
driver->request, driver, driver->shutting_down,
|
||||
grpc_core::StatusToString(error).c_str());
|
||||
if (!driver->shutting_down && error.ok()) {
|
||||
grpc_ares_ev_driver_shutdown_locked(driver);
|
||||
}
|
||||
grpc_ares_ev_driver_unref(driver);
|
||||
}
|
||||
|
||||
static void grpc_ares_notify_on_event_locked(grpc_ares_ev_driver* ev_driver)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&grpc_ares_request::mu);
|
||||
|
||||
// In case of non-responsive DNS servers, dropped packets, etc., c-ares has
|
||||
// intelligent timeout and retry logic, which we can take advantage of by
|
||||
// polling ares_process_fd on time intervals. Overall, the c-ares library is
|
||||
// meant to be called into and given a chance to proceed name resolution:
|
||||
// a) when fd events happen
|
||||
// b) when some time has passed without fd events having happened
|
||||
// For the latter, we use this backup poller. Also see
|
||||
// https://github.com/grpc/grpc/pull/17688 description for more details.
|
||||
static void on_ares_backup_poll_alarm(void* arg, grpc_error_handle error) {
|
||||
grpc_ares_ev_driver* driver = static_cast<grpc_ares_ev_driver*>(arg);
|
||||
grpc_core::MutexLock lock(&driver->request->mu);
|
||||
GRPC_CARES_TRACE_LOG(
|
||||
"request:%p ev_driver=%p on_ares_backup_poll_alarm_locked. "
|
||||
"driver->shutting_down=%d. "
|
||||
"err=%s",
|
||||
driver->request, driver, driver->shutting_down,
|
||||
grpc_core::StatusToString(error).c_str());
|
||||
if (!driver->shutting_down && error.ok()) {
|
||||
fd_node* fdn = driver->fds;
|
||||
while (fdn != nullptr) {
|
||||
if (!fdn->already_shutdown) {
|
||||
GRPC_CARES_TRACE_LOG(
|
||||
"request:%p ev_driver=%p on_ares_backup_poll_alarm_locked; "
|
||||
"ares_process_fd. fd=%s",
|
||||
driver->request, driver, fdn->grpc_polled_fd->GetName());
|
||||
ares_socket_t as = fdn->grpc_polled_fd->GetWrappedAresSocketLocked();
|
||||
ares_process_fd(driver->channel, as, as);
|
||||
}
|
||||
fdn = fdn->next;
|
||||
}
|
||||
if (!driver->shutting_down) {
|
||||
// InvalidateNow to avoid getting stuck re-initializing this timer
|
||||
// in a loop while draining the currently-held WorkSerializer.
|
||||
// Also see https://github.com/grpc/grpc/issues/26079.
|
||||
grpc_core::ExecCtx::Get()->InvalidateNow();
|
||||
grpc_core::Timestamp next_ares_backup_poll_alarm =
|
||||
calculate_next_ares_backup_poll_alarm(driver);
|
||||
grpc_ares_ev_driver_ref(driver);
|
||||
GRPC_CLOSURE_INIT(&driver->on_ares_backup_poll_alarm_locked,
|
||||
on_ares_backup_poll_alarm, driver,
|
||||
grpc_schedule_on_exec_ctx);
|
||||
grpc_timer_init(&driver->ares_backup_poll_alarm,
|
||||
next_ares_backup_poll_alarm,
|
||||
&driver->on_ares_backup_poll_alarm_locked);
|
||||
}
|
||||
grpc_ares_notify_on_event_locked(driver);
|
||||
}
|
||||
grpc_ares_ev_driver_unref(driver);
|
||||
}
|
||||
|
||||
static void on_readable(void* arg, grpc_error_handle error) {
|
||||
fd_node* fdn = static_cast<fd_node*>(arg);
|
||||
grpc_core::MutexLock lock(&fdn->ev_driver->request->mu);
|
||||
GPR_ASSERT(fdn->readable_registered);
|
||||
grpc_ares_ev_driver* ev_driver = fdn->ev_driver;
|
||||
const ares_socket_t as = fdn->grpc_polled_fd->GetWrappedAresSocketLocked();
|
||||
fdn->readable_registered = false;
|
||||
GRPC_CARES_TRACE_LOG("request:%p readable on %s", fdn->ev_driver->request,
|
||||
fdn->grpc_polled_fd->GetName());
|
||||
if (error.ok() && !ev_driver->shutting_down) {
|
||||
ares_process_fd(ev_driver->channel, as, ARES_SOCKET_BAD);
|
||||
} else {
|
||||
// If error is not absl::OkStatus() or the resolution was cancelled, it
|
||||
// means the fd has been shutdown or timed out. The pending lookups made on
|
||||
// this ev_driver will be cancelled by the following ares_cancel() and the
|
||||
// on_done callbacks will be invoked with a status of ARES_ECANCELLED. The
|
||||
// remaining file descriptors in this ev_driver will be cleaned up in the
|
||||
// follwing grpc_ares_notify_on_event_locked().
|
||||
ares_cancel(ev_driver->channel);
|
||||
}
|
||||
grpc_ares_notify_on_event_locked(ev_driver);
|
||||
grpc_ares_ev_driver_unref(ev_driver);
|
||||
}
|
||||
|
||||
static void on_writable(void* arg, grpc_error_handle error) {
|
||||
fd_node* fdn = static_cast<fd_node*>(arg);
|
||||
grpc_core::MutexLock lock(&fdn->ev_driver->request->mu);
|
||||
GPR_ASSERT(fdn->writable_registered);
|
||||
grpc_ares_ev_driver* ev_driver = fdn->ev_driver;
|
||||
const ares_socket_t as = fdn->grpc_polled_fd->GetWrappedAresSocketLocked();
|
||||
fdn->writable_registered = false;
|
||||
GRPC_CARES_TRACE_LOG("request:%p writable on %s", ev_driver->request,
|
||||
fdn->grpc_polled_fd->GetName());
|
||||
if (error.ok() && !ev_driver->shutting_down) {
|
||||
ares_process_fd(ev_driver->channel, ARES_SOCKET_BAD, as);
|
||||
} else {
|
||||
// If error is not absl::OkStatus() or the resolution was cancelled, it
|
||||
// means the fd has been shutdown or timed out. The pending lookups made on
|
||||
// this ev_driver will be cancelled by the following ares_cancel() and the
|
||||
// on_done callbacks will be invoked with a status of ARES_ECANCELLED. The
|
||||
// remaining file descriptors in this ev_driver will be cleaned up in the
|
||||
// follwing grpc_ares_notify_on_event_locked().
|
||||
ares_cancel(ev_driver->channel);
|
||||
}
|
||||
grpc_ares_notify_on_event_locked(ev_driver);
|
||||
grpc_ares_ev_driver_unref(ev_driver);
|
||||
}
|
||||
|
||||
// Get the file descriptors used by the ev_driver's ares channel, register
|
||||
// driver_closure with these filedescriptors.
|
||||
static void grpc_ares_notify_on_event_locked(grpc_ares_ev_driver* ev_driver)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&grpc_ares_request::mu) {
|
||||
fd_node* new_list = nullptr;
|
||||
if (!ev_driver->shutting_down) {
|
||||
ares_socket_t socks[ARES_GETSOCK_MAXNUM];
|
||||
int socks_bitmask =
|
||||
ares_getsock(ev_driver->channel, socks, ARES_GETSOCK_MAXNUM);
|
||||
for (size_t i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
|
||||
if (ARES_GETSOCK_READABLE(socks_bitmask, i) ||
|
||||
ARES_GETSOCK_WRITABLE(socks_bitmask, i)) {
|
||||
fd_node* fdn = pop_fd_node_locked(&ev_driver->fds, socks[i]);
|
||||
// Create a new fd_node if sock[i] is not in the fd_node list.
|
||||
if (fdn == nullptr) {
|
||||
fdn = new fd_node(ev_driver);
|
||||
fdn->grpc_polled_fd =
|
||||
ev_driver->polled_fd_factory->NewGrpcPolledFdLocked(
|
||||
socks[i], ev_driver->pollset_set);
|
||||
GRPC_CARES_TRACE_LOG("request:%p new fd: %s", ev_driver->request,
|
||||
fdn->grpc_polled_fd->GetName());
|
||||
fdn->readable_registered = false;
|
||||
fdn->writable_registered = false;
|
||||
fdn->already_shutdown = false;
|
||||
}
|
||||
fdn->next = new_list;
|
||||
new_list = fdn;
|
||||
// Register read_closure if the socket is readable and read_closure has
|
||||
// not been registered with this socket.
|
||||
if (ARES_GETSOCK_READABLE(socks_bitmask, i) &&
|
||||
!fdn->readable_registered) {
|
||||
grpc_ares_ev_driver_ref(ev_driver);
|
||||
GRPC_CLOSURE_INIT(&fdn->read_closure, on_readable, fdn,
|
||||
grpc_schedule_on_exec_ctx);
|
||||
if (fdn->grpc_polled_fd->IsFdStillReadableLocked()) {
|
||||
GRPC_CARES_TRACE_LOG("request:%p schedule direct read on: %s",
|
||||
ev_driver->request,
|
||||
fdn->grpc_polled_fd->GetName());
|
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, &fdn->read_closure,
|
||||
absl::OkStatus());
|
||||
} else {
|
||||
GRPC_CARES_TRACE_LOG("request:%p notify read on: %s",
|
||||
ev_driver->request,
|
||||
fdn->grpc_polled_fd->GetName());
|
||||
fdn->grpc_polled_fd->RegisterForOnReadableLocked(
|
||||
&fdn->read_closure);
|
||||
}
|
||||
fdn->readable_registered = true;
|
||||
}
|
||||
// Register write_closure if the socket is writable and write_closure
|
||||
// has not been registered with this socket.
|
||||
if (ARES_GETSOCK_WRITABLE(socks_bitmask, i) &&
|
||||
!fdn->writable_registered) {
|
||||
GRPC_CARES_TRACE_LOG("request:%p notify write on: %s",
|
||||
ev_driver->request,
|
||||
fdn->grpc_polled_fd->GetName());
|
||||
grpc_ares_ev_driver_ref(ev_driver);
|
||||
GRPC_CLOSURE_INIT(&fdn->write_closure, on_writable, fdn,
|
||||
grpc_schedule_on_exec_ctx);
|
||||
fdn->grpc_polled_fd->RegisterForOnWriteableLocked(
|
||||
&fdn->write_closure);
|
||||
fdn->writable_registered = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Any remaining fds in ev_driver->fds were not returned by ares_getsock() and
|
||||
// are therefore no longer in use, so they can be shut down and removed from
|
||||
// the list.
|
||||
while (ev_driver->fds != nullptr) {
|
||||
fd_node* cur = ev_driver->fds;
|
||||
ev_driver->fds = ev_driver->fds->next;
|
||||
fd_node_shutdown_locked(cur, "c-ares fd shutdown");
|
||||
if (!cur->readable_registered && !cur->writable_registered) {
|
||||
fd_node_destroy_locked(cur);
|
||||
} else {
|
||||
cur->next = new_list;
|
||||
new_list = cur;
|
||||
}
|
||||
}
|
||||
ev_driver->fds = new_list;
|
||||
}
|
||||
|
||||
void grpc_ares_ev_driver_start_locked(grpc_ares_ev_driver* ev_driver)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&grpc_ares_request::mu) {
|
||||
grpc_ares_notify_on_event_locked(ev_driver);
|
||||
// Initialize overall DNS resolution timeout alarm
|
||||
grpc_core::Duration timeout =
|
||||
ev_driver->query_timeout_ms == 0
|
||||
? grpc_core::Duration::Infinity()
|
||||
: grpc_core::Duration::Milliseconds(ev_driver->query_timeout_ms);
|
||||
GRPC_CARES_TRACE_LOG(
|
||||
"request:%p ev_driver=%p grpc_ares_ev_driver_start_locked. timeout in "
|
||||
"%" PRId64 " ms",
|
||||
ev_driver->request, ev_driver, timeout.millis());
|
||||
grpc_ares_ev_driver_ref(ev_driver);
|
||||
GRPC_CLOSURE_INIT(&ev_driver->on_timeout_locked, on_timeout, ev_driver,
|
||||
grpc_schedule_on_exec_ctx);
|
||||
grpc_timer_init(&ev_driver->query_timeout,
|
||||
grpc_core::Timestamp::Now() + timeout,
|
||||
&ev_driver->on_timeout_locked);
|
||||
// Initialize the backup poll alarm
|
||||
grpc_core::Timestamp next_ares_backup_poll_alarm =
|
||||
calculate_next_ares_backup_poll_alarm(ev_driver);
|
||||
grpc_ares_ev_driver_ref(ev_driver);
|
||||
GRPC_CLOSURE_INIT(&ev_driver->on_ares_backup_poll_alarm_locked,
|
||||
on_ares_backup_poll_alarm, ev_driver,
|
||||
grpc_schedule_on_exec_ctx);
|
||||
grpc_timer_init(&ev_driver->ares_backup_poll_alarm,
|
||||
next_ares_backup_poll_alarm,
|
||||
&ev_driver->on_ares_backup_poll_alarm_locked);
|
||||
}
|
||||
|
||||
static void noop_inject_channel_config(ares_channel* /*channel*/) {}
|
||||
|
||||
void (*grpc_ares_test_only_inject_config)(ares_channel* channel) =
|
||||
noop_inject_channel_config;
|
||||
|
||||
bool g_grpc_ares_test_only_force_tcp = false;
|
||||
|
||||
grpc_error_handle grpc_ares_ev_driver_create_locked(
|
||||
grpc_ares_ev_driver** ev_driver, grpc_pollset_set* pollset_set,
|
||||
int query_timeout_ms, grpc_ares_request* request)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(request->mu) {
|
||||
*ev_driver = new grpc_ares_ev_driver(request);
|
||||
ares_options opts;
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
opts.flags |= ARES_FLAG_STAYOPEN;
|
||||
if (g_grpc_ares_test_only_force_tcp) {
|
||||
opts.flags |= ARES_FLAG_USEVC;
|
||||
}
|
||||
int status = ares_init_options(&(*ev_driver)->channel, &opts, ARES_OPT_FLAGS);
|
||||
grpc_ares_test_only_inject_config(&(*ev_driver)->channel);
|
||||
GRPC_CARES_TRACE_LOG("request:%p grpc_ares_ev_driver_create_locked", request);
|
||||
if (status != ARES_SUCCESS) {
|
||||
grpc_error_handle err = GRPC_ERROR_CREATE(absl::StrCat(
|
||||
"Failed to init ares channel. C-ares error: ", ares_strerror(status)));
|
||||
delete *ev_driver;
|
||||
return err;
|
||||
}
|
||||
gpr_ref_init(&(*ev_driver)->refs, 1);
|
||||
(*ev_driver)->pollset_set = pollset_set;
|
||||
(*ev_driver)->fds = nullptr;
|
||||
(*ev_driver)->shutting_down = false;
|
||||
(*ev_driver)->polled_fd_factory =
|
||||
grpc_core::NewGrpcPolledFdFactory(&(*ev_driver)->request->mu);
|
||||
(*ev_driver)
|
||||
->polled_fd_factory->ConfigureAresChannelLocked((*ev_driver)->channel);
|
||||
(*ev_driver)->query_timeout_ms = query_timeout_ms;
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
static void log_address_sorting_list(const grpc_ares_request* r,
|
||||
const EndpointAddressesList& addresses,
|
||||
const char* input_output_str) {
|
||||
for (size_t i = 0; i < addresses.size(); i++) {
|
||||
auto addr_str = grpc_sockaddr_to_string(&addresses[i].address(), true);
|
||||
gpr_log(GPR_INFO,
|
||||
"(c-ares resolver) request:%p c-ares address sorting: %s[%" PRIuPTR
|
||||
"]=%s",
|
||||
r, input_output_str, i,
|
||||
addr_str.ok() ? addr_str->c_str()
|
||||
: addr_str.status().ToString().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void grpc_cares_wrapper_address_sorting_sort(const grpc_ares_request* r,
|
||||
EndpointAddressesList* addresses) {
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_cares_address_sorting)) {
|
||||
log_address_sorting_list(r, *addresses, "input");
|
||||
}
|
||||
address_sorting_sortable* sortables = static_cast<address_sorting_sortable*>(
|
||||
gpr_zalloc(sizeof(address_sorting_sortable) * addresses->size()));
|
||||
for (size_t i = 0; i < addresses->size(); ++i) {
|
||||
sortables[i].user_data = &(*addresses)[i];
|
||||
memcpy(&sortables[i].dest_addr.addr, &(*addresses)[i].address().addr,
|
||||
(*addresses)[i].address().len);
|
||||
sortables[i].dest_addr.len = (*addresses)[i].address().len;
|
||||
}
|
||||
address_sorting_rfc_6724_sort(sortables, addresses->size());
|
||||
EndpointAddressesList sorted;
|
||||
sorted.reserve(addresses->size());
|
||||
for (size_t i = 0; i < addresses->size(); ++i) {
|
||||
sorted.emplace_back(
|
||||
*static_cast<EndpointAddresses*>(sortables[i].user_data));
|
||||
}
|
||||
gpr_free(sortables);
|
||||
*addresses = std::move(sorted);
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_cares_address_sorting)) {
|
||||
log_address_sorting_list(r, *addresses, "output");
|
||||
}
|
||||
}
|
||||
|
||||
static void grpc_ares_request_ref_locked(grpc_ares_request* r)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(r->mu) {
|
||||
r->pending_queries++;
|
||||
}
|
||||
|
||||
static void grpc_ares_request_unref_locked(grpc_ares_request* r)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(r->mu) {
|
||||
r->pending_queries--;
|
||||
if (r->pending_queries == 0u) {
|
||||
grpc_ares_ev_driver_on_queries_complete_locked(r->ev_driver);
|
||||
}
|
||||
}
|
||||
|
||||
void grpc_ares_complete_request_locked(grpc_ares_request* r)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(r->mu) {
|
||||
// Invoke on_done callback and destroy the request
|
||||
r->ev_driver = nullptr;
|
||||
if (r->addresses_out != nullptr && *r->addresses_out != nullptr) {
|
||||
grpc_cares_wrapper_address_sorting_sort(r, r->addresses_out->get());
|
||||
r->error = absl::OkStatus();
|
||||
// TODO(apolcyn): allow c-ares to return a service config
|
||||
// with no addresses along side it
|
||||
}
|
||||
if (r->balancer_addresses_out != nullptr) {
|
||||
EndpointAddressesList* balancer_addresses =
|
||||
r->balancer_addresses_out->get();
|
||||
if (balancer_addresses != nullptr) {
|
||||
grpc_cares_wrapper_address_sorting_sort(r, balancer_addresses);
|
||||
}
|
||||
}
|
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, r->on_done, r->error);
|
||||
}
|
||||
|
||||
// Note that the returned object takes a reference to qtype, so
|
||||
// qtype must outlive it.
|
||||
static grpc_ares_hostbyname_request* create_hostbyname_request_locked(
|
||||
grpc_ares_request* parent_request, const char* host, uint16_t port,
|
||||
bool is_balancer, const char* qtype)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(parent_request->mu) {
|
||||
GRPC_CARES_TRACE_LOG(
|
||||
"request:%p create_hostbyname_request_locked host:%s port:%d "
|
||||
"is_balancer:%d qtype:%s",
|
||||
parent_request, host, port, is_balancer, qtype);
|
||||
grpc_ares_hostbyname_request* hr = new grpc_ares_hostbyname_request();
|
||||
hr->parent_request = parent_request;
|
||||
hr->host = gpr_strdup(host);
|
||||
hr->port = port;
|
||||
hr->is_balancer = is_balancer;
|
||||
hr->qtype = qtype;
|
||||
grpc_ares_request_ref_locked(parent_request);
|
||||
return hr;
|
||||
}
|
||||
|
||||
static void destroy_hostbyname_request_locked(grpc_ares_hostbyname_request* hr)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(hr->parent_request->mu) {
|
||||
grpc_ares_request_unref_locked(hr->parent_request);
|
||||
gpr_free(hr->host);
|
||||
delete hr;
|
||||
}
|
||||
|
||||
static void on_hostbyname_done_locked(void* arg, int status, int /*timeouts*/,
|
||||
struct hostent* hostent)
|
||||
ABSL_NO_THREAD_SAFETY_ANALYSIS {
|
||||
// This callback is invoked from the c-ares library, so disable thread safety
|
||||
// analysis. Note that we are guaranteed to be holding r->mu, though.
|
||||
grpc_ares_hostbyname_request* hr =
|
||||
static_cast<grpc_ares_hostbyname_request*>(arg);
|
||||
grpc_ares_request* r = hr->parent_request;
|
||||
if (status == ARES_SUCCESS) {
|
||||
GRPC_CARES_TRACE_LOG(
|
||||
"request:%p on_hostbyname_done_locked qtype=%s host=%s ARES_SUCCESS", r,
|
||||
hr->qtype, hr->host);
|
||||
std::unique_ptr<EndpointAddressesList>* address_list_ptr =
|
||||
hr->is_balancer ? r->balancer_addresses_out : r->addresses_out;
|
||||
if (*address_list_ptr == nullptr) {
|
||||
*address_list_ptr = std::make_unique<EndpointAddressesList>();
|
||||
}
|
||||
EndpointAddressesList& addresses = **address_list_ptr;
|
||||
for (size_t i = 0; hostent->h_addr_list[i] != nullptr; ++i) {
|
||||
grpc_core::ChannelArgs args;
|
||||
if (hr->is_balancer) {
|
||||
args = args.Set(GRPC_ARG_DEFAULT_AUTHORITY, hr->host);
|
||||
}
|
||||
grpc_resolved_address address;
|
||||
memset(&address, 0, sizeof(address));
|
||||
switch (hostent->h_addrtype) {
|
||||
case AF_INET6: {
|
||||
address.len = sizeof(struct sockaddr_in6);
|
||||
auto* addr = reinterpret_cast<struct sockaddr_in6*>(&address.addr);
|
||||
memcpy(&addr->sin6_addr, hostent->h_addr_list[i],
|
||||
sizeof(struct in6_addr));
|
||||
addr->sin6_family = static_cast<unsigned char>(hostent->h_addrtype);
|
||||
addr->sin6_port = hr->port;
|
||||
char output[INET6_ADDRSTRLEN];
|
||||
ares_inet_ntop(AF_INET6, &addr->sin6_addr, output, INET6_ADDRSTRLEN);
|
||||
GRPC_CARES_TRACE_LOG(
|
||||
"request:%p c-ares resolver gets a AF_INET6 result: \n"
|
||||
" addr: %s\n port: %d\n sin6_scope_id: %d\n",
|
||||
r, output, ntohs(hr->port), addr->sin6_scope_id);
|
||||
break;
|
||||
}
|
||||
case AF_INET: {
|
||||
address.len = sizeof(struct sockaddr_in);
|
||||
auto* addr = reinterpret_cast<struct sockaddr_in*>(&address.addr);
|
||||
memcpy(&addr->sin_addr, hostent->h_addr_list[i],
|
||||
sizeof(struct in_addr));
|
||||
addr->sin_family = static_cast<unsigned char>(hostent->h_addrtype);
|
||||
addr->sin_port = hr->port;
|
||||
char output[INET_ADDRSTRLEN];
|
||||
ares_inet_ntop(AF_INET, &addr->sin_addr, output, INET_ADDRSTRLEN);
|
||||
GRPC_CARES_TRACE_LOG(
|
||||
"request:%p c-ares resolver gets a AF_INET result: \n"
|
||||
" addr: %s\n port: %d\n",
|
||||
r, output, ntohs(hr->port));
|
||||
break;
|
||||
}
|
||||
}
|
||||
addresses.emplace_back(address, args);
|
||||
}
|
||||
} else {
|
||||
std::string error_msg = absl::StrFormat(
|
||||
"C-ares status is not ARES_SUCCESS qtype=%s name=%s is_balancer=%d: %s",
|
||||
hr->qtype, hr->host, hr->is_balancer, ares_strerror(status));
|
||||
GRPC_CARES_TRACE_LOG("request:%p on_hostbyname_done_locked: %s", r,
|
||||
error_msg.c_str());
|
||||
grpc_error_handle error = GRPC_ERROR_CREATE(error_msg);
|
||||
r->error = grpc_error_add_child(error, r->error);
|
||||
}
|
||||
destroy_hostbyname_request_locked(hr);
|
||||
}
|
||||
|
||||
static void on_srv_query_done_locked(void* arg, int status, int /*timeouts*/,
|
||||
unsigned char* abuf,
|
||||
int alen) ABSL_NO_THREAD_SAFETY_ANALYSIS {
|
||||
// This callback is invoked from the c-ares library, so disable thread safety
|
||||
// analysis. Note that we are guaranteed to be holding r->mu, though.
|
||||
GrpcAresQuery* q = static_cast<GrpcAresQuery*>(arg);
|
||||
grpc_ares_request* r = q->parent_request();
|
||||
if (status == ARES_SUCCESS) {
|
||||
GRPC_CARES_TRACE_LOG(
|
||||
"request:%p on_srv_query_done_locked name=%s ARES_SUCCESS", r,
|
||||
q->name().c_str());
|
||||
struct ares_srv_reply* reply;
|
||||
const int parse_status = ares_parse_srv_reply(abuf, alen, &reply);
|
||||
GRPC_CARES_TRACE_LOG("request:%p ares_parse_srv_reply: %d", r,
|
||||
parse_status);
|
||||
if (parse_status == ARES_SUCCESS) {
|
||||
for (struct ares_srv_reply* srv_it = reply; srv_it != nullptr;
|
||||
srv_it = srv_it->next) {
|
||||
if (grpc_ares_query_ipv6()) {
|
||||
grpc_ares_hostbyname_request* hr = create_hostbyname_request_locked(
|
||||
r, srv_it->host, htons(srv_it->port), true /* is_balancer */,
|
||||
"AAAA");
|
||||
ares_gethostbyname(r->ev_driver->channel, hr->host, AF_INET6,
|
||||
on_hostbyname_done_locked, hr);
|
||||
}
|
||||
grpc_ares_hostbyname_request* hr = create_hostbyname_request_locked(
|
||||
r, srv_it->host, htons(srv_it->port), true /* is_balancer */, "A");
|
||||
ares_gethostbyname(r->ev_driver->channel, hr->host, AF_INET,
|
||||
on_hostbyname_done_locked, hr);
|
||||
}
|
||||
}
|
||||
if (reply != nullptr) {
|
||||
ares_free_data(reply);
|
||||
}
|
||||
} else {
|
||||
std::string error_msg = absl::StrFormat(
|
||||
"C-ares status is not ARES_SUCCESS qtype=SRV name=%s: %s", q->name(),
|
||||
ares_strerror(status));
|
||||
GRPC_CARES_TRACE_LOG("request:%p on_srv_query_done_locked: %s", r,
|
||||
error_msg.c_str());
|
||||
grpc_error_handle error = GRPC_ERROR_CREATE(error_msg);
|
||||
r->error = grpc_error_add_child(error, r->error);
|
||||
}
|
||||
delete q;
|
||||
}
|
||||
|
||||
static const char g_service_config_attribute_prefix[] = "grpc_config=";
|
||||
|
||||
static void on_txt_done_locked(void* arg, int status, int /*timeouts*/,
|
||||
unsigned char* buf,
|
||||
int len) ABSL_NO_THREAD_SAFETY_ANALYSIS {
|
||||
// This callback is invoked from the c-ares library, so disable thread safety
|
||||
// analysis. Note that we are guaranteed to be holding r->mu, though.
|
||||
GrpcAresQuery* q = static_cast<GrpcAresQuery*>(arg);
|
||||
std::unique_ptr<GrpcAresQuery> query_deleter(q);
|
||||
grpc_ares_request* r = q->parent_request();
|
||||
const size_t prefix_len = sizeof(g_service_config_attribute_prefix) - 1;
|
||||
struct ares_txt_ext* result = nullptr;
|
||||
struct ares_txt_ext* reply = nullptr;
|
||||
grpc_error_handle error;
|
||||
if (status != ARES_SUCCESS) goto fail;
|
||||
GRPC_CARES_TRACE_LOG("request:%p on_txt_done_locked name=%s ARES_SUCCESS", r,
|
||||
q->name().c_str());
|
||||
status = ares_parse_txt_reply_ext(buf, len, &reply);
|
||||
if (status != ARES_SUCCESS) goto fail;
|
||||
// Find service config in TXT record.
|
||||
for (result = reply; result != nullptr; result = result->next) {
|
||||
if (result->record_start &&
|
||||
memcmp(result->txt, g_service_config_attribute_prefix, prefix_len) ==
|
||||
0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Found a service config record.
|
||||
if (result != nullptr) {
|
||||
size_t service_config_len = result->length - prefix_len;
|
||||
*r->service_config_json_out =
|
||||
static_cast<char*>(gpr_malloc(service_config_len + 1));
|
||||
memcpy(*r->service_config_json_out, result->txt + prefix_len,
|
||||
service_config_len);
|
||||
for (result = result->next; result != nullptr && !result->record_start;
|
||||
result = result->next) {
|
||||
*r->service_config_json_out = static_cast<char*>(
|
||||
gpr_realloc(*r->service_config_json_out,
|
||||
service_config_len + result->length + 1));
|
||||
memcpy(*r->service_config_json_out + service_config_len, result->txt,
|
||||
result->length);
|
||||
service_config_len += result->length;
|
||||
}
|
||||
(*r->service_config_json_out)[service_config_len] = '\0';
|
||||
GRPC_CARES_TRACE_LOG("request:%p found service config: %s", r,
|
||||
*r->service_config_json_out);
|
||||
}
|
||||
// Clean up.
|
||||
ares_free_data(reply);
|
||||
grpc_ares_request_unref_locked(r);
|
||||
return;
|
||||
fail:
|
||||
std::string error_msg =
|
||||
absl::StrFormat("C-ares status is not ARES_SUCCESS qtype=TXT name=%s: %s",
|
||||
q->name(), ares_strerror(status));
|
||||
GRPC_CARES_TRACE_LOG("request:%p on_txt_done_locked %s", r,
|
||||
error_msg.c_str());
|
||||
error = GRPC_ERROR_CREATE(error_msg);
|
||||
r->error = grpc_error_add_child(error, r->error);
|
||||
}
|
||||
|
||||
grpc_error_handle set_request_dns_server(grpc_ares_request* r,
|
||||
absl::string_view dns_server)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(r->mu) {
|
||||
if (!dns_server.empty()) {
|
||||
GRPC_CARES_TRACE_LOG("request:%p Using DNS server %s", r,
|
||||
dns_server.data());
|
||||
grpc_resolved_address addr;
|
||||
if (grpc_parse_ipv4_hostport(dns_server, &addr, /*log_errors=*/false)) {
|
||||
r->dns_server_addr.family = AF_INET;
|
||||
struct sockaddr_in* in = reinterpret_cast<struct sockaddr_in*>(addr.addr);
|
||||
memcpy(&r->dns_server_addr.addr.addr4, &in->sin_addr,
|
||||
sizeof(struct in_addr));
|
||||
r->dns_server_addr.tcp_port = grpc_sockaddr_get_port(&addr);
|
||||
r->dns_server_addr.udp_port = grpc_sockaddr_get_port(&addr);
|
||||
} else if (grpc_parse_ipv6_hostport(dns_server, &addr,
|
||||
/*log_errors=*/false)) {
|
||||
r->dns_server_addr.family = AF_INET6;
|
||||
struct sockaddr_in6* in6 =
|
||||
reinterpret_cast<struct sockaddr_in6*>(addr.addr);
|
||||
memcpy(&r->dns_server_addr.addr.addr6, &in6->sin6_addr,
|
||||
sizeof(struct in6_addr));
|
||||
r->dns_server_addr.tcp_port = grpc_sockaddr_get_port(&addr);
|
||||
r->dns_server_addr.udp_port = grpc_sockaddr_get_port(&addr);
|
||||
} else {
|
||||
return GRPC_ERROR_CREATE(
|
||||
absl::StrCat("cannot parse authority ", dns_server));
|
||||
}
|
||||
int status =
|
||||
ares_set_servers_ports(r->ev_driver->channel, &r->dns_server_addr);
|
||||
if (status != ARES_SUCCESS) {
|
||||
return GRPC_ERROR_CREATE(absl::StrCat(
|
||||
"C-ares status is not ARES_SUCCESS: ", ares_strerror(status)));
|
||||
}
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
// Common logic for all lookup methods.
|
||||
// If an error occurs, callers must run the client callback.
|
||||
grpc_error_handle grpc_dns_lookup_ares_continued(
|
||||
grpc_ares_request* r, const char* dns_server, const char* name,
|
||||
const char* default_port, grpc_pollset_set* interested_parties,
|
||||
int query_timeout_ms, std::string* host, std::string* port, bool check_port)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(r->mu) {
|
||||
grpc_error_handle error;
|
||||
// parse name, splitting it into host and port parts
|
||||
grpc_core::SplitHostPort(name, host, port);
|
||||
if (host->empty()) {
|
||||
error =
|
||||
grpc_error_set_str(GRPC_ERROR_CREATE("unparseable host:port"),
|
||||
grpc_core::StatusStrProperty::kTargetAddress, name);
|
||||
return error;
|
||||
} else if (check_port && port->empty()) {
|
||||
if (default_port == nullptr || strlen(default_port) == 0) {
|
||||
error = grpc_error_set_str(GRPC_ERROR_CREATE("no port in name"),
|
||||
grpc_core::StatusStrProperty::kTargetAddress,
|
||||
name);
|
||||
return error;
|
||||
}
|
||||
*port = default_port;
|
||||
}
|
||||
error = grpc_ares_ev_driver_create_locked(&r->ev_driver, interested_parties,
|
||||
query_timeout_ms, r);
|
||||
if (!error.ok()) return error;
|
||||
// If dns_server is specified, use it.
|
||||
error = set_request_dns_server(r, dns_server);
|
||||
return error;
|
||||
}
|
||||
|
||||
static bool inner_resolve_as_ip_literal_locked(
|
||||
const char* name, const char* default_port,
|
||||
std::unique_ptr<grpc_core::EndpointAddressesList>* addrs, std::string* host,
|
||||
std::string* port, std::string* hostport) {
|
||||
if (!grpc_core::SplitHostPort(name, host, port)) {
|
||||
gpr_log(GPR_ERROR,
|
||||
"Failed to parse %s to host:port while attempting to resolve as ip "
|
||||
"literal.",
|
||||
name);
|
||||
return false;
|
||||
}
|
||||
if (port->empty()) {
|
||||
if (default_port == nullptr || strlen(default_port) == 0) {
|
||||
gpr_log(GPR_ERROR,
|
||||
"No port or default port for %s while attempting to resolve as "
|
||||
"ip literal.",
|
||||
name);
|
||||
return false;
|
||||
}
|
||||
*port = default_port;
|
||||
}
|
||||
grpc_resolved_address addr;
|
||||
*hostport = grpc_core::JoinHostPort(*host, atoi(port->c_str()));
|
||||
if (grpc_parse_ipv4_hostport(hostport->c_str(), &addr,
|
||||
false /* log errors */) ||
|
||||
grpc_parse_ipv6_hostport(hostport->c_str(), &addr,
|
||||
false /* log errors */)) {
|
||||
GPR_ASSERT(*addrs == nullptr);
|
||||
*addrs = std::make_unique<EndpointAddressesList>();
|
||||
(*addrs)->emplace_back(addr, grpc_core::ChannelArgs());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool resolve_as_ip_literal_locked(
|
||||
const char* name, const char* default_port,
|
||||
std::unique_ptr<grpc_core::EndpointAddressesList>* addrs) {
|
||||
std::string host;
|
||||
std::string port;
|
||||
std::string hostport;
|
||||
bool out = inner_resolve_as_ip_literal_locked(name, default_port, addrs,
|
||||
&host, &port, &hostport);
|
||||
return out;
|
||||
}
|
||||
|
||||
static bool target_matches_localhost_inner(const char* name, std::string* host,
|
||||
std::string* port) {
|
||||
if (!grpc_core::SplitHostPort(name, host, port)) {
|
||||
gpr_log(GPR_ERROR, "Unable to split host and port for name: %s", name);
|
||||
return false;
|
||||
}
|
||||
return gpr_stricmp(host->c_str(), "localhost") == 0;
|
||||
}
|
||||
|
||||
static bool target_matches_localhost(const char* name) {
|
||||
std::string host;
|
||||
std::string port;
|
||||
return target_matches_localhost_inner(name, &host, &port);
|
||||
}
|
||||
|
||||
#ifdef GRPC_ARES_RESOLVE_LOCALHOST_MANUALLY
|
||||
static bool inner_maybe_resolve_localhost_manually_locked(
|
||||
const grpc_ares_request* r, const char* name, const char* default_port,
|
||||
std::unique_ptr<grpc_core::EndpointAddressesList>* addrs, std::string* host,
|
||||
std::string* port) {
|
||||
grpc_core::SplitHostPort(name, host, port);
|
||||
if (host->empty()) {
|
||||
gpr_log(GPR_ERROR,
|
||||
"Failed to parse %s into host:port during manual localhost "
|
||||
"resolution check.",
|
||||
name);
|
||||
return false;
|
||||
}
|
||||
if (port->empty()) {
|
||||
if (default_port == nullptr || strlen(default_port) == 0) {
|
||||
gpr_log(GPR_ERROR,
|
||||
"No port or default port for %s during manual localhost "
|
||||
"resolution check.",
|
||||
name);
|
||||
return false;
|
||||
}
|
||||
*port = default_port;
|
||||
}
|
||||
if (gpr_stricmp(host->c_str(), "localhost") == 0) {
|
||||
GPR_ASSERT(*addrs == nullptr);
|
||||
*addrs = std::make_unique<grpc_core::EndpointAddressesList>();
|
||||
uint16_t numeric_port = grpc_strhtons(port->c_str());
|
||||
grpc_resolved_address address;
|
||||
// Append the ipv6 loopback address.
|
||||
memset(&address, 0, sizeof(address));
|
||||
auto* ipv6_loopback_addr =
|
||||
reinterpret_cast<struct sockaddr_in6*>(&address.addr);
|
||||
((char*)&ipv6_loopback_addr->sin6_addr)[15] = 1;
|
||||
ipv6_loopback_addr->sin6_family = AF_INET6;
|
||||
ipv6_loopback_addr->sin6_port = numeric_port;
|
||||
address.len = sizeof(struct sockaddr_in6);
|
||||
(*addrs)->emplace_back(address, grpc_core::ChannelArgs());
|
||||
// Append the ipv4 loopback address.
|
||||
memset(&address, 0, sizeof(address));
|
||||
auto* ipv4_loopback_addr =
|
||||
reinterpret_cast<struct sockaddr_in*>(&address.addr);
|
||||
((char*)&ipv4_loopback_addr->sin_addr)[0] = 0x7f;
|
||||
((char*)&ipv4_loopback_addr->sin_addr)[3] = 0x01;
|
||||
ipv4_loopback_addr->sin_family = AF_INET;
|
||||
ipv4_loopback_addr->sin_port = numeric_port;
|
||||
address.len = sizeof(struct sockaddr_in);
|
||||
(*addrs)->emplace_back(address, grpc_core::ChannelArgs());
|
||||
// Let the address sorter figure out which one should be tried first.
|
||||
grpc_cares_wrapper_address_sorting_sort(r, addrs->get());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool grpc_ares_maybe_resolve_localhost_manually_locked(
|
||||
const grpc_ares_request* r, const char* name, const char* default_port,
|
||||
std::unique_ptr<grpc_core::EndpointAddressesList>* addrs) {
|
||||
std::string host;
|
||||
std::string port;
|
||||
return inner_maybe_resolve_localhost_manually_locked(r, name, default_port,
|
||||
addrs, &host, &port);
|
||||
}
|
||||
#else // GRPC_ARES_RESOLVE_LOCALHOST_MANUALLY
|
||||
static bool grpc_ares_maybe_resolve_localhost_manually_locked(
|
||||
const grpc_ares_request* /*r*/, const char* /*name*/,
|
||||
const char* /*default_port*/,
|
||||
std::unique_ptr<grpc_core::EndpointAddressesList>* /*addrs*/) {
|
||||
return false;
|
||||
}
|
||||
#endif // GRPC_ARES_RESOLVE_LOCALHOST_MANUALLY
|
||||
|
||||
static grpc_ares_request* grpc_dns_lookup_hostname_ares_impl(
|
||||
const char* dns_server, const char* name, const char* default_port,
|
||||
grpc_pollset_set* interested_parties, grpc_closure* on_done,
|
||||
std::unique_ptr<grpc_core::EndpointAddressesList>* addrs,
|
||||
int query_timeout_ms) {
|
||||
grpc_ares_request* r = new grpc_ares_request();
|
||||
grpc_core::MutexLock lock(&r->mu);
|
||||
r->ev_driver = nullptr;
|
||||
r->on_done = on_done;
|
||||
r->addresses_out = addrs;
|
||||
GRPC_CARES_TRACE_LOG(
|
||||
"request:%p c-ares grpc_dns_lookup_hostname_ares_impl name=%s, "
|
||||
"default_port=%s",
|
||||
r, name, default_port);
|
||||
// Early out if the target is an ipv4 or ipv6 literal.
|
||||
if (resolve_as_ip_literal_locked(name, default_port, addrs)) {
|
||||
grpc_ares_complete_request_locked(r);
|
||||
return r;
|
||||
}
|
||||
// Early out if the target is localhost and we're on Windows.
|
||||
if (grpc_ares_maybe_resolve_localhost_manually_locked(r, name, default_port,
|
||||
addrs)) {
|
||||
grpc_ares_complete_request_locked(r);
|
||||
return r;
|
||||
}
|
||||
// Look up name using c-ares lib.
|
||||
std::string host;
|
||||
std::string port;
|
||||
grpc_error_handle error = grpc_dns_lookup_ares_continued(
|
||||
r, dns_server, name, default_port, interested_parties, query_timeout_ms,
|
||||
&host, &port, true);
|
||||
if (!error.ok()) {
|
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, r->on_done, error);
|
||||
return r;
|
||||
}
|
||||
r->pending_queries = 1;
|
||||
grpc_ares_hostbyname_request* hr = nullptr;
|
||||
if (grpc_ares_query_ipv6()) {
|
||||
hr = create_hostbyname_request_locked(r, host.c_str(),
|
||||
grpc_strhtons(port.c_str()),
|
||||
/*is_balancer=*/false, "AAAA");
|
||||
ares_gethostbyname(r->ev_driver->channel, hr->host, AF_INET6,
|
||||
on_hostbyname_done_locked, hr);
|
||||
}
|
||||
hr = create_hostbyname_request_locked(r, host.c_str(),
|
||||
grpc_strhtons(port.c_str()),
|
||||
/*is_balancer=*/false, "A");
|
||||
ares_gethostbyname(r->ev_driver->channel, hr->host, AF_INET,
|
||||
on_hostbyname_done_locked, hr);
|
||||
grpc_ares_ev_driver_start_locked(r->ev_driver);
|
||||
grpc_ares_request_unref_locked(r);
|
||||
return r;
|
||||
}
|
||||
|
||||
grpc_ares_request* grpc_dns_lookup_srv_ares_impl(
|
||||
const char* dns_server, const char* name,
|
||||
grpc_pollset_set* interested_parties, grpc_closure* on_done,
|
||||
std::unique_ptr<grpc_core::EndpointAddressesList>* balancer_addresses,
|
||||
int query_timeout_ms) {
|
||||
grpc_ares_request* r = new grpc_ares_request();
|
||||
grpc_core::MutexLock lock(&r->mu);
|
||||
r->ev_driver = nullptr;
|
||||
r->on_done = on_done;
|
||||
r->balancer_addresses_out = balancer_addresses;
|
||||
GRPC_CARES_TRACE_LOG(
|
||||
"request:%p c-ares grpc_dns_lookup_srv_ares_impl name=%s", r, name);
|
||||
grpc_error_handle error;
|
||||
// Don't query for SRV records if the target is "localhost"
|
||||
if (target_matches_localhost(name)) {
|
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, r->on_done, error);
|
||||
return r;
|
||||
}
|
||||
// Look up name using c-ares lib.
|
||||
std::string host;
|
||||
std::string port;
|
||||
error = grpc_dns_lookup_ares_continued(r, dns_server, name, nullptr,
|
||||
interested_parties, query_timeout_ms,
|
||||
&host, &port, false);
|
||||
if (!error.ok()) {
|
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, r->on_done, error);
|
||||
return r;
|
||||
}
|
||||
r->pending_queries = 1;
|
||||
// Query the SRV record
|
||||
std::string service_name = absl::StrCat("_grpclb._tcp.", host);
|
||||
GrpcAresQuery* srv_query = new GrpcAresQuery(r, service_name);
|
||||
ares_query(r->ev_driver->channel, service_name.c_str(), ns_c_in, ns_t_srv,
|
||||
on_srv_query_done_locked, srv_query);
|
||||
grpc_ares_ev_driver_start_locked(r->ev_driver);
|
||||
grpc_ares_request_unref_locked(r);
|
||||
return r;
|
||||
}
|
||||
|
||||
grpc_ares_request* grpc_dns_lookup_txt_ares_impl(
|
||||
const char* dns_server, const char* name,
|
||||
grpc_pollset_set* interested_parties, grpc_closure* on_done,
|
||||
char** service_config_json, int query_timeout_ms) {
|
||||
grpc_ares_request* r = new grpc_ares_request();
|
||||
grpc_core::MutexLock lock(&r->mu);
|
||||
r->ev_driver = nullptr;
|
||||
r->on_done = on_done;
|
||||
r->service_config_json_out = service_config_json;
|
||||
GRPC_CARES_TRACE_LOG(
|
||||
"request:%p c-ares grpc_dns_lookup_txt_ares_impl name=%s", r, name);
|
||||
grpc_error_handle error;
|
||||
// Don't query for TXT records if the target is "localhost"
|
||||
if (target_matches_localhost(name)) {
|
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, r->on_done, error);
|
||||
return r;
|
||||
}
|
||||
// Look up name using c-ares lib.
|
||||
std::string host;
|
||||
std::string port;
|
||||
error = grpc_dns_lookup_ares_continued(r, dns_server, name, nullptr,
|
||||
interested_parties, query_timeout_ms,
|
||||
&host, &port, false);
|
||||
if (!error.ok()) {
|
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, r->on_done, error);
|
||||
return r;
|
||||
}
|
||||
r->pending_queries = 1;
|
||||
// Query the TXT record
|
||||
std::string config_name = absl::StrCat("_grpc_config.", host);
|
||||
GrpcAresQuery* txt_query = new GrpcAresQuery(r, config_name);
|
||||
ares_search(r->ev_driver->channel, config_name.c_str(), ns_c_in, ns_t_txt,
|
||||
on_txt_done_locked, txt_query);
|
||||
grpc_ares_ev_driver_start_locked(r->ev_driver);
|
||||
grpc_ares_request_unref_locked(r);
|
||||
return r;
|
||||
}
|
||||
|
||||
grpc_ares_request* (*grpc_dns_lookup_hostname_ares)(
|
||||
const char* dns_server, const char* name, const char* default_port,
|
||||
grpc_pollset_set* interested_parties, grpc_closure* on_done,
|
||||
std::unique_ptr<grpc_core::EndpointAddressesList>* addrs,
|
||||
int query_timeout_ms) = grpc_dns_lookup_hostname_ares_impl;
|
||||
|
||||
grpc_ares_request* (*grpc_dns_lookup_srv_ares)(
|
||||
const char* dns_server, const char* name,
|
||||
grpc_pollset_set* interested_parties, grpc_closure* on_done,
|
||||
std::unique_ptr<grpc_core::EndpointAddressesList>* balancer_addresses,
|
||||
int query_timeout_ms) = grpc_dns_lookup_srv_ares_impl;
|
||||
|
||||
grpc_ares_request* (*grpc_dns_lookup_txt_ares)(
|
||||
const char* dns_server, const char* name,
|
||||
grpc_pollset_set* interested_parties, grpc_closure* on_done,
|
||||
char** service_config_json,
|
||||
int query_timeout_ms) = grpc_dns_lookup_txt_ares_impl;
|
||||
|
||||
static void grpc_cancel_ares_request_impl(grpc_ares_request* r) {
|
||||
GPR_ASSERT(r != nullptr);
|
||||
grpc_core::MutexLock lock(&r->mu);
|
||||
GRPC_CARES_TRACE_LOG("request:%p grpc_cancel_ares_request ev_driver:%p", r,
|
||||
r->ev_driver);
|
||||
if (r->ev_driver != nullptr) {
|
||||
grpc_ares_ev_driver_shutdown_locked(r->ev_driver);
|
||||
}
|
||||
}
|
||||
|
||||
void (*grpc_cancel_ares_request)(grpc_ares_request* r) =
|
||||
grpc_cancel_ares_request_impl;
|
||||
|
||||
// ares_library_init and ares_library_cleanup are currently no-op except under
|
||||
// Windows. Calling them may cause race conditions when other parts of the
|
||||
// binary calls these functions concurrently.
|
||||
#ifdef GPR_WINDOWS
|
||||
grpc_error_handle grpc_ares_init(void) {
|
||||
int status = ares_library_init(ARES_LIB_INIT_ALL);
|
||||
if (status != ARES_SUCCESS) {
|
||||
return GRPC_ERROR_CREATE(
|
||||
absl::StrCat("ares_library_init failed: ", ares_strerror(status)));
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void grpc_ares_cleanup(void) { ares_library_cleanup(); }
|
||||
#else
|
||||
grpc_error_handle grpc_ares_init(void) { return absl::OkStatus(); }
|
||||
void grpc_ares_cleanup(void) {}
|
||||
#endif // GPR_WINDOWS
|
||||
|
||||
#endif // GRPC_ARES == 1
|
||||
140
Pods/gRPC-Core/src/core/resolver/dns/c_ares/grpc_ares_wrapper.h
generated
Normal file
140
Pods/gRPC-Core/src/core/resolver/dns/c_ares/grpc_ares_wrapper.h
generated
Normal file
@@ -0,0 +1,140 @@
|
||||
//
|
||||
//
|
||||
// Copyright 2016 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
//
|
||||
|
||||
#ifndef GRPC_SRC_CORE_RESOLVER_DNS_C_ARES_GRPC_ARES_WRAPPER_H
|
||||
#define GRPC_SRC_CORE_RESOLVER_DNS_C_ARES_GRPC_ARES_WRAPPER_H
|
||||
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <ares.h>
|
||||
|
||||
#include "absl/base/thread_annotations.h"
|
||||
|
||||
#include <grpc/support/log.h>
|
||||
|
||||
#include "src/core/lib/debug/trace.h"
|
||||
#include "src/core/lib/gprpp/sync.h"
|
||||
#include "src/core/lib/iomgr/closure.h"
|
||||
#include "src/core/lib/iomgr/error.h"
|
||||
#include "src/core/lib/iomgr/iomgr_fwd.h"
|
||||
#include "src/core/resolver/endpoint_addresses.h"
|
||||
|
||||
#define GRPC_DNS_ARES_DEFAULT_QUERY_TIMEOUT_MS 120000
|
||||
|
||||
extern grpc_core::TraceFlag grpc_trace_cares_address_sorting;
|
||||
|
||||
extern grpc_core::TraceFlag grpc_trace_cares_resolver;
|
||||
|
||||
#define GRPC_CARES_TRACE_LOG(format, ...) \
|
||||
do { \
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_cares_resolver)) { \
|
||||
gpr_log(GPR_DEBUG, "(c-ares resolver) " format, __VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
typedef struct grpc_ares_ev_driver grpc_ares_ev_driver;
|
||||
|
||||
struct grpc_ares_request {
|
||||
/// synchronizes access to this request, and also to associated
|
||||
/// ev_driver and fd_node objects
|
||||
grpc_core::Mutex mu;
|
||||
/// indicates the DNS server to use, if specified
|
||||
struct ares_addr_port_node dns_server_addr ABSL_GUARDED_BY(mu);
|
||||
/// following members are set in grpc_resolve_address_ares_impl
|
||||
/// closure to call when the request completes
|
||||
grpc_closure* on_done ABSL_GUARDED_BY(mu) = nullptr;
|
||||
/// the pointer to receive the resolved addresses
|
||||
std::unique_ptr<grpc_core::EndpointAddressesList>* addresses_out
|
||||
ABSL_GUARDED_BY(mu);
|
||||
/// the pointer to receive the resolved balancer addresses
|
||||
std::unique_ptr<grpc_core::EndpointAddressesList>* balancer_addresses_out
|
||||
ABSL_GUARDED_BY(mu);
|
||||
/// the pointer to receive the service config in JSON
|
||||
char** service_config_json_out ABSL_GUARDED_BY(mu) = nullptr;
|
||||
/// the event driver used by this request
|
||||
grpc_ares_ev_driver* ev_driver ABSL_GUARDED_BY(mu) = nullptr;
|
||||
/// number of ongoing queries
|
||||
size_t pending_queries ABSL_GUARDED_BY(mu) = 0;
|
||||
/// the errors explaining query failures, appended to in query callbacks
|
||||
grpc_error_handle error ABSL_GUARDED_BY(mu);
|
||||
};
|
||||
|
||||
// Asynchronously resolve \a name (A/AAAA records only).
|
||||
// It uses \a default_port if a port isn't designated in \a name, otherwise it
|
||||
// uses the port in \a name. grpc_ares_init() must be called at least once
|
||||
// before this function. The returned grpc_ares_request object is owned by the
|
||||
// caller and it is safe to free after on_done is called back.
|
||||
|
||||
// Note on synchronization: \a as on_done might be called from another thread
|
||||
//~immediately, access to the grpc_ares_request* return value must be
|
||||
// synchronized by the caller. TODO(apolcyn): we should remove this requirement
|
||||
// by changing this API to use two phase initialization - one API to create
|
||||
// the grpc_ares_request* and another to start the async work.
|
||||
extern grpc_ares_request* (*grpc_dns_lookup_hostname_ares)(
|
||||
const char* dns_server, const char* name, const char* default_port,
|
||||
grpc_pollset_set* interested_parties, grpc_closure* on_done,
|
||||
std::unique_ptr<grpc_core::EndpointAddressesList>* addresses,
|
||||
int query_timeout_ms);
|
||||
|
||||
// Asynchronously resolve a SRV record.
|
||||
// See \a grpc_dns_lookup_hostname_ares for usage details and caveats.
|
||||
extern grpc_ares_request* (*grpc_dns_lookup_srv_ares)(
|
||||
const char* dns_server, const char* name,
|
||||
grpc_pollset_set* interested_parties, grpc_closure* on_done,
|
||||
std::unique_ptr<grpc_core::EndpointAddressesList>* balancer_addresses,
|
||||
int query_timeout_ms);
|
||||
|
||||
// Asynchronously resolve a TXT record.
|
||||
// See \a grpc_dns_lookup_hostname_ares for usage details and caveats.
|
||||
extern grpc_ares_request* (*grpc_dns_lookup_txt_ares)(
|
||||
const char* dns_server, const char* name,
|
||||
grpc_pollset_set* interested_parties, grpc_closure* on_done,
|
||||
char** service_config_json, int query_timeout_ms);
|
||||
|
||||
// Cancel the pending grpc_ares_request \a request
|
||||
extern void (*grpc_cancel_ares_request)(grpc_ares_request* request);
|
||||
|
||||
// Initialize gRPC ares wrapper. Must be called at least once before
|
||||
// grpc_resolve_address_ares().
|
||||
grpc_error_handle grpc_ares_init(void);
|
||||
|
||||
// Uninitialized gRPC ares wrapper. If there was more than one previous call to
|
||||
// grpc_ares_init(), this function uninitializes the gRPC ares wrapper only if
|
||||
// it has been called the same number of times as grpc_ares_init().
|
||||
void grpc_ares_cleanup(void);
|
||||
|
||||
// Indicates whether or not AAAA queries should be attempted.
|
||||
// E.g., return false if ipv6 is known to not be available.
|
||||
bool grpc_ares_query_ipv6();
|
||||
|
||||
// Sorts destinations in lb_addrs according to RFC 6724.
|
||||
void grpc_cares_wrapper_address_sorting_sort(
|
||||
const grpc_ares_request* request,
|
||||
grpc_core::EndpointAddressesList* addresses);
|
||||
|
||||
// Exposed in this header for C-core tests only
|
||||
extern void (*grpc_ares_test_only_inject_config)(ares_channel* channel);
|
||||
|
||||
// Exposed in this header for C-core tests only
|
||||
extern bool g_grpc_ares_test_only_force_tcp;
|
||||
|
||||
#endif // GRPC_SRC_CORE_RESOLVER_DNS_C_ARES_GRPC_ARES_WRAPPER_H
|
||||
29
Pods/gRPC-Core/src/core/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc
generated
Normal file
29
Pods/gRPC-Core/src/core/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc
generated
Normal file
@@ -0,0 +1,29 @@
|
||||
//
|
||||
//
|
||||
// Copyright 2016 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
//
|
||||
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include "src/core/lib/iomgr/port.h"
|
||||
#if GRPC_ARES == 1 && defined(GRPC_POSIX_SOCKET_ARES_EV_DRIVER)
|
||||
|
||||
#include "src/core/resolver/dns/c_ares/grpc_ares_wrapper.h"
|
||||
#include "src/core/lib/iomgr/socket_utils_posix.h"
|
||||
|
||||
bool grpc_ares_query_ipv6() { return grpc_ipv6_loopback_available(); }
|
||||
|
||||
#endif // GRPC_ARES == 1 && defined(GRPC_POSIX_SOCKET_ARES_EV_DRIVER)
|
||||
35
Pods/gRPC-Core/src/core/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc
generated
Normal file
35
Pods/gRPC-Core/src/core/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc
generated
Normal file
@@ -0,0 +1,35 @@
|
||||
//
|
||||
//
|
||||
// Copyright 2016 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
//
|
||||
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include "src/core/lib/iomgr/port.h" // IWYU pragma: keep
|
||||
|
||||
#if GRPC_ARES == 1 && defined(GRPC_WINDOWS_SOCKET_ARES_EV_DRIVER)
|
||||
|
||||
#include <grpc/support/string_util.h>
|
||||
|
||||
#include "src/core/resolver/dns/c_ares/grpc_ares_wrapper.h"
|
||||
#include "src/core/lib/address_utils/parse_address.h"
|
||||
#include "src/core/lib/gpr/string.h"
|
||||
#include "src/core/lib/iomgr/socket_windows.h"
|
||||
#include "src/core/resolver/endpoint_addresses.h"
|
||||
|
||||
bool grpc_ares_query_ipv6() { return grpc_ipv6_loopback_available(); }
|
||||
|
||||
#endif // GRPC_ARES == 1 && defined(GRPC_WINDOWS_SOCKET_ARES_EV_DRIVER)
|
||||
68
Pods/gRPC-Core/src/core/resolver/dns/dns_resolver_plugin.cc
generated
Normal file
68
Pods/gRPC-Core/src/core/resolver/dns/dns_resolver_plugin.cc
generated
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright 2022 The gRPC Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include "src/core/resolver/dns/dns_resolver_plugin.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "absl/strings/match.h"
|
||||
|
||||
#include <grpc/support/log.h>
|
||||
|
||||
#include "src/core/resolver/dns/c_ares/dns_resolver_ares.h"
|
||||
#include "src/core/resolver/dns/event_engine/event_engine_client_channel_resolver.h"
|
||||
#include "src/core/resolver/dns/native/dns_resolver.h"
|
||||
#include "src/core/lib/config/config_vars.h"
|
||||
#include "src/core/lib/experiments/experiments.h"
|
||||
#include "src/core/lib/gprpp/crash.h"
|
||||
#include "src/core/resolver/resolver_factory.h"
|
||||
|
||||
namespace grpc_core {
|
||||
|
||||
void RegisterDnsResolver(CoreConfiguration::Builder* builder) {
|
||||
#ifdef GRPC_IOS_EVENT_ENGINE_CLIENT
|
||||
gpr_log(GPR_DEBUG, "Using EventEngine dns resolver");
|
||||
builder->resolver_registry()->RegisterResolverFactory(
|
||||
std::make_unique<EventEngineClientChannelDNSResolverFactory>());
|
||||
return;
|
||||
#endif
|
||||
#ifndef GRPC_DO_NOT_INSTANTIATE_POSIX_POLLER
|
||||
if (IsEventEngineDnsEnabled()) {
|
||||
gpr_log(GPR_DEBUG, "Using EventEngine dns resolver");
|
||||
builder->resolver_registry()->RegisterResolverFactory(
|
||||
std::make_unique<EventEngineClientChannelDNSResolverFactory>());
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
auto resolver = ConfigVars::Get().DnsResolver();
|
||||
// ---- Ares resolver ----
|
||||
if (ShouldUseAresDnsResolver(resolver)) {
|
||||
gpr_log(GPR_DEBUG, "Using ares dns resolver");
|
||||
RegisterAresDnsResolver(builder);
|
||||
return;
|
||||
}
|
||||
// ---- Native resolver ----
|
||||
if (absl::EqualsIgnoreCase(resolver, "native") ||
|
||||
!builder->resolver_registry()->HasResolverFactory("dns")) {
|
||||
gpr_log(GPR_DEBUG, "Using native dns resolver");
|
||||
RegisterNativeDnsResolver(builder);
|
||||
return;
|
||||
}
|
||||
Crash(
|
||||
"Unable to set DNS resolver! Likely a logic error in gRPC-core, "
|
||||
"please file a bug.");
|
||||
}
|
||||
|
||||
} // namespace grpc_core
|
||||
27
Pods/gRPC-Core/src/core/resolver/dns/dns_resolver_plugin.h
generated
Normal file
27
Pods/gRPC-Core/src/core/resolver/dns/dns_resolver_plugin.h
generated
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright 2022 The gRPC Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#ifndef GRPC_SRC_CORE_RESOLVER_DNS_DNS_RESOLVER_PLUGIN_H
|
||||
#define GRPC_SRC_CORE_RESOLVER_DNS_DNS_RESOLVER_PLUGIN_H
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include "src/core/lib/config/core_configuration.h"
|
||||
|
||||
namespace grpc_core {
|
||||
|
||||
// Centralized decision logic about which client channel DNS resolver to enable.
|
||||
void RegisterDnsResolver(CoreConfiguration::Builder* builder);
|
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_SRC_CORE_RESOLVER_DNS_DNS_RESOLVER_PLUGIN_H
|
||||
588
Pods/gRPC-Core/src/core/resolver/dns/event_engine/event_engine_client_channel_resolver.cc
generated
Normal file
588
Pods/gRPC-Core/src/core/resolver/dns/event_engine/event_engine_client_channel_resolver.cc
generated
Normal file
@@ -0,0 +1,588 @@
|
||||
// Copyright 2023 The gRPC Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include "src/core/resolver/dns/event_engine/event_engine_client_channel_resolver.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/base/thread_annotations.h"
|
||||
#include "absl/cleanup/cleanup.h"
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/match.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/strip.h"
|
||||
#include "absl/types/optional.h"
|
||||
|
||||
#include <grpc/event_engine/event_engine.h>
|
||||
#include <grpc/impl/channel_arg_names.h>
|
||||
#include <grpc/support/log.h>
|
||||
|
||||
#include "src/core/lib/backoff/backoff.h"
|
||||
#include "src/core/lib/channel/channel_args.h"
|
||||
#include "src/core/lib/debug/trace.h"
|
||||
#include "src/core/lib/event_engine/resolved_address_internal.h"
|
||||
#include "src/core/lib/gprpp/debug_location.h"
|
||||
#include "src/core/lib/gprpp/ref_counted_ptr.h"
|
||||
#include "src/core/lib/gprpp/sync.h"
|
||||
#include "src/core/lib/gprpp/time.h"
|
||||
#include "src/core/lib/gprpp/validation_errors.h"
|
||||
#include "src/core/lib/iomgr/exec_ctx.h"
|
||||
#include "src/core/lib/iomgr/resolve_address.h"
|
||||
#include "src/core/service_config/service_config.h"
|
||||
#include "src/core/service_config/service_config_impl.h"
|
||||
#include "src/core/load_balancing/grpclb/grpclb_balancer_addresses.h"
|
||||
#include "src/core/resolver/dns/event_engine/service_config_helper.h"
|
||||
#include "src/core/resolver/endpoint_addresses.h"
|
||||
#include "src/core/resolver/polling_resolver.h"
|
||||
#include "src/core/resolver/resolver.h"
|
||||
#include "src/core/resolver/resolver_factory.h"
|
||||
|
||||
// IWYU pragma: no_include <ratio>
|
||||
|
||||
namespace grpc_core {
|
||||
namespace {
|
||||
|
||||
#define GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS 1
|
||||
#define GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER 1.6
|
||||
#define GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS 120
|
||||
#define GRPC_DNS_RECONNECT_JITTER 0.2
|
||||
#define GRPC_DNS_DEFAULT_QUERY_TIMEOUT_MS 120000
|
||||
|
||||
using grpc_event_engine::experimental::EventEngine;
|
||||
|
||||
// TODO(hork): Investigate adding a resolver test scenario where the first
|
||||
// balancer hostname lookup result is an error, and the second contains valid
|
||||
// addresses.
|
||||
// TODO(hork): Add a test that checks for proper authority from balancer
|
||||
// addresses.
|
||||
|
||||
// TODO(hork): replace this with `dns_resolver` when all other resolver
|
||||
// implementations are removed.
|
||||
TraceFlag grpc_event_engine_client_channel_resolver_trace(
|
||||
false, "event_engine_client_channel_resolver");
|
||||
|
||||
#define GRPC_EVENT_ENGINE_RESOLVER_TRACE(format, ...) \
|
||||
if (GRPC_TRACE_FLAG_ENABLED( \
|
||||
grpc_event_engine_client_channel_resolver_trace)) { \
|
||||
gpr_log(GPR_DEBUG, "(event_engine client channel resolver) " format, \
|
||||
__VA_ARGS__); \
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// EventEngineClientChannelDNSResolver
|
||||
// ----------------------------------------------------------------------------
|
||||
class EventEngineClientChannelDNSResolver : public PollingResolver {
|
||||
public:
|
||||
EventEngineClientChannelDNSResolver(ResolverArgs args,
|
||||
Duration min_time_between_resolutions);
|
||||
OrphanablePtr<Orphanable> StartRequest() override;
|
||||
|
||||
private:
|
||||
// ----------------------------------------------------------------------------
|
||||
// EventEngineDNSRequestWrapper declaration
|
||||
// ----------------------------------------------------------------------------
|
||||
class EventEngineDNSRequestWrapper
|
||||
: public InternallyRefCounted<EventEngineDNSRequestWrapper> {
|
||||
public:
|
||||
EventEngineDNSRequestWrapper(
|
||||
RefCountedPtr<EventEngineClientChannelDNSResolver> resolver,
|
||||
std::unique_ptr<EventEngine::DNSResolver> event_engine_resolver);
|
||||
~EventEngineDNSRequestWrapper() override;
|
||||
|
||||
// Note that thread safety cannot be analyzed due to this being invoked from
|
||||
// OrphanablePtr<>, and there's no way to pass the lock annotation through
|
||||
// there.
|
||||
void Orphan() override ABSL_NO_THREAD_SAFETY_ANALYSIS;
|
||||
|
||||
private:
|
||||
void OnTimeout() ABSL_LOCKS_EXCLUDED(on_resolved_mu_);
|
||||
void OnHostnameResolved(
|
||||
absl::StatusOr<std::vector<EventEngine::ResolvedAddress>> addresses);
|
||||
void OnSRVResolved(
|
||||
absl::StatusOr<std::vector<EventEngine::DNSResolver::SRVRecord>>
|
||||
srv_records);
|
||||
void OnBalancerHostnamesResolved(
|
||||
std::string authority,
|
||||
absl::StatusOr<std::vector<EventEngine::ResolvedAddress>> addresses);
|
||||
void OnTXTResolved(absl::StatusOr<std::vector<std::string>> service_config);
|
||||
// Returns a Result if resolution is complete.
|
||||
// callers must release the lock and call OnRequestComplete if a Result is
|
||||
// returned. This is because OnRequestComplete may Orphan the resolver,
|
||||
// which requires taking the lock.
|
||||
absl::optional<Resolver::Result> OnResolvedLocked()
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(on_resolved_mu_);
|
||||
// Helper method to populate server addresses on resolver result.
|
||||
void MaybePopulateAddressesLocked(Resolver::Result* result)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(on_resolved_mu_);
|
||||
// Helper method to populate balancer addresses on resolver result.
|
||||
void MaybePopulateBalancerAddressesLocked(Resolver::Result* result)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(on_resolved_mu_);
|
||||
// Helper method to populate service config on resolver result.
|
||||
void MaybePopulateServiceConfigLocked(Resolver::Result* result)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(on_resolved_mu_);
|
||||
|
||||
RefCountedPtr<EventEngineClientChannelDNSResolver> resolver_;
|
||||
Mutex on_resolved_mu_;
|
||||
// Lookup callbacks
|
||||
bool is_hostname_inflight_ ABSL_GUARDED_BY(on_resolved_mu_) = false;
|
||||
bool is_srv_inflight_ ABSL_GUARDED_BY(on_resolved_mu_) = false;
|
||||
bool is_txt_inflight_ ABSL_GUARDED_BY(on_resolved_mu_) = false;
|
||||
// Output fields from requests.
|
||||
EndpointAddressesList addresses_ ABSL_GUARDED_BY(on_resolved_mu_);
|
||||
EndpointAddressesList balancer_addresses_ ABSL_GUARDED_BY(on_resolved_mu_);
|
||||
ValidationErrors errors_ ABSL_GUARDED_BY(on_resolved_mu_);
|
||||
absl::StatusOr<std::string> service_config_json_
|
||||
ABSL_GUARDED_BY(on_resolved_mu_);
|
||||
// Other internal state
|
||||
size_t number_of_balancer_hostnames_initiated_
|
||||
ABSL_GUARDED_BY(on_resolved_mu_) = 0;
|
||||
size_t number_of_balancer_hostnames_resolved_
|
||||
ABSL_GUARDED_BY(on_resolved_mu_) = 0;
|
||||
bool orphaned_ ABSL_GUARDED_BY(on_resolved_mu_) = false;
|
||||
absl::optional<EventEngine::TaskHandle> timeout_handle_
|
||||
ABSL_GUARDED_BY(on_resolved_mu_);
|
||||
std::unique_ptr<EventEngine::DNSResolver> event_engine_resolver_;
|
||||
};
|
||||
|
||||
/// whether to request the service config
|
||||
const bool request_service_config_;
|
||||
// whether or not to enable SRV DNS queries
|
||||
const bool enable_srv_queries_;
|
||||
// timeout in milliseconds for active DNS queries
|
||||
EventEngine::Duration query_timeout_ms_;
|
||||
std::shared_ptr<EventEngine> event_engine_;
|
||||
};
|
||||
|
||||
EventEngineClientChannelDNSResolver::EventEngineClientChannelDNSResolver(
|
||||
ResolverArgs args, Duration min_time_between_resolutions)
|
||||
: PollingResolver(std::move(args), min_time_between_resolutions,
|
||||
BackOff::Options()
|
||||
.set_initial_backoff(Duration::Milliseconds(
|
||||
GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS * 1000))
|
||||
.set_multiplier(GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER)
|
||||
.set_jitter(GRPC_DNS_RECONNECT_JITTER)
|
||||
.set_max_backoff(Duration::Milliseconds(
|
||||
GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS * 1000)),
|
||||
&grpc_event_engine_client_channel_resolver_trace),
|
||||
request_service_config_(
|
||||
!channel_args()
|
||||
.GetBool(GRPC_ARG_SERVICE_CONFIG_DISABLE_RESOLUTION)
|
||||
.value_or(true)),
|
||||
enable_srv_queries_(channel_args()
|
||||
.GetBool(GRPC_ARG_DNS_ENABLE_SRV_QUERIES)
|
||||
.value_or(false)),
|
||||
// TODO(yijiem): decide if the ares channel arg timeout should be reused.
|
||||
query_timeout_ms_(std::chrono::milliseconds(
|
||||
std::max(0, channel_args()
|
||||
.GetInt(GRPC_ARG_DNS_ARES_QUERY_TIMEOUT_MS)
|
||||
.value_or(GRPC_DNS_DEFAULT_QUERY_TIMEOUT_MS)))),
|
||||
event_engine_(channel_args().GetObjectRef<EventEngine>()) {}
|
||||
|
||||
OrphanablePtr<Orphanable> EventEngineClientChannelDNSResolver::StartRequest() {
|
||||
auto dns_resolver =
|
||||
event_engine_->GetDNSResolver({/*dns_server=*/authority()});
|
||||
if (!dns_resolver.ok()) {
|
||||
Result result;
|
||||
result.addresses = dns_resolver.status();
|
||||
result.service_config = dns_resolver.status();
|
||||
OnRequestComplete(std::move(result));
|
||||
return nullptr;
|
||||
}
|
||||
return MakeOrphanable<EventEngineDNSRequestWrapper>(
|
||||
RefAsSubclass<EventEngineClientChannelDNSResolver>(DEBUG_LOCATION,
|
||||
"dns-resolving"),
|
||||
std::move(*dns_resolver));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// EventEngineDNSRequestWrapper definition
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
EventEngineClientChannelDNSResolver::EventEngineDNSRequestWrapper::
|
||||
EventEngineDNSRequestWrapper(
|
||||
RefCountedPtr<EventEngineClientChannelDNSResolver> resolver,
|
||||
std::unique_ptr<EventEngine::DNSResolver> event_engine_resolver)
|
||||
: resolver_(std::move(resolver)),
|
||||
event_engine_resolver_(std::move(event_engine_resolver)) {
|
||||
// Locking to prevent completion before all records are queried
|
||||
MutexLock lock(&on_resolved_mu_);
|
||||
GRPC_EVENT_ENGINE_RESOLVER_TRACE(
|
||||
"DNSResolver::%p Starting hostname resolution for %s", resolver_.get(),
|
||||
resolver_->name_to_resolve().c_str());
|
||||
is_hostname_inflight_ = true;
|
||||
event_engine_resolver_->LookupHostname(
|
||||
[self = Ref(DEBUG_LOCATION, "OnHostnameResolved")](
|
||||
absl::StatusOr<std::vector<EventEngine::ResolvedAddress>>
|
||||
addresses) mutable {
|
||||
ApplicationCallbackExecCtx callback_exec_ctx;
|
||||
ExecCtx exec_ctx;
|
||||
self->OnHostnameResolved(std::move(addresses));
|
||||
self.reset();
|
||||
},
|
||||
resolver_->name_to_resolve(), kDefaultSecurePort);
|
||||
if (resolver_->enable_srv_queries_) {
|
||||
GRPC_EVENT_ENGINE_RESOLVER_TRACE(
|
||||
"DNSResolver::%p Starting SRV record resolution for %s",
|
||||
resolver_.get(), resolver_->name_to_resolve().c_str());
|
||||
is_srv_inflight_ = true;
|
||||
event_engine_resolver_->LookupSRV(
|
||||
[self = Ref(DEBUG_LOCATION, "OnSRVResolved")](
|
||||
absl::StatusOr<std::vector<EventEngine::DNSResolver::SRVRecord>>
|
||||
srv_records) mutable {
|
||||
ApplicationCallbackExecCtx callback_exec_ctx;
|
||||
ExecCtx exec_ctx;
|
||||
self->OnSRVResolved(std::move(srv_records));
|
||||
self.reset();
|
||||
},
|
||||
absl::StrCat("_grpclb._tcp.", resolver_->name_to_resolve()));
|
||||
}
|
||||
if (resolver_->request_service_config_) {
|
||||
GRPC_EVENT_ENGINE_RESOLVER_TRACE(
|
||||
"DNSResolver::%p Starting TXT record resolution for %s",
|
||||
resolver_.get(), resolver_->name_to_resolve().c_str());
|
||||
is_txt_inflight_ = true;
|
||||
event_engine_resolver_->LookupTXT(
|
||||
[self = Ref(DEBUG_LOCATION, "OnTXTResolved")](
|
||||
absl::StatusOr<std::vector<std::string>> service_config) mutable {
|
||||
ApplicationCallbackExecCtx callback_exec_ctx;
|
||||
ExecCtx exec_ctx;
|
||||
self->OnTXTResolved(std::move(service_config));
|
||||
self.reset();
|
||||
},
|
||||
absl::StrCat("_grpc_config.", resolver_->name_to_resolve()));
|
||||
}
|
||||
// Initialize overall DNS resolution timeout alarm.
|
||||
auto timeout = resolver_->query_timeout_ms_.count() == 0
|
||||
? EventEngine::Duration::max()
|
||||
: resolver_->query_timeout_ms_;
|
||||
timeout_handle_ = resolver_->event_engine_->RunAfter(
|
||||
timeout, [self = Ref(DEBUG_LOCATION, "OnTimeout")]() mutable {
|
||||
ApplicationCallbackExecCtx callback_exec_ctx;
|
||||
ExecCtx exec_ctx;
|
||||
self->OnTimeout();
|
||||
self.reset();
|
||||
});
|
||||
}
|
||||
|
||||
EventEngineClientChannelDNSResolver::EventEngineDNSRequestWrapper::
|
||||
~EventEngineDNSRequestWrapper() {
|
||||
resolver_.reset(DEBUG_LOCATION, "dns-resolving");
|
||||
}
|
||||
|
||||
void EventEngineClientChannelDNSResolver::EventEngineDNSRequestWrapper::
|
||||
Orphan() {
|
||||
{
|
||||
MutexLock lock(&on_resolved_mu_);
|
||||
orphaned_ = true;
|
||||
if (timeout_handle_.has_value()) {
|
||||
resolver_->event_engine_->Cancel(*timeout_handle_);
|
||||
timeout_handle_.reset();
|
||||
}
|
||||
// Even if cancellation fails here, OnResolvedLocked will return early, and
|
||||
// the resolver will never see a completed request.
|
||||
event_engine_resolver_.reset();
|
||||
}
|
||||
Unref(DEBUG_LOCATION, "Orphan");
|
||||
}
|
||||
|
||||
void EventEngineClientChannelDNSResolver::EventEngineDNSRequestWrapper::
|
||||
OnTimeout() {
|
||||
MutexLock lock(&on_resolved_mu_);
|
||||
GRPC_EVENT_ENGINE_RESOLVER_TRACE("DNSResolver::%p OnTimeout",
|
||||
resolver_.get());
|
||||
timeout_handle_.reset();
|
||||
event_engine_resolver_.reset();
|
||||
}
|
||||
|
||||
void EventEngineClientChannelDNSResolver::EventEngineDNSRequestWrapper::
|
||||
OnHostnameResolved(absl::StatusOr<std::vector<EventEngine::ResolvedAddress>>
|
||||
new_addresses) {
|
||||
absl::optional<Resolver::Result> result;
|
||||
{
|
||||
MutexLock lock(&on_resolved_mu_);
|
||||
// Make sure field destroys before cleanup.
|
||||
ValidationErrors::ScopedField field(&errors_, "hostname lookup");
|
||||
if (orphaned_) return;
|
||||
is_hostname_inflight_ = false;
|
||||
if (!new_addresses.ok()) {
|
||||
errors_.AddError(new_addresses.status().message());
|
||||
} else {
|
||||
addresses_.reserve(addresses_.size() + new_addresses->size());
|
||||
for (const auto& addr : *new_addresses) {
|
||||
addresses_.emplace_back(CreateGRPCResolvedAddress(addr), ChannelArgs());
|
||||
}
|
||||
}
|
||||
result = OnResolvedLocked();
|
||||
}
|
||||
if (result.has_value()) {
|
||||
resolver_->OnRequestComplete(std::move(*result));
|
||||
}
|
||||
}
|
||||
|
||||
void EventEngineClientChannelDNSResolver::EventEngineDNSRequestWrapper::
|
||||
OnSRVResolved(
|
||||
absl::StatusOr<std::vector<EventEngine::DNSResolver::SRVRecord>>
|
||||
srv_records) {
|
||||
absl::optional<Resolver::Result> result;
|
||||
auto cleanup = absl::MakeCleanup([&]() {
|
||||
if (result.has_value()) {
|
||||
resolver_->OnRequestComplete(std::move(*result));
|
||||
}
|
||||
});
|
||||
MutexLock lock(&on_resolved_mu_);
|
||||
// Make sure field destroys before cleanup.
|
||||
ValidationErrors::ScopedField field(&errors_, "srv lookup");
|
||||
if (orphaned_) return;
|
||||
is_srv_inflight_ = false;
|
||||
if (!srv_records.ok()) {
|
||||
// An error has occurred, finish resolving.
|
||||
errors_.AddError(srv_records.status().message());
|
||||
result = OnResolvedLocked();
|
||||
return;
|
||||
}
|
||||
if (srv_records->empty()) {
|
||||
result = OnResolvedLocked();
|
||||
return;
|
||||
}
|
||||
if (!timeout_handle_.has_value()) {
|
||||
// We could reach here if timeout happened while an SRV query was finishing.
|
||||
errors_.AddError(
|
||||
"timed out - not initiating subsequent balancer hostname requests");
|
||||
result = OnResolvedLocked();
|
||||
return;
|
||||
}
|
||||
// Do a subsequent hostname query since SRV records were returned
|
||||
for (auto& srv_record : *srv_records) {
|
||||
GRPC_EVENT_ENGINE_RESOLVER_TRACE(
|
||||
"DNSResolver::%p Starting balancer hostname resolution for %s:%d",
|
||||
resolver_.get(), srv_record.host.c_str(), srv_record.port);
|
||||
++number_of_balancer_hostnames_initiated_;
|
||||
event_engine_resolver_->LookupHostname(
|
||||
[host = srv_record.host,
|
||||
self = Ref(DEBUG_LOCATION, "OnBalancerHostnamesResolved")](
|
||||
absl::StatusOr<std::vector<EventEngine::ResolvedAddress>>
|
||||
new_balancer_addresses) mutable {
|
||||
ApplicationCallbackExecCtx callback_exec_ctx;
|
||||
ExecCtx exec_ctx;
|
||||
self->OnBalancerHostnamesResolved(std::move(host),
|
||||
std::move(new_balancer_addresses));
|
||||
self.reset();
|
||||
},
|
||||
srv_record.host, std::to_string(srv_record.port));
|
||||
}
|
||||
}
|
||||
|
||||
void EventEngineClientChannelDNSResolver::EventEngineDNSRequestWrapper::
|
||||
OnBalancerHostnamesResolved(
|
||||
std::string authority,
|
||||
absl::StatusOr<std::vector<EventEngine::ResolvedAddress>>
|
||||
new_balancer_addresses) {
|
||||
absl::optional<Resolver::Result> result;
|
||||
auto cleanup = absl::MakeCleanup([&]() {
|
||||
if (result.has_value()) {
|
||||
resolver_->OnRequestComplete(std::move(*result));
|
||||
}
|
||||
});
|
||||
MutexLock lock(&on_resolved_mu_);
|
||||
// Make sure field destroys before cleanup.
|
||||
ValidationErrors::ScopedField field(
|
||||
&errors_, absl::StrCat("balancer lookup for ", authority));
|
||||
if (orphaned_) return;
|
||||
++number_of_balancer_hostnames_resolved_;
|
||||
if (!new_balancer_addresses.ok()) {
|
||||
// An error has occurred, finish resolving.
|
||||
errors_.AddError(new_balancer_addresses.status().message());
|
||||
} else {
|
||||
// Capture the addresses and finish resolving.
|
||||
balancer_addresses_.reserve(balancer_addresses_.size() +
|
||||
new_balancer_addresses->size());
|
||||
auto srv_channel_args =
|
||||
ChannelArgs().Set(GRPC_ARG_DEFAULT_AUTHORITY, authority);
|
||||
for (const auto& addr : *new_balancer_addresses) {
|
||||
balancer_addresses_.emplace_back(CreateGRPCResolvedAddress(addr),
|
||||
srv_channel_args);
|
||||
}
|
||||
}
|
||||
result = OnResolvedLocked();
|
||||
}
|
||||
|
||||
void EventEngineClientChannelDNSResolver::EventEngineDNSRequestWrapper::
|
||||
OnTXTResolved(absl::StatusOr<std::vector<std::string>> service_config) {
|
||||
absl::optional<Resolver::Result> result;
|
||||
{
|
||||
MutexLock lock(&on_resolved_mu_);
|
||||
// Make sure field destroys before cleanup.
|
||||
ValidationErrors::ScopedField field(&errors_, "txt lookup");
|
||||
if (orphaned_) return;
|
||||
GPR_ASSERT(is_txt_inflight_);
|
||||
is_txt_inflight_ = false;
|
||||
if (!service_config.ok()) {
|
||||
errors_.AddError(service_config.status().message());
|
||||
service_config_json_ = service_config.status();
|
||||
} else {
|
||||
static constexpr absl::string_view kServiceConfigAttributePrefix =
|
||||
"grpc_config=";
|
||||
auto result = std::find_if(service_config->begin(), service_config->end(),
|
||||
[&](absl::string_view s) {
|
||||
return absl::StartsWith(
|
||||
s, kServiceConfigAttributePrefix);
|
||||
});
|
||||
if (result != service_config->end()) {
|
||||
// Found a service config record.
|
||||
service_config_json_ =
|
||||
result->substr(kServiceConfigAttributePrefix.size());
|
||||
GRPC_EVENT_ENGINE_RESOLVER_TRACE(
|
||||
"DNSResolver::%p found service config: %s",
|
||||
event_engine_resolver_.get(), service_config_json_->c_str());
|
||||
} else {
|
||||
service_config_json_ = absl::UnavailableError(absl::StrCat(
|
||||
"failed to find attribute prefix: ", kServiceConfigAttributePrefix,
|
||||
" in TXT records"));
|
||||
errors_.AddError(service_config_json_.status().message());
|
||||
}
|
||||
}
|
||||
result = OnResolvedLocked();
|
||||
}
|
||||
if (result.has_value()) {
|
||||
resolver_->OnRequestComplete(std::move(*result));
|
||||
}
|
||||
}
|
||||
|
||||
void EventEngineClientChannelDNSResolver::EventEngineDNSRequestWrapper::
|
||||
MaybePopulateAddressesLocked(Resolver::Result* result) {
|
||||
if (addresses_.empty()) return;
|
||||
result->addresses = std::move(addresses_);
|
||||
}
|
||||
|
||||
void EventEngineClientChannelDNSResolver::EventEngineDNSRequestWrapper::
|
||||
MaybePopulateBalancerAddressesLocked(Resolver::Result* result) {
|
||||
if (!balancer_addresses_.empty()) {
|
||||
result->args =
|
||||
SetGrpcLbBalancerAddresses(result->args, balancer_addresses_);
|
||||
}
|
||||
}
|
||||
|
||||
void EventEngineClientChannelDNSResolver::EventEngineDNSRequestWrapper::
|
||||
MaybePopulateServiceConfigLocked(Resolver::Result* result) {
|
||||
// This function is called only if we are returning addresses. In that case,
|
||||
// we currently ignore TXT lookup failures.
|
||||
// TODO(roth): Consider differentiating between NXDOMAIN and other failures,
|
||||
// so that we can return an error in the non-NXDOMAIN case.
|
||||
if (!service_config_json_.ok()) return;
|
||||
// TXT lookup succeeded, so parse the config.
|
||||
auto service_config = ChooseServiceConfig(*service_config_json_);
|
||||
if (!service_config.ok()) {
|
||||
result->service_config = absl::UnavailableError(absl::StrCat(
|
||||
"failed to parse service config: ", service_config.status().message()));
|
||||
return;
|
||||
}
|
||||
if (service_config->empty()) return;
|
||||
GRPC_EVENT_ENGINE_RESOLVER_TRACE(
|
||||
"DNSResolver::%p selected service config choice: %s",
|
||||
event_engine_resolver_.get(), service_config->c_str());
|
||||
result->service_config =
|
||||
ServiceConfigImpl::Create(resolver_->channel_args(), *service_config);
|
||||
if (!result->service_config.ok()) {
|
||||
result->service_config = absl::UnavailableError(
|
||||
absl::StrCat("failed to parse service config: ",
|
||||
result->service_config.status().message()));
|
||||
}
|
||||
}
|
||||
|
||||
absl::optional<Resolver::Result> EventEngineClientChannelDNSResolver::
|
||||
EventEngineDNSRequestWrapper::OnResolvedLocked() {
|
||||
if (orphaned_) return absl::nullopt;
|
||||
// Wait for all requested queries to return.
|
||||
if (is_hostname_inflight_ || is_srv_inflight_ || is_txt_inflight_ ||
|
||||
number_of_balancer_hostnames_resolved_ !=
|
||||
number_of_balancer_hostnames_initiated_) {
|
||||
GRPC_EVENT_ENGINE_RESOLVER_TRACE(
|
||||
"DNSResolver::%p OnResolved() waiting for results (hostname: %s, "
|
||||
"srv: %s, "
|
||||
"txt: %s, "
|
||||
"balancer addresses: %" PRIuPTR "/%" PRIuPTR " complete",
|
||||
this, is_hostname_inflight_ ? "waiting" : "done",
|
||||
is_srv_inflight_ ? "waiting" : "done",
|
||||
is_txt_inflight_ ? "waiting" : "done",
|
||||
number_of_balancer_hostnames_resolved_,
|
||||
number_of_balancer_hostnames_initiated_);
|
||||
return absl::nullopt;
|
||||
}
|
||||
GRPC_EVENT_ENGINE_RESOLVER_TRACE(
|
||||
"DNSResolver::%p OnResolvedLocked() proceeding", this);
|
||||
Resolver::Result result;
|
||||
result.args = resolver_->channel_args();
|
||||
// If both addresses and balancer addresses failed, return an error for both
|
||||
// addresses and service config.
|
||||
if (addresses_.empty() && balancer_addresses_.empty()) {
|
||||
absl::Status status = errors_.status(
|
||||
absl::StatusCode::kUnavailable,
|
||||
absl::StrCat("errors resolving ", resolver_->name_to_resolve()));
|
||||
if (status.ok()) {
|
||||
// If no errors were returned, but the results are empty, we still need to
|
||||
// return an error. Validation errors may be empty.
|
||||
status = absl::UnavailableError("No results from DNS queries");
|
||||
}
|
||||
GRPC_EVENT_ENGINE_RESOLVER_TRACE("%s", status.message().data());
|
||||
result.addresses = status;
|
||||
result.service_config = status;
|
||||
return std::move(result);
|
||||
}
|
||||
if (!errors_.ok()) {
|
||||
result.resolution_note = errors_.message(
|
||||
absl::StrCat("errors resolving ", resolver_->name_to_resolve()));
|
||||
}
|
||||
// We have at least one of addresses or balancer addresses, so we're going to
|
||||
// return a non-error for addresses.
|
||||
result.addresses.emplace();
|
||||
MaybePopulateAddressesLocked(&result);
|
||||
MaybePopulateServiceConfigLocked(&result);
|
||||
MaybePopulateBalancerAddressesLocked(&result);
|
||||
return std::move(result);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool EventEngineClientChannelDNSResolverFactory::IsValidUri(
|
||||
const URI& uri) const {
|
||||
if (absl::StripPrefix(uri.path(), "/").empty()) {
|
||||
gpr_log(GPR_ERROR, "no server name supplied in dns URI");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
OrphanablePtr<Resolver>
|
||||
EventEngineClientChannelDNSResolverFactory::CreateResolver(
|
||||
ResolverArgs args) const {
|
||||
Duration min_time_between_resolutions = std::max(
|
||||
Duration::Zero(), args.args
|
||||
.GetDurationFromIntMillis(
|
||||
GRPC_ARG_DNS_MIN_TIME_BETWEEN_RESOLUTIONS_MS)
|
||||
.value_or(Duration::Seconds(30)));
|
||||
return MakeOrphanable<EventEngineClientChannelDNSResolver>(
|
||||
std::move(args), min_time_between_resolutions);
|
||||
}
|
||||
|
||||
} // namespace grpc_core
|
||||
35
Pods/gRPC-Core/src/core/resolver/dns/event_engine/event_engine_client_channel_resolver.h
generated
Normal file
35
Pods/gRPC-Core/src/core/resolver/dns/event_engine/event_engine_client_channel_resolver.h
generated
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright 2023 The gRPC Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#ifndef GRPC_SRC_CORE_RESOLVER_DNS_EVENT_ENGINE_EVENT_ENGINE_CLIENT_CHANNEL_RESOLVER_H
|
||||
#define GRPC_SRC_CORE_RESOLVER_DNS_EVENT_ENGINE_EVENT_ENGINE_CLIENT_CHANNEL_RESOLVER_H
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
#include "src/core/lib/gprpp/orphanable.h"
|
||||
#include "src/core/resolver/resolver.h"
|
||||
#include "src/core/resolver/resolver_factory.h"
|
||||
#include "src/core/lib/uri/uri_parser.h"
|
||||
|
||||
namespace grpc_core {
|
||||
class EventEngineClientChannelDNSResolverFactory : public ResolverFactory {
|
||||
public:
|
||||
absl::string_view scheme() const override { return "dns"; }
|
||||
bool IsValidUri(const URI& uri) const override;
|
||||
OrphanablePtr<Resolver> CreateResolver(ResolverArgs args) const override;
|
||||
};
|
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_SRC_CORE_RESOLVER_DNS_EVENT_ENGINE_EVENT_ENGINE_CLIENT_CHANNEL_RESOLVER_H
|
||||
97
Pods/gRPC-Core/src/core/resolver/dns/event_engine/service_config_helper.cc
generated
Normal file
97
Pods/gRPC-Core/src/core/resolver/dns/event_engine/service_config_helper.cc
generated
Normal file
@@ -0,0 +1,97 @@
|
||||
// Copyright 2023 The gRPC Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include "src/core/resolver/dns/event_engine/service_config_helper.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
#include "src/core/lib/gprpp/status_helper.h"
|
||||
#include "src/core/lib/iomgr/gethostname.h"
|
||||
#include "src/core/lib/json/json.h"
|
||||
#include "src/core/lib/json/json_args.h"
|
||||
#include "src/core/lib/json/json_object_loader.h"
|
||||
#include "src/core/lib/json/json_reader.h"
|
||||
#include "src/core/lib/json/json_writer.h"
|
||||
|
||||
namespace grpc_core {
|
||||
|
||||
namespace {
|
||||
|
||||
struct ServiceConfigChoice {
|
||||
std::vector<std::string> client_language;
|
||||
int percentage = -1;
|
||||
std::vector<std::string> client_hostname;
|
||||
Json::Object service_config;
|
||||
|
||||
static const JsonLoaderInterface* JsonLoader(const JsonArgs&) {
|
||||
static const auto* loader =
|
||||
JsonObjectLoader<ServiceConfigChoice>()
|
||||
.OptionalField("clientLanguage",
|
||||
&ServiceConfigChoice::client_language)
|
||||
.OptionalField("percentage", &ServiceConfigChoice::percentage)
|
||||
.OptionalField("clientHostname",
|
||||
&ServiceConfigChoice::client_hostname)
|
||||
.Field("serviceConfig", &ServiceConfigChoice::service_config)
|
||||
.Finish();
|
||||
return loader;
|
||||
}
|
||||
};
|
||||
|
||||
bool vector_contains(const std::vector<std::string>& v,
|
||||
const std::string& value) {
|
||||
return std::find(v.begin(), v.end(), value) != v.end();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
absl::StatusOr<std::string> ChooseServiceConfig(
|
||||
absl::string_view service_config_json) {
|
||||
auto json = JsonParse(service_config_json);
|
||||
GRPC_RETURN_IF_ERROR(json.status());
|
||||
auto choices = LoadFromJson<std::vector<ServiceConfigChoice>>(*json);
|
||||
GRPC_RETURN_IF_ERROR(choices.status());
|
||||
for (const ServiceConfigChoice& choice : *choices) {
|
||||
// Check client language, if specified.
|
||||
if (!choice.client_language.empty() &&
|
||||
!vector_contains(choice.client_language, "c++")) {
|
||||
continue;
|
||||
}
|
||||
// Check client hostname, if specified.
|
||||
if (!choice.client_hostname.empty()) {
|
||||
const char* hostname = grpc_gethostname();
|
||||
if (!vector_contains(choice.client_hostname, hostname)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Check percentage, if specified.
|
||||
if (choice.percentage != -1) {
|
||||
int random_pct = rand() % 100;
|
||||
if (random_pct > choice.percentage || choice.percentage == 0) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return JsonDump(Json::FromObject(choice.service_config));
|
||||
}
|
||||
// No matching service config was found
|
||||
return "";
|
||||
}
|
||||
|
||||
} // namespace grpc_core
|
||||
32
Pods/gRPC-Core/src/core/resolver/dns/event_engine/service_config_helper.h
generated
Normal file
32
Pods/gRPC-Core/src/core/resolver/dns/event_engine/service_config_helper.h
generated
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright 2023 The gRPC Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_RESOLVER_DNS_EVENT_ENGINE_SERVICE_CONFIG_HELPER_H
|
||||
#define GRPC_SRC_CORE_RESOLVER_DNS_EVENT_ENGINE_SERVICE_CONFIG_HELPER_H
|
||||
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
namespace grpc_core {
|
||||
|
||||
absl::StatusOr<std::string> ChooseServiceConfig(
|
||||
absl::string_view service_config_json);
|
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_SRC_CORE_RESOLVER_DNS_EVENT_ENGINE_SERVICE_CONFIG_HELPER_H
|
||||
183
Pods/gRPC-Core/src/core/resolver/dns/native/dns_resolver.cc
generated
Normal file
183
Pods/gRPC-Core/src/core/resolver/dns/native/dns_resolver.cc
generated
Normal file
@@ -0,0 +1,183 @@
|
||||
//
|
||||
// Copyright 2015 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/functional/bind_front.h"
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/strings/strip.h"
|
||||
#include "absl/types/optional.h"
|
||||
|
||||
#include <grpc/impl/channel_arg_names.h>
|
||||
#include <grpc/support/log.h>
|
||||
|
||||
#include "src/core/resolver/polling_resolver.h"
|
||||
#include "src/core/lib/backoff/backoff.h"
|
||||
#include "src/core/lib/channel/channel_args.h"
|
||||
#include "src/core/lib/config/core_configuration.h"
|
||||
#include "src/core/lib/debug/trace.h"
|
||||
#include "src/core/lib/gprpp/debug_location.h"
|
||||
#include "src/core/lib/gprpp/orphanable.h"
|
||||
#include "src/core/lib/gprpp/ref_counted_ptr.h"
|
||||
#include "src/core/lib/gprpp/time.h"
|
||||
#include "src/core/lib/iomgr/resolve_address.h"
|
||||
#include "src/core/lib/iomgr/resolved_address.h"
|
||||
#include "src/core/resolver/endpoint_addresses.h"
|
||||
#include "src/core/resolver/resolver.h"
|
||||
#include "src/core/resolver/resolver_factory.h"
|
||||
#include "src/core/lib/uri/uri_parser.h"
|
||||
|
||||
#define GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS 1
|
||||
#define GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER 1.6
|
||||
#define GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS 120
|
||||
#define GRPC_DNS_RECONNECT_JITTER 0.2
|
||||
|
||||
namespace grpc_core {
|
||||
|
||||
namespace {
|
||||
|
||||
TraceFlag grpc_trace_dns_resolver(false, "dns_resolver");
|
||||
|
||||
class NativeClientChannelDNSResolver : public PollingResolver {
|
||||
public:
|
||||
NativeClientChannelDNSResolver(ResolverArgs args,
|
||||
Duration min_time_between_resolutions);
|
||||
~NativeClientChannelDNSResolver() override;
|
||||
|
||||
OrphanablePtr<Orphanable> StartRequest() override;
|
||||
|
||||
private:
|
||||
// No-op request class, used so that the PollingResolver code knows
|
||||
// when there is a request in flight, even if the request is not
|
||||
// actually cancellable.
|
||||
class Request : public Orphanable {
|
||||
public:
|
||||
Request() = default;
|
||||
|
||||
void Orphan() override { delete this; }
|
||||
};
|
||||
|
||||
void OnResolved(
|
||||
absl::StatusOr<std::vector<grpc_resolved_address>> addresses_or);
|
||||
};
|
||||
|
||||
NativeClientChannelDNSResolver::NativeClientChannelDNSResolver(
|
||||
ResolverArgs args, Duration min_time_between_resolutions)
|
||||
: PollingResolver(std::move(args), min_time_between_resolutions,
|
||||
BackOff::Options()
|
||||
.set_initial_backoff(Duration::Milliseconds(
|
||||
GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS * 1000))
|
||||
.set_multiplier(GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER)
|
||||
.set_jitter(GRPC_DNS_RECONNECT_JITTER)
|
||||
.set_max_backoff(Duration::Milliseconds(
|
||||
GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS * 1000)),
|
||||
&grpc_trace_dns_resolver) {
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_dns_resolver)) {
|
||||
gpr_log(GPR_DEBUG, "[dns_resolver=%p] created", this);
|
||||
}
|
||||
}
|
||||
|
||||
NativeClientChannelDNSResolver::~NativeClientChannelDNSResolver() {
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_dns_resolver)) {
|
||||
gpr_log(GPR_DEBUG, "[dns_resolver=%p] destroyed", this);
|
||||
}
|
||||
}
|
||||
|
||||
OrphanablePtr<Orphanable> NativeClientChannelDNSResolver::StartRequest() {
|
||||
Ref(DEBUG_LOCATION, "dns_request").release();
|
||||
auto dns_request_handle = GetDNSResolver()->LookupHostname(
|
||||
absl::bind_front(&NativeClientChannelDNSResolver::OnResolved, this),
|
||||
name_to_resolve(), kDefaultSecurePort, kDefaultDNSRequestTimeout,
|
||||
interested_parties(), /*name_server=*/"");
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_dns_resolver)) {
|
||||
gpr_log(GPR_DEBUG, "[dns_resolver=%p] starting request=%p", this,
|
||||
DNSResolver::HandleToString(dns_request_handle).c_str());
|
||||
}
|
||||
return MakeOrphanable<Request>();
|
||||
}
|
||||
|
||||
void NativeClientChannelDNSResolver::OnResolved(
|
||||
absl::StatusOr<std::vector<grpc_resolved_address>> addresses_or) {
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_dns_resolver)) {
|
||||
gpr_log(GPR_DEBUG, "[dns_resolver=%p] request complete, status=\"%s\"",
|
||||
this, addresses_or.status().ToString().c_str());
|
||||
}
|
||||
// Convert result from iomgr DNS API into Resolver::Result.
|
||||
Result result;
|
||||
if (addresses_or.ok()) {
|
||||
EndpointAddressesList addresses;
|
||||
for (auto& addr : *addresses_or) {
|
||||
addresses.emplace_back(addr, ChannelArgs());
|
||||
}
|
||||
result.addresses = std::move(addresses);
|
||||
} else {
|
||||
result.addresses = absl::UnavailableError(
|
||||
absl::StrCat("DNS resolution failed for ", name_to_resolve(), ": ",
|
||||
addresses_or.status().ToString()));
|
||||
}
|
||||
result.args = channel_args();
|
||||
OnRequestComplete(std::move(result));
|
||||
Unref(DEBUG_LOCATION, "dns_request");
|
||||
}
|
||||
|
||||
//
|
||||
// Factory
|
||||
//
|
||||
|
||||
class NativeClientChannelDNSResolverFactory : public ResolverFactory {
|
||||
public:
|
||||
absl::string_view scheme() const override { return "dns"; }
|
||||
|
||||
bool IsValidUri(const URI& uri) const override {
|
||||
if (GPR_UNLIKELY(!uri.authority().empty())) {
|
||||
gpr_log(GPR_ERROR, "authority based dns uri's not supported");
|
||||
return false;
|
||||
}
|
||||
if (absl::StripPrefix(uri.path(), "/").empty()) {
|
||||
gpr_log(GPR_ERROR, "no server name supplied in dns URI");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
OrphanablePtr<Resolver> CreateResolver(ResolverArgs args) const override {
|
||||
if (!IsValidUri(args.uri)) return nullptr;
|
||||
Duration min_time_between_resolutions = std::max(
|
||||
Duration::Zero(), args.args
|
||||
.GetDurationFromIntMillis(
|
||||
GRPC_ARG_DNS_MIN_TIME_BETWEEN_RESOLUTIONS_MS)
|
||||
.value_or(Duration::Seconds(30)));
|
||||
return MakeOrphanable<NativeClientChannelDNSResolver>(
|
||||
std::move(args), min_time_between_resolutions);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void RegisterNativeDnsResolver(CoreConfiguration::Builder* builder) {
|
||||
builder->resolver_registry()->RegisterResolverFactory(
|
||||
std::make_unique<NativeClientChannelDNSResolverFactory>());
|
||||
}
|
||||
|
||||
} // namespace grpc_core
|
||||
24
Pods/gRPC-Core/src/core/resolver/dns/native/dns_resolver.h
generated
Normal file
24
Pods/gRPC-Core/src/core/resolver/dns/native/dns_resolver.h
generated
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright 2022 The gRPC Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#ifndef GRPC_SRC_CORE_RESOLVER_DNS_NATIVE_DNS_RESOLVER_H
|
||||
#define GRPC_SRC_CORE_RESOLVER_DNS_NATIVE_DNS_RESOLVER_H
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include "src/core/lib/config/core_configuration.h"
|
||||
|
||||
namespace grpc_core {
|
||||
void RegisterNativeDnsResolver(CoreConfiguration::Builder* builder);
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_SRC_CORE_RESOLVER_DNS_NATIVE_DNS_RESOLVER_H
|
||||
147
Pods/gRPC-Core/src/core/resolver/endpoint_addresses.cc
generated
Normal file
147
Pods/gRPC-Core/src/core/resolver/endpoint_addresses.cc
generated
Normal file
@@ -0,0 +1,147 @@
|
||||
//
|
||||
//
|
||||
// Copyright 2018 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
//
|
||||
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include "src/core/resolver/endpoint_addresses.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/str_join.h"
|
||||
|
||||
#include <grpc/support/log.h>
|
||||
|
||||
#include "src/core/lib/address_utils/sockaddr_utils.h"
|
||||
#include "src/core/lib/channel/channel_args.h"
|
||||
#include "src/core/lib/gpr/useful.h"
|
||||
|
||||
// IWYU pragma: no_include <sys/socket.h>
|
||||
|
||||
namespace grpc_core {
|
||||
|
||||
EndpointAddresses::EndpointAddresses(const grpc_resolved_address& address,
|
||||
const ChannelArgs& args)
|
||||
: addresses_(1, address), args_(args) {}
|
||||
|
||||
EndpointAddresses::EndpointAddresses(
|
||||
std::vector<grpc_resolved_address> addresses, const ChannelArgs& args)
|
||||
: addresses_(std::move(addresses)), args_(args) {
|
||||
GPR_ASSERT(!addresses_.empty());
|
||||
}
|
||||
|
||||
EndpointAddresses::EndpointAddresses(const EndpointAddresses& other)
|
||||
: addresses_(other.addresses_), args_(other.args_) {}
|
||||
|
||||
EndpointAddresses& EndpointAddresses::operator=(
|
||||
const EndpointAddresses& other) {
|
||||
if (&other == this) return *this;
|
||||
addresses_ = other.addresses_;
|
||||
args_ = other.args_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
EndpointAddresses::EndpointAddresses(EndpointAddresses&& other) noexcept
|
||||
: addresses_(std::move(other.addresses_)), args_(std::move(other.args_)) {}
|
||||
|
||||
EndpointAddresses& EndpointAddresses::operator=(
|
||||
EndpointAddresses&& other) noexcept {
|
||||
addresses_ = std::move(other.addresses_);
|
||||
args_ = std::move(other.args_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
int EndpointAddresses::Cmp(const EndpointAddresses& other) const {
|
||||
for (size_t i = 0; i < addresses_.size(); ++i) {
|
||||
if (other.addresses_.size() == i) return 1;
|
||||
if (addresses_[i].len > other.addresses_[i].len) return 1;
|
||||
if (addresses_[i].len < other.addresses_[i].len) return -1;
|
||||
int retval =
|
||||
memcmp(addresses_[i].addr, other.addresses_[i].addr, addresses_[i].len);
|
||||
if (retval != 0) return retval;
|
||||
}
|
||||
if (other.addresses_.size() > addresses_.size()) return -1;
|
||||
return QsortCompare(args_, other.args_);
|
||||
}
|
||||
|
||||
std::string EndpointAddresses::ToString() const {
|
||||
std::vector<std::string> addr_strings;
|
||||
for (const auto& address : addresses_) {
|
||||
auto addr_str = grpc_sockaddr_to_string(&address, false);
|
||||
addr_strings.push_back(addr_str.ok() ? std::move(*addr_str)
|
||||
: addr_str.status().ToString());
|
||||
}
|
||||
std::vector<std::string> parts = {
|
||||
absl::StrCat("addrs=[", absl::StrJoin(addr_strings, ", "), "]")};
|
||||
if (args_ != ChannelArgs()) {
|
||||
parts.emplace_back(absl::StrCat("args=", args_.ToString()));
|
||||
}
|
||||
return absl::StrJoin(parts, " ");
|
||||
}
|
||||
|
||||
bool ResolvedAddressLessThan::operator()(
|
||||
const grpc_resolved_address& addr1,
|
||||
const grpc_resolved_address& addr2) const {
|
||||
if (addr1.len < addr2.len) return true;
|
||||
return memcmp(addr1.addr, addr2.addr, addr1.len) < 0;
|
||||
}
|
||||
|
||||
bool EndpointAddressSet::operator==(const EndpointAddressSet& other) const {
|
||||
if (addresses_.size() != other.addresses_.size()) return false;
|
||||
auto other_it = other.addresses_.begin();
|
||||
for (auto it = addresses_.begin(); it != addresses_.end(); ++it) {
|
||||
GPR_ASSERT(other_it != other.addresses_.end());
|
||||
if (it->len != other_it->len ||
|
||||
memcmp(it->addr, other_it->addr, it->len) != 0) {
|
||||
return false;
|
||||
}
|
||||
++other_it;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EndpointAddressSet::operator<(const EndpointAddressSet& other) const {
|
||||
auto other_it = other.addresses_.begin();
|
||||
for (auto it = addresses_.begin(); it != addresses_.end(); ++it) {
|
||||
if (other_it == other.addresses_.end()) return false;
|
||||
if (it->len < other_it->len) return true;
|
||||
if (it->len > other_it->len) return false;
|
||||
int r = memcmp(it->addr, other_it->addr, it->len);
|
||||
if (r != 0) return r < 0;
|
||||
++other_it;
|
||||
}
|
||||
return other_it != other.addresses_.end();
|
||||
}
|
||||
|
||||
std::string EndpointAddressSet::ToString() const {
|
||||
std::vector<std::string> parts;
|
||||
parts.reserve(addresses_.size());
|
||||
for (const auto& address : addresses_) {
|
||||
parts.push_back(
|
||||
grpc_sockaddr_to_string(&address, false).value_or("<unknown>"));
|
||||
}
|
||||
return absl::StrCat("{", absl::StrJoin(parts, ", "), "}");
|
||||
}
|
||||
|
||||
} // namespace grpc_core
|
||||
164
Pods/gRPC-Core/src/core/resolver/endpoint_addresses.h
generated
Normal file
164
Pods/gRPC-Core/src/core/resolver/endpoint_addresses.h
generated
Normal file
@@ -0,0 +1,164 @@
|
||||
//
|
||||
//
|
||||
// Copyright 2018 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
//
|
||||
|
||||
#ifndef GRPC_SRC_CORE_RESOLVER_ENDPOINT_ADDRESSES_H
|
||||
#define GRPC_SRC_CORE_RESOLVER_ENDPOINT_ADDRESSES_H
|
||||
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/functional/function_ref.h"
|
||||
|
||||
#include "src/core/lib/channel/channel_args.h"
|
||||
#include "src/core/lib/iomgr/resolved_address.h"
|
||||
|
||||
// A channel arg key prefix used for args that are intended to be used
|
||||
// only internally to resolvers and LB policies and should not be part
|
||||
// of the subchannel key. The channel will automatically filter out any
|
||||
// args with this prefix from the subchannel's args.
|
||||
#define GRPC_ARG_NO_SUBCHANNEL_PREFIX "grpc.internal.no_subchannel."
|
||||
|
||||
// A channel arg indicating the weight of an address.
|
||||
#define GRPC_ARG_ADDRESS_WEIGHT GRPC_ARG_NO_SUBCHANNEL_PREFIX "address.weight"
|
||||
|
||||
namespace grpc_core {
|
||||
|
||||
// A list of addresses for a given endpoint with an associated set of channel
|
||||
// args. Any args present here will be merged into the channel args when a
|
||||
// subchannel is created for each address.
|
||||
class EndpointAddresses {
|
||||
public:
|
||||
// For backward compatibility.
|
||||
// TODO(roth): Remove when callers have been updated.
|
||||
EndpointAddresses(const grpc_resolved_address& address,
|
||||
const ChannelArgs& args);
|
||||
|
||||
// addresses must not be empty.
|
||||
EndpointAddresses(std::vector<grpc_resolved_address> addresses,
|
||||
const ChannelArgs& args);
|
||||
|
||||
// Copyable.
|
||||
EndpointAddresses(const EndpointAddresses& other);
|
||||
EndpointAddresses& operator=(const EndpointAddresses& other);
|
||||
|
||||
// Movable.
|
||||
EndpointAddresses(EndpointAddresses&& other) noexcept;
|
||||
EndpointAddresses& operator=(EndpointAddresses&& other) noexcept;
|
||||
|
||||
bool operator==(const EndpointAddresses& other) const {
|
||||
return Cmp(other) == 0;
|
||||
}
|
||||
bool operator!=(const EndpointAddresses& other) const {
|
||||
return Cmp(other) != 0;
|
||||
}
|
||||
bool operator<(const EndpointAddresses& other) const {
|
||||
return Cmp(other) < 0;
|
||||
}
|
||||
|
||||
int Cmp(const EndpointAddresses& other) const;
|
||||
|
||||
// For backward compatibility only.
|
||||
// TODO(roth): Remove when all callers have been updated.
|
||||
const grpc_resolved_address& address() const { return addresses_[0]; }
|
||||
|
||||
const std::vector<grpc_resolved_address>& addresses() const {
|
||||
return addresses_;
|
||||
}
|
||||
const ChannelArgs& args() const { return args_; }
|
||||
|
||||
// TODO(ctiller): Prior to making this a public API we should ensure that the
|
||||
// channel args are not part of the generated string, lest we make that debug
|
||||
// format load-bearing via Hyrum's law.
|
||||
std::string ToString() const;
|
||||
|
||||
private:
|
||||
std::vector<grpc_resolved_address> addresses_;
|
||||
ChannelArgs args_;
|
||||
};
|
||||
|
||||
using EndpointAddressesList = std::vector<EndpointAddresses>;
|
||||
|
||||
struct ResolvedAddressLessThan {
|
||||
bool operator()(const grpc_resolved_address& addr1,
|
||||
const grpc_resolved_address& addr2) const;
|
||||
};
|
||||
|
||||
class EndpointAddressSet {
|
||||
public:
|
||||
explicit EndpointAddressSet(
|
||||
const std::vector<grpc_resolved_address>& addresses)
|
||||
: addresses_(addresses.begin(), addresses.end()) {}
|
||||
|
||||
bool operator==(const EndpointAddressSet& other) const;
|
||||
bool operator<(const EndpointAddressSet& other) const;
|
||||
|
||||
std::string ToString() const;
|
||||
|
||||
private:
|
||||
std::set<grpc_resolved_address, ResolvedAddressLessThan> addresses_;
|
||||
};
|
||||
|
||||
// An iterator interface for endpoints.
|
||||
class EndpointAddressesIterator {
|
||||
public:
|
||||
virtual ~EndpointAddressesIterator() = default;
|
||||
|
||||
// Invokes callback once for each endpoint.
|
||||
virtual void ForEach(
|
||||
absl::FunctionRef<void(const EndpointAddresses&)> callback) const = 0;
|
||||
};
|
||||
|
||||
// Iterator over a fixed list of endpoints.
|
||||
class EndpointAddressesListIterator : public EndpointAddressesIterator {
|
||||
public:
|
||||
explicit EndpointAddressesListIterator(EndpointAddressesList endpoints)
|
||||
: endpoints_(std::move(endpoints)) {}
|
||||
|
||||
void ForEach(absl::FunctionRef<void(const EndpointAddresses&)> callback)
|
||||
const override {
|
||||
for (const auto& endpoint : endpoints_) {
|
||||
callback(endpoint);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
EndpointAddressesList endpoints_;
|
||||
};
|
||||
|
||||
// Iterator that returns only a single endpoint.
|
||||
class SingleEndpointIterator : public EndpointAddressesIterator {
|
||||
public:
|
||||
explicit SingleEndpointIterator(EndpointAddresses endpoint)
|
||||
: endpoint_(std::move(endpoint)) {}
|
||||
|
||||
void ForEach(absl::FunctionRef<void(const EndpointAddresses&)> callback)
|
||||
const override {
|
||||
callback(endpoint_);
|
||||
}
|
||||
|
||||
private:
|
||||
EndpointAddresses endpoint_;
|
||||
};
|
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_SRC_CORE_RESOLVER_ENDPOINT_ADDRESSES_H
|
||||
255
Pods/gRPC-Core/src/core/resolver/fake/fake_resolver.cc
generated
Normal file
255
Pods/gRPC-Core/src/core/resolver/fake/fake_resolver.cc
generated
Normal file
@@ -0,0 +1,255 @@
|
||||
//
|
||||
// Copyright 2016 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
// This is similar to the sockaddr resolver, except that it supports a
|
||||
// bunch of query args that are useful for dependency injection in tests.
|
||||
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include "src/core/resolver/fake/fake_resolver.h"
|
||||
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
#include <grpc/support/log.h>
|
||||
|
||||
#include "src/core/lib/channel/channel_args.h"
|
||||
#include "src/core/lib/config/core_configuration.h"
|
||||
#include "src/core/lib/gpr/useful.h"
|
||||
#include "src/core/lib/gprpp/debug_location.h"
|
||||
#include "src/core/lib/gprpp/orphanable.h"
|
||||
#include "src/core/lib/gprpp/work_serializer.h"
|
||||
#include "src/core/resolver/resolver_factory.h"
|
||||
#include "src/core/lib/uri/uri_parser.h"
|
||||
|
||||
namespace grpc_core {
|
||||
|
||||
// This cannot be in an anonymous namespace, because it is a friend of
|
||||
// FakeResolverResponseGenerator.
|
||||
class FakeResolver : public Resolver {
|
||||
public:
|
||||
explicit FakeResolver(ResolverArgs args);
|
||||
|
||||
void StartLocked() override;
|
||||
|
||||
void RequestReresolutionLocked() override;
|
||||
|
||||
private:
|
||||
friend class FakeResolverResponseGenerator;
|
||||
|
||||
void ShutdownLocked() override;
|
||||
|
||||
void MaybeSendResultLocked();
|
||||
|
||||
// passed-in parameters
|
||||
std::shared_ptr<WorkSerializer> work_serializer_;
|
||||
std::unique_ptr<ResultHandler> result_handler_;
|
||||
ChannelArgs channel_args_;
|
||||
RefCountedPtr<FakeResolverResponseGenerator> response_generator_;
|
||||
// The next resolution result to be returned, if any. Present when we
|
||||
// get a result before the resolver is started.
|
||||
absl::optional<Result> next_result_;
|
||||
// True after the call to StartLocked().
|
||||
bool started_ = false;
|
||||
// True after the call to ShutdownLocked().
|
||||
bool shutdown_ = false;
|
||||
};
|
||||
|
||||
FakeResolver::FakeResolver(ResolverArgs args)
|
||||
: work_serializer_(std::move(args.work_serializer)),
|
||||
result_handler_(std::move(args.result_handler)),
|
||||
channel_args_(
|
||||
// Channels sharing the same subchannels may have different resolver
|
||||
// response generators. If we don't remove this arg, subchannel pool
|
||||
// will create new subchannels for the same address instead of
|
||||
// reusing existing ones because of different values of this channel
|
||||
// arg. Can't just use GRPC_ARG_NO_SUBCHANNEL_PREFIX, since
|
||||
// that can't be passed into the channel from test code.
|
||||
args.args.Remove(GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR)),
|
||||
response_generator_(
|
||||
args.args.GetObjectRef<FakeResolverResponseGenerator>()) {
|
||||
if (response_generator_ != nullptr) {
|
||||
response_generator_->SetFakeResolver(RefAsSubclass<FakeResolver>());
|
||||
}
|
||||
}
|
||||
|
||||
void FakeResolver::StartLocked() {
|
||||
started_ = true;
|
||||
MaybeSendResultLocked();
|
||||
}
|
||||
|
||||
void FakeResolver::RequestReresolutionLocked() {
|
||||
// Re-resolution can't happen until after we return an initial result.
|
||||
GPR_ASSERT(response_generator_ != nullptr);
|
||||
response_generator_->ReresolutionRequested();
|
||||
}
|
||||
|
||||
void FakeResolver::ShutdownLocked() {
|
||||
shutdown_ = true;
|
||||
if (response_generator_ != nullptr) {
|
||||
response_generator_->SetFakeResolver(nullptr);
|
||||
response_generator_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void FakeResolver::MaybeSendResultLocked() {
|
||||
if (!started_ || shutdown_) return;
|
||||
if (next_result_.has_value()) {
|
||||
// When both next_results_ and channel_args_ contain an arg with the same
|
||||
// name, use the one in next_results_.
|
||||
next_result_->args = next_result_->args.UnionWith(channel_args_);
|
||||
result_handler_->ReportResult(std::move(*next_result_));
|
||||
next_result_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// FakeResolverResponseGenerator
|
||||
//
|
||||
|
||||
FakeResolverResponseGenerator::FakeResolverResponseGenerator() {}
|
||||
|
||||
FakeResolverResponseGenerator::~FakeResolverResponseGenerator() {}
|
||||
|
||||
void FakeResolverResponseGenerator::SetResponseAndNotify(
|
||||
Resolver::Result result, Notification* notify_when_set) {
|
||||
RefCountedPtr<FakeResolver> resolver;
|
||||
{
|
||||
MutexLock lock(&mu_);
|
||||
if (resolver_ == nullptr) {
|
||||
result_ = std::move(result);
|
||||
if (notify_when_set != nullptr) notify_when_set->Notify();
|
||||
return;
|
||||
}
|
||||
resolver = resolver_;
|
||||
}
|
||||
SendResultToResolver(std::move(resolver), std::move(result), notify_when_set);
|
||||
}
|
||||
|
||||
void FakeResolverResponseGenerator::SetFakeResolver(
|
||||
RefCountedPtr<FakeResolver> resolver) {
|
||||
Resolver::Result result;
|
||||
{
|
||||
MutexLock lock(&mu_);
|
||||
resolver_ = resolver;
|
||||
if (resolver_set_cv_ != nullptr) resolver_set_cv_->SignalAll();
|
||||
if (resolver == nullptr) return;
|
||||
if (!result_.has_value()) return;
|
||||
result = std::move(*result_);
|
||||
result_.reset();
|
||||
}
|
||||
SendResultToResolver(std::move(resolver), std::move(result), nullptr);
|
||||
}
|
||||
|
||||
void FakeResolverResponseGenerator::SendResultToResolver(
|
||||
RefCountedPtr<FakeResolver> resolver, Resolver::Result result,
|
||||
Notification* notify_when_set) {
|
||||
auto* resolver_ptr = resolver.get();
|
||||
resolver_ptr->work_serializer_->Run(
|
||||
[resolver = std::move(resolver), result = std::move(result),
|
||||
notify_when_set]() mutable {
|
||||
if (!resolver->shutdown_) {
|
||||
resolver->next_result_ = std::move(result);
|
||||
resolver->MaybeSendResultLocked();
|
||||
}
|
||||
if (notify_when_set != nullptr) notify_when_set->Notify();
|
||||
},
|
||||
DEBUG_LOCATION);
|
||||
}
|
||||
|
||||
bool FakeResolverResponseGenerator::WaitForResolverSet(absl::Duration timeout) {
|
||||
MutexLock lock(&mu_);
|
||||
if (resolver_ == nullptr) {
|
||||
CondVar condition;
|
||||
resolver_set_cv_ = &condition;
|
||||
condition.WaitWithTimeout(&mu_, timeout);
|
||||
resolver_set_cv_ = nullptr;
|
||||
}
|
||||
return resolver_ != nullptr;
|
||||
}
|
||||
|
||||
bool FakeResolverResponseGenerator::WaitForReresolutionRequest(
|
||||
absl::Duration timeout) {
|
||||
MutexLock lock(&reresolution_mu_);
|
||||
if (!reresolution_requested_) {
|
||||
CondVar condition;
|
||||
reresolution_cv_ = &condition;
|
||||
condition.WaitWithTimeout(&reresolution_mu_, timeout);
|
||||
reresolution_cv_ = nullptr;
|
||||
}
|
||||
return std::exchange(reresolution_requested_, false);
|
||||
}
|
||||
|
||||
void FakeResolverResponseGenerator::ReresolutionRequested() {
|
||||
MutexLock lock(&reresolution_mu_);
|
||||
reresolution_requested_ = true;
|
||||
if (reresolution_cv_ != nullptr) reresolution_cv_->SignalAll();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
void* ResponseGeneratorChannelArgCopy(void* p) {
|
||||
auto* generator = static_cast<FakeResolverResponseGenerator*>(p);
|
||||
generator->Ref().release();
|
||||
return p;
|
||||
}
|
||||
|
||||
void ResponseGeneratorChannelArgDestroy(void* p) {
|
||||
auto* generator = static_cast<FakeResolverResponseGenerator*>(p);
|
||||
generator->Unref();
|
||||
}
|
||||
|
||||
int ResponseGeneratorChannelArgCmp(void* a, void* b) {
|
||||
return QsortCompare(a, b);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const grpc_arg_pointer_vtable
|
||||
FakeResolverResponseGenerator::kChannelArgPointerVtable = {
|
||||
ResponseGeneratorChannelArgCopy, ResponseGeneratorChannelArgDestroy,
|
||||
ResponseGeneratorChannelArgCmp};
|
||||
|
||||
//
|
||||
// Factory
|
||||
//
|
||||
|
||||
namespace {
|
||||
|
||||
class FakeResolverFactory : public ResolverFactory {
|
||||
public:
|
||||
absl::string_view scheme() const override { return "fake"; }
|
||||
|
||||
bool IsValidUri(const URI& /*uri*/) const override { return true; }
|
||||
|
||||
OrphanablePtr<Resolver> CreateResolver(ResolverArgs args) const override {
|
||||
return MakeOrphanable<FakeResolver>(std::move(args));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void RegisterFakeResolver(CoreConfiguration::Builder* builder) {
|
||||
builder->resolver_registry()->RegisterResolverFactory(
|
||||
std::make_unique<FakeResolverFactory>());
|
||||
}
|
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
void grpc_resolver_fake_shutdown() {}
|
||||
129
Pods/gRPC-Core/src/core/resolver/fake/fake_resolver.h
generated
Normal file
129
Pods/gRPC-Core/src/core/resolver/fake/fake_resolver.h
generated
Normal file
@@ -0,0 +1,129 @@
|
||||
//
|
||||
// Copyright 2016 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#ifndef GRPC_SRC_CORE_RESOLVER_FAKE_FAKE_RESOLVER_H
|
||||
#define GRPC_SRC_CORE_RESOLVER_FAKE_FAKE_RESOLVER_H
|
||||
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "absl/base/thread_annotations.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/time/time.h"
|
||||
#include "absl/types/optional.h"
|
||||
|
||||
#include <grpc/grpc.h>
|
||||
|
||||
#include "src/core/lib/gpr/useful.h"
|
||||
#include "src/core/lib/gprpp/notification.h"
|
||||
#include "src/core/lib/gprpp/ref_counted.h"
|
||||
#include "src/core/lib/gprpp/ref_counted_ptr.h"
|
||||
#include "src/core/lib/gprpp/sync.h"
|
||||
#include "src/core/resolver/resolver.h"
|
||||
|
||||
#define GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR \
|
||||
"grpc.fake_resolver.response_generator"
|
||||
|
||||
namespace grpc_core {
|
||||
|
||||
class FakeResolver;
|
||||
|
||||
/// A mechanism for generating responses for the fake resolver.
|
||||
/// An instance of this class is passed to the fake resolver via a channel
|
||||
/// argument and used to inject and trigger custom resolutions.
|
||||
// TODO(roth): I would ideally like this to be InternallyRefCounted
|
||||
// instead of RefCounted, but external refs are currently needed to
|
||||
// encode this in channel args. Once channel_args are converted to C++,
|
||||
// see if we can find a way to fix this.
|
||||
class FakeResolverResponseGenerator
|
||||
: public RefCounted<FakeResolverResponseGenerator> {
|
||||
public:
|
||||
static const grpc_arg_pointer_vtable kChannelArgPointerVtable;
|
||||
|
||||
FakeResolverResponseGenerator();
|
||||
~FakeResolverResponseGenerator() override;
|
||||
|
||||
// Instructs the fake resolver associated with the response generator
|
||||
// instance to trigger a new resolution with the specified result. If the
|
||||
// resolver is not available yet, delays response setting until it is. This
|
||||
// can be called at most once before the resolver is available.
|
||||
// notify_when_set is an optional notification to signal when the response has
|
||||
// been set.
|
||||
void SetResponseAndNotify(Resolver::Result result,
|
||||
Notification* notify_when_set);
|
||||
|
||||
// Same as SetResponseAndNotify(), assume that async setting is fine
|
||||
void SetResponseAsync(Resolver::Result result) {
|
||||
SetResponseAndNotify(std::move(result), nullptr);
|
||||
}
|
||||
|
||||
// Same as SetResponseAndNotify(), but create and wait for the notification
|
||||
void SetResponseSynchronously(Resolver::Result result) {
|
||||
Notification n;
|
||||
SetResponseAndNotify(std::move(result), &n);
|
||||
n.WaitForNotification();
|
||||
}
|
||||
|
||||
// Waits up to timeout for a re-resolution request. Returns true if a
|
||||
// re-resolution request is seen, or false if timeout occurs. Returns
|
||||
// true immediately if there was a re-resolution request since the
|
||||
// last time this method was called.
|
||||
bool WaitForReresolutionRequest(absl::Duration timeout);
|
||||
|
||||
// Wait for a resolver to be set (setting may be happening asynchronously, so
|
||||
// this may block - consider it test only).
|
||||
bool WaitForResolverSet(absl::Duration timeout);
|
||||
|
||||
static absl::string_view ChannelArgName() {
|
||||
return GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR;
|
||||
}
|
||||
|
||||
static int ChannelArgsCompare(const FakeResolverResponseGenerator* a,
|
||||
const FakeResolverResponseGenerator* b) {
|
||||
return QsortCompare(a, b);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class FakeResolver;
|
||||
|
||||
// Set the corresponding FakeResolver to this generator.
|
||||
void SetFakeResolver(RefCountedPtr<FakeResolver> resolver);
|
||||
|
||||
// Called by FakeResolver when re-resolution is requested.
|
||||
void ReresolutionRequested();
|
||||
|
||||
// Helper function to send a result to the resolver.
|
||||
static void SendResultToResolver(RefCountedPtr<FakeResolver> resolver,
|
||||
Resolver::Result result,
|
||||
Notification* notify_when_set);
|
||||
|
||||
// Mutex protecting the members below.
|
||||
Mutex mu_;
|
||||
CondVar* resolver_set_cv_ ABSL_GUARDED_BY(mu_) = nullptr;
|
||||
RefCountedPtr<FakeResolver> resolver_ ABSL_GUARDED_BY(mu_);
|
||||
// Temporarily stores the result when it gets set before the response
|
||||
// generator is seen by the FakeResolver.
|
||||
absl::optional<Resolver::Result> result_ ABSL_GUARDED_BY(mu_);
|
||||
|
||||
Mutex reresolution_mu_;
|
||||
CondVar* reresolution_cv_ ABSL_GUARDED_BY(reresolution_mu_) = nullptr;
|
||||
bool reresolution_requested_ ABSL_GUARDED_BY(reresolution_mu_) = false;
|
||||
};
|
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_SRC_CORE_RESOLVER_FAKE_FAKE_RESOLVER_H
|
||||
326
Pods/gRPC-Core/src/core/resolver/google_c2p/google_c2p_resolver.cc
generated
Normal file
326
Pods/gRPC-Core/src/core/resolver/google_c2p/google_c2p_resolver.cc
generated
Normal file
@@ -0,0 +1,326 @@
|
||||
//
|
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/strings/strip.h"
|
||||
#include "absl/types/optional.h"
|
||||
|
||||
#include <grpc/support/json.h>
|
||||
#include <grpc/support/log.h>
|
||||
|
||||
#include "src/core/ext/gcp/metadata_query.h"
|
||||
#include "src/core/ext/xds/xds_bootstrap.h"
|
||||
#include "src/core/ext/xds/xds_client_grpc.h"
|
||||
#include "src/core/lib/channel/channel_args.h"
|
||||
#include "src/core/lib/config/core_configuration.h"
|
||||
#include "src/core/lib/gprpp/debug_location.h"
|
||||
#include "src/core/lib/gprpp/env.h"
|
||||
#include "src/core/lib/gprpp/orphanable.h"
|
||||
#include "src/core/lib/gprpp/ref_counted_ptr.h"
|
||||
#include "src/core/lib/gprpp/time.h"
|
||||
#include "src/core/lib/gprpp/work_serializer.h"
|
||||
#include "src/core/lib/iomgr/polling_entity.h"
|
||||
#include "src/core/lib/json/json.h"
|
||||
#include "src/core/lib/json/json_writer.h"
|
||||
#include "src/core/resolver/resolver.h"
|
||||
#include "src/core/resolver/resolver_factory.h"
|
||||
#include "src/core/resolver/resolver_registry.h"
|
||||
#include "src/core/lib/resource_quota/resource_quota.h"
|
||||
#include "src/core/lib/security/credentials/alts/check_gcp_environment.h"
|
||||
#include "src/core/lib/uri/uri_parser.h"
|
||||
|
||||
namespace grpc_core {
|
||||
|
||||
namespace {
|
||||
|
||||
const char* kC2PAuthority = "traffic-director-c2p.xds.googleapis.com";
|
||||
|
||||
class GoogleCloud2ProdResolver : public Resolver {
|
||||
public:
|
||||
explicit GoogleCloud2ProdResolver(ResolverArgs args);
|
||||
|
||||
void StartLocked() override;
|
||||
void RequestReresolutionLocked() override;
|
||||
void ResetBackoffLocked() override;
|
||||
void ShutdownLocked() override;
|
||||
|
||||
private:
|
||||
void ZoneQueryDone(std::string zone);
|
||||
void IPv6QueryDone(bool ipv6_supported);
|
||||
void StartXdsResolver();
|
||||
|
||||
ResourceQuotaRefPtr resource_quota_;
|
||||
std::shared_ptr<WorkSerializer> work_serializer_;
|
||||
grpc_polling_entity pollent_;
|
||||
bool using_dns_ = false;
|
||||
OrphanablePtr<Resolver> child_resolver_;
|
||||
std::string metadata_server_name_ = "metadata.google.internal.";
|
||||
bool shutdown_ = false;
|
||||
|
||||
OrphanablePtr<MetadataQuery> zone_query_;
|
||||
absl::optional<std::string> zone_;
|
||||
|
||||
OrphanablePtr<MetadataQuery> ipv6_query_;
|
||||
absl::optional<bool> supports_ipv6_;
|
||||
};
|
||||
|
||||
//
|
||||
// GoogleCloud2ProdResolver
|
||||
//
|
||||
|
||||
bool XdsBootstrapConfigured() {
|
||||
return GetEnv("GRPC_XDS_BOOTSTRAP").has_value() ||
|
||||
GetEnv("GRPC_XDS_BOOTSTRAP_CONFIG").has_value();
|
||||
}
|
||||
|
||||
GoogleCloud2ProdResolver::GoogleCloud2ProdResolver(ResolverArgs args)
|
||||
: resource_quota_(args.args.GetObjectRef<ResourceQuota>()),
|
||||
work_serializer_(std::move(args.work_serializer)),
|
||||
pollent_(grpc_polling_entity_create_from_pollset_set(args.pollset_set)) {
|
||||
absl::string_view name_to_resolve = absl::StripPrefix(args.uri.path(), "/");
|
||||
// If we're not running on GCP, we can't use DirectPath, so delegate
|
||||
// to the DNS resolver.
|
||||
const bool test_only_pretend_running_on_gcp =
|
||||
args.args
|
||||
.GetBool("grpc.testing.google_c2p_resolver_pretend_running_on_gcp")
|
||||
.value_or(false);
|
||||
const bool running_on_gcp =
|
||||
test_only_pretend_running_on_gcp || grpc_alts_is_running_on_gcp();
|
||||
const bool federation_enabled = XdsFederationEnabled();
|
||||
if (!running_on_gcp ||
|
||||
// If the client is already using xDS and federation is not enabled,
|
||||
// we can't use it here, because they may be talking to a completely
|
||||
// different xDS server than we want to.
|
||||
// TODO(roth): When we remove xDS federation env var protection,
|
||||
// remove this constraint.
|
||||
(!federation_enabled && XdsBootstrapConfigured())) {
|
||||
using_dns_ = true;
|
||||
child_resolver_ =
|
||||
CoreConfiguration::Get().resolver_registry().CreateResolver(
|
||||
absl::StrCat("dns:", name_to_resolve), args.args, args.pollset_set,
|
||||
work_serializer_, std::move(args.result_handler));
|
||||
GPR_ASSERT(child_resolver_ != nullptr);
|
||||
return;
|
||||
}
|
||||
// Maybe override metadata server name for testing
|
||||
absl::optional<std::string> test_only_metadata_server_override =
|
||||
args.args.GetOwnedString(
|
||||
"grpc.testing.google_c2p_resolver_metadata_server_override");
|
||||
if (test_only_metadata_server_override.has_value() &&
|
||||
!test_only_metadata_server_override->empty()) {
|
||||
metadata_server_name_ = std::move(*test_only_metadata_server_override);
|
||||
}
|
||||
// Create xds resolver.
|
||||
std::string xds_uri =
|
||||
federation_enabled
|
||||
? absl::StrCat("xds://", kC2PAuthority, "/", name_to_resolve)
|
||||
: absl::StrCat("xds:", name_to_resolve);
|
||||
child_resolver_ = CoreConfiguration::Get().resolver_registry().CreateResolver(
|
||||
xds_uri, args.args, args.pollset_set, work_serializer_,
|
||||
std::move(args.result_handler));
|
||||
GPR_ASSERT(child_resolver_ != nullptr);
|
||||
}
|
||||
|
||||
void GoogleCloud2ProdResolver::StartLocked() {
|
||||
if (using_dns_) {
|
||||
child_resolver_->StartLocked();
|
||||
return;
|
||||
}
|
||||
// Using xDS. Start metadata server queries.
|
||||
zone_query_ = MakeOrphanable<MetadataQuery>(
|
||||
metadata_server_name_, std::string(MetadataQuery::kZoneAttribute),
|
||||
&pollent_,
|
||||
[resolver = RefAsSubclass<GoogleCloud2ProdResolver>()](
|
||||
std::string /* attribute */,
|
||||
absl::StatusOr<std::string> result) mutable {
|
||||
resolver->work_serializer_->Run(
|
||||
[resolver, result = std::move(result)]() mutable {
|
||||
resolver->ZoneQueryDone(result.ok() ? std::move(result).value()
|
||||
: "");
|
||||
},
|
||||
DEBUG_LOCATION);
|
||||
},
|
||||
Duration::Seconds(10));
|
||||
ipv6_query_ = MakeOrphanable<MetadataQuery>(
|
||||
metadata_server_name_, std::string(MetadataQuery::kIPv6Attribute),
|
||||
&pollent_,
|
||||
[resolver = RefAsSubclass<GoogleCloud2ProdResolver>()](
|
||||
std::string /* attribute */,
|
||||
absl::StatusOr<std::string> result) mutable {
|
||||
resolver->work_serializer_->Run(
|
||||
[resolver, result = std::move(result)]() {
|
||||
resolver->IPv6QueryDone(result.ok());
|
||||
},
|
||||
DEBUG_LOCATION);
|
||||
},
|
||||
Duration::Seconds(10));
|
||||
}
|
||||
|
||||
void GoogleCloud2ProdResolver::RequestReresolutionLocked() {
|
||||
if (child_resolver_ != nullptr) {
|
||||
child_resolver_->RequestReresolutionLocked();
|
||||
}
|
||||
}
|
||||
|
||||
void GoogleCloud2ProdResolver::ResetBackoffLocked() {
|
||||
if (child_resolver_ != nullptr) {
|
||||
child_resolver_->ResetBackoffLocked();
|
||||
}
|
||||
}
|
||||
|
||||
void GoogleCloud2ProdResolver::ShutdownLocked() {
|
||||
shutdown_ = true;
|
||||
zone_query_.reset();
|
||||
ipv6_query_.reset();
|
||||
child_resolver_.reset();
|
||||
}
|
||||
|
||||
void GoogleCloud2ProdResolver::ZoneQueryDone(std::string zone) {
|
||||
zone_query_.reset();
|
||||
zone_ = std::move(zone);
|
||||
if (supports_ipv6_.has_value()) StartXdsResolver();
|
||||
}
|
||||
|
||||
void GoogleCloud2ProdResolver::IPv6QueryDone(bool ipv6_supported) {
|
||||
ipv6_query_.reset();
|
||||
supports_ipv6_ = ipv6_supported;
|
||||
if (zone_.has_value()) StartXdsResolver();
|
||||
}
|
||||
|
||||
void GoogleCloud2ProdResolver::StartXdsResolver() {
|
||||
if (shutdown_) {
|
||||
return;
|
||||
}
|
||||
// Construct bootstrap JSON.
|
||||
std::random_device rd;
|
||||
std::mt19937 mt(rd());
|
||||
std::uniform_int_distribution<uint64_t> dist(1, UINT64_MAX);
|
||||
Json::Object node = {
|
||||
{"id", Json::FromString(absl::StrCat("C2P-", dist(mt)))},
|
||||
};
|
||||
if (!zone_->empty()) {
|
||||
node["locality"] = Json::FromObject({
|
||||
{"zone", Json::FromString(*zone_)},
|
||||
});
|
||||
};
|
||||
if (*supports_ipv6_) {
|
||||
node["metadata"] = Json::FromObject({
|
||||
{"TRAFFICDIRECTOR_DIRECTPATH_C2P_IPV6_CAPABLE", Json::FromBool(true)},
|
||||
});
|
||||
}
|
||||
// Allow the TD server uri to be overridden for testing purposes.
|
||||
auto override_server =
|
||||
GetEnv("GRPC_TEST_ONLY_GOOGLE_C2P_RESOLVER_TRAFFIC_DIRECTOR_URI");
|
||||
const char* server_uri =
|
||||
override_server.has_value() && !override_server->empty()
|
||||
? override_server->c_str()
|
||||
: "directpath-pa.googleapis.com";
|
||||
Json xds_server = Json::FromArray({
|
||||
Json::FromObject({
|
||||
{"server_uri", Json::FromString(server_uri)},
|
||||
{"channel_creds",
|
||||
Json::FromArray({
|
||||
Json::FromObject({
|
||||
{"type", Json::FromString("google_default")},
|
||||
}),
|
||||
})},
|
||||
{"server_features",
|
||||
Json::FromArray({Json::FromString("ignore_resource_deletion")})},
|
||||
}),
|
||||
});
|
||||
Json bootstrap = Json::FromObject({
|
||||
{"xds_servers", xds_server},
|
||||
{"authorities",
|
||||
Json::FromObject({
|
||||
{kC2PAuthority, Json::FromObject({
|
||||
{"xds_servers", std::move(xds_server)},
|
||||
})},
|
||||
})},
|
||||
{"node", Json::FromObject(std::move(node))},
|
||||
});
|
||||
// Inject bootstrap JSON as fallback config.
|
||||
internal::SetXdsFallbackBootstrapConfig(JsonDump(bootstrap).c_str());
|
||||
// Now start xDS resolver.
|
||||
child_resolver_->StartLocked();
|
||||
}
|
||||
|
||||
//
|
||||
// Factory
|
||||
//
|
||||
|
||||
class GoogleCloud2ProdResolverFactory : public ResolverFactory {
|
||||
public:
|
||||
absl::string_view scheme() const override { return "google-c2p"; }
|
||||
|
||||
bool IsValidUri(const URI& uri) const override {
|
||||
if (GPR_UNLIKELY(!uri.authority().empty())) {
|
||||
gpr_log(GPR_ERROR, "google-c2p URI scheme does not support authorities");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
OrphanablePtr<Resolver> CreateResolver(ResolverArgs args) const override {
|
||||
if (!IsValidUri(args.uri)) return nullptr;
|
||||
return MakeOrphanable<GoogleCloud2ProdResolver>(std::move(args));
|
||||
}
|
||||
};
|
||||
|
||||
// TODO(apolcyn): remove this class after user code has updated to the
|
||||
// stable "google-c2p" URI scheme.
|
||||
class ExperimentalGoogleCloud2ProdResolverFactory : public ResolverFactory {
|
||||
public:
|
||||
absl::string_view scheme() const override {
|
||||
return "google-c2p-experimental";
|
||||
}
|
||||
|
||||
bool IsValidUri(const URI& uri) const override {
|
||||
if (GPR_UNLIKELY(!uri.authority().empty())) {
|
||||
gpr_log(
|
||||
GPR_ERROR,
|
||||
"google-c2p-experimental URI scheme does not support authorities");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
OrphanablePtr<Resolver> CreateResolver(ResolverArgs args) const override {
|
||||
if (!IsValidUri(args.uri)) return nullptr;
|
||||
return MakeOrphanable<GoogleCloud2ProdResolver>(std::move(args));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void RegisterCloud2ProdResolver(CoreConfiguration::Builder* builder) {
|
||||
builder->resolver_registry()->RegisterResolverFactory(
|
||||
std::make_unique<GoogleCloud2ProdResolverFactory>());
|
||||
builder->resolver_registry()->RegisterResolverFactory(
|
||||
std::make_unique<ExperimentalGoogleCloud2ProdResolverFactory>());
|
||||
}
|
||||
|
||||
} // namespace grpc_core
|
||||
272
Pods/gRPC-Core/src/core/resolver/polling_resolver.cc
generated
Normal file
272
Pods/gRPC-Core/src/core/resolver/polling_resolver.cc
generated
Normal file
@@ -0,0 +1,272 @@
|
||||
//
|
||||
// Copyright 2015 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include "src/core/resolver/polling_resolver.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/strip.h"
|
||||
|
||||
#include <grpc/support/log.h>
|
||||
|
||||
#include "src/core/lib/backoff/backoff.h"
|
||||
#include "src/core/lib/channel/channel_args.h"
|
||||
#include "src/core/lib/gprpp/debug_location.h"
|
||||
#include "src/core/lib/gprpp/ref_counted_ptr.h"
|
||||
#include "src/core/lib/gprpp/work_serializer.h"
|
||||
#include "src/core/lib/iomgr/exec_ctx.h"
|
||||
#include "src/core/resolver/endpoint_addresses.h"
|
||||
#include "src/core/service_config/service_config.h"
|
||||
#include "src/core/lib/uri/uri_parser.h"
|
||||
|
||||
namespace grpc_core {
|
||||
|
||||
using ::grpc_event_engine::experimental::EventEngine;
|
||||
|
||||
PollingResolver::PollingResolver(ResolverArgs args,
|
||||
Duration min_time_between_resolutions,
|
||||
BackOff::Options backoff_options,
|
||||
TraceFlag* tracer)
|
||||
: authority_(args.uri.authority()),
|
||||
name_to_resolve_(absl::StripPrefix(args.uri.path(), "/")),
|
||||
channel_args_(std::move(args.args)),
|
||||
work_serializer_(std::move(args.work_serializer)),
|
||||
result_handler_(std::move(args.result_handler)),
|
||||
tracer_(tracer),
|
||||
interested_parties_(args.pollset_set),
|
||||
min_time_between_resolutions_(min_time_between_resolutions),
|
||||
backoff_(backoff_options) {
|
||||
if (GPR_UNLIKELY(tracer_ != nullptr && tracer_->enabled())) {
|
||||
gpr_log(GPR_INFO, "[polling resolver %p] created", this);
|
||||
}
|
||||
}
|
||||
|
||||
PollingResolver::~PollingResolver() {
|
||||
if (GPR_UNLIKELY(tracer_ != nullptr && tracer_->enabled())) {
|
||||
gpr_log(GPR_INFO, "[polling resolver %p] destroying", this);
|
||||
}
|
||||
}
|
||||
|
||||
void PollingResolver::StartLocked() { MaybeStartResolvingLocked(); }
|
||||
|
||||
void PollingResolver::RequestReresolutionLocked() {
|
||||
if (request_ == nullptr) {
|
||||
// If we're still waiting for a result-health callback from the last
|
||||
// result we reported, don't trigger the re-resolution until we get
|
||||
// that callback.
|
||||
if (result_status_state_ ==
|
||||
ResultStatusState::kResultHealthCallbackPending) {
|
||||
result_status_state_ =
|
||||
ResultStatusState::kReresolutionRequestedWhileCallbackWasPending;
|
||||
} else {
|
||||
MaybeStartResolvingLocked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PollingResolver::ResetBackoffLocked() {
|
||||
backoff_.Reset();
|
||||
if (next_resolution_timer_handle_.has_value()) {
|
||||
MaybeCancelNextResolutionTimer();
|
||||
StartResolvingLocked();
|
||||
}
|
||||
}
|
||||
|
||||
void PollingResolver::ShutdownLocked() {
|
||||
if (GPR_UNLIKELY(tracer_ != nullptr && tracer_->enabled())) {
|
||||
gpr_log(GPR_INFO, "[polling resolver %p] shutting down", this);
|
||||
}
|
||||
shutdown_ = true;
|
||||
MaybeCancelNextResolutionTimer();
|
||||
request_.reset();
|
||||
}
|
||||
|
||||
void PollingResolver::ScheduleNextResolutionTimer(const Duration& timeout) {
|
||||
next_resolution_timer_handle_ =
|
||||
channel_args_.GetObject<EventEngine>()->RunAfter(
|
||||
timeout, [self = RefAsSubclass<PollingResolver>()]() mutable {
|
||||
ApplicationCallbackExecCtx callback_exec_ctx;
|
||||
ExecCtx exec_ctx;
|
||||
auto* self_ptr = self.get();
|
||||
self_ptr->work_serializer_->Run(
|
||||
[self = std::move(self)]() { self->OnNextResolutionLocked(); },
|
||||
DEBUG_LOCATION);
|
||||
});
|
||||
}
|
||||
|
||||
void PollingResolver::OnNextResolutionLocked() {
|
||||
if (GPR_UNLIKELY(tracer_ != nullptr && tracer_->enabled())) {
|
||||
gpr_log(GPR_INFO,
|
||||
"[polling resolver %p] re-resolution timer fired: shutdown_=%d",
|
||||
this, shutdown_);
|
||||
}
|
||||
// If we haven't been cancelled nor shutdown, then start resolving.
|
||||
if (next_resolution_timer_handle_.has_value() && !shutdown_) {
|
||||
next_resolution_timer_handle_.reset();
|
||||
StartResolvingLocked();
|
||||
}
|
||||
}
|
||||
|
||||
void PollingResolver::MaybeCancelNextResolutionTimer() {
|
||||
if (next_resolution_timer_handle_.has_value()) {
|
||||
if (GPR_UNLIKELY(tracer_ != nullptr && tracer_->enabled())) {
|
||||
gpr_log(GPR_INFO, "[polling resolver %p] cancel re-resolution timer",
|
||||
this);
|
||||
}
|
||||
channel_args_.GetObject<EventEngine>()->Cancel(
|
||||
*next_resolution_timer_handle_);
|
||||
next_resolution_timer_handle_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void PollingResolver::OnRequestComplete(Result result) {
|
||||
Ref(DEBUG_LOCATION, "OnRequestComplete").release();
|
||||
work_serializer_->Run(
|
||||
[this, result]() mutable { OnRequestCompleteLocked(std::move(result)); },
|
||||
DEBUG_LOCATION);
|
||||
}
|
||||
|
||||
void PollingResolver::OnRequestCompleteLocked(Result result) {
|
||||
if (GPR_UNLIKELY(tracer_ != nullptr && tracer_->enabled())) {
|
||||
gpr_log(GPR_INFO, "[polling resolver %p] request complete", this);
|
||||
}
|
||||
request_.reset();
|
||||
if (!shutdown_) {
|
||||
if (GPR_UNLIKELY(tracer_ != nullptr && tracer_->enabled())) {
|
||||
gpr_log(GPR_INFO,
|
||||
"[polling resolver %p] returning result: "
|
||||
"addresses=%s, service_config=%s, resolution_note=%s",
|
||||
this,
|
||||
result.addresses.ok()
|
||||
? absl::StrCat("<", result.addresses->size(), " addresses>")
|
||||
.c_str()
|
||||
: result.addresses.status().ToString().c_str(),
|
||||
result.service_config.ok()
|
||||
? (*result.service_config == nullptr
|
||||
? "<null>"
|
||||
: std::string((*result.service_config)->json_string())
|
||||
.c_str())
|
||||
: result.service_config.status().ToString().c_str(),
|
||||
result.resolution_note.c_str());
|
||||
}
|
||||
GPR_ASSERT(result.result_health_callback == nullptr);
|
||||
result.result_health_callback =
|
||||
[self = RefAsSubclass<PollingResolver>(
|
||||
DEBUG_LOCATION, "result_health_callback")](absl::Status status) {
|
||||
self->GetResultStatus(std::move(status));
|
||||
};
|
||||
result_status_state_ = ResultStatusState::kResultHealthCallbackPending;
|
||||
result_handler_->ReportResult(std::move(result));
|
||||
}
|
||||
Unref(DEBUG_LOCATION, "OnRequestComplete");
|
||||
}
|
||||
|
||||
void PollingResolver::GetResultStatus(absl::Status status) {
|
||||
if (GPR_UNLIKELY(tracer_ != nullptr && tracer_->enabled())) {
|
||||
gpr_log(GPR_INFO, "[polling resolver %p] result status from channel: %s",
|
||||
this, status.ToString().c_str());
|
||||
}
|
||||
if (status.ok()) {
|
||||
// Reset backoff state so that we start from the beginning when the
|
||||
// next request gets triggered.
|
||||
backoff_.Reset();
|
||||
// If a re-resolution attempt was requested while the result-status
|
||||
// callback was pending, trigger a new request now.
|
||||
if (std::exchange(result_status_state_, ResultStatusState::kNone) ==
|
||||
ResultStatusState::kReresolutionRequestedWhileCallbackWasPending) {
|
||||
MaybeStartResolvingLocked();
|
||||
}
|
||||
} else {
|
||||
// Set up for retry.
|
||||
// InvalidateNow to avoid getting stuck re-initializing this timer
|
||||
// in a loop while draining the currently-held WorkSerializer.
|
||||
// Also see https://github.com/grpc/grpc/issues/26079.
|
||||
ExecCtx::Get()->InvalidateNow();
|
||||
const Timestamp next_try = backoff_.NextAttemptTime();
|
||||
const Duration timeout = next_try - Timestamp::Now();
|
||||
GPR_ASSERT(!next_resolution_timer_handle_.has_value());
|
||||
if (GPR_UNLIKELY(tracer_ != nullptr && tracer_->enabled())) {
|
||||
if (timeout > Duration::Zero()) {
|
||||
gpr_log(GPR_INFO, "[polling resolver %p] retrying in %" PRId64 " ms",
|
||||
this, timeout.millis());
|
||||
} else {
|
||||
gpr_log(GPR_INFO, "[polling resolver %p] retrying immediately", this);
|
||||
}
|
||||
}
|
||||
ScheduleNextResolutionTimer(timeout);
|
||||
// Reset result_status_state_. Note that even if re-resolution was
|
||||
// requested while the result-health callback was pending, we can
|
||||
// ignore it here, because we are in backoff to re-resolve anyway.
|
||||
result_status_state_ = ResultStatusState::kNone;
|
||||
}
|
||||
}
|
||||
|
||||
void PollingResolver::MaybeStartResolvingLocked() {
|
||||
// If there is an existing timer, the time it fires is the earliest time we
|
||||
// can start the next resolution.
|
||||
if (next_resolution_timer_handle_.has_value()) return;
|
||||
if (last_resolution_timestamp_.has_value()) {
|
||||
// InvalidateNow to avoid getting stuck re-initializing this timer
|
||||
// in a loop while draining the currently-held WorkSerializer.
|
||||
// Also see https://github.com/grpc/grpc/issues/26079.
|
||||
ExecCtx::Get()->InvalidateNow();
|
||||
const Timestamp earliest_next_resolution =
|
||||
*last_resolution_timestamp_ + min_time_between_resolutions_;
|
||||
const Duration time_until_next_resolution =
|
||||
earliest_next_resolution - Timestamp::Now();
|
||||
if (time_until_next_resolution > Duration::Zero()) {
|
||||
if (GPR_UNLIKELY(tracer_ != nullptr && tracer_->enabled())) {
|
||||
const Duration last_resolution_ago =
|
||||
Timestamp::Now() - *last_resolution_timestamp_;
|
||||
gpr_log(GPR_INFO,
|
||||
"[polling resolver %p] in cooldown from last resolution "
|
||||
"(from %" PRId64 " ms ago); will resolve again in %" PRId64
|
||||
" ms",
|
||||
this, last_resolution_ago.millis(),
|
||||
time_until_next_resolution.millis());
|
||||
}
|
||||
ScheduleNextResolutionTimer(time_until_next_resolution);
|
||||
return;
|
||||
}
|
||||
}
|
||||
StartResolvingLocked();
|
||||
}
|
||||
|
||||
void PollingResolver::StartResolvingLocked() {
|
||||
request_ = StartRequest();
|
||||
last_resolution_timestamp_ = Timestamp::Now();
|
||||
if (GPR_UNLIKELY(tracer_ != nullptr && tracer_->enabled())) {
|
||||
if (request_ != nullptr) {
|
||||
gpr_log(GPR_INFO,
|
||||
"[polling resolver %p] starting resolution, request_=%p", this,
|
||||
request_.get());
|
||||
} else {
|
||||
gpr_log(GPR_INFO, "[polling resolver %p] StartRequest failed", this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace grpc_core
|
||||
121
Pods/gRPC-Core/src/core/resolver/polling_resolver.h
generated
Normal file
121
Pods/gRPC-Core/src/core/resolver/polling_resolver.h
generated
Normal file
@@ -0,0 +1,121 @@
|
||||
//
|
||||
// Copyright 2015 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#ifndef GRPC_SRC_CORE_RESOLVER_POLLING_RESOLVER_H
|
||||
#define GRPC_SRC_CORE_RESOLVER_POLLING_RESOLVER_H
|
||||
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/types/optional.h"
|
||||
|
||||
#include <grpc/event_engine/event_engine.h>
|
||||
|
||||
#include "src/core/lib/backoff/backoff.h"
|
||||
#include "src/core/lib/channel/channel_args.h"
|
||||
#include "src/core/lib/debug/trace.h"
|
||||
#include "src/core/lib/gprpp/orphanable.h"
|
||||
#include "src/core/lib/gprpp/time.h"
|
||||
#include "src/core/lib/gprpp/work_serializer.h"
|
||||
#include "src/core/lib/iomgr/iomgr_fwd.h"
|
||||
#include "src/core/resolver/resolver.h"
|
||||
#include "src/core/resolver/resolver_factory.h"
|
||||
|
||||
namespace grpc_core {
|
||||
|
||||
// A base class for polling-based resolvers.
|
||||
// Handles cooldown and backoff timers.
|
||||
// Implementations need only to implement StartRequest().
|
||||
class PollingResolver : public Resolver {
|
||||
public:
|
||||
PollingResolver(ResolverArgs args, Duration min_time_between_resolutions,
|
||||
BackOff::Options backoff_options, TraceFlag* tracer);
|
||||
~PollingResolver() override;
|
||||
|
||||
void StartLocked() override;
|
||||
void RequestReresolutionLocked() override;
|
||||
void ResetBackoffLocked() override;
|
||||
void ShutdownLocked() override;
|
||||
|
||||
protected:
|
||||
// Implemented by subclass.
|
||||
// Starts a request, returning an object representing the pending
|
||||
// request. Orphaning that object should cancel the request.
|
||||
// When the request is complete, the implementation must call
|
||||
// OnRequestComplete() with the result.
|
||||
virtual OrphanablePtr<Orphanable> StartRequest() = 0;
|
||||
|
||||
// To be invoked by the subclass when a request is complete.
|
||||
void OnRequestComplete(Result result);
|
||||
|
||||
// Convenient accessor methods for subclasses.
|
||||
const std::string& authority() const { return authority_; }
|
||||
const std::string& name_to_resolve() const { return name_to_resolve_; }
|
||||
grpc_pollset_set* interested_parties() const { return interested_parties_; }
|
||||
const ChannelArgs& channel_args() const { return channel_args_; }
|
||||
WorkSerializer* work_serializer() { return work_serializer_.get(); }
|
||||
|
||||
private:
|
||||
void MaybeStartResolvingLocked();
|
||||
void StartResolvingLocked();
|
||||
|
||||
void OnRequestCompleteLocked(Result result);
|
||||
void GetResultStatus(absl::Status status);
|
||||
|
||||
void ScheduleNextResolutionTimer(const Duration& timeout);
|
||||
void OnNextResolutionLocked();
|
||||
void MaybeCancelNextResolutionTimer();
|
||||
|
||||
/// authority
|
||||
std::string authority_;
|
||||
/// name to resolve
|
||||
std::string name_to_resolve_;
|
||||
/// channel args
|
||||
ChannelArgs channel_args_;
|
||||
std::shared_ptr<WorkSerializer> work_serializer_;
|
||||
std::unique_ptr<ResultHandler> result_handler_;
|
||||
TraceFlag* tracer_;
|
||||
/// pollset_set to drive the name resolution process
|
||||
grpc_pollset_set* interested_parties_ = nullptr;
|
||||
/// are we shutting down?
|
||||
bool shutdown_ = false;
|
||||
/// are we currently resolving?
|
||||
OrphanablePtr<Orphanable> request_;
|
||||
/// min time between DNS requests
|
||||
Duration min_time_between_resolutions_;
|
||||
/// timestamp of last DNS request
|
||||
absl::optional<Timestamp> last_resolution_timestamp_;
|
||||
/// retry backoff state
|
||||
BackOff backoff_;
|
||||
/// state for handling interactions between re-resolution requests and
|
||||
/// result health callbacks
|
||||
enum class ResultStatusState {
|
||||
kNone,
|
||||
kResultHealthCallbackPending,
|
||||
kReresolutionRequestedWhileCallbackWasPending,
|
||||
};
|
||||
ResultStatusState result_status_state_ = ResultStatusState::kNone;
|
||||
/// next resolution timer
|
||||
absl::optional<grpc_event_engine::experimental::EventEngine::TaskHandle>
|
||||
next_resolution_timer_handle_;
|
||||
};
|
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_SRC_CORE_RESOLVER_POLLING_RESOLVER_H
|
||||
37
Pods/gRPC-Core/src/core/resolver/resolver.cc
generated
Normal file
37
Pods/gRPC-Core/src/core/resolver/resolver.cc
generated
Normal file
@@ -0,0 +1,37 @@
|
||||
//
|
||||
//
|
||||
// Copyright 2015 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
//
|
||||
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include "src/core/resolver/resolver.h"
|
||||
|
||||
grpc_core::DebugOnlyTraceFlag grpc_trace_resolver_refcount(false,
|
||||
"resolver_refcount");
|
||||
|
||||
namespace grpc_core {
|
||||
|
||||
//
|
||||
// Resolver
|
||||
//
|
||||
|
||||
Resolver::Resolver()
|
||||
: InternallyRefCounted(GRPC_TRACE_FLAG_ENABLED(grpc_trace_resolver_refcount)
|
||||
? "Resolver"
|
||||
: nullptr) {}
|
||||
|
||||
} // namespace grpc_core
|
||||
139
Pods/gRPC-Core/src/core/resolver/resolver.h
generated
Normal file
139
Pods/gRPC-Core/src/core/resolver/resolver.h
generated
Normal file
@@ -0,0 +1,139 @@
|
||||
//
|
||||
// Copyright 2015 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#ifndef GRPC_SRC_CORE_RESOLVER_RESOLVER_H
|
||||
#define GRPC_SRC_CORE_RESOLVER_RESOLVER_H
|
||||
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
|
||||
#include "src/core/lib/channel/channel_args.h"
|
||||
#include "src/core/lib/debug/trace.h"
|
||||
#include "src/core/lib/gprpp/orphanable.h"
|
||||
#include "src/core/lib/gprpp/ref_counted_ptr.h"
|
||||
#include "src/core/resolver/endpoint_addresses.h"
|
||||
#include "src/core/resolver/server_address.h" // IWYU pragma: keep
|
||||
#include "src/core/service_config/service_config.h"
|
||||
|
||||
extern grpc_core::DebugOnlyTraceFlag grpc_trace_resolver_refcount;
|
||||
|
||||
// Name associated with individual address, if available.
|
||||
#define GRPC_ARG_ADDRESS_NAME "grpc.address_name"
|
||||
|
||||
namespace grpc_core {
|
||||
|
||||
/// Interface for name resolution.
|
||||
///
|
||||
/// This interface is designed to support both push-based and pull-based
|
||||
/// mechanisms. A push-based mechanism is one where the resolver will
|
||||
/// subscribe to updates for a given name, and the name service will
|
||||
/// proactively send new data to the resolver whenever the data associated
|
||||
/// with the name changes. A pull-based mechanism is one where the resolver
|
||||
/// needs to query the name service again to get updated information (e.g.,
|
||||
/// DNS).
|
||||
///
|
||||
/// Note: All methods with a "Locked" suffix must be called from the
|
||||
/// work_serializer passed to the constructor.
|
||||
class Resolver : public InternallyRefCounted<Resolver> {
|
||||
public:
|
||||
/// Results returned by the resolver.
|
||||
struct Result {
|
||||
/// A list of endpoints, each with one or more addresses, or an error.
|
||||
absl::StatusOr<EndpointAddressesList> addresses;
|
||||
/// A service config, or an error.
|
||||
absl::StatusOr<RefCountedPtr<ServiceConfig>> service_config = nullptr;
|
||||
/// An optional human-readable note describing context about the resolution,
|
||||
/// to be passed along to the LB policy for inclusion in RPC failure status
|
||||
/// messages in cases where neither \a addresses nor \a service_config
|
||||
/// has a non-OK status. For example, a resolver that returns an empty
|
||||
/// address list but a valid service config may set to this to something
|
||||
/// like "no DNS entries found for <name>".
|
||||
std::string resolution_note;
|
||||
// TODO(roth): Before making this a public API, figure out a way to
|
||||
// avoid exposing channel args this way.
|
||||
ChannelArgs args;
|
||||
// If non-null, this callback will be invoked when the LB policy has
|
||||
// processed the result. The status value passed to the callback
|
||||
// indicates whether the LB policy accepted the update. For polling
|
||||
// resolvers, if the reported status is non-OK, then the resolver
|
||||
// should put itself into backoff to retry the resolution later.
|
||||
// The resolver impl must not call ResultHandler::ReportResult()
|
||||
// again until after this callback has been invoked.
|
||||
// The callback will be invoked within the channel's WorkSerializer.
|
||||
// It may or may not be invoked before ResultHandler::ReportResult()
|
||||
// returns, which is why it's a separate callback.
|
||||
std::function<void(absl::Status)> result_health_callback;
|
||||
};
|
||||
|
||||
/// A proxy object used by the resolver to return results to the
|
||||
/// client channel.
|
||||
class ResultHandler {
|
||||
public:
|
||||
virtual ~ResultHandler() {}
|
||||
|
||||
/// Reports a result to the channel.
|
||||
virtual void ReportResult(Result result) = 0; // NOLINT
|
||||
};
|
||||
|
||||
// Not copyable nor movable.
|
||||
Resolver(const Resolver&) = delete;
|
||||
Resolver& operator=(const Resolver&) = delete;
|
||||
~Resolver() override = default;
|
||||
|
||||
/// Starts resolving.
|
||||
virtual void StartLocked() = 0;
|
||||
|
||||
/// Asks the resolver to obtain an updated resolver result, if
|
||||
/// applicable.
|
||||
///
|
||||
/// This is useful for pull-based implementations to decide when to
|
||||
/// re-resolve. However, the implementation is not required to
|
||||
/// re-resolve immediately upon receiving this call; it may instead
|
||||
/// elect to delay based on some configured minimum time between
|
||||
/// queries, to avoid hammering the name service with queries.
|
||||
///
|
||||
/// For push-based implementations, this may be a no-op.
|
||||
///
|
||||
/// Note: Implementations must not invoke any method on the
|
||||
/// ResultHandler from within this call.
|
||||
virtual void RequestReresolutionLocked() {}
|
||||
|
||||
/// Resets the re-resolution backoff, if any.
|
||||
/// This needs to be implemented only by pull-based implementations;
|
||||
/// for push-based implementations, it will be a no-op.
|
||||
virtual void ResetBackoffLocked() {}
|
||||
|
||||
// Note: This must be invoked while holding the work_serializer.
|
||||
void Orphan() override {
|
||||
ShutdownLocked();
|
||||
Unref();
|
||||
}
|
||||
|
||||
protected:
|
||||
Resolver();
|
||||
|
||||
/// Shuts down the resolver.
|
||||
virtual void ShutdownLocked() = 0;
|
||||
};
|
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_SRC_CORE_RESOLVER_RESOLVER_H
|
||||
78
Pods/gRPC-Core/src/core/resolver/resolver_factory.h
generated
Normal file
78
Pods/gRPC-Core/src/core/resolver/resolver_factory.h
generated
Normal file
@@ -0,0 +1,78 @@
|
||||
//
|
||||
// Copyright 2015 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#ifndef GRPC_SRC_CORE_RESOLVER_RESOLVER_FACTORY_H
|
||||
#define GRPC_SRC_CORE_RESOLVER_RESOLVER_FACTORY_H
|
||||
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/strings/strip.h"
|
||||
|
||||
#include "src/core/lib/channel/channel_args.h"
|
||||
#include "src/core/lib/gprpp/orphanable.h"
|
||||
#include "src/core/lib/iomgr/iomgr_fwd.h"
|
||||
#include "src/core/resolver/resolver.h"
|
||||
#include "src/core/lib/uri/uri_parser.h"
|
||||
|
||||
namespace grpc_core {
|
||||
|
||||
// TODO(yashkt): Move WorkSerializer to its own Bazel target, depend on that
|
||||
// target from this one, and remove this forward declaration.
|
||||
class WorkSerializer;
|
||||
|
||||
struct ResolverArgs {
|
||||
/// The parsed URI to resolve.
|
||||
URI uri;
|
||||
/// Channel args to be included in resolver results.
|
||||
ChannelArgs args;
|
||||
/// Used to drive I/O in the name resolution process.
|
||||
grpc_pollset_set* pollset_set = nullptr;
|
||||
/// The work_serializer under which all resolver calls will be run.
|
||||
std::shared_ptr<WorkSerializer> work_serializer;
|
||||
/// The result handler to be used by the resolver.
|
||||
std::unique_ptr<Resolver::ResultHandler> result_handler;
|
||||
};
|
||||
|
||||
class ResolverFactory {
|
||||
public:
|
||||
virtual ~ResolverFactory() {}
|
||||
|
||||
/// Returns the URI scheme that this factory implements.
|
||||
/// Must not include any upper-case characters.
|
||||
virtual absl::string_view scheme() const = 0;
|
||||
|
||||
/// Returns a bool indicating whether the input uri is valid to create a
|
||||
/// resolver.
|
||||
virtual bool IsValidUri(const URI& uri) const = 0;
|
||||
|
||||
/// Returns a new resolver instance.
|
||||
virtual OrphanablePtr<Resolver> CreateResolver(ResolverArgs args) const = 0;
|
||||
|
||||
/// Returns a string representing the default authority to use for this
|
||||
/// scheme. By default, we %-encode the path part of the target URI,
|
||||
/// excluding the initial '/' character.
|
||||
virtual std::string GetDefaultAuthority(const URI& uri) const {
|
||||
return URI::PercentEncodeAuthority(absl::StripPrefix(uri.path(), "/"));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_SRC_CORE_RESOLVER_RESOLVER_FACTORY_H
|
||||
162
Pods/gRPC-Core/src/core/resolver/resolver_registry.cc
generated
Normal file
162
Pods/gRPC-Core/src/core/resolver/resolver_registry.cc
generated
Normal file
@@ -0,0 +1,162 @@
|
||||
//
|
||||
// Copyright 2015 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include "src/core/resolver/resolver_registry.h"
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/ascii.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
|
||||
#include <grpc/support/log.h>
|
||||
|
||||
namespace grpc_core {
|
||||
|
||||
//
|
||||
// ResolverRegistry::Builder
|
||||
//
|
||||
|
||||
ResolverRegistry::Builder::Builder() { Reset(); }
|
||||
|
||||
void ResolverRegistry::Builder::SetDefaultPrefix(std::string default_prefix) {
|
||||
state_.default_prefix = std::move(default_prefix);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
bool IsLowerCase(absl::string_view str) {
|
||||
for (unsigned char c : str) {
|
||||
if (absl::ascii_isalpha(c) && !absl::ascii_islower(c)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void ResolverRegistry::Builder::RegisterResolverFactory(
|
||||
std::unique_ptr<ResolverFactory> factory) {
|
||||
GPR_ASSERT(IsLowerCase(factory->scheme()));
|
||||
auto p = state_.factories.emplace(factory->scheme(), std::move(factory));
|
||||
GPR_ASSERT(p.second);
|
||||
}
|
||||
|
||||
bool ResolverRegistry::Builder::HasResolverFactory(
|
||||
absl::string_view scheme) const {
|
||||
return state_.factories.find(scheme) != state_.factories.end();
|
||||
}
|
||||
|
||||
void ResolverRegistry::Builder::Reset() {
|
||||
state_.factories.clear();
|
||||
state_.default_prefix = "dns:///";
|
||||
}
|
||||
|
||||
ResolverRegistry ResolverRegistry::Builder::Build() {
|
||||
return ResolverRegistry(std::move(state_));
|
||||
}
|
||||
|
||||
//
|
||||
// ResolverRegistry
|
||||
//
|
||||
|
||||
bool ResolverRegistry::IsValidTarget(absl::string_view target) const {
|
||||
std::string canonical_target;
|
||||
URI uri;
|
||||
ResolverFactory* factory =
|
||||
FindResolverFactory(target, &uri, &canonical_target);
|
||||
if (factory == nullptr) return false;
|
||||
return factory->IsValidUri(uri);
|
||||
}
|
||||
|
||||
OrphanablePtr<Resolver> ResolverRegistry::CreateResolver(
|
||||
absl::string_view target, const ChannelArgs& args,
|
||||
grpc_pollset_set* pollset_set,
|
||||
std::shared_ptr<WorkSerializer> work_serializer,
|
||||
std::unique_ptr<Resolver::ResultHandler> result_handler) const {
|
||||
std::string canonical_target;
|
||||
ResolverArgs resolver_args;
|
||||
ResolverFactory* factory =
|
||||
FindResolverFactory(target, &resolver_args.uri, &canonical_target);
|
||||
if (factory == nullptr) return nullptr;
|
||||
resolver_args.args = args;
|
||||
resolver_args.pollset_set = pollset_set;
|
||||
resolver_args.work_serializer = std::move(work_serializer);
|
||||
resolver_args.result_handler = std::move(result_handler);
|
||||
return factory->CreateResolver(std::move(resolver_args));
|
||||
}
|
||||
|
||||
std::string ResolverRegistry::GetDefaultAuthority(
|
||||
absl::string_view target) const {
|
||||
std::string canonical_target;
|
||||
URI uri;
|
||||
ResolverFactory* factory =
|
||||
FindResolverFactory(target, &uri, &canonical_target);
|
||||
if (factory == nullptr) return "";
|
||||
return factory->GetDefaultAuthority(uri);
|
||||
}
|
||||
|
||||
std::string ResolverRegistry::AddDefaultPrefixIfNeeded(
|
||||
absl::string_view target) const {
|
||||
std::string canonical_target;
|
||||
URI uri;
|
||||
FindResolverFactory(target, &uri, &canonical_target);
|
||||
return canonical_target.empty() ? std::string(target) : canonical_target;
|
||||
}
|
||||
|
||||
ResolverFactory* ResolverRegistry::LookupResolverFactory(
|
||||
absl::string_view scheme) const {
|
||||
auto it = state_.factories.find(scheme);
|
||||
if (it == state_.factories.end()) return nullptr;
|
||||
return it->second.get();
|
||||
}
|
||||
|
||||
// Returns the factory for the scheme of \a target. If \a target does
|
||||
// not parse as a URI, prepends \a default_prefix_ and tries again.
|
||||
// If URI parsing is successful (in either attempt), sets \a uri to
|
||||
// point to the parsed URI.
|
||||
ResolverFactory* ResolverRegistry::FindResolverFactory(
|
||||
absl::string_view target, URI* uri, std::string* canonical_target) const {
|
||||
GPR_ASSERT(uri != nullptr);
|
||||
absl::StatusOr<URI> tmp_uri = URI::Parse(target);
|
||||
ResolverFactory* factory =
|
||||
tmp_uri.ok() ? LookupResolverFactory(tmp_uri->scheme()) : nullptr;
|
||||
if (factory != nullptr) {
|
||||
*uri = std::move(*tmp_uri);
|
||||
return factory;
|
||||
}
|
||||
*canonical_target = absl::StrCat(state_.default_prefix, target);
|
||||
absl::StatusOr<URI> tmp_uri2 = URI::Parse(*canonical_target);
|
||||
factory = tmp_uri2.ok() ? LookupResolverFactory(tmp_uri2->scheme()) : nullptr;
|
||||
if (factory != nullptr) {
|
||||
*uri = std::move(*tmp_uri2);
|
||||
return factory;
|
||||
}
|
||||
if (!tmp_uri.ok() || !tmp_uri2.ok()) {
|
||||
gpr_log(GPR_ERROR, "%s",
|
||||
absl::StrFormat("Error parsing URI(s). '%s':%s; '%s':%s", target,
|
||||
tmp_uri.status().ToString(), *canonical_target,
|
||||
tmp_uri2.status().ToString())
|
||||
.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
gpr_log(GPR_ERROR, "Don't know how to resolve '%s' or '%s'.",
|
||||
std::string(target).c_str(), canonical_target->c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace grpc_core
|
||||
123
Pods/gRPC-Core/src/core/resolver/resolver_registry.h
generated
Normal file
123
Pods/gRPC-Core/src/core/resolver/resolver_registry.h
generated
Normal file
@@ -0,0 +1,123 @@
|
||||
//
|
||||
// Copyright 2015 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#ifndef GRPC_SRC_CORE_RESOLVER_RESOLVER_REGISTRY_H
|
||||
#define GRPC_SRC_CORE_RESOLVER_RESOLVER_REGISTRY_H
|
||||
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
#include "src/core/lib/channel/channel_args.h"
|
||||
#include "src/core/lib/gprpp/orphanable.h"
|
||||
#include "src/core/lib/iomgr/iomgr_fwd.h"
|
||||
#include "src/core/resolver/resolver.h"
|
||||
#include "src/core/resolver/resolver_factory.h"
|
||||
#include "src/core/lib/uri/uri_parser.h"
|
||||
|
||||
namespace grpc_core {
|
||||
|
||||
class ResolverRegistry {
|
||||
private:
|
||||
// Forward declaration needed to use this in Builder.
|
||||
struct State {
|
||||
std::map<absl::string_view, std::unique_ptr<ResolverFactory>> factories;
|
||||
std::string default_prefix;
|
||||
};
|
||||
|
||||
public:
|
||||
/// Methods used to create and populate the ResolverRegistry.
|
||||
/// NOT THREAD SAFE -- to be used only during global gRPC
|
||||
/// initialization and shutdown.
|
||||
class Builder {
|
||||
public:
|
||||
Builder();
|
||||
|
||||
/// Sets the default URI prefix to \a default_prefix.
|
||||
void SetDefaultPrefix(std::string default_prefix);
|
||||
|
||||
/// Registers a resolver factory. The factory will be used to create a
|
||||
/// resolver for any URI whose scheme matches that of the factory.
|
||||
void RegisterResolverFactory(std::unique_ptr<ResolverFactory> factory);
|
||||
|
||||
/// Returns true iff scheme already has a registered factory.
|
||||
bool HasResolverFactory(absl::string_view scheme) const;
|
||||
|
||||
/// Wipe everything in the registry and reset to empty.
|
||||
void Reset();
|
||||
|
||||
ResolverRegistry Build();
|
||||
|
||||
private:
|
||||
ResolverRegistry::State state_;
|
||||
};
|
||||
|
||||
ResolverRegistry(const ResolverRegistry&) = delete;
|
||||
ResolverRegistry& operator=(const ResolverRegistry&) = delete;
|
||||
ResolverRegistry(ResolverRegistry&&) noexcept;
|
||||
ResolverRegistry& operator=(ResolverRegistry&&) noexcept;
|
||||
|
||||
/// Checks whether the user input \a target is valid to create a resolver.
|
||||
bool IsValidTarget(absl::string_view target) const;
|
||||
|
||||
/// Creates a resolver given \a target.
|
||||
/// First tries to parse \a target as a URI. If this succeeds, tries
|
||||
/// to locate a registered resolver factory based on the URI scheme.
|
||||
/// If parsing fails or there is no factory for the URI's scheme,
|
||||
/// prepends default_prefix to target and tries again.
|
||||
/// If a resolver factory is found, uses it to instantiate a resolver and
|
||||
/// returns it; otherwise, returns nullptr.
|
||||
/// \a args, \a pollset_set, and \a work_serializer are passed to the
|
||||
/// factory's \a CreateResolver() method. \a args are the channel args to be
|
||||
/// included in resolver results. \a pollset_set is used to drive I/O in the
|
||||
/// name resolution process. \a work_serializer is the work_serializer under
|
||||
/// which all resolver calls will be run. \a result_handler is used to return
|
||||
/// results from the resolver.
|
||||
OrphanablePtr<Resolver> CreateResolver(
|
||||
absl::string_view target, const ChannelArgs& args,
|
||||
grpc_pollset_set* pollset_set,
|
||||
std::shared_ptr<WorkSerializer> work_serializer,
|
||||
std::unique_ptr<Resolver::ResultHandler> result_handler) const;
|
||||
|
||||
/// Returns the default authority to pass from a client for \a target.
|
||||
std::string GetDefaultAuthority(absl::string_view target) const;
|
||||
|
||||
/// Returns \a target with the default prefix prepended, if needed.
|
||||
std::string AddDefaultPrefixIfNeeded(absl::string_view target) const;
|
||||
|
||||
/// Returns the resolver factory for \a scheme.
|
||||
/// Caller does NOT own the return value.
|
||||
ResolverFactory* LookupResolverFactory(absl::string_view scheme) const;
|
||||
|
||||
private:
|
||||
explicit ResolverRegistry(State state) : state_(std::move(state)) {}
|
||||
|
||||
// TODO(ctiller): fix callers such that the canonical_target argument can be
|
||||
// removed, and replaced with uri.ToString().
|
||||
ResolverFactory* FindResolverFactory(absl::string_view target, URI* uri,
|
||||
std::string* canonical_target) const;
|
||||
|
||||
State state_;
|
||||
};
|
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_SRC_CORE_RESOLVER_RESOLVER_REGISTRY_H
|
||||
35
Pods/gRPC-Core/src/core/resolver/server_address.h
generated
Normal file
35
Pods/gRPC-Core/src/core/resolver/server_address.h
generated
Normal file
@@ -0,0 +1,35 @@
|
||||
//
|
||||
//
|
||||
// Copyright 2018 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
//
|
||||
|
||||
#ifndef GRPC_SRC_CORE_RESOLVER_SERVER_ADDRESS_H
|
||||
#define GRPC_SRC_CORE_RESOLVER_SERVER_ADDRESS_H
|
||||
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include "src/core/resolver/endpoint_addresses.h"
|
||||
|
||||
namespace grpc_core {
|
||||
|
||||
// For backward compatibility only.
|
||||
// TODO(roth): Remove this file when all callers have been updated.
|
||||
using ServerAddress = EndpointAddresses;
|
||||
using ServerAddressList = EndpointAddressesList;
|
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_SRC_CORE_RESOLVER_SERVER_ADDRESS_H
|
||||
202
Pods/gRPC-Core/src/core/resolver/sockaddr/sockaddr_resolver.cc
generated
Normal file
202
Pods/gRPC-Core/src/core/resolver/sockaddr/sockaddr_resolver.cc
generated
Normal file
@@ -0,0 +1,202 @@
|
||||
//
|
||||
// Copyright 2015 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/str_split.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
#include <grpc/support/log.h>
|
||||
|
||||
#include "src/core/lib/address_utils/parse_address.h"
|
||||
#include "src/core/lib/channel/channel_args.h"
|
||||
#include "src/core/lib/config/core_configuration.h"
|
||||
#include "src/core/lib/gprpp/orphanable.h"
|
||||
#include "src/core/lib/iomgr/port.h"
|
||||
#include "src/core/lib/iomgr/resolved_address.h"
|
||||
#include "src/core/resolver/endpoint_addresses.h"
|
||||
#include "src/core/resolver/resolver.h"
|
||||
#include "src/core/resolver/resolver_factory.h"
|
||||
#include "src/core/lib/uri/uri_parser.h"
|
||||
|
||||
namespace grpc_core {
|
||||
|
||||
namespace {
|
||||
|
||||
class SockaddrResolver : public Resolver {
|
||||
public:
|
||||
SockaddrResolver(EndpointAddressesList addresses, ResolverArgs args);
|
||||
|
||||
void StartLocked() override;
|
||||
|
||||
void ShutdownLocked() override {}
|
||||
|
||||
private:
|
||||
std::unique_ptr<ResultHandler> result_handler_;
|
||||
EndpointAddressesList addresses_;
|
||||
ChannelArgs channel_args_;
|
||||
};
|
||||
|
||||
SockaddrResolver::SockaddrResolver(EndpointAddressesList addresses,
|
||||
ResolverArgs args)
|
||||
: result_handler_(std::move(args.result_handler)),
|
||||
addresses_(std::move(addresses)),
|
||||
channel_args_(std::move(args.args)) {}
|
||||
|
||||
void SockaddrResolver::StartLocked() {
|
||||
Result result;
|
||||
result.addresses = std::move(addresses_);
|
||||
result.args = std::move(channel_args_);
|
||||
result_handler_->ReportResult(std::move(result));
|
||||
}
|
||||
|
||||
//
|
||||
// Factory
|
||||
//
|
||||
|
||||
bool ParseUri(const URI& uri,
|
||||
bool parse(const URI& uri, grpc_resolved_address* dst),
|
||||
EndpointAddressesList* addresses) {
|
||||
if (!uri.authority().empty()) {
|
||||
gpr_log(GPR_ERROR, "authority-based URIs not supported by the %s scheme",
|
||||
uri.scheme().c_str());
|
||||
return false;
|
||||
}
|
||||
// Construct addresses.
|
||||
bool errors_found = false;
|
||||
for (absl::string_view ith_path : absl::StrSplit(uri.path(), ',')) {
|
||||
if (ith_path.empty()) {
|
||||
// Skip targets which are empty.
|
||||
continue;
|
||||
}
|
||||
auto ith_uri = URI::Create(uri.scheme(), "", std::string(ith_path), {}, "");
|
||||
grpc_resolved_address addr;
|
||||
if (!ith_uri.ok() || !parse(*ith_uri, &addr)) {
|
||||
errors_found = true;
|
||||
break;
|
||||
}
|
||||
if (addresses != nullptr) {
|
||||
addresses->emplace_back(addr, ChannelArgs());
|
||||
}
|
||||
}
|
||||
return !errors_found;
|
||||
}
|
||||
|
||||
OrphanablePtr<Resolver> CreateSockaddrResolver(
|
||||
ResolverArgs args, bool parse(const URI& uri, grpc_resolved_address* dst)) {
|
||||
EndpointAddressesList addresses;
|
||||
if (!ParseUri(args.uri, parse, &addresses)) return nullptr;
|
||||
// Instantiate resolver.
|
||||
return MakeOrphanable<SockaddrResolver>(std::move(addresses),
|
||||
std::move(args));
|
||||
}
|
||||
|
||||
class IPv4ResolverFactory : public ResolverFactory {
|
||||
public:
|
||||
absl::string_view scheme() const override { return "ipv4"; }
|
||||
|
||||
bool IsValidUri(const URI& uri) const override {
|
||||
return ParseUri(uri, grpc_parse_ipv4, nullptr);
|
||||
}
|
||||
|
||||
OrphanablePtr<Resolver> CreateResolver(ResolverArgs args) const override {
|
||||
return CreateSockaddrResolver(std::move(args), grpc_parse_ipv4);
|
||||
}
|
||||
};
|
||||
|
||||
class IPv6ResolverFactory : public ResolverFactory {
|
||||
public:
|
||||
absl::string_view scheme() const override { return "ipv6"; }
|
||||
|
||||
bool IsValidUri(const URI& uri) const override {
|
||||
return ParseUri(uri, grpc_parse_ipv6, nullptr);
|
||||
}
|
||||
|
||||
OrphanablePtr<Resolver> CreateResolver(ResolverArgs args) const override {
|
||||
return CreateSockaddrResolver(std::move(args), grpc_parse_ipv6);
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef GRPC_HAVE_UNIX_SOCKET
|
||||
class UnixResolverFactory : public ResolverFactory {
|
||||
public:
|
||||
absl::string_view scheme() const override { return "unix"; }
|
||||
|
||||
bool IsValidUri(const URI& uri) const override {
|
||||
return ParseUri(uri, grpc_parse_unix, nullptr);
|
||||
}
|
||||
|
||||
OrphanablePtr<Resolver> CreateResolver(ResolverArgs args) const override {
|
||||
return CreateSockaddrResolver(std::move(args), grpc_parse_unix);
|
||||
}
|
||||
};
|
||||
|
||||
class UnixAbstractResolverFactory : public ResolverFactory {
|
||||
public:
|
||||
absl::string_view scheme() const override { return "unix-abstract"; }
|
||||
|
||||
bool IsValidUri(const URI& uri) const override {
|
||||
return ParseUri(uri, grpc_parse_unix_abstract, nullptr);
|
||||
}
|
||||
|
||||
OrphanablePtr<Resolver> CreateResolver(ResolverArgs args) const override {
|
||||
return CreateSockaddrResolver(std::move(args), grpc_parse_unix_abstract);
|
||||
}
|
||||
};
|
||||
#endif // GRPC_HAVE_UNIX_SOCKET
|
||||
|
||||
#ifdef GRPC_HAVE_VSOCK
|
||||
class VSockResolverFactory : public ResolverFactory {
|
||||
public:
|
||||
absl::string_view scheme() const override { return "vsock"; }
|
||||
|
||||
bool IsValidUri(const URI& uri) const override {
|
||||
return ParseUri(uri, grpc_parse_vsock, nullptr);
|
||||
}
|
||||
|
||||
OrphanablePtr<Resolver> CreateResolver(ResolverArgs args) const override {
|
||||
return CreateSockaddrResolver(std::move(args), grpc_parse_vsock);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // GRPC_HAVE_VSOCK
|
||||
|
||||
} // namespace
|
||||
|
||||
void RegisterSockaddrResolver(CoreConfiguration::Builder* builder) {
|
||||
builder->resolver_registry()->RegisterResolverFactory(
|
||||
std::make_unique<IPv4ResolverFactory>());
|
||||
builder->resolver_registry()->RegisterResolverFactory(
|
||||
std::make_unique<IPv6ResolverFactory>());
|
||||
#ifdef GRPC_HAVE_UNIX_SOCKET
|
||||
builder->resolver_registry()->RegisterResolverFactory(
|
||||
std::make_unique<UnixResolverFactory>());
|
||||
builder->resolver_registry()->RegisterResolverFactory(
|
||||
std::make_unique<UnixAbstractResolverFactory>());
|
||||
#endif
|
||||
#ifdef GRPC_HAVE_VSOCK
|
||||
builder->resolver_registry()->RegisterResolverFactory(
|
||||
std::make_unique<VSockResolverFactory>());
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace grpc_core
|
||||
1031
Pods/gRPC-Core/src/core/resolver/xds/xds_dependency_manager.cc
generated
Normal file
1031
Pods/gRPC-Core/src/core/resolver/xds/xds_dependency_manager.cc
generated
Normal file
@@ -0,0 +1,1031 @@
|
||||
//
|
||||
// Copyright 2019 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include "src/core/resolver/xds/xds_dependency_manager.h"
|
||||
|
||||
#include "absl/strings/str_join.h"
|
||||
|
||||
#include "src/core/ext/xds/xds_routing.h"
|
||||
#include "src/core/lib/config/core_configuration.h"
|
||||
#include "src/core/lib/gprpp/match.h"
|
||||
#include "src/core/load_balancing/xds/xds_channel_args.h"
|
||||
#include "src/core/resolver/fake/fake_resolver.h"
|
||||
#include "src/core/resolver/xds/xds_resolver_trace.h"
|
||||
|
||||
namespace grpc_core {
|
||||
|
||||
namespace {
|
||||
|
||||
// Max depth of aggregate cluster dependency graph.
|
||||
constexpr int kMaxXdsAggregateClusterRecursionDepth = 16;
|
||||
|
||||
} // namespace
|
||||
|
||||
//
|
||||
// XdsDependencyManager::XdsConfig::ClusterConfig
|
||||
//
|
||||
|
||||
XdsDependencyManager::XdsConfig::ClusterConfig::ClusterConfig(
|
||||
std::shared_ptr<const XdsClusterResource> cluster,
|
||||
std::shared_ptr<const XdsEndpointResource> endpoints,
|
||||
std::string resolution_note)
|
||||
: cluster(std::move(cluster)),
|
||||
children(absl::in_place_type_t<EndpointConfig>(), std::move(endpoints),
|
||||
std::move(resolution_note)) {}
|
||||
|
||||
XdsDependencyManager::XdsConfig::ClusterConfig::ClusterConfig(
|
||||
std::shared_ptr<const XdsClusterResource> cluster,
|
||||
std::vector<absl::string_view> leaf_clusters)
|
||||
: cluster(std::move(cluster)),
|
||||
children(absl::in_place_type_t<AggregateConfig>(),
|
||||
std::move(leaf_clusters)) {}
|
||||
|
||||
//
|
||||
// XdsDependencyManager::XdsConfig
|
||||
//
|
||||
|
||||
std::string XdsDependencyManager::XdsConfig::ToString() const {
|
||||
std::vector<std::string> parts = {
|
||||
"{\n listener: {", listener->ToString(),
|
||||
"}\n route_config: {", route_config->ToString(),
|
||||
"}\n virtual_host: {", virtual_host->ToString(),
|
||||
"}\n clusters: {\n"};
|
||||
for (const auto& p : clusters) {
|
||||
parts.push_back(absl::StrCat(" \"", p.first, "\": "));
|
||||
if (!p.second.ok()) {
|
||||
parts.push_back(p.second.status().ToString());
|
||||
parts.push_back("\n");
|
||||
} else {
|
||||
parts.push_back(
|
||||
absl::StrCat(" {\n"
|
||||
" cluster: {",
|
||||
p.second->cluster->ToString(), "}\n"));
|
||||
Match(
|
||||
p.second->children,
|
||||
[&](const ClusterConfig::EndpointConfig& endpoint_config) {
|
||||
parts.push_back(
|
||||
absl::StrCat(" endpoints: {",
|
||||
endpoint_config.endpoints == nullptr
|
||||
? "<null>"
|
||||
: endpoint_config.endpoints->ToString(),
|
||||
"}\n"
|
||||
" resolution_note: \"",
|
||||
endpoint_config.resolution_note, "\"\n"));
|
||||
},
|
||||
[&](const ClusterConfig::AggregateConfig& aggregate_config) {
|
||||
parts.push_back(absl::StrCat(
|
||||
" leaf_clusters: [",
|
||||
absl::StrJoin(aggregate_config.leaf_clusters, ", "), "]\n"));
|
||||
});
|
||||
parts.push_back(
|
||||
" }\n"
|
||||
" ]\n");
|
||||
}
|
||||
}
|
||||
parts.push_back(" }\n}");
|
||||
return absl::StrJoin(parts, "");
|
||||
}
|
||||
|
||||
//
|
||||
// XdsDependencyManager::ListenerWatcher
|
||||
//
|
||||
|
||||
class XdsDependencyManager::ListenerWatcher
|
||||
: public XdsListenerResourceType::WatcherInterface {
|
||||
public:
|
||||
explicit ListenerWatcher(RefCountedPtr<XdsDependencyManager> dependency_mgr)
|
||||
: dependency_mgr_(std::move(dependency_mgr)) {}
|
||||
|
||||
void OnResourceChanged(
|
||||
std::shared_ptr<const XdsListenerResource> listener,
|
||||
RefCountedPtr<XdsClient::ReadDelayHandle> read_delay_handle) override {
|
||||
dependency_mgr_->work_serializer_->Run(
|
||||
[dependency_mgr = dependency_mgr_, listener = std::move(listener),
|
||||
read_delay_handle = std::move(read_delay_handle)]() mutable {
|
||||
dependency_mgr->OnListenerUpdate(std::move(listener));
|
||||
},
|
||||
DEBUG_LOCATION);
|
||||
}
|
||||
|
||||
void OnError(
|
||||
absl::Status status,
|
||||
RefCountedPtr<XdsClient::ReadDelayHandle> read_delay_handle) override {
|
||||
dependency_mgr_->work_serializer_->Run(
|
||||
[dependency_mgr = dependency_mgr_, status = std::move(status),
|
||||
read_delay_handle = std::move(read_delay_handle)]() mutable {
|
||||
dependency_mgr->OnError(dependency_mgr->listener_resource_name_,
|
||||
std::move(status));
|
||||
},
|
||||
DEBUG_LOCATION);
|
||||
}
|
||||
|
||||
void OnResourceDoesNotExist(
|
||||
RefCountedPtr<XdsClient::ReadDelayHandle> read_delay_handle) override {
|
||||
dependency_mgr_->work_serializer_->Run(
|
||||
[dependency_mgr = dependency_mgr_,
|
||||
read_delay_handle = std::move(read_delay_handle)]() {
|
||||
dependency_mgr->OnResourceDoesNotExist(
|
||||
absl::StrCat(dependency_mgr->listener_resource_name_,
|
||||
": xDS listener resource does not exist"));
|
||||
},
|
||||
DEBUG_LOCATION);
|
||||
}
|
||||
|
||||
private:
|
||||
RefCountedPtr<XdsDependencyManager> dependency_mgr_;
|
||||
};
|
||||
|
||||
//
|
||||
// XdsDependencyManager::RouteConfigWatcher
|
||||
//
|
||||
|
||||
class XdsDependencyManager::RouteConfigWatcher
|
||||
: public XdsRouteConfigResourceType::WatcherInterface {
|
||||
public:
|
||||
RouteConfigWatcher(RefCountedPtr<XdsDependencyManager> dependency_mgr,
|
||||
std::string name)
|
||||
: dependency_mgr_(std::move(dependency_mgr)), name_(std::move(name)) {}
|
||||
|
||||
void OnResourceChanged(
|
||||
std::shared_ptr<const XdsRouteConfigResource> route_config,
|
||||
RefCountedPtr<XdsClient::ReadDelayHandle> read_delay_handle) override {
|
||||
dependency_mgr_->work_serializer_->Run(
|
||||
[self = RefAsSubclass<RouteConfigWatcher>(),
|
||||
route_config = std::move(route_config),
|
||||
read_delay_handle = std::move(read_delay_handle)]() mutable {
|
||||
self->dependency_mgr_->OnRouteConfigUpdate(self->name_,
|
||||
std::move(route_config));
|
||||
},
|
||||
DEBUG_LOCATION);
|
||||
}
|
||||
|
||||
void OnError(
|
||||
absl::Status status,
|
||||
RefCountedPtr<XdsClient::ReadDelayHandle> read_delay_handle) override {
|
||||
dependency_mgr_->work_serializer_->Run(
|
||||
[self = RefAsSubclass<RouteConfigWatcher>(), status = std::move(status),
|
||||
read_delay_handle = std::move(read_delay_handle)]() mutable {
|
||||
self->dependency_mgr_->OnError(self->name_, std::move(status));
|
||||
},
|
||||
DEBUG_LOCATION);
|
||||
}
|
||||
|
||||
void OnResourceDoesNotExist(
|
||||
RefCountedPtr<XdsClient::ReadDelayHandle> read_delay_handle) override {
|
||||
dependency_mgr_->work_serializer_->Run(
|
||||
[self = RefAsSubclass<RouteConfigWatcher>(),
|
||||
read_delay_handle = std::move(read_delay_handle)]() {
|
||||
self->dependency_mgr_->OnResourceDoesNotExist(absl::StrCat(
|
||||
self->name_,
|
||||
": xDS route configuration resource does not exist"));
|
||||
},
|
||||
DEBUG_LOCATION);
|
||||
}
|
||||
|
||||
private:
|
||||
RefCountedPtr<XdsDependencyManager> dependency_mgr_;
|
||||
std::string name_;
|
||||
};
|
||||
|
||||
//
|
||||
// XdsDependencyManager::ClusterWatcher
|
||||
//
|
||||
|
||||
class XdsDependencyManager::ClusterWatcher
|
||||
: public XdsClusterResourceType::WatcherInterface {
|
||||
public:
|
||||
ClusterWatcher(RefCountedPtr<XdsDependencyManager> dependency_mgr,
|
||||
absl::string_view name)
|
||||
: dependency_mgr_(std::move(dependency_mgr)), name_(name) {}
|
||||
|
||||
void OnResourceChanged(
|
||||
std::shared_ptr<const XdsClusterResource> cluster,
|
||||
RefCountedPtr<XdsClient::ReadDelayHandle> read_delay_handle) override {
|
||||
dependency_mgr_->work_serializer_->Run(
|
||||
[self = RefAsSubclass<ClusterWatcher>(), cluster = std::move(cluster),
|
||||
read_delay_handle = std::move(read_delay_handle)]() mutable {
|
||||
self->dependency_mgr_->OnClusterUpdate(self->name_,
|
||||
std::move(cluster));
|
||||
},
|
||||
DEBUG_LOCATION);
|
||||
}
|
||||
|
||||
void OnError(
|
||||
absl::Status status,
|
||||
RefCountedPtr<XdsClient::ReadDelayHandle> read_delay_handle) override {
|
||||
dependency_mgr_->work_serializer_->Run(
|
||||
[self = RefAsSubclass<ClusterWatcher>(), status = std::move(status),
|
||||
read_delay_handle = std::move(read_delay_handle)]() mutable {
|
||||
self->dependency_mgr_->OnClusterError(self->name_, std::move(status));
|
||||
},
|
||||
DEBUG_LOCATION);
|
||||
}
|
||||
|
||||
void OnResourceDoesNotExist(
|
||||
RefCountedPtr<XdsClient::ReadDelayHandle> read_delay_handle) override {
|
||||
dependency_mgr_->work_serializer_->Run(
|
||||
[self = RefAsSubclass<ClusterWatcher>(),
|
||||
read_delay_handle = std::move(read_delay_handle)]() {
|
||||
self->dependency_mgr_->OnClusterDoesNotExist(self->name_);
|
||||
},
|
||||
DEBUG_LOCATION);
|
||||
}
|
||||
|
||||
private:
|
||||
RefCountedPtr<XdsDependencyManager> dependency_mgr_;
|
||||
std::string name_;
|
||||
};
|
||||
|
||||
//
|
||||
// XdsDependencyManager::EndpointWatcher
|
||||
//
|
||||
|
||||
class XdsDependencyManager::EndpointWatcher
|
||||
: public XdsEndpointResourceType::WatcherInterface {
|
||||
public:
|
||||
EndpointWatcher(RefCountedPtr<XdsDependencyManager> dependency_mgr,
|
||||
absl::string_view name)
|
||||
: dependency_mgr_(std::move(dependency_mgr)), name_(name) {}
|
||||
|
||||
void OnResourceChanged(
|
||||
std::shared_ptr<const XdsEndpointResource> endpoint,
|
||||
RefCountedPtr<XdsClient::ReadDelayHandle> read_delay_handle) override {
|
||||
dependency_mgr_->work_serializer_->Run(
|
||||
[self = RefAsSubclass<EndpointWatcher>(),
|
||||
endpoint = std::move(endpoint),
|
||||
read_delay_handle = std::move(read_delay_handle)]() mutable {
|
||||
self->dependency_mgr_->OnEndpointUpdate(self->name_,
|
||||
std::move(endpoint));
|
||||
},
|
||||
DEBUG_LOCATION);
|
||||
}
|
||||
|
||||
void OnError(
|
||||
absl::Status status,
|
||||
RefCountedPtr<XdsClient::ReadDelayHandle> read_delay_handle) override {
|
||||
dependency_mgr_->work_serializer_->Run(
|
||||
[self = RefAsSubclass<EndpointWatcher>(), status = std::move(status),
|
||||
read_delay_handle = std::move(read_delay_handle)]() mutable {
|
||||
self->dependency_mgr_->OnEndpointError(self->name_,
|
||||
std::move(status));
|
||||
},
|
||||
DEBUG_LOCATION);
|
||||
}
|
||||
|
||||
void OnResourceDoesNotExist(
|
||||
RefCountedPtr<XdsClient::ReadDelayHandle> read_delay_handle) override {
|
||||
dependency_mgr_->work_serializer_->Run(
|
||||
[self = RefAsSubclass<EndpointWatcher>(),
|
||||
read_delay_handle = std::move(read_delay_handle)]() {
|
||||
self->dependency_mgr_->OnEndpointDoesNotExist(self->name_);
|
||||
},
|
||||
DEBUG_LOCATION);
|
||||
}
|
||||
|
||||
private:
|
||||
RefCountedPtr<XdsDependencyManager> dependency_mgr_;
|
||||
std::string name_;
|
||||
};
|
||||
|
||||
//
|
||||
// XdsDependencyManager::DnsResultHandler
|
||||
//
|
||||
|
||||
class XdsDependencyManager::DnsResultHandler : public Resolver::ResultHandler {
|
||||
public:
|
||||
DnsResultHandler(RefCountedPtr<XdsDependencyManager> dependency_mgr,
|
||||
std::string name)
|
||||
: dependency_mgr_(std::move(dependency_mgr)), name_(std::move(name)) {}
|
||||
|
||||
void ReportResult(Resolver::Result result) override {
|
||||
dependency_mgr_->work_serializer_->Run(
|
||||
[dependency_mgr = dependency_mgr_, name = name_,
|
||||
result = std::move(result)]() mutable {
|
||||
dependency_mgr->OnDnsResult(name, std::move(result));
|
||||
},
|
||||
DEBUG_LOCATION);
|
||||
}
|
||||
|
||||
private:
|
||||
RefCountedPtr<XdsDependencyManager> dependency_mgr_;
|
||||
std::string name_;
|
||||
};
|
||||
|
||||
//
|
||||
// XdsDependencyManager::ClusterSubscription
|
||||
//
|
||||
|
||||
void XdsDependencyManager::ClusterSubscription::Orphan() {
|
||||
dependency_mgr_->work_serializer_->Run(
|
||||
[self = WeakRef()]() {
|
||||
self->dependency_mgr_->OnClusterSubscriptionUnref(self->cluster_name_,
|
||||
self.get());
|
||||
},
|
||||
DEBUG_LOCATION);
|
||||
}
|
||||
|
||||
//
|
||||
// XdsDependencyManager
|
||||
//
|
||||
|
||||
XdsDependencyManager::XdsDependencyManager(
|
||||
RefCountedPtr<GrpcXdsClient> xds_client,
|
||||
std::shared_ptr<WorkSerializer> work_serializer,
|
||||
std::unique_ptr<Watcher> watcher, std::string data_plane_authority,
|
||||
std::string listener_resource_name, ChannelArgs args,
|
||||
grpc_pollset_set* interested_parties)
|
||||
: xds_client_(std::move(xds_client)),
|
||||
work_serializer_(std::move(work_serializer)),
|
||||
watcher_(std::move(watcher)),
|
||||
data_plane_authority_(std::move(data_plane_authority)),
|
||||
listener_resource_name_(std::move(listener_resource_name)),
|
||||
args_(std::move(args)),
|
||||
interested_parties_(interested_parties) {
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) {
|
||||
gpr_log(GPR_INFO,
|
||||
"[XdsDependencyManager %p] starting watch for listener %s", this,
|
||||
listener_resource_name_.c_str());
|
||||
}
|
||||
auto listener_watcher = MakeRefCounted<ListenerWatcher>(Ref());
|
||||
listener_watcher_ = listener_watcher.get();
|
||||
XdsListenerResourceType::StartWatch(
|
||||
xds_client_.get(), listener_resource_name_, std::move(listener_watcher));
|
||||
}
|
||||
|
||||
void XdsDependencyManager::Orphan() {
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) {
|
||||
gpr_log(GPR_INFO, "[XdsDependencyManager %p] shutting down", this);
|
||||
}
|
||||
if (listener_watcher_ != nullptr) {
|
||||
XdsListenerResourceType::CancelWatch(
|
||||
xds_client_.get(), listener_resource_name_, listener_watcher_,
|
||||
/*delay_unsubscription=*/false);
|
||||
}
|
||||
if (route_config_watcher_ != nullptr) {
|
||||
XdsRouteConfigResourceType::CancelWatch(
|
||||
xds_client_.get(), route_config_name_, route_config_watcher_,
|
||||
/*delay_unsubscription=*/false);
|
||||
}
|
||||
for (const auto& p : cluster_watchers_) {
|
||||
XdsClusterResourceType::CancelWatch(xds_client_.get(), p.first,
|
||||
p.second.watcher,
|
||||
/*delay_unsubscription=*/false);
|
||||
}
|
||||
for (const auto& p : endpoint_watchers_) {
|
||||
XdsEndpointResourceType::CancelWatch(xds_client_.get(), p.first,
|
||||
p.second.watcher,
|
||||
/*delay_unsubscription=*/false);
|
||||
}
|
||||
cluster_subscriptions_.clear();
|
||||
xds_client_.reset();
|
||||
for (auto& p : dns_resolvers_) {
|
||||
p.second.resolver.reset();
|
||||
}
|
||||
Unref();
|
||||
}
|
||||
|
||||
void XdsDependencyManager::OnListenerUpdate(
|
||||
std::shared_ptr<const XdsListenerResource> listener) {
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) {
|
||||
gpr_log(GPR_INFO, "[XdsDependencyManager %p] received Listener update",
|
||||
this);
|
||||
}
|
||||
if (xds_client_ == nullptr) return;
|
||||
const auto* hcm = absl::get_if<XdsListenerResource::HttpConnectionManager>(
|
||||
&listener->listener);
|
||||
if (hcm == nullptr) {
|
||||
return OnError(listener_resource_name_,
|
||||
absl::UnavailableError("not an API listener"));
|
||||
}
|
||||
current_listener_ = std::move(listener);
|
||||
Match(
|
||||
hcm->route_config,
|
||||
// RDS resource name
|
||||
[&](const std::string& rds_name) {
|
||||
// If the RDS name changed, update the RDS watcher.
|
||||
// Note that this will be true on the initial update, because
|
||||
// route_config_name_ will be empty.
|
||||
if (route_config_name_ != rds_name) {
|
||||
// If we already had a watch (i.e., if the previous config had
|
||||
// a different RDS name), stop the previous watch.
|
||||
// There will be no previous watch if either (a) this is the
|
||||
// initial resource update or (b) the previous Listener had an
|
||||
// inlined RouteConfig.
|
||||
if (route_config_watcher_ != nullptr) {
|
||||
XdsRouteConfigResourceType::CancelWatch(
|
||||
xds_client_.get(), route_config_name_, route_config_watcher_,
|
||||
/*delay_unsubscription=*/true);
|
||||
route_config_watcher_ = nullptr;
|
||||
}
|
||||
// Start watch for the new RDS resource name.
|
||||
route_config_name_ = rds_name;
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) {
|
||||
gpr_log(
|
||||
GPR_INFO,
|
||||
"[XdsDependencyManager %p] starting watch for route config %s",
|
||||
this, route_config_name_.c_str());
|
||||
}
|
||||
auto watcher =
|
||||
MakeRefCounted<RouteConfigWatcher>(Ref(), route_config_name_);
|
||||
route_config_watcher_ = watcher.get();
|
||||
XdsRouteConfigResourceType::StartWatch(
|
||||
xds_client_.get(), route_config_name_, std::move(watcher));
|
||||
} else {
|
||||
// RDS resource name has not changed, so no watch needs to be
|
||||
// updated, but we still need to propagate any changes in the
|
||||
// HCM config (e.g., the list of HTTP filters).
|
||||
MaybeReportUpdate();
|
||||
}
|
||||
},
|
||||
// inlined RouteConfig
|
||||
[&](const std::shared_ptr<const XdsRouteConfigResource>& route_config) {
|
||||
// If the previous update specified an RDS resource instead of
|
||||
// having an inlined RouteConfig, we need to cancel the RDS watch.
|
||||
if (route_config_watcher_ != nullptr) {
|
||||
XdsRouteConfigResourceType::CancelWatch(
|
||||
xds_client_.get(), route_config_name_, route_config_watcher_);
|
||||
route_config_watcher_ = nullptr;
|
||||
route_config_name_.clear();
|
||||
}
|
||||
OnRouteConfigUpdate("", route_config);
|
||||
});
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class XdsVirtualHostListIterator : public XdsRouting::VirtualHostListIterator {
|
||||
public:
|
||||
explicit XdsVirtualHostListIterator(
|
||||
const std::vector<XdsRouteConfigResource::VirtualHost>* virtual_hosts)
|
||||
: virtual_hosts_(virtual_hosts) {}
|
||||
|
||||
size_t Size() const override { return virtual_hosts_->size(); }
|
||||
|
||||
const std::vector<std::string>& GetDomainsForVirtualHost(
|
||||
size_t index) const override {
|
||||
return (*virtual_hosts_)[index].domains;
|
||||
}
|
||||
|
||||
private:
|
||||
const std::vector<XdsRouteConfigResource::VirtualHost>* virtual_hosts_;
|
||||
};
|
||||
|
||||
// Gets the set of clusters referenced in the specified virtual host.
|
||||
absl::flat_hash_set<absl::string_view> GetClustersFromVirtualHost(
|
||||
const XdsRouteConfigResource::VirtualHost& virtual_host) {
|
||||
absl::flat_hash_set<absl::string_view> clusters;
|
||||
for (auto& route : virtual_host.routes) {
|
||||
auto* route_action =
|
||||
absl::get_if<XdsRouteConfigResource::Route::RouteAction>(&route.action);
|
||||
if (route_action == nullptr) continue;
|
||||
Match(
|
||||
route_action->action,
|
||||
// cluster name
|
||||
[&](const XdsRouteConfigResource::Route::RouteAction::ClusterName&
|
||||
cluster_name) { clusters.insert(cluster_name.cluster_name); },
|
||||
// WeightedClusters
|
||||
[&](const std::vector<
|
||||
XdsRouteConfigResource::Route::RouteAction::ClusterWeight>&
|
||||
weighted_clusters) {
|
||||
for (const auto& weighted_cluster : weighted_clusters) {
|
||||
clusters.insert(weighted_cluster.name);
|
||||
}
|
||||
},
|
||||
// ClusterSpecifierPlugin
|
||||
[&](const XdsRouteConfigResource::Route::RouteAction::
|
||||
ClusterSpecifierPluginName&) {
|
||||
// Clusters are determined dynamically in this case, so we
|
||||
// can't add any clusters here.
|
||||
});
|
||||
}
|
||||
return clusters;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void XdsDependencyManager::OnRouteConfigUpdate(
|
||||
const std::string& name,
|
||||
std::shared_ptr<const XdsRouteConfigResource> route_config) {
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) {
|
||||
gpr_log(GPR_INFO,
|
||||
"[XdsDependencyManager %p] received RouteConfig update for %s",
|
||||
this, name.empty() ? "<inline>" : name.c_str());
|
||||
}
|
||||
if (xds_client_ == nullptr) return;
|
||||
// Ignore updates for stale names.
|
||||
if (name.empty()) {
|
||||
if (!route_config_name_.empty()) return;
|
||||
} else {
|
||||
if (name != route_config_name_) return;
|
||||
}
|
||||
// Find the relevant VirtualHost from the RouteConfiguration.
|
||||
// If the resource doesn't have the right vhost, fail without updating
|
||||
// our data.
|
||||
auto vhost_index = XdsRouting::FindVirtualHostForDomain(
|
||||
XdsVirtualHostListIterator(&route_config->virtual_hosts),
|
||||
data_plane_authority_);
|
||||
if (!vhost_index.has_value()) {
|
||||
OnError(route_config_name_.empty() ? listener_resource_name_
|
||||
: route_config_name_,
|
||||
absl::UnavailableError(
|
||||
absl::StrCat("could not find VirtualHost for ",
|
||||
data_plane_authority_, " in RouteConfiguration")));
|
||||
return;
|
||||
}
|
||||
// Update our data.
|
||||
current_route_config_ = std::move(route_config);
|
||||
current_virtual_host_ = ¤t_route_config_->virtual_hosts[*vhost_index];
|
||||
clusters_from_route_config_ =
|
||||
GetClustersFromVirtualHost(*current_virtual_host_);
|
||||
MaybeReportUpdate();
|
||||
}
|
||||
|
||||
void XdsDependencyManager::OnError(std::string context, absl::Status status) {
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) {
|
||||
gpr_log(GPR_INFO,
|
||||
"[XdsDependencyManager %p] received Listener or RouteConfig "
|
||||
"error: %s %s",
|
||||
this, context.c_str(), status.ToString().c_str());
|
||||
}
|
||||
if (xds_client_ == nullptr) return;
|
||||
if (current_virtual_host_ != nullptr) return;
|
||||
watcher_->OnError(context, std::move(status));
|
||||
}
|
||||
|
||||
void XdsDependencyManager::OnResourceDoesNotExist(std::string context) {
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) {
|
||||
gpr_log(GPR_INFO, "[XdsDependencyManager %p] %s", this, context.c_str());
|
||||
}
|
||||
if (xds_client_ == nullptr) return;
|
||||
current_virtual_host_ = nullptr;
|
||||
watcher_->OnResourceDoesNotExist(std::move(context));
|
||||
}
|
||||
|
||||
void XdsDependencyManager::OnClusterUpdate(
|
||||
const std::string& name,
|
||||
std::shared_ptr<const XdsClusterResource> cluster) {
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) {
|
||||
gpr_log(GPR_INFO, "[XdsDependencyManager %p] received Cluster update: %s",
|
||||
this, name.c_str());
|
||||
}
|
||||
if (xds_client_ == nullptr) return;
|
||||
auto it = cluster_watchers_.find(name);
|
||||
if (it == cluster_watchers_.end()) return;
|
||||
it->second.update = std::move(cluster);
|
||||
MaybeReportUpdate();
|
||||
}
|
||||
|
||||
void XdsDependencyManager::OnClusterError(const std::string& name,
|
||||
absl::Status status) {
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) {
|
||||
gpr_log(GPR_INFO, "[XdsDependencyManager %p] received Cluster error: %s %s",
|
||||
this, name.c_str(), status.ToString().c_str());
|
||||
}
|
||||
if (xds_client_ == nullptr) return;
|
||||
auto it = cluster_watchers_.find(name);
|
||||
if (it == cluster_watchers_.end()) return;
|
||||
if (it->second.update.value_or(nullptr) == nullptr) {
|
||||
it->second.update =
|
||||
absl::Status(status.code(), absl::StrCat(name, ": ", status.message()));
|
||||
}
|
||||
MaybeReportUpdate();
|
||||
}
|
||||
|
||||
void XdsDependencyManager::OnClusterDoesNotExist(const std::string& name) {
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) {
|
||||
gpr_log(GPR_INFO, "[XdsDependencyManager %p] Cluster does not exist: %s",
|
||||
this, name.c_str());
|
||||
}
|
||||
if (xds_client_ == nullptr) return;
|
||||
auto it = cluster_watchers_.find(name);
|
||||
if (it == cluster_watchers_.end()) return;
|
||||
it->second.update = absl::UnavailableError(
|
||||
absl::StrCat("CDS resource ", name, " does not exist"));
|
||||
MaybeReportUpdate();
|
||||
}
|
||||
|
||||
void XdsDependencyManager::OnEndpointUpdate(
|
||||
const std::string& name,
|
||||
std::shared_ptr<const XdsEndpointResource> endpoint) {
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) {
|
||||
gpr_log(GPR_INFO, "[XdsDependencyManager %p] received Endpoint update: %s",
|
||||
this, name.c_str());
|
||||
}
|
||||
if (xds_client_ == nullptr) return;
|
||||
auto it = endpoint_watchers_.find(name);
|
||||
if (it == endpoint_watchers_.end()) return;
|
||||
if (endpoint->priorities.empty()) {
|
||||
it->second.update.resolution_note =
|
||||
absl::StrCat("EDS resource ", name, " contains no localities");
|
||||
} else {
|
||||
std::set<std::string> empty_localities;
|
||||
for (const auto& priority : endpoint->priorities) {
|
||||
for (const auto& p : priority.localities) {
|
||||
if (p.second.endpoints.empty()) {
|
||||
empty_localities.insert(p.first->AsHumanReadableString());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty_localities.empty()) {
|
||||
it->second.update.resolution_note =
|
||||
absl::StrCat("EDS resource ", name, " contains empty localities: [",
|
||||
absl::StrJoin(empty_localities, "; "), "]");
|
||||
}
|
||||
}
|
||||
it->second.update.endpoints = std::move(endpoint);
|
||||
MaybeReportUpdate();
|
||||
}
|
||||
|
||||
void XdsDependencyManager::OnEndpointError(const std::string& name,
|
||||
absl::Status status) {
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) {
|
||||
gpr_log(GPR_INFO,
|
||||
"[XdsDependencyManager %p] received Endpoint error: %s %s", this,
|
||||
name.c_str(), status.ToString().c_str());
|
||||
}
|
||||
if (xds_client_ == nullptr) return;
|
||||
auto it = endpoint_watchers_.find(name);
|
||||
if (it == endpoint_watchers_.end()) return;
|
||||
if (it->second.update.endpoints == nullptr) {
|
||||
it->second.update.resolution_note =
|
||||
absl::StrCat("EDS resource ", name, ": ", status.ToString());
|
||||
MaybeReportUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void XdsDependencyManager::OnEndpointDoesNotExist(const std::string& name) {
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) {
|
||||
gpr_log(GPR_INFO, "[XdsDependencyManager %p] Endpoint does not exist: %s",
|
||||
this, name.c_str());
|
||||
}
|
||||
if (xds_client_ == nullptr) return;
|
||||
auto it = endpoint_watchers_.find(name);
|
||||
if (it == endpoint_watchers_.end()) return;
|
||||
it->second.update.endpoints.reset();
|
||||
it->second.update.resolution_note =
|
||||
absl::StrCat("EDS resource ", name, " does not exist");
|
||||
MaybeReportUpdate();
|
||||
}
|
||||
|
||||
void XdsDependencyManager::OnDnsResult(const std::string& dns_name,
|
||||
Resolver::Result result) {
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) {
|
||||
gpr_log(GPR_INFO, "[XdsDependencyManager %p] received DNS update: %s", this,
|
||||
dns_name.c_str());
|
||||
}
|
||||
if (xds_client_ == nullptr) return;
|
||||
auto it = dns_resolvers_.find(dns_name);
|
||||
if (it == dns_resolvers_.end()) return;
|
||||
PopulateDnsUpdate(dns_name, std::move(result), &it->second);
|
||||
MaybeReportUpdate();
|
||||
}
|
||||
|
||||
void XdsDependencyManager::PopulateDnsUpdate(const std::string& dns_name,
|
||||
Resolver::Result result,
|
||||
DnsState* dns_state) {
|
||||
// Convert resolver result to EDS update.
|
||||
XdsEndpointResource::Priority::Locality locality;
|
||||
locality.name = MakeRefCounted<XdsLocalityName>("", "", "");
|
||||
locality.lb_weight = 1;
|
||||
if (result.addresses.ok()) {
|
||||
locality.endpoints = std::move(*result.addresses);
|
||||
dns_state->update.resolution_note = std::move(result.resolution_note);
|
||||
} else if (result.resolution_note.empty()) {
|
||||
dns_state->update.resolution_note =
|
||||
absl::StrCat("DNS resolution failed for ", dns_name, ": ",
|
||||
result.addresses.status().ToString());
|
||||
}
|
||||
XdsEndpointResource::Priority priority;
|
||||
priority.localities.emplace(locality.name.get(), std::move(locality));
|
||||
auto resource = std::make_shared<XdsEndpointResource>();
|
||||
resource->priorities.emplace_back(std::move(priority));
|
||||
dns_state->update.endpoints = std::move(resource);
|
||||
}
|
||||
|
||||
bool XdsDependencyManager::PopulateClusterConfigMap(
|
||||
absl::string_view name, int depth,
|
||||
absl::flat_hash_map<std::string, absl::StatusOr<XdsConfig::ClusterConfig>>*
|
||||
cluster_config_map,
|
||||
std::set<absl::string_view>* eds_resources_seen,
|
||||
std::set<absl::string_view>* dns_names_seen,
|
||||
absl::StatusOr<std::vector<absl::string_view>>* leaf_clusters) {
|
||||
if (depth > 0) GPR_ASSERT(leaf_clusters != nullptr);
|
||||
if (depth == kMaxXdsAggregateClusterRecursionDepth) {
|
||||
*leaf_clusters =
|
||||
absl::UnavailableError("aggregate cluster graph exceeds max depth");
|
||||
return true;
|
||||
}
|
||||
// Don't process the cluster again if we've already seen it in some
|
||||
// other branch of the recursion tree. We populate it with a non-OK
|
||||
// status here, since we need an entry in the map to avoid incorrectly
|
||||
// stopping the CDS watch, but we'll overwrite this below if we actually
|
||||
// have the data for the cluster.
|
||||
auto p = cluster_config_map->emplace(
|
||||
name, absl::InternalError("cluster data not yet available"));
|
||||
if (!p.second) return true;
|
||||
auto& cluster_config = p.first->second;
|
||||
auto& state = cluster_watchers_[name];
|
||||
// Create a new watcher if needed.
|
||||
if (state.watcher == nullptr) {
|
||||
auto watcher = MakeRefCounted<ClusterWatcher>(Ref(), name);
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) {
|
||||
gpr_log(GPR_INFO,
|
||||
"[XdsDependencyManager %p] starting watch for cluster %s", this,
|
||||
std::string(name).c_str());
|
||||
}
|
||||
state.watcher = watcher.get();
|
||||
XdsClusterResourceType::StartWatch(xds_client_.get(), name,
|
||||
std::move(watcher));
|
||||
return false;
|
||||
}
|
||||
// If there was an error fetching the CDS resource, report the error.
|
||||
if (!state.update.ok()) {
|
||||
cluster_config = state.update.status();
|
||||
return true;
|
||||
}
|
||||
// If we don't have the resource yet, we can't return a config yet.
|
||||
if (*state.update == nullptr) return false;
|
||||
// Populate endpoint info based on cluster type.
|
||||
return Match(
|
||||
(*state.update)->type,
|
||||
// EDS cluster.
|
||||
[&](const XdsClusterResource::Eds& eds) {
|
||||
absl::string_view eds_resource_name =
|
||||
eds.eds_service_name.empty() ? name : eds.eds_service_name;
|
||||
eds_resources_seen->insert(eds_resource_name);
|
||||
// Start EDS watch if needed.
|
||||
auto& eds_state = endpoint_watchers_[eds_resource_name];
|
||||
if (eds_state.watcher == nullptr) {
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) {
|
||||
gpr_log(GPR_INFO,
|
||||
"[XdsDependencyManager %p] starting watch for endpoint %s",
|
||||
this, std::string(eds_resource_name).c_str());
|
||||
}
|
||||
auto watcher =
|
||||
MakeRefCounted<EndpointWatcher>(Ref(), eds_resource_name);
|
||||
eds_state.watcher = watcher.get();
|
||||
XdsEndpointResourceType::StartWatch(
|
||||
xds_client_.get(), eds_resource_name, std::move(watcher));
|
||||
return false;
|
||||
}
|
||||
// Check if EDS resource has been returned.
|
||||
if (eds_state.update.endpoints == nullptr &&
|
||||
eds_state.update.resolution_note.empty()) {
|
||||
return false;
|
||||
}
|
||||
// Populate cluster config.
|
||||
cluster_config.emplace(*state.update, eds_state.update.endpoints,
|
||||
eds_state.update.resolution_note);
|
||||
if (leaf_clusters != nullptr) (*leaf_clusters)->push_back(name);
|
||||
return true;
|
||||
},
|
||||
// LOGICAL_DNS cluster.
|
||||
[&](const XdsClusterResource::LogicalDns& logical_dns) {
|
||||
dns_names_seen->insert(logical_dns.hostname);
|
||||
// Start DNS resolver if needed.
|
||||
auto& dns_state = dns_resolvers_[logical_dns.hostname];
|
||||
if (dns_state.resolver == nullptr) {
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) {
|
||||
gpr_log(GPR_INFO,
|
||||
"[XdsDependencyManager %p] starting DNS resolver for %s",
|
||||
this, logical_dns.hostname.c_str());
|
||||
}
|
||||
auto* fake_resolver_response_generator = args_.GetPointer<
|
||||
FakeResolverResponseGenerator>(
|
||||
GRPC_ARG_XDS_LOGICAL_DNS_CLUSTER_FAKE_RESOLVER_RESPONSE_GENERATOR);
|
||||
ChannelArgs args = args_;
|
||||
std::string target;
|
||||
if (fake_resolver_response_generator != nullptr) {
|
||||
target = absl::StrCat("fake:", logical_dns.hostname);
|
||||
args = args.SetObject(fake_resolver_response_generator->Ref());
|
||||
} else {
|
||||
target = absl::StrCat("dns:", logical_dns.hostname);
|
||||
}
|
||||
dns_state.resolver =
|
||||
CoreConfiguration::Get().resolver_registry().CreateResolver(
|
||||
target, args, interested_parties_, work_serializer_,
|
||||
std::make_unique<DnsResultHandler>(Ref(),
|
||||
logical_dns.hostname));
|
||||
if (dns_state.resolver == nullptr) {
|
||||
Resolver::Result result;
|
||||
result.addresses.emplace(); // Empty list.
|
||||
result.resolution_note = absl::StrCat(
|
||||
"failed to create DNS resolver for ", logical_dns.hostname);
|
||||
PopulateDnsUpdate(logical_dns.hostname, std::move(result),
|
||||
&dns_state);
|
||||
} else {
|
||||
dns_state.resolver->StartLocked();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Check if result has been returned.
|
||||
if (dns_state.update.endpoints == nullptr &&
|
||||
dns_state.update.resolution_note.empty()) {
|
||||
return false;
|
||||
}
|
||||
// Populate cluster config.
|
||||
cluster_config.emplace(*state.update, dns_state.update.endpoints,
|
||||
dns_state.update.resolution_note);
|
||||
if (leaf_clusters != nullptr) (*leaf_clusters)->push_back(name);
|
||||
return true;
|
||||
},
|
||||
// Aggregate cluster. Recursively expand to child clusters.
|
||||
[&](const XdsClusterResource::Aggregate& aggregate) {
|
||||
// Grab a ref to the CDS resource for the aggregate cluster here,
|
||||
// since our reference into cluster_watchers_ will be invalidated
|
||||
// when we recursively call ourselves and add entries to the
|
||||
// map for underlying clusters.
|
||||
auto cluster_resource = *state.update;
|
||||
// Recursively expand leaf clusters.
|
||||
absl::StatusOr<std::vector<absl::string_view>> child_leaf_clusters;
|
||||
child_leaf_clusters.emplace();
|
||||
bool have_all_resources = true;
|
||||
for (const std::string& child_name :
|
||||
aggregate.prioritized_cluster_names) {
|
||||
have_all_resources &= PopulateClusterConfigMap(
|
||||
child_name, depth + 1, cluster_config_map, eds_resources_seen,
|
||||
dns_names_seen, &child_leaf_clusters);
|
||||
if (!child_leaf_clusters.ok()) break;
|
||||
}
|
||||
// Note that we cannot use the cluster_config reference we
|
||||
// created above, because it may have been invalidated by map
|
||||
// insertions when we recursively called ourselves, so we have
|
||||
// to do the lookup in cluster_config_map again.
|
||||
auto& aggregate_cluster_config = (*cluster_config_map)[name];
|
||||
// If we exceeded max recursion depth, report an error for the
|
||||
// cluster, and propagate the error up if needed.
|
||||
if (!child_leaf_clusters.ok()) {
|
||||
aggregate_cluster_config = child_leaf_clusters.status();
|
||||
if (leaf_clusters != nullptr) {
|
||||
*leaf_clusters = child_leaf_clusters.status();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// If needed, propagate leaf cluster list up the tree.
|
||||
if (leaf_clusters != nullptr) {
|
||||
(*leaf_clusters)
|
||||
->insert((*leaf_clusters)->end(), child_leaf_clusters->begin(),
|
||||
child_leaf_clusters->end());
|
||||
}
|
||||
// If there are no leaf clusters, report an error for the cluster.
|
||||
if (have_all_resources && child_leaf_clusters->empty()) {
|
||||
aggregate_cluster_config = absl::UnavailableError(
|
||||
absl::StrCat("aggregate cluster dependency graph for ", name,
|
||||
" has no leaf clusters"));
|
||||
return true;
|
||||
}
|
||||
// Populate cluster config.
|
||||
// Note that we do this even for aggregate clusters that are not
|
||||
// at the root of the tree, because we need to make sure the list
|
||||
// of underlying cluster names stays alive so that the leaf cluster
|
||||
// list of the root aggregate cluster can point to those strings.
|
||||
aggregate_cluster_config.emplace(std::move(cluster_resource),
|
||||
std::move(*child_leaf_clusters));
|
||||
return have_all_resources;
|
||||
});
|
||||
}
|
||||
|
||||
RefCountedPtr<XdsDependencyManager::ClusterSubscription>
|
||||
XdsDependencyManager::GetClusterSubscription(absl::string_view cluster_name) {
|
||||
auto it = cluster_subscriptions_.find(cluster_name);
|
||||
if (it != cluster_subscriptions_.end()) {
|
||||
auto subscription = it->second->RefIfNonZero();
|
||||
if (subscription != nullptr) return subscription;
|
||||
}
|
||||
auto subscription = MakeRefCounted<ClusterSubscription>(cluster_name, Ref());
|
||||
cluster_subscriptions_.emplace(subscription->cluster_name(),
|
||||
subscription->WeakRef());
|
||||
// If the cluster is not already subscribed to by virtue of being
|
||||
// referenced in the route config, then trigger the CDS watch.
|
||||
if (!clusters_from_route_config_.contains(cluster_name)) {
|
||||
MaybeReportUpdate();
|
||||
}
|
||||
return subscription;
|
||||
}
|
||||
|
||||
void XdsDependencyManager::OnClusterSubscriptionUnref(
|
||||
absl::string_view cluster_name, ClusterSubscription* subscription) {
|
||||
auto it = cluster_subscriptions_.find(cluster_name);
|
||||
// Shouldn't happen, but ignore if it does.
|
||||
if (it == cluster_subscriptions_.end()) return;
|
||||
// Do nothing if the subscription has already been replaced.
|
||||
if (it->second != subscription) return;
|
||||
// Remove the entry.
|
||||
cluster_subscriptions_.erase(it);
|
||||
// If this cluster is not already subscribed to by virtue of being
|
||||
// referenced in the route config, then update watches and generate a
|
||||
// new update.
|
||||
if (!clusters_from_route_config_.contains(cluster_name)) {
|
||||
MaybeReportUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void XdsDependencyManager::MaybeReportUpdate() {
|
||||
// Populate Listener and RouteConfig fields.
|
||||
if (current_virtual_host_ == nullptr) return;
|
||||
auto config = MakeRefCounted<XdsConfig>();
|
||||
config->listener = current_listener_;
|
||||
config->route_config = current_route_config_;
|
||||
config->virtual_host = current_virtual_host_;
|
||||
// Determine the set of clusters we should be watching.
|
||||
std::set<absl::string_view> clusters_to_watch;
|
||||
for (const absl::string_view& cluster : clusters_from_route_config_) {
|
||||
clusters_to_watch.insert(cluster);
|
||||
}
|
||||
for (const auto& p : cluster_subscriptions_) {
|
||||
clusters_to_watch.insert(p.first);
|
||||
}
|
||||
// Populate Cluster map.
|
||||
// We traverse the entire graph even if we don't yet have all of the
|
||||
// resources we need to ensure that the right set of watches are active.
|
||||
std::set<absl::string_view> eds_resources_seen;
|
||||
std::set<absl::string_view> dns_names_seen;
|
||||
bool have_all_resources = true;
|
||||
for (const absl::string_view& cluster : clusters_to_watch) {
|
||||
have_all_resources &= PopulateClusterConfigMap(
|
||||
cluster, 0, &config->clusters, &eds_resources_seen, &dns_names_seen);
|
||||
}
|
||||
// Remove entries in cluster_watchers_ for any clusters not in
|
||||
// config->clusters.
|
||||
for (auto it = cluster_watchers_.begin(); it != cluster_watchers_.end();) {
|
||||
const std::string& cluster_name = it->first;
|
||||
if (config->clusters.contains(cluster_name)) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) {
|
||||
gpr_log(GPR_INFO,
|
||||
"[XdsDependencyManager %p] cancelling watch for cluster %s", this,
|
||||
cluster_name.c_str());
|
||||
}
|
||||
XdsClusterResourceType::CancelWatch(xds_client_.get(), cluster_name,
|
||||
it->second.watcher,
|
||||
/*delay_unsubscription=*/false);
|
||||
cluster_watchers_.erase(it++);
|
||||
}
|
||||
// Remove entries in endpoint_watchers_ for any EDS resources not in
|
||||
// eds_resources_seen.
|
||||
for (auto it = endpoint_watchers_.begin(); it != endpoint_watchers_.end();) {
|
||||
const std::string& eds_resource_name = it->first;
|
||||
if (eds_resources_seen.find(eds_resource_name) !=
|
||||
eds_resources_seen.end()) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) {
|
||||
gpr_log(GPR_INFO,
|
||||
"[XdsDependencyManager %p] cancelling watch for EDS resource %s",
|
||||
this, eds_resource_name.c_str());
|
||||
}
|
||||
XdsEndpointResourceType::CancelWatch(xds_client_.get(), eds_resource_name,
|
||||
it->second.watcher,
|
||||
/*delay_unsubscription=*/false);
|
||||
endpoint_watchers_.erase(it++);
|
||||
}
|
||||
// Remove entries in dns_resolvers_ for any DNS name not in
|
||||
// eds_resources_seen.
|
||||
for (auto it = dns_resolvers_.begin(); it != dns_resolvers_.end();) {
|
||||
const std::string& dns_name = it->first;
|
||||
if (dns_names_seen.find(dns_name) != dns_names_seen.end()) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) {
|
||||
gpr_log(GPR_INFO,
|
||||
"[XdsDependencyManager %p] shutting down DNS resolver for %s",
|
||||
this, dns_name.c_str());
|
||||
}
|
||||
dns_resolvers_.erase(it++);
|
||||
}
|
||||
// If we have all the data we need, then send an update.
|
||||
if (!have_all_resources) {
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) {
|
||||
gpr_log(GPR_INFO,
|
||||
"[XdsDependencyManager %p] missing data -- NOT returning config",
|
||||
this);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) {
|
||||
gpr_log(GPR_INFO, "[XdsDependencyManager %p] returning config: %s", this,
|
||||
config->ToString().c_str());
|
||||
}
|
||||
watcher_->OnUpdate(std::move(config));
|
||||
}
|
||||
|
||||
} // namespace grpc_core
|
||||
277
Pods/gRPC-Core/src/core/resolver/xds/xds_dependency_manager.h
generated
Normal file
277
Pods/gRPC-Core/src/core/resolver/xds/xds_dependency_manager.h
generated
Normal file
@@ -0,0 +1,277 @@
|
||||
//
|
||||
// Copyright 2019 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#ifndef GRPC_SRC_CORE_RESOLVER_XDS_XDS_DEPENDENCY_MANAGER_H
|
||||
#define GRPC_SRC_CORE_RESOLVER_XDS_XDS_DEPENDENCY_MANAGER_H
|
||||
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include "absl/container/flat_hash_set.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
#include "src/core/ext/xds/xds_client_grpc.h"
|
||||
#include "src/core/ext/xds/xds_cluster.h"
|
||||
#include "src/core/ext/xds/xds_endpoint.h"
|
||||
#include "src/core/ext/xds/xds_listener.h"
|
||||
#include "src/core/ext/xds/xds_route_config.h"
|
||||
#include "src/core/lib/gprpp/ref_counted.h"
|
||||
#include "src/core/resolver/resolver.h"
|
||||
|
||||
namespace grpc_core {
|
||||
|
||||
// Watches all xDS resources and handles dependencies between them.
|
||||
// Reports updates only when all necessary resources have been obtained.
|
||||
class XdsDependencyManager : public RefCounted<XdsDependencyManager>,
|
||||
public Orphanable {
|
||||
public:
|
||||
struct XdsConfig : public RefCounted<XdsConfig> {
|
||||
// Listener resource. Always non-null.
|
||||
std::shared_ptr<const XdsListenerResource> listener;
|
||||
// RouteConfig resource. Will be populated even if RouteConfig is
|
||||
// inlined into the Listener resource.
|
||||
std::shared_ptr<const XdsRouteConfigResource> route_config;
|
||||
// Virtual host. Points into route_config. Will always be non-null.
|
||||
const XdsRouteConfigResource::VirtualHost* virtual_host;
|
||||
|
||||
struct ClusterConfig {
|
||||
// Cluster resource. Always non-null.
|
||||
std::shared_ptr<const XdsClusterResource> cluster;
|
||||
// Endpoint info for EDS and LOGICAL_DNS clusters. If there was an
|
||||
// error, endpoints will be null and resolution_note will be set.
|
||||
struct EndpointConfig {
|
||||
std::shared_ptr<const XdsEndpointResource> endpoints;
|
||||
std::string resolution_note;
|
||||
|
||||
EndpointConfig(std::shared_ptr<const XdsEndpointResource> endpoints,
|
||||
std::string resolution_note)
|
||||
: endpoints(std::move(endpoints)),
|
||||
resolution_note(std::move(resolution_note)) {}
|
||||
bool operator==(const EndpointConfig& other) const {
|
||||
return endpoints == other.endpoints &&
|
||||
resolution_note == other.resolution_note;
|
||||
}
|
||||
};
|
||||
// The list of leaf clusters for an aggregate cluster.
|
||||
struct AggregateConfig {
|
||||
std::vector<absl::string_view> leaf_clusters;
|
||||
|
||||
explicit AggregateConfig(std::vector<absl::string_view> leaf_clusters)
|
||||
: leaf_clusters(std::move(leaf_clusters)) {}
|
||||
bool operator==(const AggregateConfig& other) const {
|
||||
return leaf_clusters == other.leaf_clusters;
|
||||
}
|
||||
};
|
||||
absl::variant<EndpointConfig, AggregateConfig> children;
|
||||
|
||||
// Ctor for leaf clusters.
|
||||
ClusterConfig(std::shared_ptr<const XdsClusterResource> cluster,
|
||||
std::shared_ptr<const XdsEndpointResource> endpoints,
|
||||
std::string resolution_note);
|
||||
// Ctor for aggregate clusters.
|
||||
ClusterConfig(std::shared_ptr<const XdsClusterResource> cluster,
|
||||
std::vector<absl::string_view> leaf_clusters);
|
||||
|
||||
bool operator==(const ClusterConfig& other) const {
|
||||
return cluster == other.cluster && children == other.children;
|
||||
}
|
||||
};
|
||||
// Cluster map. A cluster will have a non-OK status if either
|
||||
// (a) there was an error and we did not already have a valid
|
||||
// resource or (b) the resource does not exist.
|
||||
absl::flat_hash_map<std::string, absl::StatusOr<ClusterConfig>> clusters;
|
||||
|
||||
std::string ToString() const;
|
||||
|
||||
static absl::string_view ChannelArgName() {
|
||||
return GRPC_ARG_NO_SUBCHANNEL_PREFIX "xds_config";
|
||||
}
|
||||
static int ChannelArgsCompare(const XdsConfig* a, const XdsConfig* b) {
|
||||
return QsortCompare(a, b);
|
||||
}
|
||||
static constexpr bool ChannelArgUseConstPtr() { return true; }
|
||||
};
|
||||
|
||||
class Watcher {
|
||||
public:
|
||||
virtual ~Watcher() = default;
|
||||
|
||||
virtual void OnUpdate(RefCountedPtr<const XdsConfig> config) = 0;
|
||||
|
||||
// These methods are invoked when there is an error or
|
||||
// does-not-exist on LDS or RDS only.
|
||||
virtual void OnError(absl::string_view context, absl::Status status) = 0;
|
||||
virtual void OnResourceDoesNotExist(std::string context) = 0;
|
||||
};
|
||||
|
||||
class ClusterSubscription : public DualRefCounted<ClusterSubscription> {
|
||||
public:
|
||||
ClusterSubscription(absl::string_view cluster_name,
|
||||
RefCountedPtr<XdsDependencyManager> dependency_mgr)
|
||||
: cluster_name_(cluster_name),
|
||||
dependency_mgr_(std::move(dependency_mgr)) {}
|
||||
|
||||
void Orphan() override;
|
||||
|
||||
absl::string_view cluster_name() const { return cluster_name_; }
|
||||
|
||||
private:
|
||||
std::string cluster_name_;
|
||||
RefCountedPtr<XdsDependencyManager> dependency_mgr_;
|
||||
};
|
||||
|
||||
XdsDependencyManager(RefCountedPtr<GrpcXdsClient> xds_client,
|
||||
std::shared_ptr<WorkSerializer> work_serializer,
|
||||
std::unique_ptr<Watcher> watcher,
|
||||
std::string data_plane_authority,
|
||||
std::string listener_resource_name, ChannelArgs args,
|
||||
grpc_pollset_set* interested_parties);
|
||||
|
||||
void Orphan() override;
|
||||
|
||||
// Gets an external cluster subscription. This allows us to include
|
||||
// clusters in the config that are referenced by something other than
|
||||
// the route config (e.g., RLS). The cluster will be included in the
|
||||
// config as long as the returned object is still referenced.
|
||||
RefCountedPtr<ClusterSubscription> GetClusterSubscription(
|
||||
absl::string_view cluster_name);
|
||||
|
||||
static absl::string_view ChannelArgName() {
|
||||
return GRPC_ARG_NO_SUBCHANNEL_PREFIX "xds_dependency_manager";
|
||||
}
|
||||
static int ChannelArgsCompare(const XdsDependencyManager* a,
|
||||
const XdsDependencyManager* b) {
|
||||
return QsortCompare(a, b);
|
||||
}
|
||||
|
||||
private:
|
||||
class ListenerWatcher;
|
||||
class RouteConfigWatcher;
|
||||
class ClusterWatcher;
|
||||
class EndpointWatcher;
|
||||
|
||||
class DnsResultHandler;
|
||||
|
||||
struct ClusterWatcherState {
|
||||
// Pointer to watcher, to be used when cancelling.
|
||||
// Not owned, so do not dereference.
|
||||
ClusterWatcher* watcher = nullptr;
|
||||
// Most recent update obtained from this watcher.
|
||||
absl::StatusOr<std::shared_ptr<const XdsClusterResource>> update = nullptr;
|
||||
};
|
||||
|
||||
struct EndpointConfig {
|
||||
// If there was an error, update will be null and resolution_note
|
||||
// will be non-empty.
|
||||
std::shared_ptr<const XdsEndpointResource> endpoints;
|
||||
std::string resolution_note;
|
||||
};
|
||||
|
||||
struct EndpointWatcherState {
|
||||
// Pointer to watcher, to be used when cancelling.
|
||||
// Not owned, so do not dereference.
|
||||
EndpointWatcher* watcher = nullptr;
|
||||
// Most recent update obtained from this watcher.
|
||||
EndpointConfig update;
|
||||
};
|
||||
|
||||
struct DnsState {
|
||||
OrphanablePtr<Resolver> resolver;
|
||||
// Most recent result from the resolver.
|
||||
EndpointConfig update;
|
||||
};
|
||||
|
||||
// Event handlers.
|
||||
void OnListenerUpdate(std::shared_ptr<const XdsListenerResource> listener);
|
||||
void OnRouteConfigUpdate(
|
||||
const std::string& name,
|
||||
std::shared_ptr<const XdsRouteConfigResource> route_config);
|
||||
void OnError(std::string context, absl::Status status);
|
||||
void OnResourceDoesNotExist(std::string context);
|
||||
|
||||
void OnClusterUpdate(const std::string& name,
|
||||
std::shared_ptr<const XdsClusterResource> cluster);
|
||||
void OnClusterError(const std::string& name, absl::Status status);
|
||||
void OnClusterDoesNotExist(const std::string& name);
|
||||
|
||||
void OnEndpointUpdate(const std::string& name,
|
||||
std::shared_ptr<const XdsEndpointResource> endpoint);
|
||||
void OnEndpointError(const std::string& name, absl::Status status);
|
||||
void OnEndpointDoesNotExist(const std::string& name);
|
||||
|
||||
void OnDnsResult(const std::string& dns_name, Resolver::Result result);
|
||||
void PopulateDnsUpdate(const std::string& dns_name, Resolver::Result result,
|
||||
DnsState* dns_state);
|
||||
|
||||
// Starts CDS and EDS/DNS watches for the specified cluster if needed.
|
||||
// Adds an entry to cluster_config_map, which will contain the cluster
|
||||
// data if the data is available.
|
||||
// For each EDS cluster, adds the EDS resource to eds_resources_seen.
|
||||
// For each Logical DNS cluster, adds the DNS hostname to dns_names_seen.
|
||||
// For aggregate clusters, calls itself recursively. If leaf_clusters is
|
||||
// non-null, populates it with a list of leaf clusters, or an error if
|
||||
// max depth is exceeded.
|
||||
// Returns true if all resources have been obtained.
|
||||
bool PopulateClusterConfigMap(
|
||||
absl::string_view name, int depth,
|
||||
absl::flat_hash_map<std::string,
|
||||
absl::StatusOr<XdsConfig::ClusterConfig>>*
|
||||
cluster_config_map,
|
||||
std::set<absl::string_view>* eds_resources_seen,
|
||||
std::set<absl::string_view>* dns_names_seen,
|
||||
absl::StatusOr<std::vector<absl::string_view>>* leaf_clusters = nullptr);
|
||||
|
||||
// Called when an external cluster subscription is unreffed.
|
||||
void OnClusterSubscriptionUnref(absl::string_view cluster_name,
|
||||
ClusterSubscription* subscription);
|
||||
|
||||
// Checks whether all necessary resources have been obtained, and if
|
||||
// so reports an update to the watcher.
|
||||
void MaybeReportUpdate();
|
||||
|
||||
// Parameters passed into ctor.
|
||||
RefCountedPtr<GrpcXdsClient> xds_client_;
|
||||
std::shared_ptr<WorkSerializer> work_serializer_;
|
||||
std::unique_ptr<Watcher> watcher_;
|
||||
const std::string data_plane_authority_;
|
||||
const std::string listener_resource_name_;
|
||||
ChannelArgs args_;
|
||||
grpc_pollset_set* interested_parties_;
|
||||
|
||||
// Listener state.
|
||||
ListenerWatcher* listener_watcher_ = nullptr;
|
||||
std::shared_ptr<const XdsListenerResource> current_listener_;
|
||||
std::string route_config_name_;
|
||||
|
||||
// RouteConfig state.
|
||||
RouteConfigWatcher* route_config_watcher_ = nullptr;
|
||||
std::shared_ptr<const XdsRouteConfigResource> current_route_config_;
|
||||
const XdsRouteConfigResource::VirtualHost* current_virtual_host_ = nullptr;
|
||||
absl::flat_hash_set<absl::string_view> clusters_from_route_config_;
|
||||
|
||||
// Cluster state.
|
||||
absl::flat_hash_map<std::string, ClusterWatcherState> cluster_watchers_;
|
||||
absl::flat_hash_map<absl::string_view, WeakRefCountedPtr<ClusterSubscription>>
|
||||
cluster_subscriptions_;
|
||||
|
||||
// Endpoint state.
|
||||
absl::flat_hash_map<std::string, EndpointWatcherState> endpoint_watchers_;
|
||||
absl::flat_hash_map<std::string, DnsState> dns_resolvers_;
|
||||
};
|
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_SRC_CORE_RESOLVER_XDS_XDS_DEPENDENCY_MANAGER_H
|
||||
1135
Pods/gRPC-Core/src/core/resolver/xds/xds_resolver.cc
generated
Normal file
1135
Pods/gRPC-Core/src/core/resolver/xds/xds_resolver.cc
generated
Normal file
@@ -0,0 +1,1135 @@
|
||||
//
|
||||
// Copyright 2019 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/meta/type_traits.h"
|
||||
#include "absl/random/random.h"
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/match.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "absl/strings/str_join.h"
|
||||
#include "absl/strings/str_replace.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/strings/strip.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "absl/types/variant.h"
|
||||
#include "re2/re2.h"
|
||||
|
||||
#include <grpc/impl/channel_arg_names.h>
|
||||
#include <grpc/slice.h>
|
||||
#include <grpc/status.h>
|
||||
#include <grpc/support/log.h>
|
||||
|
||||
#include "src/core/client_channel/client_channel_internal.h"
|
||||
#include "src/core/client_channel/config_selector.h"
|
||||
#include "src/core/ext/xds/xds_bootstrap.h"
|
||||
#include "src/core/ext/xds/xds_bootstrap_grpc.h"
|
||||
#include "src/core/ext/xds/xds_client_grpc.h"
|
||||
#include "src/core/ext/xds/xds_http_filters.h"
|
||||
#include "src/core/ext/xds/xds_listener.h"
|
||||
#include "src/core/ext/xds/xds_route_config.h"
|
||||
#include "src/core/ext/xds/xds_routing.h"
|
||||
#include "src/core/lib/channel/channel_args.h"
|
||||
#include "src/core/lib/channel/channel_fwd.h"
|
||||
#include "src/core/lib/channel/channel_stack.h"
|
||||
#include "src/core/lib/channel/context.h"
|
||||
#include "src/core/lib/channel/promise_based_filter.h"
|
||||
#include "src/core/lib/channel/status_util.h"
|
||||
#include "src/core/lib/config/core_configuration.h"
|
||||
#include "src/core/lib/debug/trace.h"
|
||||
#include "src/core/lib/experiments/experiments.h"
|
||||
#include "src/core/lib/gprpp/debug_location.h"
|
||||
#include "src/core/lib/gprpp/dual_ref_counted.h"
|
||||
#include "src/core/lib/gprpp/match.h"
|
||||
#include "src/core/lib/gprpp/orphanable.h"
|
||||
#include "src/core/lib/gprpp/ref_counted.h"
|
||||
#include "src/core/lib/gprpp/ref_counted_ptr.h"
|
||||
#include "src/core/lib/gprpp/time.h"
|
||||
#include "src/core/lib/gprpp/work_serializer.h"
|
||||
#include "src/core/lib/gprpp/xxhash_inline.h"
|
||||
#include "src/core/lib/iomgr/iomgr_fwd.h"
|
||||
#include "src/core/lib/iomgr/pollset_set.h"
|
||||
#include "src/core/lib/promise/arena_promise.h"
|
||||
#include "src/core/lib/promise/context.h"
|
||||
#include "src/core/resolver/endpoint_addresses.h"
|
||||
#include "src/core/resolver/resolver.h"
|
||||
#include "src/core/resolver/resolver_factory.h"
|
||||
#include "src/core/lib/resource_quota/arena.h"
|
||||
#include "src/core/service_config/service_config.h"
|
||||
#include "src/core/service_config/service_config_impl.h"
|
||||
#include "src/core/lib/slice/slice.h"
|
||||
#include "src/core/lib/transport/metadata_batch.h"
|
||||
#include "src/core/lib/transport/transport.h"
|
||||
#include "src/core/lib/uri/uri_parser.h"
|
||||
#include "src/core/load_balancing/ring_hash/ring_hash.h"
|
||||
#include "src/core/resolver/xds/xds_dependency_manager.h"
|
||||
#include "src/core/resolver/xds/xds_resolver_attributes.h"
|
||||
#include "src/core/resolver/xds/xds_resolver_trace.h"
|
||||
|
||||
namespace grpc_core {
|
||||
|
||||
namespace {
|
||||
|
||||
//
|
||||
// XdsResolver
|
||||
//
|
||||
|
||||
class XdsResolver : public Resolver {
|
||||
public:
|
||||
XdsResolver(ResolverArgs args, std::string data_plane_authority)
|
||||
: work_serializer_(std::move(args.work_serializer)),
|
||||
result_handler_(std::move(args.result_handler)),
|
||||
args_(std::move(args.args)),
|
||||
interested_parties_(args.pollset_set),
|
||||
uri_(std::move(args.uri)),
|
||||
data_plane_authority_(std::move(data_plane_authority)),
|
||||
channel_id_(absl::Uniform<uint64_t>(absl::BitGen())) {
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) {
|
||||
gpr_log(
|
||||
GPR_INFO,
|
||||
"[xds_resolver %p] created for URI %s; data plane authority is %s",
|
||||
this, uri_.ToString().c_str(), data_plane_authority_.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
~XdsResolver() override {
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) {
|
||||
gpr_log(GPR_INFO, "[xds_resolver %p] destroyed", this);
|
||||
}
|
||||
}
|
||||
|
||||
void StartLocked() override;
|
||||
|
||||
void ShutdownLocked() override;
|
||||
|
||||
void ResetBackoffLocked() override {
|
||||
if (xds_client_ != nullptr) xds_client_->ResetBackoff();
|
||||
}
|
||||
|
||||
private:
|
||||
class XdsWatcher : public XdsDependencyManager::Watcher {
|
||||
public:
|
||||
explicit XdsWatcher(RefCountedPtr<XdsResolver> resolver)
|
||||
: resolver_(std::move(resolver)) {}
|
||||
|
||||
void OnUpdate(
|
||||
RefCountedPtr<const XdsDependencyManager::XdsConfig> config) override {
|
||||
resolver_->OnUpdate(std::move(config));
|
||||
}
|
||||
|
||||
void OnError(absl::string_view context, absl::Status status) override {
|
||||
resolver_->OnError(context, std::move(status));
|
||||
}
|
||||
|
||||
void OnResourceDoesNotExist(std::string context) override {
|
||||
resolver_->OnResourceDoesNotExist(std::move(context));
|
||||
}
|
||||
|
||||
private:
|
||||
RefCountedPtr<XdsResolver> resolver_;
|
||||
};
|
||||
|
||||
// An entry in the map of clusters that need to be present in the LB
|
||||
// policy config. The map holds a weak ref. One strong ref is held by
|
||||
// the ConfigSelector, and another is held by each call assigned to
|
||||
// the cluster by the ConfigSelector. The ref for each call is held
|
||||
// until the call is committed. When the strong refs go away, we hop
|
||||
// back into the WorkSerializer to remove the entry from the map.
|
||||
class ClusterRef : public DualRefCounted<ClusterRef> {
|
||||
public:
|
||||
ClusterRef(RefCountedPtr<XdsResolver> resolver,
|
||||
RefCountedPtr<XdsDependencyManager::ClusterSubscription>
|
||||
cluster_subscription,
|
||||
absl::string_view cluster_key)
|
||||
: resolver_(std::move(resolver)),
|
||||
cluster_subscription_(std::move(cluster_subscription)),
|
||||
cluster_key_(cluster_key) {}
|
||||
|
||||
void Orphan() override {
|
||||
XdsResolver* resolver_ptr = resolver_.get();
|
||||
resolver_ptr->work_serializer_->Run(
|
||||
[resolver = std::move(resolver_)]() {
|
||||
resolver->MaybeRemoveUnusedClusters();
|
||||
},
|
||||
DEBUG_LOCATION);
|
||||
cluster_subscription_.reset();
|
||||
}
|
||||
|
||||
const std::string& cluster_key() const { return cluster_key_; }
|
||||
|
||||
private:
|
||||
RefCountedPtr<XdsResolver> resolver_;
|
||||
RefCountedPtr<XdsDependencyManager::ClusterSubscription>
|
||||
cluster_subscription_;
|
||||
std::string cluster_key_;
|
||||
};
|
||||
|
||||
// A routing data including cluster refs and routes table held by the
|
||||
// XdsConfigSelector. A ref to this map will be taken by each call processed
|
||||
// by the XdsConfigSelector, stored in a the call's call attributes, and later
|
||||
// unreffed by the ClusterSelection filter.
|
||||
class RouteConfigData : public RefCounted<RouteConfigData> {
|
||||
public:
|
||||
struct RouteEntry {
|
||||
struct ClusterWeightState {
|
||||
uint32_t range_end;
|
||||
absl::string_view cluster;
|
||||
RefCountedPtr<ServiceConfig> method_config;
|
||||
|
||||
bool operator==(const ClusterWeightState& other) const {
|
||||
return range_end == other.range_end && cluster == other.cluster &&
|
||||
MethodConfigsEqual(method_config.get(),
|
||||
other.method_config.get());
|
||||
}
|
||||
};
|
||||
|
||||
XdsRouteConfigResource::Route route;
|
||||
RefCountedPtr<ServiceConfig> method_config;
|
||||
std::vector<ClusterWeightState> weighted_cluster_state;
|
||||
|
||||
explicit RouteEntry(const XdsRouteConfigResource::Route& r) : route(r) {}
|
||||
|
||||
bool operator==(const RouteEntry& other) const {
|
||||
return route == other.route &&
|
||||
weighted_cluster_state == other.weighted_cluster_state &&
|
||||
MethodConfigsEqual(method_config.get(),
|
||||
other.method_config.get());
|
||||
}
|
||||
};
|
||||
|
||||
static absl::StatusOr<RefCountedPtr<RouteConfigData>> Create(
|
||||
XdsResolver* resolver, const Duration& default_max_stream_duration);
|
||||
|
||||
bool operator==(const RouteConfigData& other) const {
|
||||
return clusters_ == other.clusters_ && routes_ == other.routes_;
|
||||
}
|
||||
|
||||
RefCountedPtr<ClusterRef> FindClusterRef(absl::string_view name) const {
|
||||
auto it = clusters_.find(name);
|
||||
if (it == clusters_.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
RouteEntry* GetRouteForRequest(absl::string_view path,
|
||||
grpc_metadata_batch* initial_metadata);
|
||||
|
||||
private:
|
||||
class RouteListIterator;
|
||||
|
||||
static absl::StatusOr<RefCountedPtr<ServiceConfig>> CreateMethodConfig(
|
||||
XdsResolver* resolver, const XdsRouteConfigResource::Route& route,
|
||||
const XdsRouteConfigResource::Route::RouteAction::ClusterWeight*
|
||||
cluster_weight);
|
||||
|
||||
static bool MethodConfigsEqual(const ServiceConfig* sc1,
|
||||
const ServiceConfig* sc2) {
|
||||
if (sc1 == nullptr) return sc2 == nullptr;
|
||||
if (sc2 == nullptr) return false;
|
||||
return sc1->json_string() == sc2->json_string();
|
||||
}
|
||||
|
||||
absl::Status AddRouteEntry(XdsResolver* resolver,
|
||||
const XdsRouteConfigResource::Route& route,
|
||||
const Duration& default_max_stream_duration);
|
||||
|
||||
std::map<absl::string_view, RefCountedPtr<ClusterRef>> clusters_;
|
||||
std::vector<RouteEntry> routes_;
|
||||
};
|
||||
|
||||
class XdsConfigSelector : public ConfigSelector {
|
||||
public:
|
||||
XdsConfigSelector(RefCountedPtr<XdsResolver> resolver,
|
||||
RefCountedPtr<RouteConfigData> route_config_data);
|
||||
~XdsConfigSelector() override;
|
||||
|
||||
const char* name() const override { return "XdsConfigSelector"; }
|
||||
|
||||
bool Equals(const ConfigSelector* other) const override {
|
||||
const auto* other_xds = static_cast<const XdsConfigSelector*>(other);
|
||||
// Don't need to compare resolver_, since that will always be the same.
|
||||
return *route_config_data_ == *other_xds->route_config_data_ &&
|
||||
filters_ == other_xds->filters_;
|
||||
}
|
||||
|
||||
absl::Status GetCallConfig(GetCallConfigArgs args) override;
|
||||
|
||||
std::vector<const grpc_channel_filter*> GetFilters() override {
|
||||
return filters_;
|
||||
}
|
||||
|
||||
private:
|
||||
RefCountedPtr<XdsResolver> resolver_;
|
||||
RefCountedPtr<RouteConfigData> route_config_data_;
|
||||
std::vector<const grpc_channel_filter*> filters_;
|
||||
};
|
||||
|
||||
class XdsRouteStateAttributeImpl : public XdsRouteStateAttribute {
|
||||
public:
|
||||
explicit XdsRouteStateAttributeImpl(
|
||||
RefCountedPtr<RouteConfigData> route_config_data,
|
||||
RouteConfigData::RouteEntry* route)
|
||||
: route_config_data_(std::move(route_config_data)), route_(route) {}
|
||||
|
||||
// This method can be called only once. The first call will release
|
||||
// the reference to the cluster map, and subsequent calls will return
|
||||
// nullptr.
|
||||
RefCountedPtr<ClusterRef> LockAndGetCluster(absl::string_view cluster_name);
|
||||
|
||||
bool HasClusterForRoute(absl::string_view cluster_name) const override;
|
||||
|
||||
private:
|
||||
RefCountedPtr<RouteConfigData> route_config_data_;
|
||||
RouteConfigData::RouteEntry* route_;
|
||||
};
|
||||
|
||||
class ClusterSelectionFilter
|
||||
: public ImplementChannelFilter<ClusterSelectionFilter> {
|
||||
public:
|
||||
const static grpc_channel_filter kFilter;
|
||||
|
||||
static absl::StatusOr<ClusterSelectionFilter> Create(
|
||||
const ChannelArgs& /* unused */, ChannelFilter::Args filter_args) {
|
||||
return ClusterSelectionFilter(filter_args);
|
||||
}
|
||||
|
||||
// Construct a promise for one call.
|
||||
class Call {
|
||||
public:
|
||||
void OnClientInitialMetadata(ClientMetadata& md);
|
||||
static const NoInterceptor OnServerInitialMetadata;
|
||||
static const NoInterceptor OnServerTrailingMetadata;
|
||||
static const NoInterceptor OnClientToServerMessage;
|
||||
static const NoInterceptor OnServerToClientMessage;
|
||||
static const NoInterceptor OnFinalize;
|
||||
};
|
||||
|
||||
private:
|
||||
explicit ClusterSelectionFilter(ChannelFilter::Args filter_args)
|
||||
: filter_args_(filter_args) {}
|
||||
|
||||
ChannelFilter::Args filter_args_;
|
||||
};
|
||||
|
||||
RefCountedPtr<ClusterRef> GetOrCreateClusterRef(
|
||||
absl::string_view cluster_key, absl::string_view cluster_name) {
|
||||
auto it = cluster_ref_map_.find(cluster_key);
|
||||
if (it == cluster_ref_map_.end()) {
|
||||
RefCountedPtr<XdsDependencyManager::ClusterSubscription> subscription;
|
||||
if (!cluster_name.empty()) {
|
||||
// The cluster ref will hold a subscription to ensure that the
|
||||
// XdsDependencyManager stays subscribed to the CDS resource as
|
||||
// long as the cluster ref exists.
|
||||
subscription = dependency_mgr_->GetClusterSubscription(cluster_name);
|
||||
}
|
||||
auto cluster = MakeRefCounted<ClusterRef>(
|
||||
RefAsSubclass<XdsResolver>(), std::move(subscription), cluster_key);
|
||||
cluster_ref_map_.emplace(cluster->cluster_key(), cluster->WeakRef());
|
||||
return cluster;
|
||||
}
|
||||
return it->second->Ref();
|
||||
}
|
||||
|
||||
void OnUpdate(RefCountedPtr<const XdsDependencyManager::XdsConfig> config);
|
||||
void OnError(absl::string_view context, absl::Status status);
|
||||
void OnResourceDoesNotExist(std::string context);
|
||||
|
||||
absl::StatusOr<RefCountedPtr<ServiceConfig>> CreateServiceConfig();
|
||||
void GenerateResult();
|
||||
void MaybeRemoveUnusedClusters();
|
||||
|
||||
std::shared_ptr<WorkSerializer> work_serializer_;
|
||||
std::unique_ptr<ResultHandler> result_handler_;
|
||||
ChannelArgs args_;
|
||||
grpc_pollset_set* interested_parties_;
|
||||
URI uri_;
|
||||
RefCountedPtr<GrpcXdsClient> xds_client_;
|
||||
std::string lds_resource_name_;
|
||||
std::string data_plane_authority_;
|
||||
const uint64_t channel_id_;
|
||||
|
||||
OrphanablePtr<XdsDependencyManager> dependency_mgr_;
|
||||
RefCountedPtr<const XdsDependencyManager::XdsConfig> current_config_;
|
||||
std::map<absl::string_view, WeakRefCountedPtr<ClusterRef>> cluster_ref_map_;
|
||||
};
|
||||
|
||||
const NoInterceptor
|
||||
XdsResolver::ClusterSelectionFilter::Call::OnServerInitialMetadata;
|
||||
const NoInterceptor
|
||||
XdsResolver::ClusterSelectionFilter::Call::OnServerTrailingMetadata;
|
||||
const NoInterceptor
|
||||
XdsResolver::ClusterSelectionFilter::Call::OnClientToServerMessage;
|
||||
const NoInterceptor
|
||||
XdsResolver::ClusterSelectionFilter::Call::OnServerToClientMessage;
|
||||
const NoInterceptor XdsResolver::ClusterSelectionFilter::Call::OnFinalize;
|
||||
|
||||
//
|
||||
// XdsResolver::RouteConfigData::RouteListIterator
|
||||
//
|
||||
|
||||
// Implementation of XdsRouting::RouteListIterator for getting the matching
|
||||
// route for a request.
|
||||
class XdsResolver::RouteConfigData::RouteListIterator
|
||||
: public XdsRouting::RouteListIterator {
|
||||
public:
|
||||
explicit RouteListIterator(const RouteConfigData* route_table)
|
||||
: route_table_(route_table) {}
|
||||
|
||||
size_t Size() const override { return route_table_->routes_.size(); }
|
||||
|
||||
const XdsRouteConfigResource::Route::Matchers& GetMatchersForRoute(
|
||||
size_t index) const override {
|
||||
return route_table_->routes_[index].route.matchers;
|
||||
}
|
||||
|
||||
private:
|
||||
const RouteConfigData* route_table_;
|
||||
};
|
||||
|
||||
//
|
||||
// XdsResolver::RouteConfigData
|
||||
//
|
||||
|
||||
absl::StatusOr<RefCountedPtr<XdsResolver::RouteConfigData>>
|
||||
XdsResolver::RouteConfigData::Create(
|
||||
XdsResolver* resolver, const Duration& default_max_stream_duration) {
|
||||
auto data = MakeRefCounted<RouteConfigData>();
|
||||
// Reserve the necessary entries up-front to avoid reallocation as we add
|
||||
// elements. This is necessary because the string_view in the entry's
|
||||
// weighted_cluster_state field points to the memory in the route field, so
|
||||
// moving the entry in a reallocation will cause the string_view to point to
|
||||
// invalid data.
|
||||
data->routes_.reserve(resolver->current_config_->virtual_host->routes.size());
|
||||
for (auto& route : resolver->current_config_->virtual_host->routes) {
|
||||
absl::Status status =
|
||||
data->AddRouteEntry(resolver, route, default_max_stream_duration);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
XdsResolver::RouteConfigData::RouteEntry*
|
||||
XdsResolver::RouteConfigData::GetRouteForRequest(
|
||||
absl::string_view path, grpc_metadata_batch* initial_metadata) {
|
||||
auto route_index = XdsRouting::GetRouteForRequest(RouteListIterator(this),
|
||||
path, initial_metadata);
|
||||
if (!route_index.has_value()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &routes_[*route_index];
|
||||
}
|
||||
|
||||
absl::StatusOr<RefCountedPtr<ServiceConfig>>
|
||||
XdsResolver::RouteConfigData::CreateMethodConfig(
|
||||
XdsResolver* resolver, const XdsRouteConfigResource::Route& route,
|
||||
const XdsRouteConfigResource::Route::RouteAction::ClusterWeight*
|
||||
cluster_weight) {
|
||||
std::vector<std::string> fields;
|
||||
const auto& route_action =
|
||||
absl::get<XdsRouteConfigResource::Route::RouteAction>(route.action);
|
||||
// Set retry policy if any.
|
||||
if (route_action.retry_policy.has_value() &&
|
||||
!route_action.retry_policy->retry_on.Empty()) {
|
||||
std::vector<std::string> retry_parts;
|
||||
retry_parts.push_back(absl::StrFormat(
|
||||
"\"retryPolicy\": {\n"
|
||||
" \"maxAttempts\": %d,\n"
|
||||
" \"initialBackoff\": \"%s\",\n"
|
||||
" \"maxBackoff\": \"%s\",\n"
|
||||
" \"backoffMultiplier\": 2,\n",
|
||||
route_action.retry_policy->num_retries + 1,
|
||||
route_action.retry_policy->retry_back_off.base_interval.ToJsonString(),
|
||||
route_action.retry_policy->retry_back_off.max_interval.ToJsonString()));
|
||||
std::vector<std::string> code_parts;
|
||||
if (route_action.retry_policy->retry_on.Contains(GRPC_STATUS_CANCELLED)) {
|
||||
code_parts.push_back(" \"CANCELLED\"");
|
||||
}
|
||||
if (route_action.retry_policy->retry_on.Contains(
|
||||
GRPC_STATUS_DEADLINE_EXCEEDED)) {
|
||||
code_parts.push_back(" \"DEADLINE_EXCEEDED\"");
|
||||
}
|
||||
if (route_action.retry_policy->retry_on.Contains(GRPC_STATUS_INTERNAL)) {
|
||||
code_parts.push_back(" \"INTERNAL\"");
|
||||
}
|
||||
if (route_action.retry_policy->retry_on.Contains(
|
||||
GRPC_STATUS_RESOURCE_EXHAUSTED)) {
|
||||
code_parts.push_back(" \"RESOURCE_EXHAUSTED\"");
|
||||
}
|
||||
if (route_action.retry_policy->retry_on.Contains(GRPC_STATUS_UNAVAILABLE)) {
|
||||
code_parts.push_back(" \"UNAVAILABLE\"");
|
||||
}
|
||||
retry_parts.push_back(
|
||||
absl::StrFormat(" \"retryableStatusCodes\": [\n %s ]\n",
|
||||
absl::StrJoin(code_parts, ",\n")));
|
||||
retry_parts.push_back(" }");
|
||||
fields.emplace_back(absl::StrJoin(retry_parts, ""));
|
||||
}
|
||||
// Set timeout.
|
||||
if (route_action.max_stream_duration.has_value() &&
|
||||
(route_action.max_stream_duration != Duration::Zero())) {
|
||||
fields.emplace_back(
|
||||
absl::StrFormat(" \"timeout\": \"%s\"",
|
||||
route_action.max_stream_duration->ToJsonString()));
|
||||
}
|
||||
// Handle xDS HTTP filters.
|
||||
const auto& hcm = absl::get<XdsListenerResource::HttpConnectionManager>(
|
||||
resolver->current_config_->listener->listener);
|
||||
auto result = XdsRouting::GeneratePerHTTPFilterConfigs(
|
||||
static_cast<const GrpcXdsBootstrap&>(resolver->xds_client_->bootstrap())
|
||||
.http_filter_registry(),
|
||||
hcm.http_filters, *resolver->current_config_->virtual_host, route,
|
||||
cluster_weight, resolver->args_);
|
||||
if (!result.ok()) return result.status();
|
||||
for (const auto& p : result->per_filter_configs) {
|
||||
fields.emplace_back(absl::StrCat(" \"", p.first, "\": [\n",
|
||||
absl::StrJoin(p.second, ",\n"),
|
||||
"\n ]"));
|
||||
}
|
||||
// Construct service config.
|
||||
if (!fields.empty()) {
|
||||
std::string json = absl::StrCat(
|
||||
"{\n"
|
||||
" \"methodConfig\": [ {\n"
|
||||
" \"name\": [\n"
|
||||
" {}\n"
|
||||
" ],\n"
|
||||
" ",
|
||||
absl::StrJoin(fields, ",\n"),
|
||||
"\n } ]\n"
|
||||
"}");
|
||||
return ServiceConfigImpl::Create(result->args, json.c_str());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
absl::Status XdsResolver::RouteConfigData::AddRouteEntry(
|
||||
XdsResolver* resolver, const XdsRouteConfigResource::Route& route,
|
||||
const Duration& default_max_stream_duration) {
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) {
|
||||
gpr_log(GPR_INFO, "[xds_resolver %p] XdsConfigSelector %p: route: %s",
|
||||
resolver, this, route.ToString().c_str());
|
||||
}
|
||||
routes_.emplace_back(route);
|
||||
auto* route_entry = &routes_.back();
|
||||
auto maybe_add_cluster = [&](absl::string_view cluster_key,
|
||||
absl::string_view cluster_name) {
|
||||
if (clusters_.find(cluster_key) != clusters_.end()) return;
|
||||
auto cluster_state =
|
||||
resolver->GetOrCreateClusterRef(cluster_key, cluster_name);
|
||||
absl::string_view key = cluster_state->cluster_key();
|
||||
clusters_.emplace(key, std::move(cluster_state));
|
||||
};
|
||||
auto* route_action = absl::get_if<XdsRouteConfigResource::Route::RouteAction>(
|
||||
&route_entry->route.action);
|
||||
if (route_action != nullptr) {
|
||||
// If the route doesn't specify a timeout, set its timeout to the global
|
||||
// one.
|
||||
if (!route_action->max_stream_duration.has_value()) {
|
||||
route_action->max_stream_duration = default_max_stream_duration;
|
||||
}
|
||||
absl::Status status = Match(
|
||||
route_action->action,
|
||||
// cluster name
|
||||
[&](const XdsRouteConfigResource::Route::RouteAction::ClusterName&
|
||||
cluster_name) {
|
||||
auto result =
|
||||
CreateMethodConfig(resolver, route_entry->route, nullptr);
|
||||
if (!result.ok()) {
|
||||
return result.status();
|
||||
}
|
||||
route_entry->method_config = std::move(*result);
|
||||
maybe_add_cluster(absl::StrCat("cluster:", cluster_name.cluster_name),
|
||||
cluster_name.cluster_name);
|
||||
return absl::OkStatus();
|
||||
},
|
||||
// WeightedClusters
|
||||
[&](const std::vector<
|
||||
XdsRouteConfigResource::Route::RouteAction::ClusterWeight>&
|
||||
weighted_clusters) {
|
||||
uint32_t end = 0;
|
||||
for (const auto& weighted_cluster : weighted_clusters) {
|
||||
auto result = CreateMethodConfig(resolver, route_entry->route,
|
||||
&weighted_cluster);
|
||||
if (!result.ok()) {
|
||||
return result.status();
|
||||
}
|
||||
RouteEntry::ClusterWeightState cluster_weight_state;
|
||||
cluster_weight_state.method_config = std::move(*result);
|
||||
end += weighted_cluster.weight;
|
||||
cluster_weight_state.range_end = end;
|
||||
cluster_weight_state.cluster = weighted_cluster.name;
|
||||
route_entry->weighted_cluster_state.push_back(
|
||||
std::move(cluster_weight_state));
|
||||
maybe_add_cluster(absl::StrCat("cluster:", weighted_cluster.name),
|
||||
weighted_cluster.name);
|
||||
}
|
||||
return absl::OkStatus();
|
||||
},
|
||||
// ClusterSpecifierPlugin
|
||||
[&](const XdsRouteConfigResource::Route::RouteAction::
|
||||
ClusterSpecifierPluginName& cluster_specifier_plugin_name) {
|
||||
auto result =
|
||||
CreateMethodConfig(resolver, route_entry->route, nullptr);
|
||||
if (!result.ok()) {
|
||||
return result.status();
|
||||
}
|
||||
route_entry->method_config = std::move(*result);
|
||||
maybe_add_cluster(
|
||||
absl::StrCat(
|
||||
"cluster_specifier_plugin:",
|
||||
cluster_specifier_plugin_name.cluster_specifier_plugin_name),
|
||||
/*subscription_name=*/"");
|
||||
return absl::OkStatus();
|
||||
});
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
//
|
||||
// XdsResolver::XdsConfigSelector
|
||||
//
|
||||
|
||||
XdsResolver::XdsConfigSelector::XdsConfigSelector(
|
||||
RefCountedPtr<XdsResolver> resolver,
|
||||
RefCountedPtr<RouteConfigData> route_config_data)
|
||||
: resolver_(std::move(resolver)),
|
||||
route_config_data_(std::move(route_config_data)) {
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) {
|
||||
gpr_log(GPR_INFO, "[xds_resolver %p] creating XdsConfigSelector %p",
|
||||
resolver_.get(), this);
|
||||
}
|
||||
// Populate filter list.
|
||||
const auto& http_filter_registry =
|
||||
static_cast<const GrpcXdsBootstrap&>(resolver_->xds_client_->bootstrap())
|
||||
.http_filter_registry();
|
||||
const auto& hcm = absl::get<XdsListenerResource::HttpConnectionManager>(
|
||||
resolver_->current_config_->listener->listener);
|
||||
for (const auto& http_filter : hcm.http_filters) {
|
||||
// Find filter. This is guaranteed to succeed, because it's checked
|
||||
// at config validation time in the XdsApi code.
|
||||
const XdsHttpFilterImpl* filter_impl =
|
||||
http_filter_registry.GetFilterForType(
|
||||
http_filter.config.config_proto_type_name);
|
||||
GPR_ASSERT(filter_impl != nullptr);
|
||||
// Add C-core filter to list.
|
||||
if (filter_impl->channel_filter() != nullptr) {
|
||||
filters_.push_back(filter_impl->channel_filter());
|
||||
}
|
||||
}
|
||||
filters_.push_back(&ClusterSelectionFilter::kFilter);
|
||||
}
|
||||
|
||||
XdsResolver::XdsConfigSelector::~XdsConfigSelector() {
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) {
|
||||
gpr_log(GPR_INFO, "[xds_resolver %p] destroying XdsConfigSelector %p",
|
||||
resolver_.get(), this);
|
||||
}
|
||||
route_config_data_.reset();
|
||||
if (!IsWorkSerializerDispatchEnabled()) {
|
||||
resolver_->MaybeRemoveUnusedClusters();
|
||||
return;
|
||||
}
|
||||
resolver_->work_serializer_->Run(
|
||||
[resolver = std::move(resolver_)]() {
|
||||
resolver->MaybeRemoveUnusedClusters();
|
||||
},
|
||||
DEBUG_LOCATION);
|
||||
}
|
||||
|
||||
absl::optional<uint64_t> HeaderHashHelper(
|
||||
const XdsRouteConfigResource::Route::RouteAction::HashPolicy::Header&
|
||||
header_policy,
|
||||
grpc_metadata_batch* initial_metadata) {
|
||||
std::string value_buffer;
|
||||
absl::optional<absl::string_view> header_value = XdsRouting::GetHeaderValue(
|
||||
initial_metadata, header_policy.header_name, &value_buffer);
|
||||
if (!header_value.has_value()) return absl::nullopt;
|
||||
if (header_policy.regex != nullptr) {
|
||||
// If GetHeaderValue() did not already store the value in
|
||||
// value_buffer, copy it there now, so we can modify it.
|
||||
if (header_value->data() != value_buffer.data()) {
|
||||
value_buffer = std::string(*header_value);
|
||||
}
|
||||
RE2::GlobalReplace(&value_buffer, *header_policy.regex,
|
||||
header_policy.regex_substitution);
|
||||
header_value = value_buffer;
|
||||
}
|
||||
return XXH64(header_value->data(), header_value->size(), 0);
|
||||
}
|
||||
|
||||
absl::Status XdsResolver::XdsConfigSelector::GetCallConfig(
|
||||
GetCallConfigArgs args) {
|
||||
Slice* path = args.initial_metadata->get_pointer(HttpPathMetadata());
|
||||
GPR_ASSERT(path != nullptr);
|
||||
auto* entry = route_config_data_->GetRouteForRequest(path->as_string_view(),
|
||||
args.initial_metadata);
|
||||
if (entry == nullptr) {
|
||||
return absl::UnavailableError(
|
||||
"No matching route found in xDS route config");
|
||||
}
|
||||
// Found a route match
|
||||
const auto* route_action =
|
||||
absl::get_if<XdsRouteConfigResource::Route::RouteAction>(
|
||||
&entry->route.action);
|
||||
if (route_action == nullptr) {
|
||||
return absl::UnavailableError("Matching route has inappropriate action");
|
||||
}
|
||||
std::string cluster_name;
|
||||
RefCountedPtr<ServiceConfig> method_config;
|
||||
Match(
|
||||
route_action->action,
|
||||
// cluster name
|
||||
[&](const XdsRouteConfigResource::Route::RouteAction::ClusterName&
|
||||
action_cluster_name) {
|
||||
cluster_name =
|
||||
absl::StrCat("cluster:", action_cluster_name.cluster_name);
|
||||
method_config = entry->method_config;
|
||||
},
|
||||
// WeightedClusters
|
||||
[&](const std::vector<
|
||||
XdsRouteConfigResource::Route::RouteAction::ClusterWeight>&
|
||||
/*weighted_clusters*/) {
|
||||
const uint32_t key = absl::Uniform<uint32_t>(
|
||||
absl::BitGen(), 0, entry->weighted_cluster_state.back().range_end);
|
||||
// Find the index in weighted clusters corresponding to key.
|
||||
size_t mid = 0;
|
||||
size_t start_index = 0;
|
||||
size_t end_index = entry->weighted_cluster_state.size() - 1;
|
||||
size_t index = 0;
|
||||
while (end_index > start_index) {
|
||||
mid = (start_index + end_index) / 2;
|
||||
if (entry->weighted_cluster_state[mid].range_end > key) {
|
||||
end_index = mid;
|
||||
} else if (entry->weighted_cluster_state[mid].range_end < key) {
|
||||
start_index = mid + 1;
|
||||
} else {
|
||||
index = mid + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index == 0) index = start_index;
|
||||
GPR_ASSERT(entry->weighted_cluster_state[index].range_end > key);
|
||||
cluster_name = absl::StrCat(
|
||||
"cluster:", entry->weighted_cluster_state[index].cluster);
|
||||
method_config = entry->weighted_cluster_state[index].method_config;
|
||||
},
|
||||
// ClusterSpecifierPlugin
|
||||
[&](const XdsRouteConfigResource::Route::RouteAction::
|
||||
ClusterSpecifierPluginName& cluster_specifier_plugin_name) {
|
||||
cluster_name = absl::StrCat(
|
||||
"cluster_specifier_plugin:",
|
||||
cluster_specifier_plugin_name.cluster_specifier_plugin_name);
|
||||
method_config = entry->method_config;
|
||||
});
|
||||
auto cluster = route_config_data_->FindClusterRef(cluster_name);
|
||||
GPR_ASSERT(cluster != nullptr);
|
||||
// Generate a hash.
|
||||
absl::optional<uint64_t> hash;
|
||||
for (const auto& hash_policy : route_action->hash_policies) {
|
||||
absl::optional<uint64_t> new_hash = Match(
|
||||
hash_policy.policy,
|
||||
[&](const XdsRouteConfigResource::Route::RouteAction::HashPolicy::
|
||||
Header& header) {
|
||||
return HeaderHashHelper(header, args.initial_metadata);
|
||||
},
|
||||
[&](const XdsRouteConfigResource::Route::RouteAction::HashPolicy::
|
||||
ChannelId&) -> absl::optional<uint64_t> {
|
||||
return resolver_->channel_id_;
|
||||
});
|
||||
if (new_hash.has_value()) {
|
||||
// Rotating the old value prevents duplicate hash rules from cancelling
|
||||
// each other out and preserves all of the entropy
|
||||
const uint64_t old_value =
|
||||
hash.has_value() ? ((*hash << 1) | (*hash >> 63)) : 0;
|
||||
hash = old_value ^ *new_hash;
|
||||
}
|
||||
// If the policy is a terminal policy and a hash has been generated,
|
||||
// ignore the rest of the hash policies.
|
||||
if (hash_policy.terminal && hash.has_value()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hash.has_value()) {
|
||||
hash = absl::Uniform<uint64_t>(absl::BitGen());
|
||||
}
|
||||
// Populate service config call data.
|
||||
if (method_config != nullptr) {
|
||||
auto* parsed_method_configs =
|
||||
method_config->GetMethodParsedConfigVector(grpc_empty_slice());
|
||||
args.service_config_call_data->SetServiceConfig(std::move(method_config),
|
||||
parsed_method_configs);
|
||||
}
|
||||
args.service_config_call_data->SetCallAttribute(
|
||||
args.arena->New<XdsClusterAttribute>(cluster->cluster_key()));
|
||||
args.service_config_call_data->SetCallAttribute(
|
||||
args.arena->New<RequestHashAttribute>(*hash));
|
||||
args.service_config_call_data->SetCallAttribute(
|
||||
args.arena->ManagedNew<XdsRouteStateAttributeImpl>(route_config_data_,
|
||||
entry));
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
//
|
||||
// XdsResolver::XdsRouteStateAttributeImpl
|
||||
//
|
||||
|
||||
bool XdsResolver::XdsRouteStateAttributeImpl::HasClusterForRoute(
|
||||
absl::string_view cluster_name) const {
|
||||
// Found a route match
|
||||
const auto* route_action =
|
||||
absl::get_if<XdsRouteConfigResource::Route::RouteAction>(
|
||||
&static_cast<RouteConfigData::RouteEntry*>(route_)->route.action);
|
||||
if (route_action == nullptr) return false;
|
||||
return Match(
|
||||
route_action->action,
|
||||
[&](const XdsRouteConfigResource::Route::RouteAction::ClusterName& name) {
|
||||
return name.cluster_name == cluster_name;
|
||||
},
|
||||
[&](const std::vector<
|
||||
XdsRouteConfigResource::Route::RouteAction::ClusterWeight>&
|
||||
clusters) {
|
||||
for (const auto& cluster : clusters) {
|
||||
if (cluster.name == cluster_name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
[&](const XdsRouteConfigResource::Route::RouteAction::
|
||||
ClusterSpecifierPluginName& /* name */) { return false; });
|
||||
}
|
||||
|
||||
RefCountedPtr<XdsResolver::ClusterRef>
|
||||
XdsResolver::XdsRouteStateAttributeImpl::LockAndGetCluster(
|
||||
absl::string_view cluster_name) {
|
||||
if (route_config_data_ == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
auto cluster = route_config_data_->FindClusterRef(cluster_name);
|
||||
route_config_data_.reset();
|
||||
return cluster;
|
||||
}
|
||||
|
||||
//
|
||||
// XdsResolver::ClusterSelectionFilter
|
||||
//
|
||||
|
||||
const grpc_channel_filter XdsResolver::ClusterSelectionFilter::kFilter =
|
||||
MakePromiseBasedFilter<ClusterSelectionFilter, FilterEndpoint::kClient,
|
||||
kFilterExaminesServerInitialMetadata>(
|
||||
"cluster_selection_filter");
|
||||
|
||||
void XdsResolver::ClusterSelectionFilter::Call::OnClientInitialMetadata(
|
||||
ClientMetadata&) {
|
||||
auto* service_config_call_data =
|
||||
static_cast<ClientChannelServiceConfigCallData*>(
|
||||
GetContext<grpc_call_context_element>()
|
||||
[GRPC_CONTEXT_SERVICE_CONFIG_CALL_DATA]
|
||||
.value);
|
||||
GPR_ASSERT(service_config_call_data != nullptr);
|
||||
auto* route_state_attribute = static_cast<XdsRouteStateAttributeImpl*>(
|
||||
service_config_call_data->GetCallAttribute<XdsRouteStateAttribute>());
|
||||
auto* cluster_name_attribute =
|
||||
service_config_call_data->GetCallAttribute<XdsClusterAttribute>();
|
||||
if (route_state_attribute != nullptr && cluster_name_attribute != nullptr) {
|
||||
auto cluster = route_state_attribute->LockAndGetCluster(
|
||||
cluster_name_attribute->cluster());
|
||||
if (cluster != nullptr) {
|
||||
service_config_call_data->SetOnCommit(
|
||||
[cluster = std::move(cluster)]() mutable { cluster.reset(); });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// XdsResolver
|
||||
//
|
||||
|
||||
void XdsResolver::StartLocked() {
|
||||
auto xds_client = GrpcXdsClient::GetOrCreate(args_, "xds resolver");
|
||||
if (!xds_client.ok()) {
|
||||
gpr_log(GPR_ERROR,
|
||||
"Failed to create xds client -- channel will remain in "
|
||||
"TRANSIENT_FAILURE: %s",
|
||||
xds_client.status().ToString().c_str());
|
||||
absl::Status status = absl::UnavailableError(absl::StrCat(
|
||||
"Failed to create XdsClient: ", xds_client.status().message()));
|
||||
Result result;
|
||||
result.addresses = status;
|
||||
result.service_config = std::move(status);
|
||||
result.args = args_;
|
||||
result_handler_->ReportResult(std::move(result));
|
||||
return;
|
||||
}
|
||||
xds_client_ = std::move(*xds_client);
|
||||
grpc_pollset_set_add_pollset_set(xds_client_->interested_parties(),
|
||||
interested_parties_);
|
||||
// Determine LDS resource name.
|
||||
std::string resource_name_fragment(absl::StripPrefix(uri_.path(), "/"));
|
||||
if (!uri_.authority().empty()) {
|
||||
// target_uri.authority is set case
|
||||
const auto* authority_config =
|
||||
static_cast<const GrpcXdsBootstrap::GrpcAuthority*>(
|
||||
xds_client_->bootstrap().LookupAuthority(uri_.authority()));
|
||||
if (authority_config == nullptr) {
|
||||
absl::Status status = absl::UnavailableError(
|
||||
absl::StrCat("Invalid target URI -- authority not found for ",
|
||||
uri_.authority().c_str()));
|
||||
Result result;
|
||||
result.addresses = status;
|
||||
result.service_config = std::move(status);
|
||||
result.args = args_;
|
||||
result_handler_->ReportResult(std::move(result));
|
||||
return;
|
||||
}
|
||||
std::string name_template =
|
||||
authority_config->client_listener_resource_name_template();
|
||||
if (name_template.empty()) {
|
||||
name_template = absl::StrCat(
|
||||
"xdstp://", URI::PercentEncodeAuthority(uri_.authority()),
|
||||
"/envoy.config.listener.v3.Listener/%s");
|
||||
}
|
||||
lds_resource_name_ = absl::StrReplaceAll(
|
||||
name_template,
|
||||
{{"%s", URI::PercentEncodePath(resource_name_fragment)}});
|
||||
} else {
|
||||
// target_uri.authority not set
|
||||
absl::string_view name_template =
|
||||
static_cast<const GrpcXdsBootstrap&>(xds_client_->bootstrap())
|
||||
.client_default_listener_resource_name_template();
|
||||
if (name_template.empty()) {
|
||||
name_template = "%s";
|
||||
}
|
||||
if (absl::StartsWith(name_template, "xdstp:")) {
|
||||
resource_name_fragment = URI::PercentEncodePath(resource_name_fragment);
|
||||
}
|
||||
lds_resource_name_ =
|
||||
absl::StrReplaceAll(name_template, {{"%s", resource_name_fragment}});
|
||||
}
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) {
|
||||
gpr_log(GPR_INFO, "[xds_resolver %p] Started with lds_resource_name %s.",
|
||||
this, lds_resource_name_.c_str());
|
||||
}
|
||||
// Start watch for xDS config.
|
||||
dependency_mgr_ = MakeOrphanable<XdsDependencyManager>(
|
||||
xds_client_, work_serializer_,
|
||||
std::make_unique<XdsWatcher>(RefAsSubclass<XdsResolver>()),
|
||||
data_plane_authority_, lds_resource_name_, args_, interested_parties_);
|
||||
}
|
||||
|
||||
void XdsResolver::ShutdownLocked() {
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) {
|
||||
gpr_log(GPR_INFO, "[xds_resolver %p] shutting down", this);
|
||||
}
|
||||
if (xds_client_ != nullptr) {
|
||||
dependency_mgr_.reset();
|
||||
grpc_pollset_set_del_pollset_set(xds_client_->interested_parties(),
|
||||
interested_parties_);
|
||||
xds_client_.reset(DEBUG_LOCATION, "xds resolver");
|
||||
}
|
||||
}
|
||||
|
||||
void XdsResolver::OnUpdate(
|
||||
RefCountedPtr<const XdsDependencyManager::XdsConfig> config) {
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) {
|
||||
gpr_log(GPR_INFO, "[xds_resolver %p] received updated xDS config", this);
|
||||
}
|
||||
if (xds_client_ == nullptr) return;
|
||||
current_config_ = std::move(config);
|
||||
GenerateResult();
|
||||
}
|
||||
|
||||
void XdsResolver::OnError(absl::string_view context, absl::Status status) {
|
||||
gpr_log(GPR_ERROR, "[xds_resolver %p] received error from XdsClient: %s: %s",
|
||||
this, std::string(context).c_str(), status.ToString().c_str());
|
||||
if (xds_client_ == nullptr) return;
|
||||
status =
|
||||
absl::UnavailableError(absl::StrCat(context, ": ", status.ToString()));
|
||||
Result result;
|
||||
result.addresses = status;
|
||||
result.service_config = std::move(status);
|
||||
result.args =
|
||||
args_.SetObject(xds_client_.Ref(DEBUG_LOCATION, "xds resolver result"));
|
||||
result_handler_->ReportResult(std::move(result));
|
||||
}
|
||||
|
||||
void XdsResolver::OnResourceDoesNotExist(std::string context) {
|
||||
gpr_log(GPR_ERROR,
|
||||
"[xds_resolver %p] LDS/RDS resource does not exist -- clearing "
|
||||
"update and returning empty service config",
|
||||
this);
|
||||
if (xds_client_ == nullptr) return;
|
||||
current_config_.reset();
|
||||
Result result;
|
||||
result.addresses.emplace();
|
||||
result.service_config = ServiceConfigImpl::Create(args_, "{}");
|
||||
GPR_ASSERT(result.service_config.ok());
|
||||
result.resolution_note = std::move(context);
|
||||
result.args = args_;
|
||||
result_handler_->ReportResult(std::move(result));
|
||||
}
|
||||
|
||||
absl::StatusOr<RefCountedPtr<ServiceConfig>>
|
||||
XdsResolver::CreateServiceConfig() {
|
||||
std::vector<std::string> clusters;
|
||||
for (const auto& cluster : cluster_ref_map_) {
|
||||
absl::string_view child_name = cluster.first;
|
||||
if (absl::ConsumePrefix(&child_name, "cluster_specifier_plugin:")) {
|
||||
clusters.push_back(absl::StrFormat(
|
||||
" \"%s\":{\n"
|
||||
" \"childPolicy\": %s\n"
|
||||
" }",
|
||||
cluster.first,
|
||||
current_config_->route_config->cluster_specifier_plugin_map.at(
|
||||
std::string(child_name))));
|
||||
} else {
|
||||
absl::ConsumePrefix(&child_name, "cluster:");
|
||||
clusters.push_back(
|
||||
absl::StrFormat(" \"%s\":{\n"
|
||||
" \"childPolicy\":[ {\n"
|
||||
" \"cds_experimental\":{\n"
|
||||
" \"cluster\": \"%s\"\n"
|
||||
" }\n"
|
||||
" } ]\n"
|
||||
" }",
|
||||
cluster.first, child_name));
|
||||
}
|
||||
}
|
||||
std::vector<std::string> config_parts;
|
||||
config_parts.push_back(
|
||||
"{\n"
|
||||
" \"loadBalancingConfig\":[\n"
|
||||
" { \"xds_cluster_manager_experimental\":{\n"
|
||||
" \"children\":{\n");
|
||||
config_parts.push_back(absl::StrJoin(clusters, ",\n"));
|
||||
config_parts.push_back(
|
||||
" }\n"
|
||||
" } }\n"
|
||||
" ]\n"
|
||||
"}");
|
||||
std::string json = absl::StrJoin(config_parts, "");
|
||||
return ServiceConfigImpl::Create(args_, json.c_str());
|
||||
}
|
||||
|
||||
void XdsResolver::GenerateResult() {
|
||||
if (xds_client_ == nullptr || current_config_ == nullptr) return;
|
||||
// First create XdsConfigSelector, which may add new entries to the cluster
|
||||
// state map.
|
||||
const auto& hcm = absl::get<XdsListenerResource::HttpConnectionManager>(
|
||||
current_config_->listener->listener);
|
||||
auto route_config_data =
|
||||
RouteConfigData::Create(this, hcm.http_max_stream_duration);
|
||||
if (!route_config_data.ok()) {
|
||||
OnError("could not create ConfigSelector",
|
||||
absl::UnavailableError(route_config_data.status().message()));
|
||||
return;
|
||||
}
|
||||
auto config_selector = MakeRefCounted<XdsConfigSelector>(
|
||||
RefAsSubclass<XdsResolver>(), std::move(*route_config_data));
|
||||
// Now create the service config.
|
||||
Result result;
|
||||
result.addresses.emplace();
|
||||
result.service_config = CreateServiceConfig();
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) {
|
||||
gpr_log(GPR_INFO, "[xds_resolver %p] generated service config: %s", this,
|
||||
result.service_config.ok()
|
||||
? std::string((*result.service_config)->json_string()).c_str()
|
||||
: result.service_config.status().ToString().c_str());
|
||||
}
|
||||
result.args =
|
||||
args_.SetObject(xds_client_.Ref(DEBUG_LOCATION, "xds resolver result"))
|
||||
.SetObject(config_selector)
|
||||
.SetObject(current_config_)
|
||||
.SetObject(dependency_mgr_->Ref());
|
||||
result_handler_->ReportResult(std::move(result));
|
||||
}
|
||||
|
||||
void XdsResolver::MaybeRemoveUnusedClusters() {
|
||||
bool update_needed = false;
|
||||
for (auto it = cluster_ref_map_.begin(); it != cluster_ref_map_.end();) {
|
||||
RefCountedPtr<ClusterRef> cluster_state = it->second->RefIfNonZero();
|
||||
if (cluster_state != nullptr) {
|
||||
++it;
|
||||
} else {
|
||||
update_needed = true;
|
||||
it = cluster_ref_map_.erase(it);
|
||||
}
|
||||
}
|
||||
if (update_needed) GenerateResult();
|
||||
}
|
||||
|
||||
//
|
||||
// XdsResolverFactory
|
||||
//
|
||||
|
||||
class XdsResolverFactory : public ResolverFactory {
|
||||
public:
|
||||
absl::string_view scheme() const override { return "xds"; }
|
||||
|
||||
bool IsValidUri(const URI& uri) const override {
|
||||
if (uri.path().empty() || uri.path().back() == '/') {
|
||||
gpr_log(GPR_ERROR,
|
||||
"URI path does not contain valid data plane authority");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
OrphanablePtr<Resolver> CreateResolver(ResolverArgs args) const override {
|
||||
if (!IsValidUri(args.uri)) return nullptr;
|
||||
std::string authority = GetDataPlaneAuthority(args.args, args.uri);
|
||||
return MakeOrphanable<XdsResolver>(std::move(args), std::move(authority));
|
||||
}
|
||||
|
||||
private:
|
||||
std::string GetDataPlaneAuthority(const ChannelArgs& args,
|
||||
const URI& uri) const {
|
||||
absl::optional<absl::string_view> authority =
|
||||
args.GetString(GRPC_ARG_DEFAULT_AUTHORITY);
|
||||
if (authority.has_value()) return URI::PercentEncodeAuthority(*authority);
|
||||
return GetDefaultAuthority(uri);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void RegisterXdsResolver(CoreConfiguration::Builder* builder) {
|
||||
builder->resolver_registry()->RegisterResolverFactory(
|
||||
std::make_unique<XdsResolverFactory>());
|
||||
}
|
||||
|
||||
} // namespace grpc_core
|
||||
62
Pods/gRPC-Core/src/core/resolver/xds/xds_resolver_attributes.h
generated
Normal file
62
Pods/gRPC-Core/src/core/resolver/xds/xds_resolver_attributes.h
generated
Normal file
@@ -0,0 +1,62 @@
|
||||
//
|
||||
// Copyright 2019 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#ifndef GRPC_SRC_CORE_RESOLVER_XDS_XDS_RESOLVER_ATTRIBUTES_H
|
||||
#define GRPC_SRC_CORE_RESOLVER_XDS_XDS_RESOLVER_ATTRIBUTES_H
|
||||
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
#include "src/core/lib/gprpp/unique_type_name.h"
|
||||
#include "src/core/service_config/service_config_call_data.h"
|
||||
|
||||
namespace grpc_core {
|
||||
|
||||
class XdsClusterAttribute
|
||||
: public ServiceConfigCallData::CallAttributeInterface {
|
||||
public:
|
||||
static UniqueTypeName TypeName() {
|
||||
static UniqueTypeName::Factory kFactory("xds_cluster_name");
|
||||
return kFactory.Create();
|
||||
}
|
||||
|
||||
explicit XdsClusterAttribute(absl::string_view cluster) : cluster_(cluster) {}
|
||||
|
||||
absl::string_view cluster() const { return cluster_; }
|
||||
void set_cluster(absl::string_view cluster) { cluster_ = cluster; }
|
||||
|
||||
private:
|
||||
UniqueTypeName type() const override { return TypeName(); }
|
||||
|
||||
absl::string_view cluster_;
|
||||
};
|
||||
|
||||
class XdsRouteStateAttribute
|
||||
: public ServiceConfigCallData::CallAttributeInterface {
|
||||
public:
|
||||
static UniqueTypeName TypeName() {
|
||||
static UniqueTypeName::Factory factory("xds_route_state");
|
||||
return factory.Create();
|
||||
}
|
||||
|
||||
virtual bool HasClusterForRoute(absl::string_view cluster_name) const = 0;
|
||||
UniqueTypeName type() const override { return TypeName(); }
|
||||
};
|
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_SRC_CORE_RESOLVER_XDS_XDS_RESOLVER_ATTRIBUTES_H
|
||||
25
Pods/gRPC-Core/src/core/resolver/xds/xds_resolver_trace.cc
generated
Normal file
25
Pods/gRPC-Core/src/core/resolver/xds/xds_resolver_trace.cc
generated
Normal file
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// Copyright 2019 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include "src/core/resolver/xds/xds_resolver_trace.h"
|
||||
|
||||
namespace grpc_core {
|
||||
|
||||
TraceFlag grpc_xds_resolver_trace(false, "xds_resolver");
|
||||
|
||||
} // namespace grpc_core
|
||||
30
Pods/gRPC-Core/src/core/resolver/xds/xds_resolver_trace.h
generated
Normal file
30
Pods/gRPC-Core/src/core/resolver/xds/xds_resolver_trace.h
generated
Normal file
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// Copyright 2019 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#ifndef GRPC_SRC_CORE_RESOLVER_XDS_XDS_RESOLVER_TRACE_H
|
||||
#define GRPC_SRC_CORE_RESOLVER_XDS_XDS_RESOLVER_TRACE_H
|
||||
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include "src/core/lib/debug/trace.h"
|
||||
|
||||
namespace grpc_core {
|
||||
|
||||
extern TraceFlag grpc_xds_resolver_trace;
|
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_SRC_CORE_RESOLVER_XDS_XDS_RESOLVER_TRACE_H
|
||||
Reference in New Issue
Block a user