Home | History | Annotate | Download | only in spdy
      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