blob: 56f31b34c7efca1cc5e8b559c691a3a0df97f667 [file] [log] [blame]
// Copyright 2022 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/feature_list.h"
#include "base/process/process_handle.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_future.h"
#include "build/build_config.h"
#include "content/public/browser/browser_task_traits.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/service_process_host.h"
#include "content/public/browser/service_process_info.h"
#include "content/public/common/content_features.h"
#include "content/public/test/browser_task_environment.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 "net/base/ip_endpoint.h"
#include "net/socket/tcp_socket.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "sandbox/policy/features.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/cpp/transferable_socket.h"
#include "services/network/public/mojom/network_service.mojom.h"
#include "services/network/public/mojom/network_service_test.mojom.h"
#if BUILDFLAG(IS_ANDROID)
#include "base/android/build_info.h"
#endif
namespace content {
namespace {
class TransferableSocketBrowserTest : public ContentBrowserTest {
public:
TransferableSocketBrowserTest() {
#if !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_FUCHSIA)
// Network Service Sandboxing is unconditionally enabled on these
// platforms.
scoped_feature_list_.InitAndEnableFeature(
sandbox::policy::features::kNetworkServiceSandbox);
#endif // !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_FUCHSIA)
ForceOutOfProcessNetworkService();
}
void SetUp() override {
#if BUILDFLAG(IS_WIN)
if (!sandbox::policy::features::IsNetworkSandboxSupported()) {
// On *some* Windows, sandboxing cannot be enabled. We skip all the tests
// on such platforms.
GTEST_SKIP();
}
#elif BUILDFLAG(IS_ANDROID)
if (base::android::BuildInfo::GetInstance()->sdk_int() <
base::android::SdkVersion::SDK_VERSION_R) {
// Android below R does not support transfer of sockets.
GTEST_SKIP();
}
#endif
// These assertions need to precede ContentBrowserTest::SetUp to prevent the
// test body from running when one of the assertions fails.
ASSERT_TRUE(IsOutOfProcessNetworkService());
ASSERT_TRUE(sandbox::policy::features::IsNetworkSandboxEnabled());
ContentBrowserTest::SetUp();
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
IN_PROC_BROWSER_TEST_F(TransferableSocketBrowserTest, TransferSocket) {
size_t request_attempts = 0;
base::RunLoop server_loop;
embedded_test_server()->RegisterRequestHandler(base::BindLambdaForTesting(
[&request_attempts,
&server_loop](const net::test_server::HttpRequest& request)
-> std::unique_ptr<net::test_server::HttpResponse> {
auto response = std::make_unique<net::test_server::BasicHttpResponse>();
response->set_code(net::HTTP_OK);
request_attempts++;
server_loop.Quit();
return response;
}));
ASSERT_TRUE(embedded_test_server()->Start());
// Make sure the network service has started, before attempting to get the
// PID.
network_service_test().FlushForTesting();
mojo::PendingRemote<network::mojom::NetworkServiceTest>
network_service_pending;
GetNetworkService()->BindTestInterfaceForTesting(
network_service_pending.InitWithNewPipeAndPassReceiver());
net::TCPSocket socket(nullptr, nullptr, net::NetLogSource());
socket.Open(net::AddressFamily::ADDRESS_FAMILY_IPV4);
socket.DetachFromThread();
net::IPEndPoint endpoint(net::IPAddress::IPv4Localhost(),
embedded_test_server()->port());
base::RunLoop connect_run_loop;
content::GetIOThreadTaskRunner({})->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(
&net::TCPSocket::Connect, base::Unretained(&socket), endpoint,
base::BindLambdaForTesting([&connect_run_loop](int result) {
EXPECT_EQ(result, net::OK);
connect_run_loop.Quit();
})),
base::BindLambdaForTesting([&connect_run_loop](int result) {
if (result == net::OK) {
connect_run_loop.Quit();
}
EXPECT_EQ(result, net::ERR_IO_PENDING);
}));
connect_run_loop.Run();
socket.DetachFromThread();
#if BUILDFLAG(IS_WIN)
// Obtain the running process id of the network service, as this is needed to
// duplicate the socket on Windows only.
auto processes = ServiceProcessHost::GetRunningProcessInfo();
base::Process network_process;
for (const auto& process : processes) {
if (process.IsService<network::mojom::NetworkService>()) {
ASSERT_FALSE(network_process.IsValid());
network_process = process.GetProcess().Duplicate();
}
}
ASSERT_TRUE(network_process.IsValid());
network::TransferableSocket transferable(
socket.ReleaseSocketDescriptorForTesting(), network_process);
#else
base::test::TestFuture<net::SocketDescriptor> socket_descriptor;
GetIOThreadTaskRunner({})->PostTaskAndReplyWithResult(
FROM_HERE, base::BindLambdaForTesting([&]() {
return socket.ReleaseSocketDescriptorForTesting();
}),
socket_descriptor.GetCallback());
network::TransferableSocket transferable(socket_descriptor.Get());
#endif
{
base::RunLoop network_service_runloop;
network_service_test()->MakeRequestToServer(
std::move(transferable), endpoint,
base::BindLambdaForTesting([&network_service_runloop](bool result) {
EXPECT_TRUE(result);
network_service_runloop.Quit();
}));
network_service_runloop.Run();
}
server_loop.Run();
EXPECT_EQ(1U, request_attempts);
}
} // namespace
} // namespace content