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

#include <stdint.h>

#include <string>
#include <string_view>
#include <utility>
#include <vector>

#include "base/check_op.h"
#include "base/containers/span.h"
#include "base/functional/bind.h"
#include "base/run_loop.h"
#include "base/strings/string_util.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "base/threading/thread.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "net/base/completion_once_callback.h"
#include "net/base/net_errors.h"
#include "net/base/network_anonymization_key.h"
#include "net/base/test_completion_callback.h"
#include "net/proxy_resolution/configured_proxy_resolution_service.h"
#include "net/socket/server_socket.h"
#include "net/socket/socket_test_util.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 "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_builder.h"
#include "net/url_request/url_request_test_util.h"
#include "services/network/mojo_socket_test_util.h"
#include "services/network/proxy_resolving_socket_factory_mojo.h"
#include "services/network/public/mojom/network_service.mojom.h"
#include "services/network/public/mojom/tls_socket.mojom.h"
#include "services/network/socket_factory.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace network {

namespace {

// Message sent over the tcp connection.
constexpr std::string_view kMsg = "please start tls!";

// Message sent over the tls connection.
constexpr std::string_view kSecretMsg = "here is secret.";

class TLSClientSocketTestBase {
 public:
  enum Mode { kDirect, kProxyResolving };

  explicit TLSClientSocketTestBase(Mode mode)
      : mode_(mode),
        task_environment_(base::test::TaskEnvironment::MainThreadType::IO) {}

  TLSClientSocketTestBase(const TLSClientSocketTestBase&) = delete;
  TLSClientSocketTestBase& operator=(const TLSClientSocketTestBase&) = delete;

  virtual ~TLSClientSocketTestBase() {}

  Mode mode() { return mode_; }

 protected:
  // One of the two fields will be set, depending on the mode.
  struct SocketHandle {
    mojo::Remote<mojom::TCPConnectedSocket> tcp_socket;
    mojo::Remote<mojom::ProxyResolvingSocket> proxy_socket;
  };

  struct SocketRequest {
    mojo::PendingReceiver<mojom::TCPConnectedSocket> tcp_socket_receiver;
    mojo::PendingReceiver<mojom::ProxyResolvingSocket> proxy_socket_receiver;
  };

  // Initializes the test fixture. If |use_mock_sockets|, mock client socket
  // factory will be used.
  void Init(bool use_mock_sockets, bool configure_proxy) {
    auto context_builder = net::CreateTestURLRequestContextBuilder();
    if (use_mock_sockets) {
      mock_client_socket_factory_.set_enable_read_if_ready(true);
      context_builder->set_client_socket_factory_for_testing(
          &mock_client_socket_factory_);
    }
    if (configure_proxy) {
      context_builder->set_proxy_resolution_service(
          net::ConfiguredProxyResolutionService::CreateFixedForTest(
              "http://proxy:8080", TRAFFIC_ANNOTATION_FOR_TESTS));
    }
    url_request_context_ = context_builder->Build();
    factory_ = std::make_unique<SocketFactory>(/*net_log=*/nullptr,
                                               url_request_context_.get());
    proxy_resolving_factory_ =
        std::make_unique<ProxyResolvingSocketFactoryMojo>(
            url_request_context_.get());
  }

  // Reads |num_bytes| from |handle| or reads until an error occurs. Returns the
  // bytes read as a string.
  std::string Read(mojo::ScopedDataPipeConsumerHandle* handle,
                   size_t num_bytes) {
    std::string received_contents;
    while (received_contents.size() < num_bytes) {
      base::RunLoop().RunUntilIdle();
      std::string buffer(num_bytes, '\0');
      size_t actually_read_bytes = 0;
      MojoResult result = handle->get().ReadData(
          MOJO_READ_DATA_FLAG_NONE, base::as_writable_byte_span(buffer),
          actually_read_bytes);
      if (result == MOJO_RESULT_SHOULD_WAIT)
        continue;
      if (result != MOJO_RESULT_OK)
        return received_contents;
      received_contents.append(
          std::string_view(buffer).substr(0, actually_read_bytes));
    }
    return received_contents;
  }

  SocketRequest MakeRequest(SocketHandle* handle) {
    SocketRequest result;
    if (mode_ == kDirect)
      result.tcp_socket_receiver =
          handle->tcp_socket.BindNewPipeAndPassReceiver();
    else
      result.proxy_socket_receiver =
          handle->proxy_socket.BindNewPipeAndPassReceiver();
    return result;
  }

  void ResetSocket(SocketHandle* handle) {
    if (mode_ == kDirect)
      handle->tcp_socket.reset();
    else
      handle->proxy_socket.reset();
  }

  int CreateSocketSync(SocketRequest request,
                       const net::IPEndPoint& remote_addr) {
    if (mode_ == kDirect) {
      return CreateTCPConnectedSocketSync(
          std::move(request.tcp_socket_receiver), remote_addr);
    } else {
      return CreateProxyResolvingSocketSync(
          std::move(request.proxy_socket_receiver), remote_addr);
    }
  }

  int CreateTCPConnectedSocketSync(
      mojo::PendingReceiver<mojom::TCPConnectedSocket> receiver,
      const net::IPEndPoint& remote_addr) {
    net::AddressList remote_addr_list(remote_addr);
    base::RunLoop run_loop;
    int net_error = net::ERR_FAILED;
    factory_->CreateTCPConnectedSocket(
        std::nullopt /* local_addr */, remote_addr_list,
        nullptr /* tcp_connected_socket_options */,
        net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
        std::move(receiver), pre_tls_observer()->GetObserverRemote(),
        base::BindLambdaForTesting(
            [&](int result,
                const std::optional<net::IPEndPoint>& actual_local_addr,
                const std::optional<net::IPEndPoint>& peer_addr,
                mojo::ScopedDataPipeConsumerHandle receive_pipe_handle,
                mojo::ScopedDataPipeProducerHandle send_pipe_handle) {
              net_error = result;
              pre_tls_recv_handle_ = std::move(receive_pipe_handle);
              pre_tls_send_handle_ = std::move(send_pipe_handle);
              run_loop.Quit();
            }));
    run_loop.Run();
    return net_error;
  }

  int CreateProxyResolvingSocketSync(
      mojo::PendingReceiver<mojom::ProxyResolvingSocket> receiver,
      const net::IPEndPoint& remote_addr) {
    GURL url("https://" + remote_addr.ToString());
    base::RunLoop run_loop;
    int net_error = net::ERR_FAILED;
    proxy_resolving_factory_->CreateProxyResolvingSocket(
        url, net::NetworkAnonymizationKey(), nullptr /* options */,
        net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
        std::move(receiver), mojo::NullRemote() /* observer */,
        base::BindLambdaForTesting(
            [&](int result,
                const std::optional<net::IPEndPoint>& actual_local_addr,
                const std::optional<net::IPEndPoint>& peer_addr,
                mojo::ScopedDataPipeConsumerHandle receive_pipe_handle,
                mojo::ScopedDataPipeProducerHandle send_pipe_handle) {
              net_error = result;
              pre_tls_recv_handle_ = std::move(receive_pipe_handle);
              pre_tls_send_handle_ = std::move(send_pipe_handle);
              run_loop.Quit();
            }));
    run_loop.Run();
    return net_error;
  }

  void UpgradeToTLS(SocketHandle* handle,
                    const net::HostPortPair& host_port_pair,
                    mojo::PendingReceiver<mojom::TLSClientSocket> receiver,
                    net::CompletionOnceCallback callback) {
    if (mode_ == kDirect) {
      UpgradeTCPConnectedSocketToTLS(handle->tcp_socket.get(), host_port_pair,
                                     nullptr /* options */, std::move(receiver),
                                     std::move(callback));
    } else {
      UpgradeProxyResolvingSocketToTLS(handle->proxy_socket.get(),
                                       host_port_pair, std::move(receiver),
                                       std::move(callback));
    }
  }

