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/formatter/substitution_format_string.h"
36
#include "source/common/grpc/common.h"
37
#include "source/common/http/header_utility.h"
38
#include "source/common/http/headers.h"
39
#include "source/common/http/matching/data_impl.h"
40
#include "source/common/http/path_utility.h"
41
#include "source/common/http/utility.h"
42
#include "source/common/matcher/matcher.h"
43
#include "source/common/protobuf/protobuf.h"
44
#include "source/common/protobuf/utility.h"
45
#include "source/common/router/context_impl.h"
46
#include "source/common/router/header_cluster_specifier.h"
47
#include "source/common/router/matcher_visitor.h"
48
#include "source/common/router/weighted_cluster_specifier.h"
49
#include "source/common/runtime/runtime_features.h"
50
#include "source/common/tracing/custom_tag_impl.h"
51
#include "source/common/tracing/http_tracer_impl.h"
52
#include "source/extensions/early_data/default_early_data_policy.h"
53
#include "source/extensions/matching/network/common/inputs.h"
54
#include "source/extensions/path/match/uri_template/uri_template_match.h"
55
#include "source/extensions/path/rewrite/uri_template/uri_template_rewrite.h"
56

            
57
#include "absl/container/flat_hash_set.h"
58
#include "absl/container/inlined_vector.h"
59
#include "absl/strings/match.h"
60
#include "absl/types/optional.h"
61

            
62
namespace Envoy {
63
namespace Router {
64
class RouteCreator {
65
public:
66
  static absl::StatusOr<RouteEntryImplBaseConstSharedPtr>
67
  createAndValidateRoute(const envoy::config::route::v3::Route& route_config,
68
                         const CommonVirtualHostSharedPtr& vhost,
69
                         Server::Configuration::ServerFactoryContext& factory_context,
70
12800
                         ProtobufMessage::ValidationVisitor& validator, bool validate_clusters) {
71

            
72
12800
    absl::Status creation_status = absl::OkStatus();
73
12800
    RouteEntryImplBaseConstSharedPtr route;
74
12800
    switch (route_config.match().path_specifier_case()) {
75
12303
    case envoy::config::route::v3::RouteMatch::PathSpecifierCase::kPrefix:
76
12303
      route.reset(new PrefixRouteEntryImpl(vhost, route_config, factory_context, validator,
77
12303
                                           creation_status));
78
12303
      break;
79
178
    case envoy::config::route::v3::RouteMatch::PathSpecifierCase::kPath:
80
178
      route.reset(
81
178
          new PathRouteEntryImpl(vhost, route_config, factory_context, validator, creation_status));
82
178
      break;
83
32
    case envoy::config::route::v3::RouteMatch::PathSpecifierCase::kSafeRegex:
84
32
      route.reset(new RegexRouteEntryImpl(vhost, route_config, factory_context, validator,
85
32
                                          creation_status));
86
32
      break;
87
251
    case envoy::config::route::v3::RouteMatch::PathSpecifierCase::kConnectMatcher:
88
251
      route.reset(new ConnectRouteEntryImpl(vhost, route_config, factory_context, validator,
89
251
                                            creation_status));
90
251
      break;
91
7
    case envoy::config::route::v3::RouteMatch::PathSpecifierCase::kPathSeparatedPrefix:
92
7
      route.reset(new PathSeparatedPrefixRouteEntryImpl(vhost, route_config, factory_context,
93
7
                                                        validator, creation_status));
94
7
      break;
95
29
    case envoy::config::route::v3::RouteMatch::PathSpecifierCase::kPathMatchPolicy:
96
29
      route.reset(new UriTemplateMatcherRouteEntryImpl(vhost, route_config, factory_context,
97
29
                                                       validator, creation_status));
98
29
      break;
99
    case envoy::config::route::v3::RouteMatch::PathSpecifierCase::PATH_SPECIFIER_NOT_SET:
100
      break; // return the error below.
101
12800
    }
102
12789
    if (!route) {
103
      return absl::InvalidArgumentError("Invalid route config");
104
    }
105
12789
    RETURN_IF_NOT_OK(creation_status);
106

            
107
12768
    if (validate_clusters) {
108
11477
      const Upstream::ClusterManager& cluster_manager = factory_context.clusterManager();
109
11477
      RETURN_IF_NOT_OK(route->validateClusters(cluster_manager));
110
11478
      for (const auto& shadow_policy : route->shadowPolicies()) {
111
80
        if (!shadow_policy->cluster().empty()) {
112
76
          ASSERT(shadow_policy->clusterHeader().get().empty());
113
          // if (!validation_clusters->hasCluster(shadow_policy->cluster())) {
114
76
          if (!cluster_manager.hasCluster(shadow_policy->cluster())) {
115
1
            return absl::InvalidArgumentError(
116
1
                fmt::format("route: unknown shadow cluster '{}'", shadow_policy->cluster()));
117
1
          }
118
76
        }
119
80
      }
120
11473
    }
121

            
122
12763
    return route;
123
12768
  }
124
};
125

            
126
namespace {
127

            
128
constexpr uint32_t DEFAULT_MAX_DIRECT_RESPONSE_BODY_SIZE_BYTES = 4096;
129

            
130
// Returns an array of header parsers, sorted by specificity. The `specificity_ascend` parameter
131
// specifies whether the returned parsers will be sorted from least specific to most specific
132
// (global connection manager level header parser, virtual host level header parser and finally
133
// route-level parser.) or the reverse.
134
std::array<const HeaderParser*, 3>
135
getHeaderParsers(const HeaderParser* global_route_config_header_parser,
136
                 const HeaderParser* vhost_header_parser, const HeaderParser* route_header_parser,
137
85997
                 bool specificity_ascend) {
138
85997
  if (specificity_ascend) {
139
    // Sorted from least to most specific: global connection manager level headers, virtual host
140
    // level headers and finally route-level headers.
141
8
    return {global_route_config_header_parser, vhost_header_parser, route_header_parser};
142
85989
  } else {
143
    // Sorted from most to least specific.
144
85989
    return {route_header_parser, vhost_header_parser, global_route_config_header_parser};
145
85989
  }
146
85997
}
147

            
148
// If the implementation of a cluster specifier plugin is not provided in current Envoy and the
149
// plugin is set to optional, then this null plugin will be used as a placeholder.
150
class NullClusterSpecifierPlugin : public ClusterSpecifierPlugin {
151
public:
152
  RouteConstSharedPtr route(RouteEntryAndRouteConstSharedPtr, const Http::RequestHeaderMap&,
153
                            const StreamInfo::StreamInfo&, uint64_t) const override {
154
    return nullptr;
155
  }
156
};
157

            
158
absl::StatusOr<ClusterSpecifierPluginSharedPtr>
159
getClusterSpecifierPluginByTheProto(const envoy::config::route::v3::ClusterSpecifierPlugin& plugin,
160
                                    ProtobufMessage::ValidationVisitor& validator,
161
13
                                    Server::Configuration::ServerFactoryContext& factory_context) {
162
13
  auto* factory =
163
13
      Envoy::Config::Utility::getFactory<ClusterSpecifierPluginFactoryConfig>(plugin.extension());
164
13
  if (factory == nullptr) {
165
2
    if (plugin.is_optional()) {
166
1
      return std::make_shared<NullClusterSpecifierPlugin>();
167
1
    }
168
1
    return absl::InvalidArgumentError(
169
1
        fmt::format("Didn't find a registered implementation for '{}' with type URL: '{}'",
170
1
                    plugin.extension().name(),
171
1
                    Envoy::Config::Utility::getFactoryType(plugin.extension().typed_config())));
172
2
  }
173
11
  ASSERT(factory != nullptr);
174
11
  auto config =
175
11
      Envoy::Config::Utility::translateToFactoryConfig(plugin.extension(), validator, *factory);
176
11
  return factory->createClusterSpecifierPlugin(*config, factory_context);
177
13
}
178

            
179
::Envoy::Http::Utility::RedirectConfig
180
71
createRedirectConfig(const envoy::config::route::v3::Route& route, Regex::Engine& regex_engine) {
181
71
  ::Envoy::Http::Utility::RedirectConfig redirect_config{
182
71
      route.redirect().scheme_redirect(),
183
71
      route.redirect().host_redirect(),
184
71
      route.redirect().port_redirect() ? ":" + std::to_string(route.redirect().port_redirect())
185
71
                                       : "",
186
71
      route.redirect().path_redirect(),
187
71
      route.redirect().prefix_rewrite(),
188
71
      route.redirect().has_regex_rewrite() ? route.redirect().regex_rewrite().substitution() : "",
189
71
      route.redirect().has_regex_rewrite()
190
71
          ? THROW_OR_RETURN_VALUE(Regex::Utility::parseRegex(
191
71
                                      route.redirect().regex_rewrite().pattern(), regex_engine),
192
71
                                  Regex::CompiledMatcherPtr)
193
71
          : nullptr,
194
71
      route.redirect().path_redirect().find('?') != absl::string_view::npos,
195
71
      route.redirect().https_redirect(),
196
71
      route.redirect().strip_query()};
197
71
  if (route.redirect().has_regex_rewrite()) {
198
4
    ASSERT(redirect_config.prefix_rewrite_redirect_.empty());
199
4
  }
200
71
  return redirect_config;
201
71
}
202

            
203
std::string generateNewPath(absl::string_view origin_path, absl::string_view path_to_strip,
204
82
                            absl::string_view new_path_to_replace) {
205
82
  ASSERT(path_to_strip.size() <= origin_path.size());
206

            
207
82
  std::string result;
208
82
  result.reserve(new_path_to_replace.size() + origin_path.size() - path_to_strip.size());
209
82
  result.append(new_path_to_replace);
210
82
  result.append(origin_path.substr(path_to_strip.size()));
211
82
  return result;
212
82
}
213

            
214
std::string rewritePathByPrefixOrRegex(absl::string_view path, absl::string_view matched,
215
                                       absl::string_view prefix_rewrite,
216
                                       const Regex::CompiledMatcher* regex_rewrite,
217
44180
                                       absl::string_view regex_rewrite_substitution) {
218
44180
  if (!prefix_rewrite.empty()) {
219
47
    ASSERT(absl::StartsWithIgnoreCase(path, matched));
220
47
    return generateNewPath(path, matched, prefix_rewrite);
221
47
  }
222

            
223
44133
  if (regex_rewrite != nullptr) {
224
13
    absl::string_view path_only = Http::PathUtil::removeQueryAndFragment(path);
225
13
    ASSERT(path_only.size() <= path.size());
226
13
    const std::string new_path_only =
227
13
        regex_rewrite->replaceAll(path_only, regex_rewrite_substitution);
228
    // If regex rewrite fails then return nothing.
229
13
    if (new_path_only.empty()) {
230
      return {};
231
    }
232
13
    return generateNewPath(path, path_only, new_path_only);
233
13
  }
234
44120
  return {};
235
44133
}
236

            
237
} // namespace
238

            
239
43612
const std::string& OriginalConnectPort::key() {
240
43612
  CONSTRUCT_ON_FIRST_USE(std::string, "envoy.router.original_connect_port");
241
43612
}
242

            
243
15
std::string SslRedirector::newUri(const Http::RequestHeaderMap& headers) const {
244
15
  return Http::Utility::createSslRedirectPath(headers);
245
15
}
246

            
247
HedgePolicyImpl::HedgePolicyImpl(const envoy::config::route::v3::HedgePolicy& hedge_policy)
248
5
    : additional_request_chance_(hedge_policy.additional_request_chance()),
249
5
      initial_requests_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(hedge_policy, initial_requests, 1)),
250
5
      hedge_on_per_try_timeout_(hedge_policy.hedge_on_per_try_timeout()) {}
251

            
252
385
HedgePolicyImpl::HedgePolicyImpl() : initial_requests_(1), hedge_on_per_try_timeout_(false) {}
253

            
254
absl::StatusOr<std::unique_ptr<InternalRedirectPolicyImpl>> InternalRedirectPolicyImpl::create(
255
    const envoy::config::route::v3::InternalRedirectPolicy& policy_config,
256
418
    ProtobufMessage::ValidationVisitor& validator, absl::string_view current_route_name) {
257
418
  absl::Status creation_status = absl::OkStatus();
258
418
  auto ret = std::unique_ptr<InternalRedirectPolicyImpl>(new InternalRedirectPolicyImpl(
259
418
      policy_config, validator, current_route_name, creation_status));
260
418
  RETURN_IF_NOT_OK(creation_status);
261
417
  return ret;
262
418
}
263

            
264
InternalRedirectPolicyImpl::InternalRedirectPolicyImpl(
265
    const envoy::config::route::v3::InternalRedirectPolicy& policy_config,
266
    ProtobufMessage::ValidationVisitor& validator, absl::string_view current_route_name,
267
    absl::Status& creation_status)
268
418
    : current_route_name_(current_route_name),
269
418
      redirect_response_codes_(buildRedirectResponseCodes(policy_config)),
270
      max_internal_redirects_(
271
418
          PROTOBUF_GET_WRAPPED_OR_DEFAULT(policy_config, max_internal_redirects, 1)),
272
418
      enabled_(true), allow_cross_scheme_redirect_(policy_config.allow_cross_scheme_redirect()) {
273
418
  for (const auto& predicate : policy_config.predicates()) {
274
18
    auto& factory =
275
18
        Envoy::Config::Utility::getAndCheckFactory<InternalRedirectPredicateFactory>(predicate);
276
18
    auto config = factory.createEmptyConfigProto();
277
18
    SET_AND_RETURN_IF_NOT_OK(
278
18
        Envoy::Config::Utility::translateOpaqueConfig(predicate.typed_config(), validator, *config),
279
18
        creation_status);
280
18
    predicate_factories_.emplace_back(&factory, std::move(config));
281
18
  }
282
418
  for (const auto& header : policy_config.response_headers_to_copy()) {
283
232
    if (!Http::HeaderUtility::isModifiableHeader(header)) {
284
1
      creation_status =
285
1
          absl::InvalidArgumentError(":-prefixed headers or Hosts may not be specified here.");
286
1
      return;
287
1
    }
288
231
    response_headers_to_copy_.emplace_back(header);
289
231
  }
290
418
}
291

            
292
170
std::vector<InternalRedirectPredicateSharedPtr> InternalRedirectPolicyImpl::predicates() const {
293
170
  std::vector<InternalRedirectPredicateSharedPtr> predicates;
294
170
  predicates.reserve(predicate_factories_.size());
295
170
  for (const auto& predicate_factory : predicate_factories_) {
296
36
    predicates.emplace_back(predicate_factory.first->createInternalRedirectPredicate(
297
36
        *predicate_factory.second, current_route_name_));
298
36
  }
299
170
  return predicates;
300
170
}
301

            
302
const std::vector<Http::LowerCaseString>&
303
171
InternalRedirectPolicyImpl::responseHeadersToCopy() const {
304
171
  return response_headers_to_copy_;
305
171
}
306

            
307
absl::flat_hash_set<Http::Code> InternalRedirectPolicyImpl::buildRedirectResponseCodes(
308
418
    const envoy::config::route::v3::InternalRedirectPolicy& policy_config) const {
309
418
  if (policy_config.redirect_response_codes_size() == 0) {
310
392
    return absl::flat_hash_set<Http::Code>{Http::Code::Found};
311
392
  }
312
26
  absl::flat_hash_set<Http::Code> ret;
313
26
  std::for_each(policy_config.redirect_response_codes().begin(),
314
30
                policy_config.redirect_response_codes().end(), [&ret](uint32_t response_code) {
315
30
                  const absl::flat_hash_set<uint32_t> valid_redirect_response_code = {301, 302, 303,
316
30
                                                                                      307, 308};
317
30
                  if (valid_redirect_response_code.contains(response_code)) {
318
27
                    ret.insert(static_cast<Http::Code>(response_code));
319
27
                  }
320
30
                });
321
26
  return ret;
322
418
}
323

            
324
absl::Status validateMirrorClusterSpecifier(
325
109
    const envoy::config::route::v3::RouteAction::RequestMirrorPolicy& config) {
326
109
  if (!config.cluster().empty() && !config.cluster_header().empty()) {
327
1
    return absl::InvalidArgumentError(fmt::format("Only one of cluster '{}' or cluster_header '{}' "
328
1
                                                  "in request mirror policy can be specified",
329
1
                                                  config.cluster(), config.cluster_header()));
330
108
  } else if (config.cluster().empty() && config.cluster_header().empty()) {
331
    // For shadow policies with `cluster_header_`, we only verify that this field is not
332
    // empty because the cluster name is not set yet at config time.
333
1
    return absl::InvalidArgumentError(
334
1
        "Exactly one of cluster or cluster_header in request mirror policy need to be specified");
335
1
  }
336
107
  return absl::OkStatus();
337
109
}
338

            
339
absl::StatusOr<std::shared_ptr<ShadowPolicyImpl>>
340
ShadowPolicyImpl::create(const RequestMirrorPolicy& config,
341
109
                         Server::Configuration::CommonFactoryContext& factory_context) {
342
109
  absl::Status creation_status = absl::OkStatus();
343
109
  auto ret = std::shared_ptr<ShadowPolicyImpl>(
344
109
      new ShadowPolicyImpl(config, factory_context, creation_status));
345
109
  RETURN_IF_NOT_OK(creation_status);
346
107
  return ret;
347
109
}
348

            
349
ShadowPolicyImpl::ShadowPolicyImpl(const RequestMirrorPolicy& config,
350
                                   Server::Configuration::CommonFactoryContext& factory_context,
351
                                   absl::Status& creation_status)
352
109
    : cluster_(config.cluster()), cluster_header_(config.cluster_header()),
353
109
      disable_shadow_host_suffix_append_(config.disable_shadow_host_suffix_append()),
354
109
      host_rewrite_literal_(config.host_rewrite_literal()) {
355
109
  SET_AND_RETURN_IF_NOT_OK(validateMirrorClusterSpecifier(config), creation_status);
356

            
357
107
  if (config.has_runtime_fraction()) {
358
20
    runtime_key_ = config.runtime_fraction().runtime_key();
359
20
    default_value_ = config.runtime_fraction().default_value();
360
100
  } else {
361
    // If there is no runtime fraction specified, the default is 100% sampled. By leaving
362
    // runtime_key_ empty and forcing the default to 100% this will yield the expected behavior.
363
87
    default_value_.set_numerator(100);
364
87
    default_value_.set_denominator(envoy::type::v3::FractionalPercent::HUNDRED);
365
87
  }
366
  // If trace sampling is not explicitly configured in shadow_policy, we pass null optional to
367
  // inherit the parent's sampling decision. This prevents oversampling when runtime sampling is
368
  // disabled.
369
107
  trace_sampled_ = config.has_trace_sampled() ? absl::optional<bool>(config.trace_sampled().value())
370
107
                                              : absl::nullopt;
371

            
372
  // Create HeaderMutations directly from HeaderMutation rules
373
107
  if (!config.request_headers_mutations().empty()) {
374
8
    auto mutations_or_error =
375
8
        Http::HeaderMutations::create(config.request_headers_mutations(), factory_context);
376
8
    SET_AND_RETURN_IF_NOT_OK(mutations_or_error.status(), creation_status);
377
8
    request_headers_mutations_ = std::move(mutations_or_error.value());
378
8
  }
379
107
}
380

            
381
144
const Http::HeaderEvaluator& ShadowPolicyImpl::headerEvaluator() const {
382
144
  if (request_headers_mutations_) {
383
11
    return *request_headers_mutations_;
384
11
  }
385
133
  return HeaderParser::defaultParser();
386
144
}
387

            
388
DecoratorImpl::DecoratorImpl(const envoy::config::route::v3::Decorator& decorator)
389
2
    : operation_(decorator.operation()),
390
2
      propagate_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(decorator, propagate, true)) {}
