0

Remove V1 backend implementation of Compression Dictionary

We have completed the implementation of a new V2 backend for the
CompressionDictionaryTransport feature. And we don't need to keep the V1
backend. So this CL removes the V1 backend.

Bug: 1413922
Change-Id: Ifb8e59e9f617d67a9098adfce4f1b4f1b422d06b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5274982
Reviewed-by: Patrick Meenan <pmeenan@chromium.org>
Reviewed-by: Adam Rice <ricea@chromium.org>
Commit-Queue: Tsuyoshi Horo <horo@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1257871}
This commit is contained in:
Tsuyoshi Horo
2024-02-08 12:42:22 +00:00
committed by Chromium LUCI CQ
parent 97c335626e
commit 40d4e43a01
20 changed files with 285 additions and 966 deletions

@ -3745,20 +3745,6 @@ const flags_ui::FeatureEntry::FeatureVariation
std::size(kDesktopPWAsLinkCapturingDefaultOff), nullptr}};
#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
const FeatureEntry::FeatureParam
kCompressionDictionaryTransportBackendVersionV1[]{
{"CompressionDictionaryTransportBackendVersion", "v1"}};
const FeatureEntry::FeatureParam
kCompressionDictionaryTransportBackendVersionV2[]{
{"CompressionDictionaryTransportBackendVersion", "v2"}};
const FeatureEntry::FeatureVariation
kCompressionDictionaryTransportBackendVariations[] = {
{"V1", kCompressionDictionaryTransportBackendVersionV1,
std::size(kCompressionDictionaryTransportBackendVersionV1), nullptr},
{"V2", kCompressionDictionaryTransportBackendVersionV2,
std::size(kCompressionDictionaryTransportBackendVersionV2), nullptr}};
#if BUILDFLAG(IS_ANDROID)
const FeatureEntry::Choice kAccountBookmarksAndReadingListBehindOptInChoices[] =
{
@ -10246,10 +10232,8 @@ const FeatureEntry kFeatureEntries[] = {
flag_descriptions::kCompressionDictionaryTransportBackendName,
flag_descriptions::kCompressionDictionaryTransportBackendDescription,
kOsAll,
FEATURE_WITH_PARAMS_VALUE_TYPE(
network::features::kCompressionDictionaryTransportBackend,
kCompressionDictionaryTransportBackendVariations,
"CompressionDictionaryTransportBackend")},
FEATURE_VALUE_TYPE(
network::features::kCompressionDictionaryTransportBackend)},
{"enable-compression-dictionary-transport-allow-http1",
flag_descriptions::kCompressionDictionaryTransportOverHttp1Name,

@ -107,19 +107,9 @@ class SharedDictionaryAccessObserver : public content::WebContentsObserver {
std::optional<std::string> GetAvailableDictionary(
const net::test_server::HttpRequest::HeaderMap& headers) {
switch (
network::features::kCompressionDictionaryTransportBackendVersion.Get()) {
case network::features::CompressionDictionaryTransportBackendVersion::kV1: {
auto it = headers.find("sec-available-dictionary");
return it == headers.end() ? std::nullopt
: std::make_optional(it->second);
}
case network::features::CompressionDictionaryTransportBackendVersion::kV2: {
auto it = headers.find("available-dictionary");
return it == headers.end() ? std::nullopt
: std::make_optional(it->second);
}
}
}
void CheckSharedDictionaryUseCounter(
@ -165,23 +155,14 @@ void CheckSharedDictionaryUseCounter(
// `ChromeSharedDictionaryBrowserTest` is required to test Chrome
// specific code such as Site Settings.
// See `SharedDictionaryBrowserTest` for content's version of tests.
class ChromeSharedDictionaryBrowserTest
: public InProcessBrowserTest,
public ::testing::WithParamInterface<
network::features::CompressionDictionaryTransportBackendVersion> {
class ChromeSharedDictionaryBrowserTest : public InProcessBrowserTest {
public:
ChromeSharedDictionaryBrowserTest() {
scoped_feature_list_.InitWithFeaturesAndParameters(
scoped_feature_list_.InitWithFeatures(
/*enabled_features=*/
{base::test::FeatureRefAndParams(
network::features::kCompressionDictionaryTransportBackend,
{{network::features::kCompressionDictionaryTransportBackendVersion
.name,
network::features::kCompressionDictionaryTransportBackendVersion
.GetName(GetVersion())}}),
base::test::FeatureRefAndParams(
network::features::kCompressionDictionaryTransport, {}),
base::test::FeatureRefAndParams(network::features::kSharedZstd, {})},
{network::features::kCompressionDictionaryTransportBackend,
network::features::kCompressionDictionaryTransport,
network::features::kSharedZstd},
/*disabled_features=*/{});
embedded_test_server()->RegisterRequestHandler(
@ -202,9 +183,6 @@ class ChromeSharedDictionaryBrowserTest
const ChromeSharedDictionaryBrowserTest&) = delete;
protected:
network::features::CompressionDictionaryTransportBackendVersion GetVersion() {
return GetParam();
}
net::EmbeddedTestServer* cross_origin_server() {
return cross_origin_server_.get();
}
@ -379,26 +357,7 @@ class ChromeSharedDictionaryBrowserTest
base::test::ScopedFeatureList scoped_feature_list_;
};
INSTANTIATE_TEST_SUITE_P(
All,
ChromeSharedDictionaryBrowserTest,
testing::Values(
network::features::CompressionDictionaryTransportBackendVersion::kV1,
network::features::CompressionDictionaryTransportBackendVersion::kV2),
[](const testing::TestParamInfo<
network::features::CompressionDictionaryTransportBackendVersion>&
info) {
switch (info.param) {
case network::features::CompressionDictionaryTransportBackendVersion::
kV1:
return "V1";
case network::features::CompressionDictionaryTransportBackendVersion::
kV2:
return "V2";
}
});
IN_PROC_BROWSER_TEST_P(ChromeSharedDictionaryBrowserTest, BlockWriting) {
IN_PROC_BROWSER_TEST_F(ChromeSharedDictionaryBrowserTest, BlockWriting) {
content_settings::CookieSettings* settings =
CookieSettingsFactory::GetForProfile(browser()->profile()).get();
settings->SetCookieSetting(embedded_test_server()->GetURL("/"),
@ -409,7 +368,7 @@ IN_PROC_BROWSER_TEST_P(ChromeSharedDictionaryBrowserTest, BlockWriting) {
EXPECT_FALSE(TryRegisterDictionary(*embedded_test_server()));
}
IN_PROC_BROWSER_TEST_P(ChromeSharedDictionaryBrowserTest,
IN_PROC_BROWSER_TEST_F(ChromeSharedDictionaryBrowserTest,
BlockWritingCrossOrigin) {
content_settings::CookieSettings* settings =
CookieSettingsFactory::GetForProfile(browser()->profile()).get();
@ -421,7 +380,7 @@ IN_PROC_BROWSER_TEST_P(ChromeSharedDictionaryBrowserTest,
EXPECT_FALSE(TryRegisterDictionary(*cross_origin_server()));
}
IN_PROC_BROWSER_TEST_P(ChromeSharedDictionaryBrowserTest, BlockReading) {
IN_PROC_BROWSER_TEST_F(ChromeSharedDictionaryBrowserTest, BlockReading) {
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL("/title1.html")));
EXPECT_TRUE(TryRegisterDictionary(*embedded_test_server()));
@ -438,7 +397,7 @@ IN_PROC_BROWSER_TEST_P(ChromeSharedDictionaryBrowserTest, BlockReading) {
CheckDictionaryHeader(*embedded_test_server(), /*expect_blocked=*/true));
}
IN_PROC_BROWSER_TEST_P(ChromeSharedDictionaryBrowserTest,
IN_PROC_BROWSER_TEST_F(ChromeSharedDictionaryBrowserTest,
BlockReadingCrossOrigin) {
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL("/title1.html")));
@ -456,7 +415,7 @@ IN_PROC_BROWSER_TEST_P(ChromeSharedDictionaryBrowserTest,
CheckDictionaryHeader(*cross_origin_server(), /*expect_blocked=*/true));
}
IN_PROC_BROWSER_TEST_P(ChromeSharedDictionaryBrowserTest,
IN_PROC_BROWSER_TEST_F(ChromeSharedDictionaryBrowserTest,
BlockReadingWhileNavigation) {
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL("/title1.html")));
@ -477,7 +436,7 @@ IN_PROC_BROWSER_TEST_P(ChromeSharedDictionaryBrowserTest,
/*expect_blocked=*/true));
}
IN_PROC_BROWSER_TEST_P(ChromeSharedDictionaryBrowserTest,
IN_PROC_BROWSER_TEST_F(ChromeSharedDictionaryBrowserTest,
BlockReadingWhileIframeNavigation) {
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL("/title1.html")));
@ -498,7 +457,7 @@ IN_PROC_BROWSER_TEST_P(ChromeSharedDictionaryBrowserTest,
/*expect_blocked=*/true));
}
IN_PROC_BROWSER_TEST_P(ChromeSharedDictionaryBrowserTest,
IN_PROC_BROWSER_TEST_F(ChromeSharedDictionaryBrowserTest,
UseCounterMainFrameNavigation) {
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL("/title1.html")));
@ -532,7 +491,7 @@ IN_PROC_BROWSER_TEST_P(ChromeSharedDictionaryBrowserTest,
/*expected_used_for_subresource_count=*/0);
}
IN_PROC_BROWSER_TEST_P(ChromeSharedDictionaryBrowserTest,
IN_PROC_BROWSER_TEST_F(ChromeSharedDictionaryBrowserTest,
UseCounterSubFrameNavigation) {
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL("/title1.html")));
@ -577,7 +536,7 @@ IN_PROC_BROWSER_TEST_P(ChromeSharedDictionaryBrowserTest,
/*expected_used_for_subresource_count=*/0);
}
IN_PROC_BROWSER_TEST_P(ChromeSharedDictionaryBrowserTest,
IN_PROC_BROWSER_TEST_F(ChromeSharedDictionaryBrowserTest,
UseCounterSubresource) {
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL("/title1.html")));
@ -616,7 +575,7 @@ IN_PROC_BROWSER_TEST_P(ChromeSharedDictionaryBrowserTest,
/*expected_used_for_subresource_count=*/1);
}
IN_PROC_BROWSER_TEST_P(ChromeSharedDictionaryBrowserTest,
IN_PROC_BROWSER_TEST_F(ChromeSharedDictionaryBrowserTest,
UseCounterZstdMainFrameNavigation) {
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL("/title1.html")));
@ -650,7 +609,7 @@ IN_PROC_BROWSER_TEST_P(ChromeSharedDictionaryBrowserTest,
/*expected_used_for_subresource_count=*/0);
}
IN_PROC_BROWSER_TEST_P(ChromeSharedDictionaryBrowserTest,
IN_PROC_BROWSER_TEST_F(ChromeSharedDictionaryBrowserTest,
UseCounterZstdSubresource) {
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL("/title1.html")));
@ -695,7 +654,7 @@ IN_PROC_BROWSER_TEST_P(ChromeSharedDictionaryBrowserTest,
#else
#define MAYBE_SiteDataCount SiteDataCount
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
IN_PROC_BROWSER_TEST_P(ChromeSharedDictionaryBrowserTest, MAYBE_SiteDataCount) {
IN_PROC_BROWSER_TEST_F(ChromeSharedDictionaryBrowserTest, MAYBE_SiteDataCount) {
base::Time time1 = base::Time::Now();
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL("/title1.html")));

@ -104,11 +104,8 @@ constexpr std::string_view kThirdPartyOriginTrialToken =
"OjQ0MyIsICJmZWF0dXJlIjogIkNvbXByZXNzaW9uRGljdGlvbmFyeVRyYW5zcG9ydEZvclRlc3"
"QiLCAiZXhwaXJ5IjogMjAyMTU4NzUzNiwgImlzVGhpcmRQYXJ0eSI6IHRydWV9";
// The SHA256 hash of dictionary
// (content/test/data/shared_dictionary/test.dict and test_dict.html).
constexpr std::string_view kExpectedDictionaryHash =
"53969bcf5e960e0edbf0a4bdde6b0b3e9381e156de7f5b91ce8391624270f416";
// The Structured Field sf-binary hash of sha256 of dictionary.
// (content/test/data/shared_dictionary/test.dict and test_dict.html).
constexpr std::string_view kExpectedDictionaryHashBase64 =
":U5abz16WDg7b8KS93msLPpOB4Vbef1uRzoORYkJw9BY=:";
constexpr net::SHA256HashValue kExpectedDictionaryHashValue = {
@ -116,16 +113,6 @@ constexpr net::SHA256HashValue kExpectedDictionaryHashValue = {
0xbd, 0xde, 0x6b, 0x0b, 0x3e, 0x93, 0x81, 0xe1, 0x56, 0xde, 0x7f,
0x5b, 0x91, 0xce, 0x83, 0x91, 0x62, 0x42, 0x70, 0xf4, 0x16}};
std::string_view GetExpectedDictionaryHashString() {
switch (
network::features::kCompressionDictionaryTransportBackendVersion.Get()) {
case network::features::CompressionDictionaryTransportBackendVersion::kV1:
return kExpectedDictionaryHash;
case network::features::CompressionDictionaryTransportBackendVersion::kV2:
return kExpectedDictionaryHashBase64;
}
}
constexpr std::string_view kUncompressedDataString =
"test(\"This is uncompressed.\");";
constexpr std::string_view kErrorInvalidHashString =
@ -220,16 +207,6 @@ bool WaitForHistogram(const std::string& histogram_name,
return true;
}
std::string ToString(
network::features::CompressionDictionaryTransportBackendVersion version) {
switch (version) {
case network::features::CompressionDictionaryTransportBackendVersion::kV1:
return "V1";
case network::features::CompressionDictionaryTransportBackendVersion::kV2:
return "V2";
}
}
enum class FeatureState {
kDisabled,
kBackendOnly,
@ -416,45 +393,22 @@ std::string IframeLoadScript(const GURL& url) {
}
std::optional<std::string> GetAvailableDictionary(
const net::test_server::HttpRequest::HeaderMap& headers) {
switch (
network::features::kCompressionDictionaryTransportBackendVersion.Get()) {
case network::features::CompressionDictionaryTransportBackendVersion::kV1: {
auto it = headers.find("sec-available-dictionary");
return it == headers.end() ? std::nullopt
: std::make_optional(it->second);
}
case network::features::CompressionDictionaryTransportBackendVersion::kV2: {
auto it = headers.find("available-dictionary");
return it == headers.end() ? std::nullopt
: std::make_optional(it->second);
}
}
}
bool HasSharedDictionaryAcceptEncoding(
const net::test_server::HttpRequest::HeaderMap& headers,
network::features::CompressionDictionaryTransportBackendVersion version) {
const net::test_server::HttpRequest::HeaderMap& headers) {
auto it = headers.find(net::HttpRequestHeaders::kAcceptEncoding);
if (it == headers.end()) {
return false;
}
switch (version) {
case network::features::CompressionDictionaryTransportBackendVersion::kV1: {
if (base::FeatureList::IsEnabled(network::features::kSharedZstd)) {
return it->second == "sbr, zstd-d" ||
base::EndsWith(it->second, ", sbr, zstd-d");
} else {
return it->second == "sbr" || base::EndsWith(it->second, ", sbr");
}
}
case network::features::CompressionDictionaryTransportBackendVersion::kV2: {
if (base::FeatureList::IsEnabled(network::features::kSharedZstd)) {
return it->second == "br-d, zstd-d" ||
base::EndsWith(it->second, ", br-d, zstd-d");
} else {
return it->second == "br-d" || base::EndsWith(it->second, ", br-d");
}
}
if (base::FeatureList::IsEnabled(network::features::kSharedZstd)) {
return it->second == "br-d, zstd-d" ||
base::EndsWith(it->second, ", br-d, zstd-d");
} else {
return it->second == "br-d" || base::EndsWith(it->second, ", br-d");
}
}
@ -598,10 +552,7 @@ class DummyClientCertStoreContentBrowserClient
class SharedDictionaryBrowserTestBase : public ContentBrowserTest {
public:
explicit SharedDictionaryBrowserTestBase(
const network::features::CompressionDictionaryTransportBackendVersion
version)
: version_(version) {}
SharedDictionaryBrowserTestBase() = default;
SharedDictionaryBrowserTestBase(const SharedDictionaryBrowserTestBase&) =
delete;
@ -792,8 +743,8 @@ class SharedDictionaryBrowserTestBase : public ContentBrowserTest {
std::optional<std::string> dict_hash =
GetAvailableDictionary(request.headers);
if (dict_hash) {
if (*dict_hash == GetExpectedDictionaryHashString()) {
if (HasSharedDictionaryAcceptEncoding(request.headers, version_)) {
if (*dict_hash == kExpectedDictionaryHashBase64) {
if (HasSharedDictionaryAcceptEncoding(request.headers)) {
response->AddCustomHeader("content-dictionary",
kExpectedDictionaryHashBase64);
if (base::FeatureList::IsEnabled(network::features::kSharedZstd)) {
@ -819,9 +770,6 @@ class SharedDictionaryBrowserTestBase : public ContentBrowserTest {
return response;
}
const network::features::CompressionDictionaryTransportBackendVersion
version_;
};
// Tests end to end functionality of "compression dictionary transport" feature
@ -829,13 +777,10 @@ class SharedDictionaryBrowserTestBase : public ContentBrowserTest {
// TODO(crbug.com/1413922): Remove this when we fully launch this feature.
class SharedDictionaryFeatureStateBrowserTest
: public SharedDictionaryBrowserTestBase,
public ::testing::WithParamInterface<std::tuple<
FeatureState,
network::features::CompressionDictionaryTransportBackendVersion>> {
public ::testing::WithParamInterface<FeatureState> {
public:
SharedDictionaryFeatureStateBrowserTest()
: SharedDictionaryBrowserTestBase(GetVersion()) {
std::vector<base::test::FeatureRefAndParams> enabled_features;
SharedDictionaryFeatureStateBrowserTest() {
std::vector<base::test::FeatureRef> enabled_features;
std::vector<base::test::FeatureRef> disabled_features;
switch (GetFeatureState()) {
case FeatureState::kDisabled:
@ -846,42 +791,28 @@ class SharedDictionaryFeatureStateBrowserTest
disabled_features.emplace_back(network::features::kSharedZstd);
break;
case FeatureState::kBackendOnly:
enabled_features.emplace_back(base::test::FeatureRefAndParams(
network::features::kCompressionDictionaryTransportBackend,
{{network::features::kCompressionDictionaryTransportBackendVersion
.name,
network::features::kCompressionDictionaryTransportBackendVersion
.GetName(GetVersion())}}));
enabled_features.emplace_back(
network::features::kCompressionDictionaryTransportBackend);
disabled_features.emplace_back(
network::features::kCompressionDictionaryTransport);
disabled_features.emplace_back(network::features::kSharedZstd);
break;
case FeatureState::kFullyEnabled:
enabled_features.emplace_back(base::test::FeatureRefAndParams(
network::features::kCompressionDictionaryTransportBackend,
{{network::features::kCompressionDictionaryTransportBackendVersion
.name,
network::features::kCompressionDictionaryTransportBackendVersion
.GetName(GetVersion())}}));
enabled_features.emplace_back(base::test::FeatureRefAndParams(
network::features::kCompressionDictionaryTransport, {}));
enabled_features.emplace_back(
network::features::kCompressionDictionaryTransportBackend);
enabled_features.emplace_back(
network::features::kCompressionDictionaryTransport);
disabled_features.emplace_back(network::features::kSharedZstd);
break;
case FeatureState::kFullyEnabledWithZstd:
enabled_features.emplace_back(base::test::FeatureRefAndParams(
network::features::kCompressionDictionaryTransportBackend,
{{network::features::kCompressionDictionaryTransportBackendVersion
.name,
network::features::kCompressionDictionaryTransportBackendVersion
.GetName(GetVersion())}}));
enabled_features.emplace_back(base::test::FeatureRefAndParams(
network::features::kCompressionDictionaryTransport, {}));
enabled_features.emplace_back(base::test::FeatureRefAndParams(
network::features::kSharedZstd, {}));
enabled_features.emplace_back(
network::features::kCompressionDictionaryTransportBackend);
enabled_features.emplace_back(
network::features::kCompressionDictionaryTransport);
enabled_features.emplace_back(network::features::kSharedZstd);
break;
}
scoped_feature_list_.InitWithFeaturesAndParameters(enabled_features,
disabled_features);
scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features);
}
SharedDictionaryFeatureStateBrowserTest(
const SharedDictionaryFeatureStateBrowserTest&) = delete;
@ -907,11 +838,7 @@ class SharedDictionaryFeatureStateBrowserTest
void TearDownOnMainThread() override { url_loader_interceptor_.reset(); }
protected:
FeatureState GetFeatureState() const { return std::get<0>(GetParam()); }
network::features::CompressionDictionaryTransportBackendVersion GetVersion()
const {
return std::get<1>(GetParam());
}
FeatureState GetFeatureState() const { return GetParam(); }
net::EmbeddedTestServer* https_server() { return https_server_.get(); }
bool FeatureIsFullyEnabled() const {
@ -1024,25 +951,15 @@ class SharedDictionaryFeatureStateBrowserTest
std::optional<URLLoaderInterceptor> url_loader_interceptor_;
};
INSTANTIATE_TEST_SUITE_P(
All,
SharedDictionaryFeatureStateBrowserTest,
::testing::Combine(
testing::Values(FeatureState::kDisabled,
FeatureState::kBackendOnly,
FeatureState::kFullyEnabled,
FeatureState::kFullyEnabledWithZstd),
testing::Values(network::features::
CompressionDictionaryTransportBackendVersion::kV1,
network::features::
CompressionDictionaryTransportBackendVersion::kV2)),
[](const testing::TestParamInfo<std::tuple<
FeatureState,
network::features::CompressionDictionaryTransportBackendVersion>>&
info) {
return ToString(std::get<0>(info.param)) + "_" +
ToString(std::get<1>(info.param));
});
INSTANTIATE_TEST_SUITE_P(All,
SharedDictionaryFeatureStateBrowserTest,
testing::Values(FeatureState::kDisabled,
FeatureState::kBackendOnly,
FeatureState::kFullyEnabled,
FeatureState::kFullyEnabledWithZstd),
[](const testing::TestParamInfo<FeatureState>& info) {
return ToString(info.param);
});
IN_PROC_BROWSER_TEST_P(SharedDictionaryFeatureStateBrowserTest,
LinkRelDictionary) {
@ -1190,16 +1107,6 @@ IN_PROC_BROWSER_TEST_P(SharedDictionaryFeatureStateBrowserTest,
base::BindLambdaForTesting(
[&](std::vector<network::mojom::SharedDictionaryInfoPtr> result) {
ASSERT_EQ(1u, result.size());
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
@ -1208,8 +1115,6 @@ IN_PROC_BROWSER_TEST_P(SharedDictionaryFeatureStateBrowserTest,
EXPECT_GT(result[0]->expiration,
base::Seconds(3600) -
(result[0]->response_time - test_start_time));
break;
}
loop.Quit();
}));
loop.Run();
@ -1266,23 +1171,14 @@ IN_PROC_BROWSER_TEST_P(SharedDictionaryFeatureStateBrowserTest,
// with fully enabled features.
class SharedDictionaryBrowserTest
: public SharedDictionaryBrowserTestBase,
public ::testing::WithParamInterface<std::tuple<
BrowserType,
network::features::CompressionDictionaryTransportBackendVersion>> {
public ::testing::WithParamInterface<BrowserType> {
public:
SharedDictionaryBrowserTest()
: SharedDictionaryBrowserTestBase(GetVersion()) {
scoped_feature_list_.InitWithFeaturesAndParameters(
SharedDictionaryBrowserTest() {
scoped_feature_list_.InitWithFeatures(
/*enabled_features=*/
{base::test::FeatureRefAndParams(
network::features::kCompressionDictionaryTransportBackend,
{{network::features::kCompressionDictionaryTransportBackendVersion
.name,
network::features::kCompressionDictionaryTransportBackendVersion
.GetName(GetVersion())}}),
base::test::FeatureRefAndParams(
network::features::kCompressionDictionaryTransport, {}),
base::test::FeatureRefAndParams(network::features::kSharedZstd, {})},
{network::features::kCompressionDictionaryTransportBackend,
network::features::kCompressionDictionaryTransport,
network::features::kSharedZstd},
/*disabled_features=*/{});
}
SharedDictionaryBrowserTest(const SharedDictionaryBrowserTest&) = delete;
@ -1380,11 +1276,7 @@ class SharedDictionaryBrowserTest
}
protected:
BrowserType GetBrowserType() const { return std::get<0>(GetParam()); }
network::features::CompressionDictionaryTransportBackendVersion GetVersion()
const {
return std::get<1>(GetParam());
}
BrowserType GetBrowserType() const { return GetParam(); }
net::EmbeddedTestServer* cross_origin_server() const {
return cross_origin_server_.get();
}
@ -1498,21 +1390,11 @@ class SharedDictionaryBrowserTest
std::optional<std::string> dict_hash =
GetAvailableDictionary(request.headers);
if (dict_hash) {
if (*dict_hash == GetExpectedDictionaryHashString()) {
if (HasSharedDictionaryAcceptEncoding(request.headers,
GetVersion())) {
switch (GetVersion()) {
case network::features::
CompressionDictionaryTransportBackendVersion::kV1:
response->AddCustomHeader("content-encoding", "sbr");
break;
case network::features::
CompressionDictionaryTransportBackendVersion::kV2:
response->AddCustomHeader("content-encoding", "br-d");
response->AddCustomHeader("content-dictionary",
kExpectedDictionaryHashBase64);
break;
}
if (*dict_hash == kExpectedDictionaryHashBase64) {
if (HasSharedDictionaryAcceptEncoding(request.headers)) {
response->AddCustomHeader("content-encoding", "br-d");
response->AddCustomHeader("content-dictionary",
kExpectedDictionaryHashBase64);
response->set_content(kBrotliCompressedDataString);
} else {
response->set_content(kErrorNoSharedDictionaryAcceptEncodingString);
@ -1536,22 +1418,13 @@ class SharedDictionaryBrowserTest
base::test::ScopedFeatureList scoped_feature_list_;
};
INSTANTIATE_TEST_SUITE_P(
All,
SharedDictionaryBrowserTest,
::testing::Combine(
testing::Values(BrowserType::kNormal, BrowserType::kOffTheRecord),
testing::Values(network::features::
CompressionDictionaryTransportBackendVersion::kV1,
network::features::
CompressionDictionaryTransportBackendVersion::kV2)),
[](const testing::TestParamInfo<std::tuple<
BrowserType,
network::features::CompressionDictionaryTransportBackendVersion>>&
info) {
return ToString(std::get<0>(info.param)) + "_" +
ToString(std::get<1>(info.param));
});
INSTANTIATE_TEST_SUITE_P(All,
SharedDictionaryBrowserTest,
testing::Values(BrowserType::kNormal,
BrowserType::kOffTheRecord),
[](const testing::TestParamInfo<BrowserType>& info) {
return ToString(info.param);
});
IN_PROC_BROWSER_TEST_P(SharedDictionaryBrowserTest,
LinkRelDictionarySecureContext) {
@ -2063,18 +1936,9 @@ IN_PROC_BROWSER_TEST_P(SharedDictionaryBrowserTest, MatchDestEmptyString) {
FetchTargetDataScript(dictionary_url))
.ExtractString());
switch (GetVersion()) {
case network::features::CompressionDictionaryTransportBackendVersion::kV1:
// Check that Chrome uses the dictionary while fetching a script.
EXPECT_EQ(kCompressedDataResultString,
LoadTestScript(GetURL(kTestPath + "?for_script")));
break;
case network::features::CompressionDictionaryTransportBackendVersion::kV2:
// Check that Chrome doesn't use the dictionary while fetching a script.
EXPECT_EQ(kUncompressedDataResultString,
LoadTestScript(GetURL(kTestPath + "?for_script")));
break;
}
// Check that Chrome doesn't use the dictionary while fetching a script.
EXPECT_EQ(kUncompressedDataResultString,
LoadTestScript(GetURL(kTestPath + "?for_script")));
}
IN_PROC_BROWSER_TEST_P(SharedDictionaryBrowserTest, MatchDestScript) {
@ -2098,24 +1962,12 @@ IN_PROC_BROWSER_TEST_P(SharedDictionaryBrowserTest, MatchDestScript) {
EXPECT_EQ(kCompressedDataResultString,
LoadTestScript(GetURL(kTestPath + "?for_script")));
switch (GetVersion()) {
case network::features::CompressionDictionaryTransportBackendVersion::kV1:
// Check that Chrome uses the dictionary while fetching the resource using
// Fetch API.
EXPECT_EQ(kCompressedDataOriginalString,
EvalJs(shell->web_contents()->GetPrimaryMainFrame(),
FetchTargetDataScript(dictionary_url))
.ExtractString());
break;
case network::features::CompressionDictionaryTransportBackendVersion::kV2:
// Check that Chrome doesn't use the dictionary while fetching the
// resource using Fetch API.
EXPECT_EQ(kUncompressedDataString,
EvalJs(shell->web_contents()->GetPrimaryMainFrame(),
FetchTargetDataScript(dictionary_url))
.ExtractString());
break;
}
// Check that Chrome doesn't use the dictionary while fetching the resource
// using Fetch API.
EXPECT_EQ(kUncompressedDataString,
EvalJs(shell->web_contents()->GetPrimaryMainFrame(),
FetchTargetDataScript(dictionary_url))
.ExtractString());
}
IN_PROC_BROWSER_TEST_P(

@ -193,34 +193,21 @@ Developers can check the related HTTP request and response headers
- [cbrbug.com/1479809](crbug.com/1479809): Can't use large (>8MB) dictionaries
for Shared Zstd. Fixed in M118.
## Following spec changes
## Changes in M123
Currently there are two backend implementations, V1 and V2. V2 is under active
construction to catch up the following spec changes:
The following changes have been made to Chrome since M123 to keep up with the
changes in specifications.
- Change Content-Encoding name "br-d" "zstd-d"
- Status: Implemented by https://crrev.com/c/5185977.
- Changed Content-Encoding name "br-d" "zstd-d"
- Changed match to use URLPattern
- Status: Implemented by https://crrev.com/c/5232339.
- Added support for a server-provided dictionary id
- Status: Implemented by https://crrev.com/c/5271881.
- Stop using "expires" value of "Use-As-Dictionary" header, and use the cache
expiration time calculated from the response's freshness instead.
- Status: Removed by https://crrev.com/c/5227360.
- Removed support for hash negotiation and force use of sha-256
- Status: Removed by https://crrev.com/c/5223985.
- Added the dictionary hash to the compressed response
- Status: Implemented by https://crrev.com/c/5226641.
- Dictionary hashes changed to sf-binary
- Status: Implemented by https://crrev.com/c/5224886.
- Use "Available-Dictionary" header instead of "Sec-Available-Dictionary"
- Status: Implemented by https://crrev.com/c/5224886.
- Added support for match-dest option
- Status: Implemented by https://crrev.com/c/5271881.
You can try the experimental V2 implementation by selecting
"Enabled experimental V2" in
[chrome://flags/#enable-compression-dictionary-transport-backend][backend-flag].
## Demo sites

@ -141,7 +141,7 @@ class CorsURLLoaderSharedDictionaryTest : public CorsURLLoaderTestBase {
EXPECT_EQ(dictionary_url, dictionary_info.url());
EXPECT_EQ(base::FeatureList::IsEnabled(
network::features::kCompressionDictionaryTransport)
? shared_dictionary::kDefaultExpiration
? base::Seconds(2592000)
: shared_dictionary::kMaxExpirationForOriginTrial,
dictionary_info.expiration());
EXPECT_EQ("/path*", dictionary_info.match());

@ -395,16 +395,6 @@ const base::FeatureParam<bool> kPrefetchNoVarySearchShippedByDefault{
BASE_FEATURE(kCompressionDictionaryTransportBackend,
"CompressionDictionaryTransportBackend",
base::FEATURE_ENABLED_BY_DEFAULT);
const base::FeatureParam<CompressionDictionaryTransportBackendVersion>::Option
kCompressionDictionaryTransportBackendVersionOptions[] = {
{CompressionDictionaryTransportBackendVersion::kV1, "v1"},
{CompressionDictionaryTransportBackendVersion::kV2, "v2"}};
const base::FeatureParam<CompressionDictionaryTransportBackendVersion>
kCompressionDictionaryTransportBackendVersion{
&kCompressionDictionaryTransportBackend,
"CompressionDictionaryTransportBackendVersion",
CompressionDictionaryTransportBackendVersion::kV2,
&kCompressionDictionaryTransportBackendVersionOptions};
// When both this feature and the kCompressionDictionaryTransportBackend feature
// are enabled, the following will happen:

@ -151,15 +151,6 @@ COMPONENT_EXPORT(NETWORK_CPP) BASE_DECLARE_FEATURE(kGetCookiesStringUma);
COMPONENT_EXPORT(NETWORK_CPP)
BASE_DECLARE_FEATURE(kCompressionDictionaryTransportBackend);
enum class CompressionDictionaryTransportBackendVersion {
kV1,
kV2,
};
COMPONENT_EXPORT(NETWORK_CPP)
extern const base::FeatureParam<CompressionDictionaryTransportBackendVersion>
kCompressionDictionaryTransportBackendVersion;
COMPONENT_EXPORT(NETWORK_CPP)
BASE_DECLARE_FEATURE(kCompressionDictionaryTransport);
COMPONENT_EXPORT(NETWORK_CPP)

@ -9,12 +9,7 @@
namespace network {
const char* GetSharedBrotliContentEncodingName() {
switch (features::kCompressionDictionaryTransportBackendVersion.Get()) {
case features::CompressionDictionaryTransportBackendVersion::kV1:
return "sbr";
case features::CompressionDictionaryTransportBackendVersion::kV2:
return "br-d";
}
return "br-d";
}
const char* GetSharedZstdContentEncodingName() {

@ -9,9 +9,7 @@
namespace network {
// Returns the content encoding name of Shared Brotli:
// "sbr": when CompressionDictionaryTransportBackendVersion is kV1.
// "br-d": when CompressionDictionaryTransportBackendVersion is kV2.
// Returns the content encoding name of Shared Brotli: "br-d"
COMPONENT_EXPORT(NETWORK_CPP) const char* GetSharedBrotliContentEncodingName();
// Returns the content encoding name of Shared Zstd: "zstd-d".

@ -16,16 +16,11 @@ size_t g_dictionary_size_limit = kDictionarySizeLimit;
} // namespace
const char kSecAvailableDictionaryHeaderName[] = "sec-available-dictionary";
const char kAvailableDictionaryHeaderName[] = "available-dictionary";
const char kUseAsDictionaryHeaderName[] = "use-as-dictionary";
const char kContentDictionaryHeaderName[] = "content-dictionary";
const char kOptionNameMatch[] = "match";
const char kOptionNameMatchDest[] = "match-dest";
const char kOptionNameExpires[] = "expires";
const char kOptionNameType[] = "type";
const char kOptionNameId[] = "id";

@ -13,11 +13,6 @@
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
// CompressionDictionaryTransport feature is disabled in the network service.
// TODO(crbug.com/1413922): Remove this after the Origin Trial experiment.
@ -35,11 +30,6 @@ COMPONENT_EXPORT(NETWORK_SERVICE)
base::ScopedClosureRunner SetDictionarySizeLimitForTesting(
size_t dictionary_size_limit);
// The header name of "sec-available-dictionary".
// This will not be used when V2 backend is enabled.
COMPONENT_EXPORT(NETWORK_SERVICE)
extern const char kSecAvailableDictionaryHeaderName[];
// The header name of "available-dictionary".
COMPONENT_EXPORT(NETWORK_SERVICE)
extern const char kAvailableDictionaryHeaderName[];
@ -58,10 +48,6 @@ COMPONENT_EXPORT(NETWORK_SERVICE) extern const char kOptionNameMatch[];
// The dictionary option name of "match-dest".
COMPONENT_EXPORT(NETWORK_SERVICE) extern const char kOptionNameMatchDest[];
// 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".
COMPONENT_EXPORT(NETWORK_SERVICE) extern const char kOptionNameType[];

@ -14,7 +14,6 @@
#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"
@ -162,20 +161,9 @@ void WriteDiskCacheEntry(SharedDictionaryManager* manager,
} // namespace
class SharedDictionaryManagerOnDiskTest
: public ::testing::Test,
public testing::WithParamInterface<
features::CompressionDictionaryTransportBackendVersion> {
class SharedDictionaryManagerOnDiskTest : public ::testing::Test {
public:
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() = default;
~SharedDictionaryManagerOnDiskTest() override = default;
SharedDictionaryManagerOnDiskTest(const SharedDictionaryManagerOnDiskTest&) =
@ -192,10 +180,6 @@ class SharedDictionaryManagerOnDiskTest
void TearDown() override { FlushCacheTasks(); }
protected:
features::CompressionDictionaryTransportBackendVersion GetVersion() const {
return GetParam();
}
static base::UnguessableToken GetDiskCacheKeyTokenOfFirstDictionary(
const std::map<
url::SchemeHostPort,
@ -272,26 +256,9 @@ class SharedDictionaryManagerOnDiskTest
// `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_;
};
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) {
TEST_F(SharedDictionaryManagerOnDiskTest, ReusingRefCountedSharedDictionary) {
std::unique_ptr<SharedDictionaryManager> manager =
CreateSharedDictionaryManager();
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
@ -336,7 +303,7 @@ TEST_P(SharedDictionaryManagerOnDiskTest, ReusingRefCountedSharedDictionary) {
dict1->size()));
}
TEST_P(SharedDictionaryManagerOnDiskTest,
TEST_F(SharedDictionaryManagerOnDiskTest,
MaybeCreateWriterAfterManagerDeleted) {
std::unique_ptr<SharedDictionaryManager> manager =
CreateSharedDictionaryManager();
@ -362,7 +329,7 @@ TEST_P(SharedDictionaryManagerOnDiskTest,
EXPECT_FALSE(writer);
}
TEST_P(SharedDictionaryManagerOnDiskTest, GetDictionaryAfterManagerDeleted) {
TEST_F(SharedDictionaryManagerOnDiskTest, GetDictionaryAfterManagerDeleted) {
std::unique_ptr<SharedDictionaryManager> manager =
CreateSharedDictionaryManager();
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
@ -380,7 +347,7 @@ TEST_P(SharedDictionaryManagerOnDiskTest, GetDictionaryAfterManagerDeleted) {
EXPECT_FALSE(dict);
}
TEST_P(SharedDictionaryManagerOnDiskTest,
TEST_F(SharedDictionaryManagerOnDiskTest,
DictionaryWrittenInDiskCacheAfterManagerDeleted) {
std::unique_ptr<SharedDictionaryManager> manager =
CreateSharedDictionaryManager();
@ -398,7 +365,7 @@ TEST_P(SharedDictionaryManagerOnDiskTest,
FlushCacheTasks();
}
TEST_P(SharedDictionaryManagerOnDiskTest, OverridingDictionary) {
TEST_F(SharedDictionaryManagerOnDiskTest, OverridingDictionary) {
std::unique_ptr<SharedDictionaryManager> manager =
CreateSharedDictionaryManager();
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
@ -470,7 +437,7 @@ TEST_P(SharedDictionaryManagerOnDiskTest, OverridingDictionary) {
dict1->size()));
}
TEST_P(SharedDictionaryManagerOnDiskTest, MultipleDictionaries) {
TEST_F(SharedDictionaryManagerOnDiskTest, MultipleDictionaries) {
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
kSite);
@ -555,7 +522,7 @@ TEST_P(SharedDictionaryManagerOnDiskTest, MultipleDictionaries) {
dict2->size()));
}
TEST_P(SharedDictionaryManagerOnDiskTest, GetDictionary) {
TEST_F(SharedDictionaryManagerOnDiskTest, GetDictionary) {
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
kSite);
@ -610,7 +577,7 @@ TEST_P(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_P(SharedDictionaryManagerOnDiskTest, CorruptedDiskCacheAndWriteData) {
TEST_F(SharedDictionaryManagerOnDiskTest, CorruptedDiskCacheAndWriteData) {
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
kSite);
@ -648,7 +615,7 @@ TEST_P(SharedDictionaryManagerOnDiskTest, CorruptedDiskCacheAndWriteData) {
}
}
TEST_P(SharedDictionaryManagerOnDiskTest, CorruptedDiskCacheAndGetData) {
TEST_F(SharedDictionaryManagerOnDiskTest, CorruptedDiskCacheAndGetData) {
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
kSite);
@ -699,7 +666,7 @@ TEST_P(SharedDictionaryManagerOnDiskTest, CorruptedDiskCacheAndGetData) {
}
#endif // !BUILDFLAG(IS_FUCHSIA)
TEST_P(SharedDictionaryManagerOnDiskTest, CorruptedDatabase) {
TEST_F(SharedDictionaryManagerOnDiskTest, CorruptedDatabase) {
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
kSite);
@ -787,7 +754,7 @@ TEST_P(SharedDictionaryManagerOnDiskTest, CorruptedDatabase) {
}
}
TEST_P(SharedDictionaryManagerOnDiskTest, MetadataBrokenDatabase) {
TEST_F(SharedDictionaryManagerOnDiskTest, MetadataBrokenDatabase) {
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
kSite);
@ -856,7 +823,7 @@ TEST_P(SharedDictionaryManagerOnDiskTest, MetadataBrokenDatabase) {
}
}
TEST_P(SharedDictionaryManagerOnDiskTest, LastUsedTime) {
TEST_F(SharedDictionaryManagerOnDiskTest, LastUsedTime) {
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
kSite);
base::Time last_used_time_after_second_get_dict;
@ -930,7 +897,7 @@ MATCHER_P(DictionaryUrlIs,
return arg.url().spec() == url;
}
TEST_P(SharedDictionaryManagerOnDiskTest, ClearData) {
TEST_F(SharedDictionaryManagerOnDiskTest, ClearData) {
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
kSite);
{
@ -1052,7 +1019,7 @@ TEST_P(SharedDictionaryManagerOnDiskTest, ClearData) {
DictionaryUrlIs("https://target.test/4"))))));
}
TEST_P(SharedDictionaryManagerOnDiskTest, ClearDataSerializedOperation) {
TEST_F(SharedDictionaryManagerOnDiskTest, ClearDataSerializedOperation) {
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
kSite);
std::unique_ptr<SharedDictionaryManager> manager =
@ -1109,7 +1076,7 @@ TEST_P(SharedDictionaryManagerOnDiskTest, ClearDataSerializedOperation) {
EXPECT_TRUE(GetOnDiskDictionaryMap(storage.get()).empty());
}
TEST_P(SharedDictionaryManagerOnDiskTest, ClearDataForIsolationKey) {
TEST_F(SharedDictionaryManagerOnDiskTest, ClearDataForIsolationKey) {
net::SharedDictionaryIsolationKey isolation_key1(url::Origin::Create(kUrl),
kSite);
net::SharedDictionaryIsolationKey isolation_key2(
@ -1185,7 +1152,7 @@ TEST_P(SharedDictionaryManagerOnDiskTest, ClearDataForIsolationKey) {
DictionaryUrlIs("https://origin1.test/d"))))));
}
TEST_P(SharedDictionaryManagerOnDiskTest, ExpiredDictionaryDeletionOnReload) {
TEST_F(SharedDictionaryManagerOnDiskTest, ExpiredDictionaryDeletionOnReload) {
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
kSite);
base::UnguessableToken token1, token2;
@ -1234,7 +1201,7 @@ TEST_P(SharedDictionaryManagerOnDiskTest, ExpiredDictionaryDeletionOnReload) {
EXPECT_TRUE(DiskCacheEntryExists(manager.get(), token2));
}
TEST_P(SharedDictionaryManagerOnDiskTest,
TEST_F(SharedDictionaryManagerOnDiskTest,
ExpiredDictionaryDeletionOnNewDictionary) {
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
kSite);
@ -1283,7 +1250,7 @@ TEST_P(SharedDictionaryManagerOnDiskTest,
EXPECT_TRUE(DiskCacheEntryExists(manager.get(), token2));
}
TEST_P(SharedDictionaryManagerOnDiskTest,
TEST_F(SharedDictionaryManagerOnDiskTest,
ExpiredDictionaryDeletionOnSetCacheMaxSize) {
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
kSite);
@ -1321,7 +1288,7 @@ TEST_P(SharedDictionaryManagerOnDiskTest,
EXPECT_FALSE(DiskCacheEntryExists(manager.get(), token));
}
TEST_P(SharedDictionaryManagerOnDiskTest,
TEST_F(SharedDictionaryManagerOnDiskTest,
ExpiredDictionaryDeletionOnClearData) {
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
kSite);
@ -1362,7 +1329,7 @@ TEST_P(SharedDictionaryManagerOnDiskTest,
EXPECT_FALSE(DiskCacheEntryExists(manager.get(), token));
}
TEST_P(SharedDictionaryManagerOnDiskTest,
TEST_F(SharedDictionaryManagerOnDiskTest,
ExpiredDictionaryDeletionOnClearDataForIsolationKey) {
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
kSite);
@ -1404,7 +1371,7 @@ TEST_P(SharedDictionaryManagerOnDiskTest,
EXPECT_FALSE(DiskCacheEntryExists(manager.get(), token));
}
TEST_P(SharedDictionaryManagerOnDiskTest, CacheEvictionOnReload) {
TEST_F(SharedDictionaryManagerOnDiskTest, CacheEvictionOnReload) {
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
kSite);
base::UnguessableToken token1, token2, token3;
@ -1458,7 +1425,7 @@ TEST_P(SharedDictionaryManagerOnDiskTest, CacheEvictionOnReload) {
EXPECT_TRUE(DiskCacheEntryExists(manager.get(), token3));
}
TEST_P(SharedDictionaryManagerOnDiskTest, CacheEvictionOnSetCacheMaxSize) {
TEST_F(SharedDictionaryManagerOnDiskTest, CacheEvictionOnSetCacheMaxSize) {
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
kSite);
std::unique_ptr<SharedDictionaryManager> manager =
@ -1502,7 +1469,7 @@ TEST_P(SharedDictionaryManagerOnDiskTest, CacheEvictionOnSetCacheMaxSize) {
EXPECT_TRUE(DiskCacheEntryExists(manager.get(), token3));
}
TEST_P(SharedDictionaryManagerOnDiskTest, CacheEvictionOnNewDictionary) {
TEST_F(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"));
@ -1584,7 +1551,7 @@ TEST_P(SharedDictionaryManagerOnDiskTest, CacheEvictionOnNewDictionary) {
EXPECT_TRUE(DiskCacheEntryExists(manager.get(), token3));
}
TEST_P(SharedDictionaryManagerOnDiskTest,
TEST_F(SharedDictionaryManagerOnDiskTest,
CacheEvictionPerSiteExceededSizeLimit) {
const net::SchemefulSite site1(GURL("https://site1.test"));
const net::SchemefulSite site2(GURL("https://site2.test"));
@ -1672,7 +1639,7 @@ TEST_P(SharedDictionaryManagerOnDiskTest,
EXPECT_FALSE(DiskCacheEntryExists(manager.get(), token2));
}
TEST_P(SharedDictionaryManagerOnDiskTest,
TEST_F(SharedDictionaryManagerOnDiskTest,
CacheEvictionPerSiteExceededCountLimit) {
const net::SchemefulSite site1(GURL("https://site1.test"));
const net::SchemefulSite site2(GURL("https://site2.test"));
@ -1788,7 +1755,7 @@ TEST_P(SharedDictionaryManagerOnDiskTest,
EXPECT_TRUE(DiskCacheEntryExists(manager.get(), token3));
}
TEST_P(SharedDictionaryManagerOnDiskTest,
TEST_F(SharedDictionaryManagerOnDiskTest,
CacheEvictionAfterUpdatingLastUsedTime) {
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
kSite);
@ -1864,7 +1831,7 @@ TEST_P(SharedDictionaryManagerOnDiskTest,
EXPECT_TRUE(DiskCacheEntryExists(manager.get(), token4));
}
TEST_P(SharedDictionaryManagerOnDiskTest,
TEST_F(SharedDictionaryManagerOnDiskTest,
MismatchingEntryDeletionMetadataUnavailableDictionary) {
const base::UnguessableToken token = base::UnguessableToken::Create();
const std::string entry_key = token.ToString();
@ -1894,7 +1861,7 @@ TEST_P(SharedDictionaryManagerOnDiskTest,
0, 1);
}
TEST_P(SharedDictionaryManagerOnDiskTest,
TEST_F(SharedDictionaryManagerOnDiskTest,
MismatchingEntryDeletionInvalidDiskCacheEntry) {
const std::string kTestKey = "test";
const std::string kTestData = "Hello world";
@ -1923,7 +1890,7 @@ TEST_P(SharedDictionaryManagerOnDiskTest,
0, 1);
}
TEST_P(SharedDictionaryManagerOnDiskTest,
TEST_F(SharedDictionaryManagerOnDiskTest,
MismatchingEntryDeletionDiskCacheEntryUnavailableDictionary) {
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
kSite);
@ -1992,7 +1959,7 @@ TEST_P(SharedDictionaryManagerOnDiskTest,
EXPECT_TRUE(GetOnDiskDictionaryMap(storage.get()).empty());
}
TEST_P(SharedDictionaryManagerOnDiskTest,
TEST_F(SharedDictionaryManagerOnDiskTest,
MismatchingEntryDeletionCanBeTriggeredOnlyOnce) {
std::unique_ptr<SharedDictionaryManager> manager =
CreateSharedDictionaryManager();
@ -2033,7 +2000,7 @@ TEST_P(SharedDictionaryManagerOnDiskTest,
EXPECT_TRUE(DiskCacheEntryExists(manager.get(), entry_key));
}
TEST_P(SharedDictionaryManagerOnDiskTest,
TEST_F(SharedDictionaryManagerOnDiskTest,
MismatchingEntryDeletionWritingEntryMustNotBeDeleted) {
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
kSite);
@ -2099,7 +2066,7 @@ TEST_P(SharedDictionaryManagerOnDiskTest,
DictionaryUrlIs("https://target1.test/d"))))));
}
TEST_P(SharedDictionaryManagerOnDiskTest,
TEST_F(SharedDictionaryManagerOnDiskTest,
MismatchingEntryDeletionWritingDiskCacheEntryMustNotBeDeleted) {
net::SharedDictionaryIsolationKey isolation_key(url::Origin::Create(kUrl),
kSite);

@ -16,7 +16,6 @@
#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"
@ -92,16 +91,6 @@ std::string ToString(TestManagerType type) {
}
}
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,
@ -156,12 +145,8 @@ void WriteDictionary(
}
base::TimeDelta GetDefaultExpiration() {
if (features::kCompressionDictionaryTransportBackendVersion.Get() ==
features::CompressionDictionaryTransportBackendVersion::kV2) {
return base::Seconds(2592000); // See kDefaultCacheControlHeader
}
return base::FeatureList::IsEnabled(features::kCompressionDictionaryTransport)
? shared_dictionary::kDefaultExpiration
? base::Seconds(2592000)
: shared_dictionary::kMaxExpirationForOriginTrial;
}
@ -169,19 +154,9 @@ base::TimeDelta GetDefaultExpiration() {
class SharedDictionaryManagerTest
: public ::testing::Test,
public ::testing::WithParamInterface<
std::tuple<TestManagerType,
features::CompressionDictionaryTransportBackendVersion>> {
public ::testing::WithParamInterface<TestManagerType> {
public:
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() = default;
~SharedDictionaryManagerTest() override = default;
SharedDictionaryManagerTest(const SharedDictionaryManagerTest&) = delete;
@ -203,10 +178,7 @@ class SharedDictionaryManagerTest
}
protected:
TestManagerType GetManagerType() const { return std::get<0>(GetParam()); }
features::CompressionDictionaryTransportBackendVersion GetVersion() const {
return std::get<1>(GetParam());
}
TestManagerType GetManagerType() const { return GetParam(); }
std::unique_ptr<SharedDictionaryManager> CreateSharedDictionaryManager() {
switch (GetManagerType()) {
case TestManagerType::kInMemory:
@ -267,22 +239,14 @@ 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::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));
testing::ValuesIn({TestManagerType::kInMemory, TestManagerType::kOnDisk}),
[](const testing::TestParamInfo<TestManagerType>& info) {
return ToString(info.param);
});
TEST_P(SharedDictionaryManagerTest, SameStorageForSameIsolationKey) {
@ -624,60 +588,7 @@ 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),
@ -744,30 +655,27 @@ TEST_P(SharedDictionaryManagerTest, WriterForUseAsDictionaryIdOption) {
struct {
std::string header_string;
bool expect_success_v1;
bool expect_success_v2;
bool expect_success;
std::string expected_id;
} kTestCases[] = {
// Valid `id` value.
{"match=\"test\", id=\"test_id\"", /*expect_success_v1=*/true,
/*expect_success_v2=*/true, /*expected_id=*/"test_id"},
{"match=\"test\", id=\"test_id\"",
/*expect_success=*/true, /*expected_id=*/"test_id"},
// Valid `id` value with backslash.
{"match=\"test\", id=\"test\\\\id\"", /*expect_success_v1=*/true,
/*expect_success_v2=*/true, /*expected_id=*/"test\\id"},
{"match=\"test\", id=\"test\\\\id\"",
/*expect_success=*/true, /*expected_id=*/"test\\id"},
// Valid `id` value with double quote.
{"match=\"test\", id=\"test\\\"id\"", /*expect_success_v1=*/true,
/*expect_success_v2=*/true, /*expected_id=*/"test\"id"},
{"match=\"test\", id=\"test\\\"id\"",
/*expect_success=*/true, /*expected_id=*/"test\"id"},
// `id` should not be a list.
{"match=\"test\", id=(\"id1\" \"id2\")", /*expect_success_v1=*/true,
/*expect_success_v2=*/false},
{"match=\"test\", id=(\"id1\" \"id2\")",
/*expect_success=*/false},
// `id` can be 1024 characters long.
{base::StrCat({"match=\"test\", id=\"", std::string(1024, 'x'), "\""}),
/*expect_success_v1=*/true,
/*expect_success_v2=*/true, /*expected_id=*/std::string(1024, 'x')},
/*expect_success=*/true, /*expected_id=*/std::string(1024, 'x')},
// `id` too long.
{base::StrCat({"match=\"test\", id=\"", std::string(1025, 'x'), "\""}),
/*expect_success_v1=*/true,
/*expect_success_v2=*/false},
/*expect_success=*/false},
};
for (const auto& testcase : kTestCases) {
SCOPED_TRACE(base::StringPrintf("header_string: %s",
@ -786,14 +694,7 @@ TEST_P(SharedDictionaryManagerTest, WriterForUseAsDictionaryIdOption) {
/*access_allowed_check_callback=*/base::BindOnce([]() {
return true;
}));
switch (GetVersion()) {
case features::CompressionDictionaryTransportBackendVersion::kV1:
EXPECT_EQ(testcase.expect_success_v1, !!writer);
continue;
case features::CompressionDictionaryTransportBackendVersion::kV2:
EXPECT_EQ(testcase.expect_success_v2, !!writer);
break;
}
EXPECT_EQ(testcase.expect_success, !!writer);
if (!writer) {
continue;
}
@ -824,43 +725,40 @@ TEST_P(SharedDictionaryManagerTest, WriterForUseAsDictionaryMatchDestOption) {
struct {
std::string header_string;
bool expect_success_v1;
bool expect_success_v2;
bool expect_success;
std::vector<mojom::RequestDestination> expected_match_dest;
} kTestCases[] = {
// No `match-dest` value.
{"match=\"test\"", /*expect_success_v1=*/true,
/*expect_success_v2=*/true, /*expected_match_dest=*/{}},
{"match=\"test\"",
/*expect_success=*/true, /*expected_match_dest=*/{}},
// Valid `match-dest` value.
{"match=\"test\", match-dest=(\"document\")", /*expect_success_v1=*/true,
/*expect_success_v2=*/true,
{"match=\"test\", match-dest=(\"document\")",
/*expect_success=*/true,
/*expected_match_dest=*/{mojom::RequestDestination::kDocument}},
// `match-dest` must be a list.
{"match=\"test\", match-dest=\"document\"", /*expect_success_v1=*/true,
/*expect_success_v2=*/false},
{"match=\"test\", match-dest=\"document\"",
/*expect_success=*/false},
// Unknown `match-dest` value should be treated as empty.
{"match=\"test\", match-dest=(\"unknown\")", /*expect_success_v1=*/true,
/*expect_success_v2=*/true,
{"match=\"test\", match-dest=(\"unknown\")",
/*expect_success=*/true,
/*expected_match_dest=*/{}},
//`match-dest` should not be a sf-token.
// https://github.com/httpwg/http-extensions/issues/2723
{"match=\"test\", match-dest=(document)", /*expect_success_v1=*/true,
/*expect_success_v2=*/false},
{"match=\"test\", match-dest=(document)",
/*expect_success=*/false},
// Valid `match-dest` value "".
{"match=\"test\", match-dest=(\"\")", /*expect_success_v1=*/true,
/*expect_success_v2=*/true,
{"match=\"test\", match-dest=(\"\")",
/*expect_success=*/true,
/*expected_match_dest=*/{mojom::RequestDestination::kEmpty}},
// Valid `match-dest` value ("document" "frame" "iframe").
{"match=\"test\", match-dest=(\"document\" \"frame\" \"iframe\")",
/*expect_success_v1=*/true,
/*expect_success_v2=*/true,
/*expect_success=*/true,
/*expected_match_dest=*/
{mojom::RequestDestination::kDocument, mojom::RequestDestination::kFrame,
mojom::RequestDestination::kIframe}},
// Valid `match-dest` value ("document" "frame" "iframe" "").
{"match=\"test\", match-dest=(\"document\" \"\")",
/*expect_success_v1=*/true,
/*expect_success_v2=*/true,
/*expect_success=*/true,
/*expected_match_dest=*/
{mojom::RequestDestination::kEmpty,
mojom::RequestDestination::kDocument}},
@ -886,14 +784,7 @@ TEST_P(SharedDictionaryManagerTest, WriterForUseAsDictionaryMatchDestOption) {
/*access_allowed_check_callback=*/base::BindOnce([]() {
return true;
}));
switch (GetVersion()) {
case features::CompressionDictionaryTransportBackendVersion::kV1:
EXPECT_EQ(testcase.expect_success_v1, !!writer);
continue;
case features::CompressionDictionaryTransportBackendVersion::kV2:
EXPECT_EQ(testcase.expect_success_v2, !!writer);
break;
}
EXPECT_EQ(testcase.expect_success, !!writer);
if (!writer) {
continue;
}
@ -922,13 +813,12 @@ TEST_P(SharedDictionaryManagerTest, InvalidMatch) {
ASSERT_TRUE(storage);
struct {
std::string header_string;
bool expect_success_v1;
bool expect_success_v2;
bool expect_success;
} kTestCases[] = {
// Invalid as a constructor string of URLPattern.
{"{", true, false},
{"{", false},
// Unsupported regexp group.
{"(a|b)", true, false},
{"(a|b)", false},
};
for (const auto& testcase : kTestCases) {
SCOPED_TRACE(
@ -947,14 +837,7 @@ TEST_P(SharedDictionaryManagerTest, InvalidMatch) {
/*access_allowed_check_callback=*/base::BindOnce([]() {
return true;
}));
switch (GetVersion()) {
case features::CompressionDictionaryTransportBackendVersion::kV1:
EXPECT_EQ(testcase.expect_success_v1, !!writer);
break;
case features::CompressionDictionaryTransportBackendVersion::kV2:
EXPECT_EQ(testcase.expect_success_v2, !!writer);
break;
}
EXPECT_EQ(testcase.expect_success, !!writer);
}
}

@ -153,11 +153,9 @@ SharedDictionaryNetworkTransaction::ParseSharedDictionaryEncodingType(
result = SharedDictionaryEncodingType::kSharedZstd;
}
// Check "Content-Dictionary" header if V2 backend is enabled, and the
// content encoding indicates that a dictionary is used.
if (features::kCompressionDictionaryTransportBackendVersion.Get() !=
features::CompressionDictionaryTransportBackendVersion::kV1 &&
result != SharedDictionaryEncodingType::kNotUsed) {
// Check "Content-Dictionary" header when the content encoding indicates that
// a dictionary is used.
if (result != SharedDictionaryEncodingType::kNotUsed) {
CHECK(!dictionary_hash_base64_.empty());
std::string content_dictionary;
if (!headers.GetNormalizedHeader(
@ -262,21 +260,11 @@ void SharedDictionaryNetworkTransaction::ModifyRequestHeaders(
shared_dictionary_.reset();
return;
}
switch (features::kCompressionDictionaryTransportBackendVersion.Get()) {
case features::CompressionDictionaryTransportBackendVersion::kV1:
request_headers->SetHeader(
network::shared_dictionary::kSecAvailableDictionaryHeaderName,
base::ToLowerASCII(base::HexEncode(shared_dictionary_->hash().data)));
break;
case features::CompressionDictionaryTransportBackendVersion::kV2:
dictionary_hash_base64_ = base::StrCat(
{":", base::Base64Encode(shared_dictionary_->hash().data), ":"});
request_headers->SetHeader(
network::shared_dictionary::kAvailableDictionaryHeaderName,
dictionary_hash_base64_);
break;
}
dictionary_hash_base64_ = base::StrCat(
{":", base::Base64Encode(shared_dictionary_->hash().data), ":"});
request_headers->SetHeader(
network::shared_dictionary::kAvailableDictionaryHeaderName,
dictionary_hash_base64_);
if (base::FeatureList::IsEnabled(network::features::kSharedZstd)) {
AddAcceptEncoding(request_headers,
base::StrCat({GetSharedBrotliContentEncodingName(), ", ",

@ -153,7 +153,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) SharedDictionaryNetworkTransaction
scoped_refptr<SharedDictionaryStorage> shared_dictionary_storage_;
std::unique_ptr<SharedDictionary> shared_dictionary_;
// The Structured Field sf-binary hash of sha256 of dictionary calculated when
// sending a HTTP request. This is not used when V1 backend is used.
// sending a HTTP request.
std::string dictionary_hash_base64_;
DictionaryStatus dictionary_status_ = DictionaryStatus::kNoDictionary;

@ -224,20 +224,10 @@ static void BrotliTestTransactionHandler(const net::HttpRequestInfo* request,
std::string* response_headers,
std::string* response_data) {
std::string header_value;
switch (features::kCompressionDictionaryTransportBackendVersion.Get()) {
case features::CompressionDictionaryTransportBackendVersion::kV1:
EXPECT_TRUE(request->extra_headers.GetHeader(
network::shared_dictionary::kSecAvailableDictionaryHeaderName,
&header_value));
EXPECT_EQ(kTestDictionarySha256, header_value);
break;
case features::CompressionDictionaryTransportBackendVersion::kV2:
EXPECT_TRUE(request->extra_headers.GetHeader(
network::shared_dictionary::kAvailableDictionaryHeaderName,
&header_value));
EXPECT_EQ(kTestDictionarySha256Base64, header_value);
break;
}
EXPECT_TRUE(request->extra_headers.GetHeader(
network::shared_dictionary::kAvailableDictionaryHeaderName,
&header_value));
EXPECT_EQ(kTestDictionarySha256Base64, header_value);
*response_data = kBrotliEncodedDataString;
}
@ -246,20 +236,10 @@ static void ZstdTestTransactionHandler(const net::HttpRequestInfo* request,
std::string* response_headers,
std::string* response_data) {
std::string header_value;
switch (features::kCompressionDictionaryTransportBackendVersion.Get()) {
case features::CompressionDictionaryTransportBackendVersion::kV1:
EXPECT_TRUE(request->extra_headers.GetHeader(
network::shared_dictionary::kSecAvailableDictionaryHeaderName,
&header_value));
EXPECT_EQ(kTestDictionarySha256, header_value);
break;
case features::CompressionDictionaryTransportBackendVersion::kV2:
EXPECT_TRUE(request->extra_headers.GetHeader(
network::shared_dictionary::kAvailableDictionaryHeaderName,
&header_value));
EXPECT_EQ(kTestDictionarySha256Base64, header_value);
break;
}
*response_data = kZstdEncodedDataString;
}
@ -269,37 +249,11 @@ static const auto kTestTransactionHandlerWithoutAvailableDictionary =
std::string* response_headers,
std::string* response_data) {
EXPECT_FALSE(request->extra_headers.HasHeader(
network::shared_dictionary::kSecAvailableDictionaryHeaderName));
network::shared_dictionary::kAvailableDictionaryHeaderName));
*response_data = kTestData;
});
const net::MockTransaction kBrotliDictionaryTestTransactionV1 = {
.url = "https://test.example/test",
.method = "GET",
.request_time = base::Time(),
.request_headers = "sec-fetch-dest: document\r\n",
.load_flags = net::LOAD_CAN_USE_SHARED_DICTIONARY,
.transport_info = TestSpdyTransportInfo(),
.status = "HTTP/1.1 200 OK",
.response_headers =
"content-encoding: sbr\n"
"content-dictionary: :wZcortNlA8/IGg9TWeb0cuEh93vyCi+qx5lBkSk8BiM=:\n",
.response_time = base::Time(),
.data = "", // We set the body in the `handler` function.
.dns_aliases = {},
.fps_cache_filter = std::nullopt,
.browser_run_id = std::nullopt,
.test_mode = net::TEST_MODE_NORMAL,
.handler = base::BindRepeating(&BrotliTestTransactionHandler),
.read_handler = net::MockTransactionReadHandler(),
.cert = nullptr,
.cert_status = 0,
.ssl_connection_status = 0,
.start_return_code = net::OK,
.read_return_code = net::OK,
};
const net::MockTransaction kBrotliDictionaryTestTransactionV2 = {
const net::MockTransaction kBrotliDictionaryTestTransaction = {
.url = "https://test.example/test",
.method = "GET",
.request_time = base::Time(),
@ -351,42 +305,20 @@ const net::MockTransaction kZstdDictionaryTestTransaction = {
.read_return_code = net::OK,
};
class SharedDictionaryNetworkTransactionTestBase : public ::testing::Test {
class SharedDictionaryNetworkTransactionTest : public ::testing::Test {
public:
explicit SharedDictionaryNetworkTransactionTestBase(
network::features::CompressionDictionaryTransportBackendVersion version)
: version_(version),
network_layer_(std::make_unique<net::MockNetworkLayer>()) {
scoped_feature_list_.InitWithFeaturesAndParameters(
/*enabled_features=*/
{base::test::FeatureRefAndParams(
network::features::kCompressionDictionaryTransportBackend,
{{network::features::kCompressionDictionaryTransportBackendVersion
.name,
network::features::kCompressionDictionaryTransportBackendVersion
.GetName(GetVersion())}})},
/*disabled_features=*/{});
net::AddMockTransaction(&GetBrotliDictionaryTestTransaction());
SharedDictionaryNetworkTransactionTest()
: network_layer_(std::make_unique<net::MockNetworkLayer>()) {
net::AddMockTransaction(&kBrotliDictionaryTestTransaction);
}
~SharedDictionaryNetworkTransactionTestBase() override = default;
~SharedDictionaryNetworkTransactionTest() override = default;
SharedDictionaryNetworkTransactionTestBase(
const SharedDictionaryNetworkTransactionTestBase&) = delete;
SharedDictionaryNetworkTransactionTestBase& operator=(
const SharedDictionaryNetworkTransactionTestBase&) = delete;
SharedDictionaryNetworkTransactionTest(
const SharedDictionaryNetworkTransactionTest&) = delete;
SharedDictionaryNetworkTransactionTest& operator=(
const SharedDictionaryNetworkTransactionTest&) = delete;
protected:
network::features::CompressionDictionaryTransportBackendVersion GetVersion() {
return version_;
}
const net::MockTransaction& GetBrotliDictionaryTestTransaction() {
switch (GetVersion()) {
case network::features::CompressionDictionaryTransportBackendVersion::kV1:
return kBrotliDictionaryTestTransactionV1;
case network::features::CompressionDictionaryTransportBackendVersion::kV2:
return kBrotliDictionaryTestTransactionV2;
}
}
std::unique_ptr<net::HttpTransaction> CreateNetworkTransaction() {
std::unique_ptr<net::HttpTransaction> network_transaction;
network_layer_->CreateTransaction(net::DEFAULT_PRIORITY,
@ -399,59 +331,17 @@ class SharedDictionaryNetworkTransactionTestBase : public ::testing::Test {
net::MockNetworkLayer& network_layer() { return *network_layer_.get(); }
private:
const network::features::CompressionDictionaryTransportBackendVersion
version_;
std::unique_ptr<net::MockNetworkLayer> network_layer_;
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
base::test::ScopedFeatureList scoped_feature_list_;
};
class SharedDictionaryNetworkTransactionTest
: public SharedDictionaryNetworkTransactionTestBase,
public ::testing::WithParamInterface<
network::features::CompressionDictionaryTransportBackendVersion> {
public:
SharedDictionaryNetworkTransactionTest()
: SharedDictionaryNetworkTransactionTestBase(GetVersion()) {}
~SharedDictionaryNetworkTransactionTest() override = default;
SharedDictionaryNetworkTransactionTest(
const SharedDictionaryNetworkTransactionTest&) = delete;
SharedDictionaryNetworkTransactionTest& operator=(
const SharedDictionaryNetworkTransactionTest&) = delete;
protected:
network::features::CompressionDictionaryTransportBackendVersion GetVersion() {
return GetParam();
}
};
INSTANTIATE_TEST_SUITE_P(
All,
SharedDictionaryNetworkTransactionTest,
testing::Values(
network::features::CompressionDictionaryTransportBackendVersion::kV1,
network::features::CompressionDictionaryTransportBackendVersion::kV2),
[](const testing::TestParamInfo<
network::features::CompressionDictionaryTransportBackendVersion>&
info) {
switch (info.param) {
case network::features::CompressionDictionaryTransportBackendVersion::
kV1:
return "V1";
case network::features::CompressionDictionaryTransportBackendVersion::
kV2:
return "V2";
}
});
TEST_P(SharedDictionaryNetworkTransactionTest, SyncDictionary) {
TEST_F(SharedDictionaryNetworkTransactionTest, SyncDictionary) {
DummySharedDictionaryManager manager(
base::MakeRefCounted<DummySharedDictionaryStorage>(
std::make_unique<DummySyncDictionary>(kTestDictionaryData)));
net::MockHttpRequest request(GetBrotliDictionaryTestTransaction());
net::MockHttpRequest request(kBrotliDictionaryTestTransaction);
SharedDictionaryNetworkTransaction transaction(manager,
CreateNetworkTransaction());
transaction.SetIsSharedDictionaryReadAllowedCallback(
@ -474,15 +364,14 @@ TEST_P(SharedDictionaryNetworkTransactionTest, SyncDictionary) {
EXPECT_EQ(kTestData, std::string(buf->data(), read_result));
}
TEST_P(SharedDictionaryNetworkTransactionTest, NotAllowedToUseDictionary) {
TEST_F(SharedDictionaryNetworkTransactionTest, NotAllowedToUseDictionary) {
DummySharedDictionaryManager manager(
base::MakeRefCounted<DummySharedDictionaryStorage>(
std::make_unique<DummySyncDictionary>(kTestDictionaryData)));
// Override MockTransaction to check that there is no available-dictionary
// header.
net::MockTransaction new_mock_transaction =
GetBrotliDictionaryTestTransaction();
net::MockTransaction new_mock_transaction = kBrotliDictionaryTestTransaction;
new_mock_transaction.handler =
kTestTransactionHandlerWithoutAvailableDictionary;
net::AddMockTransaction(&new_mock_transaction);
@ -510,15 +399,14 @@ TEST_P(SharedDictionaryNetworkTransactionTest, NotAllowedToUseDictionary) {
EXPECT_EQ(kTestData, std::string(buf->data(), read_result));
}
TEST_P(SharedDictionaryNetworkTransactionTest, DictionaryId) {
TEST_F(SharedDictionaryNetworkTransactionTest, DictionaryId) {
DummySharedDictionaryManager manager(
base::MakeRefCounted<DummySharedDictionaryStorage>(
std::make_unique<DummySyncDictionary>(kTestDictionaryData,
"test-id")));
// Override MockTransaction to check the dictionary-id header
net::MockTransaction new_mock_transaction =
GetBrotliDictionaryTestTransaction();
net::MockTransaction new_mock_transaction = kBrotliDictionaryTestTransaction;
new_mock_transaction.handler = base::BindRepeating(
[](const net::HttpRequestInfo* request, std::string* response_status,
std::string* response_headers, std::string* response_data) {
@ -553,7 +441,7 @@ TEST_P(SharedDictionaryNetworkTransactionTest, DictionaryId) {
EXPECT_EQ(kTestData, std::string(buf->data(), read_result));
}
TEST_P(SharedDictionaryNetworkTransactionTest,
TEST_F(SharedDictionaryNetworkTransactionTest,
DictionaryIdWithBackSlashAndDquote) {
DummySharedDictionaryManager manager(
base::MakeRefCounted<DummySharedDictionaryStorage>(
@ -561,8 +449,7 @@ TEST_P(SharedDictionaryNetworkTransactionTest,
"test\\dictionary\"id")));
// Override MockTransaction to check the dictionary-id header
net::MockTransaction new_mock_transaction =
GetBrotliDictionaryTestTransaction();
net::MockTransaction new_mock_transaction = kBrotliDictionaryTestTransaction;
new_mock_transaction.handler = base::BindRepeating(
[](const net::HttpRequestInfo* request, std::string* response_status,
std::string* response_headers, std::string* response_data) {
@ -597,14 +484,13 @@ TEST_P(SharedDictionaryNetworkTransactionTest,
EXPECT_EQ(kTestData, std::string(buf->data(), read_result));
}
TEST_P(SharedDictionaryNetworkTransactionTest, EmptyDictionaryId) {
TEST_F(SharedDictionaryNetworkTransactionTest, EmptyDictionaryId) {
DummySharedDictionaryManager manager(
base::MakeRefCounted<DummySharedDictionaryStorage>(
std::make_unique<DummySyncDictionary>(kTestDictionaryData, "")));
// Override MockTransaction to check the dictionary-id header
net::MockTransaction new_mock_transaction =
GetBrotliDictionaryTestTransaction();
net::MockTransaction new_mock_transaction = kBrotliDictionaryTestTransaction;
new_mock_transaction.handler = base::BindRepeating(
[](const net::HttpRequestInfo* request, std::string* response_status,
std::string* response_headers, std::string* response_data) {
@ -636,7 +522,7 @@ TEST_P(SharedDictionaryNetworkTransactionTest, EmptyDictionaryId) {
EXPECT_EQ(kTestData, std::string(buf->data(), read_result));
}
TEST_P(SharedDictionaryNetworkTransactionTest,
TEST_F(SharedDictionaryNetworkTransactionTest,
RequireKnownRootCertCheckFailure) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(
@ -647,8 +533,7 @@ TEST_P(SharedDictionaryNetworkTransactionTest,
// Override MockTransaction to check that there is no available-dictionary
// header.
net::MockTransaction new_mock_transaction =
GetBrotliDictionaryTestTransaction();
net::MockTransaction new_mock_transaction = kBrotliDictionaryTestTransaction;
new_mock_transaction.handler =
kTestTransactionHandlerWithoutAvailableDictionary;
new_mock_transaction.transport_info.cert_is_issued_by_known_root = false;
@ -678,7 +563,7 @@ TEST_P(SharedDictionaryNetworkTransactionTest,
EXPECT_EQ(kTestData, std::string(buf->data(), read_result));
}
TEST_P(SharedDictionaryNetworkTransactionTest,
TEST_F(SharedDictionaryNetworkTransactionTest,
RequireKnownRootCertCheckSuccess) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(
@ -689,8 +574,7 @@ TEST_P(SharedDictionaryNetworkTransactionTest,
// The BrotliTestTransactionHandler `new_mock_transaction.handler` will check
// that the there is a correct available-dictionary request header.
net::MockTransaction new_mock_transaction =
GetBrotliDictionaryTestTransaction();
net::MockTransaction new_mock_transaction = kBrotliDictionaryTestTransaction;
new_mock_transaction.transport_info.cert_is_issued_by_known_root = true;
net::AddMockTransaction(&new_mock_transaction);
@ -718,7 +602,7 @@ TEST_P(SharedDictionaryNetworkTransactionTest,
EXPECT_EQ(kTestData, std::string(buf->data(), read_result));
}
TEST_P(SharedDictionaryNetworkTransactionTest,
TEST_F(SharedDictionaryNetworkTransactionTest,
RequireKnownRootCertCheckSuccessForLocalhost) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(
@ -729,8 +613,7 @@ TEST_P(SharedDictionaryNetworkTransactionTest,
// The BrotliTestTransactionHandler `new_mock_transaction.handler` will check
// that the there is a correct available-dictionary request header.
net::MockTransaction new_mock_transaction =
GetBrotliDictionaryTestTransaction();
net::MockTransaction new_mock_transaction = kBrotliDictionaryTestTransaction;
new_mock_transaction.url = "http:///localhost:1234/test";
new_mock_transaction.transport_info.cert_is_issued_by_known_root = false;
@ -759,14 +642,13 @@ TEST_P(SharedDictionaryNetworkTransactionTest,
EXPECT_EQ(kTestData, std::string(buf->data(), read_result));
}
TEST_P(SharedDictionaryNetworkTransactionTest, NoMatchingDictionary) {
TEST_F(SharedDictionaryNetworkTransactionTest, NoMatchingDictionary) {
DummySharedDictionaryManager manager(
base::MakeRefCounted<DummySharedDictionaryStorage>(nullptr));
// Override MockTransaction to check that there is no available-dictionary
// header.
net::MockTransaction new_mock_transaction =
GetBrotliDictionaryTestTransaction();
net::MockTransaction new_mock_transaction = kBrotliDictionaryTestTransaction;
new_mock_transaction.handler =
kTestTransactionHandlerWithoutAvailableDictionary;
net::AddMockTransaction(&new_mock_transaction);
@ -794,15 +676,14 @@ TEST_P(SharedDictionaryNetworkTransactionTest, NoMatchingDictionary) {
EXPECT_EQ(kTestData, std::string(buf->data(), read_result));
}
TEST_P(SharedDictionaryNetworkTransactionTest, OpaqueFrameOrigin) {
TEST_F(SharedDictionaryNetworkTransactionTest, OpaqueFrameOrigin) {
DummySharedDictionaryManager manager(
base::MakeRefCounted<DummySharedDictionaryStorage>(
std::make_unique<DummySyncDictionary>(kTestDictionaryData)));
// Override MockTransaction to check that there is no available-dictionary
// header.
net::MockTransaction new_mock_transaction =
GetBrotliDictionaryTestTransaction();
net::MockTransaction new_mock_transaction = kBrotliDictionaryTestTransaction;
new_mock_transaction.handler =
kTestTransactionHandlerWithoutAvailableDictionary;
net::AddMockTransaction(&new_mock_transaction);
@ -831,13 +712,12 @@ TEST_P(SharedDictionaryNetworkTransactionTest, OpaqueFrameOrigin) {
EXPECT_EQ(kTestData, std::string(buf->data(), read_result));
}
TEST_P(SharedDictionaryNetworkTransactionTest, WithoutValidLoadFlag) {
TEST_F(SharedDictionaryNetworkTransactionTest, WithoutValidLoadFlag) {
DummySharedDictionaryManager manager(/*storage=*/nullptr);
// Override MockTransaction to check that there is no available-dictionary
// header.
net::MockTransaction new_mock_transaction =
GetBrotliDictionaryTestTransaction();
net::MockTransaction new_mock_transaction = kBrotliDictionaryTestTransaction;
new_mock_transaction.handler =
kTestTransactionHandlerWithoutAvailableDictionary;
net::AddMockTransaction(&new_mock_transaction);
@ -871,14 +751,13 @@ TEST_P(SharedDictionaryNetworkTransactionTest, WithoutValidLoadFlag) {
EXPECT_FALSE(manager.create_storage_called());
}
TEST_P(SharedDictionaryNetworkTransactionTest, NoSbrContentEncoding) {
TEST_F(SharedDictionaryNetworkTransactionTest, NoSbrContentEncoding) {
DummySharedDictionaryManager manager(
base::MakeRefCounted<DummySharedDictionaryStorage>(
std::make_unique<DummySyncDictionary>(kTestDictionaryData)));
// Override MockTransaction to remove `content-encoding: sbr`.
net::MockTransaction new_mock_transaction =
GetBrotliDictionaryTestTransaction();
net::MockTransaction new_mock_transaction = kBrotliDictionaryTestTransaction;
new_mock_transaction.response_headers = "";
net::AddMockTransaction(&new_mock_transaction);
@ -908,23 +787,14 @@ TEST_P(SharedDictionaryNetworkTransactionTest, NoSbrContentEncoding) {
EXPECT_EQ(kBrotliEncodedDataString, std::string(buf->data(), read_result));
}
TEST_P(SharedDictionaryNetworkTransactionTest, NoContentDictionary) {
TEST_F(SharedDictionaryNetworkTransactionTest, NoContentDictionary) {
DummySharedDictionaryManager manager(
base::MakeRefCounted<DummySharedDictionaryStorage>(
std::make_unique<DummySyncDictionary>(kTestDictionaryData)));
// Override MockTransaction to remove "content-dictionary" header.
net::MockTransaction new_mock_transaction =
GetBrotliDictionaryTestTransaction();
switch (GetVersion()) {
case network::features::CompressionDictionaryTransportBackendVersion::kV1:
new_mock_transaction.response_headers = "content-encoding: sbr\n";
break;
case network::features::CompressionDictionaryTransportBackendVersion::kV2:
new_mock_transaction.response_headers = "content-encoding: br-d\n";
break;
}
net::MockTransaction new_mock_transaction = kBrotliDictionaryTestTransaction;
new_mock_transaction.response_headers = "content-encoding: br-d\n";
net::AddMockTransaction(&new_mock_transaction);
net::MockHttpRequest request(new_mock_transaction);
@ -937,56 +807,26 @@ TEST_P(SharedDictionaryNetworkTransactionTest, NoContentDictionary) {
ASSERT_THAT(transaction.Start(&request, start_callback.callback(),
net::NetLogWithSource()),
net::test::IsError(net::ERR_IO_PENDING));
if (GetVersion() ==
network::features::CompressionDictionaryTransportBackendVersion::kV2) {
EXPECT_THAT(start_callback.WaitForResult(),
net::test::IsError(net::ERR_DICTIONARY_LOAD_FAILED));
return;
}
// When V1 backend is used, even if there is no "content-dictionary"
// header, SharedDictionaryNetworkTransaction can decode the body.
EXPECT_THAT(start_callback.WaitForResult(), net::test::IsError(net::OK));
scoped_refptr<net::IOBufferWithSize> buf =
base::MakeRefCounted<net::IOBufferWithSize>(kDefaultBufferSize);
net::TestCompletionCallback read_callback;
ASSERT_THAT(
transaction.Read(buf.get(), buf->size(), read_callback.callback()),
net::test::IsError(net::ERR_IO_PENDING));
int read_result = read_callback.WaitForResult();
EXPECT_THAT(read_result, kTestData.size());
EXPECT_EQ(kTestData, std::string(buf->data(), read_result));
EXPECT_THAT(start_callback.WaitForResult(),
net::test::IsError(net::ERR_DICTIONARY_LOAD_FAILED));
}
TEST_P(SharedDictionaryNetworkTransactionTest, WrongContentDictionary) {
TEST_F(SharedDictionaryNetworkTransactionTest, WrongContentDictionary) {
DummySharedDictionaryManager manager(
base::MakeRefCounted<DummySharedDictionaryStorage>(
std::make_unique<DummySyncDictionary>(kTestDictionaryData)));
// Override MockTransaction to change the "content-dictionary" header.
net::MockTransaction new_mock_transaction =
GetBrotliDictionaryTestTransaction();
net::MockTransaction new_mock_transaction = kBrotliDictionaryTestTransaction;
// The hash `kTestDictionaryData` is
// ":wZcortNlA8/IGg9TWeb0cuEh93vyCi+qx5lBkSk8BiM=:". But the header contains
// "content-dictionary" header with a different hash
// ":U5abz16WDg7b8KS93msLPpOB4Vbef1uRzoORYkJw9BY=:".
switch (GetVersion()) {
case network::features::CompressionDictionaryTransportBackendVersion::kV1:
new_mock_transaction.response_headers =
"content-encoding: sbr\n"
"content-dictionary: "
":U5abz16WDg7b8KS93msLPpOB4Vbef1uRzoORYkJw9BY=:\n";
break;
case network::features::CompressionDictionaryTransportBackendVersion::kV2:
new_mock_transaction.response_headers =
"content-encoding: br-d\n"
"content-dictionary: "
":U5abz16WDg7b8KS93msLPpOB4Vbef1uRzoORYkJw9BY=:\n";
break;
}
new_mock_transaction.response_headers =
"content-encoding: br-d\n"
"content-dictionary: "
":U5abz16WDg7b8KS93msLPpOB4Vbef1uRzoORYkJw9BY=:\n";
net::AddMockTransaction(&new_mock_transaction);
net::MockHttpRequest request(new_mock_transaction);
@ -999,35 +839,17 @@ TEST_P(SharedDictionaryNetworkTransactionTest, WrongContentDictionary) {
ASSERT_THAT(transaction.Start(&request, start_callback.callback(),
net::NetLogWithSource()),
net::test::IsError(net::ERR_IO_PENDING));
if (GetVersion() ==
network::features::CompressionDictionaryTransportBackendVersion::kV2) {
EXPECT_THAT(start_callback.WaitForResult(),
net::test::IsError(net::ERR_DICTIONARY_LOAD_FAILED));
return;
}
// When V1 backend is used, even if there is a wrong "content-dictionary"
// header, SharedDictionaryNetworkTransaction can decode the body.
EXPECT_THAT(start_callback.WaitForResult(), net::test::IsError(net::OK));
scoped_refptr<net::IOBufferWithSize> buf =
base::MakeRefCounted<net::IOBufferWithSize>(kDefaultBufferSize);
net::TestCompletionCallback read_callback;
ASSERT_THAT(
transaction.Read(buf.get(), buf->size(), read_callback.callback()),
net::test::IsError(net::ERR_IO_PENDING));
int read_result = read_callback.WaitForResult();
EXPECT_THAT(read_result, kTestData.size());
EXPECT_EQ(kTestData, std::string(buf->data(), read_result));
EXPECT_THAT(start_callback.WaitForResult(),
net::test::IsError(net::ERR_DICTIONARY_LOAD_FAILED));
}
TEST_P(SharedDictionaryNetworkTransactionTest, MultipleContentEncodingWithSbr) {
TEST_F(SharedDictionaryNetworkTransactionTest, MultipleContentEncodingWithSbr) {
DummySharedDictionaryManager manager(
base::MakeRefCounted<DummySharedDictionaryStorage>(
std::make_unique<DummySyncDictionary>(kTestDictionaryData)));
// Override MockTransaction to set `content-encoding: sbr, deflate`.
net::MockTransaction new_mock_transaction =
GetBrotliDictionaryTestTransaction();
net::MockTransaction new_mock_transaction = kBrotliDictionaryTestTransaction;
new_mock_transaction.response_headers = "content-encoding: sbr, deflate\n";
net::AddMockTransaction(&new_mock_transaction);
@ -1057,7 +879,7 @@ TEST_P(SharedDictionaryNetworkTransactionTest, MultipleContentEncodingWithSbr) {
EXPECT_EQ(kBrotliEncodedDataString, std::string(buf->data(), read_result));
}
TEST_P(SharedDictionaryNetworkTransactionTest,
TEST_F(SharedDictionaryNetworkTransactionTest,
AsyncDictionarySuccessBeforeStartReading) {
std::unique_ptr<DummyAsyncDictionary> dictionary =
std::make_unique<DummyAsyncDictionary>(kTestDictionaryData);
@ -1066,7 +888,7 @@ TEST_P(SharedDictionaryNetworkTransactionTest,
base::MakeRefCounted<DummySharedDictionaryStorage>(
std::move(dictionary)));
net::MockHttpRequest request(GetBrotliDictionaryTestTransaction());
net::MockHttpRequest request(kBrotliDictionaryTestTransaction);
SharedDictionaryNetworkTransaction transaction(manager,
CreateNetworkTransaction());
transaction.SetIsSharedDictionaryReadAllowedCallback(
@ -1094,7 +916,7 @@ TEST_P(SharedDictionaryNetworkTransactionTest,
EXPECT_EQ(kTestData, std::string(buf->data(), read_result));
}
TEST_P(SharedDictionaryNetworkTransactionTest,
TEST_F(SharedDictionaryNetworkTransactionTest,
AsyncDictionarySuccessAfterStartReading) {
std::unique_ptr<DummyAsyncDictionary> dictionary =
std::make_unique<DummyAsyncDictionary>(kTestDictionaryData);
@ -1103,7 +925,7 @@ TEST_P(SharedDictionaryNetworkTransactionTest,
base::MakeRefCounted<DummySharedDictionaryStorage>(
std::move(dictionary)));
net::MockHttpRequest request(GetBrotliDictionaryTestTransaction());
net::MockHttpRequest request(kBrotliDictionaryTestTransaction);
SharedDictionaryNetworkTransaction transaction(manager,
CreateNetworkTransaction());
transaction.SetIsSharedDictionaryReadAllowedCallback(
@ -1135,7 +957,7 @@ TEST_P(SharedDictionaryNetworkTransactionTest,
EXPECT_EQ(kTestData, std::string(buf->data(), read_result));
}
TEST_P(SharedDictionaryNetworkTransactionTest,
TEST_F(SharedDictionaryNetworkTransactionTest,
AsyncDictionarySuccessAfterTransactionDestroy) {
std::unique_ptr<DummyAsyncDictionary> dictionary =
std::make_unique<DummyAsyncDictionary>(kTestDictionaryData);
@ -1144,7 +966,7 @@ TEST_P(SharedDictionaryNetworkTransactionTest,
base::MakeRefCounted<DummySharedDictionaryStorage>(
std::move(dictionary)));
net::MockHttpRequest request(GetBrotliDictionaryTestTransaction());
net::MockHttpRequest request(kBrotliDictionaryTestTransaction);
std::unique_ptr<SharedDictionaryNetworkTransaction> transaction =
std::make_unique<SharedDictionaryNetworkTransaction>(
manager, CreateNetworkTransaction());
@ -1177,7 +999,7 @@ TEST_P(SharedDictionaryNetworkTransactionTest,
EXPECT_FALSE(read_callback.have_result());
}
TEST_P(SharedDictionaryNetworkTransactionTest,
TEST_F(SharedDictionaryNetworkTransactionTest,
AsyncDictionaryFailureBeforeStartReading) {
std::unique_ptr<DummyAsyncDictionary> dictionary =
std::make_unique<DummyAsyncDictionary>(kTestDictionaryData);
@ -1186,7 +1008,7 @@ TEST_P(SharedDictionaryNetworkTransactionTest,
base::MakeRefCounted<DummySharedDictionaryStorage>(
std::move(dictionary)));
net::MockHttpRequest request(GetBrotliDictionaryTestTransaction());
net::MockHttpRequest request(kBrotliDictionaryTestTransaction);
SharedDictionaryNetworkTransaction transaction(manager,
CreateNetworkTransaction());
transaction.SetIsSharedDictionaryReadAllowedCallback(
@ -1211,7 +1033,7 @@ TEST_P(SharedDictionaryNetworkTransactionTest,
net::test::IsError(net::ERR_DICTIONARY_LOAD_FAILED));
}
TEST_P(SharedDictionaryNetworkTransactionTest,
TEST_F(SharedDictionaryNetworkTransactionTest,
AsyncDictionaryFailureAfterStartReading) {
std::unique_ptr<DummyAsyncDictionary> dictionary =
std::make_unique<DummyAsyncDictionary>(kTestDictionaryData);
@ -1220,7 +1042,7 @@ TEST_P(SharedDictionaryNetworkTransactionTest,
base::MakeRefCounted<DummySharedDictionaryStorage>(
std::move(dictionary)));
net::MockHttpRequest request(GetBrotliDictionaryTestTransaction());
net::MockHttpRequest request(kBrotliDictionaryTestTransaction);
SharedDictionaryNetworkTransaction transaction(manager,
CreateNetworkTransaction());
transaction.SetIsSharedDictionaryReadAllowedCallback(
@ -1250,7 +1072,7 @@ TEST_P(SharedDictionaryNetworkTransactionTest,
EXPECT_EQ(net::ERR_DICTIONARY_LOAD_FAILED, read_callback.WaitForResult());
}
TEST_P(SharedDictionaryNetworkTransactionTest, Restart) {
TEST_F(SharedDictionaryNetworkTransactionTest, Restart) {
DummySharedDictionaryManager manager(
base::MakeRefCounted<DummySharedDictionaryStorage>(nullptr));
@ -1291,7 +1113,7 @@ TEST_P(SharedDictionaryNetworkTransactionTest, Restart) {
ASSERT_FALSE(transaction.IsReadyToRestartForAuth());
}
TEST_P(SharedDictionaryNetworkTransactionTest, StopCaching) {
TEST_F(SharedDictionaryNetworkTransactionTest, StopCaching) {
DummySharedDictionaryManager manager(
base::MakeRefCounted<DummySharedDictionaryStorage>(nullptr));
SharedDictionaryNetworkTransaction transaction(manager,
@ -1301,7 +1123,7 @@ TEST_P(SharedDictionaryNetworkTransactionTest, StopCaching) {
EXPECT_TRUE(network_layer().stop_caching_called());
}
TEST_P(SharedDictionaryNetworkTransactionTest, DoneReading) {
TEST_F(SharedDictionaryNetworkTransactionTest, DoneReading) {
DummySharedDictionaryManager manager(
base::MakeRefCounted<DummySharedDictionaryStorage>(nullptr));
@ -1312,7 +1134,7 @@ TEST_P(SharedDictionaryNetworkTransactionTest, DoneReading) {
EXPECT_TRUE(network_layer().done_reading_called());
}
TEST_P(SharedDictionaryNetworkTransactionTest, GetLoadState) {
TEST_F(SharedDictionaryNetworkTransactionTest, GetLoadState) {
DummySharedDictionaryManager manager(
base::MakeRefCounted<DummySharedDictionaryStorage>(nullptr));
@ -1341,7 +1163,7 @@ TEST_P(SharedDictionaryNetworkTransactionTest, GetLoadState) {
EXPECT_EQ(net::LOAD_STATE_READING_RESPONSE, transaction.GetLoadState());
}
TEST_P(SharedDictionaryNetworkTransactionTest, SharedZstd) {
TEST_F(SharedDictionaryNetworkTransactionTest, SharedZstd) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(network::features::kSharedZstd);
@ -1376,7 +1198,7 @@ TEST_P(SharedDictionaryNetworkTransactionTest, SharedZstd) {
EXPECT_EQ(kTestData, std::string(buf->data(), read_result));
}
TEST_P(SharedDictionaryNetworkTransactionTest, NoZstdDContentEncoding) {
TEST_F(SharedDictionaryNetworkTransactionTest, NoZstdDContentEncoding) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(network::features::kSharedZstd);
@ -1458,18 +1280,13 @@ std::string ToString(ProtocolCheckHostTestCase host_type) {
}
class SharedDictionaryNetworkTransactionProtocolCheckTest
: public SharedDictionaryNetworkTransactionTestBase,
: public SharedDictionaryNetworkTransactionTest,
public testing::WithParamInterface<
std::tuple<ProtocolCheckFeatureTestCase,
ProtocolCheckProtocolTestCase,
ProtocolCheckHostTestCase>> {
public:
SharedDictionaryNetworkTransactionProtocolCheckTest()
: SharedDictionaryNetworkTransactionTestBase(
// Protocol check logic doesn't depend on versions. So we just check
// the V2 behavior.
network::features::CompressionDictionaryTransportBackendVersion::
kV2) {
SharedDictionaryNetworkTransactionProtocolCheckTest() {
std::vector<base::test::FeatureRef> enabled_features;
std::vector<base::test::FeatureRef> disabled_features;
if (AllowHttp1()) {
@ -1489,8 +1306,7 @@ class SharedDictionaryNetworkTransactionProtocolCheckTest
protected:
net::MockTransaction CreateMockTransaction() {
net::MockTransaction mock_transaction =
GetBrotliDictionaryTestTransaction();
net::MockTransaction mock_transaction = kBrotliDictionaryTestTransaction;
if (IsLocalHost()) {
mock_transaction.url = "http://localhost/test";
}

@ -35,7 +35,7 @@ class DictionaryHeaderInfo {
public:
DictionaryHeaderInfo(std::string match,
std::set<network::mojom::RequestDestination> match_dest,
std::optional<base::TimeDelta> expiration,
base::TimeDelta expiration,
std::string type,
std::string id)
: match(std::move(match)),
@ -47,9 +47,7 @@ class DictionaryHeaderInfo {
std::string match;
std::set<network::mojom::RequestDestination> match_dest;
// TODO(crbug.com/1413922): Stop using std::optional when we remove V1 backend
// support.
std::optional<base::TimeDelta> expiration;
base::TimeDelta expiration;
std::string type;
std::string id;
};
@ -70,24 +68,10 @@ std::optional<DictionaryHeaderInfo> ParseDictionaryHeaderInfo(
return std::nullopt;
}
const bool is_v1 =
features::kCompressionDictionaryTransportBackendVersion.Get() ==
features::CompressionDictionaryTransportBackendVersion::kV1;
// 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 = is_v1;
// Don't use the value of `match-dest` in the `Use-As-Dictionary` response
// header when V1 backend is enabled.
const bool check_match_dest_dictionary_value = !is_v1;
// Don't use the value of `id` in the `Use-As-Dictionary` response header when
// V1 backend is enabled.
const bool check_id_dictionary_value = !is_v1;
std::optional<std::string> match_value;
// Maybe we don't need to support multiple match-dest.
// https://github.com/httpwg/http-extensions/issues/2722
std::set<network::mojom::RequestDestination> match_dest_values;
std::optional<base::TimeDelta> expires_value;
std::string type_value = std::string(kDefaultTypeRaw);
std::string id_value;
for (const auto& entry : dictionary.value()) {
@ -97,8 +81,7 @@ std::optional<DictionaryHeaderInfo> ParseDictionaryHeaderInfo(
return std::nullopt;
}
match_value = entry.second.member.front().item.GetString();
} else if (check_match_dest_dictionary_value &&
entry.first == shared_dictionary::kOptionNameMatchDest) {
} else if (entry.first == shared_dictionary::kOptionNameMatchDest) {
if (!entry.second.member_is_inner_list) {
// `match-dest` must be a list.
return std::nullopt;
@ -117,22 +100,13 @@ std::optional<DictionaryHeaderInfo> ParseDictionaryHeaderInfo(
match_dest_values.insert(*dest_value);
}
}
} 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 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 std::nullopt;
}
type_value = entry.second.member.front().item.GetString();
} else if (check_id_dictionary_value &&
entry.first == shared_dictionary::kOptionNameId) {
} else if (entry.first == shared_dictionary::kOptionNameId) {
if ((entry.second.member.size() != 1u) ||
!entry.second.member.front().item.is_string()) {
return std::nullopt;
@ -143,46 +117,33 @@ std::optional<DictionaryHeaderInfo> ParseDictionaryHeaderInfo(
}
}
}
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;
}
}
if (!match_value) {
return std::nullopt;
}
return DictionaryHeaderInfo(
std::move(*match_value), std::move(match_dest_values),
std::move(expires_value), std::move(type_value), std::move(id_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.
base::TimeDelta expiration =
lifetimes.freshness + lifetimes.staleness - age_on_response_time;
if (expiration <= base::TimeDelta()) {
return std::nullopt;
}
return DictionaryHeaderInfo(std::move(*match_value),
std::move(match_dest_values), expiration,
std::move(type_value), std::move(id_value));
}
} // namespace
// static
bool SharedDictionaryStorage::NeedToUseUrlPatternMatcher() {
return features::kCompressionDictionaryTransportBackendVersion.Get() !=
features::CompressionDictionaryTransportBackendVersion::kV1;
}
// static
bool SharedDictionaryStorage::NeedToRemoveMatchDestAndId() {
return features::kCompressionDictionaryTransportBackendVersion.Get() ==
features::CompressionDictionaryTransportBackendVersion::kV1;
}
SharedDictionaryStorage::SharedDictionaryStorage() = default;
SharedDictionaryStorage::~SharedDictionaryStorage() = default;
@ -200,12 +161,7 @@ SharedDictionaryStorage::MaybeCreateWriter(
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;
}
base::TimeDelta expiration = info->expiration;
if (!base::FeatureList::IsEnabled(
network::features::kCompressionDictionaryTransport)) {
// During the Origin Trial experiment, kCompressionDictionaryTransport is

@ -69,12 +69,6 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) SharedDictionaryStorage
protected:
friend class base::RefCounted<SharedDictionaryStorage>;
// Returns true when V2 backend is used.
// TODO(crbug.com/1413922): Remove this when we remove V1 backend support.
static bool NeedToUseUrlPatternMatcher();
// Returns true when V1 backend is used.
static bool NeedToRemoveMatchDestAndId();
SharedDictionaryStorage();
virtual ~SharedDictionaryStorage();
@ -128,16 +122,9 @@ DictionaryInfoType* GetMatchingDictionaryFromDictionaryInfoMap(
!base::Contains(info.match_dest(), destination)) {
continue;
}
if (!info.matcher()) {
// This is for V1 backend.
// TODO(crbug.com/1413922): Remove this after V1 backend is removed.
if (base::MatchPattern(url.path(), info.match())) {
matched_info = &info;
}
} else {
if (info.matcher()->Match(url)) {
matched_info = &info;
}
CHECK(info.matcher());
if (info.matcher()->Match(url)) {
matched_info = &info;
}
}
return matched_info;

@ -115,13 +115,11 @@ SharedDictionaryStorageInMemory::CreateWriter(
const std::set<mojom::RequestDestination>& match_dest,
const std::string& id) {
std::unique_ptr<SimpleUrlPatternMatcher> matcher;
if (NeedToUseUrlPatternMatcher()) {
auto matcher_create_result = SimpleUrlPatternMatcher::Create(match, url);
if (!matcher_create_result.has_value()) {
return nullptr;
}
matcher = std::move(matcher_create_result.value());
auto matcher_create_result = SimpleUrlPatternMatcher::Create(match, url);
if (!matcher_create_result.has_value()) {
return nullptr;
}
matcher = std::move(matcher_create_result.value());
return base::MakeRefCounted<SharedDictionaryWriterInMemory>(
base::BindOnce(&SharedDictionaryStorageInMemory::OnDictionaryWritten,
weak_factory_.GetWeakPtr(), url, response_time, expiration,

@ -244,13 +244,11 @@ SharedDictionaryStorageOnDisk::CreateWriter(
}
std::unique_ptr<SimpleUrlPatternMatcher> matcher;
if (NeedToUseUrlPatternMatcher()) {
auto matcher_create_result = SimpleUrlPatternMatcher::Create(match, url);
if (!matcher_create_result.has_value()) {
return nullptr;
}
matcher = std::move(matcher_create_result.value());
auto matcher_create_result = SimpleUrlPatternMatcher::Create(match, url);
if (!matcher_create_result.has_value()) {
return nullptr;
}
matcher = std::move(matcher_create_result.value());
return manager_->CreateWriter(
isolation_key_, url, response_time, expiration, match, match_dest, id,
base::BindOnce(&SharedDictionaryStorageOnDisk::OnDictionaryWritten,
@ -278,28 +276,17 @@ void SharedDictionaryStorageOnDisk::OnDatabaseRead(
return;
}
const bool need_matcher = NeedToUseUrlPatternMatcher();
const bool need_to_remove_dest_and_id = NeedToRemoveMatchDestAndId();
for (auto& info : result.value()) {
const url::SchemeHostPort scheme_host_port =
url::SchemeHostPort(info.url());
const std::string match = info.match();
std::unique_ptr<SimpleUrlPatternMatcher> matcher;
if (need_matcher) {
auto matcher_create_result =
SimpleUrlPatternMatcher::Create(match, info.url());
if (!matcher_create_result.has_value()) {
continue;
}
matcher = std::move(matcher_create_result.value());
}
if (need_to_remove_dest_and_id) {
info = net::SharedDictionaryInfo(
info.url(), info.response_time(), info.expiration(), info.match(),
/*match_dest_string=*/"", /*id=*/"", info.last_used_time(),
info.size(), info.hash(), info.disk_cache_key_token(),
info.primary_key_in_database());
auto matcher_create_result =
SimpleUrlPatternMatcher::Create(match, info.url());
if (!matcher_create_result.has_value()) {
continue;
}
matcher = std::move(matcher_create_result.value());
WrappedDictionaryInfo wrapped_info(std::move(info), std::move(matcher));
auto key = std::make_tuple(match, wrapped_info.match_dest());
dictionary_info_map_[scheme_host_port].insert(