  void UpgradeTCPConnectedSocketToTLS(
      mojom::TCPConnectedSocket* client_socket,
      const net::HostPortPair& host_port_pair,
      mojom::TLSClientSocketOptionsPtr options,
      mojo::PendingReceiver<mojom::TLSClientSocket> receiver,
      net::CompletionOnceCallback callback) {
    client_socket->UpgradeToTLS(
        host_port_pair, std::move(options),
        net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
        std::move(receiver), post_tls_observer()->GetObserverRemote(),
        base::BindOnce(
            [](net::CompletionOnceCallback cb,
               mojo::ScopedDataPipeConsumerHandle* consumer_handle_out,
               mojo::ScopedDataPipeProducerHandle* producer_handle_out,
               std::optional<net::SSLInfo>* ssl_info_out, int result,
               mojo::ScopedDataPipeConsumerHandle receive_pipe_handle,
               mojo::ScopedDataPipeProducerHandle send_pipe_handle,
               const std::optional<net::SSLInfo>& ssl_info) {
              *consumer_handle_out = std::move(receive_pipe_handle);
              *producer_handle_out = std::move(send_pipe_handle);
              *ssl_info_out = ssl_info;
              std::move(cb).Run(result);
            },
            std::move(callback), &post_tls_recv_handle_, &post_tls_send_handle_,
            &ssl_info_));
  }

  void UpgradeProxyResolvingSocketToTLS(
      mojom::ProxyResolvingSocket* client_socket,
      const net::HostPortPair& host_port_pair,
      mojo::PendingReceiver<mojom::TLSClientSocket> receiver,
      net::CompletionOnceCallback callback) {
    client_socket->UpgradeToTLS(
        host_port_pair,
        net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
        std::move(receiver), post_tls_observer()->GetObserverRemote(),
        base::BindOnce(
            [](net::CompletionOnceCallback cb,
               mojo::ScopedDataPipeConsumerHandle* consumer_handle,
               mojo::ScopedDataPipeProducerHandle* producer_handle, int result,
               mojo::ScopedDataPipeConsumerHandle receive_pipe_handle,
               mojo::ScopedDataPipeProducerHandle send_pipe_handle) {
              *consumer_handle = std::move(receive_pipe_handle);
              *producer_handle = std::move(send_pipe_handle);
              std::move(cb).Run(result);
            },
            std::move(callback), &post_tls_recv_handle_,
            &post_tls_send_handle_));
  }

  TestSocketObserver* pre_tls_observer() { return &pre_tls_observer_; }
  TestSocketObserver* post_tls_observer() { return &post_tls_observer_; }

  mojo::ScopedDataPipeConsumerHandle* pre_tls_recv_handle() {
    return &pre_tls_recv_handle_;
  }

  mojo::ScopedDataPipeProducerHandle* pre_tls_send_handle() {
    return &pre_tls_send_handle_;
  }

  mojo::ScopedDataPipeConsumerHandle* post_tls_recv_handle() {
    return &post_tls_recv_handle_;
  }

  mojo::ScopedDataPipeProducerHandle* post_tls_send_handle() {
    return &post_tls_send_handle_;
  }

  const std::optional<net::SSLInfo>& ssl_info() { return ssl_info_; }

  net::MockClientSocketFactory* mock_client_socket_factory() {
    return &mock_client_socket_factory_;
  }

  Mode mode() const { return mode_; }

 private:
  Mode mode_;
  base::test::TaskEnvironment task_environment_;

  // Mojo data handles obtained from CreateTCPConnectedSocket.
  mojo::ScopedDataPipeConsumerHandle pre_tls_recv_handle_;
  mojo::ScopedDataPipeProducerHandle pre_tls_send_handle_;

  // Mojo data handles obtained from UpgradeToTLS.
  mojo::ScopedDataPipeConsumerHandle post_tls_recv_handle_;
  mojo::ScopedDataPipeProducerHandle post_tls_send_handle_;

  // SSLInfo obtained from UpgradeToTLS.
  std::optional<net::SSLInfo> ssl_info_;

