• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
TestBufferedSpdyVisitor(SpdyMajorVersion spdy_version)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 
OnError(SpdyFramer::SpdyError error_code)28   virtual void OnError(SpdyFramer::SpdyError error_code) OVERRIDE {
29     LOG(INFO) << "SpdyFramer Error: " << error_code;
30     error_count_++;
31   }
32 
OnStreamError(SpdyStreamId stream_id,const std::string & description)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 
OnSynStream(SpdyStreamId stream_id,SpdyStreamId associated_stream_id,SpdyPriority priority,bool fin,bool unidirectional,const SpdyHeaderBlock & headers)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 
OnSynReply(SpdyStreamId stream_id,bool fin,const SpdyHeaderBlock & headers)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 
OnHeaders(SpdyStreamId stream_id,bool fin,const SpdyHeaderBlock & headers)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 
OnDataFrameHeader(SpdyStreamId stream_id,size_t length,bool fin)71   virtual void OnDataFrameHeader(SpdyStreamId stream_id,
72                                  size_t length,
73                                  bool fin) OVERRIDE {
74     ADD_FAILURE() << "Unexpected OnDataFrameHeader call.";
75   }
76 
OnStreamFrameData(SpdyStreamId stream_id,const char * data,size_t len,bool fin)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 
OnSettings(bool clear_persisted)84   virtual void OnSettings(bool clear_persisted) OVERRIDE {}
85 
OnSetting(SpdySettingsIds id,uint8 flags,uint32 value)86   virtual void OnSetting(SpdySettingsIds id,
87                          uint8 flags,
88                          uint32 value) OVERRIDE {
89     setting_count_++;
90   }
91 
OnPing(SpdyPingId unique_id,bool is_ack)92   virtual void OnPing(SpdyPingId unique_id, bool is_ack) OVERRIDE {}
93 
OnRstStream(SpdyStreamId stream_id,SpdyRstStreamStatus status)94   virtual void OnRstStream(SpdyStreamId stream_id,
95                            SpdyRstStreamStatus status) OVERRIDE {
96   }
97 
OnGoAway(SpdyStreamId last_accepted_stream_id,SpdyGoAwayStatus status)98   virtual void OnGoAway(SpdyStreamId last_accepted_stream_id,
99                         SpdyGoAwayStatus status) OVERRIDE {
100   }
101 
OnCredentialFrameData(const char *,size_t)102   bool OnCredentialFrameData(const char*, size_t) {
103     LOG(FATAL) << "Unexpected OnCredentialFrameData call.";
104     return false;
105   }
106 
OnDataFrameHeader(const SpdyFrame * frame)107   void OnDataFrameHeader(const SpdyFrame* frame) {
108     LOG(FATAL) << "Unexpected OnDataFrameHeader call.";
109   }
110 
OnRstStream(const SpdyFrame & frame)111   void OnRstStream(const SpdyFrame& frame) {}
OnGoAway(const SpdyFrame & frame)112   void OnGoAway(const SpdyFrame& frame) {}
OnPing(const SpdyFrame & frame)113   void OnPing(const SpdyFrame& frame) {}
OnWindowUpdate(SpdyStreamId stream_id,uint32 delta_window_size)114   virtual void OnWindowUpdate(SpdyStreamId stream_id,
115                               uint32 delta_window_size) OVERRIDE {}
116 
OnPushPromise(SpdyStreamId stream_id,SpdyStreamId promised_stream_id,const SpdyHeaderBlock & headers)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 
OnUnknownFrame(SpdyStreamId stream_id,int frame_type)128   virtual bool OnUnknownFrame(SpdyStreamId stream_id, int frame_type) OVERRIDE {
129     return true;
130   }
131 
OnCredential(const SpdyFrame & frame)132   void OnCredential(const SpdyFrame& frame) {}
133 
134   // Convenience function which runs a framer simulation with particular input.
SimulateInFramer(const unsigned char * input,size_t size)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.
CompareHeaderBlocks(const SpdyHeaderBlock * expected,const SpdyHeaderBlock * actual)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 
spdy_version()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 
TEST_P(BufferedSpdyFramerTest,OnSetting)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 
TEST_P(BufferedSpdyFramerTest,ReadSynStreamHeaderBlock)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 
TEST_P(BufferedSpdyFramerTest,ReadSynReplyHeaderBlock)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 
TEST_P(BufferedSpdyFramerTest,ReadHeadersHeaderBlock)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 
TEST_P(BufferedSpdyFramerTest,ReadPushPromiseHeaderBlock)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