1 // Copyright 2013 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/quic/quic_headers_stream.h"
7 #include "net/quic/quic_utils.h"
8 #include "net/quic/spdy_utils.h"
9 #include "net/quic/test_tools/quic_connection_peer.h"
10 #include "net/quic/test_tools/quic_spdy_session_peer.h"
11 #include "net/quic/test_tools/quic_test_utils.h"
12 #include "net/quic/test_tools/reliable_quic_stream_peer.h"
13 #include "net/spdy/spdy_protocol.h"
14 #include "net/spdy/spdy_test_utils.h"
15 #include "testing/gtest/include/gtest/gtest.h"
17 using base::StringPiece
;
21 using testing::Invoke
;
22 using testing::StrictMock
;
23 using testing::WithArgs
;
30 class MockVisitor
: public SpdyFramerVisitorInterface
{
32 MOCK_METHOD1(OnError
, void(SpdyFramer
* framer
));
33 MOCK_METHOD3(OnDataFrameHeader
, void(SpdyStreamId stream_id
,
36 MOCK_METHOD4(OnStreamFrameData
, void(SpdyStreamId stream_id
,
40 MOCK_METHOD2(OnStreamPadding
, void(SpdyStreamId stream_id
, size_t len
));
41 MOCK_METHOD3(OnControlFrameHeaderData
, bool(SpdyStreamId stream_id
,
42 const char* header_data
,
44 MOCK_METHOD5(OnSynStream
, void(SpdyStreamId stream_id
,
45 SpdyStreamId associated_stream_id
,
46 SpdyPriority priority
,
48 bool unidirectional
));
49 MOCK_METHOD2(OnSynReply
, void(SpdyStreamId stream_id
, bool fin
));
50 MOCK_METHOD2(OnRstStream
, void(SpdyStreamId stream_id
,
51 SpdyRstStreamStatus status
));
52 MOCK_METHOD1(OnSettings
, void(bool clear_persisted
));
53 MOCK_METHOD3(OnSetting
, void(SpdySettingsIds id
, uint8 flags
, uint32 value
));
54 MOCK_METHOD0(OnSettingsAck
, void());
55 MOCK_METHOD0(OnSettingsEnd
, void());
56 MOCK_METHOD2(OnPing
, void(SpdyPingId unique_id
, bool is_ack
));
57 MOCK_METHOD2(OnGoAway
, void(SpdyStreamId last_accepted_stream_id
,
58 SpdyGoAwayStatus status
));
59 MOCK_METHOD7(OnHeaders
,
60 void(SpdyStreamId stream_id
,
62 SpdyPriority priority
,
63 SpdyStreamId parent_stream_id
,
67 MOCK_METHOD2(OnWindowUpdate
,
68 void(SpdyStreamId stream_id
, int delta_window_size
));
69 MOCK_METHOD2(OnCredentialFrameData
, bool(const char* credential_data
,
71 MOCK_METHOD1(OnBlocked
, void(SpdyStreamId stream_id
));
72 MOCK_METHOD3(OnPushPromise
, void(SpdyStreamId stream_id
,
73 SpdyStreamId promised_stream_id
,
75 MOCK_METHOD2(OnContinuation
, void(SpdyStreamId stream_id
, bool end
));
76 MOCK_METHOD3(OnAltSvc
,
77 void(SpdyStreamId stream_id
,
79 const SpdyAltSvcWireFormat::AlternativeServiceVector
&
81 MOCK_METHOD2(OnUnknownFrame
, bool(SpdyStreamId stream_id
, int frame_type
));
84 // Run all tests with each version, and client or server
86 TestParams(QuicVersion version
, Perspective perspective
)
87 : version(version
), perspective(perspective
) {}
89 friend ostream
& operator<<(ostream
& os
, const TestParams
& p
) {
90 os
<< "{ version: " << QuicVersionToString(p
.version
);
91 os
<< ", perspective: " << p
.perspective
<< " }";
96 Perspective perspective
;
99 // Constructs various test permutations.
100 vector
<TestParams
> GetTestParams() {
101 vector
<TestParams
> params
;
102 QuicVersionVector all_supported_versions
= QuicSupportedVersions();
103 for (const QuicVersion version
: all_supported_versions
) {
104 params
.push_back(TestParams(version
, Perspective::IS_CLIENT
));
105 params
.push_back(TestParams(version
, Perspective::IS_SERVER
));
110 class QuicHeadersStreamTest
: public ::testing::TestWithParam
<TestParams
> {
112 QuicHeadersStreamTest()
114 new StrictMock
<MockConnection
>(perspective(), GetVersion())),
115 session_(connection_
),
116 headers_stream_(QuicSpdySessionPeer::GetHeadersStream(&session_
)),
117 body_("hello world"),
119 stream_frame_(kHeadersStreamId
, /*fin=*/false, /*offset=*/0, "") {
120 headers_
[":version"] = "HTTP/1.1";
121 headers_
[":status"] = "200 Ok";
122 headers_
["content-length"] = "11";
123 framer_
.set_visitor(&visitor_
);
124 EXPECT_EQ(version(), session_
.connection()->version());
125 EXPECT_TRUE(headers_stream_
!= nullptr);
126 VLOG(1) << GetParam();
129 QuicConsumedData
SaveIov(const QuicIOVector
& data
) {
130 const iovec
* iov
= data
.iov
;
131 int count
= data
.iov_count
;
132 for (int i
= 0 ; i
< count
; ++i
) {
133 saved_data_
.append(static_cast<char*>(iov
[i
].iov_base
), iov
[i
].iov_len
);
135 return QuicConsumedData(saved_data_
.length(), false);
138 bool SaveHeaderData(const char* data
, int len
) {
139 saved_header_data_
.append(data
, len
);
143 void SaveHeaderDataStringPiece(StringPiece data
) {
144 saved_header_data_
.append(data
.data(), data
.length());
147 void WriteHeadersAndExpectSynStream(QuicStreamId stream_id
,
149 QuicPriority priority
) {
150 WriteHeadersAndCheckData(stream_id
, fin
, priority
, SYN_STREAM
);
153 void WriteHeadersAndExpectSynReply(QuicStreamId stream_id
,
155 WriteHeadersAndCheckData(stream_id
, fin
, 0, SYN_REPLY
);
158 void WriteHeadersAndCheckData(QuicStreamId stream_id
,
160 QuicPriority priority
,
161 SpdyFrameType type
) {
162 // Write the headers and capture the outgoing data
163 EXPECT_CALL(session_
, WritevData(kHeadersStreamId
, _
, _
, false, _
, nullptr))
164 .WillOnce(WithArgs
<1>(Invoke(this, &QuicHeadersStreamTest::SaveIov
)));
165 headers_stream_
->WriteHeaders(stream_id
, headers_
, fin
, priority
, nullptr);
167 // Parse the outgoing data and check that it matches was was written.
168 if (type
== SYN_STREAM
) {
169 EXPECT_CALL(visitor_
, OnHeaders(stream_id
, kHasPriority
, priority
,
170 /*parent_stream_id=*/0,
173 fin
, kFrameComplete
));
175 EXPECT_CALL(visitor_
,
176 OnHeaders(stream_id
, !kHasPriority
,
178 /*parent_stream_id=*/0,
179 /*exclusive=*/false, fin
, kFrameComplete
));
181 EXPECT_CALL(visitor_
, OnControlFrameHeaderData(stream_id
, _
, _
))
182 .WillRepeatedly(WithArgs
<1, 2>(
183 Invoke(this, &QuicHeadersStreamTest::SaveHeaderData
)));
185 EXPECT_CALL(visitor_
, OnStreamFrameData(stream_id
, nullptr, 0, true));
187 framer_
.ProcessInput(saved_data_
.data(), saved_data_
.length());
188 EXPECT_FALSE(framer_
.HasError())
189 << SpdyFramer::ErrorCodeToString(framer_
.error_code());
195 void CheckHeaders() {
196 SpdyHeaderBlock headers
;
197 EXPECT_EQ(saved_header_data_
.length(),
198 framer_
.ParseHeaderBlockInBuffer(saved_header_data_
.data(),
199 saved_header_data_
.length(),
201 EXPECT_TRUE(CompareSpdyHeaderBlocks(headers_
, headers
));
202 saved_header_data_
.clear();
205 Perspective
perspective() { return GetParam().perspective
; }
207 QuicVersion
version() { return GetParam().version
; }
209 QuicVersionVector
GetVersion() {
210 QuicVersionVector versions
;
211 versions
.push_back(version());
215 void CloseConnection() {
216 QuicConnectionPeer::CloseConnection(connection_
);
219 static const bool kFrameComplete
= true;
220 static const bool kHasPriority
= true;
222 StrictMock
<MockConnection
>* connection_
;
223 StrictMock
<MockQuicSpdySession
> session_
;
224 QuicHeadersStream
* headers_stream_
;
225 SpdyHeaderBlock headers_
;
228 string saved_header_data_
;
230 StrictMock
<MockVisitor
> visitor_
;
231 QuicStreamFrame stream_frame_
;
234 INSTANTIATE_TEST_CASE_P(Tests
,
235 QuicHeadersStreamTest
,
236 ::testing::ValuesIn(GetTestParams()));
238 TEST_P(QuicHeadersStreamTest
, StreamId
) {
239 EXPECT_EQ(3u, headers_stream_
->id());
242 TEST_P(QuicHeadersStreamTest
, EffectivePriority
) {
243 EXPECT_EQ(0u, headers_stream_
->EffectivePriority());
246 TEST_P(QuicHeadersStreamTest
, WriteHeaders
) {
247 for (QuicStreamId stream_id
= kClientDataStreamId1
;
248 stream_id
< kClientDataStreamId3
; stream_id
+= 2) {
249 for (int count
= 0; count
< 2; ++count
) {
250 bool fin
= (count
== 0);
251 if (perspective() == Perspective::IS_SERVER
) {
252 WriteHeadersAndExpectSynReply(stream_id
, fin
);
254 for (QuicPriority priority
= 0; priority
< 7; ++priority
) {
255 // TODO(rch): implement priorities correctly.
256 WriteHeadersAndExpectSynStream(stream_id
, fin
, 0);
263 TEST_P(QuicHeadersStreamTest
, ProcessRawData
) {
264 for (QuicStreamId stream_id
= kClientDataStreamId1
;
265 stream_id
< kClientDataStreamId3
; stream_id
+= 2) {
266 for (int count
= 0; count
< 2; ++count
) {
267 bool fin
= (count
== 0);
268 for (QuicPriority priority
= 0; priority
< 7; ++priority
) {
269 // Replace with "WriteHeadersAndSaveData"
270 scoped_ptr
<SpdySerializedFrame
> frame
;
271 if (perspective() == Perspective::IS_SERVER
) {
272 SpdyHeadersIR
headers_frame(stream_id
);
273 headers_frame
.set_header_block(headers_
);
274 headers_frame
.set_fin(fin
);
275 headers_frame
.set_has_priority(true);
276 frame
.reset(framer_
.SerializeFrame(headers_frame
));
277 EXPECT_CALL(session_
, OnStreamHeadersPriority(stream_id
, 0));
279 SpdyHeadersIR
headers_frame(stream_id
);
280 headers_frame
.set_header_block(headers_
);
281 headers_frame
.set_fin(fin
);
282 frame
.reset(framer_
.SerializeFrame(headers_frame
));
284 EXPECT_CALL(session_
, OnStreamHeaders(stream_id
, _
))
285 .WillRepeatedly(WithArgs
<1>(
287 &QuicHeadersStreamTest::SaveHeaderDataStringPiece
)));
288 EXPECT_CALL(session_
,
289 OnStreamHeadersComplete(stream_id
, fin
, frame
->size()));
290 stream_frame_
.data
= StringPiece(frame
->data(), frame
->size());
291 headers_stream_
->OnStreamFrame(stream_frame_
);
292 stream_frame_
.offset
+= frame
->size();
299 TEST_P(QuicHeadersStreamTest
, ProcessLargeRawData
) {
300 // We want to create a frame that is more than the SPDY Framer's max control
301 // frame size, which is 16K, but less than the HPACK decoders max decode
302 // buffer size, which is 32K.
303 headers_
["key0"] = string(1 << 13, '.');
304 headers_
["key1"] = string(1 << 13, '.');
305 headers_
["key2"] = string(1 << 13, '.');
306 for (QuicStreamId stream_id
= kClientDataStreamId1
;
307 stream_id
< kClientDataStreamId3
; stream_id
+= 2) {
308 for (int count
= 0; count
< 2; ++count
) {
309 bool fin
= (count
== 0);
310 for (QuicPriority priority
= 0; priority
< 7; ++priority
) {
311 // Replace with "WriteHeadersAndSaveData"
312 scoped_ptr
<SpdySerializedFrame
> frame
;
313 if (perspective() == Perspective::IS_SERVER
) {
314 SpdyHeadersIR
headers_frame(stream_id
);
315 headers_frame
.set_header_block(headers_
);
316 headers_frame
.set_fin(fin
);
317 headers_frame
.set_has_priority(true);
318 frame
.reset(framer_
.SerializeFrame(headers_frame
));
319 EXPECT_CALL(session_
, OnStreamHeadersPriority(stream_id
, 0));
321 SpdyHeadersIR
headers_frame(stream_id
);
322 headers_frame
.set_header_block(headers_
);
323 headers_frame
.set_fin(fin
);
324 frame
.reset(framer_
.SerializeFrame(headers_frame
));
326 EXPECT_CALL(session_
, OnStreamHeaders(stream_id
, _
))
327 .WillRepeatedly(WithArgs
<1>(Invoke(
328 this, &QuicHeadersStreamTest::SaveHeaderDataStringPiece
)));
329 EXPECT_CALL(session_
,
330 OnStreamHeadersComplete(stream_id
, fin
, frame
->size()));
331 stream_frame_
.data
= StringPiece(frame
->data(), frame
->size());
332 headers_stream_
->OnStreamFrame(stream_frame_
);
333 stream_frame_
.offset
+= frame
->size();
340 TEST_P(QuicHeadersStreamTest
, ProcessBadData
) {
341 const char kBadData
[] = "blah blah blah";
342 EXPECT_CALL(*connection_
, SendConnectionCloseWithDetails(
343 QUIC_INVALID_HEADERS_STREAM_DATA
, _
))
344 .Times(::testing::AnyNumber());
345 stream_frame_
.data
= StringPiece(kBadData
, strlen(kBadData
));
346 headers_stream_
->OnStreamFrame(stream_frame_
);
349 TEST_P(QuicHeadersStreamTest
, ProcessSpdyDataFrame
) {
350 SpdyDataIR
data(2, "");
351 scoped_ptr
<SpdySerializedFrame
> frame(framer_
.SerializeFrame(data
));
352 EXPECT_CALL(*connection_
,
353 SendConnectionCloseWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA
,
354 "SPDY DATA frame received."))
355 .WillOnce(InvokeWithoutArgs(this,
356 &QuicHeadersStreamTest::CloseConnection
));
357 stream_frame_
.data
= StringPiece(frame
->data(), frame
->size());
358 headers_stream_
->OnStreamFrame(stream_frame_
);
361 TEST_P(QuicHeadersStreamTest
, ProcessSpdyRstStreamFrame
) {
362 SpdyRstStreamIR
data(2, RST_STREAM_PROTOCOL_ERROR
, "");
363 scoped_ptr
<SpdySerializedFrame
> frame(framer_
.SerializeFrame(data
));
364 EXPECT_CALL(*connection_
,
365 SendConnectionCloseWithDetails(
366 QUIC_INVALID_HEADERS_STREAM_DATA
,
367 "SPDY RST_STREAM frame received."))
368 .WillOnce(InvokeWithoutArgs(this,
369 &QuicHeadersStreamTest::CloseConnection
));
370 stream_frame_
.data
= StringPiece(frame
->data(), frame
->size());
371 headers_stream_
->OnStreamFrame(stream_frame_
);
374 TEST_P(QuicHeadersStreamTest
, ProcessSpdySettingsFrame
) {
376 data
.AddSetting(SETTINGS_HEADER_TABLE_SIZE
, true, true, 0);
377 scoped_ptr
<SpdySerializedFrame
> frame(framer_
.SerializeFrame(data
));
378 EXPECT_CALL(*connection_
,
379 SendConnectionCloseWithDetails(
380 QUIC_INVALID_HEADERS_STREAM_DATA
,
381 "SPDY SETTINGS frame received."))
382 .WillOnce(InvokeWithoutArgs(this,
383 &QuicHeadersStreamTest::CloseConnection
));
384 stream_frame_
.data
= StringPiece(frame
->data(), frame
->size());
385 headers_stream_
->OnStreamFrame(stream_frame_
);
388 TEST_P(QuicHeadersStreamTest
, ProcessSpdyPingFrame
) {
390 scoped_ptr
<SpdySerializedFrame
> frame(framer_
.SerializeFrame(data
));
391 EXPECT_CALL(*connection_
,
392 SendConnectionCloseWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA
,
393 "SPDY PING frame received."))
394 .WillOnce(InvokeWithoutArgs(this,
395 &QuicHeadersStreamTest::CloseConnection
));
396 stream_frame_
.data
= StringPiece(frame
->data(), frame
->size());
397 headers_stream_
->OnStreamFrame(stream_frame_
);
400 TEST_P(QuicHeadersStreamTest
, ProcessSpdyGoAwayFrame
) {
401 SpdyGoAwayIR
data(1, GOAWAY_PROTOCOL_ERROR
, "go away");
402 scoped_ptr
<SpdySerializedFrame
> frame(framer_
.SerializeFrame(data
));
403 EXPECT_CALL(*connection_
,
404 SendConnectionCloseWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA
,
405 "SPDY GOAWAY frame received."))
406 .WillOnce(InvokeWithoutArgs(this,
407 &QuicHeadersStreamTest::CloseConnection
));
408 stream_frame_
.data
= StringPiece(frame
->data(), frame
->size());
409 headers_stream_
->OnStreamFrame(stream_frame_
);
412 TEST_P(QuicHeadersStreamTest
, ProcessSpdyWindowUpdateFrame
) {
413 SpdyWindowUpdateIR
data(1, 1);
414 scoped_ptr
<SpdySerializedFrame
> frame(framer_
.SerializeFrame(data
));
415 EXPECT_CALL(*connection_
,
416 SendConnectionCloseWithDetails(
417 QUIC_INVALID_HEADERS_STREAM_DATA
,
418 "SPDY WINDOW_UPDATE frame received."))
419 .WillOnce(InvokeWithoutArgs(this,
420 &QuicHeadersStreamTest::CloseConnection
));
421 stream_frame_
.data
= StringPiece(frame
->data(), frame
->size());
422 headers_stream_
->OnStreamFrame(stream_frame_
);
425 TEST_P(QuicHeadersStreamTest
, NoConnectionLevelFlowControl
) {
426 EXPECT_FALSE(ReliableQuicStreamPeer::StreamContributesToConnectionFlowControl(