  net::MockClientSocketFactory mock_client_socket_factory_;
  std::unique_ptr<net::URLRequestContext> url_request_context_;
  std::unique_ptr<SocketFactory> factory_;
  std::unique_ptr<ProxyResolvingSocketFactoryMojo> proxy_resolving_factory_;
  TestSocketObserver pre_tls_observer_;
  TestSocketObserver post_tls_observer_;
};

class TLSClientSocketTest
    : public ::testing::TestWithParam<TLSClientSocketTestBase::Mode>,
      public TLSClientSocketTestBase {
 public:
  TLSClientSocketTest() : TLSClientSocketTestBase(GetParam()) {
    Init(true /* use_mock_sockets */, false /* configure_proxy */);
  }

  TLSClientSocketTest(const TLSClientSocketTest&) = delete;
  TLSClientSocketTest& operator=(const TLSClientSocketTest&) = delete;

  ~TLSClientSocketTest() override {}
};

// Basic test to call UpgradeToTLS, and then read/write after UpgradeToTLS is
// successful.
TEST_P(TLSClientSocketTest, UpgradeToTLS) {
  const net::MockRead kReads[] = {net::MockRead(net::ASYNC, 1, kMsg),
                                  net::MockRead(net::SYNCHRONOUS, net::OK, 2)};
  const net::MockWrite kWrites[] = {net::MockWrite(net::SYNCHRONOUS, 0, kMsg)};
  net::SequencedSocketData data_provider(kReads, kWrites);
  data_provider.set_connect_data(net::MockConnect(net::SYNCHRONOUS, net::OK));
  mock_client_socket_factory()->AddSocketDataProvider(&data_provider);
  net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
  mock_client_socket_factory()->AddSSLSocketDataProvider(&ssl_socket);

  SocketHandle client_socket;
  net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
  EXPECT_EQ(net::OK,
            CreateSocketSync(MakeRequest(&client_socket), server_addr));

  net::HostPortPair host_port_pair("example.org", 443);
  pre_tls_recv_handle()->reset();
  pre_tls_send_handle()->reset();
  net::TestCompletionCallback callback;
  mojo::Remote<mojom::TLSClientSocket> tls_socket;
  UpgradeToTLS(&client_socket, host_port_pair,
               tls_socket.BindNewPipeAndPassReceiver(), callback.callback());
  ASSERT_EQ(net::OK, callback.WaitForResult());
  ResetSocket(&client_socket);

  size_t actually_written_bytes = 0;
  EXPECT_EQ(MOJO_RESULT_OK,
            post_tls_send_handle()->get().WriteData(base::as_byte_span(kMsg),
                                                    MOJO_WRITE_DATA_FLAG_NONE,
                                                    actually_written_bytes));
  EXPECT_EQ(kMsg, Read(post_tls_recv_handle(), kMsg.size()));
  base::RunLoop().RunUntilIdle();
  EXPECT_TRUE(ssl_socket.ConnectDataConsumed());
  EXPECT_TRUE(data_provider.AllReadDataConsumed());
  EXPECT_TRUE(data_provider.AllWriteDataConsumed());
}

// Same as the UpgradeToTLS test above, except this test calls
// base::RunLoop().RunUntilIdle() after destroying the pre-tls data pipes.
TEST_P(TLSClientSocketTest, ClosePipesRunUntilIdleAndUpgradeToTLS) {
  const net::MockRead kReads[] = {net::MockRead(net::ASYNC, 1, kMsg),
                                  net::MockRead(net::SYNCHRONOUS, net::OK, 2)};
  const net::MockWrite kWrites[] = {net::MockWrite(net::SYNCHRONOUS, 0, kMsg)};
  net::SequencedSocketData data_provider(kReads, kWrites);
  data_provider.set_connect_data(net::MockConnect(net::SYNCHRONOUS, net::OK));
  mock_client_socket_factory()->AddSocketDataProvider(&data_provider);
  net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
  mock_client_socket_factory()->AddSSLSocketDataProvider(&ssl_socket);

  SocketHandle client_socket;
  net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
  EXPECT_EQ(net::OK,
            CreateSocketSync(MakeRequest(&client_socket), server_addr));

  net::HostPortPair host_port_pair("example.org", 443);

  // Call RunUntilIdle() to test the case that pipes are closed before
  // UpgradeToTLS.
  pre_tls_recv_handle()->reset();
  pre_tls_send_handle()->reset();
  base::RunLoop().RunUntilIdle();

  net::TestCompletionCallback callback;
  mojo::Remote<mojom::TLSClientSocket> tls_socket;
  UpgradeToTLS(&client_socket, host_port_pair,
               tls_socket.BindNewPipeAndPassReceiver(), callback.callback());
  ASSERT_EQ(net::OK, callback.WaitForResult());
  ResetSocket(&client_socket);

  size_t actually_written_bytes = 0;
  EXPECT_EQ(MOJO_RESULT_OK,
            post_tls_send_handle()->get().WriteData(base::as_byte_span(kMsg),
                                                    MOJO_WRITE_DATA_FLAG_NONE,
                                                    actually_written_bytes));
  EXPECT_EQ(kMsg, Read(post_tls_recv_handle(), kMsg.size()));
  base::RunLoop().RunUntilIdle();
  EXPECT_TRUE(ssl_socket.ConnectDataConsumed());
  EXPECT_TRUE(data_provider.AllReadDataConsumed());
  EXPECT_TRUE(data_provider.AllWriteDataConsumed());
}

// Calling UpgradeToTLS on the same mojo::Remote<TCPConnectedSocket> is illegal
// and should receive an error.
TEST_P(TLSClientSocketTest, UpgradeToTLSTwice) {
  const net::MockRead kReads[] = {net::MockRead(net::ASYNC, net::OK, 0)};
  net::SequencedSocketData data_provider(kReads, base::span<net::MockWrite>());
  data_provider.set_connect_data(net::MockConnect(net::SYNCHRONOUS, net::OK));
  mock_client_socket_factory()->AddSocketDataProvider(&data_provider);
  net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
  mock_client_socket_factory()->AddSSLSocketDataProvider(&ssl_socket);

  SocketHandle client_socket;
  net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
  EXPECT_EQ(net::OK,
            CreateSocketSync(MakeRequest(&client_socket), server_addr));

  net::HostPortPair host_port_pair("example.org", 443);
  pre_tls_recv_handle()->reset();
  pre_tls_send_handle()->reset();

  // First UpgradeToTLS should complete successfully.
  net::TestCompletionCallback callback;
  mojo::Remote<mojom::TLSClientSocket> tls_socket;
  UpgradeToTLS(&client_socket, host_port_pair,
               tls_socket.BindNewPipeAndPassReceiver(), callback.callback());
  ASSERT_EQ(net::OK, callback.WaitForResult());

  // Second time UpgradeToTLS is called, it should fail.
  mojo::Remote<mojom::TLSClientSocket> tls_socket2;
  base::RunLoop run_loop;
  int net_error = net::ERR_FAILED;
  if (mode() == kDirect) {
    auto upgrade2_callback = base::BindLambdaForTesting(
        [&](int result, mojo::ScopedDataPipeConsumerHandle receive_pipe_handle,
            mojo::ScopedDataPipeProducerHandle send_pipe_handle,
            const std::optional<net::SSLInfo>& ssl_info) {
          net_error = result;
          run_loop.Quit();
        });
    client_socket.tcp_socket->UpgradeToTLS(
        host_port_pair, nullptr /* ssl_config_ptr */,
        net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
        tls_socket2.BindNewPipeAndPassReceiver(),
        mojo::NullRemote() /*observer */, std::move(upgrade2_callback));
  } else {
    auto upgrade2_callback = base::BindLambdaForTesting(
        [&](int result, mojo::ScopedDataPipeConsumerHandle receive_pipe_handle,
            mojo::ScopedDataPipeProducerHandle send_pipe_handle) {
          net_error = result;
          run_loop.Quit();
        });
    client_socket.proxy_socket->UpgradeToTLS(
        host_port_pair,
        net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
        tls_socket2.BindNewPipeAndPassReceiver(),
        mojo::NullRemote() /*observer */, std::move(upgrade2_callback));
  }
  run_loop.Run();
  ASSERT_EQ(net::ERR_SOCKET_NOT_CONNECTED, net_error);

  base::RunLoop().RunUntilIdle();
  EXPECT_TRUE(ssl_socket.ConnectDataConsumed());
  EXPECT_TRUE(data_provider.AllReadDataConsumed());
  EXPECT_TRUE(data_provider.AllWriteDataConsumed());
}

TEST_P(TLSClientSocketTest, UpgradeToTLSWithCustomSSLConfig) {
  // No custom options in the proxy-resolving case.
  if (mode() != kDirect)
    return;
  const net::MockRead kReads[] = {net::MockRead(net::ASYNC, net::OK, 0)};
  net::SequencedSocketData data_provider(kReads, base::span<net::MockWrite>());
  data_provider.set_connect_data(net::MockConnect(net::SYNCHRONOUS, net::OK));
  mock_client_socket_factory()->AddSocketDataProvider(&data_provider);
  net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
  ssl_socket.expected_ssl_version_min = net::SSL_PROTOCOL_VERSION_TLS1_2;
  ssl_socket.expected_ssl_version_max = net::SSL_PROTOCOL_VERSION_TLS1_3;
  mock_client_socket_factory()->AddSSLSocketDataProvider(&ssl_socket);

  SocketHandle client_socket;
  net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
  EXPECT_EQ(net::OK,
            CreateSocketSync(MakeRequest(&client_socket), server_addr));

  net::HostPortPair host_port_pair("example.org", 443);
  pre_tls_recv_handle()->reset();
  pre_tls_send_handle()->reset();

  mojo::Remote<mojom::TLSClientSocket> tls_socket;
  base::RunLoop run_loop;
  mojom::TLSClientSocketOptionsPtr options =
      mojom::TLSClientSocketOptions::New();
  options->version_min = mojom::SSLVersion::kTLS12;
  options->version_max = mojom::SSLVersion::kTLS13;
  int net_error = net::ERR_FAILED;
  auto upgrade_callback = base::BindLambdaForTesting(
      [&](int result, mojo::ScopedDataPipeConsumerHandle receive_pipe_handle,
          mojo::ScopedDataPipeProducerHandle send_pipe_handle,
          const std::optional<net::SSLInfo>& ssl_info) {
        net_error = result;
        run_loop.Quit();
      });
  client_socket.tcp_socket->UpgradeToTLS(
      host_port_pair, std::move(options),
      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
      tls_socket.BindNewPipeAndPassReceiver(), mojo::NullRemote() /*observer */,
      std::move(upgrade_callback));
  run_loop.Run();
  ASSERT_EQ(net::OK, net_error);

  base::RunLoop().RunUntilIdle();
  EXPECT_TRUE(ssl_socket.ConnectDataConsumed());
  EXPECT_TRUE(data_provider.AllReadDataConsumed());
  EXPECT_TRUE(data_provider.AllWriteDataConsumed());
}

// Same as the UpgradeToTLS test, except this also reads and writes to the tcp
// connection before UpgradeToTLS is called.
TEST_P(TLSClientSocketTest, ReadWriteBeforeUpgradeToTLS) {
  const net::MockRead kReads[] = {net::MockRead(net::SYNCHRONOUS, 0, kMsg),
                                  net::MockRead(net::ASYNC, 3, kSecretMsg),
                                  net::MockRead(net::SYNCHRONOUS, net::OK, 4)};
  const net::MockWrite kWrites[] = {
      net::MockWrite(net::SYNCHRONOUS, 1, kMsg),
      net::MockWrite(net::SYNCHRONOUS, 2, kSecretMsg),
  };
  net::SequencedSocketData data_provider(kReads, kWrites);
  data_provider.set_connect_data(net::MockConnect(net::SYNCHRONOUS, net::OK));
  mock_client_socket_factory()->AddSocketDataProvider(&data_provider);
  net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
  mock_client_socket_factory()->AddSSLSocketDataProvider(&ssl_socket);

  SocketHandle client_socket;
  net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
  EXPECT_EQ(net::OK,
            CreateSocketSync(MakeRequest(&client_socket), server_addr));

  EXPECT_EQ(kMsg, Read(pre_tls_recv_handle(), kMsg.size()));

  size_t actually_written_bytes = 0;
  EXPECT_EQ(MOJO_RESULT_OK,
            pre_tls_send_handle()->get().WriteData(base::as_byte_span(kMsg),
                                                   MOJO_WRITE_DATA_FLAG_NONE,
                                                   actually_written_bytes));

  net::HostPortPair host_port_pair("example.org", 443);
  pre_tls_recv_handle()->reset();
  pre_tls_send_handle()->reset();
  net::TestCompletionCallback callback;
  mojo::Remote<mojom::TLSClientSocket> tls_socket;
  UpgradeToTLS(&client_socket, host_port_pair,
               tls_socket.BindNewPipeAndPassReceiver(), callback.callback());
  ASSERT_EQ(net::OK, callback.WaitForResult());
  ResetSocket(&client_socket);

  EXPECT_EQ(MOJO_RESULT_OK,
            post_tls_send_handle()->get().WriteData(
                base::as_byte_span(kSecretMsg), MOJO_WRITE_DATA_FLAG_NONE,
                actually_written_bytes));
  EXPECT_EQ(kSecretMsg, Read(post_tls_recv_handle(), kSecretMsg.size()));
  base::RunLoop().RunUntilIdle();
  EXPECT_TRUE(ssl_socket.ConnectDataConsumed());
  EXPECT_TRUE(data_provider.AllReadDataConsumed());
  EXPECT_TRUE(data_provider.AllWriteDataConsumed());
}

// Tests that a read error is encountered after UpgradeToTLS completes
// successfully.
TEST_P(TLSClientSocketTest, ReadErrorAfterUpgradeToTLS) {
  const net::MockRead kReads[] = {
      net::MockRead(net::ASYNC, 1, kSecretMsg),
      net::MockRead(net::SYNCHRONOUS, net::ERR_CONNECTION_CLOSED, 2)};
  const net::MockWrite kWrites[] = {
      net::MockWrite(net::SYNCHRONOUS, 0, kSecretMsg)};
  net::SequencedSocketData data_provider(kReads, kWrites);
  data_provider.set_connect_data(net::MockConnect(net::SYNCHRONOUS, net::OK));
  mock_client_socket_factory()->AddSocketDataProvider(&data_provider);
  net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
  mock_client_socket_factory()->AddSSLSocketDataProvider(&ssl_socket);

  SocketHandle client_socket;
  net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
  EXPECT_EQ(net::OK,
            CreateSocketSync(MakeRequest(&client_socket), server_addr));

  net::HostPortPair host_port_pair("example.org", 443);
  pre_tls_recv_handle()->reset();
  pre_tls_send_handle()->reset();
  net::TestCompletionCallback callback;
  mojo::Remote<mojom::TLSClientSocket> tls_socket;
  UpgradeToTLS(&client_socket, host_port_pair,
               tls_socket.BindNewPipeAndPassReceiver(), callback.callback());
  ASSERT_EQ(net::OK, callback.WaitForResult());
  ResetSocket(&client_socket);

  size_t actually_written_bytes = 0;
  EXPECT_EQ(MOJO_RESULT_OK,
            post_tls_send_handle()->get().WriteData(
                base::as_byte_span(kSecretMsg), MOJO_WRITE_DATA_FLAG_NONE,
                actually_written_bytes));
  EXPECT_EQ(kSecretMsg, Read(post_tls_recv_handle(), kSecretMsg.size()));
  EXPECT_EQ(net::ERR_CONNECTION_CLOSED,
            post_tls_observer()->WaitForReadError());

  base::RunLoop().RunUntilIdle();
  EXPECT_TRUE(ssl_socket.ConnectDataConsumed());
  EXPECT_TRUE(data_provider.AllReadDataConsumed());
  EXPECT_TRUE(data_provider.AllWriteDataConsumed());
}

// Tests that a read error is encountered after UpgradeToTLS completes
// successfully.
TEST_P(TLSClientSocketTest, WriteErrorAfterUpgradeToTLS) {
  const net::MockRead kReads[] = {net::MockRead(net::ASYNC, net::OK, 0)};
  const net::MockWrite kWrites[] = {
      net::MockWrite(net::SYNCHRONOUS, net::ERR_CONNECTION_CLOSED, 1)};
  net::SequencedSocketData data_provider(kReads, kWrites);
  data_provider.set_connect_data(net::MockConnect(net::SYNCHRONOUS, net::OK));
  mock_client_socket_factory()->AddSocketDataProvider(&data_provider);
  net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
  mock_client_socket_factory()->AddSSLSocketDataProvider(&ssl_socket);

  SocketHandle client_socket;
  net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
  EXPECT_EQ(net::OK,
            CreateSocketSync(MakeRequest(&client_socket), server_addr));

  net::HostPortPair host_port_pair("example.org", 443);
  pre_tls_recv_handle()->reset();
  pre_tls_send_handle()->reset();
  net::TestCompletionCallback callback;
  mojo::Remote<mojom::TLSClientSocket> tls_socket;
  UpgradeToTLS(&client_socket, host_port_pair,
               tls_socket.BindNewPipeAndPassReceiver(), callback.callback());
  ASSERT_EQ(net::OK, callback.WaitForResult());
  ResetSocket(&client_socket);

  size_t actually_written_bytes = 0;
  EXPECT_EQ(MOJO_RESULT_OK,
            post_tls_send_handle()->get().WriteData(
                base::as_byte_span(kSecretMsg), MOJO_WRITE_DATA_FLAG_NONE,
                actually_written_bytes));
  EXPECT_EQ(net::ERR_CONNECTION_CLOSED,
            post_tls_observer()->WaitForWriteError());

  base::RunLoop().RunUntilIdle();
  EXPECT_TRUE(ssl_socket.ConnectDataConsumed());
  EXPECT_TRUE(data_provider.AllReadDataConsumed());
  EXPECT_TRUE(data_provider.AllWriteDataConsumed());
}

// Tests that reading from the pre-tls data pipe is okay even after UpgradeToTLS
// is called.
TEST_P(TLSClientSocketTest, ReadFromPreTlsDataPipeAfterUpgradeToTLS) {
  const net::MockRead kReads[] = {net::MockRead(net::ASYNC, 0, kMsg),
                                  net::MockRead(net::ASYNC, 2, kSecretMsg),
                                  net::MockRead(net::SYNCHRONOUS, net::OK, 3)};
  const net::MockWrite kWrites[] = {
      net::MockWrite(net::SYNCHRONOUS, 1, kSecretMsg)};
  net::SequencedSocketData data_provider(kReads, kWrites);
  data_provider.set_connect_data(net::MockConnect(net::SYNCHRONOUS, net::OK));
  mock_client_socket_factory()->AddSocketDataProvider(&data_provider);
  net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
  mock_client_socket_factory()->AddSSLSocketDataProvider(&ssl_socket);

  SocketHandle client_socket;
  net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
  EXPECT_EQ(net::OK,
            CreateSocketSync(MakeRequest(&client_socket), server_addr));

  net::HostPortPair host_port_pair("example.org", 443);
  pre_tls_send_handle()->reset();
  net::TestCompletionCallback callback;
  mojo::Remote<mojom::TLSClientSocket> tls_socket;
  UpgradeToTLS(&client_socket, host_port_pair,
               tls_socket.BindNewPipeAndPassReceiver(), callback.callback());
  base::RunLoop().RunUntilIdle();

  EXPECT_EQ(kMsg, Read(pre_tls_recv_handle(), kMsg.size()));

  // Reset pre-tls receive pipe now and UpgradeToTLS should complete.
  pre_tls_recv_handle()->reset();
  ASSERT_EQ(net::OK, callback.WaitForResult());
  ResetSocket(&client_socket);

  size_t actually_written_bytes = 0;
  EXPECT_EQ(MOJO_RESULT_OK,
            post_tls_send_handle()->get().WriteData(
                base::as_byte_span(kSecretMsg), MOJO_WRITE_DATA_FLAG_NONE,
                actually_written_bytes));
  EXPECT_EQ(kSecretMsg, Read(post_tls_recv_handle(), kSecretMsg.size()));
  base::RunLoop().RunUntilIdle();
  EXPECT_TRUE(ssl_socket.ConnectDataConsumed());
  EXPECT_TRUE(data_provider.AllReadDataConsumed());
  EXPECT_TRUE(data_provider.AllWriteDataConsumed());
}

// Tests that writing to the pre-tls data pipe is okay even after UpgradeToTLS
// is called.
TEST_P(TLSClientSocketTest, WriteToPreTlsDataPipeAfterUpgradeToTLS) {
  const net::MockRead kReads[] = {net::MockRead(net::ASYNC, 2, kSecretMsg),
                                  net::MockRead(net::SYNCHRONOUS, net::OK, 3)};
  const net::MockWrite kWrites[] = {
      net::MockWrite(net::SYNCHRONOUS, 0, kMsg),
      net::MockWrite(net::SYNCHRONOUS, 1, kSecretMsg)};
  net::SequencedSocketData data_provider(kReads, kWrites);
  data_provider.set_connect_data(net::MockConnect(net::SYNCHRONOUS, net::OK));
  mock_client_socket_factory()->AddSocketDataProvider(&data_provider);
  net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
  mock_client_socket_factory()->AddSSLSocketDataProvider(&ssl_socket);

  SocketHandle client_socket;
  net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
  EXPECT_EQ(net::OK,
            CreateSocketSync(MakeRequest(&client_socket), server_addr));

  net::HostPortPair host_port_pair("example.org", 443);
  pre_tls_recv_handle()->reset();
  net::TestCompletionCallback callback;
  mojo::Remote<mojom::TLSClientSocket> tls_socket;
  UpgradeToTLS(&client_socket, host_port_pair,
               tls_socket.BindNewPipeAndPassReceiver(), callback.callback());
  base::RunLoop().RunUntilIdle();

  size_t actually_written_bytes = 0;
  EXPECT_EQ(MOJO_RESULT_OK,
            pre_tls_send_handle()->get().WriteData(base::as_byte_span(kMsg),
                                                   MOJO_WRITE_DATA_FLAG_NONE,
                                                   actually_written_bytes));

  // Reset pre-tls send pipe now and UpgradeToTLS should complete.
  pre_tls_send_handle()->reset();
  ASSERT_EQ(net::OK, callback.WaitForResult());
  ResetSocket(&client_socket);

  EXPECT_EQ(MOJO_RESULT_OK,
            post_tls_send_handle()->get().WriteData(
                base::as_byte_span(kSecretMsg), MOJO_WRITE_DATA_FLAG_NONE,
                actually_written_bytes));
  EXPECT_EQ(kSecretMsg, Read(post_tls_recv_handle(), kSecretMsg.size()));
  base::RunLoop().RunUntilIdle();
  EXPECT_TRUE(ssl_socket.ConnectDataConsumed());
  EXPECT_TRUE(data_provider.AllReadDataConsumed());
  EXPECT_TRUE(data_provider.AllWriteDataConsumed());
}

// Tests that reading from and writing to pre-tls data pipe is okay even after
// UpgradeToTLS is called.
TEST_P(TLSClientSocketTest, ReadAndWritePreTlsDataPipeAfterUpgradeToTLS) {
  const net::MockRead kReads[] = {net::MockRead(net::ASYNC, 0, kMsg),
                                  net::MockRead(net::ASYNC, 3, kSecretMsg),
                                  net::MockRead(net::SYNCHRONOUS, net::OK, 4)};
  const net::MockWrite kWrites[] = {
      net::MockWrite(net::SYNCHRONOUS, 1, kMsg),
      net::MockWrite(net::SYNCHRONOUS, 2, kSecretMsg)};
  net::SequencedSocketData data_provider(kReads, kWrites);
  data_provider.set_connect_data(net::MockConnect(net::SYNCHRONOUS, net::OK));
  mock_client_socket_factory()->AddSocketDataProvider(&data_provider);
  net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
  mock_client_socket_factory()->AddSSLSocketDataProvider(&ssl_socket);

  SocketHandle client_socket;
  net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
  EXPECT_EQ(net::OK,
            CreateSocketSync(MakeRequest(&client_socket), server_addr));

  net::HostPortPair host_port_pair("example.org", 443);
  base::RunLoop run_loop;
  net::TestCompletionCallback callback;
  mojo::Remote<mojom::TLSClientSocket> tls_socket;
  UpgradeToTLS(&client_socket, host_port_pair,
               tls_socket.BindNewPipeAndPassReceiver(), callback.callback());
  EXPECT_EQ(kMsg, Read(pre_tls_recv_handle(), kMsg.size()));
  size_t actually_written_bytes = 0;
  EXPECT_EQ(MOJO_RESULT_OK,
            pre_tls_send_handle()->get().WriteData(base::as_byte_span(kMsg),
                                                   MOJO_WRITE_DATA_FLAG_NONE,
                                                   actually_written_bytes));

  // Reset pre-tls pipes now and UpgradeToTLS should complete.
  pre_tls_recv_handle()->reset();
  pre_tls_send_handle()->reset();
  ASSERT_EQ(net::OK, callback.WaitForResult());
  ResetSocket(&client_socket);

  EXPECT_EQ(MOJO_RESULT_OK,
            post_tls_send_handle()->get().WriteData(
                base::as_byte_span(kSecretMsg), MOJO_WRITE_DATA_FLAG_NONE,
                actually_written_bytes));
  EXPECT_EQ(kSecretMsg, Read(post_tls_recv_handle(), kSecretMsg.size()));
  base::RunLoop().RunUntilIdle();
  EXPECT_TRUE(ssl_socket.ConnectDataConsumed());
  EXPECT_TRUE(data_provider.AllReadDataConsumed());
  EXPECT_TRUE(data_provider.AllWriteDataConsumed());
}

// Tests that a read error is encountered before UpgradeToTLS completes.
TEST_P(TLSClientSocketTest, ReadErrorBeforeUpgradeToTLS) {
  // This requires pre_tls_observer(), which is not provided by proxy resolving
  // sockets.
  if (mode() != kDirect)
    return;
  const net::MockRead kReads[] = {
      net::MockRead(net::ASYNC, 0, kMsg),
      net::MockRead(net::SYNCHRONOUS, net::ERR_CONNECTION_CLOSED, 1)};
  net::SequencedSocketData data_provider(kReads, base::span<net::MockWrite>());
  data_provider.set_connect_data(net::MockConnect(net::SYNCHRONOUS, net::OK));
  mock_client_socket_factory()->AddSocketDataProvider(&data_provider);

  SocketHandle client_socket;
  net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
  EXPECT_EQ(net::OK,
            CreateSocketSync(MakeRequest(&client_socket), server_addr));

  net::HostPortPair host_port_pair("example.org", 443);
  pre_tls_send_handle()->reset();
  net::TestCompletionCallback callback;
  mojo::Remote<mojom::TLSClientSocket> tls_socket;
  UpgradeToTLS(&client_socket, host_port_pair,
               tls_socket.BindNewPipeAndPassReceiver(), callback.callback());

  EXPECT_EQ(kMsg, Read(pre_tls_recv_handle(), kMsg.size()));
  EXPECT_EQ(net::ERR_CONNECTION_CLOSED, pre_tls_observer()->WaitForReadError());

  // Reset pre-tls receive pipe now and UpgradeToTLS should complete.
  pre_tls_recv_handle()->reset();
  ASSERT_EQ(net::ERR_SOCKET_NOT_CONNECTED, callback.WaitForResult());
  ResetSocket(&client_socket);

  base::RunLoop().RunUntilIdle();
  EXPECT_TRUE(data_provider.AllReadDataConsumed());
  EXPECT_TRUE(data_provider.AllWriteDataConsumed());
}

// Tests that a write error is encountered before UpgradeToTLS completes.
TEST_P(TLSClientSocketTest, WriteErrorBeforeUpgradeToTLS) {
  // This requires pre_tls_observer(), which is not provided by proxy resolving
  // sockets.
  if (mode() != kDirect)
    return;

  const net::MockRead kReads[] = {net::MockRead(net::ASYNC, net::OK, 1)};
  const net::MockWrite kWrites[] = {
      net::MockWrite(net::SYNCHRONOUS, net::ERR_CONNECTION_CLOSED, 0)};
  net::SequencedSocketData data_provider(kReads, kWrites);
  data_provider.set_connect_data(net::MockConnect(net::SYNCHRONOUS, net::OK));
  mock_client_socket_factory()->AddSocketDataProvider(&data_provider);

  SocketHandle client_socket;
  net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
  EXPECT_EQ(net::OK,
            CreateSocketSync(MakeRequest(&client_socket), server_addr));

  net::HostPortPair host_port_pair("example.org", 443);
  pre_tls_recv_handle()->reset();
  net::TestCompletionCallback callback;
  mojo::Remote<mojom::TLSClientSocket> tls_socket;
  UpgradeToTLS(&client_socket, host_port_pair,
               tls_socket.BindNewPipeAndPassReceiver(), callback.callback());
  size_t actually_written_bytes = 0;
  EXPECT_EQ(MOJO_RESULT_OK,
            pre_tls_send_handle()->get().WriteData(base::as_byte_span(kMsg),
                                                   MOJO_WRITE_DATA_FLAG_NONE,
                                                   actually_written_bytes));

  EXPECT_EQ(net::ERR_CONNECTION_CLOSED,
            pre_tls_observer()->WaitForWriteError());
  // Reset pre-tls send pipe now and UpgradeToTLS should complete.
  pre_tls_send_handle()->reset();
  ASSERT_EQ(net::ERR_SOCKET_NOT_CONNECTED, callback.WaitForResult());
  ResetSocket(&client_socket);

  base::RunLoop().RunUntilIdle();
  // Write failed before the mock read can be consumed.
  EXPECT_FALSE(data_provider.AllReadDataConsumed());
  EXPECT_TRUE(data_provider.AllWriteDataConsumed());
}

INSTANTIATE_TEST_SUITE_P(
    All,
    TLSClientSocketTest,
    ::testing::Values(TLSClientSocketTestBase::kDirect,
                      TLSClientSocketTestBase::kProxyResolving));

// Tests with proxy resolving socket and a proxy actually configured.
class TLSCLientSocketProxyTest : public ::testing::Test,
                                 public TLSClientSocketTestBase {
 public:
  TLSCLientSocketProxyTest()
      : TLSClientSocketTestBase(TLSClientSocketTestBase::kProxyResolving) {
    Init(true /* use_mock_sockets*/, true /* configure_proxy */);
  }

  TLSCLientSocketProxyTest(const TLSCLientSocketProxyTest&) = delete;
  TLSCLientSocketProxyTest& operator=(const TLSCLientSocketProxyTest&) = delete;

  ~TLSCLientSocketProxyTest() override {}
};

TEST_F(TLSCLientSocketProxyTest, UpgradeToTLS) {
  static constexpr std::string_view kConnectRequest =
      "CONNECT 192.168.1.1:1234 HTTP/1.1\r\n"
      "Host: 192.168.1.1:1234\r\n"
      "Proxy-Connection: keep-alive\r\n\r\n";
  static constexpr std::string_view kConnectResponse =
      "HTTP/1.1 200 OK\r\n\r\n";

  const net::MockRead kReads[] = {
      net::MockRead(net::ASYNC, 1, kConnectResponse),
      net::MockRead(net::ASYNC, 3, kMsg),
      net::MockRead(net::SYNCHRONOUS, net::OK, 4)};
  const net::MockWrite kWrites[] = {
      net::MockWrite(net::ASYNC, 0, kConnectRequest),
      net::MockWrite(net::SYNCHRONOUS, 2, kMsg)};
  net::SequencedSocketData data_provider(kReads, kWrites);
  data_provider.set_connect_data(net::MockConnect(net::SYNCHRONOUS, net::OK));
  mock_client_socket_factory()->AddSocketDataProvider(&data_provider);
  net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
  mock_client_socket_factory()->AddSSLSocketDataProvider(&ssl_socket);

  SocketHandle client_socket;
  net::IPEndPoint server_addr(net::IPAddress(192, 168, 1, 1), 1234);
  EXPECT_EQ(net::OK,
            CreateSocketSync(MakeRequest(&client_socket), server_addr));

  net::HostPortPair host_port_pair("example.org", 443);
  pre_tls_recv_handle()->reset();
  pre_tls_send_handle()->reset();
  net::TestCompletionCallback callback;
  mojo::Remote<mojom::TLSClientSocket> tls_socket;
  UpgradeToTLS(&client_socket, host_port_pair,
               tls_socket.BindNewPipeAndPassReceiver(), callback.callback());
  ASSERT_EQ(net::OK, callback.WaitForResult());
  ResetSocket(&client_socket);

  size_t actually_written_bytes = 0;
  EXPECT_EQ(MOJO_RESULT_OK,
            post_tls_send_handle()->get().WriteData(base::as_byte_span(kMsg),
                                                    MOJO_WRITE_DATA_FLAG_NONE,
                                                    actually_written_bytes));
  EXPECT_EQ(kMsg, Read(post_tls_recv_handle(), kMsg.size()));
  base::RunLoop().RunUntilIdle();
  EXPECT_TRUE(ssl_socket.ConnectDataConsumed());
  EXPECT_TRUE(data_provider.AllReadDataConsumed());
  EXPECT_TRUE(data_provider.AllWriteDataConsumed());
}

class TLSClientSocketIoModeTest : public TLSClientSocketTestBase,
                                  public testing::TestWithParam<net::IoMode> {
 public:
  TLSClientSocketIoModeTest()
      : TLSClientSocketTestBase(TLSClientSocketTestBase::kDirect) {
    Init(true /* use_mock_sockets*/, false /* configure_proxy */);
  }

  TLSClientSocketIoModeTest(const TLSClientSocketIoModeTest&) = delete;
  TLSClientSocketIoModeTest& operator=(const TLSClientSocketIoModeTest&) =
      delete;

  ~TLSClientSocketIoModeTest() override {}
};

INSTANTIATE_TEST_SUITE_P(All,
                         TLSClientSocketIoModeTest,
                         testing::Values(net::SYNCHRONOUS, net::ASYNC));

TEST_P(TLSClientSocketIoModeTest, MultipleWriteToTLSSocket) {
  const int kNumIterations = 3;
  std::vector<net::MockRead> reads;
  std::vector<net::MockWrite> writes;
  int sequence_number = 0;
  net::IoMode mode = GetParam();
  for (int j = 0; j < kNumIterations; ++j) {
    for (size_t i = 0; i < kSecretMsg.size(); ++i) {
      writes.emplace_back(mode, sequence_number++,
                          base::byte_span_from_ref(kSecretMsg[i]));
    }
    for (size_t i = 0; i < kSecretMsg.size(); ++i) {
      reads.emplace_back(net::ASYNC, sequence_number++,
                         base::byte_span_from_ref(kSecretMsg[i]));
    }
    if (j == kNumIterations - 1) {
      reads.emplace_back(mode, net::OK, sequence_number++);
    }
  }
  net::SequencedSocketData data_provider(reads, writes);
  data_provider.set_connect_data(net::MockConnect(GetParam(), net::OK));
  mock_client_socket_factory()->AddSocketDataProvider(&data_provider);
  net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
  mock_client_socket_factory()->AddSSLSocketDataProvider(&ssl_socket);

  mojo::Remote<mojom::TCPConnectedSocket> client_socket;
  net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
  EXPECT_EQ(net::OK,
            CreateTCPConnectedSocketSync(
                client_socket.BindNewPipeAndPassReceiver(), server_addr));

  net::HostPortPair host_port_pair("example.org", 443);
  pre_tls_recv_handle()->reset();
  pre_tls_send_handle()->reset();
  net::TestCompletionCallback callback;
  mojo::Remote<mojom::TLSClientSocket> tls_socket;
  UpgradeTCPConnectedSocketToTLS(
      client_socket.get(), host_port_pair, nullptr /* options */,
      tls_socket.BindNewPipeAndPassReceiver(), callback.callback());
  ASSERT_EQ(net::OK, callback.WaitForResult());
  client_socket.reset();
  EXPECT_FALSE(ssl_info());

  // Loop kNumIterations times to test that writes can follow reads, and reads
  // can follow writes.
  for (int j = 0; j < kNumIterations; ++j) {
    // Write multiple times.
    for (size_t i = 0; i < kSecretMsg.size(); ++i) {
      size_t actually_written_bytes = 0;
      EXPECT_EQ(MOJO_RESULT_OK,
                post_tls_send_handle()->get().WriteData(
                    base::as_byte_span(kSecretMsg).subspan(i, 1u),
                    MOJO_WRITE_DATA_FLAG_NONE, actually_written_bytes));
      // Flush the 1 byte write.
      base::RunLoop().RunUntilIdle();
    }
    // Reading kSecretMsgSize should coalesce the 1-byte mock reads.
    EXPECT_EQ(kSecretMsg, Read(post_tls_recv_handle(), kSecretMsg.size()));
  }
  EXPECT_TRUE(ssl_socket.ConnectDataConsumed());
  EXPECT_TRUE(data_provider.AllReadDataConsumed());
  EXPECT_TRUE(data_provider.AllWriteDataConsumed());
}

// Check SSLInfo is provided in both sync and async cases.
TEST_P(TLSClientSocketIoModeTest, SSLInfo) {
  // End of file. Reads don't matter, only the handshake does.
  std::vector<net::MockRead> reads = {net::MockRead(net::SYNCHRONOUS, net::OK)};
  std::vector<net::MockWrite> writes;
  net::SequencedSocketData data_provider(reads, writes);
  data_provider.set_connect_data(net::MockConnect(net::SYNCHRONOUS, net::OK));
  mock_client_socket_factory()->AddSocketDataProvider(&data_provider);
  net::SSLSocketDataProvider ssl_socket(GetParam(), net::OK);
  // Set a value on SSLInfo to make sure it's correctly received.
  ssl_socket.ssl_info.is_issued_by_known_root = true;
  mock_client_socket_factory()->AddSSLSocketDataProvider(&ssl_socket);

  mojo::Remote<mojom::TCPConnectedSocket> client_socket;
  net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
  EXPECT_EQ(net::OK,
            CreateTCPConnectedSocketSync(
                client_socket.BindNewPipeAndPassReceiver(), server_addr));

  net::HostPortPair host_port_pair("example.org", 443);
  pre_tls_recv_handle()->reset();
  pre_tls_send_handle()->reset();
  net::TestCompletionCallback callback;
  mojo::Remote<mojom::TLSClientSocket> tls_socket;
  mojom::TLSClientSocketOptionsPtr options =
      mojom::TLSClientSocketOptions::New();
  options->send_ssl_info = true;
  UpgradeTCPConnectedSocketToTLS(
      client_socket.get(), host_port_pair, std::move(options),
      tls_socket.BindNewPipeAndPassReceiver(), callback.callback());
  ASSERT_EQ(net::OK, callback.WaitForResult());
  ASSERT_TRUE(ssl_info());
  EXPECT_TRUE(ssl_socket.ssl_info.is_issued_by_known_root);
  EXPECT_FALSE(ssl_socket.ssl_info.is_fatal_cert_error);
}

class TLSClientSocketTestWithEmbeddedTestServerBase
    : public TLSClientSocketTestBase {
 public:
  explicit TLSClientSocketTestWithEmbeddedTestServerBase(Mode mode)
      : TLSClientSocketTestBase(mode),
        server_(net::EmbeddedTestServer::TYPE_HTTPS) {
    Init(false /* use_mock_sockets */, false /* configure_proxy */);
  }

  TLSClientSocketTestWithEmbeddedTestServerBase(
      const TLSClientSocketTestWithEmbeddedTestServerBase&) = delete;
  TLSClientSocketTestWithEmbeddedTestServerBase& operator=(
      const TLSClientSocketTestWithEmbeddedTestServerBase&) = delete;

  ~TLSClientSocketTestWithEmbeddedTestServerBase() override {}

  // Starts the test server using the specified certificate.
  [[nodiscard]] bool StartTestServer(
      net::EmbeddedTestServer::ServerCertificate certificate) {
    server_.RegisterRequestHandler(
        base::BindRepeating([](const net::test_server::HttpRequest& request) {
          if (base::StartsWith(request.relative_url, "/secret",
                               base::CompareCase::INSENSITIVE_ASCII)) {
            return std::unique_ptr<net::test_server::HttpResponse>(
                new net::test_server::RawHttpResponse("HTTP/1.1 200 OK",
                                                      "Hello There!"));
          }
          return std::unique_ptr<net::test_server::HttpResponse>();
        }));
    server_.SetSSLConfig(certificate);
    return server_.Start();
  }

  // Attempts to eastablish a TLS connection to the test server by first
  // establishing a TCP connection, and then upgrading it.  Returns the
  // resulting network error code.
  [[nodiscard]] int CreateTLSSocket() {
    SocketHandle client_socket;
    net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(),
                                server_.port());
    EXPECT_EQ(net::OK,
              CreateSocketSync(MakeRequest(&client_socket), server_addr));

    pre_tls_recv_handle()->reset();
    pre_tls_send_handle()->reset();
    net::TestCompletionCallback callback;
    UpgradeToTLS(&client_socket, server_.host_port_pair(),
                 tls_socket_.BindNewPipeAndPassReceiver(), callback.callback());
    int result = callback.WaitForResult();
    ResetSocket(&client_socket);
    return result;
  }

