blob: da4241ba23a30ef900cee8e90063713fc538fc17 [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 "quic/core/quic_dispatcher.h"
#include <memory>
#include <ostream>
#include <string>
#include <utility>
#include "absl/base/macros.h"
#include "absl/strings/str_cat.h"
#include "quic/core/chlo_extractor.h"
#include "quic/core/crypto/crypto_handshake.h"
#include "quic/core/crypto/crypto_protocol.h"
#include "quic/core/crypto/quic_crypto_server_config.h"
#include "quic/core/crypto/quic_random.h"
#include "quic/core/frames/quic_new_connection_id_frame.h"
#include "quic/core/quic_config.h"
#include "quic/core/quic_connection.h"
#include "quic/core/quic_connection_id.h"
#include "quic/core/quic_crypto_stream.h"
#include "quic/core/quic_packet_writer_wrapper.h"
#include "quic/core/quic_time_wait_list_manager.h"
#include "quic/core/quic_types.h"
#include "quic/core/quic_utils.h"
#include "quic/core/quic_versions.h"
#include "quic/platform/api/quic_expect_bug.h"
#include "quic/platform/api/quic_flags.h"
#include "quic/platform/api/quic_logging.h"
#include "quic/platform/api/quic_test.h"
#include "quic/test_tools/crypto_test_utils.h"
#include "quic/test_tools/fake_proof_source.h"
#include "quic/test_tools/first_flight.h"
#include "quic/test_tools/mock_quic_time_wait_list_manager.h"
#include "quic/test_tools/quic_buffered_packet_store_peer.h"
#include "quic/test_tools/quic_connection_peer.h"
#include "quic/test_tools/quic_crypto_server_config_peer.h"
#include "quic/test_tools/quic_dispatcher_peer.h"
#include "quic/test_tools/quic_test_utils.h"
#include "quic/test_tools/quic_time_wait_list_manager_peer.h"
#include "quic/tools/quic_simple_crypto_server_stream_helper.h"
#include "common/test_tools/quiche_test_utils.h"
using testing::_;
using testing::ByMove;
using testing::Eq;
using testing::InSequence;
using testing::Invoke;
using testing::NiceMock;
using testing::Return;
using testing::WithArg;
using testing::WithoutArgs;
static const size_t kDefaultMaxConnectionsInStore = 100;
static const size_t kMaxConnectionsWithoutCHLO =
kDefaultMaxConnectionsInStore / 2;
static const int16_t kMaxNumSessionsToCreate = 16;
namespace quic {
namespace test {
namespace {
class TestQuicSpdyServerSession : public QuicServerSessionBase {
public:
TestQuicSpdyServerSession(const QuicConfig& config,
QuicConnection* connection,
const QuicCryptoServerConfig* crypto_config,
QuicCompressedCertsCache* compressed_certs_cache)
: QuicServerSessionBase(config,
CurrentSupportedVersions(),
connection,
nullptr,
nullptr,
crypto_config,
compressed_certs_cache) {
Initialize();
}
TestQuicSpdyServerSession(const TestQuicSpdyServerSession&) = delete;
TestQuicSpdyServerSession& operator=(const TestQuicSpdyServerSession&) =
delete;
~TestQuicSpdyServerSession() override { DeleteConnection(); }
MOCK_METHOD(void,
OnConnectionClosed,
(const QuicConnectionCloseFrame& frame,
ConnectionCloseSource source),
(override));
MOCK_METHOD(QuicSpdyStream*,
CreateIncomingStream,
(QuicStreamId id),
(override));
MOCK_METHOD(QuicSpdyStream*,
CreateIncomingStream,
(PendingStream*),
(override));
MOCK_METHOD(QuicSpdyStream*,
CreateOutgoingBidirectionalStream,
(),
(override));
MOCK_METHOD(QuicSpdyStream*,
CreateOutgoingUnidirectionalStream,
(),
(override));
std::unique_ptr<QuicCryptoServerStreamBase> CreateQuicCryptoServerStream(
const QuicCryptoServerConfig* crypto_config,
QuicCompressedCertsCache* compressed_certs_cache) override {
return CreateCryptoServerStream(crypto_config, compressed_certs_cache, this,
stream_helper());
}
QuicCryptoServerStreamBase::Helper* stream_helper() {
return QuicServerSessionBase::stream_helper();
}
};
class TestDispatcher : public QuicDispatcher {
public:
TestDispatcher(const QuicConfig* config,
const QuicCryptoServerConfig* crypto_config,
QuicVersionManager* version_manager,
QuicRandom* random)
: QuicDispatcher(config,
crypto_config,
version_manager,
std::make_unique<MockQuicConnectionHelper>(),
std::unique_ptr<QuicCryptoServerStreamBase::Helper>(
new QuicSimpleCryptoServerStreamHelper()),
std::make_unique<MockAlarmFactory>(),
kQuicDefaultConnectionIdLength),
random_(random) {}
MOCK_METHOD(std::unique_ptr<QuicSession>,
CreateQuicSession,
(QuicConnectionId connection_id,
const QuicSocketAddress& self_address,
const QuicSocketAddress& peer_address,
absl::string_view alpn,
const quic::ParsedQuicVersion& version),
(override));
MOCK_METHOD(bool,
ShouldCreateOrBufferPacketForConnection,
(const ReceivedPacketInfo& packet_info),
(override));
struct TestQuicPerPacketContext : public QuicPerPacketContext {
std::string custom_packet_context;
};
std::unique_ptr<QuicPerPacketContext> GetPerPacketContext() const override {
auto test_context = std::make_unique<TestQuicPerPacketContext>();
test_context->custom_packet_context = custom_packet_context_;
return std::move(test_context);
}
void RestorePerPacketContext(
std::unique_ptr<QuicPerPacketContext> context) override {
TestQuicPerPacketContext* test_context =
static_cast<TestQuicPerPacketContext*>(context.get());
custom_packet_context_ = test_context->custom_packet_context;
}
std::string custom_packet_context_;
using QuicDispatcher::SetAllowShortInitialServerConnectionIds;
using QuicDispatcher::writer;
QuicRandom* random_;
};
// A Connection class which unregisters the session from the dispatcher when
// sending connection close.
// It'd be slightly more realistic to do this from the Session but it would
// involve a lot more mocking.
class MockServerConnection : public MockQuicConnection {
public:
MockServerConnection(QuicConnectionId connection_id,
MockQuicConnectionHelper* helper,
MockAlarmFactory* alarm_factory,
QuicDispatcher* dispatcher)
: MockQuicConnection(connection_id,
helper,
alarm_factory,
Perspective::IS_SERVER),
dispatcher_(dispatcher),
active_connection_ids_({connection_id}) {}
void AddNewConnectionId(QuicConnectionId id) {
dispatcher_->OnNewConnectionIdSent(active_connection_ids_.back(), id);
QuicConnectionPeer::SetServerConnectionId(this, id);
active_connection_ids_.push_back(id);
}
void RetireConnectionId(QuicConnectionId id) {
auto it = std::find(active_connection_ids_.begin(),
active_connection_ids_.end(), id);
QUICHE_DCHECK(it != active_connection_ids_.end());
dispatcher_->OnConnectionIdRetired(id);
active_connection_ids_.erase(it);
}
std::vector<QuicConnectionId> GetActiveServerConnectionIds() const override {
return active_connection_ids_;
}
void UnregisterOnConnectionClosed() {
QUIC_LOG(ERROR) << "Unregistering " << connection_id();
dispatcher_->OnConnectionClosed(connection_id(), QUIC_NO_ERROR,
"Unregistering.",
ConnectionCloseSource::FROM_SELF);
}
private:
QuicDispatcher* dispatcher_;
std::vector<QuicConnectionId> active_connection_ids_;
};
class QuicDispatcherTestBase : public QuicTestWithParam<ParsedQuicVersion> {
public:
QuicDispatcherTestBase()
: QuicDispatcherTestBase(crypto_test_utils::ProofSourceForTesting()) {}
explicit QuicDispatcherTestBase(std::unique_ptr<ProofSource> proof_source)
: version_(GetParam()),
version_manager_(AllSupportedVersions()),
crypto_config_(QuicCryptoServerConfig::TESTING,
QuicRandom::GetInstance(),
std::move(proof_source),
KeyExchangeSource::Default()),
server_address_(QuicIpAddress::Any4(), 5),
dispatcher_(
new NiceMock<TestDispatcher>(&config_,
&crypto_config_,
&version_manager_,
mock_helper_.GetRandomGenerator())),
time_wait_list_manager_(nullptr),
session1_(nullptr),
session2_(nullptr),
store_(nullptr),
connection_id_(1) {}
void SetUp() override {
dispatcher_->InitializeWithWriter(new NiceMock<MockPacketWriter>());
// Set the counter to some value to start with.
QuicDispatcherPeer::set_new_sessions_allowed_per_event_loop(
dispatcher_.get(), kMaxNumSessionsToCreate);
ON_CALL(*dispatcher_, ShouldCreateOrBufferPacketForConnection(_))
.WillByDefault(Return(true));
}
MockQuicConnection* connection1() {
if (session1_ == nullptr) {
return nullptr;
}
return reinterpret_cast<MockQuicConnection*>(session1_->connection());
}
MockQuicConnection* connection2() {
if (session2_ == nullptr) {
return nullptr;
}
return reinterpret_cast<MockQuicConnection*>(session2_->connection());
}
// Process a packet with an 8 byte connection id,
// 6 byte packet number, default path id, and packet number 1,
// using the version under test.
void ProcessPacket(QuicSocketAddress peer_address,
QuicConnectionId server_connection_id,
bool has_version_flag,
const std::string& data) {
ProcessPacket(peer_address, server_connection_id, has_version_flag, data,
CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER);
}
// Process a packet with a default path id, and packet number 1,
// using the version under test.
void ProcessPacket(QuicSocketAddress peer_address,
QuicConnectionId server_connection_id,
bool has_version_flag,
const std::string& data,
QuicConnectionIdIncluded server_connection_id_included,
QuicPacketNumberLength packet_number_length) {
ProcessPacket(peer_address, server_connection_id, has_version_flag, data,
server_connection_id_included, packet_number_length, 1);
}
// Process a packet using the version under test.
void ProcessPacket(QuicSocketAddress peer_address,
QuicConnectionId server_connection_id,
bool has_version_flag,
const std::string& data,
QuicConnectionIdIncluded server_connection_id_included,
QuicPacketNumberLength packet_number_length,
uint64_t packet_number) {
ProcessPacket(peer_address, server_connection_id, has_version_flag,
version_, data, true, server_connection_id_included,
packet_number_length, packet_number);
}
// Processes a packet.
void ProcessPacket(QuicSocketAddress peer_address,
QuicConnectionId server_connection_id,
bool has_version_flag,
ParsedQuicVersion version,
const std::string& data,
bool full_padding,
QuicConnectionIdIncluded server_connection_id_included,
QuicPacketNumberLength packet_number_length,
uint64_t packet_number) {
ProcessPacket(peer_address, server_connection_id, EmptyQuicConnectionId(),
has_version_flag, version, data, full_padding,
server_connection_id_included, CONNECTION_ID_ABSENT,
packet_number_length, packet_number);
}
// Processes a packet.
void ProcessPacket(QuicSocketAddress peer_address,
QuicConnectionId server_connection_id,
QuicConnectionId client_connection_id,
bool has_version_flag,
ParsedQuicVersion version,
const std::string& data,
bool full_padding,
QuicConnectionIdIncluded server_connection_id_included,
QuicConnectionIdIncluded client_connection_id_included,
QuicPacketNumberLength packet_number_length,
uint64_t packet_number) {
ParsedQuicVersionVector versions(SupportedVersions(version));
std::unique_ptr<QuicEncryptedPacket> packet(ConstructEncryptedPacket(
server_connection_id, client_connection_id, has_version_flag, false,
packet_number, data, full_padding, server_connection_id_included,
client_connection_id_included, packet_number_length, &versions));
std::unique_ptr<QuicReceivedPacket> received_packet(
ConstructReceivedPacket(*packet, mock_helper_.GetClock()->Now()));
ProcessReceivedPacket(std::move(received_packet), peer_address, version,
server_connection_id);
}
void ProcessReceivedPacket(
std::unique_ptr<QuicReceivedPacket> received_packet,
const QuicSocketAddress& peer_address,
const ParsedQuicVersion& version,
const QuicConnectionId& server_connection_id) {
if (version.UsesQuicCrypto() &&
ChloExtractor::Extract(*received_packet, version, {}, nullptr,
server_connection_id.length())) {
// Add CHLO packet to the beginning to be verified first, because it is
// also processed first by new session.
data_connection_map_[server_connection_id].push_front(
std::string(received_packet->data(), received_packet->length()));
} else {
// For non-CHLO, always append to last.
data_connection_map_[server_connection_id].push_back(
std::string(received_packet->data(), received_packet->length()));
}
dispatcher_->ProcessPacket(server_address_, peer_address, *received_packet);
}
void ValidatePacket(QuicConnectionId conn_id,
const QuicEncryptedPacket& packet) {
EXPECT_EQ(data_connection_map_[conn_id].front().length(),
packet.AsStringPiece().length());
EXPECT_EQ(data_connection_map_[conn_id].front(), packet.AsStringPiece());
data_connection_map_[conn_id].pop_front();
}
std::unique_ptr<QuicSession> CreateSession(
TestDispatcher* dispatcher,
const QuicConfig& config,
QuicConnectionId connection_id,
const QuicSocketAddress& /*peer_address*/,
MockQuicConnectionHelper* helper,
MockAlarmFactory* alarm_factory,
const QuicCryptoServerConfig* crypto_config,
QuicCompressedCertsCache* compressed_certs_cache,
TestQuicSpdyServerSession** session_ptr) {
MockServerConnection* connection = new MockServerConnection(
connection_id, helper, alarm_factory, dispatcher);
connection->SetQuicPacketWriter(dispatcher->writer(),
/*owns_writer=*/false);
auto session = std::make_unique<TestQuicSpdyServerSession>(
config, connection, crypto_config, compressed_certs_cache);
*session_ptr = session.get();
connection->set_visitor(session.get());
ON_CALL(*connection, CloseConnection(_, _, _))
.WillByDefault(WithoutArgs(Invoke(
connection, &MockServerConnection::UnregisterOnConnectionClosed)));
return session;
}
void CreateTimeWaitListManager() {
time_wait_list_manager_ = new MockTimeWaitListManager(
QuicDispatcherPeer::GetWriter(dispatcher_.get()), dispatcher_.get(),
mock_helper_.GetClock(), &mock_alarm_factory_);
// dispatcher_ takes the ownership of time_wait_list_manager_.
QuicDispatcherPeer::SetTimeWaitListManager(dispatcher_.get(),
time_wait_list_manager_);
}
std::string SerializeCHLO() {
CryptoHandshakeMessage client_hello;
client_hello.set_tag(kCHLO);
client_hello.SetStringPiece(kALPN, ExpectedAlpn());
return std::string(client_hello.GetSerialized().AsStringPiece());
}
void ProcessUndecryptableEarlyPacket(
const QuicSocketAddress& peer_address,
const QuicConnectionId& server_connection_id) {
ProcessUndecryptableEarlyPacket(version_, peer_address,
server_connection_id);
}
void ProcessUndecryptableEarlyPacket(
const ParsedQuicVersion& version,
const QuicSocketAddress& peer_address,
const QuicConnectionId& server_connection_id) {
std::unique_ptr<QuicEncryptedPacket> encrypted_packet =
GetUndecryptableEarlyPacket(version, server_connection_id);
std::unique_ptr<QuicReceivedPacket> received_packet(ConstructReceivedPacket(
*encrypted_packet, mock_helper_.GetClock()->Now()));
ProcessReceivedPacket(std::move(received_packet), peer_address, version,
server_connection_id);
}
void ProcessFirstFlight(const QuicSocketAddress& peer_address,
const QuicConnectionId& server_connection_id) {
ProcessFirstFlight(version_, peer_address, server_connection_id);
}
void ProcessFirstFlight(const ParsedQuicVersion& version,
const QuicSocketAddress& peer_address,
const QuicConnectionId& server_connection_id) {
ProcessFirstFlight(version, peer_address, server_connection_id,
EmptyQuicConnectionId());
}
void ProcessFirstFlight(const ParsedQuicVersion& version,
const QuicSocketAddress& peer_address,
const QuicConnectionId& server_connection_id,
const QuicConnectionId& client_connection_id) {
std::vector<std::unique_ptr<QuicReceivedPacket>> packets =
GetFirstFlightOfPackets(version, server_connection_id,
client_connection_id);
for (auto&& packet : packets) {
ProcessReceivedPacket(std::move(packet), peer_address, version,
server_connection_id);
}
}
std::string ExpectedAlpnForVersion(ParsedQuicVersion version) {
return AlpnForVersion(version);
}
std::string ExpectedAlpn() { return ExpectedAlpnForVersion(version_); }
void MarkSession1Deleted() { session1_ = nullptr; }
void VerifyVersionSupported(ParsedQuicVersion version) {
QuicConnectionId connection_id = TestConnectionId(++connection_id_);
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
EXPECT_CALL(*dispatcher_,
CreateQuicSession(connection_id, _, client_address,
Eq(ExpectedAlpnForVersion(version)), _))
.WillOnce(Return(ByMove(CreateSession(
dispatcher_.get(), config_, connection_id, client_address,
&mock_helper_, &mock_alarm_factory_, &crypto_config_,
QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))));
EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
ProcessUdpPacket(_, _, _))
.WillOnce(WithArg<2>(
Invoke([this, connection_id](const QuicEncryptedPacket& packet) {
ValidatePacket(connection_id, packet);
})));
EXPECT_CALL(*dispatcher_,
ShouldCreateOrBufferPacketForConnection(
ReceivedPacketInfoConnectionIdEquals(connection_id)));
ProcessFirstFlight(version, client_address, connection_id);
}
void VerifyVersionNotSupported(ParsedQuicVersion version) {
QuicConnectionId connection_id = TestConnectionId(++connection_id_);
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
EXPECT_CALL(*dispatcher_,
CreateQuicSession(connection_id, _, client_address, _, _))
.Times(0);
ProcessFirstFlight(version, client_address, connection_id);
}
void TestTlsMultiPacketClientHello(bool add_reordering);
void TestVersionNegotiationForUnknownVersionInvalidShortInitialConnectionId(
const QuicConnectionId& server_connection_id,
const QuicConnectionId& client_connection_id);
ParsedQuicVersion version_;
MockQuicConnectionHelper mock_helper_;
MockAlarmFactory mock_alarm_factory_;
QuicConfig config_;
QuicVersionManager version_manager_;
QuicCryptoServerConfig crypto_config_;
QuicSocketAddress server_address_;
std::unique_ptr<NiceMock<TestDispatcher>> dispatcher_;
MockTimeWaitListManager* time_wait_list_manager_;
TestQuicSpdyServerSession* session1_;
TestQuicSpdyServerSession* session2_;
std::map<QuicConnectionId, std::list<std::string>> data_connection_map_;
QuicBufferedPacketStore* store_;
uint64_t connection_id_;
};
class QuicDispatcherTestAllVersions : public QuicDispatcherTestBase {};
class QuicDispatcherTestOneVersion : public QuicDispatcherTestBase {};
INSTANTIATE_TEST_SUITE_P(QuicDispatcherTestsAllVersions,
QuicDispatcherTestAllVersions,
::testing::ValuesIn(CurrentSupportedVersions()),
::testing::PrintToStringParamName());
INSTANTIATE_TEST_SUITE_P(QuicDispatcherTestsOneVersion,
QuicDispatcherTestOneVersion,
::testing::Values(CurrentSupportedVersions().front()),
::testing::PrintToStringParamName());
TEST_P(QuicDispatcherTestAllVersions, TlsClientHelloCreatesSession) {
if (version_.UsesQuicCrypto()) {
return;
}
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
EXPECT_CALL(*dispatcher_,
CreateQuicSession(TestConnectionId(1), _, client_address,
Eq(ExpectedAlpn()), _))
.WillOnce(Return(ByMove(CreateSession(
dispatcher_.get(), config_, TestConnectionId(1), client_address,
&mock_helper_, &mock_alarm_factory_, &crypto_config_,
QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))));
EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
ProcessUdpPacket(_, _, _))
.WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) {
ValidatePacket(TestConnectionId(1), packet);
})));
EXPECT_CALL(*dispatcher_,
ShouldCreateOrBufferPacketForConnection(
ReceivedPacketInfoConnectionIdEquals(TestConnectionId(1))));
ProcessFirstFlight(client_address, TestConnectionId(1));
}
void QuicDispatcherTestBase::TestTlsMultiPacketClientHello(
bool add_reordering) {
if (!version_.UsesTls()) {
return;
}
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
QuicConnectionId server_connection_id = TestConnectionId();
QuicConfig client_config = DefaultQuicConfig();
// Add a 2000-byte custom parameter to increase the length of the CHLO.
constexpr auto kCustomParameterId =
static_cast<TransportParameters::TransportParameterId>(0xff33);
std::string kCustomParameterValue(2000, '-');
client_config.custom_transport_parameters_to_send()[kCustomParameterId] =
kCustomParameterValue;
std::vector<std::unique_ptr<QuicReceivedPacket>> packets =
GetFirstFlightOfPackets(version_, client_config, server_connection_id);
ASSERT_EQ(packets.size(), 2u);
if (add_reordering) {
std::swap(packets[0], packets[1]);
}
// Processing the first packet should not create a new session.
EXPECT_CALL(*dispatcher_,
ShouldCreateOrBufferPacketForConnection(
ReceivedPacketInfoConnectionIdEquals(server_connection_id)));
ProcessReceivedPacket(std::move(packets[0]), client_address, version_,
server_connection_id);
EXPECT_EQ(dispatcher_->NumSessions(), 0u)
<< "No session should be created before the rest of the CHLO arrives.";
// Processing the second packet should create the new session.
EXPECT_CALL(*dispatcher_,
CreateQuicSession(server_connection_id, _, client_address,
Eq(ExpectedAlpn()), _))
.WillOnce(Return(ByMove(CreateSession(
dispatcher_.get(), config_, server_connection_id, client_address,
&mock_helper_, &mock_alarm_factory_, &crypto_config_,
QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))));
EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
ProcessUdpPacket(_, _, _))
.Times(2);
ProcessReceivedPacket(std::move(packets[1]), client_address, version_,
server_connection_id);
EXPECT_EQ(dispatcher_->NumSessions(), 1u);
}
TEST_P(QuicDispatcherTestAllVersions, TlsMultiPacketClientHello) {
TestTlsMultiPacketClientHello(/*add_reordering=*/false);
}
TEST_P(QuicDispatcherTestAllVersions, TlsMultiPacketClientHelloWithReordering) {
TestTlsMultiPacketClientHello(/*add_reordering=*/true);
}
TEST_P(QuicDispatcherTestAllVersions, LegacyVersionEncapsulation) {
if (!version_.HasLongHeaderLengths()) {
// Decapsulating Legacy Version Encapsulation packets from these versions
// is not currently supported in QuicDispatcher.
return;
}
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
QuicConnectionId server_connection_id = TestConnectionId();
QuicConfig client_config = DefaultQuicConfig();
client_config.SetClientConnectionOptions(QuicTagVector{kQLVE});
std::vector<std::unique_ptr<QuicReceivedPacket>> packets =
GetFirstFlightOfPackets(version_, client_config, server_connection_id);
ASSERT_EQ(packets.size(), 1u);
// Validate that Legacy Version Encapsulation is actually being used by
// checking the version of the packet before processing it.
PacketHeaderFormat format = IETF_QUIC_LONG_HEADER_PACKET;
QuicLongHeaderType long_packet_type;
bool version_present;
bool has_length_prefix;
QuicVersionLabel version_label;
ParsedQuicVersion parsed_version = ParsedQuicVersion::Unsupported();
QuicConnectionId destination_connection_id, source_connection_id;
bool retry_token_present;
absl::string_view retry_token;
std::string detailed_error;
const QuicErrorCode error = QuicFramer::ParsePublicHeaderDispatcher(
QuicEncryptedPacket(packets[0]->data(), packets[0]->length()),
kQuicDefaultConnectionIdLength, &format, &long_packet_type,
&version_present, &has_length_prefix, &version_label, &parsed_version,
&destination_connection_id, &source_connection_id, &retry_token_present,
&retry_token, &detailed_error);
ASSERT_THAT(error, IsQuicNoError()) << detailed_error;
EXPECT_EQ(format, GOOGLE_QUIC_PACKET);
EXPECT_TRUE(version_present);
EXPECT_FALSE(has_length_prefix);
EXPECT_EQ(parsed_version, LegacyVersionForEncapsulation());
EXPECT_EQ(destination_connection_id, server_connection_id);
EXPECT_EQ(source_connection_id, EmptyQuicConnectionId());
EXPECT_FALSE(retry_token_present);
EXPECT_TRUE(detailed_error.empty());
// Processing the packet should create a new session.
EXPECT_CALL(*dispatcher_,
CreateQuicSession(server_connection_id, _, client_address,
Eq(ExpectedAlpn()), _))
.WillOnce(Return(ByMove(CreateSession(
dispatcher_.get(), config_, server_connection_id, client_address,
&mock_helper_, &mock_alarm_factory_, &crypto_config_,
QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))));
EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
ProcessUdpPacket(_, _, _))
.Times(2);
ProcessReceivedPacket(packets[0]->Clone(), client_address, version_,
server_connection_id);
EXPECT_EQ(dispatcher_->NumSessions(), 1u);
// Processing the same packet a second time should also be routed by the
// dispatcher to the right connection (we expect ProcessUdpPacket to be
// called twice, see the EXPECT_CALL above).
ProcessReceivedPacket(std::move(packets[0]), client_address, version_,
server_connection_id);
}
TEST_P(QuicDispatcherTestAllVersions, ProcessPackets) {
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
EXPECT_CALL(*dispatcher_,
CreateQuicSession(TestConnectionId(1), _, client_address,
Eq(ExpectedAlpn()), _))
.WillOnce(Return(ByMove(CreateSession(
dispatcher_.get(), config_, TestConnectionId(1), client_address,
&mock_helper_, &mock_alarm_factory_, &crypto_config_,
QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))));
EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
ProcessUdpPacket(_, _, _))
.WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) {
ValidatePacket(TestConnectionId(1), packet);
})));
EXPECT_CALL(*dispatcher_,
ShouldCreateOrBufferPacketForConnection(
ReceivedPacketInfoConnectionIdEquals(TestConnectionId(1))));
ProcessFirstFlight(client_address, TestConnectionId(1));
EXPECT_CALL(*dispatcher_,
CreateQuicSession(TestConnectionId(2), _, client_address,
Eq(ExpectedAlpn()), _))
.WillOnce(Return(ByMove(CreateSession(
dispatcher_.get(), config_, TestConnectionId(2), client_address,
&mock_helper_, &mock_alarm_factory_, &crypto_config_,
QuicDispatcherPeer::GetCache(dispatcher_.get()), &session2_))));
EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session2_->connection()),
ProcessUdpPacket(_, _, _))
.WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) {
ValidatePacket(TestConnectionId(2), packet);
})));
EXPECT_CALL(*dispatcher_,
ShouldCreateOrBufferPacketForConnection(
ReceivedPacketInfoConnectionIdEquals(TestConnectionId(2))));
ProcessFirstFlight(client_address, TestConnectionId(2));
EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
ProcessUdpPacket(_, _, _))
.Times(1)
.WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) {
ValidatePacket(TestConnectionId(1), packet);
})));
ProcessPacket(client_address, TestConnectionId(1), false, "data");
}
// Regression test of b/93325907.
TEST_P(QuicDispatcherTestAllVersions, DispatcherDoesNotRejectPacketNumberZero) {
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
EXPECT_CALL(*dispatcher_,
CreateQuicSession(TestConnectionId(1), _, client_address,
Eq(ExpectedAlpn()), _))
.WillOnce(Return(ByMove(CreateSession(
dispatcher_.get(), config_, TestConnectionId(1), client_address,
&mock_helper_, &mock_alarm_factory_, &crypto_config_,
QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))));
// Verify both packets 1 and 2 are processed by connection 1.
EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
ProcessUdpPacket(_, _, _))
.Times(2)
.WillRepeatedly(
WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) {
ValidatePacket(TestConnectionId(1), packet);
})));
EXPECT_CALL(*dispatcher_,
ShouldCreateOrBufferPacketForConnection(
ReceivedPacketInfoConnectionIdEquals(TestConnectionId(1))));
ProcessFirstFlight(client_address, TestConnectionId(1));
// Packet number 256 with packet number length 1 would be considered as 0 in
// dispatcher.
ProcessPacket(client_address, TestConnectionId(1), false, version_, "", true,
CONNECTION_ID_PRESENT, PACKET_1BYTE_PACKET_NUMBER, 256);
}
TEST_P(QuicDispatcherTestOneVersion, StatelessVersionNegotiation) {
CreateTimeWaitListManager();
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0);
EXPECT_CALL(
*time_wait_list_manager_,
SendVersionNegotiationPacket(TestConnectionId(1), _, _, _, _, _, _, _))
.Times(1);
ProcessFirstFlight(QuicVersionReservedForNegotiation(), client_address,
TestConnectionId(1));
}
TEST_P(QuicDispatcherTestOneVersion,
StatelessVersionNegotiationWithVeryLongConnectionId) {
QuicConnectionId connection_id = QuicUtils::CreateRandomConnectionId(33);
CreateTimeWaitListManager();
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0);
EXPECT_CALL(*time_wait_list_manager_,
SendVersionNegotiationPacket(connection_id, _, _, _, _, _, _, _))
.Times(1);
ProcessFirstFlight(QuicVersionReservedForNegotiation(), client_address,
connection_id);
}
TEST_P(QuicDispatcherTestOneVersion,
StatelessVersionNegotiationWithClientConnectionId) {
CreateTimeWaitListManager();
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0);
EXPECT_CALL(*time_wait_list_manager_,
SendVersionNegotiationPacket(
TestConnectionId(1), TestConnectionId(2), _, _, _, _, _, _))
.Times(1);
ProcessFirstFlight(QuicVersionReservedForNegotiation(), client_address,
TestConnectionId(1), TestConnectionId(2));
}
TEST_P(QuicDispatcherTestOneVersion, NoVersionNegotiationWithSmallPacket) {
CreateTimeWaitListManager();
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0);
EXPECT_CALL(*time_wait_list_manager_,
SendVersionNegotiationPacket(_, _, _, _, _, _, _, _))
.Times(0);
std::string chlo = SerializeCHLO() + std::string(1200, 'a');
// Truncate to 1100 bytes of payload which results in a packet just
// under 1200 bytes after framing, packet, and encryption overhead.
QUICHE_DCHECK_LE(1200u, chlo.length());
std::string truncated_chlo = chlo.substr(0, 1100);
QUICHE_DCHECK_EQ(1100u, truncated_chlo.length());
ProcessPacket(client_address, TestConnectionId(1), true,
QuicVersionReservedForNegotiation(), truncated_chlo, false,
CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, 1);
}
// Disabling CHLO size validation allows the dispatcher to send version
// negotiation packets in response to a CHLO that is otherwise too small.
TEST_P(QuicDispatcherTestOneVersion,
VersionNegotiationWithoutChloSizeValidation) {
crypto_config_.set_validate_chlo_size(false);
CreateTimeWaitListManager();
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0);
EXPECT_CALL(*time_wait_list_manager_,
SendVersionNegotiationPacket(_, _, _, _, _, _, _, _))
.Times(1);
std::string chlo = SerializeCHLO() + std::string(1200, 'a');
// Truncate to 1100 bytes of payload which results in a packet just
// under 1200 bytes after framing, packet, and encryption overhead.
QUICHE_DCHECK_LE(1200u, chlo.length());
std::string truncated_chlo = chlo.substr(0, 1100);
QUICHE_DCHECK_EQ(1100u, truncated_chlo.length());
ProcessPacket(client_address, TestConnectionId(1), true,
QuicVersionReservedForNegotiation(), truncated_chlo, true,
CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, 1);
}
TEST_P(QuicDispatcherTestAllVersions, Shutdown) {
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
EXPECT_CALL(*dispatcher_,
CreateQuicSession(_, _, client_address, Eq(ExpectedAlpn()), _))
.WillOnce(Return(ByMove(CreateSession(
dispatcher_.get(), config_, TestConnectionId(1), client_address,
&mock_helper_, &mock_alarm_factory_, &crypto_config_,
QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))));
EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
ProcessUdpPacket(_, _, _))
.WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) {
ValidatePacket(TestConnectionId(1), packet);
})));
EXPECT_CALL(*dispatcher_,
ShouldCreateOrBufferPacketForConnection(
ReceivedPacketInfoConnectionIdEquals(TestConnectionId(1))));
ProcessFirstFlight(client_address, TestConnectionId(1));
EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
CloseConnection(QUIC_PEER_GOING_AWAY, _, _));
dispatcher_->Shutdown();
}
TEST_P(QuicDispatcherTestAllVersions, TimeWaitListManager) {
CreateTimeWaitListManager();
// Create a new session.
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
QuicConnectionId connection_id = TestConnectionId(1);
EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, _, client_address,
Eq(ExpectedAlpn()), _))
.WillOnce(Return(ByMove(CreateSession(
dispatcher_.get(), config_, connection_id, client_address,
&mock_helper_, &mock_alarm_factory_, &crypto_config_,
QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))));
EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
ProcessUdpPacket(_, _, _))
.WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) {
ValidatePacket(TestConnectionId(1), packet);
})));
EXPECT_CALL(*dispatcher_,
ShouldCreateOrBufferPacketForConnection(
ReceivedPacketInfoConnectionIdEquals(TestConnectionId(1))));
ProcessFirstFlight(client_address, connection_id);
// Now close the connection, which should add it to the time wait list.
session1_->connection()->CloseConnection(
QUIC_INVALID_VERSION,
"Server: Packet 2 without version flag before version negotiated.",
ConnectionCloseBehavior::SILENT_CLOSE);
EXPECT_TRUE(time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id));
// Dispatcher forwards subsequent packets for this connection_id to the time
// wait list manager.
EXPECT_CALL(*time_wait_list_manager_,
ProcessPacket(_, _, connection_id, _, _, _))
.Times(1);
EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _))
.Times(0);
ProcessPacket(client_address, connection_id, true, "data");
}
TEST_P(QuicDispatcherTestAllVersions, NoVersionPacketToTimeWaitListManager) {
CreateTimeWaitListManager();
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
QuicConnectionId connection_id = TestConnectionId(1);
// Dispatcher forwards all packets for this connection_id to the time wait
// list manager.
EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0);
EXPECT_CALL(*time_wait_list_manager_,
ProcessPacket(_, _, connection_id, _, _, _))
.Times(0);
EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _))
.Times(0);
EXPECT_CALL(*time_wait_list_manager_, SendPublicReset(_, _, _, _, _, _))
.Times(1);
ProcessPacket(client_address, connection_id, /*has_version_flag=*/false,
"data");
}
TEST_P(QuicDispatcherTestAllVersions,
DonotTimeWaitPacketsWithUnknownConnectionIdAndNoVersion) {
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
CreateTimeWaitListManager();
char short_packet[21] = {0x70, 0xa7, 0x02, 0x6b};
QuicReceivedPacket packet(short_packet, 21, QuicTime::Zero());
char valid_size_packet[23] = {0x70, 0xa7, 0x02, 0x6c};
QuicReceivedPacket packet2(valid_size_packet, 23, QuicTime::Zero());
EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0);
EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _, _))
.Times(0);
EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _))
.Times(0);
// Verify small packet is silently dropped.
EXPECT_CALL(*time_wait_list_manager_, SendPublicReset(_, _, _, _, _, _))
.Times(0);
dispatcher_->ProcessPacket(server_address_, client_address, packet);
EXPECT_CALL(*time_wait_list_manager_, SendPublicReset(_, _, _, _, _, _))
.Times(1);
dispatcher_->ProcessPacket(server_address_, client_address, packet2);
}
// Makes sure nine-byte connection IDs are replaced by 8-byte ones.
TEST_P(QuicDispatcherTestAllVersions, LongConnectionIdLengthReplaced) {
if (!version_.AllowsVariableLengthConnectionIds()) {
// When variable length connection IDs are not supported, the connection
// fails. See StrayPacketTruncatedConnectionId.
return;
}
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
QuicConnectionId bad_connection_id = TestConnectionIdNineBytesLong(2);
QuicConnectionId fixed_connection_id =
QuicUtils::CreateReplacementConnectionId(bad_connection_id);
EXPECT_CALL(*dispatcher_,
CreateQuicSession(fixed_connection_id, _, client_address,
Eq(ExpectedAlpn()), _))
.WillOnce(Return(ByMove(CreateSession(
dispatcher_.get(), config_, fixed_connection_id, client_address,
&mock_helper_, &mock_alarm_factory_, &crypto_config_,
QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))));
EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
ProcessUdpPacket(_, _, _))
.WillOnce(WithArg<2>(
Invoke([this, bad_connection_id](const QuicEncryptedPacket& packet) {
ValidatePacket(bad_connection_id, packet);
})));
EXPECT_CALL(*dispatcher_,
ShouldCreateOrBufferPacketForConnection(
ReceivedPacketInfoConnectionIdEquals(bad_connection_id)));
ProcessFirstFlight(client_address, bad_connection_id);
}
// Makes sure zero-byte connection IDs are replaced by 8-byte ones.
TEST_P(QuicDispatcherTestAllVersions, InvalidShortConnectionIdLengthReplaced) {
if (!version_.AllowsVariableLengthConnectionIds()) {
// When variable length connection IDs are not supported, the connection
// fails. See StrayPacketTruncatedConnectionId.
return;
}
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
QuicConnectionId bad_connection_id = EmptyQuicConnectionId();
QuicConnectionId fixed_connection_id =
QuicUtils::CreateReplacementConnectionId(bad_connection_id);
// Disable validation of invalid short connection IDs.
dispatcher_->SetAllowShortInitialServerConnectionIds(true);
// Note that StrayPacketTruncatedConnectionId covers the case where the
// validation is still enabled.
EXPECT_CALL(*dispatcher_,
CreateQuicSession(fixed_connection_id, _, client_address,
Eq(ExpectedAlpn()), _))
.WillOnce(Return(ByMove(CreateSession(
dispatcher_.get(), config_, fixed_connection_id, client_address,
&mock_helper_, &mock_alarm_factory_, &crypto_config_,
QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))));
EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
ProcessUdpPacket(_, _, _))
.WillOnce(WithArg<2>(
Invoke([this, bad_connection_id](const QuicEncryptedPacket& packet) {
ValidatePacket(bad_connection_id, packet);
})));
EXPECT_CALL(*dispatcher_,
ShouldCreateOrBufferPacketForConnection(
ReceivedPacketInfoConnectionIdEquals(bad_connection_id)));
ProcessFirstFlight(client_address, bad_connection_id);
}
// Makes sure TestConnectionId(1) creates a new connection and
// TestConnectionIdNineBytesLong(2) gets replaced.
TEST_P(QuicDispatcherTestAllVersions, MixGoodAndBadConnectionIdLengthPackets) {
if (!version_.AllowsVariableLengthConnectionIds()) {
return;
}
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
QuicConnectionId bad_connection_id = TestConnectionIdNineBytesLong(2);
QuicConnectionId fixed_connection_id =
QuicUtils::CreateReplacementConnectionId(bad_connection_id);
EXPECT_CALL(*dispatcher_,
CreateQuicSession(TestConnectionId(1), _, client_address,
Eq(ExpectedAlpn()), _))
.WillOnce(Return(ByMove(CreateSession(
dispatcher_.get(), config_, TestConnectionId(1), client_address,
&mock_helper_, &mock_alarm_factory_, &crypto_config_,
QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))));
EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
ProcessUdpPacket(_, _, _))
.WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) {
ValidatePacket(TestConnectionId(1), packet);
})));
EXPECT_CALL(*dispatcher_,
ShouldCreateOrBufferPacketForConnection(
ReceivedPacketInfoConnectionIdEquals(TestConnectionId(1))));
ProcessFirstFlight(client_address, TestConnectionId(1));
EXPECT_CALL(*dispatcher_,
CreateQuicSession(fixed_connection_id, _, client_address,
Eq(ExpectedAlpn()), _))
.WillOnce(Return(ByMove(CreateSession(
dispatcher_.get(), config_, fixed_connection_id, client_address,
&mock_helper_, &mock_alarm_factory_, &crypto_config_,
QuicDispatcherPeer::GetCache(dispatcher_.get()), &session2_))));
EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session2_->connection()),
ProcessUdpPacket(_, _, _))
.WillOnce(WithArg<2>(
Invoke([this, bad_connection_id](const QuicEncryptedPacket& packet) {
ValidatePacket(bad_connection_id, packet);
})));
EXPECT_CALL(*dispatcher_,
ShouldCreateOrBufferPacketForConnection(
ReceivedPacketInfoConnectionIdEquals(bad_connection_id)));
ProcessFirstFlight(client_address, bad_connection_id);
EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
ProcessUdpPacket(_, _, _))
.Times(1)
.WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) {
ValidatePacket(TestConnectionId(1), packet);
})));
ProcessPacket(client_address, TestConnectionId(1), false, "data");
}
TEST_P(QuicDispatcherTestAllVersions, ProcessPacketWithZeroPort) {
CreateTimeWaitListManager();
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 0);
// dispatcher_ should drop this packet.
EXPECT_CALL(*dispatcher_,
CreateQuicSession(TestConnectionId(1), _, client_address, _, _))
.Times(0);
EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _, _))
.Times(0);
EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _))
.Times(0);
ProcessPacket(client_address, TestConnectionId(1), /*has_version_flag=*/true,
"data");
}
TEST_P(QuicDispatcherTestAllVersions,
DropPacketWithKnownVersionAndInvalidShortInitialConnectionId) {
if (!version_.AllowsVariableLengthConnectionIds()) {
return;
}
CreateTimeWaitListManager();
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
// dispatcher_ should drop this packet.
EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0);
EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _, _))
.Times(0);
EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _))
.Times(0);
ProcessFirstFlight(client_address, EmptyQuicConnectionId());
}
void QuicDispatcherTestBase::
TestVersionNegotiationForUnknownVersionInvalidShortInitialConnectionId(
const QuicConnectionId& server_connection_id,
const QuicConnectionId& client_connection_id) {
CreateTimeWaitListManager();
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0);
EXPECT_CALL(*time_wait_list_manager_,
SendVersionNegotiationPacket(
server_connection_id, client_connection_id,
/*ietf_quic=*/true,
/*use_length_prefix=*/true, _, _, client_address, _))
.Times(1);
ProcessFirstFlight(ParsedQuicVersion::ReservedForNegotiation(),
client_address, server_connection_id,
client_connection_id);
}
TEST_P(QuicDispatcherTestOneVersion,
VersionNegotiationForUnknownVersionInvalidShortInitialConnectionId) {
TestVersionNegotiationForUnknownVersionInvalidShortInitialConnectionId(
EmptyQuicConnectionId(), EmptyQuicConnectionId());
}
TEST_P(QuicDispatcherTestOneVersion,
VersionNegotiationForUnknownVersionInvalidShortInitialConnectionId2) {
char server_connection_id_bytes[3] = {1, 2, 3};
QuicConnectionId server_connection_id(server_connection_id_bytes,
sizeof(server_connection_id_bytes));
TestVersionNegotiationForUnknownVersionInvalidShortInitialConnectionId(
server_connection_id, EmptyQuicConnectionId());
}
TEST_P(QuicDispatcherTestOneVersion,
VersionNegotiationForUnknownVersionInvalidShortInitialConnectionId3) {
char client_connection_id_bytes[8] = {1, 2, 3, 4, 5, 6, 7, 8};
QuicConnectionId client_connection_id(client_connection_id_bytes,
sizeof(client_connection_id_bytes));
TestVersionNegotiationForUnknownVersionInvalidShortInitialConnectionId(
EmptyQuicConnectionId(), client_connection_id);
}
TEST_P(QuicDispatcherTestOneVersion, VersionsChangeInFlight) {
VerifyVersionNotSupported(QuicVersionReservedForNegotiation());
for (ParsedQuicVersion version : CurrentSupportedVersions()) {
VerifyVersionSupported(version);
QuicDisableVersion(version);
VerifyVersionNotSupported(version);
QuicEnableVersion(version);
VerifyVersionSupported(version);
}
}
TEST_P(QuicDispatcherTestOneVersion,
RejectDeprecatedVersionDraft28WithVersionNegotiation) {
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
CreateTimeWaitListManager();
char packet[kMinPacketSizeForVersionNegotiation] = {
0xC0, 0xFF, 0x00, 0x00, 28, /*destination connection ID length*/ 0x08};
QuicReceivedPacket received_packet(packet, ABSL_ARRAYSIZE(packet),
QuicTime::Zero());
EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0);
EXPECT_CALL(
*time_wait_list_manager_,
SendVersionNegotiationPacket(_, _, /*ietf_quic=*/true,
/*use_length_prefix=*/true, _, _, _, _))
.Times(1);
dispatcher_->ProcessPacket(server_address_, client_address, received_packet);
}
TEST_P(QuicDispatcherTestOneVersion,
RejectDeprecatedVersionDraft27WithVersionNegotiation) {
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
CreateTimeWaitListManager();
char packet[kMinPacketSizeForVersionNegotiation] = {
0xC0, 0xFF, 0x00, 0x00, 27, /*destination connection ID length*/ 0x08};
QuicReceivedPacket received_packet(packet, ABSL_ARRAYSIZE(packet),
QuicTime::Zero());
EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0);
EXPECT_CALL(
*time_wait_list_manager_,
SendVersionNegotiationPacket(_, _, /*ietf_quic=*/true,
/*use_length_prefix=*/true, _, _, _, _))
.Times(1);
dispatcher_->ProcessPacket(server_address_, client_address, received_packet);
}
TEST_P(QuicDispatcherTestOneVersion,
RejectDeprecatedVersionDraft25WithVersionNegotiation) {
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
CreateTimeWaitListManager();
char packet[kMinPacketSizeForVersionNegotiation] = {
0xC0, 0xFF, 0x00, 0x00, 25, /*destination connection ID length*/ 0x08};
QuicReceivedPacket received_packet(packet, ABSL_ARRAYSIZE(packet),
QuicTime::Zero());
EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0);
EXPECT_CALL(
*time_wait_list_manager_,
SendVersionNegotiationPacket(_, _, /*ietf_quic=*/true,
/*use_length_prefix=*/true, _, _, _, _))
.Times(1);
dispatcher_->ProcessPacket(server_address_, client_address, received_packet);
}
TEST_P(QuicDispatcherTestOneVersion,
RejectDeprecatedVersionT050WithVersionNegotiation) {
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
CreateTimeWaitListManager();
char packet[kMinPacketSizeForVersionNegotiation] = {
0xC0, 'T', '0', '5', '0', /*destination connection ID length*/ 0x08};
QuicReceivedPacket received_packet(packet, ABSL_ARRAYSIZE(packet),
QuicTime::Zero());
EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0);
EXPECT_CALL(
*time_wait_list_manager_,
SendVersionNegotiationPacket(_, _, /*ietf_quic=*/true,
/*use_length_prefix=*/true, _, _, _, _))
.Times(1);
dispatcher_->ProcessPacket(server_address_, client_address, received_packet);
}
TEST_P(QuicDispatcherTestOneVersion,
RejectDeprecatedVersionQ049WithVersionNegotiation) {
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
CreateTimeWaitListManager();
char packet[kMinPacketSizeForVersionNegotiation] = {
0xC0, 'Q', '0', '4', '9', /*destination connection ID length*/ 0x08};
QuicReceivedPacket received_packet(packet, ABSL_ARRAYSIZE(packet),
QuicTime::Zero());
EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0);
EXPECT_CALL(
*time_wait_list_manager_,
SendVersionNegotiationPacket(_, _, /*ietf_quic=*/true,
/*use_length_prefix=*/true, _, _, _, _))
.Times(1);
dispatcher_->ProcessPacket(server_address_, client_address, received_packet);
}
TEST_P(QuicDispatcherTestOneVersion,
RejectDeprecatedVersionQ048WithVersionNegotiation) {
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
CreateTimeWaitListManager();
char packet[kMinPacketSizeForVersionNegotiation] = {
0xC0, 'Q', '0', '4', '8', /*connection ID length byte*/ 0x50};
QuicReceivedPacket received_packet(packet, ABSL_ARRAYSIZE(packet),
QuicTime::Zero());
EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0);
EXPECT_CALL(
*time_wait_list_manager_,
SendVersionNegotiationPacket(_, _, /*ietf_quic=*/true,
/*use_length_prefix=*/false, _, _, _, _))
.Times(1);
dispatcher_->ProcessPacket(server_address_, client_address, received_packet);
}
TEST_P(QuicDispatcherTestOneVersion,
RejectDeprecatedVersionQ047WithVersionNegotiation) {
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
CreateTimeWaitListManager();
char packet[kMinPacketSizeForVersionNegotiation] = {
0xC0, 'Q', '0', '4', '7', /*connection ID length byte*/ 0x50};
QuicReceivedPacket received_packet(packet, ABSL_ARRAYSIZE(packet),
QuicTime::Zero());
EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0);
EXPECT_CALL(
*time_wait_list_manager_,
SendVersionNegotiationPacket(_, _, /*ietf_quic=*/true,
/*use_length_prefix=*/false, _, _, _, _))
.Times(1);
dispatcher_->ProcessPacket(server_address_, client_address, received_packet);
}
TEST_P(QuicDispatcherTestOneVersion,
RejectDeprecatedVersionQ045WithVersionNegotiation) {
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
CreateTimeWaitListManager();
char packet[kMinPacketSizeForVersionNegotiation] = {
0xC0, 'Q', '0', '4', '5', /*connection ID length byte*/ 0x50};
QuicReceivedPacket received_packet(packet, ABSL_ARRAYSIZE(packet),
QuicTime::Zero());
EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0);
EXPECT_CALL(
*time_wait_list_manager_,
SendVersionNegotiationPacket(_, _, /*ietf_quic=*/true,
/*use_length_prefix=*/false, _, _, _, _))
.Times(1);
dispatcher_->ProcessPacket(server_address_, client_address, received_packet);
}
TEST_P(QuicDispatcherTestOneVersion,
RejectDeprecatedVersionQ044WithVersionNegotiation) {
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
CreateTimeWaitListManager();
char packet44[kMinPacketSizeForVersionNegotiation] = {
0xFF, 'Q', '0', '4', '4', /*connection ID length byte*/ 0x50};
QuicReceivedPacket received_packet44(
packet44, kMinPacketSizeForVersionNegotiation, QuicTime::Zero());
EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0);
EXPECT_CALL(
*time_wait_list_manager_,
SendVersionNegotiationPacket(_, _, /*ietf_quic=*/true,
/*use_length_prefix=*/false, _, _, _, _))
.Times(1);
dispatcher_->ProcessPacket(server_address_, client_address,
received_packet44);
}
static_assert(quic::SupportedVersions().size() == 6u,
"Please add new RejectDeprecatedVersion tests above this assert "
"when deprecating versions");
TEST_P(QuicDispatcherTestOneVersion, VersionNegotiationProbe) {
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
CreateTimeWaitListManager();
char packet[1200];
char destination_connection_id_bytes[] = {0x56, 0x4e, 0x20, 0x70,
0x6c, 0x7a, 0x20, 0x21};
EXPECT_TRUE(QuicFramer::WriteClientVersionNegotiationProbePacket(
packet, sizeof(packet), destination_connection_id_bytes,
sizeof(destination_connection_id_bytes)));
QuicEncryptedPacket encrypted(packet, sizeof(packet), false);
std::unique_ptr<QuicReceivedPacket> received_packet(
ConstructReceivedPacket(encrypted, mock_helper_.GetClock()->Now()));
QuicConnectionId client_connection_id = EmptyQuicConnectionId();
QuicConnectionId server_connection_id(
destination_connection_id_bytes, sizeof(destination_connection_id_bytes));
EXPECT_CALL(*time_wait_list_manager_,
SendVersionNegotiationPacket(
server_connection_id, client_connection_id,
/*ietf_quic=*/true, /*use_length_prefix=*/true, _, _, _, _))
.Times(1);
EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0);
dispatcher_->ProcessPacket(server_address_, client_address, *received_packet);
}
// Testing packet writer that saves all packets instead of sending them.
// Useful for tests that need access to sent packets.
class SavingWriter : public QuicPacketWriterWrapper {
public:
bool IsWriteBlocked() const override { return false; }
WriteResult WritePacket(const char* buffer,
size_t buf_len,
const QuicIpAddress& /*self_client_address*/,
const QuicSocketAddress& /*peer_client_address*/,
PerPacketOptions* /*options*/) override {
packets_.push_back(
QuicEncryptedPacket(buffer, buf_len, /*owns_buffer=*/false).Clone());
return WriteResult(WRITE_STATUS_OK, buf_len);
}
std::vector<std::unique_ptr<QuicEncryptedPacket>>* packets() {
return &packets_;
}
private:
std::vector<std::unique_ptr<QuicEncryptedPacket>> packets_;
};
TEST_P(QuicDispatcherTestOneVersion, VersionNegotiationProbeEndToEnd) {
SavingWriter* saving_writer = new SavingWriter();
// dispatcher_ takes ownership of saving_writer.
QuicDispatcherPeer::UseWriter(dispatcher_.get(), saving_writer);
QuicTimeWaitListManager* time_wait_list_manager = new QuicTimeWaitListManager(
saving_writer, dispatcher_.get(), mock_helper_.GetClock(),
&mock_alarm_factory_);
// dispatcher_ takes ownership of time_wait_list_manager.
QuicDispatcherPeer::SetTimeWaitListManager(dispatcher_.get(),
time_wait_list_manager);
char packet[1200] = {};
char destination_connection_id_bytes[] = {0x56, 0x4e, 0x20, 0x70,
0x6c, 0x7a, 0x20, 0x21};
EXPECT_TRUE(QuicFramer::WriteClientVersionNegotiationProbePacket(
packet, sizeof(packet), destination_connection_id_bytes,
sizeof(destination_connection_id_bytes)));
QuicEncryptedPacket encrypted(packet, sizeof(packet), false);
std::unique_ptr<QuicReceivedPacket> received_packet(
ConstructReceivedPacket(encrypted, mock_helper_.GetClock()->Now()));
EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0);
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
dispatcher_->ProcessPacket(server_address_, client_address, *received_packet);
ASSERT_EQ(1u, saving_writer->packets()->size());
char source_connection_id_bytes[255] = {};
uint8_t source_connection_id_length = sizeof(source_connection_id_bytes);
std::string detailed_error = "foobar";
EXPECT_TRUE(QuicFramer::ParseServerVersionNegotiationProbeResponse(
(*(saving_writer->packets()))[0]->data(),
(*(saving_writer->packets()))[0]->length(), source_connection_id_bytes,
&source_connection_id_length, &detailed_error));
EXPECT_EQ("", detailed_error);
// The source connection ID of the probe response should match the
// destination connection ID of the probe request.
quiche::test::CompareCharArraysWithHexError(
"parsed probe", source_connection_id_bytes, source_connection_id_length,
destination_connection_id_bytes, sizeof(destination_connection_id_bytes));
}
TEST_P(QuicDispatcherTestOneVersion, AndroidConformanceTest) {
// WARNING: do not remove or modify this test without making sure that we
// still have adequate coverage for the Android conformance test.
SavingWriter* saving_writer = new SavingWriter();
// dispatcher_ takes ownership of saving_writer.
QuicDispatcherPeer::UseWriter(dispatcher_.get(), saving_writer);
QuicTimeWaitListManager* time_wait_list_manager = new QuicTimeWaitListManager(
saving_writer, dispatcher_.get(), mock_helper_.GetClock(),
&mock_alarm_factory_);
// dispatcher_ takes ownership of time_wait_list_manager.
QuicDispatcherPeer::SetTimeWaitListManager(dispatcher_.get(),
time_wait_list_manager);
// clang-format off
static const unsigned char packet[1200] = {
// Android UDP network conformance test packet as it was after this change:
// https://android-review.googlesource.com/c/platform/cts/+/1454515
0xc0, // long header
0xaa, 0xda, 0xca, 0xca, // reserved-space version number
0x08, // destination connection ID length
0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, // 8-byte connection ID
0x00, // source connection ID length
};
// clang-format on
QuicEncryptedPacket encrypted(reinterpret_cast<const char*>(packet),
sizeof(packet), false);
std::unique_ptr<QuicReceivedPacket> received_packet(
ConstructReceivedPacket(encrypted, mock_helper_.GetClock()->Now()));
EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0);
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
dispatcher_->ProcessPacket(server_address_, client_address, *received_packet);
ASSERT_EQ(1u, saving_writer->packets()->size());
// The Android UDP network conformance test directly checks that these bytes
// of the response match the connection ID that was sent.
ASSERT_GE((*(saving_writer->packets()))[0]->length(), 15u);
quiche::test::CompareCharArraysWithHexError(
"response connection ID", &(*(saving_writer->packets()))[0]->data()[7], 8,
reinterpret_cast<const char*>(&packet[6]), 8);
}
TEST_P(QuicDispatcherTestOneVersion, AndroidConformanceTestOld) {
// WARNING: this test covers an old Android Conformance Test that has now been
// changed, but it'll take time for the change to propagate through the
// Android ecosystem. The Android team has asked us to keep this test
// supported until at least 2021-03-31. After that date, and when we drop
// support for sending QUIC version negotiation packets using the legacy
// Google QUIC format (Q001-Q043), then we can delete this test.
// TODO(dschinazi) delete this test after 2021-03-31
SavingWriter* saving_writer = new SavingWriter();
// dispatcher_ takes ownership of saving_writer.
QuicDispatcherPeer::UseWriter(dispatcher_.get(), saving_writer);
QuicTimeWaitListManager* time_wait_list_manager = new QuicTimeWaitListManager(
saving_writer, dispatcher_.get(), mock_helper_.GetClock(),
&mock_alarm_factory_);
// dispatcher_ takes ownership of time_wait_list_manager.
QuicDispatcherPeer::SetTimeWaitListManager(dispatcher_.get(),
time_wait_list_manager);
// clang-format off
static const unsigned char packet[1200] = {
// Android UDP network conformance test packet as it was after this change:
// https://android-review.googlesource.com/c/platform/cts/+/1104285
// but before this change:
// https://android-review.googlesource.com/c/platform/cts/+/1454515
0x0d, // public flags: version, 8-byte connection ID, 1-byte packet number
0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, // 8-byte connection ID
0xaa, 0xda, 0xca, 0xaa, // reserved-space version number
0x01, // 1-byte packet number
0x00, // private flags
0x07, // PING frame
};
// clang-format on
QuicEncryptedPacket encrypted(reinterpret_cast<const char*>(packet),
sizeof(packet), false);
std::unique_ptr<QuicReceivedPacket> received_packet(
ConstructReceivedPacket(encrypted, mock_helper_.GetClock()->Now()));
EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0);
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
dispatcher_->ProcessPacket(server_address_, client_address, *received_packet);
ASSERT_EQ(1u, saving_writer->packets()->size());
// The Android UDP network conformance test directly checks that bytes 1-9
// of the response match the connection ID that was sent.
static const char connection_id_bytes[] = {0x71, 0x72, 0x73, 0x74,
0x75, 0x76, 0x77, 0x78};
ASSERT_GE((*(saving_writer->packets()))[0]->length(),
1u + sizeof(connection_id_bytes));
quiche::test::CompareCharArraysWithHexError(
"response connection ID", &(*(saving_writer->packets()))[0]->data()[1],
sizeof(connection_id_bytes), connection_id_bytes,
sizeof(connection_id_bytes));
}
TEST_P(QuicDispatcherTestAllVersions, DoNotProcessSmallPacket) {
CreateTimeWaitListManager();
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0);
EXPECT_CALL(*time_wait_list_manager_, SendPacket(_, _, _)).Times(0);
EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _))
.Times(0);
ProcessPacket(client_address, TestConnectionId(1), /*has_version_flag=*/true,
version_, SerializeCHLO(), /*full_padding=*/false,
CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, 1);
}
TEST_P(QuicDispatcherTestAllVersions, ProcessSmallCoalescedPacket) {
CreateTimeWaitListManager();
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
EXPECT_CALL(*time_wait_list_manager_, SendPacket(_, _, _)).Times(0);
// clang-format off
char coalesced_packet[1200] = {
// first coalesced packet
// public flags (long header with packet type INITIAL and
// 4-byte packet number)
0xC3,
// version
'Q', '0', '9', '9',
// destination connection ID length
0x08,
// destination connection ID
0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
// source connection ID length
0x00,
// long header packet length
0x05,
// packet number
0x12, 0x34, 0x56, 0x78,
// Padding
0x00,
// second coalesced packet
// public flags (long header with packet type ZERO_RTT_PROTECTED and
// 4-byte packet number)
0xC3,
// version
'Q', '0', '9', '9',
// destination connection ID length
0x08,
// destination connection ID
0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
// source connection ID length
0x00,
// long header packet length
0x1E,
// packet number
0x12, 0x34, 0x56, 0x79,
};
// clang-format on
QuicReceivedPacket packet(coalesced_packet, 1200, QuicTime::Zero());
dispatcher_->ProcessPacket(server_address_, client_address, packet);
}
TEST_P(QuicDispatcherTestAllVersions, StopAcceptingNewConnections) {
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
EXPECT_CALL(*dispatcher_,
CreateQuicSession(TestConnectionId(1), _, client_address,
Eq(ExpectedAlpn()), _))
.WillOnce(Return(ByMove(CreateSession(
dispatcher_.get(), config_, TestConnectionId(1), client_address,
&mock_helper_, &mock_alarm_factory_, &crypto_config_,
QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))));
EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
ProcessUdpPacket(_, _, _))
.WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) {
ValidatePacket(TestConnectionId(1), packet);
})));
ProcessFirstFlight(client_address, TestConnectionId(1));
dispatcher_->StopAcceptingNewConnections();
EXPECT_FALSE(dispatcher_->accept_new_connections());
// No more new connections afterwards.
EXPECT_CALL(*dispatcher_,
CreateQuicSession(TestConnectionId(2), _, client_address,
Eq(ExpectedAlpn()), _))
.Times(0u);
ProcessFirstFlight(client_address, TestConnectionId(2));
// Existing connections should be able to continue.
EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
ProcessUdpPacket(_, _, _))
.Times(1u)
.WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) {
ValidatePacket(TestConnectionId(1), packet);
})));
ProcessPacket(client_address, TestConnectionId(1), false, "data");
}
TEST_P(QuicDispatcherTestAllVersions, StartAcceptingNewConnections) {
dispatcher_->StopAcceptingNewConnections();
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
// No more new connections afterwards.
EXPECT_CALL(*dispatcher_,
CreateQuicSession(TestConnectionId(2), _, client_address,
Eq(ExpectedAlpn()), _))
.Times(0u);
ProcessFirstFlight(client_address, TestConnectionId(2));
dispatcher_->StartAcceptingNewConnections();
EXPECT_TRUE(dispatcher_->accept_new_connections());
EXPECT_CALL(*dispatcher_,
CreateQuicSession(TestConnectionId(1), _, client_address,
Eq(ExpectedAlpn()), _))
.WillOnce(Return(ByMove(CreateSession(
dispatcher_.get(), config_, TestConnectionId(1), client_address,
&mock_helper_, &mock_alarm_factory_, &crypto_config_,
QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))));
EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
ProcessUdpPacket(_, _, _))
.WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) {
ValidatePacket(TestConnectionId(1), packet);
})));
ProcessFirstFlight(client_address, TestConnectionId(1));
}
TEST_P(QuicDispatcherTestOneVersion, SelectAlpn) {
EXPECT_EQ(QuicDispatcherPeer::SelectAlpn(dispatcher_.get(), {}), "");
EXPECT_EQ(QuicDispatcherPeer::SelectAlpn(dispatcher_.get(), {""}), "");
EXPECT_EQ(QuicDispatcherPeer::SelectAlpn(dispatcher_.get(), {"hq"}), "hq");
// Q033 is no longer supported but Q050 is.
QuicEnableVersion(ParsedQuicVersion::Q050());
EXPECT_EQ(
QuicDispatcherPeer::SelectAlpn(dispatcher_.get(), {"h3-Q033", "h3-Q050"}),
"h3-Q050");
}
// Verify the stopgap test: Packets with truncated connection IDs should be
// dropped.
class QuicDispatcherTestStrayPacketConnectionId
: public QuicDispatcherTestBase {};
INSTANTIATE_TEST_SUITE_P(QuicDispatcherTestsStrayPacketConnectionId,
QuicDispatcherTestStrayPacketConnectionId,
::testing::ValuesIn(CurrentSupportedVersions()),
::testing::PrintToStringParamName());
// Packets with truncated connection IDs should be dropped.
TEST_P(QuicDispatcherTestStrayPacketConnectionId,
StrayPacketTruncatedConnectionId) {
CreateTimeWaitListManager();
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
QuicConnectionId connection_id = TestConnectionId(1);
EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0);
EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _, _))
.Times(0);
EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _))
.Times(0);
ProcessPacket(client_address, connection_id, true, "data",
CONNECTION_ID_ABSENT, PACKET_4BYTE_PACKET_NUMBER);
}
class BlockingWriter : public QuicPacketWriterWrapper {
public:
BlockingWriter() : write_blocked_(false) {}
bool IsWriteBlocked() const override { return write_blocked_; }
void SetWritable() override { write_blocked_ = false; }
WriteResult WritePacket(const char* /*buffer*/,
size_t /*buf_len*/,
const QuicIpAddress& /*self_client_address*/,
const QuicSocketAddress& /*peer_client_address*/,
PerPacketOptions* /*options*/) override {
// It would be quite possible to actually implement this method here with
// the fake blocked status, but it would be significantly more work in
// Chromium, and since it's not called anyway, don't bother.
QUIC_LOG(DFATAL) << "Not supported";
return WriteResult();
}
bool write_blocked_;
};
class QuicDispatcherWriteBlockedListTest : public QuicDispatcherTestBase {
public:
void SetUp() override {
QuicDispatcherTestBase::SetUp();
writer_ = new BlockingWriter;
QuicDispatcherPeer::UseWriter(dispatcher_.get(), writer_);
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
EXPECT_CALL(*dispatcher_,
CreateQuicSession(_, _, client_address, Eq(ExpectedAlpn()), _))
.WillOnce(Return(ByMove(CreateSession(
dispatcher_.get(), config_, TestConnectionId(1), client_address,
&helper_, &alarm_factory_, &crypto_config_,
QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))));
EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
ProcessUdpPacket(_, _, _))
.WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) {
ValidatePacket(TestConnectionId(1), packet);
})));
EXPECT_CALL(*dispatcher_,
ShouldCreateOrBufferPacketForConnection(
ReceivedPacketInfoConnectionIdEquals(TestConnectionId(1))));
ProcessFirstFlight(client_address, TestConnectionId(1));
EXPECT_CALL(*dispatcher_,
CreateQuicSession(_, _, client_address, Eq(ExpectedAlpn()), _))
.WillOnce(Return(ByMove(CreateSession(
dispatcher_.get(), config_, TestConnectionId(2), client_address,
&helper_, &alarm_factory_, &crypto_config_,
QuicDispatcherPeer::GetCache(dispatcher_.get()), &session2_))));
EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session2_->connection()),
ProcessUdpPacket(_, _, _))
.WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) {
ValidatePacket(TestConnectionId(2), packet);
})));
EXPECT_CALL(*dispatcher_,
ShouldCreateOrBufferPacketForConnection(
ReceivedPacketInfoConnectionIdEquals(TestConnectionId(2))));
ProcessFirstFlight(client_address, TestConnectionId(2));
blocked_list_ = QuicDispatcherPeer::GetWriteBlockedList(dispatcher_.get());
}
void TearDown() override {
if (connection1() != nullptr) {
EXPECT_CALL(*connection1(), CloseConnection(QUIC_PEER_GOING_AWAY, _, _));
}
if (connection2() != nullptr) {
EXPECT_CALL(*connection2(), CloseConnection(QUIC_PEER_GOING_AWAY, _, _));
}
dispatcher_->Shutdown();
}
// Set the dispatcher's writer to be blocked. By default, all connections use
// the same writer as the dispatcher in this test.
void SetBlocked() {
QUIC_LOG(INFO) << "set writer " << writer_ << " to blocked";
writer_->write_blocked_ = true;
}
// Simulate what happens when connection1 gets blocked when writing.
void BlockConnection1() {
Connection1Writer()->write_blocked_ = true;
dispatcher_->OnWriteBlocked(connection1());
}
BlockingWriter* Connection1Writer() {
return static_cast<BlockingWriter*>(connection1()->writer());
}
// Simulate what happens when connection2 gets blocked when writing.
void BlockConnection2() {
Connection2Writer()->write_blocked_ = true;
dispatcher_->OnWriteBlocked(connection2());
}
BlockingWriter* Connection2Writer() {
return static_cast<BlockingWriter*>(connection2()->writer());
}
protected:
MockQuicConnectionHelper helper_;
MockAlarmFactory alarm_factory_;
BlockingWriter* writer_;
QuicDispatcher::WriteBlockedList* blocked_list_;
};
INSTANTIATE_TEST_SUITE_P(QuicDispatcherWriteBlockedListTests,
QuicDispatcherWriteBlockedListTest,
::testing::Values(CurrentSupportedVersions().front()),
::testing::PrintToStringParamName());
TEST_P(QuicDispatcherWriteBlockedListTest, BasicOnCanWrite) {
// No OnCanWrite calls because no connections are blocked.
dispatcher_->OnCanWrite();
// Register connection 1 for events, and make sure it's notified.
SetBlocked();
dispatcher_->OnWriteBlocked(connection1());
EXPECT_CALL(*connection1(), OnCanWrite());
dispatcher_->OnCanWrite();
// It should get only one notification.
EXPECT_CALL(*connection1(), OnCanWrite()).Times(0);
dispatcher_->OnCanWrite();
EXPECT_FALSE(dispatcher_->HasPendingWrites());
}
TEST_P(QuicDispatcherWriteBlockedListTest, OnCanWriteOrder) {
// Make sure we handle events in order.
InSequence s;
SetBlocked();
dispatcher_->OnWriteBlocked(connection1());
dispatcher_->OnWriteBlocked(connection2());
EXPECT_CALL(*connection1(), OnCanWrite());
EXPECT_CALL(*connection2(), OnCanWrite());
dispatcher_->OnCanWrite();
// Check the other ordering.
SetBlocked();
dispatcher_->OnWriteBlocked(connection2());
dispatcher_->OnWriteBlocked(connection1());
EXPECT_CALL(*connection2(), OnCanWrite());
EXPECT_CALL(*connection1(), OnCanWrite());
dispatcher_->OnCanWrite();
}
TEST_P(QuicDispatcherWriteBlockedListTest, OnCanWriteRemove) {
// Add and remove one connction.
SetBlocked();
dispatcher_->OnWriteBlocked(connection1());
blocked_list_->erase(connection1());
EXPECT_CALL(*connection1(), OnCanWrite()).Times(0);
dispatcher_->OnCanWrite();
// Add and remove one connction and make sure it doesn't affect others.
SetBlocked();
dispatcher_->OnWriteBlocked(connection1());
dispatcher_->OnWriteBlocked(connection2());
blocked_list_->erase(connection1());
EXPECT_CALL(*connection2(), OnCanWrite());
dispatcher_->OnCanWrite();
// Add it, remove it, and add it back and make sure things are OK.
SetBlocked();
dispatcher_->OnWriteBlocked(connection1());
blocked_list_->erase(connection1());
dispatcher_->OnWriteBlocked(connection1());
EXPECT_CALL(*connection1(), OnCanWrite()).Times(1);
dispatcher_->OnCanWrite();
}
TEST_P(QuicDispatcherWriteBlockedListTest, DoubleAdd) {
// Make sure a double add does not necessitate a double remove.
SetBlocked();
dispatcher_->OnWriteBlocked(connection1());
dispatcher_->OnWriteBlocked(connection1());
blocked_list_->erase(connection1());
EXPECT_CALL(*connection1(), OnCanWrite()).Times(0);
dispatcher_->OnCanWrite();
// Make sure a double add does not result in two OnCanWrite calls.
SetBlocked();
dispatcher_->OnWriteBlocked(connection1());
dispatcher_->OnWriteBlocked(connection1());
EXPECT_CALL(*connection1(), OnCanWrite()).Times(1);
dispatcher_->OnCanWrite();
}
TEST_P(QuicDispatcherWriteBlockedListTest, OnCanWriteHandleBlockConnection1) {
// If the 1st blocked writer gets blocked in OnCanWrite, it will be added back
// into the write blocked list.
InSequence s;
SetBlocked();
dispatcher_->OnWriteBlocked(connection1());
dispatcher_->OnWriteBlocked(connection2());
EXPECT_CALL(*connection1(), OnCanWrite())
.WillOnce(
Invoke(this, &QuicDispatcherWriteBlockedListTest::BlockConnection1));
EXPECT_CALL(*connection2(), OnCanWrite());
dispatcher_->OnCanWrite();
// connection1 should be still in the write blocked list.
EXPECT_TRUE(dispatcher_->HasPendingWrites());
// Now call OnCanWrite again, connection1 should get its second chance.
EXPECT_CALL(*connection1(), OnCanWrite());
EXPECT_CALL(*connection2(), OnCanWrite()).Times(0);
dispatcher_->OnCanWrite();
EXPECT_FALSE(dispatcher_->HasPendingWrites());
}
TEST_P(QuicDispatcherWriteBlockedListTest, OnCanWriteHandleBlockConnection2) {
// If the 2nd blocked writer gets blocked in OnCanWrite, it will be added back
// into the write blocked list.
InSequence s;
SetBlocked();
dispatcher_->OnWriteBlocked(connection1());
dispatcher_->OnWriteBlocked(connection2());
EXPECT_CALL(*connection1(), OnCanWrite());
EXPECT_CALL(*connection2(), OnCanWrite())
.WillOnce(
Invoke(this, &QuicDispatcherWriteBlockedListTest::BlockConnection2));
dispatcher_->OnCanWrite();
// connection2 should be still in the write blocked list.
EXPECT_TRUE(dispatcher_->HasPendingWrites());
// Now call OnCanWrite again, connection2 should get its second chance.
EXPECT_CALL(*connection1(), OnCanWrite()).Times(0);
EXPECT_CALL(*connection2(), OnCanWrite());
dispatcher_->OnCanWrite();
EXPECT_FALSE(dispatcher_->HasPendingWrites());
}
TEST_P(QuicDispatcherWriteBlockedListTest,
OnCanWriteHandleBlockBothConnections) {
// Both connections get blocked in OnCanWrite, and added back into the write
// blocked list.
InSequence s;
SetBlocked();
dispatcher_->OnWriteBlocked(connection1());
dispatcher_->OnWriteBlocked(connection2());
EXPECT_CALL(*connection1(), OnCanWrite())
.WillOnce(
Invoke(this, &QuicDispatcherWriteBlockedListTest::BlockConnection1));
EXPECT_CALL(*connection2(), OnCanWrite())
.WillOnce(
Invoke(this, &QuicDispatcherWriteBlockedListTest::BlockConnection2));
dispatcher_->OnCanWrite();
// Both connections should be still in the write blocked list.
EXPECT_TRUE(dispatcher_->HasPendingWrites());
// Now call OnCanWrite again, both connections should get its second chance.
EXPECT_CALL(*connection1(), OnCanWrite());
EXPECT_CALL(*connection2(), OnCanWrite());
dispatcher_->OnCanWrite();
EXPECT_FALSE(dispatcher_->HasPendingWrites());
}
TEST_P(QuicDispatcherWriteBlockedListTest, PerConnectionWriterBlocked) {
// By default, all connections share the same packet writer with the
// dispatcher.
EXPECT_EQ(dispatcher_->writer(), connection1()->writer());
EXPECT_EQ(dispatcher_->writer(), connection2()->writer());
// Test the case where connection1 shares the same packet writer as the
// dispatcher, whereas connection2 owns it's packet writer.
// Change connection2's writer.
connection2()->SetQuicPacketWriter(new BlockingWriter, /*owns_writer=*/true);
EXPECT_NE(dispatcher_->writer(), connection2()->writer());
BlockConnection2();
EXPECT_TRUE(dispatcher_->HasPendingWrites());
EXPECT_CALL(*connection2(), OnCanWrite());
dispatcher_->OnCanWrite();
EXPECT_FALSE(dispatcher_->HasPendingWrites());
}
TEST_P(QuicDispatcherWriteBlockedListTest,
RemoveConnectionFromWriteBlockedListWhenDeletingSessions) {
dispatcher_->OnConnectionClosed(connection1()->connection_id(),
QUIC_PACKET_WRITE_ERROR, "Closed by test.",
ConnectionCloseSource::FROM_SELF);
SetBlocked();
ASSERT_FALSE(dispatcher_->HasPendingWrites());
SetBlocked();
dispatcher_->OnWriteBlocked(connection1());
ASSERT_TRUE(dispatcher_->HasPendingWrites());
EXPECT_QUIC_BUG(dispatcher_->DeleteSessions(),
"QuicConnection was in WriteBlockedList before destruction");
MarkSession1Deleted();
}
class QuicDispatcherSupportMultipleConnectionIdPerConnectionTest
: public QuicDispatcherTestBase {
public:
QuicDispatcherSupportMultipleConnectionIdPerConnectionTest()
: QuicDispatcherTestBase(crypto_test_utils::ProofSourceForTesting()) {
SetQuicRestartFlag(quic_use_reference_counted_sesssion_map, true);
SetQuicRestartFlag(quic_time_wait_list_support_multiple_cid_v2, true);
SetQuicRestartFlag(quic_dispatcher_support_multiple_cid_per_connection_v2,
true);
dispatcher_ = std::make_unique<NiceMock<TestDispatcher>>(
&config_, &crypto_config_, &version_manager_,
mock_helper_.GetRandomGenerator());
}
void AddConnection1() {
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
EXPECT_CALL(*dispatcher_,
CreateQuicSession(_, _, client_address, Eq(ExpectedAlpn()), _))
.WillOnce(Return(ByMove(CreateSession(
dispatcher_.get(), config_, TestConnectionId(1), client_address,
&helper_, &alarm_factory_, &crypto_config_,
QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))));
EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
ProcessUdpPacket(_, _, _))
.WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) {
ValidatePacket(TestConnectionId(1), packet);
})));
EXPECT_CALL(*dispatcher_,
ShouldCreateOrBufferPacketForConnection(
ReceivedPacketInfoConnectionIdEquals(TestConnectionId(1))));
ProcessFirstFlight(client_address, TestConnectionId(1));
}
void AddConnection2() {
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 2);
EXPECT_CALL(*dispatcher_,
CreateQuicSession(_, _, client_address, Eq(ExpectedAlpn()), _))
.WillOnce(Return(ByMove(CreateSession(
dispatcher_.get(), config_, TestConnectionId(2), client_address,
&helper_, &alarm_factory_, &crypto_config_,
QuicDispatcherPeer::GetCache(dispatcher_.get()), &session2_))));
EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session2_->connection()),
ProcessUdpPacket(_, _, _))
.WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) {
ValidatePacket(TestConnectionId(2), packet);
})));
EXPECT_CALL(*dispatcher_,
ShouldCreateOrBufferPacketForConnection(
ReceivedPacketInfoConnectionIdEquals(TestConnectionId(2))));
ProcessFirstFlight(client_address, TestConnectionId(2));
}
protected:
MockQuicConnectionHelper helper_;
MockAlarmFactory alarm_factory_;
};
INSTANTIATE_TEST_SUITE_P(
QuicDispatcherSupportMultipleConnectionIdPerConnectionTests,
QuicDispatcherSupportMultipleConnectionIdPerConnectionTest,
::testing::Values(CurrentSupportedVersions().front()),
::testing::PrintToStringParamName());
TEST_P(QuicDispatcherSupportMultipleConnectionIdPerConnectionTest,
OnNewConnectionIdSent) {
AddConnection1();
ASSERT_EQ(dispatcher_->NumSessions(), 1u);
ASSERT_THAT(session1_, testing::NotNull());
MockServerConnection* mock_server_connection1 =
reinterpret_cast<MockServerConnection*>(connection1());
{
mock_server_connection1->AddNewConnectionId(TestConnectionId(3));
EXPECT_EQ(dispatcher_->NumSessions(), 1u);
auto* session =
QuicDispatcherPeer::FindSession(dispatcher_.get(), TestConnectionId(3));
ASSERT_EQ(session, session1_);
}
{
mock_server_connection1->AddNewConnectionId(TestConnectionId(4));
EXPECT_EQ(dispatcher_->NumSessions(), 1u);
auto* session =
QuicDispatcherPeer::FindSession(dispatcher_.get(), TestConnectionId(4));
ASSERT_EQ(session, session1_);
}
EXPECT_CALL(*connection1(), CloseConnection(QUIC_PEER_GOING_AWAY, _, _));
// Would timed out unless all sessions have been removed from the session map.
dispatcher_->Shutdown();
}
TEST_P(QuicDispatcherSupportMultipleConnectionIdPerConnectionTest,
RetireConnectionIdFromSingleConnection) {
AddConnection1();
ASSERT_EQ(dispatcher_->NumSessions(), 1u);
ASSERT_THAT(session1_, testing::NotNull());
MockServerConnection* mock_server_connection1 =
reinterpret_cast<MockServerConnection*>(connection1());
// Adds 1 new connection id every turn and retires 2 connection ids every
// other turn.
for (int i = 2; i < 10; ++i) {
mock_server_connection1->AddNewConnectionId(TestConnectionId(i));
ASSERT_EQ(
QuicDispatcherPeer::FindSession(dispatcher_.get(), TestConnectionId(i)),
session1_);
ASSERT_EQ(QuicDispatcherPeer::FindSession(dispatcher_.get(),
TestConnectionId(i - 1)),
session1_);
EXPECT_EQ(dispatcher_->NumSessions(), 1u);
if (i % 2 == 1) {
mock_server_connection1->RetireConnectionId(TestConnectionId(i - 2));
mock_server_connection1->RetireConnectionId(TestConnectionId(i - 1));
}
}
EXPECT_CALL(*connection1(), CloseConnection(QUIC_PEER_GOING_AWAY, _, _));
// Would timed out unless all sessions have been removed from the session map.
dispatcher_->Shutdown();
}
TEST_P(QuicDispatcherSupportMultipleConnectionIdPerConnectionTest,
RetireConnectionIdFromMultipleConnections) {
AddConnection1();
AddConnection2();
ASSERT_EQ(dispatcher_->NumSessions(), 2u);
MockServerConnection* mock_server_connection1 =
reinterpret_cast<MockServerConnection*>(connection1());
MockServerConnection* mock_server_connection2 =
reinterpret_cast<MockServerConnection*>(connection2());
for (int i = 2; i < 10; ++i) {
mock_server_connection1->AddNewConnectionId(TestConnectionId(2 * i - 1));
mock_server_connection2->AddNewConnectionId(TestConnectionId(2 * i));
ASSERT_EQ(QuicDispatcherPeer::FindSession(dispatcher_.get(),
TestConnectionId(2 * i - 1)),
session1_);
ASSERT_EQ(QuicDispatcherPeer::FindSession(dispatcher_.get(),
TestConnectionId(2 * i)),
session2_);
EXPECT_EQ(dispatcher_->NumSessions(), 2u);
mock_server_connection1->RetireConnectionId(TestConnectionId(2 * i - 3));
mock_server_connection2->RetireConnectionId(TestConnectionId(2 * i - 2));
}
mock_server_connection1->AddNewConnectionId(TestConnectionId(19));
mock_server_connection2->AddNewConnectionId(TestConnectionId(20));
EXPECT_CALL(*connection1(), CloseConnection(QUIC_PEER_GOING_AWAY, _, _));
EXPECT_CALL(*connection2(), CloseConnection(QUIC_PEER_GOING_AWAY, _, _));
// Would timed out unless all sessions have been removed from the session map.
dispatcher_->Shutdown();
}
TEST_P(QuicDispatcherSupportMultipleConnectionIdPerConnectionTest,
TimeWaitListPoplulateCorrectly) {
QuicTimeWaitListManager* time_wait_list_manager =
QuicDispatcherPeer::GetTimeWaitListManager(dispatcher_.get());
AddConnection1();
MockServerConnection* mock_server_connection1 =
reinterpret_cast<MockServerConnection*>(connection1());
mock_server_connection1->AddNewConnectionId(TestConnectionId(2));
mock_server_connection1->AddNewConnectionId(TestConnectionId(3));
mock_server_connection1->AddNewConnectionId(TestConnectionId(4));
mock_server_connection1->RetireConnectionId(TestConnectionId(1));
mock_server_connection1->RetireConnectionId(TestConnectionId(2));
EXPECT_CALL(*connection1(), CloseConnection(QUIC_PEER_GOING_AWAY, _, _));
connection1()->CloseConnection(
QUIC_PEER_GOING_AWAY, "Close for testing",
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
EXPECT_FALSE(
time_wait_list_manager->IsConnectionIdInTimeWait(TestConnectionId(1)));
EXPECT_FALSE(
time_wait_list_manager->IsConnectionIdInTimeWait(TestConnectionId(2)));
EXPECT_TRUE(
time_wait_list_manager->IsConnectionIdInTimeWait(TestConnectionId(3)));
EXPECT_TRUE(
time_wait_list_manager->IsConnectionIdInTimeWait(TestConnectionId(4)));
dispatcher_->Shutdown();
}
class BufferedPacketStoreTest : public QuicDispatcherTestBase {
public:
BufferedPacketStoreTest()
: QuicDispatcherTestBase(),
client_addr_(QuicIpAddress::Loopback4(), 1234) {}
void ProcessFirstFlight(const ParsedQuicVersion& version,
const QuicSocketAddress& peer_address,
const QuicConnectionId& server_connection_id) {
QuicDispatcherTestBase::ProcessFirstFlight(version, peer_address,
server_connection_id);
}
void ProcessFirstFlight(const QuicSocketAddress& peer_address,
const QuicConnectionId& server_connection_id) {
ProcessFirstFlight(version_, peer_address, server_connection_id);
}
void ProcessFirstFlight(const QuicConnectionId& server_connection_id) {
ProcessFirstFlight(client_addr_, server_connection_id);
}
void ProcessFirstFlight(const ParsedQuicVersion& version,
const QuicConnectionId& server_connection_id) {
ProcessFirstFlight(version, client_addr_, server_connection_id);
}
void ProcessUndecryptableEarlyPacket(
const ParsedQuicVersion& version,
const QuicSocketAddress& peer_address,
const QuicConnectionId& server_connection_id) {
QuicDispatcherTestBase::ProcessUndecryptableEarlyPacket(
version, peer_address, server_connection_id);
}
void ProcessUndecryptableEarlyPacket(
const QuicSocketAddress& peer_address,
const QuicConnectionId& server_connection_id) {
ProcessUndecryptableEarlyPacket(version_, peer_address,
server_connection_id);
}
void ProcessUndecryptableEarlyPacket(
const QuicConnectionId& server_connection_id) {
ProcessUndecryptableEarlyPacket(version_, client_addr_,
server_connection_id);
}
protected:
QuicSocketAddress client_addr_;
};
INSTANTIATE_TEST_SUITE_P(BufferedPacketStoreTests,
BufferedPacketStoreTest,
::testing::ValuesIn(CurrentSupportedVersions()),
::testing::PrintToStringParamName());
TEST_P(BufferedPacketStoreTest, ProcessNonChloPacketBeforeChlo) {
InSequence s;
QuicConnectionId conn_id = TestConnectionId(1);
// Non-CHLO should be buffered upon arrival, and should trigger
// ShouldCreateOrBufferPacketForConnection().
EXPECT_CALL(*dispatcher_, ShouldCreateOrBufferPacketForConnection(
ReceivedPacketInfoConnectionIdEquals(conn_id)));
// Process non-CHLO packet.
ProcessUndecryptableEarlyPacket(conn_id);
EXPECT_EQ(0u, dispatcher_->NumSessions())
<< "No session should be created before CHLO arrives.";
// When CHLO arrives, a new session should be created, and all packets
// buffered should be delivered to the session.
EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, _, client_addr_,
Eq(ExpectedAlpn()), _))
.WillOnce(Return(ByMove(CreateSession(
dispatcher_.get(), config_, conn_id, client_addr_, &mock_helper_,
&mock_alarm_factory_, &crypto_config_,
QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))));
EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
ProcessUdpPacket(_, _, _))
.Times(2) // non-CHLO + CHLO.
.WillRepeatedly(
WithArg<2>(Invoke([this, conn_id](const QuicEncryptedPacket& packet) {
if (version_.UsesQuicCrypto()) {
ValidatePacket(conn_id, packet);
}
})));
ProcessFirstFlight(conn_id);
}
TEST_P(BufferedPacketStoreTest, ProcessNonChloPacketsUptoLimitAndProcessChlo) {
InSequence s;
QuicConnectionId conn_id = TestConnectionId(1);
// A bunch of non-CHLO should be buffered upon arrival, and the first one
// should trigger ShouldCreateOrBufferPacketForConnection().
EXPECT_CALL(*dispatcher_, ShouldCreateOrBufferPacketForConnection(
ReceivedPacketInfoConnectionIdEquals(conn_id)));
for (size_t i = 1; i <= kDefaultMaxUndecryptablePackets + 1; ++i) {
ProcessUndecryptableEarlyPacket(conn_id);
}
EXPECT_EQ(0u, dispatcher_->NumSessions())
<< "No session should be created before CHLO arrives.";
// Pop out the last packet as it is also be dropped by the store.
data_connection_map_[conn_id].pop_back();
// When CHLO arrives, a new session should be created, and all packets
// buffered should be delivered to the session.
EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, _, client_addr_,
Eq(ExpectedAlpn()), _))
.WillOnce(Return(ByMove(CreateSession(
dispatcher_.get(), config_, conn_id, client_addr_, &mock_helper_,
&mock_alarm_factory_, &crypto_config_,
QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))));
// Only |kDefaultMaxUndecryptablePackets| packets were buffered, and they
// should be delivered in arrival order.
EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
ProcessUdpPacket(_, _, _))
.Times(kDefaultMaxUndecryptablePackets + 1) // + 1 for CHLO.
.WillRepeatedly(
WithArg<2>(Invoke([this, conn_id](const QuicEncryptedPacket& packet) {
if (version_.UsesQuicCrypto()) {
ValidatePacket(conn_id, packet);
}
})));
ProcessFirstFlight(conn_id);
}
TEST_P(BufferedPacketStoreTest,
ProcessNonChloPacketsForDifferentConnectionsUptoLimit) {
InSequence s;
// A bunch of non-CHLO should be buffered upon arrival.
size_t kNumConnections = kMaxConnectionsWithoutCHLO + 1;
for (size_t i = 1; i <= kNumConnections; ++i) {
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), i);
QuicConnectionId conn_id = TestConnectionId(i);
EXPECT_CALL(*dispatcher_,
ShouldCreateOrBufferPacketForConnection(
ReceivedPacketInfoConnectionIdEquals(conn_id)));
ProcessUndecryptableEarlyPacket(client_address, conn_id);
}
// Pop out the packet on last connection as it shouldn't be enqueued in store
// as well.
data_connection_map_[TestConnectionId(kNumConnections)].pop_front();
// Reset session creation counter to ensure processing CHLO can always
// create session.
QuicDispatcherPeer::set_new_sessions_allowed_per_event_loop(dispatcher_.get(),
kNumConnections);
// Process CHLOs to create session for these connections.
for (size_t i = 1; i <= kNumConnections; ++i) {
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), i);
QuicConnectionId conn_id = TestConnectionId(i);
if (i == kNumConnections) {
EXPECT_CALL(*dispatcher_,
ShouldCreateOrBufferPacketForConnection(
ReceivedPacketInfoConnectionIdEquals(conn_id)));
}
EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, _, client_address,
Eq(ExpectedAlpn()), _))
.WillOnce(Return(ByMove(CreateSession(
dispatcher_.get(), config_, conn_id, client_address, &mock_helper_,
&mock_alarm_factory_, &crypto_config_,
QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))));
// First |kNumConnections| - 1 connections should have buffered
// a packet in store. The rest should have been dropped.
size_t num_packet_to_process = i <= kMaxConnectionsWithoutCHLO ? 2u : 1u;
EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
ProcessUdpPacket(_, client_address, _))
.Times(num_packet_to_process)
.WillRepeatedly(WithArg<2>(
Invoke([this, conn_id](const QuicEncryptedPacket& packet) {
if (version_.UsesQuicCrypto()) {
ValidatePacket(conn_id, packet);
}
})));
ProcessFirstFlight(client_address, conn_id);
}
}
// Tests that store delivers empty packet list if CHLO arrives firstly.
TEST_P(BufferedPacketStoreTest, DeliverEmptyPackets) {
QuicConnectionId conn_id = TestConnectionId(1);
EXPECT_CALL(*dispatcher_, ShouldCreateOrBufferPacketForConnection(
ReceivedPacketInfoConnectionIdEquals(conn_id)));
EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, _, client_addr_,
Eq(ExpectedAlpn()), _))
.WillOnce(Return(ByMove(CreateSession(
dispatcher_.get(), config_, conn_id, client_addr_, &mock_helper_,
&mock_alarm_factory_, &crypto_config_,
QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))));
EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
ProcessUdpPacket(_, client_addr_, _));
ProcessFirstFlight(conn_id);
}
// Tests that a retransmitted CHLO arrives after a connection for the
// CHLO has been created.
TEST_P(BufferedPacketStoreTest, ReceiveRetransmittedCHLO) {
InSequence s;
QuicConnectionId conn_id = TestConnectionId(1);
ProcessUndecryptableEarlyPacket(conn_id);
// When CHLO arrives, a new session should be created, and all packets
// buffered should be delivered to the session.
EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, _, client_addr_,
Eq(ExpectedAlpn()), _))
.Times(1) // Only triggered by 1st CHLO.
.WillOnce(Return(ByMove(CreateSession(
dispatcher_.get(), config_, conn_id, client_addr_, &mock_helper_,
&mock_alarm_factory_, &crypto_config_,
QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))));
EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
ProcessUdpPacket(_, _, _))
.Times(3) // Triggered by 1 data packet and 2 CHLOs.
.WillRepeatedly(
WithArg<2>(Invoke([this, conn_id](const QuicEncryptedPacket& packet) {
if (version_.UsesQuicCrypto()) {
ValidatePacket(conn_id, packet);
}
})));
std::vector<std::unique_ptr<QuicReceivedPacket>> packets =
GetFirstFlightOfPackets(version_, conn_id);
ASSERT_EQ(packets.size(), 1u);
// Receive the CHLO once.
ProcessReceivedPacket(packets[0]->Clone(), client_addr_, version_, conn_id);
// Receive the CHLO a second time to simulate retransmission.
ProcessReceivedPacket(std::move(packets[0]), client_addr_, version_, conn_id);
}
// Tests that expiration of a connection add connection id to time wait list.
TEST_P(BufferedPacketStoreTest, ReceiveCHLOAfterExpiration) {
InSequence s;
CreateTimeWaitListManager();
QuicBufferedPacketStore* store =
QuicDispatcherPeer::GetBufferedPackets(dispatcher_.get());
QuicBufferedPacketStorePeer::set_clock(store, mock_helper_.GetClock());
QuicConnectionId conn_id = TestConnectionId(1);
ProcessPacket(client_addr_, conn_id, true, absl::StrCat("data packet ", 2),
CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER,
/*packet_number=*/2);
mock_helper_.AdvanceTime(
QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs));
QuicAlarm* alarm = QuicBufferedPacketStorePeer::expiration_alarm(store);
// Cancel alarm as if it had been fired.
alarm->Cancel();
store->OnExpirationTimeout();
// New arrived CHLO will be dropped because this connection is in time wait
// list.
ASSERT_TRUE(time_wait_list_manager_->IsConnectionIdInTimeWait(conn_id));
EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, conn_id, _, _, _));
ProcessFirstFlight(conn_id);
}
TEST_P(BufferedPacketStoreTest, ProcessCHLOsUptoLimitAndBufferTheRest) {
// Process more than (|kMaxNumSessionsToCreate| +
// |kDefaultMaxConnectionsInStore|) CHLOs,
// the first |kMaxNumSessionsToCreate| should create connections immediately,
// the next |kDefaultMaxConnectionsInStore| should be buffered,
// the rest should be dropped.
QuicBufferedPacketStore* store =
QuicDispatcherPeer::GetBufferedPackets(dispatcher_.get());
const size_t kNumCHLOs =
kMaxNumSessionsToCreate + kDefaultMaxConnectionsInStore + 1;
for (uint64_t conn_id = 1; conn_id <= kNumCHLOs; ++conn_id) {
EXPECT_CALL(
*dispatcher_,
ShouldCreateOrBufferPacketForConnection(
ReceivedPacketInfoConnectionIdEquals(TestConnectionId(conn_id))));
if (conn_id <= kMaxNumSessionsToCreate) {
EXPECT_CALL(*dispatcher_,
CreateQuicSession(TestConnectionId(conn_id), _, client_addr_,
Eq(ExpectedAlpn()), _))
.WillOnce(Return(ByMove(CreateSession(
dispatcher_.get(), config_, TestConnectionId(conn_id),
client_addr_, &mock_helper_, &mock_alarm_factory_,
&crypto_config_, QuicDispatcherPeer::GetCache(dispatcher_.get()),
&session1_))));
EXPECT_CALL(
*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
ProcessUdpPacket(_, _, _))
.WillOnce(WithArg<2>(
Invoke([this, conn_id](const QuicEncryptedPacket& packet) {
if (version_.UsesQuicCrypto()) {
ValidatePacket(TestConnectionId(conn_id), packet);
}
})));
}
ProcessFirstFlight(TestConnectionId(conn_id));
if (conn_id <= kMaxNumSessionsToCreate + kDefaultMaxConnectionsInStore &&
conn_id > kMaxNumSessionsToCreate) {
EXPECT_TRUE(store->HasChloForConnection(TestConnectionId(conn_id)));
} else {
// First |kMaxNumSessionsToCreate| CHLOs should be passed to new
// connections immediately, and the last CHLO should be dropped as the
// store is full.
EXPECT_FALSE(store->HasChloForConnection(TestConnectionId(conn_id)));
}
}
// Graduately consume buffered CHLOs. The buffered connections should be
// created but the dropped one shouldn't.
for (uint64_t conn_id = kMaxNumSessionsToCreate + 1;
conn_id <= kMaxNumSessionsToCreate + kDefaultMaxConnectionsInStore;
++conn_id) {
EXPECT_CALL(*dispatcher_,
CreateQuicSession(TestConnectionId(conn_id), _, client_addr_,
Eq(ExpectedAlpn()), _))
.WillOnce(Return(ByMove(CreateSession(
dispatcher_.get(), config_, TestConnectionId(conn_id), client_addr_,
&mock_helper_, &mock_alarm_factory_, &crypto_config_,
QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))));
EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
ProcessUdpPacket(_, _, _))
.WillOnce(WithArg<2>(
Invoke([this, conn_id](const QuicEncryptedPacket& packet) {
if (version_.UsesQuicCrypto()) {
ValidatePacket(TestConnectionId(conn_id), packet);
}
})));
}
EXPECT_CALL(*dispatcher_,
CreateQuicSession(TestConnectionId(kNumCHLOs), _, client_addr_,
Eq(ExpectedAlpn()), _))
.Times(0);
while (store->HasChlosBuffered()) {
dispatcher_->ProcessBufferedChlos(kMaxNumSessionsToCreate);
}
EXPECT_EQ(TestConnectionId(static_cast<size_t>(kMaxNumSessionsToCreate) +
kDefaultMaxConnectionsInStore),
session1_->connection_id());
}
// Duplicated CHLO shouldn't be buffered.
TEST_P(BufferedPacketStoreTest, BufferDuplicatedCHLO) {
for (uint64_t conn_id = 1; conn_id <= kMaxNumSessionsToCreate + 1;
++conn_id) {
// Last CHLO will be buffered. Others will create connection right away.
if (conn_id <= kMaxNumSessionsToCreate) {
EXPECT_CALL(*dispatcher_,
CreateQuicSession(TestConnectionId(conn_id), _, client_addr_,
Eq(ExpectedAlpn()), _))
.WillOnce(Return(ByMove(CreateSession(
dispatcher_.get(), config_, TestConnectionId(conn_id),
client_addr_, &mock_helper_, &mock_alarm_factory_,
&crypto_config_, QuicDispatcherPeer::GetCache(dispatcher_.get()),
&session1_))));
EXPECT_CALL(
*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
ProcessUdpPacket(_, _, _))
.WillOnce(WithArg<2>(
Invoke([this, conn_id](const QuicEncryptedPacket& packet) {
if (version_.UsesQuicCrypto()) {
ValidatePacket(TestConnectionId(conn_id), packet);
}
})));
}
ProcessFirstFlight(TestConnectionId(conn_id));
}
// Retransmit CHLO on last connection should be dropped.
QuicConnectionId last_connection =
TestConnectionId(kMaxNumSessionsToCreate + 1);
ProcessFirstFlight(last_connection);
size_t packets_buffered = 2;
// Reset counter and process buffered CHLO.
EXPECT_CALL(*dispatcher_, CreateQuicSession(last_connection, _, client_addr_,
Eq(ExpectedAlpn()), _))
.WillOnce(Return(ByMove(CreateSession(
dispatcher_.get(), config_, last_connection, client_addr_,
&mock_helper_, &mock_alarm_factory_, &crypto_config_,
QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))));
// Only one packet(CHLO) should be process.
EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
ProcessUdpPacket(_, _, _))
.Times(packets_buffered)
.WillRepeatedly(WithArg<2>(
Invoke([this, last_connection](const QuicEncryptedPacket& packet) {
if (version_.UsesQuicCrypto()) {
ValidatePacket(last_connection, packet);
}
})));
dispatcher_->ProcessBufferedChlos(kMaxNumSessionsToCreate);
}
TEST_P(BufferedPacketStoreTest, BufferNonChloPacketsUptoLimitWithChloBuffered) {
uint64_t last_conn_id = kMaxNumSessionsToCreate + 1;
QuicConnectionId last_connection_id = TestConnectionId(last_conn_id);
for (uint64_t conn_id = 1; conn_id <= last_conn_id; ++conn_id) {
// Last CHLO will be buffered. Others will create connection right away.
if (conn_id <= kMaxNumSessionsToCreate) {
EXPECT_CALL(*dispatcher_,
CreateQuicSession(TestConnectionId(conn_id), _, client_addr_,
Eq(ExpectedAlpn()), _))
.WillOnce(Return(ByMove(CreateSession(
dispatcher_.get(), config_, TestConnectionId(conn_id),
client_addr_, &mock_helper_, &mock_alarm_factory_,
&crypto_config_, QuicDispatcherPeer::GetCache(dispatcher_.get()),
&session1_))));
EXPECT_CALL(
*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
ProcessUdpPacket(_, _, _))
.WillRepeatedly(WithArg<2>(
Invoke([this, conn_id](const QuicEncryptedPacket& packet) {
if (version_.UsesQuicCrypto()) {
ValidatePacket(TestConnectionId(conn_id), packet);
}
})));
}
ProcessFirstFlight(TestConnectionId(conn_id));
}
// Process another |kDefaultMaxUndecryptablePackets| + 1 data packets. The
// last one should be dropped.
for (uint64_t packet_number = 2;
packet_number <= kDefaultMaxUndecryptablePackets + 2; ++packet_number) {
ProcessPacket(client_addr_, last_connection_id, true, "data packet");
}
// Reset counter and process buffered CHLO.
EXPECT_CALL(*dispatcher_,
CreateQuicSession(last_connection_id, _, client_addr_,
Eq(ExpectedAlpn()), _))
.WillOnce(Return(ByMove(CreateSession(
dispatcher_.get(), config_, last_connection_id, client_addr_,
&mock_helper_, &mock_alarm_factory_, &crypto_config_,
QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))));
// Only CHLO and following |kDefaultMaxUndecryptablePackets| data packets
// should be process.
EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
ProcessUdpPacket(_, _, _))
.Times(kDefaultMaxUndecryptablePackets + 1)
.WillRepeatedly(WithArg<2>(
Invoke([this, last_connection_id](const QuicEncryptedPacket& packet) {
if (version_.UsesQuicCrypto()) {
ValidatePacket(last_connection_id, packet);
}
})));
dispatcher_->ProcessBufferedChlos(kMaxNumSessionsToCreate);
}
// Tests that when dispatcher's packet buffer is full, a CHLO on connection
// which doesn't have buffered CHLO should be buffered.
TEST_P(BufferedPacketStoreTest, ReceiveCHLOForBufferedConnection) {
QuicBufferedPacketStore* store =
QuicDispatcherPeer::GetBufferedPackets(dispatcher_.get());
uint64_t conn_id = 1;
ProcessUndecryptableEarlyPacket(TestConnectionId(conn_id));
// Fill packet buffer to full with CHLOs on other connections. Need to feed
// extra CHLOs because the first |kMaxNumSessionsToCreate| are going to create
// session directly.
for (conn_id = 2;
conn_id <= kDefaultMaxConnectionsInStore + kMaxNumSessionsToCreate;
++conn_id) {
if (conn_id <= kMaxNumSessionsToCreate + 1) {
EXPECT_CALL(*dispatcher_,
CreateQuicSession(TestConnectionId(conn_id), _, client_addr_,
Eq(ExpectedAlpn()), _))
.WillOnce(Return(ByMove(CreateSession(
dispatcher_.get(), config_, TestConnectionId(conn_id),
client_addr_, &mock_helper_, &mock_alarm_factory_,
&crypto_config_, QuicDispatcherPeer::GetCache(dispatcher_.get()),
&session1_))));
EXPECT_CALL(
*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
ProcessUdpPacket(_, _, _))
.WillOnce(WithArg<2>(
Invoke([this, conn_id](const QuicEncryptedPacket& packet) {
if (version_.UsesQuicCrypto()) {
ValidatePacket(TestConnectionId(conn_id), packet);
}
})));
}
ProcessFirstFlight(TestConnectionId(conn_id));
}
EXPECT_FALSE(store->HasChloForConnection(
/*connection_id=*/TestConnectionId(1)));
// CHLO on connection 1 should still be buffered.
ProcessFirstFlight(TestConnectionId(1));
EXPECT_TRUE(store->HasChloForConnection(
/*connection_id=*/TestConnectionId(1)));
}
// Regression test for b/117874922.
TEST_P(BufferedPacketStoreTest, ProcessBufferedChloWithDifferentVersion) {
// Ensure the preferred version is not supported by the server.
QuicDisableVersion(AllSupportedVersions().front());
uint64_t last_connection_id = kMaxNumSessionsToCreate + 5;
ParsedQuicVersionVector supported_versions = CurrentSupportedVersions();
for (uint64_t conn_id = 1; conn_id <= last_connection_id; ++conn_id) {
// Last 5 CHLOs will be buffered. Others will create connection right away.
ParsedQuicVersion version =
supported_versions[(conn_id - 1) % supported_versions.size()];
if (conn_id <= kMaxNumSessionsToCreate) {
EXPECT_CALL(
*dispatcher_,
CreateQuicSession(TestConnectionId(conn_id), _, client_addr_,
Eq(ExpectedAlpnForVersion(version)), version))
.WillOnce(Return(ByMove(CreateSession(
dispatcher_.get(), config_, TestConnectionId(conn_id),
client_addr_, &mock_helper_, &mock_alarm_factory_,
&crypto_config_, QuicDispatcherPeer::GetCache(dispatcher_.get()),
&session1_))));
EXPECT_CALL(
*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
ProcessUdpPacket(_, _, _))
.WillRepeatedly(WithArg<2>(
Invoke([this, conn_id](const QuicEncryptedPacket& packet) {
if (version_.UsesQuicCrypto()) {
ValidatePacket(TestConnectionId(conn_id), packet);
}
})));
}
ProcessFirstFlight(version, TestConnectionId(conn_id));
}
// Process buffered CHLOs. Verify the version is correct.
for (uint64_t conn_id = kMaxNumSessionsToCreate + 1;
conn_id <= last_connection_id; ++conn_id) {
ParsedQuicVersion version =
supported_versions[(conn_id - 1) % supported_versions.size()];
EXPECT_CALL(*dispatcher_,
CreateQuicSession(TestConnectionId(conn_id), _, client_addr_,
Eq(ExpectedAlpnForVersion(version)), version))
.WillOnce(Return(ByMove(CreateSession(
dispatcher_.get(), config_, TestConnectionId(conn_id), client_addr_,
&mock_helper_, &mock_alarm_factory_, &crypto_config_,
QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))));
EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
ProcessUdpPacket(_, _, _))
.WillRepeatedly(WithArg<2>(
Invoke([this, conn_id](const QuicEncryptedPacket& packet) {
if (version_.UsesQuicCrypto()) {
ValidatePacket(TestConnectionId(conn_id), packet);
}
})));
}
dispatcher_->ProcessBufferedChlos(kMaxNumSessionsToCreate);
}
} // namespace
} // namespace test
} // namespace quic