391

            
392
1
void DecoratorImpl::apply(Tracing::Span& span) const {
393
1
  if (!operation_.empty()) {
394
1
    span.setOperation(operation_);
395
1
  }
396
1
}
397

            
398
1
const std::string& DecoratorImpl::getOperation() const { return operation_; }
399

            
400
1
bool DecoratorImpl::propagate() const { return propagate_; }
401

            
402
3
RouteTracingImpl::RouteTracingImpl(const envoy::config::route::v3::Tracing& tracing) {
403
3
  if (!tracing.has_client_sampling()) {
404
1
    client_sampling_.set_numerator(100);
405
1
    client_sampling_.set_denominator(envoy::type::v3::FractionalPercent::HUNDRED);
406
2
  } else {
407
2
    client_sampling_ = tracing.client_sampling();
408
2
  }
409
3
  if (!tracing.has_random_sampling()) {
410
2
    random_sampling_.set_numerator(100);
411
2
    random_sampling_.set_denominator(envoy::type::v3::FractionalPercent::HUNDRED);
412
2
  } else {
413
1
    random_sampling_ = tracing.random_sampling();
414
1
  }
415
3
  if (!tracing.has_overall_sampling()) {
416
1
    overall_sampling_.set_numerator(100);
417
1
    overall_sampling_.set_denominator(envoy::type::v3::FractionalPercent::HUNDRED);
418
2
  } else {
419
2
    overall_sampling_ = tracing.overall_sampling();
420
2
  }
421
4
  for (const auto& tag : tracing.custom_tags()) {
422
4
    custom_tags_.emplace(tag.tag(), Tracing::CustomTagUtility::createCustomTag(tag));
423
4
  }
424
3
  if (!tracing.operation().empty()) {
425
1
    auto operation = Formatter::FormatterImpl::create(tracing.operation(), true);
426
1
    THROW_IF_NOT_OK_REF(operation.status());
427
1
    operation_ = std::move(operation.value());
428
1
  }
429
3
  if (!tracing.upstream_operation().empty()) {
430
1
    auto operation = Formatter::FormatterImpl::create(tracing.upstream_operation(), true);
431
1
    THROW_IF_NOT_OK_REF(operation.status());
432
1
    upstream_operation_ = std::move(operation.value());
433
1
  }
434
3
}
435

            
436
4
const envoy::type::v3::FractionalPercent& RouteTracingImpl::getClientSampling() const {
437
4
  return client_sampling_;
438
4
}
439

            
440
4
const envoy::type::v3::FractionalPercent& RouteTracingImpl::getRandomSampling() const {
441
4
  return random_sampling_;
442
4
}
443

            
444
4
const envoy::type::v3::FractionalPercent& RouteTracingImpl::getOverallSampling() const {
445
4
  return overall_sampling_;
446
4
}
447
1
const Tracing::CustomTagMap& RouteTracingImpl::getCustomTags() const { return custom_tags_; }
448

            
449
uint64_t getRequestBodyBufferLimit(const CommonVirtualHostSharedPtr& vhost,
450
12795
                                   const envoy::config::route::v3::Route& route) {
451
  // Route level request_body_buffer_limit takes precedence over all others.
452
12795
  if (route.has_request_body_buffer_limit()) {
453
24
    return route.request_body_buffer_limit().value();
454
24
  }
455

            
456
  // Then virtual host level request_body_buffer_limit.
457
12771
  if (const auto v = vhost->requestBodyBufferLimit(); v.has_value()) {
458
27
    return v.value();
459
27
  }
460

            
461
  // Then route level legacy per_request_buffer_limit_bytes.
462
12744
  if (route.has_per_request_buffer_limit_bytes()) {
463
1
    return route.per_request_buffer_limit_bytes().value();
464
1
  }
465

            
466
  // Then virtual host level legacy per_request_buffer_limit_bytes.
467
12743
  if (const auto v = vhost->legacyRequestBodyBufferLimit(); v.has_value()) {
468
    return v.value();
469
  }
470

            
471
  // Finally return max value to indicate no limit.
472
12743
  return std::numeric_limits<uint64_t>::max();
473
12743
}
474

            
475
RouteEntryImplBase::RouteEntryImplBase(const CommonVirtualHostSharedPtr& vhost,
476
                                       const envoy::config::route::v3::Route& route,
477
                                       Server::Configuration::ServerFactoryContext& factory_context,
478
                                       ProtobufMessage::ValidationVisitor& validator,
479
                                       absl::Status& creation_status)
480
    : path_matcher_(
481
12800
          THROW_OR_RETURN_VALUE(buildPathMatcher(route, validator), PathMatcherSharedPtr)),
482
12800
      prefix_rewrite_(route.route().prefix_rewrite()),
483
      path_rewriter_(
484
12800
          THROW_OR_RETURN_VALUE(buildPathRewriter(route, validator), PathRewriterSharedPtr)),
485
12800
      host_rewrite_(route.route().host_rewrite_literal()),
486
12800
      host_rewrite_header_(route.route().host_rewrite_header()),
487
      host_rewrite_path_regex_(
488
12800
          route.route().has_host_rewrite_path_regex()
489
12800
              ? THROW_OR_RETURN_VALUE(
490
12800
                    Regex::Utility::parseRegex(route.route().host_rewrite_path_regex().pattern(),
491
12800
                                               factory_context.regexEngine()),
492
12800
                    Regex::CompiledMatcherPtr)
493
12800
              : nullptr),
494
      host_rewrite_path_regex_substitution_(
495
12800
          route.route().has_host_rewrite_path_regex()
496
12800
              ? route.route().host_rewrite_path_regex().substitution()
497
12800
              : ""),
498
12800
      vhost_(vhost), vhost_copy_(vhost), cluster_name_(route.route().cluster()),
499
12800
      timeout_(PROTOBUF_GET_MS_OR_DEFAULT(route.route(), timeout, DEFAULT_ROUTE_TIMEOUT_MS)),
500
12800
      optional_timeouts_(buildOptionalTimeouts(route.route())), loader_(factory_context.runtime()),
501
12800
      runtime_(loadRuntimeData(route.match())),
502
12800
      redirect_config_(route.has_redirect()
503
12800
                           ? std::make_unique<::Envoy::Http::Utility::RedirectConfig>(
504
71
                                 createRedirectConfig(route, factory_context.regexEngine()))
505
12800
                           : nullptr),
506
12800
      hedge_policy_(buildHedgePolicy(vhost->hedgePolicy(), route.route())),
507
12800
      internal_redirect_policy_(
508
12800
          THROW_OR_RETURN_VALUE(buildInternalRedirectPolicy(route.route(), validator, route.name()),
509
12800
                                std::unique_ptr<InternalRedirectPolicyImpl>)),
510
      config_headers_(
511
12800
          Http::HeaderUtility::buildHeaderDataVector(route.match().headers(), factory_context)),
512
12800
      dynamic_metadata_([&]() {
513
12795
        std::vector<Envoy::Matchers::MetadataMatcher> vec;
514
12795
        for (const auto& elt : route.match().dynamic_metadata()) {
515
9
          vec.emplace_back(elt, factory_context);
516
9
        }
517
12795
        return vec;
518
12795
      }()),
519
12800
      filter_state_([&]() {
520
12795
        std::vector<Envoy::Matchers::FilterStateMatcher> vec;
521
12795
        vec.reserve(route.match().filter_state().size());
522
12795
        for (const auto& elt : route.match().filter_state()) {
523
1
          Envoy::Matchers::FilterStateMatcherPtr match = THROW_OR_RETURN_VALUE(
524
1
              Envoy::Matchers::FilterStateMatcher::create(elt, factory_context),
525
1
              Envoy::Matchers::FilterStateMatcherPtr);
526
1
          vec.emplace_back(std::move(*match));
527
1
        }
528
12795
        return vec;
529
12795
      }()),
530
12800
      opaque_config_(parseOpaqueConfig(route)), decorator_(parseDecorator(route)),
531
12800
      route_tracing_(parseRouteTracing(route)), route_name_(route.name()),
532
12800
      time_source_(factory_context.mainThreadDispatcher().timeSource()),
533
12800
      request_body_buffer_limit_(getRequestBodyBufferLimit(vhost, route)),
534
12800
      direct_response_code_(ConfigUtility::parseDirectResponseCode(route)),
535
12800
      cluster_not_found_response_code_(ConfigUtility::parseClusterNotFoundResponseCode(
536
12800
          route.route().cluster_not_found_response_code())),
537
12800
      priority_(ConfigUtility::parsePriority(route.route().priority())),
538
12800
      auto_host_rewrite_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(route.route(), auto_host_rewrite, false)),
539
12800
      append_xfh_(route.route().append_x_forwarded_host()),
540
12800
      using_new_timeouts_(route.route().has_max_stream_duration()),
541
12800
      match_grpc_(route.match().has_grpc()),