  [[nodiscard]] int CreateTLSSocketWithOptions(
      mojom::TLSClientSocketOptionsPtr options) {
    // Proxy connections don't support TLSClientSocketOptions.
    DCHECK_EQ(kDirect, mode());

    mojo::Remote<mojom::TCPConnectedSocket> tcp_socket;
    net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(),
                                server_.port());
    EXPECT_EQ(net::OK,
              CreateTCPConnectedSocketSync(
                  tcp_socket.BindNewPipeAndPassReceiver(), server_addr));

    pre_tls_recv_handle()->reset();
    pre_tls_send_handle()->reset();
    net::TestCompletionCallback callback;
    UpgradeTCPConnectedSocketToTLS(
        tcp_socket.get(), server_.host_port_pair(), std::move(options),
        tls_socket_.BindNewPipeAndPassReceiver(), callback.callback());
    int result = callback.WaitForResult();
    tcp_socket.reset();
    return result;
  }

  void TestTlsSocket() {
    ASSERT_TRUE(tls_socket_.is_bound());
    const char kTestMsg[] = "GET /secret HTTP/1.1\r\n\r\n";
    size_t actually_written_bytes = 0;
    const char kResponse[] = "HTTP/1.1 200 OK\n\n";
    EXPECT_EQ(MOJO_RESULT_OK,
              post_tls_send_handle()->get().WriteData(
                  base::byte_span_from_cstring(kTestMsg),
                  MOJO_WRITE_DATA_FLAG_NONE, actually_written_bytes));
    EXPECT_EQ(kResponse, Read(post_tls_recv_handle(), strlen(kResponse)));
  }

  net::EmbeddedTestServer* server() { return &server_; }

 private:
  net::EmbeddedTestServer server_;

  mojo::Remote<mojom::TLSClientSocket> tls_socket_;
};

