blob: 5d41159fdaf5d566b1e94d87fd9d3d60dfc5feb8 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/quic/quic_proxy_client_socket.h"
#include <memory>
#include <utility>
#include "base/functional/bind.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "net/base/network_anonymization_key.h"
#include "net/base/proxy_chain.h"
#include "net/base/proxy_server.h"
#include "net/base/proxy_string_util.h"
#include "net/base/test_proxy_delegate.h"
#include "net/http/http_response_headers.h"
#include "net/log/net_log.h"
#include "net/log/test_net_log.h"
#include "net/log/test_net_log_util.h"
#include "net/quic/quic_chromium_client_session.h"
#include "net/quic/quic_http_utils.h"
#include "net/quic/quic_proxy_client_socket_test_base.h"
#include "net/quic/test_quic_crypto_client_config_handle.h"
#include "net/quic/test_task_runner.h"
#include "net/ssl/ssl_config_service_defaults.h"
#include "net/test/gtest_util.h"
#include "net/test/test_data_directory.h"
#include "net/test/test_with_task_environment.h"
#include "net/third_party/quiche/src/quiche/quic/test_tools/qpack/qpack_test_utils.h"
#include "net/third_party/quiche/src/quiche/quic/test_tools/quic_test_utils.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
#include "url/scheme_host_port.h"
using testing::_;
using testing::AnyNumber;
using testing::Return;
namespace net::test {
class QuicProxyClientSocketTest : public QuicProxyClientSocketTestBase {
public:
void TearDown() override {
sock_.reset();
EXPECT_TRUE(mock_quic_data_.AllReadDataConsumed());
EXPECT_TRUE(mock_quic_data_.AllWriteDataConsumed());
}
void InitializeClientSocket() override {
sock_ = std::make_unique<QuicProxyClientSocket>(
std::move(stream_handle_), std::move(session_handle_),
// TODO(crbug.com/1206799) Construct `ProxyChain` with plain
// `proxy_endpoint_` once it supports `url::SchemeHostPort`.
ProxyChain(ProxyServer::SCHEME_HTTPS,
HostPortPair::FromSchemeHostPort(proxy_endpoint_)),
/*proxy_chain_index=*/0, user_agent_,
// TODO(crbug.com/1206799) Construct `QuicProxyClientSocket` with plain
// `proxy_endpoint_` once it supports `url::SchemeHostPort`.
HostPortPair::FromSchemeHostPort(destination_endpoint_),
NetLogWithSource::Make(NetLogSourceType::NONE),
base::MakeRefCounted<HttpAuthController>(
HttpAuth::AUTH_PROXY, proxy_endpoint_.GetURL(),
NetworkAnonymizationKey(), &http_auth_cache_,
http_auth_handler_factory_.get(), host_resolver_.get()),
proxy_delegate_.get());
session_->StartReading();
}
void PopulateConnectRequestIR(
spdy::Http2HeaderBlock* block,
std::optional<const HttpRequestHeaders> extra_headers) override {
(*block)[":method"] = "CONNECT";
(*block)[":authority"] =
HostPortPair::FromSchemeHostPort(destination_endpoint_).ToString();
(*block)["user-agent"] = kUserAgent;
if (extra_headers) {
HttpRequestHeaders::Iterator it(*extra_headers);
while (it.GetNext()) {
std::string name = base::ToLowerASCII(it.name());
(*block)[name] = it.value();
}
}
}
void AssertConnectSucceeds() override {
TestCompletionCallback callback;
ASSERT_THAT(sock_->Connect(callback.callback()),
test::IsError(ERR_IO_PENDING));
ASSERT_THAT(callback.WaitForResult(), test::IsOk());
}
void AssertConnectFails(int result) override {
TestCompletionCallback callback;
ASSERT_THAT(sock_->Connect(callback.callback()),
test::IsError(ERR_IO_PENDING));
EXPECT_EQ(result, callback.WaitForResult());
}
void AssertWriteReturns(const char* data, int len, int rv) override {
auto buf = base::MakeRefCounted<IOBufferWithSize>(len);
memcpy(buf->data(), data, len);
EXPECT_EQ(rv,
sock_->Write(buf.get(), buf->size(), write_callback_.callback(),
TRAFFIC_ANNOTATION_FOR_TESTS));
}
void AssertSyncWriteSucceeds(const char* data, int len) override {
auto buf = base::MakeRefCounted<IOBufferWithSize>(len);
memcpy(buf->data(), data, len);
EXPECT_EQ(len,
sock_->Write(buf.get(), buf->size(), CompletionOnceCallback(),
TRAFFIC_ANNOTATION_FOR_TESTS));
}
void AssertSyncReadEquals(const char* data, int len) override {
auto buf = base::MakeRefCounted<IOBufferWithSize>(len);
EXPECT_EQ(len, sock_->Read(buf.get(), len, CompletionOnceCallback()));
EXPECT_EQ(std::string(data, len), std::string(buf->data(), len));
ASSERT_TRUE(sock_->IsConnected());
}
void AssertAsyncReadEquals(const char* data, int len) override {
auto buf = base::MakeRefCounted<IOBufferWithSize>(len);
ASSERT_EQ(ERR_IO_PENDING,
sock_->Read(buf.get(), len, read_callback_.callback()));
EXPECT_TRUE(sock_->IsConnected());
ResumeAndRun();
EXPECT_EQ(len, read_callback_.WaitForResult());
EXPECT_TRUE(sock_->IsConnected());
EXPECT_EQ(std::string(data, len), std::string(buf->data(), len));
}
void AssertReadStarts(const char* data, int len) override {
// Issue the read, which will be completed asynchronously.
read_buf_ = base::MakeRefCounted<IOBufferWithSize>(len);
ASSERT_EQ(ERR_IO_PENDING,
sock_->Read(read_buf_.get(), len, read_callback_.callback()));
EXPECT_TRUE(sock_->IsConnected());
}
void AssertReadReturns(const char* data, int len) override {
EXPECT_TRUE(sock_->IsConnected());
// Now the read will return.
EXPECT_EQ(len, read_callback_.WaitForResult());
EXPECT_EQ(std::string(data, len), std::string(read_buf_->data(), len));
}
protected:
std::unique_ptr<QuicProxyClientSocket> sock_;
};
TEST_P(QuicProxyClientSocketTest, ConnectSendsCorrectRequest) {
int packet_number = 1;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructSettingsPacket(packet_number++));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructConnectRequestPacket(packet_number++));
mock_quic_data_.AddRead(ASYNC, ConstructServerConnectReplyPacket(1, !kFin));
mock_quic_data_.AddReadPauseForever();
mock_quic_data_.AddWrite(
SYNCHRONOUS, ConstructAckAndRstPacket(packet_number++,
quic::QUIC_STREAM_CANCELLED, 1, 1));
InitializeSession();
InitializeClientSocket();
ASSERT_FALSE(sock_->IsConnected());
AssertConnectSucceeds();
const HttpResponseInfo* response = sock_->GetConnectResponseInfo();
ASSERT_TRUE(response != nullptr);
ASSERT_EQ(200, response->headers->response_code());
// Although the underlying HTTP/3 connection uses TLS and negotiates ALPN, the
// tunnel itself is a TCP connection to the origin and should not report these
// values.
net::SSLInfo ssl_info;
EXPECT_FALSE(sock_->GetSSLInfo(&ssl_info));
EXPECT_EQ(sock_->GetNegotiatedProtocol(), NextProto::kProtoUnknown);
}
TEST_P(QuicProxyClientSocketTest, ProxyDelegateExtraHeaders) {
// TODO(crbug.com/40284947): Add a version of this test for multi-hop.
proxy_delegate_ = std::make_unique<TestProxyDelegate>();
proxy_delegate_->set_extra_header_name(kTestHeaderName);
// TODO(crbug.com/1206799) Construct `proxy_chain` with plain
// `proxy_endpoint_` once it supports `url::SchemeHostPort`.
ProxyChain proxy_chain(ProxyServer::SCHEME_HTTPS,
HostPortPair::FromSchemeHostPort(proxy_endpoint_));
const char kResponseHeaderName[] = "bar";
const char kResponseHeaderValue[] = "testing";
int packet_number = 1;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructSettingsPacket(packet_number++));
mock_quic_data_.AddWrite(
SYNCHRONOUS,
ConstructConnectRequestPacketWithExtraHeaders(
packet_number++,
// Order matters! Keep these alphabetical.
{{kTestQuicHeaderName, ProxyServerToProxyUri(proxy_chain.First())},
{"user-agent", kUserAgent}}));
mock_quic_data_.AddRead(
ASYNC, ConstructServerConnectReplyPacketWithExtraHeaders(
1, !kFin, {{kResponseHeaderName, kResponseHeaderValue}}));
mock_quic_data_.AddReadPauseForever();
mock_quic_data_.AddWrite(
SYNCHRONOUS, ConstructAckAndRstPacket(packet_number++,
quic::QUIC_STREAM_CANCELLED, 1, 1));
InitializeSession();
InitializeClientSocket();
ASSERT_FALSE(sock_->IsConnected());
AssertConnectSucceeds();
const HttpResponseInfo* response = sock_->GetConnectResponseInfo();
ASSERT_TRUE(response != nullptr);
ASSERT_EQ(200, response->headers->response_code());
ASSERT_EQ(proxy_delegate_->on_tunnel_headers_received_call_count(), 1u);
proxy_delegate_->VerifyOnTunnelHeadersReceived(proxy_chain, /*chain_index=*/0,
kResponseHeaderName,
kResponseHeaderValue);
}
TEST_P(QuicProxyClientSocketTest, ConnectWithAuthRequested) {
int packet_number = 1;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructSettingsPacket(packet_number++));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructConnectRequestPacket(packet_number++));
mock_quic_data_.AddRead(ASYNC,
ConstructServerConnectAuthReplyPacket(1, !kFin));
mock_quic_data_.AddReadPauseForever();
mock_quic_data_.AddWrite(
SYNCHRONOUS, ConstructAckAndRstPacket(packet_number++,
quic::QUIC_STREAM_CANCELLED, 1, 1));
InitializeSession();
InitializeClientSocket();
AssertConnectFails(ERR_PROXY_AUTH_REQUESTED);
const HttpResponseInfo* response = sock_->GetConnectResponseInfo();
ASSERT_TRUE(response != nullptr);
ASSERT_EQ(407, response->headers->response_code());
}
TEST_P(QuicProxyClientSocketTest, ConnectWithAuthCredentials) {
int packet_number = 1;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructSettingsPacket(packet_number++));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructConnectAuthRequestPacket(packet_number++));
mock_quic_data_.AddRead(ASYNC, ConstructServerConnectReplyPacket(1, !kFin));
mock_quic_data_.AddReadPauseForever();
mock_quic_data_.AddWrite(
SYNCHRONOUS, ConstructAckAndRstPacket(packet_number++,
quic::QUIC_STREAM_CANCELLED, 1, 1));
InitializeSession();
InitializeClientSocket();
// Add auth to cache
const std::u16string kFoo(u"foo");
const std::u16string kBar(u"bar");
http_auth_cache_.Add(
url::SchemeHostPort(GURL(kProxyUrl)), HttpAuth::AUTH_PROXY, "MyRealm1",
HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(),
"Basic realm=MyRealm1", AuthCredentials(kFoo, kBar), "/");
AssertConnectSucceeds();
const HttpResponseInfo* response = sock_->GetConnectResponseInfo();
ASSERT_TRUE(response != nullptr);
ASSERT_EQ(200, response->headers->response_code());
}
// Tests that a redirect response from a CONNECT fails.
TEST_P(QuicProxyClientSocketTest, ConnectRedirects) {
int packet_number = 1;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructSettingsPacket(packet_number++));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructConnectRequestPacket(packet_number++));
mock_quic_data_.AddRead(ASYNC,
ConstructServerConnectRedirectReplyPacket(1, !kFin));
mock_quic_data_.AddReadPauseForever();
mock_quic_data_.AddWrite(
SYNCHRONOUS, ConstructAckAndRstPacket(packet_number++,
quic::QUIC_STREAM_CANCELLED, 1, 1));
InitializeSession();
InitializeClientSocket();
AssertConnectFails(ERR_TUNNEL_CONNECTION_FAILED);
const HttpResponseInfo* response = sock_->GetConnectResponseInfo();
ASSERT_TRUE(response != nullptr);
const HttpResponseHeaders* headers = response->headers.get();
ASSERT_EQ(302, headers->response_code());
ASSERT_TRUE(headers->HasHeader("set-cookie"));
std::string location;
ASSERT_TRUE(headers->IsRedirect(&location));
ASSERT_EQ(location, kRedirectUrl);
}
TEST_P(QuicProxyClientSocketTest, ConnectFails) {
int packet_number = 1;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructSettingsPacket(packet_number++));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructConnectRequestPacket(packet_number++));
mock_quic_data_.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
InitializeSession();
InitializeClientSocket();
ASSERT_FALSE(sock_->IsConnected());
AssertConnectFails(ERR_QUIC_PROTOCOL_ERROR);
ASSERT_FALSE(sock_->IsConnected());
}
TEST_P(QuicProxyClientSocketTest, WasEverUsedReturnsCorrectValue) {
int packet_number = 1;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructSettingsPacket(packet_number++));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructConnectRequestPacket(packet_number++));
mock_quic_data_.AddRead(ASYNC, ConstructServerConnectReplyPacket(1, !kFin));
mock_quic_data_.AddReadPauseForever();
mock_quic_data_.AddWrite(
SYNCHRONOUS, ConstructAckAndRstPacket(packet_number++,
quic::QUIC_STREAM_CANCELLED, 1, 1));
InitializeSession();
InitializeClientSocket();
EXPECT_TRUE(sock_->WasEverUsed()); // Used due to crypto handshake
AssertConnectSucceeds();
EXPECT_TRUE(sock_->WasEverUsed());
sock_->Disconnect();
EXPECT_TRUE(sock_->WasEverUsed());
}
TEST_P(QuicProxyClientSocketTest, GetPeerAddressReturnsCorrectValues) {
int packet_number = 1;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructSettingsPacket(packet_number++));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructConnectRequestPacket(packet_number++));
mock_quic_data_.AddRead(ASYNC, ConstructServerConnectReplyPacket(1, !kFin));
mock_quic_data_.AddReadPause();
mock_quic_data_.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
InitializeSession();
InitializeClientSocket();
IPEndPoint addr;
EXPECT_THAT(sock_->GetPeerAddress(&addr), IsError(ERR_SOCKET_NOT_CONNECTED));
AssertConnectSucceeds();
EXPECT_TRUE(sock_->IsConnected());
EXPECT_THAT(sock_->GetPeerAddress(&addr), IsOk());
ResumeAndRun();
EXPECT_FALSE(sock_->IsConnected());
EXPECT_THAT(sock_->GetPeerAddress(&addr), IsError(ERR_SOCKET_NOT_CONNECTED));
sock_->Disconnect();
EXPECT_THAT(sock_->GetPeerAddress(&addr), IsError(ERR_SOCKET_NOT_CONNECTED));
}
TEST_P(QuicProxyClientSocketTest, IsConnectedAndIdle) {
int packet_number = 1;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructSettingsPacket(packet_number++));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructConnectRequestPacket(packet_number++));
mock_quic_data_.AddRead(ASYNC, ConstructServerConnectReplyPacket(1, !kFin));
mock_quic_data_.AddReadPause();
std::string header = ConstructDataHeader(kLen1);
mock_quic_data_.AddRead(
ASYNC, ConstructServerDataPacket(2, header + std::string(kMsg1, kLen1)));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructAckPacket(packet_number++, 2, 1));
mock_quic_data_.AddReadPauseForever();
mock_quic_data_.AddWrite(
SYNCHRONOUS,
ConstructRstPacket(packet_number++, quic::QUIC_STREAM_CANCELLED));
InitializeSession();
InitializeClientSocket();
EXPECT_FALSE(sock_->IsConnectedAndIdle());
AssertConnectSucceeds();
EXPECT_TRUE(sock_->IsConnectedAndIdle());
// The next read is consumed and buffered.
ResumeAndRun();
EXPECT_FALSE(sock_->IsConnectedAndIdle());
AssertSyncReadEquals(kMsg1, kLen1);
EXPECT_TRUE(sock_->IsConnectedAndIdle());
}
TEST_P(QuicProxyClientSocketTest, GetTotalReceivedBytes) {
int packet_number = 1;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructSettingsPacket(packet_number++));
size_t header_length;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructConnectRequestPacket(packet_number++));
mock_quic_data_.AddRead(
ASYNC, ConstructServerConnectReplyPacket(1, !kFin, &header_length));
mock_quic_data_.AddReadPause();
std::string data_header = ConstructDataHeader(kLen333);
mock_quic_data_.AddRead(ASYNC,
ConstructServerDataPacket(
2, data_header + std::string(kMsg333, kLen333)));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructAckPacket(packet_number++, 2, 1));
mock_quic_data_.AddReadPauseForever();
mock_quic_data_.AddWrite(
SYNCHRONOUS,
ConstructRstPacket(packet_number++, quic::QUIC_STREAM_CANCELLED));
InitializeSession();
InitializeClientSocket();
EXPECT_EQ(0, sock_->GetTotalReceivedBytes());
AssertConnectSucceeds();
EXPECT_EQ((int64_t)(header_length), sock_->GetTotalReceivedBytes());
// The next read is consumed and buffered.
ResumeAndRun();
EXPECT_EQ((int64_t)(header_length + data_header.length()),
sock_->GetTotalReceivedBytes());
// The payload from the single large data frame will be read across
// two different reads.
AssertSyncReadEquals(kMsg33, kLen33);
EXPECT_EQ((int64_t)(header_length + data_header.length() + kLen33),
sock_->GetTotalReceivedBytes());
AssertSyncReadEquals(kMsg3, kLen3);
EXPECT_EQ((int64_t)(header_length + kLen333 + data_header.length()),
sock_->GetTotalReceivedBytes());
}
TEST_P(QuicProxyClientSocketTest, SetStreamPriority) {
int packet_number = 1;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructSettingsPacket(packet_number++));
// Despite setting the priority to HIGHEST, the requests initial priority of
// LOWEST is used.
mock_quic_data_.AddWrite(
SYNCHRONOUS,
ConstructConnectRequestPacket(packet_number++,
/*extra_headers=*/std::nullopt, LOWEST));
mock_quic_data_.AddRead(ASYNC, ConstructServerConnectReplyPacket(1, !kFin));
mock_quic_data_.AddReadPauseForever();
mock_quic_data_.AddWrite(
SYNCHRONOUS, ConstructAckAndRstPacket(packet_number++,
quic::QUIC_STREAM_CANCELLED, 1, 1));
InitializeSession();
InitializeClientSocket();
sock_->SetStreamPriority(HIGHEST);
AssertConnectSucceeds();
}
TEST_P(QuicProxyClientSocketTest, WriteSendsDataInDataFrame) {
int packet_number = 1;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructSettingsPacket(packet_number++));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructConnectRequestPacket(packet_number++));
mock_quic_data_.AddRead(ASYNC, ConstructServerConnectReplyPacket(1, !kFin));
mock_quic_data_.AddReadPauseForever();
std::string header = ConstructDataHeader(kLen1);
mock_quic_data_.AddWrite(
SYNCHRONOUS,
ConstructAckAndDataPacket(packet_number++, 1, 1,
{header + std::string(kMsg1, kLen1)}));
std::string header2 = ConstructDataHeader(kLen2);
mock_quic_data_.AddWrite(
SYNCHRONOUS, ConstructDataPacket(packet_number++,
{header2 + std::string(kMsg2, kLen2)}));
mock_quic_data_.AddWrite(
SYNCHRONOUS,
ConstructRstPacket(packet_number++, quic::QUIC_STREAM_CANCELLED));
InitializeSession();
InitializeClientSocket();
AssertConnectSucceeds();
AssertSyncWriteSucceeds(kMsg1, kLen1);
AssertSyncWriteSucceeds(kMsg2, kLen2);
}
TEST_P(QuicProxyClientSocketTest, WriteSplitsLargeDataIntoMultiplePackets) {
int write_packet_index = 1;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructSettingsPacket(write_packet_index++));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructConnectRequestPacket(write_packet_index++));
mock_quic_data_.AddRead(ASYNC, ConstructServerConnectReplyPacket(1, !kFin));
mock_quic_data_.AddReadPauseForever();
std::string header = ConstructDataHeader(kLen1);
mock_quic_data_.AddWrite(
SYNCHRONOUS,
ConstructAckAndDataPacket(write_packet_index++, 1, 1,
{header + std::string(kMsg1, kLen1)}));
// Expect |kNumDataPackets| data packets, each containing the max possible
// amount of data.
int numDataPackets = 3;
std::string data(numDataPackets * quic::kDefaultMaxPacketSize, 'x');
quic::QuicStreamOffset offset = kLen1 + header.length();
numDataPackets++;
size_t total_data_length = 0;
for (int i = 0; i < numDataPackets; ++i) {
size_t max_packet_data_length = GetStreamFrameDataLengthFromPacketLength(
quic::kDefaultMaxPacketSize, version_, !kIncludeVersion,
!kIncludeDiversificationNonce, k8ByteConnectionId,
quic::PACKET_1BYTE_PACKET_NUMBER, offset);
if (i == 0) {
// 3661 is the data frame length from packet length.
std::string header2 = ConstructDataHeader(3661);
mock_quic_data_.AddWrite(
SYNCHRONOUS,
ConstructDataPacket(
write_packet_index++,
{header2 +
std::string(data.c_str(), max_packet_data_length - 7)}));
offset += max_packet_data_length - header2.length() - 1;
} else if (i == numDataPackets - 1) {
mock_quic_data_.AddWrite(
SYNCHRONOUS, ConstructDataPacket(write_packet_index++,
std::string(data.c_str(), 7)));
offset += 7;
} else {
mock_quic_data_.AddWrite(
SYNCHRONOUS, ConstructDataPacket(
write_packet_index++,
std::string(data.c_str(), max_packet_data_length)));
offset += max_packet_data_length;
}
if (i != 3) {
total_data_length += max_packet_data_length;
}
}
mock_quic_data_.AddWrite(
SYNCHRONOUS,
ConstructRstPacket(write_packet_index++, quic::QUIC_STREAM_CANCELLED));
InitializeSession();
InitializeClientSocket();
AssertConnectSucceeds();
// Make a small write. An ACK and STOP_WAITING will be bundled. This prevents
// ACK and STOP_WAITING from being bundled with the subsequent large write.
// This allows the test code for computing the size of data sent in each
// packet to not become too complicated.
AssertSyncWriteSucceeds(kMsg1, kLen1);
// Make large write that should be split up
AssertSyncWriteSucceeds(data.c_str(), total_data_length);
}
// ----------- Read
TEST_P(QuicProxyClientSocketTest, ReadReadsDataInDataFrame) {
int packet_number = 1;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructSettingsPacket(packet_number++));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructConnectRequestPacket(packet_number++));
mock_quic_data_.AddRead(ASYNC, ConstructServerConnectReplyPacket(1, !kFin));
mock_quic_data_.AddReadPause();
std::string header = ConstructDataHeader(kLen1);
mock_quic_data_.AddRead(
ASYNC, ConstructServerDataPacket(2, header + std::string(kMsg1, kLen1)));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructAckPacket(packet_number++, 2, 1));
mock_quic_data_.AddReadPauseForever();
mock_quic_data_.AddWrite(
SYNCHRONOUS,
ConstructRstPacket(packet_number++, quic::QUIC_STREAM_CANCELLED));
InitializeSession();
InitializeClientSocket();
AssertConnectSucceeds();
ResumeAndRun();
AssertSyncReadEquals(kMsg1, kLen1);
}
TEST_P(QuicProxyClientSocketTest, ReadDataFromBufferedFrames) {
int packet_number = 1;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructSettingsPacket(packet_number++));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructConnectRequestPacket(packet_number++));
mock_quic_data_.AddRead(ASYNC, ConstructServerConnectReplyPacket(1, !kFin));
mock_quic_data_.AddReadPause();
std::string header = ConstructDataHeader(kLen1);
mock_quic_data_.AddRead(
ASYNC, ConstructServerDataPacket(2, header + std::string(kMsg1, kLen1)));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructAckPacket(packet_number++, 2, 1));
mock_quic_data_.AddReadPause();
std::string header2 = ConstructDataHeader(kLen2);
mock_quic_data_.AddRead(
ASYNC, ConstructServerDataPacket(3, header2 + std::string(kMsg2, kLen2)));
mock_quic_data_.AddReadPauseForever();
mock_quic_data_.AddWrite(
SYNCHRONOUS, ConstructAckAndRstPacket(packet_number++,
quic::QUIC_STREAM_CANCELLED, 3, 3));
InitializeSession();
InitializeClientSocket();
AssertConnectSucceeds();
ResumeAndRun();
AssertSyncReadEquals(kMsg1, kLen1);
ResumeAndRun();
AssertSyncReadEquals(kMsg2, kLen2);
}
TEST_P(QuicProxyClientSocketTest, ReadDataMultipleBufferedFrames) {
int packet_number = 1;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructSettingsPacket(packet_number++));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructConnectRequestPacket(packet_number++));
mock_quic_data_.AddRead(ASYNC, ConstructServerConnectReplyPacket(1, !kFin));
mock_quic_data_.AddReadPause();
std::string header = ConstructDataHeader(kLen1);
mock_quic_data_.AddRead(
ASYNC, ConstructServerDataPacket(2, header + std::string(kMsg1, kLen1)));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructAckPacket(packet_number++, 2, 1));
std::string header2 = ConstructDataHeader(kLen2);
mock_quic_data_.AddRead(
ASYNC, ConstructServerDataPacket(3, header2 + std::string(kMsg2, kLen2)));
mock_quic_data_.AddReadPauseForever();
mock_quic_data_.AddWrite(
SYNCHRONOUS, ConstructAckAndRstPacket(packet_number++,
quic::QUIC_STREAM_CANCELLED, 3, 3));
InitializeSession();
InitializeClientSocket();
AssertConnectSucceeds();
// The next two reads are consumed and buffered.
ResumeAndRun();
AssertSyncReadEquals(kMsg1, kLen1);
AssertSyncReadEquals(kMsg2, kLen2);
}
TEST_P(QuicProxyClientSocketTest, LargeReadWillMergeDataFromDifferentFrames) {
int packet_number = 1;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructSettingsPacket(packet_number++));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructConnectRequestPacket(packet_number++));
mock_quic_data_.AddRead(ASYNC, ConstructServerConnectReplyPacket(1, !kFin));
mock_quic_data_.AddReadPause();
std::string header = ConstructDataHeader(kLen3);
mock_quic_data_.AddRead(
ASYNC, ConstructServerDataPacket(2, header + std::string(kMsg3, kLen3)));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructAckPacket(packet_number++, 2, 1));
std::string header2 = ConstructDataHeader(kLen3);
mock_quic_data_.AddRead(
ASYNC, ConstructServerDataPacket(3, header2 + std::string(kMsg3, kLen3)));
mock_quic_data_.AddReadPauseForever();
mock_quic_data_.AddWrite(
SYNCHRONOUS, ConstructAckAndRstPacket(packet_number++,
quic::QUIC_STREAM_CANCELLED, 3, 3));
InitializeSession();
InitializeClientSocket();
AssertConnectSucceeds();
// The next two reads are consumed and buffered.
ResumeAndRun();
// The payload from two data frames, each with kMsg3 will be combined
// together into a single read().
AssertSyncReadEquals(kMsg33, kLen33);
}
TEST_P(QuicProxyClientSocketTest, MultipleShortReadsThenMoreRead) {
int packet_number = 1;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructSettingsPacket(packet_number++));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructConnectRequestPacket(packet_number++));
mock_quic_data_.AddRead(ASYNC, ConstructServerConnectReplyPacket(1, !kFin));
mock_quic_data_.AddReadPause();
std::string header = ConstructDataHeader(kLen1);
mock_quic_data_.AddRead(
ASYNC, ConstructServerDataPacket(2, header + std::string(kMsg1, kLen1)));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructAckPacket(packet_number++, 2, 1));
std::string header2 = ConstructDataHeader(kLen3);
mock_quic_data_.AddRead(
ASYNC, ConstructServerDataPacket(3, header2 + std::string(kMsg3, kLen3)));
mock_quic_data_.AddRead(
ASYNC, ConstructServerDataPacket(4, header2 + std::string(kMsg3, kLen3)));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructAckPacket(packet_number++, 4, 3));
std::string header3 = ConstructDataHeader(kLen2);
mock_quic_data_.AddRead(
ASYNC, ConstructServerDataPacket(5, header3 + std::string(kMsg2, kLen2)));
mock_quic_data_.AddReadPauseForever();
mock_quic_data_.AddWrite(
SYNCHRONOUS, ConstructAckAndRstPacket(packet_number++,
quic::QUIC_STREAM_CANCELLED, 5, 5));
InitializeSession();
InitializeClientSocket();
AssertConnectSucceeds();
// The next 4 reads are consumed and buffered.
ResumeAndRun();
AssertSyncReadEquals(kMsg1, kLen1);
// The payload from two data frames, each with kMsg3 will be combined
// together into a single read().
AssertSyncReadEquals(kMsg33, kLen33);
AssertSyncReadEquals(kMsg2, kLen2);
}
TEST_P(QuicProxyClientSocketTest, ReadWillSplitDataFromLargeFrame) {
int packet_number = 1;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructSettingsPacket(packet_number++));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructConnectRequestPacket(packet_number++));
mock_quic_data_.AddRead(ASYNC, ConstructServerConnectReplyPacket(1, !kFin));
mock_quic_data_.AddReadPause();
std::string header = ConstructDataHeader(kLen1);
mock_quic_data_.AddRead(
ASYNC, ConstructServerDataPacket(2, header + std::string(kMsg1, kLen1)));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructAckPacket(packet_number++, 2, 1));
std::string header2 = ConstructDataHeader(kLen33);
mock_quic_data_.AddRead(ASYNC, ConstructServerDataPacket(
3, header2 + std::string(kMsg33, kLen33)));
mock_quic_data_.AddReadPauseForever();
mock_quic_data_.AddWrite(
SYNCHRONOUS, ConstructAckAndRstPacket(packet_number++,
quic::QUIC_STREAM_CANCELLED, 3, 3));
InitializeSession();
InitializeClientSocket();
AssertConnectSucceeds();
// The next 2 reads are consumed and buffered.
ResumeAndRun();
AssertSyncReadEquals(kMsg1, kLen1);
// The payload from the single large data frame will be read across
// two different reads.
AssertSyncReadEquals(kMsg3, kLen3);
AssertSyncReadEquals(kMsg3, kLen3);
}
TEST_P(QuicProxyClientSocketTest, MultipleReadsFromSameLargeFrame) {
int packet_number = 1;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructSettingsPacket(packet_number++));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructConnectRequestPacket(packet_number++));
mock_quic_data_.AddRead(ASYNC, ConstructServerConnectReplyPacket(1, !kFin));
mock_quic_data_.AddReadPause();
std::string header = ConstructDataHeader(kLen333);
mock_quic_data_.AddRead(
ASYNC,
ConstructServerDataPacket(2, header + std::string(kMsg333, kLen333)));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructAckPacket(packet_number++, 2, 1));
mock_quic_data_.AddReadPauseForever();
mock_quic_data_.AddWrite(
SYNCHRONOUS,
ConstructRstPacket(packet_number++, quic::QUIC_STREAM_CANCELLED));
InitializeSession();
InitializeClientSocket();
AssertConnectSucceeds();
// The next read is consumed and buffered.
ResumeAndRun();
// The payload from the single large data frame will be read across
// two different reads.
AssertSyncReadEquals(kMsg33, kLen33);
// Now attempt to do a read of more data than remains buffered
auto buf = base::MakeRefCounted<IOBufferWithSize>(kLen33);
ASSERT_EQ(kLen3, sock_->Read(buf.get(), kLen33, CompletionOnceCallback()));
ASSERT_EQ(std::string(kMsg3, kLen3), std::string(buf->data(), kLen3));
ASSERT_TRUE(sock_->IsConnected());
}
TEST_P(QuicProxyClientSocketTest, ReadAuthResponseBody) {
int packet_number = 1;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructSettingsPacket(packet_number++));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructConnectRequestPacket(packet_number++));
mock_quic_data_.AddRead(ASYNC,
ConstructServerConnectAuthReplyPacket(1, !kFin));
mock_quic_data_.AddReadPause();
std::string header = ConstructDataHeader(kLen1);
mock_quic_data_.AddRead(
ASYNC, ConstructServerDataPacket(2, header + std::string(kMsg1, kLen1)));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructAckPacket(packet_number++, 2, 1));
std::string header2 = ConstructDataHeader(kLen2);
mock_quic_data_.AddRead(
ASYNC, ConstructServerDataPacket(3, header2 + std::string(kMsg2, kLen2)));
mock_quic_data_.AddReadPauseForever();
mock_quic_data_.AddWrite(
SYNCHRONOUS, ConstructAckAndRstPacket(packet_number++,
quic::QUIC_STREAM_CANCELLED, 3, 3));
InitializeSession();
InitializeClientSocket();
AssertConnectFails(ERR_PROXY_AUTH_REQUESTED);
// The next two reads are consumed and buffered.
ResumeAndRun();
AssertSyncReadEquals(kMsg1, kLen1);
AssertSyncReadEquals(kMsg2, kLen2);
}
TEST_P(QuicProxyClientSocketTest, ReadErrorResponseBody) {
int packet_number = 1;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructSettingsPacket(packet_number++));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructConnectRequestPacket(packet_number++));
mock_quic_data_.AddRead(ASYNC,
ConstructServerConnectErrorReplyPacket(1, !kFin));
std::string header = ConstructDataHeader(kLen1);
mock_quic_data_.AddRead(
SYNCHRONOUS,
ConstructServerDataPacket(2, header + std::string(kMsg1, kLen1)));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructAckPacket(packet_number++, 2, 1));
std::string header2 = ConstructDataHeader(kLen2);
mock_quic_data_.AddRead(
SYNCHRONOUS,
ConstructServerDataPacket(3, header2 + std::string(kMsg2, kLen2)));
mock_quic_data_.AddReadPauseForever();
mock_quic_data_.AddWrite(
SYNCHRONOUS, ConstructAckAndRstPacket(packet_number++,
quic::QUIC_STREAM_CANCELLED, 3, 3));
InitializeSession();
InitializeClientSocket();
AssertConnectFails(ERR_TUNNEL_CONNECTION_FAILED);
}
// ----------- Reads and Writes
TEST_P(QuicProxyClientSocketTest, AsyncReadAroundWrite) {
int write_packet_index = 1;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructSettingsPacket(write_packet_index++));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructConnectRequestPacket(write_packet_index++));
mock_quic_data_.AddRead(ASYNC, ConstructServerConnectReplyPacket(1, !kFin));
mock_quic_data_.AddReadPause();
std::string header = ConstructDataHeader(kLen1);
mock_quic_data_.AddRead(
ASYNC, ConstructServerDataPacket(2, header + std::string(kMsg1, kLen1)));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructAckPacket(write_packet_index++, 2, 1));
std::string header2 = ConstructDataHeader(kLen2);
mock_quic_data_.AddWrite(
SYNCHRONOUS, ConstructDataPacket(write_packet_index++,
{header2 + std::string(kMsg2, kLen2)}));
mock_quic_data_.AddReadPause();
std::string header3 = ConstructDataHeader(kLen3);
mock_quic_data_.AddRead(
ASYNC, ConstructServerDataPacket(3, header3 + std::string(kMsg3, kLen3)));
mock_quic_data_.AddReadPauseForever();
mock_quic_data_.AddWrite(
SYNCHRONOUS, ConstructAckAndRstPacket(write_packet_index++,
quic::QUIC_STREAM_CANCELLED, 3, 3));
InitializeSession();
InitializeClientSocket();
AssertConnectSucceeds();
ResumeAndRun();
AssertSyncReadEquals(kMsg1, kLen1);
AssertReadStarts(kMsg3, kLen3);
// Read should block until after the write succeeds.
AssertSyncWriteSucceeds(kMsg2, kLen2);
ASSERT_FALSE(read_callback_.have_result());
ResumeAndRun();
// Now the read will return.
AssertReadReturns(kMsg3, kLen3);
}
TEST_P(QuicProxyClientSocketTest, AsyncWriteAroundReads) {
int packet_number = 1;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructSettingsPacket(packet_number++));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructConnectRequestPacket(packet_number++));
mock_quic_data_.AddRead(ASYNC, ConstructServerConnectReplyPacket(1, !kFin));
mock_quic_data_.AddReadPause();
std::string header = ConstructDataHeader(kLen1);
mock_quic_data_.AddRead(
ASYNC, ConstructServerDataPacket(2, header + std::string(kMsg1, kLen1)));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructAckPacket(packet_number++, 2, 1));
mock_quic_data_.AddReadPause();
std::string header2 = ConstructDataHeader(kLen3);
mock_quic_data_.AddRead(
ASYNC, ConstructServerDataPacket(3, header2 + std::string(kMsg3, kLen3)));
mock_quic_data_.AddReadPauseForever();
mock_quic_data_.AddWritePause();
std::string header3 = ConstructDataHeader(kLen2);
mock_quic_data_.AddWrite(
ASYNC, ConstructDataPacket(packet_number++,
{header3 + std::string(kMsg2, kLen2)}));
mock_quic_data_.AddWrite(
ASYNC, ConstructAckAndDataPacket(packet_number++, 3, 3,
header3 + std::string(kMsg2, kLen2)));
mock_quic_data_.AddWrite(
SYNCHRONOUS,
ConstructRstPacket(packet_number++, quic::QUIC_STREAM_CANCELLED));
InitializeSession();
InitializeClientSocket();
AssertConnectSucceeds();
ResumeAndRun();
AssertSyncReadEquals(kMsg1, kLen1);
// Write should block until the next read completes.
// QuicChromiumClientStream::Handle::WriteStreamData() will only be
// asynchronous starting with the second time it's called while the UDP socket
// is write-blocked. Therefore, at least two writes need to be called on
// |sock_| to get an asynchronous one.
AssertWriteReturns(kMsg2, kLen2, kLen2);
AssertWriteReturns(kMsg2, kLen2, ERR_IO_PENDING);
AssertAsyncReadEquals(kMsg3, kLen3);
ASSERT_FALSE(write_callback_.have_result());
// Now the write will complete
ResumeAndRun();
EXPECT_EQ(kLen2, write_callback_.WaitForResult());
}
// ----------- Reading/Writing on Closed socket
// Reading from an already closed socket should return 0
TEST_P(QuicProxyClientSocketTest, ReadOnClosedSocketReturnsZero) {
int packet_number = 1;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructSettingsPacket(packet_number++));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructConnectRequestPacket(packet_number++));
mock_quic_data_.AddRead(ASYNC, ConstructServerConnectReplyPacket(1, !kFin));
mock_quic_data_.AddReadPause();
mock_quic_data_.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
InitializeSession();
InitializeClientSocket();
AssertConnectSucceeds();
ResumeAndRun();
ASSERT_FALSE(sock_->IsConnected());
ASSERT_EQ(0, sock_->Read(nullptr, 1, CompletionOnceCallback()));
ASSERT_EQ(0, sock_->Read(nullptr, 1, CompletionOnceCallback()));
ASSERT_EQ(0, sock_->Read(nullptr, 1, CompletionOnceCallback()));
ASSERT_FALSE(sock_->IsConnectedAndIdle());
}
// Read pending when socket is closed should return 0
TEST_P(QuicProxyClientSocketTest, PendingReadOnCloseReturnsZero) {
int packet_number = 1;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructSettingsPacket(packet_number++));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructConnectRequestPacket(packet_number++));
mock_quic_data_.AddRead(ASYNC, ConstructServerConnectReplyPacket(1, !kFin));
mock_quic_data_.AddReadPause();
mock_quic_data_.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
InitializeSession();
InitializeClientSocket();
AssertConnectSucceeds();
AssertReadStarts(kMsg1, kLen1);
ResumeAndRun();
ASSERT_EQ(0, read_callback_.WaitForResult());
}
// Reading from a disconnected socket is an error
TEST_P(QuicProxyClientSocketTest, ReadOnDisconnectSocketReturnsNotConnected) {
int packet_number = 1;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructSettingsPacket(packet_number++));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructConnectRequestPacket(packet_number++));
mock_quic_data_.AddRead(ASYNC, ConstructServerConnectReplyPacket(1, !kFin));
mock_quic_data_.AddReadPauseForever();
mock_quic_data_.AddWrite(
SYNCHRONOUS, ConstructAckAndRstPacket(packet_number++,
quic::QUIC_STREAM_CANCELLED, 1, 1));
InitializeSession();
InitializeClientSocket();
AssertConnectSucceeds();
sock_->Disconnect();
ASSERT_EQ(ERR_SOCKET_NOT_CONNECTED,
sock_->Read(nullptr, 1, CompletionOnceCallback()));
}
// Reading data after receiving FIN should return buffered data received before
// FIN, then 0.
TEST_P(QuicProxyClientSocketTest, ReadAfterFinReceivedReturnsBufferedData) {
int packet_number = 1;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructSettingsPacket(packet_number++));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructConnectRequestPacket(packet_number++));
mock_quic_data_.AddRead(ASYNC, ConstructServerConnectReplyPacket(1, !kFin));
mock_quic_data_.AddReadPause();
std::string header = ConstructDataHeader(kLen1);
mock_quic_data_.AddRead(ASYNC, ConstructServerDataFinPacket(
2, header + std::string(kMsg1, kLen1)));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructAckPacket(packet_number++, 2, 1));
mock_quic_data_.AddReadPauseForever();
mock_quic_data_.AddWrite(
SYNCHRONOUS,
ConstructRstPacket(packet_number++, quic::QUIC_STREAM_CANCELLED));
InitializeSession();
InitializeClientSocket();
AssertConnectSucceeds();
ResumeAndRun();
AssertSyncReadEquals(kMsg1, kLen1);
ASSERT_EQ(0, sock_->Read(nullptr, 1, CompletionOnceCallback()));
ASSERT_EQ(0, sock_->Read(nullptr, 1, CompletionOnceCallback()));
sock_->Disconnect();
ASSERT_EQ(ERR_SOCKET_NOT_CONNECTED,
sock_->Read(nullptr, 1, CompletionOnceCallback()));
}
// Calling Write() on a closed socket is an error.
TEST_P(QuicProxyClientSocketTest, WriteOnClosedStream) {
int packet_number = 1;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructSettingsPacket(packet_number++));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructConnectRequestPacket(packet_number++));
mock_quic_data_.AddRead(ASYNC, ConstructServerConnectReplyPacket(1, !kFin));
mock_quic_data_.AddReadPause();
mock_quic_data_.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
InitializeSession();
InitializeClientSocket();
AssertConnectSucceeds();
ResumeAndRun();
AssertWriteReturns(kMsg1, kLen1, ERR_QUIC_PROTOCOL_ERROR);
}
// Calling Write() on a disconnected socket is an error.
TEST_P(QuicProxyClientSocketTest, WriteOnDisconnectedSocket) {
int packet_number = 1;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructSettingsPacket(packet_number++));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructConnectRequestPacket(packet_number++));
mock_quic_data_.AddRead(ASYNC, ConstructServerConnectReplyPacket(1, !kFin));
mock_quic_data_.AddReadPauseForever();
mock_quic_data_.AddWrite(
SYNCHRONOUS, ConstructAckAndRstPacket(packet_number++,
quic::QUIC_STREAM_CANCELLED, 1, 1));
InitializeSession();
InitializeClientSocket();
AssertConnectSucceeds();
sock_->Disconnect();
AssertWriteReturns(kMsg1, kLen1, ERR_SOCKET_NOT_CONNECTED);
}
// If the socket is closed with a pending Write(), the callback should be called
// with the same error the session was closed with.
TEST_P(QuicProxyClientSocketTest, WritePendingOnClose) {
int packet_number = 1;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructSettingsPacket(packet_number++));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructConnectRequestPacket(packet_number++));
mock_quic_data_.AddRead(ASYNC, ConstructServerConnectReplyPacket(1, !kFin));
mock_quic_data_.AddReadPauseForever();
mock_quic_data_.AddWrite(SYNCHRONOUS, ERR_IO_PENDING);
InitializeSession();
InitializeClientSocket();
AssertConnectSucceeds();
// QuicChromiumClientStream::Handle::WriteStreamData() will only be
// asynchronous starting with the second time it's called while the UDP socket
// is write-blocked. Therefore, at least two writes need to be called on
// |sock_| to get an asynchronous one.
AssertWriteReturns(kMsg1, kLen1, kLen1);
// This second write will be async. This is the pending write that's being
// tested.
AssertWriteReturns(kMsg1, kLen1, ERR_IO_PENDING);
// Make sure the write actually starts.
base::RunLoop().RunUntilIdle();
session_->CloseSessionOnError(ERR_CONNECTION_CLOSED,
quic::QUIC_INTERNAL_ERROR,
quic::ConnectionCloseBehavior::SILENT_CLOSE);
EXPECT_THAT(write_callback_.WaitForResult(), IsError(ERR_CONNECTION_CLOSED));
}
TEST_P(QuicProxyClientSocketTest, DisconnectWithWritePending) {
int packet_number = 1;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructSettingsPacket(packet_number++));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructConnectRequestPacket(packet_number++));
mock_quic_data_.AddRead(ASYNC, ConstructServerConnectReplyPacket(1, !kFin));
mock_quic_data_.AddReadPauseForever();
mock_quic_data_.AddWrite(SYNCHRONOUS, ERR_IO_PENDING);
InitializeSession();
InitializeClientSocket();
AssertConnectSucceeds();
// QuicChromiumClientStream::Handle::WriteStreamData() will only be
// asynchronous starting with the second time it's called while the UDP socket
// is write-blocked. Therefore, at least two writes need to be called on
// |sock_| to get an asynchronous one.
AssertWriteReturns(kMsg1, kLen1, kLen1);
// This second write will be async. This is the pending write that's being
// tested.
AssertWriteReturns(kMsg1, kLen1, ERR_IO_PENDING);
// Make sure the write actually starts.
base::RunLoop().RunUntilIdle();
sock_->Disconnect();
EXPECT_FALSE(sock_->IsConnected());
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(sock_->IsConnected());
EXPECT_FALSE(write_callback_.have_result());
}
// If the socket is Disconnected with a pending Read(), the callback
// should not be called.
TEST_P(QuicProxyClientSocketTest, DisconnectWithReadPending) {
int packet_number = 1;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructSettingsPacket(packet_number++));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructConnectRequestPacket(packet_number++));
mock_quic_data_.AddRead(ASYNC, ConstructServerConnectReplyPacket(1, !kFin));
mock_quic_data_.AddReadPauseForever();
mock_quic_data_.AddWrite(
SYNCHRONOUS, ConstructAckAndRstPacket(packet_number++,
quic::QUIC_STREAM_CANCELLED, 1, 1));
InitializeSession();
InitializeClientSocket();
AssertConnectSucceeds();
EXPECT_TRUE(sock_->IsConnected());
AssertReadStarts(kMsg1, kLen1);
sock_->Disconnect();
EXPECT_FALSE(sock_->IsConnected());
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(sock_->IsConnected());
EXPECT_FALSE(read_callback_.have_result());
}
// If the socket is Reset when both a read and write are pending,
// both should be called back.
TEST_P(QuicProxyClientSocketTest, RstWithReadAndWritePending) {
int packet_number = 1;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructSettingsPacket(packet_number++));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructConnectRequestPacket(packet_number++));
mock_quic_data_.AddRead(ASYNC, ConstructServerConnectReplyPacket(1, !kFin));
mock_quic_data_.AddReadPause();
mock_quic_data_.AddRead(
ASYNC, ConstructServerRstPacket(2, quic::QUIC_STREAM_CANCELLED));
mock_quic_data_.AddReadPauseForever();
std::string header = ConstructDataHeader(kLen2);
mock_quic_data_.AddWrite(
ASYNC, ConstructAckAndDataPacket(packet_number++, 1, 1,
{header + std::string(kMsg2, kLen2)}));
mock_quic_data_.AddWrite(
SYNCHRONOUS, ConstructAckAndRstOnlyPacket(
packet_number++, quic::QUIC_STREAM_CANCELLED, 2, 2));
InitializeSession();
InitializeClientSocket();
AssertConnectSucceeds();
EXPECT_TRUE(sock_->IsConnected());
AssertReadStarts(kMsg1, kLen1);
// Write should block until the next read completes.
// QuicChromiumClientStream::Handle::WriteStreamData() will only be
// asynchronous starting with the second time it's called while the UDP socket
// is write-blocked. Therefore, at least two writes need to be called on
// |sock_| to get an asynchronous one.
AssertWriteReturns(kMsg2, kLen2, kLen2);
AssertWriteReturns(kMsg2, kLen2, ERR_IO_PENDING);
ResumeAndRun();
EXPECT_TRUE(read_callback_.have_result());
EXPECT_TRUE(write_callback_.have_result());
}
// Makes sure the proxy client socket's source gets the expected NetLog events
// and only the expected NetLog events (No SpdySession events).
TEST_P(QuicProxyClientSocketTest, NetLog) {
int packet_number = 1;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructSettingsPacket(packet_number++));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructConnectRequestPacket(packet_number++));
mock_quic_data_.AddRead(ASYNC, ConstructServerConnectReplyPacket(1, !kFin));
mock_quic_data_.AddReadPause();
std::string header = ConstructDataHeader(kLen1);
mock_quic_data_.AddRead(
ASYNC, ConstructServerDataPacket(2, header + std::string(kMsg1, kLen1)));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructAckPacket(packet_number++, 2, 1));
mock_quic_data_.AddReadPauseForever();
mock_quic_data_.AddWrite(
SYNCHRONOUS,
ConstructRstPacket(packet_number++, quic::QUIC_STREAM_CANCELLED));
InitializeSession();
InitializeClientSocket();
AssertConnectSucceeds();
ResumeAndRun();
AssertSyncReadEquals(kMsg1, kLen1);
NetLogSource sock_source = sock_->NetLog().source();
sock_.reset();
auto entry_list = net_log_observer_.GetEntriesForSource(sock_source);
ASSERT_EQ(entry_list.size(), 10u);
EXPECT_TRUE(
LogContainsBeginEvent(entry_list, 0, NetLogEventType::SOCKET_ALIVE));
EXPECT_TRUE(LogContainsEvent(entry_list, 1,
NetLogEventType::HTTP2_PROXY_CLIENT_SESSION,
NetLogEventPhase::NONE));
EXPECT_TRUE(LogContainsBeginEvent(
entry_list, 2, NetLogEventType::HTTP_TRANSACTION_TUNNEL_SEND_REQUEST));
EXPECT_TRUE(LogContainsEvent(
entry_list, 3, NetLogEventType::HTTP_TRANSACTION_SEND_TUNNEL_HEADERS,
NetLogEventPhase::NONE));
EXPECT_TRUE(LogContainsEndEvent(
entry_list, 4, NetLogEventType::HTTP_TRANSACTION_TUNNEL_SEND_REQUEST));
EXPECT_TRUE(LogContainsBeginEvent(
entry_list, 5, NetLogEventType::HTTP_TRANSACTION_TUNNEL_READ_HEADERS));
EXPECT_TRUE(LogContainsEvent(
entry_list, 6,
NetLogEventType::HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS,
NetLogEventPhase::NONE));
EXPECT_TRUE(LogContainsEndEvent(
entry_list, 7, NetLogEventType::HTTP_TRANSACTION_TUNNEL_READ_HEADERS));
EXPECT_TRUE(LogContainsEvent(entry_list, 8,
NetLogEventType::SOCKET_BYTES_RECEIVED,
NetLogEventPhase::NONE));
EXPECT_TRUE(
LogContainsEndEvent(entry_list, 9, NetLogEventType::SOCKET_ALIVE));
}
// A helper class that will delete |sock| when the callback is invoked.
class DeleteSockCallback : public TestCompletionCallbackBase {
public:
explicit DeleteSockCallback(std::unique_ptr<QuicProxyClientSocket>* sock)
: sock_(sock) {}
DeleteSockCallback(const DeleteSockCallback&) = delete;
DeleteSockCallback& operator=(const DeleteSockCallback&) = delete;
~DeleteSockCallback() override = default;
CompletionOnceCallback callback() {
return base::BindOnce(&DeleteSockCallback::OnComplete,
base::Unretained(this));
}
private:
void OnComplete(int result) {
sock_->reset(nullptr);
SetResult(result);
}
raw_ptr<std::unique_ptr<QuicProxyClientSocket>> sock_;
};
// If the socket is reset when both a read and write are pending, and the
// read callback causes the socket to be deleted, the write callback should
// not be called.
TEST_P(QuicProxyClientSocketTest, RstWithReadAndWritePendingDelete) {
int packet_number = 1;
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructSettingsPacket(packet_number++));
mock_quic_data_.AddWrite(SYNCHRONOUS,
ConstructConnectRequestPacket(packet_number++));
mock_quic_data_.AddRead(ASYNC, ConstructServerConnectReplyPacket(1, !kFin));
mock_quic_data_.AddReadPause();
mock_quic_data_.AddRead(
ASYNC, ConstructServerRstPacket(2, quic::QUIC_STREAM_CANCELLED));
mock_quic_data_.AddReadPauseForever();
std::string header = ConstructDataHeader(kLen1);
mock_quic_data_.AddWrite(
ASYNC, ConstructAckAndDataPacket(packet_number++, 1, 1,
{header + std::string(kMsg1, kLen1)}));
mock_quic_data_.AddWrite(
SYNCHRONOUS, ConstructAckAndRstOnlyPacket(
packet_number++, quic::QUIC_STREAM_CANCELLED, 2, 2));
InitializeSession();
InitializeClientSocket();
AssertConnectSucceeds();
EXPECT_TRUE(sock_->IsConnected());
DeleteSockCallback read_callback(&sock_);
auto read_buf = base::MakeRefCounted<IOBufferWithSize>(kLen1);
ASSERT_EQ(ERR_IO_PENDING,
sock_->Read(read_buf.get(), kLen1, read_callback.callback()));
// QuicChromiumClientStream::Handle::WriteStreamData() will only be
// asynchronous starting with the second time it's called while the UDP socket
// is write-blocked. Therefore, at least two writes need to be called on
// |sock_| to get an asynchronous one.
AssertWriteReturns(kMsg1, kLen1, kLen1);
AssertWriteReturns(kMsg1, kLen1, ERR_IO_PENDING);
ResumeAndRun();
EXPECT_FALSE(sock_.get());
EXPECT_EQ(0, read_callback.WaitForResult());
EXPECT_FALSE(write_callback_.have_result());
}
INSTANTIATE_TEST_SUITE_P(VersionIncludeStreamDependencySequence,
QuicProxyClientSocketTest,
::testing::ValuesIn(AllSupportedQuicVersions()),
::testing::PrintToStringParamName());
} // namespace net::test