1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
|
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SERVICES_NETWORK_SHARED_DICTIONARY_SHARED_DICTIONARY_STORAGE_H_
#define SERVICES_NETWORK_SHARED_DICTIONARY_SHARED_DICTIONARY_STORAGE_H_
#include <list>
#include <map>
#include <set>
#include <string>
#include "base/component_export.h"
#include "base/containers/contains.h"
#include "base/functional/callback.h"
#include "base/memory/ref_counted.h"
#include "base/strings/pattern.h"
#include "base/time/time.h"
#include "base/types/expected.h"
#include "url/gurl.h"
#include "url/scheme_host_port.h"
class GURL;
namespace net {
class HttpResponseHeaders;
class SharedDictionary;
} // namespace net
namespace url_pattern {
class SimpleUrlPatternMatcher;
}
namespace network {
namespace mojom {
enum class FetchResponseType : int32_t;
enum class RequestDestination : int32_t;
enum class RequestMode : int32_t;
enum class SharedDictionaryError : int32_t;
} // namespace mojom
class SharedDictionaryWriter;
// Shared Dictionary Storage manages dictionaries for a particular
// net::SharedDictionaryIsolationKey.
class COMPONENT_EXPORT(NETWORK_SERVICE) SharedDictionaryStorage
: public base::RefCounted<SharedDictionaryStorage> {
public:
SharedDictionaryStorage(const SharedDictionaryStorage&) = delete;
SharedDictionaryStorage& operator=(const SharedDictionaryStorage&) = delete;
// Returns a SharedDictionaryWriter if `headers` has a valid
// `use-as-dictionary` header, and `access_allowed_check_callback`
// returns true,
static base::expected<scoped_refptr<SharedDictionaryWriter>,
mojom::SharedDictionaryError>
MaybeCreateWriter(const std::string& use_as_dictionary_header,
bool shared_dictionary_writer_enabled,
SharedDictionaryStorage* storage,
mojom::RequestMode request_mode,
mojom::FetchResponseType response_tainting,
const GURL& url,
const base::Time request_time,
const base::Time response_time,
const net::HttpResponseHeaders& headers,
bool was_fetched_via_cache,
base::OnceCallback<bool()> access_allowed_check_callback);
// Returns a matching SharedDictionary for `url`. If the metadata has not been
// read from the database, this method returns nullptr.
virtual scoped_refptr<net::SharedDictionary> GetDictionarySync(
const GURL& url,
mojom::RequestDestination destination) = 0;
// If the metadata has already been read from the database, this method calls
// `callback` synchronously with a matching `SharedDictionary`. Otherwise,
// this method waits until the metadata is available, and then calls
// `callback` with a matching `SharedDictionary`.
virtual void GetDictionary(
const GURL& url,
mojom::RequestDestination destination,
base::OnceCallback<void(scoped_refptr<net::SharedDictionary>)>
callback) = 0;
protected:
friend class base::RefCounted<SharedDictionaryStorage>;
SharedDictionaryStorage();
virtual ~SharedDictionaryStorage();
// Called to create a SharedDictionaryWriter.
virtual base::expected<scoped_refptr<SharedDictionaryWriter>,
mojom::SharedDictionaryError>
CreateWriter(
const GURL& url,
base::Time last_fetch_time,
base::Time response_time,
base::TimeDelta expiration,
const std::string& match,
const std::set<mojom::RequestDestination>& match_dest,
const std::string& id,
std::unique_ptr<url_pattern::SimpleUrlPatternMatcher> matcher) = 0;
// If the matching dictionary is already registered, this method updates the
// `last_fetch_time` of the registered dictionary, and returns true.
// Otherwise, this method returns false.
virtual bool UpdateLastFetchTimeIfAlreadyRegistered(
const GURL& url,
base::Time response_time,
base::TimeDelta expiration,
const std::string& match,
const std::set<mojom::RequestDestination>& match_dest,
const std::string& id,
const std::optional<base::TimeDelta>& ttl,
base::Time last_fetch_time) = 0;
};
// Returns a matching dictionary for `url` from `dictionary_info_map`.
// This is a template method because SharedDictionaryStorageInMemory and
// SharedDictionaryStorageOnDisk are using different class for
// DictionaryInfoType.
template <class DictionaryInfoType>
DictionaryInfoType* GetMatchingDictionaryFromDictionaryInfoMap(
std::map<
url::SchemeHostPort,
std::map<std::tuple<std::string, std::set<mojom::RequestDestination>>,
DictionaryInfoType>>& dictionary_info_map,
const GURL& url,
mojom::RequestDestination destination,
std::list<DictionaryInfoType*>& expired_entries) {
auto it = dictionary_info_map.find(url::SchemeHostPort(url));
if (it == dictionary_info_map.end()) {
return nullptr;
}
base::Time now = base::Time::Now();
DictionaryInfoType* matched_info = nullptr;
for (auto& item : it->second) {
DictionaryInfoType& info = item.second;
CHECK(std::make_tuple(info.match(), info.match_dest()) == item.first);
// Keep track of (but don't match) expired entries.
if (info.response_time() + info.expiration() <= now) {
expired_entries.push_back(&info);
continue;
}
if (matched_info &&
((matched_info->match().size() > info.match().size()) ||
(matched_info->match().size() == info.match().size() &&
matched_info->last_fetch_time() > info.last_fetch_time()))) {
continue;
}
// When `match_dest` is empty, we don't check the `destination`.
if (!info.match_dest().empty() &&
!base::Contains(info.match_dest(), destination)) {
continue;
}
CHECK(info.matcher());
if (info.matcher()->Match(url)) {
matched_info = &info;
}
}
return matched_info;
}
// Returns the matching registered dictionary in `dictionary_info_map`. This is
// used to avoid registering the same dictionary from the disk cache.
// This is a template method because SharedDictionaryStorageInMemory and
// SharedDictionaryStorageOnDisk are using different class for
// DictionaryInfoType.
template <class DictionaryInfoType>
DictionaryInfoType* FindRegisteredInDictionaryInfoMap(
std::map<
url::SchemeHostPort,
std::map<std::tuple<std::string, std::set<mojom::RequestDestination>>,
DictionaryInfoType>>& dictionary_info_map,
const GURL& url,
base::Time response_time,
base::TimeDelta expiration,
const std::string& match,
const std::set<mojom::RequestDestination>& match_dest,
const std::string& id,
const std::optional<base::TimeDelta>& ttl) {
auto it1 = dictionary_info_map.find(url::SchemeHostPort(url));
if (it1 == dictionary_info_map.end()) {
return nullptr;
}
auto it2 = it1->second.find(std::make_tuple(match, match_dest));
if (it2 == it1->second.end()) {
return nullptr;
}
// The response_time can update on every fetch if "ttl" is used so only
// check for the exact match of response_time if a ttl isn't present.
if (it2->second.url() == url &&
(ttl || it2->second.response_time() == response_time) &&
it2->second.expiration() == expiration && it2->second.id() == id) {
return &it2->second;
} else {
return nullptr;
}
}
} // namespace network
#endif // SERVICES_NETWORK_SHARED_DICTIONARY_SHARED_DICTIONARY_STORAGE_H_
|