// Copyright 2012 The Chromium Authors // 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 #include #include #include "base/check.h" #include "base/feature_list.h" #include "base/functional/bind.h" #include "base/functional/callback.h" #include "base/json/json_writer.h" #include "base/memory/raw_ptr.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/time/time.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/base/schemeful_site.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 net { const base::TimeDelta BROKEN_ALT_SVC_EXPIRE_DELAYS[10] = { base::Seconds(300), base::Seconds(600), base::Seconds(1200), base::Seconds(2400), base::Seconds(4800), base::Seconds(9600), base::Seconds(19200), base::Seconds(38400), base::Seconds(76800), base::Seconds(153600), }; class HttpServerPropertiesPeer { public: static void AddBrokenAlternativeServiceWithExpirationTime( HttpServerProperties* impl, const AlternativeService& alternative_service, base::TimeTicks when, const NetworkAnonymizationKey network_anonymization_key = NetworkAnonymizationKey()) { BrokenAlternativeService broken_alternative_service( alternative_service, network_anonymization_key, true /* use_network_anonymization_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 NetworkAnonymizationKey. HttpServerProperties::ServerInfoMapKey CreateSimpleKey( const url::SchemeHostPort& server) { return HttpServerProperties::ServerInfoMapKey( server, net::NetworkAnonymizationKey(), false /* use_network_anonymization_key */); } class HttpServerPropertiesTest : public TestWithTaskEnvironment { protected: HttpServerPropertiesTest() : TestWithTaskEnvironment( base::test::TaskEnvironment::TimeSource::MOCK_TIME), // Many tests assume partitioning is disabled by default. feature_list_(CreateFeatureListWithPartitioningDisabled()), 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::Seconds(12345)); SchemefulSite site1(GURL("https://foo.test/")); network_anonymization_key1_ = NetworkAnonymizationKey::CreateSameSite(site1); SchemefulSite site2(GURL("https://bar.test/")); network_anonymization_key2_ = NetworkAnonymizationKey::CreateSameSite(site2); } // This is a little awkward, but need to create and configure the // ScopedFeatureList before creating the HttpServerProperties. static std::unique_ptr CreateFeatureListWithPartitioningDisabled() { std::unique_ptr feature_list = std::make_unique(); feature_list->InitAndDisableFeature( features::kPartitionHttpServerPropertiesByNetworkIsolationKey); return feature_list; } bool HasAlternativeService( const url::SchemeHostPort& origin, const NetworkAnonymizationKey& network_anonymization_key) { const AlternativeServiceInfoVector alternative_service_info_vector = impl_.GetAlternativeServiceInfos(origin, network_anonymization_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::Days(1); if (alternative_service.protocol == kProtoQUIC) { impl_.SetQuicAlternativeService(origin, NetworkAnonymizationKey(), alternative_service, expiration, DefaultSupportedQuicVersions()); } else { impl_.SetHttp2AlternativeService(origin, NetworkAnonymizationKey(), alternative_service, expiration); } } void MarkBrokenAndLetExpireAlternativeServiceNTimes( const AlternativeService& alternative_service, int num_times) {} std::unique_ptr feature_list_; raw_ptr test_tick_clock_; base::SimpleTestClock test_clock_; // Two different non-empty network isolation keys for use in tests that need // them. NetworkAnonymizationKey network_anonymization_key1_; NetworkAnonymizationKey network_anonymization_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, NetworkAnonymizationKey(), true); impl_.SetSupportsSpdy(http_photo_server, NetworkAnonymizationKey(), true); impl_.SetSupportsSpdy(https_mail_server, NetworkAnonymizationKey(), false); EXPECT_TRUE( impl_.GetSupportsSpdy(https_www_server, NetworkAnonymizationKey())); EXPECT_TRUE(impl_.SupportsRequestPriority(https_www_server, NetworkAnonymizationKey())); EXPECT_TRUE( impl_.GetSupportsSpdy(http_photo_server, NetworkAnonymizationKey())); EXPECT_TRUE(impl_.SupportsRequestPriority(http_photo_server, NetworkAnonymizationKey())); EXPECT_FALSE( impl_.GetSupportsSpdy(https_mail_server, NetworkAnonymizationKey())); EXPECT_FALSE(impl_.SupportsRequestPriority(https_mail_server, NetworkAnonymizationKey())); EXPECT_FALSE( impl_.GetSupportsSpdy(http_google_server, NetworkAnonymizationKey())); EXPECT_FALSE(impl_.SupportsRequestPriority(http_google_server, NetworkAnonymizationKey())); EXPECT_FALSE( impl_.GetSupportsSpdy(https_photos_server, NetworkAnonymizationKey())); EXPECT_FALSE(impl_.SupportsRequestPriority(https_photos_server, NetworkAnonymizationKey())); EXPECT_TRUE( impl_.GetSupportsSpdy(valid_google_server, NetworkAnonymizationKey())); EXPECT_TRUE(impl_.SupportsRequestPriority(valid_google_server, NetworkAnonymizationKey())); // Flip values of two servers. impl_.SetSupportsSpdy(https_www_server, NetworkAnonymizationKey(), false); impl_.SetSupportsSpdy(https_mail_server, NetworkAnonymizationKey(), true); EXPECT_FALSE( impl_.GetSupportsSpdy(https_www_server, NetworkAnonymizationKey())); EXPECT_FALSE(impl_.SupportsRequestPriority(https_www_server, NetworkAnonymizationKey())); EXPECT_TRUE( impl_.GetSupportsSpdy(https_mail_server, NetworkAnonymizationKey())); EXPECT_TRUE(impl_.SupportsRequestPriority(https_mail_server, NetworkAnonymizationKey())); } 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, NetworkAnonymizationKey())); EXPECT_FALSE(impl_.GetSupportsSpdy(wss_server, NetworkAnonymizationKey())); EXPECT_FALSE(impl_.GetSupportsSpdy(http_server, NetworkAnonymizationKey())); EXPECT_FALSE(impl_.GetSupportsSpdy(ws_server, NetworkAnonymizationKey())); impl_.SetSupportsSpdy(wss_server, NetworkAnonymizationKey(), true); EXPECT_TRUE(impl_.GetSupportsSpdy(https_server, NetworkAnonymizationKey())); EXPECT_TRUE(impl_.GetSupportsSpdy(wss_server, NetworkAnonymizationKey())); EXPECT_FALSE(impl_.GetSupportsSpdy(http_server, NetworkAnonymizationKey())); EXPECT_FALSE(impl_.GetSupportsSpdy(ws_server, NetworkAnonymizationKey())); impl_.SetSupportsSpdy(http_server, NetworkAnonymizationKey(), true); EXPECT_TRUE(impl_.GetSupportsSpdy(https_server, NetworkAnonymizationKey())); EXPECT_TRUE(impl_.GetSupportsSpdy(wss_server, NetworkAnonymizationKey())); EXPECT_TRUE(impl_.GetSupportsSpdy(http_server, NetworkAnonymizationKey())); EXPECT_TRUE(impl_.GetSupportsSpdy(ws_server, NetworkAnonymizationKey())); impl_.SetSupportsSpdy(https_server, NetworkAnonymizationKey(), false); EXPECT_FALSE(impl_.GetSupportsSpdy(https_server, NetworkAnonymizationKey())); EXPECT_FALSE(impl_.GetSupportsSpdy(wss_server, NetworkAnonymizationKey())); EXPECT_TRUE(impl_.GetSupportsSpdy(http_server, NetworkAnonymizationKey())); EXPECT_TRUE(impl_.GetSupportsSpdy(ws_server, NetworkAnonymizationKey())); impl_.SetSupportsSpdy(ws_server, NetworkAnonymizationKey(), false); EXPECT_FALSE(impl_.GetSupportsSpdy(https_server, NetworkAnonymizationKey())); EXPECT_FALSE(impl_.GetSupportsSpdy(wss_server, NetworkAnonymizationKey())); EXPECT_FALSE(impl_.GetSupportsSpdy(http_server, NetworkAnonymizationKey())); EXPECT_FALSE(impl_.GetSupportsSpdy(ws_server, NetworkAnonymizationKey())); } TEST_F(HttpServerPropertiesTest, SetSupportsSpdyWithNetworkIsolationKey) { const url::SchemeHostPort kServer("https", "foo.test", 443); EXPECT_FALSE(impl_.GetSupportsSpdy(kServer, network_anonymization_key1_)); EXPECT_FALSE( impl_.SupportsRequestPriority(kServer, network_anonymization_key1_)); EXPECT_FALSE(impl_.GetSupportsSpdy(kServer, NetworkAnonymizationKey())); EXPECT_FALSE( impl_.SupportsRequestPriority(kServer, NetworkAnonymizationKey())); // Without network isolation keys enabled for HttpServerProperties, passing in // a NetworkAnonymizationKey should have no effect on behavior. for (const auto& network_anonymization_key_to_set : {NetworkAnonymizationKey(), network_anonymization_key1_}) { impl_.SetSupportsSpdy(kServer, network_anonymization_key_to_set, true); EXPECT_TRUE(impl_.GetSupportsSpdy(kServer, network_anonymization_key1_)); EXPECT_TRUE( impl_.SupportsRequestPriority(kServer, network_anonymization_key1_)); EXPECT_TRUE(impl_.GetSupportsSpdy(kServer, NetworkAnonymizationKey())); EXPECT_TRUE( impl_.SupportsRequestPriority(kServer, NetworkAnonymizationKey())); impl_.SetSupportsSpdy(kServer, network_anonymization_key_to_set, false); EXPECT_FALSE(impl_.GetSupportsSpdy(kServer, network_anonymization_key1_)); EXPECT_FALSE( impl_.SupportsRequestPriority(kServer, network_anonymization_key1_)); EXPECT_FALSE(impl_.GetSupportsSpdy(kServer, NetworkAnonymizationKey())); EXPECT_FALSE( impl_.SupportsRequestPriority(kServer, NetworkAnonymizationKey())); } // With network isolation keys enabled for HttpServerProperties, the // NetworkAnonymizationKey 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_anonymization_key1_)); EXPECT_FALSE( properties.SupportsRequestPriority(kServer, network_anonymization_key1_)); EXPECT_FALSE(properties.GetSupportsSpdy(kServer, NetworkAnonymizationKey())); EXPECT_FALSE( properties.SupportsRequestPriority(kServer, NetworkAnonymizationKey())); properties.SetSupportsSpdy(kServer, network_anonymization_key1_, true); EXPECT_TRUE(properties.GetSupportsSpdy(kServer, network_anonymization_key1_)); EXPECT_TRUE( properties.SupportsRequestPriority(kServer, network_anonymization_key1_)); EXPECT_FALSE(properties.GetSupportsSpdy(kServer, NetworkAnonymizationKey())); EXPECT_FALSE( properties.SupportsRequestPriority(kServer, NetworkAnonymizationKey())); properties.SetSupportsSpdy(kServer, NetworkAnonymizationKey(), true); EXPECT_TRUE(properties.GetSupportsSpdy(kServer, network_anonymization_key1_)); EXPECT_TRUE( properties.SupportsRequestPriority(kServer, network_anonymization_key1_)); EXPECT_TRUE(properties.GetSupportsSpdy(kServer, NetworkAnonymizationKey())); EXPECT_TRUE( properties.SupportsRequestPriority(kServer, NetworkAnonymizationKey())); properties.SetSupportsSpdy(kServer, network_anonymization_key1_, false); EXPECT_FALSE( properties.GetSupportsSpdy(kServer, network_anonymization_key1_)); EXPECT_FALSE( properties.SupportsRequestPriority(kServer, network_anonymization_key1_)); EXPECT_TRUE(properties.GetSupportsSpdy(kServer, NetworkAnonymizationKey())); EXPECT_TRUE( properties.SupportsRequestPriority(kServer, NetworkAnonymizationKey())); properties.SetSupportsSpdy(kServer, NetworkAnonymizationKey(), false); EXPECT_FALSE( properties.GetSupportsSpdy(kServer, network_anonymization_key1_)); EXPECT_FALSE( properties.SupportsRequestPriority(kServer, network_anonymization_key1_)); EXPECT_FALSE(properties.GetSupportsSpdy(kServer, NetworkAnonymizationKey())); EXPECT_FALSE( properties.SupportsRequestPriority(kServer, NetworkAnonymizationKey())); } 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 spdy_servers = std::make_unique(); impl_.OnServerInfoLoadedForTesting(std::move(spdy_servers)); EXPECT_FALSE( impl_.GetSupportsSpdy(spdy_server_google, NetworkAnonymizationKey())); // Check by initializing www.google.com:443 and photos.google.com:443 as spdy // servers. std::unique_ptr spdy_servers1 = std::make_unique(); 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, NetworkAnonymizationKey())); EXPECT_FALSE( impl_.GetSupportsSpdy(spdy_server_photos, NetworkAnonymizationKey())); // 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_anonymization_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_anonymization_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 spdy_servers2 = std::make_unique(); 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_anonymization_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_anonymization_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_anonymization_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_anonymization_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, NetworkAnonymizationKey())); EXPECT_TRUE( impl_.GetSupportsSpdy(spdy_server_docs, NetworkAnonymizationKey())); EXPECT_TRUE( impl_.GetSupportsSpdy(spdy_server_google, NetworkAnonymizationKey())); EXPECT_FALSE( impl_.GetSupportsSpdy(spdy_server_photos, NetworkAnonymizationKey())); // 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 spdy_servers3 = std::make_unique(); 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_anonymization_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_anonymization_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_anonymization_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_anonymization_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, NetworkAnonymizationKey())); EXPECT_TRUE( impl_.GetSupportsSpdy(spdy_server_docs, NetworkAnonymizationKey())); EXPECT_TRUE( impl_.GetSupportsSpdy(spdy_server_google, NetworkAnonymizationKey())); EXPECT_TRUE( impl_.GetSupportsSpdy(spdy_server_photos, NetworkAnonymizationKey())); } TEST_F(HttpServerPropertiesTest, SupportsRequestPriority) { url::SchemeHostPort spdy_server_empty("https", std::string(), 443); EXPECT_FALSE(impl_.SupportsRequestPriority(spdy_server_empty, NetworkAnonymizationKey())); // Add www.google.com:443 as supporting SPDY. url::SchemeHostPort spdy_server_google("https", "www.google.com", 443); impl_.SetSupportsSpdy(spdy_server_google, NetworkAnonymizationKey(), true); EXPECT_TRUE(impl_.SupportsRequestPriority(spdy_server_google, NetworkAnonymizationKey())); // 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, NetworkAnonymizationKey())); // Add docs.google.com:443 as supporting SPDY. url::SchemeHostPort spdy_server_docs("https", "docs.google.com", 443); impl_.SetSupportsSpdy(spdy_server_docs, NetworkAnonymizationKey(), true); EXPECT_TRUE(impl_.SupportsRequestPriority(spdy_server_docs, NetworkAnonymizationKey())); // 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, NetworkAnonymizationKey())); // 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, NetworkAnonymizationKey())); // Verify all the entries are the same after additions. EXPECT_TRUE(impl_.SupportsRequestPriority(spdy_server_google, NetworkAnonymizationKey())); EXPECT_FALSE(impl_.SupportsRequestPriority(spdy_server_mail, NetworkAnonymizationKey())); EXPECT_TRUE(impl_.SupportsRequestPriority(spdy_server_docs, NetworkAnonymizationKey())); EXPECT_TRUE( impl_.SupportsRequestPriority(youtube_server, NetworkAnonymizationKey())); EXPECT_TRUE( impl_.SupportsRequestPriority(example_server, NetworkAnonymizationKey())); } 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, NetworkAnonymizationKey(), true); url::SchemeHostPort spdy_server_mail("https", "mail.google.com", 443); impl_.SetSupportsSpdy(spdy_server_mail, NetworkAnonymizationKey(), true); EXPECT_TRUE( impl_.GetSupportsSpdy(spdy_server_google, NetworkAnonymizationKey())); EXPECT_TRUE( impl_.GetSupportsSpdy(spdy_server_mail, NetworkAnonymizationKey())); 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, NetworkAnonymizationKey())); EXPECT_FALSE( impl_.GetSupportsSpdy(spdy_server_mail, NetworkAnonymizationKey())); // 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, NetworkAnonymizationKey(), 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_anonymization_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, NetworkAnonymizationKey(), 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_anonymization_key.IsEmpty()); ++it; ASSERT_EQ(spdy_server_google, it->first.server); EXPECT_TRUE(it->first.network_anonymization_key.IsEmpty()); // Get www.google.com:443. It should become the most-recently-used server. EXPECT_TRUE( impl_.GetSupportsSpdy(spdy_server_google, NetworkAnonymizationKey())); 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_anonymization_key.IsEmpty()); ++it; ASSERT_EQ(spdy_server_mail, it->first.server); EXPECT_TRUE(it->first.network_anonymization_key.IsEmpty()); } typedef HttpServerPropertiesTest AlternateProtocolServerPropertiesTest; TEST_F(AlternateProtocolServerPropertiesTest, Basic) { url::SchemeHostPort test_server("http", "foo", 80); EXPECT_FALSE(HasAlternativeService(test_server, NetworkAnonymizationKey())); AlternativeService alternative_service(kProtoHTTP2, "foo", 443); SetAlternativeService(test_server, alternative_service); const AlternativeServiceInfoVector alternative_service_info_vector = impl_.GetAlternativeServiceInfos(test_server, NetworkAnonymizationKey()); 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, NetworkAnonymizationKey())); } TEST_F(AlternateProtocolServerPropertiesTest, ExcludeOrigin) { AlternativeServiceInfoVector alternative_service_info_vector; base::Time expiration = test_clock_.Now() + base::Days(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, DefaultSupportedQuicVersions()); alternative_service_info_vector.push_back(alternative_service_info4); url::SchemeHostPort test_server("https", "foo", 443); impl_.SetAlternativeServices(test_server, NetworkAnonymizationKey(), alternative_service_info_vector); const AlternativeServiceInfoVector alternative_service_info_vector2 = impl_.GetAlternativeServiceInfos(test_server, NetworkAnonymizationKey()); 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::Days(1); // 1st entry in the memory. impl_.SetHttp2AlternativeService(test_server1, NetworkAnonymizationKey(), 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::Days(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, NetworkAnonymizationKey(), alternative_service_info_vector); // Prepare |server_info_map| to be loaded by OnServerInfoLoadedForTesting(). std::unique_ptr server_info_map = std::make_unique(); const AlternativeService alternative_service3(kProtoHTTP2, "bar3", 123); base::Time expiration3 = now + base::Days(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::Days(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_anonymization_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_anonymization_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_anonymization_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, NetworkAnonymizationKey()) .size()); EXPECT_EQ( 0u, impl_.GetAlternativeServiceInfos(wss_server, NetworkAnonymizationKey()) .size()); EXPECT_EQ( 0u, impl_.GetAlternativeServiceInfos(http_server, NetworkAnonymizationKey()) .size()); EXPECT_EQ( 0u, impl_.GetAlternativeServiceInfos(ws_server, NetworkAnonymizationKey()) .size()); SetAlternativeService(wss_server, alternative_service); EXPECT_EQ( 1u, impl_.GetAlternativeServiceInfos(https_server, NetworkAnonymizationKey()) .size()); EXPECT_EQ( 1u, impl_.GetAlternativeServiceInfos(wss_server, NetworkAnonymizationKey()) .size()); EXPECT_EQ( 0u, impl_.GetAlternativeServiceInfos(http_server, NetworkAnonymizationKey()) .size()); EXPECT_EQ( 0u, impl_.GetAlternativeServiceInfos(ws_server, NetworkAnonymizationKey()) .size()); SetAlternativeService(http_server, alternative_service); EXPECT_EQ( 1u, impl_.GetAlternativeServiceInfos(https_server, NetworkAnonymizationKey()) .size()); EXPECT_EQ( 1u, impl_.GetAlternativeServiceInfos(wss_server, NetworkAnonymizationKey()) .size()); EXPECT_EQ( 1u, impl_.GetAlternativeServiceInfos(http_server, NetworkAnonymizationKey()) .size()); EXPECT_EQ( 1u, impl_.GetAlternativeServiceInfos(ws_server, NetworkAnonymizationKey()) .size()); impl_.SetAlternativeServices(https_server, NetworkAnonymizationKey(), AlternativeServiceInfoVector()); EXPECT_EQ( 0u, impl_.GetAlternativeServiceInfos(https_server, NetworkAnonymizationKey()) .size()); EXPECT_EQ( 0u, impl_.GetAlternativeServiceInfos(wss_server, NetworkAnonymizationKey()) .size()); EXPECT_EQ( 1u, impl_.GetAlternativeServiceInfos(http_server, NetworkAnonymizationKey()) .size()); EXPECT_EQ( 1u, impl_.GetAlternativeServiceInfos(ws_server, NetworkAnonymizationKey()) .size()); impl_.SetAlternativeServices(ws_server, NetworkAnonymizationKey(), AlternativeServiceInfoVector()); EXPECT_EQ( 0u, impl_.GetAlternativeServiceInfos(https_server, NetworkAnonymizationKey()) .size()); EXPECT_EQ( 0u, impl_.GetAlternativeServiceInfos(wss_server, NetworkAnonymizationKey()) .size()); EXPECT_EQ( 0u, impl_.GetAlternativeServiceInfos(http_server, NetworkAnonymizationKey()) .size()); EXPECT_EQ( 0u, impl_.GetAlternativeServiceInfos(ws_server, NetworkAnonymizationKey()) .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::Days(1) /* expiration */)}); EXPECT_TRUE( impl_.GetAlternativeServiceInfos(kServer, network_anonymization_key1_) .empty()); EXPECT_TRUE( impl_.GetAlternativeServiceInfos(kServer, NetworkAnonymizationKey()) .empty()); // Without network isolation keys enabled for HttpServerProperties, passing in // a NetworkAnonymizationKey should have no effect on behavior. for (const auto& network_anonymization_key_to_set : {NetworkAnonymizationKey(), network_anonymization_key1_}) { impl_.SetAlternativeServices(kServer, network_anonymization_key_to_set, kAlternativeServices); EXPECT_EQ(kAlternativeServices, impl_.GetAlternativeServiceInfos( kServer, network_anonymization_key1_)); EXPECT_EQ(kAlternativeServices, impl_.GetAlternativeServiceInfos( kServer, NetworkAnonymizationKey())); impl_.SetAlternativeServices(kServer, network_anonymization_key_to_set, AlternativeServiceInfoVector()); EXPECT_TRUE( impl_.GetAlternativeServiceInfos(kServer, network_anonymization_key1_) .empty()); EXPECT_TRUE( impl_.GetAlternativeServiceInfos(kServer, NetworkAnonymizationKey()) .empty()); } // Check that with network isolation keys enabled for HttpServerProperties, // the NetworkAnonymizationKey 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_anonymization_key1_, kAlternativeServices); EXPECT_EQ(kAlternativeServices, properties.GetAlternativeServiceInfos( kServer, network_anonymization_key1_)); EXPECT_TRUE( properties.GetAlternativeServiceInfos(kServer, NetworkAnonymizationKey()) .empty()); properties.SetAlternativeServices(kServer, NetworkAnonymizationKey(), kAlternativeServices); EXPECT_EQ(kAlternativeServices, properties.GetAlternativeServiceInfos( kServer, network_anonymization_key1_)); EXPECT_EQ(kAlternativeServices, properties.GetAlternativeServiceInfos( kServer, NetworkAnonymizationKey())); properties.SetAlternativeServices(kServer, network_anonymization_key1_, AlternativeServiceInfoVector()); EXPECT_TRUE( properties .GetAlternativeServiceInfos(kServer, network_anonymization_key1_) .empty()); EXPECT_EQ(kAlternativeServices, properties.GetAlternativeServiceInfos( kServer, NetworkAnonymizationKey())); properties.SetAlternativeServices(kServer, NetworkAnonymizationKey(), AlternativeServiceInfoVector()); EXPECT_TRUE( properties .GetAlternativeServiceInfos(kServer, network_anonymization_key1_) .empty()); EXPECT_TRUE( properties.GetAlternativeServiceInfos(kServer, NetworkAnonymizationKey()) .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, NetworkAnonymizationKey()); std::unique_ptr server_info_map = std::make_unique(); impl_.OnServerInfoLoadedForTesting(std::move(server_info_map)); EXPECT_TRUE(impl_.IsAlternativeServiceBroken( alternative_service_with_foo_hostname, NetworkAnonymizationKey())); const AlternativeServiceInfoVector alternative_service_info_vector = impl_.GetAlternativeServiceInfos(server, NetworkAnonymizationKey()); 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, DISABLED_EmptyVector) { url::SchemeHostPort server("https", "foo", 443); const AlternativeService alternative_service(kProtoHTTP2, "bar", 443); base::Time expiration = test_clock_.Now() - base::Days(1); const AlternativeServiceInfo alternative_service_info = AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo( alternative_service, expiration); std::unique_ptr server_info_map = std::make_unique(); 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, NetworkAnonymizationKey()) .empty()); // GetAlternativeServiceInfos() should remove this key from // |server_info_map_|, and SetAlternativeServices() should not crash. impl_.SetAlternativeServices( server, NetworkAnonymizationKey(), AlternativeServiceInfoVector(/*size=*/1, alternative_service_info)); // There should still be no alternative service assigned to |server|. ASSERT_TRUE( impl_.GetAlternativeServiceInfos(server, NetworkAnonymizationKey()) .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::Days(1); const AlternativeServiceInfo alternative_service_info = AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo( alternative_service, expiration); std::unique_ptr server_info_map = std::make_unique(); 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, NetworkAnonymizationKey()) .empty()); // GetAlternativeServiceInfos() should remove this key from // |server_info_map_|, and SetAlternativeServices() should not crash. impl_.SetAlternativeServices( canonical_server, NetworkAnonymizationKey(), AlternativeServiceInfoVector(/*size=*/1, alternative_service_info)); // There should still be no alternative service assigned to // |canonical_server|. ASSERT_TRUE(impl_ .GetAlternativeServiceInfos(canonical_server, NetworkAnonymizationKey()) .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::Days(1); const AlternativeServiceInfo alternative_service_info = AlternativeServiceInfo::CreateQuicAlternativeServiceInfo( alternative_service, expiration, DefaultSupportedQuicVersions()); impl_.SetAlternativeServices( canonical_server, NetworkAnonymizationKey(), 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, NetworkAnonymizationKey()); 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, NetworkAnonymizationKey(), AlternativeServiceInfoVector()); ASSERT_TRUE( impl_.GetAlternativeServiceInfos(server, NetworkAnonymizationKey()) .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_anonymization_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, NetworkAnonymizationKey()); 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_anonymization_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, NetworkAnonymizationKey()); 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, NetworkAnonymizationKey())); // GetAlternativeServiceInfos should return the broken alternative service. impl_.MarkAlternativeServiceBroken(alternative_service1, NetworkAnonymizationKey()); alternative_service_info_vector = impl_.GetAlternativeServiceInfos(test_server, NetworkAnonymizationKey()); 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, NetworkAnonymizationKey())); // SetAlternativeServices should add a broken alternative service to the map. AlternativeServiceInfoVector alternative_service_info_vector2; base::Time expiration = test_clock_.Now() + base::Days(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, NetworkAnonymizationKey(), alternative_service_info_vector2); alternative_service_info_vector = impl_.GetAlternativeServiceInfos(test_server, NetworkAnonymizationKey()); 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, NetworkAnonymizationKey())); EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service2, NetworkAnonymizationKey())); // SetAlternativeService should add a broken alternative service to the map. SetAlternativeService(test_server, alternative_service1); alternative_service_info_vector = impl_.GetAlternativeServiceInfos(test_server, NetworkAnonymizationKey()); 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, NetworkAnonymizationKey())); } 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, NetworkAnonymizationKey()); 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, NetworkAnonymizationKey())); // Mark the alternative service as broken until the default network changes. impl_.MarkAlternativeServiceBrokenUntilDefaultNetworkChanges( alternative_service1, NetworkAnonymizationKey()); // The alternative service should be persisted and marked as broken. alternative_service_info_vector = impl_.GetAlternativeServiceInfos(test_server, NetworkAnonymizationKey()); 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, NetworkAnonymizationKey())); // SetAlternativeServices should add a broken alternative service to the map. AlternativeServiceInfoVector alternative_service_info_vector2; base::Time expiration = test_clock_.Now() + base::Days(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, NetworkAnonymizationKey(), alternative_service_info_vector2); alternative_service_info_vector = impl_.GetAlternativeServiceInfos(test_server, NetworkAnonymizationKey()); 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, NetworkAnonymizationKey())); EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service2, NetworkAnonymizationKey())); // SetAlternativeService should add a broken alternative service to the map. SetAlternativeService(test_server, alternative_service1); alternative_service_info_vector = impl_.GetAlternativeServiceInfos(test_server, NetworkAnonymizationKey()); 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, NetworkAnonymizationKey())); } TEST_F(AlternateProtocolServerPropertiesTest, MaxAge) { AlternativeServiceInfoVector alternative_service_info_vector; base::Time now = test_clock_.Now(); base::TimeDelta one_day = base::Days(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, NetworkAnonymizationKey(), alternative_service_info_vector); AlternativeServiceInfoVector alternative_service_info_vector2 = impl_.GetAlternativeServiceInfos(test_server, NetworkAnonymizationKey()); 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::Days(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, NetworkAnonymizationKey(), alternative_service_info_vector); url::SchemeHostPort test_server("https", "foo.c.youtube.com", 443); AlternativeServiceInfoVector alternative_service_info_vector2 = impl_.GetAlternativeServiceInfos(test_server, NetworkAnonymizationKey()); 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::Days(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, NetworkAnonymizationKey(), 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_anonymization_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, NetworkAnonymizationKey()) .size()); // Set Alt-Svc list for |https_server|. impl_.SetAlternativeServices(https_server, NetworkAnonymizationKey(), alternative_service_info_vector); EXPECT_EQ( 2u, impl_.GetAlternativeServiceInfos(https_server, NetworkAnonymizationKey()) .size()); EXPECT_EQ( 2u, impl_.GetAlternativeServiceInfos(http_server, NetworkAnonymizationKey()) .size()); // Clear Alt-Svc list for |http_server|. impl_.SetAlternativeServices(http_server, NetworkAnonymizationKey(), AlternativeServiceInfoVector()); EXPECT_EQ( 0u, impl_.GetAlternativeServiceInfos(http_server, NetworkAnonymizationKey()) .size()); EXPECT_EQ( 2u, impl_.GetAlternativeServiceInfos(https_server, NetworkAnonymizationKey()) .size()); } TEST_F(AlternateProtocolServerPropertiesTest, ClearAlternativeServices) { AlternativeServiceInfoVector alternative_service_info_vector; const AlternativeService alternative_service1(kProtoHTTP2, "foo", 443); base::Time expiration = test_clock_.Now() + base::Days(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, NetworkAnonymizationKey(), 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_anonymization_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, NetworkAnonymizationKey(), 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, NetworkAnonymizationKey()); 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, NetworkAnonymizationKey()); EXPECT_TRUE(impl_.IsAlternativeServiceBroken(broken_alternative_service, NetworkAnonymizationKey())); SetAlternativeService(test_server, broken_alternative_service); alternative_service_info_vector = impl_.GetAlternativeServiceInfos(test_server, NetworkAnonymizationKey()); 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, NetworkAnonymizationKey())); } 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, NetworkAnonymizationKey()); ASSERT_TRUE(HasAlternativeService(test_server, NetworkAnonymizationKey())); EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service, NetworkAnonymizationKey())); // SetAlternativeServices should leave a broken alternative service marked // as such. impl_.SetAlternativeServices(test_server, NetworkAnonymizationKey(), AlternativeServiceInfoVector()); EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service, NetworkAnonymizationKey())); } 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::Days(1); // Without NetworkIsolationKeys enabled, the NetworkAnonymizationKey parameter // should be ignored. impl_.SetHttp2AlternativeService(server, network_anonymization_key1_, alternative_service, expiration); EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service, network_anonymization_key1_)); EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key1_)); EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service, network_anonymization_key2_)); EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key2_)); impl_.MarkAlternativeServiceBroken(alternative_service, network_anonymization_key1_); EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service, network_anonymization_key1_)); EXPECT_TRUE(impl_.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key1_)); EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service, network_anonymization_key2_)); EXPECT_TRUE(impl_.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key2_)); impl_.ConfirmAlternativeService(alternative_service, network_anonymization_key2_); EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service, network_anonymization_key1_)); EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key1_)); EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service, network_anonymization_key2_)); EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_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_anonymization_key1_, alternative_service, expiration); properties.SetHttp2AlternativeService(server, network_anonymization_key2_, alternative_service, expiration); EXPECT_FALSE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key1_)); EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key1_)); EXPECT_FALSE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key2_)); EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key2_)); properties.MarkAlternativeServiceBroken(alternative_service, network_anonymization_key1_); EXPECT_TRUE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key1_)); EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key1_)); EXPECT_FALSE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key2_)); EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key2_)); properties.MarkAlternativeServiceBroken(alternative_service, network_anonymization_key2_); EXPECT_TRUE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key1_)); EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key1_)); EXPECT_TRUE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key2_)); EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key2_)); properties.ConfirmAlternativeService(alternative_service, network_anonymization_key1_); EXPECT_FALSE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key1_)); EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key1_)); EXPECT_TRUE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key2_)); EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key2_)); properties.ConfirmAlternativeService(alternative_service, network_anonymization_key2_); EXPECT_FALSE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key1_)); EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key1_)); EXPECT_FALSE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key2_)); EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_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, NetworkAnonymizationKey())); EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken( alternative_service, NetworkAnonymizationKey())); impl_.MarkAlternativeServiceRecentlyBroken(alternative_service, NetworkAnonymizationKey()); EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service, NetworkAnonymizationKey())); EXPECT_TRUE(impl_.WasAlternativeServiceRecentlyBroken( alternative_service, NetworkAnonymizationKey())); impl_.ConfirmAlternativeService(alternative_service, NetworkAnonymizationKey()); EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service, NetworkAnonymizationKey())); EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken( alternative_service, NetworkAnonymizationKey())); } 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::Days(1); // Without NetworkIsolationKeys enabled, the NetworkAnonymizationKey parameter // should be ignored. impl_.SetHttp2AlternativeService(server, network_anonymization_key1_, alternative_service, expiration); EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service, network_anonymization_key1_)); EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key1_)); EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service, network_anonymization_key2_)); EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key2_)); impl_.MarkAlternativeServiceRecentlyBroken(alternative_service, network_anonymization_key1_); EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service, network_anonymization_key1_)); EXPECT_TRUE(impl_.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key1_)); EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service, network_anonymization_key2_)); EXPECT_TRUE(impl_.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key2_)); impl_.ConfirmAlternativeService(alternative_service, network_anonymization_key2_); EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service, network_anonymization_key1_)); EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key1_)); EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service, network_anonymization_key2_)); EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_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_anonymization_key1_, alternative_service, expiration); properties.SetHttp2AlternativeService(server, network_anonymization_key2_, alternative_service, expiration); EXPECT_FALSE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key1_)); EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key1_)); EXPECT_FALSE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key2_)); EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key2_)); properties.MarkAlternativeServiceRecentlyBroken(alternative_service, network_anonymization_key1_); EXPECT_FALSE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key1_)); EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key1_)); EXPECT_FALSE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key2_)); EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key2_)); properties.MarkAlternativeServiceRecentlyBroken(alternative_service, network_anonymization_key2_); EXPECT_FALSE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key1_)); EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key1_)); EXPECT_FALSE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key2_)); EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key2_)); properties.ConfirmAlternativeService(alternative_service, network_anonymization_key1_); EXPECT_FALSE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key1_)); EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key1_)); EXPECT_FALSE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key2_)); EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key2_)); properties.ConfirmAlternativeService(alternative_service, network_anonymization_key2_); EXPECT_FALSE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key1_)); EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key1_)); EXPECT_FALSE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key2_)); EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_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, NetworkAnonymizationKey())); EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken( alternative_service, NetworkAnonymizationKey())); impl_.MarkAlternativeServiceBrokenUntilDefaultNetworkChanges( alternative_service, NetworkAnonymizationKey()); EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service, NetworkAnonymizationKey())); EXPECT_TRUE(impl_.WasAlternativeServiceRecentlyBroken( alternative_service, NetworkAnonymizationKey())); impl_.ConfirmAlternativeService(alternative_service, NetworkAnonymizationKey()); EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service, NetworkAnonymizationKey())); EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken( alternative_service, NetworkAnonymizationKey())); } 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::Days(1); // Without NetworkIsolationKeys enabled, the NetworkAnonymizationKey parameter // should be ignored. impl_.SetHttp2AlternativeService(server, network_anonymization_key1_, alternative_service, expiration); EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service, network_anonymization_key1_)); EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key1_)); EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service, network_anonymization_key2_)); EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key2_)); impl_.MarkAlternativeServiceBrokenUntilDefaultNetworkChanges( alternative_service, network_anonymization_key1_); EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service, network_anonymization_key1_)); EXPECT_TRUE(impl_.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key1_)); EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service, network_anonymization_key2_)); EXPECT_TRUE(impl_.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key2_)); impl_.ConfirmAlternativeService(alternative_service, network_anonymization_key2_); EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service, network_anonymization_key1_)); EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key1_)); EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service, network_anonymization_key2_)); EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_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_anonymization_key1_, alternative_service, expiration); properties.SetHttp2AlternativeService(server, network_anonymization_key2_, alternative_service, expiration); EXPECT_FALSE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key1_)); EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key1_)); EXPECT_FALSE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key2_)); EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key2_)); properties.MarkAlternativeServiceBrokenUntilDefaultNetworkChanges( alternative_service, network_anonymization_key1_); EXPECT_TRUE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key1_)); EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key1_)); EXPECT_FALSE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key2_)); EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key2_)); properties.MarkAlternativeServiceBrokenUntilDefaultNetworkChanges( alternative_service, network_anonymization_key2_); EXPECT_TRUE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key1_)); EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key1_)); EXPECT_TRUE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key2_)); EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key2_)); properties.ConfirmAlternativeService(alternative_service, network_anonymization_key1_); EXPECT_FALSE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key1_)); EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key1_)); EXPECT_TRUE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key2_)); EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key2_)); properties.ConfirmAlternativeService(alternative_service, network_anonymization_key2_); EXPECT_FALSE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key1_)); EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key1_)); EXPECT_FALSE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key2_)); EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_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, NetworkAnonymizationKey())); EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken( alternative_service, NetworkAnonymizationKey())); impl_.MarkAlternativeServiceBrokenUntilDefaultNetworkChanges( alternative_service, NetworkAnonymizationKey()); EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service, NetworkAnonymizationKey())); EXPECT_TRUE(impl_.WasAlternativeServiceRecentlyBroken( alternative_service, NetworkAnonymizationKey())); // Default network change clears alt svc broken until default network changes. impl_.OnDefaultNetworkChanged(); EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service, NetworkAnonymizationKey())); EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken( alternative_service, NetworkAnonymizationKey())); impl_.MarkAlternativeServiceBrokenUntilDefaultNetworkChanges( alternative_service, NetworkAnonymizationKey()); EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service, NetworkAnonymizationKey())); EXPECT_TRUE(impl_.WasAlternativeServiceRecentlyBroken( alternative_service, NetworkAnonymizationKey())); impl_.MarkAlternativeServiceBroken(alternative_service, NetworkAnonymizationKey()); EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service, NetworkAnonymizationKey())); EXPECT_TRUE(impl_.WasAlternativeServiceRecentlyBroken( alternative_service, NetworkAnonymizationKey())); // Default network change doesn't affect alt svc that was simply marked broken // most recently. impl_.OnDefaultNetworkChanged(); EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service, NetworkAnonymizationKey())); EXPECT_TRUE(impl_.WasAlternativeServiceRecentlyBroken( alternative_service, NetworkAnonymizationKey())); impl_.MarkAlternativeServiceBrokenUntilDefaultNetworkChanges( alternative_service, NetworkAnonymizationKey()); EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service, NetworkAnonymizationKey())); EXPECT_TRUE(impl_.WasAlternativeServiceRecentlyBroken( alternative_service, NetworkAnonymizationKey())); // 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, NetworkAnonymizationKey())); EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken( alternative_service, NetworkAnonymizationKey())); } 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::Days(1); properties.SetHttp2AlternativeService(server, network_anonymization_key1_, alternative_service, expiration); properties.SetHttp2AlternativeService(server, network_anonymization_key2_, alternative_service, expiration); EXPECT_FALSE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key1_)); EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key1_)); EXPECT_FALSE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key2_)); EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key2_)); properties.MarkAlternativeServiceBrokenUntilDefaultNetworkChanges( alternative_service, network_anonymization_key1_); properties.MarkAlternativeServiceBrokenUntilDefaultNetworkChanges( alternative_service, network_anonymization_key2_); EXPECT_TRUE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key1_)); EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key1_)); EXPECT_TRUE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key2_)); EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key2_)); // Default network change clears alt svc broken until default network changes. properties.OnDefaultNetworkChanged(); EXPECT_FALSE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key1_)); EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key1_)); EXPECT_FALSE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key2_)); EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key2_)); } TEST_F(AlternateProtocolServerPropertiesTest, Canonical) { url::SchemeHostPort test_server("https", "foo.c.youtube.com", 443); EXPECT_FALSE(HasAlternativeService(test_server, NetworkAnonymizationKey())); url::SchemeHostPort canonical_server("https", "bar.c.youtube.com", 443); EXPECT_FALSE( HasAlternativeService(canonical_server, NetworkAnonymizationKey())); AlternativeServiceInfoVector alternative_service_info_vector; const AlternativeService canonical_alternative_service1( kProtoQUIC, "bar.c.youtube.com", 1234); base::Time expiration = test_clock_.Now() + base::Days(1); alternative_service_info_vector.push_back( AlternativeServiceInfo::CreateQuicAlternativeServiceInfo( canonical_alternative_service1, expiration, DefaultSupportedQuicVersions())); const AlternativeService canonical_alternative_service2(kProtoHTTP2, "", 443); alternative_service_info_vector.push_back( AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo( canonical_alternative_service2, expiration)); impl_.SetAlternativeServices(canonical_server, NetworkAnonymizationKey(), 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, NetworkAnonymizationKey()); 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, NetworkAnonymizationKey(), AlternativeServiceInfoVector()); EXPECT_FALSE(HasAlternativeService(test_server, NetworkAnonymizationKey())); } 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_anonymization_key1_)); url::SchemeHostPort canonical_server1("https", "bar.c.youtube.com", 443); EXPECT_FALSE( HasAlternativeService(canonical_server1, network_anonymization_key1_)); AlternativeServiceInfoVector alternative_service_info_vector; const AlternativeService canonical_alternative_service1( kProtoQUIC, "bar.c.youtube.com", 1234); base::Time expiration = test_clock_.Now() + base::Days(1); alternative_service_info_vector.push_back( AlternativeServiceInfo::CreateQuicAlternativeServiceInfo( canonical_alternative_service1, expiration, DefaultSupportedQuicVersions())); const AlternativeService canonical_alternative_service2(kProtoHTTP2, "", 443); alternative_service_info_vector.push_back( AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo( canonical_alternative_service2, expiration)); properties.SetAlternativeServices(canonical_server1, network_anonymization_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_anonymization_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_anonymization_key2_) .empty()); EXPECT_TRUE( properties .GetAlternativeServiceInfos(test_server, NetworkAnonymizationKey()) .empty()); // Now add an alternative service entry for network_anonymization_key2_ for a // different server and different NetworkAnonymizationKey, but with the same // canonical suffix. url::SchemeHostPort canonical_server2("https", "shrimp.c.youtube.com", 443); properties.SetAlternativeServices(canonical_server2, network_anonymization_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_anonymization_key2_) .size()); EXPECT_EQ(2u, properties .GetAlternativeServiceInfos(test_server, network_anonymization_key1_) .size()); EXPECT_TRUE( properties .GetAlternativeServiceInfos(test_server, NetworkAnonymizationKey()) .empty()); // Clearing the alternate service state of network_anonymization_key1_'s // canonical server should only affect network_anonymization_key1_. properties.SetAlternativeServices(canonical_server1, network_anonymization_key1_, {}); EXPECT_EQ(1u, properties .GetAlternativeServiceInfos(test_server, network_anonymization_key2_) .size()); EXPECT_TRUE( properties .GetAlternativeServiceInfos(test_server, network_anonymization_key1_) .empty()); EXPECT_TRUE( properties .GetAlternativeServiceInfos(test_server, NetworkAnonymizationKey()) .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, NetworkAnonymizationKey())); impl_.MarkAlternativeServiceBroken(canonical_alternative_service, NetworkAnonymizationKey()); EXPECT_FALSE(HasAlternativeService(test_server, NetworkAnonymizationKey())); } 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, NetworkAnonymizationKey())); impl_.MarkAlternativeServiceBrokenUntilDefaultNetworkChanges( canonical_alternative_service, NetworkAnonymizationKey()); EXPECT_FALSE(HasAlternativeService(test_server, NetworkAnonymizationKey())); } // 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, NetworkAnonymizationKey()); 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, NetworkAnonymizationKey()); 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, NetworkAnonymizationKey())); } TEST_F(AlternateProtocolServerPropertiesTest, ExpireBrokenAlternateProtocolMappings) { url::SchemeHostPort server("https", "foo", 443); AlternativeService alternative_service(kProtoQUIC, "foo", 443); SetAlternativeService(server, alternative_service); EXPECT_TRUE(HasAlternativeService(server, NetworkAnonymizationKey())); EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service, NetworkAnonymizationKey())); EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken( alternative_service, NetworkAnonymizationKey())); base::TimeTicks past = test_tick_clock_->NowTicks() - base::Seconds(42); HttpServerPropertiesPeer::AddBrokenAlternativeServiceWithExpirationTime( &impl_, alternative_service, past); EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service, NetworkAnonymizationKey())); EXPECT_TRUE(impl_.WasAlternativeServiceRecentlyBroken( alternative_service, NetworkAnonymizationKey())); HttpServerPropertiesPeer::ExpireBrokenAlternateProtocolMappings(&impl_); EXPECT_FALSE(HasAlternativeService(server, NetworkAnonymizationKey())); EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service, NetworkAnonymizationKey())); EXPECT_TRUE(impl_.WasAlternativeServiceRecentlyBroken( alternative_service, NetworkAnonymizationKey())); } TEST_F(AlternateProtocolServerPropertiesTest, ExpireBrokenAlternateProtocolMappingsWithNetworkIsolationKey) { url::SchemeHostPort server("https", "foo", 443); AlternativeService alternative_service(kProtoHTTP2, "foo", 444); base::TimeTicks past = test_tick_clock_->NowTicks() - base::Seconds(42); base::TimeTicks future = test_tick_clock_->NowTicks() + base::Seconds(42); const base::Time alt_service_expiration = test_clock_.Now() + base::Days(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_anonymization_key1_, alternative_service, alt_service_expiration); properties.SetHttp2AlternativeService(server, network_anonymization_key2_, alternative_service, alt_service_expiration); EXPECT_FALSE( properties.GetAlternativeServiceInfos(server, network_anonymization_key1_) .empty()); EXPECT_FALSE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key1_)); EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key1_)); EXPECT_FALSE( properties.GetAlternativeServiceInfos(server, network_anonymization_key2_) .empty()); EXPECT_FALSE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key2_)); EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key2_)); // Set broken alternative service with expiration date in the past for // |network_anonymization_key1_|. HttpServerPropertiesPeer::AddBrokenAlternativeServiceWithExpirationTime( &properties, alternative_service, past, network_anonymization_key1_); EXPECT_TRUE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key1_)); EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key1_)); EXPECT_FALSE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key2_)); EXPECT_FALSE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key2_)); // Set broken alternative service with expiration date in the future for // |network_anonymization_key1_|. HttpServerPropertiesPeer::AddBrokenAlternativeServiceWithExpirationTime( &properties, alternative_service, future, network_anonymization_key2_); EXPECT_TRUE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key1_)); EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key1_)); EXPECT_TRUE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key2_)); EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key2_)); // Only the broken entry for |network_anonymization_key1_| should be expired. HttpServerPropertiesPeer::ExpireBrokenAlternateProtocolMappings(&properties); EXPECT_TRUE( properties.GetAlternativeServiceInfos(server, network_anonymization_key1_) .empty()); EXPECT_FALSE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key1_)); EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_key1_)); EXPECT_FALSE( properties.GetAlternativeServiceInfos(server, network_anonymization_key2_) .empty()); EXPECT_TRUE(properties.IsAlternativeServiceBroken( alternative_service, network_anonymization_key2_)); EXPECT_TRUE(properties.WasAlternativeServiceRecentlyBroken( alternative_service, network_anonymization_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, NetworkAnonymizationKey())); url::SchemeHostPort bar_server1("http", "bar", 80); AlternativeService nohost_alternative_service(kProtoQUIC, "", 443); SetAlternativeService(bar_server1, nohost_alternative_service); EXPECT_TRUE(HasAlternativeService(bar_server1, NetworkAnonymizationKey())); 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, NetworkAnonymizationKey())); // Mark "bar:443" as broken. base::TimeTicks past = test_tick_clock_->NowTicks() - base::Seconds(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, NetworkAnonymizationKey())); // "bar:80" should have no alternative service now. EXPECT_FALSE(HasAlternativeService(bar_server1, NetworkAnonymizationKey())); // The alternative service of "bar:443" should be unaffected. EXPECT_TRUE(HasAlternativeService(bar_server2, NetworkAnonymizationKey())); EXPECT_TRUE(impl_.WasAlternativeServiceRecentlyBroken( bar_alternative_service, NetworkAnonymizationKey())); EXPECT_FALSE(impl_.WasAlternativeServiceRecentlyBroken( baz_alternative_service, NetworkAnonymizationKey())); } TEST_F(AlternateProtocolServerPropertiesTest, SetBrokenAlternativeServicesDelayParams1) { url::SchemeHostPort server("https", "foo", 443); AlternativeService alternative_service(kProtoQUIC, "foo", 443); SetAlternativeService(server, alternative_service); const base::TimeDelta initial_delay = base::Seconds(1); impl_.SetBrokenAlternativeServicesDelayParams(initial_delay, true); for (int i = 0; i < 10; ++i) { impl_.MarkAlternativeServiceBroken(alternative_service, NetworkAnonymizationKey()); // |impl_| should have posted task to expire the brokenness of // |alternative_service| EXPECT_EQ(1u, GetPendingMainThreadTaskCount()); EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service, NetworkAnonymizationKey())); // Advance time by just enough so that |alternative_service|'s brokenness // expires. FastForwardBy(initial_delay * (1 << i)); // Ensure brokenness of |alternative_service| has expired. EXPECT_EQ(0u, GetPendingMainThreadTaskCount()); EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service, NetworkAnonymizationKey())); } } TEST_F(AlternateProtocolServerPropertiesTest, SetBrokenAlternativeServicesDelayParams2) { url::SchemeHostPort server("https", "foo", 443); AlternativeService alternative_service(kProtoQUIC, "foo", 443); SetAlternativeService(server, alternative_service); const base::TimeDelta initial_delay = base::Seconds(5); impl_.SetBrokenAlternativeServicesDelayParams(initial_delay, false); for (int i = 0; i < 10; ++i) { impl_.MarkAlternativeServiceBroken(alternative_service, NetworkAnonymizationKey()); // |impl_| should have posted task to expire the brokenness of // |alternative_service| EXPECT_EQ(1u, GetPendingMainThreadTaskCount()); EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service, NetworkAnonymizationKey())); // Advance time by just enough so that |alternative_service|'s brokenness // expires. if (i == 0) { FastForwardBy(initial_delay); } else { FastForwardBy(base::Seconds(300) * (1 << (i - 1))); } // Ensure brokenness of |alternative_service| has expired. EXPECT_EQ(0u, GetPendingMainThreadTaskCount()); EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service, NetworkAnonymizationKey())); } } // 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, NetworkAnonymizationKey()); // |impl_| should have posted task to expire the brokenness of // |alternative_service1| EXPECT_EQ(1u, GetPendingMainThreadTaskCount()); EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service1, NetworkAnonymizationKey())); // 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, NetworkAnonymizationKey())); } impl_.MarkAlternativeServiceBroken(alternative_service1, NetworkAnonymizationKey()); impl_.MarkAlternativeServiceBroken(alternative_service2, NetworkAnonymizationKey()); EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service2, NetworkAnonymizationKey())); // 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, NetworkAnonymizationKey())); EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service2, NetworkAnonymizationKey())); // 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, NetworkAnonymizationKey())); EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service2, NetworkAnonymizationKey())); } // 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, NetworkAnonymizationKey())); // Add an entry to ServerInfo for another server, without an alternative // service value. const url::SchemeHostPort kServer2("http", "bar", 80); impl_.SetSupportsSpdy(kServer2, NetworkAnonymizationKey(), false); // Mark kAltService as broken. base::TimeTicks past = test_tick_clock_->NowTicks() - base::Seconds(42); HttpServerPropertiesPeer::AddBrokenAlternativeServiceWithExpirationTime( &impl_, kAltService, past); // Expire brokenness of kAltService. This call should not hang. HttpServerPropertiesPeer::ExpireBrokenAlternateProtocolMappings(&impl_); EXPECT_FALSE(HasAlternativeService(kServer1, NetworkAnonymizationKey())); } TEST_F(AlternateProtocolServerPropertiesTest, GetAlternativeServiceInfoAsValue) { 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::Minutes(1))); alternative_service_info_vector.push_back( AlternativeServiceInfo::CreateQuicAlternativeServiceInfo( AlternativeService(kProtoQUIC, "bar", 443), now + base::Hours(1), DefaultSupportedQuicVersions())); alternative_service_info_vector.push_back( AlternativeServiceInfo::CreateQuicAlternativeServiceInfo( AlternativeService(kProtoQUIC, "baz", 443), now + base::Hours(1), DefaultSupportedQuicVersions())); impl_.SetAlternativeServices(url::SchemeHostPort("https", "youtube.com", 443), NetworkAnonymizationKey(), alternative_service_info_vector); impl_.MarkAlternativeServiceBroken(AlternativeService(kProtoQUIC, "bar", 443), NetworkAnonymizationKey()); impl_.MarkAlternativeServiceBrokenUntilDefaultNetworkChanges( AlternativeService(kProtoQUIC, "baz", 443), NetworkAnonymizationKey()); alternative_service_info_vector.clear(); alternative_service_info_vector.push_back( AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo( AlternativeService(kProtoHTTP2, "foo2", 443), now + base::Days(1))); impl_.SetAlternativeServices(url::SchemeHostPort("http", "test.com", 80), NetworkAnonymizationKey(), alternative_service_info_vector); const char expected_json[] = "[" "{" "\"alternative_service\":" "[\"h2 foo2:443, expires 2018-01-25 15:12:53\"]," "\"network_anonymization_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_anonymization_key\":\"null\"," "\"server\":\"https://youtube.com\"" "}" "]"; 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 load_server_info_map = std::make_unique(); impl_.OnServerInfoLoadedForTesting(std::move(load_server_info_map)); const ServerNetworkStats* stats = impl_.GetServerNetworkStats(google_server, NetworkAnonymizationKey()); EXPECT_EQ(nullptr, stats); // Check by initializing with www.google.com:443. ServerNetworkStats stats_google; stats_google.srtt = base::Microseconds(10); stats_google.bandwidth_estimate = quic::QuicBandwidth::FromBitsPerSecond(100); load_server_info_map = std::make_unique(); 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, NetworkAnonymizationKey()))); // 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::Microseconds(20); stats_docs.bandwidth_estimate = quic::QuicBandwidth::FromBitsPerSecond(200); // Recency order will be |docs_server| and |google_server|. impl_.SetServerNetworkStats(docs_server, NetworkAnonymizationKey(), stats_docs); // Prepare |server_info_map| to be loaded by OnServerInfoLoadedForTesting(). std::unique_ptr server_info_map = std::make_unique(); // Change the values for |docs_server|. ServerNetworkStats new_stats_docs; new_stats_docs.srtt = base::Microseconds(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::Microseconds(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_anonymization_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_anonymization_key.IsEmpty()); ASSERT_TRUE(map_it->second.server_network_stats.has_value()); EXPECT_EQ(stats_google, *map_it->second.server_network_stats); ++map_it; EXPECT_EQ(mail_server, map_it->first.server); EXPECT_TRUE(map_it->first.network_anonymization_key.IsEmpty()); ASSERT_TRUE(map_it->second.server_network_stats.has_value()); EXPECT_EQ(stats_mail, *map_it->second.server_network_stats); } TEST_F(HttpServerPropertiesTest, SetServerNetworkStats) { url::SchemeHostPort foo_http_server("http", "foo", 443); url::SchemeHostPort foo_https_server("https", "foo", 443); EXPECT_EQ(nullptr, impl_.GetServerNetworkStats(foo_http_server, NetworkAnonymizationKey())); EXPECT_EQ(nullptr, impl_.GetServerNetworkStats(foo_https_server, NetworkAnonymizationKey())); ServerNetworkStats stats1; stats1.srtt = base::Microseconds(10); stats1.bandwidth_estimate = quic::QuicBandwidth::FromBitsPerSecond(100); impl_.SetServerNetworkStats(foo_http_server, NetworkAnonymizationKey(), stats1); const ServerNetworkStats* stats2 = impl_.GetServerNetworkStats(foo_http_server, NetworkAnonymizationKey()); EXPECT_EQ(10, stats2->srtt.ToInternalValue()); EXPECT_EQ(100, stats2->bandwidth_estimate.ToBitsPerSecond()); // Https server should have nothing set for server network stats. EXPECT_EQ(nullptr, impl_.GetServerNetworkStats(foo_https_server, NetworkAnonymizationKey())); impl_.Clear(base::OnceClosure()); EXPECT_EQ(nullptr, impl_.GetServerNetworkStats(foo_http_server, NetworkAnonymizationKey())); EXPECT_EQ(nullptr, impl_.GetServerNetworkStats(foo_https_server, NetworkAnonymizationKey())); } TEST_F(HttpServerPropertiesTest, ClearServerNetworkStats) { ServerNetworkStats stats; stats.srtt = base::Microseconds(10); stats.bandwidth_estimate = quic::QuicBandwidth::FromBitsPerSecond(100); url::SchemeHostPort foo_https_server("https", "foo", 443); impl_.SetServerNetworkStats(foo_https_server, NetworkAnonymizationKey(), stats); impl_.ClearServerNetworkStats(foo_https_server, NetworkAnonymizationKey()); EXPECT_EQ(nullptr, impl_.GetServerNetworkStats(foo_https_server, NetworkAnonymizationKey())); } TEST_F(HttpServerPropertiesTest, OnQuicServerInfoMapLoaded) { quic::QuicServerId google_quic_server_id("www.google.com", 443, true); HttpServerProperties::QuicServerInfoMapKey google_key( google_quic_server_id, NetworkAnonymizationKey(), false /* use_network_anonymization_key */); const int kMaxQuicServerEntries = 10; impl_.SetMaxServerConfigsStoredInProperties(kMaxQuicServerEntries); EXPECT_EQ(10u, impl_.quic_server_info_map().max_size()); // Check empty map. std::unique_ptr init_quic_server_info_map = std::make_unique( kMaxQuicServerEntries); impl_.OnQuicServerInfoMapLoadedForTesting( std::move(init_quic_server_info_map)); EXPECT_EQ(0u, impl_.quic_server_info_map().size()); // Check by initializing with www.google.com:443. std::string google_server_info("google_quic_server_info"); init_quic_server_info_map = std::make_unique( kMaxQuicServerEntries); init_quic_server_info_map->Put(google_key, google_server_info); impl_.OnQuicServerInfoMapLoadedForTesting( std::move(init_quic_server_info_map)); // Verify data for www.google.com:443. EXPECT_EQ(1u, impl_.quic_server_info_map().size()); EXPECT_EQ(google_server_info, *impl_.GetQuicServerInfo(google_quic_server_id, NetworkAnonymizationKey())); // Test recency order and overwriting of data. // // |docs_server| has a QuicServerInfo, which will be overwritten by // SetQuicServerInfoMap(), because |quic_server_info_map| has an // entry for |docs_server|. quic::QuicServerId docs_quic_server_id("docs.google.com", 443, true); HttpServerProperties::QuicServerInfoMapKey docs_key( docs_quic_server_id, NetworkAnonymizationKey(), false /* use_network_anonymization_key */); std::string docs_server_info("docs_quic_server_info"); impl_.SetQuicServerInfo(docs_quic_server_id, NetworkAnonymizationKey(), docs_server_info); // Recency order will be |docs_server| and |google_server|. const HttpServerProperties::QuicServerInfoMap& map = impl_.quic_server_info_map(); ASSERT_EQ(2u, map.size()); auto map_it = map.begin(); EXPECT_EQ(map_it->first, docs_key); EXPECT_EQ(docs_server_info, map_it->second); ++map_it; EXPECT_EQ(map_it->first, google_key); EXPECT_EQ(google_server_info, map_it->second); // Prepare |quic_server_info_map| to be loaded by // SetQuicServerInfoMap(). std::unique_ptr quic_server_info_map = std::make_unique( kMaxQuicServerEntries); // Change the values for |docs_server|. std::string new_docs_server_info("new_docs_quic_server_info"); quic_server_info_map->Put(docs_key, new_docs_server_info); // Add data for mail.google.com:443. quic::QuicServerId mail_quic_server_id("mail.google.com", 443, true); HttpServerProperties::QuicServerInfoMapKey mail_key( mail_quic_server_id, NetworkAnonymizationKey(), false /* use_network_anonymization_key */); std::string mail_server_info("mail_quic_server_info"); quic_server_info_map->Put(mail_key, mail_server_info); impl_.OnQuicServerInfoMapLoadedForTesting(std::move(quic_server_info_map)); // Recency order will be |docs_server|, |google_server| and |mail_server|. const HttpServerProperties::QuicServerInfoMap& memory_map = impl_.quic_server_info_map(); ASSERT_EQ(3u, memory_map.size()); auto memory_map_it = memory_map.begin(); EXPECT_EQ(memory_map_it->first, docs_key); EXPECT_EQ(new_docs_server_info, memory_map_it->second); ++memory_map_it; EXPECT_EQ(memory_map_it->first, google_key); EXPECT_EQ(google_server_info, memory_map_it->second); ++memory_map_it; EXPECT_EQ(memory_map_it->first, mail_key); EXPECT_EQ(mail_server_info, memory_map_it->second); // Shrink the size of |quic_server_info_map| and verify the MRU order is // maintained. impl_.SetMaxServerConfigsStoredInProperties(2); EXPECT_EQ(2u, impl_.quic_server_info_map().max_size()); const HttpServerProperties::QuicServerInfoMap& memory_map1 = impl_.quic_server_info_map(); ASSERT_EQ(2u, memory_map1.size()); auto memory_map1_it = memory_map1.begin(); EXPECT_EQ(memory_map1_it->first, docs_key); EXPECT_EQ(new_docs_server_info, memory_map1_it->second); ++memory_map1_it; EXPECT_EQ(memory_map1_it->first, google_key); EXPECT_EQ(google_server_info, memory_map1_it->second); // |QuicServerInfo| for |mail_quic_server_id| shouldn't be there. EXPECT_EQ(nullptr, impl_.GetQuicServerInfo(mail_quic_server_id, NetworkAnonymizationKey())); } TEST_F(HttpServerPropertiesTest, SetQuicServerInfo) { quic::QuicServerId server1("foo", 80, false /* privacy_mode_enabled */); quic::QuicServerId server2("foo", 80, true /* privacy_mode_enabled */); std::string quic_server_info1("quic_server_info1"); std::string quic_server_info2("quic_server_info2"); std::string quic_server_info3("quic_server_info3"); // Without network isolation keys enabled for HttpServerProperties, passing in // a NetworkAnonymizationKey should have no effect on behavior. impl_.SetQuicServerInfo(server1, NetworkAnonymizationKey(), quic_server_info1); EXPECT_EQ(quic_server_info1, *(impl_.GetQuicServerInfo(server1, NetworkAnonymizationKey()))); EXPECT_FALSE(impl_.GetQuicServerInfo(server2, NetworkAnonymizationKey())); EXPECT_EQ(quic_server_info1, *(impl_.GetQuicServerInfo(server1, network_anonymization_key1_))); EXPECT_FALSE(impl_.GetQuicServerInfo(server2, network_anonymization_key1_)); impl_.SetQuicServerInfo(server2, network_anonymization_key1_, quic_server_info2); EXPECT_EQ(quic_server_info1, *(impl_.GetQuicServerInfo(server1, NetworkAnonymizationKey()))); EXPECT_EQ(quic_server_info2, *(impl_.GetQuicServerInfo(server2, NetworkAnonymizationKey()))); EXPECT_EQ(quic_server_info1, *(impl_.GetQuicServerInfo(server1, network_anonymization_key1_))); EXPECT_EQ(quic_server_info2, *(impl_.GetQuicServerInfo(server2, network_anonymization_key1_))); impl_.SetQuicServerInfo(server1, network_anonymization_key1_, quic_server_info3); EXPECT_EQ(quic_server_info3, *(impl_.GetQuicServerInfo(server1, NetworkAnonymizationKey()))); EXPECT_EQ(quic_server_info2, *(impl_.GetQuicServerInfo(server2, NetworkAnonymizationKey()))); EXPECT_EQ(quic_server_info3, *(impl_.GetQuicServerInfo(server1, network_anonymization_key1_))); EXPECT_EQ(quic_server_info2, *(impl_.GetQuicServerInfo(server2, network_anonymization_key1_))); impl_.Clear(base::OnceClosure()); EXPECT_FALSE(impl_.GetQuicServerInfo(server1, NetworkAnonymizationKey())); EXPECT_FALSE(impl_.GetQuicServerInfo(server2, NetworkAnonymizationKey())); EXPECT_FALSE(impl_.GetQuicServerInfo(server1, network_anonymization_key1_)); EXPECT_FALSE(impl_.GetQuicServerInfo(server2, network_anonymization_key1_)); 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.SetQuicServerInfo(server1, NetworkAnonymizationKey(), quic_server_info1); EXPECT_EQ(quic_server_info1, *(properties.GetQuicServerInfo( server1, NetworkAnonymizationKey()))); EXPECT_FALSE( properties.GetQuicServerInfo(server2, NetworkAnonymizationKey())); EXPECT_FALSE( properties.GetQuicServerInfo(server1, network_anonymization_key1_)); EXPECT_FALSE( properties.GetQuicServerInfo(server2, network_anonymization_key1_)); properties.SetQuicServerInfo(server1, network_anonymization_key1_, quic_server_info2); EXPECT_EQ(quic_server_info1, *(properties.GetQuicServerInfo( server1, NetworkAnonymizationKey()))); EXPECT_FALSE( properties.GetQuicServerInfo(server2, NetworkAnonymizationKey())); EXPECT_EQ(quic_server_info2, *(properties.GetQuicServerInfo( server1, network_anonymization_key1_))); EXPECT_FALSE( properties.GetQuicServerInfo(server2, network_anonymization_key1_)); properties.SetQuicServerInfo(server2, network_anonymization_key1_, quic_server_info3); EXPECT_EQ(quic_server_info1, *(properties.GetQuicServerInfo( server1, NetworkAnonymizationKey()))); EXPECT_FALSE( properties.GetQuicServerInfo(server2, NetworkAnonymizationKey())); EXPECT_EQ(quic_server_info2, *(properties.GetQuicServerInfo( server1, network_anonymization_key1_))); EXPECT_EQ(quic_server_info3, *(properties.GetQuicServerInfo( server2, network_anonymization_key1_))); properties.Clear(base::OnceClosure()); EXPECT_FALSE( properties.GetQuicServerInfo(server1, NetworkAnonymizationKey())); EXPECT_FALSE( properties.GetQuicServerInfo(server2, NetworkAnonymizationKey())); EXPECT_FALSE( properties.GetQuicServerInfo(server1, network_anonymization_key1_)); EXPECT_FALSE( properties.GetQuicServerInfo(server2, network_anonymization_key1_)); } // Tests that GetQuicServerInfo() returns server info of a host // with the same canonical suffix when there is no exact host match. TEST_F(HttpServerPropertiesTest, QuicServerInfoCanonicalSuffixMatch) { // Set up HttpServerProperties. // Add a host with a canonical suffix. quic::QuicServerId foo_server_id("foo.googlevideo.com", 443, false); std::string foo_server_info("foo_server_info"); impl_.SetQuicServerInfo(foo_server_id, NetworkAnonymizationKey(), foo_server_info); // Add a host that has a different canonical suffix. quic::QuicServerId baz_server_id("baz.video.com", 443, false); std::string baz_server_info("baz_server_info"); impl_.SetQuicServerInfo(baz_server_id, NetworkAnonymizationKey(), baz_server_info); // Create SchemeHostPort with a host that has the initial canonical suffix. quic::QuicServerId bar_server_id("bar.googlevideo.com", 443, false); // Check the the server info associated with "foo" is returned for "bar". const std::string* bar_server_info = impl_.GetQuicServerInfo(bar_server_id, NetworkAnonymizationKey()); ASSERT_TRUE(bar_server_info != nullptr); EXPECT_EQ(foo_server_info, *bar_server_info); } // Make sure that canonical suffices respect NetworkIsolationKeys when using // QuicServerInfo methods. TEST_F(HttpServerPropertiesTest, QuicServerInfoCanonicalSuffixMatchWithNetworkIsolationKey) { // Two servers with same canonical suffix. quic::QuicServerId server1("foo.googlevideo.com", 80, false /* privacy_mode_enabled */); quic::QuicServerId server2("bar.googlevideo.com", 80, false /* privacy_mode_enabled */); std::string server_info1("server_info1"); std::string server_info2("server_info2"); 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_); // Set QuicServerInfo for one canononical suffix and // |network_anonymization_key1_|. It should be accessible via another // SchemeHostPort, but only when the NetworkIsolationKeys match. properties.SetQuicServerInfo(server1, network_anonymization_key1_, server_info1); const std::string* fetched_server_info = properties.GetQuicServerInfo(server1, network_anonymization_key1_); ASSERT_TRUE(fetched_server_info); EXPECT_EQ(server_info1, *fetched_server_info); fetched_server_info = properties.GetQuicServerInfo(server2, network_anonymization_key1_); ASSERT_TRUE(fetched_server_info); EXPECT_EQ(server_info1, *fetched_server_info); EXPECT_FALSE( properties.GetQuicServerInfo(server1, network_anonymization_key2_)); EXPECT_FALSE( properties.GetQuicServerInfo(server2, network_anonymization_key2_)); EXPECT_FALSE( properties.GetQuicServerInfo(server1, NetworkAnonymizationKey())); EXPECT_FALSE( properties.GetQuicServerInfo(server2, NetworkAnonymizationKey())); // Set different QuicServerInfo for the same canononical suffix and // |network_anonymization_key2_|. Both infos should be retriveable by using // the different NetworkIsolationKeys. properties.SetQuicServerInfo(server1, network_anonymization_key2_, server_info2); fetched_server_info = properties.GetQuicServerInfo(server1, network_anonymization_key1_); ASSERT_TRUE(fetched_server_info); EXPECT_EQ(server_info1, *fetched_server_info); fetched_server_info = properties.GetQuicServerInfo(server2, network_anonymization_key1_); ASSERT_TRUE(fetched_server_info); EXPECT_EQ(server_info1, *fetched_server_info); fetched_server_info = properties.GetQuicServerInfo(server1, network_anonymization_key2_); ASSERT_TRUE(fetched_server_info); EXPECT_EQ(server_info2, *fetched_server_info); fetched_server_info = properties.GetQuicServerInfo(server2, network_anonymization_key2_); ASSERT_TRUE(fetched_server_info); EXPECT_EQ(server_info2, *fetched_server_info); EXPECT_FALSE( properties.GetQuicServerInfo(server1, NetworkAnonymizationKey())); EXPECT_FALSE( properties.GetQuicServerInfo(server2, NetworkAnonymizationKey())); // Clearing should destroy all information. properties.Clear(base::OnceClosure()); EXPECT_FALSE( properties.GetQuicServerInfo(server1, network_anonymization_key1_)); EXPECT_FALSE( properties.GetQuicServerInfo(server2, network_anonymization_key1_)); EXPECT_FALSE( properties.GetQuicServerInfo(server1, network_anonymization_key2_)); EXPECT_FALSE( properties.GetQuicServerInfo(server2, network_anonymization_key2_)); EXPECT_FALSE( properties.GetQuicServerInfo(server1, NetworkAnonymizationKey())); EXPECT_FALSE( properties.GetQuicServerInfo(server2, NetworkAnonymizationKey())); } // Verifies that GetQuicServerInfo() returns the MRU entry if multiple records // match a given canonical host. TEST_F(HttpServerPropertiesTest, QuicServerInfoCanonicalSuffixMatchReturnsMruEntry) { // Set up HttpServerProperties by adding two hosts with the same canonical // suffixes. quic::QuicServerId h1_server_id("h1.googlevideo.com", 443, false); std::string h1_server_info("h1_server_info"); impl_.SetQuicServerInfo(h1_server_id, NetworkAnonymizationKey(), h1_server_info); quic::QuicServerId h2_server_id("h2.googlevideo.com", 443, false); std::string h2_server_info("h2_server_info"); impl_.SetQuicServerInfo(h2_server_id, NetworkAnonymizationKey(), h2_server_info); // Create quic::QuicServerId to use for the search. quic::QuicServerId foo_server_id("foo.googlevideo.com", 443, false); // Check that 'h2' info is returned since it is MRU. const std::string* server_info = impl_.GetQuicServerInfo(foo_server_id, NetworkAnonymizationKey()); ASSERT_TRUE(server_info != nullptr); EXPECT_EQ(h2_server_info, *server_info); // Access 'h1' info, so it becomes MRU. impl_.GetQuicServerInfo(h1_server_id, NetworkAnonymizationKey()); // Check that 'h1' info is returned since it is MRU now. server_info = impl_.GetQuicServerInfo(foo_server_id, NetworkAnonymizationKey()); ASSERT_TRUE(server_info != nullptr); EXPECT_EQ(h1_server_info, *server_info); } // Verifies that |GetQuicServerInfo| doesn't change the MRU order of the server // info map when a record is matched based on a canonical name. TEST_F(HttpServerPropertiesTest, QuicServerInfoCanonicalSuffixMatchDoesntChangeOrder) { // Add a host with a matching canonical name. quic::QuicServerId h1_server_id("h1.googlevideo.com", 443, false); HttpServerProperties::QuicServerInfoMapKey h1_key( h1_server_id, NetworkAnonymizationKey(), false /* use_network_anonymization_key */); std::string h1_server_info("h1_server_info"); impl_.SetQuicServerInfo(h1_server_id, NetworkAnonymizationKey(), h1_server_info); // Add a host hosts with a non-matching canonical name. quic::QuicServerId h2_server_id("h2.video.com", 443, false); HttpServerProperties::QuicServerInfoMapKey h2_key( h2_server_id, NetworkAnonymizationKey(), false /* use_network_anonymization_key */); std::string h2_server_info("h2_server_info"); impl_.SetQuicServerInfo(h2_server_id, NetworkAnonymizationKey(), h2_server_info); // Check that "h2.video.com" is the MRU entry in the map. EXPECT_EQ(h2_key, impl_.quic_server_info_map().begin()->first); // Search for the entry that matches the canonical name // ("h1.googlevideo.com"). quic::QuicServerId foo_server_id("foo.googlevideo.com", 443, false); const std::string* server_info = impl_.GetQuicServerInfo(foo_server_id, NetworkAnonymizationKey()); ASSERT_TRUE(server_info != nullptr); // Check that the search (although successful) hasn't changed the MRU order of // the map. EXPECT_EQ(h2_key, impl_.quic_server_info_map().begin()->first); // Search for "h1.googlevideo.com" directly, so it becomes MRU impl_.GetQuicServerInfo(h1_server_id, NetworkAnonymizationKey()); // Check that "h1.googlevideo.com" is the MRU entry now. EXPECT_EQ(h1_key, impl_.quic_server_info_map().begin()->first); } // Tests that the canonical host matching works for hosts stored in memory cache // and the ones loaded from persistent storage, i.e. server info added // using SetQuicServerInfo() and SetQuicServerInfoMap() is taken into // cosideration when searching for server info for a host with the same // canonical suffix. TEST_F(HttpServerPropertiesTest, QuicServerInfoCanonicalSuffixMatchSetInfoMap) { // Add a host info using SetQuicServerInfo(). That will simulate an info // entry stored in memory cache. quic::QuicServerId h1_server_id("h1.googlevideo.com", 443, false); std::string h1_server_info("h1_server_info_memory_cache"); impl_.SetQuicServerInfo(h1_server_id, NetworkAnonymizationKey(), h1_server_info); // Prepare a map with host info and add it using SetQuicServerInfoMap(). That // will simulate info records read from the persistence storage. quic::QuicServerId h2_server_id("h2.googlevideo.com", 443, false); HttpServerProperties::QuicServerInfoMapKey h2_key( h2_server_id, NetworkAnonymizationKey(), false /* use_network_anonymization_key */); std::string h2_server_info("h2_server_info_from_disk"); quic::QuicServerId h3_server_id("h3.ggpht.com", 443, false); HttpServerProperties::QuicServerInfoMapKey h3_key( h3_server_id, NetworkAnonymizationKey(), false /* use_network_anonymization_key */); std::string h3_server_info("h3_server_info_from_disk"); const int kMaxQuicServerEntries = 10; impl_.SetMaxServerConfigsStoredInProperties(kMaxQuicServerEntries); std::unique_ptr quic_server_info_map = std::make_unique( kMaxQuicServerEntries); quic_server_info_map->Put(h2_key, h2_server_info); quic_server_info_map->Put(h3_key, h3_server_info); impl_.OnQuicServerInfoMapLoadedForTesting(std::move(quic_server_info_map)); // Check that the server info from the memory cache is returned since unique // entries from the memory cache are added after entries from the // persistence storage and, therefore, are most recently used. quic::QuicServerId foo_server_id("foo.googlevideo.com", 443, false); const std::string* server_info = impl_.GetQuicServerInfo(foo_server_id, NetworkAnonymizationKey()); ASSERT_TRUE(server_info != nullptr); EXPECT_EQ(h1_server_info, *server_info); // Check that server info that was added using SetQuicServerInfoMap() can be // found. foo_server_id = quic::QuicServerId("foo.ggpht.com", 443, false); server_info = impl_.GetQuicServerInfo(foo_server_id, NetworkAnonymizationKey()); ASSERT_TRUE(server_info != nullptr); EXPECT_EQ(h3_server_info, *server_info); } } // namespace } // namespace net