Coverage Report

Created: 2024-09-19 09:45

/proc/self/cwd/source/common/router/config_impl.cc
Line
Count
Source (jump to first uncovered line)
1
#include "source/common/router/config_impl.h"
2
3
#include <algorithm>
4
#include <chrono>
5
#include <cstdint>
6
#include <map>
7
#include <memory>
8
#include <string>
9
#include <vector>
10
11
#include "envoy/config/common/matcher/v3/matcher.pb.h"
12
#include "envoy/config/common/matcher/v3/matcher.pb.validate.h"
13
#include "envoy/config/core/v3/base.pb.h"
14
#include "envoy/config/route/v3/route.pb.h"
15
#include "envoy/config/route/v3/route_components.pb.h"
16
#include "envoy/http/header_map.h"
17
#include "envoy/runtime/runtime.h"
18
#include "envoy/type/matcher/v3/http_inputs.pb.h"
19
#include "envoy/type/matcher/v3/http_inputs.pb.validate.h"
20
#include "envoy/type/matcher/v3/string.pb.h"
21
#include "envoy/type/v3/percent.pb.h"
22
#include "envoy/upstream/cluster_manager.h"
23
#include "envoy/upstream/upstream.h"
24
25
#include "source/common/common/assert.h"
26
#include "source/common/common/empty_string.h"
27
#include "source/common/common/fmt.h"
28
#include "source/common/common/hash.h"
29
#include "source/common/common/logger.h"
30
#include "source/common/common/regex.h"
31
#include "source/common/common/utility.h"
32
#include "source/common/config/metadata.h"
33
#include "source/common/config/utility.h"
34
#include "source/common/config/well_known_names.h"
35
#include "source/common/grpc/common.h"
36
#include "source/common/http/header_utility.h"
37
#include "source/common/http/headers.h"
38
#include "source/common/http/matching/data_impl.h"
39
#include "source/common/http/path_utility.h"
40
#include "source/common/http/utility.h"
41
#include "source/common/matcher/matcher.h"
42
#include "source/common/protobuf/protobuf.h"
43
#include "source/common/protobuf/utility.h"
44
#include "source/common/router/context_impl.h"
45
#include "source/common/router/reset_header_parser.h"
46
#include "source/common/router/retry_state_impl.h"
47
#include "source/common/runtime/runtime_features.h"
48
#include "source/common/tracing/custom_tag_impl.h"
49
#include "source/common/tracing/http_tracer_impl.h"
50
#include "source/common/upstream/retry_factory.h"
51
#include "source/extensions/early_data/default_early_data_policy.h"
52
#include "source/extensions/matching/network/common/inputs.h"
53
#include "source/extensions/path/match/uri_template/uri_template_match.h"
54
#include "source/extensions/path/rewrite/uri_template/uri_template_rewrite.h"
55
56
#include "absl/container/inlined_vector.h"
57
#include "absl/strings/match.h"
58
59
namespace Envoy {
60
namespace Router {
61
class RouteCreator {
62
public:
63
  static absl::StatusOr<RouteEntryImplBaseConstSharedPtr> createAndValidateRoute(
64
      const envoy::config::route::v3::Route& route_config, const CommonVirtualHostSharedPtr& vhost,
65
      Server::Configuration::ServerFactoryContext& factory_context,
66
      ProtobufMessage::ValidationVisitor& validator,
67
117k
      const absl::optional<Upstream::ClusterManager::ClusterInfoMaps>& validation_clusters) {
68
69
117k
    absl::Status creation_status = absl::OkStatus();
70
117k
    RouteEntryImplBaseConstSharedPtr route;
71
117k
    switch (route_config.match().path_specifier_case()) {
72
18.2k
    case envoy::config::route::v3::RouteMatch::PathSpecifierCase::kPrefix:
73
18.2k
      route.reset(new PrefixRouteEntryImpl(vhost, route_config, factory_context, validator,
74
18.2k
                                           creation_status));
75
18.2k
      break;
76
24.4k
    case envoy::config::route::v3::RouteMatch::PathSpecifierCase::kPath:
77
24.4k
      route.reset(
78
24.4k
          new PathRouteEntryImpl(vhost, route_config, factory_context, validator, creation_status));
79
24.4k
      break;
80
5.13k
    case envoy::config::route::v3::RouteMatch::PathSpecifierCase::kSafeRegex:
81
5.13k
      route.reset(new RegexRouteEntryImpl(vhost, route_config, factory_context, validator,
82
5.13k
                                          creation_status));
83
5.13k
      break;
84
62.8k
    case envoy::config::route::v3::RouteMatch::PathSpecifierCase::kConnectMatcher:
85
62.8k
      route.reset(new ConnectRouteEntryImpl(vhost, route_config, factory_context, validator,
86
62.8k
                                            creation_status));
87
62.8k
      break;
88
5.65k
    case envoy::config::route::v3::RouteMatch::PathSpecifierCase::kPathSeparatedPrefix:
89
5.65k
      route.reset(new PathSeparatedPrefixRouteEntryImpl(vhost, route_config, factory_context,
90
5.65k
                                                        validator, creation_status));
91
5.65k
      break;
92
1.63k
    case envoy::config::route::v3::RouteMatch::PathSpecifierCase::kPathMatchPolicy:
93
1.63k
      route.reset(new UriTemplateMatcherRouteEntryImpl(vhost, route_config, factory_context,
94
1.63k
                                                       validator, creation_status));
95
1.63k
      break;
96
0
    case envoy::config::route::v3::RouteMatch::PathSpecifierCase::PATH_SPECIFIER_NOT_SET:
97
0
      break; // return the error below.
98
117k
    }
99
117k
    if (!route) {
100
0
      return absl::InvalidArgumentError("Invalid route config");
101
0
    }
102
117k
    RETURN_IF_NOT_OK(creation_status);
103
104
116k
    if (validation_clusters.has_value()) {
105
47.2k
      RETURN_IF_NOT_OK(route->validateClusters(*validation_clusters));
106
47.0k
      for (const auto& shadow_policy : route->shadowPolicies()) {
107
6.75k
        if (!shadow_policy->cluster().empty()) {
108
24
          ASSERT(shadow_policy->clusterHeader().get().empty());
109
24
          if (!validation_clusters->hasCluster(shadow_policy->cluster())) {
110
24
            return absl::InvalidArgumentError(
111
24
                fmt::format("route: unknown shadow cluster '{}'", shadow_policy->cluster()));
112
24
          }
113
24
        }
114
6.75k
      }
115
47.0k
    }
116
117
116k
    return route;
118
116k
  }
119
};
120
121
namespace {
122
123
constexpr uint32_t DEFAULT_MAX_DIRECT_RESPONSE_BODY_SIZE_BYTES = 4096;
124
125
0
void mergeTransforms(Http::HeaderTransforms& dest, const Http::HeaderTransforms& src) {
126
0
  dest.headers_to_append_or_add.insert(dest.headers_to_append_or_add.end(),
127
0
                                       src.headers_to_append_or_add.begin(),
128
0
                                       src.headers_to_append_or_add.end());
129
0
  dest.headers_to_overwrite_or_add.insert(dest.headers_to_overwrite_or_add.end(),
130
0
                                          src.headers_to_overwrite_or_add.begin(),
131
0
                                          src.headers_to_overwrite_or_add.end());
132
0
  dest.headers_to_add_if_absent.insert(dest.headers_to_add_if_absent.end(),
133
0
                                       src.headers_to_add_if_absent.begin(),
134
0
                                       src.headers_to_add_if_absent.end());
135
0
  dest.headers_to_remove.insert(dest.headers_to_remove.end(), src.headers_to_remove.begin(),
136
0
                                src.headers_to_remove.end());
137
0
}
138
139
class RouteActionValidationVisitor
140
    : public Matcher::MatchTreeValidationVisitor<Http::HttpMatchingData> {
141
public:
142
  absl::Status performDataInputValidation(const Matcher::DataInputFactory<Http::HttpMatchingData>&,
143
30.8k
                                          absl::string_view type_url) override {
144
30.8k
    static std::string request_header_input_name = TypeUtil::descriptorFullNameToTypeUrl(
145
30.8k
        createReflectableMessage(
146
30.8k
            envoy::type::matcher::v3::HttpRequestHeaderMatchInput::default_instance())
147
30.8k
            ->GetDescriptor()
148
30.8k
            ->full_name());
149
30.8k
    static std::string filter_state_input_name = TypeUtil::descriptorFullNameToTypeUrl(
150
30.8k
        createReflectableMessage(envoy::extensions::matching::common_inputs::network::v3::
151
30.8k
                                     FilterStateInput::default_instance())
152
30.8k
            ->GetDescriptor()
153
30.8k
            ->full_name());
154
30.8k
    if (type_url == request_header_input_name || type_url == filter_state_input_name) {
155
19.6k
      return absl::OkStatus();
156
19.6k
    }
157
158
11.1k
    return absl::InvalidArgumentError(
159
11.1k
        fmt::format("Route table can only match on request headers, saw {}", type_url));
160
30.8k
  }
161
};
162
163
absl::Status validateWeightedClusterSpecifier(
164
37.2k
    const envoy::config::route::v3::WeightedCluster::ClusterWeight& cluster) {
165
37.2k
  if (!cluster.name().empty() && !cluster.cluster_header().empty()) {
166
31
    return absl::InvalidArgumentError("Only one of name or cluster_header can be specified");
167
37.2k
  } else if (cluster.name().empty() && cluster.cluster_header().empty()) {
168
63
    return absl::InvalidArgumentError(
169
63
        "At least one of name or cluster_header need to be specified");
170
63
  }
171
37.1k
  return absl::OkStatus();
172
37.2k
}
173
174
// Returns a vector of header parsers, sorted by specificity. The `specificity_ascend` parameter
175
// specifies whether the returned parsers will be sorted from least specific to most specific
176
// (global connection manager level header parser, virtual host level header parser and finally
177
// route-level parser.) or the reverse.
178
absl::InlinedVector<const HeaderParser*, 3>
179
getHeaderParsers(const HeaderParser* global_route_config_header_parser,
180
                 const HeaderParser* vhost_header_parser, const HeaderParser* route_header_parser,
181
2.63k
                 bool specificity_ascend) {
182
2.63k
  if (specificity_ascend) {
183
    // Sorted from least to most specific: global connection manager level headers, virtual host
184
    // level headers and finally route-level headers.
185
46
    return {global_route_config_header_parser, vhost_header_parser, route_header_parser};
186
2.58k
  } else {
187
    // Sorted from most to least specific.
188
2.58k
    return {route_header_parser, vhost_header_parser, global_route_config_header_parser};
189
2.58k
  }
190
2.63k
}
191
192
// If the implementation of a cluster specifier plugin is not provided in current Envoy and the
193
// plugin is set to optional, then this null plugin will be used as a placeholder.
194
class NullClusterSpecifierPlugin : public ClusterSpecifierPlugin {
195
public:
196
22
  RouteConstSharedPtr route(RouteConstSharedPtr, const Http::RequestHeaderMap&) const override {
197
22
    return nullptr;
198
22
  }
199
};
200
201
absl::StatusOr<ClusterSpecifierPluginSharedPtr>
202
getClusterSpecifierPluginByTheProto(const envoy::config::route::v3::ClusterSpecifierPlugin& plugin,
203
                                    ProtobufMessage::ValidationVisitor& validator,
204
19.7k
                                    Server::Configuration::ServerFactoryContext& factory_context) {
205
19.7k
  auto* factory =
206
19.7k
      Envoy::Config::Utility::getFactory<ClusterSpecifierPluginFactoryConfig>(plugin.extension());
207
19.7k
  if (factory == nullptr) {
208
19.7k
    if (plugin.is_optional()) {
209
19.6k
      return std::make_shared<NullClusterSpecifierPlugin>();
210
19.6k
    }
211
115
    return absl::InvalidArgumentError(
212
115
        fmt::format("Didn't find a registered implementation for '{}' with type URL: '{}'",
213
115
                    plugin.extension().name(),
214
115
                    Envoy::Config::Utility::getFactoryType(plugin.extension().typed_config())));
215
19.7k
  }
216
3
  ASSERT(factory != nullptr);
217
3
  auto config =
218
3
      Envoy::Config::Utility::translateToFactoryConfig(plugin.extension(), validator, *factory);
219
3
  return factory->createClusterSpecifierPlugin(*config, factory_context);
220
3
}
221
222
::Envoy::Http::Utility::RedirectConfig
223
59.8k
createRedirectConfig(const envoy::config::route::v3::Route& route, Regex::Engine& regex_engine) {
224
59.8k
  ::Envoy::Http::Utility::RedirectConfig redirect_config{
225
59.8k
      route.redirect().scheme_redirect(),
226
59.8k
      route.redirect().host_redirect(),
227
59.8k
      route.redirect().port_redirect() ? ":" + std::to_string(route.redirect().port_redirect())
228
59.8k
                                       : "",
229
59.8k
      route.redirect().path_redirect(),
230
59.8k
      route.redirect().prefix_rewrite(),
231
59.8k
      route.redirect().has_regex_rewrite() ? route.redirect().regex_rewrite().substitution() : "",
232
59.8k
      route.redirect().has_regex_rewrite()
233
59.8k
          ? Regex::Utility::parseRegex(route.redirect().regex_rewrite().pattern(), regex_engine)
234
59.8k
          : nullptr,
235
59.8k
      route.redirect().path_redirect().find('?') != absl::string_view::npos,
236
59.8k
      route.redirect().https_redirect(),
237
59.8k
      route.redirect().strip_query()};
238
59.8k
  if (route.redirect().has_regex_rewrite()) {
239
513
    ASSERT(redirect_config.prefix_rewrite_redirect_.empty());
240
513
  }
241
59.8k
  return redirect_config;
242
59.8k
}
243
244
} // namespace
245
246
1.57k
const std::string& OriginalConnectPort::key() {
247
1.57k
  CONSTRUCT_ON_FIRST_USE(std::string, "envoy.router.original_connect_port");
248
1.57k
}
249
250
0
std::string SslRedirector::newUri(const Http::RequestHeaderMap& headers) const {
251
0
  return Http::Utility::createSslRedirectPath(headers);
252
0
}
253
254
HedgePolicyImpl::HedgePolicyImpl(const envoy::config::route::v3::HedgePolicy& hedge_policy)
255
    : initial_requests_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(hedge_policy, initial_requests, 1)),
256
      additional_request_chance_(hedge_policy.additional_request_chance()),
257
36.4k
      hedge_on_per_try_timeout_(hedge_policy.hedge_on_per_try_timeout()) {}
258
259
6
HedgePolicyImpl::HedgePolicyImpl() : initial_requests_(1), hedge_on_per_try_timeout_(false) {}
260
261
absl::StatusOr<std::unique_ptr<RetryPolicyImpl>>
262
RetryPolicyImpl::create(const envoy::config::route::v3::RetryPolicy& retry_policy,
263
                        ProtobufMessage::ValidationVisitor& validation_visitor,
264
                        Upstream::RetryExtensionFactoryContext& factory_context,
265
44.2k
                        Server::Configuration::CommonFactoryContext& common_context) {
266
44.2k
  absl::Status creation_status = absl::OkStatus();
267
44.2k
  auto ret = std::unique_ptr<RetryPolicyImpl>(new RetryPolicyImpl(
268
44.2k
      retry_policy, validation_visitor, factory_context, common_context, creation_status));
269
44.2k
  RETURN_IF_NOT_OK(creation_status);
270
44.1k
  return ret;
271
44.2k
}
272
RetryPolicyImpl::RetryPolicyImpl(const envoy::config::route::v3::RetryPolicy& retry_policy,
273
                                 ProtobufMessage::ValidationVisitor& validation_visitor,
274
                                 Upstream::RetryExtensionFactoryContext& factory_context,
275
                                 Server::Configuration::CommonFactoryContext& common_context,
276
                                 absl::Status& creation_status)
277
    : retriable_headers_(Http::HeaderUtility::buildHeaderMatcherVector(
278
          retry_policy.retriable_headers(), common_context)),
279
      retriable_request_headers_(Http::HeaderUtility::buildHeaderMatcherVector(
280
          retry_policy.retriable_request_headers(), common_context)),
281
44.2k
      validation_visitor_(&validation_visitor) {
282
44.2k
  per_try_timeout_ =
283
44.2k
      std::chrono::milliseconds(PROTOBUF_GET_MS_OR_DEFAULT(retry_policy, per_try_timeout, 0));
284
44.2k
  per_try_idle_timeout_ =
285
44.2k
      std::chrono::milliseconds(PROTOBUF_GET_MS_OR_DEFAULT(retry_policy, per_try_idle_timeout, 0));
286
44.2k
  num_retries_ = PROTOBUF_GET_WRAPPED_OR_DEFAULT(retry_policy, num_retries, 1);
287
44.2k
  retry_on_ = RetryStateImpl::parseRetryOn(retry_policy.retry_on()).first;
288
44.2k
  retry_on_ |= RetryStateImpl::parseRetryGrpcOn(retry_policy.retry_on()).first;
289
290
44.2k
  for (const auto& host_predicate : retry_policy.retry_host_predicate()) {
291
9
    auto& factory = Envoy::Config::Utility::getAndCheckFactory<Upstream::RetryHostPredicateFactory>(
292
9
        host_predicate);
293
9
    auto config = Envoy::Config::Utility::translateToFactoryConfig(host_predicate,
294
9
                                                                   validation_visitor, factory);
295
9
    retry_host_predicate_configs_.emplace_back(factory, std::move(config));
296
9
  }
297
298
44.2k
  const auto& retry_priority = retry_policy.retry_priority();
299
44.2k
  if (!retry_priority.name().empty()) {
300
6
    auto& factory =
301
6
        Envoy::Config::Utility::getAndCheckFactory<Upstream::RetryPriorityFactory>(retry_priority);
302
6
    retry_priority_config_ =
303
6
        std::make_pair(&factory, Envoy::Config::Utility::translateToFactoryConfig(
304
6
                                     retry_priority, validation_visitor, factory));
305
6
  }
306
307
44.2k
  for (const auto& options_predicate : retry_policy.retry_options_predicates()) {
308
4
    auto& factory =
309
4
        Envoy::Config::Utility::getAndCheckFactory<Upstream::RetryOptionsPredicateFactory>(
310
4
            options_predicate);
311
4
    retry_options_predicates_.emplace_back(
312
4
        factory.createOptionsPredicate(*Envoy::Config::Utility::translateToFactoryConfig(
313
4
                                           options_predicate, validation_visitor, factory),
314
4
                                       factory_context));
315
4
  }
316
317
44.2k
  auto host_selection_attempts = retry_policy.host_selection_retry_max_attempts();
318
44.2k
  if (host_selection_attempts) {
319
21.2k
    host_selection_attempts_ = host_selection_attempts;
320
21.2k
  }
321
322
146k
  for (auto code : retry_policy.retriable_status_codes()) {
323
146k
    retriable_status_codes_.emplace_back(code);
324
146k
  }
325
326
44.2k
  if (retry_policy.has_retry_back_off()) {
327
6.46k
    base_interval_ = std::chrono::milliseconds(
328
6.46k
        PROTOBUF_GET_MS_REQUIRED(retry_policy.retry_back_off(), base_interval));
329
6.46k
    if ((*base_interval_).count() < 1) {
330
591
      base_interval_ = std::chrono::milliseconds(1);
331
591
    }
332
333
6.46k
    max_interval_ = PROTOBUF_GET_OPTIONAL_MS(retry_policy.retry_back_off(), max_interval);
334
6.46k
    if (max_interval_) {
335
      // Apply the same rounding to max interval in case both are set to sub-millisecond values.
336
3.15k
      if ((*max_interval_).count() < 1) {
337
58
        max_interval_ = std::chrono::milliseconds(1);
338
58
      }
339
340
3.15k
      if ((*max_interval_).count() < (*base_interval_).count()) {
341
17
        creation_status = absl::InvalidArgumentError(
342
17
            "retry_policy.max_interval must greater than or equal to the base_interval");
343
17
        return;
344
17
      }
345
3.15k
    }
346
6.46k
  }
347
348
44.1k
  if (retry_policy.has_rate_limited_retry_back_off()) {
349
1.88k
    reset_headers_ = ResetHeaderParserImpl::buildResetHeaderParserVector(
350
1.88k
        retry_policy.rate_limited_retry_back_off().reset_headers());
351
352
1.88k
    absl::optional<std::chrono::milliseconds> reset_max_interval =
353
1.88k
        PROTOBUF_GET_OPTIONAL_MS(retry_policy.rate_limited_retry_back_off(), max_interval);
354
1.88k
    if (reset_max_interval.has_value()) {
355
541
      std::chrono::milliseconds max_interval = reset_max_interval.value();
356
541
      if (max_interval.count() < 1) {
357
180
        max_interval = std::chrono::milliseconds(1);
358
180
      }
359
541
      reset_max_interval_ = max_interval;
360
541
    }
361
1.88k
  }
362
44.1k
}
363
364
0
std::vector<Upstream::RetryHostPredicateSharedPtr> RetryPolicyImpl::retryHostPredicates() const {
365
0
  std::vector<Upstream::RetryHostPredicateSharedPtr> predicates;
366
0
  predicates.reserve(retry_host_predicate_configs_.size());
367
0
  for (const auto& config : retry_host_predicate_configs_) {
368
0
    predicates.emplace_back(config.first.createHostPredicate(*config.second, num_retries_));
369
0
  }
370
371
0
  return predicates;
372
0
}
373
374
0
Upstream::RetryPrioritySharedPtr RetryPolicyImpl::retryPriority() const {
375
0
  if (retry_priority_config_.first == nullptr) {
376
0
    return nullptr;
377
0
  }
378
379
0
  return retry_priority_config_.first->createRetryPriority(*retry_priority_config_.second,
380
0
                                                           *validation_visitor_, num_retries_);
381
0
}
382
383
absl::StatusOr<std::unique_ptr<InternalRedirectPolicyImpl>> InternalRedirectPolicyImpl::create(
384
    const envoy::config::route::v3::InternalRedirectPolicy& policy_config,
385
12.5k
    ProtobufMessage::ValidationVisitor& validator, absl::string_view current_route_name) {
386
12.5k
  absl::Status creation_status = absl::OkStatus();
387
12.5k
  auto ret = std::unique_ptr<InternalRedirectPolicyImpl>(new InternalRedirectPolicyImpl(
388
12.5k
      policy_config, validator, current_route_name, creation_status));
389
12.5k
  RETURN_IF_NOT_OK(creation_status);
390
12.5k
  return ret;
391
12.5k
}
392
393
InternalRedirectPolicyImpl::InternalRedirectPolicyImpl(
394
    const envoy::config::route::v3::InternalRedirectPolicy& policy_config,
395
    ProtobufMessage::ValidationVisitor& validator, absl::string_view current_route_name,
396
    absl::Status& creation_status)
397
    : current_route_name_(current_route_name),
398
      redirect_response_codes_(buildRedirectResponseCodes(policy_config)),
399
      max_internal_redirects_(
400
          PROTOBUF_GET_WRAPPED_OR_DEFAULT(policy_config, max_internal_redirects, 1)),
401
12.5k
      enabled_(true), allow_cross_scheme_redirect_(policy_config.allow_cross_scheme_redirect()) {
402
12.5k
  for (const auto& predicate : policy_config.predicates()) {
403
5
    auto& factory =
404
5
        Envoy::Config::Utility::getAndCheckFactory<InternalRedirectPredicateFactory>(predicate);
405
5
    auto config = factory.createEmptyConfigProto();
406
5
    Envoy::Config::Utility::translateOpaqueConfig(predicate.typed_config(), validator, *config);
407
5
    predicate_factories_.emplace_back(&factory, std::move(config));
408
5
  }
409
12.5k
  for (const auto& header : policy_config.response_headers_to_copy()) {
410
6.61k
    if (!Http::HeaderUtility::isModifiableHeader(header)) {
411
4
      creation_status =
412
4
          absl::InvalidArgumentError(":-prefixed headers or Hosts may not be specified here.");
413
4
      return;
414
4
    }
415
6.60k
    response_headers_to_copy_.emplace_back(header);
416
6.60k
  }
417
12.5k
}
418
419
0
std::vector<InternalRedirectPredicateSharedPtr> InternalRedirectPolicyImpl::predicates() const {
420
0
  std::vector<InternalRedirectPredicateSharedPtr> predicates;
421
0
  predicates.reserve(predicate_factories_.size());
422
0
  for (const auto& predicate_factory : predicate_factories_) {
423
0
    predicates.emplace_back(predicate_factory.first->createInternalRedirectPredicate(
424
0
        *predicate_factory.second, current_route_name_));
425
0
  }
426
0
  return predicates;
427
0
}
428
429
const std::vector<Http::LowerCaseString>&
430
0
InternalRedirectPolicyImpl::responseHeadersToCopy() const {
431
0
  return response_headers_to_copy_;
432
0
}
433
434
absl::flat_hash_set<Http::Code> InternalRedirectPolicyImpl::buildRedirectResponseCodes(
435
12.5k
    const envoy::config::route::v3::InternalRedirectPolicy& policy_config) const {
436
12.5k
  if (policy_config.redirect_response_codes_size() == 0) {
437
8.60k
    return absl::flat_hash_set<Http::Code>{Http::Code::Found};
438
8.60k
  }
439
3.98k
  absl::flat_hash_set<Http::Code> ret;
440
3.98k
  std::for_each(policy_config.redirect_response_codes().begin(),
441
9.55k
                policy_config.redirect_response_codes().end(), [&ret](uint32_t response_code) {
442
9.55k
                  const absl::flat_hash_set<uint32_t> valid_redirect_response_code = {301, 302, 303,
443
9.55k
                                                                                      307, 308};
444
9.55k
                  if (valid_redirect_response_code.contains(response_code)) {
445
3.17k
                    ret.insert(static_cast<Http::Code>(response_code));
446
3.17k
                  }
447
9.55k
                });
448
3.98k
  return ret;
449
12.5k
}
450
451
absl::Status validateMirrorClusterSpecifier(
452
6.91k
    const envoy::config::route::v3::RouteAction::RequestMirrorPolicy& config) {
453
6.91k
  if (!config.cluster().empty() && !config.cluster_header().empty()) {
454
84
    return absl::InvalidArgumentError(fmt::format("Only one of cluster '{}' or cluster_header '{}' "
455
84
                                                  "in request mirror policy can be specified",
456
84
                                                  config.cluster(), config.cluster_header()));
457
6.82k
  } else if (config.cluster().empty() && config.cluster_header().empty()) {
458
    // For shadow policies with `cluster_header_`, we only verify that this field is not
459
    // empty because the cluster name is not set yet at config time.
460
448
    return absl::InvalidArgumentError(
461
448
        "Exactly one of cluster or cluster_header in request mirror policy need to be specified");
462
448
  }
463
6.37k
  return absl::OkStatus();
464
6.91k
}
465
466
absl::StatusOr<std::shared_ptr<ShadowPolicyImpl>>
467
6.64k
ShadowPolicyImpl::create(const RequestMirrorPolicy& config) {
468
6.64k
  absl::Status creation_status = absl::OkStatus();
469
6.64k
  auto ret = std::shared_ptr<ShadowPolicyImpl>(new ShadowPolicyImpl(config, creation_status));
470
6.64k
  RETURN_IF_NOT_OK(creation_status);
471
6.37k
  return ret;
472
6.64k
}
473
474
ShadowPolicyImpl::ShadowPolicyImpl(const RequestMirrorPolicy& config, absl::Status& creation_status)
475
    : cluster_(config.cluster()), cluster_header_(config.cluster_header()),
476
6.64k
      disable_shadow_host_suffix_append_(config.disable_shadow_host_suffix_append()) {
477
6.64k
  SET_AND_RETURN_IF_NOT_OK(validateMirrorClusterSpecifier(config), creation_status);
478
479
6.37k
  if (config.has_runtime_fraction()) {
480
330
    runtime_key_ = config.runtime_fraction().runtime_key();
481
330
    default_value_ = config.runtime_fraction().default_value();
482
6.04k
  } else {
483
    // If there is no runtime fraction specified, the default is 100% sampled. By leaving
484
    // runtime_key_ empty and forcing the default to 100% this will yield the expected behavior.
485
6.04k
    default_value_.set_numerator(100);
486
6.04k
    default_value_.set_denominator(envoy::type::v3::FractionalPercent::HUNDRED);
487
6.04k
  }
488
6.37k
  trace_sampled_ = PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, trace_sampled, true);
489
6.37k
}
490
491
DecoratorImpl::DecoratorImpl(const envoy::config::route::v3::Decorator& decorator)
492
    : operation_(decorator.operation()),
