Index: net/tools/quic/quic_dispatcher_test.cc |
diff --git a/net/tools/quic/quic_dispatcher_test.cc b/net/tools/quic/quic_dispatcher_test.cc |
index 85712d62fac61f49e4a67b411b52457c077bda02..62d7b4238d31829d814f991382f5d7b7bf61d9ea 100644 |
--- a/net/tools/quic/quic_dispatcher_test.cc |
+++ b/net/tools/quic/quic_dispatcher_test.cc |
@@ -17,8 +17,11 @@ |
#include "net/quic/core/quic_flags.h" |
#include "net/quic/core/quic_utils.h" |
#include "net/quic/test_tools/crypto_test_utils.h" |
+#include "net/quic/test_tools/fake_proof_source.h" |
#include "net/quic/test_tools/quic_buffered_packet_store_peer.h" |
+#include "net/quic/test_tools/quic_crypto_server_config_peer.h" |
#include "net/quic/test_tools/quic_test_utils.h" |
+#include "net/quic/test_tools/quic_time_wait_list_manager_peer.h" |
#include "net/test/gtest_util.h" |
#include "net/tools/epoll_server/epoll_server.h" |
#include "net/tools/quic/chlo_extractor.h" |
@@ -169,12 +172,15 @@ class MockServerConnection : public MockQuicConnection { |
class QuicDispatcherTest : public ::testing::Test { |
public: |
QuicDispatcherTest() |
+ : QuicDispatcherTest(CryptoTestUtils::ProofSourceForTesting()) {} |
+ |
+ explicit QuicDispatcherTest(std::unique_ptr<ProofSource> proof_source) |
: helper_(&eps_, QuicAllocator::BUFFER_POOL), |
alarm_factory_(&eps_), |
version_manager_(AllSupportedVersions()), |
crypto_config_(QuicCryptoServerConfig::TESTING, |
QuicRandom::GetInstance(), |
- CryptoTestUtils::ProofSourceForTesting()), |
+ std::move(proof_source)), |
dispatcher_(new TestDispatcher(config_, |
&crypto_config_, |
&version_manager_, |
@@ -1218,6 +1224,9 @@ TEST_P(BufferedPacketStoreTest, |
QuicConnectionId conn_id = i; |
if (FLAGS_quic_create_session_after_insertion && |
conn_id == kNumConnections) { |
+ // The last CHLO should trigger ShouldCreateOrBufferPacketForConnection() |
+ // since it's the |
+ // first packet arrives on that connection. |
EXPECT_CALL(*dispatcher_, |
ShouldCreateOrBufferPacketForConnection(conn_id)); |
} |
@@ -1531,6 +1540,491 @@ TEST_P(BufferedPacketStoreTest, ReceiveCHLOForBufferedConnection) { |
EXPECT_TRUE(store->HasChloForConnection(/*connection_id=*/1)); |
} |
+// Test which exercises the async GetProof codepaths, especially in the context |
+// of stateless rejection. |
+class AsyncGetProofTest : public QuicDispatcherTest { |
+ public: |
+ AsyncGetProofTest() |
+ : QuicDispatcherTest( |
+ std::unique_ptr<FakeProofSource>(new FakeProofSource())), |
+ client_addr_(net::test::Loopback4(), 1234), |
+ crypto_config_peer_(&crypto_config_) { |
+ FLAGS_enable_async_get_proof = true; |
+ FLAGS_quic_buffer_packet_till_chlo = true; |
+ FLAGS_enable_quic_stateless_reject_support = true; |
+ FLAGS_quic_use_cheap_stateless_rejects = true; |
+ FLAGS_quic_create_session_after_insertion = true; |
+ } |
+ |
+ void SetUp() override { |
+ QuicDispatcherTest::SetUp(); |
+ |
+ clock_ = QuicDispatcherPeer::GetHelper(dispatcher_.get())->GetClock(); |
+ QuicVersion version = AllSupportedVersions().front(); |
+ chlo_ = CryptoTestUtils::GenerateDefaultInchoateCHLO(clock_, version, |
+ &crypto_config_); |
+ chlo_.SetVector(net::kCOPT, net::QuicTagVector{net::kSREJ}); |
+ // Pass an inchoate CHLO. |
+ CryptoTestUtils::GenerateFullCHLO( |
+ chlo_, &crypto_config_, server_ip_, client_addr_, version, clock_, |
+ &proof_, QuicDispatcherPeer::GetCache(dispatcher_.get()), &full_chlo_); |
+ |
+ GetFakeProofSource()->Activate(); |
+ } |
+ |
+ FakeProofSource* GetFakeProofSource() const { |
+ return static_cast<FakeProofSource*>(crypto_config_peer_.GetProofSource()); |
+ } |
+ |
+ string SerializeFullCHLO() { |
+ return full_chlo_.GetSerialized().AsStringPiece().as_string(); |
+ } |
+ |
+ string SerializeCHLO() { |
+ return chlo_.GetSerialized().AsStringPiece().as_string(); |
+ } |
+ |
+ // Sets up a session, and crypto stream based on the test parameters. |
+ QuicServerSessionBase* GetSession(QuicConnectionId connection_id) { |
+ auto it = sessions_.find(connection_id); |
+ if (it != sessions_.end()) { |
+ return it->second.session; |
+ } |
+ |
+ TestQuicSpdyServerSession* session; |
+ CreateSession(dispatcher_.get(), config_, connection_id, client_addr_, |
+ &mock_helper_, &mock_alarm_factory_, &crypto_config_, |
+ QuicDispatcherPeer::GetCache(dispatcher_.get()), &session); |
+ |
+ std::unique_ptr<MockQuicCryptoServerStream> crypto_stream( |
+ new MockQuicCryptoServerStream( |
+ crypto_config_, QuicDispatcherPeer::GetCache(dispatcher_.get()), |
+ session, session->stream_helper())); |
+ session->SetCryptoStream(crypto_stream.get()); |
+ crypto_stream->SetPeerSupportsStatelessRejects(true); |
+ const bool ok = |
+ sessions_ |
+ .insert(std::make_pair( |
+ connection_id, SessionInfo{session, std::move(crypto_stream)})) |
+ .second; |
+ CHECK(ok); |
+ return session; |
+ } |
+ |
+ protected: |
+ const IPEndPoint client_addr_; |
+ |
+ private: |
+ QuicCryptoServerConfigPeer crypto_config_peer_; |
+ IPAddress server_ip_; |
+ QuicCryptoProof proof_; |
+ const QuicClock* clock_; |
+ CryptoHandshakeMessage chlo_; |
+ CryptoHandshakeMessage full_chlo_; |
+ |
+ struct SessionInfo { |
+ TestQuicSpdyServerSession* session; |
+ std::unique_ptr<MockQuicCryptoServerStream> crypto_stream; |
+ }; |
+ std::map<QuicConnectionId, SessionInfo> sessions_; |
+}; |
+ |
+// Test a simple situation of connections which the StatelessRejector will |
+// accept. |
+TEST_F(AsyncGetProofTest, BasicAccept) { |
+ QuicConnectionId conn_id = 1; |
+ |
+ testing::MockFunction<void(int check_point)> check; |
+ { |
+ InSequence s; |
+ |
+ EXPECT_CALL(check, Call(1)); |
+ EXPECT_CALL(*dispatcher_, ShouldCreateOrBufferPacketForConnection(conn_id)); |
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, client_addr_)) |
+ .WillOnce(testing::Return(GetSession(conn_id))); |
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>( |
+ GetSession(conn_id)->connection()), |
+ ProcessUdpPacket(_, _, _)) |
+ .WillOnce(testing::WithArg<2>( |
+ Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket, |
+ base::Unretained(this), conn_id)))); |
+ |
+ EXPECT_CALL(check, Call(2)); |
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>( |
+ GetSession(conn_id)->connection()), |
+ ProcessUdpPacket(_, _, _)) |
+ .WillOnce(testing::WithArg<2>( |
+ Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket, |
+ base::Unretained(this), conn_id)))); |
+ } |
+ |
+ // Send a CHLO that the StatelessRejector will accept. |
+ ProcessPacket(client_addr_, conn_id, true, false, SerializeFullCHLO()); |
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1); |
+ |
+ check.Call(1); |
+ // Complete the ProofSource::GetProof call and verify that a session is |
+ // created. |
+ GetFakeProofSource()->InvokePendingCallback(0); |
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 0); |
+ |
+ check.Call(2); |
+ // Verify that a data packet gets processed immediately. |
+ ProcessPacket(client_addr_, conn_id, true, false, "My name is Data"); |
+} |
+ |
+// Test a simple situation of connections which the StatelessRejector will |
+// reject. |
+TEST_F(AsyncGetProofTest, BasicReject) { |
+ CreateTimeWaitListManager(); |
+ |
+ QuicConnectionId conn_id = 1; |
+ |
+ testing::MockFunction<void(int check_point)> check; |
+ { |
+ InSequence s; |
+ EXPECT_CALL(check, Call(1)); |
+ EXPECT_CALL(*time_wait_list_manager_, |
+ AddConnectionIdToTimeWait(conn_id, _, true, _)); |
+ EXPECT_CALL(*time_wait_list_manager_, |
+ ProcessPacket(_, client_addr_, conn_id, _, _)); |
+ |
+ EXPECT_CALL(check, Call(2)); |
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, client_addr_)) |
+ .Times(0); |
+ EXPECT_CALL(*time_wait_list_manager_, |
+ ProcessPacket(_, client_addr_, conn_id, _, _)); |
+ } |
+ |
+ // Send a CHLO that the StatelessRejector will reject. |
+ ProcessPacket(client_addr_, conn_id, true, false, SerializeCHLO()); |
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1); |
+ |
+ // Complete the ProofSource::GetProof call and verify that the connection and |
+ // packet are processed by the time wait list manager. |
+ check.Call(1); |
+ GetFakeProofSource()->InvokePendingCallback(0); |
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 0); |
+ |
+ // Verify that a data packet is passed to the time wait list manager. |
+ check.Call(2); |
+ ProcessPacket(client_addr_, conn_id, true, false, "My name is Data"); |
+} |
+ |
+// Test a situation with multiple interleaved connections which the |
+// StatelessRejector will accept. |
+TEST_F(AsyncGetProofTest, MultipleAccept) { |
+ QuicConnectionId conn_id_1 = 1; |
+ QuicConnectionId conn_id_2 = 2; |
+ QuicBufferedPacketStore* store = |
+ QuicDispatcherPeer::GetBufferedPackets(dispatcher_.get()); |
+ |
+ testing::MockFunction<void(int check_point)> check; |
+ { |
+ InSequence s; |
+ EXPECT_CALL(check, Call(1)); |
+ EXPECT_CALL(*dispatcher_, |
+ ShouldCreateOrBufferPacketForConnection(conn_id_2)); |
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id_2, client_addr_)) |
+ .WillOnce(testing::Return(GetSession(conn_id_2))); |
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>( |
+ GetSession(conn_id_2)->connection()), |
+ ProcessUdpPacket(_, _, _)) |
+ .WillOnce(testing::WithArg<2>( |
+ Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket, |
+ base::Unretained(this), conn_id_2)))); |
+ |
+ EXPECT_CALL(check, Call(2)); |
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>( |
+ GetSession(conn_id_2)->connection()), |
+ ProcessUdpPacket(_, _, _)) |
+ .WillOnce(testing::WithArg<2>( |
+ Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket, |
+ base::Unretained(this), conn_id_2)))); |
+ |
+ EXPECT_CALL(check, Call(3)); |
+ EXPECT_CALL(*dispatcher_, |
+ ShouldCreateOrBufferPacketForConnection(conn_id_1)); |
+ |
+ EXPECT_CALL(check, Call(4)); |
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id_1, client_addr_)) |
+ .WillOnce(testing::Return(GetSession(conn_id_1))); |
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>( |
+ GetSession(conn_id_1)->connection()), |
+ ProcessUdpPacket(_, _, _)) |
+ .WillRepeatedly(testing::WithArg<2>( |
+ Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket, |
+ base::Unretained(this), conn_id_1)))); |
+ } |
+ |
+ // Send a CHLO that the StatelessRejector will accept. |
+ ProcessPacket(client_addr_, conn_id_1, true, false, SerializeFullCHLO()); |
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1); |
+ |
+ // Send another CHLO that the StatelessRejector will accept. |
+ ProcessPacket(client_addr_, conn_id_2, true, false, SerializeFullCHLO()); |
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 2); |
+ |
+ // Complete the second ProofSource::GetProof call and verify that a session is |
+ // created. |
+ check.Call(1); |
+ GetFakeProofSource()->InvokePendingCallback(1); |
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1); |
+ |
+ // Verify that a data packet on that connection gets processed immediately. |
+ check.Call(2); |
+ ProcessPacket(client_addr_, conn_id_2, true, false, "My name is Data"); |
+ |
+ // Verify that a data packet on the other connection does not get processed |
+ // yet. |
+ check.Call(3); |
+ ProcessPacket(client_addr_, conn_id_1, true, false, "My name is Data"); |
+ EXPECT_TRUE(store->HasBufferedPackets(conn_id_1)); |
+ EXPECT_FALSE(store->HasBufferedPackets(conn_id_2)); |
+ |
+ // Complete the first ProofSource::GetProof call and verify that a session is |
+ // created and the buffered packet is processed. |
+ check.Call(4); |
+ GetFakeProofSource()->InvokePendingCallback(0); |
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 0); |
+} |
+ |
+// Test a situation with multiple interleaved connections which the |
+// StatelessRejector will reject. |
+TEST_F(AsyncGetProofTest, MultipleReject) { |
+ CreateTimeWaitListManager(); |
+ |
+ QuicConnectionId conn_id_1 = 1; |
+ QuicConnectionId conn_id_2 = 2; |
+ QuicBufferedPacketStore* store = |
+ QuicDispatcherPeer::GetBufferedPackets(dispatcher_.get()); |
+ |
+ testing::MockFunction<void(int check_point)> check; |
+ { |
+ InSequence s; |
+ |
+ EXPECT_CALL(check, Call(1)); |
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id_2, client_addr_)) |
+ .Times(0); |
+ EXPECT_CALL(*time_wait_list_manager_, |
+ AddConnectionIdToTimeWait(conn_id_2, _, true, _)); |
+ EXPECT_CALL(*time_wait_list_manager_, |
+ ProcessPacket(_, client_addr_, conn_id_2, _, _)); |
+ |
+ EXPECT_CALL(check, Call(2)); |
+ EXPECT_CALL(*time_wait_list_manager_, |
+ ProcessPacket(_, client_addr_, conn_id_2, _, _)); |
+ |
+ EXPECT_CALL(check, Call(3)); |
+ EXPECT_CALL(*dispatcher_, |
+ ShouldCreateOrBufferPacketForConnection(conn_id_1)); |
+ |
+ EXPECT_CALL(check, Call(4)); |
+ EXPECT_CALL(*time_wait_list_manager_, |
+ AddConnectionIdToTimeWait(conn_id_1, _, true, _)); |
+ EXPECT_CALL(*time_wait_list_manager_, |
+ ProcessPacket(_, client_addr_, conn_id_1, _, _)); |
+ } |
+ |
+ // Send a CHLO that the StatelessRejector will reject. |
+ ProcessPacket(client_addr_, conn_id_1, true, false, SerializeCHLO()); |
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1); |
+ |
+ // Send another CHLO that the StatelessRejector will reject. |
+ ProcessPacket(client_addr_, conn_id_2, true, false, SerializeCHLO()); |
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 2); |
+ |
+ // Complete the second ProofSource::GetProof call and verify that the |
+ // connection and packet are processed by the time wait manager. |
+ check.Call(1); |
+ GetFakeProofSource()->InvokePendingCallback(1); |
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1); |
+ |
+ // Verify that a data packet on that connection gets processed immediately by |
+ // the time wait manager. |
+ check.Call(2); |
+ ProcessPacket(client_addr_, conn_id_2, true, false, "My name is Data"); |
+ |
+ // Verify that a data packet on the first connection gets buffered. |
+ check.Call(3); |
+ ProcessPacket(client_addr_, conn_id_1, true, false, "My name is Data"); |
+ EXPECT_TRUE(store->HasBufferedPackets(conn_id_1)); |
+ EXPECT_FALSE(store->HasBufferedPackets(conn_id_2)); |
+ |
+ // Complete the first ProofSource::GetProof call and verify that the CHLO is |
+ // processed by the time wait manager and the remaining packets are discarded. |
+ check.Call(4); |
+ GetFakeProofSource()->InvokePendingCallback(0); |
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 0); |
+ EXPECT_FALSE(store->HasBufferedPackets(conn_id_1)); |
+ EXPECT_FALSE(store->HasBufferedPackets(conn_id_2)); |
+} |
+ |
+// Test a situation with multiple identical CHLOs which the StatelessRejector |
+// will reject. |
+TEST_F(AsyncGetProofTest, MultipleIdenticalReject) { |
+ CreateTimeWaitListManager(); |
+ |
+ QuicConnectionId conn_id_1 = 1; |
+ QuicBufferedPacketStore* store = |
+ QuicDispatcherPeer::GetBufferedPackets(dispatcher_.get()); |
+ |
+ testing::MockFunction<void(int check_point)> check; |
+ { |
+ InSequence s; |
+ EXPECT_CALL(check, Call(1)); |
+ EXPECT_CALL(*dispatcher_, |
+ ShouldCreateOrBufferPacketForConnection(conn_id_1)); |
+ |
+ EXPECT_CALL(check, Call(2)); |
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id_1, client_addr_)) |
+ .Times(0); |
+ EXPECT_CALL(*time_wait_list_manager_, |
+ AddConnectionIdToTimeWait(conn_id_1, _, true, _)); |
+ EXPECT_CALL(*time_wait_list_manager_, |
+ ProcessPacket(_, client_addr_, conn_id_1, _, _)); |
+ } |
+ |
+ // Send a CHLO that the StatelessRejector will reject. |
+ ProcessPacket(client_addr_, conn_id_1, true, false, SerializeCHLO()); |
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1); |
+ EXPECT_FALSE(store->HasBufferedPackets(conn_id_1)); |
+ |
+ // Send an identical CHLO which should get buffered. |
+ check.Call(1); |
+ ProcessPacket(client_addr_, conn_id_1, true, false, SerializeCHLO()); |
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1); |
+ EXPECT_TRUE(store->HasBufferedPackets(conn_id_1)); |
+ |
+ // Complete the ProofSource::GetProof call and verify that the CHLO is |
+ // rejected and the copy is discarded. |
+ check.Call(2); |
+ GetFakeProofSource()->InvokePendingCallback(0); |
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 0); |
+ EXPECT_FALSE(store->HasBufferedPackets(conn_id_1)); |
+} |
+ |
+// Test dispatcher behavior when packets time out of the buffer while CHLO |
+// validation is still pending. |
+TEST_F(AsyncGetProofTest, BufferTimeout) { |
+ CreateTimeWaitListManager(); |
+ |
+ QuicConnectionId conn_id = 1; |
+ QuicBufferedPacketStore* store = |
+ QuicDispatcherPeer::GetBufferedPackets(dispatcher_.get()); |
+ QuicBufferedPacketStorePeer::set_clock(store, mock_helper_.GetClock()); |
+ |
+ testing::MockFunction<void(int check_point)> check; |
+ { |
+ InSequence s; |
+ EXPECT_CALL(check, Call(1)); |
+ EXPECT_CALL(*dispatcher_, ShouldCreateOrBufferPacketForConnection(conn_id)); |
+ |
+ EXPECT_CALL(check, Call(2)); |
+ EXPECT_CALL(*time_wait_list_manager_, |
+ ProcessPacket(_, client_addr_, conn_id, _, _)); |
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, client_addr_)) |
+ .Times(0); |
+ } |
+ |
+ // Send a CHLO that the StatelessRejector will accept. |
+ ProcessPacket(client_addr_, conn_id, true, false, SerializeFullCHLO()); |
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1); |
+ EXPECT_FALSE(store->HasBufferedPackets(conn_id)); |
+ |
+ // Send a data packet that will get buffered |
+ check.Call(1); |
+ ProcessPacket(client_addr_, conn_id, true, false, "My name is Data"); |
+ EXPECT_TRUE(store->HasBufferedPackets(conn_id)); |
+ |
+ // Pretend that enough time has gone by for the packets to get expired out of |
+ // the buffer |
+ mock_helper_.AdvanceTime( |
+ QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs)); |
+ QuicBufferedPacketStorePeer::expiration_alarm(store)->Cancel(); |
+ store->OnExpirationTimeout(); |
+ EXPECT_FALSE(store->HasBufferedPackets(conn_id)); |
+ EXPECT_TRUE(time_wait_list_manager_->IsConnectionIdInTimeWait(conn_id)); |
+ |
+ // Now allow the CHLO validation to complete, and verify that no connection |
+ // gets created. |
+ check.Call(2); |
+ GetFakeProofSource()->InvokePendingCallback(0); |
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 0); |
+ EXPECT_FALSE(store->HasBufferedPackets(conn_id)); |
+ EXPECT_TRUE(time_wait_list_manager_->IsConnectionIdInTimeWait(conn_id)); |
+} |
+ |
+// Test behavior when packets time out of the buffer *and* the connection times |
+// out of the time wait manager while CHLO validation is still pending. This |
+// *should* be impossible, but anything can happen with timing conditions. |
+TEST_F(AsyncGetProofTest, TimeWaitTimeout) { |
+ QuicConnectionId conn_id = 1; |
+ QuicBufferedPacketStore* store = |
+ QuicDispatcherPeer::GetBufferedPackets(dispatcher_.get()); |
+ QuicBufferedPacketStorePeer::set_clock(store, mock_helper_.GetClock()); |
+ CreateTimeWaitListManager(); |
+ QuicTimeWaitListManagerPeer::set_clock(time_wait_list_manager_, |
+ mock_helper_.GetClock()); |
+ |
+ testing::MockFunction<void(int check_point)> check; |
+ { |
+ InSequence s; |
+ EXPECT_CALL(check, Call(1)); |
+ EXPECT_CALL(*dispatcher_, ShouldCreateOrBufferPacketForConnection(conn_id)); |
+ |
+ EXPECT_CALL(check, Call(2)); |
+ EXPECT_CALL(*dispatcher_, ShouldCreateOrBufferPacketForConnection(conn_id)); |
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, client_addr_)) |
+ .WillOnce(testing::Return(GetSession(conn_id))); |
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>( |
+ GetSession(conn_id)->connection()), |
+ ProcessUdpPacket(_, _, _)) |
+ .WillOnce(testing::WithArg<2>( |
+ Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket, |
+ base::Unretained(this), conn_id)))); |
+ } |
+ |
+ // Send a CHLO that the StatelessRejector will accept. |
+ ProcessPacket(client_addr_, conn_id, true, false, SerializeFullCHLO()); |
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1); |
+ EXPECT_FALSE(store->HasBufferedPackets(conn_id)); |
+ |
+ // Send a data packet that will get buffered |
+ check.Call(1); |
+ ProcessPacket(client_addr_, conn_id, true, false, "My name is Data"); |
+ EXPECT_TRUE(store->HasBufferedPackets(conn_id)); |
+ |
+ // Pretend that enough time has gone by for the packets to get expired out of |
+ // the buffer |
+ mock_helper_.AdvanceTime( |
+ QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs)); |
+ QuicBufferedPacketStorePeer::expiration_alarm(store)->Cancel(); |
+ store->OnExpirationTimeout(); |
+ EXPECT_FALSE(store->HasBufferedPackets(conn_id)); |
+ EXPECT_TRUE(time_wait_list_manager_->IsConnectionIdInTimeWait(conn_id)); |
+ |
+ // Pretend that enough time has gone by for the connection ID to be removed |
+ // from the time wait manager |
+ mock_helper_.AdvanceTime( |
+ QuicTimeWaitListManagerPeer::time_wait_period(time_wait_list_manager_)); |
+ QuicTimeWaitListManagerPeer::expiration_alarm(time_wait_list_manager_) |
+ ->Cancel(); |
+ time_wait_list_manager_->CleanUpOldConnectionIds(); |
+ EXPECT_FALSE(time_wait_list_manager_->IsConnectionIdInTimeWait(conn_id)); |
+ |
+ // Now allow the CHLO validation to complete. Expect that a connection is |
+ // indeed created, since QUIC has forgotten that this connection ever existed. |
+ // This is a miniscule corner case which should never happen in the wild, so |
+ // really we are just verifying that the dispatcher does not explode in this |
+ // situation. |
+ check.Call(2); |
+ GetFakeProofSource()->InvokePendingCallback(0); |
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 0); |
+ EXPECT_FALSE(store->HasBufferedPackets(conn_id)); |
+ EXPECT_FALSE(time_wait_list_manager_->IsConnectionIdInTimeWait(conn_id)); |
+} |
+ |
} // namespace |
} // namespace test |
} // namespace net |