| // 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 "net/third_party/quiche/src/quic/core/quic_dispatcher.h" | 
 |  | 
 | #include <memory> | 
 | #include <ostream> | 
 | #include <string> | 
 | #include <utility> | 
 |  | 
 | #include "net/third_party/quiche/src/quic/core/chlo_extractor.h" | 
 | #include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h" | 
 | #include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" | 
 | #include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h" | 
 | #include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" | 
 | #include "net/third_party/quiche/src/quic/core/quic_connection_id.h" | 
 | #include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h" | 
 | #include "net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.h" | 
 | #include "net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.h" | 
 | #include "net/third_party/quiche/src/quic/core/quic_types.h" | 
 | #include "net/third_party/quiche/src/quic/core/quic_utils.h" | 
 | #include "net/third_party/quiche/src/quic/core/quic_versions.h" | 
 | #include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" | 
 | #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" | 
 | #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" | 
 | #include "net/third_party/quiche/src/quic/platform/api/quic_test.h" | 
 | #include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h" | 
 | #include "net/third_party/quiche/src/quic/test_tools/fake_proof_source.h" | 
 | #include "net/third_party/quiche/src/quic/test_tools/mock_quic_time_wait_list_manager.h" | 
 | #include "net/third_party/quiche/src/quic/test_tools/quic_buffered_packet_store_peer.h" | 
 | #include "net/third_party/quiche/src/quic/test_tools/quic_crypto_server_config_peer.h" | 
 | #include "net/third_party/quiche/src/quic/test_tools/quic_dispatcher_peer.h" | 
 | #include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" | 
 | #include "net/third_party/quiche/src/quic/test_tools/quic_time_wait_list_manager_peer.h" | 
 | #include "net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.h" | 
 | #include "net/third_party/quiche/src/common/platform/api/quiche_arraysize.h" | 
 | #include "net/third_party/quiche/src/common/platform/api/quiche_str_cat.h" | 
 | #include "net/third_party/quiche/src/common/test_tools/quiche_test_utils.h" | 
 |  | 
 | using testing::_; | 
 | using testing::ByMove; | 
 | 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_METHOD2(OnConnectionClosed, | 
 |                void(const QuicConnectionCloseFrame& frame, | 
 |                     ConnectionCloseSource source)); | 
 |   MOCK_METHOD1(CreateIncomingStream, QuicSpdyStream*(QuicStreamId id)); | 
 |   MOCK_METHOD1(CreateIncomingStream, QuicSpdyStream*(PendingStream* pending)); | 
 |   MOCK_METHOD0(CreateOutgoingBidirectionalStream, QuicSpdyStream*()); | 
 |   MOCK_METHOD0(CreateOutgoingUnidirectionalStream, QuicSpdyStream*()); | 
 |  | 
 |   std::unique_ptr<QuicCryptoServerStreamBase> CreateQuicCryptoServerStream( | 
 |       const QuicCryptoServerConfig* crypto_config, | 
 |       QuicCompressedCertsCache* compressed_certs_cache) override { | 
 |     return CreateCryptoServerStream(crypto_config, compressed_certs_cache, this, | 
 |                                     stream_helper()); | 
 |   } | 
 |  | 
 |   QuicCryptoServerStream::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<QuicCryptoServerStream::Helper>( | 
 |                            new QuicSimpleCryptoServerStreamHelper()), | 
 |                        std::make_unique<MockAlarmFactory>(), | 
 |                        kQuicDefaultConnectionIdLength), | 
 |         random_(random) {} | 
 |  | 
 |   MOCK_METHOD4( | 
 |       CreateQuicSession, | 
 |       std::unique_ptr<QuicSession>(QuicConnectionId connection_id, | 
 |                                    const QuicSocketAddress& peer_address, | 
 |                                    quiche::QuicheStringPiece alpn, | 
 |                                    const quic::ParsedQuicVersion& version)); | 
 |  | 
 |   MOCK_METHOD1(ShouldCreateOrBufferPacketForConnection, | 
 |                bool(const ReceivedPacketInfo& packet_info)); | 
 |  | 
 |   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) {} | 
 |  | 
 |   void UnregisterOnConnectionClosed() { | 
 |     QUIC_LOG(ERROR) << "Unregistering " << connection_id(); | 
 |     dispatcher_->OnConnectionClosed(connection_id(), QUIC_NO_ERROR, | 
 |                                     "Unregistering.", | 
 |                                     ConnectionCloseSource::FROM_SELF); | 
 |   } | 
 |  | 
 |  private: | 
 |   QuicDispatcher* dispatcher_; | 
 | }; | 
 |  | 
 | class QuicDispatcherTest : public QuicTest { | 
 |  public: | 
 |   QuicDispatcherTest() | 
 |       : QuicDispatcherTest(crypto_test_utils::ProofSourceForTesting()) {} | 
 |  | 
 |   explicit QuicDispatcherTest(std::unique_ptr<ProofSource> proof_source) | 
 |       : 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 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 first supported version. | 
 |   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 first supported version. | 
 |   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 first supported version. | 
 |   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, | 
 |                   CurrentSupportedVersions().front(), 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())); | 
 |  | 
 |     if (ChloExtractor::Extract(*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(packet->data(), packet->length())); | 
 |     } else { | 
 |       // For non-CHLO, always append to last. | 
 |       data_connection_map_[server_connection_id].push_back( | 
 |           std::string(packet->data(), 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, "hq"); | 
 |     return std::string(client_hello.GetSerialized().AsStringPiece()); | 
 |   } | 
 |  | 
 |   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, | 
 |                                   quiche::QuicheStringPiece("hq"), _)) | 
 |         .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))); | 
 |     ProcessPacket(client_address, connection_id, true, version, SerializeCHLO(), | 
 |                   true, CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, 1); | 
 |   } | 
 |  | 
 |   void VerifyVersionNotSupported(ParsedQuicVersion version) { | 
 |     QuicConnectionId connection_id = TestConnectionId(++connection_id_); | 
 |     QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); | 
 |     EXPECT_CALL(*dispatcher_, | 
 |                 CreateQuicSession(connection_id, client_address, | 
 |                                   quiche::QuicheStringPiece("hq"), _)) | 
 |         .Times(0); | 
 |     ProcessPacket(client_address, connection_id, true, version, SerializeCHLO(), | 
 |                   true, CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, 1); | 
 |   } | 
 |  | 
 |   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_; | 
 | }; | 
 |  | 
 | TEST_F(QuicDispatcherTest, TlsClientHelloCreatesSession) { | 
 |   if (CurrentSupportedVersions().front().handshake_protocol != | 
 |       PROTOCOL_TLS1_3) { | 
 |     return; | 
 |   } | 
 |   QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); | 
 |  | 
 |   EXPECT_CALL(*dispatcher_, | 
 |               CreateQuicSession(TestConnectionId(1), client_address, | 
 |                                 quiche::QuicheStringPiece(""), _)) | 
 |       .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)))); | 
 |   ProcessPacket(client_address, TestConnectionId(1), true, | 
 |                 CurrentSupportedVersions().front(), SerializeCHLO(), true, | 
 |                 CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, 1); | 
 | } | 
 |  | 
 | TEST_F(QuicDispatcherTest, ProcessPackets) { | 
 |   QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); | 
 |  | 
 |   EXPECT_CALL(*dispatcher_, | 
 |               CreateQuicSession(TestConnectionId(1), client_address, | 
 |                                 quiche::QuicheStringPiece("hq"), _)) | 
 |       .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)))); | 
 |   ProcessPacket(client_address, TestConnectionId(1), true, SerializeCHLO()); | 
 |  | 
 |   EXPECT_CALL(*dispatcher_, | 
 |               CreateQuicSession(TestConnectionId(2), client_address, | 
 |                                 quiche::QuicheStringPiece("hq"), _)) | 
 |       .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)))); | 
 |   ProcessPacket(client_address, TestConnectionId(2), true, SerializeCHLO()); | 
 |  | 
 |   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_F(QuicDispatcherTest, DispatcherDoesNotRejectPacketNumberZero) { | 
 |   QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); | 
 |  | 
 |   EXPECT_CALL(*dispatcher_, | 
 |               CreateQuicSession(TestConnectionId(1), client_address, | 
 |                                 quiche::QuicheStringPiece("hq"), _)) | 
 |       .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)))); | 
 |   ProcessPacket( | 
 |       client_address, TestConnectionId(1), true, | 
 |       ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, | 
 |                         CurrentSupportedVersions().front().transport_version), | 
 |       SerializeCHLO(), true, CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, | 
 |       1); | 
 |   // Packet number 256 with packet number length 1 would be considered as 0 in | 
 |   // dispatcher. | 
 |   ProcessPacket( | 
 |       client_address, TestConnectionId(1), false, | 
 |       ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, | 
 |                         CurrentSupportedVersions().front().transport_version), | 
 |       "", true, CONNECTION_ID_PRESENT, PACKET_1BYTE_PACKET_NUMBER, 256); | 
 | } | 
 |  | 
 | TEST_F(QuicDispatcherTest, 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); | 
 |   // Pad the CHLO message with enough data to make the packet large enough | 
 |   // to trigger version negotiation. | 
 |   std::string chlo = SerializeCHLO() + std::string(1200, 'a'); | 
 |   DCHECK_LE(1200u, chlo.length()); | 
 |   ProcessPacket(client_address, TestConnectionId(1), true, | 
 |                 QuicVersionReservedForNegotiation(), chlo, true, | 
 |                 CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, 1); | 
 | } | 
 |  | 
 | TEST_F(QuicDispatcherTest, | 
 |        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); | 
 |   // Pad the CHLO message with enough data to make the packet large enough | 
 |   // to trigger version negotiation. | 
 |   std::string chlo = SerializeCHLO() + std::string(1200, 'a'); | 
 |   DCHECK_LE(1200u, chlo.length()); | 
 |   ProcessPacket(client_address, connection_id, true, | 
 |                 QuicVersionReservedForNegotiation(), chlo, true, | 
 |                 CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, 1); | 
 | } | 
 |  | 
 | TEST_F(QuicDispatcherTest, 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); | 
 |   // Pad the CHLO message with enough data to make the packet large enough | 
 |   // to trigger version negotiation. | 
 |   std::string chlo = SerializeCHLO() + std::string(1200, 'a'); | 
 |   DCHECK_LE(1200u, chlo.length()); | 
 |   ProcessPacket(client_address, TestConnectionId(1), TestConnectionId(2), true, | 
 |                 QuicVersionReservedForNegotiation(), chlo, true, | 
 |                 CONNECTION_ID_PRESENT, CONNECTION_ID_PRESENT, | 
 |                 PACKET_4BYTE_PACKET_NUMBER, 1); | 
 | } | 
 |  | 
 | TEST_F(QuicDispatcherTest, 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. | 
 |   DCHECK_LE(1200u, chlo.length()); | 
 |   std::string truncated_chlo = chlo.substr(0, 1100); | 
 |   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_F(QuicDispatcherTest, 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. | 
 |   DCHECK_LE(1200u, chlo.length()); | 
 |   std::string truncated_chlo = chlo.substr(0, 1100); | 
 |   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_F(QuicDispatcherTest, Shutdown) { | 
 |   QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); | 
 |  | 
 |   EXPECT_CALL( | 
 |       *dispatcher_, | 
 |       CreateQuicSession(_, client_address, quiche::QuicheStringPiece("hq"), _)) | 
 |       .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)))); | 
 |   ProcessPacket(client_address, TestConnectionId(1), true, SerializeCHLO()); | 
 |  | 
 |   EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), | 
 |               CloseConnection(QUIC_PEER_GOING_AWAY, _, _)); | 
 |  | 
 |   dispatcher_->Shutdown(); | 
 | } | 
 |  | 
 | TEST_F(QuicDispatcherTest, 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, | 
 |                                 quiche::QuicheStringPiece("hq"), _)) | 
 |       .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)))); | 
 |   ProcessPacket(client_address, connection_id, true, SerializeCHLO()); | 
 |  | 
 |   // 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_F(QuicDispatcherTest, 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(_, _, quiche::QuicheStringPiece("hq"), _)) | 
 |       .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, false, SerializeCHLO()); | 
 | } | 
 |  | 
 | TEST_F(QuicDispatcherTest, | 
 |        DonotTimeWaitPacketsWithUnknownConnectionIdAndNoVersion) { | 
 |   QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); | 
 |   CreateTimeWaitListManager(); | 
 |  | 
 |   char short_packet[22] = {0x70, 0xa7, 0x02, 0x6b}; | 
 |   QuicReceivedPacket packet(short_packet, 22, 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_F(QuicDispatcherTest, LongConnectionIdLengthReplaced) { | 
 |   if (!CurrentSupportedVersions()[0].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, | 
 |                                 quiche::QuicheStringPiece("hq"), _)) | 
 |       .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))); | 
 |   ProcessPacket(client_address, bad_connection_id, true, SerializeCHLO()); | 
 | } | 
 |  | 
 | // Makes sure zero-byte connection IDs are replaced by 8-byte ones. | 
 | TEST_F(QuicDispatcherTest, InvalidShortConnectionIdLengthReplaced) { | 
 |   if (!CurrentSupportedVersions()[0].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, | 
 |                                 quiche::QuicheStringPiece("hq"), _)) | 
 |       .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))); | 
 |   ProcessPacket(client_address, bad_connection_id, true, SerializeCHLO()); | 
 | } | 
 |  | 
 | // Makes sure TestConnectionId(1) creates a new connection and | 
 | // TestConnectionIdNineBytesLong(2) gets replaced. | 
 | TEST_F(QuicDispatcherTest, MixGoodAndBadConnectionIdLengthPackets) { | 
 |   if (!CurrentSupportedVersions()[0].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, | 
 |                                 quiche::QuicheStringPiece("hq"), _)) | 
 |       .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)))); | 
 |   ProcessPacket(client_address, TestConnectionId(1), true, SerializeCHLO()); | 
 |  | 
 |   EXPECT_CALL(*dispatcher_, | 
 |               CreateQuicSession(fixed_connection_id, client_address, | 
 |                                 quiche::QuicheStringPiece("hq"), _)) | 
 |       .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))); | 
 |   ProcessPacket(client_address, bad_connection_id, true, SerializeCHLO()); | 
 |  | 
 |   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_F(QuicDispatcherTest, ProcessPacketWithZeroPort) { | 
 |   CreateTimeWaitListManager(); | 
 |  | 
 |   QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 0); | 
 |  | 
 |   // dispatcher_ should drop this packet. | 
 |   EXPECT_CALL(*dispatcher_, | 
 |               CreateQuicSession(TestConnectionId(1), client_address, | 
 |                                 quiche::QuicheStringPiece("hq"), _)) | 
 |       .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), true, SerializeCHLO()); | 
 | } | 
 |  | 
 | TEST_F(QuicDispatcherTest, ProcessPacketWithInvalidShortInitialConnectionId) { | 
 |   // Enable a version that supports connection IDs of length different than 8. | 
 |   CreateTimeWaitListManager(); | 
 |  | 
 |   QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); | 
 |  | 
 |   // dispatcher_ should drop this packet. | 
 |   EXPECT_CALL( | 
 |       *dispatcher_, | 
 |       CreateQuicSession(_, client_address, quiche::QuicheStringPiece("hq"), _)) | 
 |       .Times(0); | 
 |   EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _)).Times(0); | 
 |   EXPECT_CALL(*time_wait_list_manager_, | 
 |               AddConnectionIdToTimeWait(_, _, _, _, _)) | 
 |       .Times(0); | 
 |   ProcessPacket(client_address, EmptyQuicConnectionId(), true, SerializeCHLO()); | 
 | } | 
 |  | 
 | TEST_F(QuicDispatcherTest, OKSeqNoPacketProcessed) { | 
 |   QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); | 
 |   QuicConnectionId connection_id = TestConnectionId(1); | 
 |  | 
 |   EXPECT_CALL(*dispatcher_, | 
 |               CreateQuicSession(TestConnectionId(1), client_address, | 
 |                                 quiche::QuicheStringPiece("hq"), _)) | 
 |       .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); | 
 |       }))); | 
 |  | 
 |   // A packet whose packet number is the largest that is allowed to start a | 
 |   // connection. | 
 |   EXPECT_CALL(*dispatcher_, | 
 |               ShouldCreateOrBufferPacketForConnection( | 
 |                   ReceivedPacketInfoConnectionIdEquals(connection_id))); | 
 |   ProcessPacket(client_address, connection_id, true, SerializeCHLO(), | 
 |                 CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, | 
 |                 QuicDispatcher::kMaxReasonableInitialPacketNumber); | 
 | } | 
 |  | 
 | TEST_F(QuicDispatcherTest, VersionsChangeInFlight) { | 
 |   for (ParsedQuicVersion version : AllSupportedVersions()) { | 
 |     QuicEnableVersion(version); | 
 |   } | 
 |   ASSERT_EQ(AllSupportedVersions(), CurrentSupportedVersions()); | 
 |  | 
 |   VerifyVersionNotSupported(QuicVersionReservedForNegotiation()); | 
 |  | 
 |   VerifyVersionSupported(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, | 
 |                                            QuicVersionMin().transport_version)); | 
 |   VerifyVersionSupported(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, | 
 |                                            QuicVersionMax().transport_version)); | 
 |  | 
 |   // Turn off version Q050. | 
 |   SetQuicReloadableFlag(quic_disable_version_q050, true); | 
 |   VerifyVersionNotSupported( | 
 |       ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_50)); | 
 |  | 
 |   // Turn on version Q050. | 
 |   SetQuicReloadableFlag(quic_disable_version_q050, false); | 
 |   VerifyVersionSupported( | 
 |       ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_50)); | 
 | } | 
 |  | 
 | TEST_F(QuicDispatcherTest, RejectDeprecatedVersionsWithVersionNegotiation) { | 
 |   static_assert(SupportedTransportVersions().size() == 6u, | 
 |                 "Please add deprecated versions to this test"); | 
 |   QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); | 
 |   CreateTimeWaitListManager(); | 
 |  | 
 |   { | 
 |     char packet47[kMinPacketSizeForVersionNegotiation] = { | 
 |         0xC0, 'Q', '0', '4', '7', /*connection ID length byte*/ 0x50}; | 
 |     QuicReceivedPacket received_packet47( | 
 |         packet47, kMinPacketSizeForVersionNegotiation, QuicTime::Zero()); | 
 |     EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0); | 
 |     EXPECT_CALL(*time_wait_list_manager_, | 
 |                 SendVersionNegotiationPacket(_, _, _, _, _, _, _, _)) | 
 |         .Times(1); | 
 |     dispatcher_->ProcessPacket(server_address_, client_address, | 
 |                                received_packet47); | 
 |   } | 
 |  | 
 |   { | 
 |     char packet45[kMinPacketSizeForVersionNegotiation] = { | 
 |         0xC0, 'Q', '0', '4', '5', /*connection ID length byte*/ 0x50}; | 
 |     QuicReceivedPacket received_packet45( | 
 |         packet45, kMinPacketSizeForVersionNegotiation, QuicTime::Zero()); | 
 |     EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0); | 
 |     EXPECT_CALL(*time_wait_list_manager_, | 
 |                 SendVersionNegotiationPacket(_, _, _, _, _, _, _, _)) | 
 |         .Times(1); | 
 |     dispatcher_->ProcessPacket(server_address_, client_address, | 
 |                                received_packet45); | 
 |   } | 
 |  | 
 |   { | 
 |     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(_, _, _, _, _, _, _, _)) | 
 |         .Times(1); | 
 |     dispatcher_->ProcessPacket(server_address_, client_address, | 
 |                                received_packet44); | 
 |   } | 
 | } | 
 |  | 
 | TEST_F(QuicDispatcherTest, VersionNegotiationProbeOld) { | 
 |   SetQuicFlag(FLAGS_quic_prober_uses_length_prefixed_connection_ids, false); | 
 |   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)); | 
 |   bool ietf_quic = true; | 
 |   bool use_length_prefix = | 
 |       GetQuicFlag(FLAGS_quic_prober_uses_length_prefixed_connection_ids); | 
 |   EXPECT_CALL( | 
 |       *time_wait_list_manager_, | 
 |       SendVersionNegotiationPacket(server_connection_id, client_connection_id, | 
 |                                    ietf_quic, use_length_prefix, _, _, _, _)) | 
 |       .Times(1); | 
 |   EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0); | 
 |  | 
 |   dispatcher_->ProcessPacket(server_address_, client_address, *received_packet); | 
 | } | 
 |  | 
 | TEST_F(QuicDispatcherTest, VersionNegotiationProbe) { | 
 |   SetQuicFlag(FLAGS_quic_prober_uses_length_prefixed_connection_ids, true); | 
 |   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)); | 
 |   bool ietf_quic = true; | 
 |   bool use_length_prefix = | 
 |       GetQuicFlag(FLAGS_quic_prober_uses_length_prefixed_connection_ids); | 
 |   EXPECT_CALL( | 
 |       *time_wait_list_manager_, | 
 |       SendVersionNegotiationPacket(server_connection_id, client_connection_id, | 
 |                                    ietf_quic, use_length_prefix, _, _, _, _)) | 
 |       .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_F(QuicDispatcherTest, VersionNegotiationProbeEndToEndOld) { | 
 |   SetQuicFlag(FLAGS_quic_prober_uses_length_prefixed_connection_ids, false); | 
 |  | 
 |   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 = 0; | 
 |   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_F(QuicDispatcherTest, VersionNegotiationProbeEndToEnd) { | 
 |   SetQuicFlag(FLAGS_quic_prober_uses_length_prefixed_connection_ids, true); | 
 |  | 
 |   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 = 0; | 
 |   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_F(QuicDispatcherTest, AndroidConformanceTestOld) { | 
 |   // TODO(b/139691956) Remove this test once the workaround is removed. | 
 |   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[] = { | 
 |     // Android UDP network conformance test packet as it was before this change: | 
 |     // https://android-review.googlesource.com/c/platform/cts/+/1104285 | 
 |     0x0c,  // public flags: 8-byte connection ID, 1-byte packet number | 
 |     0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,  // 8-byte connection ID | 
 |     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_F(QuicDispatcherTest, 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/+/1104285 | 
 |     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_F(QuicDispatcherTest, DoNotProcessSmallPacket) { | 
 |   CreateTimeWaitListManager(); | 
 |   QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); | 
 |  | 
 |   EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0); | 
 |   if (GetQuicReloadableFlag(quic_drop_small_initial_packets)) { | 
 |     EXPECT_CALL(*time_wait_list_manager_, SendPacket(_, _, _)).Times(0); | 
 |   } else { | 
 |     EXPECT_CALL(*time_wait_list_manager_, SendPacket(_, _, _)).Times(1); | 
 |   } | 
 |   EXPECT_CALL(*time_wait_list_manager_, | 
 |               AddConnectionIdToTimeWait(_, _, _, _, _)) | 
 |       .Times(0); | 
 |   ProcessPacket(client_address, TestConnectionId(1), true, | 
 |                 CurrentSupportedVersions()[0], SerializeCHLO(), false, | 
 |                 CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, 1); | 
 | } | 
 |  | 
 | TEST_F(QuicDispatcherTest, ProcessSmallCoalescedPacket) { | 
 |   SetQuicReloadableFlag(quic_enable_version_t099, true); | 
 |   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); | 
 | } | 
 |  | 
 | // Verify the stopgap test: Packets with truncated connection IDs should be | 
 | // dropped. | 
 | class QuicDispatcherTestStrayPacketConnectionId : public QuicDispatcherTest {}; | 
 |  | 
 | // Packets with truncated connection IDs should be dropped. | 
 | TEST_F(QuicDispatcherTestStrayPacketConnectionId, | 
 |        StrayPacketTruncatedConnectionId) { | 
 |   CreateTimeWaitListManager(); | 
 |  | 
 |   QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); | 
 |   QuicConnectionId connection_id = TestConnectionId(1); | 
 |   EXPECT_CALL(*dispatcher_, | 
 |               CreateQuicSession(_, _, quiche::QuicheStringPiece("hq"), _)) | 
 |       .Times(0); | 
 |   if (VersionHasIetfInvariantHeader( | 
 |           CurrentSupportedVersions()[0].transport_version)) { | 
 |     // This IETF packet has invalid connection ID length. | 
 |     EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _)) | 
 |         .Times(0); | 
 |     EXPECT_CALL(*time_wait_list_manager_, | 
 |                 AddConnectionIdToTimeWait(_, _, _, _, _)) | 
 |         .Times(0); | 
 |   } else { | 
 |     // This is a GQUIC packet considered as IETF QUIC packet with short header | 
 |     // with unacceptable packet number. | 
 |     EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _)) | 
 |         .Times(1); | 
 |     EXPECT_CALL(*time_wait_list_manager_, | 
 |                 AddConnectionIdToTimeWait(_, _, _, _, _)) | 
 |         .Times(1); | 
 |   } | 
 |   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 QuicDispatcherTest { | 
 |  public: | 
 |   void SetUp() override { | 
 |     QuicDispatcherTest::SetUp(); | 
 |     writer_ = new BlockingWriter; | 
 |     QuicDispatcherPeer::UseWriter(dispatcher_.get(), writer_); | 
 |  | 
 |     QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); | 
 |  | 
 |     EXPECT_CALL(*dispatcher_, | 
 |                 CreateQuicSession(_, client_address, | 
 |                                   quiche::QuicheStringPiece("hq"), _)) | 
 |         .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)))); | 
 |     ProcessPacket(client_address, TestConnectionId(1), true, SerializeCHLO()); | 
 |  | 
 |     EXPECT_CALL(*dispatcher_, | 
 |                 CreateQuicSession(_, client_address, | 
 |                                   quiche::QuicheStringPiece("hq"), _)) | 
 |         .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)))); | 
 |     ProcessPacket(client_address, TestConnectionId(2), true, SerializeCHLO()); | 
 |  | 
 |     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_; | 
 | }; | 
 |  | 
 | TEST_F(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_F(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_F(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_F(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_F(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_F(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_F(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_F(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_F(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 BufferedPacketStoreTest : public QuicDispatcherTest { | 
 |  public: | 
 |   BufferedPacketStoreTest() | 
 |       : QuicDispatcherTest(), | 
 |         server_addr_(QuicSocketAddress(QuicIpAddress::Any4(), 5)), | 
 |         client_addr_(QuicIpAddress::Loopback4(), 1234), | 
 |         signed_config_(new QuicSignedServerConfig) {} | 
 |  | 
 |   void SetUp() override { | 
 |     QuicDispatcherTest::SetUp(); | 
 |     clock_ = QuicDispatcherPeer::GetHelper(dispatcher_.get())->GetClock(); | 
 |  | 
 |     // The methods below use a PROTOCOL_QUIC_CRYPTO version so we pick the | 
 |     // first one from the list of supported versions. | 
 |     QuicTransportVersion transport_version = QUIC_VERSION_UNSUPPORTED; | 
 |     for (const ParsedQuicVersion& version : AllSupportedVersions()) { | 
 |       if (version.handshake_protocol == PROTOCOL_QUIC_CRYPTO) { | 
 |         transport_version = version.transport_version; | 
 |         break; | 
 |       } | 
 |     } | 
 |     ASSERT_NE(QUIC_VERSION_UNSUPPORTED, transport_version); | 
 |  | 
 |     CryptoHandshakeMessage chlo = | 
 |         crypto_test_utils::GenerateDefaultInchoateCHLO( | 
 |             clock_, transport_version, &crypto_config_); | 
 |     // Pass an inchoate CHLO. | 
 |     crypto_test_utils::GenerateFullCHLO( | 
 |         chlo, &crypto_config_, server_addr_, client_addr_, transport_version, | 
 |         clock_, signed_config_, QuicDispatcherPeer::GetCache(dispatcher_.get()), | 
 |         &full_chlo_); | 
 |   } | 
 |  | 
 |   std::string SerializeFullCHLO() { | 
 |     return std::string(full_chlo_.GetSerialized().AsStringPiece()); | 
 |   } | 
 |  | 
 |  protected: | 
 |   QuicSocketAddress server_addr_; | 
 |   QuicSocketAddress client_addr_; | 
 |   QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config_; | 
 |   const QuicClock* clock_; | 
 |   CryptoHandshakeMessage full_chlo_; | 
 | }; | 
 |  | 
 | TEST_F(BufferedPacketStoreTest, ProcessNonChloPacketsUptoLimitAndProcessChlo) { | 
 |   InSequence s; | 
 |   QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); | 
 |   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) { | 
 |     ProcessPacket(client_address, conn_id, true, | 
 |                   quiche::QuicheStrCat("data packet ", i + 1), | 
 |                   CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, | 
 |                   /*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, | 
 |                                               quiche::QuicheStringPiece(), _)) | 
 |       .WillOnce(Return(ByMove(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( | 
 |           WithArg<2>(Invoke([this, conn_id](const QuicEncryptedPacket& packet) { | 
 |             ValidatePacket(conn_id, packet); | 
 |           }))); | 
 |   ProcessPacket(client_address, conn_id, true, SerializeFullCHLO()); | 
 | } | 
 |  | 
 | TEST_F(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))); | 
 |     ProcessPacket(client_address, conn_id, true, | 
 |                   quiche::QuicheStrCat("data packet on connection ", i), | 
 |                   CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, | 
 |                   /*packet_number=*/2); | 
 |   } | 
 |  | 
 |   // 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, | 
 |                                                 quiche::QuicheStringPiece(), _)) | 
 |         .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) { | 
 |               ValidatePacket(conn_id, packet); | 
 |             }))); | 
 |  | 
 |     ProcessPacket(client_address, conn_id, true, SerializeFullCHLO()); | 
 |   } | 
 | } | 
 |  | 
 | // Tests that store delivers empty packet list if CHLO arrives firstly. | 
 | TEST_F(BufferedPacketStoreTest, DeliverEmptyPackets) { | 
 |   QuicConnectionId conn_id = TestConnectionId(1); | 
 |   QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); | 
 |   EXPECT_CALL(*dispatcher_, ShouldCreateOrBufferPacketForConnection( | 
 |                                 ReceivedPacketInfoConnectionIdEquals(conn_id))); | 
 |   EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, client_address, | 
 |                                               quiche::QuicheStringPiece(), _)) | 
 |       .WillOnce(Return(ByMove(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(_, client_address, _)); | 
 |   ProcessPacket(client_address, conn_id, true, SerializeFullCHLO()); | 
 | } | 
 |  | 
 | // Tests that a retransmitted CHLO arrives after a connection for the | 
 | // CHLO has been created. | 
 | TEST_F(BufferedPacketStoreTest, ReceiveRetransmittedCHLO) { | 
 |   InSequence s; | 
 |   QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); | 
 |   QuicConnectionId conn_id = TestConnectionId(1); | 
 |   ProcessPacket(client_address, conn_id, true, | 
 |                 quiche::QuicheStrCat("data packet ", 2), CONNECTION_ID_PRESENT, | 
 |                 PACKET_4BYTE_PACKET_NUMBER, | 
 |                 /*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, | 
 |                                               quiche::QuicheStringPiece(), _)) | 
 |       .Times(1)  // Only triggered by 1st CHLO. | 
 |       .WillOnce(Return(ByMove(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( | 
 |           WithArg<2>(Invoke([this, conn_id](const QuicEncryptedPacket& packet) { | 
 |             ValidatePacket(conn_id, packet); | 
 |           }))); | 
 |   ProcessPacket(client_address, conn_id, true, SerializeFullCHLO()); | 
 |  | 
 |   ProcessPacket(client_address, conn_id, true, SerializeFullCHLO()); | 
 | } | 
 |  | 
 | // Tests that expiration of a connection add connection id to time wait list. | 
 | TEST_F(BufferedPacketStoreTest, ReceiveCHLOAfterExpiration) { | 
 |   InSequence s; | 
 |   CreateTimeWaitListManager(); | 
 |   QuicBufferedPacketStore* store = | 
 |       QuicDispatcherPeer::GetBufferedPackets(dispatcher_.get()); | 
 |   QuicBufferedPacketStorePeer::set_clock(store, mock_helper_.GetClock()); | 
 |  | 
 |   QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); | 
 |   QuicConnectionId conn_id = TestConnectionId(1); | 
 |   ProcessPacket(client_address, conn_id, true, | 
 |                 quiche::QuicheStrCat("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, _, _)); | 
 |   ProcessPacket(client_address, conn_id, true, SerializeFullCHLO()); | 
 | } | 
 |  | 
 | TEST_F(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_, | 
 |                                     quiche::QuicheStringPiece(), _)) | 
 |           .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) { | 
 |                 ValidatePacket(TestConnectionId(conn_id), packet); | 
 |               }))); | 
 |     } | 
 |     ProcessPacket(client_addr_, TestConnectionId(conn_id), true, | 
 |                   SerializeFullCHLO()); | 
 |     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_, | 
 |                                   quiche::QuicheStringPiece(), _)) | 
 |         .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) { | 
 |               ValidatePacket(TestConnectionId(conn_id), packet); | 
 |             }))); | 
 |   } | 
 |   EXPECT_CALL(*dispatcher_, | 
 |               CreateQuicSession(TestConnectionId(kNumCHLOs), client_addr_, | 
 |                                 quiche::QuicheStringPiece(), _)) | 
 |       .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_F(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_, | 
 |                                     quiche::QuicheStringPiece(), _)) | 
 |           .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) { | 
 |                 ValidatePacket(TestConnectionId(conn_id), packet); | 
 |               }))); | 
 |     } | 
 |     ProcessPacket(client_addr_, TestConnectionId(conn_id), true, | 
 |                   SerializeFullCHLO()); | 
 |   } | 
 |   // Retransmit CHLO on last connection should be dropped. | 
 |   QuicConnectionId last_connection = | 
 |       TestConnectionId(kMaxNumSessionsToCreate + 1); | 
 |   ProcessPacket(client_addr_, last_connection, true, SerializeFullCHLO()); | 
 |  | 
 |   size_t packets_buffered = 2; | 
 |  | 
 |   // Reset counter and process buffered CHLO. | 
 |   EXPECT_CALL(*dispatcher_, CreateQuicSession(last_connection, client_addr_, | 
 |                                               quiche::QuicheStringPiece(), _)) | 
 |       .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) { | 
 |             ValidatePacket(last_connection, packet); | 
 |           }))); | 
 |   dispatcher_->ProcessBufferedChlos(kMaxNumSessionsToCreate); | 
 | } | 
 |  | 
 | TEST_F(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_, | 
 |                                     quiche::QuicheStringPiece(), _)) | 
 |           .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) { | 
 |                 ValidatePacket(TestConnectionId(conn_id), packet); | 
 |               }))); | 
 |     } | 
 |     ProcessPacket(client_addr_, TestConnectionId(conn_id), true, | 
 |                   SerializeFullCHLO()); | 
 |   } | 
 |  | 
 |   // 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_, | 
 |                                               quiche::QuicheStringPiece(), _)) | 
 |       .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) { | 
 |             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_F(BufferedPacketStoreTest, ReceiveCHLOForBufferedConnection) { | 
 |   QuicBufferedPacketStore* store = | 
 |       QuicDispatcherPeer::GetBufferedPackets(dispatcher_.get()); | 
 |  | 
 |   uint64_t conn_id = 1; | 
 |   ProcessPacket(client_addr_, TestConnectionId(conn_id), true, "data packet", | 
 |                 CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, | 
 |                 /*packet_number=*/1); | 
 |   // 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_, | 
 |                                     quiche::QuicheStringPiece(), _)) | 
 |           .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) { | 
 |                 ValidatePacket(TestConnectionId(conn_id), packet); | 
 |               }))); | 
 |     } | 
 |     ProcessPacket(client_addr_, TestConnectionId(conn_id), true, | 
 |                   SerializeFullCHLO()); | 
 |   } | 
 |   EXPECT_FALSE(store->HasChloForConnection( | 
 |       /*connection_id=*/TestConnectionId(1))); | 
 |  | 
 |   // CHLO on connection 1 should still be buffered. | 
 |   ProcessPacket(client_addr_, /*server_connection_id=*/TestConnectionId(1), | 
 |                 true, SerializeFullCHLO()); | 
 |   EXPECT_TRUE(store->HasChloForConnection( | 
 |       /*connection_id=*/TestConnectionId(1))); | 
 | } | 
 |  | 
 | // Regression test for b/117874922. | 
 | TEST_F(BufferedPacketStoreTest, ProcessBufferedChloWithDifferentVersion) { | 
 |   // Turn off version T099, so the preferred version is not supported by the | 
 |   // server. | 
 |   SetQuicReloadableFlag(quic_enable_version_t099, false); | 
 |   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_, | 
 |                                     quiche::QuicheStringPiece(), 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) { | 
 |                 ValidatePacket(TestConnectionId(conn_id), packet); | 
 |               }))); | 
 |     } | 
 |     ProcessPacket(client_addr_, TestConnectionId(conn_id), true, version, | 
 |                   SerializeFullCHLO(), true, CONNECTION_ID_PRESENT, | 
 |                   PACKET_4BYTE_PACKET_NUMBER, 1); | 
 |   } | 
 |  | 
 |   // 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_, | 
 |                                   quiche::QuicheStringPiece(), 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) { | 
 |               ValidatePacket(TestConnectionId(conn_id), packet); | 
 |             }))); | 
 |   } | 
 |   dispatcher_->ProcessBufferedChlos(kMaxNumSessionsToCreate); | 
 | } | 
 |  | 
 | }  // namespace | 
 | }  // namespace test | 
 | }  // namespace quic |