493
14.0k
      propagate_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(decorator, propagate, true)) {}
494
495
0
void DecoratorImpl::apply(Tracing::Span& span) const {
496
0
  if (!operation_.empty()) {
497
0
    span.setOperation(operation_);
498
0
  }
499
0
}
500
501
0
const std::string& DecoratorImpl::getOperation() const { return operation_; }
502
503
0
bool DecoratorImpl::propagate() const { return propagate_; }
504
505
21.4k
RouteTracingImpl::RouteTracingImpl(const envoy::config::route::v3::Tracing& tracing) {
506
21.4k
  if (!tracing.has_client_sampling()) {
507
16.4k
    client_sampling_.set_numerator(100);
508
16.4k
    client_sampling_.set_denominator(envoy::type::v3::FractionalPercent::HUNDRED);
509
16.4k
  } else {
510
5.04k
    client_sampling_ = tracing.client_sampling();
511
5.04k
  }
512
21.4k
  if (!tracing.has_random_sampling()) {
513
19.1k
    random_sampling_.set_numerator(100);
514
19.1k
    random_sampling_.set_denominator(envoy::type::v3::FractionalPercent::HUNDRED);
515
19.1k
  } else {
516
2.28k
    random_sampling_ = tracing.random_sampling();
517
2.28k
  }
518
21.4k
  if (!tracing.has_overall_sampling()) {
519
17.3k
    overall_sampling_.set_numerator(100);
520
17.3k
    overall_sampling_.set_denominator(envoy::type::v3::FractionalPercent::HUNDRED);
521
17.3k
  } else {
522
4.14k
    overall_sampling_ = tracing.overall_sampling();
523
4.14k
  }
524
56.2k
  for (const auto& tag : tracing.custom_tags()) {
525
56.2k
    custom_tags_.emplace(tag.tag(), Tracing::CustomTagUtility::createCustomTag(tag));
526
56.2k
  }
527
21.4k
}
528
529
0
const envoy::type::v3::FractionalPercent& RouteTracingImpl::getClientSampling() const {
530
0
  return client_sampling_;
531
0
}
532
533
0
const envoy::type::v3::FractionalPercent& RouteTracingImpl::getRandomSampling() const {
534
0
  return random_sampling_;
535
0
}
536
537
0
const envoy::type::v3::FractionalPercent& RouteTracingImpl::getOverallSampling() const {
538
0
  return overall_sampling_;
539
0
}
540
0
const Tracing::CustomTagMap& RouteTracingImpl::getCustomTags() const { return custom_tags_; }
541
542
RouteEntryImplBase::RouteEntryImplBase(const CommonVirtualHostSharedPtr& vhost,
543
                                       const envoy::config::route::v3::Route& route,
544
                                       Server::Configuration::ServerFactoryContext& factory_context,
545
                                       ProtobufMessage::ValidationVisitor& validator,
546
                                       absl::Status& creation_status)
547
    : prefix_rewrite_(route.route().prefix_rewrite()),
548
      path_matcher_(
549
          THROW_OR_RETURN_VALUE(buildPathMatcher(route, validator), PathMatcherSharedPtr)),
550
      path_rewriter_(
551
          THROW_OR_RETURN_VALUE(buildPathRewriter(route, validator), PathRewriterSharedPtr)),
552
      host_rewrite_(route.route().host_rewrite_literal()), vhost_(vhost),
553
      auto_host_rewrite_header_(!route.route().host_rewrite_header().empty()
554
                                    ? absl::optional<Http::LowerCaseString>(Http::LowerCaseString(
555
                                          route.route().host_rewrite_header()))
556
                                    : absl::nullopt),
557
      host_rewrite_path_regex_(
558
          route.route().has_host_rewrite_path_regex()
559
              ? Regex::Utility::parseRegex(route.route().host_rewrite_path_regex().pattern(),
560
                                           factory_context.regexEngine())
561
              : nullptr),
562
      host_rewrite_path_regex_substitution_(
563
          route.route().has_host_rewrite_path_regex()
564
              ? route.route().host_rewrite_path_regex().substitution()
565
              : ""),
566
      cluster_name_(route.route().cluster()), cluster_header_name_(route.route().cluster_header()),
567
      timeout_(PROTOBUF_GET_MS_OR_DEFAULT(route.route(), timeout, DEFAULT_ROUTE_TIMEOUT_MS)),
568
      optional_timeouts_(buildOptionalTimeouts(route.route())), loader_(factory_context.runtime()),
569
      runtime_(loadRuntimeData(route.match())),
570
      redirect_config_(route.has_redirect()
571
                           ? std::make_unique<::Envoy::Http::Utility::RedirectConfig>(
572
                                 createRedirectConfig(route, factory_context.regexEngine()))
573
                           : nullptr),
574
      hedge_policy_(buildHedgePolicy(vhost->hedgePolicy(), route.route())),
575
      internal_redirect_policy_(
576
          THROW_OR_RETURN_VALUE(buildInternalRedirectPolicy(route.route(), validator, route.name()),
577
                                std::unique_ptr<InternalRedirectPolicyImpl>)),
578
      config_headers_(
579
          Http::HeaderUtility::buildHeaderDataVector(route.match().headers(), factory_context)),
580
117k
      dynamic_metadata_([&]() {
581
117k
        std::vector<Envoy::Matchers::MetadataMatcher> vec;
582
117k
        for (const auto& elt : route.match().dynamic_metadata()) {
583
5.49k
          vec.emplace_back(elt, factory_context);
584
5.49k
        }
585
117k
        return vec;
586
117k
      }()),
587
      opaque_config_(parseOpaqueConfig(route)), decorator_(parseDecorator(route)),
588
      route_tracing_(parseRouteTracing(route)), route_name_(route.name()),
589
      time_source_(factory_context.mainThreadDispatcher().timeSource()),
590
      retry_shadow_buffer_limit_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(
591
          route, per_request_buffer_limit_bytes, vhost->retryShadowBufferLimit())),
592
      direct_response_code_(ConfigUtility::parseDirectResponseCode(route)),
593
      cluster_not_found_response_code_(ConfigUtility::parseClusterNotFoundResponseCode(
594
          route.route().cluster_not_found_response_code())),
595
      priority_(ConfigUtility::parsePriority(route.route().priority())),
596
      auto_host_rewrite_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(route.route(), auto_host_rewrite, false)),
597
      append_xfh_(route.route().append_x_forwarded_host()),
598
      using_new_timeouts_(route.route().has_max_stream_duration()),
599
      match_grpc_(route.match().has_grpc()),