542
12800
      case_sensitive_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(route.match(), case_sensitive, true)) {
543

            
544
12800
  auto config_or_error =
545
12800
      PerFilterConfigs::create(route.typed_per_filter_config(), factory_context, validator);
546
12800
  SET_AND_RETURN_IF_NOT_OK(config_or_error.status(), creation_status);
547
12795
  per_filter_configs_ = std::move(config_or_error.value());
548

            
549
12795
  auto policy_or_error =
550
12795
      buildRetryPolicy(vhost->retryPolicy(), route.route(), validator, factory_context);
551
12795
  SET_AND_RETURN_IF_NOT_OK(policy_or_error.status(), creation_status);
552
12794
  retry_policy_ = policy_or_error.value() != nullptr ? std::move(policy_or_error.value())
553
12794
                                                     : RetryPolicyImpl::DefaultRetryPolicy;
554

            
555
12794
  if (route.has_direct_response() && route.direct_response().has_body()) {
556
464
    auto provider_or_error = Envoy::Config::DataSource::DataSourceProvider<std::string>::create(
557
464
        route.direct_response().body(), factory_context.mainThreadDispatcher(),
558
464
        factory_context.threadLocal(), factory_context.api(), true,
559
465
        [](absl::string_view data) { return std::make_shared<std::string>(data); },
560
464
        vhost_->globalRouteConfig().maxDirectResponseBodySizeBytes());
561
464
    SET_AND_RETURN_IF_NOT_OK(provider_or_error.status(), creation_status);
562
462
    direct_response_body_provider_ = std::move(provider_or_error.value());
563
462
  }
564

            
565
12792
  if (route.direct_response().has_body_format()) {
566
5
    Server::GenericFactoryContextImpl generic_context(factory_context,
567
5
                                                      factory_context.messageValidationVisitor());
568
5
    auto formatter_or_error = Formatter::SubstitutionFormatStringUtils::fromProtoConfig(
569
5
        route.direct_response().body_format(), generic_context);
570
5
    SET_AND_RETURN_IF_NOT_OK(formatter_or_error.status(), creation_status);
571
5
    direct_response_body_formatter_ = std::move(formatter_or_error.value());
572
    // Capture the content_type from body_format, using the same defaulting logic as
573
    // local_reply.cc BodyFormatter: explicit content_type > JSON format default > empty
574
    // (text/plain).
575
5
    const auto& body_format = route.direct_response().body_format();
576
5
    if (!body_format.content_type().empty()) {
577
1
      direct_response_content_type_ = body_format.content_type();
578
4
    } else if (body_format.format_case() ==
579
4
               envoy::config::core::v3::SubstitutionFormatString::FormatCase::kJsonFormat) {
580
1
      direct_response_content_type_ = Http::Headers::get().ContentTypeValues.Json;
581
1
    }
582
    // else: leave empty; sendLocalReply will use its default "text/plain"
583
5
  }
584

            
585
12792
  if (!route.request_headers_to_add().empty() || !route.request_headers_to_remove().empty()) {
586
341
    auto parser_or_error =
587
341
        HeaderParser::configure(route.request_headers_to_add(), route.request_headers_to_remove());
588
341
    SET_AND_RETURN_IF_NOT_OK(parser_or_error.status(), creation_status);
589
341
    request_headers_parser_ = std::move(parser_or_error.value());
590
341
  }
591
12792
  if (!route.response_headers_to_add().empty() || !route.response_headers_to_remove().empty()) {
592
410
    auto parser_or_error = HeaderParser::configure(route.response_headers_to_add(),
593
410
                                                   route.response_headers_to_remove());
594
410
    SET_AND_RETURN_IF_NOT_OK(parser_or_error.status(), creation_status);
595
410
    response_headers_parser_ = std::move(parser_or_error.value());
596
410
  }
597
12792
  if (route.has_metadata()) {
598
296
    metadata_ = std::make_unique<RouteMetadataPack>(route.metadata());
599
296
  }
600
12792
  if (route.route().has_metadata_match()) {
601
63
    const auto filter_it = route.route().metadata_match().filter_metadata().find(
602
63
        Envoy::Config::MetadataFilters::get().ENVOY_LB);
603
63
    if (filter_it != route.route().metadata_match().filter_metadata().end()) {
604
63
      metadata_match_criteria_ = std::make_unique<MetadataMatchCriteriaImpl>(filter_it->second);
605
63
    }
606
63
  }
607

            
608
12792
  shadow_policies_.reserve(route.route().request_mirror_policies().size());
609
12797
  for (const auto& mirror_policy_config : route.route().request_mirror_policies()) {
610
79
    auto policy_or_error = ShadowPolicyImpl::create(mirror_policy_config, factory_context);
611
79
    SET_AND_RETURN_IF_NOT_OK(policy_or_error.status(), creation_status);
612
77
    shadow_policies_.push_back(std::move(policy_or_error.value()));
613
77
  }
614

            
615
  // Inherit policies from the virtual host, which might be from the route config.
616
12790
  if (shadow_policies_.empty()) {
617
12718
    shadow_policies_ = vhost->shadowPolicies();
618
12718
  }
619

            
620
12790
  if (route.route().has_weighted_clusters()) {
621
79
    cluster_specifier_plugin_ = std::make_shared<WeightedClusterSpecifierPlugin>(
622
79
        route.route().weighted_clusters(), metadata_match_criteria_.get(), route_name_,
623
79
        factory_context, creation_status);
624
79
    RETURN_ONLY_IF_NOT_OK_REF(creation_status);
625
12715
  } else if (route.route().has_inline_cluster_specifier_plugin()) {
626
7
    auto plugin_or_error = getClusterSpecifierPluginByTheProto(
627
7
        route.route().inline_cluster_specifier_plugin(), validator, factory_context);
628
7
    SET_AND_RETURN_IF_NOT_OK(plugin_or_error.status(), creation_status);
629
6
    cluster_specifier_plugin_ = std::move(plugin_or_error.value());
630
12706
  } else if (route.route().has_cluster_specifier_plugin()) {
631
4
    auto plugin_or_error = vhost_->globalRouteConfig().clusterSpecifierPlugin(
632
4
        route.route().cluster_specifier_plugin());
633
4
    SET_AND_RETURN_IF_NOT_OK(plugin_or_error.status(), creation_status);
634
3
    cluster_specifier_plugin_ = std::move(plugin_or_error.value());
635
12700
  } else if (route.route().has_cluster_header()) {
636
127
    cluster_specifier_plugin_ =
637
127
        std::make_shared<HeaderClusterSpecifierPlugin>(route.route().cluster_header());
638
127
  }
639

            
640
12786
  for (const auto& query_parameter : route.match().query_parameters()) {
641
12
    config_query_parameters_.push_back(
642
12
        std::make_unique<ConfigUtility::QueryParameterMatcher>(query_parameter, factory_context));
643
12
  }
644

            
645
12786
  for (const auto& cookie_matcher : route.match().cookies()) {
646
3
    config_cookies_.push_back(
647
3
        std::make_unique<ConfigUtility::CookieMatcher>(cookie_matcher, factory_context));
648
3
  }
649
12786
  if (!config_cookies_.empty()) {
650
2
    config_cookie_names_.reserve(config_cookies_.size());
651
3
    for (const auto& matcher : config_cookies_) {
652
3
      config_cookie_names_.insert(matcher->name());
653
3
    }
654
2
  }
655

            
656
12786
  if (!route.route().hash_policy().empty()) {
657
64
    hash_policy_ = THROW_OR_RETURN_VALUE(
658
64
        Http::HashPolicyImpl::create(route.route().hash_policy(), factory_context.regexEngine()),
659
64
        std::unique_ptr<Http::HashPolicyImpl>);
660
64
  }
661

            
662
12786
  if (route.match().has_tls_context()) {
663
10
    tls_context_match_criteria_ =
664
10
        std::make_unique<TlsContextMatchCriteriaImpl>(route.match().tls_context());
665
10
  }
666

            
667
12786
  if (!route.route().rate_limits().empty()) {
668
39
    rate_limit_policy_ = std::make_unique<RateLimitPolicyImpl>(route.route().rate_limits(),
669
39
                                                               factory_context, creation_status);
670
39
    if (!creation_status.ok()) {
671
      return;
672
    }
673
39
  }
674

            
675
  // Returns true if include_vh_rate_limits is explicitly set to true otherwise it defaults to false
676
  // which is similar to VhRateLimitOptions::Override and will only use virtual host rate limits if
677
  // the route is empty
678
12786
  include_vh_rate_limits_ =
679
12786
      PROTOBUF_GET_WRAPPED_OR_DEFAULT(route.route(), include_vh_rate_limits, false);
680

            
681
12786
  if (route.route().has_cors()) {
682
46
    cors_policy_ = std::make_unique<CorsPolicyImpl>(route.route().cors(), factory_context);
683
46
  }
684
12786
  for (const auto& upgrade_config : route.route().upgrade_configs()) {
685
271
    const bool enabled = upgrade_config.has_enabled() ? upgrade_config.enabled().value() : true;
686
271
    const bool success =
687
271
        upgrade_map_
688
271
            .emplace(std::make_pair(
689
271
                Envoy::Http::LowerCaseString(upgrade_config.upgrade_type()).get(), enabled))
690
271
            .second;
691
271
    if (!success) {
692
1
      creation_status = absl::InvalidArgumentError(
693
1
          absl::StrCat("Duplicate upgrade ", upgrade_config.upgrade_type()));
694
1
      return;
695
1
    }
696
270
    if (upgrade_config.has_connect_config()) {
697
148
      if (absl::EqualsIgnoreCase(upgrade_config.upgrade_type(),
698
148
                                 Http::Headers::get().MethodValues.Connect) ||
699
148
          absl::EqualsIgnoreCase(upgrade_config.upgrade_type(),
700
147
                                 Http::Headers::get().UpgradeValues.ConnectUdp)) {
701
147
        connect_config_ = std::make_unique<ConnectConfig>(upgrade_config.connect_config());
702
147
      } else {
703
1
        creation_status = absl::InvalidArgumentError(absl::StrCat(
704
1
            "Non-CONNECT upgrade type ", upgrade_config.upgrade_type(), " has ConnectConfig"));
705
1
        return;
706
1
      }
707
148
    }
708
270
  }
709

            
710
12784
  int num_rewrite_polices = 0;
711
12784
  if (path_rewriter_ != nullptr) {
712
21
    ++num_rewrite_polices;
713
21
  }
714

            
715
12784
  if (!prefix_rewrite_.empty()) {
716
36
    ++num_rewrite_polices;
717
36
  }
718

            
719
12784
  if (route.route().has_regex_rewrite()) {
720
5
    ++num_rewrite_polices;
721
5
  }
722

            
723
12784
  if (!route.route().path_rewrite().empty()) {
724
2
    ++num_rewrite_polices;
725
2
  }
726

            
727
12784
  if (num_rewrite_polices > 1) {
728
1
    creation_status = absl::InvalidArgumentError(
729
1
        "Specify only one of prefix_rewrite, regex_rewrite or path_rewrite_policy");
730
1
    return;
731
1
  }
732

            
733
12783
  if (!prefix_rewrite_.empty() && path_matcher_ != nullptr) {
734
1
    creation_status =
735
1
        absl::InvalidArgumentError("Cannot use prefix_rewrite with matcher extension");
736
1
    return;
737
1
  }
738

            
739
12782
  if (route.route().has_regex_rewrite()) {
740
4
    auto rewrite_spec = route.route().regex_rewrite();
741
4
    regex_rewrite_ = THROW_OR_RETURN_VALUE(
742
4
        Regex::Utility::parseRegex(rewrite_spec.pattern(), factory_context.regexEngine()),
743
4
        Regex::CompiledMatcherPtr);
744
4
    regex_rewrite_substitution_ = rewrite_spec.substitution();
745
4
  }
746

            
747
12782
  if (!route.route().path_rewrite().empty()) {
748
2
    auto formatter_or = Envoy::Formatter::FormatterImpl::create(route.route().path_rewrite(), true);
749
2
    if (!formatter_or.ok()) {
750
1
      creation_status = absl::InvalidArgumentError(
751
1
          absl::StrCat("Failed to create path rewrite formatter: ", formatter_or.status()));
752
1
      return;
753
1
    }
754
1
    path_rewrite_formatter_ = std::move(formatter_or.value());
755
1
  }
756

            
757
12781
  if (path_rewriter_ != nullptr) {
758
21
    SET_AND_RETURN_IF_NOT_OK(path_rewriter_->isCompatiblePathMatcher(path_matcher_),
759
21
                             creation_status);
760
20
  }
761

            
762
12780
  if (!route.route().host_rewrite().empty()) {
763
2
    auto formatter_or =
764
2
        Envoy::Formatter::FormatterImpl::create(route.route().host_rewrite(), false);
765
2
    if (!formatter_or.ok()) {
766
1
      creation_status = absl::InvalidArgumentError(
767
1
          absl::StrCat("Failed to create host rewrite formatter: ", formatter_or.status()));
768
1
      return;
769
1
    }
770
1
    host_rewrite_formatter_ = std::move(formatter_or.value());
771
1
  }
772

            
773
12779
  if (redirect_config_ != nullptr && redirect_config_->path_redirect_has_query_ &&
774
12779
      redirect_config_->strip_query_) {
775
1
    ENVOY_LOG(debug,
776
1
              "`strip_query` is set to true, but `path_redirect` contains query string and it will "
777
1
              "not be stripped: {}",
778
1
              redirect_config_->path_redirect_);
779
1
  }
780
12779
  if (!route.stat_prefix().empty()) {
781
13
    route_stats_context_ = std::make_unique<RouteStatsContextImpl>(
782
13
        factory_context.scope(), factory_context.routerContext().routeStatNames(),
783
13
        vhost->statName(), route.stat_prefix());
784
13
  }
785

            
786
12779
  if (route.route().has_early_data_policy()) {
787
5
    auto& factory = Envoy::Config::Utility::getAndCheckFactory<EarlyDataPolicyFactory>(
788
5
        route.route().early_data_policy());
789
5
    auto message = Envoy::Config::Utility::translateToFactoryConfig(
790
5
        route.route().early_data_policy(), validator, factory);
791
5
    early_data_policy_ = factory.createEarlyDataPolicy(*message);
792
12774
  } else {
793
12774
    early_data_policy_ = std::make_unique<DefaultEarlyDataPolicy>(/*allow_safe_request*/ true);
794
12774
  }
795
12779
}
796

            
797
91329
bool RouteEntryImplBase::evaluateRuntimeMatch(const uint64_t random_value) const {
798
91329
  return runtime_ == nullptr
799
91329
             ? true
800
91329
             : loader_.snapshot().featureEnabled(runtime_->fractional_runtime_key_,
801
8
                                                 runtime_->fractional_runtime_default_,
802
8
                                                 random_value);
803
91329
}
804

            
805
absl::string_view
806
90062
RouteEntryImplBase::sanitizePathBeforePathMatching(const absl::string_view path) const {
807
90062
  absl::string_view ret = path;
808
90062
  if (vhost_->globalRouteConfig().ignorePathParametersInPathMatching()) {
809
3
    auto pos = ret.find_first_of(';');
810
3
    if (pos != absl::string_view::npos) {
811
3
      ret.remove_suffix(ret.length() - pos);
812
3
    }
813
3
  }
814
90062
  return ret;
815
90062
}
816

            
817
90643
bool RouteEntryImplBase::evaluateTlsContextMatch(const StreamInfo::StreamInfo& stream_info) const {
818
90643
  bool matches = true;
819

            
820
90643
  if (!tlsContextMatchCriteria()) {
821
90611
    return matches;
822
90611
  }
823

            
824
32
  const TlsContextMatchCriteria& criteria = *tlsContextMatchCriteria();
825

            
826
32
  if (criteria.presented().has_value()) {
827
19
    const bool peer_presented =
828
19
        stream_info.downstreamAddressProvider().sslConnection() &&
829
19
        stream_info.downstreamAddressProvider().sslConnection()->peerCertificatePresented();
830
19
    matches &= criteria.presented().value() == peer_presented;
831
19
  }
832

            
833
32
  if (criteria.validated().has_value()) {
834
13
    const bool peer_validated =
835
13
        stream_info.downstreamAddressProvider().sslConnection() &&
836
13
        stream_info.downstreamAddressProvider().sslConnection()->peerCertificateValidated();
837
13
    matches &= criteria.validated().value() == peer_validated;
838
13
  }
839

            
840
32
  return matches;
841
90643
}
842

            
843
bool RouteEntryImplBase::isRedirect() const {
844
  if (!isDirectResponse()) {
845
    return false;
846
  }
847
  if (redirect_config_ == nullptr) {
848
    return false;
849
  }
850
  return !redirect_config_->host_redirect_.empty() || !redirect_config_->path_redirect_.empty() ||
851
         !redirect_config_->prefix_rewrite_redirect_.empty() ||
852
         redirect_config_->regex_rewrite_redirect_ != nullptr;
853
}
854

            
855
bool RouteEntryImplBase::matchRoute(const Http::RequestHeaderMap& headers,
856
                                    const StreamInfo::StreamInfo& stream_info,
857
91329
                                    uint64_t random_value) const {
858
91329
  bool matches = true;
859

            
860
91329
  matches &= evaluateRuntimeMatch(random_value);
861
91329
  if (!matches) {
862
    // No need to waste further cycles calculating a route match.
863
2
    return false;
864
2
  }
865

            
866
91327
  if (match_grpc_) {
867
5
    matches &= Grpc::Common::isGrpcRequestHeaders(headers);
868
5
    if (!matches) {
869
1
      return false;
870
1
    }
871
5
  }
872

            
873
91326
  matches &= Http::HeaderUtility::matchHeaders(headers, config_headers_);
874
91326
  if (!matches) {
875
651
    return false;
876
651
  }
877
90675
  if (!config_query_parameters_.empty()) {
878
37
    auto query_parameters =
879
37
        Http::Utility::QueryParamsMulti::parseQueryString(headers.getPathValue());
880
37
    matches &= ConfigUtility::matchQueryParams(query_parameters, config_query_parameters_);
881
37
    if (!matches) {
882
28
      return false;
883
28
    }
884
37
  }
885

            
886
90647
  if (!config_cookies_.empty()) {
887
7
    const auto cookies =
888
9
        Http::Utility::parseCookies(headers, [this](absl::string_view key) -> bool {
889
9
          return config_cookie_names_.find(key) != config_cookie_names_.end();
890
9
        });
891
7
    if (!ConfigUtility::matchCookies(cookies, config_cookies_)) {
892
4
      return false;
893
4
    }
894
7
  }
895

            
896
90643
  matches &= evaluateTlsContextMatch(stream_info);
897

            
898
90643
  for (const auto& m : dynamic_metadata_) {
899
49
    if (!matches) {
900
      // No need to check anymore as all dynamic metadata matchers must match for a match to occur.
901
3
      break;
902
3
    }
903
46
    matches &= m.match(stream_info.dynamicMetadata());
904
46
  }
905

            
906
90643
  for (const auto& m : filter_state_) {
907
19
    if (!matches) {
908
      // No need to check anymore as all filter state matchers must match for a match to occur.
909
      break;
910
    }
911
19
    matches &= m.match(stream_info.filterState());
912
19
  }
913

            
914
90643
  return matches;
915
90647
}
916

            
917
132082
const std::string& RouteEntryImplBase::clusterName() const { return cluster_name_; }
918

            
919
void RouteEntryImplBase::finalizePathHeaderForRedirect(Http::RequestHeaderMap& headers,
920
                                                       absl::string_view matched_path,
921
154
                                                       bool keep_old_path) const {
922
154
  if (redirect_config_ == nullptr) {
923
127
    return;
924
127
  }
925
27
  const std::string new_path = rewritePathByPrefixOrRegex(
926
27
      headers.getPathValue(), matched_path, redirect_config_->prefix_rewrite_redirect_,
927
27
      redirect_config_->regex_rewrite_redirect_.get(),
928
27
      redirect_config_->regex_rewrite_redirect_substitution_);
929

            
930
  // Empty new_path means there is no rewrite or the rewrite fails. Then we do nothing.
931
27
  if (!new_path.empty()) {
932
22
    if (keep_old_path) {
933
22
      headers.setEnvoyOriginalPath(headers.getPathValue());
934
22
    }
935
22
    headers.setPath(new_path);
936
22
  }
937
27
}
938

            
939
void RouteEntryImplBase::finalizePathHeader(Http::RequestHeaderMap& headers,
940
                                            const Formatter::Context& context,
941
                                            const StreamInfo::StreamInfo& stream_info,
942
44175
                                            bool keep_old_path) const {
943
44175
  const std::string new_path = currentUrlPathAfterRewrite(headers, context, stream_info);
944

            
945
  // Empty new_path means there is no rewrite or the rewrite fails. Then we do nothing.
946
44175
  if (!new_path.empty()) {
947
60
    if (keep_old_path) {
948
59
      headers.setEnvoyOriginalPath(headers.getPathValue());
949
59
    }
950
60
    headers.setPath(new_path);
951
60
  }
952
44175
}
953

            
954
void RouteEntryImplBase::finalizeHostHeader(Http::RequestHeaderMap& headers,
955
                                            const Formatter::Context& context,
956
                                            const StreamInfo::StreamInfo& stream_info,
957
44175
                                            bool keep_old_host) const {
958
44175
  absl::string_view hostname;
959
44175
  std::string buffer;
960

            
961
44175
  if (!host_rewrite_.empty()) {
962
13
    hostname = host_rewrite_;
963
44162
  } else if (!host_rewrite_header_.get().empty()) {
964
3
    if (const auto header = headers.get(host_rewrite_header_); !header.empty()) {
965
2
      hostname = header[0]->value().getStringView();
966
2
    }
967
44159
  } else if (host_rewrite_path_regex_) {
968
3
    absl::string_view path = headers.getPathValue();
969
3
    buffer = host_rewrite_path_regex_->replaceAll(Http::PathUtil::removeQueryAndFragment(path),
970
3
                                                  host_rewrite_path_regex_substitution_);
971
3
    hostname = buffer;
972
44156
  } else if (host_rewrite_formatter_) {
973
1
    buffer = host_rewrite_formatter_->format(context, stream_info);
974
1
    hostname = buffer;
975
1
  }
976

            
977
44175
  if (hostname.empty()) {
978
44156
    return;
979
44156
  }
980
19
  Http::Utility::updateAuthority(headers, hostname, append_xfh_, keep_old_host);
981
19
}
982

            
983
void RouteEntryImplBase::finalizeRequestHeaders(Http::RequestHeaderMap& headers,
984
                                                const Formatter::Context& context,
985
                                                const StreamInfo::StreamInfo& stream_info,
986
44175
                                                bool keep_original_host_or_path) const {
987
  // Apply header transformations configured via request_headers_to_add first.
988
  // This is important because host/path rewriting may depend on headers added here.
989
44175
  for (const HeaderParser* header_parser : getRequestHeaderParsers(
990
132525
           /*specificity_ascend=*/vhost_->globalRouteConfig().mostSpecificHeaderMutationsWins())) {
991
    // Later evaluated header parser wins.
992
132525
    header_parser->evaluateHeaders(headers, context, stream_info);
993
132525
  }
994

            
995
  // Restore the port if this was a CONNECT request.
996
  // Note this will restore the port for HTTP/2 CONNECT-upgrades as well as as HTTP/1.1 style
997
  // CONNECT requests.
998
44175
  if (Http::HeaderUtility::getPortStart(headers.getHostValue()) == absl::string_view::npos) {
999
43600
    if (auto typed_state = stream_info.filterState().getDataReadOnly<OriginalConnectPort>(
43600
            OriginalConnectPort::key());
43600
        typed_state != nullptr) {
12
      headers.setHost(absl::StrCat(headers.getHostValue(), ":", typed_state->value()));
12
    }
43600
  }
  // Handle host rewrite.
44175
  finalizeHostHeader(headers, context, stream_info, keep_original_host_or_path);
  // Handle path rewrite.
44175
  finalizePathHeader(headers, context, stream_info, keep_original_host_or_path);
44175
}
void RouteEntryImplBase::finalizeResponseHeaders(Http::ResponseHeaderMap& headers,
                                                 const Formatter::Context& context,
41800
                                                 const StreamInfo::StreamInfo& stream_info) const {
41800
  for (const HeaderParser* header_parser : getResponseHeaderParsers(
125400
           /*specificity_ascend=*/vhost_->globalRouteConfig().mostSpecificHeaderMutationsWins())) {
    // Later evaluated header parser wins.
125400
    header_parser->evaluateHeaders(headers, context, stream_info);
125400
  }
41800
}
Http::HeaderTransforms
RouteEntryImplBase::responseHeaderTransforms(const StreamInfo::StreamInfo& stream_info,
10
                                             bool do_formatting) const {
10
  Http::HeaderTransforms transforms;
10
  for (const HeaderParser* header_parser : getResponseHeaderParsers(
30
           /*specificity_ascend=*/vhost_->globalRouteConfig().mostSpecificHeaderMutationsWins())) {
    // Later evaluated header parser wins.
30
    mergeTransforms(transforms, header_parser->getHeaderTransforms(stream_info, do_formatting));
30
  }
10
  return transforms;
10
}
Http::HeaderTransforms
RouteEntryImplBase::requestHeaderTransforms(const StreamInfo::StreamInfo& stream_info,
12
                                            bool do_formatting) const {
12
  Http::HeaderTransforms transforms;
12
  for (const HeaderParser* header_parser : getRequestHeaderParsers(
36
           /*specificity_ascend=*/vhost_->globalRouteConfig().mostSpecificHeaderMutationsWins())) {
    // Later evaluated header parser wins.
36
    mergeTransforms(transforms, header_parser->getHeaderTransforms(stream_info, do_formatting));
36
  }
12
  return transforms;
12
}
std::array<const HeaderParser*, 3>
44187
RouteEntryImplBase::getRequestHeaderParsers(bool specificity_ascend) const {
44187
  return getHeaderParsers(&vhost_->globalRouteConfig().requestHeaderParser(),
44187
                          &vhost_->requestHeaderParser(), &requestHeaderParser(),
44187
                          specificity_ascend);
44187
}
std::array<const HeaderParser*, 3>
41810
RouteEntryImplBase::getResponseHeaderParsers(bool specificity_ascend) const {
41810
  return getHeaderParsers(&vhost_->globalRouteConfig().responseHeaderParser(),
41810
                          &vhost_->responseHeaderParser(), &responseHeaderParser(),
41810
                          specificity_ascend);
41810
}
std::unique_ptr<const RouteEntryImplBase::RuntimeData>
12797
RouteEntryImplBase::loadRuntimeData(const envoy::config::route::v3::RouteMatch& route_match) {
12797
  if (route_match.has_runtime_fraction()) {
9
    auto runtime_data = std::make_unique<RouteEntryImplBase::RuntimeData>();
9
    runtime_data->fractional_runtime_default_ = route_match.runtime_fraction().default_value();
9
    runtime_data->fractional_runtime_key_ = route_match.runtime_fraction().runtime_key();
9
    return runtime_data;
9
  }
12788
  return nullptr;
12797
}
// currentUrlPathAfterRewriteWithMatchedPath does the "standard" path rewriting.
// The "matched_path" argument applies only to the
// prefix rewriting, and describes the portion of the path (excluding query
// parameters) that should be replaced by the rewrite. A "regex_rewrite"
// applies to the entire path (excluding query parameters), regardless of what
// portion was matched.
std::string RouteEntryImplBase::currentUrlPathAfterRewriteWithMatchedPath(
    const Http::RequestHeaderMap& headers, const Formatter::Context& context,
44175
    const StreamInfo::StreamInfo& info, absl::string_view matched_path) const {
44175
  absl::string_view path_with_query = headers.getPathValue();
  // Handle the case where a path formatter is configured.
44175
  if (path_rewrite_formatter_ != nullptr) {
2
    const std::string new_path_only = path_rewrite_formatter_->format(context, info);
    // If formatter produces empty string then return nothing.
2
    if (new_path_only.empty()) {
      return {};
    }
2
    absl::string_view path_only = Http::PathUtil::removeQueryAndFragment(path_with_query);
2
    return generateNewPath(path_with_query, path_only, new_path_only);
2
  }
  // Handle the case where path_rewrite_policy is configured.
44173
  if (path_rewriter_ != nullptr) {
20
    absl::string_view path_only = Http::PathUtil::removeQueryAndFragment(path_with_query);
20
    absl::StatusOr<std::string> new_path_only =
20
        path_rewriter_->rewritePath(path_only, matched_path);
    // If rewrite fails or produces empty string then return nothing.
20
    if (!new_path_only.ok() || new_path_only->empty()) {
      return {};
    }
20
    return generateNewPath(path_with_query, path_only, new_path_only.value());
20
  }
  // Handle the case where prefix_rewrite or regex_rewrite is configured.
44153
  return rewritePathByPrefixOrRegex(path_with_query, matched_path, prefix_rewrite_,
44153
                                    regex_rewrite_.get(), regex_rewrite_substitution_);
44173
}
202
std::string RouteEntryImplBase::newUri(const Http::RequestHeaderMap& headers) const {
202
  ASSERT(isDirectResponse());
202
  return ::Envoy::Http::Utility::newUri(
202
      ::Envoy::makeOptRefFromPtr(
202
          const_cast<const ::Envoy::Http::Utility::RedirectConfig*>(redirect_config_.get())),
202
      headers);
202
}
absl::string_view RouteEntryImplBase::formatBody(const Http::RequestHeaderMap& request_headers,
                                                 const Http::ResponseHeaderMap& response_headers,
                                                 const StreamInfo::StreamInfo& stream_info,
137
                                                 std::string& body_out) const {
137
  absl::string_view direct_body = (direct_response_body_provider_ != nullptr &&
137
                                   direct_response_body_provider_->data() != nullptr)
137
                                      ? *direct_response_body_provider_->data()
137
                                      : EMPTY_STRING;
137
  if (direct_response_body_formatter_ == nullptr) {
135
    return direct_body;
135
  }
2
  body_out = direct_response_body_formatter_->format(
2
      {&request_headers, &response_headers, nullptr, direct_body}, stream_info);
2
  return body_out;
137
}
std::multimap<std::string, std::string>
12795
RouteEntryImplBase::parseOpaqueConfig(const envoy::config::route::v3::Route& route) {
12795
  std::multimap<std::string, std::string> ret;
12795
  if (route.has_metadata()) {
296
    auto filter_metadata = route.metadata().filter_metadata().find("envoy.filters.http.router");
296
    if (filter_metadata == route.metadata().filter_metadata().end()) {
295
      return ret;
295
    }
2
    for (const auto& it : filter_metadata->second.fields()) {
2
      if (it.second.kind_case() == Protobuf::Value::kStringValue) {
2
        ret.emplace(it.first, it.second.string_value());
2
      }
2
    }
1
  }
12500
  return ret;
12795
}
std::unique_ptr<HedgePolicyImpl> RouteEntryImplBase::buildHedgePolicy(
    HedgePolicyConstOptRef vhost_hedge_policy,
12797
    const envoy::config::route::v3::RouteAction& route_config) const {
  // Route specific policy wins, if available.
12797
  if (route_config.has_hedge_policy()) {
4
    return std::make_unique<HedgePolicyImpl>(route_config.hedge_policy());
4
  }
  // If not, we fall back to the virtual host policy if there is one.
12793
  if (vhost_hedge_policy.has_value()) {
1
    return std::make_unique<HedgePolicyImpl>(*vhost_hedge_policy);
1
  }
  // Otherwise, an empty policy will do.
12792
  return nullptr;
12793
}
absl::StatusOr<RetryPolicyConstSharedPtr> RouteEntryImplBase::buildRetryPolicy(
    const RetryPolicyConstSharedPtr& vhost_retry_policy,
    const envoy::config::route::v3::RouteAction& route_config,
    ProtobufMessage::ValidationVisitor& validation_visitor,
12790
    Server::Configuration::CommonFactoryContext& factory_context) const {
  // Route specific policy wins, if available.
12790
  if (route_config.has_retry_policy()) {
136
    return RetryPolicyImpl::create(route_config.retry_policy(), validation_visitor,
136
                                   factory_context);
136
  }
  // If not, we fallback to the virtual host policy if there is one. Note the
  // virtual host policy may be nullptr.
12654
  return vhost_retry_policy;
12790
}
absl::StatusOr<std::unique_ptr<InternalRedirectPolicyImpl>>
RouteEntryImplBase::buildInternalRedirectPolicy(
    const envoy::config::route::v3::RouteAction& route_config,
12797
    ProtobufMessage::ValidationVisitor& validator, absl::string_view current_route_name) const {
12797
  if (route_config.has_internal_redirect_policy()) {
418
    return InternalRedirectPolicyImpl::create(route_config.internal_redirect_policy(), validator,
418
                                              current_route_name);
418
  }
12379
  envoy::config::route::v3::InternalRedirectPolicy policy_config;
12379
  switch (route_config.internal_redirect_action()) {
  case envoy::config::route::v3::RouteAction::HANDLE_INTERNAL_REDIRECT:
    break;
12379
  case envoy::config::route::v3::RouteAction::PASS_THROUGH_INTERNAL_REDIRECT:
12379
    FALLTHRU;
12379
  default:
12379
    return nullptr;
12379
  }
  if (route_config.has_max_internal_redirects()) {
    *policy_config.mutable_max_internal_redirects() = route_config.max_internal_redirects();
  }
  return InternalRedirectPolicyImpl::create(policy_config, validator, current_route_name);
12379
}
RouteEntryImplBase::OptionalTimeouts RouteEntryImplBase::buildOptionalTimeouts(
12797
    const envoy::config::route::v3::RouteAction& route) const {
  // Calculate how many values are actually set, to initialize `OptionalTimeouts` packed_struct,
  // avoiding memory re-allocation on each set() call.
12797
  int num_timeouts_set = route.has_idle_timeout() ? 1 : 0;
12797
  num_timeouts_set += route.has_flush_timeout() ? 1 : 0;
12797
  num_timeouts_set += route.has_max_grpc_timeout() ? 1 : 0;
12797
  num_timeouts_set += route.has_grpc_timeout_offset() ? 1 : 0;
12797
  if (route.has_max_stream_duration()) {
7
    num_timeouts_set += route.max_stream_duration().has_max_stream_duration() ? 1 : 0;
7
    num_timeouts_set += route.max_stream_duration().has_grpc_timeout_header_max() ? 1 : 0;
7
    num_timeouts_set += route.max_stream_duration().has_grpc_timeout_header_offset() ? 1 : 0;
7
  }
12797
  OptionalTimeouts timeouts(num_timeouts_set);
12797
  if (route.has_idle_timeout()) {
167
    timeouts.set<OptionalTimeoutNames::IdleTimeout>(
167
        std::chrono::milliseconds(PROTOBUF_GET_MS_REQUIRED(route, idle_timeout)));
167
  }
12797
  if (route.has_flush_timeout()) {
    timeouts.set<OptionalTimeoutNames::FlushTimeout>(
        std::chrono::milliseconds(PROTOBUF_GET_MS_REQUIRED(route, flush_timeout)));
  }
12797
  if (route.has_max_grpc_timeout()) {
5
    timeouts.set<OptionalTimeoutNames::MaxGrpcTimeout>(
5
        std::chrono::milliseconds(PROTOBUF_GET_MS_REQUIRED(route, max_grpc_timeout)));
5
  }
12797
  if (route.has_grpc_timeout_offset()) {
3
    timeouts.set<OptionalTimeoutNames::GrpcTimeoutOffset>(
3
        std::chrono::milliseconds(PROTOBUF_GET_MS_REQUIRED(route, grpc_timeout_offset)));
3
  }
12797
  if (route.has_max_stream_duration()) {
7
    if (route.max_stream_duration().has_max_stream_duration()) {
1
      timeouts.set<OptionalTimeoutNames::MaxStreamDuration>(std::chrono::milliseconds(
1
          PROTOBUF_GET_MS_REQUIRED(route.max_stream_duration(), max_stream_duration)));
1
    }
7
    if (route.max_stream_duration().has_grpc_timeout_header_max()) {
7
      timeouts.set<OptionalTimeoutNames::GrpcTimeoutHeaderMax>(std::chrono::milliseconds(
7
          PROTOBUF_GET_MS_REQUIRED(route.max_stream_duration(), grpc_timeout_header_max)));
7
    }
7
    if (route.max_stream_duration().has_grpc_timeout_header_offset()) {
1
      timeouts.set<OptionalTimeoutNames::GrpcTimeoutHeaderOffset>(std::chrono::milliseconds(
1
          PROTOBUF_GET_MS_REQUIRED(route.max_stream_duration(), grpc_timeout_header_offset)));
1
    }
7
  }
12797
  return timeouts;
12797
}
absl::StatusOr<PathRewriterSharedPtr>
RouteEntryImplBase::buildPathRewriter(const envoy::config::route::v3::Route& route,
12797
                                      ProtobufMessage::ValidationVisitor& validator) const {
12797
  if (!route.route().has_path_rewrite_policy()) {
12776
    return nullptr;
12776
  }
21
  auto& factory = Envoy::Config::Utility::getAndCheckFactory<PathRewriterFactory>(
21
      route.route().path_rewrite_policy());
21
  ProtobufTypes::MessagePtr config = Envoy::Config::Utility::translateAnyToFactoryConfig(
21
      route.route().path_rewrite_policy().typed_config(), validator, factory);
21
  absl::StatusOr<PathRewriterSharedPtr> rewriter = factory.createPathRewriter(*config);
21
  RETURN_IF_NOT_OK_REF(rewriter.status());
21
  return rewriter.value();
21
}
absl::StatusOr<PathMatcherSharedPtr>
RouteEntryImplBase::buildPathMatcher(const envoy::config::route::v3::Route& route,
12800
                                     ProtobufMessage::ValidationVisitor& validator) const {
12800
  if (!route.match().has_path_match_policy()) {
12771
    return nullptr;
12771
  }
29
  auto& factory = Envoy::Config::Utility::getAndCheckFactory<PathMatcherFactory>(
29
      route.match().path_match_policy());
29
  ProtobufTypes::MessagePtr config = Envoy::Config::Utility::translateAnyToFactoryConfig(
29
      route.match().path_match_policy().typed_config(), validator, factory);
29
  absl::StatusOr<PathMatcherSharedPtr> matcher = factory.createPathMatcher(*config);
29
  RETURN_IF_NOT_OK_REF(matcher.status());
26
  return matcher.value();
29
}
12795
DecoratorConstPtr RouteEntryImplBase::parseDecorator(const envoy::config::route::v3::Route& route) {
12795
  DecoratorConstPtr ret;
12795
  if (route.has_decorator()) {
2
    ret = DecoratorConstPtr(new DecoratorImpl(route.decorator()));
2
  }
12795
  return ret;
12795
}
RouteTracingConstPtr
12795
RouteEntryImplBase::parseRouteTracing(const envoy::config::route::v3::Route& route) {
12795
  RouteTracingConstPtr ret;
12795
  if (route.has_tracing()) {
3
    ret = RouteTracingConstPtr(new RouteTracingImpl(route.tracing()));
3
  }
12795
  return ret;
12795
}
44456
const DirectResponseEntry* RouteEntryImplBase::directResponseEntry() const {
  // A route for a request can exclusively be a route entry, a direct response entry,
  // or a redirect entry.
44456
  if (isDirectResponse()) {
246
    return this;
44311
  } else {
44210
    return nullptr;
44210
  }
44456
}
538343
const RouteEntry* RouteEntryImplBase::routeEntry() const {
  // A route for a request can exclusively be a route entry, a direct response entry,
  // or a redirect entry.
538343
  if (isDirectResponse()) {
1254
    return nullptr;
537394
  } else {
537089
    return this;
537089
  }
538343
}
RouteConstSharedPtr RouteEntryImplBase::clusterEntry(const Http::RequestHeaderMap& headers,
                                                     const StreamInfo::StreamInfo& stream_info,
88594
                                                     uint64_t random_value) const {
88594
  if (cluster_specifier_plugin_ != nullptr) {
391
    return cluster_specifier_plugin_->route(shared_from_this(), headers, stream_info, random_value);
391
  }
88203
  return shared_from_this();
88594
}
11477
absl::Status RouteEntryImplBase::validateClusters(const Upstream::ClusterManager& cm) const {
11477
  if (!cluster_name_.empty()) {
10722
    return !cm.hasCluster(cluster_name_) ? absl::InvalidArgumentError(fmt::format(
3
                                               "route: unknown cluster '{}'", cluster_name_))
10722
                                         : absl::OkStatus();
10722
  }
755
  if (cluster_specifier_plugin_ != nullptr) {
175
    return cluster_specifier_plugin_->validateClusters(cm);
175
  }
580
  return absl::OkStatus();
755
}
98985
absl::optional<bool> RouteEntryImplBase::filterDisabled(absl::string_view config_name) const {
98985
  absl::optional<bool> result = per_filter_configs_->disabled(config_name);
98985
  if (result.has_value()) {
291
    return result.value();
291
  }
98694
  return vhost_->filterDisabled(config_name);
98985
}
RouteSpecificFilterConfigs
1601
RouteEntryImplBase::perFilterConfigs(absl::string_view filter_name) const {
1601
  auto result = vhost_->perFilterConfigs(filter_name);
1601
  const auto* maybe_route_config = per_filter_configs_->get(filter_name);
1601
  if (maybe_route_config != nullptr) {
79
    result.push_back(maybe_route_config);
79
  }
1601
  return result;
1601
}
416
const envoy::config::core::v3::Metadata& RouteEntryImplBase::metadata() const {
416
  return metadata_ != nullptr ? metadata_->proto_metadata_
416
                              : DefaultRouteMetadataPack::get().proto_metadata_;
416
}
3
const Envoy::Config::TypedMetadata& RouteEntryImplBase::typedMetadata() const {
3
  return metadata_ != nullptr ? metadata_->typed_metadata_
3
                              : DefaultRouteMetadataPack::get().typed_metadata_;
3
}
UriTemplateMatcherRouteEntryImpl::UriTemplateMatcherRouteEntryImpl(
    const CommonVirtualHostSharedPtr& vhost, const envoy::config::route::v3::Route& route,
    Server::Configuration::ServerFactoryContext& factory_context,
    ProtobufMessage::ValidationVisitor& validator, absl::Status& creation_status)
