blob: 8257363edb0f785cd401e7643e95bf772a344282 [file] [log] [blame]
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/third_party/quiche/src/quic/core/qpack/qpack_blocking_manager.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
namespace quic {
namespace test {
class QpackBlockingManagerPeer {
public:
static bool stream_is_blocked(const QpackBlockingManager* manager,
QuicStreamId stream_id) {
for (const auto& header_blocks_for_stream : manager->header_blocks_) {
if (header_blocks_for_stream.first != stream_id) {
continue;
}
for (const auto& indices : header_blocks_for_stream.second) {
if (QpackBlockingManager::RequiredInsertCount(indices) >
manager->known_received_count_) {
return true;
}
}
}
return false;
}
};
namespace {
class QpackBlockingManagerTest : public QuicTest {
protected:
QpackBlockingManagerTest() = default;
~QpackBlockingManagerTest() override = default;
bool stream_is_blocked(QuicStreamId stream_id) const {
return QpackBlockingManagerPeer::stream_is_blocked(&manager_, stream_id);
}
QpackBlockingManager manager_;
};
TEST_F(QpackBlockingManagerTest, Empty) {
EXPECT_EQ(0u, manager_.known_received_count());
EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
manager_.smallest_blocking_index());
EXPECT_FALSE(manager_.OnHeaderAcknowledgement(0));
EXPECT_FALSE(manager_.OnHeaderAcknowledgement(1));
}
TEST_F(QpackBlockingManagerTest, NotBlockedByInsertCountIncrement) {
manager_.OnInsertCountIncrement(2);
// Stream 0 is not blocked, because it only references entries that are
// already acknowledged by an Insert Count Increment instruction.
manager_.OnHeaderBlockSent(0, {1, 0});
EXPECT_FALSE(stream_is_blocked(0));
}
TEST_F(QpackBlockingManagerTest, UnblockedByInsertCountIncrement) {
manager_.OnHeaderBlockSent(0, {1, 0});
EXPECT_TRUE(stream_is_blocked(0));
manager_.OnInsertCountIncrement(2);
EXPECT_FALSE(stream_is_blocked(0));
}
TEST_F(QpackBlockingManagerTest, NotBlockedByHeaderAcknowledgement) {
manager_.OnHeaderBlockSent(0, {2, 1, 1});
EXPECT_TRUE(stream_is_blocked(0));
EXPECT_TRUE(manager_.OnHeaderAcknowledgement(0));
EXPECT_FALSE(stream_is_blocked(0));
// Stream 1 is not blocked, because it only references entries that are
// already acknowledged by a Header Acknowledgement instruction.
manager_.OnHeaderBlockSent(1, {2, 2});
EXPECT_FALSE(stream_is_blocked(1));
}
TEST_F(QpackBlockingManagerTest, UnblockedByHeaderAcknowledgement) {
manager_.OnHeaderBlockSent(0, {2, 1, 1});
manager_.OnHeaderBlockSent(1, {2, 2});
EXPECT_TRUE(stream_is_blocked(0));
EXPECT_TRUE(stream_is_blocked(1));
EXPECT_TRUE(manager_.OnHeaderAcknowledgement(0));
EXPECT_FALSE(stream_is_blocked(0));
EXPECT_FALSE(stream_is_blocked(1));
}
TEST_F(QpackBlockingManagerTest, KnownReceivedCount) {
EXPECT_EQ(0u, manager_.known_received_count());
// Sending a header block does not change Known Received Count.
manager_.OnHeaderBlockSent(0, {0});
EXPECT_EQ(0u, manager_.known_received_count());
manager_.OnHeaderBlockSent(1, {1});
EXPECT_EQ(0u, manager_.known_received_count());
// Header Acknowledgement might increase Known Received Count.
EXPECT_TRUE(manager_.OnHeaderAcknowledgement(0));
EXPECT_EQ(1u, manager_.known_received_count());
manager_.OnHeaderBlockSent(2, {5});
EXPECT_EQ(1u, manager_.known_received_count());
EXPECT_TRUE(manager_.OnHeaderAcknowledgement(1));
EXPECT_EQ(2u, manager_.known_received_count());
// Insert Count Increment increases Known Received Count.
manager_.OnInsertCountIncrement(2);
EXPECT_EQ(4u, manager_.known_received_count());
EXPECT_TRUE(manager_.OnHeaderAcknowledgement(2));
EXPECT_EQ(6u, manager_.known_received_count());
// Stream Cancellation does not change Known Received Count.
manager_.OnStreamCancellation(0);
EXPECT_EQ(6u, manager_.known_received_count());
// Header Acknowledgement of a block with smaller Required Insert Count does
// not increase Known Received Count.
manager_.OnHeaderBlockSent(0, {3});
EXPECT_EQ(6u, manager_.known_received_count());
EXPECT_TRUE(manager_.OnHeaderAcknowledgement(0));
EXPECT_EQ(6u, manager_.known_received_count());
// Header Acknowledgement of a block with equal Required Insert Count does not
// increase Known Received Count.
manager_.OnHeaderBlockSent(1, {5});
EXPECT_EQ(6u, manager_.known_received_count());
EXPECT_TRUE(manager_.OnHeaderAcknowledgement(1));
EXPECT_EQ(6u, manager_.known_received_count());
}
TEST_F(QpackBlockingManagerTest, SmallestBlockingIndex) {
EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
manager_.smallest_blocking_index());
manager_.OnHeaderBlockSent(0, {0});
EXPECT_EQ(0u, manager_.smallest_blocking_index());
manager_.OnHeaderBlockSent(1, {2});
EXPECT_EQ(0u, manager_.smallest_blocking_index());
EXPECT_TRUE(manager_.OnHeaderAcknowledgement(0));
EXPECT_EQ(2u, manager_.smallest_blocking_index());
manager_.OnHeaderBlockSent(1, {1});
EXPECT_EQ(1u, manager_.smallest_blocking_index());
EXPECT_TRUE(manager_.OnHeaderAcknowledgement(1));
EXPECT_EQ(1u, manager_.smallest_blocking_index());
// Insert Count Increment does not change smallest blocking index.
manager_.OnInsertCountIncrement(2);
EXPECT_EQ(1u, manager_.smallest_blocking_index());
manager_.OnStreamCancellation(1);
EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
manager_.smallest_blocking_index());
}
TEST_F(QpackBlockingManagerTest, HeaderAcknowledgementsOnSingleStream) {
EXPECT_EQ(0u, manager_.known_received_count());
EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
manager_.smallest_blocking_index());
manager_.OnHeaderBlockSent(0, {2, 1, 1});
EXPECT_EQ(0u, manager_.known_received_count());
EXPECT_TRUE(stream_is_blocked(0));
EXPECT_EQ(1u, manager_.smallest_blocking_index());
manager_.OnHeaderBlockSent(0, {1, 0});
EXPECT_EQ(0u, manager_.known_received_count());
EXPECT_TRUE(stream_is_blocked(0));
EXPECT_EQ(0u, manager_.smallest_blocking_index());
EXPECT_TRUE(manager_.OnHeaderAcknowledgement(0));
EXPECT_EQ(3u, manager_.known_received_count());
EXPECT_FALSE(stream_is_blocked(0));
EXPECT_EQ(0u, manager_.smallest_blocking_index());
manager_.OnHeaderBlockSent(0, {3});
EXPECT_EQ(3u, manager_.known_received_count());
EXPECT_TRUE(stream_is_blocked(0));
EXPECT_EQ(0u, manager_.smallest_blocking_index());
EXPECT_TRUE(manager_.OnHeaderAcknowledgement(0));
EXPECT_EQ(3u, manager_.known_received_count());
EXPECT_TRUE(stream_is_blocked(0));
EXPECT_EQ(3u, manager_.smallest_blocking_index());
EXPECT_TRUE(manager_.OnHeaderAcknowledgement(0));
EXPECT_EQ(4u, manager_.known_received_count());
EXPECT_FALSE(stream_is_blocked(0));
EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
manager_.smallest_blocking_index());
EXPECT_FALSE(manager_.OnHeaderAcknowledgement(0));
}
TEST_F(QpackBlockingManagerTest, CancelStream) {
manager_.OnHeaderBlockSent(0, {3});
EXPECT_TRUE(stream_is_blocked(0));
EXPECT_EQ(3u, manager_.smallest_blocking_index());
manager_.OnHeaderBlockSent(0, {2});
EXPECT_TRUE(stream_is_blocked(0));
EXPECT_EQ(2u, manager_.smallest_blocking_index());
manager_.OnHeaderBlockSent(1, {4});
EXPECT_TRUE(stream_is_blocked(0));
EXPECT_TRUE(stream_is_blocked(1));
EXPECT_EQ(2u, manager_.smallest_blocking_index());
manager_.OnStreamCancellation(0);
EXPECT_FALSE(stream_is_blocked(0));
EXPECT_TRUE(stream_is_blocked(1));
EXPECT_EQ(4u, manager_.smallest_blocking_index());
manager_.OnStreamCancellation(1);
EXPECT_FALSE(stream_is_blocked(0));
EXPECT_FALSE(stream_is_blocked(1));
EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
manager_.smallest_blocking_index());
}
TEST_F(QpackBlockingManagerTest,
ReferenceOnEncoderStreamUnblockedByInsertCountIncrement) {
EXPECT_EQ(0u, manager_.known_received_count());
EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
manager_.smallest_blocking_index());
// Entry 1 refers to entry 0.
manager_.OnReferenceSentOnEncoderStream(1, 0);
// Entry 2 also refers to entry 0.
manager_.OnReferenceSentOnEncoderStream(2, 0);
EXPECT_EQ(0u, manager_.known_received_count());
EXPECT_EQ(0u, manager_.smallest_blocking_index());
// Acknowledging entry 1 still leaves one unacknowledged reference to entry 0.
manager_.OnInsertCountIncrement(2);
EXPECT_EQ(2u, manager_.known_received_count());
EXPECT_EQ(0u, manager_.smallest_blocking_index());
// Entry 3 also refers to entry 2.
manager_.OnReferenceSentOnEncoderStream(3, 2);
EXPECT_EQ(2u, manager_.known_received_count());
EXPECT_EQ(0u, manager_.smallest_blocking_index());
// Acknowledging entry 2 removes last reference to entry 0.
manager_.OnInsertCountIncrement(1);
EXPECT_EQ(3u, manager_.known_received_count());
EXPECT_EQ(2u, manager_.smallest_blocking_index());
// Acknowledging entry 4 (and implicitly 3) removes reference to entry 2.
manager_.OnInsertCountIncrement(2);
EXPECT_EQ(5u, manager_.known_received_count());
EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
manager_.smallest_blocking_index());
}
TEST_F(QpackBlockingManagerTest,
ReferenceOnEncoderStreamUnblockedByHeaderAcknowledgement) {
EXPECT_EQ(0u, manager_.known_received_count());
EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
manager_.smallest_blocking_index());
// Entry 1 refers to entry 0.
manager_.OnReferenceSentOnEncoderStream(1, 0);
// Entry 2 also refers to entry 0.
manager_.OnReferenceSentOnEncoderStream(2, 0);
EXPECT_EQ(0u, manager_.known_received_count());
EXPECT_EQ(0u, manager_.smallest_blocking_index());
// Acknowledging a header block with entries up to 1 still leave one
// unacknowledged reference to entry 0.
manager_.OnHeaderBlockSent(/* stream_id = */ 0, {0, 1});
manager_.OnHeaderAcknowledgement(/* stream_id = */ 0);
EXPECT_EQ(2u, manager_.known_received_count());
EXPECT_EQ(0u, manager_.smallest_blocking_index());
// Entry 3 also refers to entry 2.
manager_.OnReferenceSentOnEncoderStream(3, 2);
// Acknowledging a header block with entries up to 2 removes last reference to
// entry 0.
manager_.OnHeaderBlockSent(/* stream_id = */ 0, {2, 0, 2});
manager_.OnHeaderAcknowledgement(/* stream_id = */ 0);
EXPECT_EQ(3u, manager_.known_received_count());
EXPECT_EQ(2u, manager_.smallest_blocking_index());
// Acknowledging entry 4 (and implicitly 3) removes reference to entry 2.
manager_.OnHeaderBlockSent(/* stream_id = */ 0, {1, 4, 2, 0});
manager_.OnHeaderAcknowledgement(/* stream_id = */ 0);
EXPECT_EQ(5u, manager_.known_received_count());
EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
manager_.smallest_blocking_index());
}
TEST_F(QpackBlockingManagerTest, BlockingAllowedOnStream) {
const QuicStreamId kStreamId1 = 1;
const QuicStreamId kStreamId2 = 2;
const QuicStreamId kStreamId3 = 3;
// No stream can block if limit is 0.
EXPECT_FALSE(manager_.blocking_allowed_on_stream(kStreamId1, 0));
EXPECT_FALSE(manager_.blocking_allowed_on_stream(kStreamId2, 0));
// Either stream can block if limit is larger.
EXPECT_TRUE(manager_.blocking_allowed_on_stream(kStreamId1, 1));
EXPECT_TRUE(manager_.blocking_allowed_on_stream(kStreamId2, 1));
// Doubly block first stream.
manager_.OnHeaderBlockSent(kStreamId1, {0});
manager_.OnHeaderBlockSent(kStreamId1, {1});
// First stream is already blocked so it can carry more blocking references.
EXPECT_TRUE(manager_.blocking_allowed_on_stream(kStreamId1, 1));
// Second stream is not allowed to block if limit is already reached.
EXPECT_FALSE(manager_.blocking_allowed_on_stream(kStreamId2, 1));
// Either stream can block if limit is larger than number of blocked streams.
EXPECT_TRUE(manager_.blocking_allowed_on_stream(kStreamId1, 2));
EXPECT_TRUE(manager_.blocking_allowed_on_stream(kStreamId2, 2));
// Block second stream.
manager_.OnHeaderBlockSent(kStreamId2, {2});
// Streams are already blocked so either can carry more blocking references.
EXPECT_TRUE(manager_.blocking_allowed_on_stream(kStreamId1, 2));
EXPECT_TRUE(manager_.blocking_allowed_on_stream(kStreamId2, 2));
// Third, unblocked stream is not allowed to block unless limit is strictly
// larger than number of blocked streams.
EXPECT_FALSE(manager_.blocking_allowed_on_stream(kStreamId3, 2));
EXPECT_TRUE(manager_.blocking_allowed_on_stream(kStreamId3, 3));
// Acknowledge decoding of first header block on first stream.
// Stream is still blocked on its second header block.
manager_.OnHeaderAcknowledgement(kStreamId1);
EXPECT_TRUE(manager_.blocking_allowed_on_stream(kStreamId1, 2));
EXPECT_TRUE(manager_.blocking_allowed_on_stream(kStreamId2, 2));
// Acknowledge decoding of second header block on first stream.
// This unblocks the stream.
manager_.OnHeaderAcknowledgement(kStreamId1);
// First stream is not allowed to block if limit is already reached.
EXPECT_FALSE(manager_.blocking_allowed_on_stream(kStreamId1, 1));
// Second stream is already blocked so it can carry more blocking references.
EXPECT_TRUE(manager_.blocking_allowed_on_stream(kStreamId2, 1));
// Either stream can block if limit is larger than number of blocked streams.
EXPECT_TRUE(manager_.blocking_allowed_on_stream(kStreamId1, 2));
EXPECT_TRUE(manager_.blocking_allowed_on_stream(kStreamId2, 2));
// Unblock second stream.
manager_.OnHeaderAcknowledgement(kStreamId2);
// No stream can block if limit is 0.
EXPECT_FALSE(manager_.blocking_allowed_on_stream(kStreamId1, 0));
EXPECT_FALSE(manager_.blocking_allowed_on_stream(kStreamId2, 0));
// Either stream can block if limit is larger.
EXPECT_TRUE(manager_.blocking_allowed_on_stream(kStreamId1, 1));
EXPECT_TRUE(manager_.blocking_allowed_on_stream(kStreamId2, 1));
}
} // namespace
} // namespace test
} // namespace quic