|  | // Copyright (c) 2012 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/socket/tcp_client_socket.h" | 
|  |  | 
|  | #include "base/basictypes.h" | 
|  | #include "base/memory/ref_counted.h" | 
|  | #include "base/memory/scoped_ptr.h" | 
|  | #include "net/base/address_list.h" | 
|  | #include "net/base/io_buffer.h" | 
|  | #include "net/base/net_errors.h" | 
|  | #include "net/base/net_log.h" | 
|  | #include "net/base/net_log_unittest.h" | 
|  | #include "net/base/test_completion_callback.h" | 
|  | #include "net/base/winsock_init.h" | 
|  | #include "net/dns/mock_host_resolver.h" | 
|  | #include "net/socket/client_socket_factory.h" | 
|  | #include "net/socket/tcp_listen_socket.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  | #include "testing/platform_test.h" | 
|  |  | 
|  | namespace net { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | const char kServerReply[] = "HTTP/1.1 404 Not Found"; | 
|  |  | 
|  | enum ClientSocketTestTypes { | 
|  | TCP, | 
|  | SCTP | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | class TransportClientSocketTest | 
|  | : public StreamListenSocket::Delegate, | 
|  | public ::testing::TestWithParam<ClientSocketTestTypes> { | 
|  | public: | 
|  | TransportClientSocketTest() | 
|  | : listen_port_(0), | 
|  | socket_factory_(ClientSocketFactory::GetDefaultFactory()), | 
|  | close_server_socket_on_next_send_(false) { | 
|  | } | 
|  |  | 
|  | virtual ~TransportClientSocketTest() { | 
|  | } | 
|  |  | 
|  | // Implement StreamListenSocket::Delegate methods | 
|  | void DidAccept(StreamListenSocket* server, | 
|  | scoped_ptr<StreamListenSocket> connection) override { | 
|  | connected_sock_.reset( | 
|  | static_cast<TCPListenSocket*>(connection.release())); | 
|  | } | 
|  | void DidRead(StreamListenSocket*, const char* str, int len) override { | 
|  | // TODO(dkegel): this might not be long enough to tickle some bugs. | 
|  | connected_sock_->Send(kServerReply, arraysize(kServerReply) - 1, | 
|  | false /* Don't append line feed */); | 
|  | if (close_server_socket_on_next_send_) | 
|  | CloseServerSocket(); | 
|  | } | 
|  | void DidClose(StreamListenSocket* sock) override {} | 
|  |  | 
|  | // Testcase hooks | 
|  | void SetUp() override; | 
|  |  | 
|  | void CloseServerSocket() { | 
|  | // delete the connected_sock_, which will close it. | 
|  | connected_sock_.reset(); | 
|  | } | 
|  |  | 
|  | void PauseServerReads() { | 
|  | connected_sock_->PauseReads(); | 
|  | } | 
|  |  | 
|  | void ResumeServerReads() { | 
|  | connected_sock_->ResumeReads(); | 
|  | } | 
|  |  | 
|  | int DrainClientSocket(IOBuffer* buf, | 
|  | uint32 buf_len, | 
|  | uint32 bytes_to_read, | 
|  | TestCompletionCallback* callback); | 
|  |  | 
|  | void SendClientRequest(); | 
|  |  | 
|  | void set_close_server_socket_on_next_send(bool close) { | 
|  | close_server_socket_on_next_send_ = close; | 
|  | } | 
|  |  | 
|  | protected: | 
|  | uint16 listen_port_; | 
|  | CapturingNetLog net_log_; | 
|  | ClientSocketFactory* const socket_factory_; | 
|  | scoped_ptr<StreamSocket> sock_; | 
|  |  | 
|  | private: | 
|  | scoped_ptr<TCPListenSocket> listen_sock_; | 
|  | scoped_ptr<TCPListenSocket> connected_sock_; | 
|  | bool close_server_socket_on_next_send_; | 
|  | }; | 
|  |  | 
|  | void TransportClientSocketTest::SetUp() { | 
|  | ::testing::TestWithParam<ClientSocketTestTypes>::SetUp(); | 
|  |  | 
|  | // Find a free port to listen on | 
|  | scoped_ptr<TCPListenSocket> sock; | 
|  | uint16 port; | 
|  | // Range of ports to listen on.  Shouldn't need to try many. | 
|  | const uint16 kMinPort = 10100; | 
|  | const uint16 kMaxPort = 10200; | 
|  | #if defined(OS_WIN) | 
|  | EnsureWinsockInit(); | 
|  | #endif | 
|  | for (port = kMinPort; port < kMaxPort; port++) { | 
|  | sock = TCPListenSocket::CreateAndListen("127.0.0.1", port, this); | 
|  | if (sock.get()) | 
|  | break; | 
|  | } | 
|  | ASSERT_TRUE(sock.get() != NULL); | 
|  | listen_sock_ = sock.Pass(); | 
|  | listen_port_ = port; | 
|  |  | 
|  | AddressList addr; | 
|  | // MockHostResolver resolves everything to 127.0.0.1. | 
|  | scoped_ptr<HostResolver> resolver(new MockHostResolver()); | 
|  | HostResolver::RequestInfo info(HostPortPair("localhost", listen_port_)); | 
|  | TestCompletionCallback callback; | 
|  | int rv = resolver->Resolve( | 
|  | info, DEFAULT_PRIORITY, &addr, callback.callback(), NULL, BoundNetLog()); | 
|  | CHECK_EQ(ERR_IO_PENDING, rv); | 
|  | rv = callback.WaitForResult(); | 
|  | CHECK_EQ(rv, OK); | 
|  | sock_ = | 
|  | socket_factory_->CreateTransportClientSocket(addr, | 
|  | &net_log_, | 
|  | NetLog::Source()); | 
|  | } | 
|  |  | 
|  | int TransportClientSocketTest::DrainClientSocket( | 
|  | IOBuffer* buf, uint32 buf_len, | 
|  | uint32 bytes_to_read, TestCompletionCallback* callback) { | 
|  | int rv = OK; | 
|  | uint32 bytes_read = 0; | 
|  |  | 
|  | while (bytes_read < bytes_to_read) { | 
|  | rv = sock_->Read(buf, buf_len, callback->callback()); | 
|  | EXPECT_TRUE(rv >= 0 || rv == ERR_IO_PENDING); | 
|  |  | 
|  | if (rv == ERR_IO_PENDING) | 
|  | rv = callback->WaitForResult(); | 
|  |  | 
|  | EXPECT_GE(rv, 0); | 
|  | bytes_read += rv; | 
|  | } | 
|  |  | 
|  | return static_cast<int>(bytes_read); | 
|  | } | 
|  |  | 
|  | void TransportClientSocketTest::SendClientRequest() { | 
|  | const char request_text[] = "GET / HTTP/1.0\r\n\r\n"; | 
|  | scoped_refptr<IOBuffer> request_buffer( | 
|  | new IOBuffer(arraysize(request_text) - 1)); | 
|  | TestCompletionCallback callback; | 
|  | int rv; | 
|  |  | 
|  | memcpy(request_buffer->data(), request_text, arraysize(request_text) - 1); | 
|  | rv = sock_->Write( | 
|  | request_buffer.get(), arraysize(request_text) - 1, callback.callback()); | 
|  | EXPECT_TRUE(rv >= 0 || rv == ERR_IO_PENDING); | 
|  |  | 
|  | if (rv == ERR_IO_PENDING) | 
|  | rv = callback.WaitForResult(); | 
|  | EXPECT_EQ(rv, static_cast<int>(arraysize(request_text) - 1)); | 
|  | } | 
|  |  | 
|  | // TODO(leighton):  Add SCTP to this list when it is ready. | 
|  | INSTANTIATE_TEST_CASE_P(StreamSocket, | 
|  | TransportClientSocketTest, | 
|  | ::testing::Values(TCP)); | 
|  |  | 
|  | TEST_P(TransportClientSocketTest, Connect) { | 
|  | TestCompletionCallback callback; | 
|  | EXPECT_FALSE(sock_->IsConnected()); | 
|  |  | 
|  | int rv = sock_->Connect(callback.callback()); | 
|  |  | 
|  | net::CapturingNetLog::CapturedEntryList net_log_entries; | 
|  | net_log_.GetEntries(&net_log_entries); | 
|  | EXPECT_TRUE(net::LogContainsBeginEvent( | 
|  | net_log_entries, 0, net::NetLog::TYPE_SOCKET_ALIVE)); | 
|  | EXPECT_TRUE(net::LogContainsBeginEvent( | 
|  | net_log_entries, 1, net::NetLog::TYPE_TCP_CONNECT)); | 
|  | if (rv != OK) { | 
|  | ASSERT_EQ(rv, ERR_IO_PENDING); | 
|  | rv = callback.WaitForResult(); | 
|  | EXPECT_EQ(rv, OK); | 
|  | } | 
|  |  | 
|  | EXPECT_TRUE(sock_->IsConnected()); | 
|  | net_log_.GetEntries(&net_log_entries); | 
|  | EXPECT_TRUE(net::LogContainsEndEvent( | 
|  | net_log_entries, -1, net::NetLog::TYPE_TCP_CONNECT)); | 
|  |  | 
|  | sock_->Disconnect(); | 
|  | EXPECT_FALSE(sock_->IsConnected()); | 
|  | } | 
|  |  | 
|  | TEST_P(TransportClientSocketTest, IsConnected) { | 
|  | scoped_refptr<IOBuffer> buf(new IOBuffer(4096)); | 
|  | TestCompletionCallback callback; | 
|  | uint32 bytes_read; | 
|  |  | 
|  | EXPECT_FALSE(sock_->IsConnected()); | 
|  | EXPECT_FALSE(sock_->IsConnectedAndIdle()); | 
|  | int rv = sock_->Connect(callback.callback()); | 
|  | if (rv != OK) { | 
|  | ASSERT_EQ(rv, ERR_IO_PENDING); | 
|  | rv = callback.WaitForResult(); | 
|  | EXPECT_EQ(rv, OK); | 
|  | } | 
|  | EXPECT_TRUE(sock_->IsConnected()); | 
|  | EXPECT_TRUE(sock_->IsConnectedAndIdle()); | 
|  |  | 
|  | // Send the request and wait for the server to respond. | 
|  | SendClientRequest(); | 
|  |  | 
|  | // Drain a single byte so we know we've received some data. | 
|  | bytes_read = DrainClientSocket(buf.get(), 1, 1, &callback); | 
|  | ASSERT_EQ(bytes_read, 1u); | 
|  |  | 
|  | // Socket should be considered connected, but not idle, due to | 
|  | // pending data. | 
|  | EXPECT_TRUE(sock_->IsConnected()); | 
|  | EXPECT_FALSE(sock_->IsConnectedAndIdle()); | 
|  |  | 
|  | bytes_read = DrainClientSocket( | 
|  | buf.get(), 4096, arraysize(kServerReply) - 2, &callback); | 
|  | ASSERT_EQ(bytes_read, arraysize(kServerReply) - 2); | 
|  |  | 
|  | // After draining the data, the socket should be back to connected | 
|  | // and idle. | 
|  | EXPECT_TRUE(sock_->IsConnected()); | 
|  | EXPECT_TRUE(sock_->IsConnectedAndIdle()); | 
|  |  | 
|  | // This time close the server socket immediately after the server response. | 
|  | set_close_server_socket_on_next_send(true); | 
|  | SendClientRequest(); | 
|  |  | 
|  | bytes_read = DrainClientSocket(buf.get(), 1, 1, &callback); | 
|  | ASSERT_EQ(bytes_read, 1u); | 
|  |  | 
|  | // As above because of data. | 
|  | EXPECT_TRUE(sock_->IsConnected()); | 
|  | EXPECT_FALSE(sock_->IsConnectedAndIdle()); | 
|  |  | 
|  | bytes_read = DrainClientSocket( | 
|  | buf.get(), 4096, arraysize(kServerReply) - 2, &callback); | 
|  | ASSERT_EQ(bytes_read, arraysize(kServerReply) - 2); | 
|  |  | 
|  | // Once the data is drained, the socket should now be seen as not | 
|  | // connected. | 
|  | if (sock_->IsConnected()) { | 
|  | // In the unlikely event that the server's connection closure is not | 
|  | // processed in time, wait for the connection to be closed. | 
|  | rv = sock_->Read(buf.get(), 4096, callback.callback()); | 
|  | EXPECT_EQ(0, callback.GetResult(rv)); | 
|  | EXPECT_FALSE(sock_->IsConnected()); | 
|  | } | 
|  | EXPECT_FALSE(sock_->IsConnectedAndIdle()); | 
|  | } | 
|  |  | 
|  | TEST_P(TransportClientSocketTest, Read) { | 
|  | TestCompletionCallback callback; | 
|  | int rv = sock_->Connect(callback.callback()); | 
|  | if (rv != OK) { | 
|  | ASSERT_EQ(rv, ERR_IO_PENDING); | 
|  |  | 
|  | rv = callback.WaitForResult(); | 
|  | EXPECT_EQ(rv, OK); | 
|  | } | 
|  | SendClientRequest(); | 
|  |  | 
|  | scoped_refptr<IOBuffer> buf(new IOBuffer(4096)); | 
|  | uint32 bytes_read = DrainClientSocket( | 
|  | buf.get(), 4096, arraysize(kServerReply) - 1, &callback); | 
|  | ASSERT_EQ(bytes_read, arraysize(kServerReply) - 1); | 
|  |  | 
|  | // All data has been read now.  Read once more to force an ERR_IO_PENDING, and | 
|  | // then close the server socket, and note the close. | 
|  |  | 
|  | rv = sock_->Read(buf.get(), 4096, callback.callback()); | 
|  | ASSERT_EQ(ERR_IO_PENDING, rv); | 
|  | CloseServerSocket(); | 
|  | EXPECT_EQ(0, callback.WaitForResult()); | 
|  | } | 
|  |  | 
|  | TEST_P(TransportClientSocketTest, Read_SmallChunks) { | 
|  | TestCompletionCallback callback; | 
|  | int rv = sock_->Connect(callback.callback()); | 
|  | if (rv != OK) { | 
|  | ASSERT_EQ(rv, ERR_IO_PENDING); | 
|  |  | 
|  | rv = callback.WaitForResult(); | 
|  | EXPECT_EQ(rv, OK); | 
|  | } | 
|  | SendClientRequest(); | 
|  |  | 
|  | scoped_refptr<IOBuffer> buf(new IOBuffer(1)); | 
|  | uint32 bytes_read = 0; | 
|  | while (bytes_read < arraysize(kServerReply) - 1) { | 
|  | rv = sock_->Read(buf.get(), 1, callback.callback()); | 
|  | EXPECT_TRUE(rv >= 0 || rv == ERR_IO_PENDING); | 
|  |  | 
|  | if (rv == ERR_IO_PENDING) | 
|  | rv = callback.WaitForResult(); | 
|  |  | 
|  | ASSERT_EQ(1, rv); | 
|  | bytes_read += rv; | 
|  | } | 
|  |  | 
|  | // All data has been read now.  Read once more to force an ERR_IO_PENDING, and | 
|  | // then close the server socket, and note the close. | 
|  |  | 
|  | rv = sock_->Read(buf.get(), 1, callback.callback()); | 
|  | ASSERT_EQ(ERR_IO_PENDING, rv); | 
|  | CloseServerSocket(); | 
|  | EXPECT_EQ(0, callback.WaitForResult()); | 
|  | } | 
|  |  | 
|  | TEST_P(TransportClientSocketTest, Read_Interrupted) { | 
|  | TestCompletionCallback callback; | 
|  | int rv = sock_->Connect(callback.callback()); | 
|  | if (rv != OK) { | 
|  | ASSERT_EQ(ERR_IO_PENDING, rv); | 
|  |  | 
|  | rv = callback.WaitForResult(); | 
|  | EXPECT_EQ(rv, OK); | 
|  | } | 
|  | SendClientRequest(); | 
|  |  | 
|  | // Do a partial read and then exit.  This test should not crash! | 
|  | scoped_refptr<IOBuffer> buf(new IOBuffer(16)); | 
|  | rv = sock_->Read(buf.get(), 16, callback.callback()); | 
|  | EXPECT_TRUE(rv >= 0 || rv == ERR_IO_PENDING); | 
|  |  | 
|  | if (rv == ERR_IO_PENDING) | 
|  | rv = callback.WaitForResult(); | 
|  |  | 
|  | EXPECT_NE(0, rv); | 
|  | } | 
|  |  | 
|  | TEST_P(TransportClientSocketTest, DISABLED_FullDuplex_ReadFirst) { | 
|  | TestCompletionCallback callback; | 
|  | int rv = sock_->Connect(callback.callback()); | 
|  | if (rv != OK) { | 
|  | ASSERT_EQ(rv, ERR_IO_PENDING); | 
|  |  | 
|  | rv = callback.WaitForResult(); | 
|  | EXPECT_EQ(rv, OK); | 
|  | } | 
|  |  | 
|  | // Read first.  There's no data, so it should return ERR_IO_PENDING. | 
|  | const int kBufLen = 4096; | 
|  | scoped_refptr<IOBuffer> buf(new IOBuffer(kBufLen)); | 
|  | rv = sock_->Read(buf.get(), kBufLen, callback.callback()); | 
|  | EXPECT_EQ(ERR_IO_PENDING, rv); | 
|  |  | 
|  | PauseServerReads(); | 
|  | const int kWriteBufLen = 64 * 1024; | 
|  | scoped_refptr<IOBuffer> request_buffer(new IOBuffer(kWriteBufLen)); | 
|  | char* request_data = request_buffer->data(); | 
|  | memset(request_data, 'A', kWriteBufLen); | 
|  | TestCompletionCallback write_callback; | 
|  |  | 
|  | while (true) { | 
|  | rv = sock_->Write( | 
|  | request_buffer.get(), kWriteBufLen, write_callback.callback()); | 
|  | ASSERT_TRUE(rv >= 0 || rv == ERR_IO_PENDING); | 
|  |  | 
|  | if (rv == ERR_IO_PENDING) { | 
|  | ResumeServerReads(); | 
|  | rv = write_callback.WaitForResult(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | // 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_P(TransportClientSocketTest, DISABLED_FullDuplex_WriteFirst) { | 
|  | TestCompletionCallback callback; | 
|  | int rv = sock_->Connect(callback.callback()); | 
|  | if (rv != OK) { | 
|  | ASSERT_EQ(ERR_IO_PENDING, rv); | 
|  |  | 
|  | rv = callback.WaitForResult(); | 
|  | EXPECT_EQ(OK, rv); | 
|  | } | 
|  |  | 
|  | PauseServerReads(); | 
|  | const int kWriteBufLen = 64 * 1024; | 
|  | scoped_refptr<IOBuffer> request_buffer(new IOBuffer(kWriteBufLen)); | 
|  | char* request_data = request_buffer->data(); | 
|  | memset(request_data, 'A', kWriteBufLen); | 
|  | TestCompletionCallback write_callback; | 
|  |  | 
|  | while (true) { | 
|  | rv = sock_->Write( | 
|  | request_buffer.get(), kWriteBufLen, write_callback.callback()); | 
|  | ASSERT_TRUE(rv >= 0 || rv == ERR_IO_PENDING); | 
|  |  | 
|  | if (rv == ERR_IO_PENDING) | 
|  | break; | 
|  | } | 
|  |  | 
|  | // 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<IOBuffer> buf(new IOBuffer(kBufLen)); | 
|  | while (true) { | 
|  | rv = sock_->Read(buf.get(), kBufLen, callback.callback()); | 
|  | ASSERT_TRUE(rv >= 0 || rv == ERR_IO_PENDING); | 
|  | if (rv == 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. | 
|  |  | 
|  | ResumeServerReads(); | 
|  | rv = write_callback.WaitForResult(); | 
|  | EXPECT_GE(rv, 0); | 
|  |  | 
|  | // It's possible the read is blocked because it's already read all the data. | 
|  | // Close the server socket, so there will at least be a 0-byte read. | 
|  | CloseServerSocket(); | 
|  |  | 
|  | rv = callback.WaitForResult(); | 
|  | EXPECT_GE(rv, 0); | 
|  | } | 
|  |  | 
|  | }  // namespace net |