Stop checking "expires" option in Use-As-Dictionary response header
Currently Chromium is using "expires" option in Use-As-Dictionary response header for the dictionary lifetime. The option name has been renamed to "ttl" in the spec discussed in IETF HTTPWG. And the new change in the spac [1] removed this, and the lifetime should be calculated from the response's freshness [2]. After this CL, when V2 backend is enabled, Chromium will not check "expires" option, and it will check the response's freshness. [1]: https://github.com/httpwg/http-extensions/pull/2709 [2]: https://datatracker.ietf.org/doc/html/rfc9111#name-freshness Bug: 1413922 Change-Id: Idd2cdeb526099a1f2854ea0094541cedc0a64af8 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5227360 Commit-Queue: Tsuyoshi Horo <horo@chromium.org> Reviewed-by: Kenichi Ishibashi <bashi@chromium.org> Cr-Commit-Position: refs/heads/main@{#1251343}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
14be928e11
commit
47afd65f83
chrome/browser/net
content
browser
test
docs/experiments
services/network
@ -336,6 +336,7 @@ class ChromeSharedDictionaryBrowserTest
|
||||
if (request.relative_url == "/dictionary") {
|
||||
response->set_content_type("text/plain");
|
||||
response->AddCustomHeader("use-as-dictionary", "match=\"/path/*\"");
|
||||
response->AddCustomHeader("cache-control", "max-age=3600");
|
||||
response->set_content(kTestDictionaryString);
|
||||
return response;
|
||||
} else if (request.relative_url == "/path/check_header") {
|
||||
|
@ -1176,6 +1176,7 @@ IN_PROC_BROWSER_TEST_P(SharedDictionaryFeatureStateBrowserTest,
|
||||
// We want to check the behavior of kBackendOnly and kFullyEnabled.
|
||||
return;
|
||||
}
|
||||
base::Time test_start_time = base::Time::Now();
|
||||
RunWriteDictionaryTest(
|
||||
FetchType::kLinkRelDictionary,
|
||||
GURL("https://shared-dictionary.test/blank.html?ot=enabled"),
|
||||
@ -1190,10 +1191,26 @@ IN_PROC_BROWSER_TEST_P(SharedDictionaryFeatureStateBrowserTest,
|
||||
base::BindLambdaForTesting(
|
||||
[&](std::vector<network::mojom::SharedDictionaryInfoPtr> result) {
|
||||
ASSERT_EQ(1u, result.size());
|
||||
EXPECT_EQ(GetFeatureState() == FeatureState::kBackendOnly
|
||||
? base::Days(30)
|
||||
: base::Seconds(31536000),
|
||||
result[0]->expiration);
|
||||
switch (GetVersion()) {
|
||||
case network::features::
|
||||
CompressionDictionaryTransportBackendVersion::kV1:
|
||||
EXPECT_EQ(GetFeatureState() == FeatureState::kBackendOnly
|
||||
? base::Days(30)
|
||||
: base::Seconds(31536000),
|
||||
result[0]->expiration);
|
||||
break;
|
||||
case network::features::
|
||||
CompressionDictionaryTransportBackendVersion::kV2:
|
||||
// The dictionary response was treated as somewhat aged
|
||||
// (response time - request time). So the expiration is a bit
|
||||
// smaller than 3600 seconds which is set in the Cache-Control
|
||||
// header's max-age directive.
|
||||
EXPECT_LT(result[0]->expiration, base::Seconds(3600));
|
||||
EXPECT_GT(result[0]->expiration,
|
||||
base::Seconds(3600) -
|
||||
(result[0]->response_time - test_start_time));
|
||||
break;
|
||||
}
|
||||
loop.Quit();
|
||||
}));
|
||||
loop.Run();
|
||||
|
@ -1,3 +1,4 @@
|
||||
HTTP/1.1 200 OK
|
||||
Use-As-Dictionary: match="/shared_dictionary/path/*"
|
||||
Access-Control-Allow-Origin: *
|
||||
Cache-Control: max-age=3600
|
||||
|
@ -1,4 +1,5 @@
|
||||
HTTP/1.1 200 OK
|
||||
Use-As-Dictionary: match="/shared_dictionary/path/*"
|
||||
Access-Control-Allow-Origin: *
|
||||
Cache-Control: max-age=3600
|
||||
Content-Type: text/html
|
||||
|
@ -1,2 +1,3 @@
|
||||
HTTP/1.1 200 OK
|
||||
Use-As-Dictionary: match="/shared_dictionary/path/*"
|
||||
Cache-Control: max-age=3600
|
||||
|
@ -204,8 +204,9 @@ construction to catch up the following spec changes:
|
||||
- Stauts: Not yet implemented.
|
||||
- Added support for a server-provided dictionary id
|
||||
- Stauts: Not yet implemented.
|
||||
- Updated the default dictionary ttl to 14 days since last fetched
|
||||
- Stauts: Not yet implemented.
|
||||
- Stop using "expires" value of "Use-As-Dictionary" header, and use the cache
|
||||
expiration time calculated from the response's freshness instead.
|
||||
- Stauts: Removed by https://crrev.com/c/5227360.
|
||||
- Removed support for hash negotiation and force use of sha-256
|
||||
- Stauts: Removed by https://crrev.com/c/5223985.
|
||||
- Added the dictionary hash to the compressed response
|
||||
|
@ -617,8 +617,8 @@ void CorsURLLoader::OnReceiveResponse(
|
||||
// registration.
|
||||
CHECK_NE(mojom::FetchResponseType::kOpaque, response_tainting_);
|
||||
auto writer = shared_dictionary_storage_->MaybeCreateWriter(
|
||||
request_.url, response_head->response_time, *response_head->headers,
|
||||
response_head->was_fetched_via_cache,
|
||||
request_.url, response_head->request_time, response_head->response_time,
|
||||
*response_head->headers, response_head->was_fetched_via_cache,
|
||||
base::BindOnce(
|
||||
&SharedDictionaryAccessChecker::CheckAllowedToWriteAndReport,
|
||||
std::make_unique<SharedDictionaryAccessChecker>(
|
||||
|
@ -100,6 +100,7 @@ class CorsURLLoaderSharedDictionaryTest : public CorsURLLoaderTestBase {
|
||||
extra_headers.emplace_back(
|
||||
network::shared_dictionary::kUseAsDictionaryHeaderName,
|
||||
"match=\"/path*\"");
|
||||
extra_headers.emplace_back("cache-control", "max-age=2592000");
|
||||
NotifyLoaderClientOnReceiveResponse(extra_headers,
|
||||
std::move(consumer_handle_));
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ namespace network::shared_dictionary {
|
||||
|
||||
// The default value (1 year) of expiration time in "use-as-dictionary"
|
||||
// HTTP header.
|
||||
// This will not be used when V2 backend is enabled.
|
||||
constexpr base::TimeDelta kDefaultExpiration = base::Seconds(31536000);
|
||||
|
||||
// The max expiration time (30 days) for Origin Trial. This is used when
|
||||
@ -55,6 +56,7 @@ extern const char kContentDictionaryHeaderName[];
|
||||
COMPONENT_EXPORT(NETWORK_SERVICE) extern const char kOptionNameMatch[];
|
||||
|
||||
// The dictionary option name of "expires".
|
||||
// This will not be used when V2 backend is enabled.
|
||||
COMPONENT_EXPORT(NETWORK_SERVICE) extern const char kOptionNameExpires[];
|
||||
|
||||
// The dictionary option name of "type".
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "services/network/shared_dictionary/shared_dictionary_manager_on_disk.h"
|
||||
|
||||
#include "base/feature_list.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/files/scoped_temp_dir.h"
|
||||
@ -13,6 +14,7 @@
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/test/bind.h"
|
||||
#include "base/test/metrics/histogram_tester.h"
|
||||
#include "base/test/scoped_feature_list.h"
|
||||
#include "base/test/task_environment.h"
|
||||
#include "base/test/test_file_util.h"
|
||||
#include "base/time/time.h"
|
||||
@ -27,6 +29,7 @@
|
||||
#include "net/disk_cache/disk_cache_test_util.h"
|
||||
#include "net/extras/shared_dictionary/shared_dictionary_info.h"
|
||||
#include "net/http/http_response_headers.h"
|
||||
#include "services/network/public/cpp/features.h"
|
||||
#include "services/network/shared_dictionary/shared_dictionary.h"
|
||||
#include "services/network/shared_dictionary/shared_dictionary_constants.h"
|
||||
#include "services/network/shared_dictionary/shared_dictionary_disk_cache.h"
|
||||
@ -54,6 +57,10 @@ const net::SchemefulSite kSite(kUrl);
|
||||
const std::string kTestData1 = "Hello world";
|
||||
const std::string kTestData2 = "Bonjour le monde";
|
||||
|
||||
// Default cache control header for dictionary entries which expires in 30 days.
|
||||
const std::string kDefaultCacheControlHeader =
|
||||
"cache-control: max-age=2592000\n";
|
||||
|
||||
const int kCurrentVersionNumber = 1;
|
||||
|
||||
base::OnceCallback<bool()> DummyAccessAllowedCheckCallback() {
|
||||
@ -67,10 +74,11 @@ void WriteDictionary(SharedDictionaryStorage* storage,
|
||||
scoped_refptr<net::HttpResponseHeaders> headers =
|
||||
net::HttpResponseHeaders::TryToCreate(base::StrCat(
|
||||
{"HTTP/1.1 200 OK\n", shared_dictionary::kUseAsDictionaryHeaderName,
|
||||
": match=\"/", match, "\"\n\n"}));
|
||||
": match=\"/", match, "\"\n", kDefaultCacheControlHeader, "\n"}));
|
||||
ASSERT_TRUE(headers);
|
||||
scoped_refptr<SharedDictionaryWriter> writer = storage->MaybeCreateWriter(
|
||||
dictionary_url, base::Time::Now(), *headers,
|
||||
dictionary_url, /*request_time=*/base::Time::Now(),
|
||||
/*response_time=*/base::Time::Now(), *headers,
|
||||
/*was_fetched_via_cache=*/false, DummyAccessAllowedCheckCallback());
|
||||
ASSERT_TRUE(writer);
|
||||
writer->Append(data.c_str(), data.size());
|
||||
@ -85,10 +93,13 @@ void WriteDictionaryWithExpiry(SharedDictionaryStorage* storage,
|
||||
net::HttpResponseHeaders::TryToCreate(base::StrCat(
|
||||
{"HTTP/1.1 200 OK\n", shared_dictionary::kUseAsDictionaryHeaderName,
|
||||
": match=\"/", match,
|
||||
"\", expires=", base::NumberToString(expires.InSeconds()), "\n\n"}));
|
||||
"\", expires=", base::NumberToString(expires.InSeconds()), "\n",
|
||||
"cache-control: max-age=", base::NumberToString(expires.InSeconds()),
|
||||
"\n\n"}));
|
||||
ASSERT_TRUE(headers);
|
||||
scoped_refptr<SharedDictionaryWriter> writer = storage->MaybeCreateWriter(
|
||||
dictionary_url, base::Time::Now(), *headers,
|
||||
dictionary_url, /*request_time=*/base::Time::Now(),
|
||||
/*response_time=*/base::Time::Now(), *headers,
|
||||
/*was_fetched_via_cache=*/false, DummyAccessAllowedCheckCallback());
|
||||
ASSERT_TRUE(writer);
|
||||
writer->Append(data.c_str(), data.size());
|
||||
@ -163,9 +174,20 @@ base::UnguessableToken GetDiskCacheKeyTokenOfFirstDictionary(
|
||||
|
||||
} // namespace
|
||||
|
||||
class SharedDictionaryManagerOnDiskTest : public ::testing::Test {
|
||||
class SharedDictionaryManagerOnDiskTest
|
||||
: public ::testing::Test,
|
||||
public testing::WithParamInterface<
|
||||
features::CompressionDictionaryTransportBackendVersion> {
|
||||
public:
|
||||
SharedDictionaryManagerOnDiskTest() = default;
|
||||
SharedDictionaryManagerOnDiskTest() {
|
||||
std::vector<base::test::FeatureRefAndParams> enabled_features;
|
||||
enabled_features.emplace_back(base::test::FeatureRefAndParams(
|
||||
features::kCompressionDictionaryTransportBackend,
|
||||
{{features::kCompressionDictionaryTransportBackendVersion.name,
|
||||
features::kCompressionDictionaryTransportBackendVersion.GetName(
|
||||
GetVersion())}}));
|
||||
scoped_feature_list_.InitWithFeaturesAndParameters(enabled_features, {});
|
||||
}
|
||||
~SharedDictionaryManagerOnDiskTest() override = default;
|
||||
|
||||
SharedDictionaryManagerOnDiskTest(const SharedDictionaryManagerOnDiskTest&) =
|
||||
@ -182,6 +204,9 @@ class SharedDictionaryManagerOnDiskTest : public ::testing::Test {
|
||||
void TearDown() override { FlushCacheTasks(); }
|
||||
|
||||
protected:
|
||||
features::CompressionDictionaryTransportBackendVersion GetVersion() const {
|
||||
return GetParam();
|
||||
}
|
||||
std::unique_ptr<SharedDictionaryManager> CreateSharedDictionaryManager(
|
||||
uint64_t cache_max_size = 0,
|
||||
uint64_t cache_max_count =
|
||||
@ -242,9 +267,26 @@ class SharedDictionaryManagerOnDiskTest : public ::testing::Test {
|
||||
// `file_permissions_restorer_` must be below `tmp_directory_` to restore the
|
||||
// file permission correctly.
|
||||
std::unique_ptr<base::FilePermissionRestorer> file_permissions_restorer_;
|
||||
base::test::ScopedFeatureList scoped_feature_list_;
|
||||
};
|
||||
|
||||
TEST_F(SharedDictionaryManagerOnDiskTest, ReusingRefCountedSharedDictionary) {
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
All,
|
||||
SharedDictionaryManagerOnDiskTest,
|
||||
testing::ValuesIn(
|
||||
{features::CompressionDictionaryTransportBackendVersion::kV1,
|
||||
features::CompressionDictionaryTransportBackendVersion::kV2}),
|
||||
[](const testing::TestParamInfo<
|
||||
features::CompressionDictionaryTransportBackendVersion>& info) {
|
||||
switch (info.param) {
|
||||
case features::CompressionDictionaryTransportBackendVersion::kV1:
|
||||
return "V1";
|
||||
case features::CompressionDictionaryTransportBackendVersion::kV2:
|
||||
return "V2";
|
||||
}
|
||||
});
|
||||
|
||||
TEST_P(SharedDictionaryManagerOnDiskTest, ReusingRefCountedSharedDictionary) {
|
||||
std::unique_ptr<SharedDictionaryManager> manager =
|
||||
CreateSharedDictionaryManager();
|
||||
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
|
||||
@ -287,7 +329,7 @@ TEST_F(SharedDictionaryManagerOnDiskTest, ReusingRefCountedSharedDictionary) {
|
||||
dict1->size()));
|
||||
}
|
||||
|
||||
TEST_F(SharedDictionaryManagerOnDiskTest,
|
||||
TEST_P(SharedDictionaryManagerOnDiskTest,
|
||||
MaybeCreateWriterAfterManagerDeleted) {
|
||||
std::unique_ptr<SharedDictionaryManager> manager =
|
||||
CreateSharedDictionaryManager();
|
||||
@ -302,17 +344,18 @@ TEST_F(SharedDictionaryManagerOnDiskTest,
|
||||
scoped_refptr<net::HttpResponseHeaders> headers =
|
||||
net::HttpResponseHeaders::TryToCreate(base::StrCat(
|
||||
{"HTTP/1.1 200 OK\n", shared_dictionary::kUseAsDictionaryHeaderName,
|
||||
": match=\"/testfile*\"\n\n"}));
|
||||
": match=\"/testfile*\"\n", kDefaultCacheControlHeader, "\n"}));
|
||||
ASSERT_TRUE(headers);
|
||||
|
||||
// MaybeCreateWriter() must return nullptr, after `manager` was deleted.
|
||||
scoped_refptr<SharedDictionaryWriter> writer = storage->MaybeCreateWriter(
|
||||
GURL("https://origin.test/dict"), base::Time::Now(), *headers,
|
||||
GURL("https://origin.test/dict"), /*request_time=*/base::Time::Now(),
|
||||
/*response_time=*/base::Time::Now(), *headers,
|
||||
/*was_fetched_via_cache=*/false, DummyAccessAllowedCheckCallback());
|
||||
EXPECT_FALSE(writer);
|
||||
}
|
||||
|
||||
TEST_F(SharedDictionaryManagerOnDiskTest, GetDictionaryAfterManagerDeleted) {
|
||||
TEST_P(SharedDictionaryManagerOnDiskTest, GetDictionaryAfterManagerDeleted) {
|
||||
std::unique_ptr<SharedDictionaryManager> manager =
|
||||
CreateSharedDictionaryManager();
|
||||
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
|
||||
@ -329,7 +372,7 @@ TEST_F(SharedDictionaryManagerOnDiskTest, GetDictionaryAfterManagerDeleted) {
|
||||
EXPECT_FALSE(dict);
|
||||
}
|
||||
|
||||
TEST_F(SharedDictionaryManagerOnDiskTest,
|
||||
TEST_P(SharedDictionaryManagerOnDiskTest,
|
||||
DictionaryWrittenInDiskCacheAfterManagerDeleted) {
|
||||
std::unique_ptr<SharedDictionaryManager> manager =
|
||||
CreateSharedDictionaryManager();
|
||||
@ -347,7 +390,7 @@ TEST_F(SharedDictionaryManagerOnDiskTest,
|
||||
FlushCacheTasks();
|
||||
}
|
||||
|
||||
TEST_F(SharedDictionaryManagerOnDiskTest, OverridingDictionary) {
|
||||
TEST_P(SharedDictionaryManagerOnDiskTest, OverridingDictionary) {
|
||||
std::unique_ptr<SharedDictionaryManager> manager =
|
||||
CreateSharedDictionaryManager();
|
||||
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
|
||||
@ -419,7 +462,7 @@ TEST_F(SharedDictionaryManagerOnDiskTest, OverridingDictionary) {
|
||||
dict1->size()));
|
||||
}
|
||||
|
||||
TEST_F(SharedDictionaryManagerOnDiskTest, MultipleDictionaries) {
|
||||
TEST_P(SharedDictionaryManagerOnDiskTest, MultipleDictionaries) {
|
||||
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
|
||||
kSite);
|
||||
|
||||
@ -502,7 +545,7 @@ TEST_F(SharedDictionaryManagerOnDiskTest, MultipleDictionaries) {
|
||||
dict2->size()));
|
||||
}
|
||||
|
||||
TEST_F(SharedDictionaryManagerOnDiskTest, GetDictionary) {
|
||||
TEST_P(SharedDictionaryManagerOnDiskTest, GetDictionary) {
|
||||
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
|
||||
kSite);
|
||||
|
||||
@ -556,7 +599,7 @@ TEST_F(SharedDictionaryManagerOnDiskTest, GetDictionary) {
|
||||
// Test that corruptted disk cache doesn't cause crash.
|
||||
// CorruptDiskCache() doesn't work on Fuchsia. So disabling the following tests
|
||||
// on Fuchsia.
|
||||
TEST_F(SharedDictionaryManagerOnDiskTest, CorruptedDiskCacheAndWriteData) {
|
||||
TEST_P(SharedDictionaryManagerOnDiskTest, CorruptedDiskCacheAndWriteData) {
|
||||
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
|
||||
kSite);
|
||||
|
||||
@ -594,7 +637,7 @@ TEST_F(SharedDictionaryManagerOnDiskTest, CorruptedDiskCacheAndWriteData) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SharedDictionaryManagerOnDiskTest, CorruptedDiskCacheAndGetData) {
|
||||
TEST_P(SharedDictionaryManagerOnDiskTest, CorruptedDiskCacheAndGetData) {
|
||||
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
|
||||
kSite);
|
||||
|
||||
@ -643,7 +686,7 @@ TEST_F(SharedDictionaryManagerOnDiskTest, CorruptedDiskCacheAndGetData) {
|
||||
}
|
||||
#endif // !BUILDFLAG(IS_FUCHSIA)
|
||||
|
||||
TEST_F(SharedDictionaryManagerOnDiskTest, CorruptedDatabase) {
|
||||
TEST_P(SharedDictionaryManagerOnDiskTest, CorruptedDatabase) {
|
||||
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
|
||||
kSite);
|
||||
|
||||
@ -730,7 +773,7 @@ TEST_F(SharedDictionaryManagerOnDiskTest, CorruptedDatabase) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SharedDictionaryManagerOnDiskTest, MetadataBrokenDatabase) {
|
||||
TEST_P(SharedDictionaryManagerOnDiskTest, MetadataBrokenDatabase) {
|
||||
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
|
||||
kSite);
|
||||
|
||||
@ -799,7 +842,7 @@ TEST_F(SharedDictionaryManagerOnDiskTest, MetadataBrokenDatabase) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SharedDictionaryManagerOnDiskTest, LastUsedTime) {
|
||||
TEST_P(SharedDictionaryManagerOnDiskTest, LastUsedTime) {
|
||||
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
|
||||
kSite);
|
||||
base::Time last_used_time_after_second_get_dict;
|
||||
@ -871,7 +914,7 @@ MATCHER_P(DictionaryUrlIs,
|
||||
return arg.url().spec() == url;
|
||||
}
|
||||
|
||||
TEST_F(SharedDictionaryManagerOnDiskTest, ClearData) {
|
||||
TEST_P(SharedDictionaryManagerOnDiskTest, ClearData) {
|
||||
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
|
||||
kSite);
|
||||
{
|
||||
@ -973,7 +1016,7 @@ TEST_F(SharedDictionaryManagerOnDiskTest, ClearData) {
|
||||
Pair("/p4*", DictionaryUrlIs("https://target.test/4"))))));
|
||||
}
|
||||
|
||||
TEST_F(SharedDictionaryManagerOnDiskTest, ClearDataSerializedOperation) {
|
||||
TEST_P(SharedDictionaryManagerOnDiskTest, ClearDataSerializedOperation) {
|
||||
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
|
||||
kSite);
|
||||
std::unique_ptr<SharedDictionaryManager> manager =
|
||||
@ -1024,7 +1067,7 @@ TEST_F(SharedDictionaryManagerOnDiskTest, ClearDataSerializedOperation) {
|
||||
EXPECT_TRUE(GetOnDiskDictionaryMap(storage.get()).empty());
|
||||
}
|
||||
|
||||
TEST_F(SharedDictionaryManagerOnDiskTest, ClearDataForIsolationKey) {
|
||||
TEST_P(SharedDictionaryManagerOnDiskTest, ClearDataForIsolationKey) {
|
||||
net::SharedDictionaryIsolationKey isolation_key1(url::Origin::Create(kUrl),
|
||||
kSite);
|
||||
net::SharedDictionaryIsolationKey isolation_key2(
|
||||
@ -1096,7 +1139,7 @@ TEST_F(SharedDictionaryManagerOnDiskTest, ClearDataForIsolationKey) {
|
||||
"/p*", DictionaryUrlIs("https://origin1.test/d"))))));
|
||||
}
|
||||
|
||||
TEST_F(SharedDictionaryManagerOnDiskTest, ExpiredDictionaryDeletionOnReload) {
|
||||
TEST_P(SharedDictionaryManagerOnDiskTest, ExpiredDictionaryDeletionOnReload) {
|
||||
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
|
||||
kSite);
|
||||
base::UnguessableToken token1, token2;
|
||||
@ -1143,7 +1186,7 @@ TEST_F(SharedDictionaryManagerOnDiskTest, ExpiredDictionaryDeletionOnReload) {
|
||||
EXPECT_TRUE(DiskCacheEntryExists(manager.get(), token2));
|
||||
}
|
||||
|
||||
TEST_F(SharedDictionaryManagerOnDiskTest,
|
||||
TEST_P(SharedDictionaryManagerOnDiskTest,
|
||||
ExpiredDictionaryDeletionOnNewDictionary) {
|
||||
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
|
||||
kSite);
|
||||
@ -1188,7 +1231,7 @@ TEST_F(SharedDictionaryManagerOnDiskTest,
|
||||
EXPECT_TRUE(DiskCacheEntryExists(manager.get(), token2));
|
||||
}
|
||||
|
||||
TEST_F(SharedDictionaryManagerOnDiskTest,
|
||||
TEST_P(SharedDictionaryManagerOnDiskTest,
|
||||
ExpiredDictionaryDeletionOnSetCacheMaxSize) {
|
||||
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
|
||||
kSite);
|
||||
@ -1224,7 +1267,7 @@ TEST_F(SharedDictionaryManagerOnDiskTest,
|
||||
EXPECT_FALSE(DiskCacheEntryExists(manager.get(), token));
|
||||
}
|
||||
|
||||
TEST_F(SharedDictionaryManagerOnDiskTest,
|
||||
TEST_P(SharedDictionaryManagerOnDiskTest,
|
||||
ExpiredDictionaryDeletionOnClearData) {
|
||||
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
|
||||
kSite);
|
||||
@ -1263,7 +1306,7 @@ TEST_F(SharedDictionaryManagerOnDiskTest,
|
||||
EXPECT_FALSE(DiskCacheEntryExists(manager.get(), token));
|
||||
}
|
||||
|
||||
TEST_F(SharedDictionaryManagerOnDiskTest,
|
||||
TEST_P(SharedDictionaryManagerOnDiskTest,
|
||||
ExpiredDictionaryDeletionOnClearDataForIsolationKey) {
|
||||
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
|
||||
kSite);
|
||||
@ -1303,7 +1346,7 @@ TEST_F(SharedDictionaryManagerOnDiskTest,
|
||||
EXPECT_FALSE(DiskCacheEntryExists(manager.get(), token));
|
||||
}
|
||||
|
||||
TEST_F(SharedDictionaryManagerOnDiskTest, CacheEvictionOnReload) {
|
||||
TEST_P(SharedDictionaryManagerOnDiskTest, CacheEvictionOnReload) {
|
||||
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
|
||||
kSite);
|
||||
base::UnguessableToken token1, token2, token3;
|
||||
@ -1355,7 +1398,7 @@ TEST_F(SharedDictionaryManagerOnDiskTest, CacheEvictionOnReload) {
|
||||
EXPECT_TRUE(DiskCacheEntryExists(manager.get(), token3));
|
||||
}
|
||||
|
||||
TEST_F(SharedDictionaryManagerOnDiskTest, CacheEvictionOnSetCacheMaxSize) {
|
||||
TEST_P(SharedDictionaryManagerOnDiskTest, CacheEvictionOnSetCacheMaxSize) {
|
||||
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
|
||||
kSite);
|
||||
std::unique_ptr<SharedDictionaryManager> manager =
|
||||
@ -1397,7 +1440,7 @@ TEST_F(SharedDictionaryManagerOnDiskTest, CacheEvictionOnSetCacheMaxSize) {
|
||||
EXPECT_TRUE(DiskCacheEntryExists(manager.get(), token3));
|
||||
}
|
||||
|
||||
TEST_F(SharedDictionaryManagerOnDiskTest, CacheEvictionOnNewDictionary) {
|
||||
TEST_P(SharedDictionaryManagerOnDiskTest, CacheEvictionOnNewDictionary) {
|
||||
const net::SchemefulSite site1(GURL("https://site1.test"));
|
||||
const net::SchemefulSite site2(GURL("https://site2.test"));
|
||||
const net::SchemefulSite site3(GURL("https://site3.test"));
|
||||
@ -1473,7 +1516,7 @@ TEST_F(SharedDictionaryManagerOnDiskTest, CacheEvictionOnNewDictionary) {
|
||||
EXPECT_TRUE(DiskCacheEntryExists(manager.get(), token3));
|
||||
}
|
||||
|
||||
TEST_F(SharedDictionaryManagerOnDiskTest,
|
||||
TEST_P(SharedDictionaryManagerOnDiskTest,
|
||||
CacheEvictionPerSiteExceededSizeLimit) {
|
||||
const net::SchemefulSite site1(GURL("https://site1.test"));
|
||||
const net::SchemefulSite site2(GURL("https://site2.test"));
|
||||
@ -1553,7 +1596,7 @@ TEST_F(SharedDictionaryManagerOnDiskTest,
|
||||
EXPECT_FALSE(DiskCacheEntryExists(manager.get(), token2));
|
||||
}
|
||||
|
||||
TEST_F(SharedDictionaryManagerOnDiskTest,
|
||||
TEST_P(SharedDictionaryManagerOnDiskTest,
|
||||
CacheEvictionPerSiteExceededCountLimit) {
|
||||
const net::SchemefulSite site1(GURL("https://site1.test"));
|
||||
const net::SchemefulSite site2(GURL("https://site2.test"));
|
||||
@ -1655,7 +1698,7 @@ TEST_F(SharedDictionaryManagerOnDiskTest,
|
||||
EXPECT_TRUE(DiskCacheEntryExists(manager.get(), token3));
|
||||
}
|
||||
|
||||
TEST_F(SharedDictionaryManagerOnDiskTest,
|
||||
TEST_P(SharedDictionaryManagerOnDiskTest,
|
||||
CacheEvictionAfterUpdatingLastUsedTime) {
|
||||
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
|
||||
kSite);
|
||||
@ -1727,7 +1770,7 @@ TEST_F(SharedDictionaryManagerOnDiskTest,
|
||||
EXPECT_TRUE(DiskCacheEntryExists(manager.get(), token4));
|
||||
}
|
||||
|
||||
TEST_F(SharedDictionaryManagerOnDiskTest,
|
||||
TEST_P(SharedDictionaryManagerOnDiskTest,
|
||||
MismatchingEntryDeletionMetadataUnavailableDictionary) {
|
||||
const base::UnguessableToken token = base::UnguessableToken::Create();
|
||||
const std::string entry_key = token.ToString();
|
||||
@ -1757,7 +1800,7 @@ TEST_F(SharedDictionaryManagerOnDiskTest,
|
||||
0, 1);
|
||||
}
|
||||
|
||||
TEST_F(SharedDictionaryManagerOnDiskTest,
|
||||
TEST_P(SharedDictionaryManagerOnDiskTest,
|
||||
MismatchingEntryDeletionInvalidDiskCacheEntry) {
|
||||
const std::string kTestKey = "test";
|
||||
const std::string kTestData = "Hello world";
|
||||
@ -1786,7 +1829,7 @@ TEST_F(SharedDictionaryManagerOnDiskTest,
|
||||
0, 1);
|
||||
}
|
||||
|
||||
TEST_F(SharedDictionaryManagerOnDiskTest,
|
||||
TEST_P(SharedDictionaryManagerOnDiskTest,
|
||||
MismatchingEntryDeletionDiskCacheEntryUnavailableDictionary) {
|
||||
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
|
||||
kSite);
|
||||
@ -1853,7 +1896,7 @@ TEST_F(SharedDictionaryManagerOnDiskTest,
|
||||
EXPECT_TRUE(GetOnDiskDictionaryMap(storage.get()).empty());
|
||||
}
|
||||
|
||||
TEST_F(SharedDictionaryManagerOnDiskTest,
|
||||
TEST_P(SharedDictionaryManagerOnDiskTest,
|
||||
MismatchingEntryDeletionCanBeTriggeredOnlyOnce) {
|
||||
std::unique_ptr<SharedDictionaryManager> manager =
|
||||
CreateSharedDictionaryManager();
|
||||
@ -1894,7 +1937,7 @@ TEST_F(SharedDictionaryManagerOnDiskTest,
|
||||
EXPECT_TRUE(DiskCacheEntryExists(manager.get(), entry_key));
|
||||
}
|
||||
|
||||
TEST_F(SharedDictionaryManagerOnDiskTest,
|
||||
TEST_P(SharedDictionaryManagerOnDiskTest,
|
||||
MismatchingEntryDeletionWritingEntryMustNotBeDeleted) {
|
||||
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
|
||||
kSite);
|
||||
@ -1908,10 +1951,11 @@ TEST_F(SharedDictionaryManagerOnDiskTest,
|
||||
scoped_refptr<net::HttpResponseHeaders> headers =
|
||||
net::HttpResponseHeaders::TryToCreate(base::StrCat(
|
||||
{"HTTP/1.1 200 OK\n", shared_dictionary::kUseAsDictionaryHeaderName,
|
||||
": match=\"/p*\"\n\n"}));
|
||||
": match=\"/p*\"\n", kDefaultCacheControlHeader, "\n"}));
|
||||
ASSERT_TRUE(headers);
|
||||
scoped_refptr<SharedDictionaryWriter> writer = storage->MaybeCreateWriter(
|
||||
GURL("https://target1.test/d"), base::Time::Now(), *headers,
|
||||
GURL("https://target1.test/d"), /*request_time=*/base::Time::Now(),
|
||||
/*response_time=*/base::Time::Now(), *headers,
|
||||
/*was_fetched_via_cache=*/false, DummyAccessAllowedCheckCallback());
|
||||
ASSERT_TRUE(writer);
|
||||
writer->Append(kTestData1.c_str(), kTestData1.size());
|
||||
@ -1957,7 +2001,7 @@ TEST_F(SharedDictionaryManagerOnDiskTest,
|
||||
"/p*", DictionaryUrlIs("https://target1.test/d"))))));
|
||||
}
|
||||
|
||||
TEST_F(SharedDictionaryManagerOnDiskTest,
|
||||
TEST_P(SharedDictionaryManagerOnDiskTest,
|
||||
MismatchingEntryDeletionWritingDiskCacheEntryMustNotBeDeleted) {
|
||||
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
|
||||
kSite);
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
#include "services/network/shared_dictionary/shared_dictionary_manager.h"
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/feature_list.h"
|
||||
@ -14,6 +16,7 @@
|
||||
#include "base/strings/strcat.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/test/bind.h"
|
||||
#include "base/test/scoped_feature_list.h"
|
||||
#include "base/test/task_environment.h"
|
||||
#include "base/test/test_future.h"
|
||||
#include "base/time/time.h"
|
||||
@ -76,6 +79,29 @@ const net::SHA256HashValue kTestData2Hash = {
|
||||
|
||||
const size_t kCacheMaxCount = 100;
|
||||
|
||||
// Default cache control header for dictionary entries which expires in 30 days.
|
||||
const std::string kDefaultCacheControlHeader =
|
||||
"cache-control: max-age=2592000\n";
|
||||
|
||||
std::string ToString(TestManagerType type) {
|
||||
switch (type) {
|
||||
case TestManagerType::kInMemory:
|
||||
return "InMemory";
|
||||
case TestManagerType::kOnDisk:
|
||||
return "OnDisk";
|
||||
}
|
||||
}
|
||||
|
||||
std::string ToString(
|
||||
features::CompressionDictionaryTransportBackendVersion version) {
|
||||
switch (version) {
|
||||
case features::CompressionDictionaryTransportBackendVersion::kV1:
|
||||
return "V1";
|
||||
case features::CompressionDictionaryTransportBackendVersion::kV2:
|
||||
return "V2";
|
||||
}
|
||||
}
|
||||
|
||||
void CheckDiskCacheEntryDataEquals(
|
||||
SharedDictionaryDiskCache& disk_cache,
|
||||
const base::UnguessableToken& disk_cache_key_token,
|
||||
@ -104,18 +130,22 @@ void CheckDiskCacheEntryDataEquals(
|
||||
read_buffer->size()));
|
||||
}
|
||||
|
||||
void WriteDictionary(SharedDictionaryStorage* storage,
|
||||
const GURL& dictionary_url,
|
||||
const std::string& match,
|
||||
const std::vector<std::string>& data_list,
|
||||
const std::string& additional_options = std::string()) {
|
||||
void WriteDictionary(
|
||||
SharedDictionaryStorage* storage,
|
||||
const GURL& dictionary_url,
|
||||
const std::string& match,
|
||||
const std::vector<std::string>& data_list,
|
||||
const std::string& additional_options = std::string(),
|
||||
const std::string& additional_header = kDefaultCacheControlHeader) {
|
||||
scoped_refptr<net::HttpResponseHeaders> headers =
|
||||
net::HttpResponseHeaders::TryToCreate(base::StrCat(
|
||||
{"HTTP/1.1 200 OK\n", shared_dictionary::kUseAsDictionaryHeaderName,
|
||||
": match=\"/", match, "\"", additional_options, "\n\n"}));
|
||||
": match=\"/", match, "\"", additional_options, "\n",
|
||||
additional_header, "\n"}));
|
||||
ASSERT_TRUE(headers);
|
||||
scoped_refptr<SharedDictionaryWriter> writer = storage->MaybeCreateWriter(
|
||||
dictionary_url, base::Time::Now(), *headers,
|
||||
dictionary_url, /*request_time=*/base::Time::Now(),
|
||||
/*response_time=*/base::Time::Now(), *headers,
|
||||
/*was_fetched_via_cache=*/false,
|
||||
/*access_allowed_check_callback=*/base::BindOnce([]() { return true; }));
|
||||
ASSERT_TRUE(writer);
|
||||
@ -126,8 +156,11 @@ void WriteDictionary(SharedDictionaryStorage* storage,
|
||||
}
|
||||
|
||||
base::TimeDelta GetDefaultExpiration() {
|
||||
return base::FeatureList::IsEnabled(
|
||||
network::features::kCompressionDictionaryTransport)
|
||||
if (features::kCompressionDictionaryTransportBackendVersion.Get() ==
|
||||
features::CompressionDictionaryTransportBackendVersion::kV2) {
|
||||
return base::Seconds(2592000); // See kDefaultCacheControlHeader
|
||||
}
|
||||
return base::FeatureList::IsEnabled(features::kCompressionDictionaryTransport)
|
||||
? shared_dictionary::kDefaultExpiration
|
||||
: shared_dictionary::kMaxExpirationForOriginTrial;
|
||||
}
|
||||
@ -136,9 +169,19 @@ base::TimeDelta GetDefaultExpiration() {
|
||||
|
||||
class SharedDictionaryManagerTest
|
||||
: public ::testing::Test,
|
||||
public testing::WithParamInterface<TestManagerType> {
|
||||
public ::testing::WithParamInterface<
|
||||
std::tuple<TestManagerType,
|
||||
features::CompressionDictionaryTransportBackendVersion>> {
|
||||
public:
|
||||
SharedDictionaryManagerTest() = default;
|
||||
SharedDictionaryManagerTest() {
|
||||
std::vector<base::test::FeatureRefAndParams> enabled_features;
|
||||
enabled_features.emplace_back(base::test::FeatureRefAndParams(
|
||||
features::kCompressionDictionaryTransportBackend,
|
||||
{{features::kCompressionDictionaryTransportBackendVersion.name,
|
||||
features::kCompressionDictionaryTransportBackendVersion.GetName(
|
||||
GetVersion())}}));
|
||||
scoped_feature_list_.InitWithFeaturesAndParameters(enabled_features, {});
|
||||
}
|
||||
~SharedDictionaryManagerTest() override = default;
|
||||
|
||||
SharedDictionaryManagerTest(const SharedDictionaryManagerTest&) = delete;
|
||||
@ -146,7 +189,7 @@ class SharedDictionaryManagerTest
|
||||
delete;
|
||||
|
||||
void SetUp() override {
|
||||
if (GetParam() == TestManagerType::kOnDisk) {
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
ASSERT_TRUE(tmp_directory_.CreateUniqueTempDir());
|
||||
database_path_ = tmp_directory_.GetPath().Append(FILE_PATH_LITERAL("db"));
|
||||
cache_directory_path_ =
|
||||
@ -154,14 +197,18 @@ class SharedDictionaryManagerTest
|
||||
}
|
||||
}
|
||||
void TearDown() override {
|
||||
if (GetParam() == TestManagerType::kOnDisk) {
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
FlushCacheTasks();
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
TestManagerType GetManagerType() const { return std::get<0>(GetParam()); }
|
||||
features::CompressionDictionaryTransportBackendVersion GetVersion() const {
|
||||
return std::get<1>(GetParam());
|
||||
}
|
||||
std::unique_ptr<SharedDictionaryManager> CreateSharedDictionaryManager() {
|
||||
switch (GetParam()) {
|
||||
switch (GetManagerType()) {
|
||||
case TestManagerType::kInMemory:
|
||||
return SharedDictionaryManager::CreateInMemory(/*cache_max_size=*/0,
|
||||
kCacheMaxCount);
|
||||
@ -217,19 +264,22 @@ class SharedDictionaryManagerTest
|
||||
base::ScopedTempDir tmp_directory_;
|
||||
base::FilePath database_path_;
|
||||
base::FilePath cache_directory_path_;
|
||||
base::test::ScopedFeatureList scoped_feature_list_;
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
All,
|
||||
SharedDictionaryManagerTest,
|
||||
testing::ValuesIn({TestManagerType::kInMemory, TestManagerType::kOnDisk}),
|
||||
[](const testing::TestParamInfo<TestManagerType>& info) {
|
||||
switch (info.param) {
|
||||
case TestManagerType::kInMemory:
|
||||
return "InMemory";
|
||||
case TestManagerType::kOnDisk:
|
||||
return "OnDisk";
|
||||
}
|
||||
::testing::Combine(
|
||||
testing::Values(TestManagerType::kInMemory, TestManagerType::kOnDisk),
|
||||
testing::Values(
|
||||
features::CompressionDictionaryTransportBackendVersion::kV1,
|
||||
features::CompressionDictionaryTransportBackendVersion::kV2)),
|
||||
[](const testing::TestParamInfo<std::tuple<
|
||||
TestManagerType,
|
||||
features::CompressionDictionaryTransportBackendVersion>>& info) {
|
||||
return ToString(std::get<0>(info.param)) + "_" +
|
||||
ToString(std::get<1>(info.param));
|
||||
});
|
||||
|
||||
TEST_P(SharedDictionaryManagerTest, SameStorageForSameIsolationKey) {
|
||||
@ -285,7 +335,7 @@ TEST_P(SharedDictionaryManagerTest, CachedStorage) {
|
||||
// Write the test data to the dictionary.
|
||||
WriteDictionary(storage.get(), GURL("https://origin1.test/dict"), "p*",
|
||||
{"Hello"});
|
||||
if (GetParam() == TestManagerType::kOnDisk) {
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
FlushCacheTasks();
|
||||
}
|
||||
|
||||
@ -311,7 +361,7 @@ TEST_P(SharedDictionaryManagerTest, CachedStorageEvicted) {
|
||||
// Write the test data to the dictionary.
|
||||
WriteDictionary(storage.get(), GURL("https://origin1.test/dict"), "p*",
|
||||
{"Hello"});
|
||||
if (GetParam() == TestManagerType::kOnDisk) {
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
FlushCacheTasks();
|
||||
}
|
||||
|
||||
@ -364,7 +414,7 @@ TEST_P(SharedDictionaryManagerTest,
|
||||
// Write the test data to the dictionary.
|
||||
WriteDictionary(storage.get(), GURL("https://origin1.test/dict"), "p*",
|
||||
{"Hello"});
|
||||
if (GetParam() == TestManagerType::kOnDisk) {
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
FlushCacheTasks();
|
||||
}
|
||||
|
||||
@ -395,7 +445,7 @@ TEST_P(SharedDictionaryManagerTest,
|
||||
// Write the test data to the dictionary.
|
||||
WriteDictionary(storage.get(), GURL("https://origin1.test/dict"), "p*",
|
||||
{"Hello"});
|
||||
if (GetParam() == TestManagerType::kOnDisk) {
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
FlushCacheTasks();
|
||||
}
|
||||
|
||||
@ -421,7 +471,7 @@ TEST_P(SharedDictionaryManagerTest,
|
||||
// Write the test data to the dictionary.
|
||||
WriteDictionary(storage.get(), GURL("https://origin1.test/dict"), "p*",
|
||||
{"Hello"});
|
||||
if (GetParam() == TestManagerType::kOnDisk) {
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
FlushCacheTasks();
|
||||
}
|
||||
|
||||
@ -452,7 +502,7 @@ TEST_P(SharedDictionaryManagerTest,
|
||||
// Write the test data to the dictionary.
|
||||
WriteDictionary(storage.get(), GURL("https://origin1.test/dict"), "p*",
|
||||
{"Hello"});
|
||||
if (GetParam() == TestManagerType::kOnDisk) {
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
FlushCacheTasks();
|
||||
}
|
||||
|
||||
@ -486,7 +536,9 @@ TEST_P(SharedDictionaryManagerTest, NoWriterForNoUseAsDictionaryHeader) {
|
||||
net::HttpResponseHeaders::TryToCreate("HTTP/1.1 200 OK\n");
|
||||
ASSERT_TRUE(headers);
|
||||
scoped_refptr<SharedDictionaryWriter> writer = storage->MaybeCreateWriter(
|
||||
GURL("https://origin1.test/testfile.txt"), base::Time::Now(), *headers,
|
||||
GURL("https://origin1.test/testfile.txt"),
|
||||
/*request_time=*/base::Time::Now(), /*response_time=*/base::Time::Now(),
|
||||
*headers,
|
||||
/*was_fetched_via_cache=*/false,
|
||||
/*access_allowed_check_callback=*/base::BindOnce([]() { return true; }));
|
||||
EXPECT_FALSE(writer);
|
||||
@ -525,13 +577,6 @@ TEST_P(SharedDictionaryManagerTest, WriterForUseAsDictionaryHeader) {
|
||||
// Token `match` value is not supported.
|
||||
{"match=test", false},
|
||||
|
||||
// Valid `expires` value.
|
||||
{"match=\"test\", expires=1000", true},
|
||||
// List `expires` value is not supported.
|
||||
{"match=\"test\", expires=(1000 2000)", false},
|
||||
// String `expires` value is not supported.
|
||||
{"match=\"test\", expires=PI", false},
|
||||
|
||||
// We support `raw` type.
|
||||
{"match=\"test\", type=raw", true},
|
||||
{"match=\"test\", type=(raw)", true},
|
||||
@ -548,10 +593,13 @@ TEST_P(SharedDictionaryManagerTest, WriterForUseAsDictionaryHeader) {
|
||||
scoped_refptr<net::HttpResponseHeaders> headers =
|
||||
net::HttpResponseHeaders::TryToCreate(base::StrCat(
|
||||
{"HTTP/1.1 200 OK\n", shared_dictionary::kUseAsDictionaryHeaderName,
|
||||
": ", testcase.header_string, "\n\n"}));
|
||||
": ", testcase.header_string, "\n", kDefaultCacheControlHeader,
|
||||
"\n"}));
|
||||
ASSERT_TRUE(headers);
|
||||
scoped_refptr<SharedDictionaryWriter> writer = storage->MaybeCreateWriter(
|
||||
GURL("https://origin1.test/testfile.txt"), base::Time::Now(), *headers,
|
||||
GURL("https://origin1.test/testfile.txt"),
|
||||
/*request_time=*/base::Time::Now(), /*response_time=*/base::Time::Now(),
|
||||
*headers,
|
||||
/*was_fetched_via_cache=*/false,
|
||||
/*access_allowed_check_callback=*/base::BindOnce([]() {
|
||||
return true;
|
||||
@ -560,6 +608,114 @@ TEST_P(SharedDictionaryManagerTest, WriterForUseAsDictionaryHeader) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(SharedDictionaryManagerTest,
|
||||
WriterForUseAsDictionaryHeaderExpiresOption) {
|
||||
// We don't support `expires` option in the V2 backend.
|
||||
if (GetVersion() ==
|
||||
features::CompressionDictionaryTransportBackendVersion::kV2) {
|
||||
return;
|
||||
}
|
||||
std::unique_ptr<SharedDictionaryManager> manager =
|
||||
CreateSharedDictionaryManager();
|
||||
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl1),
|
||||
kSite1);
|
||||
|
||||
scoped_refptr<SharedDictionaryStorage> storage =
|
||||
manager->GetStorage(isolation_key);
|
||||
ASSERT_TRUE(storage);
|
||||
|
||||
struct {
|
||||
std::string header_string;
|
||||
bool expect_success;
|
||||
} kTestCases[] = {
|
||||
// Valid `expires` value.
|
||||
{"match=\"test\", expires=1000", true},
|
||||
// List `expires` value is not supported.
|
||||
{"match=\"test\", expires=(1000 2000)", false},
|
||||
// String `expires` value is not supported.
|
||||
{"match=\"test\", expires=PI", false},
|
||||
};
|
||||
for (const auto& testcase : kTestCases) {
|
||||
SCOPED_TRACE(base::StringPrintf("header_string: %s",
|
||||
testcase.header_string.c_str()));
|
||||
scoped_refptr<net::HttpResponseHeaders> headers =
|
||||
net::HttpResponseHeaders::TryToCreate(base::StrCat(
|
||||
{"HTTP/1.1 200 OK\n", shared_dictionary::kUseAsDictionaryHeaderName,
|
||||
": ", testcase.header_string, "\n\n"}));
|
||||
ASSERT_TRUE(headers);
|
||||
scoped_refptr<SharedDictionaryWriter> writer = storage->MaybeCreateWriter(
|
||||
GURL("https://origin1.test/testfile.txt"),
|
||||
/*request_time=*/base::Time::Now(), /*response_time=*/base::Time::Now(),
|
||||
*headers,
|
||||
/*was_fetched_via_cache=*/false,
|
||||
/*access_allowed_check_callback=*/base::BindOnce([]() {
|
||||
return true;
|
||||
}));
|
||||
EXPECT_EQ(testcase.expect_success, !!writer);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(SharedDictionaryManagerTest, DictionaryLifetimeFromCacheControlHeader) {
|
||||
// We don't use the cache conterol header for the lifetime of the dictionary
|
||||
// in the V1 backend.
|
||||
if (GetVersion() ==
|
||||
features::CompressionDictionaryTransportBackendVersion::kV1) {
|
||||
return;
|
||||
}
|
||||
std::unique_ptr<SharedDictionaryManager> manager =
|
||||
CreateSharedDictionaryManager();
|
||||
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl1),
|
||||
kSite1);
|
||||
|
||||
scoped_refptr<SharedDictionaryStorage> storage =
|
||||
manager->GetStorage(isolation_key);
|
||||
ASSERT_TRUE(storage);
|
||||
|
||||
struct {
|
||||
std::string header_string;
|
||||
std::optional<base::TimeDelta> expected_expiration;
|
||||
} kTestCases[] = {
|
||||
// Empty
|
||||
{"", std::nullopt},
|
||||
{"cache-control:max-age=100", base::Seconds(100)},
|
||||
{"cache-control:max-age=100, stale-while-revalidate=50",
|
||||
base::Seconds(150)},
|
||||
{"cache-control:max-age=100\nage:10", base::Seconds(90)},
|
||||
|
||||
};
|
||||
for (const auto& testcase : kTestCases) {
|
||||
SCOPED_TRACE(base::StringPrintf("header_string: %s",
|
||||
testcase.header_string.c_str()));
|
||||
scoped_refptr<net::HttpResponseHeaders> headers =
|
||||
net::HttpResponseHeaders::TryToCreate(base::StrCat(
|
||||
{"HTTP/1.1 200 OK\n", shared_dictionary::kUseAsDictionaryHeaderName,
|
||||
": match=\"test\"\n", testcase.header_string, "\n"}));
|
||||
ASSERT_TRUE(headers);
|
||||
const base::Time request_time = base::Time::Now();
|
||||
const base::Time response_time = request_time;
|
||||
scoped_refptr<SharedDictionaryWriter> writer = storage->MaybeCreateWriter(
|
||||
GURL("https://origin1.test/testfile.txt"), request_time, response_time,
|
||||
*headers,
|
||||
/*was_fetched_via_cache=*/false,
|
||||
/*access_allowed_check_callback=*/base::BindOnce([]() {
|
||||
return true;
|
||||
}));
|
||||
EXPECT_EQ(!!testcase.expected_expiration, !!writer);
|
||||
if (!writer) {
|
||||
continue;
|
||||
}
|
||||
writer->Append(kTestData1.c_str(), kTestData1.size());
|
||||
writer->Finish();
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
FlushCacheTasks();
|
||||
}
|
||||
std::vector<network::mojom::SharedDictionaryInfoPtr> result =
|
||||
GetSharedDictionaryInfo(manager.get(), isolation_key);
|
||||
ASSERT_EQ(1u, result.size());
|
||||
EXPECT_EQ(*testcase.expected_expiration, result[0]->expiration);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(SharedDictionaryManagerTest, AccessAllowedCheckReturnTrue) {
|
||||
std::unique_ptr<SharedDictionaryManager> manager =
|
||||
CreateSharedDictionaryManager();
|
||||
@ -572,11 +728,13 @@ TEST_P(SharedDictionaryManagerTest, AccessAllowedCheckReturnTrue) {
|
||||
scoped_refptr<net::HttpResponseHeaders> headers =
|
||||
net::HttpResponseHeaders::TryToCreate(base::StrCat(
|
||||
{"HTTP/1.1 200 OK\n", shared_dictionary::kUseAsDictionaryHeaderName,
|
||||
": match=\"test\"\n\n"}));
|
||||
": match=\"test\"\ncache-control:max-age=100\n\n"}));
|
||||
ASSERT_TRUE(headers);
|
||||
bool callback_called = false;
|
||||
scoped_refptr<SharedDictionaryWriter> writer = storage->MaybeCreateWriter(
|
||||
GURL("https://origin1.test/testfile.txt"), base::Time::Now(), *headers,
|
||||
GURL("https://origin1.test/testfile.txt"),
|
||||
/*request_time=*/base::Time::Now(), /*response_time=*/base::Time::Now(),
|
||||
*headers,
|
||||
/*was_fetched_via_cache=*/false,
|
||||
/*access_allowed_check_callback=*/base::BindLambdaForTesting([&]() {
|
||||
callback_called = true;
|
||||
@ -598,11 +756,13 @@ TEST_P(SharedDictionaryManagerTest, AccessAllowedCheckReturnFalse) {
|
||||
scoped_refptr<net::HttpResponseHeaders> headers =
|
||||
net::HttpResponseHeaders::TryToCreate(base::StrCat(
|
||||
{"HTTP/1.1 200 OK\n", shared_dictionary::kUseAsDictionaryHeaderName,
|
||||
": match=\"test\"\n\n"}));
|
||||
": match=\"test\"\ncache-control:max-age=100\n\n"}));
|
||||
ASSERT_TRUE(headers);
|
||||
bool callback_called = false;
|
||||
scoped_refptr<SharedDictionaryWriter> writer = storage->MaybeCreateWriter(
|
||||
GURL("https://origin1.test/testfile.txt"), base::Time::Now(), *headers,
|
||||
GURL("https://origin1.test/testfile.txt"),
|
||||
/*request_time=*/base::Time::Now(), /*response_time=*/base::Time::Now(),
|
||||
*headers,
|
||||
/*was_fetched_via_cache=*/false,
|
||||
/*access_allowed_check_callback=*/base::BindLambdaForTesting([&]() {
|
||||
callback_called = true;
|
||||
@ -626,10 +786,11 @@ TEST_P(SharedDictionaryManagerTest, SameDictionaryFromDiskCache) {
|
||||
scoped_refptr<net::HttpResponseHeaders> headers =
|
||||
net::HttpResponseHeaders::TryToCreate(base::StrCat(
|
||||
{"HTTP/1.1 200 OK\n", shared_dictionary::kUseAsDictionaryHeaderName,
|
||||
": match=\"test\"\n\n"}));
|
||||
": match=\"test\"\ncache-control:max-age=100\n\n"}));
|
||||
ASSERT_TRUE(headers);
|
||||
scoped_refptr<SharedDictionaryWriter> writer1 = storage->MaybeCreateWriter(
|
||||
dictionary_url, response_time, *headers,
|
||||
dictionary_url, /*request_time=*/response_time,
|
||||
/*response_time=*/response_time, *headers,
|
||||
/*was_fetched_via_cache=*/false,
|
||||
/*access_allowed_check_callback=*/base::BindLambdaForTesting([&]() {
|
||||
return true;
|
||||
@ -637,11 +798,12 @@ TEST_P(SharedDictionaryManagerTest, SameDictionaryFromDiskCache) {
|
||||
ASSERT_TRUE(writer1);
|
||||
writer1->Append(kTestData1.c_str(), kTestData1.size());
|
||||
writer1->Finish();
|
||||
if (GetParam() == TestManagerType::kOnDisk) {
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
FlushCacheTasks();
|
||||
}
|
||||
scoped_refptr<SharedDictionaryWriter> writer2 = storage->MaybeCreateWriter(
|
||||
dictionary_url, response_time, *headers,
|
||||
dictionary_url, /*request_time=*/response_time,
|
||||
/*response_time=*/response_time, *headers,
|
||||
/*was_fetched_via_cache=*/true,
|
||||
/*access_allowed_check_callback=*/base::BindLambdaForTesting([&]() {
|
||||
return true;
|
||||
@ -665,10 +827,11 @@ TEST_P(SharedDictionaryManagerTest, DifferentDictionaryFromDiskCache) {
|
||||
scoped_refptr<net::HttpResponseHeaders> headers1 =
|
||||
net::HttpResponseHeaders::TryToCreate(base::StrCat(
|
||||
{"HTTP/1.1 200 OK\n", shared_dictionary::kUseAsDictionaryHeaderName,
|
||||
": match=\"test1\"\n\n"}));
|
||||
": match=\"test1\"\ncache-control:max-age=100\n\n"}));
|
||||
ASSERT_TRUE(headers1);
|
||||
scoped_refptr<SharedDictionaryWriter> writer1 = storage->MaybeCreateWriter(
|
||||
dictionary_url, response_time, *headers1,
|
||||
dictionary_url, /*request_time=*/response_time,
|
||||
/*response_time=*/response_time, *headers1,
|
||||
/*was_fetched_via_cache=*/false,
|
||||
/*access_allowed_check_callback=*/base::BindLambdaForTesting([&]() {
|
||||
return true;
|
||||
@ -676,17 +839,18 @@ TEST_P(SharedDictionaryManagerTest, DifferentDictionaryFromDiskCache) {
|
||||
ASSERT_TRUE(writer1);
|
||||
writer1->Append(kTestData1.c_str(), kTestData1.size());
|
||||
writer1->Finish();
|
||||
if (GetParam() == TestManagerType::kOnDisk) {
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
FlushCacheTasks();
|
||||
}
|
||||
|
||||
scoped_refptr<net::HttpResponseHeaders> headers2 =
|
||||
net::HttpResponseHeaders::TryToCreate(base::StrCat(
|
||||
{"HTTP/1.1 200 OK\n", shared_dictionary::kUseAsDictionaryHeaderName,
|
||||
": match=\"test2\"\n\n"}));
|
||||
": match=\"test2\"\ncache-control:max-age=100\n\n"}));
|
||||
ASSERT_TRUE(headers1);
|
||||
scoped_refptr<SharedDictionaryWriter> writer2 = storage->MaybeCreateWriter(
|
||||
dictionary_url, response_time, *headers2,
|
||||
dictionary_url, /*request_time=*/response_time,
|
||||
/*response_time=*/response_time, *headers2,
|
||||
/*was_fetched_via_cache=*/true,
|
||||
/*access_allowed_check_callback=*/base::BindLambdaForTesting([&]() {
|
||||
return true;
|
||||
@ -706,7 +870,7 @@ TEST_P(SharedDictionaryManagerTest, WriteAndGetDictionary) {
|
||||
ASSERT_TRUE(storage);
|
||||
WriteDictionary(storage.get(), GURL("https://origin1.test/dict"), "testfile*",
|
||||
{"hello world"});
|
||||
if (GetParam() == TestManagerType::kOnDisk) {
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
FlushCacheTasks();
|
||||
}
|
||||
|
||||
@ -743,7 +907,7 @@ TEST_P(SharedDictionaryManagerTest, WriteAndReadDictionary) {
|
||||
net::SHA256HashValue sha256;
|
||||
secure_hash->Finish(sha256.data, sizeof(sha256.data));
|
||||
|
||||
if (GetParam() == TestManagerType::kOnDisk) {
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
FlushCacheTasks();
|
||||
}
|
||||
|
||||
@ -755,7 +919,7 @@ TEST_P(SharedDictionaryManagerTest, WriteAndReadDictionary) {
|
||||
EXPECT_EQ(sha256, dict->hash());
|
||||
|
||||
// Read and check the dictionary binary.
|
||||
switch (GetParam()) {
|
||||
switch (GetManagerType()) {
|
||||
case TestManagerType::kInMemory: {
|
||||
EXPECT_EQ(net::OK,
|
||||
dict->ReadAll(base::BindOnce([](int rv) { NOTREACHED(); })));
|
||||
@ -776,7 +940,7 @@ TEST_P(SharedDictionaryManagerTest, WriteAndReadDictionary) {
|
||||
ASSERT_TRUE(dict->data());
|
||||
EXPECT_EQ(data1 + data2, std::string(dict->data()->data(), dict->size()));
|
||||
|
||||
switch (GetParam()) {
|
||||
switch (GetManagerType()) {
|
||||
case TestManagerType::kInMemory: {
|
||||
// Check the internal state of SharedDictionaryStorageInMemory.
|
||||
const auto& dictionary_map = GetInMemoryDictionaryMap(storage.get());
|
||||
@ -839,7 +1003,7 @@ TEST_P(SharedDictionaryManagerTest, OverrideDictionary) {
|
||||
// Write a test dictionary.
|
||||
WriteDictionary(storage.get(), url1, match, {data1});
|
||||
|
||||
if (GetParam() == TestManagerType::kOnDisk) {
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
FlushCacheTasks();
|
||||
}
|
||||
|
||||
@ -851,7 +1015,7 @@ TEST_P(SharedDictionaryManagerTest, OverrideDictionary) {
|
||||
// Write another dictionary with same `match`.
|
||||
WriteDictionary(storage.get(), url2, match, {data2});
|
||||
|
||||
if (GetParam() == TestManagerType::kOnDisk) {
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
FlushCacheTasks();
|
||||
}
|
||||
|
||||
@ -898,7 +1062,7 @@ TEST_P(SharedDictionaryManagerTest,
|
||||
WriteDictionary(storage.get(), GURL("https://origin3.test/d1"), "p3*",
|
||||
{kTestData1});
|
||||
|
||||
if (GetParam() == TestManagerType::kOnDisk) {
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
FlushCacheTasks();
|
||||
}
|
||||
|
||||
@ -906,7 +1070,7 @@ TEST_P(SharedDictionaryManagerTest,
|
||||
|
||||
manager->SetCacheMaxSize(/*cache_max_size=*/kTestData1.size() * 2);
|
||||
|
||||
if (GetParam() == TestManagerType::kOnDisk) {
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
FlushCacheTasks();
|
||||
}
|
||||
|
||||
@ -935,7 +1099,7 @@ TEST_P(SharedDictionaryManagerTest, CacheEvictionZeroMaxSizeCountExceeded) {
|
||||
storage.get(),
|
||||
GURL(base::StringPrintf("https://origin.test/d%03" PRIuS, i)),
|
||||
base::StringPrintf("p%03" PRIuS, i), {kTestData1});
|
||||
if (GetParam() == TestManagerType::kOnDisk) {
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
FlushCacheTasks();
|
||||
}
|
||||
task_environment_.FastForwardBy(base::Seconds(1));
|
||||
@ -962,7 +1126,7 @@ TEST_P(SharedDictionaryManagerTest, CacheEvictionZeroMaxSizeCountExceeded) {
|
||||
kCacheMaxCount)),
|
||||
base::StringPrintf("p%03" PRIuS, kCacheMaxCount),
|
||||
{kTestData1});
|
||||
if (GetParam() == TestManagerType::kOnDisk) {
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
FlushCacheTasks();
|
||||
}
|
||||
task_environment_.FastForwardBy(base::Seconds(1));
|
||||
@ -1009,7 +1173,7 @@ TEST_P(SharedDictionaryManagerTest,
|
||||
{kTestData1});
|
||||
WriteDictionary(storage2.get(), GURL("https://origin2.test/d2"), "p2*",
|
||||
{kTestData1});
|
||||
if (GetParam() == TestManagerType::kOnDisk) {
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
FlushCacheTasks();
|
||||
}
|
||||
EXPECT_TRUE(storage1->GetDictionarySync(GURL("https://origin1.test/p1?")));
|
||||
@ -1018,7 +1182,7 @@ TEST_P(SharedDictionaryManagerTest,
|
||||
task_environment_.FastForwardBy(base::Seconds(1));
|
||||
WriteDictionary(storage3.get(), GURL("https://origin3.test/d1"), "p3*",
|
||||
{kTestData1});
|
||||
if (GetParam() == TestManagerType::kOnDisk) {
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
FlushCacheTasks();
|
||||
}
|
||||
EXPECT_FALSE(storage1->GetDictionarySync(GURL("https://origin1.test/p1?")));
|
||||
@ -1057,7 +1221,7 @@ TEST_P(SharedDictionaryManagerTest, CacheEvictionAfterUpdatingLastUsedTime) {
|
||||
WriteDictionary(storage2.get(), GURL("https://origin2.test/d2"), "p2*",
|
||||
{kTestData1});
|
||||
|
||||
if (GetParam() == TestManagerType::kOnDisk) {
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
FlushCacheTasks();
|
||||
}
|
||||
|
||||
@ -1072,7 +1236,7 @@ TEST_P(SharedDictionaryManagerTest, CacheEvictionAfterUpdatingLastUsedTime) {
|
||||
// kTestData1.size() * 2.7 (3 * 0.9).
|
||||
manager->SetCacheMaxSize(/*cache_max_size=*/kTestData1.size() * 3);
|
||||
|
||||
if (GetParam() == TestManagerType::kOnDisk) {
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
FlushCacheTasks();
|
||||
}
|
||||
|
||||
@ -1108,7 +1272,7 @@ TEST_P(SharedDictionaryManagerTest, CacheEvictionPerSiteSizeExceeded) {
|
||||
{kTestData1});
|
||||
WriteDictionary(storage3.get(), GURL("https://origin3.test/d"), "p*",
|
||||
{kTestData1});
|
||||
if (GetParam() == TestManagerType::kOnDisk) {
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
FlushCacheTasks();
|
||||
}
|
||||
EXPECT_TRUE(storage1->GetDictionarySync(GURL("https://origin1.test/p?")));
|
||||
@ -1120,7 +1284,7 @@ TEST_P(SharedDictionaryManagerTest, CacheEvictionPerSiteSizeExceeded) {
|
||||
|
||||
WriteDictionary(storage1.get(), GURL("https://origin4.test/d"), "p*",
|
||||
{kTestData1});
|
||||
if (GetParam() == TestManagerType::kOnDisk) {
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
FlushCacheTasks();
|
||||
}
|
||||
EXPECT_FALSE(storage1->GetDictionarySync(GURL("https://origin1.test/p?")));
|
||||
@ -1145,7 +1309,7 @@ TEST_P(SharedDictionaryManagerTest,
|
||||
storage.get(),
|
||||
GURL(base::StringPrintf("https://origin.test/d%03" PRIuS, i)),
|
||||
base::StringPrintf("p%03" PRIuS, i), {kTestData1});
|
||||
if (GetParam() == TestManagerType::kOnDisk) {
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
FlushCacheTasks();
|
||||
}
|
||||
task_environment_.FastForwardBy(base::Seconds(1));
|
||||
@ -1163,7 +1327,7 @@ TEST_P(SharedDictionaryManagerTest,
|
||||
cache_max_count_per_site)),
|
||||
base::StringPrintf("p%03" PRIuS, cache_max_count_per_site),
|
||||
{kTestData1});
|
||||
if (GetParam() == TestManagerType::kOnDisk) {
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
FlushCacheTasks();
|
||||
}
|
||||
task_environment_.FastForwardBy(base::Seconds(1));
|
||||
@ -1195,7 +1359,7 @@ TEST_P(SharedDictionaryManagerTest,
|
||||
storage.get(),
|
||||
GURL(base::StringPrintf("https://origin.test/d%03" PRIuS, i)),
|
||||
base::StringPrintf("p%03" PRIuS, i), {kTestData1});
|
||||
if (GetParam() == TestManagerType::kOnDisk) {
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
FlushCacheTasks();
|
||||
}
|
||||
task_environment_.FastForwardBy(base::Seconds(1));
|
||||
@ -1213,7 +1377,7 @@ TEST_P(SharedDictionaryManagerTest,
|
||||
cache_max_count_per_site)),
|
||||
base::StringPrintf("p%03" PRIuS, cache_max_count_per_site),
|
||||
{kTestData1});
|
||||
if (GetParam() == TestManagerType::kOnDisk) {
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
FlushCacheTasks();
|
||||
}
|
||||
task_environment_.FastForwardBy(base::Seconds(1));
|
||||
@ -1245,7 +1409,7 @@ TEST_P(SharedDictionaryManagerTest,
|
||||
storage.get(),
|
||||
GURL(base::StringPrintf("https://origin.test/d%03" PRIuS, i)),
|
||||
base::StringPrintf("p%03" PRIuS, i), {kTestData1});
|
||||
if (GetParam() == TestManagerType::kOnDisk) {
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
FlushCacheTasks();
|
||||
}
|
||||
task_environment_.FastForwardBy(base::Seconds(1));
|
||||
@ -1263,7 +1427,7 @@ TEST_P(SharedDictionaryManagerTest,
|
||||
cache_max_count_per_site)),
|
||||
base::StringPrintf("p%03" PRIuS, cache_max_count_per_site),
|
||||
{kTestData1, kTestData1});
|
||||
if (GetParam() == TestManagerType::kOnDisk) {
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
FlushCacheTasks();
|
||||
}
|
||||
task_environment_.FastForwardBy(base::Seconds(1));
|
||||
@ -1438,7 +1602,7 @@ TEST_P(SharedDictionaryManagerTest, ClearDataDoNotInvalidateActiveDictionary) {
|
||||
// Move the clock forward by 12 hours.
|
||||
task_environment_.FastForwardBy(base::Hours(12));
|
||||
|
||||
if (GetParam() == TestManagerType::kOnDisk) {
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
FlushCacheTasks();
|
||||
}
|
||||
|
||||
@ -1490,7 +1654,7 @@ TEST_P(SharedDictionaryManagerTest, ClearDataForIsolationKey) {
|
||||
WriteDictionary(storage2.get(), GURL("https://origin2.test/2"), "p2*",
|
||||
{kTestData1});
|
||||
|
||||
if (GetParam() == TestManagerType::kOnDisk) {
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
FlushCacheTasks();
|
||||
}
|
||||
|
||||
@ -1533,7 +1697,7 @@ TEST_P(SharedDictionaryManagerTest, GetUsageInfo) {
|
||||
WriteDictionary(storage2.get(), GURL("https://origin2.test/2"), "p2*",
|
||||
{kTestData2});
|
||||
|
||||
if (GetParam() == TestManagerType::kOnDisk) {
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
FlushCacheTasks();
|
||||
}
|
||||
|
||||
@ -1592,9 +1756,10 @@ TEST_P(SharedDictionaryManagerTest, GetSharedDictionaryInfo) {
|
||||
manager->GetStorage(isolation_key2);
|
||||
task_environment_.FastForwardBy(base::Seconds(1));
|
||||
WriteDictionary(storage2.get(), GURL("https://origin2.test/d"), "p*",
|
||||
{kTestData2}, /*additional_options=*/",expires=123456");
|
||||
{kTestData2}, /*additional_options=*/",expires=123456",
|
||||
/*additional_header=*/"cache-control:max-age=123456\n");
|
||||
|
||||
if (GetParam() == TestManagerType::kOnDisk) {
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
FlushCacheTasks();
|
||||
}
|
||||
|
||||
@ -1672,7 +1837,7 @@ TEST_P(SharedDictionaryManagerTest, GetTotalSizeAndOrigins) {
|
||||
WriteDictionary(storage2.get(), GURL("https://origin2.test/d"), "p*",
|
||||
{kTestData2});
|
||||
|
||||
if (GetParam() == TestManagerType::kOnDisk) {
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
FlushCacheTasks();
|
||||
}
|
||||
|
||||
@ -1704,14 +1869,16 @@ TEST_P(SharedDictionaryManagerTest, DeleteExpiredDictionariesOnGetDictionary) {
|
||||
scoped_refptr<SharedDictionaryStorage> storage =
|
||||
manager->GetStorage(isolation_key);
|
||||
WriteDictionary(storage.get(), GURL("https://origin.test/d1"), "p1*",
|
||||
{kTestData1}, /*additional_options=*/",expires=20");
|
||||
{kTestData1}, /*additional_options=*/",expires=20",
|
||||
/*additional_header=*/"cache-control:max-age=20\n");
|
||||
|
||||
task_environment_.FastForwardBy(base::Seconds(10));
|
||||
|
||||
WriteDictionary(storage.get(), GURL("https://origin.test/d1"), "p2*",
|
||||
{kTestData2}, /*additional_options=*/",expires=5");
|
||||
{kTestData2}, /*additional_options=*/",expires=5",
|
||||
/*additional_header=*/"cache-control:max-age=5\n");
|
||||
|
||||
if (GetParam() == TestManagerType::kOnDisk) {
|
||||
if (GetManagerType() == TestManagerType::kOnDisk) {
|
||||
FlushCacheTasks();
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "services/network/shared_dictionary/shared_dictionary_storage.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
#include "base/containers/contains.h"
|
||||
@ -18,7 +19,6 @@
|
||||
#include "services/network/public/cpp/features.h"
|
||||
#include "services/network/shared_dictionary/shared_dictionary_constants.h"
|
||||
#include "services/network/shared_dictionary/shared_dictionary_writer.h"
|
||||
#include "third_party/abseil-cpp/absl/types/optional.h"
|
||||
#include "url/gurl.h"
|
||||
#include "url/scheme_host_port.h"
|
||||
|
||||
@ -28,65 +28,94 @@ namespace {
|
||||
|
||||
constexpr std::string_view kDefaultTypeRaw = "raw";
|
||||
|
||||
class UseAsDictionaryHeaderInfo {
|
||||
class DictionaryHeaderInfo {
|
||||
public:
|
||||
UseAsDictionaryHeaderInfo(std::string match,
|
||||
absl::optional<base::TimeDelta> expiration,
|
||||
std::string type)
|
||||
DictionaryHeaderInfo(std::string match,
|
||||
std::optional<base::TimeDelta> expiration,
|
||||
std::string type)
|
||||
: match(std::move(match)),
|
||||
expiration(expiration),
|
||||
type(std::move(type)) {}
|
||||
~UseAsDictionaryHeaderInfo() = default;
|
||||
~DictionaryHeaderInfo() = default;
|
||||
|
||||
std::string match;
|
||||
absl::optional<base::TimeDelta> expiration;
|
||||
// TODO(crbug.com/1413922): Stop using std::optional when we remove V1 backend
|
||||
// support.
|
||||
std::optional<base::TimeDelta> expiration;
|
||||
std::string type;
|
||||
};
|
||||
|
||||
absl::optional<UseAsDictionaryHeaderInfo> ParseUseAsDictionaryHeaderInfo(
|
||||
const net::HttpResponseHeaders& headers) {
|
||||
std::optional<DictionaryHeaderInfo> ParseDictionaryHeaderInfo(
|
||||
const net::HttpResponseHeaders& headers,
|
||||
const base::Time request_time,
|
||||
const base::Time response_time) {
|
||||
std::string use_as_dictionary_header;
|
||||
if (!headers.GetNormalizedHeader(
|
||||
shared_dictionary::kUseAsDictionaryHeaderName,
|
||||
&use_as_dictionary_header)) {
|
||||
return absl::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
absl::optional<net::structured_headers::Dictionary> dictionary =
|
||||
std::optional<net::structured_headers::Dictionary> dictionary =
|
||||
net::structured_headers::ParseDictionary(use_as_dictionary_header);
|
||||
if (!dictionary) {
|
||||
return absl::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
absl::optional<std::string> match_value;
|
||||
absl::optional<base::TimeDelta> expires_value;
|
||||
// Don't use the value of `expires` in the `Use-As-Dictionary` response header
|
||||
// when V2 backend is enabled.
|
||||
const bool check_expires_dictionary_value =
|
||||
features::kCompressionDictionaryTransportBackendVersion.Get() ==
|
||||
features::CompressionDictionaryTransportBackendVersion::kV1;
|
||||
|
||||
std::optional<std::string> match_value;
|
||||
std::optional<base::TimeDelta> expires_value;
|
||||
std::string type_value = std::string(kDefaultTypeRaw);
|
||||
for (const auto& entry : dictionary.value()) {
|
||||
if (entry.first == shared_dictionary::kOptionNameMatch) {
|
||||
if ((entry.second.member.size() != 1u) ||
|
||||
!entry.second.member.front().item.is_string()) {
|
||||
return absl::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
match_value = entry.second.member.front().item.GetString();
|
||||
} else if (entry.first == shared_dictionary::kOptionNameExpires) {
|
||||
} else if (check_expires_dictionary_value &&
|
||||
entry.first == shared_dictionary::kOptionNameExpires) {
|
||||
if ((entry.second.member.size() != 1u) ||
|
||||
!entry.second.member.front().item.is_integer()) {
|
||||
return absl::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
expires_value =
|
||||
base::Seconds(entry.second.member.front().item.GetInteger());
|
||||
} else if (entry.first == shared_dictionary::kOptionNameType) {
|
||||
if ((entry.second.member.size() != 1u) ||
|
||||
!entry.second.member.front().item.is_token()) {
|
||||
return absl::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
type_value = entry.second.member.front().item.GetString();
|
||||
}
|
||||
}
|
||||
if (!match_value) {
|
||||
return absl::nullopt;
|
||||
|
||||
if (!check_expires_dictionary_value) {
|
||||
// Use the fressness lifetime caliculated from the response header.
|
||||
net::HttpResponseHeaders::FreshnessLifetimes lifetimes =
|
||||
headers.GetFreshnessLifetimes(response_time);
|
||||
// We calculate `expires_value` which is a delta from the response time to
|
||||
// the expiration time. So we get the age of the response on the response
|
||||
// time by setting `current_time` argument to `response_time`.
|
||||
base::TimeDelta age_on_response_time =
|
||||
headers.GetCurrentAge(request_time, response_time,
|
||||
/*current_time=*/response_time);
|
||||
// We can use `freshness + staleness - current_age` as the expiration time.
|
||||
expires_value =
|
||||
lifetimes.freshness + lifetimes.staleness - age_on_response_time;
|
||||
if (*expires_value <= base::TimeDelta()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
return UseAsDictionaryHeaderInfo(*match_value, std::move(expires_value),
|
||||
type_value);
|
||||
if (!match_value) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return DictionaryHeaderInfo(std::move(*match_value), std::move(expires_value),
|
||||
std::move(type_value));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@ -98,15 +127,18 @@ SharedDictionaryStorage::~SharedDictionaryStorage() = default;
|
||||
scoped_refptr<SharedDictionaryWriter>
|
||||
SharedDictionaryStorage::MaybeCreateWriter(
|
||||
const GURL& url,
|
||||
base::Time response_time,
|
||||
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) {
|
||||
absl::optional<UseAsDictionaryHeaderInfo> info =
|
||||
ParseUseAsDictionaryHeaderInfo(headers);
|
||||
std::optional<DictionaryHeaderInfo> info =
|
||||
ParseDictionaryHeaderInfo(headers, request_time, response_time);
|
||||
if (!info) {
|
||||
return nullptr;
|
||||
}
|
||||
// TODO(crubg.com/1413922) Stop using kDefaultExpiration when we remove V1
|
||||
// backend support.
|
||||
base::TimeDelta expiration = shared_dictionary::kDefaultExpiration;
|
||||
if (info->expiration) {
|
||||
expiration = *info->expiration;
|
||||
|
@ -54,7 +54,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) SharedDictionaryStorage
|
||||
// returns true,
|
||||
scoped_refptr<SharedDictionaryWriter> MaybeCreateWriter(
|
||||
const GURL& url,
|
||||
base::Time response_time,
|
||||
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);
|
||||
|
Reference in New Issue
Block a user