29
    : RouteEntryImplBase(vhost, route, factory_context, validator, creation_status),
29
      uri_template_(path_matcher_->uriTemplate()) {};
void UriTemplateMatcherRouteEntryImpl::rewritePathHeader(Http::RequestHeaderMap& headers,
                                                         bool insert_envoy_original_path) const {
  finalizePathHeaderForRedirect(headers, path_matcher_->uriTemplate(), insert_envoy_original_path);
}
std::string UriTemplateMatcherRouteEntryImpl::currentUrlPathAfterRewrite(
    const Http::RequestHeaderMap& headers, const Formatter::Context& context,
20
    const StreamInfo::StreamInfo& stream_info) const {
20
  return currentUrlPathAfterRewriteWithMatchedPath(headers, context, stream_info,
20
                                                   path_matcher_->uriTemplate());
20
}
RouteConstSharedPtr
UriTemplateMatcherRouteEntryImpl::matches(const Http::RequestHeaderMap& headers,
                                          const StreamInfo::StreamInfo& stream_info,
38
                                          uint64_t random_value) const {
38
  if (RouteEntryImplBase::matchRoute(headers, stream_info, random_value) &&
38
      path_matcher_->match(headers.getPathValue())) {
31
    return clusterEntry(headers, stream_info, random_value);
31
  }
7
  return nullptr;
38
}
PrefixRouteEntryImpl::PrefixRouteEntryImpl(
    const CommonVirtualHostSharedPtr& vhost, const envoy::config::route::v3::Route& route,
    Server::Configuration::ServerFactoryContext& factory_context,
    ProtobufMessage::ValidationVisitor& validator, absl::Status& creation_status)
