| // 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 "services/network/tcp_client_socket_brokered.h" |
| |
| #include "base/test/bind.h" |
| #include "base/test/scoped_run_loop_timeout.h" |
| #include "base/test/task_environment.h" |
| #include "build/build_config.h" |
| #include "net/base/completion_repeating_callback.h" |
| #include "net/base/ip_address.h" |
| #include "net/base/ip_endpoint.h" |
| #include "net/base/net_errors.h" |
| #include "net/base/test_completion_callback.h" |
| #include "net/log/net_log_source.h" |
| #include "net/nqe/network_quality_estimator_test_util.h" |
| #include "net/socket/connection_attempts.h" |
| #include "net/socket/socket_performance_watcher.h" |
| #include "net/socket/socket_test_util.h" |
| #include "net/socket/tcp_server_socket.h" |
| #include "net/socket/transport_client_socket_test_util.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "net/test/gtest_util.h" |
| #include "net/test/test_with_task_environment.h" |
| #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" |
| #include "services/network/brokered_client_socket_factory.h" |
| #include "services/network/test/test_socket_broker_impl.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using net::test::IsError; |
| using net::test::IsOk; |
| using testing::Not; |
| |
| namespace network { |
| |
| class TCPClientSocketBrokeredTest : public testing::Test, |
| public net::WithTaskEnvironment { |
| public: |
| TCPClientSocketBrokeredTest() |
| : receiver_(&socket_broker_impl_), |
| client_socket_factory_( |
| BrokeredClientSocketFactory(receiver_.BindNewPipeAndPassRemote())) { |
| } |
| |
| ~TCPClientSocketBrokeredTest() override = default; |
| |
| void SetUp() override { |
| // Open a server socket on an ephemeral port. |
| listen_socket = |
| std::make_unique<net::TCPServerSocket>(nullptr, net::NetLogSource()); |
| net::IPEndPoint local_address(net::IPAddress::IPv4Localhost(), 0); |
| ASSERT_THAT( |
| listen_socket->Listen(local_address, 1, /*ipv6_only=*/absl::nullopt), |
| IsOk()); |
| // Get the server's address (including the actual port number). |
| ASSERT_THAT(listen_socket->GetLocalAddress(&local_address), IsOk()); |
| listen_socket->Accept(&server_socket_, server_callback_.callback()); |
| net::AddressList addr = net::AddressList::CreateFromIPAddress( |
| net::IPAddress::IPv4Localhost(), local_address.port()); |
| |
| socket_ = client_socket_factory_.CreateTransportClientSocket( |
| addr, nullptr, nullptr, net::NetLog::Get(), net::NetLogSource()); |
| |
| // Confirm that we fail gracefully when making certain calls before |
| // connecting. |
| net::IPEndPoint test_address; |
| EXPECT_EQ(socket_->GetPeerAddress(&test_address), |
| net::ERR_SOCKET_NOT_CONNECTED); |
| EXPECT_EQ(socket_->GetLocalAddress(&test_address), |
| net::ERR_SOCKET_NOT_CONNECTED); |
| EXPECT_EQ(socket_->SetReceiveBufferSize(256), |
| net::ERR_SOCKET_NOT_CONNECTED); |
| EXPECT_EQ(socket_->SetSendBufferSize(256), net::ERR_SOCKET_NOT_CONNECTED); |
| } |
| |
| void ConnectClientSocket(net::TestCompletionCallback* connect_callback) { |
| int connect_result = socket_->Connect(connect_callback->callback()); |
| |
| if (connect_result != net::OK) { |
| ASSERT_EQ(connect_result, net::ERR_IO_PENDING); |
| int accept_result = server_callback_.WaitForResult(); |
| connect_result = connect_callback->WaitForResult(); |
| EXPECT_EQ(accept_result, net::OK); |
| EXPECT_EQ(connect_result, net::OK); |
| } |
| EXPECT_TRUE(socket_->IsConnected()); |
| EXPECT_THAT(connect_callback->GetResult(connect_result), IsOk()); |
| net::IPEndPoint test_address; |
| EXPECT_THAT(socket_->GetPeerAddress(&test_address), IsOk()); |
| EXPECT_THAT(socket_->GetLocalAddress(&test_address), IsOk()); |
| EXPECT_THAT(socket_->SetReceiveBufferSize(256), IsOk()); |
| EXPECT_THAT(socket_->SetSendBufferSize(256), IsOk()); |
| } |
| |
| protected: |
| std::unique_ptr<net::TransportClientSocket> socket_; |
| std::unique_ptr<net::TCPServerSocket> listen_socket; |
| std::unique_ptr<net::StreamSocket> server_socket_; |
| mojo::Receiver<mojom::SocketBroker> receiver_; |
| BrokeredClientSocketFactory client_socket_factory_; |
| net::TestCompletionCallback server_callback_; |
| |
| net::MockClientSocketFactory mock_client_socket_factory_; |
| net::StaticSocketDataProvider data_; |
| |
| TestSocketBrokerImpl socket_broker_impl_; |
| |
| bool close_server_socket_on_next_send_; |
| }; |
| |
| TEST_F(TCPClientSocketBrokeredTest, FailedConnect) { |
| net::TestCompletionCallback callback; |
| base::test::ScopedDisableRunLoopTimeout disable_timeout; |
| |
| socket_broker_impl_.SetMockSocketTest(true); |
| |
| int result = socket_->Connect(callback.callback()); |
| |
| ASSERT_EQ(result, net::ERR_IO_PENDING); |
| result = callback.WaitForResult(); |
| EXPECT_EQ(result, net::ERR_CONNECTION_FAILED); |
| } |
| |
| TEST_F(TCPClientSocketBrokeredTest, WasEverUsed) { |
| net::TestCompletionCallback callback; |
| EXPECT_FALSE(socket_->WasEverUsed()); |
| |
| // Writing before connecting should return ERR_SOCKET_NOT_CONNECTED |
| const char kRequest[] = "GET / HTTP/1.0"; |
| auto write_buffer = base::MakeRefCounted<net::StringIOBuffer>(kRequest); |
| net::TestCompletionCallback write_callback_before_connect; |
| int result = socket_->Write(write_buffer.get(), write_buffer->size(), |
| write_callback_before_connect.callback(), |
| TRAFFIC_ANNOTATION_FOR_TESTS); |
| EXPECT_EQ(result, net::ERR_SOCKET_NOT_CONNECTED); |
| ConnectClientSocket(&callback); |
| |
| // Just connecting the socket should not set WasEverUsed. |
| EXPECT_FALSE(socket_->WasEverUsed()); |
| |
| // Writing some data to the socket _should_ set WasEverUsed. |
| net::TestCompletionCallback write_callback_after_connect; |
| result = socket_->Write(write_buffer.get(), write_buffer->size(), |
| write_callback_after_connect.callback(), |
| TRAFFIC_ANNOTATION_FOR_TESTS); |
| EXPECT_TRUE(socket_->WasEverUsed()); |
| socket_->Disconnect(); |
| EXPECT_FALSE(socket_->IsConnected()); |
| |
| EXPECT_TRUE(socket_->WasEverUsed()); |
| |
| socket_->Disconnect(); |
| } |
| |
| TEST_F(TCPClientSocketBrokeredTest, SetKeepAlive) { |
| net::TestCompletionCallback callback; |
| |
| // Non-connected sockets should not be able to set KeepAlive. |
| ASSERT_FALSE(socket_->IsConnected()); |
| EXPECT_FALSE(socket_->SetKeepAlive(true /* enable */, 14 /* delay */)); |
| |
| ConnectClientSocket(&callback); |
| |
| // Connected sockets should be able to enable and disable KeepAlive. |
| ASSERT_TRUE(socket_->IsConnected()); |
| EXPECT_TRUE(socket_->SetKeepAlive(true /* enable */, 22 /* delay */)); |
| EXPECT_TRUE(socket_->SetKeepAlive(false /* disable */, 3 /* delay */)); |
| |
| socket_->Disconnect(); |
| } |
| |
| TEST_F(TCPClientSocketBrokeredTest, SetNoDelay) { |
| net::TestCompletionCallback callback; |
| |
| // Non-connected sockets should not be able to set NoDelay. |
| ASSERT_FALSE(socket_->IsConnected()); |
| EXPECT_FALSE(socket_->SetNoDelay(true /* no_delay */)); |
| |
| ConnectClientSocket(&callback); |
| |
| // Connected sockets should be able to enable and disable NoDelay. |
| ASSERT_TRUE(socket_->IsConnected()); |
| EXPECT_TRUE(socket_->SetNoDelay(true /* no_delay */)); |
| EXPECT_TRUE(socket_->SetNoDelay(false /* no_delay */)); |
| |
| socket_->Disconnect(); |
| } |
| |
| TEST_F(TCPClientSocketBrokeredTest, CancelReadIfReady) { |
| net::TestCompletionCallback callback; |
| ASSERT_FALSE(socket_->IsConnected()); |
| |
| net::TestCompletionCallback read_callback; |
| auto read_buf = base::MakeRefCounted<net::IOBuffer>(1); |
| |
| // Check that we gracefully fail when attempting to read or cancel reads |
| // before connecting. |
| ASSERT_EQ(socket_->Read(read_buf.get(), 1, read_callback.callback()), |
| net::ERR_SOCKET_NOT_CONNECTED); |
| ASSERT_EQ(socket_->ReadIfReady(read_buf.get(), 1, read_callback.callback()), |
| net::ERR_SOCKET_NOT_CONNECTED); |
| ASSERT_EQ(socket_->CancelReadIfReady(), net::ERR_SOCKET_NOT_CONNECTED); |
| |
| // Confirm no bytes have been read |
| ASSERT_EQ(socket_->GetTotalReceivedBytes(), 0); |
| |
| ConnectClientSocket(&callback); |
| |
| // Attempt to read from the socket. There will not be anything to read. |
| // Cancel the read immediately afterwards. |
| int read_ret = |
| socket_->ReadIfReady(read_buf.get(), 1, read_callback.callback()); |
| ASSERT_THAT(read_ret, IsError(net::ERR_IO_PENDING)); |
| ASSERT_THAT(socket_->CancelReadIfReady(), IsOk()); |
| |
| // After the client writes data, the server should still not pick up a result. |
| auto write_buf = base::MakeRefCounted<net::StringIOBuffer>("a"); |
| net::TestCompletionCallback write_callback; |
| ASSERT_EQ(write_callback.GetResult(server_socket_->Write( |
| write_buf.get(), write_buf->size(), write_callback.callback(), |
| TRAFFIC_ANNOTATION_FOR_TESTS)), |
| write_buf->size()); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(read_callback.have_result()); |
| |
| // After a canceled read, future reads are still possible. |
| while (true) { |
| net::TestCompletionCallback read_callback2; |
| read_ret = |
| socket_->ReadIfReady(read_buf.get(), 1, read_callback2.callback()); |
| if (read_ret != net::ERR_IO_PENDING) { |
| break; |
| } |
| ASSERT_THAT(read_callback2.GetResult(read_ret), IsOk()); |
| } |
| EXPECT_EQ(socket_->GetTotalReceivedBytes(), 1); |
| ASSERT_EQ(1, read_ret); |
| EXPECT_EQ(read_buf->data()[0], 'a'); |
| |
| socket_->Disconnect(); |
| } |
| |
| // IsConnected, FullDuplex_ReadFirst, and FullDuplex_WriteFirst are duplicated |
| // from transport_client_socket_unittest.cc since tests in //net can't depend on |
| // anything outside of //net. |
| TEST_F(TCPClientSocketBrokeredTest, IsConnected) { |
| scoped_refptr<net::IOBuffer> buf = base::MakeRefCounted<net::IOBuffer>(4096); |
| net::TestCompletionCallback callback; |
| uint32_t bytes_read; |
| const char kServerReply[] = "HTTP/1.1 404 Not Found"; |
| |
| EXPECT_FALSE(socket_->IsConnected()); |
| EXPECT_FALSE(socket_->IsConnectedAndIdle()); |
| |
| ConnectClientSocket(&callback); |
| |
| EXPECT_TRUE(socket_->IsConnected()); |
| EXPECT_TRUE(socket_->IsConnectedAndIdle()); |
| |
| // Send the request and wait for the server to respond. |
| net::SendRequestAndResponse(socket_.get(), server_socket_.get()); |
| |
| // Drain a single byte so we know we've received some data. |
| bytes_read = |
| net::DrainStreamSocket(socket_.get(), buf.get(), 1, 1, &callback); |
| ASSERT_EQ(bytes_read, 1u); |
| |
| // Socket should be considered connected, but not idle, due to |
| // pending data. |
| EXPECT_TRUE(socket_->IsConnected()); |
| EXPECT_FALSE(socket_->IsConnectedAndIdle()); |
| |
| bytes_read = net::DrainStreamSocket(socket_.get(), buf.get(), 4096, |
| strlen(kServerReply) - 1, &callback); |
| ASSERT_EQ(bytes_read, strlen(kServerReply) - 1); |
| |
| // After draining the data, the socket should be back to connected |
| // and idle. |
| EXPECT_TRUE(socket_->IsConnected()); |
| EXPECT_TRUE(socket_->IsConnectedAndIdle()); |
| |
| // This time close the server socket immediately after the server response. |
| net::SendRequestAndResponse(socket_.get(), server_socket_.get()); |
| server_socket_.reset(); |
| |
| bytes_read = |
| net::DrainStreamSocket(socket_.get(), buf.get(), 1, 1, &callback); |
| ASSERT_EQ(bytes_read, 1u); |
| |
| // As above because of data. |
| EXPECT_TRUE(socket_->IsConnected()); |
| EXPECT_FALSE(socket_->IsConnectedAndIdle()); |
| |
| bytes_read = net::DrainStreamSocket(socket_.get(), buf.get(), 4096, |
| strlen(kServerReply) - 1, &callback); |
| ASSERT_EQ(bytes_read, strlen(kServerReply) - 1); |
| |
| // Once the data is drained, the socket should now be seen as not |
| // connected. |
| if (socket_->IsConnected()) { |
| // In the unlikely event that the server's connection closure is not |
| // processed in time, wait for the connection to be closed. |
| int rv = socket_->Read(buf.get(), 4096, callback.callback()); |
| EXPECT_EQ(0, callback.GetResult(rv)); |
| EXPECT_FALSE(socket_->IsConnected()); |
| } |
| EXPECT_FALSE(socket_->IsConnectedAndIdle()); |
| } |
| |
| TEST_F(TCPClientSocketBrokeredTest, FullDuplex_ReadFirst) { |
| net::TestCompletionCallback callback; |
| ConnectClientSocket(&callback); |
| |
| // Read first. There's no data, so it should return ERR_IO_PENDING. |
| const int kBufLen = 4096; |
| scoped_refptr<net::IOBuffer> buf = |
| base::MakeRefCounted<net::IOBuffer>(kBufLen); |
| int rv = socket_->Read(buf.get(), kBufLen, callback.callback()); |
| EXPECT_THAT(rv, IsError(net::ERR_IO_PENDING)); |
| |
| const int kWriteBufLen = 64 * 1024; |
| scoped_refptr<net::IOBuffer> request_buffer = |
| base::MakeRefCounted<net::IOBuffer>(kWriteBufLen); |
| char* request_data = request_buffer->data(); |
| memset(request_data, 'A', kWriteBufLen); |
| net::TestCompletionCallback write_callback; |
| |
| int bytes_written = 0; |
| while (true) { |
| rv = |
| socket_->Write(request_buffer.get(), kWriteBufLen, |
| write_callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS); |
| ASSERT_TRUE(rv >= 0 || rv == net::ERR_IO_PENDING); |
| if (rv == net::ERR_IO_PENDING) { |
| net::ReadDataOfExpectedLength(server_socket_.get(), bytes_written); |
| net::SendServerResponse(server_socket_.get()); |
| rv = write_callback.WaitForResult(); |
| break; |
| } |
| bytes_written += rv; |
| } |
| |
| // At this point, both read and write have returned ERR_IO_PENDING, and the |
| // write callback has executed. We wait for the read callback to run now to |
| // make sure that the socket can handle full duplex communications. |
| |
| rv = callback.WaitForResult(); |
| EXPECT_GE(rv, 0); |
| } |
| |
| TEST_F(TCPClientSocketBrokeredTest, FullDuplex_WriteFirst) { |
| net::TestCompletionCallback callback; |
| ConnectClientSocket(&callback); |
| |
| const int kWriteBufLen = 64 * 1024; |
| scoped_refptr<net::IOBuffer> request_buffer = |
| base::MakeRefCounted<net::IOBuffer>(kWriteBufLen); |
| char* request_data = request_buffer->data(); |
| memset(request_data, 'A', kWriteBufLen); |
| net::TestCompletionCallback write_callback; |
| |
| int bytes_written = 0; |
| while (true) { |
| int rv = |
| socket_->Write(request_buffer.get(), kWriteBufLen, |
| write_callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS); |
| ASSERT_TRUE(rv >= 0 || rv == net::ERR_IO_PENDING); |
| |
| if (rv == net::ERR_IO_PENDING) |
| break; |
| bytes_written += rv; |
| } |
| |
| // Now we have the Write() blocked on ERR_IO_PENDING. It's time to force the |
| // Read() to block on ERR_IO_PENDING too. |
| |
| const int kBufLen = 4096; |
| scoped_refptr<net::IOBuffer> buf = |
| base::MakeRefCounted<net::IOBuffer>(kBufLen); |
| while (true) { |
| int rv = socket_->Read(buf.get(), kBufLen, callback.callback()); |
| ASSERT_TRUE(rv >= 0 || rv == net::ERR_IO_PENDING); |
| if (rv == net::ERR_IO_PENDING) |
| break; |
| } |
| |
| // At this point, both read and write have returned ERR_IO_PENDING. Now we |
| // run the write and read callbacks to make sure they can handle full duplex |
| // communications. |
| |
| net::ReadDataOfExpectedLength(server_socket_.get(), bytes_written); |
| net::SendServerResponse(server_socket_.get()); |
| int rv = write_callback.WaitForResult(); |
| EXPECT_GE(rv, 0); |
| |
| rv = callback.WaitForResult(); |
| EXPECT_GT(rv, 0); |
| } |
| |
| // Tests that setting a socket option in the BeforeConnectCallback works. With |
| // real sockets, socket options often have to be set before the connect() call, |
| // and the BeforeConnectCallback is the only way to do that, with a |
| // TCPClientSocket. |
| TEST_F(TCPClientSocketBrokeredTest, BeforeConnectCallback) { |
| net::TestCompletionCallback callback; |
| |
| EXPECT_FALSE(socket_->IsConnected()); |
| EXPECT_FALSE(socket_->IsConnectedAndIdle()); |
| |
| bool callback_was_called = false; |
| socket_->SetBeforeConnectCallback(base::BindLambdaForTesting([&] { |
| EXPECT_FALSE(socket_->IsConnected()); |
| callback_was_called = true; |
| return int{net::OK}; |
| })); |
| |
| ConnectClientSocket(&callback); |
| |
| EXPECT_TRUE(callback_was_called); |
| } |
| |
| // Duplicated from tcp_client_socket_unittest.cc since tests in //net can't |
| // depend on anything outside of //net. |
| // |
| // On Android, where socket tagging is |
| // supported, verify that TCPClientSocketBrokered::Tag works as expected. |
| #if BUILDFLAG(IS_ANDROID) |
| TEST_F(TCPClientSocketBrokeredTest, Tag) { |
| if (!net::CanGetTaggedBytes()) { |
| DVLOG(0) << "Skipping test - GetTaggedBytes unsupported."; |
| return; |
| } |
| |
| // Start test server. |
| net::EmbeddedTestServer test_server; |
| test_server.AddDefaultHandlers(base::FilePath()); |
| ASSERT_TRUE(test_server.Start()); |
| |
| net::AddressList addr_list; |
| ASSERT_TRUE(test_server.GetAddressList(&addr_list)); |
| |
| TCPClientSocketBrokered client_socket(addr_list, nullptr, nullptr, nullptr, |
| net::NetLogSource(), |
| &client_socket_factory_); |
| |
| // Verify TCP connect packets are tagged and counted properly. |
| int32_t tag_val1 = 0x12345678; |
| uint64_t old_traffic = net::GetTaggedBytes(tag_val1); |
| net::SocketTag tag1(net::SocketTag::UNSET_UID, tag_val1); |
| client_socket.ApplySocketTag(tag1); |
| |
| // Connect socket. |
| net::TestCompletionCallback connect_callback; |
| int connect_result = client_socket.Connect(connect_callback.callback()); |
| EXPECT_THAT(connect_callback.GetResult(connect_result), IsOk()); |
| EXPECT_GT(net::GetTaggedBytes(tag_val1), old_traffic); |
| |
| // Verify socket can be retagged with a new value and the current process's |
| // UID. |
| int32_t tag_val2 = 0x87654321; |
| old_traffic = net::GetTaggedBytes(tag_val2); |
| net::SocketTag tag2(getuid(), tag_val2); |
| client_socket.ApplySocketTag(tag2); |
| const char kRequest1[] = "GET / HTTP/1.0"; |
| scoped_refptr<net::IOBuffer> write_buffer1 = |
| base::MakeRefCounted<net::StringIOBuffer>(kRequest1); |
| net::TestCompletionCallback write_callback1; |
| EXPECT_EQ(client_socket.Write(write_buffer1.get(), strlen(kRequest1), |
| write_callback1.callback(), |
| TRAFFIC_ANNOTATION_FOR_TESTS), |
| static_cast<int>(strlen(kRequest1))); |
| EXPECT_GT(net::GetTaggedBytes(tag_val2), old_traffic); |
| |
| // Verify socket can be retagged with a new value and the current process's |
| // UID. |
| old_traffic = net::GetTaggedBytes(tag_val1); |
| client_socket.ApplySocketTag(tag1); |
| const char kRequest2[] = "\n\n"; |
| scoped_refptr<net::IOBufferWithSize> write_buffer2 = |
| base::MakeRefCounted<net::IOBufferWithSize>(strlen(kRequest2)); |
| memmove(write_buffer2->data(), kRequest2, strlen(kRequest2)); |
| net::TestCompletionCallback write_callback2; |
| EXPECT_EQ(client_socket.Write(write_buffer2.get(), strlen(kRequest2), |
| write_callback2.callback(), |
| TRAFFIC_ANNOTATION_FOR_TESTS), |
| static_cast<int>(strlen(kRequest2))); |
| EXPECT_GT(net::GetTaggedBytes(tag_val1), old_traffic); |
| |
| client_socket.Disconnect(); |
| } |
| #endif // BUILDFLAG(IS_ANDROID) |
| |
| } // namespace network |