class TLSClientSocketTestWithEmbeddedTestServer
    : public TLSClientSocketTestWithEmbeddedTestServerBase,
      public ::testing::TestWithParam<TLSClientSocketTestBase::Mode> {
 public:
  TLSClientSocketTestWithEmbeddedTestServer()
      : TLSClientSocketTestWithEmbeddedTestServerBase(GetParam()) {}

  TLSClientSocketTestWithEmbeddedTestServer(
      const TLSClientSocketTestWithEmbeddedTestServer&) = delete;
  TLSClientSocketTestWithEmbeddedTestServer& operator=(
      const TLSClientSocketTestWithEmbeddedTestServer&) = delete;

  ~TLSClientSocketTestWithEmbeddedTestServer() override {}
};

TEST_P(TLSClientSocketTestWithEmbeddedTestServer, Basic) {
  ASSERT_TRUE(StartTestServer(net::EmbeddedTestServer::CERT_OK));
  ASSERT_EQ(net::OK, CreateTLSSocket());
  // No SSLInfo should be received by default. SSLInfo is only supported in the
  // kDirect case, but it doesn't hurt to check it's null it in the
  // kProxyResolving case.
  EXPECT_FALSE(ssl_info());
  TestTlsSocket();
}

TEST_P(TLSClientSocketTestWithEmbeddedTestServer, ServerCertError) {
  ASSERT_TRUE(StartTestServer(net::EmbeddedTestServer::CERT_MISMATCHED_NAME));
  ASSERT_EQ(net::ERR_CERT_COMMON_NAME_INVALID, CreateTLSSocket());
  // No SSLInfo should be received by default. SSLInfo is only supported in the
  // kDirect case, but it doesn't hurt to check it's null in the kProxyResolving
  // case.
  EXPECT_FALSE(ssl_info());

  // Handles should be invalid.
  EXPECT_FALSE(post_tls_recv_handle()->is_valid());
  EXPECT_FALSE(post_tls_send_handle()->is_valid());
}