600
117k
      case_sensitive_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(route.match(), case_sensitive, true)) {
601
117k
  auto config_or_error =
602
117k
      PerFilterConfigs::create(route.typed_per_filter_config(), factory_context, validator);
603
117k
  SET_AND_RETURN_IF_NOT_OK(config_or_error.status(), creation_status);
604
117k
  per_filter_configs_ = std::move(config_or_error.value());
605
606
117k
  auto policy_or_error =
607
117k
      buildRetryPolicy(vhost->retryPolicy(), route.route(), validator, factory_context);
608
117k
  SET_AND_RETURN_IF_NOT_OK(policy_or_error.status(), creation_status);
609
117k
  retry_policy_ = std::move(policy_or_error.value());
610
611
117k
  if (route.has_direct_response() && route.direct_response().has_body()) {
612
2.95k
    auto provider_or_error = Envoy::Config::DataSource::DataSourceProvider::create(
613
2.95k
        route.direct_response().body(), factory_context.mainThreadDispatcher(),
614
2.95k
        factory_context.threadLocal(), factory_context.api(), true,
615
2.95k
        vhost_->globalRouteConfig().maxDirectResponseBodySizeBytes());
616
2.95k
    SET_AND_RETURN_IF_NOT_OK(provider_or_error.status(), creation_status);
617
2.89k
    direct_response_body_provider_ = std::move(provider_or_error.value());
618
2.89k
  }
619
620
117k
  if (!route.request_headers_to_add().empty() || !route.request_headers_to_remove().empty()) {
621
22.4k
    auto parser_or_error =
622
22.4k
        HeaderParser::configure(route.request_headers_to_add(), route.request_headers_to_remove());
623
22.4k
    SET_AND_RETURN_IF_NOT_OK(parser_or_error.status(), creation_status);
624
22.4k
    request_headers_parser_ = std::move(parser_or_error.value());
625
22.4k
  }
626
117k
  if (!route.response_headers_to_add().empty() || !route.response_headers_to_remove().empty()) {
627
23.3k
    auto parser_or_error = HeaderParser::configure(route.response_headers_to_add(),
628
23.3k
                                                   route.response_headers_to_remove());
629
23.3k
    SET_AND_RETURN_IF_NOT_OK(parser_or_error.status(), creation_status);
630
23.2k
    response_headers_parser_ = std::move(parser_or_error.value());
631
23.2k
  }
632
117k
  if (route.has_metadata()) {
633
15.8k
    metadata_ = std::make_unique<RouteMetadataPack>(route.metadata());
634
15.8k
  }
635
117k
  if (route.route().has_metadata_match()) {
636
9.30k
    const auto filter_it = route.route().metadata_match().filter_metadata().find(
637
9.30k
        Envoy::Config::MetadataFilters::get().ENVOY_LB);
638
9.30k
    if (filter_it != route.route().metadata_match().filter_metadata().end()) {
639
5.98k
      metadata_match_criteria_ = std::make_unique<MetadataMatchCriteriaImpl>(filter_it->second);
640
5.98k
    }
641
9.30k
  }
642
643
117k
  shadow_policies_.reserve(route.route().request_mirror_policies().size());
644
117k
  for (const auto& mirror_policy_config : route.route().request_mirror_policies()) {
645
3.64k
    auto policy_or_error = ShadowPolicyImpl::create(mirror_policy_config);
646
3.64k
    SET_AND_RETURN_IF_NOT_OK(policy_or_error.status(), creation_status);
647
3.60k
    shadow_policies_.push_back(std::move(policy_or_error.value()));
648
3.60k
  }
649
650
  // Inherit policies from the virtual host, which might be from the route config.
651
117k
  if (shadow_policies_.empty()) {
652
116k
    shadow_policies_ = vhost->shadowPolicies();
653
116k
  }
654
655
  // If this is a weighted_cluster, we create N internal route entries
656
  // (called WeightedClusterEntry), such that each object is a simple
657
  // single cluster, pointing back to the parent. Metadata criteria
658
  // from the weighted cluster (if any) are merged with and override
659
  // the criteria from the route.
660
117k
  if (route.route().cluster_specifier_case() ==
661
117k
      envoy::config::route::v3::RouteAction::ClusterSpecifierCase::kWeightedClusters) {
662
3.40k
    uint64_t total_weight = 0UL;
663
3.40k
    const std::string& runtime_key_prefix = route.route().weighted_clusters().runtime_key_prefix();
664
665
3.40k
    std::vector<WeightedClusterEntrySharedPtr> weighted_clusters;
666
3.40k
    weighted_clusters.reserve(route.route().weighted_clusters().clusters().size());
667
37.2k
    for (const auto& cluster : route.route().weighted_clusters().clusters()) {
668
37.2k
      auto cluster_entry = THROW_OR_RETURN_VALUE(
669
37.2k
          WeightedClusterEntry::create(this, runtime_key_prefix + "." + cluster.name(),
670
37.2k
                                       factory_context, validator, cluster),
671
37.2k
          std::unique_ptr<WeightedClusterEntry>);
672
37.2k
      weighted_clusters.emplace_back(std::move(cluster_entry));
673
37.2k
      total_weight += weighted_clusters.back()->clusterWeight();
674
37.2k
      if (total_weight > std::numeric_limits<uint32_t>::max()) {
675
14
        creation_status = absl::InvalidArgumentError(
676
14
            fmt::format("The sum of weights of all weighted clusters of route {} exceeds {}",
677
14
                        route_name_, std::numeric_limits<uint32_t>::max()));
678
14
        return;
679
14
      }
680
37.2k
    }
681
682
    // Reject the config if the total_weight of all clusters is 0.
683
3.39k
    if (total_weight == 0) {
684
449
      creation_status = absl::InvalidArgumentError(
685
449
          "Sum of weights in the weighted_cluster must be greater than 0.");
686
449
      return;
687
449
    }
688
689
2.94k
    weighted_clusters_config_ = std::make_unique<WeightedClustersConfig>(
690
2.94k
        std::move(weighted_clusters), total_weight, route.route().weighted_clusters().header_name(),
691
2.94k
        route.route().weighted_clusters().runtime_key_prefix());
692
693
114k
  } else if (route.route().cluster_specifier_case() ==
694
114k
             envoy::config::route::v3::RouteAction::ClusterSpecifierCase::
695
114k
                 kInlineClusterSpecifierPlugin) {
696
283
    auto plugin_or_error = getClusterSpecifierPluginByTheProto(
697
283
        route.route().inline_cluster_specifier_plugin(), validator, factory_context);
698
283
    SET_AND_RETURN_IF_NOT_OK(plugin_or_error.status(), creation_status);
699
279
    cluster_specifier_plugin_ = std::move(plugin_or_error.value());
700
114k
  } else if (route.route().has_cluster_specifier_plugin()) {
701
182
    auto plugin_or_error = vhost_->globalRouteConfig().clusterSpecifierPlugin(
702
182
        route.route().cluster_specifier_plugin());
703
182
    SET_AND_RETURN_IF_NOT_OK(plugin_or_error.status(), creation_status);
704
89
    cluster_specifier_plugin_ = std::move(plugin_or_error.value());
705
89
  }
706
707
117k
  for (const auto& query_parameter : route.match().query_parameters()) {
708
25.9k
    config_query_parameters_.push_back(
709
25.9k
        std::make_unique<ConfigUtility::QueryParameterMatcher>(query_parameter, factory_context));
710
25.9k
  }
711
712
117k
  if (!route.route().hash_policy().empty()) {
713
7.36k
    hash_policy_ = std::make_unique<Http::HashPolicyImpl>(route.route().hash_policy(),
714
7.36k
                                                          factory_context.regexEngine());
715
7.36k
  }
716
717
117k
  if (route.match().has_tls_context()) {
718
19.7k
    tls_context_match_criteria_ =
719
19.7k
        std::make_unique<TlsContextMatchCriteriaImpl>(route.match().tls_context());
720
19.7k
  }
721
722
117k
  if (!route.route().rate_limits().empty()) {
723
3.87k
    rate_limit_policy_ = std::make_unique<RateLimitPolicyImpl>(route.route().rate_limits(),
724
3.87k
                                                               factory_context, creation_status);
725
3.87k
    if (!creation_status.ok()) {
726
0
      return;
727
0
    }
728
3.87k
  }
729
730
  // Returns true if include_vh_rate_limits is explicitly set to true otherwise it defaults to false
731
  // which is similar to VhRateLimitOptions::Override and will only use virtual host rate limits if
732
  // the route is empty
733
117k
  include_vh_rate_limits_ =
734
117k
      PROTOBUF_GET_WRAPPED_OR_DEFAULT(route.route(), include_vh_rate_limits, false);
735
736
117k
  if (route.route().has_cors()) {
737
7.84k
    cors_policy_ = std::make_unique<CorsPolicyImpl>(route.route().cors(), factory_context);
738
7.84k
  }
739
117k
  for (const auto& upgrade_config : route.route().upgrade_configs()) {
740
9.71k
    const bool enabled = upgrade_config.has_enabled() ? upgrade_config.enabled().value() : true;
741
9.71k
    const bool success =
742
9.71k
        upgrade_map_
743
9.71k
            .emplace(std::make_pair(
744
9.71k
                Envoy::Http::LowerCaseString(upgrade_config.upgrade_type()).get(), enabled))
745
9.71k
            .second;
746
9.71k
    if (!success) {
747
13
      creation_status = absl::InvalidArgumentError(
748
13
          absl::StrCat("Duplicate upgrade ", upgrade_config.upgrade_type()));
749
13
      return;
750
13
    }
751
9.69k
    if (absl::EqualsIgnoreCase(upgrade_config.upgrade_type(),
752
9.69k
                               Http::Headers::get().MethodValues.Connect) ||
753
9.69k
        absl::EqualsIgnoreCase(upgrade_config.upgrade_type(),
754
8.13k
                               Http::Headers::get().UpgradeValues.ConnectUdp)) {
755
1.56k
      if (Runtime::runtimeFeatureEnabled(
756
1.56k
              "envoy.reloadable_features.http_route_connect_proxy_by_default")) {
757
1.56k
        if (upgrade_config.has_connect_config()) {
758
542
          connect_config_ = std::make_unique<ConnectConfig>(upgrade_config.connect_config());
759
542
        }
760
1.56k
      } else {
761
0
        connect_config_ = std::make_unique<ConnectConfig>(upgrade_config.connect_config());
762
0
      }
763
8.13k
    } else if (upgrade_config.has_connect_config()) {
764
15
      creation_status = absl::InvalidArgumentError(absl::StrCat(
765
15
          "Non-CONNECT upgrade type ", upgrade_config.upgrade_type(), " has ConnectConfig"));
766
15
      return;
767
15
    }
768
9.69k
  }
769
770
117k
  int num_rewrite_polices = 0;
771
117k
  if (path_rewriter_ != nullptr) {
772
1.06k
    ++num_rewrite_polices;
773
1.06k
  }
774
775
117k
  if (!prefix_rewrite_.empty()) {
776
8.72k
    ++num_rewrite_polices;
777
8.72k
  }
778
779
117k
  if (route.route().has_regex_rewrite()) {
780
1.92k
    ++num_rewrite_polices;
781
1.92k
  }
782
783
117k
  if (num_rewrite_polices > 1) {
784
12
    creation_status = absl::InvalidArgumentError(
785
12
        "Specify only one of prefix_rewrite, regex_rewrite or path_rewrite_policy");
786
12
    return;
787
12
  }
788
789
117k
  if (!prefix_rewrite_.empty() && path_matcher_ != nullptr) {
790
8
    creation_status =
791
8
        absl::InvalidArgumentError("Cannot use prefix_rewrite with matcher extension");
792
8
    return;
793
8
  }
794
795
117k
  if (route.route().has_regex_rewrite()) {
796
1.92k
    auto rewrite_spec = route.route().regex_rewrite();
797
1.92k
    regex_rewrite_ =
798
1.92k
        Regex::Utility::parseRegex(rewrite_spec.pattern(), factory_context.regexEngine());
799
1.92k
    regex_rewrite_substitution_ = rewrite_spec.substitution();
800
1.92k
  }
801
802
117k
  if (path_rewriter_ != nullptr) {
803
1.06k
    SET_AND_RETURN_IF_NOT_OK(path_rewriter_->isCompatiblePathMatcher(path_matcher_),
804
1.06k
                             creation_status);
805
1.00k
  }
806
807
117k
  if (redirect_config_ != nullptr && redirect_config_->path_redirect_has_query_ &&
808
117k
      redirect_config_->strip_query_) {
809
82
    ENVOY_LOG(warn,
810
82
              "`strip_query` is set to true, but `path_redirect` contains query string and it will "
811
82
              "not be stripped: {}",
812
82
              redirect_config_->path_redirect_);
813
82
  }
814
117k
  if (!route.stat_prefix().empty()) {
815
31.8k
    route_stats_context_ = std::make_unique<RouteStatsContextImpl>(
816
31.8k
        factory_context.scope(), factory_context.routerContext().routeStatNames(),
817
31.8k
        vhost->statName(), route.stat_prefix());
818
31.8k
  }
819
820
117k
  if (route.route().has_early_data_policy()) {
821
72
    auto& factory = Envoy::Config::Utility::getAndCheckFactory<EarlyDataPolicyFactory>(
822
72
        route.route().early_data_policy());
823
72
    auto message = Envoy::Config::Utility::translateToFactoryConfig(
824
72
        route.route().early_data_policy(), validator, factory);
825
72
    early_data_policy_ = factory.createEarlyDataPolicy(*message);
826
116k
  } else {
827
116k
    early_data_policy_ = std::make_unique<DefaultEarlyDataPolicy>(/*allow_safe_request*/ true);
828
116k
  }
829
117k
}
830
831
18.6k
bool RouteEntryImplBase::evaluateRuntimeMatch(const uint64_t random_value) const {
832
18.6k
  return runtime_ == nullptr
833
18.6k
             ? true
834
18.6k
             : loader_.snapshot().featureEnabled(runtime_->fractional_runtime_key_,
835
278
                                                 runtime_->fractional_runtime_default_,
836
278
                                                 random_value);
837
18.6k
}
838
839
absl::string_view
840
14.3k
RouteEntryImplBase::sanitizePathBeforePathMatching(const absl::string_view path) const {
841
14.3k
  absl::string_view ret = path;
842
14.3k
  if (vhost_->globalRouteConfig().ignorePathParametersInPathMatching()) {
843
6.20k
    auto pos = ret.find_first_of(';');
844
6.20k
    if (pos != absl::string_view::npos) {
845
4.06k
      ret.remove_suffix(ret.length() - pos);
846
4.06k
    }
847
6.20k
  }
848
14.3k
  return ret;
849
14.3k
}
850
851
14.7k
bool RouteEntryImplBase::evaluateTlsContextMatch(const StreamInfo::StreamInfo& stream_info) const {
852
14.7k
  bool matches = true;
853
854
14.7k
  if (!tlsContextMatchCriteria()) {
855
13.9k
    return matches;
856
13.9k
  }
857
858
798
  const TlsContextMatchCriteria& criteria = *tlsContextMatchCriteria();
859
860
798
  if (criteria.presented().has_value()) {
861
256
    const bool peer_presented =
862
256
        stream_info.downstreamAddressProvider().sslConnection() &&
863
256
        stream_info.downstreamAddressProvider().sslConnection()->peerCertificatePresented();
864
256
    matches &= criteria.presented().value() == peer_presented;
865
256
  }
866
867
798
  if (criteria.validated().has_value()) {
868
163
    const bool peer_validated =
869
163
        stream_info.downstreamAddressProvider().sslConnection() &&
870
163
        stream_info.downstreamAddressProvider().sslConnection()->peerCertificateValidated();
871
163
    matches &= criteria.validated().value() == peer_validated;
872
163
  }
873
874
798
  return matches;
875
14.7k
}
876
877
bool RouteEntryImplBase::matchRoute(const Http::RequestHeaderMap& headers,
878
                                    const StreamInfo::StreamInfo& stream_info,
879
18.6k
                                    uint64_t random_value) const {
880
18.6k
  bool matches = true;
881
882
18.6k
  matches &= evaluateRuntimeMatch(random_value);
883
18.6k
  if (!matches) {
884
    // No need to waste further cycles calculating a route match.
885
278
    return false;
886
278
  }
887
888
18.3k
  if (match_grpc_) {
889
935
    matches &= Grpc::Common::isGrpcRequestHeaders(headers);
890
935
    if (!matches) {
891
925
      return false;
892
925
    }
893
935
  }
894
895
17.4k
  matches &= Http::HeaderUtility::matchHeaders(headers, config_headers_);
896
17.4k
  if (!matches) {
897
612
    return false;
898
612
  }
899
16.7k
  if (!config_query_parameters_.empty()) {
900
2.77k
    auto query_parameters =
901
2.77k
        Http::Utility::QueryParamsMulti::parseQueryString(headers.getPathValue());
902
2.77k
    matches &= ConfigUtility::matchQueryParams(query_parameters, config_query_parameters_);
903
2.77k
    if (!matches) {
904
2.04k
      return false;
905
2.04k
    }
906
2.77k
  }
907
908
14.7k
  matches &= evaluateTlsContextMatch(stream_info);
909
910
14.7k
  for (const auto& m : dynamic_metadata_) {
911
251
    if (!matches) {
912
      // No need to check anymore as all dynamic metadata matchers must match for a match to occur.
913
40
      break;
914
40
    }
915
211
    matches &= m.match(stream_info.dynamicMetadata());
916
211
  }
917
918
14.7k
  return matches;
919
16.7k
}
920
921
1.99k
const std::string& RouteEntryImplBase::clusterName() const { return cluster_name_; }
922
923
const std::string
924
1.59k
RouteEntryImplBase::getRequestHostValue(const Http::RequestHeaderMap& headers) const {
925
1.59k
  if (!host_rewrite_.empty()) {
926
78
    return host_rewrite_;
927
78
  }
928
929
1.52k
  if (auto_host_rewrite_header_) {
930
41
    const auto& header = headers.get(*auto_host_rewrite_header_);
931
41
    if (!header.empty()) {
932
27
      const absl::string_view header_value = header[0]->value().getStringView();
933
27
      if (!header_value.empty()) {
934
22
        return std::string(header_value);
935
22
      }
936
27
    }
937
41
  }
938
939
1.49k
  if (host_rewrite_path_regex_) {
940
30
    absl::string_view path = headers.getPathValue();
941
30
    return host_rewrite_path_regex_->replaceAll(Http::PathUtil::removeQueryAndFragment(path),
942
30
                                                host_rewrite_path_regex_substitution_);
943
30
  }
944
945
  // Fallback to original host value
946
1.46k
  return std::string(headers.getHostValue());
947
1.49k
}
948
949
void RouteEntryImplBase::finalizeRequestHeaders(Http::RequestHeaderMap& headers,
950
                                                const StreamInfo::StreamInfo& stream_info,
951
1.59k
                                                bool insert_envoy_original_path) const {
952
1.59k
  for (const HeaderParser* header_parser : getRequestHeaderParsers(
953
4.79k
           /*specificity_ascend=*/vhost_->globalRouteConfig().mostSpecificHeaderMutationsWins())) {
954
    // Later evaluated header parser wins.
955
4.79k
    header_parser->evaluateHeaders(headers, stream_info);
956
4.79k
  }
957
958
  // Restore the port if this was a CONNECT request.
959
  // Note this will restore the port for HTTP/2 CONNECT-upgrades as well as as HTTP/1.1 style
960
  // CONNECT requests.
961
1.59k
  if (Http::HeaderUtility::getPortStart(headers.getHostValue()) == absl::string_view::npos) {
962
1.57k
    if (auto typed_state = stream_info.filterState().getDataReadOnly<OriginalConnectPort>(
963
1.57k
            OriginalConnectPort::key());
964
1.57k
        typed_state != nullptr) {
965
0
      headers.setHost(absl::StrCat(headers.getHostValue(), ":", typed_state->value()));
966
0
    }
967
1.57k
  }
968
969
1.59k
  auto final_host_value = getRequestHostValue(headers);
970
1.59k
  if (final_host_value != headers.getHostValue()) {
971
125
    Http::Utility::updateAuthority(headers, final_host_value, append_xfh_);
972
125
  }
973
974
  // Handle path rewrite
975
1.59k
  absl::optional<std::string> container;
976
1.59k
  if (!getPathRewrite(headers, container).empty() || regex_rewrite_ != nullptr ||
977
1.59k
      path_rewriter_ != nullptr) {
978
225
    rewritePathHeader(headers, insert_envoy_original_path);
979
225
  }
980
1.59k
}
981
982
void RouteEntryImplBase::finalizeResponseHeaders(Http::ResponseHeaderMap& headers,
983
1.03k
                                                 const StreamInfo::StreamInfo& stream_info) const {
984
1.03k
  for (const HeaderParser* header_parser : getResponseHeaderParsers(
985
3.10k
           /*specificity_ascend=*/vhost_->globalRouteConfig().mostSpecificHeaderMutationsWins())) {
986
    // Later evaluated header parser wins.
987
3.10k
    header_parser->evaluateHeaders(headers, {stream_info.getRequestHeaders(), &headers},
988
3.10k
                                   stream_info);
989
3.10k
  }
990
1.03k
}
991
992
Http::HeaderTransforms
993
RouteEntryImplBase::responseHeaderTransforms(const StreamInfo::StreamInfo& stream_info,
994
0
                                             bool do_formatting) const {
995
0
  Http::HeaderTransforms transforms;
996
0
  for (const HeaderParser* header_parser : getResponseHeaderParsers(
997
0
           /*specificity_ascend=*/vhost_->globalRouteConfig().mostSpecificHeaderMutationsWins())) {
998
    // Later evaluated header parser wins.
999
0
    mergeTransforms(transforms, header_parser->getHeaderTransforms(stream_info, do_formatting));
1000
0
  }
1001
0
  return transforms;
1002
0
}
1003
1004
Http::HeaderTransforms
1005
RouteEntryImplBase::requestHeaderTransforms(const StreamInfo::StreamInfo& stream_info,
1006
0
                                            bool do_formatting) const {
1007
0
  Http::HeaderTransforms transforms;
1008
0
  for (const HeaderParser* header_parser : getRequestHeaderParsers(
1009
0
           /*specificity_ascend=*/vhost_->globalRouteConfig().mostSpecificHeaderMutationsWins())) {
1010
    // Later evaluated header parser wins.
1011
0
    mergeTransforms(transforms, header_parser->getHeaderTransforms(stream_info, do_formatting));
1012
0
  }
1013
0
  return transforms;
1014
0
}
1015
1016
absl::InlinedVector<const HeaderParser*, 3>
1017
1.59k
RouteEntryImplBase::getRequestHeaderParsers(bool specificity_ascend) const {
1018
1.59k
  return getHeaderParsers(&vhost_->globalRouteConfig().requestHeaderParser(),
1019
1.59k
                          &vhost_->requestHeaderParser(), &requestHeaderParser(),
1020
1.59k
                          specificity_ascend);
1021
1.59k
}
1022
1023
absl::InlinedVector<const HeaderParser*, 3>
1024
1.03k
RouteEntryImplBase::getResponseHeaderParsers(bool specificity_ascend) const {
1025
1.03k
  return getHeaderParsers(&vhost_->globalRouteConfig().responseHeaderParser(),
1026
1.03k
                          &vhost_->responseHeaderParser(), &responseHeaderParser(),
1027
1.03k
                          specificity_ascend);
1028
1.03k
}
1029
1030
std::unique_ptr<const RouteEntryImplBase::RuntimeData>
1031
117k
RouteEntryImplBase::loadRuntimeData(const envoy::config::route::v3::RouteMatch& route_match) {
1032
117k
  if (route_match.has_runtime_fraction()) {
1033
6.37k
    auto runtime_data = std::make_unique<RouteEntryImplBase::RuntimeData>();
1034
6.37k
    runtime_data->fractional_runtime_default_ = route_match.runtime_fraction().default_value();
1035
6.37k
    runtime_data->fractional_runtime_key_ = route_match.runtime_fraction().runtime_key();
1036
6.37k
    return runtime_data;
1037
6.37k
  }
1038
111k
  return nullptr;
1039
117k
}
1040
1041
const std::string&
1042
RouteEntryImplBase::getPathRewrite(const Http::RequestHeaderMap& headers,
1043
1.91k
                                   absl::optional<std::string>& container) const {
1044
  // Just use the prefix rewrite if this isn't a redirect.
1045
1.91k
  if (!isRedirect()) {
1046
1.91k
    return prefix_rewrite_;
1047
1.91k
  }
1048
1049
  // Return the regex rewrite substitution for redirects, if set.
1050
  // redirect_config_ is known to not be nullptr here, because of the isRedirect check above.
1051
0
  ASSERT(redirect_config_ != nullptr);
1052
0
  if (redirect_config_->regex_rewrite_redirect_ != nullptr) {
1053
    // Copy just the path and rewrite it using the regex.
1054
    //
1055
    // Store the result in the output container, and return a reference to the underlying string.
1056
0
    auto just_path(Http::PathUtil::removeQueryAndFragment(headers.getPathValue()));
1057
0
    container = redirect_config_->regex_rewrite_redirect_->replaceAll(
1058
0
        just_path, redirect_config_->regex_rewrite_redirect_substitution_);
1059
1060
0
    return container.value();
1061
0
  }
1062
1063
  // Otherwise, return the prefix rewrite used for redirects.
1064
0
  return redirect_config_->prefix_rewrite_redirect_;
1065
0
}
1066
1067
void RouteEntryImplBase::finalizePathHeader(Http::RequestHeaderMap& headers,
1068
                                            absl::string_view matched_path,
1069
313
                                            bool insert_envoy_original_path) const {
1070
313
  absl::optional<std::string> new_path =
1071
313
      currentUrlPathAfterRewriteWithMatchedPath(headers, matched_path);
1072
313
  if (!new_path.has_value()) {
1073
    // There are no rewrites configured. Just return.
1074
88
    return;
1075
88
  }
1076
1077
225
  if (insert_envoy_original_path) {
1078
225
    headers.setEnvoyOriginalPath(headers.getPathValue());
1079
225
  }
1080
1081
225
  headers.setPath(new_path.value());
1082
225
}
1083
1084
// currentUrlPathAfterRewriteWithMatchedPath does the "standard" path rewriting, meaning that it
1085
// handles the "prefix_rewrite" and "regex_rewrite" route actions, only one of
1086
// which can be specified. The "matched_path" argument applies only to the
1087
// prefix rewriting, and describes the portion of the path (excluding query
1088
// parameters) that should be replaced by the rewrite. A "regex_rewrite"
1089
// applies to the entire path (excluding query parameters), regardless of what
1090
// portion was matched.
1091
absl::optional<std::string> RouteEntryImplBase::currentUrlPathAfterRewriteWithMatchedPath(
1092
313
    const Http::RequestHeaderMap& headers, absl::string_view matched_path) const {
1093
313
  absl::optional<std::string> container;
1094
313
  const auto& rewrite = getPathRewrite(headers, container);
1095
313
  if (rewrite.empty() && regex_rewrite_ == nullptr && path_rewriter_ == nullptr) {
1096
    // There are no rewrites configured.
1097
88
    return {};
1098
88
  }
1099
1100
  // TODO(perf): can we avoid the string copy for the common case?
1101
225
  std::string path(headers.getPathValue());
1102
225
  if (!rewrite.empty()) {
1103
150
    if (redirect_config_ != nullptr && redirect_config_->regex_rewrite_redirect_ != nullptr) {
1104
      // As the rewrite constant may contain the result of a regex rewrite for a redirect, we must
1105
      // replace the full path if this is the case. This is because the matched path does not need
1106
      // to correspond to the full path, e.g. in the case of prefix matches.
1107
0
      auto just_path(Http::PathUtil::removeQueryAndFragment(path));
1108
0
      return path.replace(0, just_path.size(), rewrite);
1109
0
    }
1110
150
    ASSERT(case_sensitive() ? absl::StartsWith(path, matched_path)
1111
150
                            : absl::StartsWithIgnoreCase(path, matched_path));
1112
150
    return path.replace(0, matched_path.size(), rewrite);
1113
150
  }
1114
1115
75
  if (regex_rewrite_ != nullptr) {
1116
    // Replace the entire path, but preserve the query parameters
1117
59
    auto just_path(Http::PathUtil::removeQueryAndFragment(path));
1118
59
    return path.replace(0, just_path.size(),
1119
59
                        regex_rewrite_->replaceAll(just_path, regex_rewrite_substitution_));
1120
59
  }
1121
1122
16
  if (path_rewriter_ != nullptr) {
1123
16
    absl::string_view just_path(Http::PathUtil::removeQueryAndFragment(headers.getPathValue()));
1124
1125
16
    absl::StatusOr<std::string> new_path = path_rewriter_->rewritePath(just_path, matched_path);
1126
1127
    // if rewrite fails return old path.
1128
16
    if (!new_path.ok()) {
1129
0
      return std::string(headers.getPathValue());
1130
0
    }
1131
16
    return path.replace(0, just_path.size(), new_path.value());
1132
16
  }
1133
1134
  // There are no rewrites configured.
1135
0
  return {};
1136
16
}
1137
1138
88
std::string RouteEntryImplBase::newUri(const Http::RequestHeaderMap& headers) const {
1139
88
  ASSERT(isDirectResponse());
1140
88
  return ::Envoy::Http::Utility::newUri(
1141
88
      ::Envoy::makeOptRefFromPtr(
1142
88
          const_cast<const ::Envoy::Http::Utility::RedirectConfig*>(redirect_config_.get())),
1143
88
      headers);
1144
88
}
1145
1146
std::multimap<std::string, std::string>
1147
117k
RouteEntryImplBase::parseOpaqueConfig(const envoy::config::route::v3::Route& route) {
1148
117k
  std::multimap<std::string, std::string> ret;
1149
117k
  if (route.has_metadata()) {
1150
15.8k
    auto filter_metadata = route.metadata().filter_metadata().find("envoy.filters.http.router");
1151
15.8k
    if (filter_metadata == route.metadata().filter_metadata().end()) {
1152
15.3k
      return ret;
1153
15.3k
    }
1154
4.93k
    for (const auto& it : filter_metadata->second.fields()) {
1155
4.93k
      if (it.second.kind_case() == ProtobufWkt::Value::kStringValue) {
1156
3.66k
        ret.emplace(it.first, it.second.string_value());
1157
3.66k
      }
1158
4.93k
    }
1159
588
  }
1160
102k
  return ret;
1161
117k
}
1162
1163
std::unique_ptr<HedgePolicyImpl> RouteEntryImplBase::buildHedgePolicy(
1164
    HedgePolicyConstOptRef vhost_hedge_policy,
1165
117k
    const envoy::config::route::v3::RouteAction& route_config) const {
1166
  // Route specific policy wins, if available.
1167
117k
  if (route_config.has_hedge_policy()) {
1168
6.09k
    return std::make_unique<HedgePolicyImpl>(route_config.hedge_policy());
1169
6.09k
  }
1170
1171
  // If not, we fall back to the virtual host policy if there is one.
1172
111k
  if (vhost_hedge_policy.has_value()) {
1173
30.3k
    return std::make_unique<HedgePolicyImpl>(*vhost_hedge_policy);
1174
30.3k
  }
1175
1176
  // Otherwise, an empty policy will do.
1177
81.3k
  return nullptr;
1178
111k
}
1179
1180
absl::StatusOr<std::unique_ptr<RetryPolicyImpl>> RouteEntryImplBase::buildRetryPolicy(
1181
    RetryPolicyConstOptRef vhost_retry_policy,
1182
    const envoy::config::route::v3::RouteAction& route_config,
1183
    ProtobufMessage::ValidationVisitor& validation_visitor,
1184
117k
    Server::Configuration::ServerFactoryContext& factory_context) const {
1185
117k
  Upstream::RetryExtensionFactoryContextImpl retry_factory_context(
1186
117k
      factory_context.singletonManager());
1187
  // Route specific policy wins, if available.
1188
117k
  if (route_config.has_retry_policy()) {
1189
4.29k
    return RetryPolicyImpl::create(route_config.retry_policy(), validation_visitor,
1190
4.29k
                                   retry_factory_context, factory_context);
1191
4.29k
  }
1192
1193
  // If not, we fallback to the virtual host policy if there is one.
1194
113k
  if (vhost_retry_policy.has_value()) {
1195
39.9k
    return RetryPolicyImpl::create(*vhost_retry_policy, validation_visitor, retry_factory_context,
1196
39.9k
                                   factory_context);
1197
39.9k
  }
1198
1199
  // Otherwise, an empty policy will do.
1200
73.4k
  return nullptr;
1201
113k
}
1202
1203
absl::StatusOr<std::unique_ptr<InternalRedirectPolicyImpl>>
1204
RouteEntryImplBase::buildInternalRedirectPolicy(
1205
    const envoy::config::route::v3::RouteAction& route_config,
1206
117k
    ProtobufMessage::ValidationVisitor& validator, absl::string_view current_route_name) const {
1207
117k
  if (route_config.has_internal_redirect_policy()) {
1208
6.50k
    return InternalRedirectPolicyImpl::create(route_config.internal_redirect_policy(), validator,
1209
6.50k
                                              current_route_name);
1210
6.50k
  }
1211
111k
  envoy::config::route::v3::InternalRedirectPolicy policy_config;
1212
111k
  switch (route_config.internal_redirect_action()) {
1213
6.09k
  case envoy::config::route::v3::RouteAction::HANDLE_INTERNAL_REDIRECT:
1214
6.09k
    break;
1215
105k
  case envoy::config::route::v3::RouteAction::PASS_THROUGH_INTERNAL_REDIRECT:
1216
105k
    FALLTHRU;
1217
105k
  default:
1218
105k
    return nullptr;
1219
111k
  }
1220
6.09k
  if (route_config.has_max_internal_redirects()) {
1221
3.01k
    *policy_config.mutable_max_internal_redirects() = route_config.max_internal_redirects();
1222
3.01k
  }
1223
6.09k
  return InternalRedirectPolicyImpl::create(policy_config, validator, current_route_name);
1224
111k
}
1225
1226
RouteEntryImplBase::OptionalTimeouts RouteEntryImplBase::buildOptionalTimeouts(
1227
117k
    const envoy::config::route::v3::RouteAction& route) const {
1228
  // Calculate how many values are actually set, to initialize `OptionalTimeouts` packed_struct,
1229
  // avoiding memory re-allocation on each set() call.
1230
117k
  int num_timeouts_set = route.has_idle_timeout() ? 1 : 0;
1231
117k
  num_timeouts_set += route.has_max_grpc_timeout() ? 1 : 0;
1232
117k
  num_timeouts_set += route.has_grpc_timeout_offset() ? 1 : 0;
1233
117k
  if (route.has_max_stream_duration()) {
1234
9.61k
    num_timeouts_set += route.max_stream_duration().has_max_stream_duration() ? 1 : 0;
1235
9.61k
    num_timeouts_set += route.max_stream_duration().has_grpc_timeout_header_max() ? 1 : 0;
1236
9.61k
    num_timeouts_set += route.max_stream_duration().has_grpc_timeout_header_offset() ? 1 : 0;
1237
9.61k
  }
1238
117k
  OptionalTimeouts timeouts(num_timeouts_set);
1239
117k
  if (route.has_idle_timeout()) {
1240
6.62k
    timeouts.set<OptionalTimeoutNames::IdleTimeout>(
1241
6.62k
        std::chrono::milliseconds(PROTOBUF_GET_MS_REQUIRED(route, idle_timeout)));
1242
6.62k
  }
1243
117k
  if (route.has_max_grpc_timeout()) {
1244
8.23k
    timeouts.set<OptionalTimeoutNames::MaxGrpcTimeout>(
1245
8.23k
        std::chrono::milliseconds(PROTOBUF_GET_MS_REQUIRED(route, max_grpc_timeout)));
1246
8.23k
  }
1247
117k
  if (route.has_grpc_timeout_offset()) {
1248
5.60k
    timeouts.set<OptionalTimeoutNames::GrpcTimeoutOffset>(
1249
5.60k
        std::chrono::milliseconds(PROTOBUF_GET_MS_REQUIRED(route, grpc_timeout_offset)));
1250
5.60k
  }
1251
117k
  if (route.has_max_stream_duration()) {
1252
9.61k
    if (route.max_stream_duration().has_max_stream_duration()) {
1253
5.49k
      timeouts.set<OptionalTimeoutNames::MaxStreamDuration>(std::chrono::milliseconds(
1254
5.49k
          PROTOBUF_GET_MS_REQUIRED(route.max_stream_duration(), max_stream_duration)));
1255
5.49k
    }
1256
9.61k
    if (route.max_stream_duration().has_grpc_timeout_header_max()) {
1257
2.38k
      timeouts.set<OptionalTimeoutNames::GrpcTimeoutHeaderMax>(std::chrono::milliseconds(
1258
2.38k
          PROTOBUF_GET_MS_REQUIRED(route.max_stream_duration(), grpc_timeout_header_max)));
1259
2.38k
    }
1260
9.61k
    if (route.max_stream_duration().has_grpc_timeout_header_offset()) {
1261
2.30k
      timeouts.set<OptionalTimeoutNames::GrpcTimeoutHeaderOffset>(std::chrono::milliseconds(
1262
2.30k
          PROTOBUF_GET_MS_REQUIRED(route.max_stream_duration(), grpc_timeout_header_offset)));
1263
2.30k
    }
1264
9.61k
  }
1265
117k
  return timeouts;
1266
117k
}
1267
1268
absl::StatusOr<PathRewriterSharedPtr>
1269
RouteEntryImplBase::buildPathRewriter(envoy::config::route::v3::Route route,
1270
117k
                                      ProtobufMessage::ValidationVisitor& validator) const {
1271
117k
  if (!route.route().has_path_rewrite_policy()) {
1272
116k
    return nullptr;
1273
116k
  }
1274
1275
1.09k
  auto& factory = Envoy::Config::Utility::getAndCheckFactory<PathRewriterFactory>(
1276
1.09k
      route.route().path_rewrite_policy());
1277
1278
1.09k
  ProtobufTypes::MessagePtr config = Envoy::Config::Utility::translateAnyToFactoryConfig(
1279
1.09k
      route.route().path_rewrite_policy().typed_config(), validator, factory);
1280
1281
1.09k
  absl::StatusOr<PathRewriterSharedPtr> rewriter = factory.createPathRewriter(*config);
1282
1.09k
  RETURN_IF_NOT_OK_REF(rewriter.status());
1283
1284
1.07k
  return rewriter.value();
1285
1.09k
}
1286
1287
absl::StatusOr<PathMatcherSharedPtr>
1288
RouteEntryImplBase::buildPathMatcher(envoy::config::route::v3::Route route,
1289
117k
                                     ProtobufMessage::ValidationVisitor& validator) const {
1290
117k
  if (!route.match().has_path_match_policy()) {
1291
116k
    return nullptr;
1292
116k
  }
1293
1.63k
  auto& factory = Envoy::Config::Utility::getAndCheckFactory<PathMatcherFactory>(
1294
1.63k
      route.match().path_match_policy());
1295
1296
1.63k
  ProtobufTypes::MessagePtr config = Envoy::Config::Utility::translateAnyToFactoryConfig(
1297
1.63k
      route.match().path_match_policy().typed_config(), validator, factory);
1298
1299
1.63k
  absl::StatusOr<PathMatcherSharedPtr> matcher = factory.createPathMatcher(*config);
1300
1.63k
  RETURN_IF_NOT_OK_REF(matcher.status());
1301
1302
1.54k
  return matcher.value();
1303
1.63k
}
1304
1305
117k
DecoratorConstPtr RouteEntryImplBase::parseDecorator(const envoy::config::route::v3::Route& route) {
1306
117k
  DecoratorConstPtr ret;
1307
117k
  if (route.has_decorator()) {
1308
14.0k
    ret = DecoratorConstPtr(new DecoratorImpl(route.decorator()));
1309
14.0k
  }
1310
117k
  return ret;
1311
117k
}
1312
1313
RouteTracingConstPtr
1314
117k
RouteEntryImplBase::parseRouteTracing(const envoy::config::route::v3::Route& route) {
1315
117k
  RouteTracingConstPtr ret;
1316
117k
  if (route.has_tracing()) {
1317
21.4k
    ret = RouteTracingConstPtr(new RouteTracingImpl(route.tracing()));
1318
21.4k
  }
1319
117k
  return ret;
1320
117k
}
1321
1322
1.07k
const DirectResponseEntry* RouteEntryImplBase::directResponseEntry() const {
1323
  // A route for a request can exclusively be a route entry, a direct response entry,
1324
  // or a redirect entry.
1325
1.07k
  if (isDirectResponse()) {
1326
88
    return this;
1327
982
  } else {
1328
982
    return nullptr;
1329
982
  }
1330
1.07k
}
1331
1332
8.33k
const RouteEntry* RouteEntryImplBase::routeEntry() const {
1333
  // A route for a request can exclusively be a route entry, a direct response entry,
1334
  // or a redirect entry.
1335
8.33k
  if (isDirectResponse()) {
1336
493
    return nullptr;
1337
7.84k
  } else {
1338
7.84k
    return this;
1339
7.84k
  }
1340
8.33k
}
1341
1342
RouteConstSharedPtr RouteEntryImplBase::pickClusterViaClusterHeader(
1343
    const Http::LowerCaseString& cluster_header_name, const Http::HeaderMap& headers,
1344
172
    const RouteEntryAndRoute* route_selector_override) const {
1345
172
  const auto entry = headers.get(cluster_header_name);
1346
172
  std::string final_cluster_name;
1347
172
  if (!entry.empty()) {
1348
    // This is an implicitly untrusted header, so per the API documentation only
1349
    // the first value is used.
1350
62
    final_cluster_name = std::string(entry[0]->value().getStringView());
1351
62
  }
1352
1353
172
  return std::make_shared<DynamicRouteEntry>(route_selector_override
1354
172
                                                 ? route_selector_override
1355
172
                                                 : static_cast<const RouteEntryAndRoute*>(this),
1356
172
                                             shared_from_this(), final_cluster_name);
1357
172
}
1358
1359
RouteConstSharedPtr RouteEntryImplBase::clusterEntry(const Http::RequestHeaderMap& headers,
1360
1.86k
                                                     uint64_t random_value) const {
1361
  // Gets the route object chosen from the list of weighted clusters
1362
  // (if there is one) or returns self.
1363
1.86k
  if (weighted_clusters_config_ == nullptr) {
1364
1.67k
    if (!cluster_name_.empty() || isDirectResponse()) {
1365
1.51k
      return shared_from_this();
1366
1.51k
    } else if (!cluster_header_name_.get().empty()) {
1367
143
      return pickClusterViaClusterHeader(cluster_header_name_, headers,
1368
143
                                         /*route_selector_override=*/nullptr);
1369
143
    } else {
1370
      // TODO(wbpcode): make the cluster header or weighted clusters an implementation of the
1371
      // cluster specifier plugin.
1372
22
      ASSERT(cluster_specifier_plugin_ != nullptr);
1373
22
      return cluster_specifier_plugin_->route(shared_from_this(), headers);
1374
22
    }
1375
1.67k
  }
1376
189
  return pickWeightedCluster(headers, random_value);
1377
1.86k
}
1378
1379
// Selects a cluster depending on weight parameters from configuration or from headers.
1380
// This function takes into account the weights set through configuration or through
1381
// runtime parameters.
1382
// Returns selected cluster, or nullptr if weighted configuration is invalid.
1383
RouteConstSharedPtr RouteEntryImplBase::pickWeightedCluster(const Http::HeaderMap& headers,
1384
189
                                                            const uint64_t random_value) const {
1385
189
  absl::optional<uint64_t> random_value_from_header;
1386
  // Retrieve the random value from the header if corresponding header name is specified.
1387
  // weighted_clusters_config_ is known not to be nullptr here. If it were, pickWeightedCluster
1388
  // would not be called.
1389
189
  ASSERT(weighted_clusters_config_ != nullptr);
1390
189
  if (!weighted_clusters_config_->random_value_header_name_.empty()) {
1391
26
    const auto header_value = headers.get(
1392
26
        Envoy::Http::LowerCaseString(weighted_clusters_config_->random_value_header_name_));
1393
26
    if (!header_value.empty() && header_value.size() == 1) {
1394
      // We expect single-valued header here, otherwise it will potentially cause inconsistent
1395
      // weighted cluster picking throughout the process because different values are used to
1396
      // compute the selected value. So, we treat multi-valued header as invalid input and fall back
1397
      // to use internally generated random number.
1398
4
      uint64_t random_value = 0;
1399
4
      if (absl::SimpleAtoi(header_value[0]->value().getStringView(), &random_value)) {
1400
2
        random_value_from_header = random_value;
1401
2
      }
1402
4
    }
1403
1404
26
    if (!random_value_from_header.has_value()) {
1405
      // Random value should be found here. But if it is not set due to some errors, log the
1406
      // information and fallback to the random value that is set by stream id.
1407
24
      ENVOY_LOG(debug, "The random value can not be found from the header and it will fall back to "
1408
24
                       "the value that is set by stream id");
1409
24
    }
1410
26
  }
1411
1412
189
  auto runtime_key_prefix_configured =
1413
189
      (weighted_clusters_config_->runtime_key_prefix_.length() ? true : false);
1414
189
  uint32_t total_cluster_weight = weighted_clusters_config_->total_cluster_weight_;
1415
189
  absl::InlinedVector<uint32_t, 4> cluster_weights;
1416
1417
  // if runtime config is used, we need to recompute total_weight
1418
189
  if (runtime_key_prefix_configured) {
1419
    // Temporary storage to hold consistent cluster weights. Since cluster weight
1420
    // can be changed with runtime keys, we need a way to gather all the weight
1421
    // and aggregate the total without a change in between.
1422
    // The InlinedVector will be able to handle at least 4 cluster weights
1423
    // without allocation. For cases when more clusters are needed, it is
1424
    // reserved to ensure at most a single allocation.
1425
70
    cluster_weights.reserve(weighted_clusters_config_->weighted_clusters_.size());
1426
1427
70
    total_cluster_weight = 0;
1428
70
    for (const WeightedClusterEntrySharedPtr& cluster :
1429
412
         weighted_clusters_config_->weighted_clusters_) {
1430
412
      auto cluster_weight = cluster->clusterWeight();
1431
412
      cluster_weights.push_back(cluster_weight);
1432
412
      if (cluster_weight > std::numeric_limits<uint32_t>::max() - total_cluster_weight) {
1433
0
        IS_ENVOY_BUG("Sum of weight cannot overflow 2^32");
1434
0
        return nullptr;
1435
0
      }
1436
412
      total_cluster_weight += cluster_weight;
1437
412
    }
1438
70
  }
1439
1440
189
  if (total_cluster_weight == 0) {
1441
0
    IS_ENVOY_BUG("Sum of weight cannot be zero");
1442
0
    return nullptr;
1443
0
  }
1444
189
  const uint64_t selected_value =
1445
189
      (random_value_from_header.has_value() ? random_value_from_header.value() : random_value) %
1446
189
      total_cluster_weight;
1447
189
  uint64_t begin = 0;
1448
189
  uint64_t end = 0;
1449
189
  auto cluster_weight = cluster_weights.begin();
1450
1451
  // Find the right cluster to route to based on the interval in which
1452
  // the selected value falls. The intervals are determined as
1453
  // [0, cluster1_weight), [cluster1_weight, cluster1_weight+cluster2_weight),..
1454
189
  for (const WeightedClusterEntrySharedPtr& cluster :
1455
498
       weighted_clusters_config_->weighted_clusters_) {
1456
1457
498
    if (runtime_key_prefix_configured) {
1458
269
      end = begin + *cluster_weight++;
1459
269
    } else {
1460
229
      end = begin + cluster->clusterWeight();
1461
229
    }
1462
1463
498
    if (selected_value >= begin && selected_value < end) {
1464
189
      if (!cluster->clusterHeaderName().get().empty() &&
1465
189
          !headers.get(cluster->clusterHeaderName()).empty()) {
1466
29
        return pickClusterViaClusterHeader(cluster->clusterHeaderName(), headers,
1467
29
                                           static_cast<RouteEntryAndRoute*>(cluster.get()));
1468
29
      }
1469
      // The WeightedClusterEntry does not contain reference to the RouteEntryImplBase to
1470
      // avoid circular reference. To ensure that the RouteEntryImplBase is not destructed
1471
      // before the WeightedClusterEntry, additional wrapper is used to hold the reference
1472
      // to the RouteEntryImplBase.
1473
160
      return std::make_shared<DynamicRouteEntry>(cluster.get(), shared_from_this(),
1474
160
                                                 cluster->clusterName());
1475
189
    }
1476
309
    begin = end;
1477
309
  }
1478
1479
0
  IS_ENVOY_BUG("unexpected");
1480
0
  return nullptr;
1481
0
}
1482
1483
absl::Status RouteEntryImplBase::validateClusters(
1484
47.2k
    const Upstream::ClusterManager::ClusterInfoMaps& cluster_info_maps) const {
1485
47.2k
  if (isDirectResponse()) {
1486
30.8k
    return absl::OkStatus();
1487
30.8k
  }
1488
1489
  // Currently, we verify that the cluster exists in the CM if we have an explicit cluster or
1490
  // weighted cluster rule. We obviously do not verify a cluster_header rule. This means that
1491
  // trying to use all CDS clusters with a static route table will not work. In the upcoming RDS
1492
  // change we will make it so that dynamically loaded route tables do *not* perform CM checks.
1493
  // In the future we might decide to also have a config option that turns off checks for static
1494
  // route tables. This would enable the all CDS with static route table case.
1495
16.3k
  if (!cluster_name_.empty()) {
1496
1.27k
    if (!cluster_info_maps.hasCluster(cluster_name_)) {
1497
114
      return absl::InvalidArgumentError(fmt::format("route: unknown cluster '{}'", cluster_name_));
1498
114
    }
1499
15.0k
  } else if (weighted_clusters_config_ != nullptr) {
1500
68
    for (const WeightedClusterEntrySharedPtr& cluster :
1501
169
         weighted_clusters_config_->weighted_clusters_) {
1502
169
      if (!cluster->clusterName().empty()) {
1503
14
        if (!cluster_info_maps.hasCluster(cluster->clusterName())) {
1504
14
          return absl::InvalidArgumentError(
1505
14
              fmt::format("route: unknown weighted cluster '{}'", cluster->clusterName()));
1506
14
        }
1507
14
      }
1508
      // For weighted clusters with `cluster_header_name`, we only verify that this field is
1509
      // not empty because the cluster name is not set yet at config time (hence the validation
1510
      // here).
1511
155
      else if (cluster->clusterHeaderName().get().empty()) {
1512
0
        return absl::InvalidArgumentError(
1513
0
            "route: unknown weighted cluster with no cluster_header field");
1514
0
      }
1515
169
    }
1516
68
  }
1517
16.2k
  return absl::OkStatus();
1518
16.3k
}
1519
1520
1.91k
absl::optional<bool> RouteEntryImplBase::filterDisabled(absl::string_view config_name) const {
1521
1.91k
  absl::optional<bool> result = per_filter_configs_->disabled(config_name);
1522
1.91k
  if (result.has_value()) {
1523
0
    return result.value();
1524
0
  }
1525
1.91k
  return vhost_->filterDisabled(config_name);
1526
1.91k
}
1527
1528
RouteSpecificFilterConfigs
1529
815
RouteEntryImplBase::perFilterConfigs(absl::string_view filter_name) const {
1530
815
  auto result = vhost_->perFilterConfigs(filter_name);
1531
1532
815
  const auto* maybe_route_config = per_filter_configs_->get(filter_name);
1533
815
  if (maybe_route_config != nullptr) {
1534
0
    result.push_back(maybe_route_config);
1535
0
  }
1536
815
  return result;
1537
815
}
1538
1539
0
const envoy::config::core::v3::Metadata& RouteEntryImplBase::metadata() const {
1540
0
  return metadata_ != nullptr ? metadata_->proto_metadata_
1541
0
                              : DefaultRouteMetadataPack::get().proto_metadata_;
1542
0
}
1543
0
const Envoy::Config::TypedMetadata& RouteEntryImplBase::typedMetadata() const {
1544
0
  return metadata_ != nullptr ? metadata_->typed_metadata_
1545
0
                              : DefaultRouteMetadataPack::get().typed_metadata_;
1546
0
}
1547
1548
absl::StatusOr<std::unique_ptr<RouteEntryImplBase::WeightedClusterEntry>>
1549
RouteEntryImplBase::WeightedClusterEntry::create(
1550
    const RouteEntryImplBase* parent, const std::string& runtime_key,
1551
    Server::Configuration::ServerFactoryContext& factory_context,
1552
    ProtobufMessage::ValidationVisitor& validator,
1553
37.2k
    const envoy::config::route::v3::WeightedCluster::ClusterWeight& cluster) {
1554
37.2k
  RETURN_IF_NOT_OK(validateWeightedClusterSpecifier(cluster));
1555
37.1k
  return std::unique_ptr<WeightedClusterEntry>(
1556
37.1k
      new WeightedClusterEntry(parent, runtime_key, factory_context, validator, cluster));
1557
37.2k
}
1558
1559
RouteEntryImplBase::WeightedClusterEntry::WeightedClusterEntry(
1560
    const RouteEntryImplBase* parent, const std::string& runtime_key,
1561
    Server::Configuration::ServerFactoryContext& factory_context,
1562
    ProtobufMessage::ValidationVisitor& validator,
1563
    const envoy::config::route::v3::WeightedCluster::ClusterWeight& cluster)
