| 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
|
|
|