| // Copyright (c) 2012 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/quic/quic_stream_sequencer.h" |
| |
| #include <utility> |
| #include <vector> |
| |
| #include "base/rand_util.h" |
| #include "net/quic/reliable_quic_stream.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using base::StringPiece; |
| using std::min; |
| using std::pair; |
| using std::vector; |
| using testing::_; |
| using testing::AnyNumber; |
| using testing::InSequence; |
| using testing::Return; |
| using testing::StrEq; |
| |
| namespace net { |
| |
| class QuicStreamSequencerPeer : public QuicStreamSequencer { |
| public: |
| explicit QuicStreamSequencerPeer(ReliableQuicStream* stream) |
| : QuicStreamSequencer(stream) { |
| } |
| |
| QuicStreamSequencerPeer(int32 max_mem, ReliableQuicStream* stream) |
| : QuicStreamSequencer(max_mem, stream) {} |
| |
| virtual bool OnFrame(QuicStreamOffset byte_offset, |
| const char* data, |
| uint32 data_len) { |
| QuicStreamFrame frame; |
| frame.stream_id = 1; |
| frame.offset = byte_offset; |
| frame.data = StringPiece(data, data_len); |
| return OnStreamFrame(frame); |
| } |
| |
| void SetMemoryLimit(size_t limit) { |
| max_frame_memory_ = limit; |
| } |
| |
| const ReliableQuicStream* stream() const { return stream_; } |
| uint64 num_bytes_consumed() const { return num_bytes_consumed_; } |
| const FrameMap* frames() const { return &frames_; } |
| int32 max_frame_memory() const { return max_frame_memory_; } |
| QuicStreamOffset close_offset() const { return close_offset_; } |
| }; |
| |
| class MockStream : public ReliableQuicStream { |
| public: |
| MockStream(QuicSession* session, QuicStreamId id) |
| : ReliableQuicStream(id, session) { |
| } |
| |
| MOCK_METHOD1(TerminateFromPeer, void(bool half_close)); |
| MOCK_METHOD2(ProcessData, uint32(const char* data, uint32 data_len)); |
| MOCK_METHOD1(Close, void(QuicErrorCode error)); |
| MOCK_METHOD0(OnCanWrite, void()); |
| }; |
| |
| namespace { |
| |
| static const char kPayload[] = |
| "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; |
| |
| class QuicStreamSequencerTest : public ::testing::Test { |
| protected: |
| QuicStreamSequencerTest() |
| : session_(NULL), |
| stream_(session_, 1), |
| sequencer_(new QuicStreamSequencerPeer(&stream_)) { |
| } |
| |
| QuicSession* session_; |
| testing::StrictMock<MockStream> stream_; |
| scoped_ptr<QuicStreamSequencerPeer> sequencer_; |
| }; |
| |
| TEST_F(QuicStreamSequencerTest, RejectOldFrame) { |
| EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)) |
| .WillOnce(Return(3)); |
| |
| EXPECT_TRUE(sequencer_->OnFrame(0, "abc", 3)); |
| EXPECT_EQ(0u, sequencer_->frames()->size()); |
| EXPECT_EQ(3u, sequencer_->num_bytes_consumed()); |
| // Nack this - it matches a past sequence number and we should not see it |
| // again. |
| EXPECT_FALSE(sequencer_->OnFrame(0, "def", 3)); |
| EXPECT_EQ(0u, sequencer_->frames()->size()); |
| } |
| |
| TEST_F(QuicStreamSequencerTest, RejectOverlyLargeFrame) { |
| /* |
| EXPECT_DFATAL(sequencer_.reset(new QuicStreamSequencerPeer(2, &stream_)), |
| "Setting max frame memory to 2. " |
| "Some frames will be impossible to handle."); |
| |
| EXPECT_DEBUG_DEATH(sequencer_->OnFrame(0, "abc", 3), ""); |
| */ |
| } |
| |
| TEST_F(QuicStreamSequencerTest, DropFramePastBuffering) { |
| sequencer_->SetMemoryLimit(3); |
| |
| EXPECT_FALSE(sequencer_->OnFrame(3, "abc", 3)); |
| } |
| |
| TEST_F(QuicStreamSequencerTest, RejectBufferedFrame) { |
| EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)); |
| |
| EXPECT_TRUE(sequencer_->OnFrame(0, "abc", 3)); |
| EXPECT_EQ(1u, sequencer_->frames()->size()); |
| EXPECT_EQ(0u, sequencer_->num_bytes_consumed()); |
| // Ignore this - it matches a buffered frame. |
| // Right now there's no checking that the payload is consistent. |
| EXPECT_FALSE(sequencer_->OnFrame(0, "def", 3)); |
| EXPECT_EQ(1u, sequencer_->frames()->size()); |
| } |
| |
| TEST_F(QuicStreamSequencerTest, FullFrameConsumed) { |
| EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)).WillOnce(Return(3)); |
| |
| EXPECT_TRUE(sequencer_->OnFrame(0, "abc", 3)); |
| EXPECT_EQ(0u, sequencer_->frames()->size()); |
| EXPECT_EQ(3u, sequencer_->num_bytes_consumed()); |
| } |
| |
| TEST_F(QuicStreamSequencerTest, PartialFrameConsumed) { |
| EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)).WillOnce(Return(2)); |
| |
| EXPECT_TRUE(sequencer_->OnFrame(0, "abc", 3)); |
| EXPECT_EQ(1u, sequencer_->frames()->size()); |
| EXPECT_EQ(2u, sequencer_->num_bytes_consumed()); |
| EXPECT_EQ("c", sequencer_->frames()->find(2)->second); |
| } |
| |
| TEST_F(QuicStreamSequencerTest, NextxFrameNotConsumed) { |
| EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)).WillOnce(Return(0)); |
| |
| EXPECT_TRUE(sequencer_->OnFrame(0, "abc", 3)); |
| EXPECT_EQ(1u, sequencer_->frames()->size()); |
| EXPECT_EQ(0u, sequencer_->num_bytes_consumed()); |
| EXPECT_EQ("abc", sequencer_->frames()->find(0)->second); |
| } |
| |
| TEST_F(QuicStreamSequencerTest, FutureFrameNotProcessed) { |
| EXPECT_TRUE(sequencer_->OnFrame(3, "abc", 3)); |
| EXPECT_EQ(1u, sequencer_->frames()->size()); |
| EXPECT_EQ(0u, sequencer_->num_bytes_consumed()); |
| EXPECT_EQ("abc", sequencer_->frames()->find(3)->second); |
| } |
| |
| TEST_F(QuicStreamSequencerTest, OutOfOrderFrameProcessed) { |
| // Buffer the first |
| EXPECT_TRUE(sequencer_->OnFrame(6, "ghi", 3)); |
| EXPECT_EQ(1u, sequencer_->frames()->size()); |
| EXPECT_EQ(0u, sequencer_->num_bytes_consumed()); |
| // Buffer the second |
| EXPECT_TRUE(sequencer_->OnFrame(3, "def", 3)); |
| EXPECT_EQ(2u, sequencer_->frames()->size()); |
| EXPECT_EQ(0u, sequencer_->num_bytes_consumed()); |
| |
| InSequence s; |
| EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)).WillOnce(Return(3)); |
| EXPECT_CALL(stream_, ProcessData(StrEq("def"), 3)).WillOnce(Return(3)); |
| EXPECT_CALL(stream_, ProcessData(StrEq("ghi"), 3)).WillOnce(Return(3)); |
| |
| // Ack right away |
| EXPECT_TRUE(sequencer_->OnFrame(0, "abc", 3)); |
| EXPECT_EQ(9u, sequencer_->num_bytes_consumed()); |
| |
| EXPECT_EQ(0u, sequencer_->frames()->size()); |
| } |
| |
| TEST_F(QuicStreamSequencerTest, OutOfOrderFramesProcessedWithBuffering) { |
| sequencer_->SetMemoryLimit(9); |
| |
| // Too far to buffer. |
| EXPECT_FALSE(sequencer_->OnFrame(9, "jkl", 3)); |
| |
| // We can afford to buffer this. |
| EXPECT_TRUE(sequencer_->OnFrame(6, "ghi", 3)); |
| EXPECT_EQ(0u, sequencer_->num_bytes_consumed()); |
| |
| InSequence s; |
| EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)).WillOnce(Return(3)); |
| |
| // Ack right away |
| EXPECT_TRUE(sequencer_->OnFrame(0, "abc", 3)); |
| EXPECT_EQ(3u, sequencer_->num_bytes_consumed()); |
| |
| // We should be willing to buffer this now. |
| EXPECT_TRUE(sequencer_->OnFrame(9, "jkl", 3)); |
| EXPECT_EQ(3u, sequencer_->num_bytes_consumed()); |
| |
| EXPECT_CALL(stream_, ProcessData(StrEq("def"), 3)).WillOnce(Return(3)); |
| EXPECT_CALL(stream_, ProcessData(StrEq("ghi"), 3)).WillOnce(Return(3)); |
| EXPECT_CALL(stream_, ProcessData(StrEq("jkl"), 3)).WillOnce(Return(3)); |
| |
| EXPECT_TRUE(sequencer_->OnFrame(3, "def", 3)); |
| EXPECT_EQ(12u, sequencer_->num_bytes_consumed()); |
| EXPECT_EQ(0u, sequencer_->frames()->size()); |
| } |
| |
| TEST_F(QuicStreamSequencerTest, BasicCloseOrdered) { |
| InSequence s; |
| EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)).WillOnce(Return(3)); |
| EXPECT_TRUE(sequencer_->OnFrame(0, "abc", 3)); |
| |
| EXPECT_CALL(stream_, TerminateFromPeer(false)); |
| sequencer_->CloseStreamAtOffset(3, false); |
| EXPECT_EQ(3u, sequencer_->close_offset()); |
| } |
| |
| TEST_F(QuicStreamSequencerTest, BasicHalfOrdered) { |
| InSequence s; |
| |
| EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)).WillOnce(Return(3)); |
| EXPECT_TRUE(sequencer_->OnFrame(0, "abc", 3)); |
| |
| EXPECT_CALL(stream_, TerminateFromPeer(true)); |
| sequencer_->CloseStreamAtOffset(3, true); |
| EXPECT_EQ(3u, sequencer_->close_offset()); |
| } |
| |
| TEST_F(QuicStreamSequencerTest, BasicCloseUnordered) { |
| sequencer_->CloseStreamAtOffset(3, false); |
| EXPECT_EQ(3u, sequencer_->close_offset()); |
| |
| InSequence s; |
| EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)).WillOnce(Return(3)); |
| EXPECT_CALL(stream_, TerminateFromPeer(false)); |
| |
| EXPECT_TRUE(sequencer_->OnFrame(0, "abc", 3)); |
| } |
| |
| TEST_F(QuicStreamSequencerTest, BasicHalfUnorderedWithFlush) { |
| sequencer_->CloseStreamAtOffset(6, true); |
| EXPECT_EQ(6u, sequencer_->close_offset()); |
| InSequence s; |
| EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)).WillOnce(Return(3)); |
| EXPECT_CALL(stream_, ProcessData(StrEq("def"), 3)).WillOnce(Return(3)); |
| EXPECT_CALL(stream_, TerminateFromPeer(true)); |
| |
| EXPECT_TRUE(sequencer_->OnFrame(3, "def", 3)); |
| EXPECT_TRUE(sequencer_->OnFrame(0, "abc", 3)); |
| } |
| |
| TEST_F(QuicStreamSequencerTest, BasicCloseUnorderedWithFlush) { |
| sequencer_->CloseStreamAtOffset(6, false); |
| EXPECT_EQ(6u, sequencer_->close_offset()); |
| |
| InSequence s; |
| EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)).WillOnce(Return(3)); |
| EXPECT_CALL(stream_, ProcessData(StrEq("def"), 3)).WillOnce(Return(3)); |
| EXPECT_CALL(stream_, TerminateFromPeer(false)); |
| |
| EXPECT_TRUE(sequencer_->OnFrame(3, "def", 3)); |
| EXPECT_TRUE(sequencer_->OnFrame(0, "abc", 3)); |
| } |
| |
| TEST_F(QuicStreamSequencerTest, BasicHalfUnordered) { |
| sequencer_->CloseStreamAtOffset(3, true); |
| EXPECT_EQ(3u, sequencer_->close_offset()); |
| InSequence s; |
| EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)).WillOnce(Return(3)); |
| EXPECT_CALL(stream_, TerminateFromPeer(true)); |
| |
| EXPECT_TRUE(sequencer_->OnFrame(0, "abc", 3)); |
| } |
| |
| TEST_F(QuicStreamSequencerTest, TerminateStreamBeforeCloseEqual) { |
| sequencer_->CloseStreamAtOffset(3, true); |
| EXPECT_EQ(3u, sequencer_->close_offset()); |
| |
| sequencer_->CloseStreamAtOffset(3, false); |
| EXPECT_EQ(3u, sequencer_->close_offset()); |
| |
| InSequence s; |
| EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)).WillOnce(Return(3)); |
| EXPECT_CALL(stream_, TerminateFromPeer(false)); |
| EXPECT_TRUE(sequencer_->OnFrame(0, "abc", 3)); |
| } |
| |
| TEST_F(QuicStreamSequencerTest, CloseBeforeTermianteEqual) { |
| sequencer_->CloseStreamAtOffset(3, false); |
| EXPECT_EQ(3u, sequencer_->close_offset()); |
| |
| sequencer_->CloseStreamAtOffset(3, true); |
| EXPECT_EQ(3u, sequencer_->close_offset()); |
| |
| InSequence s; |
| EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)).WillOnce(Return(3)); |
| EXPECT_CALL(stream_, TerminateFromPeer(false)); |
| EXPECT_TRUE(sequencer_->OnFrame(0, "abc", 3)); |
| } |
| |
| TEST_F(QuicStreamSequencerTest, MutipleOffsets) { |
| sequencer_->CloseStreamAtOffset(3, false); |
| EXPECT_EQ(3u, sequencer_->close_offset()); |
| |
| EXPECT_CALL(stream_, Close(QUIC_MULTIPLE_TERMINATION_OFFSETS)); |
| sequencer_->CloseStreamAtOffset(5, false); |
| EXPECT_EQ(3u, sequencer_->close_offset()); |
| |
| EXPECT_CALL(stream_, Close(QUIC_MULTIPLE_TERMINATION_OFFSETS)); |
| sequencer_->CloseStreamAtOffset(1, false); |
| EXPECT_EQ(3u, sequencer_->close_offset()); |
| |
| sequencer_->CloseStreamAtOffset(3, false); |
| EXPECT_EQ(3u, sequencer_->close_offset()); |
| } |
| |
| class QuicSequencerRandomTest : public QuicStreamSequencerTest { |
| public: |
| typedef pair<int, string> Frame; |
| typedef vector<Frame> FrameList; |
| |
| void CreateFrames() { |
| int payload_size = arraysize(kPayload) - 1; |
| int remaining_payload = payload_size; |
| while (remaining_payload != 0) { |
| int size = min(OneToN(6), remaining_payload); |
| int index = payload_size - remaining_payload; |
| list_.push_back(make_pair(index, string(kPayload + index, size))); |
| remaining_payload -= size; |
| } |
| } |
| |
| QuicSequencerRandomTest() { |
| CreateFrames(); |
| } |
| |
| int OneToN(int n) { |
| return base::RandInt(1, n); |
| } |
| |
| int MaybeProcessMaybeBuffer(const char* data, uint32 len) { |
| int to_process = len; |
| if (base::RandUint64() % 2 != 0) { |
| to_process = base::RandInt(0, len); |
| } |
| output_.append(data, to_process); |
| return to_process; |
| } |
| |
| string output_; |
| FrameList list_; |
| }; |
| |
| // All frames are processed as soon as we have sequential data. |
| // Infinite buffering, so all frames are acked right away. |
| TEST_F(QuicSequencerRandomTest, RandomFramesNoDroppingNoBackup) { |
| InSequence s; |
| for (size_t i = 0; i < list_.size(); ++i) { |
| string* data = &list_[i].second; |
| EXPECT_CALL(stream_, ProcessData(StrEq(*data), data->size())) |
| .WillOnce(Return(data->size())); |
| } |
| |
| while (list_.size() != 0) { |
| int index = OneToN(list_.size()) - 1; |
| EXPECT_TRUE(sequencer_->OnFrame( |
| list_[index].first, list_[index].second.data(), |
| list_[index].second.size())); |
| list_.erase(list_.begin() + index); |
| } |
| } |
| |
| // All frames are processed as soon as we have sequential data. |
| // Buffering, so some frames are rejected. |
| TEST_F(QuicSequencerRandomTest, RandomFramesDroppingNoBackup) { |
| sequencer_->SetMemoryLimit(26); |
| |
| InSequence s; |
| for (size_t i = 0; i < list_.size(); ++i) { |
| string* data = &list_[i].second; |
| EXPECT_CALL(stream_, ProcessData(StrEq(*data), data->size())) |
| .WillOnce(Return(data->size())); |
| } |
| |
| while (list_.size() != 0) { |
| int index = OneToN(list_.size()) - 1; |
| bool acked = sequencer_->OnFrame( |
| list_[index].first, list_[index].second.data(), |
| list_[index].second.size()); |
| if (acked) { |
| list_.erase(list_.begin() + index); |
| } |
| } |
| } |
| |
| } // namespace |
| |
| } // namespace net |