chromium/net/socket/tcp_client_socket_unittest.cc

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// This file contains some tests for TCPClientSocket.
// transport_client_socket_unittest.cc contans some other tests that
// are common for TCP and other types of sockets.

#include "net/socket/tcp_client_socket.h"

#include <stddef.h>

#include <set>
#include <string>
#include <vector>

#include "base/strings/string_number_conversions.h"
#include "base/test/bind.h"
#include "base/test/power_monitor_test.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "build/build_config.h"
#include "net/base/features.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/socket_performance_watcher.h"
#include "net/socket/socket_test_util.h"
#include "net/socket/tcp_server_socket.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/gtest_util.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"

// This matches logic in tcp_client_socket.cc. Only used once, but defining it
// in this file instead of just inlining the OS checks where its used makes it
// more grep-able.
#if !BUILDFLAG(IS_ANDROID)
#define TCP_CLIENT_SOCKET_OBSERVES_SUSPEND
#endif

IsError;
IsOk;
Not;

namespace base {
class TimeDelta;
}

namespace net {

namespace {

class TCPClientSocketTest : public testing::Test {};

// Try binding a socket to loopback interface and verify that we can
// still connect to a server on the same interface.
TEST_F(TCPClientSocketTest, BindLoopbackToLoopback) {}

// Try to bind socket to the loopback interface and connect to an
// external address, verify that connection fails.
TEST_F(TCPClientSocketTest, BindLoopbackToExternal) {}

// Bind a socket to the IPv4 loopback interface and try to connect to
// the IPv6 loopback interface, verify that connection fails.
TEST_F(TCPClientSocketTest, BindLoopbackToIPv6) {}

TEST_F(TCPClientSocketTest, WasEverUsed) {}

// Tests that DNS aliases can be stored in a socket for reuse.
TEST_F(TCPClientSocketTest, DnsAliasesPersistForReuse) {}

class TestSocketPerformanceWatcher : public SocketPerformanceWatcher {};

// TestSocketPerformanceWatcher requires kernel support for tcp_info struct, and
// so it is enabled only on certain platforms.
#if defined(TCP_INFO) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
#define MAYBE_TestSocketPerformanceWatcher
#else
#define MAYBE_TestSocketPerformanceWatcher
#endif
// Tests if the socket performance watcher is notified if the same socket is
// used for a different connection.
TEST_F(TCPClientSocketTest, MAYBE_TestSocketPerformanceWatcher) {}

// On Android, where socket tagging is supported, verify that
// TCPClientSocket::Tag works as expected.
#if BUILDFLAG(IS_ANDROID)
TEST_F(TCPClientSocketTest, Tag) {
  if (!CanGetTaggedBytes()) {
    DVLOG(0) << "Skipping test - GetTaggedBytes unsupported.";
    return;
  }

  // Start test server.
  EmbeddedTestServer test_server;
  test_server.AddDefaultHandlers(base::FilePath());
  ASSERT_TRUE(test_server.Start());

  AddressList addr_list;
  ASSERT_TRUE(test_server.GetAddressList(&addr_list));
  TCPClientSocket s(addr_list, nullptr, nullptr, nullptr, NetLogSource());

  // Verify TCP connect packets are tagged and counted properly.
  int32_t tag_val1 = 0x12345678;
  uint64_t old_traffic = GetTaggedBytes(tag_val1);
  SocketTag tag1(SocketTag::UNSET_UID, tag_val1);
  s.ApplySocketTag(tag1);
  TestCompletionCallback connect_callback;
  int connect_result = s.Connect(connect_callback.callback());
  EXPECT_THAT(connect_callback.GetResult(connect_result), IsOk());
  EXPECT_GT(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 = GetTaggedBytes(tag_val2);
  SocketTag tag2(getuid(), tag_val2);
  s.ApplySocketTag(tag2);
  const char kRequest1[] = "GET / HTTP/1.0";
  auto write_buffer1 = base::MakeRefCounted<StringIOBuffer>(kRequest1);
  TestCompletionCallback write_callback1;
  EXPECT_EQ(s.Write(write_buffer1.get(), strlen(kRequest1),
                    write_callback1.callback(), TRAFFIC_ANNOTATION_FOR_TESTS),
            static_cast<int>(strlen(kRequest1)));
  EXPECT_GT(GetTaggedBytes(tag_val2), old_traffic);

  // Verify socket can be retagged with a new value and the current process's
  // UID.
  old_traffic = GetTaggedBytes(tag_val1);
  s.ApplySocketTag(tag1);
  const char kRequest2[] = "\n\n";
  scoped_refptr<IOBufferWithSize> write_buffer2 =
      base::MakeRefCounted<IOBufferWithSize>(strlen(kRequest2));
  memmove(write_buffer2->data(), kRequest2, strlen(kRequest2));
  TestCompletionCallback write_callback2;
  EXPECT_EQ(s.Write(write_buffer2.get(), strlen(kRequest2),
                    write_callback2.callback(), TRAFFIC_ANNOTATION_FOR_TESTS),
            static_cast<int>(strlen(kRequest2)));
  EXPECT_GT(GetTaggedBytes(tag_val1), old_traffic);

  s.Disconnect();
}

TEST_F(TCPClientSocketTest, TagAfterConnect) {
  if (!CanGetTaggedBytes()) {
    DVLOG(0) << "Skipping test - GetTaggedBytes unsupported.";
    return;
  }

  // Start test server.
  EmbeddedTestServer test_server;
  test_server.AddDefaultHandlers(base::FilePath());
  ASSERT_TRUE(test_server.Start());

  AddressList addr_list;
  ASSERT_TRUE(test_server.GetAddressList(&addr_list));
  TCPClientSocket s(addr_list, nullptr, nullptr, nullptr, NetLogSource());

  // Connect socket.
  TestCompletionCallback connect_callback;
  int connect_result = s.Connect(connect_callback.callback());
  EXPECT_THAT(connect_callback.GetResult(connect_result), IsOk());

  // Verify socket can be tagged with a new value and the current process's
  // UID.
  int32_t tag_val2 = 0x87654321;
  uint64_t old_traffic = GetTaggedBytes(tag_val2);
  SocketTag tag2(getuid(), tag_val2);
  s.ApplySocketTag(tag2);
  const char kRequest1[] = "GET / HTTP/1.0";
  auto write_buffer1 = base::MakeRefCounted<StringIOBuffer>(kRequest1);
  TestCompletionCallback write_callback1;
  EXPECT_EQ(s.Write(write_buffer1.get(), strlen(kRequest1),
                    write_callback1.callback(), TRAFFIC_ANNOTATION_FOR_TESTS),
            static_cast<int>(strlen(kRequest1)));
  EXPECT_GT(GetTaggedBytes(tag_val2), old_traffic);

  // Verify socket can be retagged with a new value and the current process's
  // UID.
  int32_t tag_val1 = 0x12345678;
  old_traffic = GetTaggedBytes(tag_val1);
  SocketTag tag1(SocketTag::UNSET_UID, tag_val1);
  s.ApplySocketTag(tag1);
  const char kRequest2[] = "\n\n";
  auto write_buffer2 = base::MakeRefCounted<StringIOBuffer>(kRequest2);
  TestCompletionCallback write_callback2;
  EXPECT_EQ(s.Write(write_buffer2.get(), strlen(kRequest2),
                    write_callback2.callback(), TRAFFIC_ANNOTATION_FOR_TESTS),
            static_cast<int>(strlen(kRequest2)));
  EXPECT_GT(GetTaggedBytes(tag_val1), old_traffic);

  s.Disconnect();
}
#endif  // BUILDFLAG(IS_ANDROID)

// TCP socket that hangs indefinitely when establishing a connection.
class NeverConnectingTCPClientSocket : public TCPClientSocket {};

// Tests for closing sockets on suspend mode.
#if defined(TCP_CLIENT_SOCKET_OBSERVES_SUSPEND)

// Entering suspend mode shouldn't affect sockets that haven't connected yet, or
// listening server sockets.
TEST_F(TCPClientSocketTest, SuspendBeforeConnect) {}

TEST_F(TCPClientSocketTest, SuspendDuringConnect) {}

TEST_F(TCPClientSocketTest, SuspendDuringConnectMultipleAddresses) {}

TEST_F(TCPClientSocketTest, SuspendWhileIdle) {}

TEST_F(TCPClientSocketTest, SuspendDuringRead) {}

TEST_F(TCPClientSocketTest, SuspendDuringWrite) {}

TEST_F(TCPClientSocketTest, SuspendDuringReadAndWrite) {}

#endif  // defined(TCP_CLIENT_SOCKET_OBSERVES_SUSPEND)

// Scoped helper to override the TCP connect attempt policy.
class OverrideTcpConnectAttemptTimeout {};

// Test fixture that uses a MOCK_TIME test environment, so time can
// be advanced programmatically.
class TCPClientSocketMockTimeTest : public testing::Test {};

// Tests that no TCP connect timeout is enforced by default (i.e.
// when the feature is disabled).
TEST_F(TCPClientSocketMockTimeTest, NoConnectAttemptTimeoutByDefault) {}

// Tests that the maximum timeout is used when there is no estimated
// RTT.
TEST_F(TCPClientSocketMockTimeTest, ConnectAttemptTimeoutUsesMaxWhenNoRTT) {}

// Tests that the minimum timeout is used when the adaptive timeout using RTT
// ends up being too low.
TEST_F(TCPClientSocketMockTimeTest, ConnectAttemptTimeoutUsesMinWhenRTTLow) {}

// Tests that the maximum timeout is used when the adaptive timeout from RTT is
// too high.
TEST_F(TCPClientSocketMockTimeTest, ConnectAttemptTimeoutUsesMinWhenRTTHigh) {}

// Tests that an adaptive timeout is used for TCP connection attempts based on
// the estimated RTT.
TEST_F(TCPClientSocketMockTimeTest, ConnectAttemptTimeoutUsesRTT) {}

// Tests that when multiple TCP connect attempts are made, the timeout for each
// one is applied independently.
TEST_F(TCPClientSocketMockTimeTest, ConnectAttemptTimeoutIndependent) {}

}  // namespace

}  // namespace net