1 /*
2 *
3 * Copyright 2018 gRPC authors.
4 *
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
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
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.
16 *
17 */
18
19 #include <grpc/support/port_platform.h>
20
21 #include <algorithm>
22 #include <cctype>
23 #include <cstdint>
24 #include <cstdlib>
25 #include <string>
26
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"
31
32 #include "upb/upb.hpp"
33
34 #include <grpc/impl/codegen/log.h>
35 #include <grpc/support/alloc.h>
36 #include <grpc/support/string_util.h>
37
38 #include "src/core/ext/xds/xds_api.h"
39 #include "src/core/lib/gpr/env.h"
40 #include "src/core/lib/gpr/string.h"
41 #include "src/core/lib/gpr/useful.h"
42 #include "src/core/lib/iomgr/error.h"
43 #include "src/core/lib/iomgr/sockaddr_utils.h"
44 #include "src/core/lib/slice/slice_utils.h"
45
46 #include "envoy/config/cluster/v3/circuit_breaker.upb.h"
47 #include "envoy/config/cluster/v3/cluster.upb.h"
48 #include "envoy/config/cluster/v3/cluster.upbdefs.h"
49 #include "envoy/config/core/v3/address.upb.h"
50 #include "envoy/config/core/v3/base.upb.h"
51 #include "envoy/config/core/v3/config_source.upb.h"
52 #include "envoy/config/core/v3/health_check.upb.h"
53 #include "envoy/config/core/v3/protocol.upb.h"
54 #include "envoy/config/endpoint/v3/endpoint.upb.h"
55 #include "envoy/config/endpoint/v3/endpoint.upbdefs.h"
56 #include "envoy/config/endpoint/v3/endpoint_components.upb.h"
57 #include "envoy/config/endpoint/v3/load_report.upb.h"
58 #include "envoy/config/listener/v3/api_listener.upb.h"
59 #include "envoy/config/listener/v3/listener.upb.h"
60 #include "envoy/config/route/v3/route.upb.h"
61 #include "envoy/config/route/v3/route.upbdefs.h"
62 #include "envoy/config/route/v3/route_components.upb.h"
63 #include "envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.upb.h"
64 #include "envoy/extensions/transport_sockets/tls/v3/common.upb.h"
65 #include "envoy/extensions/transport_sockets/tls/v3/tls.upb.h"
66 #include "envoy/service/cluster/v3/cds.upb.h"
67 #include "envoy/service/cluster/v3/cds.upbdefs.h"
68 #include "envoy/service/discovery/v3/discovery.upb.h"
69 #include "envoy/service/discovery/v3/discovery.upbdefs.h"
70 #include "envoy/service/endpoint/v3/eds.upb.h"
71 #include "envoy/service/endpoint/v3/eds.upbdefs.h"
72 #include "envoy/service/listener/v3/lds.upb.h"
73 #include "envoy/service/load_stats/v3/lrs.upb.h"
74 #include "envoy/service/load_stats/v3/lrs.upbdefs.h"
75 #include "envoy/service/route/v3/rds.upb.h"
76 #include "envoy/service/route/v3/rds.upbdefs.h"
77 #include "envoy/type/matcher/v3/regex.upb.h"
78 #include "envoy/type/matcher/v3/string.upb.h"
79 #include "envoy/type/v3/percent.upb.h"
80 #include "envoy/type/v3/range.upb.h"
81 #include "google/protobuf/any.upb.h"
82 #include "google/protobuf/duration.upb.h"
83 #include "google/protobuf/struct.upb.h"
84 #include "google/protobuf/wrappers.upb.h"
85 #include "google/rpc/status.upb.h"
86 #include "upb/text_encode.h"
87 #include "upb/upb.h"
88
89 namespace grpc_core {
90
91 // TODO (donnadionne): Check to see if timeout is enabled, this will be
92 // removed once timeout feature is fully integration-tested and enabled by
93 // default.
XdsTimeoutEnabled()94 bool XdsTimeoutEnabled() {
95 char* value = gpr_getenv("GRPC_XDS_EXPERIMENTAL_ENABLE_TIMEOUT");
96 bool parsed_value;
97 bool parse_succeeded = gpr_parse_bool_value(value, &parsed_value);
98 gpr_free(value);
99 return parse_succeeded && parsed_value;
100 }
101
102 // TODO(yashykt): Check to see if xDS security is enabled. This will be
103 // removed once this feature is fully integration-tested and enabled by
104 // default.
XdsSecurityEnabled()105 bool XdsSecurityEnabled() {
106 char* value = gpr_getenv("GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT");
107 bool parsed_value;
108 bool parse_succeeded = gpr_parse_bool_value(value, &parsed_value);
109 gpr_free(value);
110 return parse_succeeded && parsed_value;
111 }
112
113 //
114 // XdsApi::Route::Matchers::PathMatcher
115 //
116
PathMatcher(const PathMatcher & other)117 XdsApi::Route::Matchers::PathMatcher::PathMatcher(const PathMatcher& other)
118 : type(other.type), case_sensitive(other.case_sensitive) {
119 if (type == PathMatcherType::REGEX) {
120 RE2::Options options;
121 options.set_case_sensitive(case_sensitive);
122 regex_matcher =
123 absl::make_unique<RE2>(other.regex_matcher->pattern(), options);
124 } else {
125 string_matcher = other.string_matcher;
126 }
127 }
128
129 XdsApi::Route::Matchers::PathMatcher& XdsApi::Route::Matchers::PathMatcher::
operator =(const PathMatcher & other)130 operator=(const PathMatcher& other) {
131 type = other.type;
132 case_sensitive = other.case_sensitive;
133 if (type == PathMatcherType::REGEX) {
134 RE2::Options options;
135 options.set_case_sensitive(case_sensitive);
136 regex_matcher =
137 absl::make_unique<RE2>(other.regex_matcher->pattern(), options);
138 } else {
139 string_matcher = other.string_matcher;
140 }
141 return *this;
142 }
143
operator ==(const PathMatcher & other) const144 bool XdsApi::Route::Matchers::PathMatcher::operator==(
145 const PathMatcher& other) const {
146 if (type != other.type) return false;
147 if (case_sensitive != other.case_sensitive) return false;
148 if (type == PathMatcherType::REGEX) {
149 // Should never be null.
150 if (regex_matcher == nullptr || other.regex_matcher == nullptr) {
151 return false;
152 }
153 return regex_matcher->pattern() == other.regex_matcher->pattern();
154 }
155 return string_matcher == other.string_matcher;
156 }
157
ToString() const158 std::string XdsApi::Route::Matchers::PathMatcher::ToString() const {
159 std::string path_type_string;
160 switch (type) {
161 case PathMatcherType::PATH:
162 path_type_string = "path match";
163 break;
164 case PathMatcherType::PREFIX:
165 path_type_string = "prefix match";
166 break;
167 case PathMatcherType::REGEX:
168 path_type_string = "regex match";
169 break;
170 default:
171 break;
172 }
173 return absl::StrFormat("Path %s:%s%s", path_type_string,
174 type == PathMatcherType::REGEX
175 ? regex_matcher->pattern()
176 : string_matcher,
177 case_sensitive ? "" : "[case_sensitive=false]");
178 }
179
180 //
181 // XdsApi::Route::Matchers::HeaderMatcher
182 //
183
HeaderMatcher(const HeaderMatcher & other)184 XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcher(
185 const HeaderMatcher& other)
186 : name(other.name), type(other.type), invert_match(other.invert_match) {
187 switch (type) {
188 case HeaderMatcherType::REGEX:
189 regex_match = absl::make_unique<RE2>(other.regex_match->pattern());
190 break;
191 case HeaderMatcherType::RANGE:
192 range_start = other.range_start;
193 range_end = other.range_end;
194 break;
195 case HeaderMatcherType::PRESENT:
196 present_match = other.present_match;
197 break;
198 default:
199 string_matcher = other.string_matcher;
200 }
201 }
202
203 XdsApi::Route::Matchers::HeaderMatcher& XdsApi::Route::Matchers::HeaderMatcher::
operator =(const HeaderMatcher & other)204 operator=(const HeaderMatcher& other) {
205 name = other.name;
206 type = other.type;
207 invert_match = other.invert_match;
208 switch (type) {
209 case HeaderMatcherType::REGEX:
210 regex_match = absl::make_unique<RE2>(other.regex_match->pattern());
211 break;
212 case HeaderMatcherType::RANGE:
213 range_start = other.range_start;
214 range_end = other.range_end;
215 break;
216 case HeaderMatcherType::PRESENT:
217 present_match = other.present_match;
218 break;
219 default:
220 string_matcher = other.string_matcher;
221 }
222 return *this;
223 }
224
operator ==(const HeaderMatcher & other) const225 bool XdsApi::Route::Matchers::HeaderMatcher::operator==(
226 const HeaderMatcher& other) const {
227 if (name != other.name) return false;
228 if (type != other.type) return false;
229 if (invert_match != other.invert_match) return false;
230 switch (type) {
231 case HeaderMatcherType::REGEX:
232 return regex_match->pattern() != other.regex_match->pattern();
233 case HeaderMatcherType::RANGE:
234 return range_start != other.range_start && range_end != other.range_end;
235 case HeaderMatcherType::PRESENT:
236 return present_match != other.present_match;
237 default:
238 return string_matcher != other.string_matcher;
239 }
240 }
241
ToString() const242 std::string XdsApi::Route::Matchers::HeaderMatcher::ToString() const {
243 switch (type) {
244 case HeaderMatcherType::EXACT:
245 return absl::StrFormat("Header exact match:%s %s:%s",
246 invert_match ? " not" : "", name, string_matcher);
247 case HeaderMatcherType::REGEX:
248 return absl::StrFormat("Header regex match:%s %s:%s",
249 invert_match ? " not" : "", name,
250 regex_match->pattern());
251 case HeaderMatcherType::RANGE:
252 return absl::StrFormat("Header range match:%s %s:[%d, %d)",
253 invert_match ? " not" : "", name, range_start,
254 range_end);
255 case HeaderMatcherType::PRESENT:
256 return absl::StrFormat("Header present match:%s %s:%s",
257 invert_match ? " not" : "", name,
258 present_match ? "true" : "false");
259 case HeaderMatcherType::PREFIX:
260 return absl::StrFormat("Header prefix match:%s %s:%s",
261 invert_match ? " not" : "", name, string_matcher);
262 case HeaderMatcherType::SUFFIX:
263 return absl::StrFormat("Header suffix match:%s %s:%s",
264 invert_match ? " not" : "", name, string_matcher);
265 default:
266 return "";
267 }
268 }
269
270 //
271 // XdsApi::Route
272 //
273
ToString() const274 std::string XdsApi::Route::Matchers::ToString() const {
275 std::vector<std::string> contents;
276 contents.push_back(path_matcher.ToString());
277 for (const HeaderMatcher& header_matcher : header_matchers) {
278 contents.push_back(header_matcher.ToString());
279 }
280 if (fraction_per_million.has_value()) {
281 contents.push_back(absl::StrFormat("Fraction Per Million %d",
282 fraction_per_million.value()));
283 }
284 return absl::StrJoin(contents, "\n");
285 }
286
ToString() const287 std::string XdsApi::Route::ClusterWeight::ToString() const {
288 return absl::StrFormat("{cluster=%s, weight=%d}", name, weight);
289 }
290
ToString() const291 std::string XdsApi::Route::ToString() const {
292 std::vector<std::string> contents;
293 contents.push_back(matchers.ToString());
294 if (!cluster_name.empty()) {
295 contents.push_back(absl::StrFormat("Cluster name: %s", cluster_name));
296 }
297 for (const ClusterWeight& cluster_weight : weighted_clusters) {
298 contents.push_back(cluster_weight.ToString());
299 }
300 if (max_stream_duration.has_value()) {
301 contents.push_back(max_stream_duration->ToString());
302 }
303 return absl::StrJoin(contents, "\n");
304 }
305
306 //
307 // XdsApi::RdsUpdate
308 //
309
ToString() const310 std::string XdsApi::RdsUpdate::ToString() const {
311 std::vector<std::string> vhosts;
312 for (const VirtualHost& vhost : virtual_hosts) {
313 vhosts.push_back(
314 absl::StrCat("vhost={\n"
315 " domains=[",
316 absl::StrJoin(vhost.domains, ", "),
317 "]\n"
318 " routes=[\n"));
319 for (const XdsApi::Route& route : vhost.routes) {
320 vhosts.push_back(" {\n");
321 vhosts.push_back(route.ToString());
322 vhosts.push_back("\n }\n");
323 }
324 vhosts.push_back(" ]\n");
325 vhosts.push_back("]\n");
326 }
327 return absl::StrJoin(vhosts, "");
328 }
329
330 namespace {
331
332 // Better match type has smaller value.
333 enum MatchType {
334 EXACT_MATCH,
335 SUFFIX_MATCH,
336 PREFIX_MATCH,
337 UNIVERSE_MATCH,
338 INVALID_MATCH,
339 };
340
341 // Returns true if match succeeds.
DomainMatch(MatchType match_type,const std::string & domain_pattern_in,const std::string & expected_host_name_in)342 bool DomainMatch(MatchType match_type, const std::string& domain_pattern_in,
343 const std::string& expected_host_name_in) {
344 // Normalize the args to lower-case. Domain matching is case-insensitive.
345 std::string domain_pattern = domain_pattern_in;
346 std::string expected_host_name = expected_host_name_in;
347 std::transform(domain_pattern.begin(), domain_pattern.end(),
348 domain_pattern.begin(),
349 [](unsigned char c) { return std::tolower(c); });
350 std::transform(expected_host_name.begin(), expected_host_name.end(),
351 expected_host_name.begin(),
352 [](unsigned char c) { return std::tolower(c); });
353 if (match_type == EXACT_MATCH) {
354 return domain_pattern == expected_host_name;
355 } else if (match_type == SUFFIX_MATCH) {
356 // Asterisk must match at least one char.
357 if (expected_host_name.size() < domain_pattern.size()) return false;
358 absl::string_view pattern_suffix(domain_pattern.c_str() + 1);
359 absl::string_view host_suffix(expected_host_name.c_str() +
360 expected_host_name.size() -
361 pattern_suffix.size());
362 return pattern_suffix == host_suffix;
363 } else if (match_type == PREFIX_MATCH) {
364 // Asterisk must match at least one char.
365 if (expected_host_name.size() < domain_pattern.size()) return false;
366 absl::string_view pattern_prefix(domain_pattern.c_str(),
367 domain_pattern.size() - 1);
368 absl::string_view host_prefix(expected_host_name.c_str(),
369 pattern_prefix.size());
370 return pattern_prefix == host_prefix;
371 } else {
372 return match_type == UNIVERSE_MATCH;
373 }
374 }
375
DomainPatternMatchType(const std::string & domain_pattern)376 MatchType DomainPatternMatchType(const std::string& domain_pattern) {
377 if (domain_pattern.empty()) return INVALID_MATCH;
378 if (domain_pattern.find('*') == std::string::npos) return EXACT_MATCH;
379 if (domain_pattern == "*") return UNIVERSE_MATCH;
380 if (domain_pattern[0] == '*') return SUFFIX_MATCH;
381 if (domain_pattern[domain_pattern.size() - 1] == '*') return PREFIX_MATCH;
382 return INVALID_MATCH;
383 }
384
385 } // namespace
386
FindVirtualHostForDomain(const std::string & domain)387 XdsApi::RdsUpdate::VirtualHost* XdsApi::RdsUpdate::FindVirtualHostForDomain(
388 const std::string& domain) {
389 // Find the best matched virtual host.
390 // The search order for 4 groups of domain patterns:
391 // 1. Exact match.
392 // 2. Suffix match (e.g., "*ABC").
393 // 3. Prefix match (e.g., "ABC*").
394 // 4. Universe match (i.e., "*").
395 // Within each group, longest match wins.
396 // If the same best matched domain pattern appears in multiple virtual hosts,
397 // the first matched virtual host wins.
398 VirtualHost* target_vhost = nullptr;
399 MatchType best_match_type = INVALID_MATCH;
400 size_t longest_match = 0;
401 // Check each domain pattern in each virtual host to determine the best
402 // matched virtual host.
403 for (VirtualHost& vhost : virtual_hosts) {
404 for (const std::string& domain_pattern : vhost.domains) {
405 // Check the match type first. Skip the pattern if it's not better than
406 // current match.
407 const MatchType match_type = DomainPatternMatchType(domain_pattern);
408 // This should be caught by RouteConfigParse().
409 GPR_ASSERT(match_type != INVALID_MATCH);
410 if (match_type > best_match_type) continue;
411 if (match_type == best_match_type &&
412 domain_pattern.size() <= longest_match) {
413 continue;
414 }
415 // Skip if match fails.
416 if (!DomainMatch(match_type, domain_pattern, domain)) continue;
417 // Choose this match.
418 target_vhost = &vhost;
419 best_match_type = match_type;
420 longest_match = domain_pattern.size();
421 if (best_match_type == EXACT_MATCH) break;
422 }
423 if (best_match_type == EXACT_MATCH) break;
424 }
425 return target_vhost;
426 }
427
428 //
429 // XdsApi::StringMatcher
430 //
431
StringMatcher(StringMatcherType type,const std::string & matcher,bool ignore_case)432 XdsApi::StringMatcher::StringMatcher(StringMatcherType type,
433 const std::string& matcher,
434 bool ignore_case)
435 : type_(type), ignore_case_(ignore_case) {
436 if (type_ == StringMatcherType::SAFE_REGEX) {
437 regex_matcher_ = absl::make_unique<RE2>(matcher);
438 } else {
439 string_matcher_ = matcher;
440 }
441 }
442
StringMatcher(const StringMatcher & other)443 XdsApi::StringMatcher::StringMatcher(const StringMatcher& other)
444 : type_(other.type_), ignore_case_(other.ignore_case_) {
445 switch (type_) {
446 case StringMatcherType::SAFE_REGEX:
447 regex_matcher_ = absl::make_unique<RE2>(other.regex_matcher_->pattern());
448 break;
449 default:
450 string_matcher_ = other.string_matcher_;
451 }
452 }
453
operator =(const StringMatcher & other)454 XdsApi::StringMatcher& XdsApi::StringMatcher::operator=(
455 const StringMatcher& other) {
456 type_ = other.type_;
457 switch (type_) {
458 case StringMatcherType::SAFE_REGEX:
459 regex_matcher_ = absl::make_unique<RE2>(other.regex_matcher_->pattern());
460 break;
461 default:
462 string_matcher_ = other.string_matcher_;
463 }
464 ignore_case_ = other.ignore_case_;
465 return *this;
466 }
467
operator ==(const StringMatcher & other) const468 bool XdsApi::StringMatcher::operator==(const StringMatcher& other) const {
469 if (type_ != other.type_ || ignore_case_ != other.ignore_case_) return false;
470 switch (type_) {
471 case StringMatcherType::SAFE_REGEX:
472 return regex_matcher_->pattern() == other.regex_matcher_->pattern();
473 default:
474 return string_matcher_ == other.string_matcher_;
475 }
476 }
477
Match(absl::string_view value) const478 bool XdsApi::StringMatcher::Match(absl::string_view value) const {
479 switch (type_) {
480 case XdsApi::StringMatcher::StringMatcherType::EXACT:
481 return ignore_case_ ? absl::EqualsIgnoreCase(value, string_matcher_)
482 : value == string_matcher_;
483 case XdsApi::StringMatcher::StringMatcherType::PREFIX:
484 return ignore_case_ ? absl::StartsWithIgnoreCase(value, string_matcher_)
485 : absl::StartsWith(value, string_matcher_);
486 case XdsApi::StringMatcher::StringMatcherType::SUFFIX:
487 return ignore_case_ ? absl::EndsWithIgnoreCase(value, string_matcher_)
488 : absl::EndsWith(value, string_matcher_);
489 case XdsApi::StringMatcher::StringMatcherType::CONTAINS:
490 return ignore_case_
491 ? absl::StrContains(absl::AsciiStrToLower(value),
492 absl::AsciiStrToLower(string_matcher_))
493 : absl::StrContains(value, string_matcher_);
494 case XdsApi::StringMatcher::StringMatcherType::SAFE_REGEX:
495 // ignore_case_ is ignored for SAFE_REGEX
496 return RE2::FullMatch(std::string(value), *regex_matcher_);
497 default:
498 return false;
499 }
500 }
501
ToString() const502 std::string XdsApi::StringMatcher::ToString() const {
503 switch (type_) {
504 case StringMatcherType::EXACT:
505 return absl::StrFormat("StringMatcher{exact=%s%s}", string_matcher_,
506 ignore_case_ ? ", ignore_case" : "");
507 case StringMatcherType::PREFIX:
508 return absl::StrFormat("StringMatcher{prefix=%s%s}", string_matcher_,
509 ignore_case_ ? ", ignore_case" : "");
510 case StringMatcherType::SUFFIX:
511 return absl::StrFormat("StringMatcher{suffix=%s%s}", string_matcher_,
512 ignore_case_ ? ", ignore_case" : "");
513 case StringMatcherType::CONTAINS:
514 return absl::StrFormat("StringMatcher{contains=%s%s}", string_matcher_,
515 ignore_case_ ? ", ignore_case" : "");
516 case StringMatcherType::SAFE_REGEX:
517 return absl::StrFormat("StringMatcher{safe_regex=%s}",
518 regex_matcher_->pattern());
519 default:
520 return "";
521 }
522 }
523
524 //
525 // XdsApi::CommonTlsContext::CertificateValidationContext
526 //
527
ToString() const528 std::string XdsApi::CommonTlsContext::CertificateValidationContext::ToString()
529 const {
530 std::vector<std::string> contents;
531 for (const auto& match : match_subject_alt_names) {
532 contents.push_back(match.ToString());
533 }
534 return absl::StrFormat("{match_subject_alt_names=[%s]}",
535 absl::StrJoin(contents, ", "));
536 }
537
Empty() const538 bool XdsApi::CommonTlsContext::CertificateValidationContext::Empty() const {
539 return match_subject_alt_names.empty();
540 }
541
542 //
543 // XdsApi::CommonTlsContext::CertificateValidationContext
544 //
545
ToString() const546 std::string XdsApi::CommonTlsContext::CertificateProviderInstance::ToString()
547 const {
548 absl::InlinedVector<std::string, 2> contents;
549 if (!instance_name.empty()) {
550 contents.push_back(absl::StrFormat("instance_name=%s", instance_name));
551 }
552 if (!certificate_name.empty()) {
553 contents.push_back(
554 absl::StrFormat("certificate_name=%s", certificate_name));
555 }
556 return absl::StrCat("{", absl::StrJoin(contents, ", "), "}");
557 }
558
Empty() const559 bool XdsApi::CommonTlsContext::CertificateProviderInstance::Empty() const {
560 return instance_name.empty() && certificate_name.empty();
561 }
562
563 //
564 // XdsApi::CommonTlsContext::CombinedCertificateValidationContext
565 //
566
567 std::string
ToString() const568 XdsApi::CommonTlsContext::CombinedCertificateValidationContext::ToString()
569 const {
570 absl::InlinedVector<std::string, 2> contents;
571 if (!default_validation_context.Empty()) {
572 contents.push_back(absl::StrFormat("default_validation_context=%s",
573 default_validation_context.ToString()));
574 }
575 if (!validation_context_certificate_provider_instance.Empty()) {
576 contents.push_back(absl::StrFormat(
577 "validation_context_certificate_provider_instance=%s",
578 validation_context_certificate_provider_instance.ToString()));
579 }
580 return absl::StrCat("{", absl::StrJoin(contents, ", "), "}");
581 }
582
Empty() const583 bool XdsApi::CommonTlsContext::CombinedCertificateValidationContext::Empty()
584 const {
585 return default_validation_context.Empty() &&
586 validation_context_certificate_provider_instance.Empty();
587 }
588
589 //
590 // XdsApi::CommonTlsContext
591 //
592
ToString() const593 std::string XdsApi::CommonTlsContext::ToString() const {
594 absl::InlinedVector<std::string, 2> contents;
595 if (!tls_certificate_certificate_provider_instance.Empty()) {
596 contents.push_back(absl::StrFormat(
597 "tls_certificate_certificate_provider_instance=%s",
598 tls_certificate_certificate_provider_instance.ToString()));
599 }
600 if (!combined_validation_context.Empty()) {
601 contents.push_back(absl::StrFormat("combined_validation_context=%s",
602 combined_validation_context.ToString()));
603 }
604 return absl::StrCat("{", absl::StrJoin(contents, ", "), "}");
605 }
606
Empty() const607 bool XdsApi::CommonTlsContext::Empty() const {
608 return tls_certificate_certificate_provider_instance.Empty() &&
609 combined_validation_context.Empty();
610 }
611
612 //
613 // XdsApi::CdsUpdate
614 //
615
ToString() const616 std::string XdsApi::CdsUpdate::ToString() const {
617 absl::InlinedVector<std::string, 4> contents;
618 if (!eds_service_name.empty()) {
619 contents.push_back(
620 absl::StrFormat("eds_service_name=%s", eds_service_name));
621 }
622 if (!common_tls_context.Empty()) {
623 contents.push_back(absl::StrFormat("common_tls_context=%s",
624 common_tls_context.ToString()));
625 }
626 if (lrs_load_reporting_server_name.has_value()) {
627 contents.push_back(absl::StrFormat("lrs_load_reporting_server_name=%s",
628 lrs_load_reporting_server_name.value()));
629 }
630 contents.push_back(
631 absl::StrFormat("max_concurrent_requests=%d", max_concurrent_requests));
632 return absl::StrCat("{", absl::StrJoin(contents, ", "), "}");
633 }
634
635 //
636 // XdsApi::EdsUpdate
637 //
638
ToString() const639 std::string XdsApi::EdsUpdate::Priority::Locality::ToString() const {
640 std::vector<std::string> endpoint_strings;
641 for (const ServerAddress& endpoint : endpoints) {
642 endpoint_strings.emplace_back(endpoint.ToString());
643 }
644 return absl::StrCat("{name=", name->AsHumanReadableString(),
645 ", lb_weight=", lb_weight, ", endpoints=[",
646 absl::StrJoin(endpoint_strings, ", "), "]}");
647 }
648
operator ==(const Priority & other) const649 bool XdsApi::EdsUpdate::Priority::operator==(const Priority& other) const {
650 if (localities.size() != other.localities.size()) return false;
651 auto it1 = localities.begin();
652 auto it2 = other.localities.begin();
653 while (it1 != localities.end()) {
654 if (*it1->first != *it2->first) return false;
655 if (it1->second != it2->second) return false;
656 ++it1;
657 ++it2;
658 }
659 return true;
660 }
661
ToString() const662 std::string XdsApi::EdsUpdate::Priority::ToString() const {
663 std::vector<std::string> locality_strings;
664 for (const auto& p : localities) {
665 locality_strings.emplace_back(p.second.ToString());
666 }
667 return absl::StrCat("[", absl::StrJoin(locality_strings, ", "), "]");
668 }
669
ShouldDrop(const std::string ** category_name) const670 bool XdsApi::EdsUpdate::DropConfig::ShouldDrop(
671 const std::string** category_name) const {
672 for (size_t i = 0; i < drop_category_list_.size(); ++i) {
673 const auto& drop_category = drop_category_list_[i];
674 // Generate a random number in [0, 1000000).
675 const uint32_t random = static_cast<uint32_t>(rand()) % 1000000;
676 if (random < drop_category.parts_per_million) {
677 *category_name = &drop_category.name;
678 return true;
679 }
680 }
681 return false;
682 }
683
ToString() const684 std::string XdsApi::EdsUpdate::DropConfig::ToString() const {
685 std::vector<std::string> category_strings;
686 for (const DropCategory& category : drop_category_list_) {
687 category_strings.emplace_back(
688 absl::StrCat(category.name, "=", category.parts_per_million));
689 }
690 return absl::StrCat("{[", absl::StrJoin(category_strings, ", "),
691 "], drop_all=", drop_all_, "}");
692 }
693
ToString() const694 std::string XdsApi::EdsUpdate::ToString() const {
695 std::vector<std::string> priority_strings;
696 for (size_t i = 0; i < priorities.size(); ++i) {
697 const Priority& priority = priorities[i];
698 priority_strings.emplace_back(
699 absl::StrCat("priority ", i, ": ", priority.ToString()));
700 }
701 return absl::StrCat("priorities=[", absl::StrJoin(priority_strings, ", "),
702 "], drop_config=", drop_config->ToString());
703 }
704
705 //
706 // XdsApi
707 //
708
709 const char* XdsApi::kLdsTypeUrl =
710 "type.googleapis.com/envoy.config.listener.v3.Listener";
711 const char* XdsApi::kRdsTypeUrl =
712 "type.googleapis.com/envoy.config.route.v3.RouteConfiguration";
713 const char* XdsApi::kCdsTypeUrl =
714 "type.googleapis.com/envoy.config.cluster.v3.Cluster";
715 const char* XdsApi::kEdsTypeUrl =
716 "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment";
717
718 namespace {
719
720 const char* kLdsV2TypeUrl = "type.googleapis.com/envoy.api.v2.Listener";
721 const char* kRdsV2TypeUrl =
722 "type.googleapis.com/envoy.api.v2.RouteConfiguration";
723 const char* kCdsV2TypeUrl = "type.googleapis.com/envoy.api.v2.Cluster";
724 const char* kEdsV2TypeUrl =
725 "type.googleapis.com/envoy.api.v2.ClusterLoadAssignment";
726
IsLds(absl::string_view type_url)727 bool IsLds(absl::string_view type_url) {
728 return type_url == XdsApi::kLdsTypeUrl || type_url == kLdsV2TypeUrl;
729 }
730
IsRds(absl::string_view type_url)731 bool IsRds(absl::string_view type_url) {
732 return type_url == XdsApi::kRdsTypeUrl || type_url == kRdsV2TypeUrl;
733 }
734
IsCds(absl::string_view type_url)735 bool IsCds(absl::string_view type_url) {
736 return type_url == XdsApi::kCdsTypeUrl || type_url == kCdsV2TypeUrl;
737 }
738
IsEds(absl::string_view type_url)739 bool IsEds(absl::string_view type_url) {
740 return type_url == XdsApi::kEdsTypeUrl || type_url == kEdsV2TypeUrl;
741 }
742
743 } // namespace
744
XdsApi(XdsClient * client,TraceFlag * tracer,const XdsBootstrap::Node * node)745 XdsApi::XdsApi(XdsClient* client, TraceFlag* tracer,
746 const XdsBootstrap::Node* node)
747 : client_(client),
748 tracer_(tracer),
749 node_(node),
750 build_version_(absl::StrCat("gRPC C-core ", GPR_PLATFORM_STRING, " ",
751 grpc_version_string())),
752 user_agent_name_(absl::StrCat("gRPC C-core ", GPR_PLATFORM_STRING)) {}
753
754 namespace {
755
756 // Works for both std::string and absl::string_view.
757 template <typename T>
StdStringToUpbString(const T & str)758 inline upb_strview StdStringToUpbString(const T& str) {
759 return upb_strview_make(str.data(), str.size());
760 }
761
762 void PopulateMetadataValue(upb_arena* arena, google_protobuf_Value* value_pb,
763 const Json& value);
764
PopulateListValue(upb_arena * arena,google_protobuf_ListValue * list_value,const Json::Array & values)765 void PopulateListValue(upb_arena* arena, google_protobuf_ListValue* list_value,
766 const Json::Array& values) {
767 for (const auto& value : values) {
768 auto* value_pb = google_protobuf_ListValue_add_values(list_value, arena);
769 PopulateMetadataValue(arena, value_pb, value);
770 }
771 }
772
PopulateMetadata(upb_arena * arena,google_protobuf_Struct * metadata_pb,const Json::Object & metadata)773 void PopulateMetadata(upb_arena* arena, google_protobuf_Struct* metadata_pb,
774 const Json::Object& metadata) {
775 for (const auto& p : metadata) {
776 google_protobuf_Value* value = google_protobuf_Value_new(arena);
777 PopulateMetadataValue(arena, value, p.second);
778 google_protobuf_Struct_fields_set(
779 metadata_pb, StdStringToUpbString(p.first), value, arena);
780 }
781 }
782
PopulateMetadataValue(upb_arena * arena,google_protobuf_Value * value_pb,const Json & value)783 void PopulateMetadataValue(upb_arena* arena, google_protobuf_Value* value_pb,
784 const Json& value) {
785 switch (value.type()) {
786 case Json::Type::JSON_NULL:
787 google_protobuf_Value_set_null_value(value_pb, 0);
788 break;
789 case Json::Type::NUMBER:
790 google_protobuf_Value_set_number_value(
791 value_pb, strtod(value.string_value().c_str(), nullptr));
792 break;
793 case Json::Type::STRING:
794 google_protobuf_Value_set_string_value(
795 value_pb, StdStringToUpbString(value.string_value()));
796 break;
797 case Json::Type::JSON_TRUE:
798 google_protobuf_Value_set_bool_value(value_pb, true);
799 break;
800 case Json::Type::JSON_FALSE:
801 google_protobuf_Value_set_bool_value(value_pb, false);
802 break;
803 case Json::Type::OBJECT: {
804 google_protobuf_Struct* struct_value =
805 google_protobuf_Value_mutable_struct_value(value_pb, arena);
806 PopulateMetadata(arena, struct_value, value.object_value());
807 break;
808 }
809 case Json::Type::ARRAY: {
810 google_protobuf_ListValue* list_value =
811 google_protobuf_Value_mutable_list_value(value_pb, arena);
812 PopulateListValue(arena, list_value, value.array_value());
813 break;
814 }
815 }
816 }
817
818 // Helper functions to manually do protobuf string encoding, so that we
819 // can populate the node build_version field that was removed in v3.
EncodeVarint(uint64_t val)820 std::string EncodeVarint(uint64_t val) {
821 std::string data;
822 do {
823 uint8_t byte = val & 0x7fU;
824 val >>= 7;
825 if (val) byte |= 0x80U;
826 data += byte;
827 } while (val);
828 return data;
829 }
EncodeTag(uint32_t field_number,uint8_t wire_type)830 std::string EncodeTag(uint32_t field_number, uint8_t wire_type) {
831 return EncodeVarint((field_number << 3) | wire_type);
832 }
EncodeStringField(uint32_t field_number,const std::string & str)833 std::string EncodeStringField(uint32_t field_number, const std::string& str) {
834 static const uint8_t kDelimitedWireType = 2;
835 return EncodeTag(field_number, kDelimitedWireType) +
836 EncodeVarint(str.size()) + str;
837 }
838
PopulateBuildVersion(upb_arena * arena,envoy_config_core_v3_Node * node_msg,const std::string & build_version)839 void PopulateBuildVersion(upb_arena* arena, envoy_config_core_v3_Node* node_msg,
840 const std::string& build_version) {
841 std::string encoded_build_version = EncodeStringField(5, build_version);
842 // TODO(roth): This should use upb_msg_addunknown(), but that API is
843 // broken in the current version of upb, so we're using the internal
844 // API for now. Change this once we upgrade to a version of upb that
845 // fixes this bug.
846 _upb_msg_addunknown(node_msg, encoded_build_version.data(),
847 encoded_build_version.size(), arena);
848 }
849
PopulateNode(upb_arena * arena,const XdsBootstrap::Node * node,bool use_v3,const std::string & build_version,const std::string & user_agent_name,envoy_config_core_v3_Node * node_msg)850 void PopulateNode(upb_arena* arena, const XdsBootstrap::Node* node, bool use_v3,
851 const std::string& build_version,
852 const std::string& user_agent_name,
853 envoy_config_core_v3_Node* node_msg) {
854 if (node != nullptr) {
855 if (!node->id.empty()) {
856 envoy_config_core_v3_Node_set_id(node_msg,
857 StdStringToUpbString(node->id));
858 }
859 if (!node->cluster.empty()) {
860 envoy_config_core_v3_Node_set_cluster(
861 node_msg, StdStringToUpbString(node->cluster));
862 }
863 if (!node->metadata.object_value().empty()) {
864 google_protobuf_Struct* metadata =
865 envoy_config_core_v3_Node_mutable_metadata(node_msg, arena);
866 PopulateMetadata(arena, metadata, node->metadata.object_value());
867 }
868 if (!node->locality_region.empty() || !node->locality_zone.empty() ||
869 !node->locality_subzone.empty()) {
870 envoy_config_core_v3_Locality* locality =
871 envoy_config_core_v3_Node_mutable_locality(node_msg, arena);
872 if (!node->locality_region.empty()) {
873 envoy_config_core_v3_Locality_set_region(
874 locality, StdStringToUpbString(node->locality_region));
875 }
876 if (!node->locality_zone.empty()) {
877 envoy_config_core_v3_Locality_set_zone(
878 locality, StdStringToUpbString(node->locality_zone));
879 }
880 if (!node->locality_subzone.empty()) {
881 envoy_config_core_v3_Locality_set_sub_zone(
882 locality, StdStringToUpbString(node->locality_subzone));
883 }
884 }
885 }
886 if (!use_v3) {
887 PopulateBuildVersion(arena, node_msg, build_version);
888 }
889 envoy_config_core_v3_Node_set_user_agent_name(
890 node_msg, StdStringToUpbString(user_agent_name));
891 envoy_config_core_v3_Node_set_user_agent_version(
892 node_msg, upb_strview_makez(grpc_version_string()));
893 envoy_config_core_v3_Node_add_client_features(
894 node_msg, upb_strview_makez("envoy.lb.does_not_support_overprovisioning"),
895 arena);
896 }
897
UpbStringToAbsl(const upb_strview & str)898 inline absl::string_view UpbStringToAbsl(const upb_strview& str) {
899 return absl::string_view(str.data, str.size);
900 }
901
UpbStringToStdString(const upb_strview & str)902 inline std::string UpbStringToStdString(const upb_strview& str) {
903 return std::string(str.data, str.size);
904 }
905
MaybeLogDiscoveryRequest(XdsClient * client,TraceFlag * tracer,upb_symtab * symtab,const envoy_service_discovery_v3_DiscoveryRequest * request)906 void MaybeLogDiscoveryRequest(
907 XdsClient* client, TraceFlag* tracer, upb_symtab* symtab,
908 const envoy_service_discovery_v3_DiscoveryRequest* request) {
909 if (GRPC_TRACE_FLAG_ENABLED(*tracer) &&
910 gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
911 const upb_msgdef* msg_type =
912 envoy_service_discovery_v3_DiscoveryRequest_getmsgdef(symtab);
913 char buf[10240];
914 upb_text_encode(request, msg_type, nullptr, 0, buf, sizeof(buf));
915 gpr_log(GPR_DEBUG, "[xds_client %p] constructed ADS request: %s", client,
916 buf);
917 }
918 }
919
SerializeDiscoveryRequest(upb_arena * arena,envoy_service_discovery_v3_DiscoveryRequest * request)920 grpc_slice SerializeDiscoveryRequest(
921 upb_arena* arena, envoy_service_discovery_v3_DiscoveryRequest* request) {
922 size_t output_length;
923 char* output = envoy_service_discovery_v3_DiscoveryRequest_serialize(
924 request, arena, &output_length);
925 return grpc_slice_from_copied_buffer(output, output_length);
926 }
927
TypeUrlExternalToInternal(bool use_v3,const std::string & type_url)928 absl::string_view TypeUrlExternalToInternal(bool use_v3,
929 const std::string& type_url) {
930 if (!use_v3) {
931 if (type_url == XdsApi::kLdsTypeUrl) {
932 return kLdsV2TypeUrl;
933 }
934 if (type_url == XdsApi::kRdsTypeUrl) {
935 return kRdsV2TypeUrl;
936 }
937 if (type_url == XdsApi::kCdsTypeUrl) {
938 return kCdsV2TypeUrl;
939 }
940 if (type_url == XdsApi::kEdsTypeUrl) {
941 return kEdsV2TypeUrl;
942 }
943 }
944 return type_url;
945 }
946
947 } // namespace
948
CreateAdsRequest(const XdsBootstrap::XdsServer & server,const std::string & type_url,const std::set<absl::string_view> & resource_names,const std::string & version,const std::string & nonce,grpc_error * error,bool populate_node)949 grpc_slice XdsApi::CreateAdsRequest(
950 const XdsBootstrap::XdsServer& server, const std::string& type_url,
951 const std::set<absl::string_view>& resource_names,
952 const std::string& version, const std::string& nonce, grpc_error* error,
953 bool populate_node) {
954 upb::Arena arena;
955 // Create a request.
956 envoy_service_discovery_v3_DiscoveryRequest* request =
957 envoy_service_discovery_v3_DiscoveryRequest_new(arena.ptr());
958 // Set type_url.
959 absl::string_view real_type_url =
960 TypeUrlExternalToInternal(server.ShouldUseV3(), type_url);
961 envoy_service_discovery_v3_DiscoveryRequest_set_type_url(
962 request, StdStringToUpbString(real_type_url));
963 // Set version_info.
964 if (!version.empty()) {
965 envoy_service_discovery_v3_DiscoveryRequest_set_version_info(
966 request, StdStringToUpbString(version));
967 }
968 // Set nonce.
969 if (!nonce.empty()) {
970 envoy_service_discovery_v3_DiscoveryRequest_set_response_nonce(
971 request, StdStringToUpbString(nonce));
972 }
973 // Set error_detail if it's a NACK.
974 if (error != GRPC_ERROR_NONE) {
975 google_rpc_Status* error_detail =
976 envoy_service_discovery_v3_DiscoveryRequest_mutable_error_detail(
977 request, arena.ptr());
978 // Hard-code INVALID_ARGUMENT as the status code.
979 // TODO(roth): If at some point we decide we care about this value,
980 // we could attach a status code to the individual errors where we
981 // generate them in the parsing code, and then use that here.
982 google_rpc_Status_set_code(error_detail, GRPC_STATUS_INVALID_ARGUMENT);
983 // Error description comes from the error that was passed in.
984 grpc_slice error_description_slice;
985 GPR_ASSERT(grpc_error_get_str(error, GRPC_ERROR_STR_DESCRIPTION,
986 &error_description_slice));
987 upb_strview error_description_strview =
988 StdStringToUpbString(StringViewFromSlice(error_description_slice));
989 google_rpc_Status_set_message(error_detail, error_description_strview);
990 GRPC_ERROR_UNREF(error);
991 }
992 // Populate node.
993 if (populate_node) {
994 envoy_config_core_v3_Node* node_msg =
995 envoy_service_discovery_v3_DiscoveryRequest_mutable_node(request,
996 arena.ptr());
997 PopulateNode(arena.ptr(), node_, server.ShouldUseV3(), build_version_,
998 user_agent_name_, node_msg);
999 }
1000 // Add resource_names.
1001 for (const auto& resource_name : resource_names) {
1002 envoy_service_discovery_v3_DiscoveryRequest_add_resource_names(
1003 request, StdStringToUpbString(resource_name), arena.ptr());
1004 }
1005 MaybeLogDiscoveryRequest(client_, tracer_, symtab_.ptr(), request);
1006 return SerializeDiscoveryRequest(arena.ptr(), request);
1007 }
1008
1009 namespace {
1010
MaybeLogDiscoveryResponse(XdsClient * client,TraceFlag * tracer,upb_symtab * symtab,const envoy_service_discovery_v3_DiscoveryResponse * response)1011 void MaybeLogDiscoveryResponse(
1012 XdsClient* client, TraceFlag* tracer, upb_symtab* symtab,
1013 const envoy_service_discovery_v3_DiscoveryResponse* response) {
1014 if (GRPC_TRACE_FLAG_ENABLED(*tracer) &&
1015 gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
1016 const upb_msgdef* msg_type =
1017 envoy_service_discovery_v3_DiscoveryResponse_getmsgdef(symtab);
1018 char buf[10240];
1019 upb_text_encode(response, msg_type, nullptr, 0, buf, sizeof(buf));
1020 gpr_log(GPR_DEBUG, "[xds_client %p] received response: %s", client, buf);
1021 }
1022 }
1023
MaybeLogRouteConfiguration(XdsClient * client,TraceFlag * tracer,upb_symtab * symtab,const envoy_config_route_v3_RouteConfiguration * route_config)1024 void MaybeLogRouteConfiguration(
1025 XdsClient* client, TraceFlag* tracer, upb_symtab* symtab,
1026 const envoy_config_route_v3_RouteConfiguration* route_config) {
1027 if (GRPC_TRACE_FLAG_ENABLED(*tracer) &&
1028 gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
1029 const upb_msgdef* msg_type =
1030 envoy_config_route_v3_RouteConfiguration_getmsgdef(symtab);
1031 char buf[10240];
1032 upb_text_encode(route_config, msg_type, nullptr, 0, buf, sizeof(buf));
1033 gpr_log(GPR_DEBUG, "[xds_client %p] RouteConfiguration: %s", client, buf);
1034 }
1035 }
1036
MaybeLogCluster(XdsClient * client,TraceFlag * tracer,upb_symtab * symtab,const envoy_config_cluster_v3_Cluster * cluster)1037 void MaybeLogCluster(XdsClient* client, TraceFlag* tracer, upb_symtab* symtab,
1038 const envoy_config_cluster_v3_Cluster* cluster) {
1039 if (GRPC_TRACE_FLAG_ENABLED(*tracer) &&
1040 gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
1041 const upb_msgdef* msg_type =
1042 envoy_config_cluster_v3_Cluster_getmsgdef(symtab);
1043 char buf[10240];
1044 upb_text_encode(cluster, msg_type, nullptr, 0, buf, sizeof(buf));
1045 gpr_log(GPR_DEBUG, "[xds_client %p] Cluster: %s", client, buf);
1046 }
1047 }
1048
MaybeLogClusterLoadAssignment(XdsClient * client,TraceFlag * tracer,upb_symtab * symtab,const envoy_config_endpoint_v3_ClusterLoadAssignment * cla)1049 void MaybeLogClusterLoadAssignment(
1050 XdsClient* client, TraceFlag* tracer, upb_symtab* symtab,
1051 const envoy_config_endpoint_v3_ClusterLoadAssignment* cla) {
1052 if (GRPC_TRACE_FLAG_ENABLED(*tracer) &&
1053 gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
1054 const upb_msgdef* msg_type =
1055 envoy_config_endpoint_v3_ClusterLoadAssignment_getmsgdef(symtab);
1056 char buf[10240];
1057 upb_text_encode(cla, msg_type, nullptr, 0, buf, sizeof(buf));
1058 gpr_log(GPR_DEBUG, "[xds_client %p] ClusterLoadAssignment: %s", client,
1059 buf);
1060 }
1061 }
1062
RoutePathMatchParse(const envoy_config_route_v3_RouteMatch * match,XdsApi::Route * route,bool * ignore_route)1063 grpc_error* RoutePathMatchParse(const envoy_config_route_v3_RouteMatch* match,
1064 XdsApi::Route* route, bool* ignore_route) {
1065 auto* case_sensitive = envoy_config_route_v3_RouteMatch_case_sensitive(match);
1066 if (case_sensitive != nullptr) {
1067 route->matchers.path_matcher.case_sensitive =
1068 google_protobuf_BoolValue_value(case_sensitive);
1069 }
1070 if (envoy_config_route_v3_RouteMatch_has_prefix(match)) {
1071 absl::string_view prefix =
1072 UpbStringToAbsl(envoy_config_route_v3_RouteMatch_prefix(match));
1073 // Empty prefix "" is accepted.
1074 if (!prefix.empty()) {
1075 // Prefix "/" is accepted.
1076 if (prefix[0] != '/') {
1077 // Prefix which does not start with a / will never match anything, so
1078 // ignore this route.
1079 *ignore_route = true;
1080 return GRPC_ERROR_NONE;
1081 }
1082 std::vector<absl::string_view> prefix_elements =
1083 absl::StrSplit(prefix.substr(1), absl::MaxSplits('/', 2));
1084 if (prefix_elements.size() > 2) {
1085 // Prefix cannot have more than 2 slashes.
1086 *ignore_route = true;
1087 return GRPC_ERROR_NONE;
1088 } else if (prefix_elements.size() == 2 && prefix_elements[0].empty()) {
1089 // Prefix contains empty string between the 2 slashes
1090 *ignore_route = true;
1091 return GRPC_ERROR_NONE;
1092 }
1093 }
1094 route->matchers.path_matcher.type =
1095 XdsApi::Route::Matchers::PathMatcher::PathMatcherType::PREFIX;
1096 route->matchers.path_matcher.string_matcher = std::string(prefix);
1097 } else if (envoy_config_route_v3_RouteMatch_has_path(match)) {
1098 absl::string_view path =
1099 UpbStringToAbsl(envoy_config_route_v3_RouteMatch_path(match));
1100 if (path.empty()) {
1101 // Path that is empty will never match anything, so ignore this route.
1102 *ignore_route = true;
1103 return GRPC_ERROR_NONE;
1104 }
1105 if (path[0] != '/') {
1106 // Path which does not start with a / will never match anything, so
1107 // ignore this route.
1108 *ignore_route = true;
1109 return GRPC_ERROR_NONE;
1110 }
1111 std::vector<absl::string_view> path_elements =
1112 absl::StrSplit(path.substr(1), absl::MaxSplits('/', 2));
1113 if (path_elements.size() != 2) {
1114 // Path not in the required format of /service/method will never match
1115 // anything, so ignore this route.
1116 *ignore_route = true;
1117 return GRPC_ERROR_NONE;
1118 } else if (path_elements[0].empty()) {
1119 // Path contains empty service name will never match anything, so ignore
1120 // this route.
1121 *ignore_route = true;
1122 return GRPC_ERROR_NONE;
1123 } else if (path_elements[1].empty()) {
1124 // Path contains empty method name will never match anything, so ignore
1125 // this route.
1126 *ignore_route = true;
1127 return GRPC_ERROR_NONE;
1128 }
1129 route->matchers.path_matcher.type =
1130 XdsApi::Route::Matchers::PathMatcher::PathMatcherType::PATH;
1131 route->matchers.path_matcher.string_matcher = std::string(path);
1132 } else if (envoy_config_route_v3_RouteMatch_has_safe_regex(match)) {
1133 const envoy_type_matcher_v3_RegexMatcher* regex_matcher =
1134 envoy_config_route_v3_RouteMatch_safe_regex(match);
1135 GPR_ASSERT(regex_matcher != nullptr);
1136 std::string matcher = UpbStringToStdString(
1137 envoy_type_matcher_v3_RegexMatcher_regex(regex_matcher));
1138 RE2::Options options;
1139 options.set_case_sensitive(route->matchers.path_matcher.case_sensitive);
1140 auto regex = absl::make_unique<RE2>(std::move(matcher), options);
1141 if (!regex->ok()) {
1142 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1143 "Invalid regex string specified in path matcher.");
1144 }
1145 route->matchers.path_matcher.type =
1146 XdsApi::Route::Matchers::PathMatcher::PathMatcherType::REGEX;
1147 route->matchers.path_matcher.regex_matcher = std::move(regex);
1148 } else {
1149 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1150 "Invalid route path specifier specified.");
1151 }
1152 return GRPC_ERROR_NONE;
1153 }
1154
RouteHeaderMatchersParse(const envoy_config_route_v3_RouteMatch * match,XdsApi::Route * route)1155 grpc_error* RouteHeaderMatchersParse(
1156 const envoy_config_route_v3_RouteMatch* match, XdsApi::Route* route) {
1157 size_t size;
1158 const envoy_config_route_v3_HeaderMatcher* const* headers =
1159 envoy_config_route_v3_RouteMatch_headers(match, &size);
1160 for (size_t i = 0; i < size; ++i) {
1161 const envoy_config_route_v3_HeaderMatcher* header = headers[i];
1162 XdsApi::Route::Matchers::HeaderMatcher header_matcher;
1163 header_matcher.name =
1164 UpbStringToStdString(envoy_config_route_v3_HeaderMatcher_name(header));
1165 if (envoy_config_route_v3_HeaderMatcher_has_exact_match(header)) {
1166 header_matcher.type =
1167 XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::EXACT;
1168 header_matcher.string_matcher = UpbStringToStdString(
1169 envoy_config_route_v3_HeaderMatcher_exact_match(header));
1170 } else if (envoy_config_route_v3_HeaderMatcher_has_safe_regex_match(
1171 header)) {
1172 const envoy_type_matcher_v3_RegexMatcher* regex_matcher =
1173 envoy_config_route_v3_HeaderMatcher_safe_regex_match(header);
1174 GPR_ASSERT(regex_matcher != nullptr);
1175 const std::string matcher = UpbStringToStdString(
1176 envoy_type_matcher_v3_RegexMatcher_regex(regex_matcher));
1177 std::unique_ptr<RE2> regex = absl::make_unique<RE2>(matcher);
1178 if (!regex->ok()) {
1179 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1180 "Invalid regex string specified in header matcher.");
1181 }
1182 header_matcher.type =
1183 XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::REGEX;
1184 header_matcher.regex_match = std::move(regex);
1185 } else if (envoy_config_route_v3_HeaderMatcher_has_range_match(header)) {
1186 header_matcher.type =
1187 XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::RANGE;
1188 const envoy_type_v3_Int64Range* range_matcher =
1189 envoy_config_route_v3_HeaderMatcher_range_match(header);
1190 header_matcher.range_start =
1191 envoy_type_v3_Int64Range_start(range_matcher);
1192 header_matcher.range_end = envoy_type_v3_Int64Range_end(range_matcher);
1193 if (header_matcher.range_end < header_matcher.range_start) {
1194 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1195 "Invalid range header matcher specifier specified: end "
1196 "cannot be smaller than start.");
1197 }
1198 } else if (envoy_config_route_v3_HeaderMatcher_has_present_match(header)) {
1199 header_matcher.type =
1200 XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::PRESENT;
1201 header_matcher.present_match =
1202 envoy_config_route_v3_HeaderMatcher_present_match(header);
1203 } else if (envoy_config_route_v3_HeaderMatcher_has_prefix_match(header)) {
1204 header_matcher.type =
1205 XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::PREFIX;
1206 header_matcher.string_matcher = UpbStringToStdString(
1207 envoy_config_route_v3_HeaderMatcher_prefix_match(header));
1208 } else if (envoy_config_route_v3_HeaderMatcher_has_suffix_match(header)) {
1209 header_matcher.type =
1210 XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::SUFFIX;
1211 header_matcher.string_matcher = UpbStringToStdString(
1212 envoy_config_route_v3_HeaderMatcher_suffix_match(header));
1213 } else {
1214 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1215 "Invalid route header matcher specified.");
1216 }
1217 header_matcher.invert_match =
1218 envoy_config_route_v3_HeaderMatcher_invert_match(header);
1219 route->matchers.header_matchers.emplace_back(std::move(header_matcher));
1220 }
1221 return GRPC_ERROR_NONE;
1222 }
1223
RouteRuntimeFractionParse(const envoy_config_route_v3_RouteMatch * match,XdsApi::Route * route)1224 grpc_error* RouteRuntimeFractionParse(
1225 const envoy_config_route_v3_RouteMatch* match, XdsApi::Route* route) {
1226 const envoy_config_core_v3_RuntimeFractionalPercent* runtime_fraction =
1227 envoy_config_route_v3_RouteMatch_runtime_fraction(match);
1228 if (runtime_fraction != nullptr) {
1229 const envoy_type_v3_FractionalPercent* fraction =
1230 envoy_config_core_v3_RuntimeFractionalPercent_default_value(
1231 runtime_fraction);
1232 if (fraction != nullptr) {
1233 uint32_t numerator = envoy_type_v3_FractionalPercent_numerator(fraction);
1234 const auto denominator =
1235 static_cast<envoy_type_v3_FractionalPercent_DenominatorType>(
1236 envoy_type_v3_FractionalPercent_denominator(fraction));
1237 // Normalize to million.
1238 switch (denominator) {
1239 case envoy_type_v3_FractionalPercent_HUNDRED:
1240 numerator *= 10000;
1241 break;
1242 case envoy_type_v3_FractionalPercent_TEN_THOUSAND:
1243 numerator *= 100;
1244 break;
1245 case envoy_type_v3_FractionalPercent_MILLION:
1246 break;
1247 default:
1248 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1249 "Unknown denominator type");
1250 }
1251 route->matchers.fraction_per_million = numerator;
1252 }
1253 }
1254 return GRPC_ERROR_NONE;
1255 }
1256
RouteActionParse(const envoy_config_route_v3_Route * route_msg,XdsApi::Route * route,bool * ignore_route)1257 grpc_error* RouteActionParse(const envoy_config_route_v3_Route* route_msg,
1258 XdsApi::Route* route, bool* ignore_route) {
1259 if (!envoy_config_route_v3_Route_has_route(route_msg)) {
1260 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1261 "No RouteAction found in route.");
1262 }
1263 const envoy_config_route_v3_RouteAction* route_action =
1264 envoy_config_route_v3_Route_route(route_msg);
1265 // Get the cluster or weighted_clusters in the RouteAction.
1266 if (envoy_config_route_v3_RouteAction_has_cluster(route_action)) {
1267 route->cluster_name = UpbStringToStdString(
1268 envoy_config_route_v3_RouteAction_cluster(route_action));
1269 if (route->cluster_name.empty()) {
1270 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1271 "RouteAction cluster contains empty cluster name.");
1272 }
1273 } else if (envoy_config_route_v3_RouteAction_has_weighted_clusters(
1274 route_action)) {
1275 const envoy_config_route_v3_WeightedCluster* weighted_cluster =
1276 envoy_config_route_v3_RouteAction_weighted_clusters(route_action);
1277 uint32_t total_weight = 100;
1278 const google_protobuf_UInt32Value* weight =
1279 envoy_config_route_v3_WeightedCluster_total_weight(weighted_cluster);
1280 if (weight != nullptr) {
1281 total_weight = google_protobuf_UInt32Value_value(weight);
1282 }
1283 size_t clusters_size;
1284 const envoy_config_route_v3_WeightedCluster_ClusterWeight* const* clusters =
1285 envoy_config_route_v3_WeightedCluster_clusters(weighted_cluster,
1286 &clusters_size);
1287 uint32_t sum_of_weights = 0;
1288 for (size_t j = 0; j < clusters_size; ++j) {
1289 const envoy_config_route_v3_WeightedCluster_ClusterWeight*
1290 cluster_weight = clusters[j];
1291 XdsApi::Route::ClusterWeight cluster;
1292 cluster.name = UpbStringToStdString(
1293 envoy_config_route_v3_WeightedCluster_ClusterWeight_name(
1294 cluster_weight));
1295 if (cluster.name.empty()) {
1296 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1297 "RouteAction weighted_cluster cluster contains empty cluster "
1298 "name.");
1299 }
1300 const google_protobuf_UInt32Value* weight =
1301 envoy_config_route_v3_WeightedCluster_ClusterWeight_weight(
1302 cluster_weight);
1303 if (weight == nullptr) {
1304 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1305 "RouteAction weighted_cluster cluster missing weight");
1306 }
1307 cluster.weight = google_protobuf_UInt32Value_value(weight);
1308 if (cluster.weight == 0) continue;
1309 sum_of_weights += cluster.weight;
1310 route->weighted_clusters.emplace_back(std::move(cluster));
1311 }
1312 if (total_weight != sum_of_weights) {
1313 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1314 "RouteAction weighted_cluster has incorrect total weight");
1315 }
1316 if (route->weighted_clusters.empty()) {
1317 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1318 "RouteAction weighted_cluster has no valid clusters specified.");
1319 }
1320 } else {
1321 // No cluster or weighted_clusters found in RouteAction, ignore this route.
1322 *ignore_route = true;
1323 }
1324 if (XdsTimeoutEnabled() && !*ignore_route) {
1325 const envoy_config_route_v3_RouteAction_MaxStreamDuration*
1326 max_stream_duration =
1327 envoy_config_route_v3_RouteAction_max_stream_duration(route_action);
1328 if (max_stream_duration != nullptr) {
1329 const google_protobuf_Duration* duration =
1330 envoy_config_route_v3_RouteAction_MaxStreamDuration_grpc_timeout_header_max(
1331 max_stream_duration);
1332 if (duration == nullptr) {
1333 duration =
1334 envoy_config_route_v3_RouteAction_MaxStreamDuration_max_stream_duration(
1335 max_stream_duration);
1336 }
1337 if (duration != nullptr) {
1338 XdsApi::Duration duration_in_route;
1339 duration_in_route.seconds = google_protobuf_Duration_seconds(duration);
1340 duration_in_route.nanos = google_protobuf_Duration_nanos(duration);
1341 route->max_stream_duration = duration_in_route;
1342 }
1343 }
1344 }
1345 return GRPC_ERROR_NONE;
1346 }
1347
RouteConfigParse(XdsClient * client,TraceFlag * tracer,upb_symtab * symtab,const envoy_config_route_v3_RouteConfiguration * route_config,XdsApi::RdsUpdate * rds_update)1348 grpc_error* RouteConfigParse(
1349 XdsClient* client, TraceFlag* tracer, upb_symtab* symtab,
1350 const envoy_config_route_v3_RouteConfiguration* route_config,
1351 XdsApi::RdsUpdate* rds_update) {
1352 MaybeLogRouteConfiguration(client, tracer, symtab, route_config);
1353 // Get the virtual hosts.
1354 size_t size;
1355 const envoy_config_route_v3_VirtualHost* const* virtual_hosts =
1356 envoy_config_route_v3_RouteConfiguration_virtual_hosts(route_config,
1357 &size);
1358 for (size_t i = 0; i < size; ++i) {
1359 rds_update->virtual_hosts.emplace_back();
1360 XdsApi::RdsUpdate::VirtualHost& vhost = rds_update->virtual_hosts.back();
1361 // Parse domains.
1362 size_t domain_size;
1363 upb_strview const* domains = envoy_config_route_v3_VirtualHost_domains(
1364 virtual_hosts[i], &domain_size);
1365 for (size_t j = 0; j < domain_size; ++j) {
1366 std::string domain_pattern = UpbStringToStdString(domains[j]);
1367 const MatchType match_type = DomainPatternMatchType(domain_pattern);
1368 if (match_type == INVALID_MATCH) {
1369 return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1370 absl::StrCat("Invalid domain pattern \"", domain_pattern, "\".")
1371 .c_str());
1372 }
1373 vhost.domains.emplace_back(std::move(domain_pattern));
1374 }
1375 if (vhost.domains.empty()) {
1376 return GRPC_ERROR_CREATE_FROM_STATIC_STRING("VirtualHost has no domains");
1377 }
1378 // Parse routes.
1379 size_t num_routes;
1380 const envoy_config_route_v3_Route* const* routes =
1381 envoy_config_route_v3_VirtualHost_routes(virtual_hosts[i], &num_routes);
1382 if (num_routes < 1) {
1383 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1384 "No route found in the virtual host.");
1385 }
1386 // Loop over the whole list of routes
1387 for (size_t j = 0; j < num_routes; ++j) {
1388 const envoy_config_route_v3_RouteMatch* match =
1389 envoy_config_route_v3_Route_match(routes[j]);
1390 size_t query_parameters_size;
1391 static_cast<void>(envoy_config_route_v3_RouteMatch_query_parameters(
1392 match, &query_parameters_size));
1393 if (query_parameters_size > 0) {
1394 continue;
1395 }
1396 XdsApi::Route route;
1397 bool ignore_route = false;
1398 grpc_error* error = RoutePathMatchParse(match, &route, &ignore_route);
1399 if (error != GRPC_ERROR_NONE) return error;
1400 if (ignore_route) continue;
1401 error = RouteHeaderMatchersParse(match, &route);
1402 if (error != GRPC_ERROR_NONE) return error;
1403 error = RouteRuntimeFractionParse(match, &route);
1404 if (error != GRPC_ERROR_NONE) return error;
1405 error = RouteActionParse(routes[j], &route, &ignore_route);
1406 if (error != GRPC_ERROR_NONE) return error;
1407 if (ignore_route) continue;
1408 vhost.routes.emplace_back(std::move(route));
1409 }
1410 if (vhost.routes.empty()) {
1411 return GRPC_ERROR_CREATE_FROM_STATIC_STRING("No valid routes specified.");
1412 }
1413 }
1414 return GRPC_ERROR_NONE;
1415 }
1416
LdsResponseParse(XdsClient * client,TraceFlag * tracer,upb_symtab * symtab,const envoy_service_discovery_v3_DiscoveryResponse * response,const std::set<absl::string_view> & expected_listener_names,XdsApi::LdsUpdateMap * lds_update_map,upb_arena * arena)1417 grpc_error* LdsResponseParse(
1418 XdsClient* client, TraceFlag* tracer, upb_symtab* symtab,
1419 const envoy_service_discovery_v3_DiscoveryResponse* response,
1420 const std::set<absl::string_view>& expected_listener_names,
1421 XdsApi::LdsUpdateMap* lds_update_map, upb_arena* arena) {
1422 // Get the resources from the response.
1423 size_t size;
1424 const google_protobuf_Any* const* resources =
1425 envoy_service_discovery_v3_DiscoveryResponse_resources(response, &size);
1426 for (size_t i = 0; i < size; ++i) {
1427 // Check the type_url of the resource.
1428 absl::string_view type_url =
1429 UpbStringToAbsl(google_protobuf_Any_type_url(resources[i]));
1430 if (!IsLds(type_url)) {
1431 return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Resource is not LDS.");
1432 }
1433 // Decode the listener.
1434 const upb_strview encoded_listener =
1435 google_protobuf_Any_value(resources[i]);
1436 const envoy_config_listener_v3_Listener* listener =
1437 envoy_config_listener_v3_Listener_parse(encoded_listener.data,
1438 encoded_listener.size, arena);
1439 if (listener == nullptr) {
1440 return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Can't decode listener.");
1441 }
1442 // Check listener name. Ignore unexpected listeners.
1443 std::string listener_name =
1444 UpbStringToStdString(envoy_config_listener_v3_Listener_name(listener));
1445 if (expected_listener_names.find(listener_name) ==
1446 expected_listener_names.end()) {
1447 continue;
1448 }
1449 // Fail if listener name is duplicated.
1450 if (lds_update_map->find(listener_name) != lds_update_map->end()) {
1451 return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1452 absl::StrCat("duplicate listener name \"", listener_name, "\"")
1453 .c_str());
1454 }
1455 XdsApi::LdsUpdate& lds_update = (*lds_update_map)[listener_name];
1456 // Get api_listener and decode it to http_connection_manager.
1457 const envoy_config_listener_v3_ApiListener* api_listener =
1458 envoy_config_listener_v3_Listener_api_listener(listener);
1459 if (api_listener == nullptr) {
1460 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1461 "Listener has no ApiListener.");
1462 }
1463 const upb_strview encoded_api_listener = google_protobuf_Any_value(
1464 envoy_config_listener_v3_ApiListener_api_listener(api_listener));
1465 const envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager*
1466 http_connection_manager =
1467 envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_parse(
1468 encoded_api_listener.data, encoded_api_listener.size, arena);
1469 if (http_connection_manager == nullptr) {
1470 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1471 "Could not parse HttpConnectionManager config from ApiListener");
1472 }
1473 if (XdsTimeoutEnabled()) {
1474 // Obtain max_stream_duration from Http Protocol Options.
1475 const envoy_config_core_v3_HttpProtocolOptions* options =
1476 envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_common_http_protocol_options(
1477 http_connection_manager);
1478 if (options != nullptr) {
1479 const google_protobuf_Duration* duration =
1480 envoy_config_core_v3_HttpProtocolOptions_max_stream_duration(
1481 options);
1482 if (duration != nullptr) {
1483 lds_update.http_max_stream_duration.seconds =
1484 google_protobuf_Duration_seconds(duration);
1485 lds_update.http_max_stream_duration.nanos =
1486 google_protobuf_Duration_nanos(duration);
1487 }
1488 }
1489 }
1490 // Found inlined route_config. Parse it to find the cluster_name.
1491 if (envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_has_route_config(
1492 http_connection_manager)) {
1493 const envoy_config_route_v3_RouteConfiguration* route_config =
1494 envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_route_config(
1495 http_connection_manager);
1496 XdsApi::RdsUpdate rds_update;
1497 grpc_error* error =
1498 RouteConfigParse(client, tracer, symtab, route_config, &rds_update);
1499 if (error != GRPC_ERROR_NONE) return error;
1500 lds_update.rds_update = std::move(rds_update);
1501 continue;
1502 }
1503 // Validate that RDS must be used to get the route_config dynamically.
1504 if (!envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_has_rds(
1505 http_connection_manager)) {
1506 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1507 "HttpConnectionManager neither has inlined route_config nor RDS.");
1508 }
1509 const envoy_extensions_filters_network_http_connection_manager_v3_Rds* rds =
1510 envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_rds(
1511 http_connection_manager);
1512 // Check that the ConfigSource specifies ADS.
1513 const envoy_config_core_v3_ConfigSource* config_source =
1514 envoy_extensions_filters_network_http_connection_manager_v3_Rds_config_source(
1515 rds);
1516 if (config_source == nullptr) {
1517 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1518 "HttpConnectionManager missing config_source for RDS.");
1519 }
1520 if (!envoy_config_core_v3_ConfigSource_has_ads(config_source)) {
1521 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1522 "HttpConnectionManager ConfigSource for RDS does not specify ADS.");
1523 }
1524 // Get the route_config_name.
1525 lds_update.route_config_name = UpbStringToStdString(
1526 envoy_extensions_filters_network_http_connection_manager_v3_Rds_route_config_name(
1527 rds));
1528 }
1529 return GRPC_ERROR_NONE;
1530 }
1531
RdsResponseParse(XdsClient * client,TraceFlag * tracer,upb_symtab * symtab,const envoy_service_discovery_v3_DiscoveryResponse * response,const std::set<absl::string_view> & expected_route_configuration_names,XdsApi::RdsUpdateMap * rds_update_map,upb_arena * arena)1532 grpc_error* RdsResponseParse(
1533 XdsClient* client, TraceFlag* tracer, upb_symtab* symtab,
1534 const envoy_service_discovery_v3_DiscoveryResponse* response,
1535 const std::set<absl::string_view>& expected_route_configuration_names,
1536 XdsApi::RdsUpdateMap* rds_update_map, upb_arena* arena) {
1537 // Get the resources from the response.
1538 size_t size;
1539 const google_protobuf_Any* const* resources =
1540 envoy_service_discovery_v3_DiscoveryResponse_resources(response, &size);
1541 for (size_t i = 0; i < size; ++i) {
1542 // Check the type_url of the resource.
1543 absl::string_view type_url =
1544 UpbStringToAbsl(google_protobuf_Any_type_url(resources[i]));
1545 if (!IsRds(type_url)) {
1546 return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Resource is not RDS.");
1547 }
1548 // Decode the route_config.
1549 const upb_strview encoded_route_config =
1550 google_protobuf_Any_value(resources[i]);
1551 const envoy_config_route_v3_RouteConfiguration* route_config =
1552 envoy_config_route_v3_RouteConfiguration_parse(
1553 encoded_route_config.data, encoded_route_config.size, arena);
1554 if (route_config == nullptr) {
1555 return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Can't decode route_config.");
1556 }
1557 // Check route_config_name. Ignore unexpected route_config.
1558 std::string route_config_name = UpbStringToStdString(
1559 envoy_config_route_v3_RouteConfiguration_name(route_config));
1560 if (expected_route_configuration_names.find(route_config_name) ==
1561 expected_route_configuration_names.end()) {
1562 continue;
1563 }
1564 // Fail if route config name is duplicated.
1565 if (rds_update_map->find(route_config_name) != rds_update_map->end()) {
1566 return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1567 absl::StrCat("duplicate route config name \"", route_config_name,
1568 "\"")
1569 .c_str());
1570 }
1571 // Parse the route_config.
1572 XdsApi::RdsUpdate& rds_update =
1573 (*rds_update_map)[std::move(route_config_name)];
1574 grpc_error* error =
1575 RouteConfigParse(client, tracer, symtab, route_config, &rds_update);
1576 if (error != GRPC_ERROR_NONE) return error;
1577 }
1578 return GRPC_ERROR_NONE;
1579 }
1580
1581 XdsApi::CommonTlsContext::CertificateProviderInstance
CertificateProviderInstanceParse(const envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CertificateProviderInstance * certificate_provider_instance_proto)1582 CertificateProviderInstanceParse(
1583 const envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CertificateProviderInstance*
1584 certificate_provider_instance_proto) {
1585 return {
1586 UpbStringToStdString(
1587 envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CertificateProviderInstance_instance_name(
1588 certificate_provider_instance_proto)),
1589 UpbStringToStdString(
1590 envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CertificateProviderInstance_certificate_name(
1591 certificate_provider_instance_proto))};
1592 }
1593
1594 grpc_error* CommonTlsContextParse(
1595 const envoy_extensions_transport_sockets_tls_v3_CommonTlsContext*
1596 common_tls_context_proto,
1597 XdsApi::CommonTlsContext* common_tls_context) GRPC_MUST_USE_RESULT;
CommonTlsContextParse(const envoy_extensions_transport_sockets_tls_v3_CommonTlsContext * common_tls_context_proto,XdsApi::CommonTlsContext * common_tls_context)1598 grpc_error* CommonTlsContextParse(
1599 const envoy_extensions_transport_sockets_tls_v3_CommonTlsContext*
1600 common_tls_context_proto,
1601 XdsApi::CommonTlsContext* common_tls_context) {
1602 auto* combined_validation_context =
1603 envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_combined_validation_context(
1604 common_tls_context_proto);
1605 if (combined_validation_context != nullptr) {
1606 auto* default_validation_context =
1607 envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CombinedCertificateValidationContext_default_validation_context(
1608 combined_validation_context);
1609 if (default_validation_context != nullptr) {
1610 size_t len = 0;
1611 auto* subject_alt_names_matchers =
1612 envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext_match_subject_alt_names(
1613 default_validation_context, &len);
1614 for (size_t i = 0; i < len; ++i) {
1615 XdsApi::StringMatcher::StringMatcherType type;
1616 std::string matcher;
1617 if (envoy_type_matcher_v3_StringMatcher_has_exact(
1618 subject_alt_names_matchers[i])) {
1619 type = XdsApi::StringMatcher::StringMatcherType::EXACT;
1620 matcher =
1621 UpbStringToStdString(envoy_type_matcher_v3_StringMatcher_exact(
1622 subject_alt_names_matchers[i]));
1623 } else if (envoy_type_matcher_v3_StringMatcher_has_prefix(
1624 subject_alt_names_matchers[i])) {
1625 type = XdsApi::StringMatcher::StringMatcherType::PREFIX;
1626 matcher =
1627 UpbStringToStdString(envoy_type_matcher_v3_StringMatcher_prefix(
1628 subject_alt_names_matchers[i]));
1629 } else if (envoy_type_matcher_v3_StringMatcher_has_suffix(
1630 subject_alt_names_matchers[i])) {
1631 type = XdsApi::StringMatcher::StringMatcherType::SUFFIX;
1632 matcher =
1633 UpbStringToStdString(envoy_type_matcher_v3_StringMatcher_suffix(
1634 subject_alt_names_matchers[i]));
1635 } else if (envoy_type_matcher_v3_StringMatcher_has_contains(
1636 subject_alt_names_matchers[i])) {
1637 type = XdsApi::StringMatcher::StringMatcherType::CONTAINS;
1638 matcher =
1639 UpbStringToStdString(envoy_type_matcher_v3_StringMatcher_contains(
1640 subject_alt_names_matchers[i]));
1641 } else if (envoy_type_matcher_v3_StringMatcher_has_safe_regex(
1642 subject_alt_names_matchers[i])) {
1643 type = XdsApi::StringMatcher::StringMatcherType::SAFE_REGEX;
1644 auto* regex_matcher = envoy_type_matcher_v3_StringMatcher_safe_regex(
1645 subject_alt_names_matchers[i]);
1646 matcher = UpbStringToStdString(
1647 envoy_type_matcher_v3_RegexMatcher_regex(regex_matcher));
1648 } else {
1649 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1650 "Invalid StringMatcher specified");
1651 }
1652 bool ignore_case = envoy_type_matcher_v3_StringMatcher_ignore_case(
1653 subject_alt_names_matchers[i]);
1654 XdsApi::StringMatcher string_matcher(type, matcher, ignore_case);
1655 if (type == XdsApi::StringMatcher::StringMatcherType::SAFE_REGEX) {
1656 if (!string_matcher.regex_matcher()->ok()) {
1657 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1658 "Invalid regex string specified in string matcher.");
1659 }
1660 if (ignore_case) {
1661 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1662 "StringMatcher: ignore_case has no effect for SAFE_REGEX.");
1663 }
1664 }
1665 common_tls_context->combined_validation_context
1666 .default_validation_context.match_subject_alt_names.push_back(
1667 std::move(string_matcher));
1668 }
1669 }
1670 auto* validation_context_certificate_provider_instance =
1671 envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CombinedCertificateValidationContext_validation_context_certificate_provider_instance(
1672 combined_validation_context);
1673 if (validation_context_certificate_provider_instance != nullptr) {
1674 common_tls_context->combined_validation_context
1675 .validation_context_certificate_provider_instance =
1676 CertificateProviderInstanceParse(
1677 validation_context_certificate_provider_instance);
1678 }
1679 }
1680 auto* tls_certificate_certificate_provider_instance =
1681 envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_tls_certificate_certificate_provider_instance(
1682 common_tls_context_proto);
1683 if (tls_certificate_certificate_provider_instance != nullptr) {
1684 common_tls_context->tls_certificate_certificate_provider_instance =
1685 CertificateProviderInstanceParse(
1686 tls_certificate_certificate_provider_instance);
1687 }
1688 return GRPC_ERROR_NONE;
1689 }
1690
CdsResponseParse(XdsClient * client,TraceFlag * tracer,upb_symtab * symtab,const envoy_service_discovery_v3_DiscoveryResponse * response,const std::set<absl::string_view> & expected_cluster_names,XdsApi::CdsUpdateMap * cds_update_map,upb_arena * arena)1691 grpc_error* CdsResponseParse(
1692 XdsClient* client, TraceFlag* tracer, upb_symtab* symtab,
1693 const envoy_service_discovery_v3_DiscoveryResponse* response,
1694 const std::set<absl::string_view>& expected_cluster_names,
1695 XdsApi::CdsUpdateMap* cds_update_map, upb_arena* arena) {
1696 // Get the resources from the response.
1697 size_t size;
1698 const google_protobuf_Any* const* resources =
1699 envoy_service_discovery_v3_DiscoveryResponse_resources(response, &size);
1700 // Parse all the resources in the CDS response.
1701 for (size_t i = 0; i < size; ++i) {
1702 // Check the type_url of the resource.
1703 absl::string_view type_url =
1704 UpbStringToAbsl(google_protobuf_Any_type_url(resources[i]));
1705 if (!IsCds(type_url)) {
1706 return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Resource is not CDS.");
1707 }
1708 // Decode the cluster.
1709 const upb_strview encoded_cluster = google_protobuf_Any_value(resources[i]);
1710 const envoy_config_cluster_v3_Cluster* cluster =
1711 envoy_config_cluster_v3_Cluster_parse(encoded_cluster.data,
1712 encoded_cluster.size, arena);
1713 if (cluster == nullptr) {
1714 return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Can't decode cluster.");
1715 }
1716 MaybeLogCluster(client, tracer, symtab, cluster);
1717 // Ignore unexpected cluster names.
1718 std::string cluster_name =
1719 UpbStringToStdString(envoy_config_cluster_v3_Cluster_name(cluster));
1720 if (expected_cluster_names.find(cluster_name) ==
1721 expected_cluster_names.end()) {
1722 continue;
1723 }
1724 // Fail on duplicate resources.
1725 if (cds_update_map->find(cluster_name) != cds_update_map->end()) {
1726 return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1727 absl::StrCat("duplicate resource name \"", cluster_name, "\"")
1728 .c_str());
1729 }
1730 XdsApi::CdsUpdate& cds_update = (*cds_update_map)[std::move(cluster_name)];
1731 // Check the cluster_discovery_type.
1732 if (!envoy_config_cluster_v3_Cluster_has_type(cluster)) {
1733 return GRPC_ERROR_CREATE_FROM_STATIC_STRING("DiscoveryType not found.");
1734 }
1735 if (envoy_config_cluster_v3_Cluster_type(cluster) !=
1736 envoy_config_cluster_v3_Cluster_EDS) {
1737 return GRPC_ERROR_CREATE_FROM_STATIC_STRING("DiscoveryType is not EDS.");
1738 }
1739 // Check the EDS config source.
1740 const envoy_config_cluster_v3_Cluster_EdsClusterConfig* eds_cluster_config =
1741 envoy_config_cluster_v3_Cluster_eds_cluster_config(cluster);
1742 const envoy_config_core_v3_ConfigSource* eds_config =
1743 envoy_config_cluster_v3_Cluster_EdsClusterConfig_eds_config(
1744 eds_cluster_config);
1745 if (!envoy_config_core_v3_ConfigSource_has_ads(eds_config)) {
1746 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1747 "EDS ConfigSource is not ADS.");
1748 }
1749 // Record EDS service_name (if any).
1750 upb_strview service_name =
1751 envoy_config_cluster_v3_Cluster_EdsClusterConfig_service_name(
1752 eds_cluster_config);
1753 if (service_name.size != 0) {
1754 cds_update.eds_service_name = UpbStringToStdString(service_name);
1755 }
1756 // Check the LB policy.
1757 if (envoy_config_cluster_v3_Cluster_lb_policy(cluster) !=
1758 envoy_config_cluster_v3_Cluster_ROUND_ROBIN) {
1759 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1760 "LB policy is not ROUND_ROBIN.");
1761 }
1762 if (XdsSecurityEnabled()) {
1763 // Record Upstream tls context
1764 auto* transport_socket =
1765 envoy_config_cluster_v3_Cluster_transport_socket(cluster);
1766 if (transport_socket != nullptr) {
1767 absl::string_view name = UpbStringToAbsl(
1768 envoy_config_core_v3_TransportSocket_name(transport_socket));
1769 if (name == "envoy.transport_sockets.tls") {
1770 auto* typed_config =
1771 envoy_config_core_v3_TransportSocket_typed_config(
1772 transport_socket);
1773 if (typed_config != nullptr) {
1774 const upb_strview encoded_upstream_tls_context =
1775 google_protobuf_Any_value(typed_config);
1776 auto* upstream_tls_context =
1777 envoy_extensions_transport_sockets_tls_v3_UpstreamTlsContext_parse(
1778 encoded_upstream_tls_context.data,
1779 encoded_upstream_tls_context.size, arena);
1780 if (upstream_tls_context == nullptr) {
1781 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1782 "Can't decode upstream tls context.");
1783 }
1784 auto* common_tls_context =
1785 envoy_extensions_transport_sockets_tls_v3_UpstreamTlsContext_common_tls_context(
1786 upstream_tls_context);
1787 if (common_tls_context != nullptr) {
1788 grpc_error* error = CommonTlsContextParse(
1789 common_tls_context, &cds_update.common_tls_context);
1790 if (error != GRPC_ERROR_NONE) return error;
1791 }
1792 }
1793 if (cds_update.common_tls_context.combined_validation_context
1794 .validation_context_certificate_provider_instance
1795 .instance_name.empty()) {
1796 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1797 "TLS configuration provided but no "
1798 "validation_context_certificate_provider_instance found.");
1799 }
1800 }
1801 }
1802 }
1803 // Record LRS server name (if any).
1804 const envoy_config_core_v3_ConfigSource* lrs_server =
1805 envoy_config_cluster_v3_Cluster_lrs_server(cluster);
1806 if (lrs_server != nullptr) {
1807 if (!envoy_config_core_v3_ConfigSource_has_self(lrs_server)) {
1808 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1809 "LRS ConfigSource is not self.");
1810 }
1811 cds_update.lrs_load_reporting_server_name.emplace("");
1812 }
1813 // The Cluster resource encodes the circuit breaking parameters in a list of
1814 // Thresholds messages, where each message specifies the parameters for a
1815 // particular RoutingPriority. we will look only at the first entry in the
1816 // list for priority DEFAULT and default to 1024 if not found.
1817 if (envoy_config_cluster_v3_Cluster_has_circuit_breakers(cluster)) {
1818 const envoy_config_cluster_v3_CircuitBreakers* circuit_breakers =
1819 envoy_config_cluster_v3_Cluster_circuit_breakers(cluster);
1820 size_t num_thresholds;
1821 const envoy_config_cluster_v3_CircuitBreakers_Thresholds* const*
1822 thresholds = envoy_config_cluster_v3_CircuitBreakers_thresholds(
1823 circuit_breakers, &num_thresholds);
1824 for (size_t i = 0; i < num_thresholds; ++i) {
1825 const auto* threshold = thresholds[i];
1826 if (envoy_config_cluster_v3_CircuitBreakers_Thresholds_priority(
1827 threshold) == envoy_config_core_v3_DEFAULT) {
1828 const google_protobuf_UInt32Value* max_requests =
1829 envoy_config_cluster_v3_CircuitBreakers_Thresholds_max_requests(
1830 threshold);
1831 if (max_requests != nullptr) {
1832 cds_update.max_concurrent_requests =
1833 google_protobuf_UInt32Value_value(max_requests);
1834 }
1835 break;
1836 }
1837 }
1838 }
1839 }
1840 return GRPC_ERROR_NONE;
1841 }
1842
ServerAddressParseAndAppend(const envoy_config_endpoint_v3_LbEndpoint * lb_endpoint,ServerAddressList * list)1843 grpc_error* ServerAddressParseAndAppend(
1844 const envoy_config_endpoint_v3_LbEndpoint* lb_endpoint,
1845 ServerAddressList* list) {
1846 // If health_status is not HEALTHY or UNKNOWN, skip this endpoint.
1847 const int32_t health_status =
1848 envoy_config_endpoint_v3_LbEndpoint_health_status(lb_endpoint);
1849 if (health_status != envoy_config_core_v3_UNKNOWN &&
1850 health_status != envoy_config_core_v3_HEALTHY) {
1851 return GRPC_ERROR_NONE;
1852 }
1853 // Find the ip:port.
1854 const envoy_config_endpoint_v3_Endpoint* endpoint =
1855 envoy_config_endpoint_v3_LbEndpoint_endpoint(lb_endpoint);
1856 const envoy_config_core_v3_Address* address =
1857 envoy_config_endpoint_v3_Endpoint_address(endpoint);
1858 const envoy_config_core_v3_SocketAddress* socket_address =
1859 envoy_config_core_v3_Address_socket_address(address);
1860 std::string address_str = UpbStringToStdString(
1861 envoy_config_core_v3_SocketAddress_address(socket_address));
1862 uint32_t port = envoy_config_core_v3_SocketAddress_port_value(socket_address);
1863 if (GPR_UNLIKELY(port >> 16) != 0) {
1864 return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Invalid port.");
1865 }
1866 // Populate grpc_resolved_address.
1867 grpc_resolved_address addr;
1868 grpc_string_to_sockaddr(&addr, address_str.c_str(), port);
1869 // Append the address to the list.
1870 list->emplace_back(addr, nullptr);
1871 return GRPC_ERROR_NONE;
1872 }
1873
LocalityParse(const envoy_config_endpoint_v3_LocalityLbEndpoints * locality_lb_endpoints,XdsApi::EdsUpdate::Priority::Locality * output_locality,size_t * priority)1874 grpc_error* LocalityParse(
1875 const envoy_config_endpoint_v3_LocalityLbEndpoints* locality_lb_endpoints,
1876 XdsApi::EdsUpdate::Priority::Locality* output_locality, size_t* priority) {
1877 // Parse LB weight.
1878 const google_protobuf_UInt32Value* lb_weight =
1879 envoy_config_endpoint_v3_LocalityLbEndpoints_load_balancing_weight(
1880 locality_lb_endpoints);
1881 // If LB weight is not specified, it means this locality is assigned no load.
1882 // TODO(juanlishen): When we support CDS to configure the inter-locality
1883 // policy, we should change the LB weight handling.
1884 output_locality->lb_weight =
1885 lb_weight != nullptr ? google_protobuf_UInt32Value_value(lb_weight) : 0;
1886 if (output_locality->lb_weight == 0) return GRPC_ERROR_NONE;
1887 // Parse locality name.
1888 const envoy_config_core_v3_Locality* locality =
1889 envoy_config_endpoint_v3_LocalityLbEndpoints_locality(
1890 locality_lb_endpoints);
1891 std::string region =
1892 UpbStringToStdString(envoy_config_core_v3_Locality_region(locality));
1893 std::string zone =
1894 UpbStringToStdString(envoy_config_core_v3_Locality_region(locality));
1895 std::string sub_zone =
1896 UpbStringToStdString(envoy_config_core_v3_Locality_sub_zone(locality));
1897 output_locality->name = MakeRefCounted<XdsLocalityName>(
1898 std::move(region), std::move(zone), std::move(sub_zone));
1899 // Parse the addresses.
1900 size_t size;
1901 const envoy_config_endpoint_v3_LbEndpoint* const* lb_endpoints =
1902 envoy_config_endpoint_v3_LocalityLbEndpoints_lb_endpoints(
1903 locality_lb_endpoints, &size);
1904 for (size_t i = 0; i < size; ++i) {
1905 grpc_error* error = ServerAddressParseAndAppend(
1906 lb_endpoints[i], &output_locality->endpoints);
1907 if (error != GRPC_ERROR_NONE) return error;
1908 }
1909 // Parse the priority.
1910 *priority = envoy_config_endpoint_v3_LocalityLbEndpoints_priority(
1911 locality_lb_endpoints);
1912 return GRPC_ERROR_NONE;
1913 }
1914
DropParseAndAppend(const envoy_config_endpoint_v3_ClusterLoadAssignment_Policy_DropOverload * drop_overload,XdsApi::EdsUpdate::DropConfig * drop_config)1915 grpc_error* DropParseAndAppend(
1916 const envoy_config_endpoint_v3_ClusterLoadAssignment_Policy_DropOverload*
1917 drop_overload,
1918 XdsApi::EdsUpdate::DropConfig* drop_config) {
1919 // Get the category.
1920 std::string category = UpbStringToStdString(
1921 envoy_config_endpoint_v3_ClusterLoadAssignment_Policy_DropOverload_category(
1922 drop_overload));
1923 if (category.empty()) {
1924 return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Empty drop category name");
1925 }
1926 // Get the drop rate (per million).
1927 const envoy_type_v3_FractionalPercent* drop_percentage =
1928 envoy_config_endpoint_v3_ClusterLoadAssignment_Policy_DropOverload_drop_percentage(
1929 drop_overload);
1930 uint32_t numerator =
1931 envoy_type_v3_FractionalPercent_numerator(drop_percentage);
1932 const auto denominator =
1933 static_cast<envoy_type_v3_FractionalPercent_DenominatorType>(
1934 envoy_type_v3_FractionalPercent_denominator(drop_percentage));
1935 // Normalize to million.
1936 switch (denominator) {
1937 case envoy_type_v3_FractionalPercent_HUNDRED:
1938 numerator *= 10000;
1939 break;
1940 case envoy_type_v3_FractionalPercent_TEN_THOUSAND:
1941 numerator *= 100;
1942 break;
1943 case envoy_type_v3_FractionalPercent_MILLION:
1944 break;
1945 default:
1946 return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Unknown denominator type");
1947 }
1948 // Cap numerator to 1000000.
1949 numerator = GPR_MIN(numerator, 1000000);
1950 drop_config->AddCategory(std::move(category), numerator);
1951 return GRPC_ERROR_NONE;
1952 }
1953
EdsResponseParse(XdsClient * client,TraceFlag * tracer,upb_symtab * symtab,const envoy_service_discovery_v3_DiscoveryResponse * response,const std::set<absl::string_view> & expected_eds_service_names,XdsApi::EdsUpdateMap * eds_update_map,upb_arena * arena)1954 grpc_error* EdsResponseParse(
1955 XdsClient* client, TraceFlag* tracer, upb_symtab* symtab,
1956 const envoy_service_discovery_v3_DiscoveryResponse* response,
1957 const std::set<absl::string_view>& expected_eds_service_names,
1958 XdsApi::EdsUpdateMap* eds_update_map, upb_arena* arena) {
1959 // Get the resources from the response.
1960 size_t size;
1961 const google_protobuf_Any* const* resources =
1962 envoy_service_discovery_v3_DiscoveryResponse_resources(response, &size);
1963 for (size_t i = 0; i < size; ++i) {
1964 // Check the type_url of the resource.
1965 absl::string_view type_url =
1966 UpbStringToAbsl(google_protobuf_Any_type_url(resources[i]));
1967 if (!IsEds(type_url)) {
1968 return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Resource is not EDS.");
1969 }
1970 // Get the cluster_load_assignment.
1971 upb_strview encoded_cluster_load_assignment =
1972 google_protobuf_Any_value(resources[i]);
1973 envoy_config_endpoint_v3_ClusterLoadAssignment* cluster_load_assignment =
1974 envoy_config_endpoint_v3_ClusterLoadAssignment_parse(
1975 encoded_cluster_load_assignment.data,
1976 encoded_cluster_load_assignment.size, arena);
1977 if (cluster_load_assignment == nullptr) {
1978 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1979 "Can't parse cluster_load_assignment.");
1980 }
1981 MaybeLogClusterLoadAssignment(client, tracer, symtab,
1982 cluster_load_assignment);
1983 // Check the EDS service name. Ignore unexpected names.
1984 std::string eds_service_name = UpbStringToStdString(
1985 envoy_config_endpoint_v3_ClusterLoadAssignment_cluster_name(
1986 cluster_load_assignment));
1987 if (expected_eds_service_names.find(eds_service_name) ==
1988 expected_eds_service_names.end()) {
1989 continue;
1990 }
1991 // Fail on duplicate resources.
1992 if (eds_update_map->find(eds_service_name) != eds_update_map->end()) {
1993 return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1994 absl::StrCat("duplicate resource name \"", eds_service_name, "\"")
1995 .c_str());
1996 }
1997 XdsApi::EdsUpdate& eds_update =
1998 (*eds_update_map)[std::move(eds_service_name)];
1999 // Get the endpoints.
2000 size_t locality_size;
2001 const envoy_config_endpoint_v3_LocalityLbEndpoints* const* endpoints =
2002 envoy_config_endpoint_v3_ClusterLoadAssignment_endpoints(
2003 cluster_load_assignment, &locality_size);
2004 for (size_t j = 0; j < locality_size; ++j) {
2005 size_t priority;
2006 XdsApi::EdsUpdate::Priority::Locality locality;
2007 grpc_error* error = LocalityParse(endpoints[j], &locality, &priority);
2008 if (error != GRPC_ERROR_NONE) return error;
2009 // Filter out locality with weight 0.
2010 if (locality.lb_weight == 0) continue;
2011 // Make sure prorities is big enough. Note that they might not
2012 // arrive in priority order.
2013 while (eds_update.priorities.size() < priority + 1) {
2014 eds_update.priorities.emplace_back();
2015 }
2016 eds_update.priorities[priority].localities.emplace(locality.name.get(),
2017 std::move(locality));
2018 }
2019 for (const auto& priority : eds_update.priorities) {
2020 if (priority.localities.empty()) {
2021 return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
2022 "EDS update includes sparse priority list");
2023 }
2024 }
2025 // Get the drop config.
2026 eds_update.drop_config = MakeRefCounted<XdsApi::EdsUpdate::DropConfig>();
2027 const envoy_config_endpoint_v3_ClusterLoadAssignment_Policy* policy =
2028 envoy_config_endpoint_v3_ClusterLoadAssignment_policy(
2029 cluster_load_assignment);
2030 if (policy != nullptr) {
2031 size_t drop_size;
2032 const envoy_config_endpoint_v3_ClusterLoadAssignment_Policy_DropOverload* const*
2033 drop_overload =
2034 envoy_config_endpoint_v3_ClusterLoadAssignment_Policy_drop_overloads(
2035 policy, &drop_size);
2036 for (size_t j = 0; j < drop_size; ++j) {
2037 grpc_error* error =
2038 DropParseAndAppend(drop_overload[j], eds_update.drop_config.get());
2039 if (error != GRPC_ERROR_NONE) return error;
2040 }
2041 }
2042 }
2043 return GRPC_ERROR_NONE;
2044 }
2045
TypeUrlInternalToExternal(absl::string_view type_url)2046 std::string TypeUrlInternalToExternal(absl::string_view type_url) {
2047 if (type_url == kLdsV2TypeUrl) {
2048 return XdsApi::kLdsTypeUrl;
2049 } else if (type_url == kRdsV2TypeUrl) {
2050 return XdsApi::kRdsTypeUrl;
2051 } else if (type_url == kCdsV2TypeUrl) {
2052 return XdsApi::kCdsTypeUrl;
2053 } else if (type_url == kEdsV2TypeUrl) {
2054 return XdsApi::kEdsTypeUrl;
2055 }
2056 return std::string(type_url);
2057 }
2058
2059 } // namespace
2060
ParseAdsResponse(const grpc_slice & encoded_response,const std::set<absl::string_view> & expected_listener_names,const std::set<absl::string_view> & expected_route_configuration_names,const std::set<absl::string_view> & expected_cluster_names,const std::set<absl::string_view> & expected_eds_service_names)2061 XdsApi::AdsParseResult XdsApi::ParseAdsResponse(
2062 const grpc_slice& encoded_response,
2063 const std::set<absl::string_view>& expected_listener_names,
2064 const std::set<absl::string_view>& expected_route_configuration_names,
2065 const std::set<absl::string_view>& expected_cluster_names,
2066 const std::set<absl::string_view>& expected_eds_service_names) {
2067 AdsParseResult result;
2068 upb::Arena arena;
2069 // Decode the response.
2070 const envoy_service_discovery_v3_DiscoveryResponse* response =
2071 envoy_service_discovery_v3_DiscoveryResponse_parse(
2072 reinterpret_cast<const char*>(GRPC_SLICE_START_PTR(encoded_response)),
2073 GRPC_SLICE_LENGTH(encoded_response), arena.ptr());
2074 // If decoding fails, output an empty type_url and return.
2075 if (response == nullptr) {
2076 result.parse_error =
2077 GRPC_ERROR_CREATE_FROM_STATIC_STRING("Can't decode DiscoveryResponse.");
2078 return result;
2079 }
2080 MaybeLogDiscoveryResponse(client_, tracer_, symtab_.ptr(), response);
2081 // Record the type_url, the version_info, and the nonce of the response.
2082 result.type_url = TypeUrlInternalToExternal(UpbStringToAbsl(
2083 envoy_service_discovery_v3_DiscoveryResponse_type_url(response)));
2084 result.version = UpbStringToStdString(
2085 envoy_service_discovery_v3_DiscoveryResponse_version_info(response));
2086 result.nonce = UpbStringToStdString(
2087 envoy_service_discovery_v3_DiscoveryResponse_nonce(response));
2088 // Parse the response according to the resource type.
2089 if (IsLds(result.type_url)) {
2090 result.parse_error = LdsResponseParse(client_, tracer_, symtab_.ptr(),
2091 response, expected_listener_names,
2092 &result.lds_update_map, arena.ptr());
2093 } else if (IsRds(result.type_url)) {
2094 result.parse_error =
2095 RdsResponseParse(client_, tracer_, symtab_.ptr(), response,
2096 expected_route_configuration_names,
2097 &result.rds_update_map, arena.ptr());
2098 } else if (IsCds(result.type_url)) {
2099 result.parse_error = CdsResponseParse(client_, tracer_, symtab_.ptr(),
2100 response, expected_cluster_names,
2101 &result.cds_update_map, arena.ptr());
2102 } else if (IsEds(result.type_url)) {
2103 result.parse_error = EdsResponseParse(client_, tracer_, symtab_.ptr(),
2104 response, expected_eds_service_names,
2105 &result.eds_update_map, arena.ptr());
2106 }
2107 return result;
2108 }
2109
2110 namespace {
2111
MaybeLogLrsRequest(XdsClient * client,TraceFlag * tracer,upb_symtab * symtab,const envoy_service_load_stats_v3_LoadStatsRequest * request)2112 void MaybeLogLrsRequest(
2113 XdsClient* client, TraceFlag* tracer, upb_symtab* symtab,
2114 const envoy_service_load_stats_v3_LoadStatsRequest* request) {
2115 if (GRPC_TRACE_FLAG_ENABLED(*tracer) &&
2116 gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
2117 const upb_msgdef* msg_type =
2118 envoy_service_load_stats_v3_LoadStatsRequest_getmsgdef(symtab);
2119 char buf[10240];
2120 upb_text_encode(request, msg_type, nullptr, 0, buf, sizeof(buf));
2121 gpr_log(GPR_DEBUG, "[xds_client %p] constructed LRS request: %s", client,
2122 buf);
2123 }
2124 }
2125
SerializeLrsRequest(const envoy_service_load_stats_v3_LoadStatsRequest * request,upb_arena * arena)2126 grpc_slice SerializeLrsRequest(
2127 const envoy_service_load_stats_v3_LoadStatsRequest* request,
2128 upb_arena* arena) {
2129 size_t output_length;
2130 char* output = envoy_service_load_stats_v3_LoadStatsRequest_serialize(
2131 request, arena, &output_length);
2132 return grpc_slice_from_copied_buffer(output, output_length);
2133 }
2134
2135 } // namespace
2136
CreateLrsInitialRequest(const XdsBootstrap::XdsServer & server)2137 grpc_slice XdsApi::CreateLrsInitialRequest(
2138 const XdsBootstrap::XdsServer& server) {
2139 upb::Arena arena;
2140 // Create a request.
2141 envoy_service_load_stats_v3_LoadStatsRequest* request =
2142 envoy_service_load_stats_v3_LoadStatsRequest_new(arena.ptr());
2143 // Populate node.
2144 envoy_config_core_v3_Node* node_msg =
2145 envoy_service_load_stats_v3_LoadStatsRequest_mutable_node(request,
2146 arena.ptr());
2147 PopulateNode(arena.ptr(), node_, server.ShouldUseV3(), build_version_,
2148 user_agent_name_, node_msg);
2149 envoy_config_core_v3_Node_add_client_features(
2150 node_msg, upb_strview_makez("envoy.lrs.supports_send_all_clusters"),
2151 arena.ptr());
2152 MaybeLogLrsRequest(client_, tracer_, symtab_.ptr(), request);
2153 return SerializeLrsRequest(request, arena.ptr());
2154 }
2155
2156 namespace {
2157
LocalityStatsPopulate(envoy_config_endpoint_v3_UpstreamLocalityStats * output,const XdsLocalityName & locality_name,const XdsClusterLocalityStats::Snapshot & snapshot,upb_arena * arena)2158 void LocalityStatsPopulate(
2159 envoy_config_endpoint_v3_UpstreamLocalityStats* output,
2160 const XdsLocalityName& locality_name,
2161 const XdsClusterLocalityStats::Snapshot& snapshot, upb_arena* arena) {
2162 // Set locality.
2163 envoy_config_core_v3_Locality* locality =
2164 envoy_config_endpoint_v3_UpstreamLocalityStats_mutable_locality(output,
2165 arena);
2166 if (!locality_name.region().empty()) {
2167 envoy_config_core_v3_Locality_set_region(
2168 locality, StdStringToUpbString(locality_name.region()));
2169 }
2170 if (!locality_name.zone().empty()) {
2171 envoy_config_core_v3_Locality_set_zone(
2172 locality, StdStringToUpbString(locality_name.zone()));
2173 }
2174 if (!locality_name.sub_zone().empty()) {
2175 envoy_config_core_v3_Locality_set_sub_zone(
2176 locality, StdStringToUpbString(locality_name.sub_zone()));
2177 }
2178 // Set total counts.
2179 envoy_config_endpoint_v3_UpstreamLocalityStats_set_total_successful_requests(
2180 output, snapshot.total_successful_requests);
2181 envoy_config_endpoint_v3_UpstreamLocalityStats_set_total_requests_in_progress(
2182 output, snapshot.total_requests_in_progress);
2183 envoy_config_endpoint_v3_UpstreamLocalityStats_set_total_error_requests(
2184 output, snapshot.total_error_requests);
2185 envoy_config_endpoint_v3_UpstreamLocalityStats_set_total_issued_requests(
2186 output, snapshot.total_issued_requests);
2187 // Add backend metrics.
2188 for (const auto& p : snapshot.backend_metrics) {
2189 const std::string& metric_name = p.first;
2190 const XdsClusterLocalityStats::BackendMetric& metric_value = p.second;
2191 envoy_config_endpoint_v3_EndpointLoadMetricStats* load_metric =
2192 envoy_config_endpoint_v3_UpstreamLocalityStats_add_load_metric_stats(
2193 output, arena);
2194 envoy_config_endpoint_v3_EndpointLoadMetricStats_set_metric_name(
2195 load_metric, StdStringToUpbString(metric_name));
2196 envoy_config_endpoint_v3_EndpointLoadMetricStats_set_num_requests_finished_with_metric(
2197 load_metric, metric_value.num_requests_finished_with_metric);
2198 envoy_config_endpoint_v3_EndpointLoadMetricStats_set_total_metric_value(
2199 load_metric, metric_value.total_metric_value);
2200 }
2201 }
2202
2203 } // namespace
2204
CreateLrsRequest(ClusterLoadReportMap cluster_load_report_map)2205 grpc_slice XdsApi::CreateLrsRequest(
2206 ClusterLoadReportMap cluster_load_report_map) {
2207 upb::Arena arena;
2208 // Create a request.
2209 envoy_service_load_stats_v3_LoadStatsRequest* request =
2210 envoy_service_load_stats_v3_LoadStatsRequest_new(arena.ptr());
2211 for (auto& p : cluster_load_report_map) {
2212 const std::string& cluster_name = p.first.first;
2213 const std::string& eds_service_name = p.first.second;
2214 const ClusterLoadReport& load_report = p.second;
2215 // Add cluster stats.
2216 envoy_config_endpoint_v3_ClusterStats* cluster_stats =
2217 envoy_service_load_stats_v3_LoadStatsRequest_add_cluster_stats(
2218 request, arena.ptr());
2219 // Set the cluster name.
2220 envoy_config_endpoint_v3_ClusterStats_set_cluster_name(
2221 cluster_stats, StdStringToUpbString(cluster_name));
2222 // Set EDS service name, if non-empty.
2223 if (!eds_service_name.empty()) {
2224 envoy_config_endpoint_v3_ClusterStats_set_cluster_service_name(
2225 cluster_stats, StdStringToUpbString(eds_service_name));
2226 }
2227 // Add locality stats.
2228 for (const auto& p : load_report.locality_stats) {
2229 const XdsLocalityName& locality_name = *p.first;
2230 const auto& snapshot = p.second;
2231 envoy_config_endpoint_v3_UpstreamLocalityStats* locality_stats =
2232 envoy_config_endpoint_v3_ClusterStats_add_upstream_locality_stats(
2233 cluster_stats, arena.ptr());
2234 LocalityStatsPopulate(locality_stats, locality_name, snapshot,
2235 arena.ptr());
2236 }
2237 // Add dropped requests.
2238 uint64_t total_dropped_requests = 0;
2239 for (const auto& p : load_report.dropped_requests.categorized_drops) {
2240 const std::string& category = p.first;
2241 const uint64_t count = p.second;
2242 envoy_config_endpoint_v3_ClusterStats_DroppedRequests* dropped_requests =
2243 envoy_config_endpoint_v3_ClusterStats_add_dropped_requests(
2244 cluster_stats, arena.ptr());
2245 envoy_config_endpoint_v3_ClusterStats_DroppedRequests_set_category(
2246 dropped_requests, StdStringToUpbString(category));
2247 envoy_config_endpoint_v3_ClusterStats_DroppedRequests_set_dropped_count(
2248 dropped_requests, count);
2249 total_dropped_requests += count;
2250 }
2251 total_dropped_requests += load_report.dropped_requests.uncategorized_drops;
2252 // Set total dropped requests.
2253 envoy_config_endpoint_v3_ClusterStats_set_total_dropped_requests(
2254 cluster_stats, total_dropped_requests);
2255 // Set real load report interval.
2256 gpr_timespec timespec =
2257 grpc_millis_to_timespec(load_report.load_report_interval, GPR_TIMESPAN);
2258 google_protobuf_Duration* load_report_interval =
2259 envoy_config_endpoint_v3_ClusterStats_mutable_load_report_interval(
2260 cluster_stats, arena.ptr());
2261 google_protobuf_Duration_set_seconds(load_report_interval, timespec.tv_sec);
2262 google_protobuf_Duration_set_nanos(load_report_interval, timespec.tv_nsec);
2263 }
2264 MaybeLogLrsRequest(client_, tracer_, symtab_.ptr(), request);
2265 return SerializeLrsRequest(request, arena.ptr());
2266 }
2267
ParseLrsResponse(const grpc_slice & encoded_response,bool * send_all_clusters,std::set<std::string> * cluster_names,grpc_millis * load_reporting_interval)2268 grpc_error* XdsApi::ParseLrsResponse(const grpc_slice& encoded_response,
2269 bool* send_all_clusters,
2270 std::set<std::string>* cluster_names,
2271 grpc_millis* load_reporting_interval) {
2272 upb::Arena arena;
2273 // Decode the response.
2274 const envoy_service_load_stats_v3_LoadStatsResponse* decoded_response =
2275 envoy_service_load_stats_v3_LoadStatsResponse_parse(
2276 reinterpret_cast<const char*>(GRPC_SLICE_START_PTR(encoded_response)),
2277 GRPC_SLICE_LENGTH(encoded_response), arena.ptr());
2278 // Parse the response.
2279 if (decoded_response == nullptr) {
2280 return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Can't decode response.");
2281 }
2282 // Check send_all_clusters.
2283 if (envoy_service_load_stats_v3_LoadStatsResponse_send_all_clusters(
2284 decoded_response)) {
2285 *send_all_clusters = true;
2286 } else {
2287 // Store the cluster names.
2288 size_t size;
2289 const upb_strview* clusters =
2290 envoy_service_load_stats_v3_LoadStatsResponse_clusters(decoded_response,
2291 &size);
2292 for (size_t i = 0; i < size; ++i) {
2293 cluster_names->emplace(UpbStringToStdString(clusters[i]));
2294 }
2295 }
2296 // Get the load report interval.
2297 const google_protobuf_Duration* load_reporting_interval_duration =
2298 envoy_service_load_stats_v3_LoadStatsResponse_load_reporting_interval(
2299 decoded_response);
2300 gpr_timespec timespec{
2301 google_protobuf_Duration_seconds(load_reporting_interval_duration),
2302 google_protobuf_Duration_nanos(load_reporting_interval_duration),
2303 GPR_TIMESPAN};
2304 *load_reporting_interval = gpr_time_to_millis(timespec);
2305 return GRPC_ERROR_NONE;
2306 }
2307
2308 } // namespace grpc_core
2309