1564
    : DynamicRouteEntry(parent, nullptr, cluster.name()), runtime_key_(runtime_key),
1565
      loader_(factory_context.runtime()),
1566
      cluster_weight_(PROTOBUF_GET_WRAPPED_REQUIRED(cluster, weight)),
1567
      per_filter_configs_(THROW_OR_RETURN_VALUE(
1568
          PerFilterConfigs::create(cluster.typed_per_filter_config(), factory_context, validator),
1569
          std::unique_ptr<PerFilterConfigs>)),
1570
      host_rewrite_(cluster.host_rewrite_literal()),
1571
37.1k
      cluster_header_name_(cluster.cluster_header()) {
1572
37.1k
  if (!cluster.request_headers_to_add().empty() || !cluster.request_headers_to_remove().empty()) {
1573
4.70k
    request_headers_parser_ =
1574
4.70k
        THROW_OR_RETURN_VALUE(HeaderParser::configure(cluster.request_headers_to_add(),
1575
4.70k
                                                      cluster.request_headers_to_remove()),
1576
4.70k
                              Router::HeaderParserPtr);
1577
4.70k
  }
1578
37.1k
  if (!cluster.response_headers_to_add().empty() || !cluster.response_headers_to_remove().empty()) {
1579
7.85k
    response_headers_parser_ =
1580
7.85k
        THROW_OR_RETURN_VALUE(HeaderParser::configure(cluster.response_headers_to_add(),
1581
7.85k
                                                      cluster.response_headers_to_remove()),
1582
7.85k
                              Router::HeaderParserPtr);
1583
7.85k
  }
1584
1585
37.1k
  if (cluster.has_metadata_match()) {
1586
28.5k
    const auto filter_it = cluster.metadata_match().filter_metadata().find(
1587
28.5k
        Envoy::Config::MetadataFilters::get().ENVOY_LB);
1588
28.5k
    if (filter_it != cluster.metadata_match().filter_metadata().end()) {
1589
23.0k
      if (parent->metadata_match_criteria_) {
1590
19.0k
        cluster_metadata_match_criteria_ =
1591
19.0k
            parent->metadata_match_criteria_->mergeMatchCriteria(filter_it->second);
1592
19.0k
      } else {
1593
4.00k
        cluster_metadata_match_criteria_ =
1594
4.00k
            std::make_unique<MetadataMatchCriteriaImpl>(filter_it->second);
1595
4.00k
      }
1596
23.0k
    }
1597
28.5k
  }
1598
37.1k
}
1599
1600
Http::HeaderTransforms RouteEntryImplBase::WeightedClusterEntry::requestHeaderTransforms(
1601
0
    const StreamInfo::StreamInfo& stream_info, bool do_formatting) const {
1602
0
  auto transforms = requestHeaderParser().getHeaderTransforms(stream_info, do_formatting);
1603
0
  mergeTransforms(transforms,
1604
0
                  DynamicRouteEntry::requestHeaderTransforms(stream_info, do_formatting));
1605
0
  return transforms;
1606
0
}
1607
1608
Http::HeaderTransforms RouteEntryImplBase::WeightedClusterEntry::responseHeaderTransforms(
1609
0
    const StreamInfo::StreamInfo& stream_info, bool do_formatting) const {
1610
0
  auto transforms = responseHeaderParser().getHeaderTransforms(stream_info, do_formatting);
1611
0
  mergeTransforms(transforms,
1612
0
                  DynamicRouteEntry::responseHeaderTransforms(stream_info, do_formatting));
1613
0
  return transforms;
1614
0
}
1615
1616
RouteSpecificFilterConfigs
1617
0
RouteEntryImplBase::WeightedClusterEntry::perFilterConfigs(absl::string_view filter_name) const {
1618
1619
0
  auto result = DynamicRouteEntry::perFilterConfigs(filter_name);
1620
0
  const auto* cfg = per_filter_configs_->get(filter_name);
1621
0
  if (cfg != nullptr) {
1622
0
    result.push_back(cfg);
1623
0
  }
1624
0
  return result;
1625
0
}
1626
1627
UriTemplateMatcherRouteEntryImpl::UriTemplateMatcherRouteEntryImpl(
1628
    const CommonVirtualHostSharedPtr& vhost, const envoy::config::route::v3::Route& route,
1629
    Server::Configuration::ServerFactoryContext& factory_context,
1630
    ProtobufMessage::ValidationVisitor& validator, absl::Status& creation_status)