INSTANTIATE_TEST_SUITE_P(
    All,
    TLSClientSocketTestWithEmbeddedTestServer,
    ::testing::Values(TLSClientSocketTestBase::kDirect,
                      TLSClientSocketTestBase::kProxyResolving));

class TLSClientSocketDirectTestWithEmbeddedTestServer
    : public TLSClientSocketTestWithEmbeddedTestServerBase,
      public testing::Test {
 public:
  TLSClientSocketDirectTestWithEmbeddedTestServer()
      : TLSClientSocketTestWithEmbeddedTestServerBase(kDirect) {}

  TLSClientSocketDirectTestWithEmbeddedTestServer(
      const TLSClientSocketDirectTestWithEmbeddedTestServer&) = delete;
  TLSClientSocketDirectTestWithEmbeddedTestServer& operator=(
      const TLSClientSocketDirectTestWithEmbeddedTestServer&) = delete;

  ~TLSClientSocketDirectTestWithEmbeddedTestServer() override {}
};

TEST_F(TLSClientSocketDirectTestWithEmbeddedTestServer, SSLInfo) {
  ASSERT_TRUE(StartTestServer(net::EmbeddedTestServer::CERT_OK));
  mojom::TLSClientSocketOptionsPtr options =
      mojom::TLSClientSocketOptions::New();
  options->send_ssl_info = true;
  ASSERT_EQ(net::OK, CreateTLSSocketWithOptions(std::move(options)));

  ASSERT_TRUE(ssl_info());
  EXPECT_TRUE(ssl_info()->is_valid());
  EXPECT_FALSE(ssl_info()->is_fatal_cert_error);

  TestTlsSocket();
}

