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

            
3
#include "envoy/config/route/v3/scoped_route.pb.h"
4
#include "envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.pb.h"
5

            
6
#include "source/common/protobuf/utility.h"
7

            
8
namespace Envoy {
9
namespace Router {
10

            
11
5
bool ScopeKey::operator!=(const ScopeKey& other) const { return !(*this == other); }
12

            
13
14
bool ScopeKey::operator==(const ScopeKey& other) const {
14
14
  if (fragments_.empty() || other.fragments_.empty()) {
15
    // An empty key equals to nothing, "NULL" != "NULL".
16
2
    return false;
17
2
  }
18
12
  return this->hash() == other.hash();
19
14
}
20

            
21
HeaderValueExtractorImpl::HeaderValueExtractorImpl(
22
    ScopedRoutes::ScopeKeyBuilder::FragmentBuilder&& config)
23
130
    : FragmentBuilderBase(std::move(config)),
24
130
      header_value_extractor_config_(config_.header_value_extractor()) {
25
130
  ASSERT(config_.type_case() ==
26
130
             ScopedRoutes::ScopeKeyBuilder::FragmentBuilder::kHeaderValueExtractor,
27
130
         "header_value_extractor is not set.");
28
130
  if (header_value_extractor_config_.extract_type_case() ==
29
130
      ScopedRoutes::ScopeKeyBuilder::FragmentBuilder::HeaderValueExtractor::kIndex) {
30
6
    if (header_value_extractor_config_.index() != 0 &&
31
6
        header_value_extractor_config_.element_separator().empty()) {
32
1
      ProtoExceptionUtil::throwProtoValidationException(
33
1
          "Index > 0 for empty string element separator.", config_);
34
1
    }
35
6
  }
36
130
  if (header_value_extractor_config_.extract_type_case() ==
37
130
      ScopedRoutes::ScopeKeyBuilder::FragmentBuilder::HeaderValueExtractor::EXTRACT_TYPE_NOT_SET) {
38
2
    ProtoExceptionUtil::throwProtoValidationException("HeaderValueExtractor extract_type not set.",
39
2
                                                      config_);
40
2
  }
41
130
}
42

            
43
std::unique_ptr<ScopeKeyFragmentBase>
44
566
HeaderValueExtractorImpl::computeFragment(const Http::HeaderMap& headers) const {
45
566
  const auto header_entry =
46
566
      headers.get(Envoy::Http::LowerCaseString(header_value_extractor_config_.name()));
47
566
  if (header_entry.empty()) {
48
3
    return nullptr;
49
3
  }
50

            
51
  // This is an implicitly untrusted header, so per the API documentation only the first
52
  // value is used.
53
563
  std::vector<absl::string_view> elements{header_entry[0]->value().getStringView()};
54
563
  if (header_value_extractor_config_.element_separator().length() > 0) {
55
491
    elements = absl::StrSplit(header_entry[0]->value().getStringView(),
56
491
                              header_value_extractor_config_.element_separator());
57
491
  }
58
563
  switch (header_value_extractor_config_.extract_type_case()) {
59
543
  case ScopedRoutes::ScopeKeyBuilder::FragmentBuilder::HeaderValueExtractor::kElement:
60
585
    for (const auto& element : elements) {
61
585
      std::pair<absl::string_view, absl::string_view> key_value = absl::StrSplit(
62
585
          element, absl::MaxSplits(header_value_extractor_config_.element().separator(), 1));
63
585
      if (key_value.first == header_value_extractor_config_.element().key()) {
64
521
        return std::make_unique<StringKeyFragment>(key_value.second);
65
521
      }
66
585
    }
67
22
    break;
68
36
  case ScopedRoutes::ScopeKeyBuilder::FragmentBuilder::HeaderValueExtractor::kIndex:
69
20
    if (header_value_extractor_config_.index() < elements.size()) {
70
17
      return std::make_unique<StringKeyFragment>(elements[header_value_extractor_config_.index()]);
71
17
    }
72
3
    break;
73
3
  case ScopedRoutes::ScopeKeyBuilder::FragmentBuilder::HeaderValueExtractor::EXTRACT_TYPE_NOT_SET:
74
    PANIC("not reached");
75
563
  }
76

            
77
25
  return nullptr;
78
563
}
79

            
80
ScopedRouteInfo::ScopedRouteInfo(envoy::config::route::v3::ScopedRouteConfiguration&& config_proto,
81
                                 ConfigConstSharedPtr route_config)
82
497
    : config_proto_(std::move(config_proto)), route_config_(route_config),
83
497
      config_hash_(MessageUtil::hash(config_proto_)) {
84
  // TODO(stevenzzzz): Maybe worth a KeyBuilder abstraction when there are more than one type of
85
  // Fragment.
86
507
  for (const auto& fragment : config_proto_.key().fragments()) {
87
507
    switch (fragment.type_case()) {
88
507
    case envoy::config::route::v3::ScopedRouteConfiguration::Key::Fragment::TypeCase::kStringKey:
89
507
      scope_key_.addFragment(std::make_unique<StringKeyFragment>(fragment.string_key()));
90
507
      break;
91
    case envoy::config::route::v3::ScopedRouteConfiguration::Key::Fragment::TypeCase::TYPE_NOT_SET:
92
      PANIC("not implemented");
93
507
    }
94
507
  }
95
497
}
96

            
97
ScopeKeyBuilderImpl::ScopeKeyBuilderImpl(ScopedRoutes::ScopeKeyBuilder&& config)
98
121
    : ScopeKeyBuilderBase(std::move(config)) {
99
121
  fragment_builders_.reserve(config_.fragments().size());
100
124
  for (const auto& fragment_builder : config_.fragments()) {
101
124
    switch (fragment_builder.type_case()) {
102
124
    case ScopedRoutes::ScopeKeyBuilder::FragmentBuilder::kHeaderValueExtractor:
103
124
      fragment_builders_.emplace_back(std::make_unique<HeaderValueExtractorImpl>(
104
124
          ScopedRoutes::ScopeKeyBuilder::FragmentBuilder(fragment_builder)));
105
124
      break;
106
    case ScopedRoutes::ScopeKeyBuilder::FragmentBuilder::TYPE_NOT_SET:
107
      PANIC("not implemented");
108
124
    }
109
124
  }
110
121
}
111

            
112
538
ScopeKeyPtr ScopeKeyBuilderImpl::computeScopeKey(const Http::HeaderMap& headers) const {
113
538
  ScopeKey key;
114
552
  for (const auto& builder : fragment_builders_) {
115
    // returns nullopt if a null fragment is found.
116
552
    std::unique_ptr<ScopeKeyFragmentBase> fragment = builder->computeFragment(headers);
117
552
    if (fragment == nullptr) {
118
21
      return nullptr;
119
21
    }
120
531
    key.addFragment(std::move(fragment));
121
531
  }
122
517
  return std::make_unique<ScopeKey>(std::move(key));
123
538
}
124

            
125
void ScopedConfigImpl::addOrUpdateRoutingScopes(
126
473
    const std::vector<ScopedRouteInfoConstSharedPtr>& scoped_route_infos) {
127
526
  for (auto& scoped_route_info : scoped_route_infos) {
128
526
    const auto iter = scoped_route_info_by_name_.find(scoped_route_info->scopeName());
129
526
    if (iter != scoped_route_info_by_name_.end()) {
130
263
      ASSERT(scoped_route_info_by_key_.contains(iter->second->scopeKey().hash()));
131
263
      scoped_route_info_by_key_.erase(iter->second->scopeKey().hash());
132
263
    }
133
526
    scoped_route_info_by_name_[scoped_route_info->scopeName()] = scoped_route_info;
134
526
    scoped_route_info_by_key_[scoped_route_info->scopeKey().hash()] = scoped_route_info;
135
526
  }
136
473
}
137

            
138
24
void ScopedConfigImpl::removeRoutingScopes(const std::vector<std::string>& scope_names) {
139
27
  for (std::string const& scope_name : scope_names) {
140
27
    const auto iter = scoped_route_info_by_name_.find(scope_name);
141
27
    if (iter != scoped_route_info_by_name_.end()) {
142
23
      ASSERT(scoped_route_info_by_key_.contains(iter->second->scopeKey().hash()));
143
23
      scoped_route_info_by_key_.erase(iter->second->scopeKey().hash());
144
23
      scoped_route_info_by_name_.erase(iter);
145
23
    }
146
27
  }
147
24
}
148

            
149
464
Router::ConfigConstSharedPtr ScopedConfigImpl::getRouteConfig(const ScopeKeyPtr& scope_key) const {
150
464
  if (scope_key == nullptr) {
151
16
    return nullptr;
152
16
  }
153
448
  auto iter = scoped_route_info_by_key_.find(scope_key->hash());
154
448
  if (iter != scoped_route_info_by_key_.end()) {
155
383
    return iter->second->routeConfig();
156
383
  }
157
65
  return nullptr;
158
448
}
159

            
160
} // namespace Router
161
} // namespace Envoy