Line data Source code
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 0 : bool ScopeKey::operator!=(const ScopeKey& other) const { return !(*this == other); } 12 : 13 0 : bool ScopeKey::operator==(const ScopeKey& other) const { 14 0 : if (fragments_.empty() || other.fragments_.empty()) { 15 : // An empty key equals to nothing, "NULL" != "NULL". 16 0 : return false; 17 0 : } 18 0 : return this->hash() == other.hash(); 19 0 : } 20 : 21 : HeaderValueExtractorImpl::HeaderValueExtractorImpl( 22 : ScopedRoutes::ScopeKeyBuilder::FragmentBuilder&& config) 23 : : FragmentBuilderBase(std::move(config)), 24 0 : header_value_extractor_config_(config_.header_value_extractor()) { 25 0 : ASSERT(config_.type_case() == 26 0 : ScopedRoutes::ScopeKeyBuilder::FragmentBuilder::kHeaderValueExtractor, 27 0 : "header_value_extractor is not set."); 28 0 : if (header_value_extractor_config_.extract_type_case() == 29 0 : ScopedRoutes::ScopeKeyBuilder::FragmentBuilder::HeaderValueExtractor::kIndex) { 30 0 : if (header_value_extractor_config_.index() != 0 && 31 0 : header_value_extractor_config_.element_separator().empty()) { 32 0 : ProtoExceptionUtil::throwProtoValidationException( 33 0 : "Index > 0 for empty string element separator.", config_); 34 0 : } 35 0 : } 36 0 : if (header_value_extractor_config_.extract_type_case() == 37 0 : ScopedRoutes::ScopeKeyBuilder::FragmentBuilder::HeaderValueExtractor::EXTRACT_TYPE_NOT_SET) { 38 0 : ProtoExceptionUtil::throwProtoValidationException("HeaderValueExtractor extract_type not set.", 39 0 : config_); 40 0 : } 41 0 : } 42 : 43 : std::unique_ptr<ScopeKeyFragmentBase> 44 0 : HeaderValueExtractorImpl::computeFragment(const Http::HeaderMap& headers) const { 45 0 : const auto header_entry = 46 0 : headers.get(Envoy::Http::LowerCaseString(header_value_extractor_config_.name())); 47 0 : if (header_entry.empty()) { 48 0 : return nullptr; 49 0 : } 50 : 51 : // This is an implicitly untrusted header, so per the API documentation only the first 52 : // value is used. 53 0 : std::vector<absl::string_view> elements{header_entry[0]->value().getStringView()}; 54 0 : if (header_value_extractor_config_.element_separator().length() > 0) { 55 0 : elements = absl::StrSplit(header_entry[0]->value().getStringView(), 56 0 : header_value_extractor_config_.element_separator()); 57 0 : } 58 0 : switch (header_value_extractor_config_.extract_type_case()) { 59 0 : case ScopedRoutes::ScopeKeyBuilder::FragmentBuilder::HeaderValueExtractor::kElement: 60 0 : for (const auto& element : elements) { 61 0 : std::pair<absl::string_view, absl::string_view> key_value = absl::StrSplit( 62 0 : element, absl::MaxSplits(header_value_extractor_config_.element().separator(), 1)); 63 0 : if (key_value.first == header_value_extractor_config_.element().key()) { 64 0 : return std::make_unique<StringKeyFragment>(key_value.second); 65 0 : } 66 0 : } 67 0 : break; 68 0 : case ScopedRoutes::ScopeKeyBuilder::FragmentBuilder::HeaderValueExtractor::kIndex: 69 0 : if (header_value_extractor_config_.index() < elements.size()) { 70 0 : return std::make_unique<StringKeyFragment>(elements[header_value_extractor_config_.index()]); 71 0 : } 72 0 : break; 73 0 : case ScopedRoutes::ScopeKeyBuilder::FragmentBuilder::HeaderValueExtractor::EXTRACT_TYPE_NOT_SET: 74 0 : PANIC("not reached"); 75 0 : } 76 : 77 0 : return nullptr; 78 0 : } 79 : 80 : ScopedRouteInfo::ScopedRouteInfo(envoy::config::route::v3::ScopedRouteConfiguration config_proto, 81 : ConfigConstSharedPtr route_config) 82 : : config_proto_(config_proto), route_config_(route_config), 83 0 : 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 0 : for (const auto& fragment : config_proto_.key().fragments()) { 87 0 : switch (fragment.type_case()) { 88 0 : case envoy::config::route::v3::ScopedRouteConfiguration::Key::Fragment::TypeCase::kStringKey: 89 0 : scope_key_.addFragment(std::make_unique<StringKeyFragment>(fragment.string_key())); 90 0 : break; 91 0 : case envoy::config::route::v3::ScopedRouteConfiguration::Key::Fragment::TypeCase::TYPE_NOT_SET: 92 0 : PANIC("not implemented"); 93 0 : } 94 0 : } 95 0 : } 96 : 97 : ScopeKeyBuilderImpl::ScopeKeyBuilderImpl(ScopedRoutes::ScopeKeyBuilder&& config) 98 0 : : ScopeKeyBuilderBase(std::move(config)) { 99 0 : for (const auto& fragment_builder : config_.fragments()) { 100 0 : switch (fragment_builder.type_case()) { 101 0 : case ScopedRoutes::ScopeKeyBuilder::FragmentBuilder::kHeaderValueExtractor: 102 0 : fragment_builders_.emplace_back(std::make_unique<HeaderValueExtractorImpl>( 103 0 : ScopedRoutes::ScopeKeyBuilder::FragmentBuilder(fragment_builder))); 104 0 : break; 105 0 : case ScopedRoutes::ScopeKeyBuilder::FragmentBuilder::TYPE_NOT_SET: 106 0 : PANIC("not implemented"); 107 0 : } 108 0 : } 109 0 : } 110 : 111 0 : ScopeKeyPtr ScopeKeyBuilderImpl::computeScopeKey(const Http::HeaderMap& headers) const { 112 0 : ScopeKey key; 113 0 : for (const auto& builder : fragment_builders_) { 114 : // returns nullopt if a null fragment is found. 115 0 : std::unique_ptr<ScopeKeyFragmentBase> fragment = builder->computeFragment(headers); 116 0 : if (fragment == nullptr) { 117 0 : return nullptr; 118 0 : } 119 0 : key.addFragment(std::move(fragment)); 120 0 : } 121 0 : return std::make_unique<ScopeKey>(std::move(key)); 122 0 : } 123 : 124 : void ScopedConfigImpl::addOrUpdateRoutingScopes( 125 0 : const std::vector<ScopedRouteInfoConstSharedPtr>& scoped_route_infos) { 126 0 : for (auto& scoped_route_info : scoped_route_infos) { 127 0 : const auto iter = scoped_route_info_by_name_.find(scoped_route_info->scopeName()); 128 0 : if (iter != scoped_route_info_by_name_.end()) { 129 0 : ASSERT(scoped_route_info_by_key_.contains(iter->second->scopeKey().hash())); 130 0 : scoped_route_info_by_key_.erase(iter->second->scopeKey().hash()); 131 0 : } 132 0 : scoped_route_info_by_name_[scoped_route_info->scopeName()] = scoped_route_info; 133 0 : scoped_route_info_by_key_[scoped_route_info->scopeKey().hash()] = scoped_route_info; 134 0 : } 135 0 : } 136 : 137 0 : void ScopedConfigImpl::removeRoutingScopes(const std::vector<std::string>& scope_names) { 138 0 : for (std::string const& scope_name : scope_names) { 139 0 : const auto iter = scoped_route_info_by_name_.find(scope_name); 140 0 : if (iter != scoped_route_info_by_name_.end()) { 141 0 : ASSERT(scoped_route_info_by_key_.contains(iter->second->scopeKey().hash())); 142 0 : scoped_route_info_by_key_.erase(iter->second->scopeKey().hash()); 143 0 : scoped_route_info_by_name_.erase(iter); 144 0 : } 145 0 : } 146 0 : } 147 : 148 0 : Router::ConfigConstSharedPtr ScopedConfigImpl::getRouteConfig(const ScopeKeyPtr& scope_key) const { 149 0 : if (scope_key == nullptr) { 150 0 : return nullptr; 151 0 : } 152 0 : auto iter = scoped_route_info_by_key_.find(scope_key->hash()); 153 0 : if (iter != scoped_route_info_by_key_.end()) { 154 0 : return iter->second->routeConfig(); 155 0 : } 156 0 : return nullptr; 157 0 : } 158 : 159 : } // namespace Router 160 : } // namespace Envoy