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.h"
8 #include "testing/platform_test.h"
14 class TestBufferedSpdyVisitor
: public BufferedSpdyFramerVisitorInterface
{
16 TestBufferedSpdyVisitor()
19 syn_reply_frame_count_(0),
20 headers_frame_count_(0),
21 header_stream_id_(-1) {
24 void OnError(int error_code
) {
25 LOG(INFO
) << "SpdyFramer Error: " << error_code
;
29 void OnStreamError(spdy::SpdyStreamId stream_id
,
30 const std::string
& description
) {
31 LOG(INFO
) << "SpdyFramer Error on stream: " << stream_id
<< " "
36 void OnSynStream(const SpdySynStreamControlFrame
& frame
,
37 const linked_ptr
<SpdyHeaderBlock
>& headers
) {
38 header_stream_id_
= frame
.stream_id();
39 EXPECT_NE(header_stream_id_
, SpdyFramer::kInvalidStream
);
44 void OnSynReply(const SpdySynReplyControlFrame
& frame
,
45 const linked_ptr
<SpdyHeaderBlock
>& headers
) {
46 header_stream_id_
= frame
.stream_id();
47 EXPECT_NE(header_stream_id_
, SpdyFramer::kInvalidStream
);
48 syn_reply_frame_count_
++;
52 void OnHeaders(const SpdyHeadersControlFrame
& frame
,
53 const linked_ptr
<SpdyHeaderBlock
>& headers
) {
54 header_stream_id_
= frame
.stream_id();
55 EXPECT_NE(header_stream_id_
, SpdyFramer::kInvalidStream
);
56 headers_frame_count_
++;
60 void OnStreamFrameData(SpdyStreamId stream_id
,
63 LOG(FATAL
) << "Unexpected OnStreamFrameData call.";
66 bool OnCredentialFrameData(const char*, size_t) {
67 LOG(FATAL
) << "Unexpected OnCredentialFrameData call.";
71 void OnDataFrameHeader(const SpdyDataFrame
* frame
) {
72 LOG(FATAL
) << "Unexpected OnDataFrameHeader call.";
75 void OnControl(const SpdyControlFrame
* frame
) {
76 uint32 type
= frame
->type();
81 header_stream_id_
= SpdyFramer::GetControlFrameStreamId(frame
);
82 EXPECT_NE(header_stream_id_
, SpdyFramer::kInvalidStream
);
83 buffered_spdy_framer_
.OnControl(frame
);
86 LOG(FATAL
) << "Unexpected frame type." << type
;
90 void OnRstStream(const spdy::SpdyRstStreamControlFrame
& frame
) {}
91 void OnGoAway(const spdy::SpdyGoAwayControlFrame
& frame
) {}
92 void OnPing(const spdy::SpdyPingControlFrame
& frame
) {}
93 void OnSettings(const spdy::SpdySettingsControlFrame
& frame
) {}
94 void OnWindowUpdate(const spdy::SpdyWindowUpdateControlFrame
& frame
) {}
95 void OnCredential(const spdy::SpdyCredentialControlFrame
& frame
) {}
97 // Convenience function which runs a framer simulation with particular input.
98 void SimulateInFramer(const unsigned char* input
, size_t size
) {
99 buffered_spdy_framer_
.set_visitor(this);
100 size_t input_remaining
= size
;
101 const char* input_ptr
= reinterpret_cast<const char*>(input
);
102 while (input_remaining
> 0 &&
103 buffered_spdy_framer_
.error_code() == SpdyFramer::SPDY_NO_ERROR
) {
104 // To make the tests more interesting, we feed random (amd small) chunks
105 // into the framer. This simulates getting strange-sized reads from
107 const size_t kMaxReadSize
= 32;
109 (rand() % std::min(input_remaining
, kMaxReadSize
)) + 1;
110 size_t bytes_processed
=
111 buffered_spdy_framer_
.ProcessInput(input_ptr
, bytes_read
);
112 input_remaining
-= bytes_processed
;
113 input_ptr
+= bytes_processed
;
114 if (buffered_spdy_framer_
.state() == SpdyFramer::SPDY_DONE
)
115 buffered_spdy_framer_
.Reset();
119 BufferedSpdyFramer buffered_spdy_framer_
;
121 // Counters from the visitor callbacks.
123 int syn_frame_count_
;
124 int syn_reply_frame_count_
;
125 int headers_frame_count_
;
127 // Header block streaming state:
128 SpdyStreamId header_stream_id_
;
130 // Headers from OnSyn, OnSynReply and OnHeaders for verification.
131 SpdyHeaderBlock headers_
;
138 using spdy::test::TestBufferedSpdyVisitor
;
142 class BufferedSpdyFramerTest
: public PlatformTest
{
144 void EnableCompression(bool enabled
) {
145 SpdyFramer::set_enable_compression_default(enabled
);
148 // Returns true if the two header blocks have equivalent content.
149 bool CompareHeaderBlocks(const SpdyHeaderBlock
* expected
,
150 const SpdyHeaderBlock
* actual
) {
151 if (expected
->size() != actual
->size()) {
152 LOG(ERROR
) << "Expected " << expected
->size() << " headers; actually got "
153 << actual
->size() << ".";
156 for (SpdyHeaderBlock::const_iterator it
= expected
->begin();
157 it
!= expected
->end();
159 SpdyHeaderBlock::const_iterator it2
= actual
->find(it
->first
);
160 if (it2
== actual
->end()) {
161 LOG(ERROR
) << "Expected header name '" << it
->first
<< "'.";
164 if (it
->second
.compare(it2
->second
) != 0) {
165 LOG(ERROR
) << "Expected header named '" << it
->first
166 << "' to have a value of '" << it
->second
167 << "'. The actual value received was '" << it2
->second
176 TEST_F(BufferedSpdyFramerTest
, ReadSynStreamHeaderBlock
) {
177 EnableCompression(false);
179 SpdyHeaderBlock headers
;
180 headers
["aa"] = "vv";
181 headers
["bb"] = "ww";
182 BufferedSpdyFramer framer
;
183 scoped_ptr
<SpdySynStreamControlFrame
> control_frame(
184 framer
.CreateSynStream(1, // stream_id
185 0, // associated_stream_id
190 EXPECT_TRUE(control_frame
.get() != NULL
);
192 TestBufferedSpdyVisitor visitor
;
193 visitor
.SimulateInFramer(
194 reinterpret_cast<unsigned char*>(control_frame
.get()->data()),
195 control_frame
.get()->length() + SpdyControlFrame::kHeaderSize
);
196 EXPECT_EQ(0, visitor
.error_count_
);
197 EXPECT_EQ(1, visitor
.syn_frame_count_
);
198 EXPECT_EQ(0, visitor
.syn_reply_frame_count_
);
199 EXPECT_EQ(0, visitor
.headers_frame_count_
);
200 EXPECT_TRUE(CompareHeaderBlocks(&headers
, &visitor
.headers_
));
203 TEST_F(BufferedSpdyFramerTest
, ReadSynReplyHeaderBlock
) {
204 EnableCompression(false);
206 SpdyHeaderBlock headers
;
207 headers
["alpha"] = "beta";
208 headers
["gamma"] = "delta";
209 BufferedSpdyFramer framer
;
210 scoped_ptr
<SpdySynReplyControlFrame
> control_frame(
211 framer
.CreateSynReply(1, // stream_id
215 EXPECT_TRUE(control_frame
.get() != NULL
);
217 TestBufferedSpdyVisitor visitor
;
218 visitor
.SimulateInFramer(
219 reinterpret_cast<unsigned char*>(control_frame
.get()->data()),
220 control_frame
.get()->length() + SpdyControlFrame::kHeaderSize
);
221 EXPECT_EQ(0, visitor
.error_count_
);
222 EXPECT_EQ(0, visitor
.syn_frame_count_
);
223 EXPECT_EQ(1, visitor
.syn_reply_frame_count_
);
224 EXPECT_EQ(0, visitor
.headers_frame_count_
);
225 EXPECT_TRUE(CompareHeaderBlocks(&headers
, &visitor
.headers_
));
228 TEST_F(BufferedSpdyFramerTest
, ReadHeadersHeaderBlock
) {
229 EnableCompression(false);
231 SpdyHeaderBlock headers
;
232 headers
["alpha"] = "beta";
233 headers
["gamma"] = "delta";
234 BufferedSpdyFramer framer
;
235 scoped_ptr
<SpdyHeadersControlFrame
> control_frame(
236 framer
.CreateHeaders(1, // stream_id
240 EXPECT_TRUE(control_frame
.get() != NULL
);
242 TestBufferedSpdyVisitor visitor
;
243 visitor
.SimulateInFramer(
244 reinterpret_cast<unsigned char*>(control_frame
.get()->data()),
245 control_frame
.get()->length() + SpdyControlFrame::kHeaderSize
);
246 EXPECT_EQ(0, visitor
.error_count_
);
247 EXPECT_EQ(0, visitor
.syn_frame_count_
);
248 EXPECT_EQ(0, visitor
.syn_reply_frame_count_
);
249 EXPECT_EQ(1, visitor
.headers_frame_count_
);
250 EXPECT_TRUE(CompareHeaderBlocks(&headers
, &visitor
.headers_
));