TEST_F(TLSClientSocketDirectTestWithEmbeddedTestServer,
       SSLInfoServerCertError) {
  ASSERT_TRUE(StartTestServer(net::EmbeddedTestServer::CERT_MISMATCHED_NAME));
  mojom::TLSClientSocketOptionsPtr options =
      mojom::TLSClientSocketOptions::New();
  options->send_ssl_info = true;
  // Requesting SSLInfo should not bypass cert verification.
  ASSERT_EQ(net::ERR_CERT_COMMON_NAME_INVALID,
            CreateTLSSocketWithOptions(std::move(options)));

  // No SSLInfo should be provided on error.
  EXPECT_FALSE(ssl_info());

  // Handles should be invalid.
  EXPECT_FALSE(post_tls_recv_handle()->is_valid());
  EXPECT_FALSE(post_tls_send_handle()->is_valid());
}

// Check skipping cert verification always received SSLInfo, even with valid
// certs.
TEST_F(TLSClientSocketDirectTestWithEmbeddedTestServer,
       UnsafelySkipCertVerification) {
  ASSERT_TRUE(StartTestServer(net::EmbeddedTestServer::CERT_OK));
  mojom::TLSClientSocketOptionsPtr options =
      mojom::TLSClientSocketOptions::New();
  options->unsafely_skip_cert_verification = true;
  ASSERT_EQ(net::OK, CreateTLSSocketWithOptions(std::move(options)));

  ASSERT_TRUE(ssl_info());
  EXPECT_TRUE(ssl_info()->is_valid());
  EXPECT_FALSE(ssl_info()->is_fatal_cert_error);

  TestTlsSocket();
}

TEST_F(TLSClientSocketDirectTestWithEmbeddedTestServer,
       UnsafelySkipCertVerificationServerCertError) {
  ASSERT_TRUE(StartTestServer(net::EmbeddedTestServer::CERT_MISMATCHED_NAME));
  mojom::TLSClientSocketOptionsPtr options =
      mojom::TLSClientSocketOptions::New();
  options->unsafely_skip_cert_verification = true;
  ASSERT_EQ(net::OK, CreateTLSSocketWithOptions(std::move(options)));

  ASSERT_TRUE(ssl_info());
  EXPECT_TRUE(ssl_info()->is_valid());
  EXPECT_FALSE(ssl_info()->is_fatal_cert_error);

  TestTlsSocket();
}

}  // namespace

}  // namespace network
