| // 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/qbone/qbone_stream.h" | |
| #include <utility> | |
| #include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" | |
| #include "net/third_party/quiche/src/quic/core/quic_session.h" | |
| #include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h" | |
| #include "net/third_party/quiche/src/quic/core/quic_utils.h" | |
| #include "net/third_party/quiche/src/quic/platform/api/quic_test.h" | |
| #include "net/third_party/quiche/src/quic/platform/api/quic_test_loopback.h" | |
| #include "net/third_party/quiche/src/quic/qbone/qbone_constants.h" | |
| #include "net/third_party/quiche/src/quic/qbone/qbone_session_base.h" | |
| #include "net/third_party/quiche/src/quic/test_tools/mock_clock.h" | |
| #include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" | |
| #include "net/third_party/quiche/src/spdy/core/spdy_protocol.h" | |
| namespace quic { | |
| namespace { | |
| using ::testing::_; | |
| using ::testing::StrictMock; | |
| // MockQuicSession that does not create streams and writes data from | |
| // QuicStream to a string. | |
| class MockQuicSession : public QboneSessionBase { | |
| public: | |
| MockQuicSession(QuicConnection* connection, const QuicConfig& config) | |
| : QboneSessionBase(connection, | |
| nullptr /*visitor*/, | |
| config, | |
| CurrentSupportedVersions(), | |
| nullptr /*writer*/) {} | |
| ~MockQuicSession() override {} | |
| // Writes outgoing data from QuicStream to a string. | |
| QuicConsumedData WritevData(QuicStream* stream, | |
| QuicStreamId id, | |
| size_t write_length, | |
| QuicStreamOffset offset, | |
| StreamSendingState state) override { | |
| if (!writable_) { | |
| return QuicConsumedData(0, false); | |
| } | |
| return QuicConsumedData(write_length, state != StreamSendingState::NO_FIN); | |
| } | |
| QboneReadOnlyStream* CreateIncomingStream(QuicStreamId id) override { | |
| return nullptr; | |
| } | |
| const QuicCryptoStream* GetCryptoStream() const override { return nullptr; } | |
| QuicCryptoStream* GetMutableCryptoStream() override { return nullptr; } | |
| // Called by QuicStream when they want to close stream. | |
| MOCK_METHOD3(SendRstStream, | |
| void(QuicStreamId, QuicRstStreamErrorCode, QuicStreamOffset)); | |
| // Sets whether data is written to buffer, or else if this is write blocked. | |
| void set_writable(bool writable) { writable_ = writable; } | |
| // Tracks whether the stream is write blocked and its priority. | |
| void RegisterReliableStream(QuicStreamId stream_id) { | |
| // The priority effectively does not matter. Put all streams on the same | |
| // priority. | |
| write_blocked_streams()->RegisterStream( | |
| stream_id, | |
| /*is_static_stream=*/false, | |
| /* precedence= */ spdy::SpdyStreamPrecedence(3)); | |
| } | |
| // The session take ownership of the stream. | |
| void ActivateReliableStream(std::unique_ptr<QuicStream> stream) { | |
| ActivateStream(std::move(stream)); | |
| } | |
| std::unique_ptr<QuicCryptoStream> CreateCryptoStream() override { | |
| return nullptr; | |
| } | |
| MOCK_METHOD1(ProcessPacketFromPeer, void(QuicStringPiece)); | |
| MOCK_METHOD1(ProcessPacketFromNetwork, void(QuicStringPiece)); | |
| private: | |
| // Whether data is written to write_buffer_. | |
| bool writable_ = true; | |
| }; | |
| // Packet writer that does nothing. This is required for QuicConnection but | |
| // isn't used for writing data. | |
| class DummyPacketWriter : public QuicPacketWriter { | |
| public: | |
| DummyPacketWriter() {} | |
| // QuicPacketWriter overrides. | |
| WriteResult WritePacket(const char* buffer, | |
| size_t buf_len, | |
| const QuicIpAddress& self_address, | |
| const QuicSocketAddress& peer_address, | |
| PerPacketOptions* options) override { | |
| return WriteResult(WRITE_STATUS_ERROR, 0); | |
| } | |
| bool IsWriteBlocked() const override { return false; }; | |
| void SetWritable() override {} | |
| QuicByteCount GetMaxPacketSize( | |
| const QuicSocketAddress& peer_address) const override { | |
| return 0; | |
| } | |
| bool SupportsReleaseTime() const override { return false; } | |
| bool IsBatchMode() const override { return false; } | |
| char* GetNextWriteLocation(const QuicIpAddress& self_address, | |
| const QuicSocketAddress& peer_address) override { | |
| return nullptr; | |
| } | |
| WriteResult Flush() override { return WriteResult(WRITE_STATUS_OK, 0); } | |
| }; | |
| class QboneReadOnlyStreamTest : public ::testing::Test, | |
| public QuicConnectionHelperInterface { | |
| public: | |
| void CreateReliableQuicStream() { | |
| // Arbitrary values for QuicConnection. | |
| Perspective perspective = Perspective::IS_SERVER; | |
| bool owns_writer = true; | |
| alarm_factory_ = std::make_unique<test::MockAlarmFactory>(); | |
| connection_.reset(new QuicConnection( | |
| test::TestConnectionId(0), QuicSocketAddress(TestLoopback(), 0), | |
| this /*QuicConnectionHelperInterface*/, alarm_factory_.get(), | |
| new DummyPacketWriter(), owns_writer, perspective, | |
| ParsedVersionOfIndex(CurrentSupportedVersions(), 0))); | |
| clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1)); | |
| session_ = std::make_unique<StrictMock<MockQuicSession>>(connection_.get(), | |
| QuicConfig()); | |
| stream_ = new QboneReadOnlyStream(kStreamId, session_.get()); | |
| session_->ActivateReliableStream( | |
| std::unique_ptr<QboneReadOnlyStream>(stream_)); | |
| } | |
| ~QboneReadOnlyStreamTest() override {} | |
| const QuicClock* GetClock() const override { return &clock_; } | |
| QuicRandom* GetRandomGenerator() override { | |
| return QuicRandom::GetInstance(); | |
| } | |
| QuicBufferAllocator* GetStreamSendBufferAllocator() override { | |
| return &buffer_allocator_; | |
| } | |
| protected: | |
| // The QuicSession will take the ownership. | |
| QboneReadOnlyStream* stream_; | |
| std::unique_ptr<StrictMock<MockQuicSession>> session_; | |
| std::unique_ptr<QuicAlarmFactory> alarm_factory_; | |
| std::unique_ptr<QuicConnection> connection_; | |
| // Used to implement the QuicConnectionHelperInterface. | |
| SimpleBufferAllocator buffer_allocator_; | |
| MockClock clock_; | |
| const QuicStreamId kStreamId = QuicUtils::GetFirstUnidirectionalStreamId( | |
| CurrentSupportedVersions()[0].transport_version, | |
| Perspective::IS_CLIENT); | |
| }; | |
| // Read an entire string. | |
| TEST_F(QboneReadOnlyStreamTest, ReadDataWhole) { | |
| string packet = "Stuff"; | |
| CreateReliableQuicStream(); | |
| QuicStreamFrame frame(kStreamId, true, 0, packet); | |
| EXPECT_CALL(*session_, ProcessPacketFromPeer("Stuff")); | |
| stream_->OnStreamFrame(frame); | |
| } | |
| // Test buffering. | |
| TEST_F(QboneReadOnlyStreamTest, ReadBuffered) { | |
| CreateReliableQuicStream(); | |
| string packet = "Stuf"; | |
| { | |
| QuicStreamFrame frame(kStreamId, false, 0, packet); | |
| stream_->OnStreamFrame(frame); | |
| } | |
| // We didn't write 5 bytes yet... | |
| packet = "f"; | |
| EXPECT_CALL(*session_, ProcessPacketFromPeer("Stuff")); | |
| { | |
| QuicStreamFrame frame(kStreamId, true, 4, packet); | |
| stream_->OnStreamFrame(frame); | |
| } | |
| } | |
| TEST_F(QboneReadOnlyStreamTest, ReadOutOfOrder) { | |
| CreateReliableQuicStream(); | |
| string packet = "f"; | |
| { | |
| QuicStreamFrame frame(kStreamId, true, 4, packet); | |
| stream_->OnStreamFrame(frame); | |
| } | |
| packet = "S"; | |
| { | |
| QuicStreamFrame frame(kStreamId, false, 0, packet); | |
| stream_->OnStreamFrame(frame); | |
| } | |
| packet = "tuf"; | |
| EXPECT_CALL(*session_, ProcessPacketFromPeer("Stuff")); | |
| { | |
| QuicStreamFrame frame(kStreamId, false, 1, packet); | |
| stream_->OnStreamFrame(frame); | |
| } | |
| } | |
| // Test buffering too many bytes. | |
| TEST_F(QboneReadOnlyStreamTest, ReadBufferedTooLarge) { | |
| CreateReliableQuicStream(); | |
| string packet = "0123456789"; | |
| int iterations = (QboneConstants::kMaxQbonePacketBytes / packet.size()) + 2; | |
| EXPECT_CALL(*session_, | |
| SendRstStream(kStreamId, QUIC_BAD_APPLICATION_PAYLOAD, _)); | |
| for (int i = 0; i < iterations; ++i) { | |
| QuicStreamFrame frame(kStreamId, i == (iterations - 1), i * packet.size(), | |
| packet); | |
| if (!stream_->reading_stopped()) { | |
| stream_->OnStreamFrame(frame); | |
| } | |
| } | |
| // We should have nothing written to the network and the stream | |
| // should have stopped reading. | |
| EXPECT_TRUE(stream_->reading_stopped()); | |
| } | |
| } // namespace | |
| } // namespace quic |