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         push_promise_frame_count_(0),
     24         header_stream_id_(static_cast<SpdyStreamId>(-1)),
     25         promised_stream_id_(static_cast<SpdyStreamId>(-1)) {
     26   }
     27 
     28   virtual void OnError(SpdyFramer::SpdyError error_code) OVERRIDE {
     29     LOG(INFO) << "SpdyFramer Error: " << error_code;
     30     error_count_++;
     31   }
     32 
     33   virtual void OnStreamError(
     34       SpdyStreamId stream_id,
     35       const std::string& description) OVERRIDE {
     36     LOG(INFO) << "SpdyFramer Error on stream: " << stream_id  << " "
     37               << description;
     38     error_count_++;
     39   }
     40 
     41   virtual void OnSynStream(SpdyStreamId stream_id,
     42                            SpdyStreamId associated_stream_id,
     43                            SpdyPriority priority,
     44                            bool fin,
     45                            bool unidirectional,
     46                            const SpdyHeaderBlock& headers) OVERRIDE {
     47     header_stream_id_ = stream_id;
     48     EXPECT_NE(header_stream_id_, SpdyFramer::kInvalidStream);
     49     syn_frame_count_++;
     50     headers_ = headers;
     51   }
     52 
     53   virtual void OnSynReply(SpdyStreamId stream_id,
     54                           bool fin,
     55                           const SpdyHeaderBlock& headers) OVERRIDE {
     56     header_stream_id_ = stream_id;
     57     EXPECT_NE(header_stream_id_, SpdyFramer::kInvalidStream);
     58     syn_reply_frame_count_++;
     59     headers_ = headers;
     60   }
     61 
     62   virtual void OnHeaders(SpdyStreamId stream_id,
     63                          bool fin,
     64                          const SpdyHeaderBlock& headers) OVERRIDE {
     65     header_stream_id_ = stream_id;
     66     EXPECT_NE(header_stream_id_, SpdyFramer::kInvalidStream);
     67     headers_frame_count_++;
     68     headers_ = headers;
     69   }
     70 
     71   virtual void OnDataFrameHeader(SpdyStreamId stream_id,
     72                                  size_t length,
     73                                  bool fin) OVERRIDE {
     74     ADD_FAILURE() << "Unexpected OnDataFrameHeader call.";
     75   }
     76 
     77   virtual void OnStreamFrameData(SpdyStreamId stream_id,
     78                                  const char* data,
     79                                  size_t len,
     80                                  bool fin) OVERRIDE {
     81     LOG(FATAL) << "Unexpected OnStreamFrameData call.";
     82   }
     83 
     84   virtual void OnSettings(bool clear_persisted) OVERRIDE {}
     85 
     86   virtual void OnSetting(SpdySettingsIds id,
     87                          uint8 flags,
     88                          uint32 value) OVERRIDE {
     89     setting_count_++;
     90   }
     91 
     92   virtual void OnPing(SpdyPingId unique_id, bool is_ack) OVERRIDE {}
     93 
     94   virtual void OnRstStream(SpdyStreamId stream_id,
     95                            SpdyRstStreamStatus status) OVERRIDE {
     96   }
     97 
     98   virtual void OnGoAway(SpdyStreamId last_accepted_stream_id,
     99                         SpdyGoAwayStatus status) OVERRIDE {
    100   }
    101 
    102   bool OnCredentialFrameData(const char*, size_t) {
    103     LOG(FATAL) << "Unexpected OnCredentialFrameData call.";
    104     return false;
    105   }
    106 
    107   void OnDataFrameHeader(const SpdyFrame* frame) {
    108     LOG(FATAL) << "Unexpected OnDataFrameHeader call.";
    109   }
    110 
    111   void OnRstStream(const SpdyFrame& frame) {}
    112   void OnGoAway(const SpdyFrame& frame) {}
    113   void OnPing(const SpdyFrame& frame) {}
    114   virtual void OnWindowUpdate(SpdyStreamId stream_id,
    115                               uint32 delta_window_size) OVERRIDE {}
    116 
    117   virtual void OnPushPromise(SpdyStreamId stream_id,
    118                              SpdyStreamId promised_stream_id,
    119                              const SpdyHeaderBlock& headers) OVERRIDE {
    120     header_stream_id_ = stream_id;
    121     EXPECT_NE(header_stream_id_, SpdyFramer::kInvalidStream);
    122     push_promise_frame_count_++;
    123     promised_stream_id_ = promised_stream_id;
    124     EXPECT_NE(promised_stream_id_, SpdyFramer::kInvalidStream);
    125     headers_ = headers;
    126   }
    127 
    128   virtual bool OnUnknownFrame(SpdyStreamId stream_id, int frame_type) OVERRIDE {
    129     return true;
    130   }
    131 
    132   void OnCredential(const SpdyFrame& frame) {}
    133 
    134   // Convenience function which runs a framer simulation with particular input.
    135   void SimulateInFramer(const unsigned char* input, size_t size) {
    136     buffered_spdy_framer_.set_visitor(this);
    137     size_t input_remaining = size;
    138     const char* input_ptr = reinterpret_cast<const char*>(input);
    139     while (input_remaining > 0 &&
    140            buffered_spdy_framer_.error_code() == SpdyFramer::SPDY_NO_ERROR) {
    141       // To make the tests more interesting, we feed random (amd small) chunks
    142       // into the framer.  This simulates getting strange-sized reads from
    143       // the socket.
    144       const size_t kMaxReadSize = 32;
    145       size_t bytes_read =
    146           (rand() % std::min(input_remaining, kMaxReadSize)) + 1;
    147       size_t bytes_processed =
    148           buffered_spdy_framer_.ProcessInput(input_ptr, bytes_read);
    149       input_remaining -= bytes_processed;
    150       input_ptr += bytes_processed;
    151     }
    152   }
    153 
    154   BufferedSpdyFramer buffered_spdy_framer_;
    155 
    156   // Counters from the visitor callbacks.
    157   int error_count_;
    158   int setting_count_;
    159   int syn_frame_count_;
    160   int syn_reply_frame_count_;
    161   int headers_frame_count_;
    162   int push_promise_frame_count_;
    163 
    164   // Header block streaming state:
    165   SpdyStreamId header_stream_id_;
    166   SpdyStreamId promised_stream_id_;
    167 
    168   // Headers from OnSyn, OnSynReply, OnHeaders and OnPushPromise for
    169   // verification.
    170   SpdyHeaderBlock headers_;
    171 };
    172 
    173 }  // namespace
    174 
    175 class BufferedSpdyFramerTest
    176     : public PlatformTest,
    177       public ::testing::WithParamInterface<NextProto> {
    178  protected:
    179   // Returns true if the two header blocks have equivalent content.
    180   bool CompareHeaderBlocks(const SpdyHeaderBlock* expected,
    181                            const SpdyHeaderBlock* actual) {
    182     if (expected->size() != actual->size()) {
    183       LOG(ERROR) << "Expected " << expected->size() << " headers; actually got "
    184                  << actual->size() << ".";
    185       return false;
    186     }
    187     for (SpdyHeaderBlock::const_iterator it = expected->begin();
    188          it != expected->end();
    189          ++it) {
    190       SpdyHeaderBlock::const_iterator it2 = actual->find(it->first);
    191       if (it2 == actual->end()) {
    192         LOG(ERROR) << "Expected header name '" << it->first << "'.";
    193         return false;
    194       }
    195       if (it->second.compare(it2->second) != 0) {
    196         LOG(ERROR) << "Expected header named '" << it->first
    197                    << "' to have a value of '" << it->second
    198                    << "'. The actual value received was '" << it2->second
    199                    << "'.";
    200         return false;
    201       }
    202     }
    203     return true;
    204   }
    205 
    206   SpdyMajorVersion spdy_version() {
    207     return NextProtoToSpdyMajorVersion(GetParam());
    208   }
    209 };
    210 
    211 INSTANTIATE_TEST_CASE_P(
    212     NextProto,
    213     BufferedSpdyFramerTest,
    214     testing::Values(kProtoDeprecatedSPDY2,
    215                     kProtoSPDY3, kProtoSPDY31, kProtoSPDY4));
    216 
    217 TEST_P(BufferedSpdyFramerTest, OnSetting) {
    218   SpdyFramer framer(spdy_version());
    219   SpdySettingsIR settings_ir;
    220   settings_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, false, false, 2);
    221   settings_ir.AddSetting(SETTINGS_MAX_CONCURRENT_STREAMS, false, false, 3);
    222   scoped_ptr<SpdyFrame> control_frame(framer.SerializeSettings(settings_ir));
    223   TestBufferedSpdyVisitor visitor(spdy_version());
    224 
    225   visitor.SimulateInFramer(
    226       reinterpret_cast<unsigned char*>(control_frame->data()),
    227       control_frame->size());
    228   EXPECT_EQ(0, visitor.error_count_);
    229   EXPECT_EQ(2, visitor.setting_count_);
    230 }
    231 
    232 TEST_P(BufferedSpdyFramerTest, ReadSynStreamHeaderBlock) {
    233   if (spdy_version() > SPDY3) {
    234     // SYN_STREAM not supported in SPDY>3.
    235     return;
    236   }
    237   SpdyHeaderBlock headers;
    238   headers["aa"] = "vv";
    239   headers["bb"] = "ww";
    240   BufferedSpdyFramer framer(spdy_version(), true);
    241   scoped_ptr<SpdyFrame> control_frame(
    242       framer.CreateSynStream(1,                        // stream_id
    243                              0,                        // associated_stream_id
    244                              1,                        // priority
    245                              CONTROL_FLAG_NONE,
    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(1, visitor.syn_frame_count_);
    255   EXPECT_EQ(0, visitor.syn_reply_frame_count_);
    256   EXPECT_EQ(0, visitor.headers_frame_count_);
    257   EXPECT_EQ(0, visitor.push_promise_frame_count_);
    258   EXPECT_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_));
    259 }
    260 
    261 TEST_P(BufferedSpdyFramerTest, ReadSynReplyHeaderBlock) {
    262   if (spdy_version() > SPDY3) {
    263     // SYN_REPLY not supported in SPDY>3.
    264     return;
    265   }
    266   SpdyHeaderBlock headers;
    267   headers["alpha"] = "beta";
    268   headers["gamma"] = "delta";
    269   BufferedSpdyFramer framer(spdy_version(), true);
    270   scoped_ptr<SpdyFrame> control_frame(
    271       framer.CreateSynReply(1,                        // stream_id
    272                             CONTROL_FLAG_NONE,
    273                             &headers));
    274   EXPECT_TRUE(control_frame.get() != NULL);
    275 
    276   TestBufferedSpdyVisitor visitor(spdy_version());
    277   visitor.SimulateInFramer(
    278       reinterpret_cast<unsigned char*>(control_frame.get()->data()),
    279       control_frame.get()->size());
    280   EXPECT_EQ(0, visitor.error_count_);
    281   EXPECT_EQ(0, visitor.syn_frame_count_);
    282   EXPECT_EQ(0, visitor.push_promise_frame_count_);
    283   if (spdy_version() < SPDY4) {
    284     EXPECT_EQ(1, visitor.syn_reply_frame_count_);
    285     EXPECT_EQ(0, visitor.headers_frame_count_);
    286   } else {
    287     EXPECT_EQ(0, visitor.syn_reply_frame_count_);
    288     EXPECT_EQ(1, visitor.headers_frame_count_);
    289   }
    290   EXPECT_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_));
    291 }
    292 
    293 TEST_P(BufferedSpdyFramerTest, ReadHeadersHeaderBlock) {
    294   SpdyHeaderBlock headers;
    295   headers["alpha"] = "beta";
    296   headers["gamma"] = "delta";
    297   BufferedSpdyFramer framer(spdy_version(), true);
    298   scoped_ptr<SpdyFrame> control_frame(
    299       framer.CreateHeaders(1,                        // stream_id
    300                            CONTROL_FLAG_NONE,
    301                            &headers));
    302   EXPECT_TRUE(control_frame.get() != NULL);
    303 
    304   TestBufferedSpdyVisitor visitor(spdy_version());
    305   visitor.SimulateInFramer(
    306       reinterpret_cast<unsigned char*>(control_frame.get()->data()),
    307       control_frame.get()->size());
    308   EXPECT_EQ(0, visitor.error_count_);
    309   EXPECT_EQ(0, visitor.syn_frame_count_);
    310   EXPECT_EQ(0, visitor.syn_reply_frame_count_);
    311   EXPECT_EQ(1, visitor.headers_frame_count_);
    312   EXPECT_EQ(0, visitor.push_promise_frame_count_);
    313   EXPECT_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_));
    314 }
    315 
    316 TEST_P(BufferedSpdyFramerTest, ReadPushPromiseHeaderBlock) {
    317   if (spdy_version() < SPDY4)
    318     return;
    319   SpdyHeaderBlock headers;
    320   headers["alpha"] = "beta";
    321   headers["gamma"] = "delta";
    322   BufferedSpdyFramer framer(spdy_version(), true);
    323   scoped_ptr<SpdyFrame> control_frame(
    324       framer.CreatePushPromise(1, 2, &headers));
    325   EXPECT_TRUE(control_frame.get() != NULL);
    326 
    327   TestBufferedSpdyVisitor visitor(spdy_version());
    328   visitor.SimulateInFramer(
    329       reinterpret_cast<unsigned char*>(control_frame.get()->data()),
    330       control_frame.get()->size());
    331   EXPECT_EQ(0, visitor.error_count_);
    332   EXPECT_EQ(0, visitor.syn_frame_count_);
    333   EXPECT_EQ(0, visitor.syn_reply_frame_count_);
    334   EXPECT_EQ(0, visitor.headers_frame_count_);
    335   EXPECT_EQ(1, visitor.push_promise_frame_count_);
    336   EXPECT_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_));
    337   EXPECT_EQ(1u, visitor.header_stream_id_);
    338   EXPECT_EQ(2u, visitor.promised_stream_id_);
    339 }
    340 
    341 }  // namespace net
    342