/* * Copyright 2016 The WebRTC Project Authors. All rights reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "webrtc/p2p/quic/reliablequicstream.h" #include #include #include "net/base/ip_address_number.h" #include "net/quic/quic_connection.h" #include "net/quic/quic_protocol.h" #include "net/quic/quic_session.h" #include "webrtc/base/buffer.h" #include "webrtc/base/gunit.h" #include "webrtc/base/sigslot.h" #include "webrtc/base/stream.h" #include "webrtc/p2p/quic/quicconnectionhelper.h" using cricket::QuicConnectionHelper; using cricket::ReliableQuicStream; using net::IPAddress; using net::IPEndPoint; using net::PerPacketOptions; using net::Perspective; using net::QuicAckListenerInterface; using net::QuicConfig; using net::QuicConnection; using net::QuicConsumedData; using net::QuicCryptoStream; using net::QuicErrorCode; using net::QuicIOVector; using net::QuicPacketWriter; using net::QuicRstStreamErrorCode; using net::QuicSession; using net::QuicStreamId; using net::QuicStreamOffset; using net::SpdyPriority; using rtc::SR_SUCCESS; using rtc::SR_BLOCK; // Arbitrary number for a stream's write blocked priority. static const SpdyPriority kDefaultPriority = 3; // QuicSession that does not create streams and writes data from // ReliableQuicStream to a string. class MockQuicSession : public QuicSession { public: MockQuicSession(QuicConnection* connection, const QuicConfig& config, std::string* write_buffer) : QuicSession(connection, config), write_buffer_(write_buffer) {} // Writes outgoing data from ReliableQuicStream to a string. QuicConsumedData WritevData( QuicStreamId id, QuicIOVector iovector, QuicStreamOffset offset, bool fin, QuicAckListenerInterface* ack_notifier_delegate) override { if (!writable_) { return QuicConsumedData(0, false); } const char* data = reinterpret_cast(iovector.iov->iov_base); size_t len = iovector.total_length; write_buffer_->append(data, len); return QuicConsumedData(len, false); } net::ReliableQuicStream* CreateIncomingDynamicStream( QuicStreamId id) override { return nullptr; } net::ReliableQuicStream* CreateOutgoingDynamicStream( SpdyPriority priority) override { return nullptr; } QuicCryptoStream* GetCryptoStream() override { return nullptr; } // Called by ReliableQuicStream when they want to close stream. void SendRstStream(QuicStreamId id, QuicRstStreamErrorCode error, QuicStreamOffset bytes_written) 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 register_write_blocked_stream(QuicStreamId stream_id, SpdyPriority priority) { write_blocked_streams()->RegisterStream(stream_id, priority); } private: // Stores written data from ReliableQuicStream. std::string* write_buffer_; // 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. net::WriteResult WritePacket(const char* buffer, size_t buf_len, const IPAddress& self_address, const IPEndPoint& peer_address, PerPacketOptions* options) override { return net::WriteResult(net::WRITE_STATUS_ERROR, 0); } bool IsWriteBlockedDataBuffered() const override { return false; } bool IsWriteBlocked() const override { return false; }; void SetWritable() override {} net::QuicByteCount GetMaxPacketSize( const net::IPEndPoint& peer_address) const override { return 0; } }; class ReliableQuicStreamTest : public ::testing::Test, public sigslot::has_slots<> { public: ReliableQuicStreamTest() {} void CreateReliableQuicStream() { const net::QuicStreamId kStreamId = 5; // Arbitrary values for QuicConnection. QuicConnectionHelper* quic_helper = new QuicConnectionHelper(rtc::Thread::Current()); Perspective perspective = Perspective::IS_SERVER; net::IPAddress ip(0, 0, 0, 0); bool owns_writer = true; QuicConnection* connection = new QuicConnection( 0, IPEndPoint(ip, 0), quic_helper, new DummyPacketWriter(), owns_writer, perspective, net::QuicSupportedVersions()); session_.reset( new MockQuicSession(connection, QuicConfig(), &write_buffer_)); stream_.reset(new ReliableQuicStream(kStreamId, session_.get())); stream_->SignalDataReceived.connect( this, &ReliableQuicStreamTest::OnDataReceived); stream_->SignalClosed.connect(this, &ReliableQuicStreamTest::OnClosed); stream_->SignalQueuedBytesWritten.connect( this, &ReliableQuicStreamTest::OnQueuedBytesWritten); session_->register_write_blocked_stream(stream_->id(), kDefaultPriority); } void OnDataReceived(QuicStreamId id, const char* data, size_t length) { ASSERT_EQ(id, stream_->id()); read_buffer_.append(data, length); } void OnClosed(QuicStreamId id, int err) { closed_ = true; } void OnQueuedBytesWritten(QuicStreamId id, uint64_t queued_bytes_written) { queued_bytes_written_ = queued_bytes_written; } protected: std::unique_ptr stream_; std::unique_ptr session_; // Data written by the ReliableQuicStream. std::string write_buffer_; // Data read by the ReliableQuicStream. std::string read_buffer_; // Whether the ReliableQuicStream is closed. bool closed_ = false; // Bytes written by OnCanWrite(). uint64_t queued_bytes_written_; }; // Write an entire string. TEST_F(ReliableQuicStreamTest, WriteDataWhole) { CreateReliableQuicStream(); EXPECT_EQ(SR_SUCCESS, stream_->Write("Foo bar", 7)); EXPECT_EQ("Foo bar", write_buffer_); } // Write part of a string. TEST_F(ReliableQuicStreamTest, WriteDataPartial) { CreateReliableQuicStream(); EXPECT_EQ(SR_SUCCESS, stream_->Write("Hello, World!", 8)); EXPECT_EQ("Hello, W", write_buffer_); } // Test that strings are buffered correctly. TEST_F(ReliableQuicStreamTest, BufferData) { CreateReliableQuicStream(); session_->set_writable(false); EXPECT_EQ(SR_BLOCK, stream_->Write("Foo bar", 7)); EXPECT_EQ(0ul, write_buffer_.size()); EXPECT_TRUE(stream_->HasBufferedData()); session_->set_writable(true); stream_->OnCanWrite(); EXPECT_EQ(7ul, queued_bytes_written_); EXPECT_FALSE(stream_->HasBufferedData()); EXPECT_EQ("Foo bar", write_buffer_); EXPECT_EQ(SR_SUCCESS, stream_->Write("xyzzy", 5)); EXPECT_EQ("Foo barxyzzy", write_buffer_); } // Read an entire string. TEST_F(ReliableQuicStreamTest, ReadDataWhole) { CreateReliableQuicStream(); net::QuicStreamFrame frame(-1, false, 0, "Hello, World!"); stream_->OnStreamFrame(frame); EXPECT_EQ("Hello, World!", read_buffer_); } // Read part of a string. TEST_F(ReliableQuicStreamTest, ReadDataPartial) { CreateReliableQuicStream(); net::QuicStreamFrame frame(-1, false, 0, "Hello, World!"); frame.frame_length = 5; stream_->OnStreamFrame(frame); EXPECT_EQ("Hello", read_buffer_); } // Test that closing the stream results in a callback. TEST_F(ReliableQuicStreamTest, CloseStream) { CreateReliableQuicStream(); EXPECT_FALSE(closed_); stream_->OnClose(); EXPECT_TRUE(closed_); }