1
#include "source/common/router/config_utility.h"
2

            
3
#include <string>
4
#include <vector>
5

            
6
#include "envoy/config/core/v3/base.pb.h"
7
#include "envoy/config/route/v3/route_components.pb.h"
8
#include "envoy/type/matcher/v3/string.pb.h"
9

            
10
#include "source/common/common/assert.h"
11
#include "source/common/common/regex.h"
12
#include "source/common/config/datasource.h"
13
#include "source/common/runtime/runtime_features.h"
14

            
15
namespace Envoy {
16
namespace Router {
17
namespace {
18

            
19
absl::optional<Matchers::StringMatcherImpl>
20
maybeCreateStringMatcher(const envoy::config::route::v3::QueryParameterMatcher& config,
21
50
                         Server::Configuration::CommonFactoryContext& context) {
22
50
  switch (config.query_parameter_match_specifier_case()) {
23
40
  case envoy::config::route::v3::QueryParameterMatcher::QueryParameterMatchSpecifierCase::
24
40
      kStringMatch:
25
40
    return Matchers::StringMatcherImpl(config.string_match(), context);
26
7
  case envoy::config::route::v3::QueryParameterMatcher::QueryParameterMatchSpecifierCase::
27
7
      kPresentMatch:
28
7
    return absl::nullopt;
29
3
  case envoy::config::route::v3::QueryParameterMatcher::QueryParameterMatchSpecifierCase::
30
3
      QUERY_PARAMETER_MATCH_SPECIFIER_NOT_SET:
31
3
    return absl::nullopt;
32
50
  }
33

            
34
  return absl::nullopt;
35
50
}
36

            
37
} // namespace
38

            
39
ConfigUtility::QueryParameterMatcher::QueryParameterMatcher(
40
    const envoy::config::route::v3::QueryParameterMatcher& config,
41
    Server::Configuration::CommonFactoryContext& context)
42
50
    : name_(config.name()),
43
50
      present_match_(config.has_present_match() ? absl::make_optional(config.present_match())
44
50
                                                : absl::nullopt),
45
50
      matcher_(maybeCreateStringMatcher(config, context)) {}
46

            
47
bool ConfigUtility::QueryParameterMatcher::matches(
48
104
    const Http::Utility::QueryParamsMulti& request_query_params) const {
49
  // This preserves the legacy behavior of ignoring all but the first value for a given key
50
104
  auto data = request_query_params.getFirstValue(name_);
51

            
52
  // If we're doing a present_match, return whether the parameter exists and matches the expected
53
  // presence
54
104
  if (Runtime::runtimeFeatureEnabled(
55
104
          "envoy.reloadable_features.enable_new_query_param_present_match_behavior") &&
56
104
      present_match_.has_value()) {
57
19
    return data.has_value() == present_match_.value();
58
19
  }
59

            
60
  // If the parameter doesn't exist, no match
61
85
  if (!data.has_value()) {
62
32
    return false;
63
32
  }
64

            
65
  // If there's no matcher, treat it as a present check
66
53
  if (!matcher_.has_value()) {
67
9
    return true;
68
9
  }
69

            
70
  // Match the value against the string matcher
71
44
  return matcher_.value().match(data.value());
72
53
}
73

            
74
ConfigUtility::CookieMatcher::CookieMatcher(const envoy::config::route::v3::CookieMatcher& config,
75
                                            Server::Configuration::CommonFactoryContext& context)
76
3
    : name_(config.name()), invert_match_(config.invert_match()),
77
3
      string_match_(config.string_match(), context) {}
78

            
79
bool ConfigUtility::CookieMatcher::matches(
80
10
    const absl::optional<absl::string_view>& cookie_value) const {
81
10
  bool matched = false;
82
10
  if (cookie_value.has_value()) {
83
8
    matched = string_match_.match(cookie_value.value());
84
8
  }
85
10
  return matched != invert_match_;
86
10
}
87

            
88
Upstream::ResourcePriority
89
12795
ConfigUtility::parsePriority(const envoy::config::core::v3::RoutingPriority& priority) {
90
12795
  switch (priority) {
91
    PANIC_ON_PROTO_ENUM_SENTINEL_VALUES;
92
12794
  case envoy::config::core::v3::DEFAULT:
93
12794
    return Upstream::ResourcePriority::Default;
94
1
  case envoy::config::core::v3::HIGH:
95
1
    return Upstream::ResourcePriority::High;
96
12795
  }
97
  PANIC_DUE_TO_CORRUPT_ENUM;
98
}
99

            
100
bool ConfigUtility::matchQueryParams(
101
    const Http::Utility::QueryParamsMulti& query_params,
102
77
    const std::vector<QueryParameterMatcherPtr>& config_query_params) {
103
78
  for (const auto& config_query_param : config_query_params) {
104
78
    if (!config_query_param->matches(query_params)) {
105
42
      return false;
106
42
    }
107
78
  }
108

            
109
35
  return true;
110
77
}
111

            
112
bool ConfigUtility::matchCookies(const absl::flat_hash_map<std::string, std::string>& cookies,
113
7
                                 const std::vector<CookieMatcherPtr>& matchers) {
114
10
  for (const auto& matcher : matchers) {
115
10
    absl::optional<absl::string_view> cookie_value;
116
10
    const auto it = cookies.find(matcher->name());
117
10
    if (it != cookies.end()) {
118
8
      cookie_value = it->second;
119
8
    }
120
10
    if (!matcher->matches(cookie_value)) {
121
4
      return false;
122
4
    }
123
10
  }
124

            
125
3
  return true;
126
7
}
127

            
128
Http::Code ConfigUtility::parseRedirectResponseCode(
129
76
    const envoy::config::route::v3::RedirectAction::RedirectResponseCode& code) {
130
76
  switch (code) {
131
    PANIC_ON_PROTO_ENUM_SENTINEL_VALUES;
132
71
  case envoy::config::route::v3::RedirectAction::MOVED_PERMANENTLY:
133
71
    return Http::Code::MovedPermanently;
134
1
  case envoy::config::route::v3::RedirectAction::FOUND:
135
1
    return Http::Code::Found;
136
1
  case envoy::config::route::v3::RedirectAction::SEE_OTHER:
137
1
    return Http::Code::SeeOther;
138
2
  case envoy::config::route::v3::RedirectAction::TEMPORARY_REDIRECT:
139
2
    return Http::Code::TemporaryRedirect;
140
1
  case envoy::config::route::v3::RedirectAction::PERMANENT_REDIRECT:
141
1
    return Http::Code::PermanentRedirect;
142
76
  }
143
  PANIC_DUE_TO_CORRUPT_ENUM;
144
}
145

            
146
absl::optional<Http::Code>
147
12795
ConfigUtility::parseDirectResponseCode(const envoy::config::route::v3::Route& route) {
148
12795
  if (route.has_redirect()) {
149
71
    return parseRedirectResponseCode(route.redirect().response_code());
150
12724
  } else if (route.has_direct_response()) {
151
514
    return static_cast<Http::Code>(route.direct_response().status());
152
514
  }
153
12210
  return {};
154
12795
}
155

            
156
Http::Code ConfigUtility::parseClusterNotFoundResponseCode(
157
12795
    const envoy::config::route::v3::RouteAction::ClusterNotFoundResponseCode& code) {
158
12795
  switch (code) {
159
    PANIC_ON_PROTO_ENUM_SENTINEL_VALUES;
160
12779
  case envoy::config::route::v3::RouteAction::SERVICE_UNAVAILABLE:
161
12779
    return Http::Code::ServiceUnavailable;
162
15
  case envoy::config::route::v3::RouteAction::NOT_FOUND:
163
15
    return Http::Code::NotFound;
164
1
  case envoy::config::route::v3::RouteAction::INTERNAL_SERVER_ERROR:
165
1
    return Http::Code::InternalServerError;
166
12795
  }
167
  PANIC_DUE_TO_CORRUPT_ENUM;
168
}
169

            
170
68
void mergeTransforms(Http::HeaderTransforms& dest, const Http::HeaderTransforms& src) {
171
68
  dest.headers_to_append_or_add.insert(dest.headers_to_append_or_add.end(),
172
68
                                       src.headers_to_append_or_add.begin(),
173
68
                                       src.headers_to_append_or_add.end());
174
68
  dest.headers_to_overwrite_or_add.insert(dest.headers_to_overwrite_or_add.end(),
175
68
                                          src.headers_to_overwrite_or_add.begin(),
176
68
                                          src.headers_to_overwrite_or_add.end());
177
68
  dest.headers_to_add_if_absent.insert(dest.headers_to_add_if_absent.end(),
178
68
                                       src.headers_to_add_if_absent.begin(),
179
68
                                       src.headers_to_add_if_absent.end());
180
68
  dest.headers_to_remove.insert(dest.headers_to_remove.end(), src.headers_to_remove.begin(),
181
68
                                src.headers_to_remove.end());
182
68
}
183

            
184
} // namespace Router
185
} // namespace Envoy