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/gprpp/host_port.h"
43 #include "src/core/lib/iomgr/error.h"
44 #include "src/core/lib/iomgr/sockaddr_utils.h"
45 #include "src/core/lib/slice/slice_utils.h"
47 #include "envoy/config/cluster/v3/circuit_breaker.upb.h"
48 #include "envoy/config/cluster/v3/cluster.upb.h"
49 #include "envoy/config/cluster/v3/cluster.upbdefs.h"
50 #include "envoy/config/core/v3/address.upb.h"
51 #include "envoy/config/core/v3/base.upb.h"
52 #include "envoy/config/core/v3/config_source.upb.h"
53 #include "envoy/config/core/v3/health_check.upb.h"
54 #include "envoy/config/core/v3/protocol.upb.h"
55 #include "envoy/config/endpoint/v3/endpoint.upb.h"
56 #include "envoy/config/endpoint/v3/endpoint.upbdefs.h"
57 #include "envoy/config/endpoint/v3/endpoint_components.upb.h"
58 #include "envoy/config/endpoint/v3/load_report.upb.h"
59 #include "envoy/config/listener/v3/api_listener.upb.h"
60 #include "envoy/config/listener/v3/listener.upb.h"
61 #include "envoy/config/listener/v3/listener_components.upb.h"
62 #include "envoy/config/route/v3/route.upb.h"
63 #include "envoy/config/route/v3/route.upbdefs.h"
64 #include "envoy/config/route/v3/route_components.upb.h"
65 #include "envoy/extensions/clusters/aggregate/v3/cluster.upb.h"
66 #include "envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.upb.h"
67 #include "envoy/extensions/transport_sockets/tls/v3/common.upb.h"
68 #include "envoy/extensions/transport_sockets/tls/v3/tls.upb.h"
69 #include "envoy/service/cluster/v3/cds.upb.h"
70 #include "envoy/service/cluster/v3/cds.upbdefs.h"
71 #include "envoy/service/discovery/v3/discovery.upb.h"
72 #include "envoy/service/discovery/v3/discovery.upbdefs.h"
73 #include "envoy/service/endpoint/v3/eds.upb.h"
74 #include "envoy/service/endpoint/v3/eds.upbdefs.h"
75 #include "envoy/service/listener/v3/lds.upb.h"
76 #include "envoy/service/load_stats/v3/lrs.upb.h"
77 #include "envoy/service/load_stats/v3/lrs.upbdefs.h"
78 #include "envoy/service/route/v3/rds.upb.h"
79 #include "envoy/service/route/v3/rds.upbdefs.h"
80 #include "envoy/type/matcher/v3/regex.upb.h"
81 #include "envoy/type/matcher/v3/string.upb.h"
82 #include "envoy/type/v3/percent.upb.h"
83 #include "envoy/type/v3/range.upb.h"
84 #include "google/protobuf/any.upb.h"
85 #include "google/protobuf/duration.upb.h"
86 #include "google/protobuf/struct.upb.h"
87 #include "google/protobuf/wrappers.upb.h"
88 #include "google/rpc/status.upb.h"
89 #include "upb/text_encode.h"
94 // TODO(donnadionne): Check to see if timeout is enabled, this will be
95 // removed once timeout feature is fully integration-tested and enabled by
97 bool XdsTimeoutEnabled() {
98 char* value = gpr_getenv("GRPC_XDS_EXPERIMENTAL_ENABLE_TIMEOUT");
100 bool parse_succeeded = gpr_parse_bool_value(value, &parsed_value);
102 return parse_succeeded && parsed_value;
105 // TODO(donnadionne): Check to see if cluster types aggregate_cluster and
106 // logical_dns are enabled, this will be
107 // removed once the cluster types are fully integration-tested and enabled by
109 bool XdsAggregateAndLogicalDnsClusterEnabled() {
110 char* value = gpr_getenv(
111 "GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER");
113 bool parse_succeeded = gpr_parse_bool_value(value, &parsed_value);
115 return parse_succeeded && parsed_value;
118 // TODO(donnadionne): Check to see if ring hash policy is enabled, this will be
119 // removed once ring hash policy is fully integration-tested and enabled by
121 bool XdsRingHashEnabled() {
122 char* value = gpr_getenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH");
124 bool parse_succeeded = gpr_parse_bool_value(value, &parsed_value);
126 return parse_succeeded && parsed_value;
129 // TODO(yashykt): Check to see if xDS security is enabled. This will be
130 // removed once this feature is fully integration-tested and enabled by
132 bool XdsSecurityEnabled() {
133 char* value = gpr_getenv("GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT");
135 bool parse_succeeded = gpr_parse_bool_value(value, &parsed_value);
137 return parse_succeeded && parsed_value;
144 std::string XdsApi::Route::Matchers::ToString() const {
145 std::vector<std::string> contents;
147 absl::StrFormat("PathMatcher{%s}", path_matcher.ToString()));
148 for (const HeaderMatcher& header_matcher : header_matchers) {
149 contents.push_back(header_matcher.ToString());
151 if (fraction_per_million.has_value()) {
152 contents.push_back(absl::StrFormat("Fraction Per Million %d",
153 fraction_per_million.value()));
155 return absl::StrJoin(contents, "\n");
158 std::string XdsApi::Route::ClusterWeight::ToString() const {
159 return absl::StrFormat("{cluster=%s, weight=%d}", name, weight);
162 std::string XdsApi::Route::ToString() const {
163 std::vector<std::string> contents;
164 contents.push_back(matchers.ToString());
165 if (!cluster_name.empty()) {
166 contents.push_back(absl::StrFormat("Cluster name: %s", cluster_name));
168 for (const ClusterWeight& cluster_weight : weighted_clusters) {
169 contents.push_back(cluster_weight.ToString());
171 if (max_stream_duration.has_value()) {
172 contents.push_back(max_stream_duration->ToString());
174 return absl::StrJoin(contents, "\n");
181 std::string XdsApi::RdsUpdate::ToString() const {
182 std::vector<std::string> vhosts;
183 for (const VirtualHost& vhost : virtual_hosts) {
185 absl::StrCat("vhost={\n"
187 absl::StrJoin(vhost.domains, ", "),
190 for (const XdsApi::Route& route : vhost.routes) {
191 vhosts.push_back(" {\n");
192 vhosts.push_back(route.ToString());
193 vhosts.push_back("\n }\n");
195 vhosts.push_back(" ]\n");
196 vhosts.push_back("]\n");
198 return absl::StrJoin(vhosts, "");
203 // Better match type has smaller value.
212 // Returns true if match succeeds.
213 bool DomainMatch(MatchType match_type, const std::string& domain_pattern_in,
214 const std::string& expected_host_name_in) {
215 // Normalize the args to lower-case. Domain matching is case-insensitive.
216 std::string domain_pattern = domain_pattern_in;
217 std::string expected_host_name = expected_host_name_in;
218 std::transform(domain_pattern.begin(), domain_pattern.end(),
219 domain_pattern.begin(),
220 [](unsigned char c) { return std::tolower(c); });
221 std::transform(expected_host_name.begin(), expected_host_name.end(),
222 expected_host_name.begin(),
223 [](unsigned char c) { return std::tolower(c); });
224 if (match_type == EXACT_MATCH) {
225 return domain_pattern == expected_host_name;
226 } else if (match_type == SUFFIX_MATCH) {
227 // Asterisk must match at least one char.
228 if (expected_host_name.size() < domain_pattern.size()) return false;
229 absl::string_view pattern_suffix(domain_pattern.c_str() + 1);
230 absl::string_view host_suffix(expected_host_name.c_str() +
231 expected_host_name.size() -
232 pattern_suffix.size());
233 return pattern_suffix == host_suffix;
234 } else if (match_type == PREFIX_MATCH) {
235 // Asterisk must match at least one char.
236 if (expected_host_name.size() < domain_pattern.size()) return false;
237 absl::string_view pattern_prefix(domain_pattern.c_str(),
238 domain_pattern.size() - 1);
239 absl::string_view host_prefix(expected_host_name.c_str(),
240 pattern_prefix.size());
241 return pattern_prefix == host_prefix;
243 return match_type == UNIVERSE_MATCH;
247 MatchType DomainPatternMatchType(const std::string& domain_pattern) {
248 if (domain_pattern.empty()) return INVALID_MATCH;
249 if (domain_pattern.find('*') == std::string::npos) return EXACT_MATCH;
250 if (domain_pattern == "*") return UNIVERSE_MATCH;
251 if (domain_pattern[0] == '*') return SUFFIX_MATCH;
252 if (domain_pattern[domain_pattern.size() - 1] == '*') return PREFIX_MATCH;
253 return INVALID_MATCH;
258 XdsApi::RdsUpdate::VirtualHost* XdsApi::RdsUpdate::FindVirtualHostForDomain(
259 const std::string& domain) {
260 // Find the best matched virtual host.
261 // The search order for 4 groups of domain patterns:
263 // 2. Suffix match (e.g., "*ABC").
264 // 3. Prefix match (e.g., "ABC*").
265 // 4. Universe match (i.e., "*").
266 // Within each group, longest match wins.
267 // If the same best matched domain pattern appears in multiple virtual hosts,
268 // the first matched virtual host wins.
269 VirtualHost* target_vhost = nullptr;
270 MatchType best_match_type = INVALID_MATCH;
271 size_t longest_match = 0;
272 // Check each domain pattern in each virtual host to determine the best
273 // matched virtual host.
274 for (VirtualHost& vhost : virtual_hosts) {
275 for (const std::string& domain_pattern : vhost.domains) {
276 // Check the match type first. Skip the pattern if it's not better than
278 const MatchType match_type = DomainPatternMatchType(domain_pattern);
279 // This should be caught by RouteConfigParse().
280 GPR_ASSERT(match_type != INVALID_MATCH);
281 if (match_type > best_match_type) continue;
282 if (match_type == best_match_type &&
283 domain_pattern.size() <= longest_match) {
286 // Skip if match fails.
287 if (!DomainMatch(match_type, domain_pattern, domain)) continue;
288 // Choose this match.
289 target_vhost = &vhost;
290 best_match_type = match_type;
291 longest_match = domain_pattern.size();
292 if (best_match_type == EXACT_MATCH) break;
294 if (best_match_type == EXACT_MATCH) break;
300 // XdsApi::CommonTlsContext::CertificateValidationContext
303 std::string XdsApi::CommonTlsContext::CertificateValidationContext::ToString()
305 std::vector<std::string> contents;
306 for (const auto& match : match_subject_alt_names) {
307 contents.push_back(match.ToString());
309 return absl::StrFormat("{match_subject_alt_names=[%s]}",
310 absl::StrJoin(contents, ", "));
313 bool XdsApi::CommonTlsContext::CertificateValidationContext::Empty() const {
314 return match_subject_alt_names.empty();
318 // XdsApi::CommonTlsContext::CertificateValidationContext
321 std::string XdsApi::CommonTlsContext::CertificateProviderInstance::ToString()
323 absl::InlinedVector<std::string, 2> contents;
324 if (!instance_name.empty()) {
325 contents.push_back(absl::StrFormat("instance_name=%s", instance_name));
327 if (!certificate_name.empty()) {
329 absl::StrFormat("certificate_name=%s", certificate_name));
331 return absl::StrCat("{", absl::StrJoin(contents, ", "), "}");
334 bool XdsApi::CommonTlsContext::CertificateProviderInstance::Empty() const {
335 return instance_name.empty() && certificate_name.empty();
339 // XdsApi::CommonTlsContext::CombinedCertificateValidationContext
343 XdsApi::CommonTlsContext::CombinedCertificateValidationContext::ToString()
345 absl::InlinedVector<std::string, 2> contents;
346 if (!default_validation_context.Empty()) {
347 contents.push_back(absl::StrFormat("default_validation_context=%s",
348 default_validation_context.ToString()));
350 if (!validation_context_certificate_provider_instance.Empty()) {
351 contents.push_back(absl::StrFormat(
352 "validation_context_certificate_provider_instance=%s",
353 validation_context_certificate_provider_instance.ToString()));
355 return absl::StrCat("{", absl::StrJoin(contents, ", "), "}");
358 bool XdsApi::CommonTlsContext::CombinedCertificateValidationContext::Empty()
360 return default_validation_context.Empty() &&
361 validation_context_certificate_provider_instance.Empty();
365 // XdsApi::CommonTlsContext
368 std::string XdsApi::CommonTlsContext::ToString() const {
369 absl::InlinedVector<std::string, 2> contents;
370 if (!tls_certificate_certificate_provider_instance.Empty()) {
371 contents.push_back(absl::StrFormat(
372 "tls_certificate_certificate_provider_instance=%s",
373 tls_certificate_certificate_provider_instance.ToString()));
375 if (!combined_validation_context.Empty()) {
376 contents.push_back(absl::StrFormat("combined_validation_context=%s",
377 combined_validation_context.ToString()));
379 return absl::StrCat("{", absl::StrJoin(contents, ", "), "}");
382 bool XdsApi::CommonTlsContext::Empty() const {
383 return tls_certificate_certificate_provider_instance.Empty() &&
384 combined_validation_context.Empty();
388 // XdsApi::DownstreamTlsContext
391 std::string XdsApi::DownstreamTlsContext::ToString() const {
392 return absl::StrFormat("common_tls_context=%s, require_client_certificate=%s",
393 common_tls_context.ToString(),
394 require_client_certificate ? "true" : "false");
397 bool XdsApi::DownstreamTlsContext::Empty() const {
398 return common_tls_context.Empty();
405 std::string XdsApi::LdsUpdate::ToString() const {
406 absl::InlinedVector<std::string, 3> contents;
407 if (type == ListenerType::kTcpListener) {
408 if (!downstream_tls_context.Empty()) {
409 contents.push_back(absl::StrFormat("downstream_tls_context=%s",
410 downstream_tls_context.ToString()));
412 } else if (type == ListenerType::kHttpApiListener) {
413 contents.push_back(absl::StrFormat(
414 "route_config_name=%s",
415 !route_config_name.empty() ? route_config_name.c_str() : "<inlined>"));
416 contents.push_back(absl::StrFormat("http_max_stream_duration=%s",
417 http_max_stream_duration.ToString()));
418 if (rds_update.has_value()) {
420 absl::StrFormat("rds_update=%s", rds_update->ToString()));
423 return absl::StrCat("{", absl::StrJoin(contents, ", "), "}");
430 std::string XdsApi::CdsUpdate::ToString() const {
431 absl::InlinedVector<std::string, 4> contents;
432 if (!eds_service_name.empty()) {
434 absl::StrFormat("eds_service_name=%s", eds_service_name));
436 if (!common_tls_context.Empty()) {
437 contents.push_back(absl::StrFormat("common_tls_context=%s",
438 common_tls_context.ToString()));
440 if (lrs_load_reporting_server_name.has_value()) {
441 contents.push_back(absl::StrFormat("lrs_load_reporting_server_name=%s",
442 lrs_load_reporting_server_name.value()));
445 absl::StrFormat("max_concurrent_requests=%d", max_concurrent_requests));
446 return absl::StrCat("{", absl::StrJoin(contents, ", "), "}");
453 std::string XdsApi::EdsUpdate::Priority::Locality::ToString() const {
454 std::vector<std::string> endpoint_strings;
455 for (const ServerAddress& endpoint : endpoints) {
456 endpoint_strings.emplace_back(endpoint.ToString());
458 return absl::StrCat("{name=", name->AsHumanReadableString(),
459 ", lb_weight=", lb_weight, ", endpoints=[",
460 absl::StrJoin(endpoint_strings, ", "), "]}");
463 bool XdsApi::EdsUpdate::Priority::operator==(const Priority& other) const {
464 if (localities.size() != other.localities.size()) return false;
465 auto it1 = localities.begin();
466 auto it2 = other.localities.begin();
467 while (it1 != localities.end()) {
468 if (*it1->first != *it2->first) return false;
469 if (it1->second != it2->second) return false;
476 std::string XdsApi::EdsUpdate::Priority::ToString() const {
477 std::vector<std::string> locality_strings;
478 for (const auto& p : localities) {
479 locality_strings.emplace_back(p.second.ToString());
481 return absl::StrCat("[", absl::StrJoin(locality_strings, ", "), "]");
484 bool XdsApi::EdsUpdate::DropConfig::ShouldDrop(
485 const std::string** category_name) const {
486 for (size_t i = 0; i < drop_category_list_.size(); ++i) {
487 const auto& drop_category = drop_category_list_[i];
488 // Generate a random number in [0, 1000000).
489 const uint32_t random = static_cast<uint32_t>(rand()) % 1000000;
490 if (random < drop_category.parts_per_million) {
491 *category_name = &drop_category.name;
498 std::string XdsApi::EdsUpdate::DropConfig::ToString() const {
499 std::vector<std::string> category_strings;
500 for (const DropCategory& category : drop_category_list_) {
501 category_strings.emplace_back(
502 absl::StrCat(category.name, "=", category.parts_per_million));
504 return absl::StrCat("{[", absl::StrJoin(category_strings, ", "),
505 "], drop_all=", drop_all_, "}");
508 std::string XdsApi::EdsUpdate::ToString() const {
509 std::vector<std::string> priority_strings;
510 for (size_t i = 0; i < priorities.size(); ++i) {
511 const Priority& priority = priorities[i];
512 priority_strings.emplace_back(
513 absl::StrCat("priority ", i, ": ", priority.ToString()));
515 return absl::StrCat("priorities=[", absl::StrJoin(priority_strings, ", "),
516 "], drop_config=", drop_config->ToString());
523 const char* XdsApi::kLdsTypeUrl =
524 "type.googleapis.com/envoy.config.listener.v3.Listener";
525 const char* XdsApi::kRdsTypeUrl =
526 "type.googleapis.com/envoy.config.route.v3.RouteConfiguration";
527 const char* XdsApi::kCdsTypeUrl =
528 "type.googleapis.com/envoy.config.cluster.v3.Cluster";
529 const char* XdsApi::kEdsTypeUrl =
530 "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment";
534 const char* kLdsV2TypeUrl = "type.googleapis.com/envoy.api.v2.Listener";
535 const char* kRdsV2TypeUrl =
536 "type.googleapis.com/envoy.api.v2.RouteConfiguration";
537 const char* kCdsV2TypeUrl = "type.googleapis.com/envoy.api.v2.Cluster";
538 const char* kEdsV2TypeUrl =
539 "type.googleapis.com/envoy.api.v2.ClusterLoadAssignment";
541 bool IsLds(absl::string_view type_url) {
542 return type_url == XdsApi::kLdsTypeUrl || type_url == kLdsV2TypeUrl;
545 bool IsRds(absl::string_view type_url) {
546 return type_url == XdsApi::kRdsTypeUrl || type_url == kRdsV2TypeUrl;
549 bool IsCds(absl::string_view type_url) {
550 return type_url == XdsApi::kCdsTypeUrl || type_url == kCdsV2TypeUrl;
553 bool IsEds(absl::string_view type_url) {
554 return type_url == XdsApi::kEdsTypeUrl || type_url == kEdsV2TypeUrl;
559 XdsApi::XdsApi(XdsClient* client, TraceFlag* tracer,
560 const XdsBootstrap::Node* node)
564 build_version_(absl::StrCat("gRPC C-core ", GPR_PLATFORM_STRING, " ",
565 grpc_version_string())),
566 user_agent_name_(absl::StrCat("gRPC C-core ", GPR_PLATFORM_STRING)) {}
570 // Works for both std::string and absl::string_view.
571 template <typename T>
572 inline upb_strview StdStringToUpbString(const T& str) {
573 return upb_strview_make(str.data(), str.size());
576 void PopulateMetadataValue(upb_arena* arena, google_protobuf_Value* value_pb,
579 void PopulateListValue(upb_arena* arena, google_protobuf_ListValue* list_value,
580 const Json::Array& values) {
581 for (const auto& value : values) {
582 auto* value_pb = google_protobuf_ListValue_add_values(list_value, arena);
583 PopulateMetadataValue(arena, value_pb, value);
587 void PopulateMetadata(upb_arena* arena, google_protobuf_Struct* metadata_pb,
588 const Json::Object& metadata) {
589 for (const auto& p : metadata) {
590 google_protobuf_Value* value = google_protobuf_Value_new(arena);
591 PopulateMetadataValue(arena, value, p.second);
592 google_protobuf_Struct_fields_set(
593 metadata_pb, StdStringToUpbString(p.first), value, arena);
597 void PopulateMetadataValue(upb_arena* arena, google_protobuf_Value* value_pb,
599 switch (value.type()) {
600 case Json::Type::JSON_NULL:
601 google_protobuf_Value_set_null_value(value_pb, 0);
603 case Json::Type::NUMBER:
604 google_protobuf_Value_set_number_value(
605 value_pb, strtod(value.string_value().c_str(), nullptr));
607 case Json::Type::STRING:
608 google_protobuf_Value_set_string_value(
609 value_pb, StdStringToUpbString(value.string_value()));
611 case Json::Type::JSON_TRUE:
612 google_protobuf_Value_set_bool_value(value_pb, true);
614 case Json::Type::JSON_FALSE:
615 google_protobuf_Value_set_bool_value(value_pb, false);
617 case Json::Type::OBJECT: {
618 google_protobuf_Struct* struct_value =
619 google_protobuf_Value_mutable_struct_value(value_pb, arena);
620 PopulateMetadata(arena, struct_value, value.object_value());
623 case Json::Type::ARRAY: {
624 google_protobuf_ListValue* list_value =
625 google_protobuf_Value_mutable_list_value(value_pb, arena);
626 PopulateListValue(arena, list_value, value.array_value());
632 // Helper functions to manually do protobuf string encoding, so that we
633 // can populate the node build_version field that was removed in v3.
634 std::string EncodeVarint(uint64_t val) {
637 uint8_t byte = val & 0x7fU;
639 if (val) byte |= 0x80U;
644 std::string EncodeTag(uint32_t field_number, uint8_t wire_type) {
645 return EncodeVarint((field_number << 3) | wire_type);
647 std::string EncodeStringField(uint32_t field_number, const std::string& str) {
648 static const uint8_t kDelimitedWireType = 2;
649 return EncodeTag(field_number, kDelimitedWireType) +
650 EncodeVarint(str.size()) + str;
653 void PopulateBuildVersion(upb_arena* arena, envoy_config_core_v3_Node* node_msg,
654 const std::string& build_version) {
655 std::string encoded_build_version = EncodeStringField(5, build_version);
656 // TODO(roth): This should use upb_msg_addunknown(), but that API is
657 // broken in the current version of upb, so we're using the internal
658 // API for now. Change this once we upgrade to a version of upb that
660 _upb_msg_addunknown(node_msg, encoded_build_version.data(),
661 encoded_build_version.size(), arena);
664 void PopulateNode(upb_arena* arena, const XdsBootstrap::Node* node, bool use_v3,
665 const std::string& build_version,
666 const std::string& user_agent_name,
667 envoy_config_core_v3_Node* node_msg) {
668 if (node != nullptr) {
669 if (!node->id.empty()) {
670 envoy_config_core_v3_Node_set_id(node_msg,
671 StdStringToUpbString(node->id));
673 if (!node->cluster.empty()) {
674 envoy_config_core_v3_Node_set_cluster(
675 node_msg, StdStringToUpbString(node->cluster));
677 if (!node->metadata.object_value().empty()) {
678 google_protobuf_Struct* metadata =
679 envoy_config_core_v3_Node_mutable_metadata(node_msg, arena);
680 PopulateMetadata(arena, metadata, node->metadata.object_value());
682 if (!node->locality_region.empty() || !node->locality_zone.empty() ||
683 !node->locality_subzone.empty()) {
684 envoy_config_core_v3_Locality* locality =
685 envoy_config_core_v3_Node_mutable_locality(node_msg, arena);
686 if (!node->locality_region.empty()) {
687 envoy_config_core_v3_Locality_set_region(
688 locality, StdStringToUpbString(node->locality_region));
690 if (!node->locality_zone.empty()) {
691 envoy_config_core_v3_Locality_set_zone(
692 locality, StdStringToUpbString(node->locality_zone));
694 if (!node->locality_subzone.empty()) {
695 envoy_config_core_v3_Locality_set_sub_zone(
696 locality, StdStringToUpbString(node->locality_subzone));
701 PopulateBuildVersion(arena, node_msg, build_version);
703 envoy_config_core_v3_Node_set_user_agent_name(
704 node_msg, StdStringToUpbString(user_agent_name));
705 envoy_config_core_v3_Node_set_user_agent_version(
706 node_msg, upb_strview_makez(grpc_version_string()));
707 envoy_config_core_v3_Node_add_client_features(
708 node_msg, upb_strview_makez("envoy.lb.does_not_support_overprovisioning"),
712 inline absl::string_view UpbStringToAbsl(const upb_strview& str) {
713 return absl::string_view(str.data, str.size);
716 inline std::string UpbStringToStdString(const upb_strview& str) {
717 return std::string(str.data, str.size);
720 void MaybeLogDiscoveryRequest(
721 XdsClient* client, TraceFlag* tracer, upb_symtab* symtab,
722 const envoy_service_discovery_v3_DiscoveryRequest* request) {
723 if (GRPC_TRACE_FLAG_ENABLED(*tracer) &&
724 gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
725 const upb_msgdef* msg_type =
726 envoy_service_discovery_v3_DiscoveryRequest_getmsgdef(symtab);
728 upb_text_encode(request, msg_type, nullptr, 0, buf, sizeof(buf));
729 gpr_log(GPR_DEBUG, "[xds_client %p] constructed ADS request: %s", client,
734 grpc_slice SerializeDiscoveryRequest(
735 upb_arena* arena, envoy_service_discovery_v3_DiscoveryRequest* request) {
736 size_t output_length;
737 char* output = envoy_service_discovery_v3_DiscoveryRequest_serialize(
738 request, arena, &output_length);
739 return grpc_slice_from_copied_buffer(output, output_length);
742 absl::string_view TypeUrlExternalToInternal(bool use_v3,
743 const std::string& type_url) {
745 if (type_url == XdsApi::kLdsTypeUrl) {
746 return kLdsV2TypeUrl;
748 if (type_url == XdsApi::kRdsTypeUrl) {
749 return kRdsV2TypeUrl;
751 if (type_url == XdsApi::kCdsTypeUrl) {
752 return kCdsV2TypeUrl;
754 if (type_url == XdsApi::kEdsTypeUrl) {
755 return kEdsV2TypeUrl;
763 grpc_slice XdsApi::CreateAdsRequest(
764 const XdsBootstrap::XdsServer& server, const std::string& type_url,
765 const std::set<absl::string_view>& resource_names,
766 const std::string& version, const std::string& nonce, grpc_error* error,
767 bool populate_node) {
770 envoy_service_discovery_v3_DiscoveryRequest* request =
771 envoy_service_discovery_v3_DiscoveryRequest_new(arena.ptr());
773 absl::string_view real_type_url =
774 TypeUrlExternalToInternal(server.ShouldUseV3(), type_url);
775 envoy_service_discovery_v3_DiscoveryRequest_set_type_url(
776 request, StdStringToUpbString(real_type_url));
778 if (!version.empty()) {
779 envoy_service_discovery_v3_DiscoveryRequest_set_version_info(
780 request, StdStringToUpbString(version));
783 if (!nonce.empty()) {
784 envoy_service_discovery_v3_DiscoveryRequest_set_response_nonce(
785 request, StdStringToUpbString(nonce));
787 // Set error_detail if it's a NACK.
788 if (error != GRPC_ERROR_NONE) {
789 google_rpc_Status* error_detail =
790 envoy_service_discovery_v3_DiscoveryRequest_mutable_error_detail(
791 request, arena.ptr());
792 // Hard-code INVALID_ARGUMENT as the status code.
793 // TODO(roth): If at some point we decide we care about this value,
794 // we could attach a status code to the individual errors where we
795 // generate them in the parsing code, and then use that here.
796 google_rpc_Status_set_code(error_detail, GRPC_STATUS_INVALID_ARGUMENT);
797 // Error description comes from the error that was passed in.
798 upb_strview error_description =
799 StdStringToUpbString(absl::string_view(grpc_error_string(error)));
800 google_rpc_Status_set_message(error_detail, error_description);
801 GRPC_ERROR_UNREF(error);
805 envoy_config_core_v3_Node* node_msg =
806 envoy_service_discovery_v3_DiscoveryRequest_mutable_node(request,
808 PopulateNode(arena.ptr(), node_, server.ShouldUseV3(), build_version_,
809 user_agent_name_, node_msg);
811 // Add resource_names.
812 for (const auto& resource_name : resource_names) {
813 envoy_service_discovery_v3_DiscoveryRequest_add_resource_names(
814 request, StdStringToUpbString(resource_name), arena.ptr());
816 MaybeLogDiscoveryRequest(client_, tracer_, symtab_.ptr(), request);
817 return SerializeDiscoveryRequest(arena.ptr(), request);
822 void MaybeLogDiscoveryResponse(
823 XdsClient* client, TraceFlag* tracer, upb_symtab* symtab,
824 const envoy_service_discovery_v3_DiscoveryResponse* response) {
825 if (GRPC_TRACE_FLAG_ENABLED(*tracer) &&
826 gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
827 const upb_msgdef* msg_type =
828 envoy_service_discovery_v3_DiscoveryResponse_getmsgdef(symtab);
830 upb_text_encode(response, msg_type, nullptr, 0, buf, sizeof(buf));
831 gpr_log(GPR_DEBUG, "[xds_client %p] received response: %s", client, buf);
835 void MaybeLogRouteConfiguration(
836 XdsClient* client, TraceFlag* tracer, upb_symtab* symtab,
837 const envoy_config_route_v3_RouteConfiguration* route_config) {
838 if (GRPC_TRACE_FLAG_ENABLED(*tracer) &&
839 gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
840 const upb_msgdef* msg_type =
841 envoy_config_route_v3_RouteConfiguration_getmsgdef(symtab);
843 upb_text_encode(route_config, msg_type, nullptr, 0, buf, sizeof(buf));
844 gpr_log(GPR_DEBUG, "[xds_client %p] RouteConfiguration: %s", client, buf);
848 void MaybeLogCluster(XdsClient* client, TraceFlag* tracer, upb_symtab* symtab,
849 const envoy_config_cluster_v3_Cluster* cluster) {
850 if (GRPC_TRACE_FLAG_ENABLED(*tracer) &&
851 gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
852 const upb_msgdef* msg_type =
853 envoy_config_cluster_v3_Cluster_getmsgdef(symtab);
855 upb_text_encode(cluster, msg_type, nullptr, 0, buf, sizeof(buf));
856 gpr_log(GPR_DEBUG, "[xds_client %p] Cluster: %s", client, buf);
860 void MaybeLogClusterLoadAssignment(
861 XdsClient* client, TraceFlag* tracer, upb_symtab* symtab,
862 const envoy_config_endpoint_v3_ClusterLoadAssignment* cla) {
863 if (GRPC_TRACE_FLAG_ENABLED(*tracer) &&
864 gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
865 const upb_msgdef* msg_type =
866 envoy_config_endpoint_v3_ClusterLoadAssignment_getmsgdef(symtab);
868 upb_text_encode(cla, msg_type, nullptr, 0, buf, sizeof(buf));
869 gpr_log(GPR_DEBUG, "[xds_client %p] ClusterLoadAssignment: %s", client,
874 grpc_error* RoutePathMatchParse(const envoy_config_route_v3_RouteMatch* match,
875 XdsApi::Route* route, bool* ignore_route) {
876 auto* case_sensitive_ptr =
877 envoy_config_route_v3_RouteMatch_case_sensitive(match);
878 bool case_sensitive = true;
879 if (case_sensitive_ptr != nullptr) {
880 case_sensitive = google_protobuf_BoolValue_value(case_sensitive_ptr);
882 StringMatcher::Type type;
883 std::string match_string;
884 if (envoy_config_route_v3_RouteMatch_has_prefix(match)) {
885 absl::string_view prefix =
886 UpbStringToAbsl(envoy_config_route_v3_RouteMatch_prefix(match));
887 // Empty prefix "" is accepted.
888 if (!prefix.empty()) {
889 // Prefix "/" is accepted.
890 if (prefix[0] != '/') {
891 // Prefix which does not start with a / will never match anything, so
892 // ignore this route.
893 *ignore_route = true;
894 return GRPC_ERROR_NONE;
896 std::vector<absl::string_view> prefix_elements =
897 absl::StrSplit(prefix.substr(1), absl::MaxSplits('/', 2));
898 if (prefix_elements.size() > 2) {
899 // Prefix cannot have more than 2 slashes.
900 *ignore_route = true;
901 return GRPC_ERROR_NONE;
902 } else if (prefix_elements.size() == 2 && prefix_elements[0].empty()) {
903 // Prefix contains empty string between the 2 slashes
904 *ignore_route = true;
905 return GRPC_ERROR_NONE;
908 type = StringMatcher::Type::PREFIX;
909 match_string = std::string(prefix);
910 } else if (envoy_config_route_v3_RouteMatch_has_path(match)) {
911 absl::string_view path =
912 UpbStringToAbsl(envoy_config_route_v3_RouteMatch_path(match));
914 // Path that is empty will never match anything, so ignore this route.
915 *ignore_route = true;
916 return GRPC_ERROR_NONE;
918 if (path[0] != '/') {
919 // Path which does not start with a / will never match anything, so
920 // ignore this route.
921 *ignore_route = true;
922 return GRPC_ERROR_NONE;
924 std::vector<absl::string_view> path_elements =
925 absl::StrSplit(path.substr(1), absl::MaxSplits('/', 2));
926 if (path_elements.size() != 2) {
927 // Path not in the required format of /service/method will never match
928 // anything, so ignore this route.
929 *ignore_route = true;
930 return GRPC_ERROR_NONE;
931 } else if (path_elements[0].empty()) {
932 // Path contains empty service name will never match anything, so ignore
934 *ignore_route = true;
935 return GRPC_ERROR_NONE;
936 } else if (path_elements[1].empty()) {
937 // Path contains empty method name will never match anything, so ignore
939 *ignore_route = true;
940 return GRPC_ERROR_NONE;
942 type = StringMatcher::Type::EXACT;
943 match_string = std::string(path);
944 } else if (envoy_config_route_v3_RouteMatch_has_safe_regex(match)) {
945 const envoy_type_matcher_v3_RegexMatcher* regex_matcher =
946 envoy_config_route_v3_RouteMatch_safe_regex(match);
947 GPR_ASSERT(regex_matcher != nullptr);
948 type = StringMatcher::Type::SAFE_REGEX;
949 match_string = UpbStringToStdString(
950 envoy_type_matcher_v3_RegexMatcher_regex(regex_matcher));
952 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
953 "Invalid route path specifier specified.");
955 absl::StatusOr<StringMatcher> string_matcher =
956 StringMatcher::Create(type, match_string, case_sensitive);
957 if (!string_matcher.ok()) {
958 return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
959 absl::StrCat("path matcher: ", string_matcher.status().message())
963 route->matchers.path_matcher = std::move(string_matcher.value());
964 return GRPC_ERROR_NONE;
967 grpc_error* RouteHeaderMatchersParse(
968 const envoy_config_route_v3_RouteMatch* match, XdsApi::Route* route) {
970 const envoy_config_route_v3_HeaderMatcher* const* headers =
971 envoy_config_route_v3_RouteMatch_headers(match, &size);
972 for (size_t i = 0; i < size; ++i) {
973 const envoy_config_route_v3_HeaderMatcher* header = headers[i];
974 const std::string name =
975 UpbStringToStdString(envoy_config_route_v3_HeaderMatcher_name(header));
976 HeaderMatcher::Type type;
977 std::string match_string;
978 int64_t range_start = 0;
979 int64_t range_end = 0;
980 bool present_match = false;
981 if (envoy_config_route_v3_HeaderMatcher_has_exact_match(header)) {
982 type = HeaderMatcher::Type::EXACT;
983 match_string = UpbStringToStdString(
984 envoy_config_route_v3_HeaderMatcher_exact_match(header));
985 } else if (envoy_config_route_v3_HeaderMatcher_has_safe_regex_match(
987 const envoy_type_matcher_v3_RegexMatcher* regex_matcher =
988 envoy_config_route_v3_HeaderMatcher_safe_regex_match(header);
989 GPR_ASSERT(regex_matcher != nullptr);
990 type = HeaderMatcher::Type::SAFE_REGEX;
991 match_string = UpbStringToStdString(
992 envoy_type_matcher_v3_RegexMatcher_regex(regex_matcher));
993 } else if (envoy_config_route_v3_HeaderMatcher_has_range_match(header)) {
994 type = HeaderMatcher::Type::RANGE;
995 const envoy_type_v3_Int64Range* range_matcher =
996 envoy_config_route_v3_HeaderMatcher_range_match(header);
997 range_start = envoy_type_v3_Int64Range_start(range_matcher);
998 range_end = envoy_type_v3_Int64Range_end(range_matcher);
999 } else if (envoy_config_route_v3_HeaderMatcher_has_present_match(header)) {
1000 type = HeaderMatcher::Type::PRESENT;
1001 present_match = envoy_config_route_v3_HeaderMatcher_present_match(header);
1002 } else if (envoy_config_route_v3_HeaderMatcher_has_prefix_match(header)) {
1003 type = HeaderMatcher::Type::PREFIX;
1004 match_string = UpbStringToStdString(
1005 envoy_config_route_v3_HeaderMatcher_prefix_match(header));
1006 } else if (envoy_config_route_v3_HeaderMatcher_has_suffix_match(header)) {
1007 type = HeaderMatcher::Type::SUFFIX;
1008 match_string = UpbStringToStdString(
1009 envoy_config_route_v3_HeaderMatcher_suffix_match(header));
1010 } else if (envoy_config_route_v3_HeaderMatcher_has_contains_match(header)) {
1011 type = HeaderMatcher::Type::CONTAINS;
1012 match_string = UpbStringToStdString(
1013 envoy_config_route_v3_HeaderMatcher_contains_match(header));
1015 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1016 "Invalid route header matcher specified.");
1019 envoy_config_route_v3_HeaderMatcher_invert_match(header);
1020 absl::StatusOr<HeaderMatcher> header_matcher =
1021 HeaderMatcher::Create(name, type, match_string, range_start, range_end,
1022 present_match, invert_match);
1023 if (!header_matcher.ok()) {
1024 return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1025 absl::StrCat("header matcher: ", header_matcher.status().message())
1028 route->matchers.header_matchers.emplace_back(
1029 std::move(header_matcher.value()));
1031 return GRPC_ERROR_NONE;
1034 grpc_error* RouteRuntimeFractionParse(
1035 const envoy_config_route_v3_RouteMatch* match, XdsApi::Route* route) {
1036 const envoy_config_core_v3_RuntimeFractionalPercent* runtime_fraction =
1037 envoy_config_route_v3_RouteMatch_runtime_fraction(match);
1038 if (runtime_fraction != nullptr) {
1039 const envoy_type_v3_FractionalPercent* fraction =
1040 envoy_config_core_v3_RuntimeFractionalPercent_default_value(
1042 if (fraction != nullptr) {
1043 uint32_t numerator = envoy_type_v3_FractionalPercent_numerator(fraction);
1044 const auto denominator =
1045 static_cast<envoy_type_v3_FractionalPercent_DenominatorType>(
1046 envoy_type_v3_FractionalPercent_denominator(fraction));
1047 // Normalize to million.
1048 switch (denominator) {
1049 case envoy_type_v3_FractionalPercent_HUNDRED:
1052 case envoy_type_v3_FractionalPercent_TEN_THOUSAND:
1055 case envoy_type_v3_FractionalPercent_MILLION:
1058 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1059 "Unknown denominator type");
1061 route->matchers.fraction_per_million = numerator;
1064 return GRPC_ERROR_NONE;
1067 grpc_error* RouteActionParse(const envoy_config_route_v3_Route* route_msg,
1068 XdsApi::Route* route, bool* ignore_route) {
1069 if (!envoy_config_route_v3_Route_has_route(route_msg)) {
1070 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1071 "No RouteAction found in route.");
1073 const envoy_config_route_v3_RouteAction* route_action =
1074 envoy_config_route_v3_Route_route(route_msg);
1075 // Get the cluster or weighted_clusters in the RouteAction.
1076 if (envoy_config_route_v3_RouteAction_has_cluster(route_action)) {
1077 route->cluster_name = UpbStringToStdString(
1078 envoy_config_route_v3_RouteAction_cluster(route_action));
1079 if (route->cluster_name.empty()) {
1080 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1081 "RouteAction cluster contains empty cluster name.");
1083 } else if (envoy_config_route_v3_RouteAction_has_weighted_clusters(
1085 const envoy_config_route_v3_WeightedCluster* weighted_cluster =
1086 envoy_config_route_v3_RouteAction_weighted_clusters(route_action);
1087 uint32_t total_weight = 100;
1088 const google_protobuf_UInt32Value* weight =
1089 envoy_config_route_v3_WeightedCluster_total_weight(weighted_cluster);
1090 if (weight != nullptr) {
1091 total_weight = google_protobuf_UInt32Value_value(weight);
1093 size_t clusters_size;
1094 const envoy_config_route_v3_WeightedCluster_ClusterWeight* const* clusters =
1095 envoy_config_route_v3_WeightedCluster_clusters(weighted_cluster,
1097 uint32_t sum_of_weights = 0;
1098 for (size_t j = 0; j < clusters_size; ++j) {
1099 const envoy_config_route_v3_WeightedCluster_ClusterWeight*
1100 cluster_weight = clusters[j];
1101 XdsApi::Route::ClusterWeight cluster;
1102 cluster.name = UpbStringToStdString(
1103 envoy_config_route_v3_WeightedCluster_ClusterWeight_name(
1105 if (cluster.name.empty()) {
1106 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1107 "RouteAction weighted_cluster cluster contains empty cluster "
1110 const google_protobuf_UInt32Value* weight =
1111 envoy_config_route_v3_WeightedCluster_ClusterWeight_weight(
1113 if (weight == nullptr) {
1114 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1115 "RouteAction weighted_cluster cluster missing weight");
1117 cluster.weight = google_protobuf_UInt32Value_value(weight);
1118 if (cluster.weight == 0) continue;
1119 sum_of_weights += cluster.weight;
1120 route->weighted_clusters.emplace_back(std::move(cluster));
1122 if (total_weight != sum_of_weights) {
1123 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1124 "RouteAction weighted_cluster has incorrect total weight");
1126 if (route->weighted_clusters.empty()) {
1127 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1128 "RouteAction weighted_cluster has no valid clusters specified.");
1131 // No cluster or weighted_clusters found in RouteAction, ignore this route.
1132 *ignore_route = true;
1134 if (XdsTimeoutEnabled() && !*ignore_route) {
1135 const envoy_config_route_v3_RouteAction_MaxStreamDuration*
1136 max_stream_duration =
1137 envoy_config_route_v3_RouteAction_max_stream_duration(route_action);
1138 if (max_stream_duration != nullptr) {
1139 const google_protobuf_Duration* duration =
1140 envoy_config_route_v3_RouteAction_MaxStreamDuration_grpc_timeout_header_max(
1141 max_stream_duration);
1142 if (duration == nullptr) {
1144 envoy_config_route_v3_RouteAction_MaxStreamDuration_max_stream_duration(
1145 max_stream_duration);
1147 if (duration != nullptr) {
1148 XdsApi::Duration duration_in_route;
1149 duration_in_route.seconds = google_protobuf_Duration_seconds(duration);
1150 duration_in_route.nanos = google_protobuf_Duration_nanos(duration);
1151 route->max_stream_duration = duration_in_route;
1155 return GRPC_ERROR_NONE;
1158 grpc_error* RouteConfigParse(
1159 XdsClient* client, TraceFlag* tracer, upb_symtab* symtab,
1160 const envoy_config_route_v3_RouteConfiguration* route_config,
1161 XdsApi::RdsUpdate* rds_update) {
1162 MaybeLogRouteConfiguration(client, tracer, symtab, route_config);
1163 // Get the virtual hosts.
1165 const envoy_config_route_v3_VirtualHost* const* virtual_hosts =
1166 envoy_config_route_v3_RouteConfiguration_virtual_hosts(route_config,
1168 for (size_t i = 0; i < size; ++i) {
1169 rds_update->virtual_hosts.emplace_back();
1170 XdsApi::RdsUpdate::VirtualHost& vhost = rds_update->virtual_hosts.back();
1173 upb_strview const* domains = envoy_config_route_v3_VirtualHost_domains(
1174 virtual_hosts[i], &domain_size);
1175 for (size_t j = 0; j < domain_size; ++j) {
1176 std::string domain_pattern = UpbStringToStdString(domains[j]);
1177 const MatchType match_type = DomainPatternMatchType(domain_pattern);
1178 if (match_type == INVALID_MATCH) {
1179 return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1180 absl::StrCat("Invalid domain pattern \"", domain_pattern, "\".")
1183 vhost.domains.emplace_back(std::move(domain_pattern));
1185 if (vhost.domains.empty()) {
1186 return GRPC_ERROR_CREATE_FROM_STATIC_STRING("VirtualHost has no domains");
1190 const envoy_config_route_v3_Route* const* routes =
1191 envoy_config_route_v3_VirtualHost_routes(virtual_hosts[i], &num_routes);
1192 if (num_routes < 1) {
1193 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1194 "No route found in the virtual host.");
1196 // Loop over the whole list of routes
1197 for (size_t j = 0; j < num_routes; ++j) {
1198 const envoy_config_route_v3_RouteMatch* match =
1199 envoy_config_route_v3_Route_match(routes[j]);
1200 size_t query_parameters_size;
1201 static_cast<void>(envoy_config_route_v3_RouteMatch_query_parameters(
1202 match, &query_parameters_size));
1203 if (query_parameters_size > 0) {
1206 XdsApi::Route route;
1207 bool ignore_route = false;
1208 grpc_error* error = RoutePathMatchParse(match, &route, &ignore_route);
1209 if (error != GRPC_ERROR_NONE) return error;
1210 if (ignore_route) continue;
1211 error = RouteHeaderMatchersParse(match, &route);
1212 if (error != GRPC_ERROR_NONE) return error;
1213 error = RouteRuntimeFractionParse(match, &route);
1214 if (error != GRPC_ERROR_NONE) return error;
1215 error = RouteActionParse(routes[j], &route, &ignore_route);
1216 if (error != GRPC_ERROR_NONE) return error;
1217 if (ignore_route) continue;
1218 vhost.routes.emplace_back(std::move(route));
1220 if (vhost.routes.empty()) {
1221 return GRPC_ERROR_CREATE_FROM_STATIC_STRING("No valid routes specified.");
1224 return GRPC_ERROR_NONE;
1227 XdsApi::CommonTlsContext::CertificateProviderInstance
1228 CertificateProviderInstanceParse(
1229 const envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CertificateProviderInstance*
1230 certificate_provider_instance_proto) {
1232 UpbStringToStdString(
1233 envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CertificateProviderInstance_instance_name(
1234 certificate_provider_instance_proto)),
1235 UpbStringToStdString(
1236 envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CertificateProviderInstance_certificate_name(
1237 certificate_provider_instance_proto))};
1240 grpc_error* CommonTlsContextParse(
1241 const envoy_extensions_transport_sockets_tls_v3_CommonTlsContext*
1242 common_tls_context_proto,
1243 XdsApi::CommonTlsContext* common_tls_context) GRPC_MUST_USE_RESULT;
1244 grpc_error* CommonTlsContextParse(
1245 const envoy_extensions_transport_sockets_tls_v3_CommonTlsContext*
1246 common_tls_context_proto,
1247 XdsApi::CommonTlsContext* common_tls_context) {
1248 auto* combined_validation_context =
1249 envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_combined_validation_context(
1250 common_tls_context_proto);
1251 if (combined_validation_context != nullptr) {
1252 auto* default_validation_context =
1253 envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CombinedCertificateValidationContext_default_validation_context(
1254 combined_validation_context);
1255 if (default_validation_context != nullptr) {
1257 auto* subject_alt_names_matchers =
1258 envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext_match_subject_alt_names(
1259 default_validation_context, &len);
1260 for (size_t i = 0; i < len; ++i) {
1261 StringMatcher::Type type;
1262 std::string matcher;
1263 if (envoy_type_matcher_v3_StringMatcher_has_exact(
1264 subject_alt_names_matchers[i])) {
1265 type = StringMatcher::Type::EXACT;
1267 UpbStringToStdString(envoy_type_matcher_v3_StringMatcher_exact(
1268 subject_alt_names_matchers[i]));
1269 } else if (envoy_type_matcher_v3_StringMatcher_has_prefix(
1270 subject_alt_names_matchers[i])) {
1271 type = StringMatcher::Type::PREFIX;
1273 UpbStringToStdString(envoy_type_matcher_v3_StringMatcher_prefix(
1274 subject_alt_names_matchers[i]));
1275 } else if (envoy_type_matcher_v3_StringMatcher_has_suffix(
1276 subject_alt_names_matchers[i])) {
1277 type = StringMatcher::Type::SUFFIX;
1279 UpbStringToStdString(envoy_type_matcher_v3_StringMatcher_suffix(
1280 subject_alt_names_matchers[i]));
1281 } else if (envoy_type_matcher_v3_StringMatcher_has_contains(
1282 subject_alt_names_matchers[i])) {
1283 type = StringMatcher::Type::CONTAINS;
1285 UpbStringToStdString(envoy_type_matcher_v3_StringMatcher_contains(
1286 subject_alt_names_matchers[i]));
1287 } else if (envoy_type_matcher_v3_StringMatcher_has_safe_regex(
1288 subject_alt_names_matchers[i])) {
1289 type = StringMatcher::Type::SAFE_REGEX;
1290 auto* regex_matcher = envoy_type_matcher_v3_StringMatcher_safe_regex(
1291 subject_alt_names_matchers[i]);
1292 matcher = UpbStringToStdString(
1293 envoy_type_matcher_v3_RegexMatcher_regex(regex_matcher));
1295 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1296 "Invalid StringMatcher specified");
1298 bool ignore_case = envoy_type_matcher_v3_StringMatcher_ignore_case(
1299 subject_alt_names_matchers[i]);
1300 absl::StatusOr<StringMatcher> string_matcher =
1301 StringMatcher::Create(type, matcher,
1302 /*case_sensitive=*/!ignore_case);
1303 if (!string_matcher.ok()) {
1304 return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1305 absl::StrCat("string matcher: ",
1306 string_matcher.status().message())
1309 if (type == StringMatcher::Type::SAFE_REGEX && ignore_case) {
1310 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1311 "StringMatcher: ignore_case has no effect for SAFE_REGEX.");
1313 common_tls_context->combined_validation_context
1314 .default_validation_context.match_subject_alt_names.push_back(
1315 std::move(string_matcher.value()));
1318 auto* validation_context_certificate_provider_instance =
1319 envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CombinedCertificateValidationContext_validation_context_certificate_provider_instance(
1320 combined_validation_context);
1321 if (validation_context_certificate_provider_instance != nullptr) {
1322 common_tls_context->combined_validation_context
1323 .validation_context_certificate_provider_instance =
1324 CertificateProviderInstanceParse(
1325 validation_context_certificate_provider_instance);
1328 auto* tls_certificate_certificate_provider_instance =
1329 envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_tls_certificate_certificate_provider_instance(
1330 common_tls_context_proto);
1331 if (tls_certificate_certificate_provider_instance != nullptr) {
1332 common_tls_context->tls_certificate_certificate_provider_instance =
1333 CertificateProviderInstanceParse(
1334 tls_certificate_certificate_provider_instance);
1336 return GRPC_ERROR_NONE;
1339 grpc_error* LdsResponseParseClient(
1340 XdsClient* client, TraceFlag* tracer, upb_symtab* symtab, upb_arena* arena,
1341 const envoy_config_listener_v3_ApiListener* api_listener,
1342 XdsApi::LdsUpdate* lds_update) {
1343 lds_update->type = XdsApi::LdsUpdate::ListenerType::kHttpApiListener;
1344 const upb_strview encoded_api_listener = google_protobuf_Any_value(
1345 envoy_config_listener_v3_ApiListener_api_listener(api_listener));
1346 const envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager*
1347 http_connection_manager =
1348 envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_parse(
1349 encoded_api_listener.data, encoded_api_listener.size, arena);
1350 if (http_connection_manager == nullptr) {
1351 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1352 "Could not parse HttpConnectionManager config from ApiListener");
1354 if (XdsTimeoutEnabled()) {
1355 // Obtain max_stream_duration from Http Protocol Options.
1356 const envoy_config_core_v3_HttpProtocolOptions* options =
1357 envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_common_http_protocol_options(
1358 http_connection_manager);
1359 if (options != nullptr) {
1360 const google_protobuf_Duration* duration =
1361 envoy_config_core_v3_HttpProtocolOptions_max_stream_duration(options);
1362 if (duration != nullptr) {
1363 lds_update->http_max_stream_duration.seconds =
1364 google_protobuf_Duration_seconds(duration);
1365 lds_update->http_max_stream_duration.nanos =
1366 google_protobuf_Duration_nanos(duration);
1370 // Found inlined route_config. Parse it to find the cluster_name.
1371 if (envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_has_route_config(
1372 http_connection_manager)) {
1373 const envoy_config_route_v3_RouteConfiguration* route_config =
1374 envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_route_config(
1375 http_connection_manager);
1376 XdsApi::RdsUpdate rds_update;
1378 RouteConfigParse(client, tracer, symtab, route_config, &rds_update);
1379 if (error != GRPC_ERROR_NONE) return error;
1380 lds_update->rds_update = std::move(rds_update);
1381 return GRPC_ERROR_NONE;
1383 // Validate that RDS must be used to get the route_config dynamically.
1384 if (!envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_has_rds(
1385 http_connection_manager)) {
1386 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1387 "HttpConnectionManager neither has inlined route_config nor RDS.");
1389 const envoy_extensions_filters_network_http_connection_manager_v3_Rds* rds =
1390 envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_rds(
1391 http_connection_manager);
1392 // Check that the ConfigSource specifies ADS.
1393 const envoy_config_core_v3_ConfigSource* config_source =
1394 envoy_extensions_filters_network_http_connection_manager_v3_Rds_config_source(
1396 if (config_source == nullptr) {
1397 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1398 "HttpConnectionManager missing config_source for RDS.");
1400 if (!envoy_config_core_v3_ConfigSource_has_ads(config_source)) {
1401 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1402 "HttpConnectionManager ConfigSource for RDS does not specify ADS.");
1404 // Get the route_config_name.
1405 lds_update->route_config_name = UpbStringToStdString(
1406 envoy_extensions_filters_network_http_connection_manager_v3_Rds_route_config_name(
1408 return GRPC_ERROR_NONE;
1411 grpc_error* LdsResponseParseServer(
1412 upb_arena* arena, const envoy_config_listener_v3_Listener* listener,
1413 const std::string& /*listener_name*/,
1414 const envoy_config_core_v3_Address* /*address*/,
1415 XdsApi::LdsUpdate* lds_update) {
1416 lds_update->type = XdsApi::LdsUpdate::ListenerType::kTcpListener;
1417 // TODO(yashykt): Support filter chain match.
1418 // Right now, we are supporting and expecting only one entry in filter_chains.
1420 auto* filter_chains =
1421 envoy_config_listener_v3_Listener_filter_chains(listener, &size);
1423 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1424 "Only one filter_chain supported.");
1426 // Get the DownstreamTlsContext from the match
1427 if (XdsSecurityEnabled()) {
1428 auto* transport_socket =
1429 envoy_config_listener_v3_FilterChain_transport_socket(filter_chains[0]);
1430 if (transport_socket != nullptr) {
1431 absl::string_view name = UpbStringToAbsl(
1432 envoy_config_core_v3_TransportSocket_name(transport_socket));
1433 if (name == "envoy.transport_sockets.tls") {
1434 auto* typed_config =
1435 envoy_config_core_v3_TransportSocket_typed_config(transport_socket);
1436 if (typed_config != nullptr) {
1437 const upb_strview encoded_downstream_tls_context =
1438 google_protobuf_Any_value(typed_config);
1439 auto* downstream_tls_context =
1440 envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_parse(
1441 encoded_downstream_tls_context.data,
1442 encoded_downstream_tls_context.size, arena);
1443 if (downstream_tls_context == nullptr) {
1444 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1445 "Can't decode downstream tls context.");
1447 auto* common_tls_context =
1448 envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_common_tls_context(
1449 downstream_tls_context);
1450 if (common_tls_context != nullptr) {
1451 grpc_error* error = CommonTlsContextParse(
1453 &lds_update->downstream_tls_context.common_tls_context);
1454 if (error != GRPC_ERROR_NONE) return error;
1456 auto* require_client_certificate =
1457 envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_require_client_certificate(
1458 downstream_tls_context);
1459 if (require_client_certificate != nullptr) {
1460 lds_update->downstream_tls_context.require_client_certificate =
1461 google_protobuf_BoolValue_value(require_client_certificate);
1464 if (lds_update->downstream_tls_context.common_tls_context
1465 .tls_certificate_certificate_provider_instance.instance_name
1467 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1468 "TLS configuration provided but no "
1469 "tls_certificate_certificate_provider_instance found.");
1474 return GRPC_ERROR_NONE;
1477 grpc_error* LdsResponseParse(
1478 XdsClient* client, TraceFlag* tracer, upb_symtab* symtab,
1479 const envoy_service_discovery_v3_DiscoveryResponse* response,
1480 const std::set<absl::string_view>& expected_listener_names,
1481 XdsApi::LdsUpdateMap* lds_update_map,
1482 std::set<std::string>* resource_names_failed, upb_arena* arena) {
1483 std::vector<grpc_error*> errors;
1484 // Get the resources from the response.
1486 const google_protobuf_Any* const* resources =
1487 envoy_service_discovery_v3_DiscoveryResponse_resources(response, &size);
1488 for (size_t i = 0; i < size; ++i) {
1489 // Check the type_url of the resource.
1490 absl::string_view type_url =
1491 UpbStringToAbsl(google_protobuf_Any_type_url(resources[i]));
1492 if (!IsLds(type_url)) {
1493 errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1494 absl::StrCat("resource index ", i, ": Resource is not LDS.")
1498 // Decode the listener.
1499 const upb_strview encoded_listener =
1500 google_protobuf_Any_value(resources[i]);
1501 const envoy_config_listener_v3_Listener* listener =
1502 envoy_config_listener_v3_Listener_parse(encoded_listener.data,
1503 encoded_listener.size, arena);
1504 if (listener == nullptr) {
1505 errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1506 absl::StrCat("resource index ", i, ": Can't decode listener.")
1510 // Check listener name. Ignore unexpected listeners.
1511 std::string listener_name =
1512 UpbStringToStdString(envoy_config_listener_v3_Listener_name(listener));
1513 if (expected_listener_names.find(listener_name) ==
1514 expected_listener_names.end()) {
1517 // Fail if listener name is duplicated.
1518 if (lds_update_map->find(listener_name) != lds_update_map->end()) {
1519 errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1520 absl::StrCat("duplicate listener name \"", listener_name, "\"")
1522 resource_names_failed->insert(listener_name);
1525 XdsApi::LdsUpdate& lds_update = (*lds_update_map)[listener_name];
1526 // Check whether it's a client or server listener.
1527 const envoy_config_listener_v3_ApiListener* api_listener =
1528 envoy_config_listener_v3_Listener_api_listener(listener);
1529 const envoy_config_core_v3_Address* address =
1530 envoy_config_listener_v3_Listener_address(listener);
1531 if (api_listener != nullptr && address != nullptr) {
1532 errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1533 absl::StrCat(listener_name,
1534 ": Listener has both address and ApiListener")
1536 resource_names_failed->insert(listener_name);
1539 if (api_listener == nullptr && address == nullptr) {
1540 errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1541 absl::StrCat(listener_name,
1542 ": Listener has neither address nor ApiListener")
1544 resource_names_failed->insert(listener_name);
1547 grpc_error* error = GRPC_ERROR_NONE;
1548 if (api_listener != nullptr) {
1549 error = LdsResponseParseClient(client, tracer, symtab, arena,
1550 api_listener, &lds_update);
1552 error = LdsResponseParseServer(arena, listener, listener_name, address,
1555 if (error != GRPC_ERROR_NONE) {
1556 errors.push_back(grpc_error_add_child(
1557 GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1558 absl::StrCat(listener_name, ": validation error").c_str()),
1560 resource_names_failed->insert(listener_name);
1563 return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing LDS response", &errors);
1566 grpc_error* RdsResponseParse(
1567 XdsClient* client, TraceFlag* tracer, upb_symtab* symtab,
1568 const envoy_service_discovery_v3_DiscoveryResponse* response,
1569 const std::set<absl::string_view>& expected_route_configuration_names,
1570 XdsApi::RdsUpdateMap* rds_update_map,
1571 std::set<std::string>* resource_names_failed, upb_arena* arena) {
1572 std::vector<grpc_error*> errors;
1573 // Get the resources from the response.
1575 const google_protobuf_Any* const* resources =
1576 envoy_service_discovery_v3_DiscoveryResponse_resources(response, &size);
1577 for (size_t i = 0; i < size; ++i) {
1578 // Check the type_url of the resource.
1579 absl::string_view type_url =
1580 UpbStringToAbsl(google_protobuf_Any_type_url(resources[i]));
1581 if (!IsRds(type_url)) {
1582 errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1583 absl::StrCat("resource index ", i, ": Resource is not RDS.")
1587 // Decode the route_config.
1588 const upb_strview encoded_route_config =
1589 google_protobuf_Any_value(resources[i]);
1590 const envoy_config_route_v3_RouteConfiguration* route_config =
1591 envoy_config_route_v3_RouteConfiguration_parse(
1592 encoded_route_config.data, encoded_route_config.size, arena);
1593 if (route_config == nullptr) {
1594 errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1595 absl::StrCat("resource index ", i, ": Can't decode route_config.")
1599 // Check route_config_name. Ignore unexpected route_config.
1600 std::string route_config_name = UpbStringToStdString(
1601 envoy_config_route_v3_RouteConfiguration_name(route_config));
1602 if (expected_route_configuration_names.find(route_config_name) ==
1603 expected_route_configuration_names.end()) {
1606 // Fail if route config name is duplicated.
1607 if (rds_update_map->find(route_config_name) != rds_update_map->end()) {
1608 errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1609 absl::StrCat("duplicate route config name \"", route_config_name,
1612 resource_names_failed->insert(route_config_name);
1615 // Parse the route_config.
1616 XdsApi::RdsUpdate& rds_update = (*rds_update_map)[route_config_name];
1618 RouteConfigParse(client, tracer, symtab, route_config, &rds_update);
1619 if (error != GRPC_ERROR_NONE) {
1620 errors.push_back(grpc_error_add_child(
1621 GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1622 absl::StrCat(route_config_name, ": validation error").c_str()),
1624 resource_names_failed->insert(route_config_name);
1627 return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing RDS response", &errors);
1630 grpc_error* CdsResponseParse(
1631 XdsClient* client, TraceFlag* tracer, upb_symtab* symtab,
1632 const envoy_service_discovery_v3_DiscoveryResponse* response,
1633 const std::set<absl::string_view>& expected_cluster_names,
1634 XdsApi::CdsUpdateMap* cds_update_map,
1635 std::set<std::string>* resource_names_failed, upb_arena* arena) {
1636 std::vector<grpc_error*> errors;
1637 // Get the resources from the response.
1639 const google_protobuf_Any* const* resources =
1640 envoy_service_discovery_v3_DiscoveryResponse_resources(response, &size);
1641 // Parse all the resources in the CDS response.
1642 for (size_t i = 0; i < size; ++i) {
1643 // Check the type_url of the resource.
1644 absl::string_view type_url =
1645 UpbStringToAbsl(google_protobuf_Any_type_url(resources[i]));
1646 if (!IsCds(type_url)) {
1647 errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1648 absl::StrCat("resource index ", i, ": Resource is not CDS.")
1652 // Decode the cluster.
1653 const upb_strview encoded_cluster = google_protobuf_Any_value(resources[i]);
1654 const envoy_config_cluster_v3_Cluster* cluster =
1655 envoy_config_cluster_v3_Cluster_parse(encoded_cluster.data,
1656 encoded_cluster.size, arena);
1657 if (cluster == nullptr) {
1658 errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1659 absl::StrCat("resource index ", i, ": Can't decode cluster.")
1663 MaybeLogCluster(client, tracer, symtab, cluster);
1664 // Ignore unexpected cluster names.
1665 std::string cluster_name =
1666 UpbStringToStdString(envoy_config_cluster_v3_Cluster_name(cluster));
1667 if (expected_cluster_names.find(cluster_name) ==
1668 expected_cluster_names.end()) {
1671 // Fail on duplicate resources.
1672 if (cds_update_map->find(cluster_name) != cds_update_map->end()) {
1673 errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1674 absl::StrCat("duplicate resource name \"", cluster_name, "\"")
1676 resource_names_failed->insert(cluster_name);
1679 XdsApi::CdsUpdate& cds_update = (*cds_update_map)[cluster_name];
1680 // Check the cluster_discovery_type.
1681 if (!envoy_config_cluster_v3_Cluster_has_type(cluster) &&
1682 !envoy_config_cluster_v3_Cluster_has_cluster_type(cluster)) {
1683 errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1684 absl::StrCat(cluster_name, ": DiscoveryType not found.").c_str()));
1685 resource_names_failed->insert(cluster_name);
1688 if (envoy_config_cluster_v3_Cluster_type(cluster) ==
1689 envoy_config_cluster_v3_Cluster_EDS) {
1690 cds_update.cluster_type = XdsApi::CdsUpdate::ClusterType::EDS;
1691 // Check the EDS config source.
1692 const envoy_config_cluster_v3_Cluster_EdsClusterConfig*
1693 eds_cluster_config =
1694 envoy_config_cluster_v3_Cluster_eds_cluster_config(cluster);
1695 const envoy_config_core_v3_ConfigSource* eds_config =
1696 envoy_config_cluster_v3_Cluster_EdsClusterConfig_eds_config(
1697 eds_cluster_config);
1698 if (!envoy_config_core_v3_ConfigSource_has_ads(eds_config)) {
1699 errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1700 absl::StrCat(cluster_name, ": EDS ConfigSource is not ADS.")
1702 resource_names_failed->insert(cluster_name);
1705 // Record EDS service_name (if any).
1706 upb_strview service_name =
1707 envoy_config_cluster_v3_Cluster_EdsClusterConfig_service_name(
1708 eds_cluster_config);
1709 if (service_name.size != 0) {
1710 cds_update.eds_service_name = UpbStringToStdString(service_name);
1712 } else if (!XdsAggregateAndLogicalDnsClusterEnabled()) {
1713 errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1714 absl::StrCat(cluster_name, ": DiscoveryType is not valid.").c_str()));
1715 resource_names_failed->insert(cluster_name);
1717 } else if (envoy_config_cluster_v3_Cluster_type(cluster) ==
1718 envoy_config_cluster_v3_Cluster_LOGICAL_DNS) {
1719 cds_update.cluster_type = XdsApi::CdsUpdate::ClusterType::LOGICAL_DNS;
1721 if (envoy_config_cluster_v3_Cluster_has_cluster_type(cluster)) {
1722 const envoy_config_cluster_v3_Cluster_CustomClusterType*
1723 custom_cluster_type =
1724 envoy_config_cluster_v3_Cluster_cluster_type(cluster);
1725 upb_strview type_name =
1726 envoy_config_cluster_v3_Cluster_CustomClusterType_name(
1727 custom_cluster_type);
1728 if (UpbStringToAbsl(type_name) == "envoy.clusters.aggregate") {
1729 cds_update.cluster_type = XdsApi::CdsUpdate::ClusterType::AGGREGATE;
1730 // Retrieve aggregate clusters.
1731 const google_protobuf_Any* typed_config =
1732 envoy_config_cluster_v3_Cluster_CustomClusterType_typed_config(
1733 custom_cluster_type);
1734 const upb_strview aggregate_cluster_config_upb_strview =
1735 google_protobuf_Any_value(typed_config);
1736 const envoy_extensions_clusters_aggregate_v3_ClusterConfig*
1737 aggregate_cluster_config =
1738 envoy_extensions_clusters_aggregate_v3_ClusterConfig_parse(
1739 aggregate_cluster_config_upb_strview.data,
1740 aggregate_cluster_config_upb_strview.size, arena);
1741 if (aggregate_cluster_config == nullptr) {
1742 errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1743 absl::StrCat(cluster_name, ": Can't parse aggregate cluster.")
1745 resource_names_failed->insert(cluster_name);
1749 const upb_strview* clusters =
1750 envoy_extensions_clusters_aggregate_v3_ClusterConfig_clusters(
1751 aggregate_cluster_config, &size);
1752 for (size_t i = 0; i < size; ++i) {
1753 const upb_strview cluster = clusters[i];
1754 cds_update.prioritized_cluster_names.emplace_back(
1755 UpbStringToStdString(cluster));
1758 errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1759 absl::StrCat(cluster_name, ": DiscoveryType is not valid.")
1761 resource_names_failed->insert(cluster_name);
1765 errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1766 absl::StrCat(cluster_name, ": DiscoveryType is not valid.")
1768 resource_names_failed->insert(cluster_name);
1772 // Check the LB policy.
1773 if (envoy_config_cluster_v3_Cluster_lb_policy(cluster) ==
1774 envoy_config_cluster_v3_Cluster_ROUND_ROBIN) {
1775 cds_update.lb_policy = "ROUND_ROBIN";
1776 } else if (XdsRingHashEnabled() &&
1777 envoy_config_cluster_v3_Cluster_lb_policy(cluster) ==
1778 envoy_config_cluster_v3_Cluster_RING_HASH) {
1779 cds_update.lb_policy = "RING_HASH";
1780 // Record ring hash lb config
1781 auto* ring_hash_config =
1782 envoy_config_cluster_v3_Cluster_ring_hash_lb_config(cluster);
1783 if (ring_hash_config == nullptr) {
1784 errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1785 absl::StrCat(cluster_name,
1786 ": ring hash lb config required but not present.")
1788 resource_names_failed->insert(cluster_name);
1791 const google_protobuf_UInt64Value* max_ring_size =
1792 envoy_config_cluster_v3_Cluster_RingHashLbConfig_maximum_ring_size(
1794 if (max_ring_size != nullptr) {
1795 cds_update.max_ring_size =
1796 google_protobuf_UInt64Value_value(max_ring_size);
1797 if (cds_update.max_ring_size > 8388608 ||
1798 cds_update.max_ring_size == 0) {
1799 errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1802 ": max_ring_size is not in the range of 1 to 8388608.")
1804 resource_names_failed->insert(cluster_name);
1808 const google_protobuf_UInt64Value* min_ring_size =
1809 envoy_config_cluster_v3_Cluster_RingHashLbConfig_minimum_ring_size(
1811 if (min_ring_size != nullptr) {
1812 cds_update.min_ring_size =
1813 google_protobuf_UInt64Value_value(min_ring_size);
1814 if (cds_update.min_ring_size > 8388608 ||
1815 cds_update.min_ring_size == 0) {
1816 errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1819 ": min_ring_size is not in the range of 1 to 8388608.")
1821 resource_names_failed->insert(cluster_name);
1824 if (cds_update.min_ring_size > cds_update.max_ring_size) {
1825 errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1828 ": min_ring_size cannot be greater than max_ring_size.")
1830 resource_names_failed->insert(cluster_name);
1834 if (envoy_config_cluster_v3_Cluster_RingHashLbConfig_hash_function(
1835 ring_hash_config) ==
1836 envoy_config_cluster_v3_Cluster_RingHashLbConfig_XX_HASH) {
1837 cds_update.hash_function = XdsApi::CdsUpdate::HashFunction::XX_HASH;
1839 envoy_config_cluster_v3_Cluster_RingHashLbConfig_hash_function(
1840 ring_hash_config) ==
1841 envoy_config_cluster_v3_Cluster_RingHashLbConfig_MURMUR_HASH_2) {
1842 cds_update.hash_function =
1843 XdsApi::CdsUpdate::HashFunction::MURMUR_HASH_2;
1845 errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1846 absl::StrCat(cluster_name,
1847 ": ring hash lb config has invalid hash function.")
1849 resource_names_failed->insert(cluster_name);
1853 errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1854 absl::StrCat(cluster_name, ": LB policy is not supported.").c_str()));
1855 resource_names_failed->insert(cluster_name);
1858 if (XdsSecurityEnabled()) {
1859 // Record Upstream tls context
1860 auto* transport_socket =
1861 envoy_config_cluster_v3_Cluster_transport_socket(cluster);
1862 if (transport_socket != nullptr) {
1863 absl::string_view name = UpbStringToAbsl(
1864 envoy_config_core_v3_TransportSocket_name(transport_socket));
1865 if (name == "envoy.transport_sockets.tls") {
1866 auto* typed_config =
1867 envoy_config_core_v3_TransportSocket_typed_config(
1869 if (typed_config != nullptr) {
1870 const upb_strview encoded_upstream_tls_context =
1871 google_protobuf_Any_value(typed_config);
1872 auto* upstream_tls_context =
1873 envoy_extensions_transport_sockets_tls_v3_UpstreamTlsContext_parse(
1874 encoded_upstream_tls_context.data,
1875 encoded_upstream_tls_context.size, arena);
1876 if (upstream_tls_context == nullptr) {
1877 errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1878 absl::StrCat(cluster_name,
1879 ": Can't decode upstream tls context.")
1881 resource_names_failed->insert(cluster_name);
1884 auto* common_tls_context =
1885 envoy_extensions_transport_sockets_tls_v3_UpstreamTlsContext_common_tls_context(
1886 upstream_tls_context);
1887 if (common_tls_context != nullptr) {
1888 grpc_error* error = CommonTlsContextParse(
1889 common_tls_context, &cds_update.common_tls_context);
1890 if (error != GRPC_ERROR_NONE) {
1891 errors.push_back(grpc_error_add_child(
1892 GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1893 absl::StrCat(cluster_name, ": error in TLS context")
1896 resource_names_failed->insert(cluster_name);
1901 if (cds_update.common_tls_context.combined_validation_context
1902 .validation_context_certificate_provider_instance
1903 .instance_name.empty()) {
1904 errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1905 absl::StrCat(cluster_name,
1906 "TLS configuration provided but no "
1907 "validation_context_certificate_provider_instance "
1910 resource_names_failed->insert(cluster_name);
1916 // Record LRS server name (if any).
1917 const envoy_config_core_v3_ConfigSource* lrs_server =
1918 envoy_config_cluster_v3_Cluster_lrs_server(cluster);
1919 if (lrs_server != nullptr) {
1920 if (!envoy_config_core_v3_ConfigSource_has_self(lrs_server)) {
1921 errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1922 absl::StrCat(cluster_name, ": LRS ConfigSource is not self.")
1924 resource_names_failed->insert(cluster_name);
1927 cds_update.lrs_load_reporting_server_name.emplace("");
1929 // The Cluster resource encodes the circuit breaking parameters in a list of
1930 // Thresholds messages, where each message specifies the parameters for a
1931 // particular RoutingPriority. we will look only at the first entry in the
1932 // list for priority DEFAULT and default to 1024 if not found.
1933 if (envoy_config_cluster_v3_Cluster_has_circuit_breakers(cluster)) {
1934 const envoy_config_cluster_v3_CircuitBreakers* circuit_breakers =
1935 envoy_config_cluster_v3_Cluster_circuit_breakers(cluster);
1936 size_t num_thresholds;
1937 const envoy_config_cluster_v3_CircuitBreakers_Thresholds* const*
1938 thresholds = envoy_config_cluster_v3_CircuitBreakers_thresholds(
1939 circuit_breakers, &num_thresholds);
1940 for (size_t i = 0; i < num_thresholds; ++i) {
1941 const auto* threshold = thresholds[i];
1942 if (envoy_config_cluster_v3_CircuitBreakers_Thresholds_priority(
1943 threshold) == envoy_config_core_v3_DEFAULT) {
1944 const google_protobuf_UInt32Value* max_requests =
1945 envoy_config_cluster_v3_CircuitBreakers_Thresholds_max_requests(
1947 if (max_requests != nullptr) {
1948 cds_update.max_concurrent_requests =
1949 google_protobuf_UInt32Value_value(max_requests);
1956 return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing CDS response", &errors);
1959 grpc_error* ServerAddressParseAndAppend(
1960 const envoy_config_endpoint_v3_LbEndpoint* lb_endpoint,
1961 ServerAddressList* list) {
1962 // If health_status is not HEALTHY or UNKNOWN, skip this endpoint.
1963 const int32_t health_status =
1964 envoy_config_endpoint_v3_LbEndpoint_health_status(lb_endpoint);
1965 if (health_status != envoy_config_core_v3_UNKNOWN &&
1966 health_status != envoy_config_core_v3_HEALTHY) {
1967 return GRPC_ERROR_NONE;
1969 // Find the ip:port.
1970 const envoy_config_endpoint_v3_Endpoint* endpoint =
1971 envoy_config_endpoint_v3_LbEndpoint_endpoint(lb_endpoint);
1972 const envoy_config_core_v3_Address* address =
1973 envoy_config_endpoint_v3_Endpoint_address(endpoint);
1974 const envoy_config_core_v3_SocketAddress* socket_address =
1975 envoy_config_core_v3_Address_socket_address(address);
1976 std::string address_str = UpbStringToStdString(
1977 envoy_config_core_v3_SocketAddress_address(socket_address));
1978 uint32_t port = envoy_config_core_v3_SocketAddress_port_value(socket_address);
1979 if (GPR_UNLIKELY(port >> 16) != 0) {
1980 return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Invalid port.");
1982 // Populate grpc_resolved_address.
1983 grpc_resolved_address addr;
1984 grpc_string_to_sockaddr(&addr, address_str.c_str(), port);
1985 // Append the address to the list.
1986 list->emplace_back(addr, nullptr);
1987 return GRPC_ERROR_NONE;
1990 grpc_error* LocalityParse(
1991 const envoy_config_endpoint_v3_LocalityLbEndpoints* locality_lb_endpoints,
1992 XdsApi::EdsUpdate::Priority::Locality* output_locality, size_t* priority) {
1994 const google_protobuf_UInt32Value* lb_weight =
1995 envoy_config_endpoint_v3_LocalityLbEndpoints_load_balancing_weight(
1996 locality_lb_endpoints);
1997 // If LB weight is not specified, it means this locality is assigned no load.
1998 // TODO(juanlishen): When we support CDS to configure the inter-locality
1999 // policy, we should change the LB weight handling.
2000 output_locality->lb_weight =
2001 lb_weight != nullptr ? google_protobuf_UInt32Value_value(lb_weight) : 0;
2002 if (output_locality->lb_weight == 0) return GRPC_ERROR_NONE;
2003 // Parse locality name.
2004 const envoy_config_core_v3_Locality* locality =
2005 envoy_config_endpoint_v3_LocalityLbEndpoints_locality(
2006 locality_lb_endpoints);
2007 std::string region =
2008 UpbStringToStdString(envoy_config_core_v3_Locality_region(locality));
2010 UpbStringToStdString(envoy_config_core_v3_Locality_region(locality));
2011 std::string sub_zone =
2012 UpbStringToStdString(envoy_config_core_v3_Locality_sub_zone(locality));
2013 output_locality->name = MakeRefCounted<XdsLocalityName>(
2014 std::move(region), std::move(zone), std::move(sub_zone));
2015 // Parse the addresses.
2017 const envoy_config_endpoint_v3_LbEndpoint* const* lb_endpoints =
2018 envoy_config_endpoint_v3_LocalityLbEndpoints_lb_endpoints(
2019 locality_lb_endpoints, &size);
2020 for (size_t i = 0; i < size; ++i) {
2021 grpc_error* error = ServerAddressParseAndAppend(
2022 lb_endpoints[i], &output_locality->endpoints);
2023 if (error != GRPC_ERROR_NONE) return error;
2025 // Parse the priority.
2026 *priority = envoy_config_endpoint_v3_LocalityLbEndpoints_priority(
2027 locality_lb_endpoints);
2028 return GRPC_ERROR_NONE;
2031 grpc_error* DropParseAndAppend(
2032 const envoy_config_endpoint_v3_ClusterLoadAssignment_Policy_DropOverload*
2034 XdsApi::EdsUpdate::DropConfig* drop_config) {
2035 // Get the category.
2036 std::string category = UpbStringToStdString(
2037 envoy_config_endpoint_v3_ClusterLoadAssignment_Policy_DropOverload_category(
2039 if (category.empty()) {
2040 return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Empty drop category name");
2042 // Get the drop rate (per million).
2043 const envoy_type_v3_FractionalPercent* drop_percentage =
2044 envoy_config_endpoint_v3_ClusterLoadAssignment_Policy_DropOverload_drop_percentage(
2046 uint32_t numerator =
2047 envoy_type_v3_FractionalPercent_numerator(drop_percentage);
2048 const auto denominator =
2049 static_cast<envoy_type_v3_FractionalPercent_DenominatorType>(
2050 envoy_type_v3_FractionalPercent_denominator(drop_percentage));
2051 // Normalize to million.
2052 switch (denominator) {
2053 case envoy_type_v3_FractionalPercent_HUNDRED:
2056 case envoy_type_v3_FractionalPercent_TEN_THOUSAND:
2059 case envoy_type_v3_FractionalPercent_MILLION:
2062 return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Unknown denominator type");
2064 // Cap numerator to 1000000.
2065 numerator = GPR_MIN(numerator, 1000000);
2066 drop_config->AddCategory(std::move(category), numerator);
2067 return GRPC_ERROR_NONE;
2070 grpc_error* EdsResponseParse(
2071 XdsClient* client, TraceFlag* tracer, upb_symtab* symtab,
2072 const envoy_service_discovery_v3_DiscoveryResponse* response,
2073 const std::set<absl::string_view>& expected_eds_service_names,
2074 XdsApi::EdsUpdateMap* eds_update_map,
2075 std::set<std::string>* resource_names_failed, upb_arena* arena) {
2076 std::vector<grpc_error*> errors;
2077 // Get the resources from the response.
2079 const google_protobuf_Any* const* resources =
2080 envoy_service_discovery_v3_DiscoveryResponse_resources(response, &size);
2081 for (size_t i = 0; i < size; ++i) {
2082 // Check the type_url of the resource.
2083 absl::string_view type_url =
2084 UpbStringToAbsl(google_protobuf_Any_type_url(resources[i]));
2085 if (!IsEds(type_url)) {
2086 errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2087 absl::StrCat("resource index ", i, ": Resource is not EDS.")
2091 // Get the cluster_load_assignment.
2092 upb_strview encoded_cluster_load_assignment =
2093 google_protobuf_Any_value(resources[i]);
2094 envoy_config_endpoint_v3_ClusterLoadAssignment* cluster_load_assignment =
2095 envoy_config_endpoint_v3_ClusterLoadAssignment_parse(
2096 encoded_cluster_load_assignment.data,
2097 encoded_cluster_load_assignment.size, arena);
2098 if (cluster_load_assignment == nullptr) {
2099 errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2100 absl::StrCat("resource index ", i,
2101 ": Can't parse cluster_load_assignment.")
2105 MaybeLogClusterLoadAssignment(client, tracer, symtab,
2106 cluster_load_assignment);
2107 // Check the EDS service name. Ignore unexpected names.
2108 std::string eds_service_name = UpbStringToStdString(
2109 envoy_config_endpoint_v3_ClusterLoadAssignment_cluster_name(
2110 cluster_load_assignment));
2111 if (expected_eds_service_names.find(eds_service_name) ==
2112 expected_eds_service_names.end()) {
2115 // Fail on duplicate resources.
2116 if (eds_update_map->find(eds_service_name) != eds_update_map->end()) {
2117 errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2118 absl::StrCat("duplicate resource name \"", eds_service_name, "\"")
2120 resource_names_failed->insert(eds_service_name);
2123 XdsApi::EdsUpdate& eds_update = (*eds_update_map)[eds_service_name];
2124 // Get the endpoints.
2125 size_t locality_size;
2126 const envoy_config_endpoint_v3_LocalityLbEndpoints* const* endpoints =
2127 envoy_config_endpoint_v3_ClusterLoadAssignment_endpoints(
2128 cluster_load_assignment, &locality_size);
2129 grpc_error* error = GRPC_ERROR_NONE;
2130 for (size_t j = 0; j < locality_size; ++j) {
2132 XdsApi::EdsUpdate::Priority::Locality locality;
2133 error = LocalityParse(endpoints[j], &locality, &priority);
2134 if (error != GRPC_ERROR_NONE) break;
2135 // Filter out locality with weight 0.
2136 if (locality.lb_weight == 0) continue;
2137 // Make sure prorities is big enough. Note that they might not
2138 // arrive in priority order.
2139 while (eds_update.priorities.size() < priority + 1) {
2140 eds_update.priorities.emplace_back();
2142 eds_update.priorities[priority].localities.emplace(locality.name.get(),
2143 std::move(locality));
2145 if (error != GRPC_ERROR_NONE) {
2146 errors.push_back(grpc_error_add_child(
2147 GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2148 absl::StrCat(eds_service_name, ": locality validation error")
2151 resource_names_failed->insert(eds_service_name);
2154 for (const auto& priority : eds_update.priorities) {
2155 if (priority.localities.empty()) {
2156 errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2157 absl::StrCat(eds_service_name, ": sparse priority list").c_str()));
2158 resource_names_failed->insert(eds_service_name);
2162 // Get the drop config.
2163 eds_update.drop_config = MakeRefCounted<XdsApi::EdsUpdate::DropConfig>();
2164 const envoy_config_endpoint_v3_ClusterLoadAssignment_Policy* policy =
2165 envoy_config_endpoint_v3_ClusterLoadAssignment_policy(
2166 cluster_load_assignment);
2167 if (policy != nullptr) {
2169 const envoy_config_endpoint_v3_ClusterLoadAssignment_Policy_DropOverload* const*
2171 envoy_config_endpoint_v3_ClusterLoadAssignment_Policy_drop_overloads(
2172 policy, &drop_size);
2173 for (size_t j = 0; j < drop_size; ++j) {
2175 DropParseAndAppend(drop_overload[j], eds_update.drop_config.get());
2176 if (error != GRPC_ERROR_NONE) break;
2178 if (error != GRPC_ERROR_NONE) {
2179 errors.push_back(grpc_error_add_child(
2180 GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2181 absl::StrCat(eds_service_name, ": drop config validation error")
2184 resource_names_failed->insert(eds_service_name);
2189 return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing EDS response", &errors);
2192 std::string TypeUrlInternalToExternal(absl::string_view type_url) {
2193 if (type_url == kLdsV2TypeUrl) {
2194 return XdsApi::kLdsTypeUrl;
2195 } else if (type_url == kRdsV2TypeUrl) {
2196 return XdsApi::kRdsTypeUrl;
2197 } else if (type_url == kCdsV2TypeUrl) {
2198 return XdsApi::kCdsTypeUrl;
2199 } else if (type_url == kEdsV2TypeUrl) {
2200 return XdsApi::kEdsTypeUrl;
2202 return std::string(type_url);
2205 template <typename UpdateMap>
2206 void MoveUpdatesToFailedSet(UpdateMap* update_map,
2207 std::set<std::string>* resource_names_failed) {
2208 for (const auto& p : *update_map) {
2209 resource_names_failed->insert(p.first);
2211 update_map->clear();
2216 XdsApi::AdsParseResult XdsApi::ParseAdsResponse(
2217 const grpc_slice& encoded_response,
2218 const std::set<absl::string_view>& expected_listener_names,
2219 const std::set<absl::string_view>& expected_route_configuration_names,
2220 const std::set<absl::string_view>& expected_cluster_names,
2221 const std::set<absl::string_view>& expected_eds_service_names) {
2222 AdsParseResult result;
2224 // Decode the response.
2225 const envoy_service_discovery_v3_DiscoveryResponse* response =
2226 envoy_service_discovery_v3_DiscoveryResponse_parse(
2227 reinterpret_cast<const char*>(GRPC_SLICE_START_PTR(encoded_response)),
2228 GRPC_SLICE_LENGTH(encoded_response), arena.ptr());
2229 // If decoding fails, output an empty type_url and return.
2230 if (response == nullptr) {
2231 result.parse_error =
2232 GRPC_ERROR_CREATE_FROM_STATIC_STRING("Can't decode DiscoveryResponse.");
2235 MaybeLogDiscoveryResponse(client_, tracer_, symtab_.ptr(), response);
2236 // Record the type_url, the version_info, and the nonce of the response.
2237 result.type_url = TypeUrlInternalToExternal(UpbStringToAbsl(
2238 envoy_service_discovery_v3_DiscoveryResponse_type_url(response)));
2239 result.version = UpbStringToStdString(
2240 envoy_service_discovery_v3_DiscoveryResponse_version_info(response));
2241 result.nonce = UpbStringToStdString(
2242 envoy_service_discovery_v3_DiscoveryResponse_nonce(response));
2243 // Parse the response according to the resource type.
2244 if (IsLds(result.type_url)) {
2245 result.parse_error = LdsResponseParse(
2246 client_, tracer_, symtab_.ptr(), response, expected_listener_names,
2247 &result.lds_update_map, &result.resource_names_failed, arena.ptr());
2248 if (result.parse_error != GRPC_ERROR_NONE) {
2249 MoveUpdatesToFailedSet(&result.lds_update_map,
2250 &result.resource_names_failed);
2252 } else if (IsRds(result.type_url)) {
2253 result.parse_error = RdsResponseParse(
2254 client_, tracer_, symtab_.ptr(), response,
2255 expected_route_configuration_names, &result.rds_update_map,
2256 &result.resource_names_failed, arena.ptr());
2257 if (result.parse_error != GRPC_ERROR_NONE) {
2258 MoveUpdatesToFailedSet(&result.rds_update_map,
2259 &result.resource_names_failed);
2261 } else if (IsCds(result.type_url)) {
2262 result.parse_error = CdsResponseParse(
2263 client_, tracer_, symtab_.ptr(), response, expected_cluster_names,
2264 &result.cds_update_map, &result.resource_names_failed, arena.ptr());
2265 if (result.parse_error != GRPC_ERROR_NONE) {
2266 MoveUpdatesToFailedSet(&result.cds_update_map,
2267 &result.resource_names_failed);
2269 } else if (IsEds(result.type_url)) {
2270 result.parse_error = EdsResponseParse(
2271 client_, tracer_, symtab_.ptr(), response, expected_eds_service_names,
2272 &result.eds_update_map, &result.resource_names_failed, arena.ptr());
2273 if (result.parse_error != GRPC_ERROR_NONE) {
2274 MoveUpdatesToFailedSet(&result.eds_update_map,
2275 &result.resource_names_failed);
2283 void MaybeLogLrsRequest(
2284 XdsClient* client, TraceFlag* tracer, upb_symtab* symtab,
2285 const envoy_service_load_stats_v3_LoadStatsRequest* request) {
2286 if (GRPC_TRACE_FLAG_ENABLED(*tracer) &&
2287 gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
2288 const upb_msgdef* msg_type =
2289 envoy_service_load_stats_v3_LoadStatsRequest_getmsgdef(symtab);
2291 upb_text_encode(request, msg_type, nullptr, 0, buf, sizeof(buf));
2292 gpr_log(GPR_DEBUG, "[xds_client %p] constructed LRS request: %s", client,
2297 grpc_slice SerializeLrsRequest(
2298 const envoy_service_load_stats_v3_LoadStatsRequest* request,
2300 size_t output_length;
2301 char* output = envoy_service_load_stats_v3_LoadStatsRequest_serialize(
2302 request, arena, &output_length);
2303 return grpc_slice_from_copied_buffer(output, output_length);
2308 grpc_slice XdsApi::CreateLrsInitialRequest(
2309 const XdsBootstrap::XdsServer& server) {
2311 // Create a request.
2312 envoy_service_load_stats_v3_LoadStatsRequest* request =
2313 envoy_service_load_stats_v3_LoadStatsRequest_new(arena.ptr());
2315 envoy_config_core_v3_Node* node_msg =
2316 envoy_service_load_stats_v3_LoadStatsRequest_mutable_node(request,
2318 PopulateNode(arena.ptr(), node_, server.ShouldUseV3(), build_version_,
2319 user_agent_name_, node_msg);
2320 envoy_config_core_v3_Node_add_client_features(
2321 node_msg, upb_strview_makez("envoy.lrs.supports_send_all_clusters"),
2323 MaybeLogLrsRequest(client_, tracer_, symtab_.ptr(), request);
2324 return SerializeLrsRequest(request, arena.ptr());
2329 void LocalityStatsPopulate(
2330 envoy_config_endpoint_v3_UpstreamLocalityStats* output,
2331 const XdsLocalityName& locality_name,
2332 const XdsClusterLocalityStats::Snapshot& snapshot, upb_arena* arena) {
2334 envoy_config_core_v3_Locality* locality =
2335 envoy_config_endpoint_v3_UpstreamLocalityStats_mutable_locality(output,
2337 if (!locality_name.region().empty()) {
2338 envoy_config_core_v3_Locality_set_region(
2339 locality, StdStringToUpbString(locality_name.region()));
2341 if (!locality_name.zone().empty()) {
2342 envoy_config_core_v3_Locality_set_zone(
2343 locality, StdStringToUpbString(locality_name.zone()));
2345 if (!locality_name.sub_zone().empty()) {
2346 envoy_config_core_v3_Locality_set_sub_zone(
2347 locality, StdStringToUpbString(locality_name.sub_zone()));
2349 // Set total counts.
2350 envoy_config_endpoint_v3_UpstreamLocalityStats_set_total_successful_requests(
2351 output, snapshot.total_successful_requests);
2352 envoy_config_endpoint_v3_UpstreamLocalityStats_set_total_requests_in_progress(
2353 output, snapshot.total_requests_in_progress);
2354 envoy_config_endpoint_v3_UpstreamLocalityStats_set_total_error_requests(
2355 output, snapshot.total_error_requests);
2356 envoy_config_endpoint_v3_UpstreamLocalityStats_set_total_issued_requests(
2357 output, snapshot.total_issued_requests);
2358 // Add backend metrics.
2359 for (const auto& p : snapshot.backend_metrics) {
2360 const std::string& metric_name = p.first;
2361 const XdsClusterLocalityStats::BackendMetric& metric_value = p.second;
2362 envoy_config_endpoint_v3_EndpointLoadMetricStats* load_metric =
2363 envoy_config_endpoint_v3_UpstreamLocalityStats_add_load_metric_stats(
2365 envoy_config_endpoint_v3_EndpointLoadMetricStats_set_metric_name(
2366 load_metric, StdStringToUpbString(metric_name));
2367 envoy_config_endpoint_v3_EndpointLoadMetricStats_set_num_requests_finished_with_metric(
2368 load_metric, metric_value.num_requests_finished_with_metric);
2369 envoy_config_endpoint_v3_EndpointLoadMetricStats_set_total_metric_value(
2370 load_metric, metric_value.total_metric_value);
2376 grpc_slice XdsApi::CreateLrsRequest(
2377 ClusterLoadReportMap cluster_load_report_map) {
2379 // Create a request.
2380 envoy_service_load_stats_v3_LoadStatsRequest* request =
2381 envoy_service_load_stats_v3_LoadStatsRequest_new(arena.ptr());
2382 for (auto& p : cluster_load_report_map) {
2383 const std::string& cluster_name = p.first.first;
2384 const std::string& eds_service_name = p.first.second;
2385 const ClusterLoadReport& load_report = p.second;
2386 // Add cluster stats.
2387 envoy_config_endpoint_v3_ClusterStats* cluster_stats =
2388 envoy_service_load_stats_v3_LoadStatsRequest_add_cluster_stats(
2389 request, arena.ptr());
2390 // Set the cluster name.
2391 envoy_config_endpoint_v3_ClusterStats_set_cluster_name(
2392 cluster_stats, StdStringToUpbString(cluster_name));
2393 // Set EDS service name, if non-empty.
2394 if (!eds_service_name.empty()) {
2395 envoy_config_endpoint_v3_ClusterStats_set_cluster_service_name(
2396 cluster_stats, StdStringToUpbString(eds_service_name));
2398 // Add locality stats.
2399 for (const auto& p : load_report.locality_stats) {
2400 const XdsLocalityName& locality_name = *p.first;
2401 const auto& snapshot = p.second;
2402 envoy_config_endpoint_v3_UpstreamLocalityStats* locality_stats =
2403 envoy_config_endpoint_v3_ClusterStats_add_upstream_locality_stats(
2404 cluster_stats, arena.ptr());
2405 LocalityStatsPopulate(locality_stats, locality_name, snapshot,
2408 // Add dropped requests.
2409 uint64_t total_dropped_requests = 0;
2410 for (const auto& p : load_report.dropped_requests.categorized_drops) {
2411 const std::string& category = p.first;
2412 const uint64_t count = p.second;
2413 envoy_config_endpoint_v3_ClusterStats_DroppedRequests* dropped_requests =
2414 envoy_config_endpoint_v3_ClusterStats_add_dropped_requests(
2415 cluster_stats, arena.ptr());
2416 envoy_config_endpoint_v3_ClusterStats_DroppedRequests_set_category(
2417 dropped_requests, StdStringToUpbString(category));
2418 envoy_config_endpoint_v3_ClusterStats_DroppedRequests_set_dropped_count(
2419 dropped_requests, count);
2420 total_dropped_requests += count;
2422 total_dropped_requests += load_report.dropped_requests.uncategorized_drops;
2423 // Set total dropped requests.
2424 envoy_config_endpoint_v3_ClusterStats_set_total_dropped_requests(
2425 cluster_stats, total_dropped_requests);
2426 // Set real load report interval.
2427 gpr_timespec timespec =
2428 grpc_millis_to_timespec(load_report.load_report_interval, GPR_TIMESPAN);
2429 google_protobuf_Duration* load_report_interval =
2430 envoy_config_endpoint_v3_ClusterStats_mutable_load_report_interval(
2431 cluster_stats, arena.ptr());
2432 google_protobuf_Duration_set_seconds(load_report_interval, timespec.tv_sec);
2433 google_protobuf_Duration_set_nanos(load_report_interval, timespec.tv_nsec);
2435 MaybeLogLrsRequest(client_, tracer_, symtab_.ptr(), request);
2436 return SerializeLrsRequest(request, arena.ptr());
2439 grpc_error* XdsApi::ParseLrsResponse(const grpc_slice& encoded_response,
2440 bool* send_all_clusters,
2441 std::set<std::string>* cluster_names,
2442 grpc_millis* load_reporting_interval) {
2444 // Decode the response.
2445 const envoy_service_load_stats_v3_LoadStatsResponse* decoded_response =
2446 envoy_service_load_stats_v3_LoadStatsResponse_parse(
2447 reinterpret_cast<const char*>(GRPC_SLICE_START_PTR(encoded_response)),
2448 GRPC_SLICE_LENGTH(encoded_response), arena.ptr());
2449 // Parse the response.
2450 if (decoded_response == nullptr) {
2451 return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Can't decode response.");
2453 // Check send_all_clusters.
2454 if (envoy_service_load_stats_v3_LoadStatsResponse_send_all_clusters(
2455 decoded_response)) {
2456 *send_all_clusters = true;
2458 // Store the cluster names.
2460 const upb_strview* clusters =
2461 envoy_service_load_stats_v3_LoadStatsResponse_clusters(decoded_response,
2463 for (size_t i = 0; i < size; ++i) {
2464 cluster_names->emplace(UpbStringToStdString(clusters[i]));
2467 // Get the load report interval.
2468 const google_protobuf_Duration* load_reporting_interval_duration =
2469 envoy_service_load_stats_v3_LoadStatsResponse_load_reporting_interval(
2471 gpr_timespec timespec{
2472 google_protobuf_Duration_seconds(load_reporting_interval_duration),
2473 google_protobuf_Duration_nanos(load_reporting_interval_duration),
2475 *load_reporting_interval = gpr_time_to_millis(timespec);
2476 return GRPC_ERROR_NONE;
2479 } // namespace grpc_core