|  | // 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 "quic/qbone/qbone_stream.h" | 
|  |  | 
|  | #include <utility> | 
|  |  | 
|  | #include "absl/strings/string_view.h" | 
|  | #include "quic/core/crypto/quic_random.h" | 
|  | #include "quic/core/quic_session.h" | 
|  | #include "quic/core/quic_simple_buffer_allocator.h" | 
|  | #include "quic/core/quic_utils.h" | 
|  | #include "quic/platform/api/quic_test.h" | 
|  | #include "quic/platform/api/quic_test_loopback.h" | 
|  | #include "quic/qbone/qbone_constants.h" | 
|  | #include "quic/qbone/qbone_session_base.h" | 
|  | #include "quic/test_tools/mock_clock.h" | 
|  | #include "quic/test_tools/quic_test_utils.h" | 
|  | #include "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(QuicStreamId id, | 
|  | size_t write_length, | 
|  | QuicStreamOffset offset, | 
|  | StreamSendingState state, | 
|  | TransmissionType type, | 
|  | absl::optional<EncryptionLevel> level) override { | 
|  | if (!writable_) { | 
|  | return QuicConsumedData(0, false); | 
|  | } | 
|  |  | 
|  | return QuicConsumedData(write_length, state != StreamSendingState::NO_FIN); | 
|  | } | 
|  |  | 
|  | QboneReadOnlyStream* CreateIncomingStream(QuicStreamId id) override { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | // Called by QuicStream when they want to close stream. | 
|  | MOCK_METHOD(void, | 
|  | MaybeSendRstStreamFrame, | 
|  | (QuicStreamId stream_id, | 
|  | QuicRstStreamErrorCode error, | 
|  | QuicStreamOffset bytes_written), | 
|  | (override)); | 
|  | MOCK_METHOD(void, | 
|  | MaybeSendStopSendingFrame, | 
|  | (QuicStreamId stream_id, QuicRstStreamErrorCode error), | 
|  | (override)); | 
|  |  | 
|  | // 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 std::make_unique<test::MockQuicCryptoStream>(this); | 
|  | } | 
|  |  | 
|  | MOCK_METHOD(void, ProcessPacketFromPeer, (absl::string_view), (override)); | 
|  | MOCK_METHOD(void, ProcessPacketFromNetwork, (absl::string_view), (override)); | 
|  |  | 
|  | 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; } | 
|  |  | 
|  | QuicPacketBuffer GetNextWriteLocation( | 
|  | const QuicIpAddress& self_address, | 
|  | const QuicSocketAddress& peer_address) override { | 
|  | return {nullptr, 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), | 
|  | 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()); | 
|  | session_->Initialize(); | 
|  | 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) { | 
|  | std::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(); | 
|  | std::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(); | 
|  | std::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(); | 
|  | std::string packet = "0123456789"; | 
|  | int iterations = (QboneConstants::kMaxQbonePacketBytes / packet.size()) + 2; | 
|  | EXPECT_CALL(*session_, MaybeSendStopSendingFrame( | 
|  | kStreamId, QUIC_BAD_APPLICATION_PAYLOAD)); | 
|  | EXPECT_CALL(*session_, MaybeSendRstStreamFrame( | 
|  | 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 |