12303
    : RouteEntryImplBase(vhost, route, factory_context, validator, creation_status),
12303
      path_matcher_(Matchers::PathMatcher::createPrefix(route.match().prefix(), !case_sensitive(),
12303
                                                        factory_context)) {
  // The createPrefix function never returns nullptr.
12303
  ASSERT(path_matcher_ != nullptr);
12303
}
void PrefixRouteEntryImpl::rewritePathHeader(Http::RequestHeaderMap& headers,
147
                                             bool insert_envoy_original_path) const {
147
  finalizePathHeaderForRedirect(headers, matcher(), insert_envoy_original_path);
147
}
std::string
PrefixRouteEntryImpl::currentUrlPathAfterRewrite(const Http::RequestHeaderMap& headers,
                                                 const Formatter::Context& context,
43577
                                                 const StreamInfo::StreamInfo& stream_info) const {
43577
  return currentUrlPathAfterRewriteWithMatchedPath(headers, context, stream_info, matcher());
43577
}
RouteConstSharedPtr PrefixRouteEntryImpl::matches(const Http::RequestHeaderMap& headers,
                                                  const StreamInfo::StreamInfo& stream_info,
89924
                                                  uint64_t random_value) const {
89924
  if (RouteEntryImplBase::matchRoute(headers, stream_info, random_value) &&
89924
      path_matcher_->match(sanitizePathBeforePathMatching(headers.getPathValue()))) {
87860
    return clusterEntry(headers, stream_info, random_value);
87860
  }
2064
  return nullptr;
89924
}
PathRouteEntryImpl::PathRouteEntryImpl(const CommonVirtualHostSharedPtr& vhost,
                                       const envoy::config::route::v3::Route& route,
                                       Server::Configuration::ServerFactoryContext& factory_context,
                                       ProtobufMessage::ValidationVisitor& validator,
                                       absl::Status& creation_status)
