|  | @@ -74,11 +74,10 @@
 | 
	
		
			
				|  |  |  namespace grpc_core {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  //
 | 
	
		
			
				|  |  | -// XdsApi::RdsUpdate::RdsRoute::Matchers::PathMatcher
 | 
	
		
			
				|  |  | +// XdsApi::Route::Matchers::PathMatcher
 | 
	
		
			
				|  |  |  //
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -XdsApi::RdsUpdate::RdsRoute::Matchers::PathMatcher::PathMatcher(
 | 
	
		
			
				|  |  | -    const PathMatcher& other)
 | 
	
		
			
				|  |  | +XdsApi::Route::Matchers::PathMatcher::PathMatcher(const PathMatcher& other)
 | 
	
		
			
				|  |  |      : type(other.type) {
 | 
	
		
			
				|  |  |    if (type == PathMatcherType::REGEX) {
 | 
	
		
			
				|  |  |      regex_matcher = absl::make_unique<RE2>(other.regex_matcher->pattern());
 | 
	
	
		
			
				|  | @@ -87,9 +86,8 @@ XdsApi::RdsUpdate::RdsRoute::Matchers::PathMatcher::PathMatcher(
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -XdsApi::RdsUpdate::RdsRoute::Matchers::PathMatcher&
 | 
	
		
			
				|  |  | -XdsApi::RdsUpdate::RdsRoute::Matchers::PathMatcher::operator=(
 | 
	
		
			
				|  |  | -    const PathMatcher& other) {
 | 
	
		
			
				|  |  | +XdsApi::Route::Matchers::PathMatcher& XdsApi::Route::Matchers::PathMatcher::
 | 
	
		
			
				|  |  | +operator=(const PathMatcher& other) {
 | 
	
		
			
				|  |  |    type = other.type;
 | 
	
		
			
				|  |  |    if (type == PathMatcherType::REGEX) {
 | 
	
		
			
				|  |  |      regex_matcher = absl::make_unique<RE2>(other.regex_matcher->pattern());
 | 
	
	
		
			
				|  | @@ -99,7 +97,7 @@ XdsApi::RdsUpdate::RdsRoute::Matchers::PathMatcher::operator=(
 | 
	
		
			
				|  |  |    return *this;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -bool XdsApi::RdsUpdate::RdsRoute::Matchers::PathMatcher::operator==(
 | 
	
		
			
				|  |  | +bool XdsApi::Route::Matchers::PathMatcher::operator==(
 | 
	
		
			
				|  |  |      const PathMatcher& other) const {
 | 
	
		
			
				|  |  |    if (type != other.type) return false;
 | 
	
		
			
				|  |  |    if (type == PathMatcherType::REGEX) {
 | 
	
	
		
			
				|  | @@ -112,8 +110,7 @@ bool XdsApi::RdsUpdate::RdsRoute::Matchers::PathMatcher::operator==(
 | 
	
		
			
				|  |  |    return string_matcher == other.string_matcher;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -std::string XdsApi::RdsUpdate::RdsRoute::Matchers::PathMatcher::ToString()
 | 
	
		
			
				|  |  | -    const {
 | 
	
		
			
				|  |  | +std::string XdsApi::Route::Matchers::PathMatcher::ToString() const {
 | 
	
		
			
				|  |  |    std::string path_type_string;
 | 
	
		
			
				|  |  |    switch (type) {
 | 
	
		
			
				|  |  |      case PathMatcherType::PATH:
 | 
	
	
		
			
				|  | @@ -135,10 +132,10 @@ std::string XdsApi::RdsUpdate::RdsRoute::Matchers::PathMatcher::ToString()
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  //
 | 
	
		
			
				|  |  | -// XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher
 | 
	
		
			
				|  |  | +// XdsApi::Route::Matchers::HeaderMatcher
 | 
	
		
			
				|  |  |  //
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher::HeaderMatcher(
 | 
	
		
			
				|  |  | +XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcher(
 | 
	
		
			
				|  |  |      const HeaderMatcher& other)
 | 
	
		
			
				|  |  |      : name(other.name), type(other.type), invert_match(other.invert_match) {
 | 
	
		
			
				|  |  |    switch (type) {
 | 
	
	
		
			
				|  | @@ -157,9 +154,8 @@ XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher::HeaderMatcher(
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher&
 | 
	
		
			
				|  |  | -XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher::operator=(
 | 
	
		
			
				|  |  | -    const HeaderMatcher& other) {
 | 
	
		
			
				|  |  | +XdsApi::Route::Matchers::HeaderMatcher& XdsApi::Route::Matchers::HeaderMatcher::
 | 
	
		
			
				|  |  | +operator=(const HeaderMatcher& other) {
 | 
	
		
			
				|  |  |    name = other.name;
 | 
	
		
			
				|  |  |    type = other.type;
 | 
	
		
			
				|  |  |    invert_match = other.invert_match;
 | 
	
	
		
			
				|  | @@ -180,7 +176,7 @@ XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher::operator=(
 | 
	
		
			
				|  |  |    return *this;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -bool XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher::operator==(
 | 
	
		
			
				|  |  | +bool XdsApi::Route::Matchers::HeaderMatcher::operator==(
 | 
	
		
			
				|  |  |      const HeaderMatcher& other) const {
 | 
	
		
			
				|  |  |    if (name != other.name) return false;
 | 
	
		
			
				|  |  |    if (type != other.type) return false;
 | 
	
	
		
			
				|  | @@ -197,8 +193,7 @@ bool XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher::operator==(
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -std::string XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher::ToString()
 | 
	
		
			
				|  |  | -    const {
 | 
	
		
			
				|  |  | +std::string XdsApi::Route::Matchers::HeaderMatcher::ToString() const {
 | 
	
		
			
				|  |  |    switch (type) {
 | 
	
		
			
				|  |  |      case HeaderMatcherType::EXACT:
 | 
	
		
			
				|  |  |        return absl::StrFormat("Header exact match:%s %s:%s",
 | 
	
	
		
			
				|  | @@ -226,7 +221,11 @@ std::string XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher::ToString()
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -std::string XdsApi::RdsUpdate::RdsRoute::Matchers::ToString() const {
 | 
	
		
			
				|  |  | +//
 | 
	
		
			
				|  |  | +// XdsApi::Route
 | 
	
		
			
				|  |  | +//
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +std::string XdsApi::Route::Matchers::ToString() const {
 | 
	
		
			
				|  |  |    std::vector<std::string> contents;
 | 
	
		
			
				|  |  |    contents.push_back(path_matcher.ToString());
 | 
	
		
			
				|  |  |    for (const auto& header_it : header_matchers) {
 | 
	
	
		
			
				|  | @@ -239,11 +238,11 @@ std::string XdsApi::RdsUpdate::RdsRoute::Matchers::ToString() const {
 | 
	
		
			
				|  |  |    return absl::StrJoin(contents, "\n");
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -std::string XdsApi::RdsUpdate::RdsRoute::ClusterWeight::ToString() const {
 | 
	
		
			
				|  |  | +std::string XdsApi::Route::ClusterWeight::ToString() const {
 | 
	
		
			
				|  |  |    return absl::StrFormat("{cluster=%s, weight=%d}", name, weight);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -std::string XdsApi::RdsUpdate::RdsRoute::ToString() const {
 | 
	
		
			
				|  |  | +std::string XdsApi::Route::ToString() const {
 | 
	
		
			
				|  |  |    std::vector<std::string> contents;
 | 
	
		
			
				|  |  |    contents.push_back(matchers.ToString());
 | 
	
		
			
				|  |  |    if (!cluster_name.empty()) {
 | 
	
	
		
			
				|  | @@ -255,12 +254,124 @@ std::string XdsApi::RdsUpdate::RdsRoute::ToString() const {
 | 
	
		
			
				|  |  |    return absl::StrJoin(contents, "\n");
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +//
 | 
	
		
			
				|  |  | +// XdsApi::RdsUpdate
 | 
	
		
			
				|  |  | +//
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  std::string XdsApi::RdsUpdate::ToString() const {
 | 
	
		
			
				|  |  | -  std::vector<std::string> contents;
 | 
	
		
			
				|  |  | -  for (const auto& route_it : routes) {
 | 
	
		
			
				|  |  | -    contents.push_back(route_it.ToString());
 | 
	
		
			
				|  |  | +  std::vector<std::string> vhosts;
 | 
	
		
			
				|  |  | +  for (const VirtualHost& vhost : virtual_hosts) {
 | 
	
		
			
				|  |  | +    vhosts.push_back(
 | 
	
		
			
				|  |  | +        absl::StrCat("vhost={\n"
 | 
	
		
			
				|  |  | +                     "  domains=[",
 | 
	
		
			
				|  |  | +                     absl::StrJoin(vhost.domains, ", "),
 | 
	
		
			
				|  |  | +                     "]\n"
 | 
	
		
			
				|  |  | +                     "  routes=[\n"));
 | 
	
		
			
				|  |  | +    for (const XdsApi::Route& route : vhost.routes) {
 | 
	
		
			
				|  |  | +      vhosts.push_back("    {\n");
 | 
	
		
			
				|  |  | +      vhosts.push_back(route.ToString());
 | 
	
		
			
				|  |  | +      vhosts.push_back("\n    }\n");
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    vhosts.push_back("  ]\n");
 | 
	
		
			
				|  |  | +    vhosts.push_back("]\n");
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -  return absl::StrJoin(contents, ",\n");
 | 
	
		
			
				|  |  | +  return absl::StrJoin(vhosts, "");
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +namespace {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// Better match type has smaller value.
 | 
	
		
			
				|  |  | +enum MatchType {
 | 
	
		
			
				|  |  | +  EXACT_MATCH,
 | 
	
		
			
				|  |  | +  SUFFIX_MATCH,
 | 
	
		
			
				|  |  | +  PREFIX_MATCH,
 | 
	
		
			
				|  |  | +  UNIVERSE_MATCH,
 | 
	
		
			
				|  |  | +  INVALID_MATCH,
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// Returns true if match succeeds.
 | 
	
		
			
				|  |  | +bool DomainMatch(MatchType match_type, std::string domain_pattern,
 | 
	
		
			
				|  |  | +                 std::string expected_host_name) {
 | 
	
		
			
				|  |  | +  // Normalize the args to lower-case. Domain matching is case-insensitive.
 | 
	
		
			
				|  |  | +  std::transform(domain_pattern.begin(), domain_pattern.end(),
 | 
	
		
			
				|  |  | +                 domain_pattern.begin(),
 | 
	
		
			
				|  |  | +                 [](unsigned char c) { return std::tolower(c); });
 | 
	
		
			
				|  |  | +  std::transform(expected_host_name.begin(), expected_host_name.end(),
 | 
	
		
			
				|  |  | +                 expected_host_name.begin(),
 | 
	
		
			
				|  |  | +                 [](unsigned char c) { return std::tolower(c); });
 | 
	
		
			
				|  |  | +  if (match_type == EXACT_MATCH) {
 | 
	
		
			
				|  |  | +    return domain_pattern == expected_host_name;
 | 
	
		
			
				|  |  | +  } else if (match_type == SUFFIX_MATCH) {
 | 
	
		
			
				|  |  | +    // Asterisk must match at least one char.
 | 
	
		
			
				|  |  | +    if (expected_host_name.size() < domain_pattern.size()) return false;
 | 
	
		
			
				|  |  | +    absl::string_view pattern_suffix(domain_pattern.c_str() + 1);
 | 
	
		
			
				|  |  | +    absl::string_view host_suffix(expected_host_name.c_str() +
 | 
	
		
			
				|  |  | +                                  expected_host_name.size() -
 | 
	
		
			
				|  |  | +                                  pattern_suffix.size());
 | 
	
		
			
				|  |  | +    return pattern_suffix == host_suffix;
 | 
	
		
			
				|  |  | +  } else if (match_type == PREFIX_MATCH) {
 | 
	
		
			
				|  |  | +    // Asterisk must match at least one char.
 | 
	
		
			
				|  |  | +    if (expected_host_name.size() < domain_pattern.size()) return false;
 | 
	
		
			
				|  |  | +    absl::string_view pattern_prefix(domain_pattern.c_str(),
 | 
	
		
			
				|  |  | +                                     domain_pattern.size() - 1);
 | 
	
		
			
				|  |  | +    absl::string_view host_prefix(expected_host_name.c_str(),
 | 
	
		
			
				|  |  | +                                  pattern_prefix.size());
 | 
	
		
			
				|  |  | +    return pattern_prefix == host_prefix;
 | 
	
		
			
				|  |  | +  } else {
 | 
	
		
			
				|  |  | +    return match_type == UNIVERSE_MATCH;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +MatchType DomainPatternMatchType(const std::string& domain_pattern) {
 | 
	
		
			
				|  |  | +  if (domain_pattern.empty()) return INVALID_MATCH;
 | 
	
		
			
				|  |  | +  if (domain_pattern.find('*') == std::string::npos) return EXACT_MATCH;
 | 
	
		
			
				|  |  | +  if (domain_pattern == "*") return UNIVERSE_MATCH;
 | 
	
		
			
				|  |  | +  if (domain_pattern[0] == '*') return SUFFIX_MATCH;
 | 
	
		
			
				|  |  | +  if (domain_pattern[domain_pattern.size() - 1] == '*') return PREFIX_MATCH;
 | 
	
		
			
				|  |  | +  return INVALID_MATCH;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +}  // namespace
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +const XdsApi::RdsUpdate::VirtualHost*
 | 
	
		
			
				|  |  | +XdsApi::RdsUpdate::FindVirtualHostForDomain(const std::string& domain) const {
 | 
	
		
			
				|  |  | +  // Find the best matched virtual host.
 | 
	
		
			
				|  |  | +  // The search order for 4 groups of domain patterns:
 | 
	
		
			
				|  |  | +  //   1. Exact match.
 | 
	
		
			
				|  |  | +  //   2. Suffix match (e.g., "*ABC").
 | 
	
		
			
				|  |  | +  //   3. Prefix match (e.g., "ABC*").
 | 
	
		
			
				|  |  | +  //   4. Universe match (i.e., "*").
 | 
	
		
			
				|  |  | +  // Within each group, longest match wins.
 | 
	
		
			
				|  |  | +  // If the same best matched domain pattern appears in multiple virtual hosts,
 | 
	
		
			
				|  |  | +  // the first matched virtual host wins.
 | 
	
		
			
				|  |  | +  const VirtualHost* target_vhost = nullptr;
 | 
	
		
			
				|  |  | +  MatchType best_match_type = INVALID_MATCH;
 | 
	
		
			
				|  |  | +  size_t longest_match = 0;
 | 
	
		
			
				|  |  | +  // Check each domain pattern in each virtual host to determine the best
 | 
	
		
			
				|  |  | +  // matched virtual host.
 | 
	
		
			
				|  |  | +  for (const VirtualHost& vhost : virtual_hosts) {
 | 
	
		
			
				|  |  | +    for (const std::string& domain_pattern : vhost.domains) {
 | 
	
		
			
				|  |  | +      // Check the match type first. Skip the pattern if it's not better than
 | 
	
		
			
				|  |  | +      // current match.
 | 
	
		
			
				|  |  | +      const MatchType match_type = DomainPatternMatchType(domain_pattern);
 | 
	
		
			
				|  |  | +      // This should be caught by RouteConfigParse().
 | 
	
		
			
				|  |  | +      GPR_ASSERT(match_type != INVALID_MATCH);
 | 
	
		
			
				|  |  | +      if (match_type > best_match_type) continue;
 | 
	
		
			
				|  |  | +      if (match_type == best_match_type &&
 | 
	
		
			
				|  |  | +          domain_pattern.size() <= longest_match) {
 | 
	
		
			
				|  |  | +        continue;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      // Skip if match fails.
 | 
	
		
			
				|  |  | +      if (!DomainMatch(match_type, domain_pattern, domain)) continue;
 | 
	
		
			
				|  |  | +      // Choose this match.
 | 
	
		
			
				|  |  | +      target_vhost = &vhost;
 | 
	
		
			
				|  |  | +      best_match_type = match_type;
 | 
	
		
			
				|  |  | +      longest_match = domain_pattern.size();
 | 
	
		
			
				|  |  | +      if (best_match_type == EXACT_MATCH) break;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    if (best_match_type == EXACT_MATCH) break;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  return target_vhost;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  //
 | 
	
	
		
			
				|  | @@ -1163,60 +1274,8 @@ void MaybeLogClusterLoadAssignment(
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -// Better match type has smaller value.
 | 
	
		
			
				|  |  | -enum MatchType {
 | 
	
		
			
				|  |  | -  EXACT_MATCH,
 | 
	
		
			
				|  |  | -  SUFFIX_MATCH,
 | 
	
		
			
				|  |  | -  PREFIX_MATCH,
 | 
	
		
			
				|  |  | -  UNIVERSE_MATCH,
 | 
	
		
			
				|  |  | -  INVALID_MATCH,
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -// Returns true if match succeeds.
 | 
	
		
			
				|  |  | -bool DomainMatch(MatchType match_type, std::string domain_pattern,
 | 
	
		
			
				|  |  | -                 std::string expected_host_name) {
 | 
	
		
			
				|  |  | -  // Normalize the args to lower-case. Domain matching is case-insensitive.
 | 
	
		
			
				|  |  | -  std::transform(domain_pattern.begin(), domain_pattern.end(),
 | 
	
		
			
				|  |  | -                 domain_pattern.begin(),
 | 
	
		
			
				|  |  | -                 [](unsigned char c) { return std::tolower(c); });
 | 
	
		
			
				|  |  | -  std::transform(expected_host_name.begin(), expected_host_name.end(),
 | 
	
		
			
				|  |  | -                 expected_host_name.begin(),
 | 
	
		
			
				|  |  | -                 [](unsigned char c) { return std::tolower(c); });
 | 
	
		
			
				|  |  | -  if (match_type == EXACT_MATCH) {
 | 
	
		
			
				|  |  | -    return domain_pattern == expected_host_name;
 | 
	
		
			
				|  |  | -  } else if (match_type == SUFFIX_MATCH) {
 | 
	
		
			
				|  |  | -    // Asterisk must match at least one char.
 | 
	
		
			
				|  |  | -    if (expected_host_name.size() < domain_pattern.size()) return false;
 | 
	
		
			
				|  |  | -    absl::string_view pattern_suffix(domain_pattern.c_str() + 1);
 | 
	
		
			
				|  |  | -    absl::string_view host_suffix(expected_host_name.c_str() +
 | 
	
		
			
				|  |  | -                                  expected_host_name.size() -
 | 
	
		
			
				|  |  | -                                  pattern_suffix.size());
 | 
	
		
			
				|  |  | -    return pattern_suffix == host_suffix;
 | 
	
		
			
				|  |  | -  } else if (match_type == PREFIX_MATCH) {
 | 
	
		
			
				|  |  | -    // Asterisk must match at least one char.
 | 
	
		
			
				|  |  | -    if (expected_host_name.size() < domain_pattern.size()) return false;
 | 
	
		
			
				|  |  | -    absl::string_view pattern_prefix(domain_pattern.c_str(),
 | 
	
		
			
				|  |  | -                                     domain_pattern.size() - 1);
 | 
	
		
			
				|  |  | -    absl::string_view host_prefix(expected_host_name.c_str(),
 | 
	
		
			
				|  |  | -                                  pattern_prefix.size());
 | 
	
		
			
				|  |  | -    return pattern_prefix == host_prefix;
 | 
	
		
			
				|  |  | -  } else {
 | 
	
		
			
				|  |  | -    return match_type == UNIVERSE_MATCH;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -MatchType DomainPatternMatchType(const std::string& domain_pattern) {
 | 
	
		
			
				|  |  | -  if (domain_pattern.empty()) return INVALID_MATCH;
 | 
	
		
			
				|  |  | -  if (domain_pattern.find('*') == std::string::npos) return EXACT_MATCH;
 | 
	
		
			
				|  |  | -  if (domain_pattern == "*") return UNIVERSE_MATCH;
 | 
	
		
			
				|  |  | -  if (domain_pattern[0] == '*') return SUFFIX_MATCH;
 | 
	
		
			
				|  |  | -  if (domain_pattern[domain_pattern.size() - 1] == '*') return PREFIX_MATCH;
 | 
	
		
			
				|  |  | -  return INVALID_MATCH;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  grpc_error* RoutePathMatchParse(const envoy_config_route_v3_RouteMatch* match,
 | 
	
		
			
				|  |  | -                                XdsApi::RdsUpdate::RdsRoute* rds_route,
 | 
	
		
			
				|  |  | -                                bool* ignore_route) {
 | 
	
		
			
				|  |  | +                                XdsApi::Route* route, bool* ignore_route) {
 | 
	
		
			
				|  |  |    if (envoy_config_route_v3_RouteMatch_has_prefix(match)) {
 | 
	
		
			
				|  |  |      absl::string_view prefix =
 | 
	
		
			
				|  |  |          UpbStringToAbsl(envoy_config_route_v3_RouteMatch_prefix(match));
 | 
	
	
		
			
				|  | @@ -1241,9 +1300,9 @@ grpc_error* RoutePathMatchParse(const envoy_config_route_v3_RouteMatch* match,
 | 
	
		
			
				|  |  |          return GRPC_ERROR_NONE;
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -    rds_route->matchers.path_matcher.type = XdsApi::RdsUpdate::RdsRoute::
 | 
	
		
			
				|  |  | -        Matchers::PathMatcher::PathMatcherType::PREFIX;
 | 
	
		
			
				|  |  | -    rds_route->matchers.path_matcher.string_matcher = std::string(prefix);
 | 
	
		
			
				|  |  | +    route->matchers.path_matcher.type =
 | 
	
		
			
				|  |  | +        XdsApi::Route::Matchers::PathMatcher::PathMatcherType::PREFIX;
 | 
	
		
			
				|  |  | +    route->matchers.path_matcher.string_matcher = std::string(prefix);
 | 
	
		
			
				|  |  |    } else if (envoy_config_route_v3_RouteMatch_has_path(match)) {
 | 
	
		
			
				|  |  |      absl::string_view path =
 | 
	
		
			
				|  |  |          UpbStringToAbsl(envoy_config_route_v3_RouteMatch_path(match));
 | 
	
	
		
			
				|  | @@ -1276,9 +1335,9 @@ grpc_error* RoutePathMatchParse(const envoy_config_route_v3_RouteMatch* match,
 | 
	
		
			
				|  |  |        *ignore_route = true;
 | 
	
		
			
				|  |  |        return GRPC_ERROR_NONE;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -    rds_route->matchers.path_matcher.type = XdsApi::RdsUpdate::RdsRoute::
 | 
	
		
			
				|  |  | -        Matchers::PathMatcher::PathMatcherType::PATH;
 | 
	
		
			
				|  |  | -    rds_route->matchers.path_matcher.string_matcher = std::string(path);
 | 
	
		
			
				|  |  | +    route->matchers.path_matcher.type =
 | 
	
		
			
				|  |  | +        XdsApi::Route::Matchers::PathMatcher::PathMatcherType::PATH;
 | 
	
		
			
				|  |  | +    route->matchers.path_matcher.string_matcher = std::string(path);
 | 
	
		
			
				|  |  |    } else if (envoy_config_route_v3_RouteMatch_has_safe_regex(match)) {
 | 
	
		
			
				|  |  |      const envoy_type_matcher_v3_RegexMatcher* regex_matcher =
 | 
	
		
			
				|  |  |          envoy_config_route_v3_RouteMatch_safe_regex(match);
 | 
	
	
		
			
				|  | @@ -1290,9 +1349,9 @@ grpc_error* RoutePathMatchParse(const envoy_config_route_v3_RouteMatch* match,
 | 
	
		
			
				|  |  |        return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  |            "Invalid regex string specified in path matcher.");
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -    rds_route->matchers.path_matcher.type = XdsApi::RdsUpdate::RdsRoute::
 | 
	
		
			
				|  |  | -        Matchers::PathMatcher::PathMatcherType::REGEX;
 | 
	
		
			
				|  |  | -    rds_route->matchers.path_matcher.regex_matcher = std::move(regex);
 | 
	
		
			
				|  |  | +    route->matchers.path_matcher.type =
 | 
	
		
			
				|  |  | +        XdsApi::Route::Matchers::PathMatcher::PathMatcherType::REGEX;
 | 
	
		
			
				|  |  | +    route->matchers.path_matcher.regex_matcher = std::move(regex);
 | 
	
		
			
				|  |  |    } else {
 | 
	
		
			
				|  |  |      return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  |          "Invalid route path specifier specified.");
 | 
	
	
		
			
				|  | @@ -1301,19 +1360,18 @@ grpc_error* RoutePathMatchParse(const envoy_config_route_v3_RouteMatch* match,
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  grpc_error* RouteHeaderMatchersParse(
 | 
	
		
			
				|  |  | -    const envoy_config_route_v3_RouteMatch* match,
 | 
	
		
			
				|  |  | -    XdsApi::RdsUpdate::RdsRoute* rds_route) {
 | 
	
		
			
				|  |  | +    const envoy_config_route_v3_RouteMatch* match, XdsApi::Route* route) {
 | 
	
		
			
				|  |  |    size_t size;
 | 
	
		
			
				|  |  |    const envoy_config_route_v3_HeaderMatcher* const* headers =
 | 
	
		
			
				|  |  |        envoy_config_route_v3_RouteMatch_headers(match, &size);
 | 
	
		
			
				|  |  |    for (size_t i = 0; i < size; ++i) {
 | 
	
		
			
				|  |  |      const envoy_config_route_v3_HeaderMatcher* header = headers[i];
 | 
	
		
			
				|  |  | -    XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher header_matcher;
 | 
	
		
			
				|  |  | +    XdsApi::Route::Matchers::HeaderMatcher header_matcher;
 | 
	
		
			
				|  |  |      header_matcher.name =
 | 
	
		
			
				|  |  |          UpbStringToStdString(envoy_config_route_v3_HeaderMatcher_name(header));
 | 
	
		
			
				|  |  |      if (envoy_config_route_v3_HeaderMatcher_has_exact_match(header)) {
 | 
	
		
			
				|  |  | -      header_matcher.type = XdsApi::RdsUpdate::RdsRoute::Matchers::
 | 
	
		
			
				|  |  | -          HeaderMatcher::HeaderMatcherType::EXACT;
 | 
	
		
			
				|  |  | +      header_matcher.type =
 | 
	
		
			
				|  |  | +          XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::EXACT;
 | 
	
		
			
				|  |  |        header_matcher.string_matcher = UpbStringToStdString(
 | 
	
		
			
				|  |  |            envoy_config_route_v3_HeaderMatcher_exact_match(header));
 | 
	
		
			
				|  |  |      } else if (envoy_config_route_v3_HeaderMatcher_has_safe_regex_match(
 | 
	
	
		
			
				|  | @@ -1328,12 +1386,12 @@ grpc_error* RouteHeaderMatchersParse(
 | 
	
		
			
				|  |  |          return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  |              "Invalid regex string specified in header matcher.");
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  | -      header_matcher.type = XdsApi::RdsUpdate::RdsRoute::Matchers::
 | 
	
		
			
				|  |  | -          HeaderMatcher::HeaderMatcherType::REGEX;
 | 
	
		
			
				|  |  | +      header_matcher.type =
 | 
	
		
			
				|  |  | +          XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::REGEX;
 | 
	
		
			
				|  |  |        header_matcher.regex_match = std::move(regex);
 | 
	
		
			
				|  |  |      } else if (envoy_config_route_v3_HeaderMatcher_has_range_match(header)) {
 | 
	
		
			
				|  |  | -      header_matcher.type = XdsApi::RdsUpdate::RdsRoute::Matchers::
 | 
	
		
			
				|  |  | -          HeaderMatcher::HeaderMatcherType::RANGE;
 | 
	
		
			
				|  |  | +      header_matcher.type =
 | 
	
		
			
				|  |  | +          XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::RANGE;
 | 
	
		
			
				|  |  |        const envoy_type_v3_Int64Range* range_matcher =
 | 
	
		
			
				|  |  |            envoy_config_route_v3_HeaderMatcher_range_match(header);
 | 
	
		
			
				|  |  |        header_matcher.range_start =
 | 
	
	
		
			
				|  | @@ -1345,18 +1403,18 @@ grpc_error* RouteHeaderMatchersParse(
 | 
	
		
			
				|  |  |              "cannot be smaller than start.");
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |      } else if (envoy_config_route_v3_HeaderMatcher_has_present_match(header)) {
 | 
	
		
			
				|  |  | -      header_matcher.type = XdsApi::RdsUpdate::RdsRoute::Matchers::
 | 
	
		
			
				|  |  | -          HeaderMatcher::HeaderMatcherType::PRESENT;
 | 
	
		
			
				|  |  | +      header_matcher.type =
 | 
	
		
			
				|  |  | +          XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::PRESENT;
 | 
	
		
			
				|  |  |        header_matcher.present_match =
 | 
	
		
			
				|  |  |            envoy_config_route_v3_HeaderMatcher_present_match(header);
 | 
	
		
			
				|  |  |      } else if (envoy_config_route_v3_HeaderMatcher_has_prefix_match(header)) {
 | 
	
		
			
				|  |  | -      header_matcher.type = XdsApi::RdsUpdate::RdsRoute::Matchers::
 | 
	
		
			
				|  |  | -          HeaderMatcher::HeaderMatcherType::PREFIX;
 | 
	
		
			
				|  |  | +      header_matcher.type =
 | 
	
		
			
				|  |  | +          XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::PREFIX;
 | 
	
		
			
				|  |  |        header_matcher.string_matcher = UpbStringToStdString(
 | 
	
		
			
				|  |  |            envoy_config_route_v3_HeaderMatcher_prefix_match(header));
 | 
	
		
			
				|  |  |      } else if (envoy_config_route_v3_HeaderMatcher_has_suffix_match(header)) {
 | 
	
		
			
				|  |  | -      header_matcher.type = XdsApi::RdsUpdate::RdsRoute::Matchers::
 | 
	
		
			
				|  |  | -          HeaderMatcher::HeaderMatcherType::SUFFIX;
 | 
	
		
			
				|  |  | +      header_matcher.type =
 | 
	
		
			
				|  |  | +          XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::SUFFIX;
 | 
	
		
			
				|  |  |        header_matcher.string_matcher = UpbStringToStdString(
 | 
	
		
			
				|  |  |            envoy_config_route_v3_HeaderMatcher_suffix_match(header));
 | 
	
		
			
				|  |  |      } else {
 | 
	
	
		
			
				|  | @@ -1365,14 +1423,13 @@ grpc_error* RouteHeaderMatchersParse(
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      header_matcher.invert_match =
 | 
	
		
			
				|  |  |          envoy_config_route_v3_HeaderMatcher_invert_match(header);
 | 
	
		
			
				|  |  | -    rds_route->matchers.header_matchers.emplace_back(std::move(header_matcher));
 | 
	
		
			
				|  |  | +    route->matchers.header_matchers.emplace_back(std::move(header_matcher));
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    return GRPC_ERROR_NONE;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  grpc_error* RouteRuntimeFractionParse(
 | 
	
		
			
				|  |  | -    const envoy_config_route_v3_RouteMatch* match,
 | 
	
		
			
				|  |  | -    XdsApi::RdsUpdate::RdsRoute* rds_route) {
 | 
	
		
			
				|  |  | +    const envoy_config_route_v3_RouteMatch* match, XdsApi::Route* route) {
 | 
	
		
			
				|  |  |    const envoy_config_core_v3_RuntimeFractionalPercent* runtime_fraction =
 | 
	
		
			
				|  |  |        envoy_config_route_v3_RouteMatch_runtime_fraction(match);
 | 
	
		
			
				|  |  |    if (runtime_fraction != nullptr) {
 | 
	
	
		
			
				|  | @@ -1398,26 +1455,25 @@ grpc_error* RouteRuntimeFractionParse(
 | 
	
		
			
				|  |  |            return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  |                "Unknown denominator type");
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  | -      rds_route->matchers.fraction_per_million = numerator;
 | 
	
		
			
				|  |  | +      route->matchers.fraction_per_million = numerator;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    return GRPC_ERROR_NONE;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -grpc_error* RouteActionParse(const envoy_config_route_v3_Route* route,
 | 
	
		
			
				|  |  | -                             XdsApi::RdsUpdate::RdsRoute* rds_route,
 | 
	
		
			
				|  |  | -                             bool* ignore_route) {
 | 
	
		
			
				|  |  | -  if (!envoy_config_route_v3_Route_has_route(route)) {
 | 
	
		
			
				|  |  | +grpc_error* RouteActionParse(const envoy_config_route_v3_Route* route_msg,
 | 
	
		
			
				|  |  | +                             XdsApi::Route* route, bool* ignore_route) {
 | 
	
		
			
				|  |  | +  if (!envoy_config_route_v3_Route_has_route(route_msg)) {
 | 
	
		
			
				|  |  |      return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  |          "No RouteAction found in route.");
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    const envoy_config_route_v3_RouteAction* route_action =
 | 
	
		
			
				|  |  | -      envoy_config_route_v3_Route_route(route);
 | 
	
		
			
				|  |  | +      envoy_config_route_v3_Route_route(route_msg);
 | 
	
		
			
				|  |  |    // Get the cluster or weighted_clusters in the RouteAction.
 | 
	
		
			
				|  |  |    if (envoy_config_route_v3_RouteAction_has_cluster(route_action)) {
 | 
	
		
			
				|  |  | -    rds_route->cluster_name = UpbStringToStdString(
 | 
	
		
			
				|  |  | +    route->cluster_name = UpbStringToStdString(
 | 
	
		
			
				|  |  |          envoy_config_route_v3_RouteAction_cluster(route_action));
 | 
	
		
			
				|  |  | -    if (rds_route->cluster_name.size() == 0) {
 | 
	
		
			
				|  |  | +    if (route->cluster_name.size() == 0) {
 | 
	
		
			
				|  |  |        return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  |            "RouteAction cluster contains empty cluster name.");
 | 
	
		
			
				|  |  |      }
 | 
	
	
		
			
				|  | @@ -1439,7 +1495,7 @@ grpc_error* RouteActionParse(const envoy_config_route_v3_Route* route,
 | 
	
		
			
				|  |  |      for (size_t j = 0; j < clusters_size; ++j) {
 | 
	
		
			
				|  |  |        const envoy_config_route_v3_WeightedCluster_ClusterWeight*
 | 
	
		
			
				|  |  |            cluster_weight = clusters[j];
 | 
	
		
			
				|  |  | -      XdsApi::RdsUpdate::RdsRoute::ClusterWeight cluster;
 | 
	
		
			
				|  |  | +      XdsApi::Route::ClusterWeight cluster;
 | 
	
		
			
				|  |  |        cluster.name = UpbStringToStdString(
 | 
	
		
			
				|  |  |            envoy_config_route_v3_WeightedCluster_ClusterWeight_name(
 | 
	
		
			
				|  |  |                cluster_weight));
 | 
	
	
		
			
				|  | @@ -1457,13 +1513,13 @@ grpc_error* RouteActionParse(const envoy_config_route_v3_Route* route,
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |        cluster.weight = google_protobuf_UInt32Value_value(weight);
 | 
	
		
			
				|  |  |        sum_of_weights += cluster.weight;
 | 
	
		
			
				|  |  | -      rds_route->weighted_clusters.emplace_back(std::move(cluster));
 | 
	
		
			
				|  |  | +      route->weighted_clusters.emplace_back(std::move(cluster));
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      if (total_weight != sum_of_weights) {
 | 
	
		
			
				|  |  |        return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  |            "RouteAction weighted_cluster has incorrect total weight");
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -    if (rds_route->weighted_clusters.empty()) {
 | 
	
		
			
				|  |  | +    if (route->weighted_clusters.empty()) {
 | 
	
		
			
				|  |  |        return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  |            "RouteAction weighted_cluster has no valid clusters specified.");
 | 
	
		
			
				|  |  |      }
 | 
	
	
		
			
				|  | @@ -1478,101 +1534,73 @@ grpc_error* RouteActionParse(const envoy_config_route_v3_Route* route,
 | 
	
		
			
				|  |  |  grpc_error* RouteConfigParse(
 | 
	
		
			
				|  |  |      XdsClient* client, TraceFlag* tracer,
 | 
	
		
			
				|  |  |      const envoy_config_route_v3_RouteConfiguration* route_config,
 | 
	
		
			
				|  |  | -    const std::string& expected_server_name, XdsApi::RdsUpdate* rds_update) {
 | 
	
		
			
				|  |  | +    XdsApi::RdsUpdate* rds_update) {
 | 
	
		
			
				|  |  |    MaybeLogRouteConfiguration(client, tracer, route_config);
 | 
	
		
			
				|  |  |    // Get the virtual hosts.
 | 
	
		
			
				|  |  |    size_t size;
 | 
	
		
			
				|  |  |    const envoy_config_route_v3_VirtualHost* const* virtual_hosts =
 | 
	
		
			
				|  |  |        envoy_config_route_v3_RouteConfiguration_virtual_hosts(route_config,
 | 
	
		
			
				|  |  |                                                               &size);
 | 
	
		
			
				|  |  | -  // Find the best matched virtual host.
 | 
	
		
			
				|  |  | -  // The search order for 4 groups of domain patterns:
 | 
	
		
			
				|  |  | -  //   1. Exact match.
 | 
	
		
			
				|  |  | -  //   2. Suffix match (e.g., "*ABC").
 | 
	
		
			
				|  |  | -  //   3. Prefix match (e.g., "ABC*").
 | 
	
		
			
				|  |  | -  //   4. Universe match (i.e., "*").
 | 
	
		
			
				|  |  | -  // Within each group, longest match wins.
 | 
	
		
			
				|  |  | -  // If the same best matched domain pattern appears in multiple virtual hosts,
 | 
	
		
			
				|  |  | -  // the first matched virtual host wins.
 | 
	
		
			
				|  |  | -  const envoy_config_route_v3_VirtualHost* target_virtual_host = nullptr;
 | 
	
		
			
				|  |  | -  MatchType best_match_type = INVALID_MATCH;
 | 
	
		
			
				|  |  | -  size_t longest_match = 0;
 | 
	
		
			
				|  |  | -  // Check each domain pattern in each virtual host to determine the best
 | 
	
		
			
				|  |  | -  // matched virtual host.
 | 
	
		
			
				|  |  |    for (size_t i = 0; i < size; ++i) {
 | 
	
		
			
				|  |  | +    rds_update->virtual_hosts.emplace_back();
 | 
	
		
			
				|  |  | +    XdsApi::RdsUpdate::VirtualHost& vhost = rds_update->virtual_hosts.back();
 | 
	
		
			
				|  |  | +    // Parse domains.
 | 
	
		
			
				|  |  |      size_t domain_size;
 | 
	
		
			
				|  |  |      upb_strview const* domains = envoy_config_route_v3_VirtualHost_domains(
 | 
	
		
			
				|  |  |          virtual_hosts[i], &domain_size);
 | 
	
		
			
				|  |  |      for (size_t j = 0; j < domain_size; ++j) {
 | 
	
		
			
				|  |  | -      const std::string domain_pattern(domains[j].data, domains[j].size);
 | 
	
		
			
				|  |  | -      // Check the match type first. Skip the pattern if it's not better than
 | 
	
		
			
				|  |  | -      // current match.
 | 
	
		
			
				|  |  | +      std::string domain_pattern = UpbStringToStdString(domains[j]);
 | 
	
		
			
				|  |  |        const MatchType match_type = DomainPatternMatchType(domain_pattern);
 | 
	
		
			
				|  |  |        if (match_type == INVALID_MATCH) {
 | 
	
		
			
				|  |  |          return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Invalid domain pattern.");
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  | -      if (match_type > best_match_type) continue;
 | 
	
		
			
				|  |  | -      if (match_type == best_match_type &&
 | 
	
		
			
				|  |  | -          domain_pattern.size() <= longest_match) {
 | 
	
		
			
				|  |  | +      vhost.domains.emplace_back(std::move(domain_pattern));
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    if (vhost.domains.empty()) {
 | 
	
		
			
				|  |  | +      return GRPC_ERROR_CREATE_FROM_STATIC_STRING("VirtualHost has no domains");
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    // Parse routes.
 | 
	
		
			
				|  |  | +    size_t num_routes;
 | 
	
		
			
				|  |  | +    const envoy_config_route_v3_Route* const* routes =
 | 
	
		
			
				|  |  | +        envoy_config_route_v3_VirtualHost_routes(virtual_hosts[i], &num_routes);
 | 
	
		
			
				|  |  | +    if (num_routes < 1) {
 | 
	
		
			
				|  |  | +      return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | +          "No route found in the virtual host.");
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    // Loop over the whole list of routes
 | 
	
		
			
				|  |  | +    for (size_t j = 0; j < num_routes; ++j) {
 | 
	
		
			
				|  |  | +      const envoy_config_route_v3_RouteMatch* match =
 | 
	
		
			
				|  |  | +          envoy_config_route_v3_Route_match(routes[j]);
 | 
	
		
			
				|  |  | +      size_t query_parameters_size;
 | 
	
		
			
				|  |  | +      static_cast<void>(envoy_config_route_v3_RouteMatch_query_parameters(
 | 
	
		
			
				|  |  | +          match, &query_parameters_size));
 | 
	
		
			
				|  |  | +      if (query_parameters_size > 0) {
 | 
	
		
			
				|  |  |          continue;
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  | -      // Skip if match fails.
 | 
	
		
			
				|  |  | -      if (!DomainMatch(match_type, domain_pattern, expected_server_name)) {
 | 
	
		
			
				|  |  | -        continue;
 | 
	
		
			
				|  |  | +      XdsApi::Route route;
 | 
	
		
			
				|  |  | +      bool ignore_route = false;
 | 
	
		
			
				|  |  | +      grpc_error* error = RoutePathMatchParse(match, &route, &ignore_route);
 | 
	
		
			
				|  |  | +      if (error != GRPC_ERROR_NONE) return error;
 | 
	
		
			
				|  |  | +      if (ignore_route) continue;
 | 
	
		
			
				|  |  | +      error = RouteHeaderMatchersParse(match, &route);
 | 
	
		
			
				|  |  | +      if (error != GRPC_ERROR_NONE) return error;
 | 
	
		
			
				|  |  | +      error = RouteRuntimeFractionParse(match, &route);
 | 
	
		
			
				|  |  | +      if (error != GRPC_ERROR_NONE) return error;
 | 
	
		
			
				|  |  | +      error = RouteActionParse(routes[j], &route, &ignore_route);
 | 
	
		
			
				|  |  | +      if (error != GRPC_ERROR_NONE) return error;
 | 
	
		
			
				|  |  | +      if (ignore_route) continue;
 | 
	
		
			
				|  |  | +      const google_protobuf_BoolValue* case_sensitive =
 | 
	
		
			
				|  |  | +          envoy_config_route_v3_RouteMatch_case_sensitive(match);
 | 
	
		
			
				|  |  | +      if (case_sensitive != nullptr &&
 | 
	
		
			
				|  |  | +          !google_protobuf_BoolValue_value(case_sensitive)) {
 | 
	
		
			
				|  |  | +        return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | +            "case_sensitive if set must be set to true.");
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  | -      // Choose this match.
 | 
	
		
			
				|  |  | -      target_virtual_host = virtual_hosts[i];
 | 
	
		
			
				|  |  | -      best_match_type = match_type;
 | 
	
		
			
				|  |  | -      longest_match = domain_pattern.size();
 | 
	
		
			
				|  |  | -      if (best_match_type == EXACT_MATCH) break;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    if (best_match_type == EXACT_MATCH) break;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  if (target_virtual_host == nullptr) {
 | 
	
		
			
				|  |  | -    return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | -        "No matched virtual host found in the route config.");
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  // Get the route list from the matched virtual host.
 | 
	
		
			
				|  |  | -  const envoy_config_route_v3_Route* const* routes =
 | 
	
		
			
				|  |  | -      envoy_config_route_v3_VirtualHost_routes(target_virtual_host, &size);
 | 
	
		
			
				|  |  | -  if (size < 1) {
 | 
	
		
			
				|  |  | -    return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | -        "No route found in the virtual host.");
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  // Loop over the whole list of routes
 | 
	
		
			
				|  |  | -  for (size_t i = 0; i < size; ++i) {
 | 
	
		
			
				|  |  | -    const envoy_config_route_v3_Route* route = routes[i];
 | 
	
		
			
				|  |  | -    const envoy_config_route_v3_RouteMatch* match =
 | 
	
		
			
				|  |  | -        envoy_config_route_v3_Route_match(route);
 | 
	
		
			
				|  |  | -    size_t query_parameters_size;
 | 
	
		
			
				|  |  | -    static_cast<void>(envoy_config_route_v3_RouteMatch_query_parameters(
 | 
	
		
			
				|  |  | -        match, &query_parameters_size));
 | 
	
		
			
				|  |  | -    if (query_parameters_size > 0) {
 | 
	
		
			
				|  |  | -      continue;
 | 
	
		
			
				|  |  | +      vhost.routes.emplace_back(std::move(route));
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -    XdsApi::RdsUpdate::RdsRoute rds_route;
 | 
	
		
			
				|  |  | -    bool ignore_route = false;
 | 
	
		
			
				|  |  | -    grpc_error* error = RoutePathMatchParse(match, &rds_route, &ignore_route);
 | 
	
		
			
				|  |  | -    if (error != GRPC_ERROR_NONE) return error;
 | 
	
		
			
				|  |  | -    if (ignore_route) continue;
 | 
	
		
			
				|  |  | -    error = RouteHeaderMatchersParse(match, &rds_route);
 | 
	
		
			
				|  |  | -    if (error != GRPC_ERROR_NONE) return error;
 | 
	
		
			
				|  |  | -    error = RouteRuntimeFractionParse(match, &rds_route);
 | 
	
		
			
				|  |  | -    if (error != GRPC_ERROR_NONE) return error;
 | 
	
		
			
				|  |  | -    error = RouteActionParse(route, &rds_route, &ignore_route);
 | 
	
		
			
				|  |  | -    if (error != GRPC_ERROR_NONE) return error;
 | 
	
		
			
				|  |  | -    if (ignore_route) continue;
 | 
	
		
			
				|  |  | -    const google_protobuf_BoolValue* case_sensitive =
 | 
	
		
			
				|  |  | -        envoy_config_route_v3_RouteMatch_case_sensitive(match);
 | 
	
		
			
				|  |  | -    if (case_sensitive != nullptr &&
 | 
	
		
			
				|  |  | -        !google_protobuf_BoolValue_value(case_sensitive)) {
 | 
	
		
			
				|  |  | -      return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | -          "case_sensitive if set must be set to true.");
 | 
	
		
			
				|  |  | +    if (vhost.routes.empty()) {
 | 
	
		
			
				|  |  | +      return GRPC_ERROR_CREATE_FROM_STATIC_STRING("No valid routes specified.");
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -    rds_update->routes.emplace_back(std::move(rds_route));
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  if (rds_update->routes.empty()) {
 | 
	
		
			
				|  |  | -    return GRPC_ERROR_CREATE_FROM_STATIC_STRING("No valid routes specified.");
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    return GRPC_ERROR_NONE;
 | 
	
		
			
				|  |  |  }
 | 
	
	
		
			
				|  | @@ -1630,11 +1658,11 @@ grpc_error* LdsResponseParse(
 | 
	
		
			
				|  |  |            envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_route_config(
 | 
	
		
			
				|  |  |                http_connection_manager);
 | 
	
		
			
				|  |  |        XdsApi::RdsUpdate rds_update;
 | 
	
		
			
				|  |  | -      grpc_error* error = RouteConfigParse(client, tracer, route_config,
 | 
	
		
			
				|  |  | -                                           expected_server_name, &rds_update);
 | 
	
		
			
				|  |  | +      grpc_error* error =
 | 
	
		
			
				|  |  | +          RouteConfigParse(client, tracer, route_config, &rds_update);
 | 
	
		
			
				|  |  |        if (error != GRPC_ERROR_NONE) return error;
 | 
	
		
			
				|  |  |        lds_update->emplace();
 | 
	
		
			
				|  |  | -      (*lds_update)->rds_update.emplace(std::move(rds_update));
 | 
	
		
			
				|  |  | +      (*lds_update)->rds_update = std::move(rds_update);
 | 
	
		
			
				|  |  |        return GRPC_ERROR_NONE;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      // Validate that RDS must be used to get the route_config dynamically.
 | 
	
	
		
			
				|  | @@ -1671,7 +1699,6 @@ grpc_error* LdsResponseParse(
 | 
	
		
			
				|  |  |  grpc_error* RdsResponseParse(
 | 
	
		
			
				|  |  |      XdsClient* client, TraceFlag* tracer,
 | 
	
		
			
				|  |  |      const envoy_service_discovery_v3_DiscoveryResponse* response,
 | 
	
		
			
				|  |  | -    const std::string& expected_server_name,
 | 
	
		
			
				|  |  |      const std::set<absl::string_view>& expected_route_configuration_names,
 | 
	
		
			
				|  |  |      absl::optional<XdsApi::RdsUpdate>* rds_update, upb_arena* arena) {
 | 
	
		
			
				|  |  |    // Get the resources from the response.
 | 
	
	
		
			
				|  | @@ -1703,8 +1730,8 @@ grpc_error* RdsResponseParse(
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      // Parse the route_config.
 | 
	
		
			
				|  |  |      XdsApi::RdsUpdate local_rds_update;
 | 
	
		
			
				|  |  | -    grpc_error* error = RouteConfigParse(
 | 
	
		
			
				|  |  | -        client, tracer, route_config, expected_server_name, &local_rds_update);
 | 
	
		
			
				|  |  | +    grpc_error* error =
 | 
	
		
			
				|  |  | +        RouteConfigParse(client, tracer, route_config, &local_rds_update);
 | 
	
		
			
				|  |  |      if (error != GRPC_ERROR_NONE) return error;
 | 
	
		
			
				|  |  |      rds_update->emplace(std::move(local_rds_update));
 | 
	
		
			
				|  |  |      return GRPC_ERROR_NONE;
 | 
	
	
		
			
				|  | @@ -2043,9 +2070,9 @@ XdsApi::AdsParseResult XdsApi::ParseAdsResponse(
 | 
	
		
			
				|  |  |          LdsResponseParse(client_, tracer_, response, expected_server_name,
 | 
	
		
			
				|  |  |                           &result.lds_update, arena.ptr());
 | 
	
		
			
				|  |  |    } else if (IsRds(result.type_url)) {
 | 
	
		
			
				|  |  | -    result.parse_error = RdsResponseParse(
 | 
	
		
			
				|  |  | -        client_, tracer_, response, expected_server_name,
 | 
	
		
			
				|  |  | -        expected_route_configuration_names, &result.rds_update, arena.ptr());
 | 
	
		
			
				|  |  | +    result.parse_error = RdsResponseParse(client_, tracer_, response,
 | 
	
		
			
				|  |  | +                                          expected_route_configuration_names,
 | 
	
		
			
				|  |  | +                                          &result.rds_update, arena.ptr());
 | 
	
		
			
				|  |  |    } else if (IsCds(result.type_url)) {
 | 
	
		
			
				|  |  |      result.parse_error =
 | 
	
		
			
				|  |  |          CdsResponseParse(client_, tracer_, response, expected_cluster_names,
 |