buffered_spdy_framer_unittest.cc revision 4ad1aa43a48567659193a298fad74f55e00b3dd9
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
10namespace net {
11
12namespace {
13
14class 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                           bool fin,
43                           bool unidirectional,
44                           const SpdyHeaderBlock& headers) OVERRIDE {
45    header_stream_id_ = stream_id;
46    EXPECT_NE(header_stream_id_, SpdyFramer::kInvalidStream);
47    syn_frame_count_++;
48    headers_ = headers;
49  }
50
51  virtual void OnSynReply(SpdyStreamId stream_id,
52                          bool fin,
53                          const SpdyHeaderBlock& headers) OVERRIDE {
54    header_stream_id_ = stream_id;
55    EXPECT_NE(header_stream_id_, SpdyFramer::kInvalidStream);
56    syn_reply_frame_count_++;
57    headers_ = headers;
58  }
59
60  virtual void OnHeaders(SpdyStreamId stream_id,
61                         bool fin,
62                         const SpdyHeaderBlock& headers) OVERRIDE {
63    header_stream_id_ = stream_id;
64    EXPECT_NE(header_stream_id_, SpdyFramer::kInvalidStream);
65    headers_frame_count_++;
66    headers_ = headers;
67  }
68
69  virtual void OnDataFrameHeader(SpdyStreamId stream_id,
70                                 size_t length,
71                                 bool fin) OVERRIDE {
72    ADD_FAILURE() << "Unexpected OnDataFrameHeader call.";
73  }
74
75  virtual void OnStreamFrameData(SpdyStreamId stream_id,
76                                 const char* data,
77                                 size_t len,
78                                 bool fin) OVERRIDE {
79    LOG(FATAL) << "Unexpected OnStreamFrameData call.";
80  }
81
82  virtual void OnSettings(bool clear_persisted) OVERRIDE {}
83
84  virtual void OnSetting(SpdySettingsIds id,
85                         uint8 flags,
86                         uint32 value) OVERRIDE {
87    setting_count_++;
88  }
89
90  virtual void OnPing(SpdyPingId unique_id, bool is_ack) OVERRIDE {}
91
92  virtual void OnRstStream(SpdyStreamId stream_id,
93                           SpdyRstStreamStatus status) OVERRIDE {
94  }
95
96  virtual void OnGoAway(SpdyStreamId last_accepted_stream_id,
97                        SpdyGoAwayStatus status) OVERRIDE {
98  }
99
100  bool OnCredentialFrameData(const char*, size_t) {
101    LOG(FATAL) << "Unexpected OnCredentialFrameData call.";
102    return false;
103  }
104
105  void OnDataFrameHeader(const SpdyFrame* frame) {
106    LOG(FATAL) << "Unexpected OnDataFrameHeader call.";
107  }
108
109  void OnRstStream(const SpdyFrame& frame) {}
110  void OnGoAway(const SpdyFrame& frame) {}
111  void OnPing(const SpdyFrame& frame) {}
112  virtual void OnWindowUpdate(SpdyStreamId stream_id,
113                              uint32 delta_window_size) OVERRIDE {}
114  virtual void OnPushPromise(SpdyStreamId stream_id,
115                             SpdyStreamId promised_stream_id) OVERRIDE {}
116  void OnCredential(const SpdyFrame& frame) {}
117
118  // Convenience function which runs a framer simulation with particular input.
119  void SimulateInFramer(const unsigned char* input, size_t size) {
120    buffered_spdy_framer_.set_visitor(this);
121    size_t input_remaining = size;
122    const char* input_ptr = reinterpret_cast<const char*>(input);
123    while (input_remaining > 0 &&
124           buffered_spdy_framer_.error_code() == SpdyFramer::SPDY_NO_ERROR) {
125      // To make the tests more interesting, we feed random (amd small) chunks
126      // into the framer.  This simulates getting strange-sized reads from
127      // the socket.
128      const size_t kMaxReadSize = 32;
129      size_t bytes_read =
130          (rand() % std::min(input_remaining, kMaxReadSize)) + 1;
131      size_t bytes_processed =
132          buffered_spdy_framer_.ProcessInput(input_ptr, bytes_read);
133      input_remaining -= bytes_processed;
134      input_ptr += bytes_processed;
135    }
136  }
137
138  BufferedSpdyFramer buffered_spdy_framer_;
139
140  // Counters from the visitor callbacks.
141  int error_count_;
142  int setting_count_;
143  int syn_frame_count_;
144  int syn_reply_frame_count_;
145  int headers_frame_count_;
146
147  // Header block streaming state:
148  SpdyStreamId header_stream_id_;
149
150  // Headers from OnSyn, OnSynReply and OnHeaders for verification.
151  SpdyHeaderBlock headers_;
152};
153
154}  // namespace
155
156class BufferedSpdyFramerTest
157    : public PlatformTest,
158      public ::testing::WithParamInterface<NextProto> {
159 protected:
160  // Returns true if the two header blocks have equivalent content.
161  bool CompareHeaderBlocks(const SpdyHeaderBlock* expected,
162                           const SpdyHeaderBlock* actual) {
163    if (expected->size() != actual->size()) {
164      LOG(ERROR) << "Expected " << expected->size() << " headers; actually got "
165                 << actual->size() << ".";
166      return false;
167    }
168    for (SpdyHeaderBlock::const_iterator it = expected->begin();
169         it != expected->end();
170         ++it) {
171      SpdyHeaderBlock::const_iterator it2 = actual->find(it->first);
172      if (it2 == actual->end()) {
173        LOG(ERROR) << "Expected header name '" << it->first << "'.";
174        return false;
175      }
176      if (it->second.compare(it2->second) != 0) {
177        LOG(ERROR) << "Expected header named '" << it->first
178                   << "' to have a value of '" << it->second
179                   << "'. The actual value received was '" << it2->second
180                   << "'.";
181        return false;
182      }
183    }
184    return true;
185  }
186
187  SpdyMajorVersion spdy_version() {
188    return NextProtoToSpdyMajorVersion(GetParam());
189  }
190};
191
192INSTANTIATE_TEST_CASE_P(
193    NextProto,
194    BufferedSpdyFramerTest,
195    testing::Values(kProtoDeprecatedSPDY2,
196                    kProtoSPDY3, kProtoSPDY31, kProtoSPDY4a2,
197                    kProtoHTTP2Draft04));
198
199TEST_P(BufferedSpdyFramerTest, OnSetting) {
200  SpdyFramer framer(spdy_version());
201  SpdySettingsIR settings_ir;
202  settings_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, false, false, 2);
203  settings_ir.AddSetting(SETTINGS_MAX_CONCURRENT_STREAMS, false, false, 3);
204  scoped_ptr<SpdyFrame> control_frame(framer.SerializeSettings(settings_ir));
205  TestBufferedSpdyVisitor visitor(spdy_version());
206
207  visitor.SimulateInFramer(
208      reinterpret_cast<unsigned char*>(control_frame->data()),
209      control_frame->size());
210  EXPECT_EQ(0, visitor.error_count_);
211  EXPECT_EQ(2, visitor.setting_count_);
212}
213
214TEST_P(BufferedSpdyFramerTest, ReadSynStreamHeaderBlock) {
215  SpdyHeaderBlock headers;
216  headers["aa"] = "vv";
217  headers["bb"] = "ww";
218  BufferedSpdyFramer framer(spdy_version(), true);
219  scoped_ptr<SpdyFrame> control_frame(
220      framer.CreateSynStream(1,                        // stream_id
221                             0,                        // associated_stream_id
222                             1,                        // priority
223                             CONTROL_FLAG_NONE,
224                             &headers));
225  EXPECT_TRUE(control_frame.get() != NULL);
226
227  TestBufferedSpdyVisitor visitor(spdy_version());
228  visitor.SimulateInFramer(
229      reinterpret_cast<unsigned char*>(control_frame.get()->data()),
230      control_frame.get()->size());
231  EXPECT_EQ(0, visitor.error_count_);
232  EXPECT_EQ(1, visitor.syn_frame_count_);
233  EXPECT_EQ(0, visitor.syn_reply_frame_count_);
234  EXPECT_EQ(0, visitor.headers_frame_count_);
235  EXPECT_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_));
236}
237
238TEST_P(BufferedSpdyFramerTest, ReadSynReplyHeaderBlock) {
239  SpdyHeaderBlock headers;
240  headers["alpha"] = "beta";
241  headers["gamma"] = "delta";
242  BufferedSpdyFramer framer(spdy_version(), true);
243  scoped_ptr<SpdyFrame> control_frame(
244      framer.CreateSynReply(1,                        // stream_id
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(0, visitor.syn_frame_count_);
255  if(spdy_version() < SPDY4) {
256    EXPECT_EQ(1, visitor.syn_reply_frame_count_);
257    EXPECT_EQ(0, visitor.headers_frame_count_);
258  } else {
259    EXPECT_EQ(0, visitor.syn_reply_frame_count_);
260    EXPECT_EQ(1, visitor.headers_frame_count_);
261  }
262  EXPECT_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_));
263}
264
265TEST_P(BufferedSpdyFramerTest, ReadHeadersHeaderBlock) {
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.CreateHeaders(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.syn_reply_frame_count_);
283  EXPECT_EQ(1, visitor.headers_frame_count_);
284  EXPECT_TRUE(CompareHeaderBlocks(&headers, &visitor.headers_));
285}
286
287}  // namespace net
288