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 <memory> | |
10 #include <utility> | |
11 | |
12 #include "base/logging.h" | |
13 #include "base/memory/ref_counted.h" | |
14 #include "base/strings/string_number_conversions.h" | |
15 #include "net/base/request_priority.h" | |
16 #include "net/log/net_log_with_source.h" | |
17 #include "net/spdy/platform/api/spdy_string.h" | |
18 #include "net/spdy/spdy_buffer_producer.h" | |
19 #include "net/spdy/spdy_stream.h" | |
20 #include "testing/gtest/include/gtest/gtest.h" | |
21 #include "url/gurl.h" | |
22 | |
23 namespace net { | |
24 | |
25 namespace { | |
26 | |
27 const char kOriginal[] = "original"; | |
28 const char kRequeued[] = "requeued"; | |
29 | |
30 class SpdyWriteQueueTest : public ::testing::Test {}; | |
31 | |
32 // Makes a SpdyFrameProducer producing a frame with the data in the | |
33 // given string. | |
34 std::unique_ptr<SpdyBufferProducer> StringToProducer(const SpdyString& s) { | |
35 std::unique_ptr<char[]> data(new char[s.size()]); | |
36 std::memcpy(data.get(), s.data(), s.size()); | |
37 return std::unique_ptr<SpdyBufferProducer>( | |
38 new SimpleBufferProducer(std::unique_ptr<SpdyBuffer>( | |
39 new SpdyBuffer(std::unique_ptr<SpdySerializedFrame>( | |
40 new SpdySerializedFrame(data.release(), s.size(), true)))))); | |
41 } | |
42 | |
43 // Makes a SpdyBufferProducer producing a frame with the data in the | |
44 // given int (converted to a string). | |
45 std::unique_ptr<SpdyBufferProducer> IntToProducer(int i) { | |
46 return StringToProducer(base::IntToString(i)); | |
47 } | |
48 | |
49 // Producer whose produced buffer will enqueue yet another buffer into the | |
50 // SpdyWriteQueue upon destruction. | |
51 class RequeingBufferProducer : public SpdyBufferProducer { | |
52 public: | |
53 explicit RequeingBufferProducer(SpdyWriteQueue* queue) { | |
54 buffer_.reset(new SpdyBuffer(kOriginal, arraysize(kOriginal))); | |
55 buffer_->AddConsumeCallback( | |
56 base::Bind(RequeingBufferProducer::ConsumeCallback, queue)); | |
57 } | |
58 | |
59 std::unique_ptr<SpdyBuffer> ProduceBuffer() override { | |
60 return std::move(buffer_); | |
61 } | |
62 | |
63 size_t EstimateMemoryUsage() const override { | |
64 NOTREACHED(); | |
65 return 0; | |
66 } | |
67 | |
68 static void ConsumeCallback(SpdyWriteQueue* queue, | |
69 size_t size, | |
70 SpdyBuffer::ConsumeSource source) { | |
71 std::unique_ptr<SpdyBufferProducer> producer( | |
72 new SimpleBufferProducer(std::unique_ptr<SpdyBuffer>( | |
73 new SpdyBuffer(kRequeued, arraysize(kRequeued))))); | |
74 | |
75 queue->Enqueue(MEDIUM, SpdyFrameType::RST_STREAM, std::move(producer), | |
76 base::WeakPtr<SpdyStream>()); | |
77 } | |
78 | |
79 private: | |
80 std::unique_ptr<SpdyBuffer> buffer_; | |
81 }; | |
82 | |
83 // Produces a frame with the given producer and returns a copy of its | |
84 // data as a string. | |
85 SpdyString ProducerToString(std::unique_ptr<SpdyBufferProducer> producer) { | |
86 std::unique_ptr<SpdyBuffer> buffer = producer->ProduceBuffer(); | |
87 return SpdyString(buffer->GetRemainingData(), buffer->GetRemainingSize()); | |
88 } | |
89 | |
90 // Produces a frame with the given producer and returns a copy of its | |
91 // data as an int (converted from a string). | |
92 int ProducerToInt(std::unique_ptr<SpdyBufferProducer> producer) { | |
93 int i = 0; | |
94 EXPECT_TRUE(base::StringToInt(ProducerToString(std::move(producer)), &i)); | |
95 return i; | |
96 } | |
97 | |
98 // Makes a SpdyStream with the given priority and a NULL SpdySession | |
99 // -- be careful to not call any functions that expect the session to | |
100 // be there. | |
101 SpdyStream* MakeTestStream(RequestPriority priority) { | |
102 return new SpdyStream(SPDY_BIDIRECTIONAL_STREAM, base::WeakPtr<SpdySession>(), | |
103 GURL(), priority, 0, 0, NetLogWithSource()); | |
104 } | |
105 | |
106 // Add some frame producers of different priority. The producers | |
107 // should be dequeued in priority order with their associated stream. | |
108 TEST_F(SpdyWriteQueueTest, DequeuesByPriority) { | |
109 SpdyWriteQueue write_queue; | |
110 | |
111 std::unique_ptr<SpdyBufferProducer> producer_low = StringToProducer("LOW"); | |
112 std::unique_ptr<SpdyBufferProducer> producer_medium = | |
113 StringToProducer("MEDIUM"); | |
114 std::unique_ptr<SpdyBufferProducer> producer_highest = | |
115 StringToProducer("HIGHEST"); | |
116 | |
117 std::unique_ptr<SpdyStream> stream_medium(MakeTestStream(MEDIUM)); | |
118 std::unique_ptr<SpdyStream> stream_highest(MakeTestStream(HIGHEST)); | |
119 | |
120 // A NULL stream should still work. | |
121 write_queue.Enqueue(LOW, SpdyFrameType::HEADERS, std::move(producer_low), | |
122 base::WeakPtr<SpdyStream>()); | |
123 write_queue.Enqueue(MEDIUM, SpdyFrameType::HEADERS, | |
124 std::move(producer_medium), stream_medium->GetWeakPtr()); | |
125 write_queue.Enqueue(HIGHEST, SpdyFrameType::RST_STREAM, | |
126 std::move(producer_highest), | |
127 stream_highest->GetWeakPtr()); | |
128 | |
129 SpdyFrameType frame_type = SpdyFrameType::DATA; | |
130 std::unique_ptr<SpdyBufferProducer> frame_producer; | |
131 base::WeakPtr<SpdyStream> stream; | |
132 ASSERT_TRUE(write_queue.Dequeue(&frame_type, &frame_producer, &stream)); | |
133 EXPECT_EQ(SpdyFrameType::RST_STREAM, frame_type); | |
134 EXPECT_EQ("HIGHEST", ProducerToString(std::move(frame_producer))); | |
135 EXPECT_EQ(stream_highest.get(), stream.get()); | |
136 | |
137 ASSERT_TRUE(write_queue.Dequeue(&frame_type, &frame_producer, &stream)); | |
138 EXPECT_EQ(SpdyFrameType::HEADERS, frame_type); | |
139 EXPECT_EQ("MEDIUM", ProducerToString(std::move(frame_producer))); | |
140 EXPECT_EQ(stream_medium.get(), stream.get()); | |
141 | |
142 ASSERT_TRUE(write_queue.Dequeue(&frame_type, &frame_producer, &stream)); | |
143 EXPECT_EQ(SpdyFrameType::HEADERS, frame_type); | |
144 EXPECT_EQ("LOW", ProducerToString(std::move(frame_producer))); | |
145 EXPECT_EQ(nullptr, stream.get()); | |
146 | |
147 EXPECT_FALSE(write_queue.Dequeue(&frame_type, &frame_producer, &stream)); | |
148 } | |
149 | |
150 // Add some frame producers with the same priority. The producers | |
151 // should be dequeued in FIFO order with their associated stream. | |
152 TEST_F(SpdyWriteQueueTest, DequeuesFIFO) { | |
153 SpdyWriteQueue write_queue; | |
154 | |
155 std::unique_ptr<SpdyBufferProducer> producer1 = IntToProducer(1); | |
156 std::unique_ptr<SpdyBufferProducer> producer2 = IntToProducer(2); | |
157 std::unique_ptr<SpdyBufferProducer> producer3 = IntToProducer(3); | |
158 | |
159 std::unique_ptr<SpdyStream> stream1(MakeTestStream(DEFAULT_PRIORITY)); | |
160 std::unique_ptr<SpdyStream> stream2(MakeTestStream(DEFAULT_PRIORITY)); | |
161 std::unique_ptr<SpdyStream> stream3(MakeTestStream(DEFAULT_PRIORITY)); | |
162 | |
163 write_queue.Enqueue(DEFAULT_PRIORITY, SpdyFrameType::HEADERS, | |
164 std::move(producer1), stream1->GetWeakPtr()); | |
165 write_queue.Enqueue(DEFAULT_PRIORITY, SpdyFrameType::HEADERS, | |
166 std::move(producer2), stream2->GetWeakPtr()); | |
167 write_queue.Enqueue(DEFAULT_PRIORITY, SpdyFrameType::RST_STREAM, | |
168 std::move(producer3), stream3->GetWeakPtr()); | |
169 | |
170 SpdyFrameType frame_type = SpdyFrameType::DATA; | |
171 std::unique_ptr<SpdyBufferProducer> frame_producer; | |
172 base::WeakPtr<SpdyStream> stream; | |
173 ASSERT_TRUE(write_queue.Dequeue(&frame_type, &frame_producer, &stream)); | |
174 EXPECT_EQ(SpdyFrameType::HEADERS, frame_type); | |
175 EXPECT_EQ(1, ProducerToInt(std::move(frame_producer))); | |
176 EXPECT_EQ(stream1.get(), stream.get()); | |
177 | |
178 ASSERT_TRUE(write_queue.Dequeue(&frame_type, &frame_producer, &stream)); | |
179 EXPECT_EQ(SpdyFrameType::HEADERS, frame_type); | |
180 EXPECT_EQ(2, ProducerToInt(std::move(frame_producer))); | |
181 EXPECT_EQ(stream2.get(), stream.get()); | |
182 | |
183 ASSERT_TRUE(write_queue.Dequeue(&frame_type, &frame_producer, &stream)); | |
184 EXPECT_EQ(SpdyFrameType::RST_STREAM, frame_type); | |
185 EXPECT_EQ(3, ProducerToInt(std::move(frame_producer))); | |
186 EXPECT_EQ(stream3.get(), stream.get()); | |
187 | |
188 EXPECT_FALSE(write_queue.Dequeue(&frame_type, &frame_producer, &stream)); | |
189 } | |
190 | |
191 // Enqueue a bunch of writes and then call | |
192 // RemovePendingWritesForStream() on one of the streams. No dequeued | |
193 // write should be for that stream. | |
194 TEST_F(SpdyWriteQueueTest, RemovePendingWritesForStream) { | |
195 SpdyWriteQueue write_queue; | |
196 | |
197 std::unique_ptr<SpdyStream> stream1(MakeTestStream(DEFAULT_PRIORITY)); | |
198 std::unique_ptr<SpdyStream> stream2(MakeTestStream(DEFAULT_PRIORITY)); | |
199 | |
200 for (int i = 0; i < 100; ++i) { | |
201 base::WeakPtr<SpdyStream> stream = | |
202 (((i % 3) == 0) ? stream1 : stream2)->GetWeakPtr(); | |
203 write_queue.Enqueue(DEFAULT_PRIORITY, SpdyFrameType::HEADERS, | |
204 IntToProducer(i), stream); | |
205 } | |
206 | |
207 write_queue.RemovePendingWritesForStream(stream2->GetWeakPtr()); | |
208 | |
209 for (int i = 0; i < 100; i += 3) { | |
210 SpdyFrameType frame_type = SpdyFrameType::DATA; | |
211 std::unique_ptr<SpdyBufferProducer> frame_producer; | |
212 base::WeakPtr<SpdyStream> stream; | |
213 ASSERT_TRUE(write_queue.Dequeue(&frame_type, &frame_producer, &stream)); | |
214 EXPECT_EQ(SpdyFrameType::HEADERS, frame_type); | |
215 EXPECT_EQ(i, ProducerToInt(std::move(frame_producer))); | |
216 EXPECT_EQ(stream1.get(), stream.get()); | |
217 } | |
218 | |
219 SpdyFrameType frame_type = SpdyFrameType::DATA; | |
220 std::unique_ptr<SpdyBufferProducer> frame_producer; | |
221 base::WeakPtr<SpdyStream> stream; | |
222 EXPECT_FALSE(write_queue.Dequeue(&frame_type, &frame_producer, &stream)); | |
223 } | |
224 | |
225 // Enqueue a bunch of writes and then call | |
226 // RemovePendingWritesForStreamsAfter(). No dequeued write should be for | |
227 // those streams without a stream id, or with a stream_id after that | |
228 // argument. | |
229 TEST_F(SpdyWriteQueueTest, RemovePendingWritesForStreamsAfter) { | |
230 SpdyWriteQueue write_queue; | |
231 | |
232 std::unique_ptr<SpdyStream> stream1(MakeTestStream(DEFAULT_PRIORITY)); | |
233 stream1->set_stream_id(1); | |
234 std::unique_ptr<SpdyStream> stream2(MakeTestStream(DEFAULT_PRIORITY)); | |
235 stream2->set_stream_id(3); | |
236 std::unique_ptr<SpdyStream> stream3(MakeTestStream(DEFAULT_PRIORITY)); | |
237 stream3->set_stream_id(5); | |
238 // No stream id assigned. | |
239 std::unique_ptr<SpdyStream> stream4(MakeTestStream(DEFAULT_PRIORITY)); | |
240 base::WeakPtr<SpdyStream> streams[] = { | |
241 stream1->GetWeakPtr(), stream2->GetWeakPtr(), | |
242 stream3->GetWeakPtr(), stream4->GetWeakPtr() | |
243 }; | |
244 | |
245 for (int i = 0; i < 100; ++i) { | |
246 write_queue.Enqueue(DEFAULT_PRIORITY, SpdyFrameType::HEADERS, | |
247 IntToProducer(i), streams[i % arraysize(streams)]); | |
248 } | |
249 | |
250 write_queue.RemovePendingWritesForStreamsAfter(stream1->stream_id()); | |
251 | |
252 for (int i = 0; i < 100; i += arraysize(streams)) { | |
253 SpdyFrameType frame_type = SpdyFrameType::DATA; | |
254 std::unique_ptr<SpdyBufferProducer> frame_producer; | |
255 base::WeakPtr<SpdyStream> stream; | |
256 ASSERT_TRUE(write_queue.Dequeue(&frame_type, &frame_producer, &stream)) | |
257 << "Unable to Dequeue i: " << i; | |
258 EXPECT_EQ(SpdyFrameType::HEADERS, frame_type); | |
259 EXPECT_EQ(i, ProducerToInt(std::move(frame_producer))); | |
260 EXPECT_EQ(stream1.get(), stream.get()); | |
261 } | |
262 | |
263 SpdyFrameType frame_type = SpdyFrameType::DATA; | |
264 std::unique_ptr<SpdyBufferProducer> frame_producer; | |
265 base::WeakPtr<SpdyStream> stream; | |
266 EXPECT_FALSE(write_queue.Dequeue(&frame_type, &frame_producer, &stream)); | |
267 } | |
268 | |
269 // Enqueue a bunch of writes and then call Clear(). The write queue | |
270 // should clean up the memory properly, and Dequeue() should return | |
271 // false. | |
272 TEST_F(SpdyWriteQueueTest, Clear) { | |
273 SpdyWriteQueue write_queue; | |
274 | |
275 for (int i = 0; i < 100; ++i) { | |
276 write_queue.Enqueue(DEFAULT_PRIORITY, SpdyFrameType::HEADERS, | |
277 IntToProducer(i), base::WeakPtr<SpdyStream>()); | |
278 } | |
279 | |
280 write_queue.Clear(); | |
281 | |
282 SpdyFrameType frame_type = SpdyFrameType::DATA; | |
283 std::unique_ptr<SpdyBufferProducer> frame_producer; | |
284 base::WeakPtr<SpdyStream> stream; | |
285 EXPECT_FALSE(write_queue.Dequeue(&frame_type, &frame_producer, &stream)); | |
286 } | |
287 | |
288 TEST_F(SpdyWriteQueueTest, RequeingProducerWithoutReentrance) { | |
289 SpdyWriteQueue queue; | |
290 queue.Enqueue( | |
291 DEFAULT_PRIORITY, SpdyFrameType::HEADERS, | |
292 std::unique_ptr<SpdyBufferProducer>(new RequeingBufferProducer(&queue)), | |
293 base::WeakPtr<SpdyStream>()); | |
294 { | |
295 SpdyFrameType frame_type; | |
296 std::unique_ptr<SpdyBufferProducer> producer; | |
297 base::WeakPtr<SpdyStream> stream; | |
298 | |
299 EXPECT_TRUE(queue.Dequeue(&frame_type, &producer, &stream)); | |
300 EXPECT_TRUE(queue.IsEmpty()); | |
301 EXPECT_EQ(SpdyString(kOriginal), | |
302 producer->ProduceBuffer()->GetRemainingData()); | |
303 } | |
304 // |producer| was destroyed, and a buffer is re-queued. | |
305 EXPECT_FALSE(queue.IsEmpty()); | |
306 | |
307 SpdyFrameType frame_type; | |
308 std::unique_ptr<SpdyBufferProducer> producer; | |
309 base::WeakPtr<SpdyStream> stream; | |
310 | |
311 EXPECT_TRUE(queue.Dequeue(&frame_type, &producer, &stream)); | |
312 EXPECT_EQ(SpdyString(kRequeued), | |
313 producer->ProduceBuffer()->GetRemainingData()); | |
314 } | |
315 | |
316 TEST_F(SpdyWriteQueueTest, ReentranceOnClear) { | |
317 SpdyWriteQueue queue; | |
318 queue.Enqueue( | |
319 DEFAULT_PRIORITY, SpdyFrameType::HEADERS, | |
320 std::unique_ptr<SpdyBufferProducer>(new RequeingBufferProducer(&queue)), | |
321 base::WeakPtr<SpdyStream>()); | |
322 | |
323 queue.Clear(); | |
324 EXPECT_FALSE(queue.IsEmpty()); | |
325 | |
326 SpdyFrameType frame_type; | |
327 std::unique_ptr<SpdyBufferProducer> producer; | |
328 base::WeakPtr<SpdyStream> stream; | |
329 | |
330 EXPECT_TRUE(queue.Dequeue(&frame_type, &producer, &stream)); | |
331 EXPECT_EQ(SpdyString(kRequeued), | |
332 producer->ProduceBuffer()->GetRemainingData()); | |
333 } | |
334 | |
335 TEST_F(SpdyWriteQueueTest, ReentranceOnRemovePendingWritesAfter) { | |
336 std::unique_ptr<SpdyStream> stream(MakeTestStream(DEFAULT_PRIORITY)); | |
337 stream->set_stream_id(2); | |
338 | |
339 SpdyWriteQueue queue; | |
340 queue.Enqueue( | |
341 DEFAULT_PRIORITY, SpdyFrameType::HEADERS, | |
342 std::unique_ptr<SpdyBufferProducer>(new RequeingBufferProducer(&queue)), | |
343 stream->GetWeakPtr()); | |
344 | |
345 queue.RemovePendingWritesForStreamsAfter(1); | |
346 EXPECT_FALSE(queue.IsEmpty()); | |
347 | |
348 SpdyFrameType frame_type; | |
349 std::unique_ptr<SpdyBufferProducer> producer; | |
350 base::WeakPtr<SpdyStream> weak_stream; | |
351 | |
352 EXPECT_TRUE(queue.Dequeue(&frame_type, &producer, &weak_stream)); | |
353 EXPECT_EQ(SpdyString(kRequeued), | |
354 producer->ProduceBuffer()->GetRemainingData()); | |
355 } | |
356 | |
357 TEST_F(SpdyWriteQueueTest, ReentranceOnRemovePendingWritesForStream) { | |
358 std::unique_ptr<SpdyStream> stream(MakeTestStream(DEFAULT_PRIORITY)); | |
359 stream->set_stream_id(2); | |
360 | |
361 SpdyWriteQueue queue; | |
362 queue.Enqueue( | |
363 DEFAULT_PRIORITY, SpdyFrameType::HEADERS, | |
364 std::unique_ptr<SpdyBufferProducer>(new RequeingBufferProducer(&queue)), | |
365 stream->GetWeakPtr()); | |
366 | |
367 queue.RemovePendingWritesForStream(stream->GetWeakPtr()); | |
368 EXPECT_FALSE(queue.IsEmpty()); | |
369 | |
370 SpdyFrameType frame_type; | |
371 std::unique_ptr<SpdyBufferProducer> producer; | |
372 base::WeakPtr<SpdyStream> weak_stream; | |
373 | |
374 EXPECT_TRUE(queue.Dequeue(&frame_type, &producer, &weak_stream)); | |
375 EXPECT_EQ(SpdyString(kRequeued), | |
376 producer->ProduceBuffer()->GetRemainingData()); | |
377 } | |
378 | |
379 } // namespace | |
380 | |
381 } // namespace net | |
OLD | NEW |