1 // Copyright (c) 2013 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.
5 #include "net/spdy/spdy_write_queue.h"
11 #include "base/basictypes.h"
12 #include "base/memory/ref_counted.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "net/base/request_priority.h"
16 #include "net/log/net_log.h"
17 #include "net/spdy/spdy_buffer_producer.h"
18 #include "net/spdy/spdy_stream.h"
19 #include "testing/gtest/include/gtest/gtest.h"
28 const char kOriginal
[] = "original";
29 const char kRequeued
[] = "requeued";
31 class SpdyWriteQueueTest
: public ::testing::Test
{};
33 // Makes a SpdyFrameProducer producing a frame with the data in the
35 scoped_ptr
<SpdyBufferProducer
> StringToProducer(const std::string
& s
) {
36 scoped_ptr
<char[]> data(new char[s
.size()]);
37 std::memcpy(data
.get(), s
.data(), s
.size());
38 return scoped_ptr
<SpdyBufferProducer
>(
39 new SimpleBufferProducer(
40 scoped_ptr
<SpdyBuffer
>(
42 scoped_ptr
<SpdyFrame
>(
43 new SpdyFrame(data
.release(), s
.size(), true))))));
46 // Makes a SpdyBufferProducer producing a frame with the data in the
47 // given int (converted to a string).
48 scoped_ptr
<SpdyBufferProducer
> IntToProducer(int i
) {
49 return StringToProducer(base::IntToString(i
));
52 // Producer whose produced buffer will enqueue yet another buffer into the
53 // SpdyWriteQueue upon destruction.
54 class RequeingBufferProducer
: public SpdyBufferProducer
{
56 RequeingBufferProducer(SpdyWriteQueue
* queue
) {
57 buffer_
.reset(new SpdyBuffer(kOriginal
, arraysize(kOriginal
)));
58 buffer_
->AddConsumeCallback(
59 base::Bind(RequeingBufferProducer::ConsumeCallback
, queue
));
62 scoped_ptr
<SpdyBuffer
> ProduceBuffer() override
{ return buffer_
.Pass(); }
64 static void ConsumeCallback(SpdyWriteQueue
* queue
,
66 SpdyBuffer::ConsumeSource source
) {
67 scoped_ptr
<SpdyBufferProducer
> producer(
68 new SimpleBufferProducer(scoped_ptr
<SpdyBuffer
>(
69 new SpdyBuffer(kRequeued
, arraysize(kRequeued
)))));
72 MEDIUM
, RST_STREAM
, producer
.Pass(), base::WeakPtr
<SpdyStream
>());
76 scoped_ptr
<SpdyBuffer
> buffer_
;
79 // Produces a frame with the given producer and returns a copy of its
81 std::string
ProducerToString(scoped_ptr
<SpdyBufferProducer
> producer
) {
82 scoped_ptr
<SpdyBuffer
> buffer
= producer
->ProduceBuffer();
83 return std::string(buffer
->GetRemainingData(), buffer
->GetRemainingSize());
86 // Produces a frame with the given producer and returns a copy of its
87 // data as an int (converted from a string).
88 int ProducerToInt(scoped_ptr
<SpdyBufferProducer
> producer
) {
90 EXPECT_TRUE(base::StringToInt(ProducerToString(producer
.Pass()), &i
));
94 // Makes a SpdyStream with the given priority and a NULL SpdySession
95 // -- be careful to not call any functions that expect the session to
97 SpdyStream
* MakeTestStream(RequestPriority priority
) {
98 return new SpdyStream(
99 SPDY_BIDIRECTIONAL_STREAM
, base::WeakPtr
<SpdySession
>(),
100 GURL(), priority
, 0, 0, BoundNetLog());
103 // Add some frame producers of different priority. The producers
104 // should be dequeued in priority order with their associated stream.
105 TEST_F(SpdyWriteQueueTest
, DequeuesByPriority
) {
106 SpdyWriteQueue write_queue
;
108 scoped_ptr
<SpdyBufferProducer
> producer_low
= StringToProducer("LOW");
109 scoped_ptr
<SpdyBufferProducer
> producer_medium
= StringToProducer("MEDIUM");
110 scoped_ptr
<SpdyBufferProducer
> producer_highest
= StringToProducer("HIGHEST");
112 scoped_ptr
<SpdyStream
> stream_medium(MakeTestStream(MEDIUM
));
113 scoped_ptr
<SpdyStream
> stream_highest(MakeTestStream(HIGHEST
));
115 // A NULL stream should still work.
117 LOW
, SYN_STREAM
, producer_low
.Pass(), base::WeakPtr
<SpdyStream
>());
119 MEDIUM
, SYN_REPLY
, producer_medium
.Pass(), stream_medium
->GetWeakPtr());
121 HIGHEST
, RST_STREAM
, producer_highest
.Pass(),
122 stream_highest
->GetWeakPtr());
124 SpdyFrameType frame_type
= DATA
;
125 scoped_ptr
<SpdyBufferProducer
> frame_producer
;
126 base::WeakPtr
<SpdyStream
> stream
;
127 ASSERT_TRUE(write_queue
.Dequeue(&frame_type
, &frame_producer
, &stream
));
128 EXPECT_EQ(RST_STREAM
, frame_type
);
129 EXPECT_EQ("HIGHEST", ProducerToString(frame_producer
.Pass()));
130 EXPECT_EQ(stream_highest
, stream
.get());
132 ASSERT_TRUE(write_queue
.Dequeue(&frame_type
, &frame_producer
, &stream
));
133 EXPECT_EQ(SYN_REPLY
, frame_type
);
134 EXPECT_EQ("MEDIUM", ProducerToString(frame_producer
.Pass()));
135 EXPECT_EQ(stream_medium
, stream
.get());
137 ASSERT_TRUE(write_queue
.Dequeue(&frame_type
, &frame_producer
, &stream
));
138 EXPECT_EQ(SYN_STREAM
, frame_type
);
139 EXPECT_EQ("LOW", ProducerToString(frame_producer
.Pass()));
140 EXPECT_EQ(NULL
, stream
.get());
142 EXPECT_FALSE(write_queue
.Dequeue(&frame_type
, &frame_producer
, &stream
));
145 // Add some frame producers with the same priority. The producers
146 // should be dequeued in FIFO order with their associated stream.
147 TEST_F(SpdyWriteQueueTest
, DequeuesFIFO
) {
148 SpdyWriteQueue write_queue
;
150 scoped_ptr
<SpdyBufferProducer
> producer1
= IntToProducer(1);
151 scoped_ptr
<SpdyBufferProducer
> producer2
= IntToProducer(2);
152 scoped_ptr
<SpdyBufferProducer
> producer3
= IntToProducer(3);
154 scoped_ptr
<SpdyStream
> stream1(MakeTestStream(DEFAULT_PRIORITY
));
155 scoped_ptr
<SpdyStream
> stream2(MakeTestStream(DEFAULT_PRIORITY
));
156 scoped_ptr
<SpdyStream
> stream3(MakeTestStream(DEFAULT_PRIORITY
));
158 write_queue
.Enqueue(DEFAULT_PRIORITY
, SYN_STREAM
, producer1
.Pass(),
159 stream1
->GetWeakPtr());
160 write_queue
.Enqueue(DEFAULT_PRIORITY
, SYN_REPLY
, producer2
.Pass(),
161 stream2
->GetWeakPtr());
162 write_queue
.Enqueue(DEFAULT_PRIORITY
, RST_STREAM
, producer3
.Pass(),
163 stream3
->GetWeakPtr());
165 SpdyFrameType frame_type
= DATA
;
166 scoped_ptr
<SpdyBufferProducer
> frame_producer
;
167 base::WeakPtr
<SpdyStream
> stream
;
168 ASSERT_TRUE(write_queue
.Dequeue(&frame_type
, &frame_producer
, &stream
));
169 EXPECT_EQ(SYN_STREAM
, frame_type
);
170 EXPECT_EQ(1, ProducerToInt(frame_producer
.Pass()));
171 EXPECT_EQ(stream1
, stream
.get());
173 ASSERT_TRUE(write_queue
.Dequeue(&frame_type
, &frame_producer
, &stream
));
174 EXPECT_EQ(SYN_REPLY
, frame_type
);
175 EXPECT_EQ(2, ProducerToInt(frame_producer
.Pass()));
176 EXPECT_EQ(stream2
, stream
.get());
178 ASSERT_TRUE(write_queue
.Dequeue(&frame_type
, &frame_producer
, &stream
));
179 EXPECT_EQ(RST_STREAM
, frame_type
);
180 EXPECT_EQ(3, ProducerToInt(frame_producer
.Pass()));
181 EXPECT_EQ(stream3
, stream
.get());
183 EXPECT_FALSE(write_queue
.Dequeue(&frame_type
, &frame_producer
, &stream
));
186 // Enqueue a bunch of writes and then call
187 // RemovePendingWritesForStream() on one of the streams. No dequeued
188 // write should be for that stream.
189 TEST_F(SpdyWriteQueueTest
, RemovePendingWritesForStream
) {
190 SpdyWriteQueue write_queue
;
192 scoped_ptr
<SpdyStream
> stream1(MakeTestStream(DEFAULT_PRIORITY
));
193 scoped_ptr
<SpdyStream
> stream2(MakeTestStream(DEFAULT_PRIORITY
));
195 for (int i
= 0; i
< 100; ++i
) {
196 base::WeakPtr
<SpdyStream
> stream
=
197 (((i
% 3) == 0) ? stream1
: stream2
)->GetWeakPtr();
198 write_queue
.Enqueue(DEFAULT_PRIORITY
, SYN_STREAM
, IntToProducer(i
), stream
);
201 write_queue
.RemovePendingWritesForStream(stream2
->GetWeakPtr());
203 for (int i
= 0; i
< 100; i
+= 3) {
204 SpdyFrameType frame_type
= DATA
;
205 scoped_ptr
<SpdyBufferProducer
> frame_producer
;
206 base::WeakPtr
<SpdyStream
> stream
;
207 ASSERT_TRUE(write_queue
.Dequeue(&frame_type
, &frame_producer
, &stream
));
208 EXPECT_EQ(SYN_STREAM
, frame_type
);
209 EXPECT_EQ(i
, ProducerToInt(frame_producer
.Pass()));
210 EXPECT_EQ(stream1
, stream
.get());
213 SpdyFrameType frame_type
= DATA
;
214 scoped_ptr
<SpdyBufferProducer
> frame_producer
;
215 base::WeakPtr
<SpdyStream
> stream
;
216 EXPECT_FALSE(write_queue
.Dequeue(&frame_type
, &frame_producer
, &stream
));
219 // Enqueue a bunch of writes and then call
220 // RemovePendingWritesForStreamsAfter(). No dequeued write should be for
221 // those streams without a stream id, or with a stream_id after that
223 TEST_F(SpdyWriteQueueTest
, RemovePendingWritesForStreamsAfter
) {
224 SpdyWriteQueue write_queue
;
226 scoped_ptr
<SpdyStream
> stream1(MakeTestStream(DEFAULT_PRIORITY
));
227 stream1
->set_stream_id(1);
228 scoped_ptr
<SpdyStream
> stream2(MakeTestStream(DEFAULT_PRIORITY
));
229 stream2
->set_stream_id(3);
230 scoped_ptr
<SpdyStream
> stream3(MakeTestStream(DEFAULT_PRIORITY
));
231 stream3
->set_stream_id(5);
232 // No stream id assigned.
233 scoped_ptr
<SpdyStream
> stream4(MakeTestStream(DEFAULT_PRIORITY
));
234 base::WeakPtr
<SpdyStream
> streams
[] = {
235 stream1
->GetWeakPtr(), stream2
->GetWeakPtr(),
236 stream3
->GetWeakPtr(), stream4
->GetWeakPtr()
239 for (int i
= 0; i
< 100; ++i
) {
240 write_queue
.Enqueue(DEFAULT_PRIORITY
, SYN_STREAM
, IntToProducer(i
),
241 streams
[i
% arraysize(streams
)]);
244 write_queue
.RemovePendingWritesForStreamsAfter(stream1
->stream_id());
246 for (int i
= 0; i
< 100; i
+= arraysize(streams
)) {
247 SpdyFrameType frame_type
= DATA
;
248 scoped_ptr
<SpdyBufferProducer
> frame_producer
;
249 base::WeakPtr
<SpdyStream
> stream
;
250 ASSERT_TRUE(write_queue
.Dequeue(&frame_type
, &frame_producer
, &stream
))
251 << "Unable to Dequeue i: " << i
;
252 EXPECT_EQ(SYN_STREAM
, frame_type
);
253 EXPECT_EQ(i
, ProducerToInt(frame_producer
.Pass()));
254 EXPECT_EQ(stream1
, stream
.get());
257 SpdyFrameType frame_type
= DATA
;
258 scoped_ptr
<SpdyBufferProducer
> frame_producer
;
259 base::WeakPtr
<SpdyStream
> stream
;
260 EXPECT_FALSE(write_queue
.Dequeue(&frame_type
, &frame_producer
, &stream
));
263 // Enqueue a bunch of writes and then call Clear(). The write queue
264 // should clean up the memory properly, and Dequeue() should return
266 TEST_F(SpdyWriteQueueTest
, Clear
) {
267 SpdyWriteQueue write_queue
;
269 for (int i
= 0; i
< 100; ++i
) {
270 write_queue
.Enqueue(DEFAULT_PRIORITY
, SYN_STREAM
, IntToProducer(i
),
271 base::WeakPtr
<SpdyStream
>());
276 SpdyFrameType frame_type
= DATA
;
277 scoped_ptr
<SpdyBufferProducer
> frame_producer
;
278 base::WeakPtr
<SpdyStream
> stream
;
279 EXPECT_FALSE(write_queue
.Dequeue(&frame_type
, &frame_producer
, &stream
));
282 TEST_F(SpdyWriteQueueTest
, RequeingProducerWithoutReentrance
) {
283 SpdyWriteQueue queue
;
287 scoped_ptr
<SpdyBufferProducer
>(new RequeingBufferProducer(&queue
)),
288 base::WeakPtr
<SpdyStream
>());
290 SpdyFrameType frame_type
;
291 scoped_ptr
<SpdyBufferProducer
> producer
;
292 base::WeakPtr
<SpdyStream
> stream
;
294 EXPECT_TRUE(queue
.Dequeue(&frame_type
, &producer
, &stream
));
295 EXPECT_TRUE(queue
.IsEmpty());
296 EXPECT_EQ(string(kOriginal
), producer
->ProduceBuffer()->GetRemainingData());
298 // |producer| was destroyed, and a buffer is re-queued.
299 EXPECT_FALSE(queue
.IsEmpty());
301 SpdyFrameType frame_type
;
302 scoped_ptr
<SpdyBufferProducer
> producer
;
303 base::WeakPtr
<SpdyStream
> stream
;
305 EXPECT_TRUE(queue
.Dequeue(&frame_type
, &producer
, &stream
));
306 EXPECT_EQ(string(kRequeued
), producer
->ProduceBuffer()->GetRemainingData());
309 TEST_F(SpdyWriteQueueTest
, ReentranceOnClear
) {
310 SpdyWriteQueue queue
;
314 scoped_ptr
<SpdyBufferProducer
>(new RequeingBufferProducer(&queue
)),
315 base::WeakPtr
<SpdyStream
>());
318 EXPECT_FALSE(queue
.IsEmpty());
320 SpdyFrameType frame_type
;
321 scoped_ptr
<SpdyBufferProducer
> producer
;
322 base::WeakPtr
<SpdyStream
> stream
;
324 EXPECT_TRUE(queue
.Dequeue(&frame_type
, &producer
, &stream
));
325 EXPECT_EQ(string(kRequeued
), producer
->ProduceBuffer()->GetRemainingData());
328 TEST_F(SpdyWriteQueueTest
, ReentranceOnRemovePendingWritesAfter
) {
329 scoped_ptr
<SpdyStream
> stream(MakeTestStream(DEFAULT_PRIORITY
));
330 stream
->set_stream_id(2);
332 SpdyWriteQueue queue
;
336 scoped_ptr
<SpdyBufferProducer
>(new RequeingBufferProducer(&queue
)),
337 stream
->GetWeakPtr());
339 queue
.RemovePendingWritesForStreamsAfter(1);
340 EXPECT_FALSE(queue
.IsEmpty());
342 SpdyFrameType frame_type
;
343 scoped_ptr
<SpdyBufferProducer
> producer
;
344 base::WeakPtr
<SpdyStream
> weak_stream
;
346 EXPECT_TRUE(queue
.Dequeue(&frame_type
, &producer
, &weak_stream
));
347 EXPECT_EQ(string(kRequeued
), producer
->ProduceBuffer()->GetRemainingData());
350 TEST_F(SpdyWriteQueueTest
, ReentranceOnRemovePendingWritesForStream
) {
351 scoped_ptr
<SpdyStream
> stream(MakeTestStream(DEFAULT_PRIORITY
));
352 stream
->set_stream_id(2);
354 SpdyWriteQueue queue
;
358 scoped_ptr
<SpdyBufferProducer
>(new RequeingBufferProducer(&queue
)),
359 stream
->GetWeakPtr());
361 queue
.RemovePendingWritesForStream(stream
->GetWeakPtr());
362 EXPECT_FALSE(queue
.IsEmpty());
364 SpdyFrameType frame_type
;
365 scoped_ptr
<SpdyBufferProducer
> producer
;
366 base::WeakPtr
<SpdyStream
> weak_stream
;
368 EXPECT_TRUE(queue
.Dequeue(&frame_type
, &producer
, &weak_stream
));
369 EXPECT_EQ(string(kRequeued
), producer
->ProduceBuffer()->GetRemainingData());