|  | // Copyright 2017 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "net/test/tcp_socket_proxy.h" | 
|  |  | 
|  | #include "base/message_loop/message_loop.h" | 
|  | #include "base/threading/thread.h" | 
|  | #include "build/build_config.h" | 
|  | #include "net/base/io_buffer.h" | 
|  | #include "net/base/test_completion_callback.h" | 
|  | #include "net/socket/tcp_client_socket.h" | 
|  | #include "net/socket/tcp_server_socket.h" | 
|  | #include "net/test/gtest_util.h" | 
|  | #include "net/test/test_with_scoped_task_environment.h" | 
|  | #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" | 
|  | #include "testing/gmock/include/gmock/gmock.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | using net::test::IsOk; | 
|  |  | 
|  | namespace net { | 
|  |  | 
|  | class TcpSocketProxyTest : public TestWithScopedTaskEnvironment { | 
|  | public: | 
|  | TcpSocketProxyTest() : io_thread_("TcpSocketProxyTest IO Thread") { | 
|  | EXPECT_TRUE(io_thread_.StartWithOptions( | 
|  | base::Thread::Options(base::MessageLoop::TYPE_IO, 0))); | 
|  |  | 
|  | listen_socket_ = | 
|  | std::make_unique<TCPServerSocket>(nullptr, net::NetLogSource()); | 
|  | int result = | 
|  | listen_socket_->Listen(IPEndPoint(IPAddress::IPv4Localhost(), 0), 5); | 
|  | EXPECT_THAT(result, IsOk()); | 
|  |  | 
|  | // Get local address. | 
|  | IPEndPoint address; | 
|  | result = listen_socket_->GetLocalAddress(&address); | 
|  | EXPECT_THAT(result, IsOk()); | 
|  |  | 
|  | proxy_ = std::make_unique<TcpSocketProxy>(io_thread_.task_runner()); | 
|  | EXPECT_TRUE(proxy_->Initialize()); | 
|  |  | 
|  | proxy_address_ = | 
|  | IPEndPoint(IPAddress::IPv4Localhost(), proxy_->local_port()); | 
|  | proxy_->Start(address); | 
|  | } | 
|  |  | 
|  | void MakeConnection(std::unique_ptr<StreamSocket>* client_socket, | 
|  | std::unique_ptr<StreamSocket>* server_socket) { | 
|  | TestCompletionCallback connect_callback; | 
|  | *client_socket = std::make_unique<TCPClientSocket>( | 
|  | AddressList(proxy_address_), nullptr, nullptr, NetLogSource()); | 
|  | int connect_result = (*client_socket)->Connect(connect_callback.callback()); | 
|  |  | 
|  | TestCompletionCallback accept_callback; | 
|  | int result = | 
|  | listen_socket_->Accept(server_socket, accept_callback.callback()); | 
|  |  | 
|  | ASSERT_THAT(connect_callback.GetResult(connect_result), IsOk()); | 
|  | ASSERT_THAT(accept_callback.GetResult(result), IsOk()); | 
|  |  | 
|  | EXPECT_TRUE((*server_socket)->IsConnected()); | 
|  | EXPECT_TRUE((*client_socket)->IsConnected()); | 
|  | } | 
|  |  | 
|  | void SendAndReceiveData(StreamSocket* socket1, StreamSocket* socket2) { | 
|  | // Send just one byte to ensure we will need only one Write() and only one | 
|  | // Read(). | 
|  | char test_message = '0'; | 
|  |  | 
|  | scoped_refptr<IOBuffer> write_buffer = base::MakeRefCounted<IOBuffer>(1); | 
|  | *write_buffer->data() = test_message; | 
|  | TestCompletionCallback write_callback; | 
|  | int write_result = | 
|  | socket1->Write(write_buffer.get(), 1, write_callback.callback(), | 
|  | TRAFFIC_ANNOTATION_FOR_TESTS); | 
|  |  | 
|  | scoped_refptr<IOBufferWithSize> read_buffer = | 
|  | base::MakeRefCounted<IOBufferWithSize>(1024); | 
|  | TestCompletionCallback read_callback; | 
|  | int read_result = socket2->Read(read_buffer.get(), read_buffer->size(), | 
|  | read_callback.callback()); | 
|  |  | 
|  | ASSERT_EQ(write_callback.GetResult(write_result), 1); | 
|  | ASSERT_EQ(read_callback.GetResult(read_result), 1); | 
|  |  | 
|  | EXPECT_EQ(test_message, *read_buffer->data()); | 
|  | } | 
|  |  | 
|  | void ExpectClosed(StreamSocket* socket) { | 
|  | scoped_refptr<IOBufferWithSize> read_buffer = | 
|  | base::MakeRefCounted<IOBufferWithSize>(1024); | 
|  | TestCompletionCallback read_callback; | 
|  | int read_result = socket->Read(read_buffer.get(), read_buffer->size(), | 
|  | read_callback.callback()); | 
|  |  | 
|  | EXPECT_EQ(read_callback.GetResult(read_result), 0); | 
|  | EXPECT_FALSE(socket->IsConnected()); | 
|  | } | 
|  |  | 
|  | protected: | 
|  | base::Thread io_thread_; | 
|  |  | 
|  | // Server socket that simulates testserver that TcpSocketProxy normally | 
|  | // would connect to. | 
|  | std::unique_ptr<TCPServerSocket> listen_socket_; | 
|  |  | 
|  | std::unique_ptr<TcpSocketProxy> proxy_; | 
|  |  | 
|  | private: | 
|  | IPEndPoint proxy_address_; | 
|  | }; | 
|  |  | 
|  | TEST_F(TcpSocketProxyTest, SendAndReceive) { | 
|  | std::unique_ptr<StreamSocket> client_socket; | 
|  | std::unique_ptr<StreamSocket> server_socket; | 
|  | MakeConnection(&client_socket, &server_socket); | 
|  | SendAndReceiveData(client_socket.get(), server_socket.get()); | 
|  | SendAndReceiveData(server_socket.get(), client_socket.get()); | 
|  | } | 
|  |  | 
|  | TEST_F(TcpSocketProxyTest, TwoConnections) { | 
|  | std::unique_ptr<StreamSocket> client_socket1; | 
|  | std::unique_ptr<StreamSocket> server_socket1; | 
|  | MakeConnection(&client_socket1, &server_socket1); | 
|  |  | 
|  | std::unique_ptr<StreamSocket> client_socket2; | 
|  | std::unique_ptr<StreamSocket> server_socket2; | 
|  | MakeConnection(&client_socket2, &server_socket2); | 
|  |  | 
|  | SendAndReceiveData(client_socket1.get(), server_socket1.get()); | 
|  | SendAndReceiveData(client_socket2.get(), server_socket2.get()); | 
|  | SendAndReceiveData(server_socket1.get(), client_socket1.get()); | 
|  | SendAndReceiveData(server_socket2.get(), client_socket2.get()); | 
|  | } | 
|  |  | 
|  | // Close socket on the server side and verify that it's closed on the client | 
|  | // side. | 
|  | // TODO(crbug.com/804429): This test hangs occasionally on iOS. | 
|  | #if defined(OS_IOS) | 
|  | #define MAYBE_DisconnectServer DISABLED_DisconnectServer | 
|  | #else | 
|  | #define MAYBE_DisconnectServer DisconnectServer | 
|  | #endif | 
|  | TEST_F(TcpSocketProxyTest, MAYBE_DisconnectServer) { | 
|  | std::unique_ptr<StreamSocket> client_socket; | 
|  | std::unique_ptr<StreamSocket> server_socket; | 
|  | MakeConnection(&client_socket, &server_socket); | 
|  | server_socket.reset(); | 
|  | ExpectClosed(client_socket.get()); | 
|  | } | 
|  |  | 
|  | // Close socket on the client side and verify that it's closed on the server | 
|  | // side. | 
|  | // TODO(crbug.com/804429): This test hangs occasionally on iOS. | 
|  | #if defined(OS_IOS) | 
|  | #define MAYBE_DisconnectClient DISABLED_DisconnectClient | 
|  | #else | 
|  | #define MAYBE_DisconnectClient DisconnectClient | 
|  | #endif | 
|  | TEST_F(TcpSocketProxyTest, MAYBE_DisconnectClient) { | 
|  | std::unique_ptr<StreamSocket> client_socket; | 
|  | std::unique_ptr<StreamSocket> server_socket; | 
|  | MakeConnection(&client_socket, &server_socket); | 
|  | client_socket.reset(); | 
|  | ExpectClosed(server_socket.get()); | 
|  | } | 
|  |  | 
|  | // Starboard does not explicitly disable listening on the same port and win32 | 
|  | // actually does allow two sockets used by the same application to listen on | 
|  | // the same port under some circumstances even though expected behavior in | 
|  | // documentation is "undetermined". | 
|  | #ifndef STARBOARD | 
|  | // Initialize() must fail if the port is in use. | 
|  | TEST_F(TcpSocketProxyTest, PortInUse) { | 
|  | // Try initializing second proxy on the same port. | 
|  | auto proxy2 = std::make_unique<TcpSocketProxy>(io_thread_.task_runner()); | 
|  | EXPECT_FALSE(proxy2->Initialize(proxy_->local_port())); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | }  // namespace net |