blob: fbc6b184715b23d65f39eb41841643b1e005399d [file] [log] [blame]
// Copyright 2018 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 <stdint.h>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/test/bind_test_util.h"
#include "base/test/scoped_task_environment.h"
#include "base/threading/thread.h"
#include "net/base/completion_once_callback.h"
#include "net/base/net_errors.h"
#include "net/base/test_completion_callback.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_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/socket_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace network {
namespace {
// Message sent over the tcp connection.
const char kMsg[] = "please start tls!";
const size_t kMsgSize = strlen(kMsg);
// Message sent over the tls connection.
const char kSecretMsg[] = "here is secret.";
const size_t kSecretMsgSize = strlen(kSecretMsg);
class TLSClientSocketTestBase {
public:
enum Mode { kDirect, kProxyResolving };
explicit TLSClientSocketTestBase(Mode mode)
: mode_(mode),
scoped_task_environment_(
base::test::ScopedTaskEnvironment::MainThreadType::IO),
url_request_context_(true) {}
virtual ~TLSClientSocketTestBase() {}
Mode mode() { return mode_; }
protected:
// One of the two fields will be set, depending on the mode.
struct SocketHandle {
mojom::TCPConnectedSocketPtr tcp_socket;
mojom::ProxyResolvingSocketPtr proxy_socket;
};
struct SocketRequest {
mojom::TCPConnectedSocketRequest tcp_socket_request;
mojom::ProxyResolvingSocketRequest proxy_socket_request;
};
// Initializes the test fixture. If |use_mock_sockets|, mock client socket
// factory will be used.
void Init(bool use_mock_sockets, bool configure_proxy) {
if (use_mock_sockets) {
mock_client_socket_factory_.set_enable_read_if_ready(true);
url_request_context_.set_client_socket_factory(
&mock_client_socket_factory_);
}
if (configure_proxy) {
proxy_resolution_service_ = net::ProxyResolutionService::CreateFixed(
"http://proxy:8080", TRAFFIC_ANNOTATION_FOR_TESTS);
url_request_context_.set_proxy_resolution_service(
proxy_resolution_service_.get());
}
url_request_context_.Init();
factory_ = std::make_unique<SocketFactory>(nullptr /*net_log*/,
&url_request_context_);
proxy_resolving_factory_ =
std::make_unique<ProxyResolvingSocketFactoryMojo>(
&url_request_context_);
}
// 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::vector<char> buffer(num_bytes);
uint32_t read_size = static_cast<uint32_t>(num_bytes);
MojoResult result = handle->get().ReadData(buffer.data(), &read_size,
MOJO_READ_DATA_FLAG_NONE);
if (result == MOJO_RESULT_SHOULD_WAIT)
continue;
if (result != MOJO_RESULT_OK)
return received_contents;
received_contents.append(buffer.data(), read_size);
}
return received_contents;
}
SocketRequest MakeRequest(SocketHandle* handle) {
SocketRequest result;
if (mode_ == kDirect)
result.tcp_socket_request = mojo::MakeRequest(&handle->tcp_socket);
else
result.proxy_socket_request = mojo::MakeRequest(&handle->proxy_socket);
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_request),
remote_addr);
} else {
return CreateProxyResolvingSocketSync(
std::move(request.proxy_socket_request), remote_addr);
}
}
int CreateTCPConnectedSocketSync(mojom::TCPConnectedSocketRequest request,
const net::IPEndPoint& remote_addr) {
net::AddressList remote_addr_list(remote_addr);
base::RunLoop run_loop;
int net_error = net::ERR_FAILED;
factory_->CreateTCPConnectedSocket(
base::nullopt /* local_addr */, remote_addr_list,
nullptr /* tcp_connected_socket_options */,
TRAFFIC_ANNOTATION_FOR_TESTS, std::move(request),
pre_tls_observer()->GetObserverPtr(),
base::BindLambdaForTesting(
[&](int result,
const base::Optional<net::IPEndPoint>& actual_local_addr,
const base::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(mojom::ProxyResolvingSocketRequest request,
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, nullptr /* options */,
net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
std::move(request), nullptr /* observer */,
base::BindLambdaForTesting(
[&](int result,
const base::Optional<net::IPEndPoint>& actual_local_addr,
const base::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,
mojom::TLSClientSocketRequest request,
net::CompletionOnceCallback callback) {
if (mode_ == kDirect) {
UpgradeTCPConnectedSocketToTLS(handle->tcp_socket.get(), host_port_pair,
nullptr /* options */, std::move(request),
std::move(callback));
} else {
UpgradeProxyResolvingSocketToTLS(handle->proxy_socket.get(),
host_port_pair, std::move(request),
std::move(callback));
}
}
void UpgradeTCPConnectedSocketToTLS(mojom::TCPConnectedSocket* client_socket,
const net::HostPortPair& host_port_pair,
mojom::TLSClientSocketOptionsPtr options,
mojom::TLSClientSocketRequest request,
net::CompletionOnceCallback callback) {
client_socket->UpgradeToTLS(
host_port_pair, std::move(options),
net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
std::move(request), post_tls_observer()->GetObserverPtr(),
base::BindOnce(
[](net::CompletionOnceCallback cb,
mojo::ScopedDataPipeConsumerHandle* consumer_handle_out,
mojo::ScopedDataPipeProducerHandle* producer_handle_out,
base::Optional<net::SSLInfo>* ssl_info_out, int result,
mojo::ScopedDataPipeConsumerHandle receive_pipe_handle,
mojo::ScopedDataPipeProducerHandle send_pipe_handle,
const base::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,
mojom::TLSClientSocketRequest request,
net::CompletionOnceCallback callback) {
client_socket->UpgradeToTLS(
host_port_pair,
net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
std::move(request), post_tls_observer()->GetObserverPtr(),
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 base::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::ScopedTaskEnvironment scoped_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.
base::Optional<net::SSLInfo> ssl_info_;
std::unique_ptr<net::ProxyResolutionService> proxy_resolution_service_;
net::TestURLRequestContext url_request_context_;
net::MockClientSocketFactory mock_client_socket_factory_;
std::unique_ptr<SocketFactory> factory_;
std::unique_ptr<ProxyResolvingSocketFactoryMojo> proxy_resolving_factory_;
TestSocketObserver pre_tls_observer_;
TestSocketObserver post_tls_observer_;
mojo::StrongBindingSet<mojom::TCPServerSocket> tcp_server_socket_bindings_;
mojo::StrongBindingSet<mojom::TCPConnectedSocket>
tcp_connected_socket_bindings_;
DISALLOW_COPY_AND_ASSIGN(TLSClientSocketTestBase);
};
class TLSClientSocketTest
: public ::testing::TestWithParam<TLSClientSocketTestBase::Mode>,
public TLSClientSocketTestBase {
public:
TLSClientSocketTest() : TLSClientSocketTestBase(GetParam()) {
Init(true /* use_mock_sockets */, false /* configure_proxy */);
}
~TLSClientSocketTest() override {}
private:
DISALLOW_COPY_AND_ASSIGN(TLSClientSocketTest);
};
// 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, kMsg, kMsgSize, 1),
net::MockRead(net::SYNCHRONOUS, net::OK, 2)};
const net::MockWrite kWrites[] = {
net::MockWrite(net::SYNCHRONOUS, kMsg, kMsgSize, 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);
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;
mojom::TLSClientSocketPtr tls_socket;
UpgradeToTLS(&client_socket, host_port_pair, mojo::MakeRequest(&tls_socket),
callback.callback());
ASSERT_EQ(net::OK, callback.WaitForResult());
ResetSocket(&client_socket);
uint32_t num_bytes = strlen(kMsg);
EXPECT_EQ(MOJO_RESULT_OK, post_tls_send_handle()->get().WriteData(
&kMsg, &num_bytes, MOJO_WRITE_DATA_FLAG_NONE));
EXPECT_EQ(kMsg, Read(post_tls_recv_handle(), kMsgSize));
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, kMsg, kMsgSize, 1),
net::MockRead(net::SYNCHRONOUS, net::OK, 2)};
const net::MockWrite kWrites[] = {
net::MockWrite(net::SYNCHRONOUS, kMsg, kMsgSize, 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);
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;
mojom::TLSClientSocketPtr tls_socket;
UpgradeToTLS(&client_socket, host_port_pair, mojo::MakeRequest(&tls_socket),
callback.callback());
ASSERT_EQ(net::OK, callback.WaitForResult());
ResetSocket(&client_socket);
uint32_t num_bytes = strlen(kMsg);
EXPECT_EQ(MOJO_RESULT_OK, post_tls_send_handle()->get().WriteData(
&kMsg, &num_bytes, MOJO_WRITE_DATA_FLAG_NONE));
EXPECT_EQ(kMsg, Read(post_tls_recv_handle(), kMsgSize));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(ssl_socket.ConnectDataConsumed());
EXPECT_TRUE(data_provider.AllReadDataConsumed());
EXPECT_TRUE(data_provider.AllWriteDataConsumed());
}
// Calling UpgradeToTLS on the same TCPConnectedSocketPtr 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;
mojom::TLSClientSocketPtr tls_socket;
UpgradeToTLS(&client_socket, host_port_pair, mojo::MakeRequest(&tls_socket),
callback.callback());
ASSERT_EQ(net::OK, callback.WaitForResult());
// Second time UpgradeToTLS is called, it should fail.
mojom::TLSClientSocketPtr 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 base::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),
mojo::MakeRequest(&tls_socket2), nullptr /*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),
mojo::MakeRequest(&tls_socket2), nullptr /*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_1;
ssl_socket.expected_ssl_version_max = net::SSL_PROTOCOL_VERSION_TLS1_2;
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();
mojom::TLSClientSocketPtr tls_socket;
base::RunLoop run_loop;
mojom::TLSClientSocketOptionsPtr options =
mojom::TLSClientSocketOptions::New();
options->version_min = mojom::SSLVersion::kTLS11;
options->version_max = mojom::SSLVersion::kTLS12;
int net_error = net::ERR_FAILED;
auto upgrade_callback = base::BindLambdaForTesting(
[&](int result, mojo::ScopedDataPipeConsumerHandle receive_pipe_handle,
mojo::ScopedDataPipeProducerHandle send_pipe_handle,
const base::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),
mojo::MakeRequest(&tls_socket), nullptr /*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, kMsg, kMsgSize, 0),
net::MockRead(net::ASYNC, kSecretMsg, kSecretMsgSize, 3),
net::MockRead(net::SYNCHRONOUS, net::OK, 4)};
const net::MockWrite kWrites[] = {
net::MockWrite(net::SYNCHRONOUS, kMsg, kMsgSize, 1),
net::MockWrite(net::SYNCHRONOUS, kSecretMsg, kSecretMsgSize, 2),
};
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(), kMsgSize));
uint32_t num_bytes = kMsgSize;
EXPECT_EQ(MOJO_RESULT_OK, pre_tls_send_handle()->get().WriteData(
&kMsg, &num_bytes, MOJO_WRITE_DATA_FLAG_NONE));
net::HostPortPair host_port_pair("example.org", 443);
pre_tls_recv_handle()->reset();
pre_tls_send_handle()->reset();
net::TestCompletionCallback callback;
mojom::TLSClientSocketPtr tls_socket;
UpgradeToTLS(&client_socket, host_port_pair, mojo::MakeRequest(&tls_socket),
callback.callback());
ASSERT_EQ(net::OK, callback.WaitForResult());
ResetSocket(&client_socket);
num_bytes = strlen(kSecretMsg);
EXPECT_EQ(MOJO_RESULT_OK,
post_tls_send_handle()->get().WriteData(&kSecretMsg, &num_bytes,
MOJO_WRITE_DATA_FLAG_NONE));
EXPECT_EQ(kSecretMsg, Read(post_tls_recv_handle(), kSecretMsgSize));
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, kSecretMsg, kSecretMsgSize, 1),
net::MockRead(net::SYNCHRONOUS, net::ERR_CONNECTION_CLOSED, 2)};
const net::MockWrite kWrites[] = {
net::MockWrite(net::SYNCHRONOUS, kSecretMsg, kSecretMsgSize, 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);
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;
mojom::TLSClientSocketPtr tls_socket;
UpgradeToTLS(&client_socket, host_port_pair, mojo::MakeRequest(&tls_socket),
callback.callback());
ASSERT_EQ(net::OK, callback.WaitForResult());
ResetSocket(&client_socket);
uint32_t num_bytes = strlen(kSecretMsg);
EXPECT_EQ(MOJO_RESULT_OK,
post_tls_send_handle()->get().WriteData(&kSecretMsg, &num_bytes,
MOJO_WRITE_DATA_FLAG_NONE));
EXPECT_EQ(kSecretMsg, Read(post_tls_recv_handle(), kSecretMsgSize));
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;
mojom::TLSClientSocketPtr tls_socket;
UpgradeToTLS(&client_socket, host_port_pair, mojo::MakeRequest(&tls_socket),
callback.callback());
ASSERT_EQ(net::OK, callback.WaitForResult());
ResetSocket(&client_socket);
uint32_t num_bytes = strlen(kSecretMsg);
EXPECT_EQ(MOJO_RESULT_OK,
post_tls_send_handle()->get().WriteData(&kSecretMsg, &num_bytes,
MOJO_WRITE_DATA_FLAG_NONE));
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, kMsg, kMsgSize, 0),
net::MockRead(net::ASYNC, kSecretMsg, kSecretMsgSize, 2),
net::MockRead(net::SYNCHRONOUS, net::OK, 3)};
const net::MockWrite kWrites[] = {
net::MockWrite(net::SYNCHRONOUS, kSecretMsg, kSecretMsgSize, 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_send_handle()->reset();
net::TestCompletionCallback callback;
mojom::TLSClientSocketPtr tls_socket;
UpgradeToTLS(&client_socket, host_port_pair, mojo::MakeRequest(&tls_socket),
callback.callback());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(kMsg, Read(pre_tls_recv_handle(), kMsgSize));
// Reset pre-tls receive pipe now and UpgradeToTLS should complete.
pre_tls_recv_handle()->reset();
ASSERT_EQ(net::OK, callback.WaitForResult());
ResetSocket(&client_socket);
uint32_t num_bytes = strlen(kSecretMsg);
EXPECT_EQ(MOJO_RESULT_OK,
post_tls_send_handle()->get().WriteData(&kSecretMsg, &num_bytes,
MOJO_WRITE_DATA_FLAG_NONE));
EXPECT_EQ(kSecretMsg, Read(post_tls_recv_handle(), kSecretMsgSize));
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, kSecretMsg, kSecretMsgSize, 2),
net::MockRead(net::SYNCHRONOUS, net::OK, 3)};
const net::MockWrite kWrites[] = {
net::MockWrite(net::SYNCHRONOUS, kMsg, kMsgSize, 0),
net::MockWrite(net::SYNCHRONOUS, kSecretMsg, kSecretMsgSize, 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();
net::TestCompletionCallback callback;
mojom::TLSClientSocketPtr tls_socket;
UpgradeToTLS(&client_socket, host_port_pair, mojo::MakeRequest(&tls_socket),
callback.callback());
base::RunLoop().RunUntilIdle();
uint32_t num_bytes = strlen(kMsg);
EXPECT_EQ(MOJO_RESULT_OK, pre_tls_send_handle()->get().WriteData(
&kMsg, &num_bytes, MOJO_WRITE_DATA_FLAG_NONE));
// Reset pre-tls send pipe now and UpgradeToTLS should complete.
pre_tls_send_handle()->reset();
ASSERT_EQ(net::OK, callback.WaitForResult());
ResetSocket(&client_socket);
num_bytes = strlen(kSecretMsg);
EXPECT_EQ(MOJO_RESULT_OK,
post_tls_send_handle()->get().WriteData(&kSecretMsg, &num_bytes,
MOJO_WRITE_DATA_FLAG_NONE));
EXPECT_EQ(kSecretMsg, Read(post_tls_recv_handle(), kSecretMsgSize));
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, kMsg, kMsgSize, 0),
net::MockRead(net::ASYNC, kSecretMsg, kSecretMsgSize, 3),
net::MockRead(net::SYNCHRONOUS, net::OK, 4)};
const net::MockWrite kWrites[] = {
net::MockWrite(net::SYNCHRONOUS, kMsg, kMsgSize, 1),
net::MockWrite(net::SYNCHRONOUS, kSecretMsg, kSecretMsgSize, 2)};
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;
mojom::TLSClientSocketPtr tls_socket;
UpgradeToTLS(&client_socket, host_port_pair, mojo::MakeRequest(&tls_socket),
callback.callback());
EXPECT_EQ(kMsg, Read(pre_tls_recv_handle(), kMsgSize));
uint32_t num_bytes = strlen(kMsg);
EXPECT_EQ(MOJO_RESULT_OK, pre_tls_send_handle()->get().WriteData(
&kMsg, &num_bytes, MOJO_WRITE_DATA_FLAG_NONE));
// 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);
num_bytes = strlen(kSecretMsg);
EXPECT_EQ(MOJO_RESULT_OK,
post_tls_send_handle()->get().WriteData(&kSecretMsg, &num_bytes,
MOJO_WRITE_DATA_FLAG_NONE));
EXPECT_EQ(kSecretMsg, Read(post_tls_recv_handle(), kSecretMsgSize));
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, kMsg, kMsgSize, 0),
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;
mojom::TLSClientSocketPtr tls_socket;
UpgradeToTLS(&client_socket, host_port_pair, mojo::MakeRequest(&tls_socket),
callback.callback());
EXPECT_EQ(kMsg, Read(pre_tls_recv_handle(), kMsgSize));
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;
mojom::TLSClientSocketPtr tls_socket;
UpgradeToTLS(&client_socket, host_port_pair, mojo::MakeRequest(&tls_socket),
callback.callback());
uint32_t num_bytes = strlen(kMsg);
EXPECT_EQ(MOJO_RESULT_OK, pre_tls_send_handle()->get().WriteData(
&kMsg, &num_bytes, MOJO_WRITE_DATA_FLAG_NONE));
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(
/* no prefix */,
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() override {}
private:
DISALLOW_COPY_AND_ASSIGN(TLSCLientSocketProxyTest);
};
TEST_F(TLSCLientSocketProxyTest, UpgradeToTLS) {
const char 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";
const char kConnectResponse[] = "HTTP/1.1 200 OK\r\n\r\n";
const net::MockRead kReads[] = {
net::MockRead(net::ASYNC, kConnectResponse, strlen(kConnectResponse), 1),
net::MockRead(net::ASYNC, kMsg, kMsgSize, 3),
net::MockRead(net::SYNCHRONOUS, net::OK, 4)};
const net::MockWrite kWrites[] = {
net::MockWrite(net::ASYNC, kConnectRequest, strlen(kConnectRequest), 0),
net::MockWrite(net::SYNCHRONOUS, kMsg, kMsgSize, 2)};
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;
mojom::TLSClientSocketPtr tls_socket;
UpgradeToTLS(&client_socket, host_port_pair, mojo::MakeRequest(&tls_socket),
callback.callback());
ASSERT_EQ(net::OK, callback.WaitForResult());
ResetSocket(&client_socket);
uint32_t num_bytes = strlen(kMsg);
EXPECT_EQ(MOJO_RESULT_OK, post_tls_send_handle()->get().WriteData(
&kMsg, &num_bytes, MOJO_WRITE_DATA_FLAG_NONE));
EXPECT_EQ(kMsg, Read(post_tls_recv_handle(), kMsgSize));
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() override {}
private:
DISALLOW_COPY_AND_ASSIGN(TLSClientSocketIoModeTest);
};
INSTANTIATE_TEST_SUITE_P(/* no prefix */,
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 < kSecretMsgSize; ++i) {
writes.push_back(
net::MockWrite(mode, &kSecretMsg[i], 1, sequence_number++));
}
for (size_t i = 0; i < kSecretMsgSize; ++i) {
reads.push_back(
net::MockRead(net::ASYNC, &kSecretMsg[i], 1, sequence_number++));
}
if (j == kNumIterations - 1) {
reads.push_back(net::MockRead(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);
mojom::TCPConnectedSocketPtr client_socket;
net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
EXPECT_EQ(net::OK, CreateTCPConnectedSocketSync(
mojo::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;
mojom::TLSClientSocketPtr tls_socket;
UpgradeTCPConnectedSocketToTLS(
client_socket.get(), host_port_pair, nullptr /* options */,
mojo::MakeRequest(&tls_socket), 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 < kSecretMsgSize; ++i) {
uint32_t num_bytes = 1;
EXPECT_EQ(MOJO_RESULT_OK,
post_tls_send_handle()->get().WriteData(
&kSecretMsg[i], &num_bytes, MOJO_WRITE_DATA_FLAG_NONE));
// 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(), kSecretMsgSize));
}
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);
mojom::TCPConnectedSocketPtr client_socket;
net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
EXPECT_EQ(net::OK, CreateTCPConnectedSocketSync(
mojo::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;
mojom::TLSClientSocketPtr tls_socket;
mojom::TLSClientSocketOptionsPtr options =
mojom::TLSClientSocketOptions::New();
options->send_ssl_info = true;
UpgradeTCPConnectedSocketToTLS(
client_socket.get(), host_port_pair, std::move(options),
mojo::MakeRequest(&tls_socket), 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() override {}
// Starts the test server using the specified certificate.
bool StartTestServer(net::EmbeddedTestServer::ServerCertificate certificate)
WARN_UNUSED_RESULT {
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.
int CreateTLSSocket() WARN_UNUSED_RESULT {
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(),
mojo::MakeRequest(&tls_socket_), callback.callback());
int result = callback.WaitForResult();
ResetSocket(&client_socket);
return result;
}
int CreateTLSSocketWithOptions(mojom::TLSClientSocketOptionsPtr options)
WARN_UNUSED_RESULT {
// Proxy connections don't support TLSClientSocketOptions.
DCHECK_EQ(kDirect, mode());
mojom::TCPConnectedSocketPtr tcp_socket;
net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(),
server_.port());
EXPECT_EQ(net::OK, CreateTCPConnectedSocketSync(
mojo::MakeRequest(&tcp_socket), 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),
mojo::MakeRequest(&tls_socket_), 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";
uint32_t num_bytes = strlen(kTestMsg);
const char kResponse[] = "HTTP/1.1 200 OK\n\n";
EXPECT_EQ(MOJO_RESULT_OK,
post_tls_send_handle()->get().WriteData(
&kTestMsg, &num_bytes, MOJO_WRITE_DATA_FLAG_NONE));
EXPECT_EQ(kResponse, Read(post_tls_recv_handle(), strlen(kResponse)));
}
net::EmbeddedTestServer* server() { return &server_; }
private:
net::EmbeddedTestServer server_;
mojom::TLSClientSocketPtr tls_socket_;
DISALLOW_COPY_AND_ASSIGN(TLSClientSocketTestWithEmbeddedTestServerBase);
};
class TLSClientSocketTestWithEmbeddedTestServer
: public TLSClientSocketTestWithEmbeddedTestServerBase,
public ::testing::TestWithParam<TLSClientSocketTestBase::Mode> {
public:
TLSClientSocketTestWithEmbeddedTestServer()
: TLSClientSocketTestWithEmbeddedTestServerBase(GetParam()) {}
~TLSClientSocketTestWithEmbeddedTestServer() override {}
private:
DISALLOW_COPY_AND_ASSIGN(TLSClientSocketTestWithEmbeddedTestServer);
};
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(
/* no prefix */,
TLSClientSocketTestWithEmbeddedTestServer,
::testing::Values(TLSClientSocketTestBase::kDirect,
TLSClientSocketTestBase::kProxyResolving));
class TLSClientSocketDirectTestWithEmbeddedTestServer
: public TLSClientSocketTestWithEmbeddedTestServerBase,
public testing::Test {
public:
TLSClientSocketDirectTestWithEmbeddedTestServer()
: TLSClientSocketTestWithEmbeddedTestServerBase(kDirect) {}
~TLSClientSocketDirectTestWithEmbeddedTestServer() override {}
private:
DISALLOW_COPY_AND_ASSIGN(TLSClientSocketDirectTestWithEmbeddedTestServer);
};
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