1631
    : RouteEntryImplBase(vhost, route, factory_context, validator, creation_status),
1632
1.63k
      uri_template_(path_matcher_->uriTemplate()){};
1633
1634
void UriTemplateMatcherRouteEntryImpl::rewritePathHeader(Http::RequestHeaderMap& headers,
1635
17
                                                         bool insert_envoy_original_path) const {
1636
17
  finalizePathHeader(headers, path_matcher_->uriTemplate(), insert_envoy_original_path);
1637
17
}
1638
1639
absl::optional<std::string> UriTemplateMatcherRouteEntryImpl::currentUrlPathAfterRewrite(
1640
0
    const Http::RequestHeaderMap& headers) const {
1641
0
  return currentUrlPathAfterRewriteWithMatchedPath(headers, path_matcher_->uriTemplate());
1642
0
}
1643
1644
RouteConstSharedPtr
1645
UriTemplateMatcherRouteEntryImpl::matches(const Http::RequestHeaderMap& headers,
1646
                                          const StreamInfo::StreamInfo& stream_info,
1647
46
                                          uint64_t random_value) const {
1648
46
  if (RouteEntryImplBase::matchRoute(headers, stream_info, random_value) &&
1649
46
      path_matcher_->match(headers.getPathValue())) {
1650
18
    return clusterEntry(headers, random_value);
1651
18
  }
1652
28
  return nullptr;
1653
46
}
1654
1655
PrefixRouteEntryImpl::PrefixRouteEntryImpl(
1656
    const CommonVirtualHostSharedPtr& vhost, const envoy::config::route::v3::Route& route,
1657
    Server::Configuration::ServerFactoryContext& factory_context,
1658
    ProtobufMessage::ValidationVisitor& validator, absl::Status& creation_status)
