xds: Don't identify the needed VirtualHost at validation time.

pull/23918/head
Mark D. Roth 4 years ago
parent 7c343ee45e
commit b0a8b4606c
  1. 88
      src/core/ext/filters/client_channel/lb_policy/xds/xds_routing.cc
  2. 64
      src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc
  3. 425
      src/core/ext/xds/xds_api.cc
  4. 167
      src/core/ext/xds/xds_api.h
  5. 44
      src/core/ext/xds/xds_client.cc
  6. 2
      src/core/ext/xds/xds_client.h
  7. 12
      test/cpp/end2end/xds_end2end_test.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<Route>;
@ -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<ChildPickerWrapper> 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<XdsRoutingLbConfig> 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<absl::string_view> 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<absl::string_view> 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<XdsApi::RdsUpdate::RdsRoute::Matchers::HeaderMatcher>&
header_matchers,
const std::vector<XdsApi::Route::Matchers::HeaderMatcher>& 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<RE2>(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<RE2>(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();
}

@ -74,7 +74,7 @@ class XdsResolver : public Resolver {
public:
explicit ListenerWatcher(RefCountedPtr<XdsResolver> resolver)
: resolver_(std::move(resolver)) {}
void OnListenerChanged(XdsApi::LdsUpdate listener_data) override;
void OnListenerChanged(std::vector<XdsApi::Route> 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<XdsApi::RdsUpdate::RdsRoute::ClusterWeight>&
weighted_clusters);
const std::vector<XdsApi::Route::ClusterWeight>& 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<XdsApi::Route>& 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<XdsApi::Route>& routes,
RefCountedPtr<ServiceConfig>* service_config);
std::string server_name_;
@ -131,15 +130,15 @@ class XdsResolver : public Resolver {
//
void XdsResolver::ListenerWatcher::OnListenerChanged(
XdsApi::LdsUpdate listener_data) {
std::vector<XdsApi::Route> 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<std::string> 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<XdsApi::RdsUpdate::RdsRoute::ClusterWeight>& clusters) {
const std::vector<XdsApi::Route::ClusterWeight>& clusters) {
std::vector<std::string> 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<XdsApi::RdsUpdate::RdsRoute::ClusterWeight>&
weighted_clusters) {
const std::vector<XdsApi::Route::ClusterWeight>& weighted_clusters) {
std::set<std::string> cluster_names;
std::set<std::string> cluster_weights;
for (const auto& cluster_weight : weighted_clusters) {
@ -368,8 +357,7 @@ WeightedClustersKeys GetWeightedClustersKey(
}
std::string XdsResolver::WeightedClustersActionName(
const std::vector<XdsApi::RdsUpdate::RdsRoute::ClusterWeight>&
weighted_clusters) {
const std::vector<XdsApi::Route::ClusterWeight>& 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<XdsApi::Route>& routes) {
// Construct a list of unique WeightedCluster
// actions which we need to process: to find action names
std::map<std::string /* cluster_weights_key */,
std::string /* cluster_names_key */>
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<XdsApi::Route>& routes,
RefCountedPtr<ServiceConfig>* service_config) {
UpdateWeightedClusterIndexMap(rds_update);
UpdateWeightedClusterIndexMap(routes);
std::vector<std::string> actions_vector;
std::vector<std::string> route_table;
std::set<std::string> 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

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

@ -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<RE2> 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<RE2> 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<HeaderMatcher> header_matchers;
absl::optional<uint32_t> 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<RE2> 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<RE2> 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<ClusterWeight> 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<HeaderMatcher> header_matchers;
absl::optional<uint32_t> 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<RdsRoute> 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<ClusterWeight> 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<std::string> domains;
std::vector<Route> routes;
bool operator==(const VirtualHost& other) const {
return domains == other.domains && routes == other.routes;
}
};
std::vector<VirtualHost> 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<RdsUpdate> rds_update;
bool operator==(const LdsUpdate& other) const {

@ -892,13 +892,8 @@ void XdsClient::ChannelState::AdsCallState::AcceptLdsUpdate(
? lds_update->route_config_name.c_str()
: "<inlined>"));
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(

@ -45,7 +45,7 @@ class XdsClient : public InternallyRefCounted<XdsClient> {
public:
virtual ~ListenerWatcherInterface() = default;
virtual void OnListenerChanged(XdsApi::LdsUpdate listener_data) = 0;
virtual void OnListenerChanged(std::vector<XdsApi::Route> routes) = 0;
virtual void OnError(grpc_error* error) = 0;

@ -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();

Loading…
Cancel
Save