Line data Source code
1 : #include "source/common/http/http_server_properties_cache_impl.h"
2 :
3 : #include <memory>
4 :
5 : #include "source/common/common/logger.h"
6 :
7 : #include "http3_status_tracker_impl.h"
8 : #include "quiche/spdy/core/spdy_alt_svc_wire_format.h"
9 : #include "re2/re2.h"
10 :
11 : namespace Envoy {
12 : namespace Http {
13 : namespace {
14 :
15 : struct RegexHolder {
16 0 : RegexHolder() : origin_regex("(.*)://(.*):(\\d+)") {}
17 :
18 : const re2::RE2 origin_regex;
19 : };
20 :
21 : using ConstRegexHolder = ConstSingleton<RegexHolder>;
22 :
23 : } // namespace
24 :
25 : std::string
26 0 : HttpServerPropertiesCacheImpl::originToString(const HttpServerPropertiesCache::Origin& origin) {
27 0 : return absl::StrCat(origin.scheme_, "://", origin.hostname_, ":", origin.port_);
28 0 : }
29 :
30 : absl::optional<HttpServerPropertiesCache::Origin>
31 0 : HttpServerPropertiesCacheImpl::stringToOrigin(const std::string& str) {
32 0 : const re2::RE2& origin_regex = ConstRegexHolder::get().origin_regex;
33 0 : std::string scheme;
34 0 : std::string hostname;
35 0 : int port = 0;
36 0 : if (re2::RE2::FullMatch(str.c_str(), origin_regex, &scheme, &hostname, &port)) {
37 0 : return HttpServerPropertiesCache::Origin(scheme, hostname, port);
38 0 : }
39 0 : return {};
40 0 : }
41 :
42 0 : std::string HttpServerPropertiesCacheImpl::originDataToStringForCache(const OriginData& data) {
43 0 : std::string value;
44 0 : if (!data.protocols.has_value() || data.protocols->empty()) {
45 0 : value = "clear";
46 0 : } else {
47 0 : for (auto& protocol : *data.protocols) {
48 0 : if (!value.empty()) {
49 0 : value.push_back(',');
50 0 : }
51 0 : absl::StrAppend(&value, protocol.alpn_, "=\"", protocol.hostname_, ":", protocol.port_, "\"");
52 : // Note this is _not_ actually the max age, but the absolute time at which
53 : // this entry will expire. protocolsFromString will convert back to ma.
54 0 : absl::StrAppend(
55 0 : &value, "; ma=",
56 0 : std::chrono::duration_cast<std::chrono::seconds>(protocol.expiration_.time_since_epoch())
57 0 : .count());
58 0 : }
59 0 : }
60 0 : absl::StrAppend(&value, "|", data.srtt.count(), "|", data.concurrent_streams);
61 0 : return value;
62 0 : }
63 :
64 : absl::optional<HttpServerPropertiesCacheImpl::OriginData>
65 : HttpServerPropertiesCacheImpl::originDataFromString(absl::string_view origin_data_string,
66 0 : TimeSource& time_source, bool from_cache) {
67 0 : const std::vector<absl::string_view> parts = absl::StrSplit(origin_data_string, '|');
68 0 : if (parts.size() != 3) {
69 0 : return {};
70 0 : }
71 :
72 0 : OriginData data;
73 0 : data.protocols = alternateProtocolsFromString(parts[0], time_source, from_cache);
74 :
75 0 : int64_t srtt;
76 0 : if (!absl::SimpleAtoi(parts[1], &srtt)) {
77 0 : return {};
78 0 : }
79 0 : data.srtt = std::chrono::microseconds(srtt);
80 :
81 0 : int32_t concurrency;
82 0 : if (!absl::SimpleAtoi(parts[2], &concurrency)) {
83 0 : return {};
84 0 : }
85 0 : data.concurrent_streams = concurrency;
86 :
87 0 : return data;
88 0 : }
89 :
90 : std::vector<Http::HttpServerPropertiesCache::AlternateProtocol>
91 : HttpServerPropertiesCacheImpl::alternateProtocolsFromString(absl::string_view altsvc_str,
92 : TimeSource& time_source,
93 0 : bool from_cache) {
94 0 : spdy::SpdyAltSvcWireFormat::AlternativeServiceVector altsvc_vector;
95 0 : if (!spdy::SpdyAltSvcWireFormat::ParseHeaderFieldValue(altsvc_str, &altsvc_vector)) {
96 0 : return {};
97 0 : }
98 0 : std::vector<Http::HttpServerPropertiesCache::AlternateProtocol> results;
99 0 : for (const auto& alt_svc : altsvc_vector) {
100 0 : MonotonicTime expiration;
101 0 : if (from_cache) {
102 0 : auto expire_time_from_epoch = std::chrono::seconds(alt_svc.max_age_seconds);
103 0 : auto time_since_epoch = std::chrono::duration_cast<std::chrono::seconds>(
104 0 : time_source.monotonicTime().time_since_epoch());
105 0 : if (expire_time_from_epoch < time_since_epoch) {
106 0 : expiration = time_source.monotonicTime();
107 0 : } else {
108 0 : expiration = time_source.monotonicTime() + (expire_time_from_epoch - time_since_epoch);
109 0 : }
110 0 : } else {
111 0 : expiration = time_source.monotonicTime() + std::chrono::seconds(alt_svc.max_age_seconds);
112 0 : }
113 0 : results.emplace_back(alt_svc.protocol_id, alt_svc.host, alt_svc.port, expiration);
114 0 : }
115 0 : return results;
116 0 : }
117 :
118 : HttpServerPropertiesCacheImpl::HttpServerPropertiesCacheImpl(
119 : Event::Dispatcher& dispatcher, std::vector<std::string>&& canonical_suffixes,
120 : std::unique_ptr<KeyValueStore>&& key_value_store, size_t max_entries)
121 : : dispatcher_(dispatcher), canonical_suffixes_(canonical_suffixes),
122 48 : max_entries_(max_entries > 0 ? max_entries : 1024) {
123 48 : if (key_value_store) {
124 0 : KeyValueStore::ConstIterateCb load_protocols = [this](const std::string& key,
125 0 : const std::string& value) {
126 0 : absl::optional<OriginData> origin_data =
127 0 : originDataFromString(value, dispatcher_.timeSource(), true);
128 0 : absl::optional<Origin> origin = stringToOrigin(key);
129 0 : if (origin_data.has_value() && origin.has_value()) {
130 : // We deferred transfering ownership into key_value_store_ prior, so
131 : // that we won't end up doing redundant updates to the store while
132 : // iterating.
133 0 : OptRef<std::vector<AlternateProtocol>> protocols;
134 0 : if (origin_data->protocols.has_value()) {
135 0 : protocols = *origin_data->protocols;
136 0 : }
137 0 : OriginDataWithOptRef data(protocols, origin_data->srtt, nullptr,
138 0 : origin_data->concurrent_streams);
139 0 : setPropertiesImpl(*origin, data);
140 0 : } else {
141 0 : ENVOY_LOG(warn,
142 0 : fmt::format("Unable to parse cache entry with key: {} value: {}", key, value));
143 0 : }
144 0 : return KeyValueStore::Iterate::Continue;
145 0 : };
146 0 : key_value_store->iterate(load_protocols);
147 0 : key_value_store_ = std::move(key_value_store);
148 0 : }
149 48 : }
150 :
151 48 : HttpServerPropertiesCacheImpl::~HttpServerPropertiesCacheImpl() = default;
152 :
153 : void HttpServerPropertiesCacheImpl::setAlternatives(const Origin& origin,
154 0 : std::vector<AlternateProtocol>& protocols) {
155 0 : OriginDataWithOptRef data;
156 0 : data.protocols = protocols;
157 0 : auto it = setPropertiesImpl(origin, data);
158 0 : if (key_value_store_) {
159 0 : key_value_store_->addOrUpdate(originToString(origin), originDataToStringForCache(it->second),
160 0 : absl::nullopt);
161 0 : }
162 0 : }
163 :
164 0 : void HttpServerPropertiesCacheImpl::setSrtt(const Origin& origin, std::chrono::microseconds srtt) {
165 0 : OriginDataWithOptRef data;
166 0 : data.srtt = srtt;
167 0 : auto it = setPropertiesImpl(origin, data);
168 0 : if (key_value_store_) {
169 0 : key_value_store_->addOrUpdate(originToString(origin), originDataToStringForCache(it->second),
170 0 : absl::nullopt);
171 0 : }
172 0 : }
173 :
174 0 : std::chrono::microseconds HttpServerPropertiesCacheImpl::getSrtt(const Origin& origin) const {
175 0 : auto entry_it = protocols_.find(origin);
176 0 : if (entry_it == protocols_.end()) {
177 0 : return std::chrono::microseconds(0);
178 0 : }
179 0 : return entry_it->second.srtt;
180 0 : }
181 :
182 : void HttpServerPropertiesCacheImpl::setConcurrentStreams(const Origin& origin,
183 0 : uint32_t concurrent_streams) {
184 0 : OriginDataWithOptRef data;
185 0 : data.concurrent_streams = concurrent_streams;
186 0 : auto it = setPropertiesImpl(origin, data);
187 0 : if (key_value_store_) {
188 0 : key_value_store_->addOrUpdate(originToString(origin), originDataToStringForCache(it->second),
189 0 : absl::nullopt);
190 0 : }
191 0 : }
192 :
193 0 : uint32_t HttpServerPropertiesCacheImpl::getConcurrentStreams(const Origin& origin) const {
194 0 : auto entry_it = protocols_.find(origin);
195 0 : if (entry_it == protocols_.end()) {
196 0 : return 0;
197 0 : }
198 0 : return entry_it->second.concurrent_streams;
199 0 : }
200 :
201 : HttpServerPropertiesCacheImpl::ProtocolsMap::iterator
202 : HttpServerPropertiesCacheImpl::setPropertiesImpl(const Origin& origin,
203 0 : OriginDataWithOptRef& origin_data) {
204 0 : if (origin_data.protocols.has_value()) {
205 0 : maybeSetCanonicalOrigin(origin);
206 0 : std::vector<AlternateProtocol>& protocols = *origin_data.protocols;
207 0 : static const size_t max_protocols = 10;
208 0 : if (protocols.size() > max_protocols) {
209 0 : ENVOY_LOG_MISC(trace, "Too many alternate protocols: {}, truncating", protocols.size());
210 0 : protocols.erase(protocols.begin() + max_protocols, protocols.end());
211 0 : }
212 0 : }
213 0 : auto entry_it = protocols_.find(origin);
214 0 : if (entry_it != protocols_.end()) {
215 0 : if (origin_data.protocols.has_value()) {
216 0 : entry_it->second.protocols = *origin_data.protocols;
217 0 : }
218 0 : if (origin_data.srtt.count()) {
219 0 : entry_it->second.srtt = origin_data.srtt;
220 0 : }
221 0 : if (origin_data.h3_status_tracker) {
222 0 : entry_it->second.h3_status_tracker = std::move(origin_data.h3_status_tracker);
223 0 : }
224 :
225 0 : return entry_it;
226 0 : }
227 0 : return addOriginData(origin,
228 0 : {origin_data.protocols, origin_data.srtt,
229 0 : std::move(origin_data.h3_status_tracker), origin_data.concurrent_streams});
230 0 : }
231 :
232 : HttpServerPropertiesCacheImpl::ProtocolsMap::iterator
233 0 : HttpServerPropertiesCacheImpl::addOriginData(const Origin& origin, OriginData&& origin_data) {
234 0 : ASSERT(protocols_.find(origin) == protocols_.end());
235 0 : while (protocols_.size() >= max_entries_) {
236 0 : auto iter = protocols_.begin();
237 0 : key_value_store_->remove(originToString(iter->first));
238 0 : protocols_.erase(iter);
239 0 : }
240 0 : protocols_[origin] = std::move(origin_data);
241 0 : return protocols_.find(origin);
242 0 : }
243 :
244 : OptRef<const std::vector<HttpServerPropertiesCache::AlternateProtocol>>
245 0 : HttpServerPropertiesCacheImpl::findAlternatives(const Origin& origin) {
246 0 : auto entry_it = protocols_.find(origin);
247 0 : if (entry_it == protocols_.end() || !entry_it->second.protocols.has_value()) {
248 0 : absl::optional<Origin> canonical = getCanonicalOrigin(origin.hostname_);
249 0 : if (canonical.has_value()) {
250 0 : entry_it = protocols_.find(*canonical);
251 0 : }
252 0 : if (entry_it == protocols_.end() || !entry_it->second.protocols.has_value()) {
253 0 : return makeOptRefFromPtr<const std::vector<AlternateProtocol>>(nullptr);
254 0 : }
255 0 : }
256 0 : std::vector<AlternateProtocol>& protocols = *entry_it->second.protocols;
257 :
258 0 : auto original_size = protocols.size();
259 0 : const MonotonicTime now = dispatcher_.timeSource().monotonicTime();
260 0 : protocols.erase(std::remove_if(protocols.begin(), protocols.end(),
261 0 : [now](const AlternateProtocol& protocol) {
262 0 : return (now > protocol.expiration_);
263 0 : }),
264 0 : protocols.end());
265 :
266 0 : if (protocols.empty()) {
267 0 : if (key_value_store_) {
268 0 : key_value_store_->remove(originToString(origin));
269 0 : }
270 0 : return makeOptRefFromPtr<const std::vector<AlternateProtocol>>(nullptr);
271 0 : }
272 0 : if (key_value_store_ && original_size != protocols.size()) {
273 0 : key_value_store_->addOrUpdate(originToString(origin),
274 0 : originDataToStringForCache(entry_it->second), absl::nullopt);
275 0 : }
276 0 : return makeOptRef(const_cast<const std::vector<AlternateProtocol>&>(protocols));
277 0 : }
278 :
279 0 : size_t HttpServerPropertiesCacheImpl::size() const { return protocols_.size(); }
280 :
281 : HttpServerPropertiesCache::Http3StatusTracker&
282 0 : HttpServerPropertiesCacheImpl::getOrCreateHttp3StatusTracker(const Origin& origin) {
283 0 : auto entry_it = protocols_.find(origin);
284 0 : if (entry_it != protocols_.end()) {
285 0 : if (entry_it->second.h3_status_tracker == nullptr) {
286 0 : entry_it->second.h3_status_tracker = std::make_unique<Http3StatusTrackerImpl>(dispatcher_);
287 0 : }
288 0 : return *entry_it->second.h3_status_tracker;
289 0 : }
290 :
291 0 : OriginDataWithOptRef data;
292 0 : data.h3_status_tracker = std::make_unique<Http3StatusTrackerImpl>(dispatcher_);
293 0 : auto it = setPropertiesImpl(origin, data);
294 0 : return *it->second.h3_status_tracker;
295 0 : }
296 :
297 0 : absl::string_view HttpServerPropertiesCacheImpl::getCanonicalSuffix(absl::string_view hostname) {
298 0 : for (const std::string& suffix : canonical_suffixes_) {
299 0 : if (absl::EndsWith(hostname, suffix)) {
300 0 : return suffix;
301 0 : }
302 0 : }
303 0 : return "";
304 0 : }
305 :
306 : absl::optional<HttpServerPropertiesCache::Origin>
307 0 : HttpServerPropertiesCacheImpl::getCanonicalOrigin(absl::string_view hostname) {
308 0 : absl::string_view suffix = getCanonicalSuffix(hostname);
309 0 : if (suffix.empty()) {
310 0 : return {};
311 0 : }
312 :
313 0 : auto it = canonical_alt_svc_map_.find(std::string(suffix));
314 0 : if (it == canonical_alt_svc_map_.end()) {
315 0 : return {};
316 0 : }
317 0 : return it->second;
318 0 : }
319 :
320 0 : void HttpServerPropertiesCacheImpl::maybeSetCanonicalOrigin(const Origin& origin) {
321 0 : absl::string_view suffix = getCanonicalSuffix(origin.hostname_);
322 0 : if (suffix.empty()) {
323 0 : return;
324 0 : }
325 :
326 0 : canonical_alt_svc_map_[std::string(suffix)] = origin;
327 0 : }
328 :
329 : } // namespace Http
330 : } // namespace Envoy
|