1 // Copyright (c) 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "quiche/quic/qbone/qbone_stream.h"
6
7 #include <utility>
8
9 #include "absl/strings/string_view.h"
10 #include "quiche/quic/core/crypto/quic_random.h"
11 #include "quiche/quic/core/quic_session.h"
12 #include "quiche/quic/core/quic_stream_priority.h"
13 #include "quiche/quic/core/quic_utils.h"
14 #include "quiche/quic/platform/api/quic_test.h"
15 #include "quiche/quic/platform/api/quic_test_loopback.h"
16 #include "quiche/quic/qbone/qbone_constants.h"
17 #include "quiche/quic/qbone/qbone_session_base.h"
18 #include "quiche/quic/test_tools/mock_clock.h"
19 #include "quiche/quic/test_tools/mock_connection_id_generator.h"
20 #include "quiche/quic/test_tools/quic_test_utils.h"
21 #include "quiche/common/simple_buffer_allocator.h"
22
23 namespace quic {
24
25 namespace {
26
27 using ::testing::_;
28 using ::testing::StrictMock;
29
30 // MockQuicSession that does not create streams and writes data from
31 // QuicStream to a string.
32 class MockQuicSession : public QboneSessionBase {
33 public:
MockQuicSession(QuicConnection * connection,const QuicConfig & config)34 MockQuicSession(QuicConnection* connection, const QuicConfig& config)
35 : QboneSessionBase(connection, nullptr /*visitor*/, config,
36 CurrentSupportedVersions(), nullptr /*writer*/) {}
37
~MockQuicSession()38 ~MockQuicSession() override {}
39
40 // Writes outgoing data from QuicStream to a string.
WritevData(QuicStreamId id,size_t write_length,QuicStreamOffset offset,StreamSendingState state,TransmissionType type,EncryptionLevel level)41 QuicConsumedData WritevData(QuicStreamId id, size_t write_length,
42 QuicStreamOffset offset, StreamSendingState state,
43 TransmissionType type,
44 EncryptionLevel level) override {
45 if (!writable_) {
46 return QuicConsumedData(0, false);
47 }
48
49 return QuicConsumedData(write_length, state != StreamSendingState::NO_FIN);
50 }
51
CreateIncomingStream(QuicStreamId id)52 QboneReadOnlyStream* CreateIncomingStream(QuicStreamId id) override {
53 return nullptr;
54 }
55
56 // Called by QuicStream when they want to close stream.
57 MOCK_METHOD(void, MaybeSendRstStreamFrame,
58 (QuicStreamId stream_id, QuicResetStreamError error,
59 QuicStreamOffset bytes_written),
60 (override));
61 MOCK_METHOD(void, MaybeSendStopSendingFrame,
62 (QuicStreamId stream_id, QuicResetStreamError error), (override));
63
64 // Sets whether data is written to buffer, or else if this is write blocked.
set_writable(bool writable)65 void set_writable(bool writable) { writable_ = writable; }
66
67 // Tracks whether the stream is write blocked and its priority.
RegisterReliableStream(QuicStreamId stream_id)68 void RegisterReliableStream(QuicStreamId stream_id) {
69 // The priority effectively does not matter. Put all streams on the same
70 // priority.
71 write_blocked_streams()->RegisterStream(
72 stream_id,
73 /* is_static_stream = */ false,
74 QuicStreamPriority::Default(priority_type()));
75 }
76
77 // The session take ownership of the stream.
ActivateReliableStream(std::unique_ptr<QuicStream> stream)78 void ActivateReliableStream(std::unique_ptr<QuicStream> stream) {
79 ActivateStream(std::move(stream));
80 }
81
CreateCryptoStream()82 std::unique_ptr<QuicCryptoStream> CreateCryptoStream() override {
83 return std::make_unique<test::MockQuicCryptoStream>(this);
84 }
85
86 MOCK_METHOD(void, ProcessPacketFromPeer, (absl::string_view), (override));
87 MOCK_METHOD(void, ProcessPacketFromNetwork, (absl::string_view), (override));
88
89 private:
90 // Whether data is written to write_buffer_.
91 bool writable_ = true;
92 };
93
94 // Packet writer that does nothing. This is required for QuicConnection but
95 // isn't used for writing data.
96 class DummyPacketWriter : public QuicPacketWriter {
97 public:
DummyPacketWriter()98 DummyPacketWriter() {}
99
100 // QuicPacketWriter overrides.
WritePacket(const char * buffer,size_t buf_len,const QuicIpAddress & self_address,const QuicSocketAddress & peer_address,PerPacketOptions * options,const QuicPacketWriterParams & params)101 WriteResult WritePacket(const char* buffer, size_t buf_len,
102 const QuicIpAddress& self_address,
103 const QuicSocketAddress& peer_address,
104 PerPacketOptions* options,
105 const QuicPacketWriterParams& params) override {
106 return WriteResult(WRITE_STATUS_ERROR, 0);
107 }
108
IsWriteBlocked() const109 bool IsWriteBlocked() const override { return false; };
110
SetWritable()111 void SetWritable() override {}
112
MessageTooBigErrorCode() const113 std::optional<int> MessageTooBigErrorCode() const override {
114 return std::nullopt;
115 }
116
GetMaxPacketSize(const QuicSocketAddress & peer_address) const117 QuicByteCount GetMaxPacketSize(
118 const QuicSocketAddress& peer_address) const override {
119 return 0;
120 }
121
SupportsReleaseTime() const122 bool SupportsReleaseTime() const override { return false; }
123
IsBatchMode() const124 bool IsBatchMode() const override { return false; }
125
SupportsEcn() const126 bool SupportsEcn() const override { return false; }
127
GetNextWriteLocation(const QuicIpAddress & self_address,const QuicSocketAddress & peer_address)128 QuicPacketBuffer GetNextWriteLocation(
129 const QuicIpAddress& self_address,
130 const QuicSocketAddress& peer_address) override {
131 return {nullptr, nullptr};
132 }
133
Flush()134 WriteResult Flush() override { return WriteResult(WRITE_STATUS_OK, 0); }
135 };
136
137 class QboneReadOnlyStreamTest : public ::testing::Test,
138 public QuicConnectionHelperInterface {
139 public:
CreateReliableQuicStream()140 void CreateReliableQuicStream() {
141 // Arbitrary values for QuicConnection.
142 Perspective perspective = Perspective::IS_SERVER;
143 bool owns_writer = true;
144
145 alarm_factory_ = std::make_unique<test::MockAlarmFactory>();
146
147 connection_.reset(new QuicConnection(
148 test::TestConnectionId(0), QuicSocketAddress(TestLoopback(), 0),
149 QuicSocketAddress(TestLoopback(), 0),
150 this /*QuicConnectionHelperInterface*/, alarm_factory_.get(),
151 new DummyPacketWriter(), owns_writer, perspective,
152 ParsedVersionOfIndex(CurrentSupportedVersions(), 0),
153 connection_id_generator_));
154 clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1));
155 session_ = std::make_unique<StrictMock<MockQuicSession>>(connection_.get(),
156 QuicConfig());
157 session_->Initialize();
158 stream_ = new QboneReadOnlyStream(kStreamId, session_.get());
159 session_->ActivateReliableStream(
160 std::unique_ptr<QboneReadOnlyStream>(stream_));
161 }
162
~QboneReadOnlyStreamTest()163 ~QboneReadOnlyStreamTest() override {}
164
GetClock() const165 const QuicClock* GetClock() const override { return &clock_; }
166
GetRandomGenerator()167 QuicRandom* GetRandomGenerator() override {
168 return QuicRandom::GetInstance();
169 }
170
GetStreamSendBufferAllocator()171 quiche::QuicheBufferAllocator* GetStreamSendBufferAllocator() override {
172 return &buffer_allocator_;
173 }
174
175 protected:
176 // The QuicSession will take the ownership.
177 QboneReadOnlyStream* stream_;
178 std::unique_ptr<StrictMock<MockQuicSession>> session_;
179 std::unique_ptr<QuicAlarmFactory> alarm_factory_;
180 std::unique_ptr<QuicConnection> connection_;
181 // Used to implement the QuicConnectionHelperInterface.
182 quiche::SimpleBufferAllocator buffer_allocator_;
183 MockClock clock_;
184 const QuicStreamId kStreamId = QuicUtils::GetFirstUnidirectionalStreamId(
185 CurrentSupportedVersions()[0].transport_version, Perspective::IS_CLIENT);
186 quic::test::MockConnectionIdGenerator connection_id_generator_;
187 };
188
189 // Read an entire string.
TEST_F(QboneReadOnlyStreamTest,ReadDataWhole)190 TEST_F(QboneReadOnlyStreamTest, ReadDataWhole) {
191 std::string packet = "Stuff";
192 CreateReliableQuicStream();
193 QuicStreamFrame frame(kStreamId, true, 0, packet);
194 EXPECT_CALL(*session_, ProcessPacketFromPeer("Stuff"));
195 stream_->OnStreamFrame(frame);
196 }
197
198 // Test buffering.
TEST_F(QboneReadOnlyStreamTest,ReadBuffered)199 TEST_F(QboneReadOnlyStreamTest, ReadBuffered) {
200 CreateReliableQuicStream();
201 std::string packet = "Stuf";
202 {
203 QuicStreamFrame frame(kStreamId, false, 0, packet);
204 stream_->OnStreamFrame(frame);
205 }
206 // We didn't write 5 bytes yet...
207
208 packet = "f";
209 EXPECT_CALL(*session_, ProcessPacketFromPeer("Stuff"));
210 {
211 QuicStreamFrame frame(kStreamId, true, 4, packet);
212 stream_->OnStreamFrame(frame);
213 }
214 }
215
TEST_F(QboneReadOnlyStreamTest,ReadOutOfOrder)216 TEST_F(QboneReadOnlyStreamTest, ReadOutOfOrder) {
217 CreateReliableQuicStream();
218 std::string packet = "f";
219 {
220 QuicStreamFrame frame(kStreamId, true, 4, packet);
221 stream_->OnStreamFrame(frame);
222 }
223
224 packet = "S";
225 {
226 QuicStreamFrame frame(kStreamId, false, 0, packet);
227 stream_->OnStreamFrame(frame);
228 }
229
230 packet = "tuf";
231 EXPECT_CALL(*session_, ProcessPacketFromPeer("Stuff"));
232 {
233 QuicStreamFrame frame(kStreamId, false, 1, packet);
234 stream_->OnStreamFrame(frame);
235 }
236 }
237
238 // Test buffering too many bytes.
TEST_F(QboneReadOnlyStreamTest,ReadBufferedTooLarge)239 TEST_F(QboneReadOnlyStreamTest, ReadBufferedTooLarge) {
240 CreateReliableQuicStream();
241 std::string packet = "0123456789";
242 int iterations = (QboneConstants::kMaxQbonePacketBytes / packet.size()) + 2;
243 EXPECT_CALL(*session_, MaybeSendStopSendingFrame(
244 kStreamId, QuicResetStreamError::FromInternal(
245 QUIC_BAD_APPLICATION_PAYLOAD)));
246 EXPECT_CALL(
247 *session_,
248 MaybeSendRstStreamFrame(
249 kStreamId,
250 QuicResetStreamError::FromInternal(QUIC_BAD_APPLICATION_PAYLOAD), _));
251 for (int i = 0; i < iterations; ++i) {
252 QuicStreamFrame frame(kStreamId, i == (iterations - 1), i * packet.size(),
253 packet);
254 if (!stream_->reading_stopped()) {
255 stream_->OnStreamFrame(frame);
256 }
257 }
258 // We should have nothing written to the network and the stream
259 // should have stopped reading.
260 EXPECT_TRUE(stream_->reading_stopped());
261 }
262
263 } // namespace
264
265 } // namespace quic
266