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 void OnError(SpdyFramer::SpdyError error_code
) override
{
29 LOG(INFO
) << "SpdyFramer Error: " << error_code
;
33 void OnStreamError(SpdyStreamId stream_id
,
34 const std::string
& description
) override
{
35 LOG(INFO
) << "SpdyFramer Error on stream: " << stream_id
<< " "
40 void OnSynStream(SpdyStreamId stream_id
,
41 SpdyStreamId associated_stream_id
,
42 SpdyPriority priority
,
45 const SpdyHeaderBlock
& headers
) override
{
46 header_stream_id_
= stream_id
;
47 EXPECT_NE(header_stream_id_
, SpdyFramer::kInvalidStream
);
52 void OnSynReply(SpdyStreamId stream_id
,
54 const SpdyHeaderBlock
& headers
) override
{
55 header_stream_id_
= stream_id
;
56 EXPECT_NE(header_stream_id_
, SpdyFramer::kInvalidStream
);
57 syn_reply_frame_count_
++;
61 void OnHeaders(SpdyStreamId stream_id
,
63 SpdyPriority priority
,
64 SpdyStreamId parent_stream_id
,
67 const SpdyHeaderBlock
& headers
) override
{
68 header_stream_id_
= stream_id
;
69 EXPECT_NE(header_stream_id_
, SpdyFramer::kInvalidStream
);
70 headers_frame_count_
++;
74 void OnDataFrameHeader(SpdyStreamId stream_id
,
77 ADD_FAILURE() << "Unexpected OnDataFrameHeader call.";
80 void OnStreamFrameData(SpdyStreamId stream_id
,
84 LOG(FATAL
) << "Unexpected OnStreamFrameData call.";
87 void OnStreamPadding(SpdyStreamId stream_id
, size_t len
) override
{
88 LOG(FATAL
) << "Unexpected OnStreamPadding call.";
91 void OnSettings(bool clear_persisted
) override
{}
93 void OnSetting(SpdySettingsIds id
, uint8 flags
, uint32 value
) override
{
97 void OnPing(SpdyPingId unique_id
, bool is_ack
) override
{}
99 void OnRstStream(SpdyStreamId stream_id
,
100 SpdyRstStreamStatus status
) override
{}
102 void OnGoAway(SpdyStreamId last_accepted_stream_id
,
103 SpdyGoAwayStatus status
) override
{}
105 bool OnCredentialFrameData(const char*, size_t) {
106 LOG(FATAL
) << "Unexpected OnCredentialFrameData call.";
110 void OnDataFrameHeader(const SpdyFrame
* frame
) {
111 LOG(FATAL
) << "Unexpected OnDataFrameHeader call.";
114 void OnRstStream(const SpdyFrame
& frame
) {}
115 void OnGoAway(const SpdyFrame
& frame
) {}
116 void OnPing(const SpdyFrame
& frame
) {}
117 void OnWindowUpdate(SpdyStreamId stream_id
, int delta_window_size
) override
{}
119 void OnPushPromise(SpdyStreamId stream_id
,
120 SpdyStreamId promised_stream_id
,
121 const SpdyHeaderBlock
& headers
) override
{
122 header_stream_id_
= stream_id
;
123 EXPECT_NE(header_stream_id_
, SpdyFramer::kInvalidStream
);
124 push_promise_frame_count_
++;
125 promised_stream_id_
= promised_stream_id
;
126 EXPECT_NE(promised_stream_id_
, SpdyFramer::kInvalidStream
);
130 bool OnUnknownFrame(SpdyStreamId stream_id
, int frame_type
) override
{
134 void OnCredential(const SpdyFrame
& frame
) {}
136 // Convenience function which runs a framer simulation with particular input.
137 void SimulateInFramer(const unsigned char* input
, size_t size
) {
138 buffered_spdy_framer_
.set_visitor(this);
139 size_t input_remaining
= size
;
140 const char* input_ptr
= reinterpret_cast<const char*>(input
);
141 while (input_remaining
> 0 &&
142 buffered_spdy_framer_
.error_code() == SpdyFramer::SPDY_NO_ERROR
) {
143 // To make the tests more interesting, we feed random (amd small) chunks
144 // into the framer. This simulates getting strange-sized reads from
146 const size_t kMaxReadSize
= 32;
148 (rand() % std::min(input_remaining
, kMaxReadSize
)) + 1;
149 size_t bytes_processed
=
150 buffered_spdy_framer_
.ProcessInput(input_ptr
, bytes_read
);
151 input_remaining
-= bytes_processed
;
152 input_ptr
+= bytes_processed
;
156 BufferedSpdyFramer buffered_spdy_framer_
;
158 // Counters from the visitor callbacks.
161 int syn_frame_count_
;
162 int syn_reply_frame_count_
;
163 int headers_frame_count_
;
164 int push_promise_frame_count_
;
166 // Header block streaming state:
167 SpdyStreamId header_stream_id_
;
168 SpdyStreamId promised_stream_id_
;
170 // Headers from OnSyn, OnSynReply, OnHeaders and OnPushPromise for
172 SpdyHeaderBlock headers_
;
177 class BufferedSpdyFramerTest
178 : public PlatformTest
,
179 public ::testing::WithParamInterface
<NextProto
> {
181 // Returns true if the two header blocks have equivalent content.
182 bool CompareHeaderBlocks(const SpdyHeaderBlock
* expected
,
183 const SpdyHeaderBlock
* actual
) {
184 if (expected
->size() != actual
->size()) {
185 LOG(ERROR
) << "Expected " << expected
->size() << " headers; actually got "
186 << actual
->size() << ".";
189 for (SpdyHeaderBlock::const_iterator it
= expected
->begin();
190 it
!= expected
->end();
192 SpdyHeaderBlock::const_iterator it2
= actual
->find(it
->first
);
193 if (it2
== actual
->end()) {
194 LOG(ERROR
) << "Expected header name '" << it
->first
<< "'.";
197 if (it
->second
.compare(it2
->second
) != 0) {
198 LOG(ERROR
) << "Expected header named '" << it
->first
199 << "' to have a value of '" << it
->second
200 << "'. The actual value received was '" << it2
->second
208 SpdyMajorVersion
spdy_version() {
209 return NextProtoToSpdyMajorVersion(GetParam());
213 INSTANTIATE_TEST_CASE_P(NextProto
,
214 BufferedSpdyFramerTest
,
215 testing::Values(kProtoSPDY31
,
218 TEST_P(BufferedSpdyFramerTest
, OnSetting
) {
219 SpdyFramer
framer(spdy_version());
220 SpdySettingsIR settings_ir
;
221 settings_ir
.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE
, false, false, 2);
222 settings_ir
.AddSetting(SETTINGS_MAX_CONCURRENT_STREAMS
, false, false, 3);
223 scoped_ptr
<SpdyFrame
> control_frame(framer
.SerializeSettings(settings_ir
));
224 TestBufferedSpdyVisitor
visitor(spdy_version());
226 visitor
.SimulateInFramer(
227 reinterpret_cast<unsigned char*>(control_frame
->data()),
228 control_frame
->size());
229 EXPECT_EQ(0, visitor
.error_count_
);
230 EXPECT_EQ(2, visitor
.setting_count_
);
233 TEST_P(BufferedSpdyFramerTest
, ReadSynStreamHeaderBlock
) {
234 if (spdy_version() > SPDY3
) {
235 // SYN_STREAM not supported in SPDY>3.
238 SpdyHeaderBlock headers
;
239 headers
["aa"] = "vv";
240 headers
["bb"] = "ww";
241 BufferedSpdyFramer
framer(spdy_version(), true);
242 scoped_ptr
<SpdyFrame
> control_frame(
243 framer
.CreateSynStream(1, // stream_id
244 0, // associated_stream_id
248 EXPECT_TRUE(control_frame
.get() != NULL
);
250 TestBufferedSpdyVisitor
visitor(spdy_version());
251 visitor
.SimulateInFramer(
252 reinterpret_cast<unsigned char*>(control_frame
.get()->data()),
253 control_frame
.get()->size());
254 EXPECT_EQ(0, visitor
.error_count_
);
255 EXPECT_EQ(1, visitor
.syn_frame_count_
);
256 EXPECT_EQ(0, visitor
.syn_reply_frame_count_
);
257 EXPECT_EQ(0, visitor
.headers_frame_count_
);
258 EXPECT_EQ(0, visitor
.push_promise_frame_count_
);
259 EXPECT_TRUE(CompareHeaderBlocks(&headers
, &visitor
.headers_
));
262 TEST_P(BufferedSpdyFramerTest
, ReadSynReplyHeaderBlock
) {
263 if (spdy_version() > SPDY3
) {
264 // SYN_REPLY not supported in SPDY>3.
267 SpdyHeaderBlock headers
;
268 headers
["alpha"] = "beta";
269 headers
["gamma"] = "delta";
270 BufferedSpdyFramer
framer(spdy_version(), true);
271 scoped_ptr
<SpdyFrame
> control_frame(
272 framer
.CreateSynReply(1, // stream_id
275 EXPECT_TRUE(control_frame
.get() != NULL
);
277 TestBufferedSpdyVisitor
visitor(spdy_version());
278 visitor
.SimulateInFramer(
279 reinterpret_cast<unsigned char*>(control_frame
.get()->data()),
280 control_frame
.get()->size());
281 EXPECT_EQ(0, visitor
.error_count_
);
282 EXPECT_EQ(0, visitor
.syn_frame_count_
);
283 EXPECT_EQ(0, visitor
.push_promise_frame_count_
);
284 if (spdy_version() < HTTP2
) {
285 EXPECT_EQ(1, visitor
.syn_reply_frame_count_
);
286 EXPECT_EQ(0, visitor
.headers_frame_count_
);
288 EXPECT_EQ(0, visitor
.syn_reply_frame_count_
);
289 EXPECT_EQ(1, visitor
.headers_frame_count_
);
291 EXPECT_TRUE(CompareHeaderBlocks(&headers
, &visitor
.headers_
));
294 TEST_P(BufferedSpdyFramerTest
, ReadHeadersHeaderBlock
) {
295 SpdyHeaderBlock headers
;
296 headers
["alpha"] = "beta";
297 headers
["gamma"] = "delta";
298 BufferedSpdyFramer
framer(spdy_version(), true);
299 scoped_ptr
<SpdyFrame
> control_frame(
300 framer
.CreateHeaders(1, // stream_id
304 EXPECT_TRUE(control_frame
.get() != NULL
);
306 TestBufferedSpdyVisitor
visitor(spdy_version());
307 visitor
.SimulateInFramer(
308 reinterpret_cast<unsigned char*>(control_frame
.get()->data()),
309 control_frame
.get()->size());
310 EXPECT_EQ(0, visitor
.error_count_
);
311 EXPECT_EQ(0, visitor
.syn_frame_count_
);
312 EXPECT_EQ(0, visitor
.syn_reply_frame_count_
);
313 EXPECT_EQ(1, visitor
.headers_frame_count_
);
314 EXPECT_EQ(0, visitor
.push_promise_frame_count_
);
315 EXPECT_TRUE(CompareHeaderBlocks(&headers
, &visitor
.headers_
));
318 TEST_P(BufferedSpdyFramerTest
, ReadPushPromiseHeaderBlock
) {
319 if (spdy_version() < HTTP2
)
321 SpdyHeaderBlock headers
;
322 headers
["alpha"] = "beta";
323 headers
["gamma"] = "delta";
324 BufferedSpdyFramer
framer(spdy_version(), true);
325 scoped_ptr
<SpdyFrame
> control_frame(
326 framer
.CreatePushPromise(1, 2, &headers
));
327 EXPECT_TRUE(control_frame
.get() != NULL
);
329 TestBufferedSpdyVisitor
visitor(spdy_version());
330 visitor
.SimulateInFramer(
331 reinterpret_cast<unsigned char*>(control_frame
.get()->data()),
332 control_frame
.get()->size());
333 EXPECT_EQ(0, visitor
.error_count_
);
334 EXPECT_EQ(0, visitor
.syn_frame_count_
);
335 EXPECT_EQ(0, visitor
.syn_reply_frame_count_
);
336 EXPECT_EQ(0, visitor
.headers_frame_count_
);
337 EXPECT_EQ(1, visitor
.push_promise_frame_count_
);
338 EXPECT_TRUE(CompareHeaderBlocks(&headers
, &visitor
.headers_
));
339 EXPECT_EQ(1u, visitor
.header_stream_id_
);
340 EXPECT_EQ(2u, visitor
.promised_stream_id_
);