1659
    : RouteEntryImplBase(vhost, route, factory_context, validator, creation_status),
1660
      path_matcher_(Matchers::PathMatcher::createPrefix(route.match().prefix(), !case_sensitive(),
1661
18.2k
                                                        factory_context)) {}
1662
1663
void PrefixRouteEntryImpl::rewritePathHeader(Http::RequestHeaderMap& headers,
1664
255
                                             bool insert_envoy_original_path) const {
1665
255
  finalizePathHeader(headers, matcher(), insert_envoy_original_path);
1666
255
}
1667
1668
absl::optional<std::string>
1669
0
PrefixRouteEntryImpl::currentUrlPathAfterRewrite(const Http::RequestHeaderMap& headers) const {
1670
0
  return currentUrlPathAfterRewriteWithMatchedPath(headers, matcher());
1671
0
}
1672
1673
RouteConstSharedPtr PrefixRouteEntryImpl::matches(const Http::RequestHeaderMap& headers,
1674
                                                  const StreamInfo::StreamInfo& stream_info,
1675
5.84k
                                                  uint64_t random_value) const {
1676
5.84k
  if (RouteEntryImplBase::matchRoute(headers, stream_info, random_value) &&
1677
5.84k
      path_matcher_->match(sanitizePathBeforePathMatching(headers.getPathValue()))) {
1678
1.64k
    return clusterEntry(headers, random_value);
1679
1.64k
  }
1680
4.19k
  return nullptr;
1681
5.84k
}
1682
1683
PathRouteEntryImpl::PathRouteEntryImpl(const CommonVirtualHostSharedPtr& vhost,
1684
                                       const envoy::config::route::v3::Route& route,
1685
                                       Server::Configuration::ServerFactoryContext& factory_context,
1686
                                       ProtobufMessage::ValidationVisitor& validator,
1687
                                       absl::Status& creation_status)
1688
    : RouteEntryImplBase(vhost, route, factory_context, validator, creation_status),
1689
      path_matcher_(Matchers::PathMatcher::createExact(route.match().path(), !case_sensitive(),
1690
24.4k
                                                       factory_context)) {}
1691
1692
void PathRouteEntryImpl::rewritePathHeader(Http::RequestHeaderMap& headers,
1693
23
                                           bool insert_envoy_original_path) const {
1694
23
  finalizePathHeader(headers, matcher(), insert_envoy_original_path);
1695
23
}
1696
1697
absl::optional<std::string>
1698
0
PathRouteEntryImpl::currentUrlPathAfterRewrite(const Http::RequestHeaderMap& headers) const {
1699
0
  return currentUrlPathAfterRewriteWithMatchedPath(headers, matcher());
1700
0
}
1701
1702
RouteConstSharedPtr PathRouteEntryImpl::matches(const Http::RequestHeaderMap& headers,
1703
                                                const StreamInfo::StreamInfo& stream_info,
1704
10.7k
                                                uint64_t random_value) const {
1705
10.7k
  if (RouteEntryImplBase::matchRoute(headers, stream_info, random_value) &&
1706
10.7k
      path_matcher_->match(sanitizePathBeforePathMatching(headers.getPathValue()))) {
1707
169
    return clusterEntry(headers, random_value);
1708
169
  }
1709
1710
10.5k
  return nullptr;
1711
10.7k
}
1712
1713
RegexRouteEntryImpl::RegexRouteEntryImpl(
1714
    const CommonVirtualHostSharedPtr& vhost, const envoy::config::route::v3::Route& route,
1715
    Server::Configuration::ServerFactoryContext& factory_context,
1716
    ProtobufMessage::ValidationVisitor& validator, absl::Status& creation_status)
1717
    : RouteEntryImplBase(vhost, route, factory_context, validator, creation_status),
1718
      path_matcher_(
1719
5.13k
          Matchers::PathMatcher::createSafeRegex(route.match().safe_regex(), factory_context)) {
1720
5.13k
  ASSERT(route.match().path_specifier_case() ==
1721
5.13k
         envoy::config::route::v3::RouteMatch::PathSpecifierCase::kSafeRegex);
1722
5.13k
}
1723
1724
void RegexRouteEntryImpl::rewritePathHeader(Http::RequestHeaderMap& headers,
1725
6
                                            bool insert_envoy_original_path) const {
1726
6
  absl::string_view path = Http::PathUtil::removeQueryAndFragment(headers.getPathValue());
1727
  // TODO(yuval-k): This ASSERT can happen if the path was changed by a filter without clearing
1728
  // the route cache. We should consider if ASSERT-ing is the desired behavior in this case.
1729
6
  ASSERT(path_matcher_->match(sanitizePathBeforePathMatching(path)));
1730
6
  finalizePathHeader(headers, path, insert_envoy_original_path);
1731
6
}
1732
1733
absl::optional<std::string>
1734
0
RegexRouteEntryImpl::currentUrlPathAfterRewrite(const Http::RequestHeaderMap& headers) const {
1735
0
  const absl::string_view path = Http::PathUtil::removeQueryAndFragment(headers.getPathValue());
1736
0
  return currentUrlPathAfterRewriteWithMatchedPath(headers, path);
1737
0
}
1738
1739
RouteConstSharedPtr RegexRouteEntryImpl::matches(const Http::RequestHeaderMap& headers,
1740
                                                 const StreamInfo::StreamInfo& stream_info,
1741
835
                                                 uint64_t random_value) const {
1742
835
  if (RouteEntryImplBase::matchRoute(headers, stream_info, random_value)) {
1743
705
    if (path_matcher_->match(sanitizePathBeforePathMatching(headers.getPathValue()))) {
1744
15
      return clusterEntry(headers, random_value);
1745
15
    }
1746
705
  }
1747
820
  return nullptr;
1748
835
}
1749
1750
ConnectRouteEntryImpl::ConnectRouteEntryImpl(
1751
    const CommonVirtualHostSharedPtr& vhost, const envoy::config::route::v3::Route& route,
1752
    Server::Configuration::ServerFactoryContext& factory_context,
1753
    ProtobufMessage::ValidationVisitor& validator, absl::Status& creation_status)
1754
62.8k
    : RouteEntryImplBase(vhost, route, factory_context, validator, creation_status) {}
1755
1756
void ConnectRouteEntryImpl::rewritePathHeader(Http::RequestHeaderMap& headers,
1757
10
                                              bool insert_envoy_original_path) const {
1758
10
  const absl::string_view path = Http::PathUtil::removeQueryAndFragment(headers.getPathValue());
1759
10
  finalizePathHeader(headers, path, insert_envoy_original_path);
1760
10
}
1761
1762
absl::optional<std::string>
1763
0
ConnectRouteEntryImpl::currentUrlPathAfterRewrite(const Http::RequestHeaderMap& headers) const {
1764
0
  const absl::string_view path = Http::PathUtil::removeQueryAndFragment(headers.getPathValue());
1765
0
  return currentUrlPathAfterRewriteWithMatchedPath(headers, path);
1766
0
}
1767
1768
RouteConstSharedPtr ConnectRouteEntryImpl::matches(const Http::RequestHeaderMap& headers,
1769
                                                   const StreamInfo::StreamInfo& stream_info,
1770
3.16k
                                                   uint64_t random_value) const {
1771
3.16k
  if ((Http::HeaderUtility::isConnect(headers) ||
1772
3.16k
       Http::HeaderUtility::isConnectUdpRequest(headers)) &&
1773
3.16k
      RouteEntryImplBase::matchRoute(headers, stream_info, random_value)) {
1774
12
    return clusterEntry(headers, random_value);
1775
12
  }
1776
3.14k
  return nullptr;
1777
3.16k
}
1778
1779
PathSeparatedPrefixRouteEntryImpl::PathSeparatedPrefixRouteEntryImpl(
1780
    const CommonVirtualHostSharedPtr& vhost, const envoy::config::route::v3::Route& route,
1781
    Server::Configuration::ServerFactoryContext& factory_context,
1782
    ProtobufMessage::ValidationVisitor& validator, absl::Status& creation_status)
1783
    : RouteEntryImplBase(vhost, route, factory_context, validator, creation_status),
1784
      path_matcher_(Matchers::PathMatcher::createPrefix(route.match().path_separated_prefix(),
1785
5.65k
                                                        !case_sensitive(), factory_context)) {}
1786
1787
void PathSeparatedPrefixRouteEntryImpl::rewritePathHeader(Http::RequestHeaderMap& headers,
1788
2
                                                          bool insert_envoy_original_path) const {
1789
2
  finalizePathHeader(headers, matcher(), insert_envoy_original_path);
1790
2
}
1791
1792
absl::optional<std::string> PathSeparatedPrefixRouteEntryImpl::currentUrlPathAfterRewrite(
1793
0
    const Http::RequestHeaderMap& headers) const {
1794
0
  return currentUrlPathAfterRewriteWithMatchedPath(headers, matcher());
1795
0
}
1796
1797
RouteConstSharedPtr
1798
PathSeparatedPrefixRouteEntryImpl::matches(const Http::RequestHeaderMap& headers,
1799
                                           const StreamInfo::StreamInfo& stream_info,
1800
1.13k
                                           uint64_t random_value) const {
1801
1.13k
  if (!RouteEntryImplBase::matchRoute(headers, stream_info, random_value)) {
1802
246
    return nullptr;
1803
246
  }
1804
893
  absl::string_view sanitized_path = sanitizePathBeforePathMatching(
1805
893
      Http::PathUtil::removeQueryAndFragment(headers.getPathValue()));
1806
893
  const size_t sanitized_size = sanitized_path.size();
1807
893
  const size_t matcher_size = matcher().size();
1808
893
  if (sanitized_size >= matcher_size && path_matcher_->match(sanitized_path) &&
1809
893
      (sanitized_size == matcher_size || sanitized_path[matcher_size] == '/')) {
1810
6
    return clusterEntry(headers, random_value);
1811
6
  }
1812
887
  return nullptr;
1813
893
}
1814
1815
CommonVirtualHostImpl::CommonVirtualHostImpl(
1816
    const envoy::config::route::v3::VirtualHost& virtual_host,
1817
    const CommonConfigSharedPtr& global_route_config,
1818
    Server::Configuration::ServerFactoryContext& factory_context, Stats::Scope& scope,
1819
    ProtobufMessage::ValidationVisitor& validator, absl::Status& creation_status)
1820
    : stat_name_storage_(virtual_host.name(), factory_context.scope().symbolTable()),
1821
      global_route_config_(global_route_config),
1822
      per_filter_configs_(
1823
          THROW_OR_RETURN_VALUE(PerFilterConfigs::create(virtual_host.typed_per_filter_config(),
1824
                                                         factory_context, validator),
1825
                                std::unique_ptr<PerFilterConfigs>)),
1826
      retry_shadow_buffer_limit_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(
1827
          virtual_host, per_request_buffer_limit_bytes, std::numeric_limits<uint32_t>::max())),
1828
      include_attempt_count_in_request_(virtual_host.include_request_attempt_count()),
1829
      include_attempt_count_in_response_(virtual_host.include_attempt_count_in_response()),
1830
21.4k
      include_is_timeout_retry_header_(virtual_host.include_is_timeout_retry_header()) {
1831
21.4k
  if (!virtual_host.request_headers_to_add().empty() ||
1832
21.4k
      !virtual_host.request_headers_to_remove().empty()) {
1833
1.72k
    request_headers_parser_ =
1834
1.72k
        THROW_OR_RETURN_VALUE(HeaderParser::configure(virtual_host.request_headers_to_add(),
1835
1.72k
                                                      virtual_host.request_headers_to_remove()),
1836
1.72k
                              Router::HeaderParserPtr);
1837
1.72k
  }
1838
21.4k
  if (!virtual_host.response_headers_to_add().empty() ||
1839
21.4k
      !virtual_host.response_headers_to_remove().empty()) {
1840
3.60k
    response_headers_parser_ =
1841
3.60k
        THROW_OR_RETURN_VALUE(HeaderParser::configure(virtual_host.response_headers_to_add(),
1842
3.60k
                                                      virtual_host.response_headers_to_remove()),
1843
3.60k
                              Router::HeaderParserPtr);
1844
3.60k
  }
1845
1846
  // Retry and Hedge policies must be set before routes, since they may use them.
1847
21.4k
  if (virtual_host.has_retry_policy()) {
1848
2.81k
    retry_policy_ = std::make_unique<envoy::config::route::v3::RetryPolicy>();
1849
2.81k
    retry_policy_->CopyFrom(virtual_host.retry_policy());
1850
2.81k
  }
1851
21.4k
  if (virtual_host.has_hedge_policy()) {
1852
2.37k
    hedge_policy_ = std::make_unique<envoy::config::route::v3::HedgePolicy>();
1853
2.37k
    hedge_policy_->CopyFrom(virtual_host.hedge_policy());
1854
2.37k
  }
1855
1856
21.4k
  if (!virtual_host.rate_limits().empty()) {
1857
1.03k
    rate_limit_policy_ = std::make_unique<RateLimitPolicyImpl>(virtual_host.rate_limits(),
1858
1.03k
                                                               factory_context, creation_status);
1859
1.03k
    SET_AND_RETURN_IF_NOT_OK(creation_status, creation_status);
1860
1.03k
  }
1861
1862
21.4k
  shadow_policies_.reserve(virtual_host.request_mirror_policies().size());
1863
21.4k
  for (const auto& mirror_policy_config : virtual_host.request_mirror_policies()) {
1864
1.10k
    auto policy_or_error = ShadowPolicyImpl::create(mirror_policy_config);
1865
1.10k
    SET_AND_RETURN_IF_NOT_OK(policy_or_error.status(), creation_status);
1866
1.03k
    shadow_policies_.push_back(std::move(policy_or_error.value()));
1867
1.03k
  }
1868
1869
  // Inherit policies from the global config.
1870
21.3k
  if (shadow_policies_.empty()) {
1871
20.4k
    shadow_policies_ = global_route_config_->shadowPolicies();
1872
20.4k
  }
1873
1874
21.3k
  if (virtual_host.has_matcher() && !virtual_host.routes().empty()) {
1875
6
    creation_status =
1876
6
        absl::InvalidArgumentError("cannot set both matcher and routes on virtual host");
1877
6
    return;
1878
6
  }
1879
1880
21.3k
  if (!virtual_host.virtual_clusters().empty()) {
1881
1.89k
    vcluster_scope_ = Stats::Utility::scopeFromStatNames(
1882
1.89k
        scope, {stat_name_storage_.statName(),
1883
1.89k
                factory_context.routerContext().virtualClusterStatNames().vcluster_});
1884
1.89k
    virtual_cluster_catch_all_ = std::make_unique<CatchAllVirtualCluster>(
1885
1.89k
        *vcluster_scope_, factory_context.routerContext().virtualClusterStatNames());
1886
7.97k
    for (const auto& virtual_cluster : virtual_host.virtual_clusters()) {
1887
7.97k
      if (virtual_cluster.headers().empty()) {
1888
85
        creation_status = absl::InvalidArgumentError("virtual clusters must define 'headers'");
1889
85
        return;
1890
85
      }
1891
1892
7.88k
      virtual_clusters_.push_back(
1893
7.88k
          VirtualClusterEntry(virtual_cluster, *vcluster_scope_, factory_context,
1894
7.88k
                              factory_context.routerContext().virtualClusterStatNames()));
1895
7.88k
    }
1896
1.89k
  }
1897
1898
21.2k
  if (virtual_host.has_cors()) {
1899
2.56k
    cors_policy_ = std::make_unique<CorsPolicyImpl>(virtual_host.cors(), factory_context);
1900
2.56k
  }
1901
1902
21.2k
  if (virtual_host.has_metadata()) {
1903
1.03k
    metadata_ = std::make_unique<RouteMetadataPack>(virtual_host.metadata());
1904
1.03k
  }
1905
21.2k
}
1906
1907
CommonVirtualHostImpl::VirtualClusterEntry::VirtualClusterEntry(
1908
    const envoy::config::route::v3::VirtualCluster& virtual_cluster, Stats::Scope& scope,
1909
    Server::Configuration::CommonFactoryContext& context, const VirtualClusterStatNames& stat_names)
1910
    : StatNameProvider(virtual_cluster.name(), scope.symbolTable()),
