xds_routing.cc
Go to the documentation of this file.
1 //
2 //
3 // Copyright 2021 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18 
20 
22 
23 #include <stdint.h>
24 #include <stdlib.h>
25 
26 #include <algorithm>
27 #include <cctype>
28 #include <utility>
29 
30 #include "absl/status/status.h"
31 #include "absl/status/statusor.h"
32 #include "absl/strings/match.h"
33 #include "absl/strings/str_cat.h"
34 
35 #include <grpc/support/log.h>
36 
40 
41 namespace grpc_core {
42 
43 namespace {
44 enum MatchType {
45  EXACT_MATCH,
46  SUFFIX_MATCH,
47  PREFIX_MATCH,
48  UNIVERSE_MATCH,
49  INVALID_MATCH,
50 };
51 
52 // Returns true if match succeeds.
53 bool DomainMatch(MatchType match_type, absl::string_view domain_pattern_in,
54  absl::string_view expected_host_name_in) {
55  // Normalize the args to lower-case. Domain matching is case-insensitive.
56  std::string domain_pattern = std::string(domain_pattern_in);
57  std::string expected_host_name = std::string(expected_host_name_in);
58  std::transform(domain_pattern.begin(), domain_pattern.end(),
59  domain_pattern.begin(),
60  [](unsigned char c) { return std::tolower(c); });
61  std::transform(expected_host_name.begin(), expected_host_name.end(),
62  expected_host_name.begin(),
63  [](unsigned char c) { return std::tolower(c); });
64  if (match_type == EXACT_MATCH) {
65  return domain_pattern == expected_host_name;
66  } else if (match_type == SUFFIX_MATCH) {
67  // Asterisk must match at least one char.
68  if (expected_host_name.size() < domain_pattern.size()) return false;
69  absl::string_view pattern_suffix(domain_pattern.c_str() + 1);
70  absl::string_view host_suffix(expected_host_name.c_str() +
71  expected_host_name.size() -
72  pattern_suffix.size());
73  return pattern_suffix == host_suffix;
74  } else if (match_type == PREFIX_MATCH) {
75  // Asterisk must match at least one char.
76  if (expected_host_name.size() < domain_pattern.size()) return false;
77  absl::string_view pattern_prefix(domain_pattern.c_str(),
78  domain_pattern.size() - 1);
79  absl::string_view host_prefix(expected_host_name.c_str(),
80  pattern_prefix.size());
81  return pattern_prefix == host_prefix;
82  } else {
83  return match_type == UNIVERSE_MATCH;
84  }
85 }
86 
87 MatchType DomainPatternMatchType(absl::string_view domain_pattern) {
88  if (domain_pattern.empty()) return INVALID_MATCH;
89  if (!absl::StrContains(domain_pattern, '*')) return EXACT_MATCH;
90  if (domain_pattern == "*") return UNIVERSE_MATCH;
91  if (domain_pattern[0] == '*') return SUFFIX_MATCH;
92  if (domain_pattern[domain_pattern.size() - 1] == '*') return PREFIX_MATCH;
93  return INVALID_MATCH;
94 }
95 
96 } // namespace
97 
99  const VirtualHostListIterator& vhost_iterator, absl::string_view domain) {
100  // Find the best matched virtual host.
101  // The search order for 4 groups of domain patterns:
102  // 1. Exact match.
103  // 2. Suffix match (e.g., "*ABC").
104  // 3. Prefix match (e.g., "ABC*").
105  // 4. Universe match (i.e., "*").
106  // Within each group, longest match wins.
107  // If the same best matched domain pattern appears in multiple virtual
108  // hosts, the first matched virtual host wins.
109  absl::optional<size_t> target_index;
110  MatchType best_match_type = INVALID_MATCH;
111  size_t longest_match = 0;
112  // Check each domain pattern in each virtual host to determine the best
113  // matched virtual host.
114  for (size_t i = 0; i < vhost_iterator.Size(); ++i) {
115  const auto& domains = vhost_iterator.GetDomainsForVirtualHost(i);
116  for (const std::string& domain_pattern : domains) {
117  // Check the match type first. Skip the pattern if it's not better
118  // than current match.
119  const MatchType match_type = DomainPatternMatchType(domain_pattern);
120  // This should be caught by RouteConfigParse().
121  GPR_ASSERT(match_type != INVALID_MATCH);
122  if (match_type > best_match_type) continue;
123  if (match_type == best_match_type &&
124  domain_pattern.size() <= longest_match) {
125  continue;
126  }
127  // Skip if match fails.
128  if (!DomainMatch(match_type, domain_pattern, domain)) continue;
129  // Choose this match.
130  target_index = i;
131  best_match_type = match_type;
132  longest_match = domain_pattern.size();
133  if (best_match_type == EXACT_MATCH) break;
134  }
135  if (best_match_type == EXACT_MATCH) break;
136  }
137  return target_index;
138 }
139 
140 namespace {
141 
142 bool HeadersMatch(const std::vector<HeaderMatcher>& header_matchers,
143  grpc_metadata_batch* initial_metadata) {
144  for (const auto& header_matcher : header_matchers) {
145  std::string concatenated_value;
146  if (!header_matcher.Match(XdsRouting::GetHeaderValue(
147  initial_metadata, header_matcher.name(), &concatenated_value))) {
148  return false;
149  }
150  }
151  return true;
152 }
153 
154 bool UnderFraction(const uint32_t fraction_per_million) {
155  // Generate a random number in [0, 1000000).
156  const uint32_t random_number = rand() % 1000000;
157  return random_number < fraction_per_million;
158 }
159 
160 } // namespace
161 
163  const RouteListIterator& route_list_iterator, absl::string_view path,
164  grpc_metadata_batch* initial_metadata) {
165  for (size_t i = 0; i < route_list_iterator.Size(); ++i) {
167  route_list_iterator.GetMatchersForRoute(i);
168  if (matchers.path_matcher.Match(path) &&
169  HeadersMatch(matchers.header_matchers, initial_metadata) &&
170  (!matchers.fraction_per_million.has_value() ||
171  UnderFraction(*matchers.fraction_per_million))) {
172  return i;
173  }
174  }
175  return absl::nullopt;
176 }
177 
179  return DomainPatternMatchType(domain_pattern) != INVALID_MATCH;
180 }
181 
183  grpc_metadata_batch* initial_metadata, absl::string_view header_name,
184  std::string* concatenated_value) {
185  // Note: If we ever allow binary headers here, we still need to
186  // special-case ignore "grpc-tags-bin" and "grpc-trace-bin", since
187  // they are not visible to the LB policy in grpc-go.
188  if (absl::EndsWith(header_name, "-bin")) {
189  return absl::nullopt;
190  } else if (header_name == "content-type") {
191  return "application/grpc";
192  }
193  return initial_metadata->GetStringValue(header_name, concatenated_value);
194 }
195 
196 namespace {
197 
198 const XdsHttpFilterImpl::FilterConfig* FindFilterConfigOverride(
199  const std::string& instance_name,
203  cluster_weight) {
204  // Check ClusterWeight, if any.
205  if (cluster_weight != nullptr) {
206  auto it = cluster_weight->typed_per_filter_config.find(instance_name);
207  if (it != cluster_weight->typed_per_filter_config.end()) return &it->second;
208  }
209  // Check Route.
210  auto it = route.typed_per_filter_config.find(instance_name);
211  if (it != route.typed_per_filter_config.end()) return &it->second;
212  // Check VirtualHost.
213  it = vhost.typed_per_filter_config.find(instance_name);
214  if (it != vhost.typed_per_filter_config.end()) return &it->second;
215  // Not found.
216  return nullptr;
217 }
218 
219 } // namespace
220 
221 XdsRouting::GeneratePerHttpFilterConfigsResult
223  const std::vector<XdsListenerResource::HttpConnectionManager::HttpFilter>&
224  http_filters,
228  cluster_weight,
231  result.args = args;
232  for (const auto& http_filter : http_filters) {
233  // Find filter. This is guaranteed to succeed, because it's checked
234  // at config validation time in the XdsApi code.
235  const XdsHttpFilterImpl* filter_impl =
237  http_filter.config.config_proto_type_name);
238  GPR_ASSERT(filter_impl != nullptr);
239  // If there is not actually any C-core filter associated with this
240  // xDS filter, then it won't need any config, so skip it.
241  if (filter_impl->channel_filter() == nullptr) continue;
242  // Allow filter to add channel args that may affect service config
243  // parsing.
244  result.args = filter_impl->ModifyChannelArgs(result.args);
245  // Find config override, if any.
246  const XdsHttpFilterImpl::FilterConfig* config_override =
247  FindFilterConfigOverride(http_filter.name, vhost, route,
248  cluster_weight);
249  // Generate service config for filter.
250  auto method_config_field =
251  filter_impl->GenerateServiceConfig(http_filter.config, config_override);
252  if (!method_config_field.ok()) {
254  result.args = nullptr;
256  "failed to generate method config for HTTP filter ", http_filter.name,
257  ": ", method_config_field.status().ToString()));
258  break;
259  }
260  result.per_filter_configs[method_config_field->service_config_field_name]
261  .push_back(method_config_field->element);
262  }
263  return result;
264 }
265 
266 } // namespace grpc_core
_gevent_test_main.result
result
Definition: _gevent_test_main.py:96
grpc_core::XdsRouteConfigResource::VirtualHost::typed_per_filter_config
TypedPerFilterConfig typed_per_filter_config
Definition: xds_route_config.h:179
xds_routing.h
regen-readme.it
it
Definition: regen-readme.py:15
log.h
grpc_core::XdsRouting::RouteListIterator::GetMatchersForRoute
virtual const XdsRouteConfigResource::Route::Matchers & GetMatchersForRoute(size_t index) const =0
absl::StrCat
std::string StrCat(const AlphaNum &a, const AlphaNum &b)
Definition: abseil-cpp/absl/strings/str_cat.cc:98
longest_match
uInt longest_match(deflate_state *s, IPos cur_match)
Definition: bloaty/third_party/zlib/deflate.c:1236
route
XdsRouteConfigResource::Route route
Definition: xds_resolver.cc:337
grpc_core
Definition: call_metric_recorder.h:31
grpc_core::XdsRouteConfigResource::VirtualHost
Definition: xds_route_config.h:176
absl::string_view
Definition: abseil-cpp/absl/strings/string_view.h:167
matchers
XdsRouteConfigResource::Route::Matchers matchers
Definition: xds_server_config_fetcher.cc:317
testing::internal::string
::std::string string
Definition: bloaty/third_party/protobuf/third_party/googletest/googletest/include/gtest/internal/gtest-port.h:881
xds_http_filters.h
grpc_core::XdsHttpFilterImpl::ModifyChannelArgs
virtual grpc_channel_args * ModifyChannelArgs(grpc_channel_args *args) const
Definition: xds_http_filters.h:91
check_documentation.path
path
Definition: check_documentation.py:57
grpc_core::XdsRouting::VirtualHostListIterator
Definition: xds_routing.h:44
grpc_core::XdsHttpFilterRegistry::GetFilterForType
static const XdsHttpFilterImpl * GetFilterForType(absl::string_view proto_type_name)
Definition: xds_http_filters.cc:98
grpc_channel_args
Definition: grpc_types.h:132
grpc_core::MetadataMap::GetStringValue
absl::optional< absl::string_view > GetStringValue(absl::string_view name, std::string *buffer) const
Definition: metadata_batch.h:1100
uint32_t
unsigned int uint32_t
Definition: stdint-msvc2008.h:80
grpc_core::XdsRouteConfigResource::Route::typed_per_filter_config
TypedPerFilterConfig typed_per_filter_config
Definition: xds_route_config.h:167
asyncio_get_stats.args
args
Definition: asyncio_get_stats.py:40
GPR_ASSERT
#define GPR_ASSERT(x)
Definition: include/grpc/impl/codegen/log.h:94
absl::string_view::size
constexpr size_type size() const noexcept
Definition: abseil-cpp/absl/strings/string_view.h:277
grpc_core::XdsRouteConfigResource::Route::RouteAction::ClusterWeight::typed_per_filter_config
TypedPerFilterConfig typed_per_filter_config
Definition: xds_route_config.h:128
grpc_core::XdsHttpFilterImpl::channel_filter
virtual const grpc_channel_filter * channel_filter() const =0
grpc_core::XdsHttpFilterImpl::FilterConfig
Definition: xds_http_filters.h:43
grpc_core::XdsRouting::RouteListIterator::Size
virtual size_t Size() const =0
grpc_core::XdsRouting::RouteListIterator
Definition: xds_routing.h:54
matchers.h
absl::optional< size_t >
grpc_core::XdsRouting::GetHeaderValue
static absl::optional< absl::string_view > GetHeaderValue(grpc_metadata_batch *initial_metadata, absl::string_view header_name, std::string *concatenated_value)
Definition: xds_routing.cc:182
grpc_channel_args_destroy
void grpc_channel_args_destroy(grpc_channel_args *a)
Definition: channel_args.cc:360
absl::StrContains
ABSL_NAMESPACE_BEGIN bool StrContains(absl::string_view haystack, absl::string_view needle) noexcept
Definition: third_party/abseil-cpp/absl/strings/match.h:46
grpc_core::XdsRouteConfigResource::Route
Definition: xds_route_config.h:80
stdint.h
grpc_core::XdsRouting::FindVirtualHostForDomain
static absl::optional< size_t > FindVirtualHostForDomain(const VirtualHostListIterator &vhost_iterator, absl::string_view domain)
Definition: xds_routing.cc:98
grpc_core::XdsRouteConfigResource::Route::RouteAction::ClusterWeight
Definition: xds_route_config.h:125
grpc_core::XdsHttpFilterImpl
Definition: xds_http_filters.h:41
grpc_core::XdsHttpFilterImpl::GenerateServiceConfig
virtual absl::StatusOr< ServiceConfigJsonEntry > GenerateServiceConfig(const FilterConfig &hcm_filter_config, const FilterConfig *filter_config_override) const =0
grpc_core::XdsRouting::VirtualHostListIterator::GetDomainsForVirtualHost
virtual const std::vector< std::string > & GetDomainsForVirtualHost(size_t index) const =0
grpc_core::XdsRouting::VirtualHostListIterator::Size
virtual size_t Size() const =0
GRPC_ERROR_CREATE_FROM_CPP_STRING
#define GRPC_ERROR_CREATE_FROM_CPP_STRING(desc)
Definition: error.h:297
grpc_core::XdsRouting::GetRouteForRequest
static absl::optional< size_t > GetRouteForRequest(const RouteListIterator &route_list_iterator, absl::string_view path, grpc_metadata_batch *initial_metadata)
Definition: xds_routing.cc:162
grpc_core::XdsRouting::GeneratePerHTTPFilterConfigs
static GeneratePerHttpFilterConfigsResult GeneratePerHTTPFilterConfigs(const std::vector< XdsListenerResource::HttpConnectionManager::HttpFilter > &http_filters, const XdsRouteConfigResource::VirtualHost &vhost, const XdsRouteConfigResource::Route &route, const XdsRouteConfigResource::Route::RouteAction::ClusterWeight *cluster_weight, grpc_channel_args *args)
Definition: xds_routing.cc:222
grpc_core::XdsRouting::GeneratePerHttpFilterConfigsResult
Definition: xds_routing.h:85
channel_args.h
absl::string_view::empty
constexpr bool empty() const noexcept
Definition: abseil-cpp/absl/strings/string_view.h:292
grpc_core::XdsRouteConfigResource::Route::Matchers
Definition: xds_route_config.h:82
domains
std::vector< std::string > domains
Definition: xds_server_config_fetcher.cc:337
grpc_metadata_batch
Definition: metadata_batch.h:1259
grpc_core::XdsRouting::IsValidDomainPattern
static bool IsValidDomainPattern(absl::string_view domain_pattern)
Definition: xds_routing.cc:178
absl::EndsWith
bool EndsWith(absl::string_view text, absl::string_view suffix) noexcept
Definition: third_party/abseil-cpp/absl/strings/match.h:68
i
uint64_t i
Definition: abseil-cpp/absl/container/btree_benchmark.cc:230
port_platform.h


grpc
Author(s):
autogenerated on Fri May 16 2025 03:00:59