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