
The reland fixes previous breakage by disabling the file size checks for Fuchsia builds in NetworkServiceBoundedNetLogBrowserTest. This CL adds support for a maximum file size flag when invoking net-log export from the command line. The flag allows a user to specify the max size in MB for a net-log file. Bug: 1463983, 1466701 Change-Id: I529d3efed9fa9cf312a0d091421becc8b2f89dd4 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4706170 Reviewed-by: Bo Liu <boliu@chromium.org> Reviewed-by: Matt Menke <mmenke@chromium.org> Commit-Queue: Steven Bingler <bingler@chromium.org> Reviewed-by: Will Harris <wfh@chromium.org> Cr-Commit-Position: refs/heads/main@{#1175655}
1843 lines
77 KiB
C++
1843 lines
77 KiB
C++
// 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 "base/base_paths.h"
|
|
#include "base/command_line.h"
|
|
#include "base/feature_list.h"
|
|
#include "base/files/file.h"
|
|
#include "base/files/file_enumerator.h"
|
|
#include "base/files/file_path.h"
|
|
#include "base/files/file_util.h"
|
|
#include "base/functional/bind.h"
|
|
#include "base/json/json_reader.h"
|
|
#include "base/json/json_writer.h"
|
|
#include "base/json/values_util.h"
|
|
#include "base/memory/ref_counted_memory.h"
|
|
#include "base/strings/string_number_conversions.h"
|
|
#include "base/test/bind.h"
|
|
#include "base/test/metrics/histogram_tester.h"
|
|
#include "base/test/scoped_feature_list.h"
|
|
#include "base/test/test_timeouts.h"
|
|
#include "base/threading/platform_thread.h"
|
|
#include "base/time/time.h"
|
|
#include "build/build_config.h"
|
|
#include "build/buildflag.h"
|
|
#include "content/browser/network/network_service_util_internal.h"
|
|
#include "content/browser/network_service_instance_impl.h"
|
|
#include "content/browser/storage_partition_impl.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/url_data_source.h"
|
|
#include "content/public/browser/web_contents.h"
|
|
#include "content/public/browser/web_contents_observer.h"
|
|
#include "content/public/browser/web_ui_controller.h"
|
|
#include "content/public/browser/web_ui_controller_factory.h"
|
|
#include "content/public/common/bindings_policy.h"
|
|
#include "content/public/common/content_features.h"
|
|
#include "content/public/common/content_switches.h"
|
|
#include "content/public/common/url_utils.h"
|
|
#include "content/public/test/browser_test.h"
|
|
#include "content/public/test/browser_test_utils.h"
|
|
#include "content/public/test/content_browser_test.h"
|
|
#include "content/public/test/content_browser_test_utils.h"
|
|
#include "content/public/test/simple_url_loader_test_helper.h"
|
|
#include "content/public/test/test_utils.h"
|
|
#include "content/shell/browser/shell.h"
|
|
#include "content/test/content_browser_test_utils_internal.h"
|
|
#include "mojo/public/cpp/bindings/remote.h"
|
|
#include "mojo/public/cpp/bindings/sync_call_restrictions.h"
|
|
#include "net/base/features.h"
|
|
#include "net/cookies/cookie_util.h"
|
|
#include "net/disk_cache/disk_cache.h"
|
|
#include "net/dns/mock_host_resolver.h"
|
|
#include "net/http/http_response_headers.h"
|
|
#include "net/test/embedded_test_server/default_handlers.h"
|
|
#include "net/test/embedded_test_server/http_request.h"
|
|
#include "net/test/gtest_util.h"
|
|
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
|
|
#include "sandbox/policy/features.h"
|
|
#include "services/cert_verifier/public/mojom/cert_verifier_service_factory.mojom.h"
|
|
#include "services/network/public/cpp/features.h"
|
|
#include "services/network/public/cpp/network_switches.h"
|
|
#include "services/network/public/cpp/resource_request.h"
|
|
#include "services/network/public/cpp/simple_url_loader.h"
|
|
#include "services/network/public/mojom/network_service.mojom.h"
|
|
#include "services/network/public/mojom/network_service_test.mojom.h"
|
|
#include "services/network/test/udp_socket_test_util.h"
|
|
#include "sql/database.h"
|
|
#include "sql/sql_features.h"
|
|
|
|
#if BUILDFLAG(IS_ANDROID)
|
|
#include "base/android/application_status_listener.h"
|
|
#endif
|
|
|
|
#if BUILDFLAG(IS_WIN)
|
|
#include "sandbox/policy/features.h"
|
|
#endif
|
|
|
|
namespace content {
|
|
|
|
namespace {
|
|
|
|
class WebUITestWebUIControllerFactory : public WebUIControllerFactory {
|
|
public:
|
|
std::unique_ptr<WebUIController> CreateWebUIControllerForURL(
|
|
WebUI* web_ui,
|
|
const GURL& url) override {
|
|
std::string foo(url.path());
|
|
if (url.path() == "/nobinding/")
|
|
web_ui->SetBindings(BINDINGS_POLICY_NONE);
|
|
return HasWebUIScheme(url) ? std::make_unique<WebUIController>(web_ui)
|
|
: nullptr;
|
|
}
|
|
WebUI::TypeID GetWebUIType(BrowserContext* browser_context,
|
|
const GURL& url) override {
|
|
return HasWebUIScheme(url) ? reinterpret_cast<WebUI::TypeID>(1) : nullptr;
|
|
}
|
|
bool UseWebUIForURL(BrowserContext* browser_context,
|
|
const GURL& url) override {
|
|
return HasWebUIScheme(url);
|
|
}
|
|
};
|
|
|
|
class TestWebUIDataSource : public URLDataSource {
|
|
public:
|
|
TestWebUIDataSource() {}
|
|
|
|
TestWebUIDataSource(const TestWebUIDataSource&) = delete;
|
|
TestWebUIDataSource& operator=(const TestWebUIDataSource&) = delete;
|
|
|
|
~TestWebUIDataSource() override {}
|
|
|
|
std::string GetSource() override { return "webui"; }
|
|
|
|
void StartDataRequest(const GURL& url,
|
|
const WebContents::Getter& wc_getter,
|
|
URLDataSource::GotDataCallback callback) override {
|
|
std::move(callback).Run(base::MakeRefCounted<base::RefCountedString>(
|
|
std::string("<html><body>Foo</body></html>")));
|
|
}
|
|
|
|
std::string GetMimeType(const GURL& url) override { return "text/html"; }
|
|
};
|
|
|
|
class NetworkServiceBrowserTest : public ContentBrowserTest {
|
|
public:
|
|
NetworkServiceBrowserTest() {
|
|
EXPECT_TRUE(embedded_test_server()->Start());
|
|
EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
|
|
|
|
WebUIControllerFactory::RegisterFactory(&factory_);
|
|
}
|
|
|
|
NetworkServiceBrowserTest(const NetworkServiceBrowserTest&) = delete;
|
|
NetworkServiceBrowserTest& operator=(const NetworkServiceBrowserTest&) =
|
|
delete;
|
|
|
|
bool FetchResource(const GURL& url, bool synchronous = false) {
|
|
if (!url.is_valid())
|
|
return false;
|
|
std::string script = JsReplace(
|
|
"var xhr = new XMLHttpRequest();"
|
|
"xhr.open('GET', $1, $2);"
|
|
"new Promise(resolve => {"
|
|
" xhr.onload = function (e) {"
|
|
" if (xhr.readyState === 4) {"
|
|
" resolve(xhr.status === 200);"
|
|
" }"
|
|
" };"
|
|
" xhr.onerror = function () {"
|
|
" resolve(false);"
|
|
" };"
|
|
" try {"
|
|
" xhr.send(null);"
|
|
" } catch (error) {"
|
|
" resolve(false);"
|
|
" }"
|
|
"});",
|
|
url, !synchronous);
|
|
|
|
EvalJsResult result = EvalJs(shell(), script);
|
|
if (!result.error.empty()) {
|
|
return false;
|
|
}
|
|
return result.ExtractBool();
|
|
}
|
|
|
|
bool CheckCanLoadHttp() {
|
|
return FetchResource(embedded_test_server()->GetURL("/echo"),
|
|
/*synchronous=*/false);
|
|
}
|
|
|
|
void SetUpOnMainThread() override {
|
|
URLDataSource::Add(shell()->web_contents()->GetBrowserContext(),
|
|
std::make_unique<TestWebUIDataSource>());
|
|
}
|
|
|
|
void SetUpCommandLine(base::CommandLine* command_line) override {
|
|
// Since we assume exploited renderer process, it can bypass the same origin
|
|
// policy at will. Simulate that by passing the disable-web-security flag.
|
|
command_line->AppendSwitch(switches::kDisableWebSecurity);
|
|
IsolateAllSitesForTesting(command_line);
|
|
}
|
|
|
|
base::FilePath GetCacheDirectory() { return temp_dir_.GetPath(); }
|
|
|
|
base::FilePath GetCacheIndexDirectory() {
|
|
return GetCacheDirectory()
|
|
.AppendASCII("Cache_Data")
|
|
.AppendASCII("index-dir");
|
|
}
|
|
|
|
void LoadURL(const GURL& url,
|
|
network::mojom::URLLoaderFactory* loader_factory) {
|
|
std::unique_ptr<network::ResourceRequest> request =
|
|
std::make_unique<network::ResourceRequest>();
|
|
request->url = url;
|
|
url::Origin origin = url::Origin::Create(url);
|
|
request->trusted_params = network::ResourceRequest::TrustedParams();
|
|
request->trusted_params->isolation_info =
|
|
net::IsolationInfo::CreateForInternalRequest(origin);
|
|
request->site_for_cookies =
|
|
request->trusted_params->isolation_info.site_for_cookies();
|
|
|
|
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.GetCallback());
|
|
simple_loader_helper.WaitForCallback();
|
|
ASSERT_TRUE(simple_loader_helper.response_body());
|
|
}
|
|
|
|
private:
|
|
WebUITestWebUIControllerFactory factory_;
|
|
base::ScopedTempDir temp_dir_;
|
|
};
|
|
|
|
#if BUILDFLAG(IS_ANDROID)
|
|
#define MAYBE_WebUIBindingsNoHttp DISABLED_WebUIBindingsNoHttp
|
|
#else
|
|
#define MAYBE_WebUIBindingsNoHttp WebUIBindingsNoHttp
|
|
#endif
|
|
|
|
// Verifies that WebUI pages with WebUI bindings can't make network requests.
|
|
IN_PROC_BROWSER_TEST_F(NetworkServiceBrowserTest, MAYBE_WebUIBindingsNoHttp) {
|
|
GURL test_url(GetWebUIURL("webui/"));
|
|
EXPECT_TRUE(NavigateToURL(shell(), test_url));
|
|
RenderProcessHostBadIpcMessageWaiter kill_waiter(
|
|
shell()->web_contents()->GetPrimaryMainFrame()->GetProcess());
|
|
ASSERT_FALSE(CheckCanLoadHttp());
|
|
EXPECT_EQ(bad_message::RPH_MOJO_PROCESS_ERROR, kill_waiter.Wait());
|
|
}
|
|
|
|
// Verifies that WebUI pages without WebUI bindings can make network requests.
|
|
IN_PROC_BROWSER_TEST_F(NetworkServiceBrowserTest, NoWebUIBindingsHttp) {
|
|
GURL test_url(GetWebUIURL("webui/nobinding/"));
|
|
EXPECT_TRUE(NavigateToURL(shell(), test_url));
|
|
ASSERT_TRUE(CheckCanLoadHttp());
|
|
}
|
|
|
|
// Verifies the filesystem URLLoaderFactory's check, using
|
|
// ChildProcessSecurityPolicyImpl::CanRequestURL is properly rejected.
|
|
IN_PROC_BROWSER_TEST_F(NetworkServiceBrowserTest,
|
|
FileSystemBindingsCorrectOrigin) {
|
|
GURL test_url(GetWebUIURL("webui/nobinding/"));
|
|
EXPECT_TRUE(NavigateToURL(shell(), test_url));
|
|
|
|
// Note: must be filesystem scheme (obviously).
|
|
// file: is not a safe web scheme (see IsWebSafeScheme),
|
|
// and /etc/passwd fails the CanCommitURL check.
|
|
GURL file_url("filesystem:file:///etc/passwd");
|
|
EXPECT_FALSE(FetchResource(file_url));
|
|
}
|
|
|
|
IN_PROC_BROWSER_TEST_F(NetworkServiceBrowserTest,
|
|
SimpleUrlLoader_NoAuthWhenNoWebContents) {
|
|
auto request = std::make_unique<network::ResourceRequest>();
|
|
request->url = embedded_test_server()->GetURL("/auth-basic?password=");
|
|
auto loader = network::SimpleURLLoader::Create(std::move(request),
|
|
TRAFFIC_ANNOTATION_FOR_TESTS);
|
|
auto loader_factory = shell()
|
|
->web_contents()
|
|
->GetBrowserContext()
|
|
->GetDefaultStoragePartition()
|
|
->GetURLLoaderFactoryForBrowserProcess();
|
|
scoped_refptr<net::HttpResponseHeaders> headers;
|
|
base::RunLoop loop;
|
|
loader->DownloadHeadersOnly(
|
|
loader_factory.get(),
|
|
base::BindOnce(
|
|
[](base::OnceClosure quit_closure,
|
|
scoped_refptr<net::HttpResponseHeaders>* rh_out,
|
|
scoped_refptr<net::HttpResponseHeaders> rh_in) {
|
|
*rh_out = rh_in;
|
|
std::move(quit_closure).Run();
|
|
},
|
|
loop.QuitClosure(), &headers));
|
|
loop.Run();
|
|
ASSERT_TRUE(headers.get());
|
|
ASSERT_EQ(headers->response_code(), 401);
|
|
}
|
|
|
|
#if BUILDFLAG(IS_ANDROID)
|
|
IN_PROC_BROWSER_TEST_F(NetworkServiceBrowserTest,
|
|
HttpCacheWrittenToDiskOnApplicationStateChange) {
|
|
base::ScopedAllowBlockingForTesting allow_blocking;
|
|
|
|
// Create network context with cache pointing to the temp cache dir.
|
|
mojo::Remote<network::mojom::NetworkContext> network_context;
|
|
network::mojom::NetworkContextParamsPtr context_params =
|
|
network::mojom::NetworkContextParams::New();
|
|
context_params->cert_verifier_params = GetCertVerifierParams(
|
|
cert_verifier::mojom::CertVerifierCreationParams::New());
|
|
context_params->file_paths = network::mojom::NetworkContextFilePaths::New();
|
|
context_params->file_paths->http_cache_directory = GetCacheDirectory();
|
|
CreateNetworkContextInNetworkService(
|
|
network_context.BindNewPipeAndPassReceiver(), std::move(context_params));
|
|
|
|
network::mojom::URLLoaderFactoryParamsPtr params =
|
|
network::mojom::URLLoaderFactoryParams::New();
|
|
params->process_id = network::mojom::kBrowserProcessId;
|
|
params->automatically_assign_isolation_info = true;
|
|
params->is_corb_enabled = false;
|
|
params->is_trusted = true;
|
|
mojo::Remote<network::mojom::URLLoaderFactory> loader_factory;
|
|
network_context->CreateURLLoaderFactory(
|
|
loader_factory.BindNewPipeAndPassReceiver(), std::move(params));
|
|
|
|
// Load a URL and check the cache index size.
|
|
LoadURL(embedded_test_server()->GetURL("/cachetime"), loader_factory.get());
|
|
int64_t directory_size = base::ComputeDirectorySize(GetCacheIndexDirectory());
|
|
|
|
// Load another URL, cache index should not be written to disk yet.
|
|
LoadURL(embedded_test_server()->GetURL("/cachetime?foo"),
|
|
loader_factory.get());
|
|
EXPECT_EQ(directory_size,
|
|
base::ComputeDirectorySize(GetCacheIndexDirectory()));
|
|
|
|
// After application state changes, cache index should be written to disk.
|
|
base::android::ApplicationStatusListener::NotifyApplicationStateChange(
|
|
base::android::APPLICATION_STATE_HAS_STOPPED_ACTIVITIES);
|
|
base::RunLoop().RunUntilIdle();
|
|
FlushNetworkServiceInstanceForTesting();
|
|
disk_cache::FlushCacheThreadForTesting();
|
|
|
|
EXPECT_GT(base::ComputeDirectorySize(GetCacheIndexDirectory()),
|
|
directory_size);
|
|
}
|
|
#endif // BUILDFLAG(IS_ANDROID)
|
|
|
|
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX)
|
|
class NetworkConnectionObserver
|
|
: public network::NetworkConnectionTracker::NetworkConnectionObserver {
|
|
public:
|
|
NetworkConnectionObserver() {
|
|
content::GetNetworkConnectionTracker()->AddNetworkConnectionObserver(this);
|
|
content::GetNetworkConnectionTracker()->GetConnectionType(
|
|
&last_connection_type_,
|
|
base::BindOnce(&NetworkConnectionObserver::OnConnectionChanged,
|
|
base::Unretained(this)));
|
|
}
|
|
|
|
~NetworkConnectionObserver() override {
|
|
content::GetNetworkConnectionTracker()->RemoveNetworkConnectionObserver(
|
|
this);
|
|
}
|
|
|
|
void WaitForConnectionType(network::mojom::ConnectionType type) {
|
|
type_to_wait_for_ = type;
|
|
if (last_connection_type_ == type_to_wait_for_)
|
|
return;
|
|
|
|
run_loop_ = std::make_unique<base::RunLoop>();
|
|
run_loop_->Run();
|
|
}
|
|
|
|
// network::NetworkConnectionTracker::NetworkConnectionObserver:
|
|
void OnConnectionChanged(network::mojom::ConnectionType type) override {
|
|
last_connection_type_ = type;
|
|
if (run_loop_ && type_to_wait_for_ == type)
|
|
run_loop_->Quit();
|
|
}
|
|
|
|
private:
|
|
network::mojom::ConnectionType type_to_wait_for_ =
|
|
network::mojom::ConnectionType::CONNECTION_UNKNOWN;
|
|
network::mojom::ConnectionType last_connection_type_ =
|
|
network::mojom::ConnectionType::CONNECTION_UNKNOWN;
|
|
std::unique_ptr<base::RunLoop> run_loop_;
|
|
};
|
|
|
|
class NetworkServiceConnectionTypeSyncedBrowserTest
|
|
: public NetworkServiceBrowserTest {
|
|
public:
|
|
NetworkServiceConnectionTypeSyncedBrowserTest() {
|
|
#if BUILDFLAG(IS_LINUX)
|
|
scoped_feature_list_.InitAndEnableFeature(
|
|
net::features::kAddressTrackerLinuxIsProxied);
|
|
#else
|
|
scoped_feature_list_.Init();
|
|
#endif
|
|
ForceOutOfProcessNetworkService();
|
|
}
|
|
|
|
private:
|
|
base::test::ScopedFeatureList scoped_feature_list_;
|
|
};
|
|
|
|
IN_PROC_BROWSER_TEST_F(NetworkServiceConnectionTypeSyncedBrowserTest,
|
|
ConnectionTypeChangeSyncedToNetworkProcess) {
|
|
NetworkConnectionObserver observer;
|
|
net::NetworkChangeNotifier::NotifyObserversOfConnectionTypeChangeForTests(
|
|
net::NetworkChangeNotifier::CONNECTION_WIFI);
|
|
observer.WaitForConnectionType(
|
|
network::mojom::ConnectionType::CONNECTION_WIFI);
|
|
|
|
net::NetworkChangeNotifier::NotifyObserversOfConnectionTypeChangeForTests(
|
|
net::NetworkChangeNotifier::CONNECTION_ETHERNET);
|
|
observer.WaitForConnectionType(
|
|
network::mojom::ConnectionType::CONNECTION_ETHERNET);
|
|
}
|
|
#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX)
|
|
|
|
class NetworkServiceOutOfProcessBrowserTest : public NetworkServiceBrowserTest {
|
|
public:
|
|
NetworkServiceOutOfProcessBrowserTest() { ForceOutOfProcessNetworkService(); }
|
|
|
|
void SetUpOnMainThread() override {
|
|
ASSERT_TRUE(IsOutOfProcessNetworkService());
|
|
}
|
|
|
|
private:
|
|
base::test::ScopedFeatureList scoped_feature_list_;
|
|
};
|
|
|
|
IN_PROC_BROWSER_TEST_F(NetworkServiceOutOfProcessBrowserTest,
|
|
MemoryPressureSentToNetworkProcess) {
|
|
mojo::Remote<network::mojom::NetworkServiceTest> network_service_test;
|
|
GetNetworkService()->BindTestInterfaceForTesting(
|
|
network_service_test.BindNewPipeAndPassReceiver());
|
|
// TODO(crbug.com/901026): Make sure the network process is started to avoid a
|
|
// deadlock on Android.
|
|
network_service_test.FlushForTesting();
|
|
|
|
mojo::ScopedAllowSyncCallForTesting allow_sync_call;
|
|
base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level =
|
|
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
|
|
network_service_test->GetLatestMemoryPressureLevel(&memory_pressure_level);
|
|
EXPECT_EQ(memory_pressure_level,
|
|
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE);
|
|
|
|
base::MemoryPressureListener::NotifyMemoryPressure(
|
|
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL);
|
|
base::RunLoop().RunUntilIdle();
|
|
FlushNetworkServiceInstanceForTesting();
|
|
|
|
network_service_test->GetLatestMemoryPressureLevel(&memory_pressure_level);
|
|
EXPECT_EQ(memory_pressure_level,
|
|
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL);
|
|
}
|
|
|
|
// Verifies that sync XHRs don't hang if the network service crashes.
|
|
IN_PROC_BROWSER_TEST_F(NetworkServiceOutOfProcessBrowserTest, SyncXHROnCrash) {
|
|
net::EmbeddedTestServer http_server;
|
|
http_server.AddDefaultHandlers(GetTestDataFilePath());
|
|
http_server.RegisterRequestMonitor(base::BindLambdaForTesting(
|
|
[&](const net::test_server::HttpRequest& request) {
|
|
if (request.relative_url == "/hung") {
|
|
GetUIThreadTaskRunner({})->PostTask(
|
|
FROM_HERE,
|
|
base::BindOnce(&BrowserTestBase::SimulateNetworkServiceCrash,
|
|
base::Unretained(this)));
|
|
}
|
|
}));
|
|
EXPECT_TRUE(http_server.Start());
|
|
|
|
EXPECT_TRUE(NavigateToURL(shell(), http_server.GetURL("/empty.html")));
|
|
|
|
FetchResource(http_server.GetURL("/hung"), true);
|
|
// If the renderer is hung the test will hang.
|
|
}
|
|
|
|
// Verifies that sync cookie calls don't hang if the network service crashes.
|
|
IN_PROC_BROWSER_TEST_F(NetworkServiceOutOfProcessBrowserTest,
|
|
SyncCookieGetOnCrash) {
|
|
mojo::Remote<network::mojom::NetworkServiceTest> network_service_test;
|
|
GetNetworkService()->BindTestInterfaceForTesting(
|
|
network_service_test.BindNewPipeAndPassReceiver());
|
|
network_service_test->CrashOnGetCookieList();
|
|
|
|
EXPECT_TRUE(
|
|
NavigateToURL(shell(), embedded_test_server()->GetURL("/empty.html")));
|
|
|
|
ASSERT_TRUE(content::ExecJs(shell()->web_contents(), "document.cookie"));
|
|
// If the renderer is hung the test will hang.
|
|
}
|
|
|
|
// Tests that CORS is performed by the network service when |factory_override|
|
|
// is used.
|
|
IN_PROC_BROWSER_TEST_F(NetworkServiceBrowserTest, FactoryOverride) {
|
|
class TestURLLoaderFactory final : public network::mojom::URLLoaderFactory {
|
|
public:
|
|
void CreateLoaderAndStart(
|
|
mojo::PendingReceiver<network::mojom::URLLoader> receiver,
|
|
int32_t request_id,
|
|
uint32_t options,
|
|
const network::ResourceRequest& resource_request,
|
|
mojo::PendingRemote<network::mojom::URLLoaderClient> pending_client,
|
|
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation)
|
|
override {
|
|
mojo::Remote<network::mojom::URLLoaderClient> client(
|
|
std::move(pending_client));
|
|
EXPECT_EQ(resource_request.url,
|
|
GURL("https://www.example.com/hello.txt"));
|
|
if (resource_request.method == "OPTIONS") {
|
|
has_received_preflight_ = true;
|
|
auto response = network::mojom::URLResponseHead::New();
|
|
response->headers =
|
|
base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200 OK");
|
|
response->headers->SetHeader("access-control-allow-origin",
|
|
"https://www2.example.com");
|
|
response->headers->SetHeader("access-control-allow-methods", "*");
|
|
client->OnReceiveResponse(std::move(response),
|
|
mojo::ScopedDataPipeConsumerHandle(),
|
|
absl::nullopt);
|
|
} else if (resource_request.method == "custom-method") {
|
|
has_received_request_ = true;
|
|
auto response = network::mojom::URLResponseHead::New();
|
|
response->headers = base::MakeRefCounted<net::HttpResponseHeaders>(
|
|
"HTTP/1.1 202 Accepted");
|
|
response->headers->SetHeader("access-control-allow-origin",
|
|
"https://www2.example.com");
|
|
client->OnReceiveResponse(std::move(response),
|
|
mojo::ScopedDataPipeConsumerHandle(),
|
|
absl::nullopt);
|
|
client->OnComplete(network::URLLoaderCompletionStatus(net::OK));
|
|
} else {
|
|
client->OnComplete(
|
|
network::URLLoaderCompletionStatus(net::ERR_INVALID_ARGUMENT));
|
|
}
|
|
}
|
|
void Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver)
|
|
override {
|
|
NOTREACHED();
|
|
}
|
|
|
|
bool has_received_preflight() const { return has_received_preflight_; }
|
|
bool has_received_request() const { return has_received_request_; }
|
|
|
|
private:
|
|
bool has_received_preflight_ = false;
|
|
bool has_received_request_ = false;
|
|
};
|
|
|
|
// Create a request that will trigger a CORS preflight request.
|
|
auto request = std::make_unique<network::ResourceRequest>();
|
|
request->url = GURL("https://www.example.com/hello.txt");
|
|
request->mode = network::mojom::RequestMode::kCors;
|
|
request->credentials_mode = network::mojom::CredentialsMode::kSameOrigin;
|
|
request->method = "custom-method";
|
|
request->request_initiator =
|
|
url::Origin::Create(GURL("https://www2.example.com/"));
|
|
|
|
// Inject TestURLLoaderFactory as the factory override.
|
|
auto test_loader_factory = std::make_unique<TestURLLoaderFactory>();
|
|
mojo::Receiver<network::mojom::URLLoaderFactory> test_loader_factory_receiver(
|
|
test_loader_factory.get());
|
|
mojo::Remote<network::mojom::URLLoaderFactory> loader_factory_remote;
|
|
auto loader = network::SimpleURLLoader::Create(std::move(request),
|
|
TRAFFIC_ANNOTATION_FOR_TESTS);
|
|
auto params = network::mojom::URLLoaderFactoryParams::New();
|
|
params->process_id = 0;
|
|
params->factory_override = network::mojom::URLLoaderFactoryOverride::New();
|
|
params->factory_override->overriding_factory =
|
|
test_loader_factory_receiver.BindNewPipeAndPassRemote();
|
|
shell()
|
|
->web_contents()
|
|
->GetBrowserContext()
|
|
->GetDefaultStoragePartition()
|
|
->GetNetworkContext()
|
|
->CreateURLLoaderFactory(
|
|
loader_factory_remote.BindNewPipeAndPassReceiver(),
|
|
std::move(params));
|
|
scoped_refptr<net::HttpResponseHeaders> headers;
|
|
|
|
// Perform the request.
|
|
base::RunLoop loop;
|
|
loader->DownloadHeadersOnly(
|
|
loader_factory_remote.get(),
|
|
base::BindLambdaForTesting(
|
|
[&](scoped_refptr<net::HttpResponseHeaders> passed_headers) {
|
|
headers = passed_headers;
|
|
loop.Quit();
|
|
}));
|
|
loop.Run();
|
|
ASSERT_TRUE(headers.get());
|
|
EXPECT_EQ(headers->response_code(), 202);
|
|
EXPECT_TRUE(test_loader_factory->has_received_preflight());
|
|
EXPECT_TRUE(test_loader_factory->has_received_request());
|
|
}
|
|
|
|
// Android doesn't support PRE_ tests.
|
|
// TODO(wfh): Enable this test when https://crbug.com/1257820 is fixed.
|
|
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_FUCHSIA)
|
|
class NetworkServiceBrowserCacheResetTest : public NetworkServiceBrowserTest {
|
|
public:
|
|
NetworkServiceBrowserCacheResetTest() = default;
|
|
|
|
protected:
|
|
void StoreUrl(const GURL& url) {
|
|
base::ScopedAllowBlockingForTesting allow_blocking;
|
|
|
|
base::FilePath data_file =
|
|
shell()->web_contents()->GetBrowserContext()->GetPath().Append(
|
|
FILE_PATH_LITERAL("TestData"));
|
|
std::string data;
|
|
base::JSONWriter::Write(base::Value(url.spec()), &data);
|
|
EXPECT_TRUE(base::WriteFile(data_file, data));
|
|
}
|
|
|
|
void RetrieveUrl(GURL& url) {
|
|
base::ScopedAllowBlockingForTesting allow_blocking;
|
|
|
|
base::FilePath data_file =
|
|
shell()->web_contents()->GetBrowserContext()->GetPath().Append(
|
|
FILE_PATH_LITERAL("TestData"));
|
|
std::string data;
|
|
EXPECT_TRUE(base::ReadFileToString(data_file, &data));
|
|
auto json_data = base::JSONReader::Read(data);
|
|
ASSERT_TRUE(json_data.has_value());
|
|
url = GURL(json_data->GetString());
|
|
EXPECT_TRUE(url.is_valid());
|
|
}
|
|
|
|
base::FilePath GetNetworkContextPath() {
|
|
return shell()->web_contents()->GetBrowserContext()->GetPath().Append(
|
|
FILE_PATH_LITERAL("TestContext"));
|
|
}
|
|
|
|
base::FilePath GetNetworkContextCachePath() {
|
|
return GetNetworkContextPath().Append(FILE_PATH_LITERAL("Cache"));
|
|
}
|
|
|
|
// Creates a Network context and attempts to make a request to a resource that
|
|
// is cacheable. Returns the net error code. If `load_only_from_cache` is
|
|
// specified then the request will fail if the resource cannot be served from
|
|
// the cache. `url` specifies the URL to connect to on the
|
|
// embedded_test_server host which does not need to have a server actively
|
|
// listening on it if `load_only_from_cache` is true.
|
|
int MakeNetworkContentAndLoadUrl(bool reset_cache,
|
|
bool load_only_from_cache,
|
|
const GURL& url) {
|
|
auto file_paths = network::mojom::NetworkContextFilePaths::New();
|
|
base::FilePath context_path = GetNetworkContextPath();
|
|
file_paths->data_directory = context_path.Append(FILE_PATH_LITERAL("Data"));
|
|
file_paths->unsandboxed_data_path = context_path;
|
|
file_paths->trigger_migration = true;
|
|
|
|
network::mojom::NetworkContextParamsPtr context_params =
|
|
network::mojom::NetworkContextParams::New();
|
|
context_params->file_paths = std::move(file_paths);
|
|
context_params->cert_verifier_params = GetCertVerifierParams(
|
|
cert_verifier::mojom::CertVerifierCreationParams::New());
|
|
context_params->reset_http_cache_backend = reset_cache;
|
|
context_params->http_cache_enabled = true;
|
|
context_params->file_paths->http_cache_directory =
|
|
GetNetworkContextCachePath();
|
|
|
|
mojo::Remote<network::mojom::NetworkContext> network_context;
|
|
content::CreateNetworkContextInNetworkService(
|
|
network_context.BindNewPipeAndPassReceiver(),
|
|
std::move(context_params));
|
|
|
|
network::mojom::URLLoaderFactoryParamsPtr url_loader_params =
|
|
network::mojom::URLLoaderFactoryParams::New();
|
|
url_loader_params->process_id = network::mojom::kBrowserProcessId;
|
|
url_loader_params->is_trusted = true;
|
|
mojo::Remote<network::mojom::URLLoaderFactory> url_loader_factory;
|
|
network_context->CreateURLLoaderFactory(
|
|
url_loader_factory.BindNewPipeAndPassReceiver(),
|
|
std::move(url_loader_params));
|
|
|
|
std::unique_ptr<network::ResourceRequest> request =
|
|
std::make_unique<network::ResourceRequest>();
|
|
request->url = url;
|
|
url::Origin origin = url::Origin::Create(url);
|
|
request->trusted_params = network::ResourceRequest::TrustedParams();
|
|
request->trusted_params->isolation_info =
|
|
net::IsolationInfo::CreateForInternalRequest(origin);
|
|
request->site_for_cookies =
|
|
request->trusted_params->isolation_info.site_for_cookies();
|
|
|
|
if (load_only_from_cache)
|
|
request->load_flags |= net::LOAD_ONLY_FROM_CACHE;
|
|
auto loader = network::SimpleURLLoader::Create(
|
|
std::move(request), TRAFFIC_ANNOTATION_FOR_TESTS);
|
|
|
|
scoped_refptr<net::HttpResponseHeaders> headers;
|
|
base::RunLoop loop;
|
|
loader->DownloadHeadersOnly(
|
|
url_loader_factory.get(),
|
|
base::BindLambdaForTesting(
|
|
[&](scoped_refptr<net::HttpResponseHeaders> passed_headers) {
|
|
headers = passed_headers;
|
|
loop.Quit();
|
|
}));
|
|
loop.Run();
|
|
return loader->NetError();
|
|
}
|
|
|
|
void GetCacheFileInfo(base::File::Info& info) {
|
|
base::FilePath ceontxt_path = GetNetworkContextPath();
|
|
base::FileEnumerator cache_files(GetNetworkContextCachePath(), true,
|
|
base::FileEnumerator::FILES);
|
|
// Cache entries created.
|
|
auto file_path = cache_files.Next();
|
|
ASSERT_FALSE(file_path.empty());
|
|
ASSERT_TRUE(base::GetFileInfo(file_path, &info));
|
|
}
|
|
};
|
|
|
|
// Create a network context and make an HTTP request which causes cache entry to
|
|
// be created.
|
|
IN_PROC_BROWSER_TEST_F(NetworkServiceBrowserCacheResetTest,
|
|
PRE_PRE_CacheResetTest) {
|
|
GURL url = embedded_test_server()->GetURL("/echoheadercache");
|
|
// Store the URL so the requests made by the subsequent parts of this test
|
|
// are to the same origin. Otherwise, the embedded test server might be
|
|
// operating on a different port causing incorrect cache misses.
|
|
ASSERT_NO_FATAL_FAILURE(StoreUrl(url));
|
|
|
|
EXPECT_THAT(MakeNetworkContentAndLoadUrl(
|
|
/*reset_cache=*/false, /*load_only_from_cache=*/false, url),
|
|
net::test::IsOk());
|
|
}
|
|
|
|
// Using the same network context, make an HTTP request and verify that the
|
|
// cache entry is correctly used.
|
|
IN_PROC_BROWSER_TEST_F(NetworkServiceBrowserCacheResetTest,
|
|
PRE_CacheResetTest) {
|
|
GURL url;
|
|
ASSERT_NO_FATAL_FAILURE(RetrieveUrl(url));
|
|
|
|
EXPECT_THAT(MakeNetworkContentAndLoadUrl(/*reset_cache=*/false,
|
|
/*load_only_from_cache=*/true, url),
|
|
net::test::IsOk());
|
|
}
|
|
|
|
// Using the same network context, reset the cache backend and verify that cache
|
|
// miss is correctly reported.
|
|
IN_PROC_BROWSER_TEST_F(NetworkServiceBrowserCacheResetTest, CacheResetTest) {
|
|
GURL url;
|
|
ASSERT_NO_FATAL_FAILURE(RetrieveUrl(url));
|
|
|
|
EXPECT_THAT(MakeNetworkContentAndLoadUrl(/*reset_cache=*/true,
|
|
/*load_only_from_cache=*/true, url),
|
|
net::test::IsError(net::ERR_CACHE_MISS));
|
|
}
|
|
|
|
#if BUILDFLAG(IS_POSIX)
|
|
IN_PROC_BROWSER_TEST_F(NetworkServiceBrowserCacheResetTest, CacheResetFailure) {
|
|
base::ScopedAllowBlockingForTesting allow_blocking;
|
|
const base::FilePath path = GetNetworkContextCachePath();
|
|
|
|
GURL url = embedded_test_server()->GetURL("/echoheadercache");
|
|
|
|
ASSERT_TRUE(base::CreateDirectory(path));
|
|
// Make the directory inaccessible, to see what happens when resetting the
|
|
// cache fails.
|
|
ASSERT_TRUE(base::SetPosixFilePermissions(path, /*mode=*/0));
|
|
|
|
EXPECT_THAT(MakeNetworkContentAndLoadUrl(/*reset_cache=*/true,
|
|
/*load_only_from_cache=*/true, url),
|
|
net::test::IsError(net::ERR_CACHE_MISS));
|
|
}
|
|
#endif // BUILDFLAG(IS_POSIX)
|
|
#endif // BUILDFLAG(IS_ANDROID)
|
|
|
|
// Cache data migration is not used for Fuchsia.
|
|
#if !BUILDFLAG(IS_FUCHSIA)
|
|
|
|
const base::FilePath::CharType kCheckpointFileName[] =
|
|
FILE_PATH_LITERAL("NetworkDataMigrated");
|
|
constexpr char kCookieName[] = "Name";
|
|
constexpr char kCookieValue[] = "Value";
|
|
|
|
net::CookieList GetCookies(
|
|
const mojo::Remote<network::mojom::CookieManager>& cookie_manager) {
|
|
base::RunLoop run_loop;
|
|
net::CookieList cookies_out;
|
|
cookie_manager->GetAllCookies(
|
|
base::BindLambdaForTesting([&](const net::CookieList& cookies) {
|
|
cookies_out = cookies;
|
|
run_loop.Quit();
|
|
}));
|
|
run_loop.Run();
|
|
return cookies_out;
|
|
}
|
|
|
|
void SetCookie(
|
|
const mojo::Remote<network::mojom::CookieManager>& cookie_manager) {
|
|
base::Time t = base::Time::Now();
|
|
auto cookie = net::CanonicalCookie::CreateUnsafeCookieForTesting(
|
|
kCookieName, kCookieValue, "example.test", "/", t, t + base::Days(1),
|
|
base::Time(), base::Time(), /*secure=*/true, /*http-only=*/false,
|
|
net::CookieSameSite::NO_RESTRICTION, net::COOKIE_PRIORITY_DEFAULT,
|
|
/*=same_party=*/false);
|
|
base::RunLoop run_loop;
|
|
cookie_manager->SetCanonicalCookie(
|
|
*cookie, net::cookie_util::SimulatedCookieSource(*cookie, "https"),
|
|
net::CookieOptions(),
|
|
base::BindLambdaForTesting(
|
|
[&](net::CookieAccessResult result) { run_loop.Quit(); }));
|
|
run_loop.Run();
|
|
}
|
|
|
|
void FlushCookies(
|
|
const mojo::Remote<network::mojom::CookieManager>& cookie_manager) {
|
|
base::RunLoop run_loop;
|
|
cookie_manager->FlushCookieStore(
|
|
base::BindLambdaForTesting([&]() { run_loop.Quit(); }));
|
|
run_loop.Run();
|
|
}
|
|
|
|
mojo::PendingRemote<network::mojom::NetworkContext>
|
|
CreateNetworkContextForPaths(network::mojom::NetworkContextFilePathsPtr paths,
|
|
const base::FilePath& cache_path) {
|
|
network::mojom::NetworkContextParamsPtr context_params =
|
|
network::mojom::NetworkContextParams::New();
|
|
context_params->file_paths = std::move(paths);
|
|
context_params->cert_verifier_params = GetCertVerifierParams(
|
|
cert_verifier::mojom::CertVerifierCreationParams::New());
|
|
// Not passing in a key for simplicity, so disable encryption.
|
|
context_params->enable_encrypted_cookies = false;
|
|
context_params->http_cache_enabled = true;
|
|
context_params->file_paths->http_cache_directory = cache_path;
|
|
mojo::PendingRemote<network::mojom::NetworkContext> network_context;
|
|
content::CreateNetworkContextInNetworkService(
|
|
network_context.InitWithNewPipeAndPassReceiver(),
|
|
std::move(context_params));
|
|
return network_context;
|
|
}
|
|
|
|
enum class FailureType {
|
|
kNoFailures = 0,
|
|
// A file exists with the same name as the target directory so it cannot be
|
|
// created.
|
|
kDirIsAFile = 1,
|
|
// The target migration directory already exists.
|
|
kDirAlreadyThere = 2,
|
|
// A file called 'TestCookies' already exists in the migration target
|
|
// directory.
|
|
kCookieFileAlreadyThere = 3,
|
|
#if BUILDFLAG(IS_WIN)
|
|
// The 'TestCookies' file in the destination directory is locked and cannot be
|
|
// written to. This is only valid on Windows where files can actually be
|
|
// locked.
|
|
kDestCookieFileIsLocked = 4,
|
|
// The 'TestCookies' file in the source directory is locked and cannot be read
|
|
// from (during the migration). This failure is only valid on Windows where
|
|
// files can actually be locked.
|
|
kSourceCookieFileIsLocked = 5,
|
|
#endif // BUILDFLAG(IS_WIN)
|
|
// A file exists with the same name as the Cache dir. This will cause the
|
|
// creation of the cache dir to fail, and cache to not function either
|
|
// (although we don't test for that here).
|
|
kCacheDirIsAFile = 6,
|
|
};
|
|
|
|
static const FailureType kFailureTypes[] = {
|
|
FailureType::kNoFailures,
|
|
FailureType::kDirIsAFile,
|
|
FailureType::kDirAlreadyThere,
|
|
FailureType::kCookieFileAlreadyThere,
|
|
#if BUILDFLAG(IS_WIN)
|
|
FailureType::kDestCookieFileIsLocked,
|
|
FailureType::kSourceCookieFileIsLocked,
|
|
#endif // BUILDFLAG(IS_WIN)
|
|
FailureType::kCacheDirIsAFile};
|
|
|
|
static const base::FilePath::CharType kCookieDatabaseName[] =
|
|
FILE_PATH_LITERAL("TestCookies");
|
|
static const base::FilePath::CharType kNetworkSubpath[] =
|
|
FILE_PATH_LITERAL("Network");
|
|
|
|
// Disable the following data migration tests on Android because the data
|
|
// migration logic is disabled and compiled out on this platform.
|
|
#if BUILDFLAG(IS_ANDROID)
|
|
#define MAYBE_NetworkServiceDataMigrationBrowserTest \
|
|
DISABLED_NetworkServiceDataMigrationBrowserTest
|
|
#define MAYBE_NetworkServiceDataMigrationBrowserTestWithFailures \
|
|
DISABLED_NetworkServiceDataMigrationBrowserTestWithFailures
|
|
#else
|
|
#define MAYBE_NetworkServiceDataMigrationBrowserTest \
|
|
NetworkServiceDataMigrationBrowserTest
|
|
#define MAYBE_NetworkServiceDataMigrationBrowserTestWithFailures \
|
|
NetworkServiceDataMigrationBrowserTestWithFailures
|
|
#endif // BUILDFLAG(IS_ANDROID)
|
|
|
|
// A class to test various behavior of network context data migration.
|
|
class MAYBE_NetworkServiceDataMigrationBrowserTest : public ContentBrowserTest {
|
|
public:
|
|
MAYBE_NetworkServiceDataMigrationBrowserTest() {
|
|
// Migration only supports non-WAL sqlite databases. If this feature is
|
|
// switched on by default before migration has been completed then the code
|
|
// in MaybeGrantSandboxAccessToNetworkContextData will need to be updated.
|
|
EXPECT_FALSE(
|
|
base::FeatureList::IsEnabled(sql::features::kEnableWALModeByDefault));
|
|
#if BUILDFLAG(IS_WIN)
|
|
// On Windows, the network sandbox needs to be disabled. This is because the
|
|
// code that performs the migration on Windows DCHECKs if network sandbox is
|
|
// enabled and migration is not requested, but this is used in the tests to
|
|
// verify this behavior.
|
|
win_network_sandbox_feature_.InitAndDisableFeature(
|
|
sandbox::policy::features::kNetworkServiceSandbox);
|
|
#endif
|
|
}
|
|
|
|
#if BUILDFLAG(IS_WIN)
|
|
private:
|
|
base::test::ScopedFeatureList win_network_sandbox_feature_;
|
|
#endif
|
|
};
|
|
|
|
// A parameterized test fixture that can simulate various failures in the
|
|
// migration step, and can also be run with either in-process or out-of-process
|
|
// network service.
|
|
class MAYBE_NetworkServiceDataMigrationBrowserTestWithFailures
|
|
: public MAYBE_NetworkServiceDataMigrationBrowserTest,
|
|
public ::testing::WithParamInterface<std::tuple<bool, FailureType>> {
|
|
public:
|
|
MAYBE_NetworkServiceDataMigrationBrowserTestWithFailures() {
|
|
if (IsNetworkServiceRunningInProcess()) {
|
|
ForceInProcessNetworkService();
|
|
} else {
|
|
ForceOutOfProcessNetworkService();
|
|
}
|
|
}
|
|
|
|
protected:
|
|
bool IsNetworkServiceRunningInProcess() { return std::get<0>(GetParam()); }
|
|
FailureType GetFailureType() { return std::get<1>(GetParam()); }
|
|
};
|
|
|
|
// A function to verify that data files move during migration to sandboxed data
|
|
// dir. This function uses three directories to verify the behavior. It uses the
|
|
// cookies file to verify the migration occurs correctly.
|
|
//
|
|
// Testing takes place under the browser context path. First, a network context
|
|
// is created in temp dir 'one' and then a cookie is written and flushed to
|
|
// disk. This results in cookie files(s) being created on disk.
|
|
//
|
|
// BrowserContext/
|
|
// |- tempdir 'one'/ (`tempdir_one` FilePath)
|
|
// | |- Cookies
|
|
// | |- Cookies-journal
|
|
//
|
|
// The entire 'one' dir is then copied into a new 'two' temp folder to create
|
|
// the directory structure used for migration. This is so a second network
|
|
// context can be created in the same network service.
|
|
//
|
|
// BrowserContext/
|
|
// |- tempdir 'one'/
|
|
// | |- Cookies
|
|
// | |- Cookies-journal
|
|
// |- tempdir 'two'/ (`tempdir_two` FilePath)
|
|
// | |- Cookies (copied from above)
|
|
// | |- Cookies-journal (copied from above)
|
|
//
|
|
// A new network context is then created with `unsandboxed_data_path` set to
|
|
// root of tempdir 'two' and `data_directory` set to a directory underneath
|
|
// tempdir 'two' called 'Network' to initiate the migration. After a successful
|
|
// migration, the structure should look like this:
|
|
//
|
|
// BrowserContext/
|
|
// |- tempdir 'one'/
|
|
// | |- Cookies
|
|
// | |- Cookies-journal
|
|
// |- tempdir 'two'/
|
|
// | |- Network/
|
|
// | | |- Cookies (migrated from tempdir 'two')
|
|
// | | |- Cookies-journal (migrated from tempdir 'two')
|
|
//
|
|
// This test injects various failures in the migration process to ensure that
|
|
// the network context still functions correctly if the Cookies file cannot be
|
|
// migrated.
|
|
void MigrationTestInternal(const base::FilePath& tempdir_one,
|
|
const base::FilePath& tempdir_two_parent,
|
|
FailureType failure_type) {
|
|
EXPECT_FALSE(base::PathExists(tempdir_one.Append(kCookieDatabaseName)));
|
|
|
|
auto file_paths = network::mojom::NetworkContextFilePaths::New();
|
|
file_paths->data_directory = tempdir_one;
|
|
file_paths->cookie_database_name = base::FilePath(kCookieDatabaseName);
|
|
|
|
mojo::Remote<network::mojom::NetworkContext> network_context_one(
|
|
CreateNetworkContextForPaths(
|
|
std::move(file_paths),
|
|
tempdir_one.Append(FILE_PATH_LITERAL("Cache"))));
|
|
mojo::Remote<network::mojom::CookieManager> cookie_manager_one;
|
|
network_context_one->GetCookieManager(
|
|
cookie_manager_one.BindNewPipeAndPassReceiver());
|
|
|
|
SetCookie(cookie_manager_one);
|
|
FlushCookies(cookie_manager_one);
|
|
|
|
// Verify that the cookie file exists in tempdir 'one'.
|
|
EXPECT_TRUE(base::PathExists(tempdir_one.Append(kCookieDatabaseName)));
|
|
|
|
// Now, copy the entire directory to tempdir 'two' to verify the migration.
|
|
EXPECT_TRUE(base::CopyDirectory(tempdir_one, tempdir_two_parent, true));
|
|
// base::CopyDirectory copies the directory into a new directory if the target
|
|
// directory already exists, so fix up the directory name here.
|
|
base::FilePath tempdir_two =
|
|
tempdir_two_parent.Append(tempdir_one.BaseName());
|
|
|
|
// Verify cookie file is there, copied across from the tempdir 'one'.
|
|
EXPECT_TRUE(base::PathExists(tempdir_two.Append(kCookieDatabaseName)));
|
|
#if BUILDFLAG(IS_WIN)
|
|
base::File longer_lived_file;
|
|
#endif
|
|
|
|
switch (failure_type) {
|
|
case FailureType::kNoFailures:
|
|
break;
|
|
case FailureType::kDirIsAFile: {
|
|
// Create a file called 'Network' in the path. This will cause migration
|
|
// to fail catastrophically as the directory cannot be created.
|
|
base::File scoped_file(
|
|
tempdir_two.Append(kNetworkSubpath),
|
|
base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
|
|
EXPECT_TRUE(scoped_file.IsValid());
|
|
} break;
|
|
case FailureType::kDirAlreadyThere:
|
|
EXPECT_TRUE(base::CreateDirectory(tempdir_two.Append(kNetworkSubpath)));
|
|
break;
|
|
case FailureType::kCookieFileAlreadyThere: {
|
|
EXPECT_TRUE(base::CreateDirectory(tempdir_two.Append(kNetworkSubpath)));
|
|
// Touch a file in the new dir called the same as the cookie file. This
|
|
// should be correctly overwritten by the migration code.
|
|
base::File scoped_file(
|
|
tempdir_two.Append(kNetworkSubpath).Append(kCookieDatabaseName),
|
|
base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
|
|
EXPECT_TRUE(scoped_file.IsValid());
|
|
} break;
|
|
#if BUILDFLAG(IS_WIN)
|
|
case FailureType::kDestCookieFileIsLocked:
|
|
// Create a file called 'TestCookies' in the destination path and hold a
|
|
// write lock on it so it can't be written to.
|
|
EXPECT_TRUE(base::CreateDirectory(tempdir_two.Append(kNetworkSubpath)));
|
|
longer_lived_file = base::File(
|
|
tempdir_two.Append(kNetworkSubpath).Append(kCookieDatabaseName),
|
|
base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE |
|
|
base::File::FLAG_WIN_EXCLUSIVE_WRITE |
|
|
base::File::FLAG_WIN_EXCLUSIVE_READ);
|
|
EXPECT_TRUE(longer_lived_file.IsValid());
|
|
break;
|
|
case FailureType::kSourceCookieFileIsLocked:
|
|
// Lock the Cookie file so it can't be read. This causes cookies to break
|
|
// entirely, both the migration and the normal operation. The test can
|
|
// merely verify that the migration fails and the failure is reported
|
|
// correctly.
|
|
longer_lived_file =
|
|
base::File(tempdir_two.Append(kCookieDatabaseName),
|
|
base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_WRITE |
|
|
base::File::FLAG_WIN_EXCLUSIVE_WRITE |
|
|
base::File::FLAG_WIN_EXCLUSIVE_READ);
|
|
EXPECT_TRUE(longer_lived_file.IsValid());
|
|
break;
|
|
#endif // BUILDFLAG(IS_WIN)
|
|
case FailureType::kCacheDirIsAFile: {
|
|
// Make the cache directory invalid by deleting it and making it a file,
|
|
// so it can't be created or used.
|
|
base::DeletePathRecursively(
|
|
tempdir_two.Append(FILE_PATH_LITERAL("Cache")));
|
|
base::File scoped_file(
|
|
tempdir_two.Append(FILE_PATH_LITERAL("Cache")),
|
|
base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
|
|
EXPECT_TRUE(scoped_file.IsValid());
|
|
} break;
|
|
}
|
|
// Create a new network context that will migrate the files from the tempdir
|
|
// 'two' into the new 'Network' directory underneath.
|
|
auto new_file_paths = network::mojom::NetworkContextFilePaths::New();
|
|
// Data path is now a new 'Network' directory under the tempdir 'two'.
|
|
new_file_paths->data_directory = tempdir_two.Append(kNetworkSubpath);
|
|
new_file_paths->cookie_database_name = base::FilePath(kCookieDatabaseName);
|
|
// Migrate data from the tempdir 'two' to the new path under 'Network'.
|
|
new_file_paths->unsandboxed_data_path = tempdir_two;
|
|
new_file_paths->trigger_migration = true;
|
|
|
|
base::HistogramTester histogram_tester;
|
|
mojo::Remote<network::mojom::NetworkContext> network_context_two(
|
|
CreateNetworkContextForPaths(
|
|
std::move(new_file_paths),
|
|
tempdir_two.Append(FILE_PATH_LITERAL("Cache"))));
|
|
mojo::Remote<network::mojom::CookieManager> cookie_manager_two;
|
|
network_context_two->GetCookieManager(
|
|
cookie_manager_two.BindNewPipeAndPassReceiver());
|
|
net::CookieList cookies = GetCookies(cookie_manager_two);
|
|
|
|
bool cookies_should_work = true;
|
|
|
|
switch (failure_type) {
|
|
case FailureType::kNoFailures:
|
|
case FailureType::kDirAlreadyThere:
|
|
case FailureType::kCookieFileAlreadyThere:
|
|
// Cookie file should have moved from the original `unsandboxed_data_path`
|
|
// to the new 'Network' path.
|
|
EXPECT_FALSE(base::PathExists(tempdir_two.Append(kCookieDatabaseName)));
|
|
// Into the new directory.
|
|
EXPECT_TRUE(base::PathExists(
|
|
tempdir_two.Append(kNetworkSubpath).Append(kCookieDatabaseName)));
|
|
// If there was a journal file in the original `unsandboxed_data_path`,
|
|
// check that it has also moved.
|
|
if (base::PathExists(tempdir_one.Append(sql::Database::JournalPath(
|
|
base::FilePath(kCookieDatabaseName))))) {
|
|
EXPECT_FALSE(base::PathExists(tempdir_two.Append(
|
|
sql::Database::JournalPath(base::FilePath(kCookieDatabaseName)))));
|
|
EXPECT_TRUE(
|
|
base::PathExists(tempdir_two.Append(kNetworkSubpath)
|
|
.Append(sql::Database::JournalPath(
|
|
base::FilePath(kCookieDatabaseName)))));
|
|
}
|
|
|
|
histogram_tester.ExpectUniqueSample("NetworkService.GrantSandboxResult",
|
|
/*sample=kSuccess=*/0,
|
|
/*expected_bucket_count=*/1);
|
|
// Checkpoint file should have been placed into the migrated directory.
|
|
EXPECT_TRUE(base::PathExists(
|
|
tempdir_two.Append(kNetworkSubpath).Append(kCheckpointFileName)));
|
|
break;
|
|
case FailureType::kDirIsAFile:
|
|
// Cookie file should still be in the original `unsandboxed_data_path` as
|
|
// it could not be moved.
|
|
EXPECT_TRUE(base::PathExists(tempdir_two.Append(kCookieDatabaseName)));
|
|
EXPECT_FALSE(base::PathExists(
|
|
tempdir_two.Append(kNetworkSubpath).Append(kCheckpointFileName)));
|
|
histogram_tester.ExpectUniqueSample(
|
|
"NetworkService.GrantSandboxResult",
|
|
/*sample=kFailedToCreateDataDirectory=*/2,
|
|
/*expected_bucket_count=*/1);
|
|
break;
|
|
#if BUILDFLAG(IS_WIN)
|
|
case FailureType::kDestCookieFileIsLocked:
|
|
// Cookie file should still be in the original `unsandboxed_data_path` as
|
|
// it could not be moved as the destination was locked or not writable.
|
|
EXPECT_TRUE(base::PathExists(tempdir_two.Append(kCookieDatabaseName)));
|
|
// Source file is there, but locked.
|
|
EXPECT_TRUE(base::PathExists(tempdir_two.Append(kCookieDatabaseName)));
|
|
// And locked destination file is there, but cookies are working so they
|
|
// must be backed by the original file.
|
|
EXPECT_TRUE(
|
|
base::PathExists(tempdir_two.Append(FILE_PATH_LITERAL("Network"))
|
|
.Append(kCookieDatabaseName)));
|
|
EXPECT_FALSE(base::PathExists(
|
|
tempdir_two.Append(kNetworkSubpath).Append(kCheckpointFileName)));
|
|
{
|
|
base::File attempt_to_open_locked_file(
|
|
tempdir_two.Append(kNetworkSubpath).Append(kCookieDatabaseName),
|
|
base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_READ);
|
|
// Check that the file really is locked, so the cookies must be running
|
|
// from the unsandboxed directory.
|
|
EXPECT_FALSE(attempt_to_open_locked_file.IsValid());
|
|
}
|
|
histogram_tester.ExpectUniqueSample("NetworkService.GrantSandboxResult",
|
|
/*sample=kFailedToCopyData=*/3,
|
|
/*expected_bucket_count=*/1);
|
|
break;
|
|
case FailureType::kSourceCookieFileIsLocked:
|
|
// Cookie file should still be in the original `unsandboxed_data_path` as
|
|
// it could not be moved as the destination was locked or not writable.
|
|
EXPECT_TRUE(base::PathExists(tempdir_two.Append(kCookieDatabaseName)));
|
|
// File hasn't moved, so cookies must be backed by the original file.
|
|
EXPECT_FALSE(base::PathExists(
|
|
tempdir_two.Append(kNetworkSubpath).Append(kCookieDatabaseName)));
|
|
EXPECT_FALSE(base::PathExists(
|
|
tempdir_two.Append(kNetworkSubpath).Append(kCheckpointFileName)));
|
|
histogram_tester.ExpectUniqueSample("NetworkService.GrantSandboxResult",
|
|
/*sample=kFailedToCopyData=*/3,
|
|
/*expected_bucket_count=*/1);
|
|
// In this case the source cookie file can't be read by anything including
|
|
// the migration code and the network context, so cookies should be
|
|
// totally broken. :(
|
|
cookies_should_work = false;
|
|
break;
|
|
#endif // BUILDFLAG(IS_WIN)
|
|
case FailureType::kCacheDirIsAFile:
|
|
histogram_tester.ExpectUniqueSample("NetworkService.GrantSandboxResult",
|
|
/*sample=kSuccess=*/0,
|
|
/*expected_bucket_count=*/1);
|
|
break;
|
|
}
|
|
if (!cookies_should_work)
|
|
return;
|
|
|
|
ASSERT_EQ(1u, cookies.size());
|
|
EXPECT_EQ(kCookieName, cookies[0].Name());
|
|
EXPECT_EQ(kCookieValue, cookies[0].Value());
|
|
}
|
|
|
|
IN_PROC_BROWSER_TEST_P(MAYBE_NetworkServiceDataMigrationBrowserTestWithFailures,
|
|
MigrateDataTest) {
|
|
base::ScopedAllowBlockingForTesting allow_blocking;
|
|
|
|
base::FilePath tempdir_one;
|
|
EXPECT_TRUE(base::CreateTemporaryDirInDir(
|
|
shell()->web_contents()->GetBrowserContext()->GetPath(),
|
|
/*prefix=*/FILE_PATH_LITERAL("one"), &tempdir_one));
|
|
base::FilePath tempdir_two;
|
|
EXPECT_TRUE(base::CreateTemporaryDirInDir(
|
|
shell()->web_contents()->GetBrowserContext()->GetPath(),
|
|
/*prefix=*/FILE_PATH_LITERAL("two"), &tempdir_two));
|
|
MigrationTestInternal(tempdir_one, tempdir_two, GetFailureType());
|
|
}
|
|
|
|
// This test is similar to the test above that uses two directories, but it uses
|
|
// a third directory to verify that if a migration is triggered and then later
|
|
// not triggered, then the data is still read from the new directory and not the
|
|
// old one.
|
|
IN_PROC_BROWSER_TEST_F(MAYBE_NetworkServiceDataMigrationBrowserTest,
|
|
MigrateThenNoMigrate) {
|
|
base::ScopedAllowBlockingForTesting allow_blocking;
|
|
|
|
base::FilePath tempdir_one;
|
|
EXPECT_TRUE(base::CreateTemporaryDirInDir(
|
|
shell()->web_contents()->GetBrowserContext()->GetPath(),
|
|
/*prefix=*/FILE_PATH_LITERAL("one"), &tempdir_one));
|
|
base::FilePath tempdir_two;
|
|
EXPECT_TRUE(base::CreateTemporaryDirInDir(
|
|
shell()->web_contents()->GetBrowserContext()->GetPath(),
|
|
/*prefix=*/FILE_PATH_LITERAL("two"), &tempdir_two));
|
|
// Migrate within tempdir_two.
|
|
MigrationTestInternal(tempdir_one, tempdir_two, FailureType::kNoFailures);
|
|
// base::CopyDirectory copies the directory into a new directory if the target
|
|
// directory already exists, so fix up the directory name here.
|
|
base::FilePath real_tempdir_two = tempdir_two.Append(tempdir_one.BaseName());
|
|
// Double check that the migration happened.
|
|
EXPECT_TRUE(base::PathExists(
|
|
real_tempdir_two.Append(kNetworkSubpath).Append(kCookieDatabaseName)));
|
|
// Create a third testing directory, and copy the migrated data from
|
|
// tempdir_two into it.
|
|
base::FilePath tempdir_three;
|
|
EXPECT_TRUE(base::CreateTemporaryDirInDir(
|
|
shell()->web_contents()->GetBrowserContext()->GetPath(),
|
|
/*prefix=*/FILE_PATH_LITERAL("three"), &tempdir_three));
|
|
EXPECT_TRUE(base::CopyDirectory(real_tempdir_two, tempdir_three, true));
|
|
// base::CopyDirectory copies the directory into a new directory if the target
|
|
// directory already exists, so fix up the directory name here.
|
|
base::FilePath real_tempdir_three =
|
|
tempdir_three.Append(real_tempdir_two.BaseName());
|
|
// Double check the directory was copied right.
|
|
EXPECT_TRUE(base::PathExists(
|
|
real_tempdir_three.Append(kNetworkSubpath).Append(kCookieDatabaseName)));
|
|
// Double check cookies are not in the old directory, meaning if they work
|
|
// they must have been read from the new directory.
|
|
EXPECT_FALSE(
|
|
base::PathExists(real_tempdir_three.Append(kCookieDatabaseName)));
|
|
|
|
base::HistogramTester histogram_tester;
|
|
// Now create a new network context with migration set to false (default) but
|
|
// pointing to the migrated directory. This verifies that even if no migration
|
|
// is requested, the migrated data is still read correctly and that migration
|
|
// is a one-way operation.
|
|
auto file_paths = network::mojom::NetworkContextFilePaths::New();
|
|
file_paths->data_directory = real_tempdir_three.Append(kNetworkSubpath);
|
|
file_paths->unsandboxed_data_path = real_tempdir_three;
|
|
file_paths->cookie_database_name = base::FilePath(kCookieDatabaseName);
|
|
// If defaults are ever changed, this test will need to be updated.
|
|
DCHECK_EQ(file_paths->trigger_migration, false);
|
|
mojo::Remote<network::mojom::NetworkContext> network_context(
|
|
CreateNetworkContextForPaths(
|
|
std::move(file_paths),
|
|
real_tempdir_three.Append(FILE_PATH_LITERAL("Cache"))));
|
|
mojo::Remote<network::mojom::CookieManager> cookie_manager;
|
|
network_context->GetCookieManager(
|
|
cookie_manager.BindNewPipeAndPassReceiver());
|
|
|
|
net::CookieList cookies = GetCookies(cookie_manager);
|
|
histogram_tester.ExpectUniqueSample("NetworkService.GrantSandboxResult",
|
|
/*sample=kMigrationAlreadySucceeded=*/10,
|
|
/*expected_bucket_count=*/1);
|
|
// Cookies work.
|
|
ASSERT_EQ(1u, cookies.size());
|
|
EXPECT_EQ(kCookieName, cookies[0].Name());
|
|
EXPECT_EQ(kCookieValue, cookies[0].Value());
|
|
}
|
|
|
|
// This test verifies that a new un-used data path will be initialized correctly
|
|
// if `unsandboxed_data_path` is set. The Cookie file should be placed into the
|
|
// `data_directory` and not `unsandboxed_data_path`.
|
|
IN_PROC_BROWSER_TEST_F(MAYBE_NetworkServiceDataMigrationBrowserTest,
|
|
NewDataDirWithMigrationTest) {
|
|
base::ScopedAllowBlockingForTesting allow_blocking;
|
|
|
|
base::FilePath tempdir;
|
|
EXPECT_TRUE(base::CreateTemporaryDirInDir(
|
|
shell()->web_contents()->GetBrowserContext()->GetPath(),
|
|
/*prefix=*/FILE_PATH_LITERAL(""), &tempdir));
|
|
|
|
EXPECT_FALSE(base::PathExists(tempdir.Append(kCookieDatabaseName)));
|
|
|
|
auto file_paths = network::mojom::NetworkContextFilePaths::New();
|
|
file_paths->data_directory = tempdir.Append(FILE_PATH_LITERAL("Network"));
|
|
file_paths->unsandboxed_data_path = tempdir;
|
|
file_paths->cookie_database_name = base::FilePath(kCookieDatabaseName);
|
|
file_paths->trigger_migration = true;
|
|
base::HistogramTester histogram_tester;
|
|
|
|
mojo::Remote<network::mojom::NetworkContext> network_context(
|
|
CreateNetworkContextForPaths(std::move(file_paths),
|
|
tempdir.Append(FILE_PATH_LITERAL("Cache"))));
|
|
mojo::Remote<network::mojom::CookieManager> cookie_manager;
|
|
network_context->GetCookieManager(
|
|
cookie_manager.BindNewPipeAndPassReceiver());
|
|
|
|
SetCookie(cookie_manager);
|
|
FlushCookies(cookie_manager);
|
|
|
|
// Verify that the cookie file exists in the `data_directory` and not the
|
|
// `unsandboxed_data_path`.
|
|
EXPECT_FALSE(base::PathExists(tempdir.Append(kCookieDatabaseName)));
|
|
EXPECT_TRUE(base::PathExists(tempdir.Append(FILE_PATH_LITERAL("Network"))
|
|
.Append(kCookieDatabaseName)));
|
|
|
|
net::CookieList cookies = GetCookies(cookie_manager);
|
|
histogram_tester.ExpectUniqueSample("NetworkService.GrantSandboxResult",
|
|
/*sample=kSuccess=*/0,
|
|
/*expected_bucket_count=*/1);
|
|
// Cookie should be there.
|
|
ASSERT_EQ(1u, cookies.size());
|
|
EXPECT_EQ(kCookieName, cookies[0].Name());
|
|
EXPECT_EQ(kCookieValue, cookies[0].Value());
|
|
}
|
|
|
|
// A test where a caller specifies both `data_directory` and
|
|
// `unsandboxed_data_path` but does not wish migration to occur. The data should
|
|
// be in `unsandboxed_data_path` in this case.
|
|
IN_PROC_BROWSER_TEST_F(MAYBE_NetworkServiceDataMigrationBrowserTest,
|
|
NewDataDirWithNoMigrationTest) {
|
|
base::ScopedAllowBlockingForTesting allow_blocking;
|
|
|
|
base::FilePath tempdir;
|
|
EXPECT_TRUE(base::CreateTemporaryDirInDir(
|
|
shell()->web_contents()->GetBrowserContext()->GetPath(),
|
|
/*prefix=*/FILE_PATH_LITERAL(""), &tempdir));
|
|
|
|
EXPECT_FALSE(base::PathExists(tempdir.Append(kCookieDatabaseName)));
|
|
|
|
auto file_paths = network::mojom::NetworkContextFilePaths::New();
|
|
file_paths->data_directory = tempdir.Append(FILE_PATH_LITERAL("Network"));
|
|
file_paths->unsandboxed_data_path = tempdir;
|
|
file_paths->cookie_database_name = base::FilePath(kCookieDatabaseName);
|
|
file_paths->trigger_migration = false;
|
|
base::HistogramTester histogram_tester;
|
|
|
|
mojo::Remote<network::mojom::NetworkContext> network_context(
|
|
CreateNetworkContextForPaths(std::move(file_paths),
|
|
tempdir.Append(FILE_PATH_LITERAL("Cache"))));
|
|
mojo::Remote<network::mojom::CookieManager> cookie_manager;
|
|
network_context->GetCookieManager(
|
|
cookie_manager.BindNewPipeAndPassReceiver());
|
|
|
|
SetCookie(cookie_manager);
|
|
FlushCookies(cookie_manager);
|
|
|
|
// Verify that the cookie file still exists in the `unsandboxed_data_path`.
|
|
EXPECT_TRUE(base::PathExists(tempdir.Append(kCookieDatabaseName)));
|
|
// Verify that the cookie file has not been migrated to `data_directory`.
|
|
EXPECT_FALSE(base::PathExists(tempdir.Append(FILE_PATH_LITERAL("Network"))
|
|
.Append(kCookieDatabaseName)));
|
|
// Verify no checkpoint file was written either.
|
|
EXPECT_FALSE(base::PathExists(tempdir.Append(FILE_PATH_LITERAL("Network"))
|
|
.Append(kCheckpointFileName)));
|
|
|
|
net::CookieList cookies = GetCookies(cookie_manager);
|
|
histogram_tester.ExpectUniqueSample("NetworkService.GrantSandboxResult",
|
|
/*sample=kNoMigrationRequested=*/9,
|
|
/*expected_bucket_count=*/1);
|
|
|
|
// Cookie should be there.
|
|
ASSERT_EQ(1u, cookies.size());
|
|
EXPECT_EQ(kCookieName, cookies[0].Name());
|
|
EXPECT_EQ(kCookieValue, cookies[0].Value());
|
|
}
|
|
|
|
// A test where a caller specifies `data_directory` but does not specify
|
|
// anything else, including `unsandboxed_data_path`. This verifies that existing
|
|
// behavior remains the same for call-sites that do not update anything.
|
|
IN_PROC_BROWSER_TEST_F(MAYBE_NetworkServiceDataMigrationBrowserTest,
|
|
LegacyDataDir) {
|
|
base::ScopedAllowBlockingForTesting allow_blocking;
|
|
|
|
base::FilePath tempdir;
|
|
EXPECT_TRUE(base::CreateTemporaryDirInDir(
|
|
shell()->web_contents()->GetBrowserContext()->GetPath(),
|
|
/*prefix=*/FILE_PATH_LITERAL(""), &tempdir));
|
|
|
|
EXPECT_FALSE(base::PathExists(tempdir.Append(kCookieDatabaseName)));
|
|
|
|
auto file_paths = network::mojom::NetworkContextFilePaths::New();
|
|
file_paths->data_directory = tempdir;
|
|
file_paths->cookie_database_name = base::FilePath(kCookieDatabaseName);
|
|
|
|
base::HistogramTester histogram_tester;
|
|
mojo::Remote<network::mojom::NetworkContext> network_context(
|
|
CreateNetworkContextForPaths(std::move(file_paths),
|
|
tempdir.Append(FILE_PATH_LITERAL("Cache"))));
|
|
mojo::Remote<network::mojom::CookieManager> cookie_manager;
|
|
network_context->GetCookieManager(
|
|
cookie_manager.BindNewPipeAndPassReceiver());
|
|
|
|
SetCookie(cookie_manager);
|
|
FlushCookies(cookie_manager);
|
|
|
|
// Verify that the cookie file exists in the `unsandboxed_data_path`.
|
|
EXPECT_TRUE(base::PathExists(tempdir.Append(kCookieDatabaseName)));
|
|
|
|
net::CookieList cookies = GetCookies(cookie_manager);
|
|
histogram_tester.ExpectUniqueSample(
|
|
"NetworkService.GrantSandboxResult",
|
|
/*sample=kDidNotAttemptToGrantSandboxAccess=*/7,
|
|
/*expected_bucket_count=*/1);
|
|
|
|
// Cookie should be there.
|
|
ASSERT_EQ(1u, cookies.size());
|
|
EXPECT_EQ(kCookieName, cookies[0].Name());
|
|
EXPECT_EQ(kCookieValue, cookies[0].Value());
|
|
}
|
|
|
|
// This test is similar to the tests above that use two directories, but uses a
|
|
// third directory to verify that if a migration has previously occurred using
|
|
// the previous code without the checkpoint file, and then later takes place
|
|
// using the new code, then the data is still read from the correct directory
|
|
// despite there not being a checkpoint file prior to the migration.
|
|
IN_PROC_BROWSER_TEST_F(MAYBE_NetworkServiceDataMigrationBrowserTest,
|
|
MigratedPreviouslyAndMigrateAgain) {
|
|
base::ScopedAllowBlockingForTesting allow_blocking;
|
|
|
|
base::FilePath tempdir_one;
|
|
EXPECT_TRUE(base::CreateTemporaryDirInDir(
|
|
shell()->web_contents()->GetBrowserContext()->GetPath(),
|
|
/*prefix=*/FILE_PATH_LITERAL("one"), &tempdir_one));
|
|
base::FilePath tempdir_two;
|
|
EXPECT_TRUE(base::CreateTemporaryDirInDir(
|
|
shell()->web_contents()->GetBrowserContext()->GetPath(),
|
|
/*prefix=*/FILE_PATH_LITERAL("two"), &tempdir_two));
|
|
// Migrate within tempdir_two.
|
|
MigrationTestInternal(tempdir_one, tempdir_two, FailureType::kNoFailures);
|
|
// base::CopyDirectory copies the directory into a new directory if the target
|
|
// directory already exists, so fix up the directory name here.
|
|
base::FilePath real_tempdir_two = tempdir_two.Append(tempdir_one.BaseName());
|
|
// Double check that the migration happened.
|
|
EXPECT_TRUE(base::PathExists(
|
|
real_tempdir_two.Append(kNetworkSubpath).Append(kCookieDatabaseName)));
|
|
// Create a third testing directory, and copy the migrated data from
|
|
// tempdir_two into it.
|
|
base::FilePath tempdir_three;
|
|
EXPECT_TRUE(base::CreateTemporaryDirInDir(
|
|
shell()->web_contents()->GetBrowserContext()->GetPath(),
|
|
/*prefix=*/FILE_PATH_LITERAL("three"), &tempdir_three));
|
|
EXPECT_TRUE(base::CopyDirectory(real_tempdir_two, tempdir_three, true));
|
|
// base::CopyDirectory copies the directory into a new directory if the target
|
|
// directory already exists, so fix up the directory name here.
|
|
base::FilePath real_tempdir_three =
|
|
tempdir_three.Append(real_tempdir_two.BaseName());
|
|
// Double check the directory was copied right.
|
|
EXPECT_TRUE(base::PathExists(
|
|
real_tempdir_three.Append(kNetworkSubpath).Append(kCookieDatabaseName)));
|
|
// Double check cookies are not in the old directory, meaning if they work
|
|
// they must have been read from the new directory.
|
|
EXPECT_FALSE(
|
|
base::PathExists(real_tempdir_three.Append(kCookieDatabaseName)));
|
|
base::FilePath checkpoint_file =
|
|
real_tempdir_three.Append(kNetworkSubpath).Append(kCheckpointFileName);
|
|
// The directory should be fully migrated.
|
|
EXPECT_TRUE(base::PathExists(checkpoint_file));
|
|
// Delete the checkpoint file. This simulates that the directory was
|
|
// previously migrated before the concept of a checkpoint file had been
|
|
// introduced.
|
|
EXPECT_TRUE(base::DeleteFile(checkpoint_file));
|
|
// Test would be invalid if the delete failed.
|
|
EXPECT_FALSE(base::PathExists(checkpoint_file));
|
|
|
|
base::HistogramTester histogram_tester;
|
|
auto file_paths = network::mojom::NetworkContextFilePaths::New();
|
|
file_paths->data_directory = real_tempdir_three.Append(kNetworkSubpath);
|
|
file_paths->unsandboxed_data_path = real_tempdir_three;
|
|
file_paths->cookie_database_name = base::FilePath(kCookieDatabaseName);
|
|
file_paths->trigger_migration = true;
|
|
mojo::Remote<network::mojom::NetworkContext> network_context(
|
|
CreateNetworkContextForPaths(
|
|
std::move(file_paths),
|
|
real_tempdir_three.Append(FILE_PATH_LITERAL("Cache"))));
|
|
mojo::Remote<network::mojom::CookieManager> cookie_manager;
|
|
network_context->GetCookieManager(
|
|
cookie_manager.BindNewPipeAndPassReceiver());
|
|
|
|
net::CookieList cookies = GetCookies(cookie_manager);
|
|
// Success is reported here because although no files were copied from
|
|
// `unsandboxed_data_path` to `data_directory`, the migration still succeeded
|
|
// because a fresh Checkpoint file was placed down, and existing files were
|
|
// preserved in the `data_directory`.
|
|
histogram_tester.ExpectUniqueSample("NetworkService.GrantSandboxResult",
|
|
/*sample=kSuccess=*/0,
|
|
/*expected_bucket_count=*/1);
|
|
|
|
// Cookies work.
|
|
ASSERT_EQ(1u, cookies.size());
|
|
EXPECT_EQ(kCookieName, cookies[0].Name());
|
|
EXPECT_EQ(kCookieValue, cookies[0].Value());
|
|
|
|
EXPECT_TRUE(base::PathExists(checkpoint_file));
|
|
}
|
|
|
|
// Disable instantiation of parametrized tests for disk access sandboxing on
|
|
// Android.
|
|
#if BUILDFLAG(IS_ANDROID)
|
|
#define MAYBE_InProcess DISABLED_InProcess
|
|
#define MAYBE_OutOfProcess DISABLED_OutOfProcess
|
|
#else
|
|
#define MAYBE_InProcess InProcess
|
|
#define MAYBE_OutOfProcess OutOfProcess
|
|
#endif // BUILDFLAG(IS_ANDROID)
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
MAYBE_InProcess,
|
|
MAYBE_NetworkServiceDataMigrationBrowserTestWithFailures,
|
|
::testing::Combine(::testing::Values(true),
|
|
::testing::ValuesIn(kFailureTypes)));
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
MAYBE_OutOfProcess,
|
|
MAYBE_NetworkServiceDataMigrationBrowserTestWithFailures,
|
|
::testing::Combine(::testing::Values(false),
|
|
::testing::ValuesIn(kFailureTypes)));
|
|
|
|
#endif // !BUILDFLAG(IS_FUCHSIA)
|
|
|
|
class NetworkServiceInProcessBrowserTest : public ContentBrowserTest {
|
|
public:
|
|
NetworkServiceInProcessBrowserTest() { ForceInProcessNetworkService(); }
|
|
|
|
NetworkServiceInProcessBrowserTest(
|
|
const NetworkServiceInProcessBrowserTest&) = delete;
|
|
NetworkServiceInProcessBrowserTest& operator=(
|
|
const NetworkServiceInProcessBrowserTest&) = delete;
|
|
|
|
void SetUpOnMainThread() override {
|
|
host_resolver()->AddRule("*", "127.0.0.1");
|
|
EXPECT_TRUE(embedded_test_server()->Start());
|
|
}
|
|
};
|
|
|
|
// Verifies that in-process network service works.
|
|
IN_PROC_BROWSER_TEST_F(NetworkServiceInProcessBrowserTest, Basic) {
|
|
GURL test_url = embedded_test_server()->GetURL("foo.com", "/echo");
|
|
StoragePartitionImpl* partition =
|
|
static_cast<StoragePartitionImpl*>(shell()
|
|
->web_contents()
|
|
->GetBrowserContext()
|
|
->GetDefaultStoragePartition());
|
|
EXPECT_TRUE(NavigateToURL(shell(), test_url));
|
|
ASSERT_EQ(net::OK,
|
|
LoadBasicRequest(partition->GetNetworkContext(), test_url));
|
|
}
|
|
|
|
class NetworkServiceInvalidLogBrowserTest : public ContentBrowserTest {
|
|
public:
|
|
NetworkServiceInvalidLogBrowserTest() = default;
|
|
|
|
NetworkServiceInvalidLogBrowserTest(
|
|
const NetworkServiceInvalidLogBrowserTest&) = delete;
|
|
NetworkServiceInvalidLogBrowserTest& operator=(
|
|
const NetworkServiceInvalidLogBrowserTest&) = delete;
|
|
|
|
void SetUpCommandLine(base::CommandLine* command_line) override {
|
|
command_line->AppendSwitchASCII(network::switches::kLogNetLog, "/abc/def");
|
|
}
|
|
|
|
void SetUpOnMainThread() override {
|
|
host_resolver()->AddRule("*", "127.0.0.1");
|
|
EXPECT_TRUE(embedded_test_server()->Start());
|
|
}
|
|
};
|
|
|
|
// Verifies that an invalid --log-net-log flag won't crash the browser.
|
|
IN_PROC_BROWSER_TEST_F(NetworkServiceInvalidLogBrowserTest, Basic) {
|
|
GURL test_url = embedded_test_server()->GetURL("foo.com", "/echo");
|
|
StoragePartitionImpl* partition =
|
|
static_cast<StoragePartitionImpl*>(shell()
|
|
->web_contents()
|
|
->GetBrowserContext()
|
|
->GetDefaultStoragePartition());
|
|
EXPECT_TRUE(NavigateToURL(shell(), test_url));
|
|
ASSERT_EQ(net::OK,
|
|
LoadBasicRequest(partition->GetNetworkContext(), test_url));
|
|
}
|
|
|
|
// Test fixture for using a NetworkService that has a non-default limit on the
|
|
// number of allowed open UDP sockets.
|
|
class NetworkServiceWithUDPSocketLimit : public NetworkServiceBrowserTest {
|
|
public:
|
|
NetworkServiceWithUDPSocketLimit() {
|
|
base::FieldTrialParams params;
|
|
params[net::features::kLimitOpenUDPSocketsMax.name] =
|
|
base::NumberToString(kMaxUDPSockets);
|
|
scoped_feature_list_.InitAndEnableFeatureWithParameters(
|
|
net::features::kLimitOpenUDPSockets, params);
|
|
}
|
|
|
|
protected:
|
|
static constexpr int kMaxUDPSockets = 4;
|
|
|
|
// Creates and synchronously connects a UDPSocket using |network_context|.
|
|
// Returns the network error for Connect().
|
|
int ConnectUDPSocketSync(
|
|
mojo::Remote<network::mojom::NetworkContext>* network_context,
|
|
mojo::Remote<network::mojom::UDPSocket>* socket) {
|
|
network_context->get()->CreateUDPSocket(
|
|
socket->BindNewPipeAndPassReceiver(), mojo::NullRemote());
|
|
|
|
// The address of this endpoint doesn't matter, since Connect() will not
|
|
// actually send any datagrams, and is only being called to verify the
|
|
// socket limit enforcement.
|
|
net::IPEndPoint remote_addr(net::IPAddress(127, 0, 0, 1), 8080);
|
|
|
|
network::mojom::UDPSocketOptionsPtr options =
|
|
network::mojom::UDPSocketOptions::New();
|
|
|
|
net::IPEndPoint local_addr;
|
|
network::test::UDPSocketTestHelper helper(socket);
|
|
return helper.ConnectSync(remote_addr, std::move(options), &local_addr);
|
|
}
|
|
|
|
// Creates a NetworkContext using default parameters.
|
|
mojo::Remote<network::mojom::NetworkContext> CreateNetworkContext() {
|
|
mojo::Remote<network::mojom::NetworkContext> network_context;
|
|
network::mojom::NetworkContextParamsPtr context_params =
|
|
network::mojom::NetworkContextParams::New();
|
|
context_params->cert_verifier_params = GetCertVerifierParams(
|
|
cert_verifier::mojom::CertVerifierCreationParams::New());
|
|
CreateNetworkContextInNetworkService(
|
|
network_context.BindNewPipeAndPassReceiver(),
|
|
std::move(context_params));
|
|
return network_context;
|
|
}
|
|
|
|
private:
|
|
base::test::ScopedFeatureList scoped_feature_list_;
|
|
};
|
|
|
|
// Tests calling Connect() on |kMaxUDPSockets + 4| sockets. The first
|
|
// kMaxUDPSockets should succeed, whereas the last 4 should fail with
|
|
// ERR_INSUFFICIENT_RESOURCES due to having exceeding the global bound.
|
|
IN_PROC_BROWSER_TEST_F(NetworkServiceWithUDPSocketLimit,
|
|
UDPSocketBoundEnforced) {
|
|
constexpr size_t kNumContexts = 2;
|
|
|
|
mojo::Remote<network::mojom::NetworkContext> network_contexts[kNumContexts] =
|
|
{CreateNetworkContext(), CreateNetworkContext()};
|
|
|
|
mojo::Remote<network::mojom::UDPSocket> sockets[kMaxUDPSockets];
|
|
|
|
// Try to connect the maximum number of UDP sockets (|kMaxUDPSockets|),
|
|
// spread evenly between 2 NetworkContexts. These should succeed as the
|
|
// global limit has not been reached yet. This assumes there are no
|
|
// other consumers of UDP sockets in the browser yet.
|
|
for (size_t i = 0; i < kMaxUDPSockets; ++i) {
|
|
auto* network_context = &network_contexts[i % kNumContexts];
|
|
EXPECT_EQ(net::OK, ConnectUDPSocketSync(network_context, &sockets[i]));
|
|
}
|
|
|
|
// Try to connect an additional 4 sockets, alternating between each of the
|
|
// NetworkContexts. These should all fail with ERR_INSUFFICIENT_RESOURCES as
|
|
// the limit has already been reached. Spreading across NetworkContext
|
|
// is done to ensure the socket limit is global and not per
|
|
// NetworkContext.
|
|
for (size_t i = 0; i < 4; ++i) {
|
|
auto* network_context = &network_contexts[i % kNumContexts];
|
|
mojo::Remote<network::mojom::UDPSocket> socket;
|
|
EXPECT_EQ(net::ERR_INSUFFICIENT_RESOURCES,
|
|
ConnectUDPSocketSync(network_context, &socket));
|
|
}
|
|
}
|
|
|
|
class NetworkServiceNetLogBrowserTest : public ContentBrowserTest {
|
|
public:
|
|
NetworkServiceNetLogBrowserTest() {
|
|
EXPECT_TRUE(embedded_test_server()->Start());
|
|
EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
|
|
log_path_ = temp_dir_.Take();
|
|
log_path_ = log_path_.Append(FILE_PATH_LITERAL("my_net_log_file.json"));
|
|
}
|
|
|
|
void SetUpCommandLine(base::CommandLine* command_line) override {
|
|
command_line->AppendSwitchPath(network::switches::kLogNetLog, log_path_);
|
|
}
|
|
|
|
void TearDownInProcessBrowserTestFixture() override {
|
|
// Check that the log file exists and has been written to.
|
|
base::File log_file_read(log_path_,
|
|
base::File::FLAG_OPEN | base::File::FLAG_READ);
|
|
EXPECT_TRUE(log_file_read.IsValid());
|
|
base::File::Info file_info;
|
|
log_file_read.GetInfo(&file_info);
|
|
EXPECT_GT(file_info.size, 0);
|
|
}
|
|
|
|
protected:
|
|
base::FilePath log_path_;
|
|
base::ScopedTempDir temp_dir_;
|
|
};
|
|
|
|
// Tests that a log file is generated and is of non-zero size.
|
|
IN_PROC_BROWSER_TEST_F(NetworkServiceNetLogBrowserTest, LogCreated) {
|
|
// Navigate to a page to generate some data.
|
|
EXPECT_TRUE(NavigateToURL(shell(), embedded_test_server()->GetURL("/echo")));
|
|
// Because the file isn't closed until the network service shuts down the
|
|
// final checks are performed in TearDownInProcessBrowserTestFixture().
|
|
}
|
|
|
|
class NetworkServiceBoundedNetLogBrowserTest
|
|
: public NetworkServiceNetLogBrowserTest {
|
|
public:
|
|
NetworkServiceBoundedNetLogBrowserTest() {
|
|
#if !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_FUCHSIA)
|
|
// Network sandboxing disallows the creation of a temp directory needed by
|
|
// bounded net-logs. Disable it for this test.
|
|
scoped_feature_list_.InitAndDisableFeature(
|
|
sandbox::policy::features::kNetworkServiceSandbox);
|
|
#endif
|
|
}
|
|
|
|
void SetUpCommandLine(base::CommandLine* command_line) override {
|
|
NetworkServiceNetLogBrowserTest::SetUpCommandLine(command_line);
|
|
command_line->AppendSwitchASCII(network::switches::kNetLogMaxSizeMb,
|
|
base::NumberToString(kMaxSizeMegaBytes));
|
|
command_line->AppendSwitchASCII(network::switches::kNetLogCaptureMode,
|
|
"Everything");
|
|
}
|
|
|
|
void TearDownInProcessBrowserTestFixture() override {
|
|
// Check that the log file exists and has been written to.
|
|
base::File log_file_read(log_path_,
|
|
base::File::FLAG_OPEN | base::File::FLAG_READ);
|
|
auto error = log_file_read.error_details();
|
|
EXPECT_EQ(error, base::File::FILE_OK);
|
|
EXPECT_TRUE(log_file_read.IsValid());
|
|
|
|
// Skip for Fuchsia. Fuchsia's file size operation isn't fully supported yet,
|
|
// making the file size meaningless and therefore these checks.
|
|
#if !BUILDFLAG(IS_FUCHSIA)
|
|
base::File::Info file_info;
|
|
log_file_read.GetInfo(&file_info);
|
|
|
|
// The max size is only a rough bound, so let's make sure the final file is
|
|
// within a reasonable range from our max. Let's say 10%.
|
|
const int64_t kMaxSizeUpper = kMaxSizeBytes * 1.1;
|
|
const int64_t kMaxSizeLower = kMaxSizeBytes * 0.9;
|
|
|
|
// Some devices don't always finish flushing the file to disk before
|
|
// control is returned to the test, meaning that if we were to immediately
|
|
// get the file size it would be smaller than expected because it's not
|
|
// fully written out. Keep trying until the file is within the expected
|
|
// range or quit if we reach our timeout.
|
|
|
|
base::Time timeout_time =
|
|
base::Time::Now() + TestTimeouts::action_max_timeout();
|
|
while (
|
|
!(file_info.size > kMaxSizeLower && file_info.size < kMaxSizeUpper)) {
|
|
base::PlatformThread::Sleep(base::Milliseconds(10));
|
|
log_file_read.GetInfo(&file_info);
|
|
|
|
if (base::Time::Now() >= timeout_time) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
EXPECT_GT(file_info.size, kMaxSizeLower);
|
|
EXPECT_LT(file_info.size, kMaxSizeUpper);
|
|
#endif
|
|
}
|
|
|
|
// For testing, have a max log size of 1 MB. 1024*1024 == 2^20 == left shift
|
|
// by 20 bits
|
|
const uint32_t kMaxSizeMegaBytes = 1;
|
|
const uint64_t kMaxSizeBytes = kMaxSizeMegaBytes << 20;
|
|
|
|
private:
|
|
base::test::ScopedFeatureList scoped_feature_list_;
|
|
};
|
|
|
|
// This is disabled for Mac and iOS. Mac due to the crbug below, and iOS because
|
|
// the test is flaky and the feature it's testing isn't usable on iOS.
|
|
//
|
|
// TODO(crbug.com/1466224): Try-bots use a different temp directory that the Mac
|
|
// network sandbox doesn't allow and causes this test to fail. Disable the test
|
|
// until this is resolved.
|
|
#if BUILDFLAG(IS_APPLE)
|
|
#define MAYBE_LogCreated DISABLED_LogCreated
|
|
#else
|
|
#define MAYBE_LogCreated LogCreated
|
|
#endif
|
|
|
|
IN_PROC_BROWSER_TEST_F(NetworkServiceBoundedNetLogBrowserTest,
|
|
MAYBE_LogCreated) {
|
|
// Navigate to a page to generate some data.
|
|
// Through trial and error it was found that this looping navigation results
|
|
// in a ~2MB unbounded net-log file. Since our bounded net-log is limited to
|
|
// 1MB this is fine.
|
|
|
|
// This string is roughly 8KB;
|
|
const std::string kManyAs(8192, 'a');
|
|
for (int i = 0; i < 30; i++) {
|
|
EXPECT_TRUE(NavigateToURL(
|
|
shell(), embedded_test_server()->GetURL("/echo?" + kManyAs)));
|
|
}
|
|
// Because the file isn't closed until the network service shuts down the
|
|
// final checks are performed in TearDownInProcessBrowserTestFixture().
|
|
}
|
|
|
|
#if BUILDFLAG(IS_ANDROID)
|
|
class EmptyNetworkServiceTest : public ContentBrowserTest {
|
|
public:
|
|
EmptyNetworkServiceTest() {
|
|
scoped_feature_list_.InitWithFeatures(
|
|
{network::features::kNetworkServiceEmptyOutOfProcess}, {});
|
|
ForceInProcessNetworkService();
|
|
}
|
|
EmptyNetworkServiceTest(const EmptyNetworkServiceTest&) = delete;
|
|
EmptyNetworkServiceTest& operator=(const EmptyNetworkServiceTest&) = delete;
|
|
|
|
private:
|
|
base::test::ScopedFeatureList scoped_feature_list_;
|
|
};
|
|
|
|
IN_PROC_BROWSER_TEST_F(EmptyNetworkServiceTest, Base) {
|
|
// Check if EmptyNetworkService is available.
|
|
network::mojom::EmptyNetworkService* empty_network_service =
|
|
GetEmptyNetworkServiceForTesting();
|
|
DCHECK(empty_network_service);
|
|
const int32_t kExpected = 42;
|
|
int32_t value = 0;
|
|
base::RunLoop loop;
|
|
empty_network_service->Ping(kExpected,
|
|
base::BindLambdaForTesting([&](int32_t val) {
|
|
value = val;
|
|
loop.Quit();
|
|
}));
|
|
loop.Run();
|
|
EXPECT_EQ(kExpected, value);
|
|
}
|
|
#endif
|
|
|
|
} // namespace
|
|
|
|
} // namespace content
|