This commit is contained in:
Yao
2024-12-20 17:49:45 +08:00
parent 86b0363ce1
commit 654d456c7d
7011 changed files with 1705926 additions and 7 deletions

View 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

View 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

View 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

View 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

View 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)

View 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)

View 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

View 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

View 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)

View 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)

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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() {}

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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_ = &current_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

View 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

View 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

View 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

View 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

View 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