blob: afe685592e9c52a2241e3e008f1928952910cc27 [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 <atomic>
#include <optional>
#include <string>
#include <unordered_set>
#include <vector>
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/memory/raw_ptr.h"
#include "base/path_service.h"
#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/bind.h"
#include "base/threading/thread_restrictions.h"
#include "base/uuid.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/content_settings/cookie_settings_factory.h"
#include "chrome/browser/net/profile_network_context_service.h"
#include "chrome/browser/net/profile_network_context_service_factory.h"
#include "chrome/browser/net/system_network_context_manager.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/safe_browsing/safe_browsing_service.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "components/content_settings/core/browser/cookie_settings.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/pref_names.h"
#include "components/embedder_support/switches.h"
#include "components/embedder_support/user_agent_utils.h"
#include "components/language/core/browser/pref_names.h"
#include "components/network_session_configurator/common/network_switches.h"
#include "components/policy/core/browser/browser_policy_connector.h"
#include "components/policy/core/common/mock_configuration_policy_provider.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/policy_constants.h"
#include "components/prefs/pref_service.h"
#include "components/proxy_config/proxy_config_dictionary.h"
#include "components/proxy_config/proxy_config_pref_names.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/network_service_instance.h"
#include "content/public/browser/network_service_util.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/storage_partition_config.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/url_constants.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/simple_url_loader_test_helper.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/system/data_pipe_utils.h"
#include "net/base/address_list.h"
#include "net/base/filename_util.h"
#include "net/base/host_port_pair.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
#include "net/cookies/canonical_cookie.h"
#include "net/cookies/cookie_options.h"
#include "net/dns/mock_host_resolver.h"
#include "net/dns/public/resolve_error_info.h"
#include "net/http/http_response_headers.h"
#include "net/reporting/reporting_policy.h"
#include "net/ssl/ssl_config.h"
#include "net/ssl/ssl_server_config.h"
#include "net/test/embedded_test_server/controllable_http_response.h"
#include "net/test/embedded_test_server/default_handlers.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/embedded_test_server_connection_listener.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "net/test/gtest_util.h"
#include "net/test/spawned_test_server/spawned_test_server.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "net/url_request/referrer_policy.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/cpp/network_connection_tracker.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "services/network/public/mojom/cookie_manager.mojom.h"
#include "services/network/public/mojom/host_resolver.mojom.h"
#include "services/network/public/mojom/network_service.mojom.h"
#include "services/network/public/mojom/url_loader.mojom.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
#include "services/network/test/test_dns_util.h"
#include "services/network/test/test_url_loader_client.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
#if BUILDFLAG(IS_MAC)
#include "base/mac/mac_util.h"
#endif
#if BUILDFLAG(ENABLE_EXTENSIONS)
#include "chrome/browser/extensions/chrome_test_extension_loader.h"
#include "extensions/browser/browsertest_util.h"
#include "extensions/test/test_extension_dir.h"
#endif
namespace {
constexpr char kHostname[] = "foo.test";
constexpr char kAddress[] = "5.8.13.21";
const char kCacheRandomPath[] = "/cacherandom";
// Path using a ControllableHttpResponse that's part of the test fixture.
const char kControllablePath[] = "/controllable";
enum class NetworkServiceState {
kEnabled,
// Similar to |kEnabled|, but will simulate a crash and run tests again the
// restarted Network Service process.
kRestarted,
};
enum class NetworkContextType {
kSystem,
kSafeBrowsing,
kProfile,
kIncognitoProfile,
kOnDiskApp,
kInMemoryApp,
kOnDiskAppWithIncognitoProfile,
};
// This list should be kept in sync with the NetworkContextType enum.
const NetworkContextType kNetworkContextTypes[] = {
NetworkContextType::kSystem,
NetworkContextType::kSafeBrowsing,
NetworkContextType::kProfile,
NetworkContextType::kIncognitoProfile,
NetworkContextType::kOnDiskApp,
NetworkContextType::kInMemoryApp,
NetworkContextType::kOnDiskAppWithIncognitoProfile,
};
struct TestCase {
NetworkServiceState network_service_state;
NetworkContextType network_context_type;
};
// Waits for the network connection type to be the specified value.
class ConnectionTypeWaiter
: public network::NetworkConnectionTracker::NetworkConnectionObserver {
public:
ConnectionTypeWaiter() : tracker_(content::GetNetworkConnectionTracker()) {
tracker_->AddNetworkConnectionObserver(this);
}
~ConnectionTypeWaiter() override {
tracker_->RemoveNetworkConnectionObserver(this);
}
void Wait(network::mojom::ConnectionType expected_type) {
auto current_type = network::mojom::ConnectionType::CONNECTION_UNKNOWN;
for (;;) {
network::NetworkConnectionTracker::ConnectionTypeCallback callback =
base::BindOnce(&ConnectionTypeWaiter::OnConnectionChanged,
base::Unretained(this));
if (tracker_->GetConnectionType(&current_type, std::move(callback)) &&
current_type == expected_type)
break;
run_loop_ = std::make_unique<base::RunLoop>(
base::RunLoop::Type::kNestableTasksAllowed);
run_loop_->Run();
}
}
private:
// network::NetworkConnectionTracker::NetworkConnectionObserver:
void OnConnectionChanged(network::mojom::ConnectionType type) override {
if (run_loop_)
run_loop_->Quit();
}
raw_ptr<network::NetworkConnectionTracker> tracker_;
std::unique_ptr<base::RunLoop> run_loop_;
};
// Tests the system, profile, and incognito profile NetworkContexts.
class NetworkContextConfigurationBrowserTest
: public InProcessBrowserTest,
public testing::WithParamInterface<TestCase> {
public:
// Backing storage types that can used for various things (HTTP cache,
// cookies, etc).
enum class StorageType {
kNone,
kMemory,
kDisk,
};
enum class CookieType {
kFirstParty,
kThirdParty,
};
enum class CookiePersistenceType {
kSession,
kPersistent,
};
NetworkContextConfigurationBrowserTest()
: https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
// TODO(crbug.com/334954143) Fix the tests when turning on the reduce
// accept-language feature.
scoped_feature_list_.InitWithFeatures(
{}, {network::features::kReduceAcceptLanguage});
// Have to get a port before setting up the command line, but can only set
// up the connection listener after there's a main thread, so can't start
// the test server here.
EXPECT_TRUE(embedded_test_server()->InitializeAndListen());
EXPECT_TRUE(https_server()->InitializeAndListen());
}
// Returns a cacheable response (10 hours) that is some random text.
static std::unique_ptr<net::test_server::HttpResponse> HandleCacheRandom(
const net::test_server::HttpRequest& request) {
if (request.relative_url != kCacheRandomPath)
return nullptr;
std::unique_ptr<net::test_server::BasicHttpResponse> response =
std::make_unique<net::test_server::BasicHttpResponse>();
response->set_content(base::Uuid::GenerateRandomV4().AsLowercaseString());
response->set_content_type("text/plain");
response->AddCustomHeader("Cache-Control", "max-age=60000");
return std::move(response);
}
NetworkContextConfigurationBrowserTest(
const NetworkContextConfigurationBrowserTest&) = delete;
NetworkContextConfigurationBrowserTest& operator=(
const NetworkContextConfigurationBrowserTest&) = delete;
~NetworkContextConfigurationBrowserTest() override {}
void SetUpInProcessBrowserTestFixture() override {
provider_.SetDefaultReturns(
/*is_initialization_complete_return=*/true,
/*is_first_policy_load_complete_return=*/true);
policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_);
}
void SetUpOnMainThread() override {
// Used in a bunch of proxy tests. Should not resolve.
host_resolver()->AddSimulatedFailure("does.not.resolve.test");
host_resolver()->AddRule(kHostname, kAddress);
controllable_http_response_ =
std::make_unique<net::test_server::ControllableHttpResponse>(
embedded_test_server(), kControllablePath);
embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
&NetworkContextConfigurationBrowserTest::HandleCacheRandom));
embedded_test_server()->StartAcceptingConnections();
net::test_server::RegisterDefaultHandlers(https_server());
https_server()->StartAcceptingConnections();
if (is_incognito())
incognito_ = CreateIncognitoBrowser();
SimulateNetworkServiceCrashIfNecessary();
#if BUILDFLAG(IS_CHROMEOS_ASH)
// On ChromeOS the connection type comes from a fake Shill service, which
// is configured with a fake ethernet connection asynchronously. Wait for
// the connection type to be available to avoid getting notified of the
// connection change halfway through the test.
ConnectionTypeWaiter().Wait(
network::mojom::ConnectionType::CONNECTION_ETHERNET);
#endif
}
// Returns true if the NetworkContext being tested is associated with an
// incognito profile.
bool is_incognito() const {
return GetParam().network_context_type ==
NetworkContextType::kIncognitoProfile ||
GetParam().network_context_type ==
NetworkContextType::kOnDiskAppWithIncognitoProfile;
}
void TearDownOnMainThread() override {
// Have to destroy this before the main message loop is torn down. Need to
// leave the embedded test server up for tests that use
// |live_during_shutdown_simple_loader_|. It's safe to destroy the
// ControllableHttpResponse before the test server.
controllable_http_response_.reset();
}
// Returns, as a string, a PAC script that will use the EmbeddedTestServer as
// a proxy.
std::string GetPacScript() const {
return base::StringPrintf(
"function FindProxyForURL(url, host){ return 'PROXY %s;' }",
net::HostPortPair::FromURL(embedded_test_server()->base_url())
.ToString()
.c_str());
}
net::EmbeddedTestServer* https_server() { return &https_server_; }
content::StoragePartition* GetStoragePartition() {
return GetStoragePartitionForContextType(GetParam().network_context_type);
}
content::StoragePartition* GetStoragePartitionForContextType(
NetworkContextType network_context_type) {
const auto kOnDiskConfig = content::StoragePartitionConfig::Create(
browser()->profile(), "foo", /*partition_name=*/"",
/*in_memory=*/false);
const auto kInMemoryConfig = content::StoragePartitionConfig::Create(
browser()->profile(), "foo", /*partition_name=*/"", /*in_memory=*/true);
switch (network_context_type) {
case NetworkContextType::kSystem:
case NetworkContextType::kSafeBrowsing:
NOTREACHED() << "Network context has no storage partition";
return nullptr;
case NetworkContextType::kProfile:
return browser()->profile()->GetDefaultStoragePartition();
case NetworkContextType::kIncognitoProfile:
DCHECK(incognito_);
return incognito_->profile()->GetDefaultStoragePartition();
case NetworkContextType::kOnDiskApp:
return browser()->profile()->GetStoragePartition(kOnDiskConfig);
case NetworkContextType::kInMemoryApp:
return browser()->profile()->GetStoragePartition(kInMemoryConfig);
case NetworkContextType::kOnDiskAppWithIncognitoProfile: {
DCHECK(incognito_);
// Note: Even though we are requesting an on-disk config, the function
// will return an in-memory config because incognito profiles are not
// supposed to to use on-disk storage.
const auto kIncognitoConfig = content::StoragePartitionConfig::Create(
incognito_->profile(), "foo", /*partition_name=*/"",
/*in_memory=*/false);
DCHECK(kIncognitoConfig.in_memory());
return incognito_->profile()->GetStoragePartition(kIncognitoConfig);
}
}
NOTREACHED();
return nullptr;
}
network::mojom::URLLoaderFactory* loader_factory() {
return GetLoaderFactoryForContextType(GetParam().network_context_type);
}
network::mojom::URLLoaderFactory* GetLoaderFactoryForContextType(
NetworkContextType network_context_type) {
switch (network_context_type) {
case NetworkContextType::kSystem:
return g_browser_process->system_network_context_manager()
->GetURLLoaderFactory();
case NetworkContextType::kSafeBrowsing:
return g_browser_process->safe_browsing_service()
->GetURLLoaderFactory(browser()->profile())
.get();
case NetworkContextType::kProfile:
case NetworkContextType::kIncognitoProfile:
case NetworkContextType::kOnDiskApp:
case NetworkContextType::kInMemoryApp:
case NetworkContextType::kOnDiskAppWithIncognitoProfile:
return GetStoragePartitionForContextType(network_context_type)
->GetURLLoaderFactoryForBrowserProcess()
.get();
}
NOTREACHED();
return nullptr;
}
network::mojom::NetworkContext* network_context() {
return GetNetworkContextForContextType(GetParam().network_context_type);
}
network::mojom::NetworkContext* GetNetworkContextForContextType(
NetworkContextType network_context_type) {
switch (network_context_type) {
case NetworkContextType::kSystem:
return g_browser_process->system_network_context_manager()
->GetContext();
case NetworkContextType::kSafeBrowsing:
return g_browser_process->safe_browsing_service()->GetNetworkContext(
browser()->profile());
case NetworkContextType::kProfile:
case NetworkContextType::kIncognitoProfile:
case NetworkContextType::kOnDiskApp:
case NetworkContextType::kInMemoryApp:
case NetworkContextType::kOnDiskAppWithIncognitoProfile:
return GetStoragePartitionForContextType(network_context_type)
->GetNetworkContext();
}
NOTREACHED();
return nullptr;
}
StorageType GetHttpCacheType() const {
switch (GetParam().network_context_type) {
case NetworkContextType::kSystem:
case NetworkContextType::kSafeBrowsing:
return StorageType::kNone;
case NetworkContextType::kProfile:
case NetworkContextType::kOnDiskApp:
return StorageType::kDisk;
case NetworkContextType::kIncognitoProfile:
case NetworkContextType::kInMemoryApp:
case NetworkContextType::kOnDiskAppWithIncognitoProfile:
return StorageType::kMemory;
}
NOTREACHED();
return StorageType::kNone;
}
StorageType GetCookieStorageType() const {
switch (GetParam().network_context_type) {
case NetworkContextType::kSystem:
case NetworkContextType::kIncognitoProfile:
case NetworkContextType::kInMemoryApp:
case NetworkContextType::kOnDiskAppWithIncognitoProfile:
return StorageType::kMemory;
case NetworkContextType::kSafeBrowsing:
case NetworkContextType::kProfile:
case NetworkContextType::kOnDiskApp:
return StorageType::kDisk;
}
NOTREACHED();
return StorageType::kNone;
}
// Returns the pref service with most prefs related to the NetworkContext
// being tested.
PrefService* GetPrefService() {
switch (GetParam().network_context_type) {
case NetworkContextType::kSystem:
return g_browser_process->local_state();
case NetworkContextType::kSafeBrowsing:
case NetworkContextType::kProfile:
case NetworkContextType::kInMemoryApp:
case NetworkContextType::kOnDiskApp:
return browser()->profile()->GetPrefs();
case NetworkContextType::kIncognitoProfile:
case NetworkContextType::kOnDiskAppWithIncognitoProfile:
// Incognito actually uses the non-incognito prefs, so this should end
// up being the same pref store as in the KProfile case.
return browser()
->profile()
->GetPrimaryOTRProfile(/*create_if_needed=*/true)
->GetPrefs();
}
}
// Sets the proxy preference on a PrefService based on the NetworkContextType,
// and waits for it to be applied.
void SetProxyPref(const net::HostPortPair& host_port_pair) {
GetPrefService()->SetDict(proxy_config::prefs::kProxy,
ProxyConfigDictionary::CreateFixedServers(
host_port_pair.ToString(), std::string()));
// Wait for the new ProxyConfig to be passed over the pipe. Needed because
// Mojo doesn't guarantee ordering of events on different Mojo pipes, and
// requests are sent on a separate pipe from ProxyConfigs.
switch (GetParam().network_context_type) {
case NetworkContextType::kSystem:
case NetworkContextType::kSafeBrowsing:
g_browser_process->system_network_context_manager()
->FlushProxyConfigMonitorForTesting();
break;
case NetworkContextType::kProfile:
case NetworkContextType::kInMemoryApp:
case NetworkContextType::kOnDiskApp:
ProfileNetworkContextServiceFactory::GetForContext(browser()->profile())
->FlushProxyConfigMonitorForTesting();
break;
case NetworkContextType::kIncognitoProfile:
case NetworkContextType::kOnDiskAppWithIncognitoProfile:
ProfileNetworkContextServiceFactory::GetForContext(
browser()->profile()->GetPrimaryOTRProfile(
/*create_if_needed=*/true))
->FlushProxyConfigMonitorForTesting();
break;
}
}
// Sends a request and expects it to be handled by embedded_test_server()
// acting as a proxy;
void TestProxyConfigured(bool expect_success) {
std::unique_ptr<network::ResourceRequest> request =
std::make_unique<network::ResourceRequest>();
// This URL should be directed to the test server because of the proxy.
request->url = GURL("http://does.not.resolve.test:1872/echo");
content::SimpleURLLoaderTestHelper simple_loader_helper;
std::unique_ptr<network::SimpleURLLoader> simple_loader =
network::SimpleURLLoader::Create(std::move(request),
TRAFFIC_ANNOTATION_FOR_TESTS);
simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
loader_factory(), simple_loader_helper.GetCallbackDeprecated());
simple_loader_helper.WaitForCallback();
if (expect_success) {
EXPECT_EQ(net::OK, simple_loader->NetError());
ASSERT_TRUE(simple_loader_helper.response_body());
EXPECT_EQ(*simple_loader_helper.response_body(), "Echo");
} else {
EXPECT_EQ(net::ERR_NAME_NOT_RESOLVED, simple_loader->NetError());
ASSERT_FALSE(simple_loader_helper.response_body());
}
}
// Makes a request that hangs, and will live until browser shutdown.
void MakeLongLivedRequestThatHangsUntilShutdown() {
std::unique_ptr<network::ResourceRequest> request =
std::make_unique<network::ResourceRequest>();
request->url = embedded_test_server()->GetURL(kControllablePath);
live_during_shutdown_simple_loader_ = network::SimpleURLLoader::Create(
std::move(request), TRAFFIC_ANNOTATION_FOR_TESTS);
live_during_shutdown_simple_loader_helper_ =
std::make_unique<content::SimpleURLLoaderTestHelper>();
live_during_shutdown_simple_loader_
->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
loader_factory(), live_during_shutdown_simple_loader_helper_
->GetCallbackDeprecated());
// Don't actually care about controlling the response, just need to wait
// until it sees the request, to make sure that a URLRequest has been
// created to potentially leak. Since the |controllable_http_response_| is
// not used to send a response to the request, the request just hangs until
// the NetworkContext is destroyed (Or the test server is shut down, but the
// NetworkContext should be destroyed before that happens, in this test).
controllable_http_response_->WaitForRequest();
}
// Sends a request to the test server's echoheader URL and sets
// |header_value_out| to the value of the specified response header. Returns
// false if the request fails. If non-null, uses |request| to make the
// request, after setting its |url| value.
[[nodiscard]] bool FetchHeaderEcho(
const std::string& header_name,
std::string* header_value_out,
std::unique_ptr<network::ResourceRequest> request = nullptr) {
if (!request)
request = std::make_unique<network::ResourceRequest>();
request->url = embedded_test_server()->GetURL(
base::StrCat({"/echoheader?", header_name}));
content::SimpleURLLoaderTestHelper simple_loader_helper;
std::unique_ptr<network::SimpleURLLoader> simple_loader =
network::SimpleURLLoader::Create(std::move(request),
TRAFFIC_ANNOTATION_FOR_TESTS);
simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
loader_factory(), simple_loader_helper.GetCallbackDeprecated());
simple_loader_helper.WaitForCallback();
if (simple_loader_helper.response_body()) {
*header_value_out = *simple_loader_helper.response_body();
return true;
}
return false;
}
// |server| should be |TYPE_HTTPS| if the cookie is third-party, because
// SameSite=None cookies must be Secure.
void SetCookie(CookieType cookie_type,
CookiePersistenceType cookie_expiration_type,
net::EmbeddedTestServer* server) {
std::unique_ptr<network::ResourceRequest> request =
std::make_unique<network::ResourceRequest>();
std::string cookie_line = "cookie";
if (cookie_expiration_type == CookiePersistenceType::kPersistent)
cookie_line += ";max-age=3600";
// Third party (i.e. SameSite=None) cookies must be secure.
if (cookie_type == CookieType::kThirdParty)
cookie_line += ";SameSite=None;Secure";
request->url = server->GetURL("/set-cookie?" + cookie_line);
url::Origin origin;
if (cookie_type == CookieType::kThirdParty)
origin = url::Origin::Create(GURL("http://example.com"));
else
origin = url::Origin::Create(server->base_url());
request->site_for_cookies = net::SiteForCookies::FromOrigin(origin);
request->trusted_params = network::ResourceRequest::TrustedParams();
request->trusted_params->isolation_info =
net::IsolationInfo::CreateForInternalRequest(origin);
content::SimpleURLLoaderTestHelper simple_loader_helper;
std::unique_ptr<network::SimpleURLLoader> simple_loader =
network::SimpleURLLoader::Create(std::move(request),
TRAFFIC_ANNOTATION_FOR_TESTS);
simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
loader_factory(), simple_loader_helper.GetCallbackDeprecated());
simple_loader_helper.WaitForCallback();
EXPECT_EQ(net::OK, simple_loader->NetError());
}
void FlushNetworkInterface() {
switch (GetParam().network_context_type) {
case NetworkContextType::kSystem:
g_browser_process->system_network_context_manager()
->FlushNetworkInterfaceForTesting();
break;
case NetworkContextType::kSafeBrowsing:
g_browser_process->safe_browsing_service()
->FlushNetworkInterfaceForTesting(GetProfile());
break;
case NetworkContextType::kProfile:
case NetworkContextType::kIncognitoProfile:
case NetworkContextType::kInMemoryApp:
case NetworkContextType::kOnDiskApp:
case NetworkContextType::kOnDiskAppWithIncognitoProfile:
GetStoragePartition()->FlushNetworkInterfaceForTesting();
break;
}
}
Profile* GetProfile() {
switch (GetParam().network_context_type) {
case NetworkContextType::kSystem:
case NetworkContextType::kSafeBrowsing:
case NetworkContextType::kProfile:
case NetworkContextType::kInMemoryApp:
case NetworkContextType::kOnDiskApp:
return browser()->profile();
case NetworkContextType::kIncognitoProfile:
case NetworkContextType::kOnDiskAppWithIncognitoProfile:
DCHECK(incognito_);
return incognito_->profile();
}
}
// Gets all cookies for given URL, as a single string.
std::string GetCookies(const GURL& url) {
return GetCookiesForContextType(GetParam().network_context_type, url);
}
std::string GetCookiesForContextType(NetworkContextType network_context_type,
const GURL& url) {
std::string cookies;
base::RunLoop run_loop;
mojo::Remote<network::mojom::CookieManager> cookie_manager;
GetNetworkContextForContextType(network_context_type)
->GetCookieManager(cookie_manager.BindNewPipeAndPassReceiver());
cookie_manager->GetCookieList(
url, net::CookieOptions::MakeAllInclusive(),
net::CookiePartitionKeyCollection(),
base::BindOnce(
[](std::string* cookies_out, base::RunLoop* run_loop,
const net::CookieAccessResultList& cookies,
const net::CookieAccessResultList& excluded_cookies) {
*cookies_out = net::CanonicalCookie::BuildCookieLine(cookies);
run_loop->Quit();
},
&cookies, &run_loop));
run_loop.Run();
return cookies;
}
void ForEachOtherContext(
base::RepeatingCallback<void(NetworkContextType network_context_type)>
callback) {
// Create Incognito Profile if needed.
if (!incognito_)
incognito_ = CreateIncognitoBrowser();
// True if the |network_context_type| corresponding to GetParam() has been
// found. Used to verify that kNetworkContextTypes is kept up to date, and
// contains no duplicates.
bool found_matching_type = false;
for (const auto network_context_type : kNetworkContextTypes) {
if (network_context_type == GetParam().network_context_type) {
EXPECT_FALSE(found_matching_type);
found_matching_type = true;
continue;
}
callback.Run(network_context_type);
}
EXPECT_TRUE(found_matching_type);
}
bool IsRestartStateWithInProcessNetworkService() {
return GetParam().network_service_state ==
NetworkServiceState::kRestarted &&
content::IsInProcessNetworkService();
}
void UpdateChromePolicy(const policy::PolicyMap& policy_map) {
provider_.UpdateChromePolicy(policy_map);
}
private:
void SimulateNetworkServiceCrashIfNecessary() {
if (GetParam().network_service_state != NetworkServiceState::kRestarted ||
content::IsInProcessNetworkService()) {
return;
}
// Make sure |network_context()| is working as expected. Use '/echoheader'
// instead of '/echo' to avoid a disk_cache bug.
// See https://crbug.com/792255.
int net_error = content::LoadBasicRequest(
network_context(), embedded_test_server()->GetURL("/echoheader"),
net::LOAD_BYPASS_PROXY);
EXPECT_THAT(net_error, net::test::IsOk());
// Crash the NetworkService process. Existing interfaces should receive
// error notifications at some point.
SimulateNetworkServiceCrash();
// Flush the interface to make sure the error notification was received.
FlushNetworkInterface();
}
raw_ptr<Browser, AcrossTasksDanglingUntriaged> incognito_ = nullptr;
net::EmbeddedTestServer https_server_;
std::unique_ptr<net::test_server::ControllableHttpResponse>
controllable_http_response_;
testing::NiceMock<policy::MockConfigurationPolicyProvider> provider_;
// Used in tests that need a live request during browser shutdown.
std::unique_ptr<network::SimpleURLLoader> live_during_shutdown_simple_loader_;
std::unique_ptr<content::SimpleURLLoaderTestHelper>
live_during_shutdown_simple_loader_helper_;
base::test::ScopedFeatureList scoped_feature_list_;
};
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest,
SecureCookiesAllowedForChromeScheme) {
if (IsRestartStateWithInProcessNetworkService())
return;
// The system and SafeBrowsing network contexts don't support cookie options,
// including the special scheme-based rules.
bool system =
GetParam().network_context_type == NetworkContextType::kSystem ||
GetParam().network_context_type == NetworkContextType::kSafeBrowsing;
if (system)
return;
// Cookies are only allowed for chrome:// schemes requesting a secure origin,
// so create an HTTPS server.
net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
net::test_server::RegisterDefaultHandlers(&https_server);
ASSERT_TRUE(https_server.Start());
if (GetPrefService()->FindPreference(prefs::kCookieControlsMode)) {
GetPrefService()->SetInteger(
prefs::kCookieControlsMode,
static_cast<int>(
content_settings::CookieControlsMode::kBlockThirdParty));
}
SetCookie(CookieType::kFirstParty, CookiePersistenceType::kPersistent,
embedded_test_server());
std::unique_ptr<network::ResourceRequest> request =
std::make_unique<network::ResourceRequest>();
request->url = https_server.GetURL("/echoheader?Cookie");
url::Origin origin = url::Origin::Create(GURL(chrome::kChromeUIPrintURL));
request->site_for_cookies = net::SiteForCookies::FromOrigin(origin);
request->trusted_params = network::ResourceRequest::TrustedParams();
request->trusted_params->isolation_info =
net::IsolationInfo::CreateForInternalRequest(origin);
content::SimpleURLLoaderTestHelper simple_loader_helper;
std::unique_ptr<network::SimpleURLLoader> simple_loader =
network::SimpleURLLoader::Create(std::move(request),
TRAFFIC_ANNOTATION_FOR_TESTS);
simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
loader_factory(), simple_loader_helper.GetCallbackDeprecated());
simple_loader_helper.WaitForCallback();
EXPECT_EQ(200, simple_loader->ResponseInfo()->headers->response_code());
ASSERT_TRUE(simple_loader_helper.response_body());
EXPECT_EQ("cookie", *simple_loader_helper.response_body());
}
#if BUILDFLAG(ENABLE_EXTENSIONS)
std::unique_ptr<net::test_server::HttpResponse> EchoCookieHeader(
const net::test_server::HttpRequest& request) {
auto response = std::make_unique<net::test_server::BasicHttpResponse>();
auto it = request.headers.find("Cookie");
if (it != request.headers.end())
response->set_content(it->second);
response->AddCustomHeader("Access-Control-Allow-Origin", "*");
return response;
}
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest,
ThirdPartyCookiesAllowedForExtensions) {
if (IsRestartStateWithInProcessNetworkService())
return;
// Loading an extension only makes sense for profile contexts.
if (GetParam().network_context_type != NetworkContextType::kProfile &&
GetParam().network_context_type !=
NetworkContextType::kIncognitoProfile) {
return;
}
net::EmbeddedTestServer test_server;
net::test_server::RegisterDefaultHandlers(&test_server);
test_server.RegisterRequestHandler(base::BindRepeating(&EchoCookieHeader));
ASSERT_TRUE(test_server.Start());
GetPrefService()->SetInteger(
prefs::kCookieControlsMode,
static_cast<int>(content_settings::CookieControlsMode::kBlockThirdParty));
SetCookie(CookieType::kFirstParty, CookiePersistenceType::kPersistent,
embedded_test_server());
extensions::TestExtensionDir extension_dir;
extension_dir.WriteManifest(R"({
"name": "Cookie Test",
"manifest_version": 2,
"version": "1.0",
"background": {
"scripts": ["background.js"]
},
"incognito": "split",
"permissions": ["<all_urls>"]
})");
extension_dir.WriteFile(FILE_PATH_LITERAL("background.js"), "");
extensions::ChromeTestExtensionLoader loader(GetProfile());
loader.set_allow_incognito_access(true);
scoped_refptr<const extensions::Extension> extension =
loader.LoadExtension(extension_dir.UnpackedPath());
ASSERT_TRUE(extension);
// This request will show up as cross-site because the chrome-extension URL
// won't match the test_server domain (127.0.0.1), but because we set
// |force_ignore_site_for_cookies| to true for extension-initiated requests,
// this will actually be able to get the cookie.
GURL url = test_server.GetURL("/echocookieheader");
std::string script = R"((url => {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onload = () => chrome.test.sendScriptResult(xhr.responseText);
xhr.send();
}))";
base::Value result =
extensions::browsertest_util::ExecuteScriptInBackgroundPage(
GetProfile(), extension->id(), script + "('" + url.spec() + "')");
EXPECT_EQ("cookie", result);
}
#endif
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest, BasicRequest) {
if (IsRestartStateWithInProcessNetworkService())
return;
std::unique_ptr<network::ResourceRequest> request =
std::make_unique<network::ResourceRequest>();
request->url = embedded_test_server()->GetURL("/echo");
content::SimpleURLLoaderTestHelper simple_loader_helper;
std::unique_ptr<network::SimpleURLLoader> simple_loader =
network::SimpleURLLoader::Create(std::move(request),
TRAFFIC_ANNOTATION_FOR_TESTS);
simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
loader_factory(), simple_loader_helper.GetCallbackDeprecated());
simple_loader_helper.WaitForCallback();
ASSERT_TRUE(simple_loader->ResponseInfo());
ASSERT_TRUE(simple_loader->ResponseInfo()->headers);
EXPECT_EQ(200, simple_loader->ResponseInfo()->headers->response_code());
ASSERT_TRUE(simple_loader_helper.response_body());
EXPECT_EQ("Echo", *simple_loader_helper.response_body());
}
// Make sure a cache is used when expected.
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest, Cache) {
if (IsRestartStateWithInProcessNetworkService())
return;
// Crashing the network service may corrupt the disk cache, so skip this test
// in tests that crash the network service and use an on-disk cache.
if (GetParam().network_service_state == NetworkServiceState::kRestarted &&
GetHttpCacheType() == StorageType::kDisk) {
return;
}
// Make a request whose response should be cached.
GURL request_url = embedded_test_server()->GetURL(kCacheRandomPath);
url::Origin request_origin =
url::Origin::Create(embedded_test_server()->base_url());
std::unique_ptr<network::ResourceRequest> request =
std::make_unique<network::ResourceRequest>();
request->url = request_url;
request->trusted_params = network::ResourceRequest::TrustedParams();
request->trusted_params->isolation_info =
net::IsolationInfo::CreateForInternalRequest(request_origin);
request->site_for_cookies =
request->trusted_params->isolation_info.site_for_cookies();
content::SimpleURLLoaderTestHelper simple_loader_helper;
std::unique_ptr<network::SimpleURLLoader> simple_loader =
network::SimpleURLLoader::Create(std::move(request),
TRAFFIC_ANNOTATION_FOR_TESTS);
simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
loader_factory(), simple_loader_helper.GetCallbackDeprecated());
simple_loader_helper.WaitForCallback();
ASSERT_TRUE(simple_loader_helper.response_body());
EXPECT_GT(simple_loader_helper.response_body()->size(), 0u);
// Make the request again, and make sure it's cached or not, according to
// expectations. Reuse the content::ResourceRequest, but nothing else.
std::unique_ptr<network::ResourceRequest> request2 =
std::make_unique<network::ResourceRequest>();
request2->url = request_url;
request2->trusted_params = network::ResourceRequest::TrustedParams();
request2->trusted_params->isolation_info =
net::IsolationInfo::CreateForInternalRequest(request_origin);
request2->site_for_cookies =
request2->trusted_params->isolation_info.site_for_cookies();
content::SimpleURLLoaderTestHelper simple_loader_helper2;
std::unique_ptr<network::SimpleURLLoader> simple_loader2 =
network::SimpleURLLoader::Create(std::move(request2),
TRAFFIC_ANNOTATION_FOR_TESTS);
simple_loader2->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
loader_factory(), simple_loader_helper2.GetCallbackDeprecated());
simple_loader_helper2.WaitForCallback();
ASSERT_TRUE(simple_loader_helper2.response_body());
if (GetHttpCacheType() == StorageType::kNone) {
// If there's no cache, the request should have returned a different
// response than before.
EXPECT_NE(*simple_loader_helper.response_body(),
*simple_loader_helper2.response_body());
} else {
// Otherwise, the request should have returned the same result as before.
EXPECT_EQ(*simple_loader_helper.response_body(),
*simple_loader_helper2.response_body());
}
}
// Make sure that NetworkContexts can't access each other's disk caches.
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest, CacheIsolation) {
if (IsRestartStateWithInProcessNetworkService())
return;
// Make a request whose response should be cached.
GURL request_url = embedded_test_server()->GetURL("/cachetime");
std::unique_ptr<network::ResourceRequest> request =
std::make_unique<network::ResourceRequest>();
request->url = request_url;
content::SimpleURLLoaderTestHelper simple_loader_helper;
std::unique_ptr<network::SimpleURLLoader> simple_loader =
network::SimpleURLLoader::Create(std::move(request),
TRAFFIC_ANNOTATION_FOR_TESTS);
simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
loader_factory(), simple_loader_helper.GetCallbackDeprecated());
simple_loader_helper.WaitForCallback();
ASSERT_TRUE(simple_loader_helper.response_body());
EXPECT_GT(simple_loader_helper.response_body()->size(), 0u);
// Stop the server.
ASSERT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
// Make the request again, for each other network context, and make sure the
// response is not cached.
ForEachOtherContext(
base::BindLambdaForTesting([&](NetworkContextType network_context_type) {
std::unique_ptr<network::ResourceRequest> request2 =
std::make_unique<network::ResourceRequest>();
request2->url = request_url;
content::SimpleURLLoaderTestHelper simple_loader_helper2;
std::unique_ptr<network::SimpleURLLoader> simple_loader2 =
network::SimpleURLLoader::Create(std::move(request2),
TRAFFIC_ANNOTATION_FOR_TESTS);
simple_loader2->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
GetLoaderFactoryForContextType(network_context_type),
simple_loader_helper2.GetCallbackDeprecated());
simple_loader_helper2.WaitForCallback();
EXPECT_FALSE(simple_loader_helper2.response_body());
EXPECT_EQ(net::ERR_CONNECTION_REFUSED, simple_loader2->NetError());
}));
}
// Make sure an on-disk cache is used when expected. PRE_DiskCache populates the
// cache. DiskCache then makes sure the cache entry is still there (Or not) as
// expected.
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest, PRE_DiskCache) {
if (IsRestartStateWithInProcessNetworkService())
return;
// Save test URL to disk, so it can be used in the next test (Test server uses
// a random port, so need to know the port to try and retrieve it from the
// cache in the next test). The profile directory is preserved between the
// PRE_DiskCache and DiskCache run, so can just keep a file there.
GURL test_url = embedded_test_server()->GetURL(kCacheRandomPath);
url::Origin test_origin = url::Origin::Create(test_url);
base::ScopedAllowBlockingForTesting allow_blocking;
base::FilePath save_url_file_path = browser()->profile()->GetPath().Append(
FILE_PATH_LITERAL("url_for_test.txt"));
// Make a request whose response should be cached.
std::unique_ptr<network::ResourceRequest> request =
std::make_unique<network::ResourceRequest>();
request->url = test_url;
request->trusted_params = network::ResourceRequest::TrustedParams();
request->trusted_params->isolation_info =
net::IsolationInfo::CreateForInternalRequest(test_origin);
request->site_for_cookies =
request->trusted_params->isolation_info.site_for_cookies();
content::SimpleURLLoaderTestHelper simple_loader_helper;
std::unique_ptr<network::SimpleURLLoader> simple_loader =
network::SimpleURLLoader::Create(std::move(request),
TRAFFIC_ANNOTATION_FOR_TESTS);
simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
loader_factory(), simple_loader_helper.GetCallbackDeprecated());
simple_loader_helper.WaitForCallback();
EXPECT_EQ(net::OK, simple_loader->NetError());
ASSERT_TRUE(simple_loader_helper.response_body());
EXPECT_FALSE(simple_loader_helper.response_body()->empty());
// Write the URL and expected response to a file.
std::string file_data =
test_url.spec() + "\n" + *simple_loader_helper.response_body();
ASSERT_TRUE(base::WriteFile(save_url_file_path, file_data));
EXPECT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
}
// Check if the URL loaded in PRE_DiskCache is still in the cache, across a
// browser restart.
// TODO(crbug.com/40922934): Fix test flake and re-enable
#if BUILDFLAG(IS_MAC)
#define MAYBE_DiskCache DISABLED_DiskCache
#else
#define MAYBE_DiskCache DiskCache
#endif
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest,
MAYBE_DiskCache) {
if (IsRestartStateWithInProcessNetworkService())
return;
// Crashing the network service may corrupt the disk cache, so skip this phase
// in tests that crash the network service and use an on-disk cache.
if (GetParam().network_service_state == NetworkServiceState::kRestarted &&
GetHttpCacheType() == StorageType::kDisk) {
return;
}
// Load URL from the above test body to disk.
base::ScopedAllowBlockingForTesting allow_blocking;
base::FilePath save_url_file_path = browser()->profile()->GetPath().Append(
FILE_PATH_LITERAL("url_for_test.txt"));
std::string file_data;
ASSERT_TRUE(ReadFileToString(save_url_file_path, &file_data));
size_t newline_pos = file_data.find('\n');
ASSERT_NE(newline_pos, std::string::npos);
GURL test_url = GURL(file_data.substr(0, newline_pos));
ASSERT_TRUE(test_url.is_valid()) << test_url.possibly_invalid_spec();
url::Origin test_origin = url::Origin::Create(test_url);
std::string original_response = file_data.substr(newline_pos + 1);
// Request the same test URL as may have been cached by PRE_DiskCache.
std::unique_ptr<network::ResourceRequest> request =
std::make_unique<network::ResourceRequest>();
request->url = test_url;
request->trusted_params = network::ResourceRequest::TrustedParams();
request->trusted_params->isolation_info =
net::IsolationInfo::CreateForInternalRequest(test_origin);
request->site_for_cookies =
request->trusted_params->isolation_info.site_for_cookies();
content::SimpleURLLoaderTestHelper simple_loader_helper;
request->load_flags = net::LOAD_ONLY_FROM_CACHE;
std::unique_ptr<network::SimpleURLLoader> simple_loader =
network::SimpleURLLoader::Create(std::move(request),
TRAFFIC_ANNOTATION_FOR_TESTS);
simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
loader_factory(), simple_loader_helper.GetCallbackDeprecated());
simple_loader_helper.WaitForCallback();
// The request should only succeed if there is an on-disk cache.
if (GetHttpCacheType() != StorageType::kDisk) {
EXPECT_FALSE(simple_loader_helper.response_body());
} else {
DCHECK_NE(GetParam().network_service_state,
NetworkServiceState::kRestarted);
ASSERT_TRUE(simple_loader_helper.response_body());
EXPECT_EQ(original_response, *simple_loader_helper.response_body());
}
}
// Make sure that NetworkContexts have separate DNS caches.
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest,
DnsCacheIsolation) {
net::NetworkAnonymizationKey network_anonymization_key =
net::NetworkAnonymizationKey::CreateTransient();
net::HostPortPair host_port_pair(kHostname, 0);
network::mojom::ResolveHostParametersPtr params =
network::mojom::ResolveHostParameters::New();
// Use A queries, to avoid running into issues with the IPv6 probe, the
// results of which can change during runtime, and can affect the DNS cache
// key.
params->dns_query_type = net::DnsQueryType ::A;
// Resolve |host_port_pair|, which should succeed and put it in the
// NetworkContext's cache.
network::DnsLookupResult result =
network::BlockingDnsLookup(network_context(), host_port_pair,
std::move(params), network_anonymization_key);
EXPECT_EQ(net::OK, result.error);
ASSERT_TRUE(result.resolved_addresses.has_value());
ASSERT_EQ(1u, result.resolved_addresses->size());
EXPECT_EQ(kAddress,
result.resolved_addresses.value()[0].ToStringWithoutPort());
// Make a cache-only request for the same hostname, for each other network
// context, and make sure no result is returned.
ForEachOtherContext(
base::BindLambdaForTesting([&](NetworkContextType network_context_type) {
params = network::mojom::ResolveHostParameters::New();
params->dns_query_type = net::DnsQueryType ::A;
// Cache only lookup.
params->cache_usage =
network::mojom::ResolveHostParameters::CacheUsage::STALE_ALLOWED;
params->source = net::HostResolverSource::LOCAL_ONLY;
network::DnsLookupResult result = network::BlockingDnsLookup(
GetNetworkContextForContextType(network_context_type),
host_port_pair, std::move(params), network_anonymization_key);
EXPECT_EQ(net::ERR_NAME_NOT_RESOLVED, result.error);
}));
// Do a cache-only lookup using the original network context, which should
// return the same result it initially did.
params = network::mojom::ResolveHostParameters::New();
// Cache only lookup.
params->dns_query_type = net::DnsQueryType ::A;
params->source = net::HostResolverSource::LOCAL_ONLY;
params->cache_usage =
network::mojom::ResolveHostParameters::CacheUsage::STALE_ALLOWED;
result =
network::BlockingDnsLookup(network_context(), host_port_pair,
std::move(params), network_anonymization_key);
EXPECT_EQ(net::OK, result.error);
ASSERT_TRUE(result.resolved_addresses.has_value());
ASSERT_EQ(1u, result.resolved_addresses->size());
EXPECT_EQ(kAddress,
result.resolved_addresses.value()[0].ToStringWithoutPort());
}
// Visits a URL with an HSTS header, and makes sure it is respected.
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest, PRE_Hsts) {
if (IsRestartStateWithInProcessNetworkService())
return;
net::test_server::EmbeddedTestServer ssl_server(
net::test_server::EmbeddedTestServer::TYPE_HTTPS);
ssl_server.SetSSLConfig(
net::test_server::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN);
ssl_server.AddDefaultHandlers(GetChromeTestDataDir());
ASSERT_TRUE(ssl_server.Start());
// Make a request whose response has an STS header.
std::unique_ptr<network::ResourceRequest> request =
std::make_unique<network::ResourceRequest>();
request->url = ssl_server.GetURL(
"/set-header?Strict-Transport-Security: max-age%3D600000");
content::SimpleURLLoaderTestHelper simple_loader_helper;
std::unique_ptr<network::SimpleURLLoader> simple_loader =
network::SimpleURLLoader::Create(std::move(request),
TRAFFIC_ANNOTATION_FOR_TESTS);
simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
loader_factory(), simple_loader_helper.GetCallbackDeprecated());
simple_loader_helper.WaitForCallback();
EXPECT_EQ(net::OK, simple_loader->NetError());
ASSERT_TRUE(simple_loader->ResponseInfo()->headers);
EXPECT_TRUE(simple_loader->ResponseInfo()->headers->HasHeaderValue(
"Strict-Transport-Security", "max-age=600000"));
// Make a cache-only request to make sure the HSTS entry is respected. Using a
// cache-only request both prevents the result from being cached, and makes
// the check identical to the one in the next test, which is run after the
// test server has been shut down.
GURL exected_ssl_url = ssl_server.base_url();
GURL::Replacements replacements;
replacements.SetSchemeStr("http");
GURL start_url = exected_ssl_url.ReplaceComponents(replacements);
request = std::make_unique<network::ResourceRequest>();
request->url = start_url;
request->load_flags = net::LOAD_ONLY_FROM_CACHE;
content::SimpleURLLoaderTestHelper simple_loader_helper2;
simple_loader = network::SimpleURLLoader::Create(
std::move(request), TRAFFIC_ANNOTATION_FOR_TESTS);
simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
loader_factory(), simple_loader_helper2.GetCallbackDeprecated());
simple_loader_helper2.WaitForCallback();
EXPECT_FALSE(simple_loader_helper2.response_body());
EXPECT_EQ(exected_ssl_url, simple_loader->GetFinalURL());
// Write the URL with HSTS information to a file, so it can be loaded in the
// next test. Have to use a file for this, since the server's port is random.
base::ScopedAllowBlockingForTesting allow_blocking;
base::FilePath save_url_file_path = browser()->profile()->GetPath().Append(
FILE_PATH_LITERAL("url_for_test.txt"));
std::string file_data = start_url.spec();
ASSERT_TRUE(base::WriteFile(save_url_file_path, file_data));
}
// Checks if the HSTS information from the last test is still available after a
// restart.
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest, Hsts) {
if (IsRestartStateWithInProcessNetworkService())
return;
// The network service must be cleanly shut down to guarantee HSTS information
// is flushed to disk, but that currently generally doesn't happen. See
// https://crbug.com/820996.
if (GetHttpCacheType() == StorageType::kDisk) {
return;
}
base::ScopedAllowBlockingForTesting allow_blocking;
base::FilePath save_url_file_path = browser()->profile()->GetPath().Append(
FILE_PATH_LITERAL("url_for_test.txt"));
std::string file_data;
ASSERT_TRUE(ReadFileToString(save_url_file_path, &file_data));
GURL start_url = GURL(file_data);
// Unfortunately, loading HSTS information is loaded asynchronously from
// disk, so there's no way to guarantee it has loaded by the time a
// request is made. As a result, may have to try multiple times to verify that
// cached HSTS information has been loaded, when it's stored on disk.
while (true) {
std::unique_ptr<network::ResourceRequest> request =
std::make_unique<network::ResourceRequest>();
request->url = start_url;
request->load_flags = net::LOAD_ONLY_FROM_CACHE;
content::SimpleURLLoaderTestHelper simple_loader_helper;
std::unique_ptr<network::SimpleURLLoader> simple_loader =
network::SimpleURLLoader::Create(std::move(request),
TRAFFIC_ANNOTATION_FOR_TESTS);
simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
loader_factory(), simple_loader_helper.GetCallbackDeprecated());
simple_loader_helper.WaitForCallback();
EXPECT_FALSE(simple_loader_helper.response_body());
// HSTS information is currently stored on disk if-and-only-if there's an
// on-disk HTTP cache.
if (GetHttpCacheType() == StorageType::kDisk) {
GURL::Replacements replacements;
replacements.SetSchemeStr("https");
GURL expected_https_url = start_url.ReplaceComponents(replacements);
// The file may not have been loaded yet, so if the cached HSTS
// information was not respected, try again.
if (expected_https_url != simple_loader->GetFinalURL())
continue;
EXPECT_EQ(expected_https_url, simple_loader->GetFinalURL());
break;
} else {
EXPECT_EQ(start_url, simple_loader->GetFinalURL());
break;
}
}
}
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest, ProxyConfig) {
if (IsRestartStateWithInProcessNetworkService())
return;
SetProxyPref(embedded_test_server()->host_port_pair());
TestProxyConfigured(/*expect_success=*/true);
}
// This test should not end in an AssertNoURLLRequests CHECK.
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest,
ShutdownWithLiveRequest) {
if (IsRestartStateWithInProcessNetworkService())
return;
MakeLongLivedRequestThatHangsUntilShutdown();
}
// Disabled due to flakiness. See crbug.com/1189031.
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC)
#define MAYBE_UserAgentAndLanguagePrefs DISABLED_UserAgentAndLanguagePrefs
#else
#define MAYBE_UserAgentAndLanguagePrefs UserAgentAndLanguagePrefs
#endif
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest,
MAYBE_UserAgentAndLanguagePrefs) {
if (IsRestartStateWithInProcessNetworkService())
return;
// The system and SafeBrowsing network contexts aren't associated with any
// profile, so changing the language settings for the profile's main network
// context won't affect what they send.
bool system =
(GetParam().network_context_type == NetworkContextType::kSystem ||
GetParam().network_context_type == NetworkContextType::kSafeBrowsing);
// echoheader returns "None" when the header isn't there in the first place.
const char kNoAcceptLanguage[] = "None";
std::string accept_language, user_agent;
// Check default.
ASSERT_TRUE(FetchHeaderEcho("accept-language", &accept_language));
EXPECT_EQ(system ? kNoAcceptLanguage : "en-US,en;q=0.9", accept_language);
ASSERT_TRUE(FetchHeaderEcho("user-agent", &user_agent));
EXPECT_EQ(embedder_support::GetUserAgent(), user_agent);
// Change AcceptLanguages preferences, and check that headers are updated.
// First, A single language.
browser()->profile()->GetPrefs()->SetString(language::prefs::kAcceptLanguages,
"zu");
FlushNetworkInterface();
std::string accept_language2, user_agent2;
ASSERT_TRUE(FetchHeaderEcho("accept-language", &accept_language2));
EXPECT_EQ(system ? kNoAcceptLanguage : "zu", accept_language2);
ASSERT_TRUE(FetchHeaderEcho("user-agent", &user_agent2));
EXPECT_EQ(embedder_support::GetUserAgent(), user_agent2);
// Second, a single language with locale.
browser()->profile()->GetPrefs()->SetString(language::prefs::kAcceptLanguages,
"zu-ZA");
FlushNetworkInterface();
std::string accept_language3, user_agent3;
ASSERT_TRUE(FetchHeaderEcho("accept-language", &accept_language3));
EXPECT_EQ(system ? kNoAcceptLanguage : "zu-ZA,zu;q=0.9", accept_language3);
ASSERT_TRUE(FetchHeaderEcho("user-agent", &user_agent3));
EXPECT_EQ(embedder_support::GetUserAgent(), user_agent3);
// Third, a list with multiple languages. Incognito mode should return only
// the first.
browser()->profile()->GetPrefs()->SetString(language::prefs::kAcceptLanguages,
"ar,am,en-GB,ru,zu");
FlushNetworkInterface();
std::string accept_language4;
std::string user_agent4;
ASSERT_TRUE(FetchHeaderEcho("accept-language", &accept_language4));
if (GetProfile()->IsOffTheRecord()) {
EXPECT_EQ(system ? kNoAcceptLanguage : "ar", accept_language4);
} else {
EXPECT_EQ(system ? kNoAcceptLanguage
: "ar,am;q=0.9,en-GB;q=0.8,en;q=0.7,ru;q=0.6,zu;q=0.5",
accept_language4);
}
ASSERT_TRUE(FetchHeaderEcho("user-agent", &user_agent4));
EXPECT_EQ(embedder_support::GetUserAgent(), user_agent4);
}
// First part of testing enable referrers. Check that referrers are enabled by
// default at browser start, and referrers are indeed sent. Then disable
// referrers, and make sure that they aren't set.
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest,
PRE_EnableReferrers) {
if (IsRestartStateWithInProcessNetworkService())
return;
const GURL kReferrer("http://referrer/");
// Referrers should be enabled by default.
EXPECT_TRUE(GetPrefService()->GetBoolean(prefs::kEnableReferrers));
// Send a request, make sure the referrer is sent.
std::string referrer;
std::unique_ptr<network::ResourceRequest> request =
std::make_unique<network::ResourceRequest>();
request->referrer = kReferrer;
ASSERT_TRUE(FetchHeaderEcho("referer", &referrer, std::move(request)));
// SafeBrowsing never sends the referrer since it doesn't need to.
if (GetParam().network_context_type == NetworkContextType::kSafeBrowsing) {
EXPECT_EQ("None", referrer);
} else {
EXPECT_EQ(kReferrer.spec(), referrer);
}
// Disable referrers, and flush the NetworkContext mojo interface it's set on,
// to avoid any races with the URLLoaderFactory pipe.
GetPrefService()->SetBoolean(prefs::kEnableReferrers, false);
FlushNetworkInterface();
// Send a request and make sure its referer is not sent.
std::string referrer2;
std::unique_ptr<network::ResourceRequest> request2 =
std::make_unique<network::ResourceRequest>();
request2->referrer = kReferrer;
ASSERT_TRUE(FetchHeaderEcho("referer", &referrer2, std::move(request2)));
EXPECT_EQ("None", referrer2);
}
// Second part of enable referrer test. Referrer should still be disabled. Make
// sure that disable referrers option is respected after startup, as to just
// after changing it.
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest,
EnableReferrers) {
if (IsRestartStateWithInProcessNetworkService())
return;
const GURL kReferrer("http://referrer/");
// The preference is expected to be reset in incognito mode.
if (is_incognito()) {
EXPECT_TRUE(GetPrefService()->GetBoolean(prefs::kEnableReferrers));
return;
}
// Referrers should still be disabled.
EXPECT_FALSE(GetPrefService()->GetBoolean(prefs::kEnableReferrers));
// Send a request and make sure its referer is not sent.
std::string referrer;
std::unique_ptr<network::ResourceRequest> request =
std::make_unique<network::ResourceRequest>();
request->referrer = kReferrer;
ASSERT_TRUE(FetchHeaderEcho("referer", &referrer, std::move(request)));
EXPECT_EQ("None", referrer);
}
// Make sure that sending referrers that violate the referrer policy results in
// errors.
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest,
PolicyViolatingReferrers) {
if (IsRestartStateWithInProcessNetworkService())
return;
std::unique_ptr<network::ResourceRequest> request =
std::make_unique<network::ResourceRequest>();
request->url = embedded_test_server()->GetURL("/echoheader?Referer");
request->referrer = GURL("http://referrer/");
request->referrer_policy = net::ReferrerPolicy::NO_REFERRER;
content::SimpleURLLoaderTestHelper simple_loader_helper;
std::unique_ptr<network::SimpleURLLoader> simple_loader =
network::SimpleURLLoader::Create(std::move(request),
TRAFFIC_ANNOTATION_FOR_TESTS);
simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
loader_factory(), simple_loader_helper.GetCallbackDeprecated());
simple_loader_helper.WaitForCallback();
if (GetParam().network_context_type == NetworkContextType::kSafeBrowsing) {
// Safebrowsing ignores referrers so the requests succeed.
EXPECT_EQ(net::OK, simple_loader->NetError());
ASSERT_TRUE(simple_loader_helper.response_body());
EXPECT_EQ("None", *simple_loader_helper.response_body());
} else {
// In all other cases, the invalid referrer causes the request to fail.
EXPECT_EQ(net::ERR_BLOCKED_BY_CLIENT, simple_loader->NetError());
}
}
// Makes sure cookies are enabled by default, and saved to disk / not saved to
// disk as expected.
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest,
PRE_CookiesEnabled) {
if (IsRestartStateWithInProcessNetworkService())
return;
EXPECT_TRUE(GetCookies(embedded_test_server()->base_url()).empty());
SetCookie(CookieType::kFirstParty, CookiePersistenceType::kPersistent,
embedded_test_server());
EXPECT_FALSE(GetCookies(embedded_test_server()->base_url()).empty());
}
// Disabled due to flakiness. See https://crbug.com/1273903.
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest,
DISABLED_CookiesEnabled) {
if (IsRestartStateWithInProcessNetworkService())
return;
// Check that the cookie from the first stage of the test was / was not
// preserved between browser restarts, as expected.
bool has_cookies = !GetCookies(embedded_test_server()->base_url()).empty();
EXPECT_EQ(GetCookieStorageType() == StorageType::kDisk, has_cookies);
}
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest,
CookieIsolation) {
if (IsRestartStateWithInProcessNetworkService())
return;
SetCookie(CookieType::kFirstParty, CookiePersistenceType::kPersistent,
embedded_test_server());
EXPECT_FALSE(GetCookies(embedded_test_server()->base_url()).empty());
ForEachOtherContext(
base::BindLambdaForTesting([&](NetworkContextType network_context_type) {
EXPECT_TRUE(GetCookiesForContextType(network_context_type,
embedded_test_server()->base_url())
.empty());
}));
}
// Disabled due to flakiness. See https://crbug.com/1126755.
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
#define MAYBE_PRE_ThirdPartyCookiesBlocked DISABLED_PRE_ThirdPartyCookiesBlocked
#define MAYBE_ThirdPartyCookiesBlocked DISABLED_ThirdPartyCookiesBlocked
#else
#define MAYBE_PRE_ThirdPartyCookiesBlocked PRE_ThirdPartyCookiesBlocked
#define MAYBE_ThirdPartyCookiesBlocked ThirdPartyCookiesBlocked
#endif
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest,
MAYBE_PRE_ThirdPartyCookiesBlocked) {
if (IsRestartStateWithInProcessNetworkService())
return;
// The system and SafeBrowsing network contexts don't support the third party
// cookie blocking options, since they have no notion of third parties.
bool system =
GetParam().network_context_type == NetworkContextType::kSystem ||
GetParam().network_context_type == NetworkContextType::kSafeBrowsing;
if (system)
return;
GetPrefService()->SetInteger(
prefs::kCookieControlsMode,
static_cast<int>(content_settings::CookieControlsMode::kBlockThirdParty));
SetCookie(CookieType::kThirdParty, CookiePersistenceType::kSession,
https_server());
EXPECT_TRUE(GetCookies(https_server()->base_url()).empty());
}
// Disabled due to flakiness. See https://crbug.com/1126755.
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest,
MAYBE_ThirdPartyCookiesBlocked) {
if (IsRestartStateWithInProcessNetworkService())
return;
// The system and SafeBrowsing network contexts don't support the third party
// cookie blocking options, since they have no notion of third parties.
bool system =
GetParam().network_context_type == NetworkContextType::kSystem ||
GetParam().network_context_type == NetworkContextType::kSafeBrowsing;
if (system)
return;
// The third-party cookies pref should carry over to the next session.
EXPECT_EQ(
static_cast<int>(content_settings::CookieControlsMode::kBlockThirdParty),
GetPrefService()->GetInteger(prefs::kCookieControlsMode));
SetCookie(CookieType::kThirdParty, CookiePersistenceType::kSession,
https_server());
EXPECT_TRUE(GetCookies(https_server()->base_url()).empty());
// Add exception, third party cookies should be allowed now.
CookieSettingsFactory::GetForProfile(browser()->profile())
->SetCookieSetting(https_server()->base_url(), CONTENT_SETTING_ALLOW);
// Set a third-party cookie. It should actually get set this time.
SetCookie(CookieType::kThirdParty, CookiePersistenceType::kSession,
https_server());
EXPECT_FALSE(GetCookies(https_server()->base_url()).empty());
}
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest,
PRE_CookieSettings) {
if (IsRestartStateWithInProcessNetworkService())
return;
// The system and SafeBrowsing network contexts don't respect cookie blocking
// options, which are per-profile.
bool system =
GetParam().network_context_type == NetworkContextType::kSystem ||
GetParam().network_context_type == NetworkContextType::kSafeBrowsing;
if (system)
return;
CookieSettingsFactory::GetForProfile(browser()->profile())
->SetDefaultCookieSetting(CONTENT_SETTING_BLOCK);
FlushNetworkInterface();
SetCookie(CookieType::kFirstParty, CookiePersistenceType::kSession,
embedded_test_server());
EXPECT_TRUE(GetCookies(embedded_test_server()->base_url()).empty());
}
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest, CookieSettings) {
if (IsRestartStateWithInProcessNetworkService())
return;
// The system and SafeBrowsing network contexts don't respect cookie blocking
// options, which are per-profile.
bool system =
GetParam().network_context_type == NetworkContextType::kSystem ||
GetParam().network_context_type == NetworkContextType::kSafeBrowsing;
if (system)
return;
// The content settings should carry over to the next session.
SetCookie(CookieType::kFirstParty, CookiePersistenceType::kSession,
embedded_test_server());
EXPECT_TRUE(GetCookies(embedded_test_server()->base_url()).empty());
// Set default setting to allow, cookies should be set now.
CookieSettingsFactory::GetForProfile(browser()->profile())
->SetDefaultCookieSetting(CONTENT_SETTING_ALLOW);
FlushNetworkInterface();
SetCookie(CookieType::kFirstParty, CookiePersistenceType::kSession,
embedded_test_server());
EXPECT_FALSE(GetCookies(embedded_test_server()->base_url()).empty());
}
// Make sure file uploads work.
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest, UploadFile) {
if (IsRestartStateWithInProcessNetworkService())
return;
std::unique_ptr<network::ResourceRequest> request =
std::make_unique<network::ResourceRequest>();
request->method = "POST";
request->url = embedded_test_server()->GetURL("/echo");
content::SimpleURLLoaderTestHelper simple_loader_helper;
std::unique_ptr<network::SimpleURLLoader> simple_loader =
network::SimpleURLLoader::Create(std::move(request),
TRAFFIC_ANNOTATION_FOR_TESTS);
base::FilePath dir_test_data;
base::PathService::Get(chrome::DIR_TEST_DATA, &dir_test_data);
base::FilePath path =
dir_test_data.Append(base::FilePath(FILE_PATH_LITERAL("simple.html")));
simple_loader->AttachFileForUpload(path, "text/html");
simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
loader_factory(), simple_loader_helper.GetCallbackDeprecated());
simple_loader_helper.WaitForCallback();
ASSERT_TRUE(simple_loader->ResponseInfo());
ASSERT_TRUE(simple_loader->ResponseInfo()->headers);
EXPECT_EQ(200, simple_loader->ResponseInfo()->headers->response_code());
ASSERT_TRUE(simple_loader_helper.response_body());
base::ScopedAllowBlockingForTesting allow_blocking;
std::string expected_response;
base::ReadFileToString(path, &expected_response);
EXPECT_EQ(expected_response.c_str(), *simple_loader_helper.response_body());
}
class NetworkContextConfigurationFixedPortBrowserTest
: public NetworkContextConfigurationBrowserTest {
public:
NetworkContextConfigurationFixedPortBrowserTest() {}
~NetworkContextConfigurationFixedPortBrowserTest() override {}
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitchASCII(
switches::kTestingFixedHttpPort,
base::StringPrintf("%u", embedded_test_server()->port()));
LOG(WARNING) << base::StringPrintf("%u", embedded_test_server()->port());
}
};
// Test that the command line switch makes it to the network service and is
// respected.
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationFixedPortBrowserTest,
TestingFixedPort) {
if (IsRestartStateWithInProcessNetworkService())
return;
std::unique_ptr<network::ResourceRequest> request =
std::make_unique<network::ResourceRequest>();
// This URL does not use the port the embedded test server is using. The
// command line switch should make it result in the request being directed to
// the test server anyways.
request->url = GURL("http://127.0.0.1/echo");
content::SimpleURLLoaderTestHelper simple_loader_helper;
std::unique_ptr<network::SimpleURLLoader> simple_loader =
network::SimpleURLLoader::Create(std::move(request),
TRAFFIC_ANNOTATION_FOR_TESTS);
simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
loader_factory(), simple_loader_helper.GetCallbackDeprecated());
simple_loader_helper.WaitForCallback();
EXPECT_EQ(net::OK, simple_loader->NetError());
ASSERT_TRUE(simple_loader_helper.response_body());
EXPECT_EQ(*simple_loader_helper.response_body(), "Echo");
}
class NetworkContextConfigurationProxyOnStartBrowserTest
: public NetworkContextConfigurationBrowserTest {
public:
NetworkContextConfigurationProxyOnStartBrowserTest() {}
NetworkContextConfigurationProxyOnStartBrowserTest(
const NetworkContextConfigurationProxyOnStartBrowserTest&) = delete;
NetworkContextConfigurationProxyOnStartBrowserTest& operator=(
const NetworkContextConfigurationProxyOnStartBrowserTest&) = delete;
~NetworkContextConfigurationProxyOnStartBrowserTest() override {}
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitchASCII(
switches::kProxyServer,
embedded_test_server()->host_port_pair().ToString());
}
};
// Test that when there's a proxy configuration at startup, the initial requests
// use that configuration.
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationProxyOnStartBrowserTest,
TestInitialProxyConfig) {
if (IsRestartStateWithInProcessNetworkService())
return;
TestProxyConfigured(/*expect_success=*/true);
}
// Make sure the system URLRequestContext can handle fetching PAC scripts from
// http URLs.
class NetworkContextConfigurationHttpPacBrowserTest
: public NetworkContextConfigurationBrowserTest {
public:
NetworkContextConfigurationHttpPacBrowserTest()
: pac_test_server_(net::test_server::EmbeddedTestServer::TYPE_HTTP) {}
NetworkContextConfigurationHttpPacBrowserTest(
const NetworkContextConfigurationHttpPacBrowserTest&) = delete;
NetworkContextConfigurationHttpPacBrowserTest& operator=(
const NetworkContextConfigurationHttpPacBrowserTest&) = delete;
~NetworkContextConfigurationHttpPacBrowserTest() override {}
void SetUpCommandLine(base::CommandLine* command_line) override {
pac_test_server_.RegisterRequestHandler(base::BindRepeating(
&NetworkContextConfigurationHttpPacBrowserTest::HandlePacRequest,
GetPacScript()));
EXPECT_TRUE(pac_test_server_.Start());
command_line->AppendSwitchASCII(switches::kProxyPacUrl,
pac_test_server_.base_url().spec().c_str());
}
static std::unique_ptr<net::test_server::HttpResponse> HandlePacRequest(
const std::string& pac_script,
const net::test_server::HttpRequest& request) {
std::unique_ptr<net::test_server::BasicHttpResponse> response =
std::make_unique<net::test_server::BasicHttpResponse>();
response->set_content(pac_script);
return response;
}
private:
net::test_server::EmbeddedTestServer pac_test_server_;
};
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationHttpPacBrowserTest, HttpPac) {
if (IsRestartStateWithInProcessNetworkService())
return;
TestProxyConfigured(/*expect_success=*/true);
}
// Make sure the system URLRequestContext can handle fetching PAC scripts from
// file URLs.
class NetworkContextConfigurationFilePacBrowserTest
: public NetworkContextConfigurationBrowserTest {
public:
NetworkContextConfigurationFilePacBrowserTest() {}
NetworkContextConfigurationFilePacBrowserTest(
const NetworkContextConfigurationFilePacBrowserTest&) = delete;
NetworkContextConfigurationFilePacBrowserTest& operator=(
const NetworkContextConfigurationFilePacBrowserTest&) = delete;
~NetworkContextConfigurationFilePacBrowserTest() override {}
void SetUpCommandLine(base::CommandLine* command_line) override {
const char kPacFileName[] = "foo.pac";
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
base::FilePath pac_file_path =
temp_dir_.GetPath().AppendASCII(kPacFileName);
std::string pac_script = GetPacScript();
ASSERT_TRUE(base::WriteFile(pac_file_path, pac_script));
command_line->AppendSwitchASCII(
switches::kProxyPacUrl, net::FilePathToFileURL(pac_file_path).spec());
}
private:
base::ScopedTempDir temp_dir_;
};
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationFilePacBrowserTest, FilePac) {
if (IsRestartStateWithInProcessNetworkService())
return;
TestProxyConfigured(false);
}
// Make sure the system URLRequestContext can handle fetching PAC scripts from
// data URLs.
class NetworkContextConfigurationDataPacBrowserTest
: public NetworkContextConfigurationBrowserTest {
public:
NetworkContextConfigurationDataPacBrowserTest() {}
NetworkContextConfigurationDataPacBrowserTest(
const NetworkContextConfigurationDataPacBrowserTest&) = delete;
NetworkContextConfigurationDataPacBrowserTest& operator=(
const NetworkContextConfigurationDataPacBrowserTest&) = delete;
~NetworkContextConfigurationDataPacBrowserTest() override {}
void SetUpCommandLine(base::CommandLine* command_line) override {
std::string contents;
// Read in kPACScript contents.
command_line->AppendSwitchASCII(switches::kProxyPacUrl,
"data:," + GetPacScript());
}
};
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationDataPacBrowserTest, DataPac) {
if (IsRestartStateWithInProcessNetworkService())
return;
TestProxyConfigured(/*expect_success=*/true);
}
class NetworkContextConfigurationProxySettingsBrowserTest
: public NetworkContextConfigurationHttpPacBrowserTest {
public:
const size_t kDefaultMaxConnectionsPerProxy = 32;
NetworkContextConfigurationProxySettingsBrowserTest() = default;
NetworkContextConfigurationProxySettingsBrowserTest(
const NetworkContextConfigurationProxySettingsBrowserTest&) = delete;
NetworkContextConfigurationProxySettingsBrowserTest& operator=(
const NetworkContextConfigurationProxySettingsBrowserTest&) = delete;
~NetworkContextConfigurationProxySettingsBrowserTest() override = default;
void SetUpOnMainThread() override {
embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
&NetworkContextConfigurationProxySettingsBrowserTest::TrackConnections,
base::Unretained(this)));
expected_connections_loop_ptr_.store(nullptr);
NetworkContextConfigurationHttpPacBrowserTest::SetUpOnMainThread();
}
virtual size_t GetExpectedMaxConnectionsPerProxy() const {
return kDefaultMaxConnectionsPerProxy;
}
std::unique_ptr<net::test_server::HttpResponse> TrackConnections(
const net::test_server::HttpRequest& request) {
if (!base::StartsWith(request.relative_url, "/hung",
base::CompareCase::INSENSITIVE_ASCII))
return nullptr;
// Record the number of connections we're seeing.
CHECK(observed_request_urls_.find(request.GetURL().spec()) ==
observed_request_urls_.end());
observed_request_urls_.emplace(request.GetURL().spec());
CHECK_GE(GetExpectedMaxConnectionsPerProxy(),
observed_request_urls_.size());
// Once we've seen at least as many connections as we expect, we can quit
// the loop on the main test thread. The test may choose to wait for
// longer to see if there are any additional unexpected connections.
if (GetExpectedMaxConnectionsPerProxy() == observed_request_urls_.size() &&
expected_connections_loop_ptr_.load() != nullptr) {
expected_connections_loop_ptr_.load()->Quit();
}
// To test the number of connections per proxy, we'll hang all responses.
return std::make_unique<net::test_server::HungResponse>();
}
void RunMaxConnectionsPerProxyTest() {
// At this point in the test, we've set up a proxy that points to our
// embedded test server. We've also set things up to hang all incoming
// requests and record how many concurrent connections we have running. To
// detect the maximum number of requests per proxy, we now just have to
// attempt to make as many requests as possible to ensure we don't see an
// incorrect number of concurrent requests.
// First of all, we're going to want to wait for at least as many
// connections as we expect.
base::RunLoop expected_connections_run_loop;
expected_connections_loop_ptr_.store(&expected_connections_run_loop);
std::vector<std::unique_ptr<network::SimpleURLLoader>> loaders(
GetExpectedMaxConnectionsPerProxy());
for (unsigned int i = 0; i < GetExpectedMaxConnectionsPerProxy() + 1; ++i) {
std::unique_ptr<network::ResourceRequest> request =
std::make_unique<network::ResourceRequest>();
request->url =
embedded_test_server()->GetURL(base::StringPrintf("foo%u.test", i),
base::StringPrintf("/hung_%u", i));
content::SimpleURLLoaderTestHelper simple_loader_helper;
std::unique_ptr<network::SimpleURLLoader> simple_loader =
network::SimpleURLLoader::Create(std::move(request),
TRAFFIC_ANNOTATION_FOR_TESTS);
simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
loader_factory(), simple_loader_helper.GetCallbackDeprecated());
loaders.emplace_back(std::move(simple_loader));
}
expected_connections_run_loop.Run();
// Then wait for any remaining connections that we should NOT get.
base::RunLoop ugly_100ms_wait;
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE, ugly_100ms_wait.QuitClosure(), base::Milliseconds(100));
ugly_100ms_wait.Run();
// Stop the server.
ASSERT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
}
private:
std::atomic<base::RunLoop*> expected_connections_loop_ptr_{nullptr};
// In RunMaxConnectionsPerProxyTest(), we'll make several network requests
// that hang. These hung requests are assumed to last for the duration of the
// test. This member, which is only accessed from the server's IO thread,
// records each observed request to ensure we see only as many connections as
// we expect.
std::unordered_set<std::string> observed_request_urls_;
};
// Test failure on macOS: crbug.com/1287934
#if BUILDFLAG(IS_MAC)
#define MAYBE_MaxConnectionsPerProxy DISABLED_MaxConnectionsPerProxy
#else
#define MAYBE_MaxConnectionsPerProxy MaxConnectionsPerProxy
#endif
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationProxySettingsBrowserTest,
MAYBE_MaxConnectionsPerProxy) {
RunMaxConnectionsPerProxyTest();
}
class NetworkContextConfigurationManagedProxySettingsBrowserTest
: public NetworkContextConfigurationProxySettingsBrowserTest {
public:
const size_t kTestMaxConnectionsPerProxy = 37;
NetworkContextConfigurationManagedProxySettingsBrowserTest() = default;
NetworkContextConfigurationManagedProxySettingsBrowserTest(
const NetworkContextConfigurationManagedProxySettingsBrowserTest&) =
delete;
NetworkContextConfigurationManagedProxySettingsBrowserTest& operator=(
const NetworkContextConfigurationManagedProxySettingsBrowserTest&) =
delete;
~NetworkContextConfigurationManagedProxySettingsBrowserTest() override =
default;
void SetUpInProcessBrowserTestFixture() override {
NetworkContextConfigurationProxySettingsBrowserTest::
SetUpInProcessBrowserTestFixture();
policy::PolicyMap policies;
policies.Set(policy::key::kMaxConnectionsPerProxy,
policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_MACHINE,
policy::POLICY_SOURCE_CLOUD,
base::Value(static_cast<int>(kTestMaxConnectionsPerProxy)),
/*external_data_fetcher=*/nullptr);
UpdateChromePolicy(policies);
}
size_t GetExpectedMaxConnectionsPerProxy() const override {
return kTestMaxConnectionsPerProxy;
}
};
// crbug.com/1288780: flaky on Mac.
#if BUILDFLAG(IS_MAC)
#define MAYBE_MaxConnectionsPerProxy DISABLED_MaxConnectionsPerProxy
#else
#define MAYBE_MaxConnectionsPerProxy MaxConnectionsPerProxy
#endif
IN_PROC_BROWSER_TEST_P(
NetworkContextConfigurationManagedProxySettingsBrowserTest,
MAYBE_MaxConnectionsPerProxy) {
RunMaxConnectionsPerProxyTest();
}
#if BUILDFLAG(ENABLE_REPORTING)
// Used to test that we persist Reporting clients and NEL policies to disk, but
// only when appropriate.
class NetworkContextConfigurationReportingAndNelBrowserTest
: public NetworkContextConfigurationBrowserTest {
public:
struct RequestState {
content::SimpleURLLoaderTestHelper helper;
std::unique_ptr<network::SimpleURLLoader> loader;
};
NetworkContextConfigurationReportingAndNelBrowserTest()
: https_server_(net::test_server::EmbeddedTestServer::TYPE_HTTPS) {
// Set up an SSL certificate so we can make requests to "https://localhost"
https_server_.SetSSLConfig(
net::test_server::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN);
// Only start the server if we actually need it in the test (i.e., we'll
// call StartAcceptingConnections somewhere in the test).
if (!IsRestartStateWithInProcessNetworkService() &&
AreReportingAndNelEnabled()) {
EXPECT_TRUE(https_server_.InitializeAndListen());
}
// Make report delivery happen instantly.
net::ReportingPolicy policy;
policy.delivery_interval = base::Seconds(0);
net::ReportingPolicy::UsePolicyForTesting(policy);
}
std::unique_ptr<RequestState> NewSimpleRequest(const GURL& url) {
auto request_state = std::make_unique<RequestState>();
std::unique_ptr<network::ResourceRequest> request =
std::make_unique<network::ResourceRequest>();
request->url = url;
request_state->loader = network::SimpleURLLoader::Create(
std::move(request), TRAFFIC_ANNOTATION_FOR_TESTS);
request_state->loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
loader_factory(), request_state->helper.GetCallbackDeprecated());
return request_state;
}
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitch(embedder_support::kShortReportingDelay);
// This switch will cause traffic to *any* port to go to https_server_,
// regardless of which arbitrary port https_server_ decides to run on.
// NEL and Reporting policies are only valid for a single origin.
if (!IsRestartStateWithInProcessNetworkService() &&
AreReportingAndNelEnabled()) {
command_line->AppendSwitchASCII(switches::kTestingFixedHttpsPort,
base::StringPrintf("%u", port()));
}
}
net::EmbeddedTestServer* http_server() { return &https_server_; }
int port() const { return https_server_.port(); }
static GURL GetCollectorURL() { return GURL("https://localhost/upload"); }
static std::string GetReportToHeader() {
return "Report-To: {\"endpoints\":[{\"url\":\"" + GetCollectorURL().spec() +
"\"}],\"max_age\":86400}\r\n";
}
static std::string GetNELHeader() {
return "NEL: "
"{\"report_to\":\"default\",\"max_age\":86400,\"success_fraction\":"
"1.0}\r\n";
}
void RespondWithHeaders(net::test_server::ControllableHttpResponse* resp) {
resp->WaitForRequest();
resp->Send("HTTP/1.1 200 OK\r\n");
resp->Send(GetReportToHeader());
resp->Send(GetNELHeader());
resp->Send("\r\n");
resp->Done();
}
void RespondWithoutHeaders(net::test_server::ControllableHttpResponse* resp) {
resp->WaitForRequest();
resp->Send("HTTP/1.1 200 OK\r\n");
resp->Send("\r\n");
resp->Done();
}
bool AreReportingAndNelEnabled() const {
switch (GetParam().network_context_type) {
case NetworkContextType::kSystem:
case NetworkContextType::kSafeBrowsing:
return false;
case NetworkContextType::kProfile:
case NetworkContextType::kIncognitoProfile:
case NetworkContextType::kOnDiskApp:
case NetworkContextType::kInMemoryApp:
case NetworkContextType::kOnDiskAppWithIncognitoProfile:
return true;
}
}
bool ExpectPersistenceEnabled() const {
switch (GetParam().network_context_type) {
case NetworkContextType::kSystem:
case NetworkContextType::kSafeBrowsing:
case NetworkContextType::kIncognitoProfile:
case NetworkContextType::kInMemoryApp:
case NetworkContextType::kOnDiskAppWithIncognitoProfile:
return false;
case NetworkContextType::kProfile:
case NetworkContextType::kOnDiskApp:
return true;
}
}
private:
net::EmbeddedTestServer https_server_;
};
namespace {
constexpr char kReportingEnabledURL[] = "https://localhost/page";
constexpr char kCrossOriginReportingEnabledURL[] = "https://localhost:444/page";
} // namespace
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationReportingAndNelBrowserTest,
PRE_PersistReportingAndNel) {
if (IsRestartStateWithInProcessNetworkService() ||
!AreReportingAndNelEnabled()) {
return;
}
net::test_server::ControllableHttpResponse original_response(http_server(),
"/page");
net::test_server::ControllableHttpResponse upload_response(http_server(),
"/upload");
http_server()->StartAcceptingConnections();
// Make a request to /page and respond with Report-To and NEL headers, which
// will turn on NEL for the localhost:443 origin.
std::unique_ptr<RequestState> request =
NewSimpleRequest(GURL(kReportingEnabledURL));
RespondWithHeaders(&original_response);
request->helper.WaitForCallback();
// Wait for a request to /upload, which should happen because the previous
// request precipitated a NEL report.
upload_response.WaitForRequest();
EXPECT_EQ(net::test_server::METHOD_POST,
upload_response.http_request()->method);
}
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationReportingAndNelBrowserTest,
PersistReportingAndNel) {
if (IsRestartStateWithInProcessNetworkService() ||
!AreReportingAndNelEnabled()) {
return;
}
net::test_server::ControllableHttpResponse original_response(http_server(),
"/page");
net::test_server::ControllableHttpResponse cross_origin_response(
http_server(), "/page");
net::test_server::ControllableHttpResponse upload_response(http_server(),
"/upload");
http_server()->StartAcceptingConnections();
// Make a request that will only precipitate a NEL report if we loaded a NEL
// policy from the store.
std::unique_ptr<RequestState> request =
NewSimpleRequest(GURL(kReportingEnabledURL));
RespondWithoutHeaders(&original_response);
request->helper.WaitForCallback();
// Make another request that will generate another upload to the same
// collector. If the previous request didn't generate a NEL report (e.g.,
// because persistence is disabled) then this'll be first upload to the NEL
// collector. Note that because this upload is to a different origin than the
// request, Reporting will make a CORS preflight request, which is what we
// look for.
std::unique_ptr<RequestState> cross_origin_request =
NewSimpleRequest(GURL(kCrossOriginReportingEnabledURL));
RespondWithHeaders(&cross_origin_response);
cross_origin_request->helper.WaitForCallback();
// Wait for a request to /upload, which should happen because the previous
// requests precipitated NEL reports.
//
// We assume that Reporting/NEL will send reports in the order we made the
// requests above, which seems to be the case today but may not necessarily
// true in general.
upload_response.WaitForRequest();
if (ExpectPersistenceEnabled()) {
// A NEL upload about https://localhost
EXPECT_EQ(net::test_server::METHOD_POST,
upload_response.http_request()->method);
} else {
// A CORS preflight request before a cross-origin NEL upload about
// https://localhost:444
EXPECT_EQ(net::test_server::METHOD_OPTIONS,
upload_response.http_request()->method);
}
}
#endif // BUILDFLAG(ENABLE_REPORTING)
// Instantiates tests with a prefix indicating which NetworkContext is being
// tested, and a suffix of "/0" if the network service is enabled, "/1" if it's
// enabled and restarted.
#define TEST_CASES(network_context_type) \
TestCase({NetworkServiceState::kEnabled, network_context_type}), \
TestCase({NetworkServiceState::kRestarted, network_context_type})
#if BUILDFLAG(ENABLE_EXTENSIONS)
#define INSTANTIATE_EXTENSION_TESTS(TestFixture) \
INSTANTIATE_TEST_SUITE_P( \
OnDiskApp, TestFixture, \
::testing::Values(TEST_CASES(NetworkContextType::kOnDiskApp))); \
\
INSTANTIATE_TEST_SUITE_P( \
InMemoryApp, TestFixture, \
::testing::Values(TEST_CASES(NetworkContextType::kInMemoryApp))); \
\
INSTANTIATE_TEST_SUITE_P( \
OnDiskAppWithIncognitoProfile, TestFixture, \
::testing::Values( \
TEST_CASES(NetworkContextType::kOnDiskAppWithIncognitoProfile)));
#else // !BUILDFLAG(ENABLE_EXTENSIONS)
#define INSTANTIATE_EXTENSION_TESTS(TestFixture)
#endif // !BUILDFLAG(ENABLE_EXTENSIONS)
#define INSTANTIATE_TEST_CASES_FOR_TEST_FIXTURE(TestFixture) \
INSTANTIATE_EXTENSION_TESTS(TestFixture) \
INSTANTIATE_TEST_SUITE_P( \
SystemNetworkContext, TestFixture, \
::testing::Values(TEST_CASES(NetworkContextType::kSystem))); \
\
INSTANTIATE_TEST_SUITE_P( \
SafeBrowsingNetworkContext, TestFixture, \
::testing::Values(TEST_CASES(NetworkContextType::kSafeBrowsing))); \
\
INSTANTIATE_TEST_SUITE_P( \
ProfileMainNetworkContext, TestFixture, \
::testing::Values(TEST_CASES(NetworkContextType::kProfile))); \
\
INSTANTIATE_TEST_SUITE_P( \
IncognitoProfileMainNetworkContext, TestFixture, \
::testing::Values(TEST_CASES(NetworkContextType::kIncognitoProfile)))
INSTANTIATE_TEST_CASES_FOR_TEST_FIXTURE(NetworkContextConfigurationBrowserTest);
INSTANTIATE_TEST_CASES_FOR_TEST_FIXTURE(
NetworkContextConfigurationFixedPortBrowserTest);
INSTANTIATE_TEST_CASES_FOR_TEST_FIXTURE(
NetworkContextConfigurationProxyOnStartBrowserTest);
INSTANTIATE_TEST_CASES_FOR_TEST_FIXTURE(
NetworkContextConfigurationHttpPacBrowserTest);
INSTANTIATE_TEST_CASES_FOR_TEST_FIXTURE(
NetworkContextConfigurationFilePacBrowserTest);
INSTANTIATE_TEST_CASES_FOR_TEST_FIXTURE(
NetworkContextConfigurationDataPacBrowserTest);
INSTANTIATE_TEST_CASES_FOR_TEST_FIXTURE(
NetworkContextConfigurationProxySettingsBrowserTest);
INSTANTIATE_TEST_CASES_FOR_TEST_FIXTURE(
NetworkContextConfigurationManagedProxySettingsBrowserTest);
#if BUILDFLAG(ENABLE_REPORTING)
INSTANTIATE_TEST_CASES_FOR_TEST_FIXTURE(
NetworkContextConfigurationReportingAndNelBrowserTest);
#endif
} // namespace