blob: ee113c7bd07dcff1b73094654a6b6a5f099edc53 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/preloading/preconnect/preconnect_manager_impl.h"
#include <algorithm>
#include <map>
#include <utility>
#include "base/format_macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "content/browser/preloading/proxy_lookup_client_impl.h"
#include "content/browser/preloading/resolve_host_client_impl.h"
#include "content/public/browser/preconnect_manager.h"
#include "content/public/browser/preconnect_request.h"
#include "content/public/common/content_features.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_browser_context.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "net/base/load_flags.h"
#include "net/base/network_anonymization_key.h"
#include "net/dns/public/resolve_error_info.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "services/network/public/mojom/connection_change_observer_client.mojom.h"
#include "services/network/test/test_network_context.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/origin.h"
using content::PreconnectRequest;
using testing::_;
using testing::Mock;
using testing::Return;
using testing::SaveArg;
using testing::StrictMock;
namespace content {
namespace {
net::ProxyInfo GetIndirectProxyInfo() {
net::ProxyInfo proxy_info;
proxy_info.UseNamedProxy("proxy.com");
return proxy_info;
}
net::ProxyInfo GetDirectProxyInfo() {
net::ProxyInfo proxy_info;
proxy_info.UseDirect();
return proxy_info;
}
class MockPreconnectManagerDelegate : public PreconnectManager::Delegate {
public:
// Gmock doesn't support mocking methods with move-only argument types.
void PreconnectFinished(std::unique_ptr<PreconnectStats> stats) override {
PreconnectFinishedProxy(stats->url);
}
MOCK_METHOD1(PreconnectFinishedProxy, void(const GURL& url));
MOCK_METHOD2(PreconnectInitiated,
void(const GURL& url, const GURL& preconnect_url));
MOCK_METHOD0(IsPreconnectEnabled, bool());
base::WeakPtr<MockPreconnectManagerDelegate> AsWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
private:
base::WeakPtrFactory<MockPreconnectManagerDelegate> weak_ptr_factory_{this};
};
class MockNetworkContext : public network::TestNetworkContext {
public:
MockNetworkContext() = default;
~MockNetworkContext() override {
EXPECT_TRUE(resolve_host_clients_.empty())
<< "Not all resolve host requests were satisfied";
}
void ResolveHost(
network::mojom::HostResolverHostPtr host,
const net::NetworkAnonymizationKey& network_anonymization_key,
network::mojom::ResolveHostParametersPtr optional_parameters,
mojo::PendingRemote<network::mojom::ResolveHostClient> response_client)
override {
const std::string& hostname = host->is_host_port_pair()
? host->get_host_port_pair().host()
: host->get_scheme_host_port().host();
EXPECT_FALSE(IsHangingHost(GURL(hostname)))
<< " Hosts marked as hanging should not be resolved.";
EXPECT_TRUE(
resolve_host_clients_
.emplace(ResolveHostClientKey{hostname, network_anonymization_key},
mojo::Remote<network::mojom::ResolveHostClient>(
std::move(response_client)))
.second);
ResolveHostProxy(hostname);
}
void LookUpProxyForURL(
const GURL& url,
const net::NetworkAnonymizationKey& network_anonymization_key,
mojo::PendingRemote<network::mojom::ProxyLookupClient>
proxy_lookup_client) override {
EXPECT_TRUE(
proxy_lookup_clients_.emplace(url, std::move(proxy_lookup_client))
.second);
if (!enabled_proxy_testing_) {
// We don't want to test proxy, return that the proxy is disabled.
CompleteProxyLookup(url, std::nullopt);
}
}
void CompleteHostLookup(
const std::string& host,
const net::NetworkAnonymizationKey& network_anonymization_key,
int result) {
DCHECK(result == net::OK || result == net::ERR_NAME_NOT_RESOLVED);
auto it = resolve_host_clients_.find(
ResolveHostClientKey{host, network_anonymization_key});
if (it == resolve_host_clients_.end()) {
ADD_FAILURE() << host << " wasn't found";
return;
}
it->second->OnComplete(result, net::ResolveErrorInfo(result),
/*resolved_addresses=*/{},
/*alternative_endpoints=*/{});
resolve_host_clients_.erase(it);
// Wait for OnComplete() to be executed on the UI thread.
base::RunLoop().RunUntilIdle();
}
void CompleteProxyLookup(const GURL& url,
const std::optional<net::ProxyInfo>& result) {
if (IsHangingHost(url)) {
return;
}
auto it = proxy_lookup_clients_.find(url);
if (it == proxy_lookup_clients_.end()) {
ADD_FAILURE() << url.spec() << " wasn't found";
return;
}
it->second->OnProxyLookupComplete(net::ERR_FAILED, result);
proxy_lookup_clients_.erase(it);
// Wait for OnProxyLookupComplete() to be executed on the UI thread.
base::RunLoop().RunUntilIdle();
}
// Preresolve/preconnect requests for all hosts in |hanging_url_requests| is
// never completed.
void SetHangingHostsFromPreconnectRequests(
const std::vector<PreconnectRequest>& hanging_url_requests) {
hanging_hosts_.clear();
for (const auto& request : hanging_url_requests) {
hanging_hosts_.push_back(request.origin.host());
}
}
void EnableProxyTesting() { enabled_proxy_testing_ = true; }
MOCK_METHOD1(ResolveHostProxy, void(const std::string& host));
MOCK_METHOD7(
PreconnectSockets,
void(
uint32_t num_streams,
const GURL& url,
network::mojom::CredentialsMode credentials_mode,
const net::NetworkAnonymizationKey& network_anonymization_key,
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
const std::optional<net::ConnectionKeepAliveConfig>& keepalive_config,
mojo::PendingRemote<network::mojom::ConnectionChangeObserverClient>
observer_client));
private:
bool IsHangingHost(const GURL& url) const {
return base::Contains(hanging_hosts_, url.host());
}
using ResolveHostClientKey =
std::pair<std::string, net::NetworkAnonymizationKey>;
std::map<ResolveHostClientKey,
mojo::Remote<network::mojom::ResolveHostClient>>
resolve_host_clients_;
std::map<GURL, mojo::Remote<network::mojom::ProxyLookupClient>>
proxy_lookup_clients_;
bool enabled_proxy_testing_ = false;
std::vector<std::string> hanging_hosts_;
};
// Creates a NetworkAnonymizationKey for a main frame navigation to URL.
net::NetworkAnonymizationKey CreateNetworkAnonymizationKey(
const GURL& main_frame_url) {
net::SchemefulSite site = net::SchemefulSite(main_frame_url);
return net::NetworkAnonymizationKey::CreateSameSite(site);
}
} // namespace
class PreconnectManagerImplTest : public testing::Test {
public:
PreconnectManagerImplTest();
PreconnectManagerImplTest(const PreconnectManagerImplTest&) = delete;
PreconnectManagerImplTest& operator=(const PreconnectManagerImplTest&) =
delete;
~PreconnectManagerImplTest() override;
void VerifyAndClearExpectations() const {
base::RunLoop().RunUntilIdle();
Mock::VerifyAndClearExpectations(mock_network_context_.get());
Mock::VerifyAndClearExpectations(mock_delegate_.get());
}
protected:
content::BrowserTaskEnvironment task_environment_;
std::unique_ptr<TestBrowserContext> browser_context_;
std::unique_ptr<StrictMock<MockNetworkContext>> mock_network_context_;
std::unique_ptr<StrictMock<MockPreconnectManagerDelegate>> mock_delegate_;
std::unique_ptr<PreconnectManagerImpl> preconnect_manager_;
};
PreconnectManagerImplTest::PreconnectManagerImplTest()
: browser_context_(std::make_unique<TestBrowserContext>()),
mock_network_context_(std::make_unique<StrictMock<MockNetworkContext>>()),
mock_delegate_(
std::make_unique<StrictMock<MockPreconnectManagerDelegate>>()),
preconnect_manager_(
std::make_unique<PreconnectManagerImpl>(mock_delegate_->AsWeakPtr(),
browser_context_.get())) {
preconnect_manager_->SetNetworkContextForTesting(mock_network_context_.get());
}
PreconnectManagerImplTest::~PreconnectManagerImplTest() {
VerifyAndClearExpectations();
}
TEST_F(PreconnectManagerImplTest, TestStartOneUrlPreresolve) {
GURL main_frame_url("http://google.com");
url::Origin origin_to_preresolve =
url::Origin::Create(GURL("http://cdn.google.com"));
EXPECT_CALL(*mock_delegate_, IsPreconnectEnabled()).WillOnce(Return(true));
EXPECT_CALL(
*mock_delegate_,
PreconnectInitiated(main_frame_url, origin_to_preresolve.GetURL()));
EXPECT_CALL(*mock_network_context_,
ResolveHostProxy(origin_to_preresolve.host()));
EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url));
preconnect_manager_->Start(
main_frame_url,
{PreconnectRequest(origin_to_preresolve, 0,
CreateNetworkAnonymizationKey(main_frame_url))},
TRAFFIC_ANNOTATION_FOR_TESTS);
mock_network_context_->CompleteHostLookup(
origin_to_preresolve.host(),
CreateNetworkAnonymizationKey(main_frame_url), net::OK);
}
TEST_F(PreconnectManagerImplTest, TestStartOneUrlPreconnect) {
GURL main_frame_url("http://google.com");
net::NetworkAnonymizationKey network_anonymization_key =
CreateNetworkAnonymizationKey(main_frame_url);
url::Origin origin_to_preconnect =
url::Origin::Create(GURL("http://cdn.google.com"));
EXPECT_CALL(*mock_delegate_, IsPreconnectEnabled()).WillOnce(Return(true));
EXPECT_CALL(
*mock_delegate_,
PreconnectInitiated(main_frame_url, origin_to_preconnect.GetURL()));
EXPECT_CALL(*mock_network_context_,
ResolveHostProxy(origin_to_preconnect.host()));
preconnect_manager_->Start(
main_frame_url,
{PreconnectRequest(origin_to_preconnect, 1, network_anonymization_key)},
TRAFFIC_ANNOTATION_FOR_TESTS);
EXPECT_CALL(
*mock_network_context_,
PreconnectSockets(
1, origin_to_preconnect.GetURL(),
network::mojom::CredentialsMode::kInclude, network_anonymization_key,
net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
_, _));
EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url));
mock_network_context_->CompleteHostLookup(origin_to_preconnect.host(),
network_anonymization_key, net::OK);
}
TEST_F(PreconnectManagerImplTest, TestLimitPreconnectCount) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(
features::kLoadingPredictorLimitPreconnectSocketCount);
GURL main_frame_url("http://google.com");
net::NetworkAnonymizationKey network_anonymization_key =
CreateNetworkAnonymizationKey(main_frame_url);
url::Origin origin_to_preconnect =
url::Origin::Create(GURL("http://cdn.google.com"));
EXPECT_CALL(*mock_delegate_, IsPreconnectEnabled()).WillOnce(Return(true));
EXPECT_CALL(
*mock_delegate_,
PreconnectInitiated(main_frame_url, origin_to_preconnect.GetURL()));
EXPECT_CALL(*mock_network_context_,
ResolveHostProxy(origin_to_preconnect.host()));
preconnect_manager_->Start(
main_frame_url,
{PreconnectRequest(origin_to_preconnect, 2, network_anonymization_key)},
TRAFFIC_ANNOTATION_FOR_TESTS);
EXPECT_CALL(
*mock_network_context_,
PreconnectSockets(
1, origin_to_preconnect.GetURL(),
network::mojom::CredentialsMode::kInclude, network_anonymization_key,
net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
_, _));
EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url));
mock_network_context_->CompleteHostLookup(origin_to_preconnect.host(),
network_anonymization_key, net::OK);
}
TEST_F(PreconnectManagerImplTest,
TestStartOneUrlPreconnectWithNetworkIsolationKey) {
GURL main_frame_url("http://google.com");
net::NetworkAnonymizationKey network_anonymization_key =
CreateNetworkAnonymizationKey(main_frame_url);
url::Origin origin_to_preconnect =
url::Origin::Create(GURL("http://cdn.google.com"));
EXPECT_CALL(*mock_delegate_, IsPreconnectEnabled()).WillOnce(Return(true));
EXPECT_CALL(
*mock_delegate_,
PreconnectInitiated(main_frame_url, origin_to_preconnect.GetURL()));
EXPECT_CALL(*mock_network_context_,
ResolveHostProxy(origin_to_preconnect.host()));
preconnect_manager_->Start(
main_frame_url,
{PreconnectRequest(origin_to_preconnect, 1, network_anonymization_key)},
TRAFFIC_ANNOTATION_FOR_TESTS);
EXPECT_CALL(
*mock_network_context_,
PreconnectSockets(
1, origin_to_preconnect.GetURL(),
network::mojom::CredentialsMode::kInclude, network_anonymization_key,
net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
_, _));
EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url));
mock_network_context_->CompleteHostLookup(origin_to_preconnect.host(),
network_anonymization_key, net::OK);
}
// Sends preconnect request for a webpage, and stops the request before
// all pertaining preconnect requests finish. Next, preconnect request
// for the same webpage is sent again. Verifies that all the preconnects
// related to the second request are dispatched on the network.
TEST_F(PreconnectManagerImplTest, TestStartOneUrlPreconnect_MultipleTimes) {
GURL main_frame_url("http://google.com");
net::NetworkAnonymizationKey network_anonymization_key =
CreateNetworkAnonymizationKey(main_frame_url);
size_t count = PreconnectManagerImpl::kMaxInflightPreresolves;
std::vector<PreconnectRequest> requests;
for (size_t i = 0; i < count + 1; ++i) {
// Exactly PreconnectManagerImpl::kMaxInflightPreresolves should be
// preresolved.
std::string url = base::StringPrintf("http://cdn%" PRIuS ".google.com", i);
requests.emplace_back(url::Origin::Create(GURL(url)), 1,
network_anonymization_key);
}
EXPECT_CALL(*mock_delegate_, IsPreconnectEnabled()).WillOnce(Return(true));
for (size_t i = 0; i < count; ++i) {
// Exactly PreconnectManagerImpl::kMaxInflightPreresolves should be
// initiated and preresolved.
EXPECT_CALL(
*mock_delegate_,
PreconnectInitiated(main_frame_url, requests[i].origin.GetURL()));
EXPECT_CALL(*mock_network_context_,
ResolveHostProxy(requests[i].origin.host()));
}
EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url));
preconnect_manager_->Start(main_frame_url, requests,
TRAFFIC_ANNOTATION_FOR_TESTS);
preconnect_manager_->Stop(main_frame_url);
for (size_t i = 0; i < count; ++i) {
mock_network_context_->CompleteHostLookup(
requests[i].origin.host(), network_anonymization_key, net::OK);
}
VerifyAndClearExpectations();
// Now, restart the preconnect request.
EXPECT_CALL(
*mock_delegate_,
PreconnectInitiated(main_frame_url, requests.back().origin.GetURL()));
EXPECT_CALL(
*mock_network_context_,
PreconnectSockets(
1, requests.back().origin.GetURL(),
network::mojom::CredentialsMode::kInclude, network_anonymization_key,
net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
_, _));
EXPECT_CALL(*mock_network_context_,
ResolveHostProxy(requests.back().origin.host()));
for (size_t i = 0; i < count; ++i) {
EXPECT_CALL(
*mock_delegate_,
PreconnectInitiated(main_frame_url, requests[i].origin.GetURL()));
EXPECT_CALL(*mock_network_context_,
PreconnectSockets(1, requests[i].origin.GetURL(),
network::mojom::CredentialsMode::kInclude,
network_anonymization_key,
net::MutableNetworkTrafficAnnotationTag(
TRAFFIC_ANNOTATION_FOR_TESTS),
_, _));
EXPECT_CALL(*mock_network_context_,
ResolveHostProxy(requests[i].origin.host()));
}
EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url));
EXPECT_CALL(*mock_delegate_, IsPreconnectEnabled()).WillOnce(Return(true));
preconnect_manager_->Start(main_frame_url, requests,
TRAFFIC_ANNOTATION_FOR_TESTS);
for (size_t i = 0; i < count + 1; ++i) {
mock_network_context_->CompleteHostLookup(
requests[i].origin.host(), network_anonymization_key, net::OK);
}
}
// Sends preconnect request for two webpages, and stops one request before
// all pertaining preconnect requests finish. Next, preconnect request
// for the same webpage is sent again. Verifies that all the preconnects
// related to the second request are dispatched on the network.
TEST_F(PreconnectManagerImplTest,
TestTwoConcurrentMainFrameUrls_MultipleTimes) {
GURL main_frame_url_1("http://google.com");
net::NetworkAnonymizationKey network_anonymization_key =
CreateNetworkAnonymizationKey(main_frame_url_1);
size_t count = PreconnectManagerImpl::kMaxInflightPreresolves;
std::vector<PreconnectRequest> requests;
for (size_t i = 0; i < count + 1; ++i) {
std::string url = base::StringPrintf("http://cdn%" PRIuS ".google.com", i);
requests.emplace_back(url::Origin::Create(GURL(url)), 1,
network_anonymization_key);
}
// This is same origin to `main_frame_url_1` because that ensures both URLs
// would, in real usage, have the same NetworkAnonymizationKey.
GURL main_frame_url_2("http://google.com/2");
EXPECT_CALL(*mock_delegate_, IsPreconnectEnabled())
.WillRepeatedly(Return(true));
EXPECT_CALL(
*mock_delegate_,
PreconnectInitiated(main_frame_url_1, requests[0].origin.GetURL()));
EXPECT_CALL(
*mock_delegate_,
PreconnectInitiated(main_frame_url_1, requests[1].origin.GetURL()));
EXPECT_CALL(*mock_delegate_,
PreconnectInitiated(main_frame_url_2,
requests[count - 1].origin.GetURL()));
for (size_t i = 0; i < count; ++i) {
EXPECT_CALL(*mock_network_context_,
ResolveHostProxy(requests[i].origin.host()));
}
EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url_1));
for (size_t i = 0; i < count - 1; ++i) {
EXPECT_CALL(*mock_network_context_,
PreconnectSockets(1, requests[i].origin.GetURL(),
network::mojom::CredentialsMode::kInclude,
network_anonymization_key,
net::MutableNetworkTrafficAnnotationTag(
TRAFFIC_ANNOTATION_FOR_TESTS),
_, _));
}
preconnect_manager_->Start(
main_frame_url_1,
std::vector<PreconnectRequest>(requests.begin(),
requests.begin() + count - 1),
TRAFFIC_ANNOTATION_FOR_TESTS);
preconnect_manager_->Start(main_frame_url_2,
std::vector<PreconnectRequest>(
requests.begin() + count - 1, requests.end()),
TRAFFIC_ANNOTATION_FOR_TESTS);
preconnect_manager_->Stop(main_frame_url_2);
for (size_t i = 0; i < count - 1; ++i) {
mock_network_context_->CompleteHostLookup(
requests[i].origin.host(), network_anonymization_key, net::OK);
}
EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url_2));
// Preconnect to |requests[count-1].origin| finishes after |main_frame_url_2|
// is stopped. Finishing of |requests[count-1].origin| should cause preconnect
// manager to clear all internal state related to |main_frame_url_2|.
mock_network_context_->CompleteHostLookup(requests[count - 1].origin.host(),
network_anonymization_key, net::OK);
VerifyAndClearExpectations();
// Now, restart the preconnect request.
EXPECT_CALL(*mock_delegate_, IsPreconnectEnabled()).WillOnce(Return(true));
EXPECT_CALL(*mock_delegate_,
PreconnectInitiated(main_frame_url_2,
requests[count - 1].origin.GetURL()));
EXPECT_CALL(*mock_network_context_,
ResolveHostProxy(requests[count - 1].origin.host()));
EXPECT_CALL(
*mock_delegate_,
PreconnectInitiated(main_frame_url_2, requests[count].origin.GetURL()));
EXPECT_CALL(*mock_network_context_,
ResolveHostProxy(requests[count].origin.host()));
EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url_2));
// Since state related to |main_frame_url_2| has been cleared,
// re-issuing a request for connect to |main_frame_url_2| should be
// successful.
preconnect_manager_->Start(main_frame_url_2,
std::vector<PreconnectRequest>(
requests.begin() + count - 1, requests.end()),
TRAFFIC_ANNOTATION_FOR_TESTS);
EXPECT_CALL(
*mock_network_context_,
PreconnectSockets(
1, requests[count - 1].origin.GetURL(),
network::mojom::CredentialsMode::kInclude, network_anonymization_key,
net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
_, _));
EXPECT_CALL(
*mock_network_context_,
PreconnectSockets(
1, requests[count].origin.GetURL(),
network::mojom::CredentialsMode::kInclude, network_anonymization_key,
net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
_, _));
mock_network_context_->CompleteHostLookup(requests[count - 1].origin.host(),
network_anonymization_key, net::OK);
mock_network_context_->CompleteHostLookup(requests[count].origin.host(),
network_anonymization_key, net::OK);
}
// Starts preconnect request for two webpages. The preconnect request for the
// second webpage is cancelled after one of its associated preconnect request
// goes in-flight.
// Verifies that if (i) Preconnect for a webpage is cancelled then its state is
// cleared after its associated in-flight requests finish; and, (ii) If the
// preconnect for that webpage is requested again, then
// the pertaining requests are dispatched to the network.
TEST_F(PreconnectManagerImplTest,
TestStartOneUrlPreconnect_MultipleTimes_CancelledAfterInFlight) {
GURL main_frame_url_1("http://google1.com");
net::NetworkAnonymizationKey network_anonymization_key_1 =
CreateNetworkAnonymizationKey(main_frame_url_1);
size_t count = PreconnectManagerImpl::kMaxInflightPreresolves;
std::vector<PreconnectRequest> requests;
EXPECT_CALL(*mock_delegate_, IsPreconnectEnabled())
.WillRepeatedly(Return(true));
for (size_t i = 0; i < count - 1; ++i) {
std::string url =
base::StringPrintf("http://hanging.cdn%" PRIuS ".google.com", i);
requests.emplace_back(url::Origin::Create(GURL(url)), 1,
network_anonymization_key_1);
// Although it hangs, the requests should still be initiated.
EXPECT_CALL(*mock_delegate_,
PreconnectInitiated(main_frame_url_1, GURL(url)));
}
mock_network_context_->SetHangingHostsFromPreconnectRequests(requests);
// Preconnect requests to |requests| would hang.
preconnect_manager_->Start(main_frame_url_1, requests,
TRAFFIC_ANNOTATION_FOR_TESTS);
GURL main_frame_url_2("http://google2.com");
net::NetworkAnonymizationKey network_anonymization_key_2 =
CreateNetworkAnonymizationKey(main_frame_url_2);
url::Origin origin_to_preconnect_1 =
url::Origin::Create(GURL("http://cdn.google1.com"));
url::Origin origin_to_preconnect_2 =
url::Origin::Create(GURL("http://cdn.google2.com"));
EXPECT_CALL(
*mock_delegate_,
PreconnectInitiated(main_frame_url_2, origin_to_preconnect_1.GetURL()));
EXPECT_CALL(*mock_network_context_,
ResolveHostProxy(origin_to_preconnect_1.host()));
// Starting and stopping preconnect request for |main_frame_url_2|
// should still dispatch the request for |origin_to_preconnect_1| on the
// network.
preconnect_manager_->Start(main_frame_url_2,
{PreconnectRequest(origin_to_preconnect_1, 1,
network_anonymization_key_2),
PreconnectRequest(origin_to_preconnect_2, 1,
network_anonymization_key_2)},
TRAFFIC_ANNOTATION_FOR_TESTS);
// preconnect request for |origin_to_preconnect_1| is still in-flight and
// Stop() is called on the associated webpage.
preconnect_manager_->Stop(main_frame_url_2);
VerifyAndClearExpectations();
EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url_2));
mock_network_context_->CompleteHostLookup(
origin_to_preconnect_1.host(), network_anonymization_key_2, net::OK);
VerifyAndClearExpectations();
// Request preconnect for |main_frame_url_2| again.
EXPECT_CALL(*mock_delegate_, IsPreconnectEnabled()).WillOnce(Return(true));
EXPECT_CALL(
*mock_delegate_,
PreconnectInitiated(main_frame_url_2, origin_to_preconnect_1.GetURL()));
EXPECT_CALL(*mock_network_context_,
ResolveHostProxy(origin_to_preconnect_1.host()));
EXPECT_CALL(
*mock_delegate_,
PreconnectInitiated(main_frame_url_2, origin_to_preconnect_2.GetURL()));
EXPECT_CALL(*mock_network_context_,
ResolveHostProxy(origin_to_preconnect_2.host()));
EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url_2));
EXPECT_CALL(*mock_network_context_,
PreconnectSockets(1, origin_to_preconnect_1.GetURL(),
network::mojom::CredentialsMode::kInclude,
network_anonymization_key_2,
net::MutableNetworkTrafficAnnotationTag(
TRAFFIC_ANNOTATION_FOR_TESTS),
_, _));
EXPECT_CALL(*mock_network_context_,
PreconnectSockets(1, origin_to_preconnect_2.GetURL(),
network::mojom::CredentialsMode::kInclude,
network_anonymization_key_2,
net::MutableNetworkTrafficAnnotationTag(
TRAFFIC_ANNOTATION_FOR_TESTS),
_, _));
preconnect_manager_->Start(main_frame_url_2,
{PreconnectRequest(origin_to_preconnect_1, 1,
network_anonymization_key_2),
PreconnectRequest(origin_to_preconnect_2, 1,
network_anonymization_key_2)},
TRAFFIC_ANNOTATION_FOR_TESTS);
mock_network_context_->CompleteHostLookup(
origin_to_preconnect_1.host(), network_anonymization_key_2, net::OK);
mock_network_context_->CompleteHostLookup(
origin_to_preconnect_2.host(), network_anonymization_key_2, net::OK);
}
// Sends a preconnect request again after the first request finishes. Verifies
// that the second preconnect request is dispatched to the network.
TEST_F(PreconnectManagerImplTest,
TestStartOneUrlPreconnect_MultipleTimes_LessThanThree) {
GURL main_frame_url("http://google.com");
net::NetworkAnonymizationKey network_anonymization_key =
CreateNetworkAnonymizationKey(main_frame_url);
url::Origin origin_to_preconnect_1 =
url::Origin::Create(GURL("http://cdn.google1.com"));
url::Origin origin_to_preconnect_2 =
url::Origin::Create(GURL("http://cdn.google2.com"));
EXPECT_CALL(*mock_delegate_, IsPreconnectEnabled()).WillOnce(Return(true));
EXPECT_CALL(
*mock_delegate_,
PreconnectInitiated(main_frame_url, origin_to_preconnect_1.GetURL()));
EXPECT_CALL(
*mock_delegate_,
PreconnectInitiated(main_frame_url, origin_to_preconnect_2.GetURL()));
EXPECT_CALL(*mock_network_context_,
ResolveHostProxy(origin_to_preconnect_1.host()));
EXPECT_CALL(*mock_network_context_,
ResolveHostProxy(origin_to_preconnect_2.host()));
preconnect_manager_->Start(
main_frame_url,
{PreconnectRequest(origin_to_preconnect_1, 1, network_anonymization_key),
PreconnectRequest(origin_to_preconnect_2, 1, network_anonymization_key)},
TRAFFIC_ANNOTATION_FOR_TESTS);
preconnect_manager_->Stop(main_frame_url);
EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url));
mock_network_context_->CompleteHostLookup(origin_to_preconnect_1.host(),
network_anonymization_key, net::OK);
mock_network_context_->CompleteHostLookup(origin_to_preconnect_2.host(),
network_anonymization_key, net::OK);
VerifyAndClearExpectations();
// Now, start the preconnect request again.
EXPECT_CALL(*mock_delegate_, IsPreconnectEnabled()).WillOnce(Return(true));
EXPECT_CALL(
*mock_delegate_,
PreconnectInitiated(main_frame_url, origin_to_preconnect_1.GetURL()));
EXPECT_CALL(
*mock_delegate_,
PreconnectInitiated(main_frame_url, origin_to_preconnect_2.GetURL()));
EXPECT_CALL(*mock_network_context_,
ResolveHostProxy(origin_to_preconnect_1.host()));
EXPECT_CALL(*mock_network_context_,
ResolveHostProxy(origin_to_preconnect_2.host()));
EXPECT_CALL(
*mock_network_context_,
PreconnectSockets(
1, origin_to_preconnect_1.GetURL(),
network::mojom::CredentialsMode::kInclude, network_anonymization_key,
net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
_, _));
EXPECT_CALL(
*mock_network_context_,
PreconnectSockets(
1, origin_to_preconnect_2.GetURL(),
network::mojom::CredentialsMode::kInclude, network_anonymization_key,
net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
_, _));
EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url));
preconnect_manager_->Start(
main_frame_url,
{PreconnectRequest(origin_to_preconnect_1, 1, network_anonymization_key),
PreconnectRequest(origin_to_preconnect_2, 1, network_anonymization_key)},
TRAFFIC_ANNOTATION_FOR_TESTS);
mock_network_context_->CompleteHostLookup(origin_to_preconnect_1.host(),
network_anonymization_key, net::OK);
mock_network_context_->CompleteHostLookup(origin_to_preconnect_2.host(),
network_anonymization_key, net::OK);
}
TEST_F(PreconnectManagerImplTest, TestStopOneUrlBeforePreconnect) {
GURL main_frame_url("http://google.com");
net::NetworkAnonymizationKey network_anonymization_key =
CreateNetworkAnonymizationKey(main_frame_url);
url::Origin origin_to_preconnect =
url::Origin::Create(GURL("http://cdn.google.com"));
EXPECT_CALL(*mock_delegate_, IsPreconnectEnabled())
.WillRepeatedly(Return(true));
EXPECT_CALL(
*mock_delegate_,
PreconnectInitiated(main_frame_url, origin_to_preconnect.GetURL()));
// Preconnect job isn't started before preresolve is completed asynchronously.
EXPECT_CALL(*mock_network_context_,
ResolveHostProxy(origin_to_preconnect.host()));
preconnect_manager_->Start(
main_frame_url,
{PreconnectRequest(origin_to_preconnect, 1, network_anonymization_key)},
TRAFFIC_ANNOTATION_FOR_TESTS);
// Stop all jobs for |main_frame_url| before we get the callback.
preconnect_manager_->Stop(main_frame_url);
EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url));
mock_network_context_->CompleteHostLookup(origin_to_preconnect.host(),
network_anonymization_key, net::OK);
}
TEST_F(PreconnectManagerImplTest, TestGetCallbackAfterDestruction) {
GURL main_frame_url("http://google.com");
net::NetworkAnonymizationKey network_anonymization_key =
CreateNetworkAnonymizationKey(main_frame_url);
url::Origin origin_to_preconnect =
url::Origin::Create(GURL("http://cdn.google.com"));
EXPECT_CALL(*mock_delegate_, IsPreconnectEnabled()).WillOnce(Return(true));
EXPECT_CALL(
*mock_delegate_,
PreconnectInitiated(main_frame_url, origin_to_preconnect.GetURL()));
EXPECT_CALL(*mock_network_context_,
ResolveHostProxy(origin_to_preconnect.host()));
preconnect_manager_->Start(
main_frame_url,
{PreconnectRequest(origin_to_preconnect, 1, network_anonymization_key)},
TRAFFIC_ANNOTATION_FOR_TESTS);
// Callback may outlive PreconnectManager but it shouldn't cause a crash.
preconnect_manager_ = nullptr;
mock_network_context_->CompleteHostLookup(origin_to_preconnect.host(),
network_anonymization_key, net::OK);
}
TEST_F(PreconnectManagerImplTest, TestUnqueuedPreresolvesCanceled) {
GURL main_frame_url("http://google.com");
net::NetworkAnonymizationKey network_anonymization_key =
CreateNetworkAnonymizationKey(main_frame_url);
size_t count = PreconnectManagerImpl::kMaxInflightPreresolves;
std::vector<PreconnectRequest> requests;
EXPECT_CALL(*mock_delegate_, IsPreconnectEnabled()).WillOnce(Return(true));
for (size_t i = 0; i < count; ++i) {
// Exactly PreconnectManagerImpl::kMaxInflightPreresolves should be
// preresolved.
std::string url = base::StringPrintf("http://cdn%" PRIuS ".google.com", i);
requests.emplace_back(url::Origin::Create(GURL(url)), 1,
network_anonymization_key);
EXPECT_CALL(*mock_delegate_,
PreconnectInitiated(main_frame_url, GURL(url)));
EXPECT_CALL(*mock_network_context_,
ResolveHostProxy(requests.back().origin.host()));
}
// This url shouldn't be preresolved.
requests.emplace_back(url::Origin::Create(GURL("http://no.preresolve.com")),
1, network_anonymization_key);
preconnect_manager_->Start(main_frame_url, requests,
TRAFFIC_ANNOTATION_FOR_TESTS);
preconnect_manager_->Stop(main_frame_url);
EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url));
for (size_t i = 0; i < count; ++i) {
mock_network_context_->CompleteHostLookup(
requests[i].origin.host(), network_anonymization_key, net::OK);
}
}
TEST_F(PreconnectManagerImplTest, TestQueueingMetricsRecorded) {
base::HistogramTester histogram_tester;
GURL main_frame_url("http://google.com");
net::NetworkAnonymizationKey network_anonymization_key =
CreateNetworkAnonymizationKey(main_frame_url);
size_t num_preresolves = PreconnectManagerImpl::kMaxInflightPreresolves;
std::vector<PreconnectRequest> requests;
EXPECT_CALL(*mock_delegate_, IsPreconnectEnabled()).WillOnce(Return(true));
for (size_t i = 0; i < num_preresolves; ++i) {
// Exactly PreconnectManagerImpl::kMaxInflightPreresolves should be
// preresolved.
std::string url = base::StringPrintf("http://cdn%" PRIuS ".google.com", i);
requests.emplace_back(url::Origin::Create(GURL(url)), 1,
network_anonymization_key);
EXPECT_CALL(*mock_delegate_,
PreconnectInitiated(main_frame_url, GURL(url)));
EXPECT_CALL(*mock_network_context_,
ResolveHostProxy(requests.back().origin.host()));
}
// This url shouldn't be preresolved.
requests.emplace_back(url::Origin::Create(GURL("http://no.preresolve.com")),
1, network_anonymization_key);
preconnect_manager_->Start(main_frame_url, requests,
TRAFFIC_ANNOTATION_FOR_TESTS);
// The number of queued jobs should have been recorded.
histogram_tester.ExpectUniqueSample(
"Navigation.Preconnect.PreresolveJobQueueLength", num_preresolves + 1, 1);
// Each job that was actually executed should have had its queueing time
// recorded.
histogram_tester.ExpectTotalCount(
"Navigation.Preconnect.PreresolveJobQueueingTime", num_preresolves);
preconnect_manager_->Stop(main_frame_url);
EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url));
for (size_t i = 0; i < num_preresolves; ++i) {
mock_network_context_->CompleteHostLookup(
requests[i].origin.host(), network_anonymization_key, net::OK);
}
}
TEST_F(PreconnectManagerImplTest, TestTwoConcurrentMainFrameUrls) {
GURL main_frame_url1("http://google.com");
net::NetworkAnonymizationKey network_anonymization_key1 =
CreateNetworkAnonymizationKey(main_frame_url1);
url::Origin origin_to_preconnect1 =
url::Origin::Create(GURL("http://cdn.google.com"));
GURL main_frame_url2("http://facebook.com");
net::NetworkAnonymizationKey network_anonymization_key2 =
CreateNetworkAnonymizationKey(main_frame_url2);
url::Origin origin_to_preconnect2 =
url::Origin::Create(GURL("http://cdn.facebook.com"));
EXPECT_CALL(*mock_delegate_, IsPreconnectEnabled())
.WillRepeatedly(Return(true));
EXPECT_CALL(
*mock_delegate_,
PreconnectInitiated(main_frame_url1, origin_to_preconnect1.GetURL()));
EXPECT_CALL(*mock_network_context_,
ResolveHostProxy(origin_to_preconnect1.host()));
EXPECT_CALL(
*mock_delegate_,
PreconnectInitiated(main_frame_url2, origin_to_preconnect2.GetURL()));
EXPECT_CALL(*mock_network_context_,
ResolveHostProxy(origin_to_preconnect2.host()));
preconnect_manager_->Start(
main_frame_url1,
{PreconnectRequest(origin_to_preconnect1, 1, network_anonymization_key1)},
TRAFFIC_ANNOTATION_FOR_TESTS);
preconnect_manager_->Start(
main_frame_url2,
{PreconnectRequest(origin_to_preconnect2, 1, network_anonymization_key2)},
TRAFFIC_ANNOTATION_FOR_TESTS);
// Check that the first url didn't block the second one.
Mock::VerifyAndClearExpectations(preconnect_manager_.get());
preconnect_manager_->Stop(main_frame_url2);
// Stopping the second url shouldn't stop the first one.
EXPECT_CALL(
*mock_network_context_,
PreconnectSockets(
1, origin_to_preconnect1.GetURL(),
network::mojom::CredentialsMode::kInclude, network_anonymization_key1,
net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
_, _));
EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url1));
mock_network_context_->CompleteHostLookup(
origin_to_preconnect1.host(), network_anonymization_key1, net::OK);
// No preconnect for the second url.
EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url2));
mock_network_context_->CompleteHostLookup(
origin_to_preconnect2.host(), network_anonymization_key2, net::OK);
}
// Checks that the PreconnectManager queues up preconnect requests for URLs
// with same host.
TEST_F(PreconnectManagerImplTest, TestTwoConcurrentSameHostMainFrameUrls) {
GURL main_frame_url1("http://google.com/search?query=cats");
net::NetworkAnonymizationKey network_anonymization_key1 =
CreateNetworkAnonymizationKey(main_frame_url1);
url::Origin origin_to_preconnect1 =
url::Origin::Create(GURL("http://cats.google.com"));
GURL main_frame_url2("http://google.com/search?query=dogs");
net::NetworkAnonymizationKey network_anonymization_key2 =
CreateNetworkAnonymizationKey(main_frame_url2);
url::Origin origin_to_preconnect2 =
url::Origin::Create(GURL("http://dogs.google.com"));
EXPECT_CALL(*mock_delegate_, IsPreconnectEnabled())
.WillRepeatedly(Return(true));
EXPECT_CALL(
*mock_delegate_,
PreconnectInitiated(main_frame_url1, origin_to_preconnect1.GetURL()));
EXPECT_CALL(*mock_network_context_,
ResolveHostProxy(origin_to_preconnect1.host()));
preconnect_manager_->Start(
main_frame_url1,
{PreconnectRequest(origin_to_preconnect1, 1, network_anonymization_key1)},
TRAFFIC_ANNOTATION_FOR_TESTS);
EXPECT_CALL(
*mock_delegate_,
PreconnectInitiated(main_frame_url2, origin_to_preconnect2.GetURL()));
EXPECT_CALL(*mock_network_context_,
ResolveHostProxy(origin_to_preconnect2.host()));
preconnect_manager_->Start(
main_frame_url2,
{PreconnectRequest(origin_to_preconnect2, 1, network_anonymization_key2)},
TRAFFIC_ANNOTATION_FOR_TESTS);
EXPECT_CALL(
*mock_network_context_,
PreconnectSockets(
1, origin_to_preconnect1.GetURL(),
network::mojom::CredentialsMode::kInclude, network_anonymization_key1,
net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
_, _));
EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url1));
mock_network_context_->CompleteHostLookup(
origin_to_preconnect1.host(), network_anonymization_key1, net::OK);
EXPECT_CALL(
*mock_network_context_,
PreconnectSockets(
1, origin_to_preconnect2.GetURL(),
network::mojom::CredentialsMode::kInclude, network_anonymization_key2,
net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
_, _));
EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url2));
mock_network_context_->CompleteHostLookup(
origin_to_preconnect2.host(), network_anonymization_key2, net::OK);
}
TEST_F(PreconnectManagerImplTest, TestStartPreresolveHost) {
GURL url("http://cdn.google.com/script.js");
GURL origin("http://cdn.google.com");
net::NetworkAnonymizationKey network_anonymization_key =
CreateNetworkAnonymizationKey(origin);
EXPECT_CALL(*mock_delegate_, IsPreconnectEnabled())
.WillRepeatedly(Return(true));
// PreconnectFinished shouldn't be called.
EXPECT_CALL(*mock_network_context_, ResolveHostProxy(origin.host()));
preconnect_manager_->StartPreresolveHost(
url, network_anonymization_key, TRAFFIC_ANNOTATION_FOR_TESTS,
/*storage_partition_config=*/nullptr);
mock_network_context_->CompleteHostLookup(origin.host(),
network_anonymization_key, net::OK);
// Non http url shouldn't be preresovled.
GURL non_http_url("file:///tmp/index.html");
preconnect_manager_->StartPreresolveHost(
non_http_url, network_anonymization_key, TRAFFIC_ANNOTATION_FOR_TESTS,
/*storage_partition_config=*/nullptr);
}
TEST_F(PreconnectManagerImplTest, TestStartPreresolveHostDisabled) {
GURL url("http://cdn.google.com/script.js");
GURL origin("http://cdn.google.com");
net::NetworkAnonymizationKey network_anonymization_key =
CreateNetworkAnonymizationKey(origin);
EXPECT_CALL(*mock_delegate_, IsPreconnectEnabled())
.WillOnce(testing::Return(false));
// mock_network_context_.ResolveHostProxy shouldn't be called. The StrictMock
// will raise an error if it happens.
preconnect_manager_->StartPreresolveHost(
url, network_anonymization_key, TRAFFIC_ANNOTATION_FOR_TESTS,
/*storage_partition_config=*/nullptr);
}
TEST_F(PreconnectManagerImplTest, TestStartPreresolveHosts) {
GURL cdn("http://cdn.google.com");
GURL fonts("http://fonts.google.com");
net::NetworkAnonymizationKey network_anonymization_key =
CreateNetworkAnonymizationKey(cdn);
EXPECT_CALL(*mock_delegate_, IsPreconnectEnabled()).WillOnce(Return(true));
EXPECT_CALL(*mock_network_context_, ResolveHostProxy(cdn.host()));
EXPECT_CALL(*mock_network_context_, ResolveHostProxy(fonts.host()));
preconnect_manager_->StartPreresolveHosts(
{cdn, fonts}, network_anonymization_key, TRAFFIC_ANNOTATION_FOR_TESTS,
/*storage_partition_config=*/nullptr);
mock_network_context_->CompleteHostLookup(cdn.host(),
network_anonymization_key, net::OK);
mock_network_context_->CompleteHostLookup(fonts.host(),
network_anonymization_key, net::OK);
}
TEST_F(PreconnectManagerImplTest, TestStartPreresolveHostsDisabled) {
GURL cdn("http://cdn.google.com");
GURL fonts("http://fonts.google.com");
net::NetworkAnonymizationKey network_anonymization_key =
CreateNetworkAnonymizationKey(cdn);
EXPECT_CALL(*mock_delegate_, IsPreconnectEnabled())
.WillOnce(testing::Return(false));
// mock_network_context_.ResolveHostProxy shouldn't be called. The StrictMock
// will raise an error if it happens.
preconnect_manager_->StartPreresolveHosts(
{cdn, fonts}, network_anonymization_key, TRAFFIC_ANNOTATION_FOR_TESTS,
/*storage_partition_config=*/nullptr);
}
TEST_F(PreconnectManagerImplTest, TestStartPreconnectUrl) {
GURL url("http://cdn.google.com/script.js");
net::NetworkAnonymizationKey network_anonymization_key =
CreateNetworkAnonymizationKey(url);
GURL origin("http://cdn.google.com");
bool allow_credentials = false;
EXPECT_CALL(*mock_delegate_, IsPreconnectEnabled())
.WillRepeatedly(Return(true));
EXPECT_CALL(*mock_network_context_, ResolveHostProxy(origin.host()));
preconnect_manager_->StartPreconnectUrl(
url, allow_credentials, network_anonymization_key,
TRAFFIC_ANNOTATION_FOR_TESTS,
/*storage_partition_config=*/nullptr,
/*keepalive_config=*/std::nullopt, mojo::NullRemote());
EXPECT_CALL(
*mock_network_context_,
PreconnectSockets(
1, origin, network::mojom::CredentialsMode::kOmit,
network_anonymization_key,
net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
_, _));
mock_network_context_->CompleteHostLookup(origin.host(),
network_anonymization_key, net::OK);
// Non http url shouldn't be preconnected.
GURL non_http_url("file:///tmp/index.html");
preconnect_manager_->StartPreconnectUrl(
non_http_url, allow_credentials, network_anonymization_key,
TRAFFIC_ANNOTATION_FOR_TESTS,
/*storage_partition_config=*/nullptr,
/*keepalive_config=*/std::nullopt, mojo::NullRemote());
}
TEST_F(PreconnectManagerImplTest, TestStartPreconnectUrlDisabled) {
GURL url("http://cdn.google.com/script.js");
net::NetworkAnonymizationKey network_anonymization_key =
CreateNetworkAnonymizationKey(url);
GURL origin("http://cdn.google.com");
bool allow_credentials = false;
EXPECT_CALL(*mock_delegate_, IsPreconnectEnabled())
.WillOnce(testing::Return(false));
// mock_network_context_.ResolveHostProxy shouldn't be called. The StrictMock
// will raise an error if it happens.
preconnect_manager_->StartPreconnectUrl(
url, allow_credentials, network_anonymization_key,
TRAFFIC_ANNOTATION_FOR_TESTS,
/*storage_partition_config=*/nullptr,
/*keepalive_config=*/std::nullopt, mojo::NullRemote());
}
TEST_F(PreconnectManagerImplTest,
TestStartPreconnectUrlWithNetworkIsolationKey) {
GURL url("http://cdn.google.com/script.js");
GURL origin("http://cdn.google.com");
bool allow_credentials = false;
net::SchemefulSite requesting_site =
net::SchemefulSite(GURL("http://foo.test"));
auto network_anonymization_key =
net::NetworkAnonymizationKey::CreateSameSite(requesting_site);
EXPECT_CALL(*mock_delegate_, IsPreconnectEnabled()).WillOnce(Return(true));
EXPECT_CALL(*mock_network_context_, ResolveHostProxy(origin.host()));
preconnect_manager_->StartPreconnectUrl(
url, allow_credentials, network_anonymization_key,
TRAFFIC_ANNOTATION_FOR_TESTS,
/*storage_partition_config=*/nullptr,
/*keepalive_config=*/std::nullopt, mojo::NullRemote());
EXPECT_CALL(
*mock_network_context_,
PreconnectSockets(
1, origin, network::mojom::CredentialsMode::kOmit,
network_anonymization_key,
net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
_, _));
mock_network_context_->CompleteHostLookup(origin.host(),
network_anonymization_key, net::OK);
}
TEST_F(PreconnectManagerImplTest, TestDetachedRequestHasHigherPriority) {
GURL main_frame_url("http://google.com");
net::NetworkAnonymizationKey network_anonymization_key =
CreateNetworkAnonymizationKey(main_frame_url);
size_t count = PreconnectManagerImpl::kMaxInflightPreresolves;
std::vector<PreconnectRequest> requests;
EXPECT_CALL(*mock_delegate_, IsPreconnectEnabled())
.WillRepeatedly(Return(true));
// Create enough asynchronous jobs to leave the last one in the queue.
for (size_t i = 0; i < count; ++i) {
std::string url = base::StringPrintf("http://cdn%" PRIuS ".google.com", i);
requests.emplace_back(url::Origin::Create(GURL(url)), 0,
network_anonymization_key);
EXPECT_CALL(*mock_delegate_,
PreconnectInitiated(main_frame_url, GURL(url)));
EXPECT_CALL(*mock_network_context_,
ResolveHostProxy(requests.back().origin.host()));
}
// This url will wait in the queue.
url::Origin queued_origin =
url::Origin::Create(GURL("http://fonts.google.com"));
requests.emplace_back(queued_origin, 0, network_anonymization_key);
preconnect_manager_->Start(main_frame_url, requests,
TRAFFIC_ANNOTATION_FOR_TESTS);
// This url should come to the front of the queue.
GURL detached_preresolve("http://ads.google.com");
preconnect_manager_->StartPreresolveHost(
detached_preresolve, network_anonymization_key,
TRAFFIC_ANNOTATION_FOR_TESTS,
/*storage_partition_config=*/nullptr);
Mock::VerifyAndClearExpectations(preconnect_manager_.get());
EXPECT_CALL(*mock_network_context_,
ResolveHostProxy(detached_preresolve.host()));
mock_network_context_->CompleteHostLookup(requests[0].origin.host(),
network_anonymization_key, net::OK);
Mock::VerifyAndClearExpectations(preconnect_manager_.get());
EXPECT_CALL(*mock_delegate_,
PreconnectInitiated(main_frame_url, queued_origin.GetURL()));
EXPECT_CALL(*mock_network_context_, ResolveHostProxy(queued_origin.host()));
mock_network_context_->CompleteHostLookup(detached_preresolve.host(),
network_anonymization_key, net::OK);
mock_network_context_->CompleteHostLookup(queued_origin.host(),
network_anonymization_key, net::OK);
EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url));
for (size_t i = 1; i < count; ++i) {
mock_network_context_->CompleteHostLookup(
requests[i].origin.host(), network_anonymization_key, net::OK);
}
}
TEST_F(PreconnectManagerImplTest, TestSuccessfulProxyLookup) {
mock_network_context_->EnableProxyTesting();
GURL main_frame_url("http://google.com");
net::NetworkAnonymizationKey network_anonymization_key =
CreateNetworkAnonymizationKey(main_frame_url);
url::Origin origin_to_preconnect =
url::Origin::Create(GURL("http://cdn.google.com"));
EXPECT_CALL(*mock_delegate_, IsPreconnectEnabled()).WillOnce(Return(true));
EXPECT_CALL(
*mock_delegate_,
PreconnectInitiated(main_frame_url, origin_to_preconnect.GetURL()));
preconnect_manager_->Start(
main_frame_url,
{PreconnectRequest(origin_to_preconnect, 1, network_anonymization_key)},
TRAFFIC_ANNOTATION_FOR_TESTS);
EXPECT_CALL(
*mock_network_context_,
PreconnectSockets(
1, origin_to_preconnect.GetURL(),
network::mojom::CredentialsMode::kInclude, network_anonymization_key,
net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
_, _));
EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url));
mock_network_context_->CompleteProxyLookup(origin_to_preconnect.GetURL(),
GetIndirectProxyInfo());
}
TEST_F(PreconnectManagerImplTest, TestStartDisabled) {
mock_network_context_->EnableProxyTesting();
GURL main_frame_url("http://google.com");
net::NetworkAnonymizationKey network_anonymization_key =
CreateNetworkAnonymizationKey(main_frame_url);
url::Origin origin_to_preconnect =
url::Origin::Create(GURL("http://cdn.google.com"));
EXPECT_CALL(*mock_delegate_, IsPreconnectEnabled())
.WillOnce(testing::Return(false));
// mock_delegate_.PreconnectInitiated shouldn't be called. The StrictMock
// will raise an error if it happens.
preconnect_manager_->Start(
main_frame_url,
{PreconnectRequest(origin_to_preconnect, 1, network_anonymization_key)},
TRAFFIC_ANNOTATION_FOR_TESTS);
}
TEST_F(PreconnectManagerImplTest,
TestSuccessfulHostLookupAfterProxyLookupFailure) {
mock_network_context_->EnableProxyTesting();
GURL main_frame_url("http://google.com");
net::NetworkAnonymizationKey network_anonymization_key =
CreateNetworkAnonymizationKey(main_frame_url);
url::Origin origin_to_preconnect =
url::Origin::Create(GURL("http://cdn.google.com"));
url::Origin origin_to_preconnect2 =
url::Origin::Create(GURL("http://ads.google.com"));
EXPECT_CALL(*mock_delegate_, IsPreconnectEnabled()).WillOnce(Return(true));
EXPECT_CALL(
*mock_delegate_,
PreconnectInitiated(main_frame_url, origin_to_preconnect.GetURL()));
EXPECT_CALL(
*mock_delegate_,
PreconnectInitiated(main_frame_url, origin_to_preconnect2.GetURL()));
preconnect_manager_->Start(
main_frame_url,
{PreconnectRequest(origin_to_preconnect, 1, network_anonymization_key),
PreconnectRequest(origin_to_preconnect2, 1, network_anonymization_key)},
TRAFFIC_ANNOTATION_FOR_TESTS);
EXPECT_CALL(*mock_network_context_,
ResolveHostProxy(origin_to_preconnect.host()));
EXPECT_CALL(*mock_network_context_,
ResolveHostProxy(origin_to_preconnect2.host()));
// First URL uses direct connection.
mock_network_context_->CompleteProxyLookup(origin_to_preconnect.GetURL(),
GetDirectProxyInfo());
// Second URL proxy lookup failed.
mock_network_context_->CompleteProxyLookup(origin_to_preconnect2.GetURL(),
std::nullopt);
Mock::VerifyAndClearExpectations(mock_network_context_.get());
EXPECT_CALL(
*mock_network_context_,
PreconnectSockets(
1, origin_to_preconnect.GetURL(),
network::mojom::CredentialsMode::kInclude, network_anonymization_key,
net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
_, _));
EXPECT_CALL(
*mock_network_context_,
PreconnectSockets(
1, origin_to_preconnect2.GetURL(),
network::mojom::CredentialsMode::kInclude, network_anonymization_key,
net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
_, _));
EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url));
mock_network_context_->CompleteHostLookup(origin_to_preconnect.host(),
network_anonymization_key, net::OK);
mock_network_context_->CompleteHostLookup(origin_to_preconnect2.host(),
network_anonymization_key, net::OK);
}
TEST_F(PreconnectManagerImplTest, TestBothProxyAndHostLookupFailed) {
mock_network_context_->EnableProxyTesting();
GURL main_frame_url("http://google.com");
net::NetworkAnonymizationKey network_anonymization_key =
CreateNetworkAnonymizationKey(main_frame_url);
url::Origin origin_to_preconnect =
url::Origin::Create(GURL("http://cdn.google.com"));
EXPECT_CALL(*mock_delegate_, IsPreconnectEnabled()).WillOnce(Return(true));
EXPECT_CALL(
*mock_delegate_,
PreconnectInitiated(main_frame_url, origin_to_preconnect.GetURL()));
preconnect_manager_->Start(
main_frame_url,
{PreconnectRequest(origin_to_preconnect, 1, network_anonymization_key)},
TRAFFIC_ANNOTATION_FOR_TESTS);
EXPECT_CALL(*mock_network_context_,
ResolveHostProxy(origin_to_preconnect.host()));
mock_network_context_->CompleteProxyLookup(origin_to_preconnect.GetURL(),
std::nullopt);
Mock::VerifyAndClearExpectations(mock_network_context_.get());
EXPECT_CALL(*mock_delegate_, PreconnectFinishedProxy(main_frame_url));
mock_network_context_->CompleteHostLookup(origin_to_preconnect.host(),
network_anonymization_key,
net::ERR_NAME_NOT_RESOLVED);
}
} // namespace content