1911
      VirtualClusterBase(virtual_cluster.name(), stat_name_storage_.statName(),
1912
7.88k
                         scope.scopeFromStatName(stat_name_storage_.statName()), stat_names) {
1913
7.88k
  ASSERT(!virtual_cluster.headers().empty());
1914
7.88k
  headers_ = Http::HeaderUtility::buildHeaderDataVector(virtual_cluster.headers(), context);
1915
7.88k
}
1916
1917
0
const CommonConfig& CommonVirtualHostImpl::routeConfig() const { return *global_route_config_; }
1918
1919
1.91k
absl::optional<bool> CommonVirtualHostImpl::filterDisabled(absl::string_view config_name) const {
1920
1.91k
  absl::optional<bool> result = per_filter_configs_->disabled(config_name);
1921
1.91k
  if (result.has_value()) {
1922
0
    return result.value();
1923
0
  }
1924
1.91k
  return global_route_config_->filterDisabled(config_name);
1925
1.91k
}
1926
1927
const RouteSpecificFilterConfig*
1928
0
CommonVirtualHostImpl::mostSpecificPerFilterConfig(absl::string_view name) const {
1929
0
  auto* per_filter_config = per_filter_configs_->get(name);
1930
0
  return per_filter_config != nullptr ? per_filter_config
1931
0
                                      : global_route_config_->perFilterConfig(name);
1932
0
}
1933
RouteSpecificFilterConfigs
1934
815
CommonVirtualHostImpl::perFilterConfigs(absl::string_view filter_name) const {
1935
815
  RouteSpecificFilterConfigs result;
1936
1937
  // Parent first.
1938
815
  if (auto* maybe_rc_config = global_route_config_->perFilterConfig(filter_name);
1939
815
      maybe_rc_config != nullptr) {
1940
0
    result.push_back(maybe_rc_config);
1941
0
  }
1942
815
  if (auto* maybe_vhost_config = per_filter_configs_->get(filter_name);
1943
815
      maybe_vhost_config != nullptr) {
1944
0
    result.push_back(maybe_vhost_config);
1945
0
  }
1946
815
  return result;
1947
815
}
1948
1949
0
const envoy::config::core::v3::Metadata& CommonVirtualHostImpl::metadata() const {
1950
0
  return metadata_ != nullptr ? metadata_->proto_metadata_
1951
0
                              : DefaultRouteMetadataPack::get().proto_metadata_;
1952
0
}
1953
0
const Envoy::Config::TypedMetadata& CommonVirtualHostImpl::typedMetadata() const {
1954
0
  return metadata_ != nullptr ? metadata_->typed_metadata_
1955
0
                              : DefaultRouteMetadataPack::get().typed_metadata_;
1956
0
}
1957
1958
absl::StatusOr<std::shared_ptr<CommonVirtualHostImpl>>
1959
CommonVirtualHostImpl::create(const envoy::config::route::v3::VirtualHost& virtual_host,
1960
                              const CommonConfigSharedPtr& global_route_config,
1961
                              Server::Configuration::ServerFactoryContext& factory_context,
1962
21.4k
                              Stats::Scope& scope, ProtobufMessage::ValidationVisitor& validator) {
1963
21.4k
  absl::Status creation_status = absl::OkStatus();
1964
21.4k
  auto ret = std::shared_ptr<CommonVirtualHostImpl>(new CommonVirtualHostImpl(
1965
21.4k
      virtual_host, global_route_config, factory_context, scope, validator, creation_status));
1966
21.4k
  RETURN_IF_NOT_OK(creation_status);
1967
21.2k
  return ret;
1968
21.4k
}
1969
1970
VirtualHostImpl::VirtualHostImpl(
1971
    const envoy::config::route::v3::VirtualHost& virtual_host,
1972
    const CommonConfigSharedPtr& global_route_config,
1973
    Server::Configuration::ServerFactoryContext& factory_context, Stats::Scope& scope,
1974
    ProtobufMessage::ValidationVisitor& validator,
1975
    const absl::optional<Upstream::ClusterManager::ClusterInfoMaps>& validation_clusters,
1976
21.4k
    absl::Status& creation_status) {
1977
1978
21.4k
  auto host_or_error = CommonVirtualHostImpl::create(virtual_host, global_route_config,
1979
21.4k
                                                     factory_context, scope, validator);
1980
21.4k
  SET_AND_RETURN_IF_NOT_OK(host_or_error.status(), creation_status);
1981
21.2k
  shared_virtual_host_ = std::move(host_or_error.value());
1982
1983
21.2k
  switch (virtual_host.require_tls()) {
1984
0
    PANIC_ON_PROTO_ENUM_SENTINEL_VALUES;
1985
19.5k
  case envoy::config::route::v3::VirtualHost::NONE:
1986
19.5k
    ssl_requirements_ = SslRequirements::None;
1987
19.5k
    break;
1988
432
  case envoy::config::route::v3::VirtualHost::EXTERNAL_ONLY:
1989
432
    ssl_requirements_ = SslRequirements::ExternalOnly;
1990
432
    break;
1991
1.15k
  case envoy::config::route::v3::VirtualHost::ALL:
1992
1.15k
    ssl_requirements_ = SslRequirements::All;
1993
1.15k
    break;
1994
21.2k
  }
1995
21.1k
  ssl_redirect_route_ = std::make_shared<SslRedirectRoute>(shared_virtual_host_);
1996
1997
21.1k
  if (virtual_host.has_matcher()) {
1998
7.63k
    RouteActionContext context{shared_virtual_host_, factory_context};
1999
7.63k
    RouteActionValidationVisitor validation_visitor;
2000
7.63k
    Matcher::MatchTreeFactory<Http::HttpMatchingData, RouteActionContext> factory(
2001
7.63k
        context, factory_context, validation_visitor);
2002
2003
7.63k
    matcher_ = factory.create(virtual_host.matcher())();
2004
2005
7.63k
    if (!validation_visitor.errors().empty()) {
2006
      // TODO(snowp): Output all violations.
2007
578
      creation_status = absl::InvalidArgumentError(
2008
578
          fmt::format("requirement violation while creating route match tree: {}",
2009
578
                      validation_visitor.errors()[0]));
2010
578
      return;
2011
578
    }
2012
13.4k
  } else {
2013
53.4k
    for (const auto& route : virtual_host.routes()) {
2014
53.4k
      auto route_or_error = RouteCreator::createAndValidateRoute(
2015
53.4k
          route, shared_virtual_host_, factory_context, validator, validation_clusters);
2016
53.4k
      SET_AND_RETURN_IF_NOT_OK(route_or_error.status(), creation_status);
2017
53.0k
      routes_.emplace_back(route_or_error.value());
2018
53.0k
    }
2019
13.4k
  }
2020
21.1k
}
2021
2022
0
const VirtualHost& SslRedirectRoute::virtualHost() const { return *virtual_host_; }
2023
2024
RouteConstSharedPtr VirtualHostImpl::getRouteFromRoutes(
2025
    const RouteCallback& cb, const Http::RequestHeaderMap& headers,
2026
    const StreamInfo::StreamInfo& stream_info, uint64_t random_value,
2027
3.29k
    absl::Span<const RouteEntryImplBaseConstSharedPtr> routes) const {
2028
24.4k
  for (auto route = routes.begin(); route != routes.end(); ++route) {
2029
22.9k
    if (!headers.Path() && !(*route)->supportsPathlessHeaders()) {
2030
1.23k
      continue;
2031
1.23k
    }
2032
2033
21.7k
    RouteConstSharedPtr route_entry = (*route)->matches(headers, stream_info, random_value);
2034
21.7k
    if (route_entry == nullptr) {
2035
19.8k
      continue;
2036
19.8k
    }
2037
2038
1.84k
    if (cb == nullptr) {
2039
1.84k
      return route_entry;
2040
1.84k
    }
2041
2042
0
    RouteEvalStatus eval_status = (std::next(route) == routes.end())
2043
0
                                      ? RouteEvalStatus::NoMoreRoutes
2044
0
                                      : RouteEvalStatus::HasMoreRoutes;
2045
0
    RouteMatchStatus match_status = cb(route_entry, eval_status);
2046
0
    if (match_status == RouteMatchStatus::Accept) {
2047
0
      return route_entry;
2048
0
    }
2049
0
    if (match_status == RouteMatchStatus::Continue &&
2050
0
        eval_status == RouteEvalStatus::NoMoreRoutes) {
2051
0
      ENVOY_LOG(debug,
2052
0
                "return null when route match status is Continue but there is no more routes");
2053
0
      return nullptr;
2054
0
    }
2055
0
  }
2056
2057
1.44k
  ENVOY_LOG(debug, "route was resolved but final route list did not match incoming request");
2058
1.44k
  return nullptr;
2059
3.29k
}
2060
2061
RouteConstSharedPtr VirtualHostImpl::getRouteFromEntries(const RouteCallback& cb,
2062
                                                         const Http::RequestHeaderMap& headers,
2063
                                                         const StreamInfo::StreamInfo& stream_info,
2064
3.54k
                                                         uint64_t random_value) const {
2065
  // In the rare case that X-Forwarded-Proto and scheme disagree (say http URL over an HTTPS
2066
  // connection), force a redirect based on underlying protocol, rather than URL
2067
  // scheme, so don't force a redirect for a http:// url served over a TLS
2068
  // connection.
2069
3.54k
  const absl::string_view scheme = headers.getForwardedProtoValue();
2070
3.54k
  if (scheme.empty()) {
2071
    // No scheme header. This normally only happens when ActiveStream::decodeHeaders
2072
    // bails early (as it rejects a request), or a buggy filter removes the :scheme header.
2073
231
    return nullptr;
2074
231
  }
2075
2076
  // First check for ssl redirect.
2077
3.31k
  if (ssl_requirements_ == SslRequirements::All && scheme != "https") {
2078
7
    return ssl_redirect_route_;
2079
3.30k
  } else if (ssl_requirements_ == SslRequirements::ExternalOnly && scheme != "https" &&
2080
3.30k
             !Http::HeaderUtility::isEnvoyInternalRequest(headers)) {
2081
10
    return ssl_redirect_route_;
2082
10
  }
2083
2084
3.29k
  if (matcher_) {
2085
1.18k
    Http::Matching::HttpMatchingDataImpl data(stream_info);
2086
1.18k
    data.onRequestHeaders(headers);
2087
2088
1.18k
    auto match = Matcher::evaluateMatch<Http::HttpMatchingData>(*matcher_, data);
2089
2090
1.18k
    if (match.result_) {
2091
1.18k
      const auto result = match.result_();
2092
1.18k
      if (result->typeUrl() == RouteMatchAction::staticTypeUrl()) {
2093
1.18k
        const RouteMatchAction& route_action = result->getTyped<RouteMatchAction>();
2094
2095
1.18k
        return getRouteFromRoutes(cb, headers, stream_info, random_value, {route_action.route()});
2096
1.18k
      } else if (result->typeUrl() == RouteListMatchAction::staticTypeUrl()) {
2097
0
        const RouteListMatchAction& action = result->getTyped<RouteListMatchAction>();
2098
2099
0
        return getRouteFromRoutes(cb, headers, stream_info, random_value, action.routes());
2100
0
      }
2101
0
      PANIC("Action in router matcher should be Route or RouteList");
2102
0
    }
2103
2104
0
    ENVOY_LOG(debug, "failed to match incoming request: {}", static_cast<int>(match.match_state_));
2105
2106
0
    return nullptr;
2107
1.18k
  }
2108
2109
  // Check for a route that matches the request.
2110
2.10k
  return getRouteFromRoutes(cb, headers, stream_info, random_value, routes_);
2111
3.29k
}
2112
2113
const VirtualHostImpl* RouteMatcher::findWildcardVirtualHost(
2114
    absl::string_view host, const RouteMatcher::WildcardVirtualHosts& wildcard_virtual_hosts,
2115
92
    RouteMatcher::SubstringFunction substring_function) const {
2116
  // We do a longest wildcard match against the host that's passed in
2117
  // (e.g. "foo-bar.baz.com" should match "*-bar.baz.com" before matching "*.baz.com" for suffix
2118
  // wildcards). This is done by scanning the length => wildcards map looking for every wildcard
2119
  // whose size is < length.
2120
504
  for (const auto& iter : wildcard_virtual_hosts) {
2121
504
    const uint32_t wildcard_length = iter.first;
2122
504
    const auto& wildcard_map = iter.second;
2123
    // >= because *.foo.com shouldn't match .foo.com.
2124
504
    if (wildcard_length >= host.size()) {
2125
123
      continue;
2126
123
    }
2127
381
    const auto match = wildcard_map.find(substring_function(host, wildcard_length));
2128
381
    if (match != wildcard_map.end()) {
2129
5
      return match->second.get();
2130
5
    }
2131
381
  }
2132
87
  return nullptr;
2133
92
}
2134
absl::StatusOr<std::unique_ptr<RouteMatcher>>
2135
RouteMatcher::create(const envoy::config::route::v3::RouteConfiguration& route_config,
2136
                     const CommonConfigSharedPtr& global_route_config,
2137
                     Server::Configuration::ServerFactoryContext& factory_context,
2138
18.4k
                     ProtobufMessage::ValidationVisitor& validator, bool validate_clusters) {
2139
18.4k
  absl::Status creation_status = absl::OkStatus();
2140
18.4k
  auto ret = std::unique_ptr<RouteMatcher>{new RouteMatcher(route_config, global_route_config,
2141
18.4k
                                                            factory_context, validator,
2142
18.4k
                                                            validate_clusters, creation_status)};
2143
18.4k
  RETURN_IF_NOT_OK(creation_status);
2144
16.1k
  return ret;
2145
18.4k
}
2146
RouteMatcher::RouteMatcher(const envoy::config::route::v3::RouteConfiguration& route_config,
2147
                           const CommonConfigSharedPtr& global_route_config,
2148
                           Server::Configuration::ServerFactoryContext& factory_context,
2149
                           ProtobufMessage::ValidationVisitor& validator, bool validate_clusters,
2150
                           absl::Status& creation_status)
2151
    : vhost_scope_(factory_context.scope().scopeFromStatName(
2152
          factory_context.routerContext().virtualClusterStatNames().vhost_)),
2153
18.4k
      ignore_port_in_host_matching_(route_config.ignore_port_in_host_matching()) {
2154
18.4k
  absl::optional<Upstream::ClusterManager::ClusterInfoMaps> validation_clusters;
2155
18.4k
  if (validate_clusters) {
2156
17.0k
    validation_clusters = factory_context.clusterManager().clusters();
2157
17.0k
  }
2158
21.4k
  for (const auto& virtual_host_config : route_config.virtual_hosts()) {
2159
21.4k
    VirtualHostSharedPtr virtual_host = std::make_shared<VirtualHostImpl>(
2160
21.4k
        virtual_host_config, global_route_config, factory_context, *vhost_scope_, validator,
2161
21.4k
        validation_clusters, creation_status);
2162
21.4k
    SET_AND_RETURN_IF_NOT_OK(creation_status, creation_status);
2163
25.4k
    for (const std::string& domain_name : virtual_host_config.domains()) {
2164
25.4k
      const Http::LowerCaseString lower_case_domain_name(domain_name);
2165
25.4k
      absl::string_view domain = lower_case_domain_name;
2166
25.4k
      bool duplicate_found = false;
2167
25.4k
      if ("*" == domain) {
2168
4.94k
        if (default_virtual_host_) {
2169
209
          creation_status = absl::InvalidArgumentError(fmt::format(
2170
209
              "Only a single wildcard domain is permitted in route {}", route_config.name()));
2171
209
          return;
2172
209
        }
2173
4.73k
        default_virtual_host_ = virtual_host;
2174
20.4k
      } else if (!domain.empty() && '*' == domain[0]) {
2175
1.24k
        duplicate_found = !wildcard_virtual_host_suffixes_[domain.size() - 1]
2176
1.24k
                               .emplace(domain.substr(1), virtual_host)
2177
1.24k
                               .second;
2178
19.2k
      } else if (!domain.empty() && '*' == domain[domain.size() - 1]) {
2179
612
        duplicate_found = !wildcard_virtual_host_prefixes_[domain.size() - 1]
2180
612
                               .emplace(domain.substr(0, domain.size() - 1), virtual_host)
2181
612
                               .second;
2182
18.6k
      } else {
2183
18.6k
        duplicate_found = !virtual_hosts_.emplace(domain, virtual_host).second;
2184
18.6k
      }
2185
25.2k
      if (duplicate_found) {
2186
1.03k
        creation_status = absl::InvalidArgumentError(
2187
1.03k
            fmt::format("Only unique values for domains are permitted. Duplicate "
2188
1.03k
                        "entry of domain {} in route {}",
2189
1.03k
                        domain, route_config.name()));
2190
1.03k
        return;
2191
1.03k
      }
2192
25.2k
    }
2193
20.3k
  }
2194
18.4k
}
2195
2196
7.13k
const VirtualHostImpl* RouteMatcher::findVirtualHost(const Http::RequestHeaderMap& headers) const {
2197
  // Fast path the case where we only have a default virtual host.
2198
7.13k
  if (virtual_hosts_.empty() && wildcard_virtual_host_suffixes_.empty() &&
2199
7.13k
      wildcard_virtual_host_prefixes_.empty()) {
2200
4.44k
    return default_virtual_host_.get();
2201
4.44k
  }
2202
2203
  // There may be no authority in early reply paths in the HTTP connection manager.
2204
2.69k
  if (headers.Host() == nullptr) {
2205
2.43k
    return nullptr;
2206
2.43k
  }
2207
2208
  // If 'ignore_port_in_host_matching' is set, ignore the port number in the host header(if any).
2209
258
  absl::string_view host_header_value = headers.getHostValue();
2210
258
  if (ignorePortInHostMatching()) {
2211
130
    if (const absl::string_view::size_type port_start =
2212
130
            Http::HeaderUtility::getPortStart(host_header_value);
2213
130
        port_start != absl::string_view::npos) {
2214
30
      host_header_value = host_header_value.substr(0, port_start);
2215
30
    }
2216
130
  }
2217
  // TODO (@rshriram) Match Origin header in WebSocket
2218
  // request with VHost, using wildcard match
2219
  // Lower-case the value of the host header, as hostnames are case insensitive.
2220
258
  const std::string host = absl::AsciiStrToLower(host_header_value);
2221
258
  const auto iter = virtual_hosts_.find(host);
2222
258
  if (iter != virtual_hosts_.end()) {
2223
143
    return iter->second.get();
2224
143
  }
2225
115
  if (!wildcard_virtual_host_suffixes_.empty()) {
2226
54
    const VirtualHostImpl* vhost = findWildcardVirtualHost(
2227
54
        host, wildcard_virtual_host_suffixes_,
2228
298
        [](absl::string_view h, int l) -> absl::string_view { return h.substr(h.size() - l); });
2229
54
    if (vhost != nullptr) {
2230
1
      return vhost;
2231
1
    }
2232
54
  }
2233
114
  if (!wildcard_virtual_host_prefixes_.empty()) {
2234
38
    const VirtualHostImpl* vhost = findWildcardVirtualHost(
2235
38
        host, wildcard_virtual_host_prefixes_,
2236
83
        [](absl::string_view h, int l) -> absl::string_view { return h.substr(0, l); });
2237
38
    if (vhost != nullptr) {
2238
4
      return vhost;
2239
4
    }
2240
38
  }
2241
110
  return default_virtual_host_.get();
2242
114
}
2243
2244
RouteConstSharedPtr RouteMatcher::route(const RouteCallback& cb,
2245
                                        const Http::RequestHeaderMap& headers,
2246
                                        const StreamInfo::StreamInfo& stream_info,
2247
7.13k
                                        uint64_t random_value) const {
2248
7.13k
  const VirtualHostImpl* virtual_host = findVirtualHost(headers);
2249
7.13k
  if (virtual_host) {
2250
3.54k
    return virtual_host->getRouteFromEntries(cb, headers, stream_info, random_value);
2251
3.59k
  } else {
2252
3.59k
    return nullptr;
2253
3.59k
  }
2254
7.13k
}
2255
2256
const SslRedirector SslRedirectRoute::SSL_REDIRECTOR;
2257
const envoy::config::core::v3::Metadata SslRedirectRoute::metadata_;
2258
const Envoy::Config::TypedMetadataImpl<Envoy::Config::TypedMetadataFactory>
2259
    SslRedirectRoute::typed_metadata_({});
