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..a857e74e66e773ed82f489f45e8f5a8faa373454 100644 |
--- a/net/tools/quic/quic_dispatcher_test.cc |
+++ b/net/tools/quic/quic_dispatcher_test.cc |
@@ -18,7 +18,9 @@ |
#include "net/quic/core/quic_utils.h" |
#include "net/quic/test_tools/crypto_test_utils.h" |
#include "net/quic/test_tools/quic_buffered_packet_store_peer.h" |
+#include "net/quic/test_tools/quic_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 +171,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 +1223,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 +1539,576 @@ TEST_P(BufferedPacketStoreTest, ReceiveCHLOForBufferedConnection) { |
EXPECT_TRUE(store->HasChloForConnection(/*connection_id=*/1)); |
} |
+// Implementation of ProofSource which delegates to a ProofSourceForTesting, |
+// except that when the async GetProof is called, it captures the call and |
+// allows tests to see that a call is pending, which they can then cause to |
+// complete at a time of their choosing. |
+class FakeProofSource : public ProofSource { |
+ public: |
+ FakeProofSource() : delegate_(CryptoTestUtils::ProofSourceForTesting()) {} |
+ |
+ // Before this object is "active", all calls to GetProof will be delegated |
+ // immediately. Once "active", the async ones will be intercepted. This |
+ // distinction is necessary to ensure that GetProof can be called without |
+ // interference during test case setup. |
+ void Activate() { active_ = true; } |
+ |
+ bool GetProof(const IPAddress& server_ip, |
+ const string& hostname, |
+ const string& server_config, |
+ QuicVersion quic_version, |
+ StringPiece chlo_hash, |
+ scoped_refptr<ProofSource::Chain>* out_chain, |
+ string* out_signature, |
+ string* out_leaf_cert_sct) override { |
+ return delegate_->GetProof(server_ip, hostname, server_config, quic_version, |
+ chlo_hash, out_chain, out_signature, |
+ out_leaf_cert_sct); |
+ } |
+ |
+ void GetProof(const IPAddress& server_ip, |
+ const string& hostname, |
+ const string& server_config, |
+ QuicVersion quic_version, |
+ StringPiece chlo_hash, |
+ std::unique_ptr<ProofSource::Callback> callback) override { |
+ if (!active_) { |
+ scoped_refptr<Chain> chain; |
+ string signature; |
+ string leaf_cert_sct; |
+ const bool ok = |
+ GetProof(server_ip, hostname, server_config, quic_version, |
+ chlo_hash.as_string(), &chain, &signature, &leaf_cert_sct); |
+ callback->Run(ok, chain, signature, leaf_cert_sct, |
+ /* details = */ nullptr); |
+ return; |
+ } |
+ |
+ params_.push_back(Params{server_ip, hostname, server_config, quic_version, |
+ chlo_hash.as_string(), std::move(callback)}); |
+ } |
+ |
+ int NumPendingCallbacks() const { return params_.size(); } |
+ |
+ void InvokePendingCallback(int n) { |
+ CHECK(NumPendingCallbacks() > n); |
+ |
+ const Params& params = params_[n]; |
+ |
+ scoped_refptr<ProofSource::Chain> chain; |
+ string signature; |
+ string leaf_cert_sct; |
+ const bool ok = delegate_->GetProof(params.server_ip, params.hostname, |
+ params.server_config, |
+ params.quic_version, params.chlo_hash, |
+ &chain, &signature, &leaf_cert_sct); |
+ |
+ params.callback->Run(ok, chain, signature, leaf_cert_sct, |
+ /* details = */ nullptr); |
+ params_.erase(params_.begin() + n); |
+ } |
+ |
+ private: |
+ std::unique_ptr<ProofSource> delegate_; |
+ bool active_ = false; |
+ |
+ struct Params { |
+ IPAddress server_ip; |
+ string hostname; |
+ string server_config; |
+ QuicVersion quic_version; |
+ string chlo_hash; |
+ std::unique_ptr<ProofSource::Callback> callback; |
+ }; |
+ |
+ std::vector<Params> params_; |
+}; |
+ |
+// 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 |