| Index: net/tools/quic/quic_dispatcher_test.cc
|
| diff --git a/net/tools/quic/quic_dispatcher_test.cc b/net/tools/quic/quic_dispatcher_test.cc
|
| index 967474df0d683b0f8a1e6a1294a13c3271cb60c4..07df1e6830971a8b8d0c5a2ec7bb0a93bdfba742 100644
|
| --- a/net/tools/quic/quic_dispatcher_test.cc
|
| +++ b/net/tools/quic/quic_dispatcher_test.cc
|
| @@ -9,7 +9,7 @@
|
| #include <string>
|
|
|
| #include "base/macros.h"
|
| -#include "base/strings/string_piece.h"
|
| +#include "base/strings/string_number_conversions.h"
|
| #include "net/quic/core/crypto/crypto_handshake.h"
|
| #include "net/quic/core/crypto/quic_crypto_server_config.h"
|
| #include "net/quic/core/crypto/quic_random.h"
|
| @@ -17,18 +17,24 @@
|
| #include "net/quic/core/quic_flags.h"
|
| #include "net/quic/core/quic_utils.h"
|
| #include "net/quic/test_tools/crypto_test_utils.h"
|
| +#include "net/quic/test_tools/quic_buffered_packet_store_peer.h"
|
| #include "net/quic/test_tools/quic_test_utils.h"
|
| +#include "net/test/gtest_util.h"
|
| #include "net/tools/epoll_server/epoll_server.h"
|
| +#include "net/tools/quic/chlo_extractor.h"
|
| #include "net/tools/quic/quic_epoll_alarm_factory.h"
|
| #include "net/tools/quic/quic_epoll_connection_helper.h"
|
| #include "net/tools/quic/quic_packet_writer_wrapper.h"
|
| #include "net/tools/quic/quic_simple_server_session_helper.h"
|
| #include "net/tools/quic/quic_time_wait_list_manager.h"
|
| +#include "net/tools/quic/stateless_rejector.h"
|
| #include "net/tools/quic/test_tools/mock_quic_time_wait_list_manager.h"
|
| #include "net/tools/quic/test_tools/quic_dispatcher_peer.h"
|
| #include "testing/gmock/include/gmock/gmock.h"
|
| +#include "testing/gmock_mutant.h"
|
| #include "testing/gtest/include/gtest/gtest.h"
|
|
|
| +using base::IntToString;
|
| using base::StringPiece;
|
| using net::EpollServer;
|
| using net::test::ConstructEncryptedPacket;
|
| @@ -36,14 +42,18 @@ using net::test::CryptoTestUtils;
|
| using net::test::MockQuicConnection;
|
| using net::test::MockQuicConnectionHelper;
|
| using net::test::ValueRestore;
|
| +using std::ostream;
|
| using std::string;
|
| using std::vector;
|
| +using testing::CreateFunctor;
|
| using testing::DoAll;
|
| using testing::InSequence;
|
| using testing::Invoke;
|
| using testing::WithoutArgs;
|
| using testing::_;
|
|
|
| +static const size_t kDefaultMaxConnectionsInStore = 100;
|
| +
|
| namespace net {
|
| namespace test {
|
| namespace {
|
| @@ -61,7 +71,8 @@ class TestQuicSpdyServerSession : public QuicServerSessionBase {
|
| crypto_config,
|
| compressed_certs_cache),
|
| crypto_stream_(QuicServerSessionBase::GetCryptoStream()) {}
|
| - ~TestQuicSpdyServerSession() override{};
|
| +
|
| + ~TestQuicSpdyServerSession() override { delete connection(); };
|
|
|
| MOCK_METHOD3(OnConnectionClosed,
|
| void(QuicErrorCode error,
|
| @@ -114,6 +125,8 @@ class TestDispatcher : public QuicDispatcher {
|
| QuicServerSessionBase*(QuicConnectionId connection_id,
|
| const IPEndPoint& client_address));
|
|
|
| + MOCK_METHOD1(OnNewConnectionAdded, void(QuicConnectionId connection_id));
|
| +
|
| using QuicDispatcher::current_server_address;
|
| using QuicDispatcher::current_client_address;
|
| };
|
| @@ -144,36 +157,12 @@ class MockServerConnection : public MockQuicConnection {
|
| QuicDispatcher* dispatcher_;
|
| };
|
|
|
| -QuicServerSessionBase* CreateSession(
|
| - QuicDispatcher* dispatcher,
|
| - const QuicConfig& config,
|
| - QuicConnectionId connection_id,
|
| - const IPEndPoint& client_address,
|
| - MockQuicConnectionHelper* helper,
|
| - MockAlarmFactory* alarm_factory,
|
| - const QuicCryptoServerConfig* crypto_config,
|
| - QuicCompressedCertsCache* compressed_certs_cache,
|
| - TestQuicSpdyServerSession** session) {
|
| - MockServerConnection* connection = new MockServerConnection(
|
| - connection_id, helper, alarm_factory, dispatcher);
|
| - *session = new TestQuicSpdyServerSession(config, connection, crypto_config,
|
| - compressed_certs_cache);
|
| - connection->set_visitor(*session);
|
| - ON_CALL(*connection, CloseConnection(_, _, _))
|
| - .WillByDefault(WithoutArgs(Invoke(
|
| - connection, &MockServerConnection::UnregisterOnConnectionClosed)));
|
| - EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>((*session)->connection()),
|
| - ProcessUdpPacket(_, client_address, _));
|
| -
|
| - return *session;
|
| -}
|
| -
|
| class QuicDispatcherTest : public ::testing::Test {
|
| public:
|
| QuicDispatcherTest()
|
| : helper_(&eps_, QuicAllocator::BUFFER_POOL),
|
| alarm_factory_(&eps_),
|
| - version_manager_(QuicSupportedVersions()),
|
| + version_manager_(AllSupportedVersions()),
|
| crypto_config_(QuicCryptoServerConfig::TESTING,
|
| QuicRandom::GetInstance(),
|
| CryptoTestUtils::ProofSourceForTesting()),
|
| @@ -183,7 +172,10 @@ class QuicDispatcherTest : public ::testing::Test {
|
| &eps_)),
|
| time_wait_list_manager_(nullptr),
|
| session1_(nullptr),
|
| - session2_(nullptr) {
|
| + session2_(nullptr),
|
| + store_(nullptr) {}
|
| +
|
| + void SetUp() override {
|
| dispatcher_->InitializeWithWriter(new QuicDefaultPacketWriter(1));
|
| }
|
|
|
| @@ -235,8 +227,8 @@ class QuicDispatcherTest : public ::testing::Test {
|
| QuicPathId path_id,
|
| QuicPacketNumber packet_number) {
|
| ProcessPacket(client_address, connection_id, has_version_flag,
|
| - QuicSupportedVersions().front(), data, connection_id_length,
|
| - packet_number_length, packet_number);
|
| + CurrentSupportedVersions().front(), data,
|
| + connection_id_length, packet_number_length, packet_number);
|
| }
|
|
|
| // Processes a packet.
|
| @@ -255,14 +247,47 @@ class QuicDispatcherTest : public ::testing::Test {
|
| std::unique_ptr<QuicReceivedPacket> received_packet(
|
| ConstructReceivedPacket(*packet, helper_.GetClock()->Now()));
|
|
|
| - data_ = string(packet->data(), packet->length());
|
| + if (ChloExtractor::Extract(*packet, versions, nullptr)) {
|
| + // Add CHLO packet to the beginning to be verified first, because it is
|
| + // also processed first by new session.
|
| + data_connection_map_[connection_id].push_front(
|
| + string(packet->data(), packet->length()));
|
| + } else {
|
| + // For non-CHLO, always append to last.
|
| + data_connection_map_[connection_id].push_back(
|
| + string(packet->data(), packet->length()));
|
| + }
|
| dispatcher_->ProcessPacket(server_address_, client_address,
|
| *received_packet);
|
| }
|
|
|
| - void ValidatePacket(const QuicEncryptedPacket& packet) {
|
| - EXPECT_EQ(data_.length(), packet.AsStringPiece().length());
|
| - EXPECT_EQ(data_, packet.AsStringPiece());
|
| + 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();
|
| + }
|
| +
|
| + QuicServerSessionBase* CreateSession(
|
| + QuicDispatcher* dispatcher,
|
| + const QuicConfig& config,
|
| + QuicConnectionId connection_id,
|
| + const IPEndPoint& client_address,
|
| + MockQuicConnectionHelper* helper,
|
| + MockAlarmFactory* alarm_factory,
|
| + const QuicCryptoServerConfig* crypto_config,
|
| + QuicCompressedCertsCache* compressed_certs_cache,
|
| + TestQuicSpdyServerSession** session) {
|
| + MockServerConnection* connection = new MockServerConnection(
|
| + connection_id, helper, alarm_factory, dispatcher);
|
| + *session = new TestQuicSpdyServerSession(config, connection, crypto_config,
|
| + compressed_certs_cache);
|
| + connection->set_visitor(*session);
|
| + ON_CALL(*connection, CloseConnection(_, _, _))
|
| + .WillByDefault(WithoutArgs(Invoke(
|
| + connection, &MockServerConnection::UnregisterOnConnectionClosed)));
|
| + return *session;
|
| }
|
|
|
| void CreateTimeWaitListManager() {
|
| @@ -293,7 +318,8 @@ class QuicDispatcherTest : public ::testing::Test {
|
| MockTimeWaitListManager* time_wait_list_manager_;
|
| TestQuicSpdyServerSession* session1_;
|
| TestQuicSpdyServerSession* session2_;
|
| - string data_;
|
| + std::map<QuicConnectionId, std::list<string>> data_connection_map_;
|
| + QuicBufferedPacketStore* store_;
|
| };
|
|
|
| TEST_F(QuicDispatcherTest, ProcessPackets) {
|
| @@ -305,6 +331,10 @@ TEST_F(QuicDispatcherTest, ProcessPackets) {
|
| dispatcher_.get(), config_, 1, client_address, &mock_helper_,
|
| &mock_alarm_factory_, &crypto_config_,
|
| QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_)));
|
| + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
|
| + ProcessUdpPacket(_, _, _))
|
| + .WillOnce(testing::WithArgs<2>(Invoke(CreateFunctor(
|
| + &QuicDispatcherTest::ValidatePacket, base::Unretained(this), 1))));
|
| ProcessPacket(client_address, 1, true, false, SerializeCHLO());
|
| EXPECT_EQ(client_address, dispatcher_->current_client_address());
|
| EXPECT_EQ(server_address_, dispatcher_->current_server_address());
|
| @@ -314,13 +344,17 @@ TEST_F(QuicDispatcherTest, ProcessPackets) {
|
| dispatcher_.get(), config_, 2, client_address, &mock_helper_,
|
| &mock_alarm_factory_, &crypto_config_,
|
| QuicDispatcherPeer::GetCache(dispatcher_.get()), &session2_)));
|
| + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session2_->connection()),
|
| + ProcessUdpPacket(_, _, _))
|
| + .WillOnce(testing::WithArgs<2>(Invoke(CreateFunctor(
|
| + &QuicDispatcherTest::ValidatePacket, base::Unretained(this), 2))));
|
| ProcessPacket(client_address, 2, true, false, SerializeCHLO());
|
|
|
| EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
|
| ProcessUdpPacket(_, _, _))
|
| .Times(1)
|
| - .WillOnce(testing::WithArgs<2>(
|
| - Invoke(this, &QuicDispatcherTest::ValidatePacket)));
|
| + .WillOnce(testing::WithArgs<2>(Invoke(CreateFunctor(
|
| + &QuicDispatcherTest::ValidatePacket, base::Unretained(this), 1))));
|
| ProcessPacket(client_address, 1, false, false, "data");
|
| }
|
|
|
| @@ -342,6 +376,10 @@ TEST_F(QuicDispatcherTest, Shutdown) {
|
| dispatcher_.get(), config_, 1, client_address, &mock_helper_,
|
| &mock_alarm_factory_, &crypto_config_,
|
| QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_)));
|
| + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
|
| + ProcessUdpPacket(_, _, _))
|
| + .WillOnce(testing::WithArgs<2>(Invoke(CreateFunctor(
|
| + &QuicDispatcherTest::ValidatePacket, base::Unretained(this), 1))));
|
|
|
| ProcessPacket(client_address, 1, true, false, SerializeCHLO());
|
|
|
| @@ -362,6 +400,11 @@ TEST_F(QuicDispatcherTest, TimeWaitListManager) {
|
| 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(testing::WithArgs<2>(Invoke(CreateFunctor(
|
| + &QuicDispatcherTest::ValidatePacket, base::Unretained(this), 1))));
|
| +
|
| ProcessPacket(client_address, connection_id, true, false, SerializeCHLO());
|
|
|
| // Close the connection by sending public reset packet.
|
| @@ -439,6 +482,10 @@ TEST_F(QuicDispatcherTest, OKSeqNoPacketProcessed) {
|
| dispatcher_.get(), config_, 1, client_address, &mock_helper_,
|
| &mock_alarm_factory_, &crypto_config_,
|
| QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_)));
|
| + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
|
| + ProcessUdpPacket(_, _, _))
|
| + .WillOnce(testing::WithArgs<2>(Invoke(CreateFunctor(
|
| + &QuicDispatcherTest::ValidatePacket, base::Unretained(this), 1))));
|
| // A packet whose packet number is the largest that is allowed to start a
|
| // connection.
|
| ProcessPacket(client_address, connection_id, true, false, SerializeCHLO(),
|
| @@ -535,7 +582,8 @@ class QuicDispatcherStatelessRejectTest
|
| : public QuicDispatcherTest,
|
| public ::testing::WithParamInterface<StatelessRejectTestParams> {
|
| public:
|
| - QuicDispatcherStatelessRejectTest() : crypto_stream1_(nullptr) {}
|
| + QuicDispatcherStatelessRejectTest()
|
| + : QuicDispatcherTest(), crypto_stream1_(nullptr) {}
|
|
|
| ~QuicDispatcherStatelessRejectTest() override {
|
| if (crypto_stream1_) {
|
| @@ -546,6 +594,7 @@ class QuicDispatcherStatelessRejectTest
|
| // This test setup assumes that all testing will be done using
|
| // crypto_stream1_.
|
| void SetUp() override {
|
| + QuicDispatcherTest::SetUp();
|
| FLAGS_enable_quic_stateless_reject_support =
|
| GetParam().enable_stateless_rejects_via_flag;
|
| }
|
| @@ -596,7 +645,11 @@ TEST_P(QuicDispatcherStatelessRejectTest, ParameterizedBasicTest) {
|
| EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address))
|
| .WillOnce(testing::Return(
|
| CreateSessionBasedOnTestParams(connection_id, client_address)));
|
| -
|
| + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
|
| + ProcessUdpPacket(_, _, _))
|
| + .WillOnce(testing::WithArgs<2>(
|
| + Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket,
|
| + base::Unretained(this), connection_id))));
|
| // Process the first packet for the connection.
|
| ProcessPacket(client_address, connection_id, true, false, SerializeCHLO());
|
| if (ExpectStatelessReject()) {
|
| @@ -622,13 +675,15 @@ TEST_P(QuicDispatcherStatelessRejectTest, ParameterizedBasicTest) {
|
| ProcessUdpPacket(_, _, _))
|
| .Times(1)
|
| .WillOnce(testing::WithArgs<2>(
|
| - Invoke(this, &QuicDispatcherTest::ValidatePacket)));
|
| + Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket,
|
| + base::Unretained(this), connection_id))));
|
| }
|
| ProcessPacket(client_address, connection_id, true, false, "data");
|
| }
|
|
|
| TEST_P(QuicDispatcherStatelessRejectTest, CheapRejects) {
|
| FLAGS_quic_use_cheap_stateless_rejects = true;
|
| + FLAGS_quic_buffer_packet_till_chlo = true;
|
| CreateTimeWaitListManager();
|
|
|
| IPEndPoint client_address(net::test::Loopback4(), 1);
|
| @@ -640,6 +695,10 @@ TEST_P(QuicDispatcherStatelessRejectTest, CheapRejects) {
|
| EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address))
|
| .WillOnce(testing::Return(
|
| CreateSessionBasedOnTestParams(connection_id, client_address)));
|
| + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
|
| + ProcessUdpPacket(_, _, _))
|
| + .WillOnce(testing::WithArgs<2>(Invoke(CreateFunctor(
|
| + &QuicDispatcherTest::ValidatePacket, base::Unretained(this), 1))));
|
| }
|
|
|
| VLOG(1) << "ExpectStatelessReject: " << ExpectStatelessReject();
|
| @@ -668,20 +727,41 @@ TEST_P(QuicDispatcherStatelessRejectTest, CheapRejects) {
|
|
|
| TEST_P(QuicDispatcherStatelessRejectTest, BufferNonChlo) {
|
| FLAGS_quic_use_cheap_stateless_rejects = true;
|
| + FLAGS_quic_always_log_bugs_for_tests = true;
|
| CreateTimeWaitListManager();
|
|
|
| const IPEndPoint client_address(net::test::Loopback4(), 1);
|
| const QuicConnectionId connection_id = 1;
|
|
|
| - if (!GetParam().enable_stateless_rejects_via_flag) {
|
| - // If stateless rejects are not being used, then a connection will be
|
| - // created immediately.
|
| + if (!GetParam().enable_stateless_rejects_via_flag &&
|
| + !FLAGS_quic_buffer_packet_till_chlo) {
|
| + // If stateless rejects are not being used and early arrived packets are not
|
| + // buffered, then a connection will be created immediately.
|
| EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address))
|
| .WillOnce(testing::Return(
|
| CreateSessionBasedOnTestParams(connection_id, client_address)));
|
| + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
|
| + ProcessUdpPacket(_, client_address, _))
|
| + .WillOnce(testing::WithArg<2>(
|
| + Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket,
|
| + base::Unretained(this), connection_id))));
|
| + }
|
| + bool first_packet_dropped = GetParam().enable_stateless_rejects_via_flag &&
|
| + !FLAGS_quic_buffer_packet_till_chlo;
|
| + if (first_packet_dropped) {
|
| + // Never do stateless reject while
|
| + // FLAGS_quic_buffer_packet_till_chlo is off.
|
| + EXPECT_DFATAL(
|
| + ProcessPacket(client_address, connection_id, true, false,
|
| + "NOT DATA FOR A CHLO"),
|
| + "Have to drop packet because buffering non-chlo packet is "
|
| + "not supported while trying to do stateless reject. "
|
| + "--gfe2_reloadable_flag_quic_buffer_packet_till_chlo false "
|
| + "--gfe2_reloadable_flag_quic_use_cheap_stateless_rejects true");
|
| + } else {
|
| + ProcessPacket(client_address, connection_id, true, false,
|
| + "NOT DATA FOR A CHLO");
|
| }
|
| - ProcessPacket(client_address, connection_id, true, false,
|
| - "NOT DATA FOR A CHLO");
|
|
|
| // Process the first packet for the connection.
|
| // clang-format off
|
| @@ -695,16 +775,33 @@ TEST_P(QuicDispatcherStatelessRejectTest, BufferNonChlo) {
|
| nullptr);
|
| // clang-format on
|
|
|
| - if (GetParam().enable_stateless_rejects_via_flag) {
|
| + if (GetParam().enable_stateless_rejects_via_flag ||
|
| + FLAGS_quic_buffer_packet_till_chlo) {
|
| // If stateless rejects are enabled then a connection will be created now
|
| // and the buffered packet will be processed
|
| EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address))
|
| .WillOnce(testing::Return(
|
| CreateSessionBasedOnTestParams(connection_id, client_address)));
|
| + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
|
| + ProcessUdpPacket(_, client_address, _))
|
| + .WillOnce(testing::WithArg<2>(
|
| + Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket,
|
| + base::Unretained(this), connection_id))));
|
| + }
|
| + if (!first_packet_dropped) {
|
| + // Expect both packets to be passed to ProcessUdpPacket(). And one of them
|
| + // is already expected in CreateSessionBasedOnTestParams().
|
| + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
|
| + ProcessUdpPacket(_, client_address, _))
|
| + .WillOnce(testing::WithArg<2>(
|
| + Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket,
|
| + base::Unretained(this), connection_id))))
|
| + .RetiresOnSaturation();
|
| + } else {
|
| + // Since first packet is dropped, remove it from map to skip
|
| + // ValidatePacket() on it.
|
| + data_connection_map_[connection_id].pop_front();
|
| }
|
| - EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
|
| - ProcessUdpPacket(_, client_address, _))
|
| - .RetiresOnSaturation();
|
| ProcessPacket(client_address, connection_id, true, false,
|
| client_hello.GetSerialized().AsStringPiece().as_string());
|
| EXPECT_FALSE(
|
| @@ -758,6 +855,7 @@ class BlockingWriter : public QuicPacketWriterWrapper {
|
| class QuicDispatcherWriteBlockedListTest : public QuicDispatcherTest {
|
| public:
|
| void SetUp() override {
|
| + QuicDispatcherTest::SetUp();
|
| writer_ = new BlockingWriter;
|
| QuicDispatcherPeer::UseWriter(dispatcher_.get(), writer_);
|
|
|
| @@ -768,6 +866,10 @@ class QuicDispatcherWriteBlockedListTest : public QuicDispatcherTest {
|
| dispatcher_.get(), config_, 1, client_address, &helper_,
|
| &alarm_factory_, &crypto_config_,
|
| QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_)));
|
| + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
|
| + ProcessUdpPacket(_, _, _))
|
| + .WillOnce(testing::WithArgs<2>(Invoke(CreateFunctor(
|
| + &QuicDispatcherTest::ValidatePacket, base::Unretained(this), 1))));
|
| ProcessPacket(client_address, 1, true, false, SerializeCHLO());
|
|
|
| EXPECT_CALL(*dispatcher_, CreateQuicSession(_, client_address))
|
| @@ -775,6 +877,10 @@ class QuicDispatcherWriteBlockedListTest : public QuicDispatcherTest {
|
| dispatcher_.get(), config_, 2, client_address, &helper_,
|
| &alarm_factory_, &crypto_config_,
|
| QuicDispatcherPeer::GetCache(dispatcher_.get()), &session2_)));
|
| + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session2_->connection()),
|
| + ProcessUdpPacket(_, _, _))
|
| + .WillOnce(testing::WithArgs<2>(Invoke(CreateFunctor(
|
| + &QuicDispatcherTest::ValidatePacket, base::Unretained(this), 2))));
|
| ProcessPacket(client_address, 2, true, false, SerializeCHLO());
|
|
|
| blocked_list_ = QuicDispatcherPeer::GetWriteBlockedList(dispatcher_.get());
|
| @@ -931,6 +1037,251 @@ TEST_F(QuicDispatcherWriteBlockedListTest, TestWriteLimits) {
|
| EXPECT_FALSE(dispatcher_->HasPendingWrites());
|
| }
|
|
|
| +// Tests that bufferring packets works in stateful reject, expensive stateless
|
| +// reject and cheap stateless reject.
|
| +struct BufferedPacketStoreTestParams {
|
| + BufferedPacketStoreTestParams(bool enable_stateless_rejects_via_flag,
|
| + bool support_cheap_stateless_reject)
|
| + : enable_stateless_rejects_via_flag(enable_stateless_rejects_via_flag),
|
| + support_cheap_stateless_reject(support_cheap_stateless_reject) {}
|
| +
|
| + friend ostream& operator<<(ostream& os,
|
| + const BufferedPacketStoreTestParams& p) {
|
| + os << "{ enable_stateless_rejects_via_flag: "
|
| + << p.enable_stateless_rejects_via_flag << std::endl;
|
| + os << " support_cheap_stateless_reject: "
|
| + << p.support_cheap_stateless_reject << " }";
|
| + return os;
|
| + }
|
| +
|
| + // This only enables the stateless reject feature via the feature-flag.
|
| + // This should be a no-op if the peer does not support them.
|
| + bool enable_stateless_rejects_via_flag;
|
| + // Whether to do cheap stateless or not.
|
| + bool support_cheap_stateless_reject;
|
| +};
|
| +
|
| +vector<BufferedPacketStoreTestParams> GetBufferedPacketStoreTestParams() {
|
| + vector<BufferedPacketStoreTestParams> params;
|
| + for (bool enable_stateless_rejects_via_flag : {true, false}) {
|
| + for (bool support_cheap_stateless_reject : {true, false}) {
|
| + params.push_back(BufferedPacketStoreTestParams(
|
| + enable_stateless_rejects_via_flag, support_cheap_stateless_reject));
|
| + }
|
| + }
|
| + return params;
|
| +}
|
| +
|
| +// A dispatcher whose stateless rejector will always ACCEPTs CHLO.
|
| +class BufferedPacketStoreTest
|
| + : public QuicDispatcherTest,
|
| + public ::testing::WithParamInterface<BufferedPacketStoreTestParams> {
|
| + public:
|
| + BufferedPacketStoreTest()
|
| + : QuicDispatcherTest(), client_addr_(Loopback4(), 1234) {
|
| + FLAGS_quic_buffer_packet_till_chlo = true;
|
| + FLAGS_quic_use_cheap_stateless_rejects =
|
| + GetParam().support_cheap_stateless_reject;
|
| + FLAGS_enable_quic_stateless_reject_support =
|
| + GetParam().enable_stateless_rejects_via_flag;
|
| + }
|
| +
|
| + void SetUp() override {
|
| + QuicDispatcherTest::SetUp();
|
| + clock_ = QuicDispatcherPeer::GetHelper(dispatcher_.get())->GetClock();
|
| +
|
| + QuicVersion version = AllSupportedVersions().front();
|
| + CryptoHandshakeMessage chlo = CryptoTestUtils::GenerateDefaultInchoateCHLO(
|
| + clock_, version, &crypto_config_);
|
| + chlo.SetVector(net::kCOPT, net::QuicTagVector{net::kSREJ});
|
| + // Pass an inchoate CHLO.
|
| + CryptoTestUtils::GenerateFullCHLO(
|
| + chlo, &crypto_config_, server_ip_, client_addr_, version, clock_,
|
| + &proof_, QuicDispatcherPeer::GetCache(dispatcher_.get()), &full_chlo_);
|
| + }
|
| +
|
| + string SerializeFullCHLO() {
|
| + return full_chlo_.GetSerialized().AsStringPiece().as_string();
|
| + }
|
| +
|
| + protected:
|
| + IPAddress server_ip_;
|
| + IPEndPoint client_addr_;
|
| + QuicCryptoProof proof_;
|
| + const QuicClock* clock_;
|
| + CryptoHandshakeMessage full_chlo_;
|
| +};
|
| +
|
| +INSTANTIATE_TEST_CASE_P(
|
| + BufferedPacketStoreTests,
|
| + BufferedPacketStoreTest,
|
| + ::testing::ValuesIn(GetBufferedPacketStoreTestParams()));
|
| +
|
| +TEST_P(BufferedPacketStoreTest, ProcessNonChloPacketsUptoLimitAndProcessChlo) {
|
| + InSequence s;
|
| + IPEndPoint client_address(Loopback4(), 1);
|
| + server_address_ = IPEndPoint(Any4(), 5);
|
| + QuicConnectionId conn_id = 1;
|
| + // A bunch of non-CHLO should be buffered upon arrival, and the first one
|
| + // should trigger OnNewConnectionAdded().
|
| + EXPECT_CALL(*dispatcher_, OnNewConnectionAdded(conn_id)).Times(1);
|
| + for (size_t i = 1; i <= kDefaultMaxUndecryptablePackets + 1; ++i) {
|
| + ProcessPacket(client_address, conn_id, true, false,
|
| + "data packet " + IntToString(i + 1),
|
| + PACKET_8BYTE_CONNECTION_ID, PACKET_6BYTE_PACKET_NUMBER,
|
| + kDefaultPathId,
|
| + /*packet_number=*/i + 1);
|
| + }
|
| + EXPECT_EQ(0u, dispatcher_->session_map().size())
|
| + << "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_address))
|
| + .WillOnce(testing::Return(CreateSession(
|
| + dispatcher_.get(), config_, conn_id, client_address, &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(testing::WithArg<2>(
|
| + Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket,
|
| + base::Unretained(this), conn_id))));
|
| + ProcessPacket(client_address, conn_id, true, false, SerializeFullCHLO());
|
| +}
|
| +
|
| +TEST_P(BufferedPacketStoreTest,
|
| + ProcessNonChloPacketsForDifferentConnectionsUptoLimit) {
|
| + InSequence s;
|
| + server_address_ = IPEndPoint(Any4(), 5);
|
| + // A bunch of non-CHLO should be buffered upon arrival.
|
| + for (size_t i = 1; i <= kDefaultMaxConnectionsInStore + 1; ++i) {
|
| + IPEndPoint client_address(Loopback4(), i);
|
| + QuicConnectionId conn_id = i;
|
| + if (i <= kDefaultMaxConnectionsInStore) {
|
| + // As they are on different connection, they should trigger
|
| + // OnNewConnectionAdded(). The last packet should be dropped.
|
| + EXPECT_CALL(*dispatcher_, OnNewConnectionAdded(conn_id));
|
| + }
|
| + ProcessPacket(client_address, conn_id, true, false,
|
| + "data packet on connection " + IntToString(i),
|
| + PACKET_8BYTE_CONNECTION_ID, PACKET_6BYTE_PACKET_NUMBER,
|
| + kDefaultPathId,
|
| + /*packet_number=*/2);
|
| + }
|
| +
|
| + // Pop out the packet on last connection as it shouldn't be enqueued in store
|
| + // as well.
|
| + data_connection_map_[kDefaultMaxConnectionsInStore + 1].pop_front();
|
| +
|
| + // Process CHLOs.
|
| + for (size_t i = 1; i <= kDefaultMaxConnectionsInStore + 1; ++i) {
|
| + IPEndPoint client_address(Loopback4(), i);
|
| + QuicConnectionId conn_id = i;
|
| + EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, client_address))
|
| + .WillOnce(testing::Return(CreateSession(
|
| + dispatcher_.get(), config_, conn_id, client_address, &mock_helper_,
|
| + &mock_alarm_factory_, &crypto_config_,
|
| + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_)));
|
| + if (conn_id == kDefaultMaxConnectionsInStore + 1) {
|
| + // The last CHLO should trigger OnNewConnectionAdded() since it's the
|
| + // first packet arrives on that connection.
|
| + EXPECT_CALL(*dispatcher_, OnNewConnectionAdded(conn_id));
|
| + }
|
| + // First |kDefaultMaxConnectionsInStore| connections should have buffered
|
| + // a packet in store. The rest should have been dropped.
|
| + if (i <= kDefaultMaxConnectionsInStore) {
|
| + EXPECT_CALL(
|
| + *reinterpret_cast<MockQuicConnection*>(session1_->connection()),
|
| + ProcessUdpPacket(_, client_address, _))
|
| + .Times(2)
|
| + .WillRepeatedly(testing::WithArg<2>(
|
| + Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket,
|
| + base::Unretained(this), conn_id))));
|
| + }
|
| + ProcessPacket(client_address, conn_id, true, false, SerializeFullCHLO());
|
| + }
|
| +}
|
| +
|
| +// Tests that store delivers empty packet list if CHLO arrives firstly.
|
| +TEST_P(BufferedPacketStoreTest, DeliverEmptyPackets) {
|
| + QuicConnectionId conn_id = 1;
|
| + IPEndPoint client_address(Loopback4(), 1);
|
| + EXPECT_CALL(*dispatcher_, OnNewConnectionAdded(conn_id));
|
| + EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, client_address))
|
| + .WillOnce(testing::Return(CreateSession(
|
| + dispatcher_.get(), config_, conn_id, client_address, &mock_helper_,
|
| + &mock_alarm_factory_, &crypto_config_,
|
| + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_)));
|
| + ProcessPacket(client_address, conn_id, true, false, SerializeFullCHLO());
|
| +}
|
| +
|
| +// Tests that by the time a retransmitted CHLO arrives, a connection for the
|
| +// CHLO should already be created.
|
| +TEST_P(BufferedPacketStoreTest, ReceiveRetransmittedCHLO) {
|
| + InSequence s;
|
| + IPEndPoint client_address(Loopback4(), 1);
|
| + server_address_ = IPEndPoint(Any4(), 5);
|
| + QuicConnectionId conn_id = 1;
|
| + ProcessPacket(client_address, conn_id, true, false,
|
| + "data packet " + IntToString(2), PACKET_8BYTE_CONNECTION_ID,
|
| + PACKET_6BYTE_PACKET_NUMBER, kDefaultPathId,
|
| + /*packet_number=*/2);
|
| +
|
| + // 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_address))
|
| + .Times(1) // Only triggered by 1st CHLO.
|
| + .WillOnce(testing::Return(CreateSession(
|
| + dispatcher_.get(), config_, conn_id, client_address, &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(testing::WithArg<2>(
|
| + Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket,
|
| + base::Unretained(this), conn_id))));
|
| + ProcessPacket(client_address, conn_id, true, false, SerializeFullCHLO());
|
| +
|
| + ProcessPacket(client_address, conn_id, true, false, SerializeFullCHLO());
|
| +}
|
| +
|
| +// 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());
|
| +
|
| + IPEndPoint client_address(Loopback4(), 1);
|
| + server_address_ = IPEndPoint(Any4(), 5);
|
| + QuicConnectionId conn_id = 1;
|
| + ProcessPacket(client_address, conn_id, true, false,
|
| + "data packet " + IntToString(2), PACKET_8BYTE_CONNECTION_ID,
|
| + PACKET_6BYTE_PACKET_NUMBER, kDefaultPathId,
|
| + /*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, _, _));
|
| + ProcessPacket(client_address, conn_id, true, false, SerializeFullCHLO());
|
| +}
|
| +
|
| } // namespace
|
| } // namespace test
|
| } // namespace net
|
|
|