| 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 129f7402b600c827290f7ff127b84b7f40cb88a4..95b0030fb350d3fb8f3353908c7e301817077bf1 100644
|
| --- a/net/tools/quic/quic_dispatcher_test.cc
|
| +++ b/net/tools/quic/quic_dispatcher_test.cc
|
| @@ -4,6 +4,7 @@
|
|
|
| #include "net/tools/quic/quic_dispatcher.h"
|
|
|
| +#include <ostream>
|
| #include <string>
|
|
|
| #include "base/strings/string_piece.h"
|
| @@ -31,6 +32,7 @@ using net::test::MockConnection;
|
| using net::test::MockSession;
|
| using net::test::ValueRestore;
|
| using std::string;
|
| +using std::vector;
|
| using testing::DoAll;
|
| using testing::InSequence;
|
| using testing::Invoke;
|
| @@ -52,21 +54,28 @@ class TestServerSession : public QuicServerSession {
|
| MOCK_METHOD1(CreateIncomingDataStream, QuicDataStream*(QuicStreamId id));
|
| MOCK_METHOD0(CreateOutgoingDataStream, QuicDataStream*());
|
|
|
| + void SetCryptoStream(QuicCryptoServerStream* crypto_stream) {
|
| + crypto_stream_ = crypto_stream;
|
| + }
|
| +
|
| + QuicCryptoServerStream* GetCryptoStream() override { return crypto_stream_; }
|
| +
|
| private:
|
| + QuicCryptoServerStream* crypto_stream_;
|
| +
|
| DISALLOW_COPY_AND_ASSIGN(TestServerSession);
|
| };
|
|
|
| class TestDispatcher : public QuicDispatcher {
|
| public:
|
| - explicit TestDispatcher(const QuicConfig& config,
|
| - const QuicCryptoServerConfig* crypto_config,
|
| - EpollServer* eps)
|
| + TestDispatcher(const QuicConfig& config,
|
| + const QuicCryptoServerConfig* crypto_config,
|
| + EpollServer* eps)
|
| : QuicDispatcher(config,
|
| crypto_config,
|
| QuicSupportedVersions(),
|
| new QuicDispatcher::DefaultPacketWriterFactory(),
|
| - new QuicEpollConnectionHelper(eps)) {
|
| - }
|
| + new QuicEpollConnectionHelper(eps)) {}
|
|
|
| MOCK_METHOD3(CreateQuicSession,
|
| QuicServerSession*(QuicConnectionId connection_id,
|
| @@ -77,8 +86,8 @@ class TestDispatcher : public QuicDispatcher {
|
| using QuicDispatcher::current_client_address;
|
| };
|
|
|
| -// A Connection class which unregisters the session from the dispatcher
|
| -// when sending connection close.
|
| +// 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 MockConnection {
|
| @@ -289,6 +298,125 @@ TEST_F(QuicDispatcherTest, NoVersionPacketToTimeWaitListManager) {
|
| ProcessPacket(client_address, connection_id, false, "data");
|
| }
|
|
|
| +// Enables mocking of the handshake-confirmation for stateless rejects.
|
| +class MockQuicCryptoServerStream : public QuicCryptoServerStream {
|
| + public:
|
| + MockQuicCryptoServerStream(const QuicCryptoServerConfig& crypto_config,
|
| + QuicSession* session)
|
| + : QuicCryptoServerStream(&crypto_config, session) {}
|
| + void set_handshake_confirmed_for_testing(bool handshake_confirmed) {
|
| + handshake_confirmed_ = handshake_confirmed;
|
| + }
|
| +
|
| + private:
|
| + DISALLOW_COPY_AND_ASSIGN(MockQuicCryptoServerStream);
|
| +};
|
| +
|
| +struct StatelessRejectTestParams {
|
| + StatelessRejectTestParams(bool enable_stateless_rejects_via_flag,
|
| + bool use_stateless_rejects_if_peer_supported,
|
| + bool client_supports_statelesss_rejects,
|
| + bool crypto_handshake_successful)
|
| + : enable_stateless_rejects_via_flag(enable_stateless_rejects_via_flag),
|
| + use_stateless_rejects_if_peer_supported(
|
| + use_stateless_rejects_if_peer_supported),
|
| + client_supports_statelesss_rejects(client_supports_statelesss_rejects),
|
| + crypto_handshake_successful(crypto_handshake_successful) {}
|
| +
|
| + friend std::ostream& operator<<(std::ostream& os,
|
| + const StatelessRejectTestParams& p) {
|
| + os << " enable_stateless_rejects_via_flag: "
|
| + << p.enable_stateless_rejects_via_flag << std::endl;
|
| + os << "{ use_stateless_rejects_if_peer_supported: "
|
| + << p.use_stateless_rejects_if_peer_supported << std::endl;
|
| + os << "{ client_supports_statelesss_rejects: "
|
| + << p.client_supports_statelesss_rejects << std::endl;
|
| + os << " crypto_handshake_successful: " << p.crypto_handshake_successful
|
| + << " }";
|
| + return os;
|
| + }
|
| +
|
| + // This only enables the stateless reject feature via the feature-flag.
|
| + // It does not force the crypto server to emit stateless rejects.
|
| + bool enable_stateless_rejects_via_flag;
|
| + // If true, this forces the server to send a stateless reject when rejecting
|
| + // messages. This should be a no-op if enable_stateless_rejects_via_flag is
|
| + // false or the peer does not support them.
|
| + bool use_stateless_rejects_if_peer_supported;
|
| + // Whether or not the client supports stateless rejects.
|
| + bool client_supports_statelesss_rejects;
|
| + // Should the initial crypto handshake succeed or not.
|
| + bool crypto_handshake_successful;
|
| +};
|
| +
|
| +// Constructs various test permutations for stateless rejects.
|
| +vector<StatelessRejectTestParams> GetStatelessRejectTestParams() {
|
| + vector<StatelessRejectTestParams> params;
|
| + for (bool enable_stateless_rejects_via_flag : {true, false}) {
|
| + for (bool use_stateless_rejects_if_peer_supported : {true, false}) {
|
| + for (bool client_supports_statelesss_rejects : {true, false}) {
|
| + for (bool crypto_handshake_successful : {true, false}) {
|
| + params.push_back(StatelessRejectTestParams(
|
| + enable_stateless_rejects_via_flag,
|
| + use_stateless_rejects_if_peer_supported,
|
| + client_supports_statelesss_rejects, crypto_handshake_successful));
|
| + }
|
| + }
|
| + }
|
| + }
|
| + return params;
|
| +}
|
| +
|
| +class QuicDispatcherStatelessRejectTest
|
| + : public QuicDispatcherTest,
|
| + public ::testing::WithParamInterface<StatelessRejectTestParams> {
|
| + public:
|
| + QuicDispatcherStatelessRejectTest() : crypto_stream1_(nullptr) {}
|
| +
|
| + ~QuicDispatcherStatelessRejectTest() override {
|
| + if (crypto_stream1_) {
|
| + delete crypto_stream1_;
|
| + }
|
| + }
|
| +
|
| + // This test setup assumes that all testing will be done using
|
| + // crypto_stream1_.
|
| + void SetUp() override {
|
| + FLAGS_enable_quic_stateless_reject_support =
|
| + GetParam().enable_stateless_rejects_via_flag;
|
| + }
|
| +
|
| + // Returns true or false, depending on whether the server will emit
|
| + // a stateless reject, depending upon the parameters of the test.
|
| + bool ExpectStatelessReject() {
|
| + return GetParam().enable_stateless_rejects_via_flag &&
|
| + GetParam().use_stateless_rejects_if_peer_supported &&
|
| + !GetParam().crypto_handshake_successful &&
|
| + GetParam().client_supports_statelesss_rejects;
|
| + }
|
| +
|
| + // Sets up dispatcher_, sesession1_, and crypto_stream1_ based on
|
| + // the test parameters.
|
| + QuicServerSession* CreateSessionBasedOnTestParams(
|
| + QuicConnectionId connection_id,
|
| + const IPEndPoint& client_address) {
|
| + CreateSession(&dispatcher_, config_, connection_id, client_address,
|
| + &session1_);
|
| +
|
| + crypto_stream1_ = new MockQuicCryptoServerStream(crypto_config_, session1_);
|
| + session1_->SetCryptoStream(crypto_stream1_);
|
| + crypto_stream1_->set_use_stateless_rejects_if_peer_supported(
|
| + GetParam().use_stateless_rejects_if_peer_supported);
|
| + crypto_stream1_->set_handshake_confirmed_for_testing(
|
| + GetParam().crypto_handshake_successful);
|
| + crypto_stream1_->set_peer_supports_stateless_rejects(
|
| + GetParam().client_supports_statelesss_rejects);
|
| + return session1_;
|
| + }
|
| +
|
| + MockQuicCryptoServerStream* crypto_stream1_;
|
| +};
|
| +
|
| TEST_F(QuicDispatcherTest, ProcessPacketWithZeroPort) {
|
| CreateTimeWaitListManager();
|
|
|
| @@ -339,6 +467,52 @@ TEST_F(QuicDispatcherTest, TooBigSeqNoPacketToTimeWaitListManager) {
|
| QuicDispatcher::kMaxReasonableInitialSequenceNumber + 1);
|
| }
|
|
|
| +INSTANTIATE_TEST_CASE_P(QuicDispatcherStatelessRejectTests,
|
| + QuicDispatcherStatelessRejectTest,
|
| + ::testing::ValuesIn(GetStatelessRejectTestParams()));
|
| +
|
| +// Parameterized test for stateless rejects. Should test all
|
| +// combinations of enabling/disabling, reject/no-reject for stateless
|
| +// rejects.
|
| +TEST_P(QuicDispatcherStatelessRejectTest, ParameterizedBasicTest) {
|
| + CreateTimeWaitListManager();
|
| +
|
| + IPEndPoint client_address(net::test::Loopback4(), 1);
|
| + QuicConnectionId connection_id = 1;
|
| + EXPECT_CALL(dispatcher_, CreateQuicSession(connection_id, _, client_address))
|
| + .WillOnce(testing::Return(
|
| + CreateSessionBasedOnTestParams(connection_id, client_address)));
|
| +
|
| + // Process the first packet for the connection.
|
| + if (ExpectStatelessReject()) {
|
| + // If this is a stateless reject, we expect the connection to close.
|
| + EXPECT_CALL(*session1_, OnConnectionClosed(_, _))
|
| + .Times(1)
|
| + .WillOnce(WithoutArgs(Invoke(
|
| + reinterpret_cast<MockServerConnection*>(session1_->connection()),
|
| + &MockServerConnection::UnregisterOnConnectionClosed)));
|
| + }
|
| + ProcessPacket(client_address, connection_id, true, "foo");
|
| +
|
| + // Send a second packet and check the results. If this is a stateless reject,
|
| + // the existing connection_id will go on the time-wait list.
|
| + EXPECT_EQ(ExpectStatelessReject(),
|
| + time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id));
|
| + if (ExpectStatelessReject()) {
|
| + // The second packet will be processed on the time-wait list.
|
| + EXPECT_CALL(*time_wait_list_manager_,
|
| + ProcessPacket(_, _, connection_id, _, _)).Times(1);
|
| + } else {
|
| + // The second packet will trigger a packet-validation
|
| + EXPECT_CALL(*reinterpret_cast<MockConnection*>(session1_->connection()),
|
| + ProcessUdpPacket(_, _, _))
|
| + .Times(1)
|
| + .WillOnce(testing::WithArgs<2>(
|
| + Invoke(this, &QuicDispatcherTest::ValidatePacket)));
|
| + }
|
| + ProcessPacket(client_address, connection_id, true, "foo");
|
| +}
|
| +
|
| // Verify the stopgap test: Packets with truncated connection IDs should be
|
| // dropped.
|
| class QuicDispatcherTestStrayPacketConnectionId
|
|
|