OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "net/spdy/spdy_write_queue.h" | |
6 | |
7 #include <cstddef> | |
8 #include <cstring> | |
9 #include <string> | |
10 | |
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/net_log.h" | |
16 #include "net/base/request_priority.h" | |
17 #include "net/spdy/spdy_buffer_producer.h" | |
18 #include "net/spdy/spdy_stream.h" | |
19 #include "testing/gtest/include/gtest/gtest.h" | |
20 #include "url/gurl.h" | |
21 | |
22 namespace net { | |
23 | |
24 namespace { | |
25 | |
26 using std::string; | |
27 | |
28 const char kOriginal[] = "original"; | |
29 const char kRequeued[] = "requeued"; | |
30 | |
31 class SpdyWriteQueueTest : public ::testing::Test {}; | |
32 | |
33 // Makes a SpdyFrameProducer producing a frame with the data in the | |
34 // given string. | |
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>( | |
41 new SpdyBuffer( | |
42 scoped_ptr<SpdyFrame>( | |
43 new SpdyFrame(data.release(), s.size(), true)))))); | |
44 } | |
45 | |
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)); | |
50 } | |
51 | |
52 // Producer whose produced buffer will enqueue yet another buffer into the | |
53 // SpdyWriteQueue upon destruction. | |
54 class RequeingBufferProducer : public SpdyBufferProducer { | |
55 public: | |
56 RequeingBufferProducer(SpdyWriteQueue* queue) { | |
57 buffer_.reset(new SpdyBuffer(kOriginal, arraysize(kOriginal))); | |
58 buffer_->AddConsumeCallback( | |
59 base::Bind(RequeingBufferProducer::ConsumeCallback, queue)); | |
60 } | |
61 | |
62 scoped_ptr<SpdyBuffer> ProduceBuffer() override { return buffer_.Pass(); } | |
63 | |
64 static void ConsumeCallback(SpdyWriteQueue* queue, | |
65 size_t size, | |
66 SpdyBuffer::ConsumeSource source) { | |
67 scoped_ptr<SpdyBufferProducer> producer( | |
68 new SimpleBufferProducer(scoped_ptr<SpdyBuffer>( | |
69 new SpdyBuffer(kRequeued, arraysize(kRequeued))))); | |
70 | |
71 queue->Enqueue( | |
72 MEDIUM, RST_STREAM, producer.Pass(), base::WeakPtr<SpdyStream>()); | |
73 } | |
74 | |
75 private: | |
76 scoped_ptr<SpdyBuffer> buffer_; | |
77 }; | |
78 | |
79 // Produces a frame with the given producer and returns a copy of its | |
80 // data as a string. | |
81 std::string ProducerToString(scoped_ptr<SpdyBufferProducer> producer) { | |
82 scoped_ptr<SpdyBuffer> buffer = producer->ProduceBuffer(); | |
83 return std::string(buffer->GetRemainingData(), buffer->GetRemainingSize()); | |
84 } | |
85 | |
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) { | |
89 int i = 0; | |
90 EXPECT_TRUE(base::StringToInt(ProducerToString(producer.Pass()), &i)); | |
91 return i; | |
92 } | |
93 | |
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 | |
96 // be there. | |
97 SpdyStream* MakeTestStream(RequestPriority priority) { | |
98 return new SpdyStream( | |
99 SPDY_BIDIRECTIONAL_STREAM, base::WeakPtr<SpdySession>(), | |
100 GURL(), priority, 0, 0, BoundNetLog()); | |
101 } | |
102 | |
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; | |
107 | |
108 scoped_ptr<SpdyBufferProducer> producer_low = StringToProducer("LOW"); | |
109 scoped_ptr<SpdyBufferProducer> producer_medium = StringToProducer("MEDIUM"); | |
110 scoped_ptr<SpdyBufferProducer> producer_highest = StringToProducer("HIGHEST"); | |
111 | |
112 scoped_ptr<SpdyStream> stream_medium(MakeTestStream(MEDIUM)); | |
113 scoped_ptr<SpdyStream> stream_highest(MakeTestStream(HIGHEST)); | |
114 | |
115 // A NULL stream should still work. | |
116 write_queue.Enqueue( | |
117 LOW, SYN_STREAM, producer_low.Pass(), base::WeakPtr<SpdyStream>()); | |
118 write_queue.Enqueue( | |
119 MEDIUM, SYN_REPLY, producer_medium.Pass(), stream_medium->GetWeakPtr()); | |
120 write_queue.Enqueue( | |
121 HIGHEST, RST_STREAM, producer_highest.Pass(), | |
122 stream_highest->GetWeakPtr()); | |
123 | |
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()); | |
131 | |
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()); | |
136 | |
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()); | |
141 | |
142 EXPECT_FALSE(write_queue.Dequeue(&frame_type, &frame_producer, &stream)); | |
143 } | |
144 | |
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; | |
149 | |
150 scoped_ptr<SpdyBufferProducer> producer1 = IntToProducer(1); | |
151 scoped_ptr<SpdyBufferProducer> producer2 = IntToProducer(2); | |
152 scoped_ptr<SpdyBufferProducer> producer3 = IntToProducer(3); | |
153 | |
154 scoped_ptr<SpdyStream> stream1(MakeTestStream(DEFAULT_PRIORITY)); | |
155 scoped_ptr<SpdyStream> stream2(MakeTestStream(DEFAULT_PRIORITY)); | |
156 scoped_ptr<SpdyStream> stream3(MakeTestStream(DEFAULT_PRIORITY)); | |
157 | |
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()); | |
164 | |
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()); | |
172 | |
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()); | |
177 | |
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()); | |
182 | |
183 EXPECT_FALSE(write_queue.Dequeue(&frame_type, &frame_producer, &stream)); | |
184 } | |
185 | |
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; | |
191 | |
192 scoped_ptr<SpdyStream> stream1(MakeTestStream(DEFAULT_PRIORITY)); | |
193 scoped_ptr<SpdyStream> stream2(MakeTestStream(DEFAULT_PRIORITY)); | |
194 | |
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); | |
199 } | |
200 | |
201 write_queue.RemovePendingWritesForStream(stream2->GetWeakPtr()); | |
202 | |
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()); | |
211 } | |
212 | |
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)); | |
217 } | |
218 | |
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 | |
222 // argument. | |
223 TEST_F(SpdyWriteQueueTest, RemovePendingWritesForStreamsAfter) { | |
224 SpdyWriteQueue write_queue; | |
225 | |
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() | |
237 }; | |
238 | |
239 for (int i = 0; i < 100; ++i) { | |
240 write_queue.Enqueue(DEFAULT_PRIORITY, SYN_STREAM, IntToProducer(i), | |
241 streams[i % arraysize(streams)]); | |
242 } | |
243 | |
244 write_queue.RemovePendingWritesForStreamsAfter(stream1->stream_id()); | |
245 | |
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()); | |
255 } | |
256 | |
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)); | |
261 } | |
262 | |
263 // Enqueue a bunch of writes and then call Clear(). The write queue | |
264 // should clean up the memory properly, and Dequeue() should return | |
265 // false. | |
266 TEST_F(SpdyWriteQueueTest, Clear) { | |
267 SpdyWriteQueue write_queue; | |
268 | |
269 for (int i = 0; i < 100; ++i) { | |
270 write_queue.Enqueue(DEFAULT_PRIORITY, SYN_STREAM, IntToProducer(i), | |
271 base::WeakPtr<SpdyStream>()); | |
272 } | |
273 | |
274 write_queue.Clear(); | |
275 | |
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)); | |
280 } | |
281 | |
282 TEST_F(SpdyWriteQueueTest, RequeingProducerWithoutReentrance) { | |
283 SpdyWriteQueue queue; | |
284 queue.Enqueue( | |
285 DEFAULT_PRIORITY, | |
286 SYN_STREAM, | |
287 scoped_ptr<SpdyBufferProducer>(new RequeingBufferProducer(&queue)), | |
288 base::WeakPtr<SpdyStream>()); | |
289 { | |
290 SpdyFrameType frame_type; | |
291 scoped_ptr<SpdyBufferProducer> producer; | |
292 base::WeakPtr<SpdyStream> stream; | |
293 | |
294 EXPECT_TRUE(queue.Dequeue(&frame_type, &producer, &stream)); | |
295 EXPECT_TRUE(queue.IsEmpty()); | |
296 EXPECT_EQ(string(kOriginal), producer->ProduceBuffer()->GetRemainingData()); | |
297 } | |
298 // |producer| was destroyed, and a buffer is re-queued. | |
299 EXPECT_FALSE(queue.IsEmpty()); | |
300 | |
301 SpdyFrameType frame_type; | |
302 scoped_ptr<SpdyBufferProducer> producer; | |
303 base::WeakPtr<SpdyStream> stream; | |
304 | |
305 EXPECT_TRUE(queue.Dequeue(&frame_type, &producer, &stream)); | |
306 EXPECT_EQ(string(kRequeued), producer->ProduceBuffer()->GetRemainingData()); | |
307 } | |
308 | |
309 TEST_F(SpdyWriteQueueTest, ReentranceOnClear) { | |
310 SpdyWriteQueue queue; | |
311 queue.Enqueue( | |
312 DEFAULT_PRIORITY, | |
313 SYN_STREAM, | |
314 scoped_ptr<SpdyBufferProducer>(new RequeingBufferProducer(&queue)), | |
315 base::WeakPtr<SpdyStream>()); | |
316 | |
317 queue.Clear(); | |
318 EXPECT_FALSE(queue.IsEmpty()); | |
319 | |
320 SpdyFrameType frame_type; | |
321 scoped_ptr<SpdyBufferProducer> producer; | |
322 base::WeakPtr<SpdyStream> stream; | |
323 | |
324 EXPECT_TRUE(queue.Dequeue(&frame_type, &producer, &stream)); | |
325 EXPECT_EQ(string(kRequeued), producer->ProduceBuffer()->GetRemainingData()); | |
326 } | |
327 | |
328 TEST_F(SpdyWriteQueueTest, ReentranceOnRemovePendingWritesAfter) { | |
329 scoped_ptr<SpdyStream> stream(MakeTestStream(DEFAULT_PRIORITY)); | |
330 stream->set_stream_id(2); | |
331 | |
332 SpdyWriteQueue queue; | |
333 queue.Enqueue( | |
334 DEFAULT_PRIORITY, | |
335 SYN_STREAM, | |
336 scoped_ptr<SpdyBufferProducer>(new RequeingBufferProducer(&queue)), | |
337 stream->GetWeakPtr()); | |
338 | |
339 queue.RemovePendingWritesForStreamsAfter(1); | |
340 EXPECT_FALSE(queue.IsEmpty()); | |
341 | |
342 SpdyFrameType frame_type; | |
343 scoped_ptr<SpdyBufferProducer> producer; | |
344 base::WeakPtr<SpdyStream> weak_stream; | |
345 | |
346 EXPECT_TRUE(queue.Dequeue(&frame_type, &producer, &weak_stream)); | |
347 EXPECT_EQ(string(kRequeued), producer->ProduceBuffer()->GetRemainingData()); | |
348 } | |
349 | |
350 TEST_F(SpdyWriteQueueTest, ReentranceOnRemovePendingWritesForStream) { | |
351 scoped_ptr<SpdyStream> stream(MakeTestStream(DEFAULT_PRIORITY)); | |
352 stream->set_stream_id(2); | |
353 | |
354 SpdyWriteQueue queue; | |
355 queue.Enqueue( | |
356 DEFAULT_PRIORITY, | |
357 SYN_STREAM, | |
358 scoped_ptr<SpdyBufferProducer>(new RequeingBufferProducer(&queue)), | |
359 stream->GetWeakPtr()); | |
360 | |
361 queue.RemovePendingWritesForStream(stream->GetWeakPtr()); | |
362 EXPECT_FALSE(queue.IsEmpty()); | |
363 | |
364 SpdyFrameType frame_type; | |
365 scoped_ptr<SpdyBufferProducer> producer; | |
366 base::WeakPtr<SpdyStream> weak_stream; | |
367 | |
368 EXPECT_TRUE(queue.Dequeue(&frame_type, &producer, &weak_stream)); | |
369 EXPECT_EQ(string(kRequeued), producer->ProduceBuffer()->GetRemainingData()); | |
370 } | |
371 | |
372 } // namespace | |
373 | |
374 } // namespace net | |
OLD | NEW |