2260
2261
const VirtualCluster*
2262
982
CommonVirtualHostImpl::virtualClusterFromEntries(const Http::HeaderMap& headers) const {
2263
982
  for (const VirtualClusterEntry& entry : virtual_clusters_) {
2264
0
    if (Http::HeaderUtility::matchHeaders(headers, entry.headers_)) {
2265
0
      return &entry;
2266
0
    }
2267
0
  }
2268
2269
982
  if (!virtual_clusters_.empty()) {
2270
0
    return virtual_cluster_catch_all_.get();
2271
0
  }
2272
2273
982
  return nullptr;
2274
982
}
2275
2276
absl::StatusOr<std::shared_ptr<CommonConfigImpl>>
2277
CommonConfigImpl::create(const envoy::config::route::v3::RouteConfiguration& config,
2278
                         Server::Configuration::ServerFactoryContext& factory_context,
2279
19.4k
                         ProtobufMessage::ValidationVisitor& validator) {
2280
19.4k
  absl::Status creation_status = absl::OkStatus();
2281
19.4k
  auto ret = std::shared_ptr<CommonConfigImpl>(
2282
19.4k
      new CommonConfigImpl(config, factory_context, validator, creation_status));
2283
19.4k
  RETURN_IF_NOT_OK(creation_status);
2284
19.3k
  return ret;
2285
19.4k
}
2286
2287
CommonConfigImpl::CommonConfigImpl(const envoy::config::route::v3::RouteConfiguration& config,
2288
                                   Server::Configuration::ServerFactoryContext& factory_context,
2289
                                   ProtobufMessage::ValidationVisitor& validator,
2290
                                   absl::Status& creation_status)
2291
    : name_(config.name()), symbol_table_(factory_context.scope().symbolTable()),
2292
      per_filter_configs_(THROW_OR_RETURN_VALUE(
2293
          PerFilterConfigs::create(config.typed_per_filter_config(), factory_context, validator),
2294
          std::unique_ptr<PerFilterConfigs>)),
2295
      max_direct_response_body_size_bytes_(
2296
          PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, max_direct_response_body_size_bytes,
2297
                                          DEFAULT_MAX_DIRECT_RESPONSE_BODY_SIZE_BYTES)),
2298
      uses_vhds_(config.has_vhds()),
2299
      most_specific_header_mutations_wins_(config.most_specific_header_mutations_wins()),
2300
19.4k
      ignore_path_parameters_in_path_matching_(config.ignore_path_parameters_in_path_matching()) {
2301
19.4k
  if (!config.request_mirror_policies().empty()) {
2302
975
    shadow_policies_.reserve(config.request_mirror_policies().size());
2303
1.89k
    for (const auto& mirror_policy_config : config.request_mirror_policies()) {
2304
1.89k
      auto policy_or_error = ShadowPolicyImpl::create(mirror_policy_config);
2305
1.89k
      SET_AND_RETURN_IF_NOT_OK(policy_or_error.status(), creation_status);
2306
1.74k
      shadow_policies_.push_back(std::move(policy_or_error.value()));
2307
1.74k
    }
2308
975
  }
2309
2310
  // Initialize all cluster specifier plugins before creating route matcher. Because the route may
2311
  // reference it by name.
2312
19.4k
  for (const auto& plugin_proto : config.cluster_specifier_plugins()) {
2313
19.4k
    auto plugin = THROW_OR_RETURN_VALUE(
2314
19.4k
        getClusterSpecifierPluginByTheProto(plugin_proto, validator, factory_context),
2315
19.4k
        ClusterSpecifierPluginSharedPtr);
2316
19.4k
    cluster_specifier_plugins_.emplace(plugin_proto.extension().name(), std::move(plugin));
2317
19.4k
  }
2318
2319
19.3k
  for (const std::string& header : config.internal_only_headers()) {
2320
3.66k
    internal_only_headers_.push_back(Http::LowerCaseString(header));
2321
3.66k
  }
2322
2323
19.3k
  if (!config.request_headers_to_add().empty() || !config.request_headers_to_remove().empty()) {
2324
3.20k
    request_headers_parser_ =
2325
3.20k
        THROW_OR_RETURN_VALUE(HeaderParser::configure(config.request_headers_to_add(),
2326
3.20k
                                                      config.request_headers_to_remove()),
2327
3.20k
                              Router::HeaderParserPtr);
2328
3.20k
  }
2329
19.3k
  if (!config.response_headers_to_add().empty() || !config.response_headers_to_remove().empty()) {
2330
3.34k
    response_headers_parser_ =
2331
3.34k
        THROW_OR_RETURN_VALUE(HeaderParser::configure(config.response_headers_to_add(),
2332
3.34k
                                                      config.response_headers_to_remove()),
2333
3.34k
                              Router::HeaderParserPtr);
2334
3.34k
  }
2335
2336
19.3k
  if (config.has_metadata()) {
2337
806
    metadata_ = std::make_unique<RouteMetadataPack>(config.metadata());
2338
806
  }
2339
19.3k
}
2340
2341
absl::StatusOr<ClusterSpecifierPluginSharedPtr>
2342
182
CommonConfigImpl::clusterSpecifierPlugin(absl::string_view provider) const {
2343
182
  auto iter = cluster_specifier_plugins_.find(provider);
2344
182
  if (iter == cluster_specifier_plugins_.end() || iter->second == nullptr) {
2345
93
    return absl::InvalidArgumentError(
2346
93
        fmt::format("Unknown cluster specifier plugin name: {} is used in the route", provider));
2347
93
  }
2348
89
  return iter->second;
2349
182
}
2350
2351
0
const envoy::config::core::v3::Metadata& CommonConfigImpl::metadata() const {
2352
0
  return metadata_ != nullptr ? metadata_->proto_metadata_
2353
0
                              : DefaultRouteMetadataPack::get().proto_metadata_;
2354
0
}
2355
0
const Envoy::Config::TypedMetadata& CommonConfigImpl::typedMetadata() const {
2356
0
  return metadata_ != nullptr ? metadata_->typed_metadata_
2357
0
                              : DefaultRouteMetadataPack::get().typed_metadata_;
2358
0
}
2359
2360
absl::StatusOr<std::shared_ptr<CommonConfigImpl>>
2361
create(const envoy::config::route::v3::RouteConfiguration& config,
2362
       Server::Configuration::ServerFactoryContext& factory_context,
2363
0
       ProtobufMessage::ValidationVisitor& validator) {
2364
0
  auto config_or_error = CommonConfigImpl::create(config, factory_context, validator);
2365
0
  RETURN_IF_NOT_OK(config_or_error.status());
2366
0
  return config_or_error.value();
2367
0
}
2368
2369
absl::StatusOr<std::shared_ptr<ConfigImpl>>
2370
ConfigImpl::create(const envoy::config::route::v3::RouteConfiguration& config,
2371
                   Server::Configuration::ServerFactoryContext& factory_context,
2372
19.4k
                   ProtobufMessage::ValidationVisitor& validator, bool validate_clusters_default) {
2373
19.4k
  absl::Status creation_status = absl::OkStatus();
2374
19.4k
  auto ret = std::shared_ptr<ConfigImpl>(new ConfigImpl(
2375
19.4k
      config, factory_context, validator, validate_clusters_default, creation_status));
2376
19.4k
  RETURN_IF_NOT_OK(creation_status);
2377
16.9k
  return ret;
2378
19.4k
}
2379
2380
ConfigImpl::ConfigImpl(const envoy::config::route::v3::RouteConfiguration& config,
2381
                       Server::Configuration::ServerFactoryContext& factory_context,
2382
                       ProtobufMessage::ValidationVisitor& validator,
2383
19.4k
                       bool validate_clusters_default, absl::Status& creation_status) {
2384
19.4k
  auto config_or_error = CommonConfigImpl::create(config, factory_context, validator);
2385
19.4k
  SET_AND_RETURN_IF_NOT_OK(config_or_error.status(), creation_status);
2386
19.3k
  shared_config_ = std::move(config_or_error.value());
2387
2388
19.3k
  auto matcher_or_error = RouteMatcher::create(
2389
19.3k
      config, shared_config_, factory_context, validator,
2390
19.3k
      PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, validate_clusters, validate_clusters_default));
2391
19.3k
  SET_AND_RETURN_IF_NOT_OK(matcher_or_error.status(), creation_status);
2392
16.9k
  route_matcher_ = std::move(matcher_or_error.value());
2393
16.9k
}
2394
2395
RouteConstSharedPtr ConfigImpl::route(const RouteCallback& cb,
2396
                                      const Http::RequestHeaderMap& headers,
2397
                                      const StreamInfo::StreamInfo& stream_info,
2398
7.13k
                                      uint64_t random_value) const {
2399
7.13k
  return route_matcher_->route(cb, headers, stream_info, random_value);
2400
7.13k
}
2401
2402
0
const envoy::config::core::v3::Metadata& NullConfigImpl::metadata() const {
2403
0
  return DefaultRouteMetadataPack::get().proto_metadata_;
2404
0
}
2405
0
const Envoy::Config::TypedMetadata& NullConfigImpl::typedMetadata() const {
2406
0
  return DefaultRouteMetadataPack::get().typed_metadata_;
2407
0
}
2408
2409
absl::StatusOr<std::unique_ptr<PerFilterConfigs>>
2410
PerFilterConfigs::create(const Protobuf::Map<std::string, ProtobufWkt::Any>& typed_configs,
2411
                         Server::Configuration::ServerFactoryContext& factory_context,
2412
195k
                         ProtobufMessage::ValidationVisitor& validator) {
2413
195k
  absl::Status creation_status = absl::OkStatus();
2414
195k
  auto ret = std::unique_ptr<PerFilterConfigs>(
2415
195k
      new PerFilterConfigs(typed_configs, factory_context, validator, creation_status));
2416
195k
  RETURN_IF_NOT_OK(creation_status);
2417
195k
  return ret;
2418
195k
}
2419
2420
absl::StatusOr<RouteSpecificFilterConfigConstSharedPtr>
2421
PerFilterConfigs::createRouteSpecificFilterConfig(
2422
    const std::string& name, const ProtobufWkt::Any& typed_config, bool is_optional,
2423
    Server::Configuration::ServerFactoryContext& factory_context,
2424
1.07k
    ProtobufMessage::ValidationVisitor& validator) {
2425
1.07k
  Server::Configuration::NamedHttpFilterConfigFactory* factory =
2426
1.07k
      Envoy::Config::Utility::getFactoryByType<Server::Configuration::NamedHttpFilterConfigFactory>(
2427
1.07k
          typed_config);
2428
1.07k
  if (factory == nullptr) {
2429
1.07k
    if (is_optional) {
2430
746
      ENVOY_LOG(warn,
2431
746
                "Can't find a registered implementation for http filter '{}' with type URL: '{}'",
2432
746
                name, Envoy::Config::Utility::getFactoryType(typed_config));
2433
746
      return nullptr;
2434
746
    } else {
2435
328
      return absl::InvalidArgumentError(
2436
328
          fmt::format("Didn't find a registered implementation for '{}' with type URL: '{}'", name,
2437
328
                      Envoy::Config::Utility::getFactoryType(typed_config)));
2438
328
    }
2439
1.07k
  }
2440
2441
5
  ProtobufTypes::MessagePtr proto_config = factory->createEmptyRouteConfigProto();
2442
5
  Envoy::Config::Utility::translateOpaqueConfig(typed_config, validator, *proto_config);
2443
5
  auto object = factory->createRouteSpecificFilterConfig(*proto_config, factory_context, validator);
2444
5
  if (object == nullptr) {
2445
0
    if (is_optional) {
2446
0
      ENVOY_LOG(
2447
0
          debug,
2448
0
          "The filter {} doesn't support virtual host or route specific configurations, and it is "
2449
0
          "optional, so ignore it.",
2450
0
          name);
2451
0
    } else {
2452
0
      return absl::InvalidArgumentError(fmt::format(
2453
0
          "The filter {} doesn't support virtual host or route specific configurations", name));
2454
0
    }
2455
0
  }
2456
5
  return object;
2457
5
}
2458
2459
PerFilterConfigs::PerFilterConfigs(
2460
    const Protobuf::Map<std::string, ProtobufWkt::Any>& typed_configs,
2461
    Server::Configuration::ServerFactoryContext& factory_context,
2462
195k
    ProtobufMessage::ValidationVisitor& validator, absl::Status& creation_status) {
2463
2464
195k
  std::string filter_config_type =
2465
195k
      envoy::config::route::v3::FilterConfig::default_instance().GetTypeName();
2466
2467
195k
  for (const auto& per_filter_config : typed_configs) {
2468
11.9k
    const std::string& name = per_filter_config.first;
2469
11.9k
    absl::StatusOr<RouteSpecificFilterConfigConstSharedPtr> config_or_error;
2470
2471
11.9k
    if (TypeUtil::typeUrlToDescriptorFullName(per_filter_config.second.type_url()) ==
2472
11.9k
        filter_config_type) {
2473
11.6k
      envoy::config::route::v3::FilterConfig filter_config;
2474
11.6k
      Envoy::Config::Utility::translateOpaqueConfig(per_filter_config.second, validator,
2475
11.6k
                                                    filter_config);
2476
2477
      // The filter is marked as disabled explicitly and the config is ignored directly.
2478
11.6k
      if (filter_config.disabled()) {
2479
9.85k
        configs_.emplace(name, FilterConfig{nullptr, true});
2480
9.85k
        continue;
2481
9.85k
      }
2482
2483
      // If the field `config` is not configured, we treat it as configuration error.
2484
1.77k
      if (!filter_config.has_config()) {
2485
17
        creation_status = absl::InvalidArgumentError(
2486
17
            fmt::format("Empty route/virtual host per filter configuration for {} filter", name));
2487
17
        return;
2488
17
      }
2489
2490
      // If the field `config` is configured but is empty, we treat the filter is enabled
2491
      // explicitly.
2492
1.75k
      if (filter_config.config().type_url().empty()) {
2493
991
        configs_.emplace(name, FilterConfig{nullptr, false});
2494
991
        continue;
2495
991
      }
2496
2497
768
      config_or_error = createRouteSpecificFilterConfig(
2498
768
          name, filter_config.config(), filter_config.is_optional(), factory_context, validator);
2499
768
    } else {
2500
313
      config_or_error = createRouteSpecificFilterConfig(name, per_filter_config.second, false,
2501
313
                                                        factory_context, validator);
2502
313
    }
2503
1.08k
    SET_AND_RETURN_IF_NOT_OK(config_or_error.status(), creation_status);
2504
2505
    // If a filter is explicitly configured we treat it as enabled.
2506
    // The config may be nullptr because the filter could be optional.
2507
753
    configs_.emplace(name, FilterConfig{std::move(config_or_error.value()), false});
2508
753
  }
2509
195k
}
2510
2511
2.44k
const RouteSpecificFilterConfig* PerFilterConfigs::get(absl::string_view name) const {
2512
2.44k
  auto it = configs_.find(name);
2513
2.44k
  return it == configs_.end() ? nullptr : it->second.config_.get();
2514
2.44k
}
2515
2516
5.74k
absl::optional<bool> PerFilterConfigs::disabled(absl::string_view name) const {
2517
  // Quick exit if there are no configs.
2518
5.74k
  if (configs_.empty()) {
2519
5.74k
    return absl::nullopt;
2520
5.74k
  }
2521
2522
0
  const auto it = configs_.find(name);
2523
0
  return it != configs_.end() ? absl::optional<bool>{it->second.disabled_} : absl::nullopt;
2524
5.74k
}
2525
2526
Matcher::ActionFactoryCb RouteMatchActionFactory::createActionFactoryCb(
2527
    const Protobuf::Message& config, RouteActionContext& context,
2528
64.9k
    ProtobufMessage::ValidationVisitor& validation_visitor) {
2529
64.9k
  const auto& route_config =
2530
64.9k
      MessageUtil::downcastAndValidate<const envoy::config::route::v3::Route&>(config,
2531
64.9k
                                                                               validation_visitor);
2532
64.9k
  auto route = THROW_OR_RETURN_VALUE(
2533
64.9k
      RouteCreator::createAndValidateRoute(route_config, context.vhost, context.factory_context,
2534
64.9k
                                           validation_visitor, absl::nullopt),
2535
64.9k
      RouteEntryImplBaseConstSharedPtr);
2536
2537
64.9k
  return [route]() { return std::make_unique<RouteMatchAction>(route); };
2538
64.9k
}
2539
REGISTER_FACTORY(RouteMatchActionFactory, Matcher::ActionFactory<RouteActionContext>);
2540
2541
Matcher::ActionFactoryCb RouteListMatchActionFactory::createActionFactoryCb(
2542
    const Protobuf::Message& config, RouteActionContext& context,
2543
0
    ProtobufMessage::ValidationVisitor& validation_visitor) {
2544
0
  const auto& route_config =
2545
0
      MessageUtil::downcastAndValidate<const envoy::config::route::v3::RouteList&>(
2546
0
          config, validation_visitor);
2547
2548
0
  std::vector<RouteEntryImplBaseConstSharedPtr> routes;
2549
0
  for (const auto& route : route_config.routes()) {
2550
0
    routes.emplace_back(THROW_OR_RETURN_VALUE(
2551
0
        RouteCreator::createAndValidateRoute(route, context.vhost, context.factory_context,
2552
0
                                             validation_visitor, absl::nullopt),
2553
0
        RouteEntryImplBaseConstSharedPtr));
2554
0
  }
2555
0
  return [routes]() { return std::make_unique<RouteListMatchAction>(routes); };
2556
0
}
2557
REGISTER_FACTORY(RouteListMatchActionFactory, Matcher::ActionFactory<RouteActionContext>);
2558
2559
} // namespace Router
2560
} // namespace Envoy