178
    : RouteEntryImplBase(vhost, route, factory_context, validator, creation_status),
178
      path_matcher_(Matchers::PathMatcher::createExact(route.match().path(), !case_sensitive(),
178
                                                       factory_context)) {
  // The createExact function never returns nullptr.
178
  ASSERT(path_matcher_ != nullptr);
178
}
void PathRouteEntryImpl::rewritePathHeader(Http::RequestHeaderMap& headers,
1
                                           bool insert_envoy_original_path) const {
1
  finalizePathHeaderForRedirect(headers, matcher(), insert_envoy_original_path);
1
}
std::string
PathRouteEntryImpl::currentUrlPathAfterRewrite(const Http::RequestHeaderMap& headers,
                                               const Formatter::Context& context,
97
                                               const StreamInfo::StreamInfo& stream_info) const {
97
  return currentUrlPathAfterRewriteWithMatchedPath(headers, context, stream_info, matcher());
97
}
RouteConstSharedPtr PathRouteEntryImpl::matches(const Http::RequestHeaderMap& headers,
                                                const StreamInfo::StreamInfo& stream_info,
757
                                                uint64_t random_value) const {
757
  if (RouteEntryImplBase::matchRoute(headers, stream_info, random_value) &&
757
      path_matcher_->match(sanitizePathBeforePathMatching(headers.getPathValue()))) {
165
    return clusterEntry(headers, stream_info, random_value);
165
  }
592
  return nullptr;
757
}
RegexRouteEntryImpl::RegexRouteEntryImpl(
    const CommonVirtualHostSharedPtr& vhost, const envoy::config::route::v3::Route& route,
    Server::Configuration::ServerFactoryContext& factory_context,
    ProtobufMessage::ValidationVisitor& validator, absl::Status& creation_status)
32
    : RouteEntryImplBase(vhost, route, factory_context, validator, creation_status),
      path_matcher_(
32
          Matchers::PathMatcher::createSafeRegex(route.match().safe_regex(), factory_context)) {
32
  ASSERT(route.match().path_specifier_case() ==
32
         envoy::config::route::v3::RouteMatch::PathSpecifierCase::kSafeRegex);
  // The createSafeRegex function never returns nullptr.
32
  ASSERT(path_matcher_ != nullptr);
32
}
void RegexRouteEntryImpl::rewritePathHeader(Http::RequestHeaderMap& headers,
5
                                            bool insert_envoy_original_path) const {
5
  absl::string_view path = Http::PathUtil::removeQueryAndFragment(headers.getPathValue());
  // TODO(yuval-k): This ASSERT can happen if the path was changed by a filter without clearing
  // the route cache. We should consider if ASSERT-ing is the desired behavior in this case.
5
  ASSERT(path_matcher_->match(sanitizePathBeforePathMatching(path)));
5
  finalizePathHeaderForRedirect(headers, path, insert_envoy_original_path);
5
}
std::string
RegexRouteEntryImpl::currentUrlPathAfterRewrite(const Http::RequestHeaderMap& headers,
                                                const Formatter::Context& context,
6
                                                const StreamInfo::StreamInfo& stream_info) const {
6
  const absl::string_view path = Http::PathUtil::removeQueryAndFragment(headers.getPathValue());
6
  return currentUrlPathAfterRewriteWithMatchedPath(headers, context, stream_info, path);
6
}
RouteConstSharedPtr RegexRouteEntryImpl::matches(const Http::RequestHeaderMap& headers,
                                                 const StreamInfo::StreamInfo& stream_info,
106
                                                 uint64_t random_value) const {
106
  if (RouteEntryImplBase::matchRoute(headers, stream_info, random_value)) {
106
    if (path_matcher_->match(sanitizePathBeforePathMatching(headers.getPathValue()))) {
41
      return clusterEntry(headers, stream_info, random_value);
41
    }
106
  }
65
  return nullptr;
106
}
ConnectRouteEntryImpl::ConnectRouteEntryImpl(
    const CommonVirtualHostSharedPtr& vhost, const envoy::config::route::v3::Route& route,
    Server::Configuration::ServerFactoryContext& factory_context,
    ProtobufMessage::ValidationVisitor& validator, absl::Status& creation_status)
251
    : RouteEntryImplBase(vhost, route, factory_context, validator, creation_status) {}
void ConnectRouteEntryImpl::rewritePathHeader(Http::RequestHeaderMap& headers,
1
                                              bool insert_envoy_original_path) const {
1
  const absl::string_view path = Http::PathUtil::removeQueryAndFragment(headers.getPathValue());
1
  finalizePathHeaderForRedirect(headers, path, insert_envoy_original_path);
1
}
std::string
ConnectRouteEntryImpl::currentUrlPathAfterRewrite(const Http::RequestHeaderMap& headers,
                                                  const Formatter::Context& context,
473
                                                  const StreamInfo::StreamInfo& stream_info) const {
473
  const absl::string_view path = Http::PathUtil::removeQueryAndFragment(headers.getPathValue());
473
  return currentUrlPathAfterRewriteWithMatchedPath(headers, context, stream_info, path);
473
}
RouteConstSharedPtr ConnectRouteEntryImpl::matches(const Http::RequestHeaderMap& headers,
                                                   const StreamInfo::StreamInfo& stream_info,
504
                                                   uint64_t random_value) const {
504
  if ((Http::HeaderUtility::isConnect(headers) ||
504
       Http::HeaderUtility::isConnectUdpRequest(headers)) &&
504
      RouteEntryImplBase::matchRoute(headers, stream_info, random_value)) {
481
    return clusterEntry(headers, stream_info, random_value);
481
  }
23
  return nullptr;
504
}
PathSeparatedPrefixRouteEntryImpl::PathSeparatedPrefixRouteEntryImpl(
    const CommonVirtualHostSharedPtr& vhost, const envoy::config::route::v3::Route& route,
    Server::Configuration::ServerFactoryContext& factory_context,
    ProtobufMessage::ValidationVisitor& validator, absl::Status& creation_status)
7
    : RouteEntryImplBase(vhost, route, factory_context, validator, creation_status),
7
      path_matcher_(Matchers::PathMatcher::createPrefix(route.match().path_separated_prefix(),
7
                                                        !case_sensitive(), factory_context)) {
  // The createPrefix function never returns nullptr.
7
  ASSERT(path_matcher_ != nullptr);
7
}
void PathSeparatedPrefixRouteEntryImpl::rewritePathHeader(Http::RequestHeaderMap& headers,
                                                          bool insert_envoy_original_path) const {
  finalizePathHeaderForRedirect(headers, matcher(), insert_envoy_original_path);
}
std::string PathSeparatedPrefixRouteEntryImpl::currentUrlPathAfterRewrite(
    const Http::RequestHeaderMap& headers, const Formatter::Context& context,
2
    const StreamInfo::StreamInfo& stream_info) const {
2
  return currentUrlPathAfterRewriteWithMatchedPath(headers, context, stream_info, matcher());
2
}
RouteConstSharedPtr
PathSeparatedPrefixRouteEntryImpl::matches(const Http::RequestHeaderMap& headers,
                                           const StreamInfo::StreamInfo& stream_info,
23
                                           uint64_t random_value) const {
23
  if (!RouteEntryImplBase::matchRoute(headers, stream_info, random_value)) {
2
    return nullptr;
2
  }
21
  absl::string_view sanitized_path = sanitizePathBeforePathMatching(
21
      Http::PathUtil::removeQueryAndFragment(headers.getPathValue()));
21
  const size_t sanitized_size = sanitized_path.size();
21
  const size_t matcher_size = matcher().size();
21
  if (sanitized_size >= matcher_size && path_matcher_->match(sanitized_path) &&
21
      (sanitized_size == matcher_size || sanitized_path[matcher_size] == '/')) {
16
    return clusterEntry(headers, stream_info, random_value);
16
  }
5
  return nullptr;
21
}
CommonVirtualHostImpl::CommonVirtualHostImpl(
    const envoy::config::route::v3::VirtualHost& virtual_host,
    const CommonConfigSharedPtr& global_route_config,
    Server::Configuration::ServerFactoryContext& factory_context, Stats::Scope& scope,
    ProtobufMessage::ValidationVisitor& validator, absl::Status& creation_status)
11615
    : name_(virtual_host.name()),
11615
      stat_name_storage_(virtual_host.name(), factory_context.scope().symbolTable()),
11615
      global_route_config_(global_route_config),
      per_filter_configs_(
11615
          THROW_OR_RETURN_VALUE(PerFilterConfigs::create(virtual_host.typed_per_filter_config(),
                                                         factory_context, validator),
                                std::unique_ptr<PerFilterConfigs>)),
      per_request_buffer_limit_(
11615
          PROTOBUF_GET_OPTIONAL_WRAPPED(virtual_host, per_request_buffer_limit_bytes)),
      request_body_buffer_limit_(
11615
          PROTOBUF_GET_OPTIONAL_WRAPPED(virtual_host, request_body_buffer_limit)),
11615
      include_attempt_count_in_request_(virtual_host.include_request_attempt_count()),
11615
      include_attempt_count_in_response_(virtual_host.include_attempt_count_in_response()),
11615
      include_is_timeout_retry_header_(virtual_host.include_is_timeout_retry_header()) {
11615
  if (!virtual_host.request_headers_to_add().empty() ||
11615
      !virtual_host.request_headers_to_remove().empty()) {
123
    request_headers_parser_ =
123
        THROW_OR_RETURN_VALUE(HeaderParser::configure(virtual_host.request_headers_to_add(),
123
                                                      virtual_host.request_headers_to_remove()),
123
                              Router::HeaderParserPtr);
123
  }
11615
  if (!virtual_host.response_headers_to_add().empty() ||
11615
      !virtual_host.response_headers_to_remove().empty()) {
277
    response_headers_parser_ =
277
        THROW_OR_RETURN_VALUE(HeaderParser::configure(virtual_host.response_headers_to_add(),
277
                                                      virtual_host.response_headers_to_remove()),
277
                              Router::HeaderParserPtr);
277
  }
  // Retry and Hedge policies must be set before routes, since they may use them.
11615
  if (virtual_host.has_retry_policy()) {
2
    auto policy_or_error =
2
        RetryPolicyImpl::create(virtual_host.retry_policy(), validator, factory_context);
2
    SET_AND_RETURN_IF_NOT_OK(policy_or_error.status(), creation_status);
2
    retry_policy_ = std::move(policy_or_error.value());
2
  }
11615
  if (virtual_host.has_hedge_policy()) {
1
    hedge_policy_ = std::make_unique<envoy::config::route::v3::HedgePolicy>();
1
    hedge_policy_->CopyFrom(virtual_host.hedge_policy());
1
  }
11615
  if (!virtual_host.rate_limits().empty()) {
7
    rate_limit_policy_ = std::make_unique<RateLimitPolicyImpl>(virtual_host.rate_limits(),
7
                                                               factory_context, creation_status);
7
    SET_AND_RETURN_IF_NOT_OK(creation_status, creation_status);
7
  }
11615
  shadow_policies_.reserve(virtual_host.request_mirror_policies().size());
11615
  for (const auto& mirror_policy_config : virtual_host.request_mirror_policies()) {
2
    auto policy_or_error = ShadowPolicyImpl::create(mirror_policy_config, factory_context);
2
    SET_AND_RETURN_IF_NOT_OK(policy_or_error.status(), creation_status);
2
    shadow_policies_.push_back(std::move(policy_or_error.value()));
2
  }
  // Inherit policies from the global config.
11615
  if (shadow_policies_.empty()) {
11589
    shadow_policies_ = global_route_config_->shadowPolicies();
11589
  }
11615
  if (virtual_host.has_matcher() && !virtual_host.routes().empty()) {
1
    creation_status =
1
        absl::InvalidArgumentError("cannot set both matcher and routes on virtual host");
1
    return;
1
  }
11614
  if (!virtual_host.virtual_clusters().empty()) {
19
    vcluster_scope_ = Stats::Utility::scopeFromStatNames(
19
        scope, {stat_name_storage_.statName(),
19
                factory_context.routerContext().virtualClusterStatNames().vcluster_});
19
    virtual_cluster_catch_all_ = std::make_unique<CatchAllVirtualCluster>(
19
        *vcluster_scope_, factory_context.routerContext().virtualClusterStatNames());
19
    virtual_clusters_.reserve(virtual_host.virtual_clusters().size());
42
    for (const auto& virtual_cluster : virtual_host.virtual_clusters()) {
42
      if (virtual_cluster.headers().empty()) {
1
        creation_status = absl::InvalidArgumentError("virtual clusters must define 'headers'");
1
        return;
1
      }
41
      virtual_clusters_.emplace_back(virtual_cluster, *vcluster_scope_, factory_context,
41
                                     factory_context.routerContext().virtualClusterStatNames());
41
    }
19
  }
11613
  if (virtual_host.has_cors()) {
10
    cors_policy_ = std::make_unique<CorsPolicyImpl>(virtual_host.cors(), factory_context);
10
  }
11613
  if (virtual_host.has_metadata()) {
256
    metadata_ = std::make_unique<RouteMetadataPack>(virtual_host.metadata());
256
  }
11613
}
CommonVirtualHostImpl::VirtualClusterEntry::VirtualClusterEntry(
    const envoy::config::route::v3::VirtualCluster& virtual_cluster, Stats::Scope& scope,
    Server::Configuration::CommonFactoryContext& context, const VirtualClusterStatNames& stat_names)
