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_(-1),
25 promised_stream_id_(-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 SpdyHeaderBlock headers
;
230 headers
["aa"] = "vv";
231 headers
["bb"] = "ww";
232 BufferedSpdyFramer
framer(spdy_version(), true);
233 scoped_ptr
<SpdyFrame
> control_frame(
234 framer
.CreateSynStream(1, // stream_id
235 0, // associated_stream_id
239 EXPECT_TRUE(control_frame
.get() != NULL
);
241 TestBufferedSpdyVisitor
visitor(spdy_version());
242 visitor
.SimulateInFramer(
243 reinterpret_cast<unsigned char*>(control_frame
.get()->data()),
244 control_frame
.get()->size());
245 EXPECT_EQ(0, visitor
.error_count_
);
246 EXPECT_EQ(1, visitor
.syn_frame_count_
);
247 EXPECT_EQ(0, visitor
.syn_reply_frame_count_
);
248 EXPECT_EQ(0, visitor
.headers_frame_count_
);
249 EXPECT_EQ(0, visitor
.push_promise_frame_count_
);
250 EXPECT_TRUE(CompareHeaderBlocks(&headers
, &visitor
.headers_
));
253 TEST_P(BufferedSpdyFramerTest
, ReadSynReplyHeaderBlock
) {
254 SpdyHeaderBlock headers
;
255 headers
["alpha"] = "beta";
256 headers
["gamma"] = "delta";
257 BufferedSpdyFramer
framer(spdy_version(), true);
258 scoped_ptr
<SpdyFrame
> control_frame(
259 framer
.CreateSynReply(1, // stream_id
262 EXPECT_TRUE(control_frame
.get() != NULL
);
264 TestBufferedSpdyVisitor
visitor(spdy_version());
265 visitor
.SimulateInFramer(
266 reinterpret_cast<unsigned char*>(control_frame
.get()->data()),
267 control_frame
.get()->size());
268 EXPECT_EQ(0, visitor
.error_count_
);
269 EXPECT_EQ(0, visitor
.syn_frame_count_
);
270 EXPECT_EQ(0, visitor
.push_promise_frame_count_
);
271 if(spdy_version() < SPDY4
) {
272 EXPECT_EQ(1, visitor
.syn_reply_frame_count_
);
273 EXPECT_EQ(0, visitor
.headers_frame_count_
);
275 EXPECT_EQ(0, visitor
.syn_reply_frame_count_
);
276 EXPECT_EQ(1, visitor
.headers_frame_count_
);
278 EXPECT_TRUE(CompareHeaderBlocks(&headers
, &visitor
.headers_
));
281 TEST_P(BufferedSpdyFramerTest
, ReadHeadersHeaderBlock
) {
282 SpdyHeaderBlock headers
;
283 headers
["alpha"] = "beta";
284 headers
["gamma"] = "delta";
285 BufferedSpdyFramer
framer(spdy_version(), true);
286 scoped_ptr
<SpdyFrame
> control_frame(
287 framer
.CreateHeaders(1, // stream_id
290 EXPECT_TRUE(control_frame
.get() != NULL
);
292 TestBufferedSpdyVisitor
visitor(spdy_version());
293 visitor
.SimulateInFramer(
294 reinterpret_cast<unsigned char*>(control_frame
.get()->data()),
295 control_frame
.get()->size());
296 EXPECT_EQ(0, visitor
.error_count_
);
297 EXPECT_EQ(0, visitor
.syn_frame_count_
);
298 EXPECT_EQ(0, visitor
.syn_reply_frame_count_
);
299 EXPECT_EQ(1, visitor
.headers_frame_count_
);
300 EXPECT_EQ(0, visitor
.push_promise_frame_count_
);
301 EXPECT_TRUE(CompareHeaderBlocks(&headers
, &visitor
.headers_
));
304 TEST_P(BufferedSpdyFramerTest
, ReadPushPromiseHeaderBlock
) {
305 if (spdy_version() < SPDY4
)
307 SpdyHeaderBlock headers
;
308 headers
["alpha"] = "beta";
309 headers
["gamma"] = "delta";
310 BufferedSpdyFramer
framer(spdy_version(), true);
311 scoped_ptr
<SpdyFrame
> control_frame(
312 framer
.CreatePushPromise(1, 2, &headers
));
313 EXPECT_TRUE(control_frame
.get() != NULL
);
315 TestBufferedSpdyVisitor
visitor(spdy_version());
316 visitor
.SimulateInFramer(
317 reinterpret_cast<unsigned char*>(control_frame
.get()->data()),
318 control_frame
.get()->size());
319 EXPECT_EQ(0, visitor
.error_count_
);
320 EXPECT_EQ(0, visitor
.syn_frame_count_
);
321 EXPECT_EQ(0, visitor
.syn_reply_frame_count_
);
322 EXPECT_EQ(0, visitor
.headers_frame_count_
);
323 EXPECT_EQ(1, visitor
.push_promise_frame_count_
);
324 EXPECT_TRUE(CompareHeaderBlocks(&headers
, &visitor
.headers_
));
325 EXPECT_EQ(1u, visitor
.header_stream_id_
);
326 EXPECT_EQ(2u, visitor
.promised_stream_id_
);