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 class SpdyWriteQueueTest : public ::testing::Test {}; 27 28 // Makes a SpdyFrameProducer producing a frame with the data in the 29 // given string. 30 scoped_ptr<SpdyBufferProducer> StringToProducer(const std::string& s) { 31 scoped_ptr<char[]> data(new char[s.size()]); 32 std::memcpy(data.get(), s.data(), s.size()); 33 return scoped_ptr<SpdyBufferProducer>( 34 new SimpleBufferProducer( 35 scoped_ptr<SpdyBuffer>( 36 new SpdyBuffer( 37 scoped_ptr<SpdyFrame>( 38 new SpdyFrame(data.release(), s.size(), true)))))); 39 } 40 41 // Makes a SpdyBufferProducer producing a frame with the data in the 42 // given int (converted to a string). 43 scoped_ptr<SpdyBufferProducer> IntToProducer(int i) { 44 return StringToProducer(base::IntToString(i)); 45 } 46 47 // Produces a frame with the given producer and returns a copy of its 48 // data as a string. 49 std::string ProducerToString(scoped_ptr<SpdyBufferProducer> producer) { 50 scoped_ptr<SpdyBuffer> buffer = producer->ProduceBuffer(); 51 return std::string(buffer->GetRemainingData(), buffer->GetRemainingSize()); 52 } 53 54 // Produces a frame with the given producer and returns a copy of its 55 // data as an int (converted from a string). 56 int ProducerToInt(scoped_ptr<SpdyBufferProducer> producer) { 57 int i = 0; 58 EXPECT_TRUE(base::StringToInt(ProducerToString(producer.Pass()), &i)); 59 return i; 60 } 61 62 // Makes a SpdyStream with the given priority and a NULL SpdySession 63 // -- be careful to not call any functions that expect the session to 64 // be there. 65 SpdyStream* MakeTestStream(RequestPriority priority) { 66 return new SpdyStream( 67 SPDY_BIDIRECTIONAL_STREAM, base::WeakPtr<SpdySession>(), 68 GURL(), priority, 0, 0, BoundNetLog()); 69 } 70 71 // Add some frame producers of different priority. The producers 72 // should be dequeued in priority order with their associated stream. 73 TEST_F(SpdyWriteQueueTest, DequeuesByPriority) { 74 SpdyWriteQueue write_queue; 75 76 scoped_ptr<SpdyBufferProducer> producer_low = StringToProducer("LOW"); 77 scoped_ptr<SpdyBufferProducer> producer_medium = StringToProducer("MEDIUM"); 78 scoped_ptr<SpdyBufferProducer> producer_highest = StringToProducer("HIGHEST"); 79 80 scoped_ptr<SpdyStream> stream_medium(MakeTestStream(MEDIUM)); 81 scoped_ptr<SpdyStream> stream_highest(MakeTestStream(HIGHEST)); 82 83 // A NULL stream should still work. 84 write_queue.Enqueue( 85 LOW, SYN_STREAM, producer_low.Pass(), base::WeakPtr<SpdyStream>()); 86 write_queue.Enqueue( 87 MEDIUM, SYN_REPLY, producer_medium.Pass(), stream_medium->GetWeakPtr()); 88 write_queue.Enqueue( 89 HIGHEST, RST_STREAM, producer_highest.Pass(), 90 stream_highest->GetWeakPtr()); 91 92 SpdyFrameType frame_type = DATA; 93 scoped_ptr<SpdyBufferProducer> frame_producer; 94 base::WeakPtr<SpdyStream> stream; 95 ASSERT_TRUE(write_queue.Dequeue(&frame_type, &frame_producer, &stream)); 96 EXPECT_EQ(RST_STREAM, frame_type); 97 EXPECT_EQ("HIGHEST", ProducerToString(frame_producer.Pass())); 98 EXPECT_EQ(stream_highest, stream.get()); 99 100 ASSERT_TRUE(write_queue.Dequeue(&frame_type, &frame_producer, &stream)); 101 EXPECT_EQ(SYN_REPLY, frame_type); 102 EXPECT_EQ("MEDIUM", ProducerToString(frame_producer.Pass())); 103 EXPECT_EQ(stream_medium, stream.get()); 104 105 ASSERT_TRUE(write_queue.Dequeue(&frame_type, &frame_producer, &stream)); 106 EXPECT_EQ(SYN_STREAM, frame_type); 107 EXPECT_EQ("LOW", ProducerToString(frame_producer.Pass())); 108 EXPECT_EQ(NULL, stream.get()); 109 110 EXPECT_FALSE(write_queue.Dequeue(&frame_type, &frame_producer, &stream)); 111 } 112 113 // Add some frame producers with the same priority. The producers 114 // should be dequeued in FIFO order with their associated stream. 115 TEST_F(SpdyWriteQueueTest, DequeuesFIFO) { 116 SpdyWriteQueue write_queue; 117 118 scoped_ptr<SpdyBufferProducer> producer1 = IntToProducer(1); 119 scoped_ptr<SpdyBufferProducer> producer2 = IntToProducer(2); 120 scoped_ptr<SpdyBufferProducer> producer3 = IntToProducer(3); 121 122 scoped_ptr<SpdyStream> stream1(MakeTestStream(DEFAULT_PRIORITY)); 123 scoped_ptr<SpdyStream> stream2(MakeTestStream(DEFAULT_PRIORITY)); 124 scoped_ptr<SpdyStream> stream3(MakeTestStream(DEFAULT_PRIORITY)); 125 126 write_queue.Enqueue(DEFAULT_PRIORITY, SYN_STREAM, producer1.Pass(), 127 stream1->GetWeakPtr()); 128 write_queue.Enqueue(DEFAULT_PRIORITY, SYN_REPLY, producer2.Pass(), 129 stream2->GetWeakPtr()); 130 write_queue.Enqueue(DEFAULT_PRIORITY, RST_STREAM, producer3.Pass(), 131 stream3->GetWeakPtr()); 132 133 SpdyFrameType frame_type = DATA; 134 scoped_ptr<SpdyBufferProducer> frame_producer; 135 base::WeakPtr<SpdyStream> stream; 136 ASSERT_TRUE(write_queue.Dequeue(&frame_type, &frame_producer, &stream)); 137 EXPECT_EQ(SYN_STREAM, frame_type); 138 EXPECT_EQ(1, ProducerToInt(frame_producer.Pass())); 139 EXPECT_EQ(stream1, stream.get()); 140 141 ASSERT_TRUE(write_queue.Dequeue(&frame_type, &frame_producer, &stream)); 142 EXPECT_EQ(SYN_REPLY, frame_type); 143 EXPECT_EQ(2, ProducerToInt(frame_producer.Pass())); 144 EXPECT_EQ(stream2, stream.get()); 145 146 ASSERT_TRUE(write_queue.Dequeue(&frame_type, &frame_producer, &stream)); 147 EXPECT_EQ(RST_STREAM, frame_type); 148 EXPECT_EQ(3, ProducerToInt(frame_producer.Pass())); 149 EXPECT_EQ(stream3, stream.get()); 150 151 EXPECT_FALSE(write_queue.Dequeue(&frame_type, &frame_producer, &stream)); 152 } 153 154 // Enqueue a bunch of writes and then call 155 // RemovePendingWritesForStream() on one of the streams. No dequeued 156 // write should be for that stream. 157 TEST_F(SpdyWriteQueueTest, RemovePendingWritesForStream) { 158 SpdyWriteQueue write_queue; 159 160 scoped_ptr<SpdyStream> stream1(MakeTestStream(DEFAULT_PRIORITY)); 161 scoped_ptr<SpdyStream> stream2(MakeTestStream(DEFAULT_PRIORITY)); 162 163 for (int i = 0; i < 100; ++i) { 164 base::WeakPtr<SpdyStream> stream = 165 (((i % 3) == 0) ? stream1 : stream2)->GetWeakPtr(); 166 write_queue.Enqueue(DEFAULT_PRIORITY, SYN_STREAM, IntToProducer(i), stream); 167 } 168 169 write_queue.RemovePendingWritesForStream(stream2->GetWeakPtr()); 170 171 for (int i = 0; i < 100; i += 3) { 172 SpdyFrameType frame_type = DATA; 173 scoped_ptr<SpdyBufferProducer> frame_producer; 174 base::WeakPtr<SpdyStream> stream; 175 ASSERT_TRUE(write_queue.Dequeue(&frame_type, &frame_producer, &stream)); 176 EXPECT_EQ(SYN_STREAM, frame_type); 177 EXPECT_EQ(i, ProducerToInt(frame_producer.Pass())); 178 EXPECT_EQ(stream1, stream.get()); 179 } 180 181 SpdyFrameType frame_type = DATA; 182 scoped_ptr<SpdyBufferProducer> frame_producer; 183 base::WeakPtr<SpdyStream> stream; 184 EXPECT_FALSE(write_queue.Dequeue(&frame_type, &frame_producer, &stream)); 185 } 186 187 // Enqueue a bunch of writes and then call 188 // RemovePendingWritesForStreamsAfter(). No dequeued write should be for 189 // those streams without a stream id, or with a stream_id after that 190 // argument. 191 TEST_F(SpdyWriteQueueTest, RemovePendingWritesForStreamsAfter) { 192 SpdyWriteQueue write_queue; 193 194 scoped_ptr<SpdyStream> stream1(MakeTestStream(DEFAULT_PRIORITY)); 195 stream1->set_stream_id(1); 196 scoped_ptr<SpdyStream> stream2(MakeTestStream(DEFAULT_PRIORITY)); 197 stream2->set_stream_id(3); 198 scoped_ptr<SpdyStream> stream3(MakeTestStream(DEFAULT_PRIORITY)); 199 stream3->set_stream_id(5); 200 // No stream id assigned. 201 scoped_ptr<SpdyStream> stream4(MakeTestStream(DEFAULT_PRIORITY)); 202 base::WeakPtr<SpdyStream> streams[] = { 203 stream1->GetWeakPtr(), stream2->GetWeakPtr(), 204 stream3->GetWeakPtr(), stream4->GetWeakPtr() 205 }; 206 207 for (int i = 0; i < 100; ++i) { 208 write_queue.Enqueue(DEFAULT_PRIORITY, SYN_STREAM, IntToProducer(i), 209 streams[i % arraysize(streams)]); 210 } 211 212 write_queue.RemovePendingWritesForStreamsAfter(stream1->stream_id()); 213 214 for (int i = 0; i < 100; i += arraysize(streams)) { 215 SpdyFrameType frame_type = DATA; 216 scoped_ptr<SpdyBufferProducer> frame_producer; 217 base::WeakPtr<SpdyStream> stream; 218 ASSERT_TRUE(write_queue.Dequeue(&frame_type, &frame_producer, &stream)) 219 << "Unable to Dequeue i: " << i; 220 EXPECT_EQ(SYN_STREAM, frame_type); 221 EXPECT_EQ(i, ProducerToInt(frame_producer.Pass())); 222 EXPECT_EQ(stream1, stream.get()); 223 } 224 225 SpdyFrameType frame_type = DATA; 226 scoped_ptr<SpdyBufferProducer> frame_producer; 227 base::WeakPtr<SpdyStream> stream; 228 EXPECT_FALSE(write_queue.Dequeue(&frame_type, &frame_producer, &stream)); 229 } 230 231 // Enqueue a bunch of writes and then call Clear(). The write queue 232 // should clean up the memory properly, and Dequeue() should return 233 // false. 234 TEST_F(SpdyWriteQueueTest, Clear) { 235 SpdyWriteQueue write_queue; 236 237 for (int i = 0; i < 100; ++i) { 238 write_queue.Enqueue(DEFAULT_PRIORITY, SYN_STREAM, IntToProducer(i), 239 base::WeakPtr<SpdyStream>()); 240 } 241 242 write_queue.Clear(); 243 244 SpdyFrameType frame_type = DATA; 245 scoped_ptr<SpdyBufferProducer> frame_producer; 246 base::WeakPtr<SpdyStream> stream; 247 EXPECT_FALSE(write_queue.Dequeue(&frame_type, &frame_producer, &stream)); 248 } 249 250 } // namespace 251 252 } // namespace net 253