41
    : StatNameProvider(virtual_cluster.name(), scope.symbolTable()),
41
      VirtualClusterBase(virtual_cluster.name(), stat_name_storage_.statName(),
41
                         scope.scopeFromStatName(stat_name_storage_.statName()), stat_names) {
41
  ASSERT(!virtual_cluster.headers().empty());
41
  headers_ = Http::HeaderUtility::buildHeaderDataVector(virtual_cluster.headers(), context);
41
}
4
const CommonConfig& CommonVirtualHostImpl::routeConfig() const { return *global_route_config_; }
98694
absl::optional<bool> CommonVirtualHostImpl::filterDisabled(absl::string_view config_name) const {
98694
  absl::optional<bool> result = per_filter_configs_->disabled(config_name);
98694
  if (result.has_value()) {
189
    return result.value();
189
  }
98505
  return global_route_config_->filterDisabled(config_name);
98694
}
const RouteSpecificFilterConfig*
4906
CommonVirtualHostImpl::mostSpecificPerFilterConfig(absl::string_view name) const {
4906
  auto* per_filter_config = per_filter_configs_->get(name);
4906
  return per_filter_config != nullptr ? per_filter_config
4906
                                      : global_route_config_->perFilterConfig(name);
4906
}
RouteSpecificFilterConfigs
1601
CommonVirtualHostImpl::perFilterConfigs(absl::string_view filter_name) const {
1601
  RouteSpecificFilterConfigs result;
  // Parent first.
1601
  if (auto* maybe_rc_config = global_route_config_->perFilterConfig(filter_name);
1601
      maybe_rc_config != nullptr) {
13
    result.push_back(maybe_rc_config);
13
  }
1601
  if (auto* maybe_vhost_config = per_filter_configs_->get(filter_name);
1601
      maybe_vhost_config != nullptr) {
48
    result.push_back(maybe_vhost_config);
48
  }
1601
  return result;
1601
}
43
const envoy::config::core::v3::Metadata& CommonVirtualHostImpl::metadata() const {
43
  return metadata_ != nullptr ? metadata_->proto_metadata_
43
                              : DefaultRouteMetadataPack::get().proto_metadata_;
43
}
1
const Envoy::Config::TypedMetadata& CommonVirtualHostImpl::typedMetadata() const {
1
  return metadata_ != nullptr ? metadata_->typed_metadata_
1
                              : DefaultRouteMetadataPack::get().typed_metadata_;
1
}
absl::StatusOr<std::shared_ptr<CommonVirtualHostImpl>>
CommonVirtualHostImpl::create(const envoy::config::route::v3::VirtualHost& virtual_host,
                              const CommonConfigSharedPtr& global_route_config,
                              Server::Configuration::ServerFactoryContext& factory_context,
11615
                              Stats::Scope& scope, ProtobufMessage::ValidationVisitor& validator) {
11615
  absl::Status creation_status = absl::OkStatus();
11615
  auto ret = std::shared_ptr<CommonVirtualHostImpl>(new CommonVirtualHostImpl(
11615
      virtual_host, global_route_config, factory_context, scope, validator, creation_status));
11615
  RETURN_IF_NOT_OK(creation_status);
11613
  return ret;
11615
}
VirtualHostImpl::VirtualHostImpl(const envoy::config::route::v3::VirtualHost& virtual_host,
                                 const CommonConfigSharedPtr& global_route_config,
                                 Server::Configuration::ServerFactoryContext& factory_context,
                                 Stats::Scope& scope, ProtobufMessage::ValidationVisitor& validator,
11615
                                 bool validate_clusters, absl::Status& creation_status) {
11615
  auto host_or_error = CommonVirtualHostImpl::create(virtual_host, global_route_config,
11615
                                                     factory_context, scope, validator);
11615
  SET_AND_RETURN_IF_NOT_OK(host_or_error.status(), creation_status);
11613
  shared_virtual_host_ = std::move(host_or_error.value());
11613
  switch (virtual_host.require_tls()) {
    PANIC_ON_PROTO_ENUM_SENTINEL_VALUES;
11562
  case envoy::config::route::v3::VirtualHost::NONE:
11562
    ssl_requirements_ = SslRequirements::None;
11562
    break;
4
  case envoy::config::route::v3::VirtualHost::EXTERNAL_ONLY:
4
    ssl_requirements_ = SslRequirements::ExternalOnly;
4
    break;
22
  case envoy::config::route::v3::VirtualHost::ALL:
22
    ssl_requirements_ = SslRequirements::All;
22
    break;
11613
  }
11588
  ssl_redirect_route_ = std::make_shared<SslRedirectRoute>(shared_virtual_host_);
11588
  if (virtual_host.has_matcher()) {
12
    RouteActionContext context{shared_virtual_host_, factory_context};
12
    RouteActionValidationVisitor validation_visitor;
12
    Matcher::MatchTreeFactory<Http::HttpMatchingData, RouteActionContext> factory(
12
        context, factory_context, validation_visitor);
12
    matcher_ = factory.create(virtual_host.matcher())();
12
    if (!validation_visitor.errors().empty()) {
      // TODO(snowp): Output all violations.
1
      creation_status = absl::InvalidArgumentError(
1
          fmt::format("requirement violation while creating route match tree: {}",
1
                      validation_visitor.errors()[0]));
1
      return;
1
    }
11579
  } else {
11576
    routes_.reserve(virtual_host.routes().size());
12781
    for (const auto& route : virtual_host.routes()) {
12780
      auto route_or_error = RouteCreator::createAndValidateRoute(
12780
          route, shared_virtual_host_, factory_context, validator, validate_clusters);
12780
      SET_AND_RETURN_IF_NOT_OK(route_or_error.status(), creation_status);
12754
      routes_.emplace_back(route_or_error.value());
12754
    }
11576
  }
11588
}
RouteConstSharedPtr VirtualHostImpl::getRouteFromRoutes(
    const RouteCallback& cb, const Http::RequestHeaderMap& headers,
    const StreamInfo::StreamInfo& stream_info, uint64_t random_value,
88659
    absl::Span<const RouteEntryImplBaseConstSharedPtr> routes) const {
91447
  for (auto route = routes.begin(); route != routes.end(); ++route) {
91373
    if (!headers.Path() && !(*route)->supportsPathlessHeaders()) {
21
      continue;
21
    }
91352
    RouteConstSharedPtr route_entry = (*route)->matches(headers, stream_info, random_value);
91352
    if (route_entry == nullptr) {
2759
      continue;
2759
    }
88593
    if (cb == nullptr) {
88580
      return route_entry;
88580
    }
13
    RouteEvalStatus eval_status = (std::next(route) == routes.end())
13
                                      ? RouteEvalStatus::NoMoreRoutes
13
                                      : RouteEvalStatus::HasMoreRoutes;
13
    RouteMatchStatus match_status = cb(route_entry, eval_status);
13
    if (match_status == RouteMatchStatus::Accept) {
3
      return route_entry;
3
    }
10
    if (match_status == RouteMatchStatus::Continue &&
10
        eval_status == RouteEvalStatus::NoMoreRoutes) {
2
      ENVOY_LOG(debug,
2
                "return null when route match status is Continue but there is no more routes");
2
      return nullptr;
2
    }
10
  }
74
  ENVOY_LOG(debug, "route was resolved but final route list did not match incoming request");
74
  return nullptr;
88659
}
RouteConstSharedPtr VirtualHostImpl::getRouteFromEntries(const RouteCallback& cb,
                                                         const Http::RequestHeaderMap& headers,
                                                         const StreamInfo::StreamInfo& stream_info,
89807
                                                         uint64_t random_value) const {
  // In the rare case that X-Forwarded-Proto and scheme disagree (say http URL over an HTTPS
  // connection), force a redirect based on underlying protocol, rather than URL
  // scheme, so don't force a redirect for a http:// url served over a TLS
  // connection.
89807
  const absl::string_view scheme = headers.getForwardedProtoValue();
89807
  if (scheme.empty()) {
    // No scheme header. This normally only happens when ActiveStream::decodeHeaders
    // bails early (as it rejects a request), or a buggy filter removes the :scheme header.
1127
    return nullptr;
1127
  }
  // First check for ssl redirect.
88680
  if (ssl_requirements_ == SslRequirements::All && scheme != "https") {
15
    return ssl_redirect_route_;
88665
  } else if (ssl_requirements_ == SslRequirements::ExternalOnly && scheme != "https" &&
88665
             !Http::HeaderUtility::isEnvoyInternalRequest(headers)) {
3
    return ssl_redirect_route_;
3
  }
88662
  if (matcher_) {
18
    Http::Matching::HttpMatchingDataImpl data(stream_info);
18
    data.onRequestHeaders(headers);
18
    Matcher::ActionMatchResult match_result =
18
        Matcher::evaluateMatch<Http::HttpMatchingData>(*matcher_, data);
18
    if (match_result.isMatch()) {
15
      const auto result = match_result.actionByMove();
15
      if (result->typeUrl() == RouteMatchAction::staticTypeUrl()) {
9
        return getRouteFromRoutes(
9
            cb, headers, stream_info, random_value,
9
            {std::dynamic_pointer_cast<const RouteEntryImplBase>(std::move(result))});
9
      } else if (result->typeUrl() == RouteListMatchAction::staticTypeUrl()) {
6
        const RouteListMatchAction& action = result->getTyped<RouteListMatchAction>();
6
        return getRouteFromRoutes(cb, headers, stream_info, random_value, action.routes());
6
      }
      PANIC("Action in router matcher should be Route or RouteList");
    }
3
    ENVOY_LOG(debug, "failed to match incoming request: {}",
3
              match_result.isNoMatch() ? "no match" : "insufficient data");
3
    return nullptr;
18
  }
  // Check for a route that matches the request.
88644
  return getRouteFromRoutes(cb, headers, stream_info, random_value, routes_);
88662
}
const VirtualHostImpl* RouteMatcher::findWildcardVirtualHost(
    absl::string_view host, const RouteMatcher::WildcardVirtualHosts& wildcard_virtual_hosts,
50
    RouteMatcher::SubstringFunction substring_function) const {
  // We do a longest wildcard match against the host that's passed in
  // (e.g. "foo-bar.baz.com" should match "*-bar.baz.com" before matching "*.baz.com" for suffix
  // wildcards). This is done by scanning the length => wildcards map looking for every wildcard
  // whose size is < length.
92
  for (const auto& iter : wildcard_virtual_hosts) {
92
    const uint32_t wildcard_length = iter.first;
92
    const auto& wildcard_map = iter.second;
    // >= because *.foo.com shouldn't match .foo.com.
92
    if (wildcard_length >= host.size()) {
49
      continue;
49
    }
43
    const auto match = wildcard_map.find(substring_function(host, wildcard_length));
43
    if (match != wildcard_map.end()) {
7
      return match->second.get();
7
    }
43
  }
43
  return nullptr;
50
}
absl::StatusOr<std::unique_ptr<RouteMatcher>>
RouteMatcher::create(const envoy::config::route::v3::RouteConfiguration& route_config,
                     const CommonConfigSharedPtr& global_route_config,
                     Server::Configuration::ServerFactoryContext& factory_context,
10160
                     ProtobufMessage::ValidationVisitor& validator, bool validate_clusters) {
10160
  absl::Status creation_status = absl::OkStatus();
10160
  auto ret = std::unique_ptr<RouteMatcher>{new RouteMatcher(route_config, global_route_config,
10160
                                                            factory_context, validator,
10160
                                                            validate_clusters, creation_status)};
10160
  RETURN_IF_NOT_OK(creation_status);
10124
  return ret;
10160
}
RouteMatcher::RouteMatcher(const envoy::config::route::v3::RouteConfiguration& route_config,
                           const CommonConfigSharedPtr& global_route_config,
                           Server::Configuration::ServerFactoryContext& factory_context,
                           ProtobufMessage::ValidationVisitor& validator, bool validate_clusters,
                           absl::Status& creation_status)
10160
    : vhost_scope_(factory_context.scope().scopeFromStatName(
10160
          factory_context.routerContext().virtualClusterStatNames().vhost_)),
10160
      ignore_port_in_host_matching_(route_config.ignore_port_in_host_matching()),
