3 * Copyright 2018 gRPC authors.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
19 #include <grpc/support/port_platform.h>
27 #include "absl/strings/str_cat.h"
28 #include "absl/strings/str_format.h"
29 #include "absl/strings/str_join.h"
30 #include "absl/strings/str_split.h"
32 #include "upb/upb.hpp"
34 #include <grpc/impl/codegen/log.h>
35 #include <grpc/support/alloc.h>
36 #include <grpc/support/string_util.h>
38 #include "src/core/ext/xds/xds_api.h"
39 #include "src/core/lib/gpr/env.h"
40 #include "src/core/lib/gpr/string.h"
41 #include "src/core/lib/gpr/useful.h"
42 #include "src/core/lib/iomgr/error.h"
43 #include "src/core/lib/iomgr/sockaddr_utils.h"
44 #include "src/core/lib/slice/slice_utils.h"
46 #include "envoy/config/cluster/v3/circuit_breaker.upb.h"
47 #include "envoy/config/cluster/v3/cluster.upb.h"
48 #include "envoy/config/cluster/v3/cluster.upbdefs.h"
49 #include "envoy/config/core/v3/address.upb.h"
50 #include "envoy/config/core/v3/base.upb.h"
51 #include "envoy/config/core/v3/config_source.upb.h"
52 #include "envoy/config/core/v3/health_check.upb.h"
53 #include "envoy/config/core/v3/protocol.upb.h"
54 #include "envoy/config/endpoint/v3/endpoint.upb.h"
55 #include "envoy/config/endpoint/v3/endpoint.upbdefs.h"
56 #include "envoy/config/endpoint/v3/endpoint_components.upb.h"
57 #include "envoy/config/endpoint/v3/load_report.upb.h"
58 #include "envoy/config/listener/v3/api_listener.upb.h"
59 #include "envoy/config/listener/v3/listener.upb.h"
60 #include "envoy/config/route/v3/route.upb.h"
61 #include "envoy/config/route/v3/route.upbdefs.h"
62 #include "envoy/config/route/v3/route_components.upb.h"
63 #include "envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.upb.h"
64 #include "envoy/extensions/transport_sockets/tls/v3/common.upb.h"
65 #include "envoy/extensions/transport_sockets/tls/v3/tls.upb.h"
66 #include "envoy/service/cluster/v3/cds.upb.h"
67 #include "envoy/service/cluster/v3/cds.upbdefs.h"
68 #include "envoy/service/discovery/v3/discovery.upb.h"
69 #include "envoy/service/discovery/v3/discovery.upbdefs.h"
70 #include "envoy/service/endpoint/v3/eds.upb.h"
71 #include "envoy/service/endpoint/v3/eds.upbdefs.h"
72 #include "envoy/service/listener/v3/lds.upb.h"
73 #include "envoy/service/load_stats/v3/lrs.upb.h"
74 #include "envoy/service/load_stats/v3/lrs.upbdefs.h"
75 #include "envoy/service/route/v3/rds.upb.h"
76 #include "envoy/service/route/v3/rds.upbdefs.h"
77 #include "envoy/type/matcher/v3/regex.upb.h"
78 #include "envoy/type/matcher/v3/string.upb.h"
79 #include "envoy/type/v3/percent.upb.h"
80 #include "envoy/type/v3/range.upb.h"
81 #include "google/protobuf/any.upb.h"
82 #include "google/protobuf/duration.upb.h"
83 #include "google/protobuf/struct.upb.h"
84 #include "google/protobuf/wrappers.upb.h"
85 #include "google/rpc/status.upb.h"
86 #include "upb/text_encode.h"
91 // TODO (donnadionne): Check to see if timeout is enabled, this will be
92 // removed once timeout feature is fully integration-tested and enabled by
94 bool XdsTimeoutEnabled() {
95 char* value = gpr_getenv("GRPC_XDS_EXPERIMENTAL_ENABLE_TIMEOUT");
97 bool parse_succeeded = gpr_parse_bool_value(value, &parsed_value);
99 return parse_succeeded && parsed_value;
102 // TODO(yashykt): Check to see if xDS security is enabled. This will be
103 // removed once this feature is fully integration-tested and enabled by
105 bool XdsSecurityEnabled() {
106 char* value = gpr_getenv("GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT");
108 bool parse_succeeded = gpr_parse_bool_value(value, &parsed_value);
110 return parse_succeeded && parsed_value;
114 // XdsApi::Route::Matchers::PathMatcher
117 XdsApi::Route::Matchers::PathMatcher::PathMatcher(const PathMatcher& other)
118 : type(other.type), case_sensitive(other.case_sensitive) {
119 if (type == PathMatcherType::REGEX) {
120 RE2::Options options;
121 options.set_case_sensitive(case_sensitive);
123 absl::make_unique<RE2>(other.regex_matcher->pattern(), options);
125 string_matcher = other.string_matcher;
129 XdsApi::Route::Matchers::PathMatcher& XdsApi::Route::Matchers::PathMatcher::
130 operator=(const PathMatcher& other) {
132 case_sensitive = other.case_sensitive;
133 if (type == PathMatcherType::REGEX) {
134 RE2::Options options;
135 options.set_case_sensitive(case_sensitive);
137 absl::make_unique<RE2>(other.regex_matcher->pattern(), options);
139 string_matcher = other.string_matcher;
144 bool XdsApi::Route::Matchers::PathMatcher::operator==(
145 const PathMatcher& other) const {
146 if (type != other.type) return false;
147 if (case_sensitive != other.case_sensitive) return false;
148 if (type == PathMatcherType::REGEX) {
149 // Should never be null.
150 if (regex_matcher == nullptr || other.regex_matcher == nullptr) {
153 return regex_matcher->pattern() == other.regex_matcher->pattern();
155 return string_matcher == other.string_matcher;
158 std::string XdsApi::Route::Matchers::PathMatcher::ToString() const {
159 std::string path_type_string;
161 case PathMatcherType::PATH:
162 path_type_string = "path match";
164 case PathMatcherType::PREFIX:
165 path_type_string = "prefix match";
167 case PathMatcherType::REGEX:
168 path_type_string = "regex match";
173 return absl::StrFormat("Path %s:%s%s", path_type_string,
174 type == PathMatcherType::REGEX
175 ? regex_matcher->pattern()
177 case_sensitive ? "" : "[case_sensitive=false]");
181 // XdsApi::Route::Matchers::HeaderMatcher
184 XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcher(
185 const HeaderMatcher& other)
186 : name(other.name), type(other.type), invert_match(other.invert_match) {
188 case HeaderMatcherType::REGEX:
189 regex_match = absl::make_unique<RE2>(other.regex_match->pattern());
191 case HeaderMatcherType::RANGE:
192 range_start = other.range_start;
193 range_end = other.range_end;
195 case HeaderMatcherType::PRESENT:
196 present_match = other.present_match;
199 string_matcher = other.string_matcher;
203 XdsApi::Route::Matchers::HeaderMatcher& XdsApi::Route::Matchers::HeaderMatcher::
204 operator=(const HeaderMatcher& other) {
207 invert_match = other.invert_match;
209 case HeaderMatcherType::REGEX:
210 regex_match = absl::make_unique<RE2>(other.regex_match->pattern());
212 case HeaderMatcherType::RANGE:
213 range_start = other.range_start;
214 range_end = other.range_end;
216 case HeaderMatcherType::PRESENT:
217 present_match = other.present_match;
220 string_matcher = other.string_matcher;
225 bool XdsApi::Route::Matchers::HeaderMatcher::operator==(
226 const HeaderMatcher& other) const {
227 if (name != other.name) return false;
228 if (type != other.type) return false;
229 if (invert_match != other.invert_match) return false;
231 case HeaderMatcherType::REGEX:
232 return regex_match->pattern() != other.regex_match->pattern();
233 case HeaderMatcherType::RANGE:
234 return range_start != other.range_start && range_end != other.range_end;
235 case HeaderMatcherType::PRESENT:
236 return present_match != other.present_match;
238 return string_matcher != other.string_matcher;
242 std::string XdsApi::Route::Matchers::HeaderMatcher::ToString() const {
244 case HeaderMatcherType::EXACT:
245 return absl::StrFormat("Header exact match:%s %s:%s",
246 invert_match ? " not" : "", name, string_matcher);
247 case HeaderMatcherType::REGEX:
248 return absl::StrFormat("Header regex match:%s %s:%s",
249 invert_match ? " not" : "", name,
250 regex_match->pattern());
251 case HeaderMatcherType::RANGE:
252 return absl::StrFormat("Header range match:%s %s:[%d, %d)",
253 invert_match ? " not" : "", name, range_start,
255 case HeaderMatcherType::PRESENT:
256 return absl::StrFormat("Header present match:%s %s:%s",
257 invert_match ? " not" : "", name,
258 present_match ? "true" : "false");
259 case HeaderMatcherType::PREFIX:
260 return absl::StrFormat("Header prefix match:%s %s:%s",
261 invert_match ? " not" : "", name, string_matcher);
262 case HeaderMatcherType::SUFFIX:
263 return absl::StrFormat("Header suffix match:%s %s:%s",
264 invert_match ? " not" : "", name, string_matcher);
274 std::string XdsApi::Route::Matchers::ToString() const {
275 std::vector<std::string> contents;
276 contents.push_back(path_matcher.ToString());
277 for (const HeaderMatcher& header_matcher : header_matchers) {
278 contents.push_back(header_matcher.ToString());
280 if (fraction_per_million.has_value()) {
281 contents.push_back(absl::StrFormat("Fraction Per Million %d",
282 fraction_per_million.value()));
284 return absl::StrJoin(contents, "\n");
287 std::string XdsApi::Route::ClusterWeight::ToString() const {
288 return absl::StrFormat("{cluster=%s, weight=%d}", name, weight);
291 std::string XdsApi::Route::ToString() const {
292 std::vector<std::string> contents;
293 contents.push_back(matchers.ToString());
294 if (!cluster_name.empty()) {
295 contents.push_back(absl::StrFormat("Cluster name: %s", cluster_name));
297 for (const ClusterWeight& cluster_weight : weighted_clusters) {
298 contents.push_back(cluster_weight.ToString());
300 if (max_stream_duration.has_value()) {
301 contents.push_back(max_stream_duration->ToString());
303 return absl::StrJoin(contents, "\n");
310 std::string XdsApi::RdsUpdate::ToString() const {
311 std::vector<std::string> vhosts;
312 for (const VirtualHost& vhost : virtual_hosts) {
314 absl::StrCat("vhost={\n"
316 absl::StrJoin(vhost.domains, ", "),
319 for (const XdsApi::Route& route : vhost.routes) {
320 vhosts.push_back(" {\n");
321 vhosts.push_back(route.ToString());
322 vhosts.push_back("\n }\n");
324 vhosts.push_back(" ]\n");
325 vhosts.push_back("]\n");
327 return absl::StrJoin(vhosts, "");
332 // Better match type has smaller value.
341 // Returns true if match succeeds.
342 bool DomainMatch(MatchType match_type, const std::string& domain_pattern_in,
343 const std::string& expected_host_name_in) {
344 // Normalize the args to lower-case. Domain matching is case-insensitive.
345 std::string domain_pattern = domain_pattern_in;
346 std::string expected_host_name = expected_host_name_in;
347 std::transform(domain_pattern.begin(), domain_pattern.end(),
348 domain_pattern.begin(),
349 [](unsigned char c) { return std::tolower(c); });
350 std::transform(expected_host_name.begin(), expected_host_name.end(),
351 expected_host_name.begin(),
352 [](unsigned char c) { return std::tolower(c); });
353 if (match_type == EXACT_MATCH) {
354 return domain_pattern == expected_host_name;
355 } else if (match_type == SUFFIX_MATCH) {
356 // Asterisk must match at least one char.
357 if (expected_host_name.size() < domain_pattern.size()) return false;
358 absl::string_view pattern_suffix(domain_pattern.c_str() + 1);
359 absl::string_view host_suffix(expected_host_name.c_str() +
360 expected_host_name.size() -
361 pattern_suffix.size());
362 return pattern_suffix == host_suffix;
363 } else if (match_type == PREFIX_MATCH) {
364 // Asterisk must match at least one char.
365 if (expected_host_name.size() < domain_pattern.size()) return false;
366 absl::string_view pattern_prefix(domain_pattern.c_str(),
367 domain_pattern.size() - 1);
368 absl::string_view host_prefix(expected_host_name.c_str(),
369 pattern_prefix.size());
370 return pattern_prefix == host_prefix;
372 return match_type == UNIVERSE_MATCH;
376 MatchType DomainPatternMatchType(const std::string& domain_pattern) {
377 if (domain_pattern.empty()) return INVALID_MATCH;
378 if (domain_pattern.find('*') == std::string::npos) return EXACT_MATCH;
379 if (domain_pattern == "*") return UNIVERSE_MATCH;
380 if (domain_pattern[0] == '*') return SUFFIX_MATCH;
381 if (domain_pattern[domain_pattern.size() - 1] == '*') return PREFIX_MATCH;
382 return INVALID_MATCH;
387 XdsApi::RdsUpdate::VirtualHost* XdsApi::RdsUpdate::FindVirtualHostForDomain(
388 const std::string& domain) {
389 // Find the best matched virtual host.
390 // The search order for 4 groups of domain patterns:
392 // 2. Suffix match (e.g., "*ABC").
393 // 3. Prefix match (e.g., "ABC*").
394 // 4. Universe match (i.e., "*").
395 // Within each group, longest match wins.
396 // If the same best matched domain pattern appears in multiple virtual hosts,
397 // the first matched virtual host wins.
398 VirtualHost* target_vhost = nullptr;
399 MatchType best_match_type = INVALID_MATCH;
400 size_t longest_match = 0;
401 // Check each domain pattern in each virtual host to determine the best
402 // matched virtual host.
403 for (VirtualHost& vhost : virtual_hosts) {
404 for (const std::string& domain_pattern : vhost.domains) {
405 // Check the match type first. Skip the pattern if it's not better than
407 const MatchType match_type = DomainPatternMatchType(domain_pattern);
408 // This should be caught by RouteConfigParse().
409 GPR_ASSERT(match_type != INVALID_MATCH);
410 if (match_type > best_match_type) continue;
411 if (match_type == best_match_type &&
412 domain_pattern.size() <= longest_match) {
415 // Skip if match fails.
416 if (!DomainMatch(match_type, domain_pattern, domain)) continue;
417 // Choose this match.
418 target_vhost = &vhost;
419 best_match_type = match_type;
420 longest_match = domain_pattern.size();
421 if (best_match_type == EXACT_MATCH) break;
423 if (best_match_type == EXACT_MATCH) break;
429 // XdsApi::StringMatcher
432 XdsApi::StringMatcher::StringMatcher(const StringMatcher& other)
435 case StringMatcherType::SAFE_REGEX:
436 regex_match = absl::make_unique<RE2>(other.regex_match->pattern());
439 string_matcher = other.string_matcher;
443 XdsApi::StringMatcher& XdsApi::StringMatcher::operator=(
444 const StringMatcher& other) {
447 case StringMatcherType::SAFE_REGEX:
448 regex_match = absl::make_unique<RE2>(other.regex_match->pattern());
451 string_matcher = other.string_matcher;
456 bool XdsApi::StringMatcher::operator==(const StringMatcher& other) const {
457 if (type != other.type) return false;
459 case StringMatcherType::SAFE_REGEX:
460 return regex_match->pattern() != other.regex_match->pattern();
462 return string_matcher != other.string_matcher;
470 std::string XdsApi::EdsUpdate::Priority::Locality::ToString() const {
471 std::vector<std::string> endpoint_strings;
472 for (const ServerAddress& endpoint : endpoints) {
473 endpoint_strings.emplace_back(endpoint.ToString());
475 return absl::StrCat("{name=", name->AsHumanReadableString(),
476 ", lb_weight=", lb_weight, ", endpoints=[",
477 absl::StrJoin(endpoint_strings, ", "), "]}");
480 bool XdsApi::EdsUpdate::Priority::operator==(const Priority& other) const {
481 if (localities.size() != other.localities.size()) return false;
482 auto it1 = localities.begin();
483 auto it2 = other.localities.begin();
484 while (it1 != localities.end()) {
485 if (*it1->first != *it2->first) return false;
486 if (it1->second != it2->second) return false;
493 std::string XdsApi::EdsUpdate::Priority::ToString() const {
494 std::vector<std::string> locality_strings;
495 for (const auto& p : localities) {
496 locality_strings.emplace_back(p.second.ToString());
498 return absl::StrCat("[", absl::StrJoin(locality_strings, ", "), "]");
501 bool XdsApi::EdsUpdate::DropConfig::ShouldDrop(
502 const std::string** category_name) const {
503 for (size_t i = 0; i < drop_category_list_.size(); ++i) {
504 const auto& drop_category = drop_category_list_[i];
505 // Generate a random number in [0, 1000000).
506 const uint32_t random = static_cast<uint32_t>(rand()) % 1000000;
507 if (random < drop_category.parts_per_million) {
508 *category_name = &drop_category.name;
515 std::string XdsApi::EdsUpdate::DropConfig::ToString() const {
516 std::vector<std::string> category_strings;
517 for (const DropCategory& category : drop_category_list_) {
518 category_strings.emplace_back(
519 absl::StrCat(category.name, "=", category.parts_per_million));
521 return absl::StrCat("{[", absl::StrJoin(category_strings, ", "),
522 "], drop_all=", drop_all_, "}");
525 std::string XdsApi::EdsUpdate::ToString() const {
526 std::vector<std::string> priority_strings;
527 for (size_t i = 0; i < priorities.size(); ++i) {
528 const Priority& priority = priorities[i];
529 priority_strings.emplace_back(
530 absl::StrCat("priority ", i, ": ", priority.ToString()));
532 return absl::StrCat("priorities=[", absl::StrJoin(priority_strings, ", "),
533 "], drop_config=", drop_config->ToString());
540 const char* XdsApi::kLdsTypeUrl =
541 "type.googleapis.com/envoy.config.listener.v3.Listener";
542 const char* XdsApi::kRdsTypeUrl =
543 "type.googleapis.com/envoy.config.route.v3.RouteConfiguration";
544 const char* XdsApi::kCdsTypeUrl =
545 "type.googleapis.com/envoy.config.cluster.v3.Cluster";
546 const char* XdsApi::kEdsTypeUrl =
547 "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment";
551 const char* kLdsV2TypeUrl = "type.googleapis.com/envoy.api.v2.Listener";
552 const char* kRdsV2TypeUrl =
553 "type.googleapis.com/envoy.api.v2.RouteConfiguration";
554 const char* kCdsV2TypeUrl = "type.googleapis.com/envoy.api.v2.Cluster";
555 const char* kEdsV2TypeUrl =
556 "type.googleapis.com/envoy.api.v2.ClusterLoadAssignment";
558 bool IsLds(absl::string_view type_url) {
559 return type_url == XdsApi::kLdsTypeUrl || type_url == kLdsV2TypeUrl;
562 bool IsRds(absl::string_view type_url) {
563 return type_url == XdsApi::kRdsTypeUrl || type_url == kRdsV2TypeUrl;
566 bool IsCds(absl::string_view type_url) {
567 return type_url == XdsApi::kCdsTypeUrl || type_url == kCdsV2TypeUrl;
570 bool IsEds(absl::string_view type_url) {
571 return type_url == XdsApi::kEdsTypeUrl || type_url == kEdsV2TypeUrl;
576 XdsApi::XdsApi(XdsClient* client, TraceFlag* tracer,
577 const XdsBootstrap::Node* node)
581 build_version_(absl::StrCat("gRPC C-core ", GPR_PLATFORM_STRING, " ",
582 grpc_version_string())),
583 user_agent_name_(absl::StrCat("gRPC C-core ", GPR_PLATFORM_STRING)) {}
587 // Works for both std::string and absl::string_view.
588 template <typename T>
589 inline upb_strview StdStringToUpbString(const T& str) {
590 return upb_strview_make(str.data(), str.size());
593 void PopulateMetadataValue(upb_arena* arena, google_protobuf_Value* value_pb,
596 void PopulateListValue(upb_arena* arena, google_protobuf_ListValue* list_value,
597 const Json::Array& values) {
598 for (const auto& value : values) {
599 auto* value_pb = google_protobuf_ListValue_add_values(list_value, arena);
600 PopulateMetadataValue(arena, value_pb, value);
604 void PopulateMetadata(upb_arena* arena, google_protobuf_Struct* metadata_pb,
605 const Json::Object& metadata) {
606 for (const auto& p : metadata) {
607 google_protobuf_Value* value = google_protobuf_Value_new(arena);
608 PopulateMetadataValue(arena, value, p.second);
609 google_protobuf_Struct_fields_set(
610 metadata_pb, StdStringToUpbString(p.first), value, arena);
614 void PopulateMetadataValue(upb_arena* arena, google_protobuf_Value* value_pb,
616 switch (value.type()) {
617 case Json::Type::JSON_NULL:
618 google_protobuf_Value_set_null_value(value_pb, 0);
620 case Json::Type::NUMBER:
621 google_protobuf_Value_set_number_value(
622 value_pb, strtod(value.string_value().c_str(), nullptr));
624 case Json::Type::STRING:
625 google_protobuf_Value_set_string_value(
626 value_pb, StdStringToUpbString(value.string_value()));
628 case Json::Type::JSON_TRUE:
629 google_protobuf_Value_set_bool_value(value_pb, true);
631 case Json::Type::JSON_FALSE:
632 google_protobuf_Value_set_bool_value(value_pb, false);
634 case Json::Type::OBJECT: {
635 google_protobuf_Struct* struct_value =
636 google_protobuf_Value_mutable_struct_value(value_pb, arena);
637 PopulateMetadata(arena, struct_value, value.object_value());
640 case Json::Type::ARRAY: {
641 google_protobuf_ListValue* list_value =
642 google_protobuf_Value_mutable_list_value(value_pb, arena);
643 PopulateListValue(arena, list_value, value.array_value());
649 // Helper functions to manually do protobuf string encoding, so that we
650 // can populate the node build_version field that was removed in v3.
651 std::string EncodeVarint(uint64_t val) {
654 uint8_t byte = val & 0x7fU;
656 if (val) byte |= 0x80U;
661 std::string EncodeTag(uint32_t field_number, uint8_t wire_type) {
662 return EncodeVarint((field_number << 3) | wire_type);
664 std::string EncodeStringField(uint32_t field_number, const std::string& str) {
665 static const uint8_t kDelimitedWireType = 2;
666 return EncodeTag(field_number, kDelimitedWireType) +
667 EncodeVarint(str.size()) + str;
670 void PopulateBuildVersion(upb_arena* arena, envoy_config_core_v3_Node* node_msg,
671 const std::string& build_version) {
672 std::string encoded_build_version = EncodeStringField(5, build_version);
673 // TODO(roth): This should use upb_msg_addunknown(), but that API is
674 // broken in the current version of upb, so we're using the internal
675 // API for now. Change this once we upgrade to a version of upb that
677 _upb_msg_addunknown(node_msg, encoded_build_version.data(),
678 encoded_build_version.size(), arena);
681 void PopulateNode(upb_arena* arena, const XdsBootstrap::Node* node, bool use_v3,
682 const std::string& build_version,
683 const std::string& user_agent_name,
684 envoy_config_core_v3_Node* node_msg) {
685 if (node != nullptr) {
686 if (!node->id.empty()) {
687 envoy_config_core_v3_Node_set_id(node_msg,
688 StdStringToUpbString(node->id));
690 if (!node->cluster.empty()) {
691 envoy_config_core_v3_Node_set_cluster(
692 node_msg, StdStringToUpbString(node->cluster));
694 if (!node->metadata.object_value().empty()) {
695 google_protobuf_Struct* metadata =
696 envoy_config_core_v3_Node_mutable_metadata(node_msg, arena);
697 PopulateMetadata(arena, metadata, node->metadata.object_value());
699 if (!node->locality_region.empty() || !node->locality_zone.empty() ||
700 !node->locality_subzone.empty()) {
701 envoy_config_core_v3_Locality* locality =
702 envoy_config_core_v3_Node_mutable_locality(node_msg, arena);
703 if (!node->locality_region.empty()) {
704 envoy_config_core_v3_Locality_set_region(
705 locality, StdStringToUpbString(node->locality_region));
707 if (!node->locality_zone.empty()) {
708 envoy_config_core_v3_Locality_set_zone(
709 locality, StdStringToUpbString(node->locality_zone));
711 if (!node->locality_subzone.empty()) {
712 envoy_config_core_v3_Locality_set_sub_zone(
713 locality, StdStringToUpbString(node->locality_subzone));
718 PopulateBuildVersion(arena, node_msg, build_version);
720 envoy_config_core_v3_Node_set_user_agent_name(
721 node_msg, StdStringToUpbString(user_agent_name));
722 envoy_config_core_v3_Node_set_user_agent_version(
723 node_msg, upb_strview_makez(grpc_version_string()));
724 envoy_config_core_v3_Node_add_client_features(
725 node_msg, upb_strview_makez("envoy.lb.does_not_support_overprovisioning"),
729 inline absl::string_view UpbStringToAbsl(const upb_strview& str) {
730 return absl::string_view(str.data, str.size);
733 inline std::string UpbStringToStdString(const upb_strview& str) {
734 return std::string(str.data, str.size);
737 void MaybeLogDiscoveryRequest(
738 XdsClient* client, TraceFlag* tracer, upb_symtab* symtab,
739 const envoy_service_discovery_v3_DiscoveryRequest* request) {
740 if (GRPC_TRACE_FLAG_ENABLED(*tracer) &&
741 gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
742 const upb_msgdef* msg_type =
743 envoy_service_discovery_v3_DiscoveryRequest_getmsgdef(symtab);
745 upb_text_encode(request, msg_type, nullptr, 0, buf, sizeof(buf));
746 gpr_log(GPR_DEBUG, "[xds_client %p] constructed ADS request: %s", client,
751 grpc_slice SerializeDiscoveryRequest(
752 upb_arena* arena, envoy_service_discovery_v3_DiscoveryRequest* request) {
753 size_t output_length;
754 char* output = envoy_service_discovery_v3_DiscoveryRequest_serialize(
755 request, arena, &output_length);
756 return grpc_slice_from_copied_buffer(output, output_length);
759 absl::string_view TypeUrlExternalToInternal(bool use_v3,
760 const std::string& type_url) {
762 if (type_url == XdsApi::kLdsTypeUrl) {
763 return kLdsV2TypeUrl;
765 if (type_url == XdsApi::kRdsTypeUrl) {
766 return kRdsV2TypeUrl;
768 if (type_url == XdsApi::kCdsTypeUrl) {
769 return kCdsV2TypeUrl;
771 if (type_url == XdsApi::kEdsTypeUrl) {
772 return kEdsV2TypeUrl;
780 grpc_slice XdsApi::CreateAdsRequest(
781 const XdsBootstrap::XdsServer& server, const std::string& type_url,
782 const std::set<absl::string_view>& resource_names,
783 const std::string& version, const std::string& nonce, grpc_error* error,
784 bool populate_node) {
787 envoy_service_discovery_v3_DiscoveryRequest* request =
788 envoy_service_discovery_v3_DiscoveryRequest_new(arena.ptr());
790 absl::string_view real_type_url =
791 TypeUrlExternalToInternal(server.ShouldUseV3(), type_url);
792 envoy_service_discovery_v3_DiscoveryRequest_set_type_url(
793 request, StdStringToUpbString(real_type_url));
795 if (!version.empty()) {
796 envoy_service_discovery_v3_DiscoveryRequest_set_version_info(
797 request, StdStringToUpbString(version));
800 if (!nonce.empty()) {
801 envoy_service_discovery_v3_DiscoveryRequest_set_response_nonce(
802 request, StdStringToUpbString(nonce));
804 // Set error_detail if it's a NACK.
805 if (error != GRPC_ERROR_NONE) {
806 google_rpc_Status* error_detail =
807 envoy_service_discovery_v3_DiscoveryRequest_mutable_error_detail(
808 request, arena.ptr());
809 // Hard-code INVALID_ARGUMENT as the status code.
810 // TODO(roth): If at some point we decide we care about this value,
811 // we could attach a status code to the individual errors where we
812 // generate them in the parsing code, and then use that here.
813 google_rpc_Status_set_code(error_detail, GRPC_STATUS_INVALID_ARGUMENT);
814 // Error description comes from the error that was passed in.
815 grpc_slice error_description_slice;
816 GPR_ASSERT(grpc_error_get_str(error, GRPC_ERROR_STR_DESCRIPTION,
817 &error_description_slice));
818 upb_strview error_description_strview =
819 StdStringToUpbString(StringViewFromSlice(error_description_slice));
820 google_rpc_Status_set_message(error_detail, error_description_strview);
821 GRPC_ERROR_UNREF(error);
825 envoy_config_core_v3_Node* node_msg =
826 envoy_service_discovery_v3_DiscoveryRequest_mutable_node(request,
828 PopulateNode(arena.ptr(), node_, server.ShouldUseV3(), build_version_,
829 user_agent_name_, node_msg);
831 // Add resource_names.
832 for (const auto& resource_name : resource_names) {
833 envoy_service_discovery_v3_DiscoveryRequest_add_resource_names(
834 request, StdStringToUpbString(resource_name), arena.ptr());
836 MaybeLogDiscoveryRequest(client_, tracer_, symtab_.ptr(), request);
837 return SerializeDiscoveryRequest(arena.ptr(), request);
842 void MaybeLogDiscoveryResponse(
843 XdsClient* client, TraceFlag* tracer, upb_symtab* symtab,
844 const envoy_service_discovery_v3_DiscoveryResponse* response) {
845 if (GRPC_TRACE_FLAG_ENABLED(*tracer) &&
846 gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
847 const upb_msgdef* msg_type =
848 envoy_service_discovery_v3_DiscoveryResponse_getmsgdef(symtab);
850 upb_text_encode(response, msg_type, nullptr, 0, buf, sizeof(buf));
851 gpr_log(GPR_DEBUG, "[xds_client %p] received response: %s", client, buf);
855 void MaybeLogRouteConfiguration(
856 XdsClient* client, TraceFlag* tracer, upb_symtab* symtab,
857 const envoy_config_route_v3_RouteConfiguration* route_config) {
858 if (GRPC_TRACE_FLAG_ENABLED(*tracer) &&
859 gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
860 const upb_msgdef* msg_type =
861 envoy_config_route_v3_RouteConfiguration_getmsgdef(symtab);
863 upb_text_encode(route_config, msg_type, nullptr, 0, buf, sizeof(buf));
864 gpr_log(GPR_DEBUG, "[xds_client %p] RouteConfiguration: %s", client, buf);
868 void MaybeLogCluster(XdsClient* client, TraceFlag* tracer, upb_symtab* symtab,
869 const envoy_config_cluster_v3_Cluster* cluster) {
870 if (GRPC_TRACE_FLAG_ENABLED(*tracer) &&
871 gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
872 const upb_msgdef* msg_type =
873 envoy_config_cluster_v3_Cluster_getmsgdef(symtab);
875 upb_text_encode(cluster, msg_type, nullptr, 0, buf, sizeof(buf));
876 gpr_log(GPR_DEBUG, "[xds_client %p] Cluster: %s", client, buf);
880 void MaybeLogClusterLoadAssignment(
881 XdsClient* client, TraceFlag* tracer, upb_symtab* symtab,
882 const envoy_config_endpoint_v3_ClusterLoadAssignment* cla) {
883 if (GRPC_TRACE_FLAG_ENABLED(*tracer) &&
884 gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
885 const upb_msgdef* msg_type =
886 envoy_config_endpoint_v3_ClusterLoadAssignment_getmsgdef(symtab);
888 upb_text_encode(cla, msg_type, nullptr, 0, buf, sizeof(buf));
889 gpr_log(GPR_DEBUG, "[xds_client %p] ClusterLoadAssignment: %s", client,
894 grpc_error* RoutePathMatchParse(const envoy_config_route_v3_RouteMatch* match,
895 XdsApi::Route* route, bool* ignore_route) {
896 auto* case_sensitive = envoy_config_route_v3_RouteMatch_case_sensitive(match);
897 if (case_sensitive != nullptr) {
898 route->matchers.path_matcher.case_sensitive =
899 google_protobuf_BoolValue_value(case_sensitive);
901 if (envoy_config_route_v3_RouteMatch_has_prefix(match)) {
902 absl::string_view prefix =
903 UpbStringToAbsl(envoy_config_route_v3_RouteMatch_prefix(match));
904 // Empty prefix "" is accepted.
905 if (!prefix.empty()) {
906 // Prefix "/" is accepted.
907 if (prefix[0] != '/') {
908 // Prefix which does not start with a / will never match anything, so
909 // ignore this route.
910 *ignore_route = true;
911 return GRPC_ERROR_NONE;
913 std::vector<absl::string_view> prefix_elements =
914 absl::StrSplit(prefix.substr(1), absl::MaxSplits('/', 2));
915 if (prefix_elements.size() > 2) {
916 // Prefix cannot have more than 2 slashes.
917 *ignore_route = true;
918 return GRPC_ERROR_NONE;
919 } else if (prefix_elements.size() == 2 && prefix_elements[0].empty()) {
920 // Prefix contains empty string between the 2 slashes
921 *ignore_route = true;
922 return GRPC_ERROR_NONE;
925 route->matchers.path_matcher.type =
926 XdsApi::Route::Matchers::PathMatcher::PathMatcherType::PREFIX;
927 route->matchers.path_matcher.string_matcher = std::string(prefix);
928 } else if (envoy_config_route_v3_RouteMatch_has_path(match)) {
929 absl::string_view path =
930 UpbStringToAbsl(envoy_config_route_v3_RouteMatch_path(match));
932 // Path that is empty will never match anything, so ignore this route.
933 *ignore_route = true;
934 return GRPC_ERROR_NONE;
936 if (path[0] != '/') {
937 // Path which does not start with a / will never match anything, so
938 // ignore this route.
939 *ignore_route = true;
940 return GRPC_ERROR_NONE;
942 std::vector<absl::string_view> path_elements =
943 absl::StrSplit(path.substr(1), absl::MaxSplits('/', 2));
944 if (path_elements.size() != 2) {
945 // Path not in the required format of /service/method will never match
946 // anything, so ignore this route.
947 *ignore_route = true;
948 return GRPC_ERROR_NONE;
949 } else if (path_elements[0].empty()) {
950 // Path contains empty service name will never match anything, so ignore
952 *ignore_route = true;
953 return GRPC_ERROR_NONE;
954 } else if (path_elements[1].empty()) {
955 // Path contains empty method name will never match anything, so ignore
957 *ignore_route = true;
958 return GRPC_ERROR_NONE;
960 route->matchers.path_matcher.type =
961 XdsApi::Route::Matchers::PathMatcher::PathMatcherType::PATH;
962 route->matchers.path_matcher.string_matcher = std::string(path);
963 } else if (envoy_config_route_v3_RouteMatch_has_safe_regex(match)) {
964 const envoy_type_matcher_v3_RegexMatcher* regex_matcher =
965 envoy_config_route_v3_RouteMatch_safe_regex(match);
966 GPR_ASSERT(regex_matcher != nullptr);
967 std::string matcher = UpbStringToStdString(
968 envoy_type_matcher_v3_RegexMatcher_regex(regex_matcher));
969 RE2::Options options;
970 options.set_case_sensitive(route->matchers.path_matcher.case_sensitive);
971 auto regex = absl::make_unique<RE2>(std::move(matcher), options);
973 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
974 "Invalid regex string specified in path matcher.");
976 route->matchers.path_matcher.type =
977 XdsApi::Route::Matchers::PathMatcher::PathMatcherType::REGEX;
978 route->matchers.path_matcher.regex_matcher = std::move(regex);
980 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
981 "Invalid route path specifier specified.");
983 return GRPC_ERROR_NONE;
986 grpc_error* RouteHeaderMatchersParse(
987 const envoy_config_route_v3_RouteMatch* match, XdsApi::Route* route) {
989 const envoy_config_route_v3_HeaderMatcher* const* headers =
990 envoy_config_route_v3_RouteMatch_headers(match, &size);
991 for (size_t i = 0; i < size; ++i) {
992 const envoy_config_route_v3_HeaderMatcher* header = headers[i];
993 XdsApi::Route::Matchers::HeaderMatcher header_matcher;
994 header_matcher.name =
995 UpbStringToStdString(envoy_config_route_v3_HeaderMatcher_name(header));
996 if (envoy_config_route_v3_HeaderMatcher_has_exact_match(header)) {
997 header_matcher.type =
998 XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::EXACT;
999 header_matcher.string_matcher = UpbStringToStdString(
1000 envoy_config_route_v3_HeaderMatcher_exact_match(header));
1001 } else if (envoy_config_route_v3_HeaderMatcher_has_safe_regex_match(
1003 const envoy_type_matcher_v3_RegexMatcher* regex_matcher =
1004 envoy_config_route_v3_HeaderMatcher_safe_regex_match(header);
1005 GPR_ASSERT(regex_matcher != nullptr);
1006 const std::string matcher = UpbStringToStdString(
1007 envoy_type_matcher_v3_RegexMatcher_regex(regex_matcher));
1008 std::unique_ptr<RE2> regex = absl::make_unique<RE2>(matcher);
1010 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1011 "Invalid regex string specified in header matcher.");
1013 header_matcher.type =
1014 XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::REGEX;
1015 header_matcher.regex_match = std::move(regex);
1016 } else if (envoy_config_route_v3_HeaderMatcher_has_range_match(header)) {
1017 header_matcher.type =
1018 XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::RANGE;
1019 const envoy_type_v3_Int64Range* range_matcher =
1020 envoy_config_route_v3_HeaderMatcher_range_match(header);
1021 header_matcher.range_start =
1022 envoy_type_v3_Int64Range_start(range_matcher);
1023 header_matcher.range_end = envoy_type_v3_Int64Range_end(range_matcher);
1024 if (header_matcher.range_end < header_matcher.range_start) {
1025 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1026 "Invalid range header matcher specifier specified: end "
1027 "cannot be smaller than start.");
1029 } else if (envoy_config_route_v3_HeaderMatcher_has_present_match(header)) {
1030 header_matcher.type =
1031 XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::PRESENT;
1032 header_matcher.present_match =
1033 envoy_config_route_v3_HeaderMatcher_present_match(header);
1034 } else if (envoy_config_route_v3_HeaderMatcher_has_prefix_match(header)) {
1035 header_matcher.type =
1036 XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::PREFIX;
1037 header_matcher.string_matcher = UpbStringToStdString(
1038 envoy_config_route_v3_HeaderMatcher_prefix_match(header));
1039 } else if (envoy_config_route_v3_HeaderMatcher_has_suffix_match(header)) {
1040 header_matcher.type =
1041 XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::SUFFIX;
1042 header_matcher.string_matcher = UpbStringToStdString(
1043 envoy_config_route_v3_HeaderMatcher_suffix_match(header));
1045 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1046 "Invalid route header matcher specified.");
1048 header_matcher.invert_match =
1049 envoy_config_route_v3_HeaderMatcher_invert_match(header);
1050 route->matchers.header_matchers.emplace_back(std::move(header_matcher));
1052 return GRPC_ERROR_NONE;
1055 grpc_error* RouteRuntimeFractionParse(
1056 const envoy_config_route_v3_RouteMatch* match, XdsApi::Route* route) {
1057 const envoy_config_core_v3_RuntimeFractionalPercent* runtime_fraction =
1058 envoy_config_route_v3_RouteMatch_runtime_fraction(match);
1059 if (runtime_fraction != nullptr) {
1060 const envoy_type_v3_FractionalPercent* fraction =
1061 envoy_config_core_v3_RuntimeFractionalPercent_default_value(
1063 if (fraction != nullptr) {
1064 uint32_t numerator = envoy_type_v3_FractionalPercent_numerator(fraction);
1065 const auto denominator =
1066 static_cast<envoy_type_v3_FractionalPercent_DenominatorType>(
1067 envoy_type_v3_FractionalPercent_denominator(fraction));
1068 // Normalize to million.
1069 switch (denominator) {
1070 case envoy_type_v3_FractionalPercent_HUNDRED:
1073 case envoy_type_v3_FractionalPercent_TEN_THOUSAND:
1076 case envoy_type_v3_FractionalPercent_MILLION:
1079 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1080 "Unknown denominator type");
1082 route->matchers.fraction_per_million = numerator;
1085 return GRPC_ERROR_NONE;
1088 grpc_error* RouteActionParse(const envoy_config_route_v3_Route* route_msg,
1089 XdsApi::Route* route, bool* ignore_route) {
1090 if (!envoy_config_route_v3_Route_has_route(route_msg)) {
1091 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1092 "No RouteAction found in route.");
1094 const envoy_config_route_v3_RouteAction* route_action =
1095 envoy_config_route_v3_Route_route(route_msg);
1096 // Get the cluster or weighted_clusters in the RouteAction.
1097 if (envoy_config_route_v3_RouteAction_has_cluster(route_action)) {
1098 route->cluster_name = UpbStringToStdString(
1099 envoy_config_route_v3_RouteAction_cluster(route_action));
1100 if (route->cluster_name.empty()) {
1101 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1102 "RouteAction cluster contains empty cluster name.");
1104 } else if (envoy_config_route_v3_RouteAction_has_weighted_clusters(
1106 const envoy_config_route_v3_WeightedCluster* weighted_cluster =
1107 envoy_config_route_v3_RouteAction_weighted_clusters(route_action);
1108 uint32_t total_weight = 100;
1109 const google_protobuf_UInt32Value* weight =
1110 envoy_config_route_v3_WeightedCluster_total_weight(weighted_cluster);
1111 if (weight != nullptr) {
1112 total_weight = google_protobuf_UInt32Value_value(weight);
1114 size_t clusters_size;
1115 const envoy_config_route_v3_WeightedCluster_ClusterWeight* const* clusters =
1116 envoy_config_route_v3_WeightedCluster_clusters(weighted_cluster,
1118 uint32_t sum_of_weights = 0;
1119 for (size_t j = 0; j < clusters_size; ++j) {
1120 const envoy_config_route_v3_WeightedCluster_ClusterWeight*
1121 cluster_weight = clusters[j];
1122 XdsApi::Route::ClusterWeight cluster;
1123 cluster.name = UpbStringToStdString(
1124 envoy_config_route_v3_WeightedCluster_ClusterWeight_name(
1126 if (cluster.name.empty()) {
1127 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1128 "RouteAction weighted_cluster cluster contains empty cluster "
1131 const google_protobuf_UInt32Value* weight =
1132 envoy_config_route_v3_WeightedCluster_ClusterWeight_weight(
1134 if (weight == nullptr) {
1135 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1136 "RouteAction weighted_cluster cluster missing weight");
1138 cluster.weight = google_protobuf_UInt32Value_value(weight);
1139 sum_of_weights += cluster.weight;
1140 route->weighted_clusters.emplace_back(std::move(cluster));
1142 if (total_weight != sum_of_weights) {
1143 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1144 "RouteAction weighted_cluster has incorrect total weight");
1146 if (route->weighted_clusters.empty()) {
1147 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1148 "RouteAction weighted_cluster has no valid clusters specified.");
1151 // No cluster or weighted_clusters found in RouteAction, ignore this route.
1152 *ignore_route = true;
1154 if (XdsTimeoutEnabled() && !*ignore_route) {
1155 const envoy_config_route_v3_RouteAction_MaxStreamDuration*
1156 max_stream_duration =
1157 envoy_config_route_v3_RouteAction_max_stream_duration(route_action);
1158 if (max_stream_duration != nullptr) {
1159 const google_protobuf_Duration* duration =
1160 envoy_config_route_v3_RouteAction_MaxStreamDuration_grpc_timeout_header_max(
1161 max_stream_duration);
1162 if (duration == nullptr) {
1164 envoy_config_route_v3_RouteAction_MaxStreamDuration_max_stream_duration(
1165 max_stream_duration);
1167 if (duration != nullptr) {
1168 XdsApi::Duration duration_in_route;
1169 duration_in_route.seconds = google_protobuf_Duration_seconds(duration);
1170 duration_in_route.nanos = google_protobuf_Duration_nanos(duration);
1171 route->max_stream_duration = duration_in_route;
1175 return GRPC_ERROR_NONE;
1178 grpc_error* RouteConfigParse(
1179 XdsClient* client, TraceFlag* tracer, upb_symtab* symtab,
1180 const envoy_config_route_v3_RouteConfiguration* route_config,
1181 XdsApi::RdsUpdate* rds_update) {
1182 MaybeLogRouteConfiguration(client, tracer, symtab, route_config);
1183 // Get the virtual hosts.
1185 const envoy_config_route_v3_VirtualHost* const* virtual_hosts =
1186 envoy_config_route_v3_RouteConfiguration_virtual_hosts(route_config,
1188 for (size_t i = 0; i < size; ++i) {
1189 rds_update->virtual_hosts.emplace_back();
1190 XdsApi::RdsUpdate::VirtualHost& vhost = rds_update->virtual_hosts.back();
1193 upb_strview const* domains = envoy_config_route_v3_VirtualHost_domains(
1194 virtual_hosts[i], &domain_size);
1195 for (size_t j = 0; j < domain_size; ++j) {
1196 std::string domain_pattern = UpbStringToStdString(domains[j]);
1197 const MatchType match_type = DomainPatternMatchType(domain_pattern);
1198 if (match_type == INVALID_MATCH) {
1199 return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1200 absl::StrCat("Invalid domain pattern \"", domain_pattern, "\".")
1203 vhost.domains.emplace_back(std::move(domain_pattern));
1205 if (vhost.domains.empty()) {
1206 return GRPC_ERROR_CREATE_FROM_STATIC_STRING("VirtualHost has no domains");
1210 const envoy_config_route_v3_Route* const* routes =
1211 envoy_config_route_v3_VirtualHost_routes(virtual_hosts[i], &num_routes);
1212 if (num_routes < 1) {
1213 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1214 "No route found in the virtual host.");
1216 // Loop over the whole list of routes
1217 for (size_t j = 0; j < num_routes; ++j) {
1218 const envoy_config_route_v3_RouteMatch* match =
1219 envoy_config_route_v3_Route_match(routes[j]);
1220 size_t query_parameters_size;
1221 static_cast<void>(envoy_config_route_v3_RouteMatch_query_parameters(
1222 match, &query_parameters_size));
1223 if (query_parameters_size > 0) {
1226 XdsApi::Route route;
1227 bool ignore_route = false;
1228 grpc_error* error = RoutePathMatchParse(match, &route, &ignore_route);
1229 if (error != GRPC_ERROR_NONE) return error;
1230 if (ignore_route) continue;
1231 error = RouteHeaderMatchersParse(match, &route);
1232 if (error != GRPC_ERROR_NONE) return error;
1233 error = RouteRuntimeFractionParse(match, &route);
1234 if (error != GRPC_ERROR_NONE) return error;
1235 error = RouteActionParse(routes[j], &route, &ignore_route);
1236 if (error != GRPC_ERROR_NONE) return error;
1237 if (ignore_route) continue;
1238 vhost.routes.emplace_back(std::move(route));
1240 if (vhost.routes.empty()) {
1241 return GRPC_ERROR_CREATE_FROM_STATIC_STRING("No valid routes specified.");
1244 return GRPC_ERROR_NONE;
1247 grpc_error* LdsResponseParse(
1248 XdsClient* client, TraceFlag* tracer, upb_symtab* symtab,
1249 const envoy_service_discovery_v3_DiscoveryResponse* response,
1250 const std::set<absl::string_view>& expected_listener_names,
1251 XdsApi::LdsUpdateMap* lds_update_map, upb_arena* arena) {
1252 // Get the resources from the response.
1254 const google_protobuf_Any* const* resources =
1255 envoy_service_discovery_v3_DiscoveryResponse_resources(response, &size);
1256 for (size_t i = 0; i < size; ++i) {
1257 // Check the type_url of the resource.
1258 absl::string_view type_url =
1259 UpbStringToAbsl(google_protobuf_Any_type_url(resources[i]));
1260 if (!IsLds(type_url)) {
1261 return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Resource is not LDS.");
1263 // Decode the listener.
1264 const upb_strview encoded_listener =
1265 google_protobuf_Any_value(resources[i]);
1266 const envoy_config_listener_v3_Listener* listener =
1267 envoy_config_listener_v3_Listener_parse(encoded_listener.data,
1268 encoded_listener.size, arena);
1269 if (listener == nullptr) {
1270 return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Can't decode listener.");
1272 // Check listener name. Ignore unexpected listeners.
1273 std::string listener_name =
1274 UpbStringToStdString(envoy_config_listener_v3_Listener_name(listener));
1275 if (expected_listener_names.find(listener_name) ==
1276 expected_listener_names.end()) {
1279 // Fail if listener name is duplicated.
1280 if (lds_update_map->find(listener_name) != lds_update_map->end()) {
1281 return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1282 absl::StrCat("duplicate listener name \"", listener_name, "\"")
1285 XdsApi::LdsUpdate& lds_update = (*lds_update_map)[listener_name];
1286 // Get api_listener and decode it to http_connection_manager.
1287 const envoy_config_listener_v3_ApiListener* api_listener =
1288 envoy_config_listener_v3_Listener_api_listener(listener);
1289 if (api_listener == nullptr) {
1290 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1291 "Listener has no ApiListener.");
1293 const upb_strview encoded_api_listener = google_protobuf_Any_value(
1294 envoy_config_listener_v3_ApiListener_api_listener(api_listener));
1295 const envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager*
1296 http_connection_manager =
1297 envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_parse(
1298 encoded_api_listener.data, encoded_api_listener.size, arena);
1299 if (http_connection_manager == nullptr) {
1300 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1301 "Could not parse HttpConnectionManager config from ApiListener");
1303 if (XdsTimeoutEnabled()) {
1304 // Obtain max_stream_duration from Http Protocol Options.
1305 const envoy_config_core_v3_HttpProtocolOptions* options =
1306 envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_common_http_protocol_options(
1307 http_connection_manager);
1308 if (options != nullptr) {
1309 const google_protobuf_Duration* duration =
1310 envoy_config_core_v3_HttpProtocolOptions_max_stream_duration(
1312 if (duration != nullptr) {
1313 lds_update.http_max_stream_duration.seconds =
1314 google_protobuf_Duration_seconds(duration);
1315 lds_update.http_max_stream_duration.nanos =
1316 google_protobuf_Duration_nanos(duration);
1320 // Found inlined route_config. Parse it to find the cluster_name.
1321 if (envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_has_route_config(
1322 http_connection_manager)) {
1323 const envoy_config_route_v3_RouteConfiguration* route_config =
1324 envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_route_config(
1325 http_connection_manager);
1326 XdsApi::RdsUpdate rds_update;
1328 RouteConfigParse(client, tracer, symtab, route_config, &rds_update);
1329 if (error != GRPC_ERROR_NONE) return error;
1330 lds_update.rds_update = std::move(rds_update);
1333 // Validate that RDS must be used to get the route_config dynamically.
1334 if (!envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_has_rds(
1335 http_connection_manager)) {
1336 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1337 "HttpConnectionManager neither has inlined route_config nor RDS.");
1339 const envoy_extensions_filters_network_http_connection_manager_v3_Rds* rds =
1340 envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_rds(
1341 http_connection_manager);
1342 // Check that the ConfigSource specifies ADS.
1343 const envoy_config_core_v3_ConfigSource* config_source =
1344 envoy_extensions_filters_network_http_connection_manager_v3_Rds_config_source(
1346 if (config_source == nullptr) {
1347 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1348 "HttpConnectionManager missing config_source for RDS.");
1350 if (!envoy_config_core_v3_ConfigSource_has_ads(config_source)) {
1351 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1352 "HttpConnectionManager ConfigSource for RDS does not specify ADS.");
1354 // Get the route_config_name.
1355 lds_update.route_config_name = UpbStringToStdString(
1356 envoy_extensions_filters_network_http_connection_manager_v3_Rds_route_config_name(
1359 return GRPC_ERROR_NONE;
1362 grpc_error* RdsResponseParse(
1363 XdsClient* client, TraceFlag* tracer, upb_symtab* symtab,
1364 const envoy_service_discovery_v3_DiscoveryResponse* response,
1365 const std::set<absl::string_view>& expected_route_configuration_names,
1366 XdsApi::RdsUpdateMap* rds_update_map, upb_arena* arena) {
1367 // Get the resources from the response.
1369 const google_protobuf_Any* const* resources =
1370 envoy_service_discovery_v3_DiscoveryResponse_resources(response, &size);
1371 for (size_t i = 0; i < size; ++i) {
1372 // Check the type_url of the resource.
1373 absl::string_view type_url =
1374 UpbStringToAbsl(google_protobuf_Any_type_url(resources[i]));
1375 if (!IsRds(type_url)) {
1376 return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Resource is not RDS.");
1378 // Decode the route_config.
1379 const upb_strview encoded_route_config =
1380 google_protobuf_Any_value(resources[i]);
1381 const envoy_config_route_v3_RouteConfiguration* route_config =
1382 envoy_config_route_v3_RouteConfiguration_parse(
1383 encoded_route_config.data, encoded_route_config.size, arena);
1384 if (route_config == nullptr) {
1385 return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Can't decode route_config.");
1387 // Check route_config_name. Ignore unexpected route_config.
1388 std::string route_config_name = UpbStringToStdString(
1389 envoy_config_route_v3_RouteConfiguration_name(route_config));
1390 if (expected_route_configuration_names.find(route_config_name) ==
1391 expected_route_configuration_names.end()) {
1394 // Fail if route config name is duplicated.
1395 if (rds_update_map->find(route_config_name) != rds_update_map->end()) {
1396 return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1397 absl::StrCat("duplicate route config name \"", route_config_name,
1401 // Parse the route_config.
1402 XdsApi::RdsUpdate& rds_update =
1403 (*rds_update_map)[std::move(route_config_name)];
1405 RouteConfigParse(client, tracer, symtab, route_config, &rds_update);
1406 if (error != GRPC_ERROR_NONE) return error;
1408 return GRPC_ERROR_NONE;
1411 XdsApi::CommonTlsContext::CertificateProviderInstance
1412 CertificateProviderInstanceParse(
1413 const envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CertificateProviderInstance*
1414 certificate_provider_instance_proto) {
1416 UpbStringToStdString(
1417 envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CertificateProviderInstance_instance_name(
1418 certificate_provider_instance_proto)),
1419 UpbStringToStdString(
1420 envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CertificateProviderInstance_certificate_name(
1421 certificate_provider_instance_proto))};
1424 grpc_error* CommonTlsContextParse(
1425 const envoy_extensions_transport_sockets_tls_v3_CommonTlsContext*
1426 common_tls_context_proto,
1427 XdsApi::CommonTlsContext* common_tls_context) GRPC_MUST_USE_RESULT;
1428 grpc_error* CommonTlsContextParse(
1429 const envoy_extensions_transport_sockets_tls_v3_CommonTlsContext*
1430 common_tls_context_proto,
1431 XdsApi::CommonTlsContext* common_tls_context) {
1432 auto* combined_validation_context =
1433 envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_combined_validation_context(
1434 common_tls_context_proto);
1435 if (combined_validation_context != nullptr) {
1436 auto* default_validation_context =
1437 envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CombinedCertificateValidationContext_default_validation_context(
1438 combined_validation_context);
1439 if (default_validation_context != nullptr) {
1441 auto* subject_alt_names_matchers =
1442 envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext_match_subject_alt_names(
1443 default_validation_context, &len);
1444 for (size_t i = 0; i < len; ++i) {
1445 XdsApi::StringMatcher matcher;
1446 if (envoy_type_matcher_v3_StringMatcher_has_exact(
1447 subject_alt_names_matchers[i])) {
1448 matcher.type = XdsApi::StringMatcher::StringMatcherType::EXACT;
1449 matcher.string_matcher =
1450 UpbStringToStdString(envoy_type_matcher_v3_StringMatcher_exact(
1451 subject_alt_names_matchers[i]));
1452 } else if (envoy_type_matcher_v3_StringMatcher_has_prefix(
1453 subject_alt_names_matchers[i])) {
1454 matcher.type = XdsApi::StringMatcher::StringMatcherType::PREFIX;
1455 matcher.string_matcher =
1456 UpbStringToStdString(envoy_type_matcher_v3_StringMatcher_prefix(
1457 subject_alt_names_matchers[i]));
1458 } else if (envoy_type_matcher_v3_StringMatcher_has_suffix(
1459 subject_alt_names_matchers[i])) {
1460 matcher.type = XdsApi::StringMatcher::StringMatcherType::SUFFIX;
1461 matcher.string_matcher =
1462 UpbStringToStdString(envoy_type_matcher_v3_StringMatcher_suffix(
1463 subject_alt_names_matchers[i]));
1464 } else if (envoy_type_matcher_v3_StringMatcher_has_safe_regex(
1465 subject_alt_names_matchers[i])) {
1466 matcher.type = XdsApi::StringMatcher::StringMatcherType::SAFE_REGEX;
1467 auto* regex_matcher = envoy_type_matcher_v3_StringMatcher_safe_regex(
1468 subject_alt_names_matchers[i]);
1469 std::unique_ptr<RE2> regex =
1470 absl::make_unique<RE2>(UpbStringToStdString(
1471 envoy_type_matcher_v3_RegexMatcher_regex(regex_matcher)));
1473 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1474 "Invalid regex string specified in string matcher.");
1476 matcher.regex_match = std::move(regex);
1478 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1479 "Invalid StringMatcher specified");
1481 matcher.ignore_case = envoy_type_matcher_v3_StringMatcher_ignore_case(
1482 subject_alt_names_matchers[i]);
1483 common_tls_context->combined_validation_context
1484 .default_validation_context.match_subject_alt_names.emplace_back(
1488 auto* validation_context_certificate_provider_instance =
1489 envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CombinedCertificateValidationContext_validation_context_certificate_provider_instance(
1490 combined_validation_context);
1491 if (validation_context_certificate_provider_instance != nullptr) {
1492 common_tls_context->combined_validation_context
1493 .validation_context_certificate_provider_instance =
1494 CertificateProviderInstanceParse(
1495 validation_context_certificate_provider_instance);
1498 auto* tls_certificate_certificate_provider_instance =
1499 envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_tls_certificate_certificate_provider_instance(
1500 common_tls_context_proto);
1501 if (tls_certificate_certificate_provider_instance != nullptr) {
1502 common_tls_context->tls_certificate_certificate_provider_instance =
1503 CertificateProviderInstanceParse(
1504 tls_certificate_certificate_provider_instance);
1506 return GRPC_ERROR_NONE;
1509 grpc_error* CdsResponseParse(
1510 XdsClient* client, TraceFlag* tracer, upb_symtab* symtab,
1511 const envoy_service_discovery_v3_DiscoveryResponse* response,
1512 const std::set<absl::string_view>& expected_cluster_names,
1513 XdsApi::CdsUpdateMap* cds_update_map, upb_arena* arena) {
1514 // Get the resources from the response.
1516 const google_protobuf_Any* const* resources =
1517 envoy_service_discovery_v3_DiscoveryResponse_resources(response, &size);
1518 // Parse all the resources in the CDS response.
1519 for (size_t i = 0; i < size; ++i) {
1520 // Check the type_url of the resource.
1521 absl::string_view type_url =
1522 UpbStringToAbsl(google_protobuf_Any_type_url(resources[i]));
1523 if (!IsCds(type_url)) {
1524 return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Resource is not CDS.");
1526 // Decode the cluster.
1527 const upb_strview encoded_cluster = google_protobuf_Any_value(resources[i]);
1528 const envoy_config_cluster_v3_Cluster* cluster =
1529 envoy_config_cluster_v3_Cluster_parse(encoded_cluster.data,
1530 encoded_cluster.size, arena);
1531 if (cluster == nullptr) {
1532 return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Can't decode cluster.");
1534 MaybeLogCluster(client, tracer, symtab, cluster);
1535 // Ignore unexpected cluster names.
1536 std::string cluster_name =
1537 UpbStringToStdString(envoy_config_cluster_v3_Cluster_name(cluster));
1538 if (expected_cluster_names.find(cluster_name) ==
1539 expected_cluster_names.end()) {
1542 // Fail on duplicate resources.
1543 if (cds_update_map->find(cluster_name) != cds_update_map->end()) {
1544 return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1545 absl::StrCat("duplicate resource name \"", cluster_name, "\"")
1548 XdsApi::CdsUpdate& cds_update = (*cds_update_map)[std::move(cluster_name)];
1549 // Check the cluster_discovery_type.
1550 if (!envoy_config_cluster_v3_Cluster_has_type(cluster)) {
1551 return GRPC_ERROR_CREATE_FROM_STATIC_STRING("DiscoveryType not found.");
1553 if (envoy_config_cluster_v3_Cluster_type(cluster) !=
1554 envoy_config_cluster_v3_Cluster_EDS) {
1555 return GRPC_ERROR_CREATE_FROM_STATIC_STRING("DiscoveryType is not EDS.");
1557 // Check the EDS config source.
1558 const envoy_config_cluster_v3_Cluster_EdsClusterConfig* eds_cluster_config =
1559 envoy_config_cluster_v3_Cluster_eds_cluster_config(cluster);
1560 const envoy_config_core_v3_ConfigSource* eds_config =
1561 envoy_config_cluster_v3_Cluster_EdsClusterConfig_eds_config(
1562 eds_cluster_config);
1563 if (!envoy_config_core_v3_ConfigSource_has_ads(eds_config)) {
1564 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1565 "EDS ConfigSource is not ADS.");
1567 // Record EDS service_name (if any).
1568 upb_strview service_name =
1569 envoy_config_cluster_v3_Cluster_EdsClusterConfig_service_name(
1570 eds_cluster_config);
1571 if (service_name.size != 0) {
1572 cds_update.eds_service_name = UpbStringToStdString(service_name);
1574 // Check the LB policy.
1575 if (envoy_config_cluster_v3_Cluster_lb_policy(cluster) !=
1576 envoy_config_cluster_v3_Cluster_ROUND_ROBIN) {
1577 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1578 "LB policy is not ROUND_ROBIN.");
1580 if (XdsSecurityEnabled()) {
1581 // Record Upstream tls context
1582 auto* transport_socket =
1583 envoy_config_cluster_v3_Cluster_transport_socket(cluster);
1584 if (transport_socket != nullptr) {
1585 absl::string_view name = UpbStringToAbsl(
1586 envoy_config_core_v3_TransportSocket_name(transport_socket));
1587 if (name == "envoy.transport_sockets.tls") {
1588 auto* typed_config =
1589 envoy_config_core_v3_TransportSocket_typed_config(
1591 if (typed_config != nullptr) {
1592 const upb_strview encoded_upstream_tls_context =
1593 google_protobuf_Any_value(typed_config);
1594 auto* upstream_tls_context =
1595 envoy_extensions_transport_sockets_tls_v3_UpstreamTlsContext_parse(
1596 encoded_upstream_tls_context.data,
1597 encoded_upstream_tls_context.size, arena);
1598 if (upstream_tls_context == nullptr) {
1599 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1600 "Can't decode upstream tls context.");
1602 auto* common_tls_context =
1603 envoy_extensions_transport_sockets_tls_v3_UpstreamTlsContext_common_tls_context(
1604 upstream_tls_context);
1605 if (common_tls_context != nullptr) {
1606 grpc_error* error = CommonTlsContextParse(
1607 common_tls_context, &cds_update.common_tls_context);
1608 if (error != GRPC_ERROR_NONE) return error;
1614 // Record LRS server name (if any).
1615 const envoy_config_core_v3_ConfigSource* lrs_server =
1616 envoy_config_cluster_v3_Cluster_lrs_server(cluster);
1617 if (lrs_server != nullptr) {
1618 if (!envoy_config_core_v3_ConfigSource_has_self(lrs_server)) {
1619 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1620 "LRS ConfigSource is not self.");
1622 cds_update.lrs_load_reporting_server_name.emplace("");
1624 // The Cluster resource encodes the circuit breaking parameters in a list of
1625 // Thresholds messages, where each message specifies the parameters for a
1626 // particular RoutingPriority. we will look only at the first entry in the
1627 // list for priority DEFAULT and default to 1024 if not found.
1628 if (envoy_config_cluster_v3_Cluster_has_circuit_breakers(cluster)) {
1629 const envoy_config_cluster_v3_CircuitBreakers* circuit_breakers =
1630 envoy_config_cluster_v3_Cluster_circuit_breakers(cluster);
1631 size_t num_thresholds;
1632 const envoy_config_cluster_v3_CircuitBreakers_Thresholds* const*
1633 thresholds = envoy_config_cluster_v3_CircuitBreakers_thresholds(
1634 circuit_breakers, &num_thresholds);
1635 for (size_t i = 0; i < num_thresholds; ++i) {
1636 const auto* threshold = thresholds[i];
1637 if (envoy_config_cluster_v3_CircuitBreakers_Thresholds_priority(
1638 threshold) == envoy_config_core_v3_DEFAULT) {
1639 const google_protobuf_UInt32Value* max_requests =
1640 envoy_config_cluster_v3_CircuitBreakers_Thresholds_max_requests(
1642 if (max_requests != nullptr) {
1643 cds_update.max_concurrent_requests =
1644 google_protobuf_UInt32Value_value(max_requests);
1651 return GRPC_ERROR_NONE;
1654 grpc_error* ServerAddressParseAndAppend(
1655 const envoy_config_endpoint_v3_LbEndpoint* lb_endpoint,
1656 ServerAddressList* list) {
1657 // If health_status is not HEALTHY or UNKNOWN, skip this endpoint.
1658 const int32_t health_status =
1659 envoy_config_endpoint_v3_LbEndpoint_health_status(lb_endpoint);
1660 if (health_status != envoy_config_core_v3_UNKNOWN &&
1661 health_status != envoy_config_core_v3_HEALTHY) {
1662 return GRPC_ERROR_NONE;
1664 // Find the ip:port.
1665 const envoy_config_endpoint_v3_Endpoint* endpoint =
1666 envoy_config_endpoint_v3_LbEndpoint_endpoint(lb_endpoint);
1667 const envoy_config_core_v3_Address* address =
1668 envoy_config_endpoint_v3_Endpoint_address(endpoint);
1669 const envoy_config_core_v3_SocketAddress* socket_address =
1670 envoy_config_core_v3_Address_socket_address(address);
1671 std::string address_str = UpbStringToStdString(
1672 envoy_config_core_v3_SocketAddress_address(socket_address));
1673 uint32_t port = envoy_config_core_v3_SocketAddress_port_value(socket_address);
1674 if (GPR_UNLIKELY(port >> 16) != 0) {
1675 return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Invalid port.");
1677 // Populate grpc_resolved_address.
1678 grpc_resolved_address addr;
1679 grpc_string_to_sockaddr(&addr, address_str.c_str(), port);
1680 // Append the address to the list.
1681 list->emplace_back(addr, nullptr);
1682 return GRPC_ERROR_NONE;
1685 grpc_error* LocalityParse(
1686 const envoy_config_endpoint_v3_LocalityLbEndpoints* locality_lb_endpoints,
1687 XdsApi::EdsUpdate::Priority::Locality* output_locality, size_t* priority) {
1689 const google_protobuf_UInt32Value* lb_weight =
1690 envoy_config_endpoint_v3_LocalityLbEndpoints_load_balancing_weight(
1691 locality_lb_endpoints);
1692 // If LB weight is not specified, it means this locality is assigned no load.
1693 // TODO(juanlishen): When we support CDS to configure the inter-locality
1694 // policy, we should change the LB weight handling.
1695 output_locality->lb_weight =
1696 lb_weight != nullptr ? google_protobuf_UInt32Value_value(lb_weight) : 0;
1697 if (output_locality->lb_weight == 0) return GRPC_ERROR_NONE;
1698 // Parse locality name.
1699 const envoy_config_core_v3_Locality* locality =
1700 envoy_config_endpoint_v3_LocalityLbEndpoints_locality(
1701 locality_lb_endpoints);
1702 std::string region =
1703 UpbStringToStdString(envoy_config_core_v3_Locality_region(locality));
1705 UpbStringToStdString(envoy_config_core_v3_Locality_region(locality));
1706 std::string sub_zone =
1707 UpbStringToStdString(envoy_config_core_v3_Locality_sub_zone(locality));
1708 output_locality->name = MakeRefCounted<XdsLocalityName>(
1709 std::move(region), std::move(zone), std::move(sub_zone));
1710 // Parse the addresses.
1712 const envoy_config_endpoint_v3_LbEndpoint* const* lb_endpoints =
1713 envoy_config_endpoint_v3_LocalityLbEndpoints_lb_endpoints(
1714 locality_lb_endpoints, &size);
1715 for (size_t i = 0; i < size; ++i) {
1716 grpc_error* error = ServerAddressParseAndAppend(
1717 lb_endpoints[i], &output_locality->endpoints);
1718 if (error != GRPC_ERROR_NONE) return error;
1720 // Parse the priority.
1721 *priority = envoy_config_endpoint_v3_LocalityLbEndpoints_priority(
1722 locality_lb_endpoints);
1723 return GRPC_ERROR_NONE;
1726 grpc_error* DropParseAndAppend(
1727 const envoy_config_endpoint_v3_ClusterLoadAssignment_Policy_DropOverload*
1729 XdsApi::EdsUpdate::DropConfig* drop_config) {
1730 // Get the category.
1731 std::string category = UpbStringToStdString(
1732 envoy_config_endpoint_v3_ClusterLoadAssignment_Policy_DropOverload_category(
1734 if (category.empty()) {
1735 return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Empty drop category name");
1737 // Get the drop rate (per million).
1738 const envoy_type_v3_FractionalPercent* drop_percentage =
1739 envoy_config_endpoint_v3_ClusterLoadAssignment_Policy_DropOverload_drop_percentage(
1741 uint32_t numerator =
1742 envoy_type_v3_FractionalPercent_numerator(drop_percentage);
1743 const auto denominator =
1744 static_cast<envoy_type_v3_FractionalPercent_DenominatorType>(
1745 envoy_type_v3_FractionalPercent_denominator(drop_percentage));
1746 // Normalize to million.
1747 switch (denominator) {
1748 case envoy_type_v3_FractionalPercent_HUNDRED:
1751 case envoy_type_v3_FractionalPercent_TEN_THOUSAND:
1754 case envoy_type_v3_FractionalPercent_MILLION:
1757 return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Unknown denominator type");
1759 // Cap numerator to 1000000.
1760 numerator = GPR_MIN(numerator, 1000000);
1761 drop_config->AddCategory(std::move(category), numerator);
1762 return GRPC_ERROR_NONE;
1765 grpc_error* EdsResponseParse(
1766 XdsClient* client, TraceFlag* tracer, upb_symtab* symtab,
1767 const envoy_service_discovery_v3_DiscoveryResponse* response,
1768 const std::set<absl::string_view>& expected_eds_service_names,
1769 XdsApi::EdsUpdateMap* eds_update_map, upb_arena* arena) {
1770 // Get the resources from the response.
1772 const google_protobuf_Any* const* resources =
1773 envoy_service_discovery_v3_DiscoveryResponse_resources(response, &size);
1774 for (size_t i = 0; i < size; ++i) {
1775 // Check the type_url of the resource.
1776 absl::string_view type_url =
1777 UpbStringToAbsl(google_protobuf_Any_type_url(resources[i]));
1778 if (!IsEds(type_url)) {
1779 return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Resource is not EDS.");
1781 // Get the cluster_load_assignment.
1782 upb_strview encoded_cluster_load_assignment =
1783 google_protobuf_Any_value(resources[i]);
1784 envoy_config_endpoint_v3_ClusterLoadAssignment* cluster_load_assignment =
1785 envoy_config_endpoint_v3_ClusterLoadAssignment_parse(
1786 encoded_cluster_load_assignment.data,
1787 encoded_cluster_load_assignment.size, arena);
1788 if (cluster_load_assignment == nullptr) {
1789 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1790 "Can't parse cluster_load_assignment.");
1792 MaybeLogClusterLoadAssignment(client, tracer, symtab,
1793 cluster_load_assignment);
1794 // Check the EDS service name. Ignore unexpected names.
1795 std::string eds_service_name = UpbStringToStdString(
1796 envoy_config_endpoint_v3_ClusterLoadAssignment_cluster_name(
1797 cluster_load_assignment));
1798 if (expected_eds_service_names.find(eds_service_name) ==
1799 expected_eds_service_names.end()) {
1802 // Fail on duplicate resources.
1803 if (eds_update_map->find(eds_service_name) != eds_update_map->end()) {
1804 return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1805 absl::StrCat("duplicate resource name \"", eds_service_name, "\"")
1808 XdsApi::EdsUpdate& eds_update =
1809 (*eds_update_map)[std::move(eds_service_name)];
1810 // Get the endpoints.
1811 size_t locality_size;
1812 const envoy_config_endpoint_v3_LocalityLbEndpoints* const* endpoints =
1813 envoy_config_endpoint_v3_ClusterLoadAssignment_endpoints(
1814 cluster_load_assignment, &locality_size);
1815 for (size_t j = 0; j < locality_size; ++j) {
1817 XdsApi::EdsUpdate::Priority::Locality locality;
1818 grpc_error* error = LocalityParse(endpoints[j], &locality, &priority);
1819 if (error != GRPC_ERROR_NONE) return error;
1820 // Filter out locality with weight 0.
1821 if (locality.lb_weight == 0) continue;
1822 // Make sure prorities is big enough. Note that they might not
1823 // arrive in priority order.
1824 while (eds_update.priorities.size() < priority + 1) {
1825 eds_update.priorities.emplace_back();
1827 eds_update.priorities[priority].localities.emplace(locality.name.get(),
1828 std::move(locality));
1830 for (const auto& priority : eds_update.priorities) {
1831 if (priority.localities.empty()) {
1832 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1833 "EDS update includes sparse priority list");
1836 // Get the drop config.
1837 eds_update.drop_config = MakeRefCounted<XdsApi::EdsUpdate::DropConfig>();
1838 const envoy_config_endpoint_v3_ClusterLoadAssignment_Policy* policy =
1839 envoy_config_endpoint_v3_ClusterLoadAssignment_policy(
1840 cluster_load_assignment);
1841 if (policy != nullptr) {
1843 const envoy_config_endpoint_v3_ClusterLoadAssignment_Policy_DropOverload* const*
1845 envoy_config_endpoint_v3_ClusterLoadAssignment_Policy_drop_overloads(
1846 policy, &drop_size);
1847 for (size_t j = 0; j < drop_size; ++j) {
1849 DropParseAndAppend(drop_overload[j], eds_update.drop_config.get());
1850 if (error != GRPC_ERROR_NONE) return error;
1854 return GRPC_ERROR_NONE;
1857 std::string TypeUrlInternalToExternal(absl::string_view type_url) {
1858 if (type_url == kLdsV2TypeUrl) {
1859 return XdsApi::kLdsTypeUrl;
1860 } else if (type_url == kRdsV2TypeUrl) {
1861 return XdsApi::kRdsTypeUrl;
1862 } else if (type_url == kCdsV2TypeUrl) {
1863 return XdsApi::kCdsTypeUrl;
1864 } else if (type_url == kEdsV2TypeUrl) {
1865 return XdsApi::kEdsTypeUrl;
1867 return std::string(type_url);
1872 XdsApi::AdsParseResult XdsApi::ParseAdsResponse(
1873 const grpc_slice& encoded_response,
1874 const std::set<absl::string_view>& expected_listener_names,
1875 const std::set<absl::string_view>& expected_route_configuration_names,
1876 const std::set<absl::string_view>& expected_cluster_names,
1877 const std::set<absl::string_view>& expected_eds_service_names) {
1878 AdsParseResult result;
1880 // Decode the response.
1881 const envoy_service_discovery_v3_DiscoveryResponse* response =
1882 envoy_service_discovery_v3_DiscoveryResponse_parse(
1883 reinterpret_cast<const char*>(GRPC_SLICE_START_PTR(encoded_response)),
1884 GRPC_SLICE_LENGTH(encoded_response), arena.ptr());
1885 // If decoding fails, output an empty type_url and return.
1886 if (response == nullptr) {
1887 result.parse_error =
1888 GRPC_ERROR_CREATE_FROM_STATIC_STRING("Can't decode DiscoveryResponse.");
1891 MaybeLogDiscoveryResponse(client_, tracer_, symtab_.ptr(), response);
1892 // Record the type_url, the version_info, and the nonce of the response.
1893 result.type_url = TypeUrlInternalToExternal(UpbStringToAbsl(
1894 envoy_service_discovery_v3_DiscoveryResponse_type_url(response)));
1895 result.version = UpbStringToStdString(
1896 envoy_service_discovery_v3_DiscoveryResponse_version_info(response));
1897 result.nonce = UpbStringToStdString(
1898 envoy_service_discovery_v3_DiscoveryResponse_nonce(response));
1899 // Parse the response according to the resource type.
1900 if (IsLds(result.type_url)) {
1901 result.parse_error = LdsResponseParse(client_, tracer_, symtab_.ptr(),
1902 response, expected_listener_names,
1903 &result.lds_update_map, arena.ptr());
1904 } else if (IsRds(result.type_url)) {
1905 result.parse_error =
1906 RdsResponseParse(client_, tracer_, symtab_.ptr(), response,
1907 expected_route_configuration_names,
1908 &result.rds_update_map, arena.ptr());
1909 } else if (IsCds(result.type_url)) {
1910 result.parse_error = CdsResponseParse(client_, tracer_, symtab_.ptr(),
1911 response, expected_cluster_names,
1912 &result.cds_update_map, arena.ptr());
1913 } else if (IsEds(result.type_url)) {
1914 result.parse_error = EdsResponseParse(client_, tracer_, symtab_.ptr(),
1915 response, expected_eds_service_names,
1916 &result.eds_update_map, arena.ptr());
1923 void MaybeLogLrsRequest(
1924 XdsClient* client, TraceFlag* tracer, upb_symtab* symtab,
1925 const envoy_service_load_stats_v3_LoadStatsRequest* request) {
1926 if (GRPC_TRACE_FLAG_ENABLED(*tracer) &&
1927 gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
1928 const upb_msgdef* msg_type =
1929 envoy_service_load_stats_v3_LoadStatsRequest_getmsgdef(symtab);
1931 upb_text_encode(request, msg_type, nullptr, 0, buf, sizeof(buf));
1932 gpr_log(GPR_DEBUG, "[xds_client %p] constructed LRS request: %s", client,
1937 grpc_slice SerializeLrsRequest(
1938 const envoy_service_load_stats_v3_LoadStatsRequest* request,
1940 size_t output_length;
1941 char* output = envoy_service_load_stats_v3_LoadStatsRequest_serialize(
1942 request, arena, &output_length);
1943 return grpc_slice_from_copied_buffer(output, output_length);
1948 grpc_slice XdsApi::CreateLrsInitialRequest(
1949 const XdsBootstrap::XdsServer& server) {
1951 // Create a request.
1952 envoy_service_load_stats_v3_LoadStatsRequest* request =
1953 envoy_service_load_stats_v3_LoadStatsRequest_new(arena.ptr());
1955 envoy_config_core_v3_Node* node_msg =
1956 envoy_service_load_stats_v3_LoadStatsRequest_mutable_node(request,
1958 PopulateNode(arena.ptr(), node_, server.ShouldUseV3(), build_version_,
1959 user_agent_name_, node_msg);
1960 envoy_config_core_v3_Node_add_client_features(
1961 node_msg, upb_strview_makez("envoy.lrs.supports_send_all_clusters"),
1963 MaybeLogLrsRequest(client_, tracer_, symtab_.ptr(), request);
1964 return SerializeLrsRequest(request, arena.ptr());
1969 void LocalityStatsPopulate(
1970 envoy_config_endpoint_v3_UpstreamLocalityStats* output,
1971 const XdsLocalityName& locality_name,
1972 const XdsClusterLocalityStats::Snapshot& snapshot, upb_arena* arena) {
1974 envoy_config_core_v3_Locality* locality =
1975 envoy_config_endpoint_v3_UpstreamLocalityStats_mutable_locality(output,
1977 if (!locality_name.region().empty()) {
1978 envoy_config_core_v3_Locality_set_region(
1979 locality, StdStringToUpbString(locality_name.region()));
1981 if (!locality_name.zone().empty()) {
1982 envoy_config_core_v3_Locality_set_zone(
1983 locality, StdStringToUpbString(locality_name.zone()));
1985 if (!locality_name.sub_zone().empty()) {
1986 envoy_config_core_v3_Locality_set_sub_zone(
1987 locality, StdStringToUpbString(locality_name.sub_zone()));
1989 // Set total counts.
1990 envoy_config_endpoint_v3_UpstreamLocalityStats_set_total_successful_requests(
1991 output, snapshot.total_successful_requests);
1992 envoy_config_endpoint_v3_UpstreamLocalityStats_set_total_requests_in_progress(
1993 output, snapshot.total_requests_in_progress);
1994 envoy_config_endpoint_v3_UpstreamLocalityStats_set_total_error_requests(
1995 output, snapshot.total_error_requests);
1996 envoy_config_endpoint_v3_UpstreamLocalityStats_set_total_issued_requests(
1997 output, snapshot.total_issued_requests);
1998 // Add backend metrics.
1999 for (const auto& p : snapshot.backend_metrics) {
2000 const std::string& metric_name = p.first;
2001 const XdsClusterLocalityStats::BackendMetric& metric_value = p.second;
2002 envoy_config_endpoint_v3_EndpointLoadMetricStats* load_metric =
2003 envoy_config_endpoint_v3_UpstreamLocalityStats_add_load_metric_stats(
2005 envoy_config_endpoint_v3_EndpointLoadMetricStats_set_metric_name(
2006 load_metric, StdStringToUpbString(metric_name));
2007 envoy_config_endpoint_v3_EndpointLoadMetricStats_set_num_requests_finished_with_metric(
2008 load_metric, metric_value.num_requests_finished_with_metric);
2009 envoy_config_endpoint_v3_EndpointLoadMetricStats_set_total_metric_value(
2010 load_metric, metric_value.total_metric_value);
2016 grpc_slice XdsApi::CreateLrsRequest(
2017 ClusterLoadReportMap cluster_load_report_map) {
2019 // Create a request.
2020 envoy_service_load_stats_v3_LoadStatsRequest* request =
2021 envoy_service_load_stats_v3_LoadStatsRequest_new(arena.ptr());
2022 for (auto& p : cluster_load_report_map) {
2023 const std::string& cluster_name = p.first.first;
2024 const std::string& eds_service_name = p.first.second;
2025 const ClusterLoadReport& load_report = p.second;
2026 // Add cluster stats.
2027 envoy_config_endpoint_v3_ClusterStats* cluster_stats =
2028 envoy_service_load_stats_v3_LoadStatsRequest_add_cluster_stats(
2029 request, arena.ptr());
2030 // Set the cluster name.
2031 envoy_config_endpoint_v3_ClusterStats_set_cluster_name(
2032 cluster_stats, StdStringToUpbString(cluster_name));
2033 // Set EDS service name, if non-empty.
2034 if (!eds_service_name.empty()) {
2035 envoy_config_endpoint_v3_ClusterStats_set_cluster_service_name(
2036 cluster_stats, StdStringToUpbString(eds_service_name));
2038 // Add locality stats.
2039 for (const auto& p : load_report.locality_stats) {
2040 const XdsLocalityName& locality_name = *p.first;
2041 const auto& snapshot = p.second;
2042 envoy_config_endpoint_v3_UpstreamLocalityStats* locality_stats =
2043 envoy_config_endpoint_v3_ClusterStats_add_upstream_locality_stats(
2044 cluster_stats, arena.ptr());
2045 LocalityStatsPopulate(locality_stats, locality_name, snapshot,
2048 // Add dropped requests.
2049 uint64_t total_dropped_requests = 0;
2050 for (const auto& p : load_report.dropped_requests.categorized_drops) {
2051 const std::string& category = p.first;
2052 const uint64_t count = p.second;
2053 envoy_config_endpoint_v3_ClusterStats_DroppedRequests* dropped_requests =
2054 envoy_config_endpoint_v3_ClusterStats_add_dropped_requests(
2055 cluster_stats, arena.ptr());
2056 envoy_config_endpoint_v3_ClusterStats_DroppedRequests_set_category(
2057 dropped_requests, StdStringToUpbString(category));
2058 envoy_config_endpoint_v3_ClusterStats_DroppedRequests_set_dropped_count(
2059 dropped_requests, count);
2060 total_dropped_requests += count;
2062 total_dropped_requests += load_report.dropped_requests.uncategorized_drops;
2063 // Set total dropped requests.
2064 envoy_config_endpoint_v3_ClusterStats_set_total_dropped_requests(
2065 cluster_stats, total_dropped_requests);
2066 // Set real load report interval.
2067 gpr_timespec timespec =
2068 grpc_millis_to_timespec(load_report.load_report_interval, GPR_TIMESPAN);
2069 google_protobuf_Duration* load_report_interval =
2070 envoy_config_endpoint_v3_ClusterStats_mutable_load_report_interval(
2071 cluster_stats, arena.ptr());
2072 google_protobuf_Duration_set_seconds(load_report_interval, timespec.tv_sec);
2073 google_protobuf_Duration_set_nanos(load_report_interval, timespec.tv_nsec);
2075 MaybeLogLrsRequest(client_, tracer_, symtab_.ptr(), request);
2076 return SerializeLrsRequest(request, arena.ptr());
2079 grpc_error* XdsApi::ParseLrsResponse(const grpc_slice& encoded_response,
2080 bool* send_all_clusters,
2081 std::set<std::string>* cluster_names,
2082 grpc_millis* load_reporting_interval) {
2084 // Decode the response.
2085 const envoy_service_load_stats_v3_LoadStatsResponse* decoded_response =
2086 envoy_service_load_stats_v3_LoadStatsResponse_parse(
2087 reinterpret_cast<const char*>(GRPC_SLICE_START_PTR(encoded_response)),
2088 GRPC_SLICE_LENGTH(encoded_response), arena.ptr());
2089 // Parse the response.
2090 if (decoded_response == nullptr) {
2091 return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Can't decode response.");
2093 // Check send_all_clusters.
2094 if (envoy_service_load_stats_v3_LoadStatsResponse_send_all_clusters(
2095 decoded_response)) {
2096 *send_all_clusters = true;
2098 // Store the cluster names.
2100 const upb_strview* clusters =
2101 envoy_service_load_stats_v3_LoadStatsResponse_clusters(decoded_response,
2103 for (size_t i = 0; i < size; ++i) {
2104 cluster_names->emplace(UpbStringToStdString(clusters[i]));
2107 // Get the load report interval.
2108 const google_protobuf_Duration* load_reporting_interval_duration =
2109 envoy_service_load_stats_v3_LoadStatsResponse_load_reporting_interval(
2111 gpr_timespec timespec{
2112 google_protobuf_Duration_seconds(load_reporting_interval_duration),
2113 google_protobuf_Duration_nanos(load_reporting_interval_duration),
2115 *load_reporting_interval = gpr_time_to_millis(timespec);
2116 return GRPC_ERROR_NONE;
2119 } // namespace grpc_core