diff --git a/src/core/ext/filters/client_channel/lb_policy/xds/xds_routing.cc b/src/core/ext/filters/client_channel/lb_policy/xds/xds_routing.cc index 22de026aefa..883dcfa55ad 100644 --- a/src/core/ext/filters/client_channel/lb_policy/xds/xds_routing.cc +++ b/src/core/ext/filters/client_channel/lb_policy/xds/xds_routing.cc @@ -58,7 +58,7 @@ constexpr char kXdsRouting[] = "xds_routing_experimental"; class XdsRoutingLbConfig : public LoadBalancingPolicy::Config { public: struct Route { - XdsApi::Route::Matchers matchers; + XdsApi::RdsUpdate::RdsRoute::Matchers matchers; std::string action; }; using RouteTable = std::vector; @@ -112,7 +112,7 @@ class XdsRoutingLb : public LoadBalancingPolicy { class RoutePicker : public SubchannelPicker { public: struct Route { - const XdsApi::Route::Matchers* matchers; + const XdsApi::RdsUpdate::RdsRoute::Matchers* matchers; RefCountedPtr picker; }; @@ -128,7 +128,7 @@ class XdsRoutingLb : public LoadBalancingPolicy { private: RouteTable route_table_; // Take a reference to config so that we can use - // XdsApi::Route::Matchers from it. + // XdsApi::RdsUpdate::RdsRoute::Matchers from it. RefCountedPtr config_; }; @@ -222,14 +222,18 @@ class XdsRoutingLb : public LoadBalancingPolicy { // XdsRoutingLb::RoutePicker // -bool PathMatch(const absl::string_view& path, - const XdsApi::Route::Matchers::PathMatcher& path_matcher) { +bool PathMatch( + const absl::string_view& path, + const XdsApi::RdsUpdate::RdsRoute::Matchers::PathMatcher& path_matcher) { switch (path_matcher.type) { - case XdsApi::Route::Matchers::PathMatcher::PathMatcherType::PREFIX: + case XdsApi::RdsUpdate::RdsRoute::Matchers::PathMatcher::PathMatcherType:: + PREFIX: return absl::StartsWith(path, path_matcher.string_matcher); - case XdsApi::Route::Matchers::PathMatcher::PathMatcherType::PATH: + case XdsApi::RdsUpdate::RdsRoute::Matchers::PathMatcher::PathMatcherType:: + PATH: return path == path_matcher.string_matcher; - case XdsApi::Route::Matchers::PathMatcher::PathMatcherType::REGEX: + case XdsApi::RdsUpdate::RdsRoute::Matchers::PathMatcher::PathMatcherType:: + REGEX: return RE2::FullMatch(path.data(), *path_matcher.regex_matcher); default: return false; @@ -258,7 +262,7 @@ absl::optional GetMetadataValue( } bool HeaderMatchHelper( - const XdsApi::Route::Matchers::HeaderMatcher& header_matcher, + const XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher& header_matcher, LoadBalancingPolicy::MetadataInterface* initial_metadata) { std::string concatenated_value; absl::optional value; @@ -275,8 +279,8 @@ bool HeaderMatchHelper( &concatenated_value); } if (!value.has_value()) { - if (header_matcher.type == - XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::PRESENT) { + if (header_matcher.type == XdsApi::RdsUpdate::RdsRoute::Matchers:: + HeaderMatcher::HeaderMatcherType::PRESENT) { return !header_matcher.present_match; } else { // For all other header matcher types, we need the header value to @@ -285,20 +289,25 @@ bool HeaderMatchHelper( } } switch (header_matcher.type) { - case XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::EXACT: + case XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher:: + HeaderMatcherType::EXACT: return value.value() == header_matcher.string_matcher; - case XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::REGEX: + case XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher:: + HeaderMatcherType::REGEX: return RE2::FullMatch(value.value().data(), *header_matcher.regex_match); - case XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::RANGE: + case XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher:: + HeaderMatcherType::RANGE: int64_t int_value; if (!absl::SimpleAtoi(value.value(), &int_value)) { return false; } return int_value >= header_matcher.range_start && int_value < header_matcher.range_end; - case XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::PREFIX: + case XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher:: + HeaderMatcherType::PREFIX: return absl::StartsWith(value.value(), header_matcher.string_matcher); - case XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::SUFFIX: + case XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher:: + HeaderMatcherType::SUFFIX: return absl::EndsWith(value.value(), header_matcher.string_matcher); default: return false; @@ -306,7 +315,8 @@ bool HeaderMatchHelper( } bool HeadersMatch( - const std::vector& header_matchers, + const std::vector& + header_matchers, LoadBalancingPolicy::MetadataInterface* initial_metadata) { for (const auto& header_matcher : header_matchers) { bool match = HeaderMatchHelper(header_matcher, initial_metadata); @@ -855,8 +865,8 @@ class XdsRoutingLbFactory : public LoadBalancingPolicyFactory { "field:prefix error: should be string")); } else { path_matcher_seen = true; - route->matchers.path_matcher.type = - XdsApi::Route::Matchers::PathMatcher::PathMatcherType::PREFIX; + route->matchers.path_matcher.type = XdsApi::RdsUpdate::RdsRoute:: + Matchers::PathMatcher::PathMatcherType::PREFIX; route->matchers.path_matcher.string_matcher = it->second.string_value(); } } @@ -871,8 +881,8 @@ class XdsRoutingLbFactory : public LoadBalancingPolicyFactory { error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( "field:path error: should be string")); } else { - route->matchers.path_matcher.type = - XdsApi::Route::Matchers::PathMatcher::PathMatcherType::PATH; + route->matchers.path_matcher.type = XdsApi::RdsUpdate::RdsRoute:: + Matchers::PathMatcher::PathMatcherType::PATH; route->matchers.path_matcher.string_matcher = it->second.string_value(); } @@ -889,8 +899,8 @@ class XdsRoutingLbFactory : public LoadBalancingPolicyFactory { error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( "field:regex error: should be string")); } else { - route->matchers.path_matcher.type = - XdsApi::Route::Matchers::PathMatcher::PathMatcherType::REGEX; + route->matchers.path_matcher.type = XdsApi::RdsUpdate::RdsRoute:: + Matchers::PathMatcher::PathMatcherType::REGEX; route->matchers.path_matcher.regex_matcher = absl::make_unique(it->second.string_value()); } @@ -915,8 +925,8 @@ class XdsRoutingLbFactory : public LoadBalancingPolicyFactory { "value should be of type object")); } else { route->matchers.header_matchers.emplace_back(); - XdsApi::Route::Matchers::HeaderMatcher& header_matcher = - route->matchers.header_matchers.back(); + XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher& + header_matcher = route->matchers.header_matchers.back(); auto header_it = header_json.object_value().find("name"); if (header_it == header_json.object_value().end()) { error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( @@ -950,8 +960,8 @@ class XdsRoutingLbFactory : public LoadBalancingPolicyFactory { error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( "field:exact_match error: should be string")); } else { - header_matcher.type = XdsApi::Route::Matchers::HeaderMatcher:: - HeaderMatcherType::EXACT; + header_matcher.type = XdsApi::RdsUpdate::RdsRoute::Matchers:: + HeaderMatcher::HeaderMatcherType::EXACT; header_matcher.string_matcher = header_it->second.string_value(); } @@ -968,8 +978,8 @@ class XdsRoutingLbFactory : public LoadBalancingPolicyFactory { error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( "field:regex_match error: should be string")); } else { - header_matcher.type = XdsApi::Route::Matchers::HeaderMatcher:: - HeaderMatcherType::REGEX; + header_matcher.type = XdsApi::RdsUpdate::RdsRoute::Matchers:: + HeaderMatcher::HeaderMatcherType::REGEX; header_matcher.regex_match = absl::make_unique(header_it->second.string_value()); } @@ -1015,8 +1025,8 @@ class XdsRoutingLbFactory : public LoadBalancingPolicyFactory { "field:end missing")); } if (header_matcher.range_end > header_matcher.range_start) { - header_matcher.type = XdsApi::Route::Matchers:: - HeaderMatcher::HeaderMatcherType::RANGE; + header_matcher.type = XdsApi::RdsUpdate::RdsRoute:: + Matchers::HeaderMatcher::HeaderMatcherType::RANGE; } } } @@ -1030,12 +1040,12 @@ class XdsRoutingLbFactory : public LoadBalancingPolicyFactory { } else { header_matcher_seen = true; if (header_it->second.type() == Json::Type::JSON_TRUE) { - header_matcher.type = XdsApi::Route::Matchers::HeaderMatcher:: - HeaderMatcherType::PRESENT; + header_matcher.type = XdsApi::RdsUpdate::RdsRoute::Matchers:: + HeaderMatcher::HeaderMatcherType::PRESENT; header_matcher.present_match = true; } else if (header_it->second.type() == Json::Type::JSON_FALSE) { - header_matcher.type = XdsApi::Route::Matchers::HeaderMatcher:: - HeaderMatcherType::PRESENT; + header_matcher.type = XdsApi::RdsUpdate::RdsRoute::Matchers:: + HeaderMatcher::HeaderMatcherType::PRESENT; header_matcher.present_match = false; } else { error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( @@ -1055,8 +1065,8 @@ class XdsRoutingLbFactory : public LoadBalancingPolicyFactory { error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( "field:prefix_match error: should be string")); } else { - header_matcher.type = XdsApi::Route::Matchers::HeaderMatcher:: - HeaderMatcherType::PREFIX; + header_matcher.type = XdsApi::RdsUpdate::RdsRoute::Matchers:: + HeaderMatcher::HeaderMatcherType::PREFIX; header_matcher.string_matcher = header_it->second.string_value(); } @@ -1074,8 +1084,8 @@ class XdsRoutingLbFactory : public LoadBalancingPolicyFactory { error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( "field:suffix_match error: should be string")); } else { - header_matcher.type = XdsApi::Route::Matchers::HeaderMatcher:: - HeaderMatcherType::SUFFIX; + header_matcher.type = XdsApi::RdsUpdate::RdsRoute::Matchers:: + HeaderMatcher::HeaderMatcherType::SUFFIX; header_matcher.string_matcher = header_it->second.string_value(); } diff --git a/src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc b/src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc index d522ce3a77a..c0b93dc69ed 100644 --- a/src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc +++ b/src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc @@ -74,7 +74,7 @@ class XdsResolver : public Resolver { public: explicit ListenerWatcher(RefCountedPtr resolver) : resolver_(std::move(resolver)) {} - void OnListenerChanged(std::vector routes) override; + void OnListenerChanged(XdsApi::LdsUpdate listener_data) override; void OnError(grpc_error* error) override; void OnResourceDoesNotExist() override; @@ -92,14 +92,15 @@ class XdsResolver : public Resolver { // Returns the weighted_clusters action name to use from // weighted_cluster_index_map_ for a WeightedClusters route action. std::string WeightedClustersActionName( - const std::vector& weighted_clusters); + const std::vector& + weighted_clusters); // Updates weighted_cluster_index_map_ that will // determine the names of the WeightedCluster actions for the current update. - void UpdateWeightedClusterIndexMap(const std::vector& routes); + void UpdateWeightedClusterIndexMap(const XdsApi::RdsUpdate& rds_update); - // Create the service config generated by the list of routes. - grpc_error* CreateServiceConfig(const std::vector& routes, + // Create the service config generated by the RdsUpdate. + grpc_error* CreateServiceConfig(const XdsApi::RdsUpdate& rds_update, RefCountedPtr* service_config); std::string server_name_; @@ -130,15 +131,15 @@ class XdsResolver : public Resolver { // void XdsResolver::ListenerWatcher::OnListenerChanged( - std::vector routes) { + XdsApi::LdsUpdate listener_data) { if (resolver_->xds_client_ == nullptr) return; if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) { gpr_log(GPR_INFO, "[xds_resolver %p] received updated listener data", resolver_.get()); } Result result; - grpc_error* error = - resolver_->CreateServiceConfig(routes, &result.service_config); + grpc_error* error = resolver_->CreateServiceConfig(*listener_data.rds_update, + &result.service_config); if (error != GRPC_ERROR_NONE) { OnError(error); return; @@ -213,20 +214,23 @@ std::string CreateServiceConfigActionCluster(const std::string& cluster_name) { } std::string CreateServiceConfigRoute(const std::string& action_name, - const XdsApi::Route& route) { + const XdsApi::RdsUpdate::RdsRoute& route) { std::vector headers; for (const auto& header : route.matchers.header_matchers) { std::string header_matcher; switch (header.type) { - case XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::EXACT: + case XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher:: + HeaderMatcherType::EXACT: header_matcher = absl::StrFormat(" \"exact_match\": \"%s\"", header.string_matcher); break; - case XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::REGEX: + case XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher:: + HeaderMatcherType::REGEX: header_matcher = absl::StrFormat(" \"regex_match\": \"%s\"", header.regex_match->pattern()); break; - case XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::RANGE: + case XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher:: + HeaderMatcherType::RANGE: header_matcher = absl::StrFormat( " \"range_match\":{\n" " \"start\":%d,\n" @@ -234,16 +238,19 @@ std::string CreateServiceConfigRoute(const std::string& action_name, " }", header.range_start, header.range_end); break; - case XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::PRESENT: + case XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher:: + HeaderMatcherType::PRESENT: header_matcher = absl::StrFormat(" \"present_match\": %s", header.present_match ? "true" : "false"); break; - case XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::PREFIX: + case XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher:: + HeaderMatcherType::PREFIX: header_matcher = absl::StrFormat( " \"prefix_match\": \"%s\"", header.string_matcher); break; - case XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::SUFFIX: + case XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher:: + HeaderMatcherType::SUFFIX: header_matcher = absl::StrFormat( " \"suffix_match\": \"%s\"", header.string_matcher); break; @@ -274,15 +281,18 @@ std::string CreateServiceConfigRoute(const std::string& action_name, } std::string path_match_str; switch (route.matchers.path_matcher.type) { - case XdsApi::Route::Matchers::PathMatcher::PathMatcherType::PREFIX: + case XdsApi::RdsUpdate::RdsRoute::Matchers::PathMatcher::PathMatcherType:: + PREFIX: path_match_str = absl::StrFormat( "\"prefix\": \"%s\",\n", route.matchers.path_matcher.string_matcher); break; - case XdsApi::Route::Matchers::PathMatcher::PathMatcherType::PATH: + case XdsApi::RdsUpdate::RdsRoute::Matchers::PathMatcher::PathMatcherType:: + PATH: path_match_str = absl::StrFormat( "\"path\": \"%s\",\n", route.matchers.path_matcher.string_matcher); break; - case XdsApi::Route::Matchers::PathMatcher::PathMatcherType::REGEX: + case XdsApi::RdsUpdate::RdsRoute::Matchers::PathMatcher::PathMatcherType:: + REGEX: path_match_str = absl::StrFormat("\"regex\": \"%s\",\n", route.matchers.path_matcher.regex_matcher->pattern()); @@ -306,7 +316,7 @@ std::string CreateServiceConfigRoute(const std::string& action_name, // Create the service config for one weighted cluster. std::string CreateServiceConfigActionWeightedCluster( const std::string& name, - const std::vector& clusters) { + const std::vector& clusters) { std::vector config_parts; config_parts.push_back( absl::StrFormat(" \"weighted:%s\":{\n" @@ -344,7 +354,8 @@ struct WeightedClustersKeys { // Returns the cluster names and weights key or the cluster names only key. WeightedClustersKeys GetWeightedClustersKey( - const std::vector& weighted_clusters) { + const std::vector& + weighted_clusters) { std::set cluster_names; std::set cluster_weights; for (const auto& cluster_weight : weighted_clusters) { @@ -357,7 +368,8 @@ WeightedClustersKeys GetWeightedClustersKey( } std::string XdsResolver::WeightedClustersActionName( - const std::vector& weighted_clusters) { + const std::vector& + weighted_clusters) { WeightedClustersKeys keys = GetWeightedClustersKey(weighted_clusters); auto cluster_names_map_it = weighted_cluster_index_map_.find(keys.cluster_names_key); @@ -372,13 +384,13 @@ std::string XdsResolver::WeightedClustersActionName( } void XdsResolver::UpdateWeightedClusterIndexMap( - const std::vector& routes) { + const XdsApi::RdsUpdate& rds_update) { // Construct a list of unique WeightedCluster // actions which we need to process: to find action names std::map actions_to_process; - for (const auto& route : routes) { + for (const auto& route : rds_update.routes) { if (!route.weighted_clusters.empty()) { WeightedClustersKeys keys = GetWeightedClustersKey(route.weighted_clusters); @@ -455,13 +467,13 @@ void XdsResolver::UpdateWeightedClusterIndexMap( } grpc_error* XdsResolver::CreateServiceConfig( - const std::vector& routes, + const XdsApi::RdsUpdate& rds_update, RefCountedPtr* service_config) { - UpdateWeightedClusterIndexMap(routes); + UpdateWeightedClusterIndexMap(rds_update); std::vector actions_vector; std::vector route_table; std::set actions_set; - for (const auto& route : routes) { + for (const auto& route : rds_update.routes) { const std::string action_name = route.weighted_clusters.empty() ? route.cluster_name diff --git a/src/core/ext/xds/xds_api.cc b/src/core/ext/xds/xds_api.cc index 5c21e580167..45f5a76f07a 100644 --- a/src/core/ext/xds/xds_api.cc +++ b/src/core/ext/xds/xds_api.cc @@ -74,10 +74,11 @@ namespace grpc_core { // -// XdsApi::Route::Matchers::PathMatcher +// XdsApi::RdsUpdate::RdsRoute::Matchers::PathMatcher // -XdsApi::Route::Matchers::PathMatcher::PathMatcher(const PathMatcher& other) +XdsApi::RdsUpdate::RdsRoute::Matchers::PathMatcher::PathMatcher( + const PathMatcher& other) : type(other.type) { if (type == PathMatcherType::REGEX) { regex_matcher = absl::make_unique(other.regex_matcher->pattern()); @@ -86,8 +87,9 @@ XdsApi::Route::Matchers::PathMatcher::PathMatcher(const PathMatcher& other) } } -XdsApi::Route::Matchers::PathMatcher& XdsApi::Route::Matchers::PathMatcher:: -operator=(const PathMatcher& other) { +XdsApi::RdsUpdate::RdsRoute::Matchers::PathMatcher& +XdsApi::RdsUpdate::RdsRoute::Matchers::PathMatcher::operator=( + const PathMatcher& other) { type = other.type; if (type == PathMatcherType::REGEX) { regex_matcher = absl::make_unique(other.regex_matcher->pattern()); @@ -97,7 +99,7 @@ operator=(const PathMatcher& other) { return *this; } -bool XdsApi::Route::Matchers::PathMatcher::operator==( +bool XdsApi::RdsUpdate::RdsRoute::Matchers::PathMatcher::operator==( const PathMatcher& other) const { if (type != other.type) return false; if (type == PathMatcherType::REGEX) { @@ -110,7 +112,8 @@ bool XdsApi::Route::Matchers::PathMatcher::operator==( return string_matcher == other.string_matcher; } -std::string XdsApi::Route::Matchers::PathMatcher::ToString() const { +std::string XdsApi::RdsUpdate::RdsRoute::Matchers::PathMatcher::ToString() + const { std::string path_type_string; switch (type) { case PathMatcherType::PATH: @@ -132,10 +135,10 @@ std::string XdsApi::Route::Matchers::PathMatcher::ToString() const { } // -// XdsApi::Route::Matchers::HeaderMatcher +// XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher // -XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcher( +XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher::HeaderMatcher( const HeaderMatcher& other) : name(other.name), type(other.type), invert_match(other.invert_match) { switch (type) { @@ -154,8 +157,9 @@ XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcher( } } -XdsApi::Route::Matchers::HeaderMatcher& XdsApi::Route::Matchers::HeaderMatcher:: -operator=(const HeaderMatcher& other) { +XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher& +XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher::operator=( + const HeaderMatcher& other) { name = other.name; type = other.type; invert_match = other.invert_match; @@ -176,7 +180,7 @@ operator=(const HeaderMatcher& other) { return *this; } -bool XdsApi::Route::Matchers::HeaderMatcher::operator==( +bool XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher::operator==( const HeaderMatcher& other) const { if (name != other.name) return false; if (type != other.type) return false; @@ -193,7 +197,8 @@ bool XdsApi::Route::Matchers::HeaderMatcher::operator==( } } -std::string XdsApi::Route::Matchers::HeaderMatcher::ToString() const { +std::string XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher::ToString() + const { switch (type) { case HeaderMatcherType::EXACT: return absl::StrFormat("Header exact match:%s %s:%s", @@ -221,11 +226,7 @@ std::string XdsApi::Route::Matchers::HeaderMatcher::ToString() const { } } -// -// XdsApi::Route -// - -std::string XdsApi::Route::Matchers::ToString() const { +std::string XdsApi::RdsUpdate::RdsRoute::Matchers::ToString() const { std::vector contents; contents.push_back(path_matcher.ToString()); for (const auto& header_it : header_matchers) { @@ -238,11 +239,11 @@ std::string XdsApi::Route::Matchers::ToString() const { return absl::StrJoin(contents, "\n"); } -std::string XdsApi::Route::ClusterWeight::ToString() const { +std::string XdsApi::RdsUpdate::RdsRoute::ClusterWeight::ToString() const { return absl::StrFormat("{cluster=%s, weight=%d}", name, weight); } -std::string XdsApi::Route::ToString() const { +std::string XdsApi::RdsUpdate::RdsRoute::ToString() const { std::vector contents; contents.push_back(matchers.ToString()); if (!cluster_name.empty()) { @@ -254,124 +255,12 @@ std::string XdsApi::Route::ToString() const { return absl::StrJoin(contents, "\n"); } -// -// XdsApi::RdsUpdate -// - std::string XdsApi::RdsUpdate::ToString() const { - std::vector 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(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; + std::vector contents; + for (const auto& route_it : routes) { + contents.push_back(route_it.ToString()); } - return target_vhost; + return absl::StrJoin(contents, ",\n"); } // @@ -1274,8 +1163,60 @@ 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::Route* route, bool* ignore_route) { + XdsApi::RdsUpdate::RdsRoute* rds_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)); @@ -1300,9 +1241,9 @@ grpc_error* RoutePathMatchParse(const envoy_config_route_v3_RouteMatch* match, return GRPC_ERROR_NONE; } } - route->matchers.path_matcher.type = - XdsApi::Route::Matchers::PathMatcher::PathMatcherType::PREFIX; - route->matchers.path_matcher.string_matcher = std::string(prefix); + rds_route->matchers.path_matcher.type = XdsApi::RdsUpdate::RdsRoute:: + Matchers::PathMatcher::PathMatcherType::PREFIX; + rds_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)); @@ -1335,9 +1276,9 @@ grpc_error* RoutePathMatchParse(const envoy_config_route_v3_RouteMatch* match, *ignore_route = true; return GRPC_ERROR_NONE; } - route->matchers.path_matcher.type = - XdsApi::Route::Matchers::PathMatcher::PathMatcherType::PATH; - route->matchers.path_matcher.string_matcher = std::string(path); + rds_route->matchers.path_matcher.type = XdsApi::RdsUpdate::RdsRoute:: + Matchers::PathMatcher::PathMatcherType::PATH; + rds_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); @@ -1349,9 +1290,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."); } - route->matchers.path_matcher.type = - XdsApi::Route::Matchers::PathMatcher::PathMatcherType::REGEX; - route->matchers.path_matcher.regex_matcher = std::move(regex); + rds_route->matchers.path_matcher.type = XdsApi::RdsUpdate::RdsRoute:: + Matchers::PathMatcher::PathMatcherType::REGEX; + rds_route->matchers.path_matcher.regex_matcher = std::move(regex); } else { return GRPC_ERROR_CREATE_FROM_STATIC_STRING( "Invalid route path specifier specified."); @@ -1360,18 +1301,19 @@ grpc_error* RoutePathMatchParse(const envoy_config_route_v3_RouteMatch* match, } grpc_error* RouteHeaderMatchersParse( - const envoy_config_route_v3_RouteMatch* match, XdsApi::Route* route) { + const envoy_config_route_v3_RouteMatch* match, + XdsApi::RdsUpdate::RdsRoute* rds_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::Route::Matchers::HeaderMatcher header_matcher; + XdsApi::RdsUpdate::RdsRoute::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::Route::Matchers::HeaderMatcher::HeaderMatcherType::EXACT; + header_matcher.type = XdsApi::RdsUpdate::RdsRoute::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( @@ -1386,12 +1328,12 @@ grpc_error* RouteHeaderMatchersParse( return GRPC_ERROR_CREATE_FROM_STATIC_STRING( "Invalid regex string specified in header matcher."); } - header_matcher.type = - XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::REGEX; + header_matcher.type = XdsApi::RdsUpdate::RdsRoute::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::Route::Matchers::HeaderMatcher::HeaderMatcherType::RANGE; + header_matcher.type = XdsApi::RdsUpdate::RdsRoute::Matchers:: + HeaderMatcher::HeaderMatcherType::RANGE; const envoy_type_v3_Int64Range* range_matcher = envoy_config_route_v3_HeaderMatcher_range_match(header); header_matcher.range_start = @@ -1403,18 +1345,18 @@ grpc_error* RouteHeaderMatchersParse( "cannot be smaller than start."); } } else if (envoy_config_route_v3_HeaderMatcher_has_present_match(header)) { - header_matcher.type = - XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::PRESENT; + header_matcher.type = XdsApi::RdsUpdate::RdsRoute::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::Route::Matchers::HeaderMatcher::HeaderMatcherType::PREFIX; + header_matcher.type = XdsApi::RdsUpdate::RdsRoute::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::Route::Matchers::HeaderMatcher::HeaderMatcherType::SUFFIX; + header_matcher.type = XdsApi::RdsUpdate::RdsRoute::Matchers:: + HeaderMatcher::HeaderMatcherType::SUFFIX; header_matcher.string_matcher = UpbStringToStdString( envoy_config_route_v3_HeaderMatcher_suffix_match(header)); } else { @@ -1423,13 +1365,14 @@ grpc_error* RouteHeaderMatchersParse( } header_matcher.invert_match = envoy_config_route_v3_HeaderMatcher_invert_match(header); - route->matchers.header_matchers.emplace_back(std::move(header_matcher)); + rds_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::Route* route) { + const envoy_config_route_v3_RouteMatch* match, + XdsApi::RdsUpdate::RdsRoute* rds_route) { const envoy_config_core_v3_RuntimeFractionalPercent* runtime_fraction = envoy_config_route_v3_RouteMatch_runtime_fraction(match); if (runtime_fraction != nullptr) { @@ -1455,25 +1398,26 @@ grpc_error* RouteRuntimeFractionParse( return GRPC_ERROR_CREATE_FROM_STATIC_STRING( "Unknown denominator type"); } - route->matchers.fraction_per_million = numerator; + rds_route->matchers.fraction_per_million = numerator; } } return GRPC_ERROR_NONE; } -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)) { +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)) { 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_msg); + envoy_config_route_v3_Route_route(route); // Get the cluster or weighted_clusters in the RouteAction. if (envoy_config_route_v3_RouteAction_has_cluster(route_action)) { - route->cluster_name = UpbStringToStdString( + rds_route->cluster_name = UpbStringToStdString( envoy_config_route_v3_RouteAction_cluster(route_action)); - if (route->cluster_name.size() == 0) { + if (rds_route->cluster_name.size() == 0) { return GRPC_ERROR_CREATE_FROM_STATIC_STRING( "RouteAction cluster contains empty cluster name."); } @@ -1495,7 +1439,7 @@ grpc_error* RouteActionParse(const envoy_config_route_v3_Route* route_msg, for (size_t j = 0; j < clusters_size; ++j) { const envoy_config_route_v3_WeightedCluster_ClusterWeight* cluster_weight = clusters[j]; - XdsApi::Route::ClusterWeight cluster; + XdsApi::RdsUpdate::RdsRoute::ClusterWeight cluster; cluster.name = UpbStringToStdString( envoy_config_route_v3_WeightedCluster_ClusterWeight_name( cluster_weight)); @@ -1513,13 +1457,13 @@ grpc_error* RouteActionParse(const envoy_config_route_v3_Route* route_msg, } cluster.weight = google_protobuf_UInt32Value_value(weight); sum_of_weights += cluster.weight; - route->weighted_clusters.emplace_back(std::move(cluster)); + rds_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 (route->weighted_clusters.empty()) { + if (rds_route->weighted_clusters.empty()) { return GRPC_ERROR_CREATE_FROM_STATIC_STRING( "RouteAction weighted_cluster has no valid clusters specified."); } @@ -1534,73 +1478,101 @@ grpc_error* RouteActionParse(const envoy_config_route_v3_Route* route_msg, grpc_error* RouteConfigParse( XdsClient* client, TraceFlag* tracer, const envoy_config_route_v3_RouteConfiguration* route_config, - XdsApi::RdsUpdate* rds_update) { + const std::string& expected_server_name, 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) { - std::string domain_pattern = UpbStringToStdString(domains[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. const MatchType match_type = DomainPatternMatchType(domain_pattern); if (match_type == INVALID_MATCH) { return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Invalid domain pattern."); } - 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(envoy_config_route_v3_RouteMatch_query_parameters( - match, &query_parameters_size)); - if (query_parameters_size > 0) { + if (match_type > best_match_type) continue; + if (match_type == best_match_type && + domain_pattern.size() <= longest_match) { 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."); + // Skip if match fails. + if (!DomainMatch(match_type, domain_pattern, expected_server_name)) { + continue; } - vhost.routes.emplace_back(std::move(route)); + // 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(envoy_config_route_v3_RouteMatch_query_parameters( + match, &query_parameters_size)); + if (query_parameters_size > 0) { + continue; } - if (vhost.routes.empty()) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("No valid routes specified."); + 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."); } + 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; } @@ -1658,11 +1630,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, &rds_update); + grpc_error* error = RouteConfigParse(client, tracer, route_config, + expected_server_name, &rds_update); if (error != GRPC_ERROR_NONE) return error; lds_update->emplace(); - (*lds_update)->rds_update = std::move(rds_update); + (*lds_update)->rds_update.emplace(std::move(rds_update)); return GRPC_ERROR_NONE; } // Validate that RDS must be used to get the route_config dynamically. @@ -1699,6 +1671,7 @@ 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& expected_route_configuration_names, absl::optional* rds_update, upb_arena* arena) { // Get the resources from the response. @@ -1730,8 +1703,8 @@ grpc_error* RdsResponseParse( } // Parse the route_config. XdsApi::RdsUpdate local_rds_update; - grpc_error* error = - RouteConfigParse(client, tracer, route_config, &local_rds_update); + grpc_error* error = RouteConfigParse( + client, tracer, route_config, expected_server_name, &local_rds_update); if (error != GRPC_ERROR_NONE) return error; rds_update->emplace(std::move(local_rds_update)); return GRPC_ERROR_NONE; @@ -2070,9 +2043,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_route_configuration_names, - &result.rds_update, arena.ptr()); + result.parse_error = RdsResponseParse( + client_, tracer_, response, expected_server_name, + 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, diff --git a/src/core/ext/xds/xds_api.h b/src/core/ext/xds/xds_api.h index 265fec43c09..3ec720e7104 100644 --- a/src/core/ext/xds/xds_api.h +++ b/src/core/ext/xds/xds_api.h @@ -46,109 +46,98 @@ class XdsApi { static const char* kCdsTypeUrl; static const char* kEdsTypeUrl; - // TODO(donnadionne): When we can use absl::variant<>, consider using that - // for: PathMatcher, HeaderMatcher, cluster_name and weighted_clusters - struct Route { - // Matchers for this route. - struct Matchers { - struct PathMatcher { - enum class PathMatcherType { - PATH, // path stored in string_matcher field - PREFIX, // prefix stored in string_matcher field - REGEX, // regex stored in regex_matcher field + struct RdsUpdate { + // TODO(donnadionne): When we can use absl::variant<>, consider using that + // for: PathMatcher, HeaderMatcher, cluster_name and weighted_clusters + struct RdsRoute { + // Matchers for this route. + struct Matchers { + struct PathMatcher { + enum class PathMatcherType { + PATH, // path stored in string_matcher field + PREFIX, // prefix stored in string_matcher field + REGEX, // regex stored in regex_matcher field + }; + PathMatcherType type; + std::string string_matcher; + std::unique_ptr regex_matcher; + + PathMatcher() = default; + PathMatcher(const PathMatcher& other); + PathMatcher& operator=(const PathMatcher& other); + bool operator==(const PathMatcher& other) const; + std::string ToString() const; }; - PathMatcherType type; - std::string string_matcher; - std::unique_ptr regex_matcher; - - PathMatcher() = default; - PathMatcher(const PathMatcher& other); - PathMatcher& operator=(const PathMatcher& other); - bool operator==(const PathMatcher& other) const; - std::string ToString() const; - }; - struct HeaderMatcher { - enum class HeaderMatcherType { - EXACT, // value stored in string_matcher field - REGEX, // uses regex_match field - RANGE, // uses range_start and range_end fields - PRESENT, // uses present_match field - PREFIX, // prefix stored in string_matcher field - SUFFIX, // suffix stored in string_matcher field + struct HeaderMatcher { + enum class HeaderMatcherType { + EXACT, // value stored in string_matcher field + REGEX, // uses regex_match field + RANGE, // uses range_start and range_end fields + PRESENT, // uses present_match field + PREFIX, // prefix stored in string_matcher field + SUFFIX, // suffix stored in string_matcher field + }; + std::string name; + HeaderMatcherType type; + int64_t range_start; + int64_t range_end; + std::string string_matcher; + std::unique_ptr regex_match; + bool present_match; + // invert_match field may or may not exisit, so initialize it to + // false. + bool invert_match = false; + + HeaderMatcher() = default; + HeaderMatcher(const HeaderMatcher& other); + HeaderMatcher& operator=(const HeaderMatcher& other); + bool operator==(const HeaderMatcher& other) const; + std::string ToString() const; }; - std::string name; - HeaderMatcherType type; - int64_t range_start; - int64_t range_end; - std::string string_matcher; - std::unique_ptr regex_match; - bool present_match; - // invert_match field may or may not exisit, so initialize it to - // false. - bool invert_match = false; - - HeaderMatcher() = default; - HeaderMatcher(const HeaderMatcher& other); - HeaderMatcher& operator=(const HeaderMatcher& other); - bool operator==(const HeaderMatcher& other) const; + + PathMatcher path_matcher; + std::vector header_matchers; + absl::optional fraction_per_million; + + bool operator==(const Matchers& other) const { + return (path_matcher == other.path_matcher && + header_matchers == other.header_matchers && + fraction_per_million == other.fraction_per_million); + } std::string ToString() const; }; - PathMatcher path_matcher; - std::vector header_matchers; - absl::optional fraction_per_million; + Matchers matchers; - bool operator==(const Matchers& other) const { - return (path_matcher == other.path_matcher && - header_matchers == other.header_matchers && - fraction_per_million == other.fraction_per_million); - } - std::string ToString() const; - }; - - Matchers matchers; + // Action for this route. + // TODO(roth): When we can use absl::variant<>, consider using that + // here, to enforce the fact that only one of the two fields can be set. + std::string cluster_name; + struct ClusterWeight { + std::string name; + uint32_t weight; + bool operator==(const ClusterWeight& other) const { + return (name == other.name && weight == other.weight); + } + std::string ToString() const; + }; + std::vector weighted_clusters; - // Action for this route. - // TODO(roth): When we can use absl::variant<>, consider using that - // here, to enforce the fact that only one of the two fields can be set. - std::string cluster_name; - struct ClusterWeight { - std::string name; - uint32_t weight; - bool operator==(const ClusterWeight& other) const { - return (name == other.name && weight == other.weight); + bool operator==(const RdsRoute& other) const { + return (matchers == other.matchers && + cluster_name == other.cluster_name && + weighted_clusters == other.weighted_clusters); } std::string ToString() const; }; - std::vector weighted_clusters; - - bool operator==(const Route& other) const { - return (matchers == other.matchers && - cluster_name == other.cluster_name && - weighted_clusters == other.weighted_clusters); - } - std::string ToString() const; - }; - - struct RdsUpdate { - struct VirtualHost { - std::vector domains; - std::vector routes; - - bool operator==(const VirtualHost& other) const { - return domains == other.domains && routes == other.routes; - } - }; - std::vector virtual_hosts; + std::vector routes; bool operator==(const RdsUpdate& other) const { - return virtual_hosts == other.virtual_hosts; + return routes == other.routes; } std::string ToString() const; - const VirtualHost* FindVirtualHostForDomain( - const std::string& domain) const; }; // TODO(roth): When we can use absl::variant<>, consider using that @@ -156,8 +145,8 @@ class XdsApi { struct LdsUpdate { // The name to use in the RDS request. std::string route_config_name; - // The RouteConfiguration to use for this listener. - // Present only if it is inlined in the LDS response. + // The name to use in the CDS request. Present if the LDS response has it + // inlined. absl::optional rds_update; bool operator==(const LdsUpdate& other) const { diff --git a/src/core/ext/xds/xds_client.cc b/src/core/ext/xds/xds_client.cc index b582a0c5c32..67ee5fd2177 100644 --- a/src/core/ext/xds/xds_client.cc +++ b/src/core/ext/xds/xds_client.cc @@ -892,8 +892,13 @@ void XdsClient::ChannelState::AdsCallState::AcceptLdsUpdate( ? lds_update->route_config_name.c_str() : "")); if (lds_update->rds_update.has_value()) { - gpr_log(GPR_INFO, "RouteConfiguration: %s", - lds_update->rds_update->ToString().c_str()); + gpr_log(GPR_INFO, "RouteConfiguration contains %" PRIuPTR " routes", + lds_update->rds_update.value().routes.size()); + for (size_t i = 0; i < lds_update->rds_update.value().routes.size(); + ++i) { + gpr_log(GPR_INFO, "Route %" PRIuPTR ":\n%s", i, + lds_update->rds_update.value().routes[i].ToString().c_str()); + } } } auto& lds_state = state_map_[XdsApi::kLdsTypeUrl]; @@ -919,16 +924,8 @@ void XdsClient::ChannelState::AdsCallState::AcceptLdsUpdate( if (xds_client()->lds_result_->rds_update.has_value()) { // If the RouteConfiguration was found inlined in LDS response, notify // the watcher immediately. - const XdsApi::RdsUpdate::VirtualHost* vhost = - xds_client()->lds_result_->rds_update->FindVirtualHostForDomain( - xds_client()->server_name_); - if (vhost == nullptr) { - xds_client()->listener_watcher_->OnError( - GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "no VirtualHost found for domain")); - } else { - xds_client()->listener_watcher_->OnListenerChanged(vhost->routes); - } + xds_client()->listener_watcher_->OnListenerChanged( + *xds_client()->lds_result_); } else { // Send RDS request for dynamic resolution. Subscribe(XdsApi::kRdsTypeUrl, @@ -947,8 +944,14 @@ void XdsClient::ChannelState::AdsCallState::AcceptRdsUpdate( return; } if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) { - gpr_log(GPR_INFO, "[xds_client %p] RDS update received:\n%s", xds_client(), - rds_update->ToString().c_str()); + gpr_log(GPR_INFO, + "[xds_client %p] RDS update received; RouteConfiguration contains " + "%" PRIuPTR " routes", + this, rds_update.value().routes.size()); + for (size_t i = 0; i < rds_update.value().routes.size(); ++i) { + gpr_log(GPR_INFO, "Route %" PRIuPTR ":\n%s", i, + rds_update.value().routes[i].ToString().c_str()); + } } auto& rds_state = state_map_[XdsApi::kRdsTypeUrl]; auto& state = @@ -966,16 +969,9 @@ void XdsClient::ChannelState::AdsCallState::AcceptRdsUpdate( } xds_client()->rds_result_ = std::move(rds_update); // Notify the watcher. - const XdsApi::RdsUpdate::VirtualHost* vhost = - xds_client()->rds_result_->FindVirtualHostForDomain( - xds_client()->server_name_); - if (vhost == nullptr) { - xds_client()->listener_watcher_->OnError( - GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "no VirtualHost found for domain")); - } else { - xds_client()->listener_watcher_->OnListenerChanged(vhost->routes); - } + XdsApi::LdsUpdate lds_result = *xds_client()->lds_result_; + lds_result.rds_update = xds_client()->rds_result_; + xds_client()->listener_watcher_->OnListenerChanged(lds_result); } void XdsClient::ChannelState::AdsCallState::AcceptCdsUpdate( diff --git a/src/core/ext/xds/xds_client.h b/src/core/ext/xds/xds_client.h index c9184348a8c..cb8864b0e27 100644 --- a/src/core/ext/xds/xds_client.h +++ b/src/core/ext/xds/xds_client.h @@ -45,7 +45,7 @@ class XdsClient : public InternallyRefCounted { public: virtual ~ListenerWatcherInterface() = default; - virtual void OnListenerChanged(std::vector routes) = 0; + virtual void OnListenerChanged(XdsApi::LdsUpdate listener_data) = 0; virtual void OnError(grpc_error* error) = 0; diff --git a/test/cpp/end2end/xds_end2end_test.cc b/test/cpp/end2end/xds_end2end_test.cc index 28820334055..b2099f12806 100644 --- a/test/cpp/end2end/xds_end2end_test.cc +++ b/test/cpp/end2end/xds_end2end_test.cc @@ -2500,7 +2500,7 @@ TEST_P(LdsRdsTest, ListenerRemoved) { AdsServiceImpl::ResponseState::ACKED); } -// Tests that LDS client ACKS but fails if matching domain can't be found in +// Tests that LDS client should send a NACK if matching domain can't be found in // the LDS response. TEST_P(LdsRdsTest, NoMatchedDomain) { RouteConfiguration route_config = @@ -2511,10 +2511,10 @@ TEST_P(LdsRdsTest, NoMatchedDomain) { SetNextResolution({}); SetNextResolutionForLbChannelAllBalancers(); CheckRpcSendFailure(); - // Do a bit of polling, to allow the ACK to get to the ADS server. - channel_->WaitForConnected(grpc_timeout_milliseconds_to_deadline(10)); const auto& response_state = RouteConfigurationResponseState(0); - EXPECT_EQ(response_state.state, AdsServiceImpl::ResponseState::ACKED); + EXPECT_EQ(response_state.state, AdsServiceImpl::ResponseState::NACKED); + EXPECT_EQ(response_state.error_message, + "No matched virtual host found in the route config."); } // Tests that LDS client should choose the virtual host with matching domain if @@ -2525,6 +2525,10 @@ TEST_P(LdsRdsTest, ChooseMatchedDomain) { *(route_config.add_virtual_hosts()) = route_config.virtual_hosts(0); route_config.mutable_virtual_hosts(0)->clear_domains(); route_config.mutable_virtual_hosts(0)->add_domains("unmatched_domain"); + route_config.mutable_virtual_hosts(0) + ->mutable_routes(0) + ->mutable_route() + ->mutable_cluster_header(); SetRouteConfiguration(0, route_config); SetNextResolution({}); SetNextResolutionForLbChannelAllBalancers();