10160
      vhost_header_(route_config.vhost_header()) {
11637
  for (const auto& virtual_host_config : route_config.virtual_hosts()) {
11615
    VirtualHostImplSharedPtr virtual_host = std::make_shared<VirtualHostImpl>(
11615
        virtual_host_config, global_route_config, factory_context, *vhost_scope_, validator,
11615
        validate_clusters, creation_status);
11615
    SET_AND_RETURN_IF_NOT_OK(creation_status, creation_status);
11605
    for (const std::string& domain_name : virtual_host_config.domains()) {
11591
      const Http::LowerCaseString lower_case_domain_name(domain_name);
11591
      absl::string_view domain = lower_case_domain_name;
11591
      bool duplicate_found = false;
11591
      if ("*" == domain) {
9627
        if (default_virtual_host_) {
2
          creation_status = absl::InvalidArgumentError(fmt::format(
2
              "Only a single wildcard domain is permitted in route {}", route_config.name()));
2
          return;
2
        }
9625
        default_virtual_host_ = virtual_host;
10788
      } else if (!domain.empty() && '*' == domain[0]) {
7
        duplicate_found = !wildcard_virtual_host_suffixes_[domain.size() - 1]
7
                               .emplace(domain.substr(1), virtual_host)
7
                               .second;
1957
      } else if (!domain.empty() && '*' == domain[domain.size() - 1]) {
4
        duplicate_found = !wildcard_virtual_host_prefixes_[domain.size() - 1]
4
                               .emplace(domain.substr(0, domain.size() - 1), virtual_host)
4
                               .second;
1953
      } else {
1953
        duplicate_found = !virtual_hosts_.emplace(domain, virtual_host).second;
1953
      }
11589
      if (duplicate_found) {
5
        creation_status = absl::InvalidArgumentError(
5
            fmt::format("Only unique values for domains are permitted. Duplicate "
5
                        "entry of domain {} in route {}",
5
                        domain, route_config.name()));
5
        return;
5
      }
11589
    }
11586
  }
10160
}
91966
const VirtualHostImpl* RouteMatcher::findVirtualHost(const Http::RequestHeaderMap& headers) const {
  // Fast path the case where we only have a default virtual host.
91966
  if (virtual_hosts_.empty() && wildcard_virtual_host_suffixes_.empty() &&
91966
      wildcard_virtual_host_prefixes_.empty()) {
87878
    return default_virtual_host_.get();
87878
  }
4088
  absl::string_view host_header_value;
4088
  if (!vhost_header_.get().empty()) {
1
    auto result = headers.get(vhost_header_);
    // If using an alternate header, it must not be empty.
1
    if (result.empty()) {
      return nullptr;
    }
1
    host_header_value = result[0]->value().getStringView();
4087
  } else {
    // There may be no authority in early reply paths in the HTTP connection manager.
4087
    if (headers.Host() == nullptr) {
4
      return nullptr;
4
    }
4083
    host_header_value = headers.getHostValue();
4083
  }
  // If 'ignore_port_in_host_matching' is set, ignore the port number in the host header(if any).
4084
  if (ignorePortInHostMatching()) {
4
    if (const absl::string_view::size_type port_start =
4
            Http::HeaderUtility::getPortStart(host_header_value);
4
        port_start != absl::string_view::npos) {
3
      host_header_value = host_header_value.substr(0, port_start);
3
    }
4
  }
  // TODO (@rshriram) Match Origin header in WebSocket
  // request with VHost, using wildcard match
  // Lower-case the value of the host header, as hostnames are case insensitive.
4084
  const std::string host = absl::AsciiStrToLower(host_header_value);
4084
  const auto iter = virtual_hosts_.find(host);
4084
  if (iter != virtual_hosts_.end()) {
1668
    return iter->second.get();
1668
  }
2416
  if (!wildcard_virtual_host_suffixes_.empty()) {
47
    const VirtualHostImpl* vhost = findWildcardVirtualHost(
47
        host, wildcard_virtual_host_suffixes_,
47
        [](absl::string_view h, int l) -> absl::string_view { return h.substr(h.size() - l); });
47
    if (vhost != nullptr) {
6
      return vhost;
6
    }
47
  }
2410
  if (!wildcard_virtual_host_prefixes_.empty()) {
3
    const VirtualHostImpl* vhost = findWildcardVirtualHost(
3
        host, wildcard_virtual_host_prefixes_,
3
        [](absl::string_view h, int l) -> absl::string_view { return h.substr(0, l); });
3
    if (vhost != nullptr) {
1
      return vhost;
1
    }
3
  }
2409
  return default_virtual_host_.get();
2410
}
VirtualHostRoute RouteMatcher::route(const RouteCallback& cb, const Http::RequestHeaderMap& headers,
                                     const StreamInfo::StreamInfo& stream_info,
91942
                                     uint64_t random_value) const {
91942
  VirtualHostRoute route_result;
91942
  const VirtualHostImpl* virtual_host = findVirtualHost(headers);
91942
  if (virtual_host) {
89807
    route_result.vhost = virtual_host->virtualHost();
89807
    route_result.route = virtual_host->getRouteFromEntries(cb, headers, stream_info, random_value);
89807
  }
91942
  return route_result;
91942
}
const SslRedirector SslRedirectRoute::SSL_REDIRECTOR;
const envoy::config::core::v3::Metadata SslRedirectRoute::metadata_;
const Envoy::Config::TypedMetadataImpl<Envoy::Config::TypedMetadataFactory>
    SslRedirectRoute::typed_metadata_({});
const VirtualCluster*
44100
CommonVirtualHostImpl::virtualClusterFromEntries(const Http::HeaderMap& headers) const {
44365
  for (const VirtualClusterEntry& entry : virtual_clusters_) {
352
    if (Http::HeaderUtility::matchHeaders(headers, entry.headers_)) {
45
      return &entry;
45
    }
352
  }
44055
  if (!virtual_clusters_.empty()) {
41
    return virtual_cluster_catch_all_.get();
41
  }
44014
  return nullptr;
44055
}
absl::StatusOr<std::shared_ptr<CommonConfigImpl>>
CommonConfigImpl::create(const envoy::config::route::v3::RouteConfiguration& config,
                         Server::Configuration::ServerFactoryContext& factory_context,
10161
                         ProtobufMessage::ValidationVisitor& validator) {
10161
  absl::Status creation_status = absl::OkStatus();
10161
  auto ret = std::shared_ptr<CommonConfigImpl>(
10161
      new CommonConfigImpl(config, factory_context, validator, creation_status));
10161
  RETURN_IF_NOT_OK(creation_status);
10161
  return ret;
10161
}
CommonConfigImpl::CommonConfigImpl(const envoy::config::route::v3::RouteConfiguration& config,
                                   Server::Configuration::ServerFactoryContext& factory_context,
                                   ProtobufMessage::ValidationVisitor& validator,
                                   absl::Status& creation_status)
10161
    : name_(config.name()), symbol_table_(factory_context.scope().symbolTable()),
10161
      per_filter_configs_(THROW_OR_RETURN_VALUE(
          PerFilterConfigs::create(config.typed_per_filter_config(), factory_context, validator),
          std::unique_ptr<PerFilterConfigs>)),
      max_direct_response_body_size_bytes_(
10161
          PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, max_direct_response_body_size_bytes,
                                          DEFAULT_MAX_DIRECT_RESPONSE_BODY_SIZE_BYTES)),
10161
      uses_vhds_(config.has_vhds()),
10161
      most_specific_header_mutations_wins_(config.most_specific_header_mutations_wins()),
10161
      ignore_path_parameters_in_path_matching_(config.ignore_path_parameters_in_path_matching()) {
10161
  if (!config.request_mirror_policies().empty()) {
1
    shadow_policies_.reserve(config.request_mirror_policies().size());
1
    for (const auto& mirror_policy_config : config.request_mirror_policies()) {
1
      auto policy_or_error = ShadowPolicyImpl::create(mirror_policy_config, factory_context);
1
      SET_AND_RETURN_IF_NOT_OK(policy_or_error.status(), creation_status);
1
      shadow_policies_.push_back(std::move(policy_or_error.value()));
1
    }
1
  }
  // Initialize all cluster specifier plugins before creating route matcher. Because the route may
  // reference it by name.
10161
  for (const auto& plugin_proto : config.cluster_specifier_plugins()) {
6
    auto plugin = THROW_OR_RETURN_VALUE(
6
        getClusterSpecifierPluginByTheProto(plugin_proto, validator, factory_context),
6
        ClusterSpecifierPluginSharedPtr);
6
    cluster_specifier_plugins_.emplace(plugin_proto.extension().name(), std::move(plugin));
6
  }
10161
  for (const std::string& header : config.internal_only_headers()) {
9
    internal_only_headers_.push_back(Http::LowerCaseString(header));
9
  }
10161
  if (!config.request_headers_to_add().empty() || !config.request_headers_to_remove().empty()) {
27
    request_headers_parser_ =
27
        THROW_OR_RETURN_VALUE(HeaderParser::configure(config.request_headers_to_add(),
27
                                                      config.request_headers_to_remove()),
27
                              Router::HeaderParserPtr);
27
  }
10161
  if (!config.response_headers_to_add().empty() || !config.response_headers_to_remove().empty()) {
22
    response_headers_parser_ =
22
        THROW_OR_RETURN_VALUE(HeaderParser::configure(config.response_headers_to_add(),
22
                                                      config.response_headers_to_remove()),
22
                              Router::HeaderParserPtr);
22
  }
10161
  if (config.has_metadata()) {
1
    metadata_ = std::make_unique<RouteMetadataPack>(config.metadata());
1
  }
10161
}
absl::StatusOr<ClusterSpecifierPluginSharedPtr>
4
CommonConfigImpl::clusterSpecifierPlugin(absl::string_view provider) const {
4
  auto iter = cluster_specifier_plugins_.find(provider);
4
  if (iter == cluster_specifier_plugins_.end() || iter->second == nullptr) {
1
    return absl::InvalidArgumentError(
1
        fmt::format("Unknown cluster specifier plugin name: {} is used in the route", provider));
1
  }
3
  return iter->second;
4
}
1
const envoy::config::core::v3::Metadata& CommonConfigImpl::metadata() const {
1
  return metadata_ != nullptr ? metadata_->proto_metadata_
1
                              : DefaultRouteMetadataPack::get().proto_metadata_;
1
}
1
const Envoy::Config::TypedMetadata& CommonConfigImpl::typedMetadata() const {
1
  return metadata_ != nullptr ? metadata_->typed_metadata_
1
                              : DefaultRouteMetadataPack::get().typed_metadata_;
1
}
absl::StatusOr<std::shared_ptr<CommonConfigImpl>>
create(const envoy::config::route::v3::RouteConfiguration& config,
       Server::Configuration::ServerFactoryContext& factory_context,
       ProtobufMessage::ValidationVisitor& validator) {
  auto config_or_error = CommonConfigImpl::create(config, factory_context, validator);
  RETURN_IF_NOT_OK(config_or_error.status());
  return config_or_error.value();
}
absl::StatusOr<std::shared_ptr<ConfigImpl>>
ConfigImpl::create(const envoy::config::route::v3::RouteConfiguration& config,
                   Server::Configuration::ServerFactoryContext& factory_context,
9917
                   ProtobufMessage::ValidationVisitor& validator, bool validate_clusters_default) {
9917
  absl::Status creation_status = absl::OkStatus();
9917
  auto ret = std::shared_ptr<ConfigImpl>(new ConfigImpl(
9917
      config, factory_context, validator, validate_clusters_default, creation_status));
9917
  RETURN_IF_NOT_OK(creation_status);
9913
  return ret;
9917
}
ConfigImpl::ConfigImpl(const envoy::config::route::v3::RouteConfiguration& config,
                       Server::Configuration::ServerFactoryContext& factory_context,
                       ProtobufMessage::ValidationVisitor& validator,
10161
                       bool validate_clusters_default, absl::Status& creation_status) {
10161
  auto config_or_error = CommonConfigImpl::create(config, factory_context, validator);
10161
  SET_AND_RETURN_IF_NOT_OK(config_or_error.status(), creation_status);
10161
  shared_config_ = std::move(config_or_error.value());
10161
  auto matcher_or_error = RouteMatcher::create(
10161
      config, shared_config_, factory_context, validator,
10161
      PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, validate_clusters, validate_clusters_default));
10161
  SET_AND_RETURN_IF_NOT_OK(matcher_or_error.status(), creation_status);
10125
  route_matcher_ = std::move(matcher_or_error.value());
10125
}
VirtualHostRoute ConfigImpl::route(const RouteCallback& cb, const Http::RequestHeaderMap& headers,
                                   const StreamInfo::StreamInfo& stream_info,
91942
                                   uint64_t random_value) const {
91942
  return route_matcher_->route(cb, headers, stream_info, random_value);
91942
}
1
const envoy::config::core::v3::Metadata& NullConfigImpl::metadata() const {
1
  return DefaultRouteMetadataPack::get().proto_metadata_;
1
}
1
const Envoy::Config::TypedMetadata& NullConfigImpl::typedMetadata() const {
1
  return DefaultRouteMetadataPack::get().typed_metadata_;
1
}
Matcher::ActionConstSharedPtr
RouteMatchActionFactory::createAction(const Protobuf::Message& config, RouteActionContext& context,
11
                                      ProtobufMessage::ValidationVisitor& validation_visitor) {
11
  const auto& route_config =
11
      MessageUtil::downcastAndValidate<const envoy::config::route::v3::Route&>(config,
11
                                                                               validation_visitor);
11
  auto route = THROW_OR_RETURN_VALUE(
11
      RouteCreator::createAndValidateRoute(route_config, context.vhost, context.factory_context,
11
                                           validation_visitor, false),
11
      RouteEntryImplBaseConstSharedPtr);
11
  return route;
11
}
REGISTER_FACTORY(RouteMatchActionFactory, Matcher::ActionFactory<RouteActionContext>);
Matcher::ActionConstSharedPtr
RouteListMatchActionFactory::createAction(const Protobuf::Message& config,
                                          RouteActionContext& context,
3
                                          ProtobufMessage::ValidationVisitor& validation_visitor) {
3
  const auto& route_config =
3
      MessageUtil::downcastAndValidate<const envoy::config::route::v3::RouteList&>(
3
          config, validation_visitor);
3
  std::vector<RouteEntryImplBaseConstSharedPtr> routes;
9
  for (const auto& route : route_config.routes()) {
9
    routes.emplace_back(THROW_OR_RETURN_VALUE(
9
        RouteCreator::createAndValidateRoute(route, context.vhost, context.factory_context,
9
                                             validation_visitor, false),
9
        RouteEntryImplBaseConstSharedPtr));
9
  }
3
  return std::make_shared<RouteListMatchAction>(std::move(routes));
3
}
REGISTER_FACTORY(RouteListMatchActionFactory, Matcher::ActionFactory<RouteActionContext>);
} // namespace Router
} // namespace Envoy