Home | History | Annotate | Download | only in spdy
      1 // Copyright (c) 2012 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/buffered_spdy_framer.h"
      6 
      7 #include "net/spdy/spdy_test_util_common.h"
      8 #include "testing/platform_test.h"
      9 
     10 namespace net {
     11 
     12 namespace {
     13 
     14 class TestBufferedSpdyVisitor : public BufferedSpdyFramerVisitorInterface {
     15  public:
     16   explicit TestBufferedSpdyVisitor(SpdyMajorVersion spdy_version)
     17       : buffered_spdy_framer_(spdy_version, true),
     18         error_count_(0),
     19         setting_count_(0),
     20         syn_frame_count_(0),
     21         syn_reply_frame_count_(0),
     22         headers_frame_count_(0),
     23         header_stream_id_(-1) {
     24   }
     25 
     26   virtual void OnError(SpdyFramer::SpdyError error_code) OVERRIDE {
     27     LOG(INFO) << "SpdyFramer Error: " << error_code;
     28     error_count_++;
     29   }
     30 
     31   virtual void OnStreamError(
     32       SpdyStreamId stream_id,
     33       const std::string& description) OVERRIDE {
     34     LOG(INFO) << "SpdyFramer Error on stream: " << stream_id  << " "
     35               << description;
     36     error_count_++;
     37   }
     38 
     39   virtual void OnSynStream(SpdyStreamId stream_id,
     40                            SpdyStreamId associated_stream_id,
     41                            SpdyPriority priority,
     42                            uint8 credential_slot,
     43                            bool fin,
     44                            bool unidirectional,
     45                            const SpdyHeaderBlock& headers) OVERRIDE {
     46     header_stream_id_ = stream_id;
     47     EXPECT_NE(header_stream_id_, SpdyFramer::kInvalidStream);
     48     syn_frame_count_++;
     49     headers_ = headers;
     50   }
     51 
     52   virtual void OnSynReply(SpdyStreamId stream_id,
     53                           bool fin,
     54                           const SpdyHeaderBlock& headers) OVERRIDE {
     55     header_stream_id_ = stream_id;
     56     EXPECT_NE(header_stream_id_, SpdyFramer::kInvalidStream);
     57     syn_reply_frame_count_++;
     58     headers_ = headers;
     59   }
     60 
     61   virtual void OnHeaders(SpdyStreamId stream_id,
     62                          bool fin,
     63                          const SpdyHeaderBlock& headers) OVERRIDE {
     64     header_stream_id_ = stream_id;
     65     EXPECT_NE(header_stream_id_, SpdyFramer::kInvalidStream);
     66     headers_frame_count_++;
     67     headers_ = headers;
     68   }
     69 
     70   virtual void OnStreamFrameData(SpdyStreamId stream_id,
     71                                  const char* data,
     72                                  size_t len,
     73                                  bool fin) OVERRIDE {
     74     LOG(FATAL) << "Unexpected OnStreamFrameData call.";
     75   }
     76 
     77   virtual void OnSettings(bool clear_persisted) OVERRIDE {}
     78 
     79   virtual void OnSetting(SpdySettingsIds id,
     80                          uint8 flags,
     81                          uint32 value) OVERRIDE {
     82     setting_count_++;
     83   }
     84 
     85   virtual void OnPing(uint32 unique_id) OVERRIDE {}
     86 
     87   virtual void OnRstStream(SpdyStreamId stream_id,
     88                            SpdyRstStreamStatus status) OVERRIDE {
     89   }
     90 
     91   virtual void OnGoAway(SpdyStreamId last_accepted_stream_id,
     92                         SpdyGoAwayStatus status) OVERRIDE {
     93   }
     94 
     95   bool OnCredentialFrameData(const char*, size_t) {
     96     LOG(FATAL) << "Unexpected OnCredentialFrameData call.";
     97     return false;
     98   }
     99 
    100   void OnDataFrameHeader(const SpdyFrame* frame) {
    101     LOG(FATAL) << "Unexpected OnDataFrameHeader call.";
    102   }
    103 
    104   void OnRstStream(const SpdyFrame& frame) {}
    105   void OnGoAway(const SpdyFrame& frame) {}
    106   void OnPing(const SpdyFrame& frame) {}
    107   virtual void OnWindowUpdate(SpdyStreamId stream_id,
    108                               uint32 delta_window_size) OVERRIDE {}
    109   virtual void OnPushPromise(SpdyStreamId stream_id,
    110                              SpdyStreamId promised_stream_id) OVERRIDE {}
    111   void OnCredential(const SpdyFrame& frame) {}
    112 
    113   // Convenience function which runs a framer simulation with particular input.
    114   void SimulateInFramer(const unsigned char* input, size_t size) {
    115     buffered_spdy_framer_.set_visitor(this);
    116     size_t input_remaining = size;
    117     const char* input_ptr = reinterpret_cast<const char*>(input);
    118     while (input_remaining > 0 &&
    119            buffered_spdy_framer_.error_code() == SpdyFramer::SPDY_NO_ERROR) {
    120       // To make the tests more interesting, we feed random (amd small) chunks
    121       // into the framer.  This simulates getting strange-sized reads from
    122       // the socket.
    123       const size_t kMaxReadSize = 32;
    124       size_t bytes_read =
    125           (rand() % std::min(input_remaining, kMaxReadSize)) + 1;
    126       size_t bytes_processed =
    127           buffered_spdy_framer_.ProcessInput(input_ptr, bytes_read);
    128       input_remaining -= bytes_processed;
    129       input_ptr += bytes_processed;
    130     }
    131   }
    132 
    133   BufferedSpdyFramer buffered_spdy_framer_;
    134 
    135   // Counters from the visitor callbacks.
    136   int error_count_;
    137   int setting_count_;
    138   int syn_frame_count_;
    139   int syn_reply_frame_count_;
    140   int headers_frame_count_;
    141 
    142   // Header block streaming state:
    143   SpdyStreamId header_stream_id_;
    144 
    145   // Headers from OnSyn, OnSynReply and OnHeaders for verification.
    146   SpdyHeaderBlock headers_;
    147 };
    148 
    149 }  // namespace
    150 
    151 class BufferedSpdyFramerTest
    152     : public PlatformTest,
    153       public ::testing::WithParamInterface<NextProto> {
    154  protected:
    155   // Returns true if the two header blocks have equivalent content.
    156   bool CompareHeaderBlocks(const SpdyHeaderBlock* expected,
    157                            const SpdyHeaderBlock* actual) {
    158     if (expected->size() != actual->size()) {
    159       LOG(ERROR) << "Expected " << expected->size() << " headers; actually got "
    160                  << actual->size() << ".";
    161       return false;
    162     }
    163     for (SpdyHeaderBlock::const_iterator it = expected->begin();
    164          it != expected->end();
    165          ++it) {
    166       SpdyHeaderBlock::const_iterator it2 = actual->find(it->first);
    167       if (it2 == actual->end()) {
    168         LOG(ERROR) << "Expected header name '" << it->first << "'.";
    169         return false;
    170       }
    171       if (it->second.compare(it2->second) != 0) {
    172         LOG(ERROR) << "Expected header named '" << it->first
    173                    << "' to have a value of '" << it->second
    174                    << "'. The actual value received was '" << it2->second
    175                    << "'.";
    176         return false;
    177       }
    178     }
    179     return true;
    180   }
    181 
    182   SpdyMajorVersion spdy_version() {
    183     return NextProtoToSpdyMajorVersion(GetParam());
    184   }
    185 };
    186 
    187 INSTANTIATE_TEST_CASE_P(
    188     NextProto,
    189     BufferedSpdyFramerTest,
    190     testing::Values(kProtoSPDY2, kProtoSPDY3, kProtoSPDY31, kProtoSPDY4a2,
    191                     kProtoHTTP2Draft04));
    192 
    193 TEST_P(BufferedSpdyFramerTest, OnSetting) {
    194   SpdyFramer framer(spdy_version());
    195   SettingsMap settings;
    196   settings[SETTINGS_UPLOAD_BANDWIDTH] =
    197       SettingsFlagsAndValue(SETTINGS_FLAG_NONE, 0x00000002);
    198   settings[SETTINGS_DOWNLOAD_BANDWIDTH] =
    199       SettingsFlagsAndValue(SETTINGS_FLAG_NONE, 0x00000003);
    200 
    201   scoped_ptr<SpdyFrame> control_frame(framer.CreateSettings(settings));
    202   TestBufferedSpdyVisitor visitor(spdy_version());
    203 
    204   visitor.SimulateInFramer(
    205       reinterpret_cast<unsigned char*>(control_frame->data()),
    206       control_frame->size());
    207   EXPECT_EQ(0, visitor.error_count_);
    208   EXPECT_EQ(2, visitor.setting_count_);
    209 }
    210 
    211 TEST_P(BufferedSpdyFramerTest, ReadSynStreamHeaderBlock) {
    212   SpdyHeaderBlock headers;
    213   headers["aa"] = "vv";
    214   headers["bb"] = "ww";
    215   BufferedSpdyFramer framer(spdy_version(), true);
    216   scoped_ptr<SpdyFrame> control_frame(
    217       framer.CreateSynStream(1,                        // stream_id
    218                              0,                        // associated_stream_id
    219                              1,                        // priority
    220                              0,                        // credential_slot
    221                              CONTROL_FLAG_NONE,
    222                              true,                     // compress
    223                              &headers));
    224   EXPECT_TRUE(control_frame.get() != NULL);
    225 
    226   TestBufferedSpdyVisitor visitor(spdy_version());
    227   visitor.SimulateInFramer(
    228       reinterpret_cast<unsigned char*>(control_frame.get()->data()),
    229       control_frame.get()->size());
    230   EXPECT_EQ(0, visitor.error_count_);
    231   EXPECT_EQ(1, visitor.syn_frame_count_);
    232   EXPECT_EQ(0, visitor.syn_reply_frame_count_);
    233   EXPECT_EQ(0, visitor.headers_frame_count_);
    234   EXPECT_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_));
    235 }
    236 
    237 TEST_P(BufferedSpdyFramerTest, ReadSynReplyHeaderBlock) {
    238   SpdyHeaderBlock headers;
    239   headers["alpha"] = "beta";
    240   headers["gamma"] = "delta";
    241   BufferedSpdyFramer framer(spdy_version(), true);
    242   scoped_ptr<SpdyFrame> control_frame(
    243       framer.CreateSynReply(1,                        // stream_id
    244                             CONTROL_FLAG_NONE,
    245                             true,                     // compress
    246                             &headers));
    247   EXPECT_TRUE(control_frame.get() != NULL);
    248 
    249   TestBufferedSpdyVisitor visitor(spdy_version());
    250   visitor.SimulateInFramer(
    251       reinterpret_cast<unsigned char*>(control_frame.get()->data()),
    252       control_frame.get()->size());
    253   EXPECT_EQ(0, visitor.error_count_);
    254   EXPECT_EQ(0, visitor.syn_frame_count_);
    255   EXPECT_EQ(1, visitor.syn_reply_frame_count_);
    256   EXPECT_EQ(0, visitor.headers_frame_count_);
    257   EXPECT_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_));
    258 }
    259 
    260 TEST_P(BufferedSpdyFramerTest, ReadHeadersHeaderBlock) {
    261   SpdyHeaderBlock headers;
    262   headers["alpha"] = "beta";
    263   headers["gamma"] = "delta";
    264   BufferedSpdyFramer framer(spdy_version(), true);
    265   scoped_ptr<SpdyFrame> control_frame(
    266       framer.CreateHeaders(1,                        // stream_id
    267                            CONTROL_FLAG_NONE,
    268                            true,                    // compress
    269                            &headers));
    270   EXPECT_TRUE(control_frame.get() != NULL);
    271 
    272   TestBufferedSpdyVisitor visitor(spdy_version());
    273   visitor.SimulateInFramer(
    274       reinterpret_cast<unsigned char*>(control_frame.get()->data()),
    275       control_frame.get()->size());
    276   EXPECT_EQ(0, visitor.error_count_);
    277   EXPECT_EQ(0, visitor.syn_frame_count_);
    278   EXPECT_EQ(0, visitor.syn_reply_frame_count_);
    279   EXPECT_EQ(1, visitor.headers_frame_count_);
    280   EXPECT_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_));
    281 }
    282 
    283 }  // namespace net
    284