blob: ecb2420af9aa08cb8625b196939c92126cff6023 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/http/http_server_properties.h"
#include <memory>
#include <string>
#include <vector>
#include "base/bind.h"
#include "base/feature_list.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/run_loop.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_clock.h"
#include "base/test/task_environment.h"
#include "base/values.h"
#include "net/base/features.h"
#include "net/base/host_port_pair.h"
#include "net/base/ip_address.h"
#include "net/http/http_network_session.h"
#include "net/test/test_with_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace base {
class ListValue;
}
namespace net {
const base::TimeDelta BROKEN_ALT_SVC_EXPIRE_DELAYS[10] = {
base::TimeDelta::FromSeconds(300), base::TimeDelta::FromSeconds(600),
base::TimeDelta::FromSeconds(1200), base::TimeDelta::FromSeconds(2400),
base::TimeDelta::FromSeconds(4800), base::TimeDelta::FromSeconds(9600),
base::TimeDelta::FromSeconds(19200), base::TimeDelta::FromSeconds(38400),
base::TimeDelta::FromSeconds(76800), base::TimeDelta::FromSeconds(153600),
};
class HttpServerPropertiesPeer {
public:
static void AddBrokenAlternativeServiceWithExpirationTime(
HttpServerProperties* impl,
const AlternativeService& alternative_service,
base::TimeTicks when,
const NetworkIsolationKey network_isolation_key = NetworkIsolationKey()) {
BrokenAlternativeService broken_alternative_service(
alternative_service, network_isolation_key,
true /* use_network_isolation_key */);
BrokenAlternativeServiceList::iterator unused_it;
impl->broken_alternative_services_.AddToBrokenListAndMap(
broken_alternative_service, when, &unused_it);
auto it =
impl->broken_alternative_services_.recently_broken_alternative_services_
.Get(broken_alternative_service);
if (it == impl->broken_alternative_services_
.recently_broken_alternative_services_.end()) {
impl->broken_alternative_services_.recently_broken_alternative_services_
.Put(broken_alternative_service, 1);
} else {
it->second++;
}
}
static void ExpireBrokenAlternateProtocolMappings(
HttpServerProperties* impl) {
impl->broken_alternative_services_.ExpireBrokenAlternateProtocolMappings();
}
};
namespace {
// Creates a ServerInfoMapKey without a NetworkIsolationKey.
HttpServerProperties::ServerInfoMapKey CreateSimpleKey(
const url::SchemeHostPort& server) {
return HttpServerProperties::ServerInfoMapKey(
server, net::NetworkIsolationKey(),
false /* use_network_isolation_key */);
}
class HttpServerPropertiesTest : public TestWithTaskEnvironment {
protected:
HttpServerPropertiesTest()
: TestWithTaskEnvironment(
base::test::TaskEnvironment::TimeSource::MOCK_TIME),
test_tick_clock_(GetMockTickClock()),
impl_(nullptr /* pref_delegate */,
nullptr /* net_log */,
test_tick_clock_,
&test_clock_) {
// Set |test_clock_| to some random time.
test_clock_.Advance(base::TimeDelta::FromSeconds(12345));
url::Origin origin1 = url::Origin::Create(GURL("https://foo.test/"));
network_isolation_key1_ = NetworkIsolationKey(origin1, origin1);
url::Origin origin2 = url::Origin::Create(GURL("https://bar.test/"));
network_isolation_key2_ = NetworkIsolationKey(origin2, origin2);
}
bool HasAlternativeService(const url::SchemeHostPort& origin,
const NetworkIsolationKey& network_isolation_key) {
const AlternativeServiceInfoVector alternative_service_info_vector =
impl_.GetAlternativeServiceInfos(origin, network_isolation_key);
return !alternative_service_info_vector.empty();
}
void SetAlternativeService(const url::SchemeHostPort& origin,
const AlternativeService& alternative_service) {
const base::Time expiration =
test_clock_.Now() + base::TimeDelta::FromDays(1);
if (alternative_service.protocol == kProtoQUIC) {
impl_.SetQuicAlternativeService(
origin, NetworkIsolationKey(), alternative_service, expiration,
HttpNetworkSession::Params().quic_params.supported_versions);
} else {
impl_.SetHttp2AlternativeService(origin, NetworkIsolationKey(),
alternative_service, expiration);
}
}
void MarkBrokenAndLetExpireAlternativeServiceNTimes(
const AlternativeService& alternative_service,
int num_times) {}
const base::TickClock* test_tick_clock_;
base::SimpleTestClock test_clock_;
// Two different non-empty network isolation keys for use in tests that need
// them.
NetworkIsolationKey network_isolation_key1_;
NetworkIsolationKey network_isolation_key2_;
HttpServerProperties impl_;
};
TEST_F(HttpServerPropertiesTest, SetSupportsSpdy) {
// Check spdy servers are correctly set with SchemeHostPort key.
url::SchemeHostPort https_www_server("https", "www.google.com", 443);
url::SchemeHostPort http_photo_server("http", "photos.google.com", 80);
url::SchemeHostPort https_mail_server("https", "mail.google.com", 443);
// Servers with port equal to default port in scheme will drop port components
// when calling Serialize().
url::SchemeHostPort http_google_server("http", "www.google.com", 443);
url::SchemeHostPort https_photos_server("https", "photos.google.com", 443);
url::SchemeHostPort valid_google_server((GURL("https://www.google.com")));
impl_.SetSupportsSpdy(https_www_server, NetworkIsolationKey(), true);
impl_.SetSupportsSpdy(http_photo_server, NetworkIsolationKey(), true);
impl_.SetSupportsSpdy(https_mail_server, NetworkIsolationKey(), false);
EXPECT_TRUE(impl_.GetSupportsSpdy(https_www_server, NetworkIsolationKey()));
EXPECT_TRUE(
impl_.SupportsRequestPriority(https_www_server, NetworkIsolationKey()));
EXPECT_TRUE(impl_.GetSupportsSpdy(http_photo_server, NetworkIsolationKey()));
EXPECT_TRUE(
impl_.SupportsRequestPriority(http_photo_server, NetworkIsolationKey()));
EXPECT_FALSE(impl_.GetSupportsSpdy(https_mail_server, NetworkIsolationKey()));
EXPECT_FALSE(
impl_.SupportsRequestPriority(https_mail_server, NetworkIsolationKey()));
EXPECT_FALSE(
impl_.GetSupportsSpdy(http_google_server, NetworkIsolationKey()));
EXPECT_FALSE(
impl_.SupportsRequestPriority(http_google_server, NetworkIsolationKey()));
EXPECT_FALSE(
impl_.GetSupportsSpdy(https_photos_server, NetworkIsolationKey()));
EXPECT_FALSE(impl_.SupportsRequestPriority(https_photos_server,
NetworkIsolationKey()));
EXPECT_TRUE(
impl_.GetSupportsSpdy(valid_google_server, NetworkIsolationKey()));
EXPECT_TRUE(impl_.SupportsRequestPriority(valid_google_server,
NetworkIsolationKey()));
// Flip values of two servers.
impl_.SetSupportsSpdy(https_www_server, NetworkIsolationKey(), false);
impl_.SetSupportsSpdy(https_mail_server, NetworkIsolationKey(), true);
EXPECT_FALSE(impl_.GetSupportsSpdy(https_www_server, NetworkIsolationKey()));
EXPECT_FALSE(
impl_.SupportsRequestPriority(https_www_server, NetworkIsolationKey()));
EXPECT_TRUE(impl_.GetSupportsSpdy(https_mail_server, NetworkIsolationKey()));
EXPECT_TRUE(
impl_.SupportsRequestPriority(https_mail_server, NetworkIsolationKey()));
}
TEST_F(HttpServerPropertiesTest, SetSupportsSpdyWebSockets) {
// The https and wss servers should be treated as the same server, as should
// the http and ws servers.
url::SchemeHostPort https_server("https", "www.test.com", 443);
url::SchemeHostPort wss_server("wss", "www.test.com", 443);
url::SchemeHostPort http_server("http", "www.test.com", 443);
url::SchemeHostPort ws_server("ws", "www.test.com", 443);
EXPECT_FALSE(impl_.GetSupportsSpdy(https_server, NetworkIsolationKey()));
EXPECT_FALSE(impl_.GetSupportsSpdy(wss_server, NetworkIsolationKey()));
EXPECT_FALSE(impl_.GetSupportsSpdy(http_server, NetworkIsolationKey()));
EXPECT_FALSE(impl_.GetSupportsSpdy(ws_server, NetworkIsolationKey()));
impl_.SetSupportsSpdy(wss_server, NetworkIsolationKey(), true);
EXPECT_TRUE(impl_.GetSupportsSpdy(https_server, NetworkIsolationKey()));
EXPECT_TRUE(impl_.GetSupportsSpdy(wss_server, NetworkIsolationKey()));
EXPECT_FALSE(impl_.GetSupportsSpdy(http_server, NetworkIsolationKey()));
EXPECT_FALSE(impl_.GetSupportsSpdy(ws_server, NetworkIsolationKey()));
impl_.SetSupportsSpdy(http_server, NetworkIsolationKey(), true);
EXPECT_TRUE(impl_.GetSupportsSpdy(https_server, NetworkIsolationKey()));
EXPECT_TRUE(impl_.GetSupportsSpdy(wss_server, NetworkIsolationKey()));
EXPECT_TRUE(impl_.GetSupportsSpdy(http_server, NetworkIsolationKey()));
EXPECT_TRUE(impl_.GetSupportsSpdy(ws_server, NetworkIsolationKey()));
impl_.SetSupportsSpdy(https_server, NetworkIsolationKey(), false);
EXPECT_FALSE(impl_.GetSupportsSpdy(https_server, NetworkIsolationKey()));
EXPECT_FALSE(impl_.GetSupportsSpdy(wss_server, NetworkIsolationKey()));
EXPECT_TRUE(impl_.GetSupportsSpdy(http_server, NetworkIsolationKey()));
EXPECT_TRUE(impl_.GetSupportsSpdy(ws_server, NetworkIsolationKey()));
impl_.SetSupportsSpdy(ws_server, NetworkIsolationKey(), false);
EXPECT_FALSE(impl_.GetSupportsSpdy(https_server, NetworkIsolationKey()));
EXPECT_FALSE(impl_.GetSupportsSpdy(wss_server, NetworkIsolationKey()));
EXPECT_FALSE(impl_.GetSupportsSpdy(http_server, NetworkIsolationKey()));
EXPECT_FALSE(impl_.GetSupportsSpdy(ws_server, NetworkIsolationKey()));
}
TEST_F(HttpServerPropertiesTest, SetSupportsSpdyWithNetworkIsolationKey) {
const url::SchemeHostPort kServer("https", "foo.test", 443);
EXPECT_FALSE(impl_.GetSupportsSpdy(kServer, network_isolation_key1_));
EXPECT_FALSE(impl_.SupportsRequestPriority(kServer, network_isolation_key1_));
EXPECT_FALSE(impl_.GetSupportsSpdy(kServer, NetworkIsolationKey()));
EXPECT_FALSE(impl_.SupportsRequestPriority(kServer, NetworkIsolationKey()));
// Without network isolation keys enabled for HttpServerProperties, passing in
// a NetworkIsolationKey should have no effect on behavior.
for (const auto network_isolation_key_to_set :
{NetworkIsolationKey(), network_isolation_key1_}) {
impl_.SetSupportsSpdy(kServer, network_isolation_key_to_set, true);
EXPECT_TRUE(impl_.GetSupportsSpdy(kServer, network_isolation_key1_));
EXPECT_TRUE(
impl_.SupportsRequestPriority(kServer, network_isolation_key1_));
EXPECT_TRUE(impl_.GetSupportsSpdy(kServer, NetworkIsolationKey()));
EXPECT_TRUE(impl_.SupportsRequestPriority(kServer, NetworkIsolationKey()));
impl_.SetSupportsSpdy(kServer, network_isolation_key_to_set, false);
EXPECT_FALSE(impl_.GetSupportsSpdy(kServer, network_isolation_key1_));
EXPECT_FALSE(
impl_.SupportsRequestPriority(kServer, network_isolation_key1_));
EXPECT_FALSE(impl_.GetSupportsSpdy(kServer, NetworkIsolationKey()));
EXPECT_FALSE(impl_.SupportsRequestPriority(kServer, NetworkIsolationKey()));
}
// With network isolation keys enabled for HttpServerProperties, the
// NetworkIsolationKey argument should be respected.
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(
features::kPartitionHttpServerPropertiesByNetworkIsolationKey);
// Since HttpServerProperties caches the feature value, have to create a new
// one.
HttpServerProperties properties(nullptr /* pref_delegate */,
nullptr /* net_log */, test_tick_clock_,
&test_clock_);
EXPECT_FALSE(properties.GetSupportsSpdy(kServer, network_isolation_key1_));
EXPECT_FALSE(
properties.SupportsRequestPriority(kServer, network_isolation_key1_));
EXPECT_FALSE(properties.GetSupportsSpdy(kServer, NetworkIsolationKey()));
EXPECT_FALSE(
properties.SupportsRequestPriority(kServer, NetworkIsolationKey()));
properties.SetSupportsSpdy(kServer, network_isolation_key1_, true);
EXPECT_TRUE(properties.GetSupportsSpdy(kServer, network_isolation_key1_));
EXPECT_TRUE(
properties.SupportsRequestPriority(kServer, network_isolation_key1_));
EXPECT_FALSE(properties.GetSupportsSpdy(kServer, NetworkIsolationKey()));
EXPECT_FALSE(
properties.SupportsRequestPriority(kServer, NetworkIsolationKey()));
properties.SetSupportsSpdy(kServer, NetworkIsolationKey(), true);
EXPECT_TRUE(properties.GetSupportsSpdy(kServer, network_isolation_key1_));
EXPECT_TRUE(
properties.SupportsRequestPriority(kServer, network_isolation_key1_));
EXPECT_TRUE(properties.GetSupportsSpdy(kServer, NetworkIsolationKey()));
EXPECT_TRUE(
properties.SupportsRequestPriority(kServer, NetworkIsolationKey()));
properties.SetSupportsSpdy(kServer, network_isolation_key1_, false);
EXPECT_FALSE(properties.GetSupportsSpdy(kServer, network_isolation_key1_));
EXPECT_FALSE(
properties.SupportsRequestPriority(kServer, network_isolation_key1_));
EXPECT_TRUE(properties.GetSupportsSpdy(kServer, NetworkIsolationKey()));
EXPECT_TRUE(
properties.SupportsRequestPriority(kServer, NetworkIsolationKey()));
properties.SetSupportsSpdy(kServer, NetworkIsolationKey(), false);
EXPECT_FALSE(properties.GetSupportsSpdy(kServer, network_isolation_key1_));
EXPECT_FALSE(
properties.SupportsRequestPriority(kServer, network_isolation_key1_));
EXPECT_FALSE(properties.GetSupportsSpdy(kServer, NetworkIsolationKey()));
EXPECT_FALSE(
properties.SupportsRequestPriority(kServer, NetworkIsolationKey()));
}
TEST_F(HttpServerPropertiesTest, LoadSupportsSpdy) {
HttpServerProperties::ServerInfo supports_spdy;
supports_spdy.supports_spdy = true;
HttpServerProperties::ServerInfo no_spdy;
no_spdy.supports_spdy = false;
url::SchemeHostPort spdy_server_google("https", "www.google.com", 443);
url::SchemeHostPort spdy_server_photos("https", "photos.google.com", 443);
url::SchemeHostPort spdy_server_docs("https", "docs.google.com", 443);
url::SchemeHostPort spdy_server_mail("https", "mail.google.com", 443);
// Check by initializing empty spdy servers.
std::unique_ptr<HttpServerProperties::ServerInfoMap> spdy_servers =
std::make_unique<HttpServerProperties::ServerInfoMap>();
impl_.OnServerInfoLoadedForTesting(std::move(spdy_servers));
EXPECT_FALSE(
impl_.GetSupportsSpdy(spdy_server_google, NetworkIsolationKey()));
// Check by initializing www.google.com:443 and photos.google.com:443 as spdy
// servers.
std::unique_ptr<HttpServerProperties::ServerInfoMap> spdy_servers1 =
std::make_unique<HttpServerProperties::ServerInfoMap>();
spdy_servers1->Put(CreateSimpleKey(spdy_server_google), supports_spdy);
spdy_servers1->Put(CreateSimpleKey(spdy_server_photos), no_spdy);
impl_.OnServerInfoLoadedForTesting(std::move(spdy_servers1));
// Note: these calls affect MRU order.
EXPECT_TRUE(impl_.GetSupportsSpdy(spdy_server_google, NetworkIsolationKey()));
EXPECT_FALSE(
impl_.GetSupportsSpdy(spdy_server_photos, NetworkIsolationKey()));
// Verify google and photos are in the list in MRU order.
ASSERT_EQ(2U, impl_.server_info_map_for_testing().size());
auto it = impl_.server_info_map_for_testing().begin();
EXPECT_EQ(spdy_server_photos, it->first.server);
EXPECT_TRUE(it->first.network_isolation_key.IsEmpty());
ASSERT_TRUE(it->second.supports_spdy.has_value());
EXPECT_FALSE(*it->second.supports_spdy);
++it;
EXPECT_EQ(spdy_server_google, it->first.server);
EXPECT_TRUE(it->first.network_isolation_key.IsEmpty());
ASSERT_TRUE(it->second.supports_spdy.has_value());
EXPECT_TRUE(*it->second.supports_spdy);
// Check by initializing mail.google.com:443 and docs.google.com:443.
std::unique_ptr<HttpServerProperties::ServerInfoMap> spdy_servers2 =
std::make_unique<HttpServerProperties::ServerInfoMap>();
spdy_servers2->Put(CreateSimpleKey(spdy_server_mail), supports_spdy);
spdy_servers2->Put(CreateSimpleKey(spdy_server_docs), supports_spdy);
impl_.OnServerInfoLoadedForTesting(std::move(spdy_servers2));
// Verify all the servers are in the list in MRU order. Note that
// OnServerInfoLoadedForTesting will put existing spdy server entries in
// front of newly added entries.
ASSERT_EQ(4U, impl_.server_info_map_for_testing().size());
it = impl_.server_info_map_for_testing().begin();
EXPECT_EQ(spdy_server_photos, it->first.server);
EXPECT_TRUE(it->first.network_isolation_key.IsEmpty());
ASSERT_TRUE(it->second.supports_spdy.has_value());
EXPECT_FALSE(*it->second.supports_spdy);
++it;
EXPECT_EQ(spdy_server_google, it->first.server);
EXPECT_TRUE(it->first.network_isolation_key.IsEmpty());
ASSERT_TRUE(it->second.supports_spdy.has_value());
EXPECT_TRUE(*it->second.supports_spdy);
++it;
EXPECT_EQ(spdy_server_docs, it->first.server);
EXPECT_TRUE(it->first.network_isolation_key.IsEmpty());
ASSERT_TRUE(it->second.supports_spdy.has_value());
EXPECT_TRUE(*it->second.supports_spdy);
++it;
EXPECT_EQ(spdy_server_mail, it->first.server);
EXPECT_TRUE(it->first.network_isolation_key.IsEmpty());
ASSERT_TRUE(it->second.supports_spdy.has_value());
EXPECT_TRUE(*it->second.supports_spdy);
// Check these in reverse MRU order so that MRU order stays the same.
EXPECT_TRUE(impl_.GetSupportsSpdy(spdy_server_mail, NetworkIsolationKey()));
EXPECT_TRUE(impl_.GetSupportsSpdy(spdy_server_docs, NetworkIsolationKey()));
EXPECT_TRUE(impl_.GetSupportsSpdy(spdy_server_google, NetworkIsolationKey()));
EXPECT_FALSE(
impl_.GetSupportsSpdy(spdy_server_photos, NetworkIsolationKey()));
// Verify that old values loaded from disk take precedence over newer learned
// values and also verify the recency list order is unchanged.
std::unique_ptr<HttpServerProperties::ServerInfoMap> spdy_servers3 =
std::make_unique<HttpServerProperties::ServerInfoMap>();
spdy_servers3->Put(CreateSimpleKey(spdy_server_mail), no_spdy);
spdy_servers3->Put(CreateSimpleKey(spdy_server_photos), supports_spdy);
impl_.OnServerInfoLoadedForTesting(std::move(spdy_servers3));
// Verify the entries are in the same order.
ASSERT_EQ(4U, impl_.server_info_map_for_testing().size());
it = impl_.server_info_map_for_testing().begin();
EXPECT_EQ(spdy_server_photos, it->first.server);
EXPECT_TRUE(it->first.network_isolation_key.IsEmpty());
ASSERT_TRUE(it->second.supports_spdy.has_value());
EXPECT_TRUE(*it->second.supports_spdy);
++it;
EXPECT_EQ(spdy_server_google, it->first.server);
EXPECT_TRUE(it->first.network_isolation_key.IsEmpty());
ASSERT_TRUE(it->second.supports_spdy.has_value());
EXPECT_TRUE(*it->second.supports_spdy);
++it;
EXPECT_EQ(spdy_server_docs, it->first.server);
EXPECT_TRUE(it->first.network_isolation_key.IsEmpty());
ASSERT_TRUE(it->second.supports_spdy.has_value());
EXPECT_TRUE(*it->second.supports_spdy);
++it;
EXPECT_EQ(spdy_server_mail, it->first.server);
EXPECT_TRUE(it->first.network_isolation_key.IsEmpty());
ASSERT_TRUE(it->second.supports_spdy.has_value());
EXPECT_FALSE(*it->second.supports_spdy);
// Verify photos server doesn't support SPDY and other servers support SPDY.
EXPECT_FALSE(impl_.GetSupportsSpdy(spdy_server_mail, NetworkIsolationKey()));
EXPECT_TRUE(impl_.GetSupportsSpdy(spdy_server_docs, NetworkIsolationKey()));
EXPECT_TRUE(impl_.GetSupportsSpdy(spdy_server_google, NetworkIsolationKey()));
EXPECT_TRUE(impl_.GetSupportsSpdy(spdy_server_photos, NetworkIsolationKey()));
}
TEST_F(HttpServerPropertiesTest, SupportsRequestPriority) {
url::SchemeHostPort spdy_server_empty("https", std::string(), 443);
EXPECT_FALSE(
impl_.SupportsRequestPriority(spdy_server_empty, NetworkIsolationKey()));
// Add www.google.com:443 as supporting SPDY.
url::SchemeHostPort spdy_server_google("https", "www.google.com", 443);
impl_.SetSupportsSpdy(spdy_server_google, NetworkIsolationKey(), true);
EXPECT_TRUE(
impl_.SupportsRequestPriority(spdy_server_google, NetworkIsolationKey()));
// Add mail.google.com:443 as not supporting SPDY.
url::SchemeHostPort spdy_server_mail("https", "mail.google.com", 443);
EXPECT_FALSE(
impl_.SupportsRequestPriority(spdy_server_mail, NetworkIsolationKey()));
// Add docs.google.com:443 as supporting SPDY.
url::SchemeHostPort spdy_server_docs("https", "docs.google.com", 443);
impl_.SetSupportsSpdy(spdy_server_docs, NetworkIsolationKey(), true);
EXPECT_TRUE(
impl_.SupportsRequestPriority(spdy_server_docs, NetworkIsolationKey()));
// Add www.youtube.com:443 as supporting QUIC.
url::SchemeHostPort youtube_server("https", "www.youtube.com", 443);
const AlternativeService alternative_service1(kProtoQUIC, "www.youtube.com",
443);
SetAlternativeService(youtube_server, alternative_service1);
EXPECT_TRUE(
impl_.SupportsRequestPriority(youtube_server, NetworkIsolationKey()));
// Add www.example.com:443 with two alternative services, one supporting QUIC.
url::SchemeHostPort example_server("https", "www.example.com", 443);
const AlternativeService alternative_service2(kProtoHTTP2, "", 443);
SetAlternativeService(example_server, alternative_service2);
SetAlternativeService(example_server, alternative_service1);
EXPECT_TRUE(
impl_.SupportsRequestPriority(example_server, NetworkIsolationKey()));
// Verify all the entries are the same after additions.
EXPECT_TRUE(
impl_.SupportsRequestPriority(spdy_server_google, NetworkIsolationKey()));
EXPECT_FALSE(
impl_.SupportsRequestPriority(spdy_server_mail, NetworkIsolationKey()));
EXPECT_TRUE(
impl_.SupportsRequestPriority(spdy_server_docs, NetworkIsolationKey()));
EXPECT_TRUE(
impl_.SupportsRequestPriority(youtube_server, NetworkIsolationKey()));
EXPECT_TRUE(
impl_.SupportsRequestPriority(example_server, NetworkIsolationKey()));
}
TEST_F(HttpServerPropertiesTest, ClearSupportsSpdy) {
// Add www.google.com:443 and mail.google.com:443 as supporting SPDY.
url::SchemeHostPort spdy_server_google("https", "www.google.com", 443);
impl_.SetSupportsSpdy(spdy_server_google, NetworkIsolationKey(), true);
url::SchemeHostPort spdy_server_mail("https", "mail.google.com", 443);
impl_.SetSupportsSpdy(spdy_server_mail, NetworkIsolationKey(), true);
EXPECT_TRUE(impl_.GetSupportsSpdy(spdy_server_google, NetworkIsolationKey()));
EXPECT_TRUE(impl_.GetSupportsSpdy(spdy_server_mail, NetworkIsolationKey()));
base::RunLoop run_loop;
bool callback_invoked_ = false;
impl_.Clear(base::BindOnce(
[](bool* callback_invoked, base::OnceClosure quit_closure) {
*callback_invoked = true;
std::move(quit_closure).Run();
},
&callback_invoked_, run_loop.QuitClosure()));
EXPECT_FALSE(
impl_.GetSupportsSpdy(spdy_server_google, NetworkIsolationKey()));
EXPECT_FALSE(impl_.GetSupportsSpdy(spdy_server_mail, NetworkIsolationKey()));
// Callback should be run asynchronously.
EXPECT_FALSE(callback_invoked_);
run_loop.Run();
EXPECT_TRUE(callback_invoked_);
}
TEST_F(HttpServerPropertiesTest, MRUOfServerInfoMap) {
url::SchemeHostPort spdy_server_google("https", "www.google.com", 443);
url::SchemeHostPort spdy_server_mail("https", "mail.google.com", 443);
// Add www.google.com:443 as supporting SPDY.
impl_.SetSupportsSpdy(spdy_server_google, NetworkIsolationKey(), true);
ASSERT_EQ(1u, impl_.server_info_map_for_testing().size());
auto it = impl_.server_info_map_for_testing().begin();
ASSERT_EQ(spdy_server_google, it->first.server);
EXPECT_TRUE(it->first.network_isolation_key.IsEmpty());
// Add mail.google.com:443 as supporting SPDY. Verify mail.google.com:443 and
// www.google.com:443 are in the list.
impl_.SetSupportsSpdy(spdy_server_mail, NetworkIsolationKey(), true);
ASSERT_EQ(2u, impl_.server_info_map_for_testing().size());
it = impl_.server_info_map_for_testing().begin();
ASSERT_EQ(spdy_server_mail, it->first.server);
EXPECT_TRUE(it->first.network_isolation_key.IsEmpty());
++it;
ASSERT_EQ(spdy_server_google, it->first.server);
EXPECT_TRUE(it->first.network_isolation_key.IsEmpty());
// Get www.google.com:443. It should become the most-recently-used server.
EXPECT_TRUE(impl_.GetSupportsSpdy(spdy_server_google, NetworkIsolationKey()));
ASSERT_EQ(2u, impl_.server_info_map_for_testing().size());
it = impl_.server_info_map_for_testing().begin();
ASSERT_EQ(spdy_server_google, it->first.server);
EXPECT_TRUE(it->first.network_isolation_key.IsEmpty());
++it;
ASSERT_EQ(spdy_server_mail, it->first.server);
EXPECT_TRUE(it->first.network_isolation_key.IsEmpty());
}
typedef HttpServerPropertiesTest AlternateProtocolServerPropertiesTest;
TEST_F(AlternateProtocolServerPropertiesTest, Basic) {
url::SchemeHostPort test_server("http", "foo", 80);
EXPECT_FALSE(HasAlternativeService(test_server, NetworkIsolationKey()));
AlternativeService alternative_service(kProtoHTTP2, "foo", 443);
SetAlternativeService(test_server, alternative_service);
const AlternativeServiceInfoVector alternative_service_info_vector =
impl_.GetAlternativeServiceInfos(test_server, NetworkIsolationKey());
ASSERT_EQ(1u, alternative_service_info_vector.size());
EXPECT_EQ(alternative_service,
alternative_service_info_vector[0].alternative_service());
impl_.Clear(base::OnceClosure());
EXPECT_FALSE(HasAlternativeService(test_server, NetworkIsolationKey()));
}
TEST_F(AlternateProtocolServerPropertiesTest, ExcludeOrigin) {
AlternativeServiceInfoVector alternative_service_info_vector;
base::Time expiration = test_clock_.Now() + base::TimeDelta::FromDays(1);
// Same hostname, same port, TCP: should be ignored.
AlternativeServiceInfo alternative_service_info1 =
AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
AlternativeService(kProtoHTTP2, "foo", 443), expiration);
alternative_service_info_vector.push_back(alternative_service_info1);
// Different hostname: GetAlternativeServiceInfos should return this one.
AlternativeServiceInfo alternative_service_info2 =
AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
AlternativeService(kProtoHTTP2, "bar", 443), expiration);
alternative_service_info_vector.push_back(alternative_service_info2);
// Different port: GetAlternativeServiceInfos should return this one too.
AlternativeServiceInfo alternative_service_info3 =
AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
AlternativeService(kProtoHTTP2, "foo", 80), expiration);
alternative_service_info_vector.push_back(alternative_service_info3);
// QUIC: GetAlternativeServices should return this one too.
AlternativeServiceInfo alternative_service_info4 =
AlternativeServiceInfo::CreateQuicAlternativeServiceInfo(
AlternativeService(kProtoQUIC, "foo", 443), expiration,
HttpNetworkSession::Params().quic_params.supported_versions);
alternative_service_info_vector.push_back(alternative_service_info4);
url::SchemeHostPort test_server("https", "foo", 443);
impl_.SetAlternativeServices(test_server, NetworkIsolationKey(),
alternative_service_info_vector);
const AlternativeServiceInfoVector alternative_service_info_vector2 =
impl_.GetAlternativeServiceInfos(test_server, NetworkIsolationKey());
ASSERT_EQ(3u, alternative_service_info_vector2.size());
EXPECT_EQ(alternative_service_info2, alternative_service_info_vector2[0]);
EXPECT_EQ(alternative_service_info3, alternative_service_info_vector2[1]);
EXPECT_EQ(alternative_service_info4, alternative_service_info_vector2[2]);
}
TEST_F(AlternateProtocolServerPropertiesTest, Set) {
// |test_server1| has an alternative service, which will not be
// affected by OnServerInfoLoadedForTesting(), because
// |server_info_map| does not have an entry for
// |test_server1|.
url::SchemeHostPort test_server1("http", "foo1", 80);
const AlternativeService alternative_service1(kProtoHTTP2, "bar1", 443);
const base::Time now = test_clock_.Now();
base::Time expiration1 = now + base::TimeDelta::FromDays(1);
// 1st entry in the memory.
impl_.SetHttp2AlternativeService(test_server1, NetworkIsolationKey(),
alternative_service1, expiration1);
// |test_server2| has an alternative service, which will be
// overwritten by OnServerInfoLoadedForTesting(), because
// |server_info_map| has an entry for |test_server2|.
AlternativeServiceInfoVector alternative_service_info_vector;
const AlternativeService alternative_service2(kProtoHTTP2, "bar2", 443);
base::Time expiration2 = now + base::TimeDelta::FromDays(2);
alternative_service_info_vector.push_back(
AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
alternative_service2, expiration2));
url::SchemeHostPort test_server2("http", "foo2", 80);
// 0th entry in the memory.
impl_.SetAlternativeServices(test_server2, NetworkIsolationKey(),
alternative_service_info_vector);
// Prepare |server_info_map| to be loaded by OnServerInfoLoadedForTesting().
std::unique_ptr<HttpServerProperties::ServerInfoMap> server_info_map =
std::make_unique<HttpServerProperties::ServerInfoMap>();
const AlternativeService alternative_service3(kProtoHTTP2, "bar3", 123);
base::Time expiration3 = now + base::TimeDelta::FromDays(3);
const AlternativeServiceInfo alternative_service_info1 =
AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
alternative_service3, expiration3);
// Simulate updating data for 0th entry with data from Preferences.
server_info_map->GetOrPut(CreateSimpleKey(test_server2))
->second.alternative_services =
AlternativeServiceInfoVector(/*size=*/1, alternative_service_info1);
url::SchemeHostPort test_server3("http", "foo3", 80);
const AlternativeService alternative_service4(kProtoHTTP2, "bar4", 1234);
base::Time expiration4 = now + base::TimeDelta::FromDays(4);
const AlternativeServiceInfo alternative_service_info2 =
AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
alternative_service4, expiration4);
// Add an old entry from Preferences, this will be added to end of recency
// list.
server_info_map->GetOrPut(CreateSimpleKey(test_server3))
->second.alternative_services =
AlternativeServiceInfoVector(/*size=*/1, alternative_service_info2);
// MRU list will be test_server2, test_server1, test_server3.
impl_.OnServerInfoLoadedForTesting(std::move(server_info_map));
// Verify server_info_map.
const HttpServerProperties::ServerInfoMap& map =
impl_.server_info_map_for_testing();
ASSERT_EQ(3u, map.size());
auto map_it = map.begin();
EXPECT_EQ(test_server2, map_it->first.server);
EXPECT_TRUE(map_it->first.network_isolation_key.IsEmpty());
ASSERT_TRUE(map_it->second.alternative_services.has_value());
const AlternativeServiceInfoVector* service_info =
&map_it->second.alternative_services.value();
ASSERT_EQ(1u, service_info->size());
EXPECT_EQ(alternative_service3, (*service_info)[0].alternative_service());
EXPECT_EQ(expiration3, (*service_info)[0].expiration());
++map_it;
EXPECT_EQ(test_server1, map_it->first.server);
EXPECT_TRUE(map_it->first.network_isolation_key.IsEmpty());
ASSERT_TRUE(map_it->second.alternative_services.has_value());
service_info = &map_it->second.alternative_services.value();
ASSERT_EQ(1u, service_info->size());
EXPECT_EQ(alternative_service1, (*service_info)[0].alternative_service());
EXPECT_EQ(expiration1, (*service_info)[0].expiration());
++map_it;
EXPECT_EQ(map_it->first.server, test_server3);
EXPECT_TRUE(map_it->first.network_isolation_key.IsEmpty());
ASSERT_TRUE(map_it->second.alternative_services.has_value());
service_info = &map_it->second.alternative_services.value();
ASSERT_EQ(1u, service_info->size());
EXPECT_EQ(alternative_service4, (*service_info)[0].alternative_service());
EXPECT_EQ(expiration4, (*service_info)[0].expiration());
}
TEST_F(AlternateProtocolServerPropertiesTest, SetWebSockets) {
// The https and wss servers should be treated as the same server, as should
// the http and ws servers.
url::SchemeHostPort https_server("https", "www.test.com", 443);
url::SchemeHostPort wss_server("wss", "www.test.com", 443);
url::SchemeHostPort http_server("http", "www.test.com", 443);
url::SchemeHostPort ws_server("ws", "www.test.com", 443);
AlternativeService alternative_service(kProtoHTTP2, "bar", 443);
EXPECT_EQ(
0u, impl_.GetAlternativeServiceInfos(https_server, NetworkIsolationKey())
.size());
EXPECT_EQ(0u,
impl_.GetAlternativeServiceInfos(wss_server, NetworkIsolationKey())
.size());
EXPECT_EQ(0u,
impl_.GetAlternativeServiceInfos(http_server, NetworkIsolationKey())
.size());
EXPECT_EQ(0u,
impl_.GetAlternativeServiceInfos(ws_server, NetworkIsolationKey())
.size());
SetAlternativeService(wss_server, alternative_service);
EXPECT_EQ(
1u, impl_.GetAlternativeServiceInfos(https_server, NetworkIsolationKey())
.size());
EXPECT_EQ(1u,
impl_.GetAlternativeServiceInfos(wss_server, NetworkIsolationKey())
.size());
EXPECT_EQ(0u,
impl_.GetAlternativeServiceInfos(http_server, NetworkIsolationKey())
.size());
EXPECT_EQ(0u,
impl_.GetAlternativeServiceInfos(ws_server, NetworkIsolationKey())
.size());
SetAlternativeService(http_server, alternative_service);
EXPECT_EQ(
1u, impl_.GetAlternativeServiceInfos(https_server, NetworkIsolationKey())
.size());
EXPECT_EQ(1u,
impl_.GetAlternativeServiceInfos(wss_server, NetworkIsolationKey())
.size());
EXPECT_EQ(1u,
impl_.GetAlternativeServiceInfos(http_server, NetworkIsolationKey())
.size());
EXPECT_EQ(1u,
impl_.GetAlternativeServiceInfos(ws_server, NetworkIsolationKey())
.size());
impl_.SetAlternativeServices(https_server, NetworkIsolationKey(),
AlternativeServiceInfoVector());
EXPECT_EQ(
0u, impl_.GetAlternativeServiceInfos(https_server, NetworkIsolationKey())
.size());
EXPECT_EQ(0u,
impl_.GetAlternativeServiceInfos(wss_server, NetworkIsolationKey())
.size());
EXPECT_EQ(1u,
impl_.GetAlternativeServiceInfos(http_server, NetworkIsolationKey())
.size());
EXPECT_EQ(1u,
impl_.GetAlternativeServiceInfos(ws_server, NetworkIsolationKey())
.size());
impl_.SetAlternativeServices(ws_server, NetworkIsolationKey(),
AlternativeServiceInfoVector());
EXPECT_EQ(
0u, impl_.GetAlternativeServiceInfos(https_server, NetworkIsolationKey())
.size());
EXPECT_EQ(0u,
impl_.GetAlternativeServiceInfos(wss_server, NetworkIsolationKey())
.size());
EXPECT_EQ(0u,
impl_.GetAlternativeServiceInfos(http_server, NetworkIsolationKey())
.size());
EXPECT_EQ(0u,
impl_.GetAlternativeServiceInfos(ws_server, NetworkIsolationKey())
.size());
}
TEST_F(AlternateProtocolServerPropertiesTest, SetWithNetworkIsolationKey) {
const url::SchemeHostPort kServer("https", "foo.test", 443);
const AlternativeServiceInfoVector kAlternativeServices(
{AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
AlternativeService(kProtoHTTP2, "foo", 443),
base::Time::Now() + base::TimeDelta::FromDays(1) /* expiration */)});
EXPECT_TRUE(impl_.GetAlternativeServiceInfos(kServer, network_isolation_key1_)
.empty());
EXPECT_TRUE(
impl_.GetAlternativeServiceInfos(kServer, NetworkIsolationKey()).empty());
// Without network isolation keys enabled for HttpServerProperties, passing in
// a NetworkIsolationKey should have no effect on behavior.
for (const auto network_isolation_key_to_set :
{NetworkIsolationKey(), network_isolation_key1_}) {
impl_.SetAlternativeServices(kServer, network_isolation_key_to_set,
kAlternativeServices);
EXPECT_EQ(kAlternativeServices, impl_.GetAlternativeServiceInfos(
kServer, network_isolation_key1_));
EXPECT_EQ(kAlternativeServices,
impl_.GetAlternativeServiceInfos(kServer, NetworkIsolationKey()));
impl_.SetAlternativeServices(kServer, network_isolation_key_to_set,
AlternativeServiceInfoVector());
EXPECT_TRUE(
impl_.GetAlternativeServiceInfos(kServer, network_isolation_key1_)
.empty());
EXPECT_TRUE(impl_.GetAlternativeServiceInfos(kServer, NetworkIsolationKey())
.empty());
}
// Check that with network isolation keys enabled for HttpServerProperties,
// the NetworkIsolationKey argument is respected.
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(
features::kPartitionHttpServerPropertiesByNetworkIsolationKey);
// Since HttpServerProperties caches the feature value, have to create a new
// one.
HttpServerProperties properties(nullptr /* pref_delegate */,
nullptr /* net_log */, test_tick_clock_,
&test_clock_);
properties.SetAlternativeServices(kServer, network_isolation_key1_,
kAlternativeServices);
EXPECT_EQ(kAlternativeServices, properties.GetAlternativeServiceInfos(
kServer, network_isolation_key1_));
EXPECT_TRUE(
properties.GetAlternativeServiceInfos(kServer, NetworkIsolationKey())
.empty());
properties.SetAlternativeServices(kServer, NetworkIsolationKey(),
kAlternativeServices);
EXPECT_EQ(kAlternativeServices, properties.GetAlternativeServiceInfos(
kServer, network_isolation_key1_));
EXPECT_EQ(kAlternativeServices, properties.GetAlternativeServiceInfos(
kServer, NetworkIsolationKey()));
properties.SetAlternativeServices(kServer, network_isolation_key1_,
AlternativeServiceInfoVector());
EXPECT_TRUE(
properties.GetAlternativeServiceInfos(kServer, network_isolation_key1_)
.empty());
EXPECT_EQ(kAlternativeServices, properties.GetAlternativeServiceInfos(
kServer, NetworkIsolationKey()));
properties.SetAlternativeServices(kServer, NetworkIsolationKey(),
AlternativeServiceInfoVector());
EXPECT_TRUE(
properties.GetAlternativeServiceInfos(kServer, network_isolation_key1_)
.empty());
EXPECT_TRUE(
properties.GetAlternativeServiceInfos(kServer, NetworkIsolationKey())
.empty());
}
// Regression test for https://crbug.com/504032:
// OnServerInfoLoadedForTesting() should not crash if there is an
// empty hostname is the mapping.
TEST_F(AlternateProtocolServerPropertiesTest, SetWithEmptyHostname) {
url::SchemeHostPort server("https", "foo", 443);
const AlternativeService alternative_service_with_empty_hostname(kProtoHTTP2,
"", 1234);
const AlternativeService alternative_service_with_foo_hostname(kProtoHTTP2,
"foo", 1234);
SetAlternativeService(server, alternative_service_with_empty_hostname);
impl_.MarkAlternativeServiceBroken(alternative_service_with_foo_hostname,
NetworkIsolationKey());
std::unique_ptr<HttpServerProperties::ServerInfoMap> server_info_map =
std::make_unique<HttpServerProperties::ServerInfoMap>();
impl_.OnServerInfoLoadedForTesting(std::move(server_info_map));
EXPECT_TRUE(impl_.IsAlternativeServiceBroken(
alternative_service_with_foo_hostname, NetworkIsolationKey()));
const AlternativeServiceInfoVector alternative_service_info_vector =
impl_.GetAlternativeServiceInfos(server, NetworkIsolationKey());
ASSERT_EQ(1u, alternative_service_info_vector.size());
EXPECT_EQ(alternative_service_with_foo_hostname,
alternative_service_info_vector[0].alternative_service());
}
// Regression test for https://crbug.com/516486:
// GetAlternativeServiceInfos() should remove |server_info_map_|
// elements with empty value.
TEST_F(AlternateProtocolServerPropertiesTest, EmptyVector) {
url::SchemeHostPort server("https", "foo", 443);
const AlternativeService alternative_service(kProtoHTTP2, "bar", 443);
base::Time expiration = test_clock_.Now() - base::TimeDelta::FromDays(1);
const AlternativeServiceInfo alternative_service_info =
AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
alternative_service, expiration);
std::unique_ptr<HttpServerProperties::ServerInfoMap> server_info_map =
std::make_unique<HttpServerProperties::ServerInfoMap>();
server_info_map->GetOrPut(CreateSimpleKey(server))
->second.alternative_services = AlternativeServiceInfoVector(
/*size=*/1, alternative_service_info);
// Prepare |server_info_map_| with a single key that has a single
// AlternativeServiceInfo with identical hostname and port.
impl_.OnServerInfoLoadedForTesting(std::move(server_info_map));
// GetAlternativeServiceInfos() should remove such AlternativeServiceInfo from
// |server_info_map_|, emptying the AlternativeServiceInfoVector
// corresponding to |server|.
ASSERT_TRUE(
impl_.GetAlternativeServiceInfos(server, NetworkIsolationKey()).empty());
// GetAlternativeServiceInfos() should remove this key from
// |server_info_map_|, and SetAlternativeServices() should not crash.
impl_.SetAlternativeServices(
server, NetworkIsolationKey(),
AlternativeServiceInfoVector(/*size=*/1, alternative_service_info));
// There should still be no alternative service assigned to |server|.
ASSERT_TRUE(
impl_.GetAlternativeServiceInfos(server, NetworkIsolationKey()).empty());
}
// Regression test for https://crbug.com/516486 for the canonical host case.
TEST_F(AlternateProtocolServerPropertiesTest, EmptyVectorForCanonical) {
url::SchemeHostPort server("https", "foo.c.youtube.com", 443);
url::SchemeHostPort canonical_server("https", "bar.c.youtube.com", 443);
const AlternativeService alternative_service(kProtoHTTP2, "", 443);
base::Time expiration = test_clock_.Now() - base::TimeDelta::FromDays(1);
const AlternativeServiceInfo alternative_service_info =
AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
alternative_service, expiration);
std::unique_ptr<HttpServerProperties::ServerInfoMap> server_info_map =
std::make_unique<HttpServerProperties::ServerInfoMap>();
server_info_map->GetOrPut(CreateSimpleKey(canonical_server))
->second.alternative_services =
AlternativeServiceInfoVector(/*size=*/1, alternative_service_info);
// Prepare |server_info_map_| with a single key that has a single
// AlternativeServiceInfo with identical hostname and port.
impl_.OnServerInfoLoadedForTesting(std::move(server_info_map));
// GetAlternativeServiceInfos() should remove such AlternativeServiceInfo from
// |server_info_map_|, emptying the AlternativeServiceInfoVector
// corresponding to |canonical_server|, even when looking up
// alternative services for |server|.
ASSERT_TRUE(
impl_.GetAlternativeServiceInfos(server, NetworkIsolationKey()).empty());
// GetAlternativeServiceInfos() should remove this key from
// |server_info_map_|, and SetAlternativeServices() should not crash.
impl_.SetAlternativeServices(
canonical_server, NetworkIsolationKey(),
AlternativeServiceInfoVector(/*size=*/1, alternative_service_info));
// There should still be no alternative service assigned to
// |canonical_server|.
ASSERT_TRUE(
impl_.GetAlternativeServiceInfos(canonical_server, NetworkIsolationKey())
.empty());
}
TEST_F(AlternateProtocolServerPropertiesTest, ClearServerWithCanonical) {
url::SchemeHostPort server("https", "foo.c.youtube.com", 443);
url::SchemeHostPort canonical_server("https", "bar.c.youtube.com", 443);
const AlternativeService alternative_service(kProtoQUIC, "", 443);
base::Time expiration = test_clock_.Now() + base::TimeDelta::FromDays(1);
const AlternativeServiceInfo alternative_service_info =
AlternativeServiceInfo::CreateQuicAlternativeServiceInfo(
alternative_service, expiration,
HttpNetworkSession::Params().quic_params.supported_versions);
impl_.SetAlternativeServices(
canonical_server, NetworkIsolationKey(),
AlternativeServiceInfoVector(/*size=*/1, alternative_service_info));
// Make sure the canonical service is returned for the other server.
const AlternativeServiceInfoVector alternative_service_info_vector =
impl_.GetAlternativeServiceInfos(server, NetworkIsolationKey());
ASSERT_EQ(1u, alternative_service_info_vector.size());
EXPECT_EQ(kProtoQUIC,
alternative_service_info_vector[0].alternative_service().protocol);
EXPECT_EQ(443, alternative_service_info_vector[0].alternative_service().port);
// Now clear the alternatives for the other server and make sure it stays
// cleared.
// GetAlternativeServices() should remove this key from
// |server_info_map_|, and SetAlternativeServices() should not crash.
impl_.SetAlternativeServices(server, NetworkIsolationKey(),
AlternativeServiceInfoVector());
ASSERT_TRUE(
impl_.GetAlternativeServiceInfos(server, NetworkIsolationKey()).empty());
}
TEST_F(AlternateProtocolServerPropertiesTest, MRUOfGetAlternativeServiceInfos) {
url::SchemeHostPort test_server1("http", "foo1", 80);
const AlternativeService alternative_service1(kProtoHTTP2, "foo1", 443);
SetAlternativeService(test_server1, alternative_service1);
url::SchemeHostPort test_server2("http", "foo2", 80);
const AlternativeService alternative_service2(kProtoHTTP2, "foo2", 1234);
SetAlternativeService(test_server2, alternative_service2);
const HttpServerProperties::ServerInfoMap& map =
impl_.server_info_map_for_testing();
auto it = map.begin();
EXPECT_EQ(test_server2, it->first.server);
EXPECT_TRUE(it->first.network_isolation_key.IsEmpty());
ASSERT_TRUE(it->second.alternative_services.has_value());
ASSERT_EQ(1u, it->second.alternative_services->size());
EXPECT_EQ(alternative_service2,
it->second.alternative_services.value()[0].alternative_service());
const AlternativeServiceInfoVector alternative_service_info_vector =
impl_.GetAlternativeServiceInfos(test_server1, NetworkIsolationKey());
ASSERT_EQ(1u, alternative_service_info_vector.size());
EXPECT_EQ(alternative_service1,
alternative_service_info_vector[0].alternative_service());
// GetAlternativeServices should reorder the AlternateProtocol map.
it = map.begin();
EXPECT_EQ(test_server1, it->first.server);
EXPECT_TRUE(it->first.network_isolation_key.IsEmpty());
ASSERT_TRUE(it->second.alternative_services.has_value());
ASSERT_EQ(1u, it->second.alternative_services->size());
EXPECT_EQ(alternative_service1,
it->second.alternative_services.value()[0].alternative_service());
}
TEST_F(AlternateProtocolServerPropertiesTest, SetBroken) {
url::SchemeHostPort test_server("http", "foo", 80);
const AlternativeService alternative_service1(kProtoHTTP2, "foo", 443);
SetAlternativeService(test_server, alternative_service1);
AlternativeServiceInfoVector alternative_service_info_vector =
impl_.GetAlternativeServiceInfos(test_server, NetworkIsolationKey());
ASSERT_EQ(1u, alternative_service_info_vector.size());
EXPECT_EQ(alternative_service1,
alternative_service_info_vector[0].alternative_service());
EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service1,
NetworkIsolationKey()));
// GetAlternativeServiceInfos should return the broken alternative service.
impl_.MarkAlternativeServiceBroken(alternative_service1,
NetworkIsolationKey());
alternative_service_info_vector =
impl_.GetAlternativeServiceInfos(test_server, NetworkIsolationKey());
ASSERT_EQ(1u, alternative_service_info_vector.size());
EXPECT_EQ(alternative_service1,
alternative_service_info_vector[0].alternative_service());
EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service1,
NetworkIsolationKey()));
// SetAlternativeServices should add a broken alternative service to the map.
AlternativeServiceInfoVector alternative_service_info_vector2;
base::Time expiration = test_clock_.Now() + base::TimeDelta::FromDays(1);
alternative_service_info_vector2.push_back(
AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
alternative_service1, expiration));
const AlternativeService alternative_service2(kProtoHTTP2, "foo", 1234);
alternative_service_info_vector2.push_back(
AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
alternative_service2, expiration));
impl_.SetAlternativeServices(test_server, NetworkIsolationKey(),
alternative_service_info_vector2);
alternative_service_info_vector =
impl_.GetAlternativeServiceInfos(test_server, NetworkIsolationKey());
ASSERT_EQ(2u, alternative_service_info_vector.size());
EXPECT_EQ(alternative_service1,
alternative_service_info_vector[0].alternative_service());
EXPECT_EQ(alternative_service2,
alternative_service_info_vector[1].alternative_service());
EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service1,
NetworkIsolationKey()));
EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service2,
NetworkIsolationKey()));
// SetAlternativeService should add a broken alternative service to the map.
SetAlternativeService(test_server, alternative_service1);
alternative_service_info_vector =
impl_.GetAlternativeServiceInfos(test_server, NetworkIsolationKey());
ASSERT_EQ(1u, alternative_service_info_vector.size());
EXPECT_EQ(alternative_service1,
alternative_service_info_vector[0].alternative_service());
EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service1,
NetworkIsolationKey()));
}
TEST_F(AlternateProtocolServerPropertiesTest,
SetBrokenUntilDefaultNetworkChanges) {
url::SchemeHostPort test_server("http", "foo", 80);
const AlternativeService alternative_service1(kProtoHTTP2, "foo", 443);
SetAlternativeService(test_server, alternative_service1);
AlternativeServiceInfoVector alternative_service_info_vector =
impl_.GetAlternativeServiceInfos(test_server, NetworkIsolationKey());
ASSERT_EQ(1u, alternative_service_info_vector.size());
EXPECT_EQ(alternative_service1,
alternative_service_info_vector[0].alternative_service());
EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service1,
NetworkIsolationKey()));
// Mark the alternative service as broken until the default network changes.
impl_.MarkAlternativeServiceBrokenUntilDefaultNetworkChanges(
alternative_service1, NetworkIsolationKey());
// The alternative service should be persisted and marked as broken.
alternative_service_info_vector =
impl_.GetAlternativeServiceInfos(test_server, NetworkIsolationKey());
ASSERT_EQ(1u, alternative_service_info_vector.size());
EXPECT_EQ(alternative_service1,
alternative_service_info_vector[0].alternative_service());
EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service1,
NetworkIsolationKey()));
// SetAlternativeServices should add a broken alternative service to the map.
AlternativeServiceInfoVector alternative_service_info_vector2;
base::Time expiration = test_clock_.Now() + base::TimeDelta::FromDays(1);
alternative_service_info_vector2.push_back(
AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
alternative_service1, expiration));
const AlternativeService alternative_service2(kProtoHTTP2, "foo", 1234);
alternative_service_info_vector2.push_back(
AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
alternative_service2, expiration));
impl_.SetAlternativeServices(test_server, NetworkIsolationKey(),
alternative_service_info_vector2);
alternative_service_info_vector =
impl_.GetAlternativeServiceInfos(test_server, NetworkIsolationKey());
ASSERT_EQ(2u, alternative_service_info_vector.size());
EXPECT_EQ(alternative_service1,
alternative_service_info_vector[0].alternative_service());
EXPECT_EQ(alternative_service2,
alternative_service_info_vector[1].alternative_service());
EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service1,
NetworkIsolationKey()));
EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service2,
NetworkIsolationKey()));
// SetAlternativeService should add a broken alternative service to the map.
SetAlternativeService(test_server, alternative_service1);
alternative_service_info_vector =
impl_.GetAlternativeServiceInfos(test_server, NetworkIsolationKey());
ASSERT_EQ(1u, alternative_service_info_vector.size());
EXPECT_EQ(alternative_service1,
alternative_service_info_vector[0].alternative_service());
EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service1,
NetworkIsolationKey()));
}
TEST_F(AlternateProtocolServerPropertiesTest, MaxAge) {
AlternativeServiceInfoVector alternative_service_info_vector;
base::Time now = test_clock_.Now();
base::TimeDelta one_day = base::TimeDelta::FromDays(1);
// First alternative service expired one day ago, should not be returned by
// GetAlternativeServiceInfos().
const AlternativeService alternative_service1(kProtoHTTP2, "foo", 443);
alternative_service_info_vector.push_back(
AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
alternative_service1, now - one_day));
// Second alterrnative service will expire one day from now, should be
// returned by GetAlternativeSerices().
const AlternativeService alternative_service2(kProtoHTTP2, "bar", 1234);
alternative_service_info_vector.push_back(
AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
alternative_service2, now + one_day));
url::SchemeHostPort test_server("http", "foo", 80);
impl_.SetAlternativeServices(test_server, NetworkIsolationKey(),
alternative_service_info_vector);
AlternativeServiceInfoVector alternative_service_info_vector2 =
impl_.GetAlternativeServiceInfos(test_server, NetworkIsolationKey());
ASSERT_EQ(1u, alternative_service_info_vector2.size());
EXPECT_EQ(alternative_service2,
alternative_service_info_vector2[0].alternative_service());
}
TEST_F(AlternateProtocolServerPropertiesTest, MaxAgeCanonical) {
AlternativeServiceInfoVector alternative_service_info_vector;
base::Time now = test_clock_.Now();
base::TimeDelta one_day = base::TimeDelta::FromDays(1);
// First alternative service expired one day ago, should not be returned by
// GetAlternativeServiceInfos().
const AlternativeService alternative_service1(kProtoHTTP2, "foo", 443);
alternative_service_info_vector.push_back(
AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
alternative_service1, now - one_day));
// Second alterrnative service will expire one day from now, should be
// returned by GetAlternativeSerices().
const AlternativeService alternative_service2(kProtoHTTP2, "bar", 1234);
alternative_service_info_vector.push_back(
AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
alternative_service2, now + one_day));
url::SchemeHostPort canonical_server("https", "bar.c.youtube.com", 443);
impl_.SetAlternativeServices(canonical_server, NetworkIsolationKey(),
alternative_service_info_vector);
url::SchemeHostPort test_server("https", "foo.c.youtube.com", 443);
AlternativeServiceInfoVector alternative_service_info_vector2 =
impl_.GetAlternativeServiceInfos(test_server, NetworkIsolationKey());
ASSERT_EQ(1u, alternative_service_info_vector2.size());
EXPECT_EQ(alternative_service2,
alternative_service_info_vector2[0].alternative_service());
}
TEST_F(AlternateProtocolServerPropertiesTest, AlternativeServiceWithScheme) {
AlternativeServiceInfoVector alternative_service_info_vector;
const AlternativeService alternative_service1(kProtoHTTP2, "foo", 443);
base::Time expiration = test_clock_.Now() + base::TimeDelta::FromDays(1);
alternative_service_info_vector.push_back(
AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
alternative_service1, expiration));
const AlternativeService alternative_service2(kProtoHTTP2, "bar", 1234);
alternative_service_info_vector.push_back(
AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
alternative_service2, expiration));
// Set Alt-Svc list for |http_server|.
url::SchemeHostPort http_server("http", "foo", 80);
impl_.SetAlternativeServices(http_server, NetworkIsolationKey(),
alternative_service_info_vector);
const net::HttpServerProperties::ServerInfoMap& map =
impl_.server_info_map_for_testing();
auto it = map.begin();
EXPECT_EQ(http_server, it->first.server);
EXPECT_TRUE(it->first.network_isolation_key.IsEmpty());
ASSERT_TRUE(it->second.alternative_services.has_value());
ASSERT_EQ(2u, it->second.alternative_services->size());
EXPECT_EQ(alternative_service1,
it->second.alternative_services.value()[0].alternative_service());
EXPECT_EQ(alternative_service2,
it->second.alternative_services.value()[1].alternative_service());
// Check Alt-Svc list should not be set for |https_server|.
url::SchemeHostPort https_server("https", "foo", 80);
EXPECT_EQ(
0u, impl_.GetAlternativeServiceInfos(https_server, NetworkIsolationKey())
.size());
// Set Alt-Svc list for |https_server|.
impl_.SetAlternativeServices(https_server, NetworkIsolationKey(),
alternative_service_info_vector);
EXPECT_EQ(
2u, impl_.GetAlternativeServiceInfos(https_server, NetworkIsolationKey())
.size());
EXPECT_EQ(2u,
impl_.GetAlternativeServiceInfos(http_server, NetworkIsolationKey())
.size());
// Clear Alt-Svc list for |http_server|.
impl_.SetAlternativeServices(http_server, NetworkIsolationKey(),
AlternativeServiceInfoVector());
EXPECT_EQ(0u,
impl_.GetAlternativeServiceInfos(http_server, NetworkIsolationKey())
.size());
EXPECT_EQ(
2u, impl_.GetAlternativeServiceInfos(https_server, NetworkIsolationKey())
.size());
}
TEST_F(AlternateProtocolServerPropertiesTest, ClearAlternativeServices) {
AlternativeServiceInfoVector alternative_service_info_vector;
const AlternativeService alternative_service1(kProtoHTTP2, "foo", 443);
base::Time expiration = test_clock_.Now() + base::TimeDelta::FromDays(1);
alternative_service_info_vector.push_back(
AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
alternative_service1, expiration));
const AlternativeService alternative_service2(kProtoHTTP2, "bar", 1234);
alternative_service_info_vector.push_back(
AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
alternative_service2, expiration));
url::SchemeHostPort test_server("http", "foo", 80);
impl_.SetAlternativeServices(test_server, NetworkIsolationKey(),
alternative_service_info_vector);
const net::HttpServerProperties::ServerInfoMap& map =
impl_.server_info_map_for_testing();
auto it = map.begin();
EXPECT_EQ(test_server, it->first.server);
EXPECT_TRUE(it->first.network_isolation_key.IsEmpty());
ASSERT_TRUE(it->second.alternative_services.has_value());
ASSERT_EQ(2u, it->second.alternative_services->size());
EXPECT_EQ(alternative_service1,
it->second.alternative_services.value()[0].alternative_service());
EXPECT_EQ(alternative_service2,
it->second.alternative_services.value()[1].alternative_service());
impl_.SetAlternativeServices(test_server, NetworkIsolationKey(),
AlternativeServiceInfoVector());
EXPECT_TRUE(map.empty());
}
// A broken alternative service in the mapping carries meaningful information,
// therefore it should not be ignored by SetAlternativeService(). In
// particular, an alternative service mapped to an origin shadows alternative
// services of canonical hosts.
TEST_F(AlternateProtocolServerPropertiesTest, BrokenShadowsCanonical) {
url::SchemeHostPort test_server("https", "foo.c.youtube.com", 443);
url::SchemeHostPort canonical_server("https", "bar.c.youtube.com", 443);
AlternativeService canonical_alternative_service(kProtoQUIC,
"bar.c.youtube.com", 1234);
SetAlternativeService(canonical_server, canonical_alternative_service);
AlternativeServiceInfoVector alternative_service_info_vector =
impl_.GetAlternativeServiceInfos(test_server, NetworkIsolationKey());
ASSERT_EQ(1u, alternative_service_info_vector.size());
EXPECT_EQ(canonical_alternative_service,
alternative_service_info_vector[0].alternative_service());
const AlternativeService broken_alternative_service(kProtoHTTP2, "foo", 443);
impl_.MarkAlternativeServiceBroken(broken_alternative_service,
NetworkIsolationKey());
EXPECT_TRUE(impl_.IsAlternativeServiceBroken(broken_alternative_service,
NetworkIsolationKey()));
SetAlternativeService(test_server, broken_alternative_service);
alternative_service_info_vector =
impl_.GetAlternativeServiceInfos(test_server, NetworkIsolationKey());
ASSERT_EQ(1u, alternative_service_info_vector.size());
EXPECT_EQ(broken_alternative_service,
alternative_service_info_vector[0].alternative_service());
EXPECT_TRUE(impl_.IsAlternativeServiceBroken(broken_alternative_service,
NetworkIsolationKey()));
}
TEST_F(AlternateProtocolServerPropertiesTest, ClearBroken) {
url::SchemeHostPort test_server("http", "foo", 80);
const AlternativeService alternative_service(kProtoHTTP2, "foo", 443);
SetAlternativeService(test_server, alternative_service);
impl_.MarkAlternativeServiceBroken(alternative_service,
NetworkIsolationKey());
ASSERT_TRUE(HasAlternativeService(test_server, NetworkIsolationKey()));
EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service,
NetworkIsolationKey()));
// SetAlternativeServices should leave a broken alternative service marked
// as such.
impl_.SetAlternativeServices(test_server, NetworkIsolationKey(),
AlternativeServiceInfoVector());
EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service,
NetworkIsolationKey()));
}
TEST_F(AlternateProtocolServerPropertiesTest,
MarkBrokenWithNetworkIsolationKey) {
url::SchemeHostPort server("http", "foo", 80);
const AlternativeService alternative_service(kProtoHTTP2, "foo", 443);
const base::Time expiration =
test_clock_.Now() + base::TimeDelta::FromDays(1);
// Without NetworkIsolationKeys enabled, the NetworkIsolationKey parameter
// should be ignored.
impl_.SetHttp2AlternativeService(server, network_isolation_key1_,
alternative_service, expiration);
EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service,
network_isolation_key1_));
EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key1_));
EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service,
network_isolation_key2_));
EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key2_));
impl_.MarkAlternativeServiceBroken(alternative_service,
network_isolation_key1_);
EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service,
network_isolation_key1_));
EXPECT_TRUE(impl_.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key1_));
EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service,
network_isolation_key2_));
EXPECT_TRUE(impl_.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key2_));
impl_.ConfirmAlternativeService(alternative_service, network_isolation_key2_);
EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service,
network_isolation_key1_));
EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key1_));
EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service,
network_isolation_key2_));
EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key2_));
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(
features::kPartitionHttpServerPropertiesByNetworkIsolationKey);
// Since HttpServerProperties caches the feature value, have to create a new
// one.
HttpServerProperties properties(nullptr /* pref_delegate */,
nullptr /* net_log */, test_tick_clock_,
&test_clock_);
properties.SetHttp2AlternativeService(server, network_isolation_key1_,
alternative_service, expiration);
properties.SetHttp2AlternativeService(server, network_isolation_key2_,
alternative_service, expiration);
EXPECT_FALSE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key1_));
EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key1_));
EXPECT_FALSE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key2_));
EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key2_));
properties.MarkAlternativeServiceBroken(alternative_service,
network_isolation_key1_);
EXPECT_TRUE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key1_));
EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key1_));
EXPECT_FALSE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key2_));
EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key2_));
properties.MarkAlternativeServiceBroken(alternative_service,
network_isolation_key2_);
EXPECT_TRUE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key1_));
EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key1_));
EXPECT_TRUE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key2_));
EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key2_));
properties.ConfirmAlternativeService(alternative_service,
network_isolation_key1_);
EXPECT_FALSE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key1_));
EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key1_));
EXPECT_TRUE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key2_));
EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key2_));
properties.ConfirmAlternativeService(alternative_service,
network_isolation_key2_);
EXPECT_FALSE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key1_));
EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key1_));
EXPECT_FALSE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key2_));
EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key2_));
}
TEST_F(AlternateProtocolServerPropertiesTest, MarkRecentlyBroken) {
url::SchemeHostPort server("http", "foo", 80);
const AlternativeService alternative_service(kProtoHTTP2, "foo", 443);
SetAlternativeService(server, alternative_service);
EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service,
NetworkIsolationKey()));
EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken(
alternative_service, NetworkIsolationKey()));
impl_.MarkAlternativeServiceRecentlyBroken(alternative_service,
NetworkIsolationKey());
EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service,
NetworkIsolationKey()));
EXPECT_TRUE(impl_.WasAlternativeServiceRecentlyBroken(alternative_service,
NetworkIsolationKey()));
impl_.ConfirmAlternativeService(alternative_service, NetworkIsolationKey());
EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service,
NetworkIsolationKey()));
EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken(
alternative_service, NetworkIsolationKey()));
}
TEST_F(AlternateProtocolServerPropertiesTest,
MarkRecentlyBrokenWithNetworkIsolationKey) {
url::SchemeHostPort server("http", "foo", 80);
const AlternativeService alternative_service(kProtoHTTP2, "foo", 443);
const base::Time expiration =
test_clock_.Now() + base::TimeDelta::FromDays(1);
// Without NetworkIsolationKeys enabled, the NetworkIsolationKey parameter
// should be ignored.
impl_.SetHttp2AlternativeService(server, network_isolation_key1_,
alternative_service, expiration);
EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service,
network_isolation_key1_));
EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key1_));
EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service,
network_isolation_key2_));
EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key2_));
impl_.MarkAlternativeServiceRecentlyBroken(alternative_service,
network_isolation_key1_);
EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service,
network_isolation_key1_));
EXPECT_TRUE(impl_.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key1_));
EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service,
network_isolation_key2_));
EXPECT_TRUE(impl_.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key2_));
impl_.ConfirmAlternativeService(alternative_service, network_isolation_key2_);
EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service,
network_isolation_key1_));
EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key1_));
EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service,
network_isolation_key2_));
EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key2_));
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(
features::kPartitionHttpServerPropertiesByNetworkIsolationKey);
// Since HttpServerProperties caches the feature value, have to create a new
// one.
HttpServerProperties properties(nullptr /* pref_delegate */,
nullptr /* net_log */, test_tick_clock_,
&test_clock_);
properties.SetHttp2AlternativeService(server, network_isolation_key1_,
alternative_service, expiration);
properties.SetHttp2AlternativeService(server, network_isolation_key2_,
alternative_service, expiration);
EXPECT_FALSE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key1_));
EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key1_));
EXPECT_FALSE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key2_));
EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key2_));
properties.MarkAlternativeServiceRecentlyBroken(alternative_service,
network_isolation_key1_);
EXPECT_FALSE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key1_));
EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key1_));
EXPECT_FALSE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key2_));
EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key2_));
properties.MarkAlternativeServiceRecentlyBroken(alternative_service,
network_isolation_key2_);
EXPECT_FALSE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key1_));
EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key1_));
EXPECT_FALSE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key2_));
EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key2_));
properties.ConfirmAlternativeService(alternative_service,
network_isolation_key1_);
EXPECT_FALSE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key1_));
EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key1_));
EXPECT_FALSE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key2_));
EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key2_));
properties.ConfirmAlternativeService(alternative_service,
network_isolation_key2_);
EXPECT_FALSE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key1_));
EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key1_));
EXPECT_FALSE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key2_));
EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key2_));
}
TEST_F(AlternateProtocolServerPropertiesTest,
MarkBrokenUntilDefaultNetworkChanges) {
url::SchemeHostPort server("http", "foo", 80);
const AlternativeService alternative_service(kProtoHTTP2, "foo", 443);
SetAlternativeService(server, alternative_service);
EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service,
NetworkIsolationKey()));
EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken(
alternative_service, NetworkIsolationKey()));
impl_.MarkAlternativeServiceBrokenUntilDefaultNetworkChanges(
alternative_service, NetworkIsolationKey());
EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service,
NetworkIsolationKey()));
EXPECT_TRUE(impl_.WasAlternativeServiceRecentlyBroken(alternative_service,
NetworkIsolationKey()));
impl_.ConfirmAlternativeService(alternative_service, NetworkIsolationKey());
EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service,
NetworkIsolationKey()));
EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken(
alternative_service, NetworkIsolationKey()));
}
TEST_F(AlternateProtocolServerPropertiesTest,
MarkBrokenUntilDefaultNetworkChangesWithNetworkIsolationKey) {
url::SchemeHostPort server("http", "foo", 80);
const AlternativeService alternative_service(kProtoHTTP2, "foo", 443);
const base::Time expiration =
test_clock_.Now() + base::TimeDelta::FromDays(1);
// Without NetworkIsolationKeys enabled, the NetworkIsolationKey parameter
// should be ignored.
impl_.SetHttp2AlternativeService(server, network_isolation_key1_,
alternative_service, expiration);
EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service,
network_isolation_key1_));
EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key1_));
EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service,
network_isolation_key2_));
EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key2_));
impl_.MarkAlternativeServiceBrokenUntilDefaultNetworkChanges(
alternative_service, network_isolation_key1_);
EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service,
network_isolation_key1_));
EXPECT_TRUE(impl_.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key1_));
EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service,
network_isolation_key2_));
EXPECT_TRUE(impl_.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key2_));
impl_.ConfirmAlternativeService(alternative_service, network_isolation_key2_);
EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service,
network_isolation_key1_));
EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key1_));
EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service,
network_isolation_key2_));
EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key2_));
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(
features::kPartitionHttpServerPropertiesByNetworkIsolationKey);
// Since HttpServerProperties caches the feature value, have to create a new
// one.
HttpServerProperties properties(nullptr /* pref_delegate */,
nullptr /* net_log */, test_tick_clock_,
&test_clock_);
properties.SetHttp2AlternativeService(server, network_isolation_key1_,
alternative_service, expiration);
properties.SetHttp2AlternativeService(server, network_isolation_key2_,
alternative_service, expiration);
EXPECT_FALSE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key1_));
EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key1_));
EXPECT_FALSE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key2_));
EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key2_));
properties.MarkAlternativeServiceBrokenUntilDefaultNetworkChanges(
alternative_service, network_isolation_key1_);
EXPECT_TRUE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key1_));
EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key1_));
EXPECT_FALSE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key2_));
EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key2_));
properties.MarkAlternativeServiceBrokenUntilDefaultNetworkChanges(
alternative_service, network_isolation_key2_);
EXPECT_TRUE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key1_));
EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key1_));
EXPECT_TRUE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key2_));
EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key2_));
properties.ConfirmAlternativeService(alternative_service,
network_isolation_key1_);
EXPECT_FALSE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key1_));
EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key1_));
EXPECT_TRUE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key2_));
EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key2_));
properties.ConfirmAlternativeService(alternative_service,
network_isolation_key2_);
EXPECT_FALSE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key1_));
EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key1_));
EXPECT_FALSE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key2_));
EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key2_));
}
TEST_F(AlternateProtocolServerPropertiesTest, OnDefaultNetworkChanged) {
url::SchemeHostPort server("http", "foo", 80);
const AlternativeService alternative_service(kProtoHTTP2, "foo", 443);
SetAlternativeService(server, alternative_service);
EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service,
NetworkIsolationKey()));
EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken(
alternative_service, NetworkIsolationKey()));
impl_.MarkAlternativeServiceBrokenUntilDefaultNetworkChanges(
alternative_service, NetworkIsolationKey());
EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service,
NetworkIsolationKey()));
EXPECT_TRUE(impl_.WasAlternativeServiceRecentlyBroken(alternative_service,
NetworkIsolationKey()));
// Default network change clears alt svc broken until default network changes.
impl_.OnDefaultNetworkChanged();
EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service,
NetworkIsolationKey()));
EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken(
alternative_service, NetworkIsolationKey()));
impl_.MarkAlternativeServiceBrokenUntilDefaultNetworkChanges(
alternative_service, NetworkIsolationKey());
EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service,
NetworkIsolationKey()));
EXPECT_TRUE(impl_.WasAlternativeServiceRecentlyBroken(alternative_service,
NetworkIsolationKey()));
impl_.MarkAlternativeServiceBroken(alternative_service,
NetworkIsolationKey());
EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service,
NetworkIsolationKey()));
EXPECT_TRUE(impl_.WasAlternativeServiceRecentlyBroken(alternative_service,
NetworkIsolationKey()));
// Default network change doesn't affect alt svc that was simply marked broken
// most recently.
impl_.OnDefaultNetworkChanged();
EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service,
NetworkIsolationKey()));
EXPECT_TRUE(impl_.WasAlternativeServiceRecentlyBroken(alternative_service,
NetworkIsolationKey()));
impl_.MarkAlternativeServiceBrokenUntilDefaultNetworkChanges(
alternative_service, NetworkIsolationKey());
EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service,
NetworkIsolationKey()));
EXPECT_TRUE(impl_.WasAlternativeServiceRecentlyBroken(alternative_service,
NetworkIsolationKey()));
// Default network change clears alt svc that was marked broken until default
// network change most recently even if the alt svc was initially marked
// broken.
impl_.OnDefaultNetworkChanged();
EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service,
NetworkIsolationKey()));
EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken(
alternative_service, NetworkIsolationKey()));
}
TEST_F(AlternateProtocolServerPropertiesTest,
OnDefaultNetworkChangedWithNetworkIsolationKey) {
url::SchemeHostPort server("http", "foo", 80);
const AlternativeService alternative_service(kProtoHTTP2, "foo", 443);
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(
features::kPartitionHttpServerPropertiesByNetworkIsolationKey);
// Since HttpServerProperties caches the feature value, have to create a new
// one.
HttpServerProperties properties(nullptr /* pref_delegate */,
nullptr /* net_log */, test_tick_clock_,
&test_clock_);
const base::Time expiration =
test_clock_.Now() + base::TimeDelta::FromDays(1);
properties.SetHttp2AlternativeService(server, network_isolation_key1_,
alternative_service, expiration);
properties.SetHttp2AlternativeService(server, network_isolation_key2_,
alternative_service, expiration);
EXPECT_FALSE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key1_));
EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key1_));
EXPECT_FALSE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key2_));
EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key2_));
properties.MarkAlternativeServiceBrokenUntilDefaultNetworkChanges(
alternative_service, network_isolation_key1_);
properties.MarkAlternativeServiceBrokenUntilDefaultNetworkChanges(
alternative_service, network_isolation_key2_);
EXPECT_TRUE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key1_));
EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key1_));
EXPECT_TRUE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key2_));
EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key2_));
// Default network change clears alt svc broken until default network changes.
properties.OnDefaultNetworkChanged();
EXPECT_FALSE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key1_));
EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key1_));
EXPECT_FALSE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key2_));
EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key2_));
}
TEST_F(AlternateProtocolServerPropertiesTest, Canonical) {
url::SchemeHostPort test_server("https", "foo.c.youtube.com", 443);
EXPECT_FALSE(HasAlternativeService(test_server, NetworkIsolationKey()));
url::SchemeHostPort canonical_server("https", "bar.c.youtube.com", 443);
EXPECT_FALSE(HasAlternativeService(canonical_server, NetworkIsolationKey()));
AlternativeServiceInfoVector alternative_service_info_vector;
const AlternativeService canonical_alternative_service1(
kProtoQUIC, "bar.c.youtube.com", 1234);
base::Time expiration = test_clock_.Now() + base::TimeDelta::FromDays(1);
alternative_service_info_vector.push_back(
AlternativeServiceInfo::CreateQuicAlternativeServiceInfo(
canonical_alternative_service1, expiration,
HttpNetworkSession::Params().quic_params.supported_versions));
const AlternativeService canonical_alternative_service2(kProtoHTTP2, "", 443);
alternative_service_info_vector.push_back(
AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
canonical_alternative_service2, expiration));
impl_.SetAlternativeServices(canonical_server, NetworkIsolationKey(),
alternative_service_info_vector);
// Since |test_server| does not have an alternative service itself,
// GetAlternativeServiceInfos should return those of |canonical_server|.
AlternativeServiceInfoVector alternative_service_info_vector2 =
impl_.GetAlternativeServiceInfos(test_server, NetworkIsolationKey());
ASSERT_EQ(2u, alternative_service_info_vector2.size());
EXPECT_EQ(canonical_alternative_service1,
alternative_service_info_vector2[0].alternative_service());
// Since |canonical_alternative_service2| has an empty host,
// GetAlternativeServiceInfos should substitute the hostname of its |origin|
// argument.
EXPECT_EQ(test_server.host(),
alternative_service_info_vector2[1].alternative_service().host);
EXPECT_EQ(canonical_alternative_service2.protocol,
alternative_service_info_vector2[1].alternative_service().protocol);
EXPECT_EQ(canonical_alternative_service2.port,
alternative_service_info_vector2[1].alternative_service().port);
// Verify the canonical suffix.
EXPECT_EQ(".c.youtube.com",
*impl_.GetCanonicalSuffixForTesting(test_server.host()));
EXPECT_EQ(".c.youtube.com",
*impl_.GetCanonicalSuffixForTesting(canonical_server.host()));
}
TEST_F(AlternateProtocolServerPropertiesTest, ClearCanonical) {
url::SchemeHostPort test_server("https", "foo.c.youtube.com", 443);
url::SchemeHostPort canonical_server("https", "bar.c.youtube.com", 443);
AlternativeService canonical_alternative_service(kProtoQUIC,
"bar.c.youtube.com", 1234);
SetAlternativeService(canonical_server, canonical_alternative_service);
impl_.SetAlternativeServices(canonical_server, NetworkIsolationKey(),
AlternativeServiceInfoVector());
EXPECT_FALSE(HasAlternativeService(test_server, NetworkIsolationKey()));
}
TEST_F(AlternateProtocolServerPropertiesTest,
CanonicalWithNetworkIsolationKey) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(
features::kPartitionHttpServerPropertiesByNetworkIsolationKey);
// Since HttpServerProperties caches the feature value, have to create a new
// one.
HttpServerProperties properties(nullptr /* pref_delegate */,
nullptr /* net_log */, test_tick_clock_,
&test_clock_);
url::SchemeHostPort test_server("https", "foo.c.youtube.com", 443);
EXPECT_FALSE(HasAlternativeService(test_server, network_isolation_key1_));
url::SchemeHostPort canonical_server1("https", "bar.c.youtube.com", 443);
EXPECT_FALSE(
HasAlternativeService(canonical_server1, network_isolation_key1_));
AlternativeServiceInfoVector alternative_service_info_vector;
const AlternativeService canonical_alternative_service1(
kProtoQUIC, "bar.c.youtube.com", 1234);
base::Time expiration = test_clock_.Now() + base::TimeDelta::FromDays(1);
alternative_service_info_vector.push_back(
AlternativeServiceInfo::CreateQuicAlternativeServiceInfo(
canonical_alternative_service1, expiration,
HttpNetworkSession::Params().quic_params.supported_versions));
const AlternativeService canonical_alternative_service2(kProtoHTTP2, "", 443);
alternative_service_info_vector.push_back(
AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
canonical_alternative_service2, expiration));
properties.SetAlternativeServices(canonical_server1, network_isolation_key1_,
alternative_service_info_vector);
// Since |test_server| does not have an alternative service itself,
// GetAlternativeServiceInfos should return those of |canonical_server|.
AlternativeServiceInfoVector alternative_service_info_vector2 =
properties.GetAlternativeServiceInfos(test_server,
network_isolation_key1_);
ASSERT_EQ(2u, alternative_service_info_vector2.size());
EXPECT_EQ(canonical_alternative_service1,
alternative_service_info_vector2[0].alternative_service());
// Canonical information should not be visible for other NetworkIsolationKeys.
EXPECT_TRUE(
properties
.GetAlternativeServiceInfos(test_server, network_isolation_key2_)
.empty());
EXPECT_TRUE(
properties.GetAlternativeServiceInfos(test_server, NetworkIsolationKey())
.empty());
// Now add an alternative service entry for network_isolation_key2_ for a
// different server and different NetworkIsolationKey, but with the same
// canonical suffix.
url::SchemeHostPort canonical_server2("https", "shrimp.c.youtube.com", 443);
properties.SetAlternativeServices(canonical_server2, network_isolation_key2_,
{alternative_service_info_vector[0]});
// The canonical server information should reachable, and different, for both
// NetworkIsolationKeys.
EXPECT_EQ(
1u, properties
.GetAlternativeServiceInfos(test_server, network_isolation_key2_)
.size());
EXPECT_EQ(
2u, properties
.GetAlternativeServiceInfos(test_server, network_isolation_key1_)
.size());
EXPECT_TRUE(
properties.GetAlternativeServiceInfos(test_server, NetworkIsolationKey())
.empty());
// Clearing the alternate service state of network_isolation_key1_'s canonical
// server should only affect network_isolation_key1_.
properties.SetAlternativeServices(canonical_server1, network_isolation_key1_,
{});
EXPECT_EQ(
1u, properties
.GetAlternativeServiceInfos(test_server, network_isolation_key2_)
.size());
EXPECT_TRUE(
properties
.GetAlternativeServiceInfos(test_server, network_isolation_key1_)
.empty());
EXPECT_TRUE(
properties.GetAlternativeServiceInfos(test_server, NetworkIsolationKey())
.empty());
}
TEST_F(AlternateProtocolServerPropertiesTest, CanonicalBroken) {
url::SchemeHostPort test_server("https", "foo.c.youtube.com", 443);
url::SchemeHostPort canonical_server("https", "bar.c.youtube.com", 443);
AlternativeService canonical_alternative_service(kProtoQUIC,
"bar.c.youtube.com", 1234);
SetAlternativeService(canonical_server, canonical_alternative_service);
EXPECT_TRUE(HasAlternativeService(test_server, NetworkIsolationKey()));
impl_.MarkAlternativeServiceBroken(canonical_alternative_service,
NetworkIsolationKey());
EXPECT_FALSE(HasAlternativeService(test_server, NetworkIsolationKey()));
}
TEST_F(AlternateProtocolServerPropertiesTest,
CanonicalBrokenUntilDefaultNetworkChanges) {
url::SchemeHostPort test_server("https", "foo.c.youtube.com", 443);
url::SchemeHostPort canonical_server("https", "bar.c.youtube.com", 443);
AlternativeService canonical_alternative_service(kProtoQUIC,
"bar.c.youtube.com", 1234);
SetAlternativeService(canonical_server, canonical_alternative_service);
EXPECT_TRUE(HasAlternativeService(test_server, NetworkIsolationKey()));
impl_.MarkAlternativeServiceBrokenUntilDefaultNetworkChanges(
canonical_alternative_service, NetworkIsolationKey());
EXPECT_FALSE(HasAlternativeService(test_server, NetworkIsolationKey()));
}
// Adding an alternative service for a new host overrides canonical host.
TEST_F(AlternateProtocolServerPropertiesTest, CanonicalOverride) {
url::SchemeHostPort foo_server("https", "foo.c.youtube.com", 443);
url::SchemeHostPort bar_server("https", "bar.c.youtube.com", 443);
AlternativeService bar_alternative_service(kProtoQUIC, "bar.c.youtube.com",
1234);
SetAlternativeService(bar_server, bar_alternative_service);
AlternativeServiceInfoVector alternative_service_info_vector =
impl_.GetAlternativeServiceInfos(foo_server, NetworkIsolationKey());
ASSERT_EQ(1u, alternative_service_info_vector.size());
EXPECT_EQ(bar_alternative_service,
alternative_service_info_vector[0].alternative_service());
url::SchemeHostPort qux_server("https", "qux.c.youtube.com", 443);
AlternativeService qux_alternative_service(kProtoQUIC, "qux.c.youtube.com",
443);
SetAlternativeService(qux_server, qux_alternative_service);
alternative_service_info_vector =
impl_.GetAlternativeServiceInfos(foo_server, NetworkIsolationKey());
ASSERT_EQ(1u, alternative_service_info_vector.size());
EXPECT_EQ(qux_alternative_service,
alternative_service_info_vector[0].alternative_service());
}
TEST_F(AlternateProtocolServerPropertiesTest, ClearWithCanonical) {
url::SchemeHostPort test_server("https", "foo.c.youtube.com", 443);
url::SchemeHostPort canonical_server("https", "bar.c.youtube.com", 443);
AlternativeService canonical_alternative_service(kProtoQUIC,
"bar.c.youtube.com", 1234);
SetAlternativeService(canonical_server, canonical_alternative_service);
impl_.Clear(base::OnceClosure());
EXPECT_FALSE(HasAlternativeService(test_server, NetworkIsolationKey()));
}
TEST_F(AlternateProtocolServerPropertiesTest,
ExpireBrokenAlternateProtocolMappings) {
url::SchemeHostPort server("https", "foo", 443);
AlternativeService alternative_service(kProtoQUIC, "foo", 443);
SetAlternativeService(server, alternative_service);
EXPECT_TRUE(HasAlternativeService(server, NetworkIsolationKey()));
EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service,
NetworkIsolationKey()));
EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken(
alternative_service, NetworkIsolationKey()));
base::TimeTicks past =
test_tick_clock_->NowTicks() - base::TimeDelta::FromSeconds(42);
HttpServerPropertiesPeer::AddBrokenAlternativeServiceWithExpirationTime(
&impl_, alternative_service, past);
EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service,
NetworkIsolationKey()));
EXPECT_TRUE(impl_.WasAlternativeServiceRecentlyBroken(alternative_service,
NetworkIsolationKey()));
HttpServerPropertiesPeer::ExpireBrokenAlternateProtocolMappings(&impl_);
EXPECT_FALSE(HasAlternativeService(server, NetworkIsolationKey()));
EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service,
NetworkIsolationKey()));
EXPECT_TRUE(impl_.WasAlternativeServiceRecentlyBroken(alternative_service,
NetworkIsolationKey()));
}
TEST_F(AlternateProtocolServerPropertiesTest,
ExpireBrokenAlternateProtocolMappingsWithNetworkIsolationKey) {
url::SchemeHostPort server("https", "foo", 443);
AlternativeService alternative_service(kProtoHTTP2, "foo", 444);
base::TimeTicks past =
test_tick_clock_->NowTicks() - base::TimeDelta::FromSeconds(42);
base::TimeTicks future =
test_tick_clock_->NowTicks() + base::TimeDelta::FromSeconds(42);
const base::Time alt_service_expiration =
test_clock_.Now() + base::TimeDelta::FromDays(1);
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(
features::kPartitionHttpServerPropertiesByNetworkIsolationKey);
// Since HttpServerProperties caches the feature value, have to create a new
// one.
HttpServerProperties properties(nullptr /* pref_delegate */,
nullptr /* net_log */, test_tick_clock_,
&test_clock_);
properties.SetHttp2AlternativeService(server, network_isolation_key1_,
alternative_service,
alt_service_expiration);
properties.SetHttp2AlternativeService(server, network_isolation_key2_,
alternative_service,
alt_service_expiration);
EXPECT_FALSE(
properties.GetAlternativeServiceInfos(server, network_isolation_key1_)
.empty());
EXPECT_FALSE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key1_));
EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key1_));
EXPECT_FALSE(
properties.GetAlternativeServiceInfos(server, network_isolation_key2_)
.empty());
EXPECT_FALSE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key2_));
EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key2_));
// Set broken alternative service with expiration date in the past for
// |network_isolation_key1_|.
HttpServerPropertiesPeer::AddBrokenAlternativeServiceWithExpirationTime(
&properties, alternative_service, past, network_isolation_key1_);
EXPECT_TRUE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key1_));
EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key1_));
EXPECT_FALSE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key2_));
EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key2_));
// Set broken alternative service with expiration date in the future for
// |network_isolation_key1_|.
HttpServerPropertiesPeer::AddBrokenAlternativeServiceWithExpirationTime(
&properties, alternative_service, future, network_isolation_key2_);
EXPECT_TRUE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key1_));
EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key1_));
EXPECT_TRUE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key2_));
EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key2_));
// Only the broken entry for |network_isolation_key1_| should be expired.
HttpServerPropertiesPeer::ExpireBrokenAlternateProtocolMappings(&properties);
EXPECT_TRUE(
properties.GetAlternativeServiceInfos(server, network_isolation_key1_)
.empty());
EXPECT_FALSE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key1_));
EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key1_));
EXPECT_FALSE(
properties.GetAlternativeServiceInfos(server, network_isolation_key2_)
.empty());
EXPECT_TRUE(properties.IsAlternativeServiceBroken(alternative_service,
network_isolation_key2_));
EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken(
alternative_service, network_isolation_key2_));
}
// Regression test for https://crbug.com/505413.
TEST_F(AlternateProtocolServerPropertiesTest, RemoveExpiredBrokenAltSvc) {
url::SchemeHostPort foo_server("https", "foo", 443);
AlternativeService bar_alternative_service(kProtoQUIC, "bar", 443);
SetAlternativeService(foo_server, bar_alternative_service);
EXPECT_TRUE(HasAlternativeService(foo_server, NetworkIsolationKey()));
url::SchemeHostPort bar_server1("http", "bar", 80);
AlternativeService nohost_alternative_service(kProtoQUIC, "", 443);
SetAlternativeService(bar_server1, nohost_alternative_service);
EXPECT_TRUE(HasAlternativeService(bar_server1, NetworkIsolationKey()));
url::SchemeHostPort bar_server2("https", "bar", 443);
AlternativeService baz_alternative_service(kProtoQUIC, "baz", 1234);
SetAlternativeService(bar_server2, baz_alternative_service);
EXPECT_TRUE(HasAlternativeService(bar_server2, NetworkIsolationKey()));
// Mark "bar:443" as broken.
base::TimeTicks past =
test_tick_clock_->NowTicks() - base::TimeDelta::FromSeconds(42);
HttpServerPropertiesPeer::AddBrokenAlternativeServiceWithExpirationTime(
&impl_, bar_alternative_service, past);
// Expire brokenness of "bar:443".
HttpServerPropertiesPeer::ExpireBrokenAlternateProtocolMappings(&impl_);
// "foo:443" should have no alternative service now.
EXPECT_FALSE(HasAlternativeService(foo_server, NetworkIsolationKey()));
// "bar:80" should have no alternative service now.
EXPECT_FALSE(HasAlternativeService(bar_server1, NetworkIsolationKey()));
// The alternative service of "bar:443" should be unaffected.
EXPECT_TRUE(HasAlternativeService(bar_server2, NetworkIsolationKey()));
EXPECT_TRUE(impl_.WasAlternativeServiceRecentlyBroken(bar_alternative_service,
NetworkIsolationKey()));
EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken(
baz_alternative_service, NetworkIsolationKey()));
}
// Regression test for https://crbug.com/724302
TEST_F(AlternateProtocolServerPropertiesTest, RemoveExpiredBrokenAltSvc2) {
// This test will mark an alternative service A that has already been marked
// broken many times, then immediately mark another alternative service B as
// broken for the first time. Because A's been marked broken many times
// already, its brokenness will be scheduled to expire much further in the
// future than B, even though it was marked broken before B. This test makes
// sure that even though A was marked broken before B, B's brokenness should
// expire before A.
url::SchemeHostPort server1("https", "foo", 443);
AlternativeService alternative_service1(kProtoQUIC, "foo", 443);
SetAlternativeService(server1, alternative_service1);
url::SchemeHostPort server2("https", "bar", 443);
AlternativeService alternative_service2(kProtoQUIC, "bar", 443);
SetAlternativeService(server2, alternative_service2);
// Repeatedly mark alt svc 1 broken and wait for its brokenness to expire.
// This will increase its time until expiration.
for (int i = 0; i < 3; ++i) {
impl_.MarkAlternativeServiceBroken(alternative_service1,
NetworkIsolationKey());
// |impl_| should have posted task to expire the brokenness of
// |alternative_service1|
EXPECT_EQ(1u, GetPendingMainThreadTaskCount());
EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service1,
NetworkIsolationKey()));
// Advance time by just enough so that |alternative_service1|'s brokenness
// expires.
FastForwardBy(BROKEN_ALT_SVC_EXPIRE_DELAYS[i]);
// Ensure brokenness of |alternative_service1| has expired.
EXPECT_EQ(0u, GetPendingMainThreadTaskCount());
EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service1,
NetworkIsolationKey()));
}
impl_.MarkAlternativeServiceBroken(alternative_service1,
NetworkIsolationKey());
impl_.MarkAlternativeServiceBroken(alternative_service2,
NetworkIsolationKey());
EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service2,
NetworkIsolationKey()));
// Advance time by just enough so that |alternative_service2|'s brokennness
// expires.
FastForwardBy(BROKEN_ALT_SVC_EXPIRE_DELAYS[0]);
EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service1,
NetworkIsolationKey()));
EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service2,
NetworkIsolationKey()));
// Advance time by enough so that |alternative_service1|'s brokenness expires.
FastForwardBy(BROKEN_ALT_SVC_EXPIRE_DELAYS[3] -
BROKEN_ALT_SVC_EXPIRE_DELAYS[0]);
EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service1,
NetworkIsolationKey()));
EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service2,
NetworkIsolationKey()));
}
// Regression test for https://crbug.com/994537. Having a ServerInfo entry
// without a populated |alternative_services| value would cause
// OnExpireBrokenAlternativeService() to hang..
TEST_F(AlternateProtocolServerPropertiesTest, RemoveExpiredBrokenAltSvc3) {
// Add an altertive service entry.
const url::SchemeHostPort kServer1("https", "foo", 443);
const AlternativeService kAltService(kProtoQUIC, "bar", 443);
SetAlternativeService(kServer1, kAltService);
EXPECT_TRUE(HasAlternativeService(kServer1, NetworkIsolationKey()));
// Add an entry to ServerInfo for another server, without an alternative
// service value.
const url::SchemeHostPort kServer2("http", "bar", 80);
impl_.SetSupportsSpdy(kServer2, NetworkIsolationKey(), false);
// Mark kAltService as broken.
base::TimeTicks past =
test_tick_clock_->NowTicks() - base::TimeDelta::FromSeconds(42);
HttpServerPropertiesPeer::AddBrokenAlternativeServiceWithExpirationTime(
&impl_, kAltService, past);
// Expire brokenness of kAltService. This call should not hang.
HttpServerPropertiesPeer::ExpireBrokenAlternateProtocolMappings(&impl_);
EXPECT_FALSE(HasAlternativeService(kServer1, NetworkIsolationKey()));
}
TEST_F(AlternateProtocolServerPropertiesTest,
GetAlternativeServiceInfoAsValue) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndDisableFeature(
features::kAppendFrameOriginToNetworkIsolationKey);
base::Time::Exploded now_exploded;
now_exploded.year = 2018;
now_exploded.month = 1;
now_exploded.day_of_week = 3;
now_exploded.day_of_month = 24;
now_exploded.hour = 15;
now_exploded.minute = 12;
now_exploded.second = 53;
now_exploded.millisecond = 0;
base::Time now;
bool result = base::Time::FromLocalExploded(now_exploded, &now);
DCHECK(result);
test_clock_.SetNow(now);
AlternativeServiceInfoVector alternative_service_info_vector;
alternative_service_info_vector.push_back(
AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
AlternativeService(kProtoHTTP2, "foo", 443),
now + base::TimeDelta::FromMinutes(1)));
alternative_service_info_vector.push_back(
AlternativeServiceInfo::CreateQuicAlternativeServiceInfo(
AlternativeService(kProtoQUIC, "bar", 443),
now + base::TimeDelta::FromHours(1),
HttpNetworkSession::Params().quic_params.supported_versions));
alternative_service_info_vector.push_back(
AlternativeServiceInfo::CreateQuicAlternativeServiceInfo(
AlternativeService(kProtoQUIC, "baz", 443),
now + base::TimeDelta::FromHours(1),
HttpNetworkSession::Params().quic_params.supported_versions));
impl_.SetAlternativeServices(url::SchemeHostPort("https", "youtube.com", 443),
NetworkIsolationKey(),
alternative_service_info_vector);
impl_.MarkAlternativeServiceBroken(AlternativeService(kProtoQUIC, "bar", 443),
NetworkIsolationKey());
impl_.MarkAlternativeServiceBrokenUntilDefaultNetworkChanges(
AlternativeService(kProtoQUIC, "baz", 443), NetworkIsolationKey());
alternative_service_info_vector.clear();
alternative_service_info_vector.push_back(
AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
AlternativeService(kProtoHTTP2, "foo2", 443),
now + base::TimeDelta::FromDays(1)));
impl_.SetAlternativeServices(url::SchemeHostPort("http", "test.com", 80),
NetworkIsolationKey(),
alternative_service_info_vector);
const char expected_json[] =
"["
"{"
"\"alternative_service\":"
"[\"h2 foo2:443, expires 2018-01-25 15:12:53\"],"
"\"network_isolation_key\":\"null\","
"\"server\":\"http://test.com\""
"},"
"{"
"\"alternative_service\":"
"[\"h2 foo:443, expires 2018-01-24 15:13:53\","
"\"quic bar:443, expires 2018-01-24 16:12:53"
" (broken until 2018-01-24 15:17:53)\","
"\"quic baz:443, expires 2018-01-24 16:12:53"
" (broken until 2018-01-24 15:17:53)\"],"
"\"network_isolation_key\":\"null\","
"\"server\":\"https://youtube.com\""
"}"
"]";
std::unique_ptr<base::Value> alternative_service_info_value =
impl_.GetAlternativeServiceInfoAsValue();
std::string alternative_service_info_json;
base::JSONWriter::Write(*alternative_service_info_value,
&alternative_service_info_json);
EXPECT_EQ(expected_json, alternative_service_info_json);
}
TEST_F(HttpServerPropertiesTest, LoadLastLocalAddressWhenQuicWorked) {
const IPAddress kEmptyAddress;
const IPAddress kValidAddress1 = IPAddress::IPv4Localhost();
const IPAddress kValidAddress2 = IPAddress::IPv6Localhost();
// Check by initializing empty address.
impl_.OnLastLocalAddressWhenQuicWorkedForTesting(kEmptyAddress);
EXPECT_FALSE(impl_.HasLastLocalAddressWhenQuicWorked());
// Empty address should not be considered an address that was used when QUIC
// worked.
EXPECT_FALSE(impl_.WasLastLocalAddressWhenQuicWorked(kEmptyAddress));
EXPECT_FALSE(impl_.WasLastLocalAddressWhenQuicWorked(kValidAddress1));
EXPECT_FALSE(impl_.WasLastLocalAddressWhenQuicWorked(kValidAddress2));
// Check by initializing with a valid address.
impl_.OnLastLocalAddressWhenQuicWorkedForTesting(kValidAddress1);
EXPECT_TRUE(impl_.HasLastLocalAddressWhenQuicWorked());
EXPECT_FALSE(impl_.WasLastLocalAddressWhenQuicWorked(kEmptyAddress));
EXPECT_TRUE(impl_.WasLastLocalAddressWhenQuicWorked(kValidAddress1));
EXPECT_FALSE(impl_.WasLastLocalAddressWhenQuicWorked(kValidAddress2));
// Try another valid address.
impl_.OnLastLocalAddressWhenQuicWorkedForTesting(kValidAddress2);
EXPECT_TRUE(impl_.HasLastLocalAddressWhenQuicWorked());
EXPECT_FALSE(impl_.WasLastLocalAddressWhenQuicWorked(kEmptyAddress));
EXPECT_FALSE(impl_.WasLastLocalAddressWhenQuicWorked(kValidAddress1));
EXPECT_TRUE(impl_.WasLastLocalAddressWhenQuicWorked(kValidAddress2));
// And loading an empty address clears the current one.
// TODO(mmenke): This seems like a bug, since if we've learned the current
// network supports QUIC, surely we want to save that to disk? Seems like a
// pre-existing value should take precedence, if non-empty, since if the
// current network is already known to support QUIC, the loaded value is no
// longer relevant.
impl_.OnLastLocalAddressWhenQuicWorkedForTesting(kEmptyAddress);
EXPECT_FALSE(impl_.HasLastLocalAddressWhenQuicWorked());
EXPECT_FALSE(impl_.WasLastLocalAddressWhenQuicWorked(kEmptyAddress));
EXPECT_FALSE(impl_.WasLastLocalAddressWhenQuicWorked(kValidAddress1));
EXPECT_FALSE(impl_.WasLastLocalAddressWhenQuicWorked(kValidAddress2));
}
TEST_F(HttpServerPropertiesTest, SetLastLocalAddressWhenQuicWorked) {
const IPAddress kEmptyAddress;
const IPAddress kValidAddress1 = IPAddress::IPv4Localhost();
const IPAddress kValidAddress2 = IPAddress::IPv6Localhost();
EXPECT_FALSE(impl_.HasLastLocalAddressWhenQuicWorked());
EXPECT_FALSE(impl_.WasLastLocalAddressWhenQuicWorked(kEmptyAddress));
EXPECT_FALSE(impl_.WasLastLocalAddressWhenQuicWorked(kValidAddress1));
EXPECT_FALSE(impl_.WasLastLocalAddressWhenQuicWorked(kValidAddress2));
// Set to a valid address.
impl_.SetLastLocalAddressWhenQuicWorked(kValidAddress1);
EXPECT_TRUE(impl_.HasLastLocalAddressWhenQuicWorked());
EXPECT_FALSE(impl_.WasLastLocalAddressWhenQuicWorked(kEmptyAddress));
EXPECT_TRUE(impl_.WasLastLocalAddressWhenQuicWorked(kValidAddress1));
EXPECT_FALSE(impl_.WasLastLocalAddressWhenQuicWorked(kValidAddress2));
// Clear only this value.
impl_.ClearLastLocalAddressWhenQuicWorked();
EXPECT_FALSE(impl_.HasLastLocalAddressWhenQuicWorked());
EXPECT_FALSE(impl_.WasLastLocalAddressWhenQuicWorked(kEmptyAddress));
EXPECT_FALSE(impl_.WasLastLocalAddressWhenQuicWorked(kValidAddress1));
EXPECT_FALSE(impl_.WasLastLocalAddressWhenQuicWorked(kValidAddress2));
// Try another valid address.
impl_.SetLastLocalAddressWhenQuicWorked(kValidAddress2);
EXPECT_TRUE(impl_.HasLastLocalAddressWhenQuicWorked());
EXPECT_FALSE(impl_.WasLastLocalAddressWhenQuicWorked(kEmptyAddress));
EXPECT_FALSE(impl_.WasLastLocalAddressWhenQuicWorked(kValidAddress1));
EXPECT_TRUE(impl_.WasLastLocalAddressWhenQuicWorked(kValidAddress2));
// Clear all values.
impl_.Clear(base::OnceClosure());
EXPECT_FALSE(impl_.HasLastLocalAddressWhenQuicWorked());
EXPECT_FALSE(impl_.WasLastLocalAddressWhenQuicWorked(kEmptyAddress));
EXPECT_FALSE(impl_.WasLastLocalAddressWhenQuicWorked(kValidAddress1));
EXPECT_FALSE(impl_.WasLastLocalAddressWhenQuicWorked(kValidAddress2));
}
TEST_F(HttpServerPropertiesTest, LoadServerNetworkStats) {
url::SchemeHostPort google_server("https", "www.google.com", 443);
// Check by initializing empty ServerNetworkStats.
std::unique_ptr<HttpServerProperties::ServerInfoMap> load_server_info_map =
std::make_unique<HttpServerProperties::ServerInfoMap>();
impl_.OnServerInfoLoadedForTesting(std::move(load_server_info_map));
const ServerNetworkStats* stats =
impl_.GetServerNetworkStats(google_server, NetworkIsolationKey());
EXPECT_EQ(nullptr, stats);
// Check by initializing with www.google.com:443.
ServerNetworkStats stats_google;
stats_google.srtt = base::TimeDelta::FromMicroseconds(10);
stats_google.bandwidth_estimate = quic::QuicBandwidth::FromBitsPerSecond(100);
load_server_info_map =
std::make_unique<HttpServerProperties::ServerInfoMap>();
load_server_info_map->GetOrPut(CreateSimpleKey(google_server))
->second.server_network_stats = stats_google;
impl_.OnServerInfoLoadedForTesting(std::move(load_server_info_map));
// Verify data for www.google.com:443.
ASSERT_EQ(1u, impl_.server_info_map_for_testing().size());
EXPECT_EQ(stats_google, *(impl_.GetServerNetworkStats(
google_server, NetworkIsolationKey())));
// Test recency order and overwriting of data.
//
// |docs_server| has a ServerNetworkStats, which will be overwritten by
// OnServerInfoLoadedForTesting(), because |server_network_stats_map| has an
// entry for |docs_server|.
url::SchemeHostPort docs_server("https", "docs.google.com", 443);
ServerNetworkStats stats_docs;
stats_docs.srtt = base::TimeDelta::FromMicroseconds(20);
stats_docs.bandwidth_estimate = quic::QuicBandwidth::FromBitsPerSecond(200);
// Recency order will be |docs_server| and |google_server|.
impl_.SetServerNetworkStats(docs_server, NetworkIsolationKey(), stats_docs);
// Prepare |server_info_map| to be loaded by OnServerInfoLoadedForTesting().
std::unique_ptr<HttpServerProperties::ServerInfoMap> server_info_map =
std::make_unique<HttpServerProperties::ServerInfoMap>();
// Change the values for |docs_server|.
ServerNetworkStats new_stats_docs;
new_stats_docs.srtt = base::TimeDelta::FromMicroseconds(25);
new_stats_docs.bandwidth_estimate =
quic::QuicBandwidth::FromBitsPerSecond(250);
server_info_map->GetOrPut(CreateSimpleKey(docs_server))
->second.server_network_stats = new_stats_docs;
// Add data for mail.google.com:443.
url::SchemeHostPort mail_server("https", "mail.google.com", 443);
ServerNetworkStats stats_mail;
stats_mail.srtt = base::TimeDelta::FromMicroseconds(30);
stats_mail.bandwidth_estimate = quic::QuicBandwidth::FromBitsPerSecond(300);
server_info_map->GetOrPut(CreateSimpleKey(mail_server))
->second.server_network_stats = stats_mail;
// Recency order will be |docs_server|, |google_server| and |mail_server|.
impl_.OnServerInfoLoadedForTesting(std::move(server_info_map));
const HttpServerProperties::ServerInfoMap& map =
impl_.server_info_map_for_testing();
ASSERT_EQ(3u, map.size());
auto map_it = map.begin();
EXPECT_EQ(docs_server, map_it->first.server);
EXPECT_TRUE(map_it->first.network_isolation_key.IsEmpty());
ASSERT_TRUE(map_it->second.server_network_stats.has_value());
EXPECT_EQ(new_stats_docs, *map_it->second.server_network_stats);
++map_it;
EXPECT_EQ(google_server, map_it->first.server);
EXPECT_TRUE(map_it->first.network_isolation_key.IsEmpty());