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 void OnCredential(const SpdyFrame
& frame
) {}
130 // Convenience function which runs a framer simulation with particular input.
131 void SimulateInFramer(const unsigned char* input
, size_t size
) {
132 buffered_spdy_framer_
.set_visitor(this);
133 size_t input_remaining
= size
;
134 const char* input_ptr
= reinterpret_cast<const char*>(input
);
135 while (input_remaining
> 0 &&
136 buffered_spdy_framer_
.error_code() == SpdyFramer::SPDY_NO_ERROR
) {
137 // To make the tests more interesting, we feed random (amd small) chunks
138 // into the framer. This simulates getting strange-sized reads from
140 const size_t kMaxReadSize
= 32;
142 (rand() % std::min(input_remaining
, kMaxReadSize
)) + 1;
143 size_t bytes_processed
=
144 buffered_spdy_framer_
.ProcessInput(input_ptr
, bytes_read
);
145 input_remaining
-= bytes_processed
;
146 input_ptr
+= bytes_processed
;
150 BufferedSpdyFramer buffered_spdy_framer_
;
152 // Counters from the visitor callbacks.
155 int syn_frame_count_
;
156 int syn_reply_frame_count_
;
157 int headers_frame_count_
;
158 int push_promise_frame_count_
;
160 // Header block streaming state:
161 SpdyStreamId header_stream_id_
;
162 SpdyStreamId promised_stream_id_
;
164 // Headers from OnSyn, OnSynReply, OnHeaders and OnPushPromise for
166 SpdyHeaderBlock headers_
;
171 class BufferedSpdyFramerTest
172 : public PlatformTest
,
173 public ::testing::WithParamInterface
<NextProto
> {
175 // Returns true if the two header blocks have equivalent content.
176 bool CompareHeaderBlocks(const SpdyHeaderBlock
* expected
,
177 const SpdyHeaderBlock
* actual
) {
178 if (expected
->size() != actual
->size()) {
179 LOG(ERROR
) << "Expected " << expected
->size() << " headers; actually got "
180 << actual
->size() << ".";
183 for (SpdyHeaderBlock::const_iterator it
= expected
->begin();
184 it
!= expected
->end();
186 SpdyHeaderBlock::const_iterator it2
= actual
->find(it
->first
);
187 if (it2
== actual
->end()) {
188 LOG(ERROR
) << "Expected header name '" << it
->first
<< "'.";
191 if (it
->second
.compare(it2
->second
) != 0) {
192 LOG(ERROR
) << "Expected header named '" << it
->first
193 << "' to have a value of '" << it
->second
194 << "'. The actual value received was '" << it2
->second
202 SpdyMajorVersion
spdy_version() {
203 return NextProtoToSpdyMajorVersion(GetParam());
207 INSTANTIATE_TEST_CASE_P(
209 BufferedSpdyFramerTest
,
210 testing::Values(kProtoDeprecatedSPDY2
,
211 kProtoSPDY3
, kProtoSPDY31
, kProtoSPDY4
));
213 TEST_P(BufferedSpdyFramerTest
, OnSetting
) {
214 SpdyFramer
framer(spdy_version());
215 SpdySettingsIR settings_ir
;
216 settings_ir
.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE
, false, false, 2);
217 settings_ir
.AddSetting(SETTINGS_MAX_CONCURRENT_STREAMS
, false, false, 3);
218 scoped_ptr
<SpdyFrame
> control_frame(framer
.SerializeSettings(settings_ir
));
219 TestBufferedSpdyVisitor
visitor(spdy_version());
221 visitor
.SimulateInFramer(
222 reinterpret_cast<unsigned char*>(control_frame
->data()),
223 control_frame
->size());
224 EXPECT_EQ(0, visitor
.error_count_
);
225 EXPECT_EQ(2, visitor
.setting_count_
);
228 TEST_P(BufferedSpdyFramerTest
, ReadSynStreamHeaderBlock
) {
229 if (spdy_version() > SPDY3
) {
230 // SYN_STREAM not supported in SPDY>3.
233 SpdyHeaderBlock headers
;
234 headers
["aa"] = "vv";
235 headers
["bb"] = "ww";
236 BufferedSpdyFramer
framer(spdy_version(), true);
237 scoped_ptr
<SpdyFrame
> control_frame(
238 framer
.CreateSynStream(1, // stream_id
239 0, // associated_stream_id
243 EXPECT_TRUE(control_frame
.get() != NULL
);
245 TestBufferedSpdyVisitor
visitor(spdy_version());
246 visitor
.SimulateInFramer(
247 reinterpret_cast<unsigned char*>(control_frame
.get()->data()),
248 control_frame
.get()->size());
249 EXPECT_EQ(0, visitor
.error_count_
);
250 EXPECT_EQ(1, visitor
.syn_frame_count_
);
251 EXPECT_EQ(0, visitor
.syn_reply_frame_count_
);
252 EXPECT_EQ(0, visitor
.headers_frame_count_
);
253 EXPECT_EQ(0, visitor
.push_promise_frame_count_
);
254 EXPECT_TRUE(CompareHeaderBlocks(&headers
, &visitor
.headers_
));
257 TEST_P(BufferedSpdyFramerTest
, ReadSynReplyHeaderBlock
) {
258 if (spdy_version() > SPDY3
) {
259 // SYN_REPLY not supported in SPDY>3.
262 SpdyHeaderBlock headers
;
263 headers
["alpha"] = "beta";
264 headers
["gamma"] = "delta";
265 BufferedSpdyFramer
framer(spdy_version(), true);
266 scoped_ptr
<SpdyFrame
> control_frame(
267 framer
.CreateSynReply(1, // stream_id
270 EXPECT_TRUE(control_frame
.get() != NULL
);
272 TestBufferedSpdyVisitor
visitor(spdy_version());
273 visitor
.SimulateInFramer(
274 reinterpret_cast<unsigned char*>(control_frame
.get()->data()),
275 control_frame
.get()->size());
276 EXPECT_EQ(0, visitor
.error_count_
);
277 EXPECT_EQ(0, visitor
.syn_frame_count_
);
278 EXPECT_EQ(0, visitor
.push_promise_frame_count_
);
279 if(spdy_version() < SPDY4
) {
280 EXPECT_EQ(1, visitor
.syn_reply_frame_count_
);
281 EXPECT_EQ(0, visitor
.headers_frame_count_
);
283 EXPECT_EQ(0, visitor
.syn_reply_frame_count_
);
284 EXPECT_EQ(1, visitor
.headers_frame_count_
);
286 EXPECT_TRUE(CompareHeaderBlocks(&headers
, &visitor
.headers_
));
289 TEST_P(BufferedSpdyFramerTest
, ReadHeadersHeaderBlock
) {
290 SpdyHeaderBlock headers
;
291 headers
["alpha"] = "beta";
292 headers
["gamma"] = "delta";
293 BufferedSpdyFramer
framer(spdy_version(), true);
294 scoped_ptr
<SpdyFrame
> control_frame(
295 framer
.CreateHeaders(1, // stream_id
298 EXPECT_TRUE(control_frame
.get() != NULL
);
300 TestBufferedSpdyVisitor
visitor(spdy_version());
301 visitor
.SimulateInFramer(
302 reinterpret_cast<unsigned char*>(control_frame
.get()->data()),
303 control_frame
.get()->size());
304 EXPECT_EQ(0, visitor
.error_count_
);
305 EXPECT_EQ(0, visitor
.syn_frame_count_
);
306 EXPECT_EQ(0, visitor
.syn_reply_frame_count_
);
307 EXPECT_EQ(1, visitor
.headers_frame_count_
);
308 EXPECT_EQ(0, visitor
.push_promise_frame_count_
);
309 EXPECT_TRUE(CompareHeaderBlocks(&headers
, &visitor
.headers_
));
312 TEST_P(BufferedSpdyFramerTest
, ReadPushPromiseHeaderBlock
) {
313 if (spdy_version() < SPDY4
)
315 SpdyHeaderBlock headers
;
316 headers
["alpha"] = "beta";
317 headers
["gamma"] = "delta";
318 BufferedSpdyFramer
framer(spdy_version(), true);
319 scoped_ptr
<SpdyFrame
> control_frame(
320 framer
.CreatePushPromise(1, 2, &headers
));
321 EXPECT_TRUE(control_frame
.get() != NULL
);
323 TestBufferedSpdyVisitor
visitor(spdy_version());
324 visitor
.SimulateInFramer(
325 reinterpret_cast<unsigned char*>(control_frame
.get()->data()),
326 control_frame
.get()->size());
327 EXPECT_EQ(0, visitor
.error_count_
);
328 EXPECT_EQ(0, visitor
.syn_frame_count_
);
329 EXPECT_EQ(0, visitor
.syn_reply_frame_count_
);
330 EXPECT_EQ(0, visitor
.headers_frame_count_
);
331 EXPECT_EQ(1, visitor
.push_promise_frame_count_
);
332 EXPECT_TRUE(CompareHeaderBlocks(&headers
, &visitor
.headers_
));
333 EXPECT_EQ(1u, visitor
.header_stream_id_
);
334 EXPECT_EQ(2u, visitor
.promised_stream_id_
);