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.
5 #include "net/spdy/buffered_spdy_framer.h"
7 #include "net/spdy/spdy_test_util_common.h"
8 #include "testing/platform_test.h"
14 class TestBufferedSpdyVisitor
: public BufferedSpdyFramerVisitorInterface
{
16 explicit TestBufferedSpdyVisitor(SpdyMajorVersion spdy_version
)
17 : buffered_spdy_framer_(spdy_version
, true),
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)) {
28 virtual void OnError(SpdyFramer::SpdyError error_code
) OVERRIDE
{
29 LOG(INFO
) << "SpdyFramer Error: " << error_code
;
33 virtual void OnStreamError(
34 SpdyStreamId stream_id
,
35 const std::string
& description
) OVERRIDE
{
36 LOG(INFO
) << "SpdyFramer Error on stream: " << stream_id
<< " "
41 virtual void OnSynStream(SpdyStreamId stream_id
,
42 SpdyStreamId associated_stream_id
,
43 SpdyPriority priority
,
46 const SpdyHeaderBlock
& headers
) OVERRIDE
{
47 header_stream_id_
= stream_id
;
48 EXPECT_NE(header_stream_id_
, SpdyFramer::kInvalidStream
);
53 virtual void OnSynReply(SpdyStreamId stream_id
,
55 const SpdyHeaderBlock
& headers
) OVERRIDE
{
56 header_stream_id_
= stream_id
;
57 EXPECT_NE(header_stream_id_
, SpdyFramer::kInvalidStream
);
58 syn_reply_frame_count_
++;
62 virtual void OnHeaders(SpdyStreamId stream_id
,
64 const SpdyHeaderBlock
& headers
) OVERRIDE
{
65 header_stream_id_
= stream_id
;
66 EXPECT_NE(header_stream_id_
, SpdyFramer::kInvalidStream
);
67 headers_frame_count_
++;
71 virtual void OnDataFrameHeader(SpdyStreamId stream_id
,
74 ADD_FAILURE() << "Unexpected OnDataFrameHeader call.";
77 virtual void OnStreamFrameData(SpdyStreamId stream_id
,
81 LOG(FATAL
) << "Unexpected OnStreamFrameData call.";
84 virtual void OnSettings(bool clear_persisted
) OVERRIDE
{}
86 virtual void OnSetting(SpdySettingsIds id
,
88 uint32 value
) OVERRIDE
{
92 virtual void OnPing(SpdyPingId unique_id
, bool is_ack
) OVERRIDE
{}
94 virtual void OnRstStream(SpdyStreamId stream_id
,
95 SpdyRstStreamStatus status
) OVERRIDE
{
98 virtual void OnGoAway(SpdyStreamId last_accepted_stream_id
,
99 SpdyGoAwayStatus status
) OVERRIDE
{
102 bool OnCredentialFrameData(const char*, size_t) {
103 LOG(FATAL
) << "Unexpected OnCredentialFrameData call.";
107 void OnDataFrameHeader(const SpdyFrame
* frame
) {
108 LOG(FATAL
) << "Unexpected OnDataFrameHeader call.";
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
{}
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
);
128 virtual bool OnUnknownFrame(SpdyStreamId stream_id
, int frame_type
) OVERRIDE
{
132 void OnCredential(const SpdyFrame
& frame
) {}
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
144 const size_t kMaxReadSize
= 32;
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
;
154 BufferedSpdyFramer buffered_spdy_framer_
;
156 // Counters from the visitor callbacks.
159 int syn_frame_count_
;
160 int syn_reply_frame_count_
;
161 int headers_frame_count_
;
162 int push_promise_frame_count_
;
164 // Header block streaming state:
165 SpdyStreamId header_stream_id_
;
166 SpdyStreamId promised_stream_id_
;
168 // Headers from OnSyn, OnSynReply, OnHeaders and OnPushPromise for
170 SpdyHeaderBlock headers_
;
175 class BufferedSpdyFramerTest
176 : public PlatformTest
,
177 public ::testing::WithParamInterface
<NextProto
> {
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() << ".";
187 for (SpdyHeaderBlock::const_iterator it
= expected
->begin();
188 it
!= expected
->end();
190 SpdyHeaderBlock::const_iterator it2
= actual
->find(it
->first
);
191 if (it2
== actual
->end()) {
192 LOG(ERROR
) << "Expected header name '" << it
->first
<< "'.";
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
206 SpdyMajorVersion
spdy_version() {
207 return NextProtoToSpdyMajorVersion(GetParam());
211 INSTANTIATE_TEST_CASE_P(
213 BufferedSpdyFramerTest
,
214 testing::Values(kProtoDeprecatedSPDY2
,
215 kProtoSPDY3
, kProtoSPDY31
, kProtoSPDY4
));
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());
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_
);
232 TEST_P(BufferedSpdyFramerTest
, ReadSynStreamHeaderBlock
) {
233 if (spdy_version() > SPDY3
) {
234 // SYN_STREAM not supported in SPDY>3.
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
247 EXPECT_TRUE(control_frame
.get() != NULL
);
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_
));
261 TEST_P(BufferedSpdyFramerTest
, ReadSynReplyHeaderBlock
) {
262 if (spdy_version() > SPDY3
) {
263 // SYN_REPLY not supported in SPDY>3.
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
274 EXPECT_TRUE(control_frame
.get() != NULL
);
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_
);
287 EXPECT_EQ(0, visitor
.syn_reply_frame_count_
);
288 EXPECT_EQ(1, visitor
.headers_frame_count_
);
290 EXPECT_TRUE(CompareHeaderBlocks(&headers
, &visitor
.headers_
));
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
302 EXPECT_TRUE(control_frame
.get() != NULL
);
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_
));
316 TEST_P(BufferedSpdyFramerTest
, ReadPushPromiseHeaderBlock
) {
317 if (spdy_version() < SPDY4
)
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
);
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_
);