blob: 23e442287546ad57ca41c1bd0d75cf5a73b07699 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <cstddef>
#include <cstdint>
#include <list>
#include <memory>
#include <ostream>
#include <string>
#include <utility>
#include <vector>
#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h"
#include "net/third_party/quiche/src/quic/core/http/http_constants.h"
#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h"
#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.h"
#include "net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h"
#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
#include "net/third_party/quiche/src/quic/core/quic_framer.h"
#include "net/third_party/quiche/src/quic/core/quic_packet_creator.h"
#include "net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.h"
#include "net/third_party/quiche/src/quic/core/quic_packets.h"
#include "net/third_party/quiche/src/quic/core/quic_session.h"
#include "net/third_party/quiche/src/quic/core/quic_utils.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_epoll.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_error_code_wrappers.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_port_utils.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_sleep.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_test_loopback.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
#include "net/quic/platform/impl/quic_socket_utils.h"
#include "net/third_party/quiche/src/quic/test_tools/bad_packet_writer.h"
#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
#include "net/third_party/quiche/src/quic/test_tools/packet_dropping_test_writer.h"
#include "net/third_party/quiche/src/quic/test_tools/packet_reordering_writer.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_client_peer.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_dispatcher_peer.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_flow_controller_peer.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_sent_packet_manager_peer.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_server_peer.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_session_peer.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_stream_id_manager_peer.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_peer.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_test_client.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_test_server.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
#include "net/third_party/quiche/src/quic/test_tools/server_thread.h"
#include "net/third_party/quiche/src/quic/tools/quic_backend_response.h"
#include "net/third_party/quiche/src/quic/tools/quic_client.h"
#include "net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h"
#include "net/third_party/quiche/src/quic/tools/quic_server.h"
#include "net/third_party/quiche/src/quic/tools/quic_simple_client_stream.h"
#include "net/third_party/quiche/src/quic/tools/quic_simple_server_stream.h"
using spdy::kV3LowestPriority;
using spdy::SETTINGS_MAX_HEADER_LIST_SIZE;
using spdy::SpdyFramer;
using spdy::SpdyHeaderBlock;
using spdy::SpdySerializedFrame;
using spdy::SpdySettingsIR;
namespace quic {
namespace test {
namespace {
const char kFooResponseBody[] = "Artichoke hearts make me happy.";
const char kBarResponseBody[] = "Palm hearts are pretty delicious, also.";
const float kSessionToStreamRatio = 1.5;
// Run all tests with the cross products of all versions.
struct TestParams {
TestParams(const ParsedQuicVersionVector& client_supported_versions,
const ParsedQuicVersionVector& server_supported_versions,
ParsedQuicVersion negotiated_version,
QuicTag congestion_control_tag)
: client_supported_versions(client_supported_versions),
server_supported_versions(server_supported_versions),
negotiated_version(negotiated_version),
congestion_control_tag(congestion_control_tag) {}
friend std::ostream& operator<<(std::ostream& os, const TestParams& p) {
os << "{ server_supported_versions: "
<< ParsedQuicVersionVectorToString(p.server_supported_versions);
os << " client_supported_versions: "
<< ParsedQuicVersionVectorToString(p.client_supported_versions);
os << " negotiated_version: "
<< ParsedQuicVersionToString(p.negotiated_version);
os << " congestion_control_tag: "
<< QuicTagToString(p.congestion_control_tag) << " }";
return os;
}
ParsedQuicVersionVector client_supported_versions;
ParsedQuicVersionVector server_supported_versions;
ParsedQuicVersion negotiated_version;
QuicTag congestion_control_tag;
};
// Constructs various test permutations.
std::vector<TestParams> GetTestParams(bool use_tls_handshake) {
QuicFlagSaver flags;
// Divide the versions into buckets in which the intra-frame format
// is compatible. When clients encounter QUIC version negotiation
// they simply retransmit all packets using the new version's
// QUIC framing. However, they are unable to change the intra-frame
// layout (for example to change HTTP/2 headers to SPDY/3, or a change in the
// handshake protocol). So these tests need to ensure that clients are never
// attempting to do 0-RTT across incompatible versions. Chromium only
// supports a single version at a time anyway. :)
SetQuicFlag(FLAGS_quic_supports_tls_handshake, use_tls_handshake);
ParsedQuicVersionVector all_supported_versions =
FilterSupportedVersions(AllSupportedVersions());
// Buckets are separated by versions: versions prior to QUIC_VERSION_47 use
// STREAM frames for the handshake, and only have QUIC crypto as the handshake
// protocol. Version 47 and greater use CRYPTO frames for the handshake, and
// must also be split based on the handshake protocol. If the handshake
// protocol (QUIC crypto or TLS) changes, the ClientHello/CHLO must be
// reconstructed for the correct protocol.
ParsedQuicVersionVector version_buckets[3];
for (const ParsedQuicVersion& version : all_supported_versions) {
if (!QuicVersionUsesCryptoFrames(version.transport_version)) {
version_buckets[0].push_back(version);
} else if (version.handshake_protocol == PROTOCOL_QUIC_CRYPTO) {
version_buckets[1].push_back(version);
} else {
version_buckets[2].push_back(version);
}
}
std::vector<TestParams> params;
for (const QuicTag congestion_control_tag :
{kRENO, kTBBR, kQBIC, kTPCC, kB2ON}) {
if (!GetQuicReloadableFlag(quic_allow_client_enabled_bbr_v2) &&
congestion_control_tag == kB2ON) {
continue;
}
for (const ParsedQuicVersionVector& client_versions : version_buckets) {
if (FilterSupportedVersions(client_versions).empty()) {
continue;
}
// Add an entry for server and client supporting all versions.
params.push_back(TestParams(client_versions, all_supported_versions,
client_versions.front(),
congestion_control_tag));
// Test client supporting all versions and server supporting
// 1 version. Simulate an old server and exercise version
// downgrade in the client. Protocol negotiation should
// occur. Skip the i = 0 case because it is essentially the
// same as the default case.
for (size_t i = 1; i < client_versions.size(); ++i) {
ParsedQuicVersionVector server_supported_versions;
server_supported_versions.push_back(client_versions[i]);
if (FilterSupportedVersions(server_supported_versions).empty()) {
continue;
}
params.push_back(TestParams(client_versions, server_supported_versions,
server_supported_versions.front(),
congestion_control_tag));
} // End of inner version loop.
} // End of outer version loop.
} // End of congestion_control_tag loop.
return params;
}
void WriteHeadersOnStream(QuicSpdyStream* stream) {
// Since QuicSpdyStream uses QuicHeaderList::empty() to detect too large
// headers, it also fails when receiving empty headers.
SpdyHeaderBlock headers;
headers["foo"] = "bar";
stream->WriteHeaders(std::move(headers), /* fin = */ false, nullptr);
}
class ServerDelegate : public PacketDroppingTestWriter::Delegate {
public:
explicit ServerDelegate(QuicDispatcher* dispatcher)
: dispatcher_(dispatcher) {}
~ServerDelegate() override = default;
void OnCanWrite() override { dispatcher_->OnCanWrite(); }
private:
QuicDispatcher* dispatcher_;
};
class ClientDelegate : public PacketDroppingTestWriter::Delegate {
public:
explicit ClientDelegate(QuicClient* client) : client_(client) {}
~ClientDelegate() override = default;
void OnCanWrite() override {
QuicEpollEvent event(EPOLLOUT);
client_->epoll_network_helper()->OnEvent(client_->GetLatestFD(), &event);
}
private:
QuicClient* client_;
};
class EndToEndTest : public QuicTestWithParam<TestParams> {
protected:
EndToEndTest()
: initialized_(false),
connect_to_server_on_initialize_(true),
server_address_(
QuicSocketAddress(TestLoopback(), QuicPickUnusedPortOrDie())),
server_hostname_("test.example.com"),
client_writer_(nullptr),
server_writer_(nullptr),
negotiated_version_(UnsupportedQuicVersion()),
chlo_multiplier_(0),
stream_factory_(nullptr),
support_server_push_(false),
override_server_connection_id_(nullptr),
override_client_connection_id_(nullptr),
expected_server_connection_id_length_(kQuicDefaultConnectionIdLength) {
SetQuicFlag(FLAGS_quic_supports_tls_handshake, true);
client_supported_versions_ = GetParam().client_supported_versions;
server_supported_versions_ = GetParam().server_supported_versions;
negotiated_version_ = GetParam().negotiated_version;
QUIC_LOG(INFO) << "Using Configuration: " << GetParam();
// Use different flow control windows for client/server.
client_config_.SetInitialStreamFlowControlWindowToSend(
2 * kInitialStreamFlowControlWindowForTest);
client_config_.SetInitialSessionFlowControlWindowToSend(
2 * kInitialSessionFlowControlWindowForTest);
server_config_.SetInitialStreamFlowControlWindowToSend(
3 * kInitialStreamFlowControlWindowForTest);
server_config_.SetInitialSessionFlowControlWindowToSend(
3 * kInitialSessionFlowControlWindowForTest);
// The default idle timeouts can be too strict when running on a busy
// machine.
const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(30);
client_config_.set_max_time_before_crypto_handshake(timeout);
client_config_.set_max_idle_time_before_crypto_handshake(timeout);
server_config_.set_max_time_before_crypto_handshake(timeout);
server_config_.set_max_idle_time_before_crypto_handshake(timeout);
AddToCache("/foo", 200, kFooResponseBody);
AddToCache("/bar", 200, kBarResponseBody);
}
~EndToEndTest() override { QuicRecyclePort(server_address_.port()); }
virtual void CreateClientWithWriter() {
client_.reset(CreateQuicClient(client_writer_));
}
QuicTestClient* CreateQuicClient(QuicPacketWriterWrapper* writer) {
QuicTestClient* client =
new QuicTestClient(server_address_, server_hostname_, client_config_,
client_supported_versions_,
crypto_test_utils::ProofVerifierForTesting());
client->UseWriter(writer);
if (!pre_shared_key_client_.empty()) {
client->client()->SetPreSharedKey(pre_shared_key_client_);
}
if (override_server_connection_id_ != nullptr) {
client->UseConnectionId(*override_server_connection_id_);
}
if (override_client_connection_id_ != nullptr) {
client->UseClientConnectionId(*override_client_connection_id_);
}
client->Connect();
return client;
}
void set_smaller_flow_control_receive_window() {
const uint32_t kClientIFCW = 64 * 1024;
const uint32_t kServerIFCW = 1024 * 1024;
set_client_initial_stream_flow_control_receive_window(kClientIFCW);
set_client_initial_session_flow_control_receive_window(
kSessionToStreamRatio * kClientIFCW);
set_server_initial_stream_flow_control_receive_window(kServerIFCW);
set_server_initial_session_flow_control_receive_window(
kSessionToStreamRatio * kServerIFCW);
}
void set_client_initial_stream_flow_control_receive_window(uint32_t window) {
CHECK(client_ == nullptr);
QUIC_DLOG(INFO) << "Setting client initial stream flow control window: "
<< window;
client_config_.SetInitialStreamFlowControlWindowToSend(window);
}
void set_client_initial_session_flow_control_receive_window(uint32_t window) {
CHECK(client_ == nullptr);
QUIC_DLOG(INFO) << "Setting client initial session flow control window: "
<< window;
client_config_.SetInitialSessionFlowControlWindowToSend(window);
}
void set_server_initial_stream_flow_control_receive_window(uint32_t window) {
CHECK(server_thread_ == nullptr);
QUIC_DLOG(INFO) << "Setting server initial stream flow control window: "
<< window;
server_config_.SetInitialStreamFlowControlWindowToSend(window);
}
void set_server_initial_session_flow_control_receive_window(uint32_t window) {
CHECK(server_thread_ == nullptr);
QUIC_DLOG(INFO) << "Setting server initial session flow control window: "
<< window;
server_config_.SetInitialSessionFlowControlWindowToSend(window);
}
const QuicSentPacketManager* GetSentPacketManagerFromFirstServerSession() {
return &GetServerConnection()->sent_packet_manager();
}
QuicConnection* GetServerConnection() {
return GetServerSession()->connection();
}
QuicSession* GetServerSession() {
QuicDispatcher* dispatcher =
QuicServerPeer::GetDispatcher(server_thread_->server());
EXPECT_EQ(1u, dispatcher->session_map().size());
return dispatcher->session_map().begin()->second.get();
}
bool Initialize() {
QuicTagVector copt;
server_config_.SetConnectionOptionsToSend(copt);
copt = client_extra_copts_;
// TODO(nimia): Consider setting the congestion control algorithm for the
// client as well according to the test parameter.
copt.push_back(GetParam().congestion_control_tag);
if (GetParam().congestion_control_tag == kTPCC &&
GetQuicReloadableFlag(quic_enable_pcc3)) {
copt.push_back(kTPCC);
}
client_config_.SetConnectionOptionsToSend(copt);
// Start the server first, because CreateQuicClient() attempts
// to connect to the server.
StartServer();
if (!connect_to_server_on_initialize_) {
initialized_ = true;
return true;
}
CreateClientWithWriter();
static QuicEpollEvent event(EPOLLOUT);
if (client_writer_ != nullptr) {
client_writer_->Initialize(
QuicConnectionPeer::GetHelper(
client_->client()->client_session()->connection()),
QuicConnectionPeer::GetAlarmFactory(
client_->client()->client_session()->connection()),
QuicMakeUnique<ClientDelegate>(client_->client()));
}
initialized_ = true;
return client_->client()->connected();
}
void SetUp() override {
// The ownership of these gets transferred to the QuicPacketWriterWrapper
// when Initialize() is executed.
client_writer_ = new PacketDroppingTestWriter();
server_writer_ = new PacketDroppingTestWriter();
}
void TearDown() override {
ASSERT_TRUE(initialized_) << "You must call Initialize() in every test "
<< "case. Otherwise, your test will leak memory.";
StopServer();
}
void StartServer() {
auto* test_server = new QuicTestServer(
crypto_test_utils::ProofSourceForTesting(), server_config_,
server_supported_versions_, &memory_cache_backend_,
expected_server_connection_id_length_);
server_thread_ = QuicMakeUnique<ServerThread>(test_server, server_address_);
if (chlo_multiplier_ != 0) {
server_thread_->server()->SetChloMultiplier(chlo_multiplier_);
}
if (!pre_shared_key_server_.empty()) {
server_thread_->server()->SetPreSharedKey(pre_shared_key_server_);
}
server_thread_->Initialize();
QuicDispatcher* dispatcher =
QuicServerPeer::GetDispatcher(server_thread_->server());
QuicDispatcherPeer::UseWriter(dispatcher, server_writer_);
server_writer_->Initialize(QuicDispatcherPeer::GetHelper(dispatcher),
QuicDispatcherPeer::GetAlarmFactory(dispatcher),
QuicMakeUnique<ServerDelegate>(dispatcher));
if (stream_factory_ != nullptr) {
static_cast<QuicTestServer*>(server_thread_->server())
->SetSpdyStreamFactory(stream_factory_);
}
server_thread_->Start();
}
void StopServer() {
if (server_thread_) {
server_thread_->Quit();
server_thread_->Join();
}
}
void AddToCache(QuicStringPiece path,
int response_code,
QuicStringPiece body) {
memory_cache_backend_.AddSimpleResponse(server_hostname_, path,
response_code, body);
}
void SetPacketLossPercentage(int32_t loss) {
client_writer_->set_fake_packet_loss_percentage(loss);
server_writer_->set_fake_packet_loss_percentage(loss);
}
void SetPacketSendDelay(QuicTime::Delta delay) {
client_writer_->set_fake_packet_delay(delay);
server_writer_->set_fake_packet_delay(delay);
}
void SetReorderPercentage(int32_t reorder) {
client_writer_->set_fake_reorder_percentage(reorder);
server_writer_->set_fake_reorder_percentage(reorder);
}
// Verifies that the client and server connections were both free of packets
// being discarded, based on connection stats.
// Calls server_thread_ Pause() and Resume(), which may only be called once
// per test.
void VerifyCleanConnection(bool had_packet_loss) {
QuicConnectionStats client_stats =
client_->client()->client_session()->connection()->GetStats();
// TODO(ianswett): Determine why this becomes even more flaky with BBR
// enabled. b/62141144
if (!had_packet_loss && !GetQuicReloadableFlag(quic_default_to_bbr)) {
EXPECT_EQ(0u, client_stats.packets_lost);
}
EXPECT_EQ(0u, client_stats.packets_discarded);
// When client starts with an unsupported version, the version negotiation
// packet sent by server for the old connection (respond for the connection
// close packet) will be dropped by the client.
if (!ServerSendsVersionNegotiation()) {
EXPECT_EQ(0u, client_stats.packets_dropped);
}
if (!ClientSupportsIetfQuicNotSupportedByServer()) {
// In this case, if client sends 0-RTT POST with v99, receives IETF
// version negotiation packet and speaks a GQUIC version. Server processes
// this connection in time wait list and keeps sending IETF version
// negotiation packet for incoming packets. But these version negotiation
// packets cannot be processed by the client speaking GQUIC.
EXPECT_EQ(client_stats.packets_received, client_stats.packets_processed);
}
server_thread_->Pause();
QuicConnectionStats server_stats = GetServerConnection()->GetStats();
if (!had_packet_loss) {
EXPECT_EQ(0u, server_stats.packets_lost);
}
EXPECT_EQ(0u, server_stats.packets_discarded);
// TODO(ianswett): Restore the check for packets_dropped equals 0.
// The expect for packets received is equal to packets processed fails
// due to version negotiation packets.
server_thread_->Resume();
}
// Client supports IETF QUIC, while it is not supported by server.
bool ClientSupportsIetfQuicNotSupportedByServer() {
return VersionHasIetfInvariantHeader(
client_supported_versions_[0].transport_version) &&
!VersionHasIetfInvariantHeader(
FilterSupportedVersions(GetParam().server_supported_versions)[0]
.transport_version);
}
// Returns true when client starts with an unsupported version, and client
// closes connection when version negotiation is received.
bool ServerSendsVersionNegotiation() {
return client_supported_versions_[0] != GetParam().negotiated_version;
}
bool SupportsIetfQuicWithTls(ParsedQuicVersion version) {
return VersionHasIetfInvariantHeader(version.transport_version) &&
version.handshake_protocol == PROTOCOL_TLS1_3;
}
void ExpectFlowControlsSynced(QuicFlowController* client,
QuicFlowController* server) {
EXPECT_EQ(QuicFlowControllerPeer::SendWindowSize(client),
QuicFlowControllerPeer::ReceiveWindowSize(server));
EXPECT_EQ(QuicFlowControllerPeer::ReceiveWindowSize(client),
QuicFlowControllerPeer::SendWindowSize(server));
}
// Must be called before Initialize to have effect.
void SetSpdyStreamFactory(QuicTestServer::StreamFactory* factory) {
stream_factory_ = factory;
}
QuicStreamId GetNthClientInitiatedBidirectionalId(int n) {
return GetNthClientInitiatedBidirectionalStreamId(
client_->client()->client_session()->connection()->transport_version(),
n);
}
QuicStreamId GetNthServerInitiatedBidirectionalId(int n) {
return GetNthServerInitiatedBidirectionalStreamId(
client_->client()->client_session()->connection()->transport_version(),
n);
}
ScopedEnvironmentForThreads environment_;
bool initialized_;
// If true, the Initialize() function will create |client_| and starts to
// connect to the server.
// Default is true.
bool connect_to_server_on_initialize_;
QuicSocketAddress server_address_;
std::string server_hostname_;
QuicMemoryCacheBackend memory_cache_backend_;
std::unique_ptr<ServerThread> server_thread_;
std::unique_ptr<QuicTestClient> client_;
PacketDroppingTestWriter* client_writer_;
PacketDroppingTestWriter* server_writer_;
QuicConfig client_config_;
QuicConfig server_config_;
ParsedQuicVersionVector client_supported_versions_;
ParsedQuicVersionVector server_supported_versions_;
QuicTagVector client_extra_copts_;
ParsedQuicVersion negotiated_version_;
size_t chlo_multiplier_;
QuicTestServer::StreamFactory* stream_factory_;
bool support_server_push_;
std::string pre_shared_key_client_;
std::string pre_shared_key_server_;
QuicConnectionId* override_server_connection_id_;
QuicConnectionId* override_client_connection_id_;
uint8_t expected_server_connection_id_length_;
};
// Run all end to end tests with all supported versions.
INSTANTIATE_TEST_SUITE_P(EndToEndTests,
EndToEndTest,
::testing::ValuesIn(GetTestParams(false)));
class EndToEndTestWithTls : public EndToEndTest {};
INSTANTIATE_TEST_SUITE_P(EndToEndTestsWithTls,
EndToEndTestWithTls,
::testing::ValuesIn(GetTestParams(true)));
TEST_P(EndToEndTestWithTls, HandshakeSuccessful) {
ASSERT_TRUE(Initialize());
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
server_thread_->WaitForCryptoHandshakeConfirmed();
// There have been occasions where it seemed that negotiated_version_ and the
// version in the connection are not in sync. If it is happening, it has not
// been recreatable; this assert is here just to check and raise a flag if it
// happens.
ASSERT_EQ(
client_->client()->client_session()->connection()->transport_version(),
negotiated_version_.transport_version);
QuicCryptoStream* crypto_stream = QuicSessionPeer::GetMutableCryptoStream(
client_->client()->client_session());
QuicStreamSequencer* sequencer = QuicStreamPeer::sequencer(crypto_stream);
EXPECT_FALSE(QuicStreamSequencerPeer::IsUnderlyingBufferAllocated(sequencer));
server_thread_->Pause();
crypto_stream = QuicSessionPeer::GetMutableCryptoStream(GetServerSession());
sequencer = QuicStreamPeer::sequencer(crypto_stream);
EXPECT_FALSE(QuicStreamSequencerPeer::IsUnderlyingBufferAllocated(sequencer));
}
TEST_P(EndToEndTest, SimpleRequestResponse) {
ASSERT_TRUE(Initialize());
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
int expected_num_client_hellos = 2;
if (ServerSendsVersionNegotiation()) {
++expected_num_client_hellos;
}
EXPECT_EQ(expected_num_client_hellos,
client_->client()->GetNumSentClientHellos());
}
TEST_P(EndToEndTestWithTls, SimpleRequestResponse) {
ASSERT_TRUE(Initialize());
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
}
TEST_P(EndToEndTest, SimpleRequestResponseForcedVersionNegotiation) {
SetQuicReloadableFlag(quic_use_parse_public_header, true);
client_supported_versions_.insert(client_supported_versions_.begin(),
QuicVersionReservedForNegotiation());
ASSERT_TRUE(Initialize());
ASSERT_TRUE(ServerSendsVersionNegotiation());
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
EXPECT_EQ(3, client_->client()->GetNumSentClientHellos());
}
TEST_P(EndToEndTestWithTls, ForcedVersionNegotiation) {
SetQuicReloadableFlag(quic_use_parse_public_header, true);
client_supported_versions_.insert(client_supported_versions_.begin(),
QuicVersionReservedForNegotiation());
ASSERT_TRUE(Initialize());
ASSERT_TRUE(ServerSendsVersionNegotiation());
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
}
TEST_P(EndToEndTest, SimpleRequestResponseZeroConnectionID) {
QuicConnectionId connection_id = QuicUtils::CreateZeroConnectionId(
GetParam().negotiated_version.transport_version);
override_server_connection_id_ = &connection_id;
expected_server_connection_id_length_ = connection_id.length();
ASSERT_TRUE(Initialize());
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
int expected_num_client_hellos = 2;
if (ServerSendsVersionNegotiation()) {
++expected_num_client_hellos;
}
EXPECT_EQ(expected_num_client_hellos,
client_->client()->GetNumSentClientHellos());
EXPECT_EQ(client_->client()->client_session()->connection()->connection_id(),
QuicUtils::CreateZeroConnectionId(
GetParam().negotiated_version.transport_version));
}
TEST_P(EndToEndTestWithTls, ZeroConnectionID) {
QuicConnectionId connection_id = QuicUtils::CreateZeroConnectionId(
GetParam().negotiated_version.transport_version);
override_server_connection_id_ = &connection_id;
expected_server_connection_id_length_ = connection_id.length();
ASSERT_TRUE(Initialize());
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
EXPECT_EQ(client_->client()->client_session()->connection()->connection_id(),
QuicUtils::CreateZeroConnectionId(
GetParam().negotiated_version.transport_version));
}
TEST_P(EndToEndTestWithTls, BadConnectionIdLength) {
if (!QuicUtils::VariableLengthConnectionIdAllowedForVersion(
GetParam().negotiated_version.transport_version)) {
ASSERT_TRUE(Initialize());
return;
}
QuicConnectionId connection_id =
TestConnectionIdNineBytesLong(UINT64_C(0xBADbadBADbad));
override_server_connection_id_ = &connection_id;
ASSERT_TRUE(Initialize());
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
EXPECT_EQ(kQuicDefaultConnectionIdLength, client_->client()
->client_session()
->connection()
->connection_id()
.length());
}
// Tests a very long (16-byte) initial destination connection ID to make
// sure the dispatcher properly replaces it with an 8-byte one.
TEST_P(EndToEndTestWithTls, LongBadConnectionIdLength) {
if (!QuicUtils::VariableLengthConnectionIdAllowedForVersion(
GetParam().negotiated_version.transport_version)) {
ASSERT_TRUE(Initialize());
return;
}
const char connection_id_bytes[16] = {0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5,
0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb,
0xbc, 0xbd, 0xbe, 0xbf};
QuicConnectionId connection_id =
QuicConnectionId(connection_id_bytes, sizeof(connection_id_bytes));
override_server_connection_id_ = &connection_id;
ASSERT_TRUE(Initialize());
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
EXPECT_EQ(kQuicDefaultConnectionIdLength, client_->client()
->client_session()
->connection()
->connection_id()
.length());
}
TEST_P(EndToEndTestWithTls, ClientConnectionId) {
if (!GetParam().negotiated_version.SupportsClientConnectionIds()) {
ASSERT_TRUE(Initialize());
return;
}
QuicConnectionId client_connection_id =
TestConnectionId(UINT64_C(0xc1c2c3c4c5c6c7c8));
override_client_connection_id_ = &client_connection_id;
ASSERT_TRUE(Initialize());
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
EXPECT_EQ(client_connection_id, client_->client()
->client_session()
->connection()
->client_connection_id());
}
TEST_P(EndToEndTestWithTls, ForcedVersionNegotiationAndClientConnectionId) {
if (!GetParam().negotiated_version.SupportsClientConnectionIds()) {
ASSERT_TRUE(Initialize());
return;
}
client_supported_versions_.insert(client_supported_versions_.begin(),
QuicVersionReservedForNegotiation());
QuicConnectionId client_connection_id =
TestConnectionId(UINT64_C(0xc1c2c3c4c5c6c7c8));
override_client_connection_id_ = &client_connection_id;
ASSERT_TRUE(Initialize());
ASSERT_TRUE(ServerSendsVersionNegotiation());
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
EXPECT_EQ(client_connection_id, client_->client()
->client_session()
->connection()
->client_connection_id());
}
TEST_P(EndToEndTestWithTls, ForcedVersionNegotiationAndBadConnectionIdLength) {
if (!QuicUtils::VariableLengthConnectionIdAllowedForVersion(
GetParam().negotiated_version.transport_version)) {
ASSERT_TRUE(Initialize());
return;
}
client_supported_versions_.insert(client_supported_versions_.begin(),
QuicVersionReservedForNegotiation());
QuicConnectionId connection_id =
TestConnectionIdNineBytesLong(UINT64_C(0xBADbadBADbad));
override_server_connection_id_ = &connection_id;
ASSERT_TRUE(Initialize());
ASSERT_TRUE(ServerSendsVersionNegotiation());
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
EXPECT_EQ(kQuicDefaultConnectionIdLength, client_->client()
->client_session()
->connection()
->connection_id()
.length());
}
// Forced Version Negotiation with a client connection ID and a long
// connection ID.
TEST_P(EndToEndTestWithTls, ForcedVersNegoAndClientCIDAndLongCID) {
if (!GetParam().negotiated_version.SupportsClientConnectionIds() ||
!QuicUtils::VariableLengthConnectionIdAllowedForVersion(
GetParam().negotiated_version.transport_version)) {
ASSERT_TRUE(Initialize());
return;
}
client_supported_versions_.insert(client_supported_versions_.begin(),
QuicVersionReservedForNegotiation());
const char connection_id_bytes[16] = {0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5,
0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb,
0xbc, 0xbd, 0xbe, 0xbf};
QuicConnectionId connection_id =
QuicConnectionId(connection_id_bytes, sizeof(connection_id_bytes));
override_server_connection_id_ = &connection_id;
const char client_connection_id_bytes[18] = {
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8,
0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xc0, 0xc1};
QuicConnectionId client_connection_id = QuicConnectionId(
client_connection_id_bytes, sizeof(client_connection_id_bytes));
override_client_connection_id_ = &client_connection_id;
ASSERT_TRUE(Initialize());
ASSERT_TRUE(ServerSendsVersionNegotiation());
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
EXPECT_EQ(kQuicDefaultConnectionIdLength, client_->client()
->client_session()
->connection()
->connection_id()
.length());
EXPECT_EQ(client_connection_id, client_->client()
->client_session()
->connection()
->client_connection_id());
}
TEST_P(EndToEndTest, MixGoodAndBadConnectionIdLengths) {
if (!QuicUtils::VariableLengthConnectionIdAllowedForVersion(
GetParam().negotiated_version.transport_version)) {
ASSERT_TRUE(Initialize());
return;
}
// Start client_ which will use a bad connection ID length.
QuicConnectionId connection_id =
TestConnectionIdNineBytesLong(UINT64_C(0xBADbadBADbad));
override_server_connection_id_ = &connection_id;
ASSERT_TRUE(Initialize());
override_server_connection_id_ = nullptr;
// Start client2 which will use a good connection ID length.
std::unique_ptr<QuicTestClient> client2(CreateQuicClient(nullptr));
SpdyHeaderBlock headers;
headers[":method"] = "POST";
headers[":path"] = "/foo";
headers[":scheme"] = "https";
headers[":authority"] = server_hostname_;
headers["content-length"] = "3";
client2->SendMessage(headers, "", /*fin=*/false);
client2->SendData("eep", true);
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
EXPECT_EQ(kQuicDefaultConnectionIdLength, client_->client()
->client_session()
->connection()
->connection_id()
.length());
client2->WaitForResponse();
EXPECT_EQ(kFooResponseBody, client2->response_body());
EXPECT_EQ("200", client2->response_headers()->find(":status")->second);
EXPECT_EQ(kQuicDefaultConnectionIdLength, client2->client()
->client_session()
->connection()
->connection_id()
.length());
}
TEST_P(EndToEndTestWithTls, SimpleRequestResponseWithIetfDraftSupport) {
if (GetParam().negotiated_version.transport_version != QUIC_VERSION_99 ||
GetParam().negotiated_version.handshake_protocol != PROTOCOL_TLS1_3) {
ASSERT_TRUE(Initialize());
return;
}
QuicVersionInitializeSupportForIetfDraft(1);
ASSERT_TRUE(Initialize());
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
}
TEST_P(EndToEndTest, SimpleRequestResponseWithLargeReject) {
chlo_multiplier_ = 1;
ASSERT_TRUE(Initialize());
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
if (ServerSendsVersionNegotiation()) {
EXPECT_EQ(4, client_->client()->GetNumSentClientHellos());
} else {
EXPECT_EQ(3, client_->client()->GetNumSentClientHellos());
}
}
TEST_P(EndToEndTestWithTls, SimpleRequestResponsev6) {
server_address_ =
QuicSocketAddress(QuicIpAddress::Loopback6(), server_address_.port());
ASSERT_TRUE(Initialize());
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
}
TEST_P(EndToEndTestWithTls, NoUndecryptablePackets) {
ASSERT_TRUE(Initialize());
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
QuicConnectionStats client_stats =
client_->client()->client_session()->connection()->GetStats();
EXPECT_EQ(0u, client_stats.undecryptable_packets_received);
server_thread_->Pause();
QuicConnectionStats server_stats = GetServerConnection()->GetStats();
EXPECT_EQ(0u, server_stats.undecryptable_packets_received);
server_thread_->Resume();
}
TEST_P(EndToEndTestWithTls, SeparateFinPacket) {
ASSERT_TRUE(Initialize());
// Send a request in two parts: the request and then an empty packet with FIN.
SpdyHeaderBlock headers;
headers[":method"] = "POST";
headers[":path"] = "/foo";
headers[":scheme"] = "https";
headers[":authority"] = server_hostname_;
client_->SendMessage(headers, "", /*fin=*/false);
client_->SendData("", true);
client_->WaitForResponse();
EXPECT_EQ(kFooResponseBody, client_->response_body());
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
// Now do the same thing but with a content length.
headers["content-length"] = "3";
client_->SendMessage(headers, "", /*fin=*/false);
client_->SendData("foo", true);
client_->WaitForResponse();
EXPECT_EQ(kFooResponseBody, client_->response_body());
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
}
TEST_P(EndToEndTestWithTls, MultipleRequestResponse) {
ASSERT_TRUE(Initialize());
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest("/bar"));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
}
TEST_P(EndToEndTest, MultipleRequestResponseZeroConnectionID) {
QuicConnectionId connection_id = QuicUtils::CreateZeroConnectionId(
GetParam().negotiated_version.transport_version);
override_server_connection_id_ = &connection_id;
expected_server_connection_id_length_ = connection_id.length();
ASSERT_TRUE(Initialize());
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest("/bar"));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
}
TEST_P(EndToEndTestWithTls, MultipleStreams) {
// Verifies quic_test_client can track responses of all active streams.
ASSERT_TRUE(Initialize());
const int kNumRequests = 10;
SpdyHeaderBlock headers;
headers[":method"] = "POST";
headers[":path"] = "/foo";
headers[":scheme"] = "https";
headers[":authority"] = server_hostname_;
headers["content-length"] = "3";
for (int i = 0; i < kNumRequests; ++i) {
client_->SendMessage(headers, "bar", /*fin=*/true);
}
while (kNumRequests > client_->num_responses()) {
client_->ClearPerRequestState();
client_->WaitForResponse();
EXPECT_EQ(kFooResponseBody, client_->response_body());
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
}
}
TEST_P(EndToEndTestWithTls, MultipleClients) {
ASSERT_TRUE(Initialize());
std::unique_ptr<QuicTestClient> client2(CreateQuicClient(nullptr));
SpdyHeaderBlock headers;
headers[":method"] = "POST";
headers[":path"] = "/foo";
headers[":scheme"] = "https";
headers[":authority"] = server_hostname_;
headers["content-length"] = "3";
client_->SendMessage(headers, "", /*fin=*/false);
client2->SendMessage(headers, "", /*fin=*/false);
client_->SendData("bar", true);
client_->WaitForResponse();
EXPECT_EQ(kFooResponseBody, client_->response_body());
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
client2->SendData("eep", true);
client2->WaitForResponse();
EXPECT_EQ(kFooResponseBody, client2->response_body());
EXPECT_EQ("200", client2->response_headers()->find(":status")->second);
}
TEST_P(EndToEndTestWithTls, RequestOverMultiplePackets) {
// Send a large enough request to guarantee fragmentation.
std::string huge_request =
"/some/path?query=" + std::string(kMaxOutgoingPacketSize, '.');
AddToCache(huge_request, 200, kBarResponseBody);
ASSERT_TRUE(Initialize());
EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest(huge_request));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
}
TEST_P(EndToEndTestWithTls, MultiplePacketsRandomOrder) {
// Send a large enough request to guarantee fragmentation.
std::string huge_request =
"/some/path?query=" + std::string(kMaxOutgoingPacketSize, '.');
AddToCache(huge_request, 200, kBarResponseBody);
ASSERT_TRUE(Initialize());
SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2));
SetReorderPercentage(50);
EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest(huge_request));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
}
TEST_P(EndToEndTestWithTls, PostMissingBytes) {
ASSERT_TRUE(Initialize());
// Add a content length header with no body.
SpdyHeaderBlock headers;
headers[":method"] = "POST";
headers[":path"] = "/foo";
headers[":scheme"] = "https";
headers[":authority"] = server_hostname_;
headers["content-length"] = "3";
// This should be detected as stream fin without complete request,
// triggering an error response.
client_->SendCustomSynchronousRequest(headers, "");
EXPECT_EQ(QuicSimpleServerStream::kErrorResponseBody,
client_->response_body());
EXPECT_EQ("500", client_->response_headers()->find(":status")->second);
}
TEST_P(EndToEndTest, LargePostNoPacketLoss) {
ASSERT_TRUE(Initialize());
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
// 1 MB body.
std::string body(1024 * 1024, 'a');
SpdyHeaderBlock headers;
headers[":method"] = "POST";
headers[":path"] = "/foo";
headers[":scheme"] = "https";
headers[":authority"] = server_hostname_;
EXPECT_EQ(kFooResponseBody,
client_->SendCustomSynchronousRequest(headers, body));
// TODO(ianswett): There should not be packet loss in this test, but on some
// platforms the receive buffer overflows.
VerifyCleanConnection(true);
}
TEST_P(EndToEndTest, LargePostNoPacketLoss1sRTT) {
ASSERT_TRUE(Initialize());
SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(1000));
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
// 100 KB body.
std::string body(100 * 1024, 'a');
SpdyHeaderBlock headers;
headers[":method"] = "POST";
headers[":path"] = "/foo";
headers[":scheme"] = "https";
headers[":authority"] = server_hostname_;
EXPECT_EQ(kFooResponseBody,
client_->SendCustomSynchronousRequest(headers, body));
VerifyCleanConnection(false);
}
TEST_P(EndToEndTest, LargePostWithPacketLoss) {
// Connect with lower fake packet loss than we'd like to test.
// Until b/10126687 is fixed, losing handshake packets is pretty
// brutal.
SetPacketLossPercentage(5);
ASSERT_TRUE(Initialize());
// Wait for the server SHLO before upping the packet loss.
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
SetPacketLossPercentage(30);
// 10 KB body.
std::string body(1024 * 10, 'a');
SpdyHeaderBlock headers;
headers[":method"] = "POST";
headers[":path"] = "/foo";
headers[":scheme"] = "https";
headers[":authority"] = server_hostname_;
EXPECT_EQ(kFooResponseBody,
client_->SendCustomSynchronousRequest(headers, body));
VerifyCleanConnection(true);
}
// Regression test for b/80090281.
TEST_P(EndToEndTest, LargePostWithPacketLossAndAlwaysBundleWindowUpdates) {
ASSERT_TRUE(Initialize());
// Wait for the server SHLO before upping the packet loss.
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
server_thread_->WaitForCryptoHandshakeConfirmed();
// Normally server only bundles a retransmittable frame once every other
// kMaxConsecutiveNonRetransmittablePackets ack-only packets. Setting the max
// to 0 to reliably reproduce b/80090281.
server_thread_->Schedule([this]() {
QuicConnectionPeer::SetMaxConsecutiveNumPacketsWithNoRetransmittableFrames(
GetServerConnection(), 0);
});
SetPacketLossPercentage(30);
// 10 KB body.
std::string body(1024 * 10, 'a');
SpdyHeaderBlock headers;
headers[":method"] = "POST";
headers[":path"] = "/foo";
headers[":scheme"] = "https";
headers[":authority"] = server_hostname_;
EXPECT_EQ(kFooResponseBody,
client_->SendCustomSynchronousRequest(headers, body));
VerifyCleanConnection(true);
}
TEST_P(EndToEndTest, LargePostWithPacketLossAndBlockedSocket) {
// Connect with lower fake packet loss than we'd like to test. Until
// b/10126687 is fixed, losing handshake packets is pretty brutal.
SetPacketLossPercentage(5);
ASSERT_TRUE(Initialize());
// Wait for the server SHLO before upping the packet loss.
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
SetPacketLossPercentage(10);
client_writer_->set_fake_blocked_socket_percentage(10);
// 10 KB body.
std::string body(1024 * 10, 'a');
SpdyHeaderBlock headers;
headers[":method"] = "POST";
headers[":path"] = "/foo";
headers[":scheme"] = "https";
headers[":authority"] = server_hostname_;
EXPECT_EQ(kFooResponseBody,
client_->SendCustomSynchronousRequest(headers, body));
}
TEST_P(EndToEndTest, LargePostNoPacketLossWithDelayAndReordering) {
ASSERT_TRUE(Initialize());
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
// Both of these must be called when the writer is not actively used.
SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2));
SetReorderPercentage(30);
// 1 MB body.
std::string body(1024 * 1024, 'a');
SpdyHeaderBlock headers;
headers[":method"] = "POST";
headers[":path"] = "/foo";
headers[":scheme"] = "https";
headers[":authority"] = server_hostname_;
EXPECT_EQ(kFooResponseBody,
client_->SendCustomSynchronousRequest(headers, body));
}
TEST_P(EndToEndTest, LargePostZeroRTTFailure) {
// Send a request and then disconnect. This prepares the client to attempt
// a 0-RTT handshake for the next request.
ASSERT_TRUE(Initialize());
std::string body(20480, 'a');
SpdyHeaderBlock headers;
headers[":method"] = "POST";
headers[":path"] = "/foo";
headers[":scheme"] = "https";
headers[":authority"] = server_hostname_;
EXPECT_EQ(kFooResponseBody,
client_->SendCustomSynchronousRequest(headers, body));
// The same session is used for both hellos, so the number of hellos sent on
// that session is 2.
EXPECT_EQ(2, client_->client()->client_session()->GetNumSentClientHellos());
if (ServerSendsVersionNegotiation()) {
EXPECT_EQ(3, client_->client()->GetNumSentClientHellos());
} else {
EXPECT_EQ(2, client_->client()->GetNumSentClientHellos());
}
client_->Disconnect();
// The 0-RTT handshake should succeed.
client_->Connect();
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
ASSERT_TRUE(client_->client()->connected());
EXPECT_EQ(kFooResponseBody,
client_->SendCustomSynchronousRequest(headers, body));
EXPECT_EQ(1, client_->client()->client_session()->GetNumSentClientHellos());
if (ServerSendsVersionNegotiation()) {
EXPECT_EQ(2, client_->client()->GetNumSentClientHellos());
} else {
EXPECT_EQ(1, client_->client()->GetNumSentClientHellos());
}
client_->Disconnect();
// Restart the server so that the 0-RTT handshake will take 1 RTT.
StopServer();
server_writer_ = new PacketDroppingTestWriter();
StartServer();
client_->Connect();
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
ASSERT_TRUE(client_->client()->connected());
EXPECT_EQ(kFooResponseBody,
client_->SendCustomSynchronousRequest(headers, body));
// The same session is used for both hellos, so the number of hellos sent on
// that session is 2.
EXPECT_EQ(2, client_->client()->client_session()->GetNumSentClientHellos());
if (ServerSendsVersionNegotiation()) {
EXPECT_EQ(3, client_->client()->GetNumSentClientHellos());
} else {
EXPECT_EQ(2, client_->client()->GetNumSentClientHellos());
}
VerifyCleanConnection(false);
}
TEST_P(EndToEndTest, SynchronousRequestZeroRTTFailure) {
// Send a request and then disconnect. This prepares the client to attempt
// a 0-RTT handshake for the next request.
ASSERT_TRUE(Initialize());
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
// The same session is used for both hellos, so the number of hellos sent on
// that session is 2.
EXPECT_EQ(2, client_->client()->client_session()->GetNumSentClientHellos());
if (ServerSendsVersionNegotiation()) {
EXPECT_EQ(3, client_->client()->GetNumSentClientHellos());
} else {
EXPECT_EQ(2, client_->client()->GetNumSentClientHellos());
}
client_->Disconnect();
// The 0-RTT handshake should succeed.
client_->Connect();
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
ASSERT_TRUE(client_->client()->connected());
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
EXPECT_EQ(1, client_->client()->client_session()->GetNumSentClientHellos());
if (ServerSendsVersionNegotiation()) {
EXPECT_EQ(2, client_->client()->GetNumSentClientHellos());
} else {
EXPECT_EQ(1, client_->client()->GetNumSentClientHellos());
}
client_->Disconnect();
// Restart the server so that the 0-RTT handshake will take 1 RTT.
StopServer();
server_writer_ = new PacketDroppingTestWriter();
StartServer();
client_->Connect();
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
ASSERT_TRUE(client_->client()->connected());
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
EXPECT_EQ(2, client_->client()->client_session()->GetNumSentClientHellos());
if (ServerSendsVersionNegotiation()) {
EXPECT_EQ(3, client_->client()->GetNumSentClientHellos());
} else {
EXPECT_EQ(2, client_->client()->GetNumSentClientHellos());
}
VerifyCleanConnection(false);
}
TEST_P(EndToEndTest, LargePostSynchronousRequest) {
// Send a request and then disconnect. This prepares the client to attempt
// a 0-RTT handshake for the next request.
ASSERT_TRUE(Initialize());
std::string body(20480, 'a');
SpdyHeaderBlock headers;
headers[":method"] = "POST";
headers[":path"] = "/foo";
headers[":scheme"] = "https";
headers[":authority"] = server_hostname_;
EXPECT_EQ(kFooResponseBody,
client_->SendCustomSynchronousRequest(headers, body));
// The same session is used for both hellos, so the number of hellos sent on
// that session is 2.
EXPECT_EQ(2, client_->client()->client_session()->GetNumSentClientHellos());
if (ServerSendsVersionNegotiation()) {
EXPECT_EQ(3, client_->client()->GetNumSentClientHellos());
} else {
EXPECT_EQ(2, client_->client()->GetNumSentClientHellos());
}
client_->Disconnect();
// The 0-RTT handshake should succeed.
client_->Connect();
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
ASSERT_TRUE(client_->client()->connected());
EXPECT_EQ(kFooResponseBody,
client_->SendCustomSynchronousRequest(headers, body));
EXPECT_EQ(1, client_->client()->client_session()->GetNumSentClientHellos());
if (ServerSendsVersionNegotiation()) {
EXPECT_EQ(2, client_->client()->GetNumSentClientHellos());
} else {
EXPECT_EQ(1, client_->client()->GetNumSentClientHellos());
}
client_->Disconnect();
// Restart the server so that the 0-RTT handshake will take 1 RTT.
StopServer();
server_writer_ = new PacketDroppingTestWriter();
StartServer();
client_->Connect();
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
ASSERT_TRUE(client_->client()->connected());
EXPECT_EQ(kFooResponseBody,
client_->SendCustomSynchronousRequest(headers, body));
EXPECT_EQ(2, client_->client()->client_session()->GetNumSentClientHellos());
if (ServerSendsVersionNegotiation()) {
EXPECT_EQ(3, client_->client()->GetNumSentClientHellos());
} else {
EXPECT_EQ(2, client_->client()->GetNumSentClientHellos());
}
VerifyCleanConnection(false);
}
TEST_P(EndToEndTest, RejectWithPacketLoss) {
// In this test, we intentionally drop the first packet from the
// server, which corresponds with the initial REJ response from
// the server.
server_writer_->set_fake_drop_first_n_packets(1);
ASSERT_TRUE(Initialize());
}
TEST_P(EndToEndTest, SetInitialReceivedConnectionOptions) {
QuicTagVector initial_received_options;
initial_received_options.push_back(kTBBR);
initial_received_options.push_back(kIW10);
initial_received_options.push_back(kPRST);
EXPECT_TRUE(server_config_.SetInitialReceivedConnectionOptions(
initial_received_options));
ASSERT_TRUE(Initialize());
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
server_thread_->WaitForCryptoHandshakeConfirmed();
EXPECT_FALSE(server_config_.SetInitialReceivedConnectionOptions(
initial_received_options));
// Verify that server's configuration is correct.
server_thread_->Pause();
EXPECT_TRUE(server_config_.HasReceivedConnectionOptions());
EXPECT_TRUE(
ContainsQuicTag(server_config_.ReceivedConnectionOptions(), kTBBR));
EXPECT_TRUE(
ContainsQuicTag(server_config_.ReceivedConnectionOptions(), kIW10));
EXPECT_TRUE(
ContainsQuicTag(server_config_.ReceivedConnectionOptions(), kPRST));
}
TEST_P(EndToEndTest, LargePostSmallBandwidthLargeBuffer) {
ASSERT_TRUE(Initialize());
SetPacketSendDelay(QuicTime::Delta::FromMicroseconds(1));
// 256KB per second with a 256KB buffer from server to client. Wireless
// clients commonly have larger buffers, but our max CWND is 200.
server_writer_->set_max_bandwidth_and_buffer_size(
QuicBandwidth::FromBytesPerSecond(256 * 1024), 256 * 1024);
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
// 1 MB body.
std::string body(1024 * 1024, 'a');
SpdyHeaderBlock headers;
headers[":method"] = "POST";
headers[":path"] = "/foo";
headers[":scheme"] = "https";
headers[":authority"] = server_hostname_;
EXPECT_EQ(kFooResponseBody,
client_->SendCustomSynchronousRequest(headers, body));
// This connection may drop packets, because the buffer is smaller than the
// max CWND.
VerifyCleanConnection(true);
}
TEST_P(EndToEndTestWithTls, DoNotSetSendAlarmIfConnectionFlowControlBlocked) {
// Regression test for b/14677858.
// Test that the resume write alarm is not set in QuicConnection::OnCanWrite
// if currently connection level flow control blocked. If set, this results in
// an infinite loop in the EpollServer, as the alarm fires and is immediately
// rescheduled.
ASSERT_TRUE(Initialize());
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
// Ensure both stream and connection level are flow control blocked by setting
// the send window offset to 0.
const uint64_t flow_control_window =
server_config_.GetInitialStreamFlowControlWindowToSend();
QuicSpdyClientStream* stream = client_->GetOrCreateStream();
QuicSession* session = client_->client()->client_session();
QuicFlowControllerPeer::SetSendWindowOffset(stream->flow_controller(), 0);
QuicFlowControllerPeer::SetSendWindowOffset(session->flow_controller(), 0);
EXPECT_TRUE(stream->flow_controller()->IsBlocked());
EXPECT_TRUE(session->flow_controller()->IsBlocked());
// Make sure that the stream has data pending so that it will be marked as
// write blocked when it receives a stream level WINDOW_UPDATE.
stream->WriteOrBufferBody("hello", false);
// The stream now attempts to write, fails because it is still connection
// level flow control blocked, and is added to the write blocked list.
QuicWindowUpdateFrame window_update(kInvalidControlFrameId, stream->id(),
2 * flow_control_window);
stream->OnWindowUpdateFrame(window_update);
// Prior to fixing b/14677858 this call would result in an infinite loop in
// Chromium. As a proxy for detecting this, we now check whether the
// send alarm is set after OnCanWrite. It should not be, as the
// connection is still flow control blocked.
session->connection()->OnCanWrite();
QuicAlarm* send_alarm =
QuicConnectionPeer::GetSendAlarm(session->connection());
EXPECT_FALSE(send_alarm->IsSet());
}
// TODO(nharper): Needs to get turned back to EndToEndTestWithTls
// when we figure out why the test doesn't work on chrome.
TEST_P(EndToEndTest, InvalidStream) {
ASSERT_TRUE(Initialize());
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
std::string body(kMaxOutgoingPacketSize, 'a');
SpdyHeaderBlock headers;
headers[":method"] = "POST";
headers[":path"] = "/foo";
headers[":scheme"] = "https";
headers[":authority"] = server_hostname_;
// Force the client to write with a stream ID belonging to a nonexistent
// server-side stream.
QuicSpdySession* session = client_->client()->client_session();
QuicSessionPeer::SetNextOutgoingBidirectionalStreamId(
session, GetNthServerInitiatedBidirectionalId(0));
client_->SendCustomSynchronousRequest(headers, body);
EXPECT_EQ(QUIC_STREAM_CONNECTION_ERROR, client_->stream_error());
EXPECT_EQ(QUIC_INVALID_STREAM_ID, client_->connection_error());
}
// Test that if the server will close the connection if the client attempts
// to send a request with overly large headers.
TEST_P(EndToEndTest, LargeHeaders) {
ASSERT_TRUE(Initialize());
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
std::string body(kMaxOutgoingPacketSize, 'a');
SpdyHeaderBlock headers;
headers[":method"] = "POST";
headers[":path"] = "/foo";
headers[":scheme"] = "https";
headers[":authority"] = server_hostname_;
headers["key1"] = std::string(15 * 1024, 'a');
headers["key2"] = std::string(15 * 1024, 'a');
headers["key3"] = std::string(15 * 1024, 'a');
client_->SendCustomSynchronousRequest(headers, body);
if (VersionUsesQpack(client_->client()
->client_session()
->connection()
->transport_version())) {
EXPECT_EQ(QUIC_HEADERS_STREAM_DATA_DECOMPRESS_FAILURE,
client_->connection_error());
} else {
EXPECT_EQ(QUIC_HEADERS_TOO_LARGE, client_->stream_error());
EXPECT_EQ(QUIC_NO_ERROR, client_->connection_error());
}
}
TEST_P(EndToEndTest, EarlyResponseWithQuicStreamNoError) {
ASSERT_TRUE(Initialize());
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
std::string large_body(1024 * 1024, 'a');
SpdyHeaderBlock headers;
headers[":method"] = "POST";
headers[":path"] = "/foo";
headers[":scheme"] = "https";
headers[":authority"] = server_hostname_;
// Insert an invalid content_length field in request to trigger an early
// response from server.
headers["content-length"] = "-3";
client_->SendCustomSynchronousRequest(headers, large_body);
EXPECT_EQ("bad", client_->response_body());
EXPECT_EQ("500", client_->response_headers()->find(":status")->second);
EXPECT_EQ(QUIC_STREAM_NO_ERROR, client_->stream_error());
EXPECT_EQ(QUIC_NO_ERROR, client_->connection_error());
}
// TODO(rch): this test seems to cause net_unittests timeouts :|
TEST_P(EndToEndTestWithTls, QUIC_TEST_DISABLED_IN_CHROME(MultipleTermination)) {
ASSERT_TRUE(Initialize());
// Set the offset so we won't frame. Otherwise when we pick up termination
// before HTTP framing is complete, we send an error and close the stream,
// and the second write is picked up as writing on a closed stream.
QuicSpdyClientStream* stream = client_->GetOrCreateStream();
ASSERT_TRUE(stream != nullptr);
QuicStreamPeer::SetStreamBytesWritten(3, stream);
client_->SendData("bar", true);
client_->WaitForWriteToFlush();
// By default the stream protects itself from writes after terminte is set.
// Override this to test the server handling buggy clients.
QuicStreamPeer::SetWriteSideClosed(false, client_->GetOrCreateStream());
EXPECT_QUIC_BUG(client_->SendData("eep", true), "Fin already buffered");
}
// TODO(nharper): Needs to get turned back to EndToEndTestWithTls
// when we figure out why the test doesn't work on chrome.
TEST_P(EndToEndTest, Timeout) {
client_config_.SetIdleNetworkTimeout(QuicTime::Delta::FromMicroseconds(500),
QuicTime::Delta::FromMicroseconds(500));
// Note: we do NOT ASSERT_TRUE: we may time out during initial handshake:
// that's enough to validate timeout in this case.
Initialize();
while (client_->client()->connected()) {
client_->client()->WaitForEvents();
}
}
TEST_P(EndToEndTestWithTls, MaxIncomingDynamicStreamsLimitRespected) {
// Set a limit on maximum number of incoming dynamic streams.
// Make sure the limit is respected.
const uint32_t kServerMaxIncomingDynamicStreams = 1;
server_config_.SetMaxIncomingBidirectionalStreamsToSend(
kServerMaxIncomingDynamicStreams);
ASSERT_TRUE(Initialize());
if (VersionHasIetfQuicFrames(
GetParam().negotiated_version.transport_version)) {
// Do not run this test for /IETF QUIC. Note that the test needs
// to be here, after calling Initialize(), because all tests end up calling
// EndToEndTest::TearDown(), which asserts that Initialize has been called
// and then proceeds to tear things down -- which fails if they are not
// properly set up.
return;
}
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
// Make the client misbehave after negotiation.
const int kServerMaxStreams = kMaxStreamsMinimumIncrement + 1;
QuicSessionPeer::SetMaxOpenOutgoingStreams(
client_->client()->client_session(), kServerMaxStreams + 1);
SpdyHeaderBlock headers;
headers[":method"] = "POST";
headers[":path"] = "/foo";
headers[":scheme"] = "https";
headers[":authority"] = server_hostname_;
headers["content-length"] = "3";
// The server supports a small number of additional streams beyond the
// negotiated limit. Open enough streams to go beyond that limit.
for (int i = 0; i < kServerMaxStreams + 1; ++i) {
client_->SendMessage(headers, "", /*fin=*/false);
}
client_->WaitForResponse();
EXPECT_TRUE(client_->connected());
EXPECT_EQ(QUIC_REFUSED_STREAM, client_->stream_error());
EXPECT_EQ(QUIC_NO_ERROR, client_->connection_error());
}
TEST_P(EndToEndTest, SetIndependentMaxIncomingDynamicStreamsLimits) {
// Each endpoint can set max incoming dynamic streams independently.
const uint32_t kClientMaxIncomingDynamicStreams = 2;
const uint32_t kServerMaxIncomingDynamicStreams = 1;
client_config_.SetMaxIncomingBidirectionalStreamsToSend(
kClientMaxIncomingDynamicStreams);
server_config_.SetMaxIncomingBidirectionalStreamsToSend(
kServerMaxIncomingDynamicStreams);
client_config_.SetMaxIncomingUnidirectionalStreamsToSend(
kClientMaxIncomingDynamicStreams);
server_config_.SetMaxIncomingUnidirectionalStreamsToSend(
kServerMaxIncomingDynamicStreams);
ASSERT_TRUE(Initialize());
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
// The client has received the server's limit and vice versa.
QuicSpdyClientSession* client_session = client_->client()->client_session();
// The value returned by max_allowed... includes the Crypto and Header
// stream (created as a part of initialization). The config. values,
// above, are treated as "number of requests/responses" - that is, they do
// not include the static Crypto and Header streams. Reduce the value
// returned by max_allowed... by 2 to remove the static streams from the
// count.
size_t client_max_open_outgoing_bidirectional_streams =
VersionHasIetfQuicFrames(
client_session->connection()->transport_version())
? QuicSessionPeer::v99_streamid_manager(client_session)
->max_allowed_outgoing_bidirectional_streams() -
QuicSessionPeer::v99_bidirectional_stream_id_manager(
client_session)
->outgoing_static_stream_count()
: QuicSessionPeer::GetStreamIdManager(client_session)
->max_open_outgoing_streams();
size_t client_max_open_outgoing_unidirectional_streams =
VersionHasIetfQuicFrames(
client_session->connection()->transport_version())
? QuicSessionPeer::v99_streamid_manager(client_session)
->max_allowed_outgoing_unidirectional_streams() -
QuicSessionPeer::v99_unidirectional_stream_id_manager(
client_session)
->outgoing_static_stream_count()
: QuicSessionPeer::GetStreamIdManager(client_session)
->max_open_outgoing_streams();
EXPECT_EQ(kServerMaxIncomingDynamicStreams,
client_max_open_outgoing_bidirectional_streams);
EXPECT_EQ(kServerMaxIncomingDynamicStreams,
client_max_open_outgoing_unidirectional_streams);
server_thread_->Pause();
QuicSession* server_session = GetServerSession();
size_t server_max_open_outgoing_bidirectional_streams =
VersionHasIetfQuicFrames(
server_session->connection()->transport_version())
? QuicSessionPeer::v99_streamid_manager(server_session)
->max_allowed_outgoing_bidirectional_streams()
: QuicSessionPeer::GetStreamIdManager(server_session)
->max_open_outgoing_streams();
size_t server_max_open_outgoing_unidirectional_streams =
VersionHasIetfQuicFrames(
server_session->connection()->transport_version())
? QuicSessionPeer::v99_streamid_manager(server_session)
->max_allowed_outgoing_unidirectional_streams() -
QuicSessionPeer::v99_unidirectional_stream_id_manager(
server_session)
->outgoing_static_stream_count()
: QuicSessionPeer::GetStreamIdManager(server_session)
->max_open_outgoing_streams();
EXPECT_EQ(kClientMaxIncomingDynamicStreams,
server_max_open_outgoing_bidirectional_streams);
EXPECT_EQ(kClientMaxIncomingDynamicStreams,
server_max_open_outgoing_unidirectional_streams);
server_thread_->Resume();
}
TEST_P(EndToEndTest, NegotiateCongestionControl) {
ASSERT_TRUE(Initialize());
// For PCC, the underlying implementation may be a stub with a
// different name-tag. Skip the rest of this test.
if (GetParam().congestion_control_tag == kTPCC) {
return;
}
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
CongestionControlType expected_congestion_control_type = kRenoBytes;
switch (GetParam().congestion_control_tag) {
case kRENO:
expected_congestion_control_type = kRenoBytes;
break;
case kTBBR:
expected_congestion_control_type = kBBR;
break;
case kQBIC:
expected_congestion_control_type = kCubicBytes;
break;
case kB2ON:
expected_congestion_control_type = kBBRv2;
break;
default:
QUIC_DLOG(FATAL) << "Unexpected congestion control tag";
}
server_thread_->Pause();
EXPECT_EQ(expected_congestion_control_type,
QuicSentPacketManagerPeer::GetSendAlgorithm(
*GetSentPacketManagerFromFirstServerSession())
->GetCongestionControlType());
server_thread_->Resume();
}
TEST_P(EndToEndTest, ClientSuggestsRTT) {
// Client suggests initial RTT, verify it is used.
const QuicTime::Delta kInitialRTT = QuicTime::Delta::FromMicroseconds(20000);
client_config_.SetInitialRoundTripTimeUsToSend(kInitialRTT.ToMicroseconds());
ASSERT_TRUE(Initialize());
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
server_thread_->WaitForCryptoHandshakeConfirmed();
// Pause the server so we can access the server's internals without races.
server_thread_->Pause();
QuicDispatcher* dispatcher =
QuicServerPeer::GetDispatcher(server_thread_->server());
ASSERT_EQ(1u, dispatcher->session_map().size());
const QuicSentPacketManager& client_sent_packet_manager =
client_->client()->client_session()->connection()->sent_packet_manager();
const QuicSentPacketManager* server_sent_packet_manager =
GetSentPacketManagerFromFirstServerSession();
EXPECT_EQ(kInitialRTT,
client_sent_packet_manager.GetRttStats()->initial_rtt());
EXPECT_EQ(kInitialRTT,
server_sent_packet_manager->GetRttStats()->initial_rtt());
server_thread_->Resume();
}
TEST_P(EndToEndTest, ClientSuggestsIgnoredRTT) {
// Client suggests initial RTT, but also specifies NRTT, so it's not used.
const QuicTime::Delta kInitialRTT = QuicTime::Delta::FromMicroseconds(20000);
client_config_.SetInitialRoundTripTimeUsToSend(kInitialRTT.ToMicroseconds());
QuicTagVector options;
options.push_back(kNRTT);
client_config_.SetConnectionOptionsToSend(options);
ASSERT_TRUE(Initialize());
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
server_thread_->WaitForCryptoHandshakeConfirmed();
// Pause the server so we can access the server's internals without races.
server_thread_->Pause();
QuicDispatcher* dispatcher =
QuicServerPeer::GetDispatcher(server_thread_->server());
ASSERT_EQ(1u, dispatcher->session_map().size());
const QuicSentPacketManager& client_sent_packet_manager =
client_->client()->client_session()->connection()->sent_packet_manager();
const QuicSentPacketManager* server_sent_packet_manager =
GetSentPacketManagerFromFirstServerSession();
EXPECT_EQ(kInitialRTT,
client_sent_packet_manager.GetRttStats()->initial_rtt());
EXPECT_EQ(kInitialRTT,
server_sent_packet_manager->GetRttStats()->initial_rtt());
server_thread_->Resume();
}
TEST_P(EndToEndTest, MaxInitialRTT) {
// Client tries to suggest twice the server's max initial rtt and the server
// uses the max.
client_config_.SetInitialRoundTripTimeUsToSend(2 *
kMaxInitialRoundTripTimeUs);
ASSERT_TRUE(Initialize());
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
server_thread_->WaitForCryptoHandshakeConfirmed();
// Pause the server so we can access the server's internals without races.
server_thread_->Pause();
const QuicSentPacketManager& client_sent_packet_manager =
client_->client()->client_session()->connection()->sent_packet_manager();
// Now that acks have been exchanged, the RTT estimate has decreased on the
// server and is not infinite on the client.
EXPECT_FALSE(
client_sent_packet_manager.GetRttStats()->smoothed_rtt().IsInfinite());
const RttStats& server_rtt_stats =
*GetServerConnection()->sent_packet_manager().GetRttStats();
EXPECT_EQ(static_cast<int64_t>(kMaxInitialRoundTripTimeUs),
server_rtt_stats.initial_rtt().ToMicroseconds());
EXPECT_GE(static_cast<int64_t>(kMaxInitialRoundTripTimeUs),
server_rtt_stats.smoothed_rtt().ToMicroseconds());
server_thread_->Resume();
}
TEST_P(EndToEndTest, MinInitialRTT) {
// Client tries to suggest 0 and the server uses the default.
client_config_.SetInitialRoundTripTimeUsToSend(0);
ASSERT_TRUE(Initialize());
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
server_thread_->WaitForCryptoHandshakeConfirmed();
// Pause the server so we can access the server's internals without races.
server_thread_->Pause();
const QuicSentPacketManager& client_sent_packet_manager =
client_->client()->client_session()->connection()->sent_packet_manager();
const QuicSentPacketManager& server_sent_packet_manager =
GetServerConnection()->sent_packet_manager();
// Now that acks have been exchanged, the RTT estimate has decreased on the
// server and is not infinite on the client.
EXPECT_FALSE(
client_sent_packet_manager.GetRttStats()->smoothed_rtt().IsInfinite());
// Expect the default rtt of 100ms.
EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100),
server_sent_packet_manager.GetRttStats()->initial_rtt());
// Ensure the bandwidth is valid.
client_sent_packet_manager.BandwidthEstimate();
server_sent_packet_manager.BandwidthEstimate();
server_thread_->Resume();
}
TEST_P(EndToEndTest, 0ByteConnectionId) {
if (VersionHasIetfInvariantHeader(
GetParam().negotiated_version.transport_version)) {
// SetBytesForConnectionIdToSend only applies to Google QUIC encoding.
ASSERT_TRUE(Initialize());
return;
}
client_config_.SetBytesForConnectionIdToSend(0);
ASSERT_TRUE(Initialize());
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
QuicConnection* client_connection =
client_->client()->client_session()->connection();
QuicPacketHeader* header =
QuicConnectionPeer::GetLastHeader(client_connection);
if (!GetQuicRestartFlag(quic_do_not_override_connection_id)) {
EXPECT_EQ(CONNECTION_ID_ABSENT, header->destination_connection_id_included);
} else {
EXPECT_EQ(CONNECTION_ID_ABSENT, header->source_connection_id_included);
}
}
TEST_P(EndToEndTestWithTls, 8ByteConnectionId) {
if (VersionHasIetfInvariantHeader(
GetParam().negotiated_version.transport_version)) {
// SetBytesForConnectionIdToSend only applies to Google QUIC encoding.
ASSERT_TRUE(Initialize());
return;
}
client_config_.SetBytesForConnectionIdToSend(8);
ASSERT_TRUE(Initialize());
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
QuicConnection* client_connection =
client_->client()->client_session()->connection();
QuicPacketHeader* header =
QuicConnectionPeer::GetLastHeader(client_connection);
EXPECT_EQ(CONNECTION_ID_PRESENT, header->destination_connection_id_included);
}
TEST_P(EndToEndTestWithTls, 15ByteConnectionId) {
if (VersionHasIetfInvariantHeader(
GetParam().negotiated_version.transport_version)) {
// SetBytesForConnectionIdToSend only applies to Google QUIC encoding.
ASSERT_TRUE(Initialize());
return;
}
client_config_.SetBytesForConnectionIdToSend(15);
ASSERT_TRUE(Initialize());
// Our server is permissive and allows for out of bounds values.
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
QuicConnection* client_connection =
client_->client()->client_session()->connection();
QuicPacketHeader* header =
QuicConnectionPeer::GetLastHeader(client_connection);
EXPECT_EQ(CONNECTION_ID_PRESENT, header->destination_connection_id_included);
}
TEST_P(EndToEndTestWithTls, ResetConnection) {
ASSERT_TRUE(Initialize());
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
client_->ResetConnection();
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest("/bar"));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
}
// TODO(nharper): Needs to get turned back to EndToEndTestWithTls
// when we figure out why the test doesn't work on chrome.
TEST_P(EndToEndTest, MaxStreamsUberTest) {
SetQuicFlag(FLAGS_quic_headers_stream_id_in_v99, 0);
// Connect with lower fake packet loss than we'd like to test. Until
// b/10126687 is fixed, losing handshake packets is pretty brutal.
SetPacketLossPercentage(1);
ASSERT_TRUE(Initialize());
std::string large_body(10240, 'a');
int max_streams = 100;
AddToCache("/large_response", 200, large_body);
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
SetPacketLossPercentage(10);
for (int i = 0; i < max_streams; ++i) {
EXPECT_LT(0, client_->SendRequest("/large_response"));
}
// WaitForEvents waits 50ms and returns true if there are outstanding
// requests.
while (client_->client()->WaitForEvents() == true) {
}
}
TEST_P(EndToEndTestWithTls, StreamCancelErrorTest) {
ASSERT_TRUE(Initialize());
std::string small_body(256, 'a');
AddToCache("/small_response", 200, small_body);
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
QuicSession* session = client_->client()->client_session();
// Lose the request.
SetPacketLossPercentage(100);
EXPECT_LT(0, client_->SendRequest("/small_response"));
client_->client()->WaitForEvents();
// Transmit the cancel, and ensure the connection is torn down properly.
SetPacketLossPercentage(0);
QuicStreamId stream_id = GetNthClientInitiatedBidirectionalId(0);
session->SendRstStream(stream_id, QUIC_STREAM_CANCELLED, 0);
// WaitForEvents waits 50ms and returns true if there are outstanding
// requests.
while (client_->client()->WaitForEvents() == true) {
}
// It should be completely fine to RST a stream before any data has been
// received for that stream.
EXPECT_EQ(QUIC_NO_ERROR, client_->connection_error());
}
TEST_P(EndToEndTest, ConnectionMigrationClientIPChanged) {
ASSERT_TRUE(Initialize());
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
// Store the client IP address which was used to send the first request.
QuicIpAddress old_host =
client_->client()->network_helper()->GetLatestClientAddress().host();
// Migrate socket to the new IP address.
QuicIpAddress new_host = TestLoopback(2);
EXPECT_NE(old_host, new_host);
ASSERT_TRUE(client_->client()->MigrateSocket(new_host));
// Send a request using the new socket.
EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest("/bar"));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
}
TEST_P(EndToEndTest, ConnectionMigrationClientPortChanged) {
// Tests that the client's port can change during an established QUIC
// connection, and that doing so does not result in the connection being
// closed by the server.
ASSERT_TRUE(Initialize());
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
// Store the client address which was used to send the first request.
QuicSocketAddress old_address =
client_->client()->network_helper()->GetLatestClientAddress();
int old_fd = client_->client()->GetLatestFD();
// Create a new socket before closing the old one, which will result in a new
// ephemeral port.
QuicClientPeer::CreateUDPSocketAndBind(client_->client());
// Stop listening and close the old FD.
QuicClientPeer::CleanUpUDPSocket(client_->client(), old_fd);
// The packet writer needs to be updated to use the new FD.
client_->client()->network_helper()->CreateQuicPacketWriter();
// Change the internal state of the client and connection to use the new port,
// this is done because in a real NAT rebinding the client wouldn't see any
// port change, and so expects no change to incoming port.
// This is kind of ugly, but needed as we are simply swapping out the client
// FD rather than any more complex NAT rebinding simulation.
int new_port =
client_->client()->network_helper()->GetLatestClientAddress().port();
QuicClientPeer::SetClientPort(client_->client(), new_port);
QuicConnectionPeer::SetSelfAddress(
client_->client()->client_session()->connection(),
QuicSocketAddress(client_->client()
->client_session()
->connection()
->self_address()
.host(),
new_port));
// Register the new FD for epoll events.
int new_fd = client_->client()->GetLatestFD();
QuicEpollServer* eps = client_->epoll_server();
eps->RegisterFD(new_fd, client_->client()->epoll_network_helper(),
EPOLLIN | EPOLLOUT | EPOLLET);
// Send a second request, using the new FD.
EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest("/bar"));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
// Verify that the client's ephemeral port is different.
QuicSocketAddress new_address =
client_->client()->network_helper()->GetLatestClientAddress();
EXPECT_EQ(old_address.host(), new_address.host());
EXPECT_NE(old_address.port(), new_address.port());
}
TEST_P(EndToEndTest, NegotiatedInitialCongestionWindow) {
SetQuicReloadableFlag(quic_unified_iw_options, true);
client_extra_copts_.push_back(kIW03);
ASSERT_TRUE(Initialize());
// Values are exchanged during crypto handshake, so wait for that to finish.
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
server_thread_->WaitForCryptoHandshakeConfirmed();
server_thread_->Pause();
QuicPacketCount cwnd =
GetServerConnection()->sent_packet_manager().initial_congestion_window();
EXPECT_EQ(3u, cwnd);
}
TEST_P(EndToEndTest, DifferentFlowControlWindows) {
// Client and server can set different initial flow control receive windows.
// These are sent in CHLO/SHLO. Tests that these values are exchanged properly
// in the crypto handshake.
const uint32_t kClientStreamIFCW = 123456;
const uint32_t kClientSessionIFCW = 234567;
set_client_initial_stream_flow_control_receive_window(kClientStreamIFCW);
set_client_initial_session_flow_control_receive_window(kClientSessionIFCW);
uint32_t kServerStreamIFCW = 32 * 1024;
uint32_t kServerSessionIFCW = 48 * 1024;
set_server_initial_stream_flow_control_receive_window(kServerStreamIFCW);
set_server_initial_session_flow_control_receive_window(kServerSessionIFCW);
ASSERT_TRUE(Initialize());
// Values are exchanged during crypto handshake, so wait for that to finish.
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
server_thread_->WaitForCryptoHandshakeConfirmed();
// Open a data stream to make sure the stream level flow control is updated.
QuicSpdyClientStream* stream = client_->GetOrCreateStream();
WriteHeadersOnStream(stream);
stream->WriteOrBufferBody("hello", false);
// Client should have the right values for server's receive window.
EXPECT_EQ(kServerStreamIFCW,
client_->client()
->client_session()
->config()
->ReceivedInitialStreamFlowControlWindowBytes());
EXPECT_EQ(kServerSessionIFCW,
client_->client()
->client_session()
->config()
->ReceivedInitialSessionFlowControlWindowBytes());
EXPECT_EQ(kServerStreamIFCW, QuicFlowControllerPeer::SendWindowOffset(
stream->flow_controller()));
EXPECT_EQ(kServerSessionIFCW,
QuicFlowControllerPeer::SendWindowOffset(
client_->client()->client_session()->flow_controller()));
// Server should have the right values for client's receive window.
server_thread_->Pause();
QuicSession* session = GetServerSession();
EXPECT_EQ(kClientStreamIFCW,
session->config()->ReceivedInitialStreamFlowControlWindowBytes());
EXPECT_EQ(kClientSessionIFCW,
session->config()->ReceivedInitialSessionFlowControlWindowBytes());
EXPECT_EQ(kClientSessionIFCW, QuicFlowControllerPeer::SendWindowOffset(
session->flow_controller()));
server_thread_->Resume();
}
// Test negotiation of IFWA connection option.
TEST_P(EndToEndTest, NegotiatedServerInitialFlowControlWindow) {
const uint32_t kClientStreamIFCW = 123456;
const uint32_t kClientSessionIFCW = 234567;
set_client_initial_stream_flow_control_receive_window(kClientStreamIFCW);
set_client_initial_session_flow_control_receive_window(kClientSessionIFCW);
uint32_t kServerStreamIFCW = 32 * 1024;
uint32_t kServerSessionIFCW = 48 * 1024;
set_server_initial_stream_flow_control_receive_window(kServerStreamIFCW);
set_server_initial_session_flow_control_receive_window(kServerSessionIFCW);
// Bump the window.
const uint32_t kExpectedStreamIFCW = 1024 * 1024;
const uint32_t kExpectedSessionIFCW = 1.5 * 1024 * 1024;
client_extra_copts_.push_back(kIFWA);
ASSERT_TRUE(Initialize());
// Values are exchanged during crypto handshake, so wait for that to finish.
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
server_thread_->WaitForCryptoHandshakeConfirmed();
// Open a data stream to make sure the stream level flow control is updated.
QuicSpdyClientStream* stream = client_->GetOrCreateStream();
WriteHeadersOnStream(stream);
stream->WriteOrBufferBody("hello", false);
// Client should have the right values for server's receive window.
EXPECT_EQ(kExpectedStreamIFCW,
client_->client()
->client_session()
->config()
->ReceivedInitialStreamFlowControlWindowBytes());
EXPECT_EQ(kExpectedSessionIFCW,
client_->client()
->client_session()
->config()
->ReceivedInitialSessionFlowControlWindowBytes());
EXPECT_EQ(kExpectedStreamIFCW, QuicFlowControllerPeer::SendWindowOffset(
stream->flow_controller()));
EXPECT_EQ(kExpectedSessionIFCW,
QuicFlowControllerPeer::SendWindowOffset(
client_->client()->client_session()->flow_controller()));
}
TEST_P(EndToEndTest, HeadersAndCryptoStreamsNoConnectionFlowControl) {
// The special headers and crypto streams should be subject to per-stream flow
// control limits, but should not be subject to connection level flow control
const uint32_t kStreamIFCW = 32 * 1024;
const uint32_t kSessionIFCW = 48 * 1024;
set_client_initial_stream_flow_control_receive_window(kStreamIFCW);
set_client_initial_session_flow_control_receive_window(kSessionIFCW);
set_server_initial_stream_flow_control_receive_window(kStreamIFCW);
set_server_initial_session_flow_control_receive_window(kSessionIFCW);
ASSERT_TRUE(Initialize());
// Wait for crypto handshake to finish. This should have contributed to the
// crypto stream flow control window, but not affected the session flow
// control window.
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
server_thread_->WaitForCryptoHandshakeConfirmed();
QuicCryptoStream* crypto_stream = QuicSessionPeer::GetMutableCryptoStream(
client_->client()->client_session());
// In v47 and later, the crypto handshake (sent in CRYPTO frames) is not
// subject to flow control.
const QuicTransportVersion transport_version =
client_->client()->client_session()->connection()->transport_version();
if (!QuicVersionUsesCryptoFrames(transport_version)) {
EXPECT_LT(QuicFlowControllerPeer::SendWindowSize(
crypto_stream->flow_controller()),
kStreamIFCW);
}
// When stream type is enabled, control streams will send settings and
// contribute to flow control windows, so this expectation is no longer valid.
if (!VersionHasStreamType(transport_version)) {
EXPECT_EQ(kSessionIFCW,
QuicFlowControllerPeer::SendWindowSize(
client_->client()->client_session()->flow_controller()));
}
// Send a request with no body, and verify that the connection level window
// has not been affected.
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
// No headers stream in IETF QUIC.
if (VersionUsesQpack(transport_version)) {
return;
}
QuicHeadersStream* headers_stream = QuicSpdySessionPeer::GetHeadersStream(
client_->client()->client_session());
EXPECT_LT(
QuicFlowControllerPeer::SendWindowSize(headers_stream->flow_controller()),
kStreamIFCW);
EXPECT_EQ(kSessionIFCW,
QuicFlowControllerPeer::SendWindowSize(
client_->client()->client_session()->flow_controller()));
// Server should be in a similar state: connection flow control window should
// not have any bytes marked as received.
server_thread_->Pause();
QuicSession* session = GetServerSession();
QuicFlowController* server_connection_flow_controller =
session->flow_controller();
EXPECT_EQ(kSessionIFCW, QuicFlowControllerPeer::ReceiveWindowSize(
server_connection_flow_controller));
server_thread_->Resume();
}
TEST_P(EndToEndTest, FlowControlsSynced) {
set_smaller_flow_control_receive_window();
ASSERT_TRUE(Initialize());
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
server_thread_->WaitForCryptoHandshakeConfirmed();
server_thread_->Pause();
QuicSpdySession* const client_session = client_->client()->client_session();
auto* server_session = static_cast<QuicSpdySession*>(GetServerSession());
if (VersionHasStreamType(client_->client()
->client_session()
->connection()
->transport_version())) {
// Settings frame will be sent through control streams, which contribute
// to the session's flow controller. And due to the timing issue described
// below, the settings frame might not be received.
HttpEncoder encoder;
SettingsFrame settings;
settings.values[6] = kDefaultMaxUncompressedHeaderSize;
std::unique_ptr<char[]> buffer;
auto header_length = encoder.SerializeSettingsFrame(settings, &buffer);
QuicByteCount win_difference1 = QuicFlowControllerPeer::ReceiveWindowSize(
server_session->flow_controller()) -
QuicFlowControllerPeer::SendWindowSize(
client_session->flow_controller());
QuicByteCount win_difference2 = QuicFlowControllerPeer::ReceiveWindowSize(
client_session->flow_controller()) -
QuicFlowControllerPeer::SendWindowSize(
server_session->flow_controller());
EXPECT_TRUE(win_difference1 == 0 ||
win_difference1 ==
header_length +
QuicDataWriter::GetVarInt62Len(kControlStream));
EXPECT_TRUE(win_difference2 == 0 ||
win_difference2 ==
header_length +
QuicDataWriter::GetVarInt62Len(kControlStream));
// The test returns early because in this version, headers stream no longer
// sends settings.
return;
}
ExpectFlowControlsSynced(client_session->flow_controller(),
server_session->flow_controller());
if (!QuicVersionUsesCryptoFrames(client_->client()
->client_session()
->connection()
->transport_version())) {
ExpectFlowControlsSynced(
QuicSessionPeer::GetMutableCryptoStream(client_session)
->flow_controller(),
QuicSessionPeer::GetMutableCryptoStream(server_session)
->flow_controller());
}
SpdyFramer spdy_framer(SpdyFramer::ENABLE_COMPRESSION);
SpdySettingsIR settings_frame;
settings_frame.AddSetting(SETTINGS_MAX_HEADER_LIST_SIZE,
kDefaultMaxUncompressedHeaderSize);
SpdySerializedFrame frame(spdy_framer.SerializeFrame(settings_frame));
QuicFlowController* client_header_stream_flow_controller =
QuicSpdySessionPeer::GetHeadersStream(client_session)->flow_controller();
QuicFlowController* server_header_stream_flow_controller =
QuicSpdySessionPeer::GetHeadersStream(server_session)->flow_controller();
// Both client and server are sending this SETTINGS frame, and the send
// window is consumed. But because of timing issue, the server may send or
// not send the frame, and the client may send/ not send / receive / not
// receive the frame.
// TODO(fayang): Rewrite this part because it is hacky.
QuicByteCount win_difference1 = QuicFlowControllerPeer::ReceiveWindowSize(
server_header_stream_flow_controller) -
QuicFlowControllerPeer::SendWindowSize(
client_header_stream_flow_controller);
QuicByteCount win_difference2 = QuicFlowControllerPeer::ReceiveWindowSize(
client_header_stream_flow_controller) -
QuicFlowControllerPeer::SendWindowSize(
server_header_stream_flow_controller);
EXPECT_TRUE(win_difference1 == 0 || win_difference1 == frame.size());
EXPECT_TRUE(win_difference2 == 0 || win_difference2 == frame.size());
// Client *may* have received the SETTINGs frame.
// TODO(fayang): Rewrite this part because it is hacky.
float ratio1 = static_cast<float>(QuicFlowControllerPeer::ReceiveWindowSize(
client_session->flow_controller())) /
QuicFlowControllerPeer::ReceiveWindowSize(
QuicSpdySessionPeer::GetHeadersStream(client_session)
->flow_controller());
float ratio2 = static_cast<float>(QuicFlowControllerPeer::ReceiveWindowSize(
client_session->flow_controller())) /
(QuicFlowControllerPeer::ReceiveWindowSize(
QuicSpdySessionPeer::GetHeadersStream(client_session)
->flow_controller()) +
frame.size());
EXPECT_TRUE(ratio1 == kSessionToStreamRatio ||
ratio2 == kSessionToStreamRatio);
server_thread_->Resume();
}
TEST_P(EndToEndTestWithTls, RequestWithNoBodyWillNeverSendStreamFrameWithFIN) {
// A stream created on receipt of a simple request with no body will never get
// a stream frame with a FIN. Verify that we don't keep track of the stream in
// the locally closed streams map: it will never be removed if so.
ASSERT_TRUE(Initialize());
// Send a simple headers only request, and receive response.
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
// Now verify that the server is not waiting for a final FIN or RST.
server_thread_->Pause();
QuicSession* session = GetServerSession();
EXPECT_EQ(
0u,
QuicSessionPeer::GetLocallyClosedStreamsHighestOffset(session).size());
server_thread_->Resume();
}
// A TestAckListener verifies that its OnAckNotification method has been
// called exactly once on destruction.
class TestAckListener : public QuicAckListenerInterface {
public:
explicit TestAckListener(int bytes_to_ack) : bytes_to_ack_(bytes_to_ack) {}
void OnPacketAcked(int acked_bytes,
QuicTime::Delta /*delta_largest_observed*/) override {
ASSERT_LE(acked_bytes, bytes_to_ack_);
bytes_to_ack_ -= acked_bytes;
}
void OnPacketRetransmitted(int /*retransmitted_bytes*/) override {}
bool has_been_notified() const { return bytes_to_ack_ == 0; }
protected:
// Object is ref counted.
~TestAckListener() override { EXPECT_EQ(0, bytes_to_ack_); }
private:
int bytes_to_ack_;
};
class TestResponseListener : public QuicSpdyClientBase::ResponseListener {
public:
void OnCompleteResponse(QuicStreamId id,
const SpdyHeaderBlock& response_headers,
const std::string& response_body) override {
QUIC_DVLOG(1) << "response for stream " << id << " "
<< response_headers.DebugString() << "\n"
<< response_body;
}
};
TEST_P(EndToEndTest, AckNotifierWithPacketLossAndBlockedSocket) {
// Verify that even in the presence of packet loss and occasionally blocked
// socket, an AckNotifierDelegate will get informed that the data it is
// interested in has been ACKed. This tests end-to-end ACK notification, and
// demonstrates that retransmissions do not break this functionality.
SetPacketLossPercentage(5);
ASSERT_TRUE(Initialize());
// Wait for the server SHLO before upping the packet loss.
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
SetPacketLossPercentage(30);
client_writer_->set_fake_blocked_socket_percentage(10);
// Create a POST request and send the headers only.
SpdyHeaderBlock headers;
headers[":method"] = "POST";
headers[":path"] = "/foo";
headers[":scheme"] = "https";
headers[":authority"] = server_hostname_;
client_->SendMessage(headers, "", /*fin=*/false);
// Size of headers on the request stream. Zero if headers are sent on the
// header stream.
size_t header_size = 0;
if (VersionUsesQpack(client_->client()
->client_session()
->connection()
->transport_version())) {
// Determine size of compressed headers.
NoopDecoderStreamErrorDelegate decoder_stream_error_delegate;
NoopQpackStreamSenderDelegate encoder_stream_sender_delegate;
QpackEncoder qpack_encoder(&decoder_stream_error_delegate,
&encoder_stream_sender_delegate);
std::string encoded_headers =
qpack_encoder.EncodeHeaderList(/* stream_id = */ 0, &headers);
header_size = encoded_headers.size();
}
// Test the AckNotifier's ability to track multiple packets by making the
// request body exceed the size of a single packet.
std::string request_string = "a request body bigger than one packet" +
std::string(kMaxOutgoingPacketSize, '.');
// The TestAckListener will cause a failure if not notified.
QuicReferenceCountedPointer<TestAckListener> ack_listener(
new TestAckListener(header_size + request_string.length()));
// Send the request, and register the delegate for ACKs.
client_->SendData(request_string, true, ack_listener);
client_->WaitForResponse();
EXPECT_EQ(kFooResponseBody, client_->response_body());
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
// Send another request to flush out any pending ACKs on the server.
client_->SendSynchronousRequest("/bar");
// Make sure the delegate does get the notification it expects.
while (!ack_listener->has_been_notified()) {
// Waits for up to 50 ms.
client_->client()->WaitForEvents();
}
}
// Send a public reset from the server.
TEST_P(EndToEndTestWithTls, ServerSendPublicReset) {
ASSERT_TRUE(Initialize());
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
QuicConnection* client_connection =
client_->client()->client_session()->connection();
if (SupportsIetfQuicWithTls(client_connection->version())) {
// TLS handshake does not support stateless reset token yet.
return;
}
QuicUint128 stateless_reset_token = 0;
if (client_connection->version().handshake_protocol == PROTOCOL_QUIC_CRYPTO) {
QuicConfig* config = client_->client()->session()->config();
EXPECT_TRUE(config->HasReceivedStatelessResetToken());
stateless_reset_token = config->ReceivedStatelessResetToken();
}
// Send the public reset.
QuicConnectionId connection_id = client_connection->connection_id();
QuicPublicResetPacket header;
header.connection_id = connection_id;
QuicFramer framer(server_supported_versions_, QuicTime::Zero(),
Perspective::IS_SERVER, kQuicDefaultConnectionIdLength);
std::unique_ptr<QuicEncryptedPacket> packet;
if (VersionHasIetfInvariantHeader(client_connection->transport_version())) {
packet = framer.BuildIetfStatelessResetPacket(connection_id,
stateless_reset_token);
} else {
packet = framer.BuildPublicResetPacket(header);
}
// We must pause the server's thread in order to call WritePacket without
// race conditions.
server_thread_->Pause();
server_writer_->WritePacket(
packet->data(), packet->length(), server_address_.host(),
client_->client()->network_helper()->GetLatestClientAddress(), nullptr);
server_thread_->Resume();
// The request should fail.
EXPECT_EQ("", client_->SendSynchronousRequest("/foo"));
EXPECT_TRUE(client_->response_headers()->empty());
EXPECT_EQ(QUIC_PUBLIC_RESET, client_->connection_error());
}
// Send a public reset from the server for a different connection ID.
// It should be ignored.
TEST_P(EndToEndTestWithTls, ServerSendPublicResetWithDifferentConnectionId) {
ASSERT_TRUE(Initialize());
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
QuicConnection* client_connection =
client_->client()->client_session()->connection();
if (SupportsIetfQuicWithTls(client_connection->version())) {
// TLS handshake does not support stateless reset token yet.
return;
}
QuicUint128 stateless_reset_token = 0;
if (client_connection->version().handshake_protocol == PROTOCOL_QUIC_CRYPTO) {
QuicConfig* config = client_->client()->session()->config();
EXPECT_TRUE(config->HasReceivedStatelessResetToken());
stateless_reset_token = config->ReceivedStatelessResetToken();
}
// Send the public reset.
QuicConnectionId incorrect_connection_id = TestConnectionId(
TestConnectionIdToUInt64(client_connection->connection_id()) + 1);
QuicPublicResetPacket header;
header.connection_id = incorrect_connection_id;
QuicFramer framer(server_supported_versions_, QuicTime::Zero(),
Perspective::IS_SERVER, kQuicDefaultConnectionIdLength);
std::unique_ptr<QuicEncryptedPacket> packet;
testing::NiceMock<MockQuicConnectionDebugVisitor> visitor;
client_->client()->client_session()->connection()->set_debug_visitor(
&visitor);
if (VersionHasIetfInvariantHeader(client_connection->transport_version())) {
packet = framer.BuildIetfStatelessResetPacket(incorrect_connection_id,
stateless_reset_token);
EXPECT_CALL(visitor, OnIncorrectConnectionId(incorrect_connection_id))
.Times(0);
} else {
packet = framer.BuildPublicResetPacket(header);
EXPECT_CALL(visitor, OnIncorrectConnectionId(incorrect_connection_id))
.Times(1);
}
// We must pause the server's thread in order to call WritePacket without
// race conditions.
server_thread_->Pause();
server_writer_->WritePacket(
packet->data(), packet->length(), server_address_.host(),
client_->client()->network_helper()->GetLatestClientAddress(), nullptr);
server_thread_->Resume();
if (VersionHasIetfInvariantHeader(client_connection->transport_version())) {
// The request should fail. IETF stateless reset does not include connection
// ID.
EXPECT_EQ("", client_->SendSynchronousRequest("/foo"));
EXPECT_TRUE(client_->response_headers()->empty());
EXPECT_EQ(QUIC_PUBLIC_RESET, client_->connection_error());
return;
}
// The connection should be unaffected.
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
client_->client()->client_session()->connection()->set_debug_visitor(nullptr);
}
// Send a public reset from the client for a different connection ID.
// It should be ignored.
TEST_P(EndToEndTestWithTls, ClientSendPublicResetWithDifferentConnectionId) {
ASSERT_TRUE(Initialize());
// Send the public reset.
QuicConnectionId incorrect_connection_id = TestConnectionId(
TestConnectionIdToUInt64(
client_->client()->client_session()->connection()->connection_id()) +
1);
QuicPublicResetPacket header;
header.connection_id = incorrect_connection_id;
QuicFramer framer(server_supported_versions_, QuicTime::Zero(),
Perspective::IS_CLIENT, kQuicDefaultConnectionIdLength);
std::unique_ptr<QuicEncryptedPacket> packet(
framer.BuildPublicResetPacket(header));
client_writer_->WritePacket(
packet->data(), packet->length(),
client_->client()->network_helper()->GetLatestClientAddress().host(),
server_address_, nullptr);
// The connection should be unaffected.
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
}
// Send a version negotiation packet from the server for a different
// connection ID. It should be ignored.
TEST_P(EndToEndTestWithTls,
ServerSendVersionNegotiationWithDifferentConnectionId) {
ASSERT_TRUE(Initialize());
EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
// Send the version negotiation packet.
QuicConnection* client_connection =
client_->client()->client_session()->connection();
QuicConnectionId incorrect_connection_id = TestConnectionId(
TestConnectionIdToUInt64(client_connection->connection_id()) + 1);
std::unique_ptr<QuicEncryptedPacket> packet(
QuicFramer::BuildVersionNegotiationPacket(
incorrect_connection_id, EmptyQuicConnectionId(),
VersionHasIetfInvariantHeader(client_connection->transport_version()),
client_connection->version().HasLengthPrefixedConnectionIds(),
server_supported_versions_));
testing::NiceMock<MockQuicConnectionDebugVisitor> visitor;
client_connection->set_debug_visitor(&visitor);
EXPECT_CALL(visitor, OnIncorrectConnectionId(incorrect_connection_id))
.Times(1);
// We must pause the server's thread in order to call WritePacket without
// race conditions.
server_thread_->Pause();
server_writer_->WritePacket(
packet->data(), packet->length(), server_address_.host(),
client_->client()->network_helper()->GetLatestClientAddress(), nullptr);
server_thread_->Resume();
// The connection should be unaffected.
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
client_connection->set_debug_visitor(nullptr);
}
// A bad header shouldn't tear down the connection, because the receiver can't
// tell the connection ID.
TEST_P(EndToEndTestWithTls, BadPacketHeaderTruncated) {
ASSERT_TRUE(Initialize());
// Start the connection.
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
// Packet with invalid public flags.
char packet[] = {// public flags (8 byte connection_id)
0x3C,
// truncated connection ID
0x11};
client_writer_->WritePacket(
&packet[0], sizeof(packet),
client_->client()->network_helper()->GetLatestClientAddress().host(),
server_address_, nullptr);
// Give the server time to process the packet.
QuicSleep(QuicTime::Delta::FromMilliseconds(100));
// Pause the server so we can access the server's internals without races.
server_thread_->Pause();
QuicDispatcher* dispatcher =