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 fcf45c2fadb90669f5d78f2a3bb96f5fe1c342b9..09364881bf0679a0dd142904018e946ae000b67b 100644 |
--- a/net/tools/quic/quic_dispatcher_test.cc |
+++ b/net/tools/quic/quic_dispatcher_test.cc |
@@ -52,6 +52,9 @@ using testing::WithoutArgs; |
using testing::_; |
static const size_t kDefaultMaxConnectionsInStore = 100; |
+static const size_t kMaxConnectionsWithoutCHLO = |
+ kDefaultMaxConnectionsInStore / 2; |
+static const int16_t kMaxNumSessionsToCreate = 16; |
namespace net { |
namespace test { |
@@ -176,6 +179,9 @@ class QuicDispatcherTest : public ::testing::Test { |
void SetUp() override { |
dispatcher_->InitializeWithWriter(new QuicDefaultPacketWriter(1)); |
+ // Set the counter to some value to start with. |
+ QuicDispatcherPeer::set_new_sessions_allowed_per_event_loop( |
+ dispatcher_.get(), kMaxNumSessionsToCreate); |
} |
~QuicDispatcherTest() override {} |
@@ -1159,10 +1165,14 @@ TEST_P(BufferedPacketStoreTest, |
InSequence s; |
server_address_ = IPEndPoint(Any4(), 5); |
// A bunch of non-CHLO should be buffered upon arrival. |
- for (size_t i = 1; i <= kDefaultMaxConnectionsInStore + 1; ++i) { |
+ size_t kNumConnections = (FLAGS_quic_limit_num_new_sessions_per_epoll_loop |
+ ? kMaxConnectionsWithoutCHLO |
+ : kDefaultMaxConnectionsInStore) + |
+ 1; |
+ for (size_t i = 1; i <= kNumConnections; ++i) { |
IPEndPoint client_address(Loopback4(), i); |
QuicConnectionId conn_id = i; |
- if (i <= kDefaultMaxConnectionsInStore) { |
+ if (i <= kNumConnections - 1) { |
// As they are on different connection, they should trigger |
// OnNewConnectionAdded(). The last packet should be dropped. |
EXPECT_CALL(*dispatcher_, OnNewConnectionAdded(conn_id)); |
@@ -1176,10 +1186,14 @@ TEST_P(BufferedPacketStoreTest, |
// Pop out the packet on last connection as it shouldn't be enqueued in store |
// as well. |
- data_connection_map_[kDefaultMaxConnectionsInStore + 1].pop_front(); |
- |
- // Process CHLOs. |
- for (size_t i = 1; i <= kDefaultMaxConnectionsInStore + 1; ++i) { |
+ data_connection_map_[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) { |
IPEndPoint client_address(Loopback4(), i); |
QuicConnectionId conn_id = i; |
EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, client_address)) |
@@ -1187,22 +1201,23 @@ TEST_P(BufferedPacketStoreTest, |
dispatcher_.get(), config_, conn_id, client_address, &mock_helper_, |
&mock_alarm_factory_, &crypto_config_, |
QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_))); |
- if (conn_id == kDefaultMaxConnectionsInStore + 1) { |
+ if (conn_id == kNumConnections) { |
// The last CHLO should trigger OnNewConnectionAdded() since it's the |
// first packet arrives on that connection. |
EXPECT_CALL(*dispatcher_, OnNewConnectionAdded(conn_id)); |
} |
- // First |kDefaultMaxConnectionsInStore| connections should have buffered |
+ // First |kNumConnections| - 1 connections should have buffered |
// a packet in store. The rest should have been dropped. |
- if (i <= kDefaultMaxConnectionsInStore) { |
- EXPECT_CALL( |
- *reinterpret_cast<MockQuicConnection*>(session1_->connection()), |
- ProcessUdpPacket(_, client_address, _)) |
- .Times(2) |
- .WillRepeatedly(testing::WithArg<2>( |
- Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket, |
- base::Unretained(this), conn_id)))); |
- } |
+ size_t upper_limit = FLAGS_quic_limit_num_new_sessions_per_epoll_loop |
+ ? kMaxConnectionsWithoutCHLO |
+ : kDefaultMaxConnectionsInStore; |
+ size_t num_packet_to_process = i <= upper_limit ? 2u : 1u; |
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), |
+ ProcessUdpPacket(_, client_address, _)) |
+ .Times(num_packet_to_process) |
+ .WillRepeatedly(testing::WithArg<2>( |
+ Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket, |
+ base::Unretained(this), conn_id)))); |
ProcessPacket(client_address, conn_id, true, false, SerializeFullCHLO()); |
} |
} |
@@ -1220,8 +1235,8 @@ TEST_P(BufferedPacketStoreTest, DeliverEmptyPackets) { |
ProcessPacket(client_address, conn_id, true, false, SerializeFullCHLO()); |
} |
-// Tests that by the time a retransmitted CHLO arrives, a connection for the |
-// CHLO should already be created. |
+// Tests that a retransmitted CHLO arrives after a connection for the |
+// CHLO has been created. |
TEST_P(BufferedPacketStoreTest, ReceiveRetransmittedCHLO) { |
InSequence s; |
IPEndPoint client_address(Loopback4(), 1); |
@@ -1280,6 +1295,203 @@ TEST_P(BufferedPacketStoreTest, ReceiveCHLOAfterExpiration) { |
ProcessPacket(client_address, conn_id, true, false, SerializeFullCHLO()); |
} |
+TEST_P(BufferedPacketStoreTest, ProcessCHLOsUptoLimitAndBufferTheRest) { |
+ FLAGS_quic_limit_num_new_sessions_per_epoll_loop = true; |
+ // 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 (size_t conn_id = 1; conn_id <= kNumCHLOs; ++conn_id) { |
+ if (conn_id < kNumCHLOs) { |
+ // Except the last connection, all connections for previous CHLOs should |
+ // be regarded as newly added. |
+ EXPECT_CALL(*dispatcher_, OnNewConnectionAdded(conn_id)); |
+ } |
+ if (conn_id <= kMaxNumSessionsToCreate) { |
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, client_addr_)) |
+ .WillOnce(testing::Return(CreateSession( |
+ dispatcher_.get(), config_, 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(testing::WithArg<2>( |
+ Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket, |
+ base::Unretained(this), conn_id)))); |
+ } |
+ ProcessPacket(client_addr_, conn_id, true, false, SerializeFullCHLO()); |
+ if (conn_id <= kMaxNumSessionsToCreate + kDefaultMaxConnectionsInStore && |
+ conn_id > kMaxNumSessionsToCreate) { |
+ EXPECT_TRUE(store->HasChloForConnection(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(conn_id)); |
+ } |
+ } |
+ |
+ // Graduately consume buffered CHLOs. The buffered connections should be |
+ // created but the dropped one shouldn't. |
+ for (size_t conn_id = kMaxNumSessionsToCreate + 1; |
+ conn_id <= kMaxNumSessionsToCreate + kDefaultMaxConnectionsInStore; |
+ ++conn_id) { |
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, client_addr_)) |
+ .WillOnce(testing::Return(CreateSession( |
+ dispatcher_.get(), config_, 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(testing::WithArg<2>( |
+ Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket, |
+ base::Unretained(this), conn_id)))); |
+ } |
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(kNumCHLOs, client_addr_)) |
+ .Times(0); |
+ |
+ while (store->HasChlosBuffered()) { |
+ dispatcher_->ProcessBufferedChlos(kMaxNumSessionsToCreate); |
+ } |
+ |
+ EXPECT_EQ(static_cast<size_t>(kMaxNumSessionsToCreate) + |
+ kDefaultMaxConnectionsInStore, |
+ session1_->connection_id()); |
+} |
+ |
+// Duplicated CHLO shouldn't be buffered. |
+TEST_P(BufferedPacketStoreTest, DropDuplicatedCHLO) { |
+ FLAGS_quic_limit_num_new_sessions_per_epoll_loop = true; |
+ for (QuicConnectionId 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(conn_id, client_addr_)) |
+ .WillOnce(testing::Return(CreateSession( |
+ dispatcher_.get(), config_, 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(testing::WithArg<2>( |
+ Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket, |
+ base::Unretained(this), conn_id)))); |
+ } |
+ ProcessPacket(client_addr_, conn_id, true, false, SerializeFullCHLO()); |
+ } |
+ // Retransmit CHLO on last connection should be dropped. |
+ QuicConnectionId last_connection = kMaxNumSessionsToCreate + 1; |
+ ProcessPacket(client_addr_, last_connection, true, false, |
+ SerializeFullCHLO()); |
+ |
+ // Reset counter and process buffered CHLO. |
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(last_connection, client_addr_)) |
+ .WillOnce(testing::Return(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(_, _, _)) |
+ .WillOnce(testing::WithArg<2>( |
+ Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket, |
+ base::Unretained(this), last_connection)))); |
+ dispatcher_->ProcessBufferedChlos(kMaxNumSessionsToCreate); |
+} |
+ |
+TEST_P(BufferedPacketStoreTest, BufferNonChloPacketsUptoLimitWithChloBuffered) { |
+ FLAGS_quic_limit_num_new_sessions_per_epoll_loop = true; |
+ QuicConnectionId last_connection_id = kMaxNumSessionsToCreate + 1; |
+ for (QuicConnectionId conn_id = 1; conn_id <= last_connection_id; ++conn_id) { |
+ // Last CHLO will be buffered. Others will create connection right away. |
+ if (conn_id <= kMaxNumSessionsToCreate) { |
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, client_addr_)) |
+ .WillOnce(testing::Return(CreateSession( |
+ dispatcher_.get(), config_, 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(testing::WithArg<2>( |
+ Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket, |
+ base::Unretained(this), conn_id)))); |
+ } |
+ ProcessPacket(client_addr_, conn_id, true, false, SerializeFullCHLO()); |
+ } |
+ |
+ // Process another |kDefaultMaxUndecryptablePackets| + 1 data packets. The |
+ // last one should be dropped. |
+ for (QuicPacketNumber packet_number = 2; |
+ packet_number <= kDefaultMaxUndecryptablePackets + 2; ++packet_number) { |
+ ProcessPacket(client_addr_, last_connection_id, true, false, "data packet"); |
+ } |
+ |
+ // Reset counter and process buffered CHLO. |
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(last_connection_id, client_addr_)) |
+ .WillOnce(testing::Return(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(testing::WithArg<2>( |
+ Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket, |
+ base::Unretained(this), last_connection_id)))); |
+ 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_P(BufferedPacketStoreTest, ReceiveCHLOForBufferedConnection) { |
+ FLAGS_quic_limit_num_new_sessions_per_epoll_loop = true; |
+ QuicBufferedPacketStore* store = |
+ QuicDispatcherPeer::GetBufferedPackets(dispatcher_.get()); |
+ |
+ QuicConnectionId conn_id = 1; |
+ ProcessPacket(client_addr_, conn_id, true, false, "data packet", |
+ PACKET_8BYTE_CONNECTION_ID, PACKET_6BYTE_PACKET_NUMBER, |
+ kDefaultPathId, |
+ /*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(conn_id, client_addr_)) |
+ .WillOnce(testing::Return(CreateSession( |
+ dispatcher_.get(), config_, 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(testing::WithArg<2>( |
+ Invoke(CreateFunctor(&QuicDispatcherTest::ValidatePacket, |
+ base::Unretained(this), conn_id)))); |
+ } |
+ ProcessPacket(client_addr_, conn_id, true, false, SerializeFullCHLO()); |
+ } |
+ EXPECT_FALSE(store->HasChloForConnection(/*connection_id=*/1)); |
+ |
+ // CHLO on connection 1 should still be buffered. |
+ ProcessPacket(client_addr_, /*connection_id=*/1, true, false, |
+ SerializeFullCHLO()); |
+ EXPECT_TRUE(store->HasChloForConnection(/*connection_id=*/1)); |
+} |
+ |
} // namespace |
} // namespace test |
} // namespace net |