1 //
2 // Copyright 2019 gRPC authors.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include <grpc/impl/channel_arg_names.h>
18 #include <grpc/slice.h>
19 #include <grpc/status.h>
20 #include <grpc/support/port_platform.h>
21 #include <stdint.h>
22 #include <string.h>
23
24 #include <algorithm>
25 #include <functional>
26 #include <map>
27 #include <memory>
28 #include <string>
29 #include <type_traits>
30 #include <utility>
31 #include <vector>
32
33 #include "absl/log/check.h"
34 #include "absl/log/log.h"
35 #include "absl/meta/type_traits.h"
36 #include "absl/random/random.h"
37 #include "absl/status/status.h"
38 #include "absl/status/statusor.h"
39 #include "absl/strings/match.h"
40 #include "absl/strings/str_cat.h"
41 #include "absl/strings/str_format.h"
42 #include "absl/strings/str_join.h"
43 #include "absl/strings/str_replace.h"
44 #include "absl/strings/string_view.h"
45 #include "absl/strings/strip.h"
46 #include "absl/types/optional.h"
47 #include "absl/types/variant.h"
48 #include "re2/re2.h"
49 #include "src/core/client_channel/client_channel_internal.h"
50 #include "src/core/client_channel/config_selector.h"
51 #include "src/core/config/core_configuration.h"
52 #include "src/core/lib/channel/channel_args.h"
53 #include "src/core/lib/channel/channel_fwd.h"
54 #include "src/core/lib/channel/channel_stack.h"
55 #include "src/core/lib/channel/promise_based_filter.h"
56 #include "src/core/lib/channel/status_util.h"
57 #include "src/core/lib/debug/trace.h"
58 #include "src/core/lib/experiments/experiments.h"
59 #include "src/core/lib/iomgr/iomgr_fwd.h"
60 #include "src/core/lib/iomgr/pollset_set.h"
61 #include "src/core/lib/promise/arena_promise.h"
62 #include "src/core/lib/promise/context.h"
63 #include "src/core/lib/resource_quota/arena.h"
64 #include "src/core/lib/slice/slice.h"
65 #include "src/core/lib/transport/metadata_batch.h"
66 #include "src/core/lib/transport/transport.h"
67 #include "src/core/load_balancing/ring_hash/ring_hash.h"
68 #include "src/core/resolver/endpoint_addresses.h"
69 #include "src/core/resolver/resolver.h"
70 #include "src/core/resolver/resolver_factory.h"
71 #include "src/core/resolver/xds/xds_dependency_manager.h"
72 #include "src/core/resolver/xds/xds_resolver_attributes.h"
73 #include "src/core/service_config/service_config.h"
74 #include "src/core/service_config/service_config_impl.h"
75 #include "src/core/util/debug_location.h"
76 #include "src/core/util/dual_ref_counted.h"
77 #include "src/core/util/match.h"
78 #include "src/core/util/orphanable.h"
79 #include "src/core/util/ref_counted.h"
80 #include "src/core/util/ref_counted_ptr.h"
81 #include "src/core/util/time.h"
82 #include "src/core/util/uri.h"
83 #include "src/core/util/work_serializer.h"
84 #include "src/core/util/xxhash_inline.h"
85 #include "src/core/xds/grpc/xds_bootstrap_grpc.h"
86 #include "src/core/xds/grpc/xds_client_grpc.h"
87 #include "src/core/xds/grpc/xds_http_filter.h"
88 #include "src/core/xds/grpc/xds_listener.h"
89 #include "src/core/xds/grpc/xds_route_config.h"
90 #include "src/core/xds/grpc/xds_routing.h"
91 #include "src/core/xds/xds_client/xds_bootstrap.h"
92
93 namespace grpc_core {
94
95 namespace {
96
97 //
98 // XdsResolver
99 //
100
101 class XdsResolver final : public Resolver {
102 public:
XdsResolver(ResolverArgs args,std::string data_plane_authority)103 XdsResolver(ResolverArgs args, std::string data_plane_authority)
104 : work_serializer_(std::move(args.work_serializer)),
105 result_handler_(std::move(args.result_handler)),
106 args_(std::move(args.args)),
107 interested_parties_(args.pollset_set),
108 uri_(std::move(args.uri)),
109 data_plane_authority_(std::move(data_plane_authority)),
110 channel_id_(absl::Uniform<uint64_t>(absl::BitGen())) {
111 GRPC_TRACE_LOG(xds_resolver, INFO)
112 << "[xds_resolver " << this << "] created for URI " << uri_.ToString()
113 << "; data plane authority is " << data_plane_authority_;
114 }
115
~XdsResolver()116 ~XdsResolver() override {
117 GRPC_TRACE_LOG(xds_resolver, INFO)
118 << "[xds_resolver " << this << "] destroyed";
119 }
120
121 void StartLocked() override;
122
123 void ShutdownLocked() override;
124
RequestReresolutionLocked()125 void RequestReresolutionLocked() override {
126 if (dependency_mgr_ != nullptr) dependency_mgr_->RequestReresolution();
127 }
128
ResetBackoffLocked()129 void ResetBackoffLocked() override {
130 if (xds_client_ != nullptr) xds_client_->ResetBackoff();
131 if (dependency_mgr_ != nullptr) dependency_mgr_->ResetBackoff();
132 }
133
134 private:
135 class XdsWatcher final : public XdsDependencyManager::Watcher {
136 public:
XdsWatcher(RefCountedPtr<XdsResolver> resolver)137 explicit XdsWatcher(RefCountedPtr<XdsResolver> resolver)
138 : resolver_(std::move(resolver)) {}
139
OnUpdate(absl::StatusOr<RefCountedPtr<const XdsConfig>> config)140 void OnUpdate(
141 absl::StatusOr<RefCountedPtr<const XdsConfig>> config) override {
142 resolver_->OnUpdate(std::move(config));
143 }
144
145 private:
146 RefCountedPtr<XdsResolver> resolver_;
147 };
148
149 // An entry in the map of clusters that need to be present in the LB
150 // policy config. The map holds a weak ref. One strong ref is held by
151 // the ConfigSelector, and another is held by each call assigned to
152 // the cluster by the ConfigSelector. The ref for each call is held
153 // until the call is committed. When the strong refs go away, we hop
154 // back into the WorkSerializer to remove the entry from the map.
155 class ClusterRef final : public DualRefCounted<ClusterRef> {
156 public:
ClusterRef(RefCountedPtr<XdsResolver> resolver,RefCountedPtr<XdsDependencyManager::ClusterSubscription> cluster_subscription,absl::string_view cluster_key)157 ClusterRef(RefCountedPtr<XdsResolver> resolver,
158 RefCountedPtr<XdsDependencyManager::ClusterSubscription>
159 cluster_subscription,
160 absl::string_view cluster_key)
161 : resolver_(std::move(resolver)),
162 cluster_subscription_(std::move(cluster_subscription)),
163 cluster_key_(cluster_key) {}
164
Orphaned()165 void Orphaned() override {
166 XdsResolver* resolver_ptr = resolver_.get();
167 resolver_ptr->work_serializer_->Run(
168 [resolver = std::move(resolver_)]() {
169 resolver->MaybeRemoveUnusedClusters();
170 },
171 DEBUG_LOCATION);
172 cluster_subscription_.reset();
173 }
174
cluster_key() const175 const std::string& cluster_key() const { return cluster_key_; }
176
177 private:
178 RefCountedPtr<XdsResolver> resolver_;
179 RefCountedPtr<XdsDependencyManager::ClusterSubscription>
180 cluster_subscription_;
181 std::string cluster_key_;
182 };
183
184 // A routing data including cluster refs and routes table held by the
185 // XdsConfigSelector. A ref to this map will be taken by each call processed
186 // by the XdsConfigSelector, stored in a the call's call attributes, and later
187 // unreffed by the ClusterSelection filter.
188 class RouteConfigData final : public RefCounted<RouteConfigData> {
189 public:
190 struct RouteEntry {
191 struct ClusterWeightState {
192 uint32_t range_end;
193 absl::string_view cluster;
194 RefCountedPtr<ServiceConfig> method_config;
195
operator ==grpc_core::__anon1bdda2e10111::XdsResolver::RouteConfigData::RouteEntry::ClusterWeightState196 bool operator==(const ClusterWeightState& other) const {
197 return range_end == other.range_end && cluster == other.cluster &&
198 MethodConfigsEqual(method_config.get(),
199 other.method_config.get());
200 }
201 };
202
203 XdsRouteConfigResource::Route route;
204 RefCountedPtr<ServiceConfig> method_config;
205 std::vector<ClusterWeightState> weighted_cluster_state;
206
RouteEntrygrpc_core::__anon1bdda2e10111::XdsResolver::RouteConfigData::RouteEntry207 explicit RouteEntry(const XdsRouteConfigResource::Route& r) : route(r) {}
208
operator ==grpc_core::__anon1bdda2e10111::XdsResolver::RouteConfigData::RouteEntry209 bool operator==(const RouteEntry& other) const {
210 return route == other.route &&
211 weighted_cluster_state == other.weighted_cluster_state &&
212 MethodConfigsEqual(method_config.get(),
213 other.method_config.get());
214 }
215 };
216
217 static absl::StatusOr<RefCountedPtr<RouteConfigData>> Create(
218 XdsResolver* resolver, const Duration& default_max_stream_duration);
219
operator ==(const RouteConfigData & other) const220 bool operator==(const RouteConfigData& other) const {
221 return clusters_ == other.clusters_ && routes_ == other.routes_;
222 }
223
FindClusterRef(absl::string_view name) const224 RefCountedPtr<ClusterRef> FindClusterRef(absl::string_view name) const {
225 auto it = clusters_.find(name);
226 if (it == clusters_.end()) {
227 return nullptr;
228 }
229 return it->second;
230 }
231
232 RouteEntry* GetRouteForRequest(absl::string_view path,
233 grpc_metadata_batch* initial_metadata);
234
235 private:
236 class RouteListIterator;
237
238 static absl::StatusOr<RefCountedPtr<ServiceConfig>> CreateMethodConfig(
239 XdsResolver* resolver, const XdsRouteConfigResource::Route& route,
240 const XdsRouteConfigResource::Route::RouteAction::ClusterWeight*
241 cluster_weight);
242
MethodConfigsEqual(const ServiceConfig * sc1,const ServiceConfig * sc2)243 static bool MethodConfigsEqual(const ServiceConfig* sc1,
244 const ServiceConfig* sc2) {
245 if (sc1 == nullptr) return sc2 == nullptr;
246 if (sc2 == nullptr) return false;
247 return sc1->json_string() == sc2->json_string();
248 }
249
250 absl::Status AddRouteEntry(XdsResolver* resolver,
251 const XdsRouteConfigResource::Route& route,
252 const Duration& default_max_stream_duration);
253
254 std::map<absl::string_view, RefCountedPtr<ClusterRef>> clusters_;
255 std::vector<RouteEntry> routes_;
256 };
257
258 class XdsConfigSelector final : public ConfigSelector {
259 public:
260 XdsConfigSelector(RefCountedPtr<XdsResolver> resolver,
261 RefCountedPtr<RouteConfigData> route_config_data);
262 ~XdsConfigSelector() override;
263
name() const264 UniqueTypeName name() const override {
265 static UniqueTypeName::Factory kFactory("XdsConfigSelector");
266 return kFactory.Create();
267 }
268
Equals(const ConfigSelector * other) const269 bool Equals(const ConfigSelector* other) const override {
270 const auto* other_xds = static_cast<const XdsConfigSelector*>(other);
271 // Don't need to compare resolver_, since that will always be the same.
272 return *route_config_data_ == *other_xds->route_config_data_ &&
273 filters_ == other_xds->filters_;
274 }
275
276 absl::Status GetCallConfig(GetCallConfigArgs args) override;
277
278 void AddFilters(InterceptionChainBuilder& builder) override;
279
280 std::vector<const grpc_channel_filter*> GetFilters() override;
281
282 private:
283 RefCountedPtr<XdsResolver> resolver_;
284 RefCountedPtr<RouteConfigData> route_config_data_;
285 std::vector<const XdsHttpFilterImpl*> filters_;
286 };
287
288 class XdsRouteStateAttributeImpl final : public XdsRouteStateAttribute {
289 public:
XdsRouteStateAttributeImpl(RefCountedPtr<RouteConfigData> route_config_data,RouteConfigData::RouteEntry * route)290 explicit XdsRouteStateAttributeImpl(
291 RefCountedPtr<RouteConfigData> route_config_data,
292 RouteConfigData::RouteEntry* route)
293 : route_config_data_(std::move(route_config_data)), route_(route) {}
294
295 // This method can be called only once. The first call will release
296 // the reference to the cluster map, and subsequent calls will return
297 // nullptr.
298 RefCountedPtr<ClusterRef> LockAndGetCluster(absl::string_view cluster_name);
299
300 bool HasClusterForRoute(absl::string_view cluster_name) const override;
301
route() const302 const XdsRouteConfigResource::Route& route() const override {
303 return route_->route;
304 }
305
306 private:
307 RefCountedPtr<RouteConfigData> route_config_data_;
308 RouteConfigData::RouteEntry* route_;
309 };
310
311 class ClusterSelectionFilter final
312 : public ImplementChannelFilter<ClusterSelectionFilter> {
313 public:
314 const static grpc_channel_filter kFilter;
315
TypeName()316 static absl::string_view TypeName() { return "cluster_selection_filter"; }
317
Create(const ChannelArgs &,ChannelFilter::Args)318 static absl::StatusOr<std::unique_ptr<ClusterSelectionFilter>> Create(
319 const ChannelArgs& /* unused */,
320 ChannelFilter::Args /* filter_args */) {
321 return std::make_unique<ClusterSelectionFilter>();
322 }
323
324 // Construct a promise for one call.
325 class Call {
326 public:
327 void OnClientInitialMetadata(ClientMetadata& md);
328 static const NoInterceptor OnServerInitialMetadata;
329 static const NoInterceptor OnServerTrailingMetadata;
330 static const NoInterceptor OnClientToServerMessage;
331 static const NoInterceptor OnClientToServerHalfClose;
332 static const NoInterceptor OnServerToClientMessage;
333 static const NoInterceptor OnFinalize;
334 };
335 };
336
GetOrCreateClusterRef(absl::string_view cluster_key,absl::string_view cluster_name)337 RefCountedPtr<ClusterRef> GetOrCreateClusterRef(
338 absl::string_view cluster_key, absl::string_view cluster_name) {
339 auto it = cluster_ref_map_.find(cluster_key);
340 if (it == cluster_ref_map_.end()) {
341 RefCountedPtr<XdsDependencyManager::ClusterSubscription> subscription;
342 if (!cluster_name.empty()) {
343 // The cluster ref will hold a subscription to ensure that the
344 // XdsDependencyManager stays subscribed to the CDS resource as
345 // long as the cluster ref exists.
346 subscription = dependency_mgr_->GetClusterSubscription(cluster_name);
347 }
348 auto cluster = MakeRefCounted<ClusterRef>(
349 RefAsSubclass<XdsResolver>(), std::move(subscription), cluster_key);
350 cluster_ref_map_.emplace(cluster->cluster_key(), cluster->WeakRef());
351 return cluster;
352 }
353 return it->second->Ref();
354 }
355
356 void OnUpdate(absl::StatusOr<RefCountedPtr<const XdsConfig>> config);
357
358 absl::StatusOr<RefCountedPtr<ServiceConfig>> CreateServiceConfig();
359 void GenerateResult();
360 void GenerateErrorResult(std::string error);
361 void MaybeRemoveUnusedClusters();
362
363 std::shared_ptr<WorkSerializer> work_serializer_;
364 std::unique_ptr<ResultHandler> result_handler_;
365 ChannelArgs args_;
366 grpc_pollset_set* interested_parties_;
367 URI uri_;
368 RefCountedPtr<GrpcXdsClient> xds_client_;
369 std::string lds_resource_name_;
370 std::string data_plane_authority_;
371 const uint64_t channel_id_;
372
373 OrphanablePtr<XdsDependencyManager> dependency_mgr_;
374 RefCountedPtr<const XdsConfig> current_config_;
375 std::map<absl::string_view, WeakRefCountedPtr<ClusterRef>> cluster_ref_map_;
376 };
377
378 const NoInterceptor
379 XdsResolver::ClusterSelectionFilter::Call::OnServerInitialMetadata;
380 const NoInterceptor
381 XdsResolver::ClusterSelectionFilter::Call::OnServerTrailingMetadata;
382 const NoInterceptor
383 XdsResolver::ClusterSelectionFilter::Call::OnClientToServerMessage;
384 const NoInterceptor
385 XdsResolver::ClusterSelectionFilter::Call::OnClientToServerHalfClose;
386 const NoInterceptor
387 XdsResolver::ClusterSelectionFilter::Call::OnServerToClientMessage;
388 const NoInterceptor XdsResolver::ClusterSelectionFilter::Call::OnFinalize;
389
390 //
391 // XdsResolver::RouteConfigData::RouteListIterator
392 //
393
394 // Implementation of XdsRouting::RouteListIterator for getting the matching
395 // route for a request.
396 class XdsResolver::RouteConfigData::RouteListIterator final
397 : public XdsRouting::RouteListIterator {
398 public:
RouteListIterator(const RouteConfigData * route_table)399 explicit RouteListIterator(const RouteConfigData* route_table)
400 : route_table_(route_table) {}
401
Size() const402 size_t Size() const override { return route_table_->routes_.size(); }
403
GetMatchersForRoute(size_t index) const404 const XdsRouteConfigResource::Route::Matchers& GetMatchersForRoute(
405 size_t index) const override {
406 return route_table_->routes_[index].route.matchers;
407 }
408
409 private:
410 const RouteConfigData* route_table_;
411 };
412
413 //
414 // XdsResolver::RouteConfigData
415 //
416
417 absl::StatusOr<RefCountedPtr<XdsResolver::RouteConfigData>>
Create(XdsResolver * resolver,const Duration & default_max_stream_duration)418 XdsResolver::RouteConfigData::Create(
419 XdsResolver* resolver, const Duration& default_max_stream_duration) {
420 auto data = MakeRefCounted<RouteConfigData>();
421 // Reserve the necessary entries up-front to avoid reallocation as we add
422 // elements. This is necessary because the string_view in the entry's
423 // weighted_cluster_state field points to the memory in the route field, so
424 // moving the entry in a reallocation will cause the string_view to point to
425 // invalid data.
426 data->routes_.reserve(resolver->current_config_->virtual_host->routes.size());
427 for (auto& route : resolver->current_config_->virtual_host->routes) {
428 absl::Status status =
429 data->AddRouteEntry(resolver, route, default_max_stream_duration);
430 if (!status.ok()) {
431 return status;
432 }
433 }
434 return data;
435 }
436
437 XdsResolver::RouteConfigData::RouteEntry*
GetRouteForRequest(absl::string_view path,grpc_metadata_batch * initial_metadata)438 XdsResolver::RouteConfigData::GetRouteForRequest(
439 absl::string_view path, grpc_metadata_batch* initial_metadata) {
440 auto route_index = XdsRouting::GetRouteForRequest(RouteListIterator(this),
441 path, initial_metadata);
442 if (!route_index.has_value()) {
443 return nullptr;
444 }
445 return &routes_[*route_index];
446 }
447
448 absl::StatusOr<RefCountedPtr<ServiceConfig>>
CreateMethodConfig(XdsResolver * resolver,const XdsRouteConfigResource::Route & route,const XdsRouteConfigResource::Route::RouteAction::ClusterWeight * cluster_weight)449 XdsResolver::RouteConfigData::CreateMethodConfig(
450 XdsResolver* resolver, const XdsRouteConfigResource::Route& route,
451 const XdsRouteConfigResource::Route::RouteAction::ClusterWeight*
452 cluster_weight) {
453 std::vector<std::string> fields;
454 const auto& route_action =
455 absl::get<XdsRouteConfigResource::Route::RouteAction>(route.action);
456 // Set retry policy if any.
457 if (route_action.retry_policy.has_value() &&
458 !route_action.retry_policy->retry_on.Empty()) {
459 std::vector<std::string> retry_parts;
460 retry_parts.push_back(absl::StrFormat(
461 "\"retryPolicy\": {\n"
462 " \"maxAttempts\": %d,\n"
463 " \"initialBackoff\": \"%s\",\n"
464 " \"maxBackoff\": \"%s\",\n"
465 " \"backoffMultiplier\": 2,\n",
466 route_action.retry_policy->num_retries + 1,
467 route_action.retry_policy->retry_back_off.base_interval.ToJsonString(),
468 route_action.retry_policy->retry_back_off.max_interval.ToJsonString()));
469 std::vector<std::string> code_parts;
470 if (route_action.retry_policy->retry_on.Contains(GRPC_STATUS_CANCELLED)) {
471 code_parts.push_back(" \"CANCELLED\"");
472 }
473 if (route_action.retry_policy->retry_on.Contains(
474 GRPC_STATUS_DEADLINE_EXCEEDED)) {
475 code_parts.push_back(" \"DEADLINE_EXCEEDED\"");
476 }
477 if (route_action.retry_policy->retry_on.Contains(GRPC_STATUS_INTERNAL)) {
478 code_parts.push_back(" \"INTERNAL\"");
479 }
480 if (route_action.retry_policy->retry_on.Contains(
481 GRPC_STATUS_RESOURCE_EXHAUSTED)) {
482 code_parts.push_back(" \"RESOURCE_EXHAUSTED\"");
483 }
484 if (route_action.retry_policy->retry_on.Contains(GRPC_STATUS_UNAVAILABLE)) {
485 code_parts.push_back(" \"UNAVAILABLE\"");
486 }
487 retry_parts.push_back(
488 absl::StrFormat(" \"retryableStatusCodes\": [\n %s ]\n",
489 absl::StrJoin(code_parts, ",\n")));
490 retry_parts.push_back(" }");
491 fields.emplace_back(absl::StrJoin(retry_parts, ""));
492 }
493 // Set timeout.
494 if (route_action.max_stream_duration.has_value() &&
495 (route_action.max_stream_duration != Duration::Zero())) {
496 fields.emplace_back(
497 absl::StrFormat(" \"timeout\": \"%s\"",
498 route_action.max_stream_duration->ToJsonString()));
499 }
500 // Handle xDS HTTP filters.
501 const auto& hcm = absl::get<XdsListenerResource::HttpConnectionManager>(
502 resolver->current_config_->listener->listener);
503 auto result = XdsRouting::GeneratePerHTTPFilterConfigsForMethodConfig(
504 DownCast<const GrpcXdsBootstrap&>(resolver->xds_client_->bootstrap())
505 .http_filter_registry(),
506 hcm.http_filters, *resolver->current_config_->virtual_host, route,
507 cluster_weight, resolver->args_);
508 if (!result.ok()) return result.status();
509 for (const auto& p : result->per_filter_configs) {
510 fields.emplace_back(absl::StrCat(" \"", p.first, "\": [\n",
511 absl::StrJoin(p.second, ",\n"),
512 "\n ]"));
513 }
514 // Construct service config.
515 if (!fields.empty()) {
516 std::string json = absl::StrCat(
517 "{\n"
518 " \"methodConfig\": [ {\n"
519 " \"name\": [\n"
520 " {}\n"
521 " ],\n"
522 " ",
523 absl::StrJoin(fields, ",\n"),
524 "\n } ]\n"
525 "}");
526 return ServiceConfigImpl::Create(result->args, json.c_str());
527 }
528 return nullptr;
529 }
530
AddRouteEntry(XdsResolver * resolver,const XdsRouteConfigResource::Route & route,const Duration & default_max_stream_duration)531 absl::Status XdsResolver::RouteConfigData::AddRouteEntry(
532 XdsResolver* resolver, const XdsRouteConfigResource::Route& route,
533 const Duration& default_max_stream_duration) {
534 GRPC_TRACE_LOG(xds_resolver, INFO)
535 << "[xds_resolver " << resolver << "] XdsConfigSelector " << this
536 << ": route: " << route.ToString();
537 routes_.emplace_back(route);
538 auto* route_entry = &routes_.back();
539 auto maybe_add_cluster = [&](absl::string_view cluster_key,
540 absl::string_view cluster_name) {
541 if (clusters_.find(cluster_key) != clusters_.end()) return;
542 auto cluster_state =
543 resolver->GetOrCreateClusterRef(cluster_key, cluster_name);
544 absl::string_view key = cluster_state->cluster_key();
545 clusters_.emplace(key, std::move(cluster_state));
546 };
547 auto* route_action = absl::get_if<XdsRouteConfigResource::Route::RouteAction>(
548 &route_entry->route.action);
549 if (route_action != nullptr) {
550 // If the route doesn't specify a timeout, set its timeout to the global
551 // one.
552 if (!route_action->max_stream_duration.has_value()) {
553 route_action->max_stream_duration = default_max_stream_duration;
554 }
555 absl::Status status = Match(
556 route_action->action,
557 // cluster name
558 [&](const XdsRouteConfigResource::Route::RouteAction::ClusterName&
559 cluster_name) {
560 auto result =
561 CreateMethodConfig(resolver, route_entry->route, nullptr);
562 if (!result.ok()) {
563 return result.status();
564 }
565 route_entry->method_config = std::move(*result);
566 maybe_add_cluster(absl::StrCat("cluster:", cluster_name.cluster_name),
567 cluster_name.cluster_name);
568 return absl::OkStatus();
569 },
570 // WeightedClusters
571 [&](const std::vector<
572 XdsRouteConfigResource::Route::RouteAction::ClusterWeight>&
573 weighted_clusters) {
574 uint32_t end = 0;
575 for (const auto& weighted_cluster : weighted_clusters) {
576 auto result = CreateMethodConfig(resolver, route_entry->route,
577 &weighted_cluster);
578 if (!result.ok()) {
579 return result.status();
580 }
581 RouteEntry::ClusterWeightState cluster_weight_state;
582 cluster_weight_state.method_config = std::move(*result);
583 end += weighted_cluster.weight;
584 cluster_weight_state.range_end = end;
585 cluster_weight_state.cluster = weighted_cluster.name;
586 route_entry->weighted_cluster_state.push_back(
587 std::move(cluster_weight_state));
588 maybe_add_cluster(absl::StrCat("cluster:", weighted_cluster.name),
589 weighted_cluster.name);
590 }
591 return absl::OkStatus();
592 },
593 // ClusterSpecifierPlugin
594 [&](const XdsRouteConfigResource::Route::RouteAction::
595 ClusterSpecifierPluginName& cluster_specifier_plugin_name) {
596 auto result =
597 CreateMethodConfig(resolver, route_entry->route, nullptr);
598 if (!result.ok()) {
599 return result.status();
600 }
601 route_entry->method_config = std::move(*result);
602 maybe_add_cluster(
603 absl::StrCat(
604 "cluster_specifier_plugin:",
605 cluster_specifier_plugin_name.cluster_specifier_plugin_name),
606 /*subscription_name=*/"");
607 return absl::OkStatus();
608 });
609 if (!status.ok()) {
610 return status;
611 }
612 }
613 return absl::OkStatus();
614 }
615
616 //
617 // XdsResolver::XdsConfigSelector
618 //
619
XdsConfigSelector(RefCountedPtr<XdsResolver> resolver,RefCountedPtr<RouteConfigData> route_config_data)620 XdsResolver::XdsConfigSelector::XdsConfigSelector(
621 RefCountedPtr<XdsResolver> resolver,
622 RefCountedPtr<RouteConfigData> route_config_data)
623 : resolver_(std::move(resolver)),
624 route_config_data_(std::move(route_config_data)) {
625 GRPC_TRACE_LOG(xds_resolver, INFO) << "[xds_resolver " << resolver_.get()
626 << "] creating XdsConfigSelector " << this;
627 // Populate filter list.
628 const auto& http_filter_registry =
629 static_cast<const GrpcXdsBootstrap&>(resolver_->xds_client_->bootstrap())
630 .http_filter_registry();
631 const auto& hcm = absl::get<XdsListenerResource::HttpConnectionManager>(
632 resolver_->current_config_->listener->listener);
633 for (const auto& http_filter : hcm.http_filters) {
634 // Find filter. This is guaranteed to succeed, because it's checked
635 // at config validation time in the XdsApi code.
636 const XdsHttpFilterImpl* filter_impl =
637 http_filter_registry.GetFilterForType(
638 http_filter.config.config_proto_type_name);
639 CHECK_NE(filter_impl, nullptr);
640 // Add filter to list.
641 filters_.push_back(filter_impl);
642 }
643 }
644
~XdsConfigSelector()645 XdsResolver::XdsConfigSelector::~XdsConfigSelector() {
646 GRPC_TRACE_LOG(xds_resolver, INFO)
647 << "[xds_resolver " << resolver_.get()
648 << "] destroying XdsConfigSelector " << this;
649 route_config_data_.reset();
650 if (!IsWorkSerializerDispatchEnabled()) {
651 resolver_->MaybeRemoveUnusedClusters();
652 return;
653 }
654 resolver_->work_serializer_->Run(
655 [resolver = std::move(resolver_)]() {
656 resolver->MaybeRemoveUnusedClusters();
657 },
658 DEBUG_LOCATION);
659 }
660
HeaderHashHelper(const XdsRouteConfigResource::Route::RouteAction::HashPolicy::Header & header_policy,grpc_metadata_batch * initial_metadata)661 absl::optional<uint64_t> HeaderHashHelper(
662 const XdsRouteConfigResource::Route::RouteAction::HashPolicy::Header&
663 header_policy,
664 grpc_metadata_batch* initial_metadata) {
665 std::string value_buffer;
666 absl::optional<absl::string_view> header_value = XdsRouting::GetHeaderValue(
667 initial_metadata, header_policy.header_name, &value_buffer);
668 if (!header_value.has_value()) return absl::nullopt;
669 if (header_policy.regex != nullptr) {
670 // If GetHeaderValue() did not already store the value in
671 // value_buffer, copy it there now, so we can modify it.
672 if (header_value->data() != value_buffer.data()) {
673 value_buffer = std::string(*header_value);
674 }
675 RE2::GlobalReplace(&value_buffer, *header_policy.regex,
676 header_policy.regex_substitution);
677 header_value = value_buffer;
678 }
679 return XXH64(header_value->data(), header_value->size(), 0);
680 }
681
GetCallConfig(GetCallConfigArgs args)682 absl::Status XdsResolver::XdsConfigSelector::GetCallConfig(
683 GetCallConfigArgs args) {
684 Slice* path = args.initial_metadata->get_pointer(HttpPathMetadata());
685 CHECK_NE(path, nullptr);
686 auto* entry = route_config_data_->GetRouteForRequest(path->as_string_view(),
687 args.initial_metadata);
688 if (entry == nullptr) {
689 return absl::UnavailableError(
690 "No matching route found in xDS route config");
691 }
692 // Found a route match
693 const auto* route_action =
694 absl::get_if<XdsRouteConfigResource::Route::RouteAction>(
695 &entry->route.action);
696 if (route_action == nullptr) {
697 return absl::UnavailableError("Matching route has inappropriate action");
698 }
699 std::string cluster_name;
700 RefCountedPtr<ServiceConfig> method_config;
701 Match(
702 route_action->action,
703 // cluster name
704 [&](const XdsRouteConfigResource::Route::RouteAction::ClusterName&
705 action_cluster_name) {
706 cluster_name =
707 absl::StrCat("cluster:", action_cluster_name.cluster_name);
708 method_config = entry->method_config;
709 },
710 // WeightedClusters
711 [&](const std::vector<
712 XdsRouteConfigResource::Route::RouteAction::ClusterWeight>&
713 /*weighted_clusters*/) {
714 const uint32_t key = absl::Uniform<uint32_t>(
715 absl::BitGen(), 0, entry->weighted_cluster_state.back().range_end);
716 // Find the index in weighted clusters corresponding to key.
717 size_t mid = 0;
718 size_t start_index = 0;
719 size_t end_index = entry->weighted_cluster_state.size() - 1;
720 size_t index = 0;
721 while (end_index > start_index) {
722 mid = (start_index + end_index) / 2;
723 if (entry->weighted_cluster_state[mid].range_end > key) {
724 end_index = mid;
725 } else if (entry->weighted_cluster_state[mid].range_end < key) {
726 start_index = mid + 1;
727 } else {
728 index = mid + 1;
729 break;
730 }
731 }
732 if (index == 0) index = start_index;
733 CHECK(entry->weighted_cluster_state[index].range_end > key);
734 cluster_name = absl::StrCat(
735 "cluster:", entry->weighted_cluster_state[index].cluster);
736 method_config = entry->weighted_cluster_state[index].method_config;
737 },
738 // ClusterSpecifierPlugin
739 [&](const XdsRouteConfigResource::Route::RouteAction::
740 ClusterSpecifierPluginName& cluster_specifier_plugin_name) {
741 cluster_name = absl::StrCat(
742 "cluster_specifier_plugin:",
743 cluster_specifier_plugin_name.cluster_specifier_plugin_name);
744 method_config = entry->method_config;
745 });
746 auto cluster = route_config_data_->FindClusterRef(cluster_name);
747 CHECK(cluster != nullptr);
748 // Generate a hash.
749 absl::optional<uint64_t> hash;
750 for (const auto& hash_policy : route_action->hash_policies) {
751 absl::optional<uint64_t> new_hash = Match(
752 hash_policy.policy,
753 [&](const XdsRouteConfigResource::Route::RouteAction::HashPolicy::
754 Header& header) {
755 return HeaderHashHelper(header, args.initial_metadata);
756 },
757 [&](const XdsRouteConfigResource::Route::RouteAction::HashPolicy::
758 ChannelId&) -> absl::optional<uint64_t> {
759 return resolver_->channel_id_;
760 });
761 if (new_hash.has_value()) {
762 // Rotating the old value prevents duplicate hash rules from cancelling
763 // each other out and preserves all of the entropy
764 const uint64_t old_value =
765 hash.has_value() ? ((*hash << 1) | (*hash >> 63)) : 0;
766 hash = old_value ^ *new_hash;
767 }
768 // If the policy is a terminal policy and a hash has been generated,
769 // ignore the rest of the hash policies.
770 if (hash_policy.terminal && hash.has_value()) {
771 break;
772 }
773 }
774 if (!hash.has_value()) {
775 hash = absl::Uniform<uint64_t>(absl::BitGen());
776 }
777 // Populate service config call data.
778 if (method_config != nullptr) {
779 auto* parsed_method_configs =
780 method_config->GetMethodParsedConfigVector(grpc_empty_slice());
781 args.service_config_call_data->SetServiceConfig(std::move(method_config),
782 parsed_method_configs);
783 }
784 args.service_config_call_data->SetCallAttribute(
785 args.arena->New<XdsClusterAttribute>(cluster->cluster_key()));
786 args.service_config_call_data->SetCallAttribute(
787 args.arena->New<RequestHashAttribute>(*hash));
788 args.service_config_call_data->SetCallAttribute(
789 args.arena->ManagedNew<XdsRouteStateAttributeImpl>(route_config_data_,
790 entry));
791 return absl::OkStatus();
792 }
793
AddFilters(InterceptionChainBuilder & builder)794 void XdsResolver::XdsConfigSelector::AddFilters(
795 InterceptionChainBuilder& builder) {
796 for (const XdsHttpFilterImpl* filter : filters_) {
797 filter->AddFilter(builder);
798 }
799 builder.Add<ClusterSelectionFilter>();
800 }
801
802 std::vector<const grpc_channel_filter*>
GetFilters()803 XdsResolver::XdsConfigSelector::GetFilters() {
804 std::vector<const grpc_channel_filter*> filters;
805 for (const XdsHttpFilterImpl* filter : filters_) {
806 if (filter->channel_filter() != nullptr) {
807 filters.push_back(filter->channel_filter());
808 }
809 }
810 filters.push_back(&ClusterSelectionFilter::kFilter);
811 return filters;
812 }
813
814 //
815 // XdsResolver::XdsRouteStateAttributeImpl
816 //
817
HasClusterForRoute(absl::string_view cluster_name) const818 bool XdsResolver::XdsRouteStateAttributeImpl::HasClusterForRoute(
819 absl::string_view cluster_name) const {
820 // Found a route match
821 const auto* route_action =
822 absl::get_if<XdsRouteConfigResource::Route::RouteAction>(
823 &static_cast<RouteConfigData::RouteEntry*>(route_)->route.action);
824 if (route_action == nullptr) return false;
825 return Match(
826 route_action->action,
827 [&](const XdsRouteConfigResource::Route::RouteAction::ClusterName& name) {
828 return name.cluster_name == cluster_name;
829 },
830 [&](const std::vector<
831 XdsRouteConfigResource::Route::RouteAction::ClusterWeight>&
832 clusters) {
833 for (const auto& cluster : clusters) {
834 if (cluster.name == cluster_name) {
835 return true;
836 }
837 }
838 return false;
839 },
840 [&](const XdsRouteConfigResource::Route::RouteAction::
841 ClusterSpecifierPluginName& /* name */) { return false; });
842 }
843
844 RefCountedPtr<XdsResolver::ClusterRef>
LockAndGetCluster(absl::string_view cluster_name)845 XdsResolver::XdsRouteStateAttributeImpl::LockAndGetCluster(
846 absl::string_view cluster_name) {
847 if (route_config_data_ == nullptr) {
848 return nullptr;
849 }
850 auto cluster = route_config_data_->FindClusterRef(cluster_name);
851 route_config_data_.reset();
852 return cluster;
853 }
854
855 //
856 // XdsResolver::ClusterSelectionFilter
857 //
858
859 const grpc_channel_filter XdsResolver::ClusterSelectionFilter::kFilter =
860 MakePromiseBasedFilter<ClusterSelectionFilter, FilterEndpoint::kClient,
861 kFilterExaminesServerInitialMetadata>();
862
OnClientInitialMetadata(ClientMetadata &)863 void XdsResolver::ClusterSelectionFilter::Call::OnClientInitialMetadata(
864 ClientMetadata&) {
865 auto* service_config_call_data =
866 GetContext<ClientChannelServiceConfigCallData>();
867 CHECK_NE(service_config_call_data, nullptr);
868 auto* route_state_attribute = static_cast<XdsRouteStateAttributeImpl*>(
869 service_config_call_data->GetCallAttribute<XdsRouteStateAttribute>());
870 auto* cluster_name_attribute =
871 service_config_call_data->GetCallAttribute<XdsClusterAttribute>();
872 if (route_state_attribute != nullptr && cluster_name_attribute != nullptr) {
873 auto cluster = route_state_attribute->LockAndGetCluster(
874 cluster_name_attribute->cluster());
875 if (cluster != nullptr) {
876 service_config_call_data->SetOnCommit(
877 [cluster = std::move(cluster)]() mutable { cluster.reset(); });
878 }
879 }
880 }
881
882 //
883 // XdsResolver
884 //
885
StartLocked()886 void XdsResolver::StartLocked() {
887 auto xds_client =
888 GrpcXdsClient::GetOrCreate(uri_.ToString(), args_, "xds resolver");
889 if (!xds_client.ok()) {
890 LOG(ERROR) << "Failed to create xds client -- channel will remain in "
891 "TRANSIENT_FAILURE: "
892 << xds_client.status();
893 absl::Status status = absl::UnavailableError(absl::StrCat(
894 "Failed to create XdsClient: ", xds_client.status().message()));
895 Result result;
896 result.addresses = status;
897 result.service_config = std::move(status);
898 result.args = args_;
899 result_handler_->ReportResult(std::move(result));
900 return;
901 }
902 xds_client_ = std::move(*xds_client);
903 grpc_pollset_set_add_pollset_set(xds_client_->interested_parties(),
904 interested_parties_);
905 // Determine LDS resource name.
906 std::string resource_name_fragment(absl::StripPrefix(uri_.path(), "/"));
907 if (!uri_.authority().empty()) {
908 // target_uri.authority is set case
909 const auto* authority_config =
910 static_cast<const GrpcXdsBootstrap::GrpcAuthority*>(
911 xds_client_->bootstrap().LookupAuthority(uri_.authority()));
912 if (authority_config == nullptr) {
913 absl::Status status = absl::UnavailableError(
914 absl::StrCat("Invalid target URI -- authority not found for ",
915 uri_.authority().c_str()));
916 Result result;
917 result.addresses = status;
918 result.service_config = std::move(status);
919 result.args = args_;
920 result_handler_->ReportResult(std::move(result));
921 return;
922 }
923 std::string name_template =
924 authority_config->client_listener_resource_name_template();
925 if (name_template.empty()) {
926 name_template = absl::StrCat(
927 "xdstp://", URI::PercentEncodeAuthority(uri_.authority()),
928 "/envoy.config.listener.v3.Listener/%s");
929 }
930 lds_resource_name_ = absl::StrReplaceAll(
931 name_template,
932 {{"%s", URI::PercentEncodePath(resource_name_fragment)}});
933 } else {
934 // target_uri.authority not set
935 absl::string_view name_template =
936 static_cast<const GrpcXdsBootstrap&>(xds_client_->bootstrap())
937 .client_default_listener_resource_name_template();
938 if (name_template.empty()) {
939 name_template = "%s";
940 }
941 if (absl::StartsWith(name_template, "xdstp:")) {
942 resource_name_fragment = URI::PercentEncodePath(resource_name_fragment);
943 }
944 lds_resource_name_ =
945 absl::StrReplaceAll(name_template, {{"%s", resource_name_fragment}});
946 }
947 GRPC_TRACE_LOG(xds_resolver, INFO)
948 << "[xds_resolver " << this << "] Started with lds_resource_name "
949 << lds_resource_name_;
950 // Start watch for xDS config.
951 dependency_mgr_ = MakeOrphanable<XdsDependencyManager>(
952 xds_client_, work_serializer_,
953 std::make_unique<XdsWatcher>(RefAsSubclass<XdsResolver>()),
954 data_plane_authority_, lds_resource_name_, args_, interested_parties_);
955 }
956
ShutdownLocked()957 void XdsResolver::ShutdownLocked() {
958 GRPC_TRACE_LOG(xds_resolver, INFO)
959 << "[xds_resolver " << this << "] shutting down";
960 if (xds_client_ != nullptr) {
961 dependency_mgr_.reset();
962 grpc_pollset_set_del_pollset_set(xds_client_->interested_parties(),
963 interested_parties_);
964 xds_client_.reset(DEBUG_LOCATION, "xds resolver");
965 }
966 }
967
OnUpdate(absl::StatusOr<RefCountedPtr<const XdsConfig>> config)968 void XdsResolver::OnUpdate(
969 absl::StatusOr<RefCountedPtr<const XdsConfig>> config) {
970 GRPC_TRACE_LOG(xds_resolver, INFO)
971 << "[xds_resolver " << this << "] received updated xDS config";
972 if (xds_client_ == nullptr) return;
973 if (!config.ok()) {
974 LOG(ERROR) << "[xds_resolver " << this << "] config error ("
975 << config.status()
976 << ") -- clearing update and returning empty service config";
977 current_config_.reset();
978 GenerateErrorResult(std::string(config.status().message()));
979 return;
980 }
981 current_config_ = std::move(*config);
982 GenerateResult();
983 }
984
985 absl::StatusOr<RefCountedPtr<ServiceConfig>>
CreateServiceConfig()986 XdsResolver::CreateServiceConfig() {
987 std::vector<std::string> clusters;
988 for (const auto& cluster : cluster_ref_map_) {
989 absl::string_view child_name = cluster.first;
990 if (absl::ConsumePrefix(&child_name, "cluster_specifier_plugin:")) {
991 clusters.push_back(absl::StrFormat(
992 " \"%s\":{\n"
993 " \"childPolicy\": %s\n"
994 " }",
995 cluster.first,
996 current_config_->route_config->cluster_specifier_plugin_map.at(
997 std::string(child_name))));
998 } else {
999 absl::ConsumePrefix(&child_name, "cluster:");
1000 clusters.push_back(
1001 absl::StrFormat(" \"%s\":{\n"
1002 " \"childPolicy\":[ {\n"
1003 " \"cds_experimental\":{\n"
1004 " \"cluster\": \"%s\"\n"
1005 " }\n"
1006 " } ]\n"
1007 " }",
1008 cluster.first, child_name));
1009 }
1010 }
1011 std::vector<std::string> config_parts;
1012 config_parts.push_back(
1013 absl::StrCat(" \"loadBalancingConfig\":[\n"
1014 " { \"xds_cluster_manager_experimental\":{\n"
1015 " \"children\":{\n",
1016 absl::StrJoin(clusters, ",\n"),
1017 " }\n"
1018 " } }\n"
1019 " ]"));
1020 auto& hcm = absl::get<XdsListenerResource::HttpConnectionManager>(
1021 current_config_->listener->listener);
1022 auto filter_configs =
1023 XdsRouting::GeneratePerHTTPFilterConfigsForServiceConfig(
1024 static_cast<const GrpcXdsBootstrap&>(xds_client_->bootstrap())
1025 .http_filter_registry(),
1026 hcm.http_filters, args_);
1027 if (!filter_configs.ok()) return filter_configs.status();
1028 for (const auto& p : filter_configs->per_filter_configs) {
1029 config_parts.emplace_back(absl::StrCat(
1030 " \"", p.first, "\": [\n", absl::StrJoin(p.second, ",\n"), "\n ]"));
1031 }
1032 std::string json = absl::StrCat("{", absl::StrJoin(config_parts, ",\n"), "}");
1033 return ServiceConfigImpl::Create(filter_configs->args, json.c_str());
1034 }
1035
GenerateResult()1036 void XdsResolver::GenerateResult() {
1037 if (xds_client_ == nullptr || current_config_ == nullptr) return;
1038 // First create XdsConfigSelector, which may add new entries to the cluster
1039 // state map.
1040 const auto& hcm = absl::get<XdsListenerResource::HttpConnectionManager>(
1041 current_config_->listener->listener);
1042 auto route_config_data =
1043 RouteConfigData::Create(this, hcm.http_max_stream_duration);
1044 if (!route_config_data.ok()) {
1045 GenerateErrorResult(absl::StrCat("could not create ConfigSelector: ",
1046 route_config_data.status().message()));
1047 return;
1048 }
1049 auto config_selector = MakeRefCounted<XdsConfigSelector>(
1050 RefAsSubclass<XdsResolver>(), std::move(*route_config_data));
1051 // Now create the service config.
1052 Result result;
1053 result.addresses.emplace();
1054 result.service_config = CreateServiceConfig();
1055 GRPC_TRACE_LOG(xds_resolver, INFO)
1056 << "[xds_resolver " << this << "] generated service config: "
1057 << (result.service_config.ok()
1058 ? ((*result.service_config)->json_string())
1059 : result.service_config.status().ToString());
1060 result.args =
1061 args_.SetObject(xds_client_.Ref(DEBUG_LOCATION, "xds resolver result"))
1062 .SetObject(config_selector)
1063 .SetObject(current_config_)
1064 .SetObject(dependency_mgr_->Ref());
1065 result_handler_->ReportResult(std::move(result));
1066 }
1067
GenerateErrorResult(std::string error)1068 void XdsResolver::GenerateErrorResult(std::string error) {
1069 Result result;
1070 result.addresses.emplace();
1071 result.service_config = ServiceConfigImpl::Create(args_, "{}");
1072 CHECK(result.service_config.ok());
1073 result.resolution_note = std::move(error);
1074 result.args = args_;
1075 result_handler_->ReportResult(std::move(result));
1076 }
1077
MaybeRemoveUnusedClusters()1078 void XdsResolver::MaybeRemoveUnusedClusters() {
1079 bool update_needed = false;
1080 for (auto it = cluster_ref_map_.begin(); it != cluster_ref_map_.end();) {
1081 RefCountedPtr<ClusterRef> cluster_state = it->second->RefIfNonZero();
1082 if (cluster_state != nullptr) {
1083 ++it;
1084 } else {
1085 update_needed = true;
1086 it = cluster_ref_map_.erase(it);
1087 }
1088 }
1089 if (update_needed) GenerateResult();
1090 }
1091
1092 //
1093 // XdsResolverFactory
1094 //
1095
1096 class XdsResolverFactory final : public ResolverFactory {
1097 public:
scheme() const1098 absl::string_view scheme() const override { return "xds"; }
1099
IsValidUri(const URI & uri) const1100 bool IsValidUri(const URI& uri) const override {
1101 if (uri.path().empty() || uri.path().back() == '/') {
1102 LOG(ERROR) << "URI path does not contain valid data plane authority";
1103 return false;
1104 }
1105 return true;
1106 }
1107
CreateResolver(ResolverArgs args) const1108 OrphanablePtr<Resolver> CreateResolver(ResolverArgs args) const override {
1109 if (!IsValidUri(args.uri)) return nullptr;
1110 std::string authority = GetDataPlaneAuthority(args.args, args.uri);
1111 return MakeOrphanable<XdsResolver>(std::move(args), std::move(authority));
1112 }
1113
1114 private:
GetDataPlaneAuthority(const ChannelArgs & args,const URI & uri) const1115 std::string GetDataPlaneAuthority(const ChannelArgs& args,
1116 const URI& uri) const {
1117 absl::optional<absl::string_view> authority =
1118 args.GetString(GRPC_ARG_DEFAULT_AUTHORITY);
1119 if (authority.has_value()) return URI::PercentEncodeAuthority(*authority);
1120 return GetDefaultAuthority(uri);
1121 }
1122 };
1123
1124 } // namespace
1125
RegisterXdsResolver(CoreConfiguration::Builder * builder)1126 void RegisterXdsResolver(CoreConfiguration::Builder* builder) {
1127 builder->resolver_registry()->RegisterResolverFactory(
1128 std::make_unique<XdsResolverFactory>());
1129 }
1130
1131 } // namespace grpc_core
1132