From b0a8b4606c7e500ce12e44b32d1771e2d7db0341 Mon Sep 17 00:00:00 2001 From: "Mark D. Roth" Date: Fri, 21 Aug 2020 11:39:35 -0700 Subject: [PATCH] xds: Don't identify the needed VirtualHost at validation time. --- .../lb_policy/xds/xds_routing.cc | 88 ++-- .../resolver/xds/xds_resolver.cc | 64 ++- src/core/ext/xds/xds_api.cc | 425 ++++++++++-------- src/core/ext/xds/xds_api.h | 167 +++---- src/core/ext/xds/xds_client.cc | 44 +- src/core/ext/xds/xds_client.h | 2 +- test/cpp/end2end/xds_end2end_test.cc | 12 +- 7 files changed, 409 insertions(+), 393 deletions(-) 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 883dcfa55ad..22de026aefa 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::RdsUpdate::RdsRoute::Matchers matchers; + XdsApi::Route::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::RdsUpdate::RdsRoute::Matchers* matchers; + const XdsApi::Route::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::RdsUpdate::RdsRoute::Matchers from it. + // XdsApi::Route::Matchers from it. RefCountedPtr config_; }; @@ -222,18 +222,14 @@ class XdsRoutingLb : public LoadBalancingPolicy { // XdsRoutingLb::RoutePicker // -bool PathMatch( - const absl::string_view& path, - const XdsApi::RdsUpdate::RdsRoute::Matchers::PathMatcher& path_matcher) { +bool PathMatch(const absl::string_view& path, + const XdsApi::Route::Matchers::PathMatcher& path_matcher) { switch (path_matcher.type) { - case XdsApi::RdsUpdate::RdsRoute::Matchers::PathMatcher::PathMatcherType:: - PREFIX: + case XdsApi::Route::Matchers::PathMatcher::PathMatcherType::PREFIX: return absl::StartsWith(path, path_matcher.string_matcher); - case XdsApi::RdsUpdate::RdsRoute::Matchers::PathMatcher::PathMatcherType:: - PATH: + case XdsApi::Route::Matchers::PathMatcher::PathMatcherType::PATH: return path == path_matcher.string_matcher; - case XdsApi::RdsUpdate::RdsRoute::Matchers::PathMatcher::PathMatcherType:: - REGEX: + case XdsApi::Route::Matchers::PathMatcher::PathMatcherType::REGEX: return RE2::FullMatch(path.data(), *path_matcher.regex_matcher); default: return false; @@ -262,7 +258,7 @@ absl::optional GetMetadataValue( } bool HeaderMatchHelper( - const XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher& header_matcher, + const XdsApi::Route::Matchers::HeaderMatcher& header_matcher, LoadBalancingPolicy::MetadataInterface* initial_metadata) { std::string concatenated_value; absl::optional value; @@ -279,8 +275,8 @@ bool HeaderMatchHelper( &concatenated_value); } if (!value.has_value()) { - if (header_matcher.type == XdsApi::RdsUpdate::RdsRoute::Matchers:: - HeaderMatcher::HeaderMatcherType::PRESENT) { + if (header_matcher.type == + XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::PRESENT) { return !header_matcher.present_match; } else { // For all other header matcher types, we need the header value to @@ -289,25 +285,20 @@ bool HeaderMatchHelper( } } switch (header_matcher.type) { - case XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher:: - HeaderMatcherType::EXACT: + case XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::EXACT: return value.value() == header_matcher.string_matcher; - case XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher:: - HeaderMatcherType::REGEX: + case XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::REGEX: return RE2::FullMatch(value.value().data(), *header_matcher.regex_match); - case XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher:: - HeaderMatcherType::RANGE: + case XdsApi::Route::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::RdsUpdate::RdsRoute::Matchers::HeaderMatcher:: - HeaderMatcherType::PREFIX: + case XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::PREFIX: return absl::StartsWith(value.value(), header_matcher.string_matcher); - case XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher:: - HeaderMatcherType::SUFFIX: + case XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::SUFFIX: return absl::EndsWith(value.value(), header_matcher.string_matcher); default: return false; @@ -315,8 +306,7 @@ 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); @@ -865,8 +855,8 @@ class XdsRoutingLbFactory : public LoadBalancingPolicyFactory { "field:prefix error: should be string")); } else { path_matcher_seen = true; - route->matchers.path_matcher.type = XdsApi::RdsUpdate::RdsRoute:: - Matchers::PathMatcher::PathMatcherType::PREFIX; + route->matchers.path_matcher.type = + XdsApi::Route::Matchers::PathMatcher::PathMatcherType::PREFIX; route->matchers.path_matcher.string_matcher = it->second.string_value(); } } @@ -881,8 +871,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::RdsUpdate::RdsRoute:: - Matchers::PathMatcher::PathMatcherType::PATH; + route->matchers.path_matcher.type = + XdsApi::Route::Matchers::PathMatcher::PathMatcherType::PATH; route->matchers.path_matcher.string_matcher = it->second.string_value(); } @@ -899,8 +889,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::RdsUpdate::RdsRoute:: - Matchers::PathMatcher::PathMatcherType::REGEX; + route->matchers.path_matcher.type = + XdsApi::Route::Matchers::PathMatcher::PathMatcherType::REGEX; route->matchers.path_matcher.regex_matcher = absl::make_unique(it->second.string_value()); } @@ -925,8 +915,8 @@ class XdsRoutingLbFactory : public LoadBalancingPolicyFactory { "value should be of type object")); } else { route->matchers.header_matchers.emplace_back(); - XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher& - header_matcher = route->matchers.header_matchers.back(); + XdsApi::Route::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( @@ -960,8 +950,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::RdsUpdate::RdsRoute::Matchers:: - HeaderMatcher::HeaderMatcherType::EXACT; + header_matcher.type = XdsApi::Route::Matchers::HeaderMatcher:: + HeaderMatcherType::EXACT; header_matcher.string_matcher = header_it->second.string_value(); } @@ -978,8 +968,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::RdsUpdate::RdsRoute::Matchers:: - HeaderMatcher::HeaderMatcherType::REGEX; + header_matcher.type = XdsApi::Route::Matchers::HeaderMatcher:: + HeaderMatcherType::REGEX; header_matcher.regex_match = absl::make_unique(header_it->second.string_value()); } @@ -1025,8 +1015,8 @@ class XdsRoutingLbFactory : public LoadBalancingPolicyFactory { "field:end missing")); } if (header_matcher.range_end > header_matcher.range_start) { - header_matcher.type = XdsApi::RdsUpdate::RdsRoute:: - Matchers::HeaderMatcher::HeaderMatcherType::RANGE; + header_matcher.type = XdsApi::Route::Matchers:: + HeaderMatcher::HeaderMatcherType::RANGE; } } } @@ -1040,12 +1030,12 @@ class XdsRoutingLbFactory : public LoadBalancingPolicyFactory { } else { header_matcher_seen = true; if (header_it->second.type() == Json::Type::JSON_TRUE) { - header_matcher.type = XdsApi::RdsUpdate::RdsRoute::Matchers:: - HeaderMatcher::HeaderMatcherType::PRESENT; + header_matcher.type = XdsApi::Route::Matchers::HeaderMatcher:: + HeaderMatcherType::PRESENT; header_matcher.present_match = true; } else if (header_it->second.type() == Json::Type::JSON_FALSE) { - header_matcher.type = XdsApi::RdsUpdate::RdsRoute::Matchers:: - HeaderMatcher::HeaderMatcherType::PRESENT; + header_matcher.type = XdsApi::Route::Matchers::HeaderMatcher:: + HeaderMatcherType::PRESENT; header_matcher.present_match = false; } else { error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( @@ -1065,8 +1055,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::RdsUpdate::RdsRoute::Matchers:: - HeaderMatcher::HeaderMatcherType::PREFIX; + header_matcher.type = XdsApi::Route::Matchers::HeaderMatcher:: + HeaderMatcherType::PREFIX; header_matcher.string_matcher = header_it->second.string_value(); } @@ -1084,8 +1074,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::RdsUpdate::RdsRoute::Matchers:: - HeaderMatcher::HeaderMatcherType::SUFFIX; + header_matcher.type = XdsApi::Route::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 c0b93dc69ed..d522ce3a77a 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(XdsApi::LdsUpdate listener_data) override; + void OnListenerChanged(std::vector routes) override; void OnError(grpc_error* error) override; void OnResourceDoesNotExist() override; @@ -92,15 +92,14 @@ 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 XdsApi::RdsUpdate& rds_update); + void UpdateWeightedClusterIndexMap(const std::vector& routes); - // Create the service config generated by the RdsUpdate. - grpc_error* CreateServiceConfig(const XdsApi::RdsUpdate& rds_update, + // Create the service config generated by the list of routes. + grpc_error* CreateServiceConfig(const std::vector& routes, RefCountedPtr* service_config); std::string server_name_; @@ -131,15 +130,15 @@ class XdsResolver : public Resolver { // void XdsResolver::ListenerWatcher::OnListenerChanged( - XdsApi::LdsUpdate listener_data) { + std::vector routes) { 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(*listener_data.rds_update, - &result.service_config); + grpc_error* error = + resolver_->CreateServiceConfig(routes, &result.service_config); if (error != GRPC_ERROR_NONE) { OnError(error); return; @@ -214,23 +213,20 @@ std::string CreateServiceConfigActionCluster(const std::string& cluster_name) { } std::string CreateServiceConfigRoute(const std::string& action_name, - const XdsApi::RdsUpdate::RdsRoute& route) { + const XdsApi::Route& route) { std::vector headers; for (const auto& header : route.matchers.header_matchers) { std::string header_matcher; switch (header.type) { - case XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher:: - HeaderMatcherType::EXACT: + case XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::EXACT: header_matcher = absl::StrFormat(" \"exact_match\": \"%s\"", header.string_matcher); break; - case XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher:: - HeaderMatcherType::REGEX: + case XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::REGEX: header_matcher = absl::StrFormat(" \"regex_match\": \"%s\"", header.regex_match->pattern()); break; - case XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher:: - HeaderMatcherType::RANGE: + case XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::RANGE: header_matcher = absl::StrFormat( " \"range_match\":{\n" " \"start\":%d,\n" @@ -238,19 +234,16 @@ std::string CreateServiceConfigRoute(const std::string& action_name, " }", header.range_start, header.range_end); break; - case XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher:: - HeaderMatcherType::PRESENT: + case XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::PRESENT: header_matcher = absl::StrFormat(" \"present_match\": %s", header.present_match ? "true" : "false"); break; - case XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher:: - HeaderMatcherType::PREFIX: + case XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::PREFIX: header_matcher = absl::StrFormat( " \"prefix_match\": \"%s\"", header.string_matcher); break; - case XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher:: - HeaderMatcherType::SUFFIX: + case XdsApi::Route::Matchers::HeaderMatcher::HeaderMatcherType::SUFFIX: header_matcher = absl::StrFormat( " \"suffix_match\": \"%s\"", header.string_matcher); break; @@ -281,18 +274,15 @@ std::string CreateServiceConfigRoute(const std::string& action_name, } std::string path_match_str; switch (route.matchers.path_matcher.type) { - case XdsApi::RdsUpdate::RdsRoute::Matchers::PathMatcher::PathMatcherType:: - PREFIX: + case XdsApi::Route::Matchers::PathMatcher::PathMatcherType::PREFIX: path_match_str = absl::StrFormat( "\"prefix\": \"%s\",\n", route.matchers.path_matcher.string_matcher); break; - case XdsApi::RdsUpdate::RdsRoute::Matchers::PathMatcher::PathMatcherType:: - PATH: + case XdsApi::Route::Matchers::PathMatcher::PathMatcherType::PATH: path_match_str = absl::StrFormat( "\"path\": \"%s\",\n", route.matchers.path_matcher.string_matcher); break; - case XdsApi::RdsUpdate::RdsRoute::Matchers::PathMatcher::PathMatcherType:: - REGEX: + case XdsApi::Route::Matchers::PathMatcher::PathMatcherType::REGEX: path_match_str = absl::StrFormat("\"regex\": \"%s\",\n", route.matchers.path_matcher.regex_matcher->pattern()); @@ -316,7 +306,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" @@ -354,8 +344,7 @@ 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) { @@ -368,8 +357,7 @@ 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); @@ -384,13 +372,13 @@ std::string XdsResolver::WeightedClustersActionName( } void XdsResolver::UpdateWeightedClusterIndexMap( - const XdsApi::RdsUpdate& rds_update) { + const std::vector& routes) { // 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 : rds_update.routes) { + for (const auto& route : routes) { if (!route.weighted_clusters.empty()) { WeightedClustersKeys keys = GetWeightedClustersKey(route.weighted_clusters); @@ -467,13 +455,13 @@ void XdsResolver::UpdateWeightedClusterIndexMap( } grpc_error* XdsResolver::CreateServiceConfig( - const XdsApi::RdsUpdate& rds_update, + const std::vector& routes, RefCountedPtr* service_config) { - UpdateWeightedClusterIndexMap(rds_update); + UpdateWeightedClusterIndexMap(routes); std::vector actions_vector; std::vector route_table; std::set actions_set; - for (const auto& route : rds_update.routes) { + for (const auto& route : 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 45f5a76f07a..5c21e580167 100644 --- a/src/core/ext/xds/xds_api.cc +++ b/src/core/ext/xds/xds_api.cc @@ -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(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(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 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 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 contents; - for (const auto& route_it : routes) { - contents.push_back(route_it.ToString()); + 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(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(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(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& expected_route_configuration_names, absl::optional* 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, diff --git a/src/core/ext/xds/xds_api.h b/src/core/ext/xds/xds_api.h index 3ec720e7104..265fec43c09 100644 --- a/src/core/ext/xds/xds_api.h +++ b/src/core/ext/xds/xds_api.h @@ -46,98 +46,109 @@ class XdsApi { static const char* kCdsTypeUrl; static const char* kEdsTypeUrl; - 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; - }; - - 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; + // 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 }; - - 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); - } + 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; }; - 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 { + 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; - uint32_t weight; - bool operator==(const ClusterWeight& other) const { - return (name == other.name && weight == other.weight); - } + 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::vector weighted_clusters; - bool operator==(const RdsRoute& other) const { - return (matchers == other.matchers && - cluster_name == other.cluster_name && - weighted_clusters == other.weighted_clusters); + 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; }; - std::vector routes; + 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; + + 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; bool operator==(const RdsUpdate& other) const { - return routes == other.routes; + return virtual_hosts == other.virtual_hosts; } std::string ToString() const; + const VirtualHost* FindVirtualHostForDomain( + const std::string& domain) const; }; // TODO(roth): When we can use absl::variant<>, consider using that @@ -145,8 +156,8 @@ class XdsApi { struct LdsUpdate { // The name to use in the RDS request. std::string route_config_name; - // The name to use in the CDS request. Present if the LDS response has it - // inlined. + // The RouteConfiguration to use for this listener. + // Present only if it is inlined in the LDS response. 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 67ee5fd2177..b582a0c5c32 100644 --- a/src/core/ext/xds/xds_client.cc +++ b/src/core/ext/xds/xds_client.cc @@ -892,13 +892,8 @@ void XdsClient::ChannelState::AdsCallState::AcceptLdsUpdate( ? lds_update->route_config_name.c_str() : "")); if (lds_update->rds_update.has_value()) { - 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()); - } + gpr_log(GPR_INFO, "RouteConfiguration: %s", + lds_update->rds_update->ToString().c_str()); } } auto& lds_state = state_map_[XdsApi::kLdsTypeUrl]; @@ -924,8 +919,16 @@ 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. - xds_client()->listener_watcher_->OnListenerChanged( - *xds_client()->lds_result_); + 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); + } } else { // Send RDS request for dynamic resolution. Subscribe(XdsApi::kRdsTypeUrl, @@ -944,14 +947,8 @@ 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; 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()); - } + gpr_log(GPR_INFO, "[xds_client %p] RDS update received:\n%s", xds_client(), + rds_update->ToString().c_str()); } auto& rds_state = state_map_[XdsApi::kRdsTypeUrl]; auto& state = @@ -969,9 +966,16 @@ void XdsClient::ChannelState::AdsCallState::AcceptRdsUpdate( } xds_client()->rds_result_ = std::move(rds_update); // Notify the watcher. - XdsApi::LdsUpdate lds_result = *xds_client()->lds_result_; - lds_result.rds_update = xds_client()->rds_result_; - xds_client()->listener_watcher_->OnListenerChanged(lds_result); + 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); + } } void XdsClient::ChannelState::AdsCallState::AcceptCdsUpdate( diff --git a/src/core/ext/xds/xds_client.h b/src/core/ext/xds/xds_client.h index cb8864b0e27..c9184348a8c 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(XdsApi::LdsUpdate listener_data) = 0; + virtual void OnListenerChanged(std::vector routes) = 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 b2099f12806..28820334055 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 should send a NACK if matching domain can't be found in +// Tests that LDS client ACKS but fails 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::NACKED); - EXPECT_EQ(response_state.error_message, - "No matched virtual host found in the route config."); + EXPECT_EQ(response_state.state, AdsServiceImpl::ResponseState::ACKED); } // Tests that LDS client should choose the virtual host with matching domain if @@ -2525,10 +2525,6 @@ 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();