OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "net/quic/quic_headers_stream.h" | |
6 | |
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_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 "testing/gtest/include/gtest/gtest.h" | |
15 | |
16 using base::StringPiece; | |
17 using std::ostream; | |
18 using std::string; | |
19 using std::vector; | |
20 using testing::Invoke; | |
21 using testing::StrictMock; | |
22 using testing::WithArgs; | |
23 using testing::_; | |
24 | |
25 namespace net { | |
26 namespace test { | |
27 namespace { | |
28 | |
29 class MockVisitor : public SpdyFramerVisitorInterface { | |
30 public: | |
31 MOCK_METHOD1(OnError, void(SpdyFramer* framer)); | |
32 MOCK_METHOD3(OnDataFrameHeader, void(SpdyStreamId stream_id, | |
33 size_t length, | |
34 bool fin)); | |
35 MOCK_METHOD4(OnStreamFrameData, void(SpdyStreamId stream_id, | |
36 const char* data, | |
37 size_t len, | |
38 bool fin)); | |
39 MOCK_METHOD3(OnControlFrameHeaderData, bool(SpdyStreamId stream_id, | |
40 const char* header_data, | |
41 size_t len)); | |
42 MOCK_METHOD5(OnSynStream, void(SpdyStreamId stream_id, | |
43 SpdyStreamId associated_stream_id, | |
44 SpdyPriority priority, | |
45 bool fin, | |
46 bool unidirectional)); | |
47 MOCK_METHOD2(OnSynReply, void(SpdyStreamId stream_id, bool fin)); | |
48 MOCK_METHOD2(OnRstStream, void(SpdyStreamId stream_id, | |
49 SpdyRstStreamStatus status)); | |
50 MOCK_METHOD1(OnSettings, void(bool clear_persisted)); | |
51 MOCK_METHOD3(OnSetting, void(SpdySettingsIds id, uint8 flags, uint32 value)); | |
52 MOCK_METHOD0(OnSettingsAck, void()); | |
53 MOCK_METHOD0(OnSettingsEnd, void()); | |
54 MOCK_METHOD2(OnPing, void(SpdyPingId unique_id, bool is_ack)); | |
55 MOCK_METHOD2(OnGoAway, void(SpdyStreamId last_accepted_stream_id, | |
56 SpdyGoAwayStatus status)); | |
57 MOCK_METHOD5(OnHeaders, void(SpdyStreamId stream_id, bool has_priority, | |
58 SpdyPriority priority, bool fin, bool end)); | |
59 MOCK_METHOD2(OnWindowUpdate, void(SpdyStreamId stream_id, | |
60 uint32 delta_window_size)); | |
61 MOCK_METHOD2(OnCredentialFrameData, bool(const char* credential_data, | |
62 size_t len)); | |
63 MOCK_METHOD1(OnBlocked, void(SpdyStreamId stream_id)); | |
64 MOCK_METHOD3(OnPushPromise, void(SpdyStreamId stream_id, | |
65 SpdyStreamId promised_stream_id, | |
66 bool end)); | |
67 MOCK_METHOD2(OnContinuation, void(SpdyStreamId stream_id, bool end)); | |
68 MOCK_METHOD6(OnAltSvc, void(SpdyStreamId stream_id, | |
69 uint32 max_age, | |
70 uint16 port, | |
71 StringPiece protocol_id, | |
72 StringPiece host, | |
73 StringPiece origin)); | |
74 MOCK_METHOD2(OnUnknownFrame, bool(SpdyStreamId stream_id, int frame_type)); | |
75 }; | |
76 | |
77 // Run all tests with each version, and client or server | |
78 struct TestParams { | |
79 TestParams(QuicVersion version, bool is_server) | |
80 : version(version), is_server(is_server) {} | |
81 | |
82 friend ostream& operator<<(ostream& os, const TestParams& p) { | |
83 os << "{ version: " << QuicVersionToString(p.version); | |
84 os << ", is_server: " << p.is_server << " }"; | |
85 return os; | |
86 } | |
87 | |
88 QuicVersion version; | |
89 bool is_server; | |
90 }; | |
91 | |
92 // Constructs various test permutations. | |
93 vector<TestParams> GetTestParams() { | |
94 vector<TestParams> params; | |
95 QuicVersionVector all_supported_versions = QuicSupportedVersions(); | |
96 for (const QuicVersion version : all_supported_versions) { | |
97 params.push_back(TestParams(version, false)); | |
98 params.push_back(TestParams(version, true)); | |
99 } | |
100 return params; | |
101 } | |
102 | |
103 class QuicHeadersStreamTest : public ::testing::TestWithParam<TestParams> { | |
104 public: | |
105 QuicHeadersStreamTest() | |
106 : connection_(new StrictMock<MockConnection>(is_server(), GetVersion())), | |
107 session_(connection_), | |
108 headers_stream_(QuicSessionPeer::GetHeadersStream(&session_)), | |
109 body_("hello world"), | |
110 framer_(version() > QUIC_VERSION_23 ? SPDY4 : SPDY3) { | |
111 headers_[":version"] = "HTTP/1.1"; | |
112 headers_[":status"] = "200 Ok"; | |
113 headers_["content-length"] = "11"; | |
114 framer_.set_visitor(&visitor_); | |
115 EXPECT_EQ(version(), session_.connection()->version()); | |
116 EXPECT_TRUE(headers_stream_ != nullptr); | |
117 VLOG(1) << GetParam(); | |
118 } | |
119 | |
120 QuicConsumedData SaveIov(const IOVector& data) { | |
121 const iovec* iov = data.iovec(); | |
122 int count = data.Capacity(); | |
123 for (int i = 0 ; i < count; ++i) { | |
124 saved_data_.append(static_cast<char*>(iov[i].iov_base), iov[i].iov_len); | |
125 } | |
126 return QuicConsumedData(saved_data_.length(), false); | |
127 } | |
128 | |
129 bool SaveHeaderData(const char* data, int len) { | |
130 saved_header_data_.append(data, len); | |
131 return true; | |
132 } | |
133 | |
134 void SaveHeaderDataStringPiece(StringPiece data) { | |
135 saved_header_data_.append(data.data(), data.length()); | |
136 } | |
137 | |
138 void WriteHeadersAndExpectSynStream(QuicStreamId stream_id, | |
139 bool fin, | |
140 QuicPriority priority) { | |
141 WriteHeadersAndCheckData(stream_id, fin, priority, SYN_STREAM); | |
142 } | |
143 | |
144 void WriteHeadersAndExpectSynReply(QuicStreamId stream_id, | |
145 bool fin) { | |
146 WriteHeadersAndCheckData(stream_id, fin, 0, SYN_REPLY); | |
147 } | |
148 | |
149 void WriteHeadersAndCheckData(QuicStreamId stream_id, | |
150 bool fin, | |
151 QuicPriority priority, | |
152 SpdyFrameType type) { | |
153 // Write the headers and capture the outgoing data | |
154 EXPECT_CALL(session_, WritevData(kHeadersStreamId, _, _, false, _, nullptr)) | |
155 .WillOnce(WithArgs<1>(Invoke(this, &QuicHeadersStreamTest::SaveIov))); | |
156 headers_stream_->WriteHeaders(stream_id, headers_, fin, priority, nullptr); | |
157 | |
158 // Parse the outgoing data and check that it matches was was written. | |
159 if (type == SYN_STREAM) { | |
160 if (version() > QUIC_VERSION_23) { | |
161 EXPECT_CALL(visitor_, OnHeaders(stream_id, kHasPriority, priority, fin, | |
162 kFrameComplete)); | |
163 } else { | |
164 EXPECT_CALL(visitor_, | |
165 OnSynStream(stream_id, kNoAssociatedStream, | |
166 /*priority=*/0, fin, kNotUnidirectional)); | |
167 } | |
168 } else { | |
169 if (version() > QUIC_VERSION_23) { | |
170 EXPECT_CALL(visitor_, OnHeaders(stream_id, !kHasPriority, | |
171 /*priority=*/0, fin, kFrameComplete)); | |
172 } else { | |
173 EXPECT_CALL(visitor_, OnSynReply(stream_id, fin)); | |
174 } | |
175 } | |
176 EXPECT_CALL(visitor_, OnControlFrameHeaderData(stream_id, _, _)) | |
177 .WillRepeatedly(WithArgs<1, 2>( | |
178 Invoke(this, &QuicHeadersStreamTest::SaveHeaderData))); | |
179 if (fin) { | |
180 EXPECT_CALL(visitor_, OnStreamFrameData(stream_id, nullptr, 0, true)); | |
181 } | |
182 framer_.ProcessInput(saved_data_.data(), saved_data_.length()); | |
183 EXPECT_FALSE(framer_.HasError()) | |
184 << SpdyFramer::ErrorCodeToString(framer_.error_code()); | |
185 | |
186 CheckHeaders(); | |
187 saved_data_.clear(); | |
188 } | |
189 | |
190 void CheckHeaders() { | |
191 SpdyHeaderBlock headers; | |
192 EXPECT_EQ(saved_header_data_.length(), | |
193 framer_.ParseHeaderBlockInBuffer(saved_header_data_.data(), | |
194 saved_header_data_.length(), | |
195 &headers)); | |
196 EXPECT_EQ(headers_, headers); | |
197 saved_header_data_.clear(); | |
198 } | |
199 | |
200 bool is_server() { return GetParam().is_server; } | |
201 | |
202 QuicVersion version() { return GetParam().version; } | |
203 | |
204 QuicVersionVector GetVersion() { | |
205 QuicVersionVector versions; | |
206 versions.push_back(version()); | |
207 return versions; | |
208 } | |
209 | |
210 void CloseConnection() { | |
211 QuicConnectionPeer::CloseConnection(connection_); | |
212 } | |
213 | |
214 static const bool kFrameComplete = true; | |
215 static const bool kHasPriority = true; | |
216 static const bool kNotUnidirectional = false; | |
217 static const bool kNoAssociatedStream = false; | |
218 | |
219 StrictMock<MockConnection>* connection_; | |
220 StrictMock<MockSession> session_; | |
221 QuicHeadersStream* headers_stream_; | |
222 SpdyHeaderBlock headers_; | |
223 string body_; | |
224 string saved_data_; | |
225 string saved_header_data_; | |
226 SpdyFramer framer_; | |
227 StrictMock<MockVisitor> visitor_; | |
228 }; | |
229 | |
230 INSTANTIATE_TEST_CASE_P(Tests, | |
231 QuicHeadersStreamTest, | |
232 ::testing::ValuesIn(GetTestParams())); | |
233 | |
234 TEST_P(QuicHeadersStreamTest, StreamId) { | |
235 EXPECT_EQ(3u, headers_stream_->id()); | |
236 } | |
237 | |
238 TEST_P(QuicHeadersStreamTest, EffectivePriority) { | |
239 EXPECT_EQ(0u, headers_stream_->EffectivePriority()); | |
240 } | |
241 | |
242 TEST_P(QuicHeadersStreamTest, WriteHeaders) { | |
243 for (QuicStreamId stream_id = kClientDataStreamId1; | |
244 stream_id < kClientDataStreamId3; stream_id += 2) { | |
245 for (int count = 0; count < 2; ++count) { | |
246 bool fin = (count == 0); | |
247 if (is_server()) { | |
248 WriteHeadersAndExpectSynReply(stream_id, fin); | |
249 } else { | |
250 for (QuicPriority priority = 0; priority < 7; ++priority) { | |
251 // TODO(rch): implement priorities correctly. | |
252 WriteHeadersAndExpectSynStream(stream_id, fin, 0); | |
253 } | |
254 } | |
255 } | |
256 } | |
257 } | |
258 | |
259 TEST_P(QuicHeadersStreamTest, ProcessRawData) { | |
260 for (QuicStreamId stream_id = kClientDataStreamId1; | |
261 stream_id < kClientDataStreamId3; stream_id += 2) { | |
262 for (int count = 0; count < 2; ++count) { | |
263 bool fin = (count == 0); | |
264 for (QuicPriority priority = 0; priority < 7; ++priority) { | |
265 // Replace with "WriteHeadersAndSaveData" | |
266 scoped_ptr<SpdySerializedFrame> frame; | |
267 if (is_server()) { | |
268 if (version() > QUIC_VERSION_23) { | |
269 SpdyHeadersIR headers_frame(stream_id); | |
270 headers_frame.set_name_value_block(headers_); | |
271 headers_frame.set_fin(fin); | |
272 headers_frame.set_has_priority(true); | |
273 frame.reset(framer_.SerializeFrame(headers_frame)); | |
274 } else { | |
275 SpdySynStreamIR syn_stream(stream_id); | |
276 syn_stream.set_name_value_block(headers_); | |
277 syn_stream.set_fin(fin); | |
278 frame.reset(framer_.SerializeSynStream(syn_stream)); | |
279 } | |
280 EXPECT_CALL(session_, OnStreamHeadersPriority(stream_id, 0)); | |
281 } else { | |
282 if (version() > QUIC_VERSION_23) { | |
283 SpdyHeadersIR headers_frame(stream_id); | |
284 headers_frame.set_name_value_block(headers_); | |
285 headers_frame.set_fin(fin); | |
286 frame.reset(framer_.SerializeFrame(headers_frame)); | |
287 } else { | |
288 SpdySynReplyIR syn_reply(stream_id); | |
289 syn_reply.set_name_value_block(headers_); | |
290 syn_reply.set_fin(fin); | |
291 frame.reset(framer_.SerializeSynReply(syn_reply)); | |
292 } | |
293 } | |
294 EXPECT_CALL(session_, OnStreamHeaders(stream_id, _)) | |
295 .WillRepeatedly(WithArgs<1>( | |
296 Invoke(this, | |
297 &QuicHeadersStreamTest::SaveHeaderDataStringPiece))); | |
298 EXPECT_CALL(session_, | |
299 OnStreamHeadersComplete(stream_id, fin, frame->size())); | |
300 headers_stream_->ProcessRawData(frame->data(), frame->size()); | |
301 CheckHeaders(); | |
302 } | |
303 } | |
304 } | |
305 } | |
306 | |
307 TEST_P(QuicHeadersStreamTest, ProcessLargeRawData) { | |
308 // We want to create a frame that is more than the SPDY Framer's max control | |
309 // frame size, which is 16K, but less than the HPACK decoders max decode | |
310 // buffer size, which is 32K. | |
311 headers_["key0"] = string(1 << 13, '.'); | |
312 headers_["key1"] = string(1 << 13, '.'); | |
313 headers_["key2"] = string(1 << 13, '.'); | |
314 for (QuicStreamId stream_id = kClientDataStreamId1; | |
315 stream_id < kClientDataStreamId3; stream_id += 2) { | |
316 for (int count = 0; count < 2; ++count) { | |
317 bool fin = (count == 0); | |
318 for (QuicPriority priority = 0; priority < 7; ++priority) { | |
319 // Replace with "WriteHeadersAndSaveData" | |
320 scoped_ptr<SpdySerializedFrame> frame; | |
321 if (is_server()) { | |
322 if (version() > QUIC_VERSION_23) { | |
323 SpdyHeadersIR headers_frame(stream_id); | |
324 headers_frame.set_name_value_block(headers_); | |
325 headers_frame.set_fin(fin); | |
326 headers_frame.set_has_priority(true); | |
327 frame.reset(framer_.SerializeFrame(headers_frame)); | |
328 } else { | |
329 SpdySynStreamIR syn_stream(stream_id); | |
330 syn_stream.set_name_value_block(headers_); | |
331 syn_stream.set_fin(fin); | |
332 frame.reset(framer_.SerializeSynStream(syn_stream)); | |
333 } | |
334 EXPECT_CALL(session_, OnStreamHeadersPriority(stream_id, 0)); | |
335 } else { | |
336 if (version() > QUIC_VERSION_23) { | |
337 SpdyHeadersIR headers_frame(stream_id); | |
338 headers_frame.set_name_value_block(headers_); | |
339 headers_frame.set_fin(fin); | |
340 frame.reset(framer_.SerializeFrame(headers_frame)); | |
341 } else { | |
342 SpdySynReplyIR syn_reply(stream_id); | |
343 syn_reply.set_name_value_block(headers_); | |
344 syn_reply.set_fin(fin); | |
345 frame.reset(framer_.SerializeSynReply(syn_reply)); | |
346 } | |
347 } | |
348 EXPECT_CALL(session_, OnStreamHeaders(stream_id, _)) | |
349 .WillRepeatedly(WithArgs<1>(Invoke( | |
350 this, &QuicHeadersStreamTest::SaveHeaderDataStringPiece))); | |
351 EXPECT_CALL(session_, | |
352 OnStreamHeadersComplete(stream_id, fin, frame->size())); | |
353 headers_stream_->ProcessRawData(frame->data(), frame->size()); | |
354 CheckHeaders(); | |
355 } | |
356 } | |
357 } | |
358 } | |
359 | |
360 TEST_P(QuicHeadersStreamTest, ProcessBadData) { | |
361 const char kBadData[] = "blah blah blah"; | |
362 EXPECT_CALL(*connection_, SendConnectionCloseWithDetails( | |
363 QUIC_INVALID_HEADERS_STREAM_DATA, _)) | |
364 .Times(::testing::AnyNumber()); | |
365 headers_stream_->ProcessRawData(kBadData, strlen(kBadData)); | |
366 } | |
367 | |
368 TEST_P(QuicHeadersStreamTest, ProcessSpdyDataFrame) { | |
369 SpdyDataIR data(2, ""); | |
370 scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data)); | |
371 EXPECT_CALL(*connection_, | |
372 SendConnectionCloseWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA, | |
373 "SPDY DATA frame received.")) | |
374 .WillOnce(InvokeWithoutArgs(this, | |
375 &QuicHeadersStreamTest::CloseConnection)); | |
376 headers_stream_->ProcessRawData(frame->data(), frame->size()); | |
377 } | |
378 | |
379 TEST_P(QuicHeadersStreamTest, ProcessSpdyRstStreamFrame) { | |
380 SpdyRstStreamIR data(2, RST_STREAM_PROTOCOL_ERROR, ""); | |
381 scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data)); | |
382 EXPECT_CALL(*connection_, | |
383 SendConnectionCloseWithDetails( | |
384 QUIC_INVALID_HEADERS_STREAM_DATA, | |
385 "SPDY RST_STREAM frame received.")) | |
386 .WillOnce(InvokeWithoutArgs(this, | |
387 &QuicHeadersStreamTest::CloseConnection)); | |
388 headers_stream_->ProcessRawData(frame->data(), frame->size()); | |
389 } | |
390 | |
391 TEST_P(QuicHeadersStreamTest, ProcessSpdySettingsFrame) { | |
392 SpdySettingsIR data; | |
393 if (version() > QUIC_VERSION_23) { | |
394 data.AddSetting(SETTINGS_HEADER_TABLE_SIZE, true, true, 0); | |
395 } else { | |
396 data.AddSetting(SETTINGS_UPLOAD_BANDWIDTH, true, true, 0); | |
397 } | |
398 scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data)); | |
399 EXPECT_CALL(*connection_, | |
400 SendConnectionCloseWithDetails( | |
401 QUIC_INVALID_HEADERS_STREAM_DATA, | |
402 "SPDY SETTINGS frame received.")) | |
403 .WillOnce(InvokeWithoutArgs(this, | |
404 &QuicHeadersStreamTest::CloseConnection)); | |
405 headers_stream_->ProcessRawData(frame->data(), frame->size()); | |
406 } | |
407 | |
408 TEST_P(QuicHeadersStreamTest, ProcessSpdyPingFrame) { | |
409 SpdyPingIR data(1); | |
410 scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data)); | |
411 EXPECT_CALL(*connection_, | |
412 SendConnectionCloseWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA, | |
413 "SPDY PING frame received.")) | |
414 .WillOnce(InvokeWithoutArgs(this, | |
415 &QuicHeadersStreamTest::CloseConnection)); | |
416 headers_stream_->ProcessRawData(frame->data(), frame->size()); | |
417 } | |
418 | |
419 TEST_P(QuicHeadersStreamTest, ProcessSpdyGoAwayFrame) { | |
420 SpdyGoAwayIR data(1, GOAWAY_PROTOCOL_ERROR, "go away"); | |
421 scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data)); | |
422 EXPECT_CALL(*connection_, | |
423 SendConnectionCloseWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA, | |
424 "SPDY GOAWAY frame received.")) | |
425 .WillOnce(InvokeWithoutArgs(this, | |
426 &QuicHeadersStreamTest::CloseConnection)); | |
427 headers_stream_->ProcessRawData(frame->data(), frame->size()); | |
428 } | |
429 | |
430 TEST_P(QuicHeadersStreamTest, ProcessSpdyHeadersFrame) { | |
431 if (version() > QUIC_VERSION_23) { | |
432 // HEADERS frames are an error when using SPDY/3, but | |
433 // when using SPDY/4 they're the "normal" way of sending headers | |
434 // so we test their handling in the ProcessRawData test. | |
435 return; | |
436 } | |
437 SpdyHeadersIR data(1); | |
438 scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data)); | |
439 EXPECT_CALL(*connection_, | |
440 SendConnectionCloseWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA, | |
441 "SPDY HEADERS frame received.")) | |
442 .WillOnce(InvokeWithoutArgs(this, | |
443 &QuicHeadersStreamTest::CloseConnection)); | |
444 headers_stream_->ProcessRawData(frame->data(), frame->size()); | |
445 } | |
446 | |
447 TEST_P(QuicHeadersStreamTest, ProcessSpdyWindowUpdateFrame) { | |
448 SpdyWindowUpdateIR data(1, 1); | |
449 scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data)); | |
450 EXPECT_CALL(*connection_, | |
451 SendConnectionCloseWithDetails( | |
452 QUIC_INVALID_HEADERS_STREAM_DATA, | |
453 "SPDY WINDOW_UPDATE frame received.")) | |
454 .WillOnce(InvokeWithoutArgs(this, | |
455 &QuicHeadersStreamTest::CloseConnection)); | |
456 headers_stream_->ProcessRawData(frame->data(), frame->size()); | |
457 } | |
458 | |
459 TEST_P(QuicHeadersStreamTest, NoConnectionLevelFlowControl) { | |
460 EXPECT_TRUE(headers_stream_->flow_controller()->IsEnabled()); | |
461 EXPECT_FALSE(ReliableQuicStreamPeer::StreamContributesToConnectionFlowControl( | |
462 headers_stream_)); | |
463 } | |
464 | |
465 } // namespace | |
466 } // namespace test | |
467 } // namespace net | |
OLD | NEW |