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 <string> | |
8 | |
9 #include "base/strings/string_number_conversions.h" | |
10 #include "net/quic/quic_bug_tracker.h" | |
11 #include "net/quic/quic_utils.h" | |
12 #include "net/quic/spdy_utils.h" | |
13 #include "net/quic/test_tools/quic_connection_peer.h" | |
14 #include "net/quic/test_tools/quic_headers_stream_peer.h" | |
15 #include "net/quic/test_tools/quic_spdy_session_peer.h" | |
16 #include "net/quic/test_tools/quic_test_utils.h" | |
17 #include "net/quic/test_tools/reliable_quic_stream_peer.h" | |
18 #include "net/spdy/spdy_alt_svc_wire_format.h" | |
19 #include "net/spdy/spdy_flags.h" | |
20 #include "net/spdy/spdy_protocol.h" | |
21 #include "net/spdy/spdy_test_utils.h" | |
22 #include "net/test/gtest_util.h" | |
23 #include "testing/gtest/include/gtest/gtest.h" | |
24 | |
25 using base::StringPiece; | |
26 using std::ostream; | |
27 using std::string; | |
28 using std::vector; | |
29 using testing::ElementsAre; | |
30 using testing::_; | |
31 using testing::AtLeast; | |
32 using testing::HasSubstr; | |
33 using testing::InSequence; | |
34 using testing::Invoke; | |
35 using testing::Return; | |
36 using testing::StrictMock; | |
37 using testing::WithArgs; | |
38 using testing::_; | |
39 | |
40 // TODO(bnc): Merge these correctly. | |
41 bool FLAGS_use_http2_frame_decoder_adapter; | |
42 bool FLAGS_spdy_use_hpack_decoder2; | |
43 bool FLAGS_spdy_framer_use_new_methods2; | |
44 | |
45 namespace net { | |
46 namespace test { | |
47 | |
48 class MockHpackDebugVisitor : public QuicHeadersStream::HpackDebugVisitor { | |
49 public: | |
50 explicit MockHpackDebugVisitor() : HpackDebugVisitor() {} | |
51 | |
52 MOCK_METHOD1(OnUseEntry, void(QuicTime::Delta elapsed)); | |
53 | |
54 private: | |
55 DISALLOW_COPY_AND_ASSIGN(MockHpackDebugVisitor); | |
56 }; | |
57 | |
58 namespace { | |
59 | |
60 // TODO(ckrasic): this workaround is due to absence of std::initializer_list | |
61 const bool kFins[] = {false, true}; | |
62 | |
63 class MockVisitor : public SpdyFramerVisitorInterface { | |
64 public: | |
65 MOCK_METHOD1(OnError, void(SpdyFramer* framer)); | |
66 MOCK_METHOD3(OnDataFrameHeader, | |
67 void(SpdyStreamId stream_id, size_t length, bool fin)); | |
68 MOCK_METHOD3(OnStreamFrameData, | |
69 void(SpdyStreamId stream_id, const char* data, size_t len)); | |
70 MOCK_METHOD1(OnStreamEnd, void(SpdyStreamId stream_id)); | |
71 MOCK_METHOD2(OnStreamPadding, void(SpdyStreamId stream_id, size_t len)); | |
72 MOCK_METHOD1(OnHeaderFrameStart, | |
73 SpdyHeadersHandlerInterface*(SpdyStreamId stream_id)); | |
74 MOCK_METHOD2(OnHeaderFrameEnd, void(SpdyStreamId stream_id, bool end)); | |
75 MOCK_METHOD3(OnControlFrameHeaderData, | |
76 bool(SpdyStreamId stream_id, | |
77 const char* header_data, | |
78 size_t len)); | |
79 MOCK_METHOD5(OnSynStream, | |
80 void(SpdyStreamId stream_id, | |
81 SpdyStreamId associated_stream_id, | |
82 SpdyPriority priority, | |
83 bool fin, | |
84 bool unidirectional)); | |
85 MOCK_METHOD2(OnSynReply, void(SpdyStreamId stream_id, bool fin)); | |
86 MOCK_METHOD2(OnRstStream, | |
87 void(SpdyStreamId stream_id, SpdyRstStreamStatus status)); | |
88 MOCK_METHOD1(OnSettings, void(bool clear_persisted)); | |
89 MOCK_METHOD3(OnSetting, | |
90 void(SpdySettingsIds id, uint8_t flags, uint32_t value)); | |
91 MOCK_METHOD0(OnSettingsAck, void()); | |
92 MOCK_METHOD0(OnSettingsEnd, void()); | |
93 MOCK_METHOD2(OnPing, void(SpdyPingId unique_id, bool is_ack)); | |
94 MOCK_METHOD2(OnGoAway, | |
95 void(SpdyStreamId last_accepted_stream_id, | |
96 SpdyGoAwayStatus status)); | |
97 MOCK_METHOD7(OnHeaders, | |
98 void(SpdyStreamId stream_id, | |
99 bool has_priority, | |
100 int weight, | |
101 SpdyStreamId parent_stream_id, | |
102 bool exclusive, | |
103 bool fin, | |
104 bool end)); | |
105 MOCK_METHOD2(OnWindowUpdate, | |
106 void(SpdyStreamId stream_id, int delta_window_size)); | |
107 MOCK_METHOD1(OnBlocked, void(SpdyStreamId stream_id)); | |
108 MOCK_METHOD3(OnPushPromise, | |
109 void(SpdyStreamId stream_id, | |
110 SpdyStreamId promised_stream_id, | |
111 bool end)); | |
112 MOCK_METHOD2(OnContinuation, void(SpdyStreamId stream_id, bool end)); | |
113 MOCK_METHOD4(OnPriority, | |
114 void(SpdyStreamId stream_id, | |
115 SpdyStreamId parent_id, | |
116 int weight, | |
117 bool exclusive)); | |
118 MOCK_METHOD3(OnAltSvc, | |
119 void(SpdyStreamId stream_id, | |
120 StringPiece origin, | |
121 const SpdyAltSvcWireFormat::AlternativeServiceVector& | |
122 altsvc_vector)); | |
123 MOCK_METHOD2(OnUnknownFrame, bool(SpdyStreamId stream_id, int frame_type)); | |
124 }; | |
125 | |
126 class ForceHolAckListener : public QuicAckListenerInterface { | |
127 public: | |
128 ForceHolAckListener() : total_acked_bytes_(0) {} | |
129 | |
130 void OnPacketAcked(int acked_bytes, QuicTime::Delta ack_delay_time) override { | |
131 total_acked_bytes_ += acked_bytes; | |
132 } | |
133 | |
134 void OnPacketRetransmitted(int retransmitted_bytes) override {} | |
135 | |
136 size_t total_acked_bytes() { return total_acked_bytes_; } | |
137 | |
138 private: | |
139 ~ForceHolAckListener() override {} | |
140 | |
141 size_t total_acked_bytes_; | |
142 | |
143 DISALLOW_COPY_AND_ASSIGN(ForceHolAckListener); | |
144 }; | |
145 | |
146 enum Http2DecoderChoice { | |
147 HTTP2_DECODER_SPDY, | |
148 HTTP2_DECODER_NESTED_SPDY, | |
149 HTTP2_DECODER_NEW | |
150 }; | |
151 ostream& operator<<(ostream& os, Http2DecoderChoice v) { | |
152 switch (v) { | |
153 case HTTP2_DECODER_SPDY: | |
154 return os << "SPDY"; | |
155 case HTTP2_DECODER_NESTED_SPDY: | |
156 return os << "NESTED_SPDY"; | |
157 case HTTP2_DECODER_NEW: | |
158 return os << "NEW"; | |
159 } | |
160 return os; | |
161 } | |
162 | |
163 enum HpackDecoderChoice { HPACK_DECODER_SPDY, HPACK_DECODER_NEW }; | |
164 ostream& operator<<(ostream& os, HpackDecoderChoice v) { | |
165 switch (v) { | |
166 case HPACK_DECODER_SPDY: | |
167 return os << "SPDY"; | |
168 case HPACK_DECODER_NEW: | |
169 return os << "NEW"; | |
170 } | |
171 return os; | |
172 } | |
173 | |
174 typedef std:: | |
175 tuple<QuicVersion, Perspective, Http2DecoderChoice, HpackDecoderChoice> | |
176 TestParamsTuple; | |
177 | |
178 struct TestParams { | |
179 explicit TestParams(TestParamsTuple params) | |
180 : version(std::get<0>(params)), | |
181 perspective(std::get<1>(params)), | |
182 http2_decoder(std::get<2>(params)), | |
183 hpack_decoder(std::get<3>(params)) { | |
184 switch (http2_decoder) { | |
185 case HTTP2_DECODER_SPDY: | |
186 FLAGS_use_nested_spdy_framer_decoder = false; | |
187 FLAGS_use_http2_frame_decoder_adapter = false; | |
188 break; | |
189 case HTTP2_DECODER_NESTED_SPDY: | |
190 FLAGS_use_nested_spdy_framer_decoder = true; | |
191 FLAGS_use_http2_frame_decoder_adapter = false; | |
192 break; | |
193 case HTTP2_DECODER_NEW: | |
194 FLAGS_use_nested_spdy_framer_decoder = false; | |
195 FLAGS_use_http2_frame_decoder_adapter = true; | |
196 // Http2FrameDecoderAdapter needs the new header methods, else | |
197 // --use_http2_frame_decoder_adapter=true will be ignored. | |
198 FLAGS_spdy_framer_use_new_methods2 = true; | |
199 break; | |
200 } | |
201 switch (hpack_decoder) { | |
202 case HPACK_DECODER_SPDY: | |
203 FLAGS_spdy_use_hpack_decoder2 = false; | |
204 break; | |
205 case HPACK_DECODER_NEW: | |
206 FLAGS_spdy_use_hpack_decoder2 = true; | |
207 // Needs new header methods to be used. | |
208 FLAGS_spdy_framer_use_new_methods2 = true; | |
209 break; | |
210 } | |
211 FLAGS_quic_supports_push_promise = true; | |
212 FLAGS_quic_always_log_bugs_for_tests = true; | |
213 VLOG(1) << "TestParams: version: " << QuicVersionToString(version) | |
214 << ", perspective: " << perspective | |
215 << ", http2_decoder: " << http2_decoder | |
216 << ", hpack_decoder: " << hpack_decoder; | |
217 } | |
218 | |
219 QuicVersion version; | |
220 Perspective perspective; | |
221 Http2DecoderChoice http2_decoder; | |
222 HpackDecoderChoice hpack_decoder; | |
223 }; | |
224 | |
225 class QuicHeadersStreamTest : public ::testing::TestWithParam<TestParamsTuple> { | |
226 public: | |
227 // Constructing the test_params_ object will set the necessary flags before | |
228 // the MockQuicConnection is constructed, which we need because the latter | |
229 // will construct a SpdyFramer that will use those flags to decide whether | |
230 // to construct a decoder adapter. | |
231 QuicHeadersStreamTest() | |
232 : test_params_(GetParam()), | |
233 connection_(new StrictMock<MockQuicConnection>(&helper_, | |
234 &alarm_factory_, | |
235 perspective(), | |
236 GetVersion())), | |
237 session_(connection_), | |
238 headers_stream_(QuicSpdySessionPeer::GetHeadersStream(&session_)), | |
239 body_("hello world"), | |
240 hpack_encoder_visitor_(new StrictMock<MockHpackDebugVisitor>), | |
241 hpack_decoder_visitor_(new StrictMock<MockHpackDebugVisitor>), | |
242 stream_frame_(kHeadersStreamId, /*fin=*/false, /*offset=*/0, ""), | |
243 next_promised_stream_id_(2) { | |
244 headers_[":version"] = "HTTP/1.1"; | |
245 headers_[":status"] = "200 Ok"; | |
246 headers_["content-length"] = "11"; | |
247 framer_ = std::unique_ptr<SpdyFramer>(new SpdyFramer(HTTP2)); | |
248 framer_->set_visitor(&visitor_); | |
249 EXPECT_EQ(version(), session_.connection()->version()); | |
250 EXPECT_TRUE(headers_stream_ != nullptr); | |
251 connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); | |
252 } | |
253 | |
254 QuicConsumedData SaveIov(const QuicIOVector& data) { | |
255 const iovec* iov = data.iov; | |
256 int count = data.iov_count; | |
257 int consumed = 0; | |
258 for (int i = 0; i < count; ++i) { | |
259 saved_data_.append(static_cast<char*>(iov[i].iov_base), iov[i].iov_len); | |
260 consumed += iov[i].iov_len; | |
261 } | |
262 return QuicConsumedData(consumed, false); | |
263 } | |
264 | |
265 QuicConsumedData SaveIovAndNotifyAckListener( | |
266 const QuicIOVector& data, | |
267 QuicAckListenerInterface* ack_listener) { | |
268 QuicConsumedData result = SaveIov(data); | |
269 if (ack_listener) { | |
270 ack_listener->OnPacketAcked(result.bytes_consumed, | |
271 QuicTime::Delta::Zero()); | |
272 } | |
273 return result; | |
274 } | |
275 | |
276 void SavePayload(const char* data, size_t len) { | |
277 saved_payloads_.append(data, len); | |
278 } | |
279 | |
280 bool SaveHeaderData(const char* data, int len) { | |
281 saved_header_data_.append(data, len); | |
282 return true; | |
283 } | |
284 | |
285 void SaveHeaderDataStringPiece(StringPiece data) { | |
286 saved_header_data_.append(data.data(), data.length()); | |
287 } | |
288 | |
289 void SavePromiseHeaderList(QuicStreamId /* stream_id */, | |
290 QuicStreamId /* promised_stream_id */, | |
291 size_t size, | |
292 const QuicHeaderList& header_list) { | |
293 SaveToHandler(size, header_list); | |
294 } | |
295 | |
296 void SaveHeaderList(QuicStreamId /* stream_id */, | |
297 bool /* fin */, | |
298 size_t size, | |
299 const QuicHeaderList& header_list) { | |
300 SaveToHandler(size, header_list); | |
301 } | |
302 | |
303 void SaveToHandler(size_t size, const QuicHeaderList& header_list) { | |
304 headers_handler_.reset(new TestHeadersHandler); | |
305 headers_handler_->OnHeaderBlockStart(); | |
306 for (const auto& p : header_list) { | |
307 headers_handler_->OnHeader(p.first, p.second); | |
308 } | |
309 headers_handler_->OnHeaderBlockEnd(size); | |
310 } | |
311 | |
312 void WriteHeadersAndExpectSynStream(QuicStreamId stream_id, | |
313 bool fin, | |
314 SpdyPriority priority) { | |
315 WriteHeadersAndCheckData(stream_id, fin, priority, SYN_STREAM); | |
316 } | |
317 | |
318 void WriteHeadersAndExpectSynReply(QuicStreamId stream_id, bool fin) { | |
319 WriteHeadersAndCheckData(stream_id, fin, 0, SYN_REPLY); | |
320 } | |
321 | |
322 void WriteHeadersAndCheckData(QuicStreamId stream_id, | |
323 bool fin, | |
324 SpdyPriority priority, | |
325 SpdyFrameType type) { | |
326 // Write the headers and capture the outgoing data | |
327 EXPECT_CALL(session_, WritevData(headers_stream_, kHeadersStreamId, _, _, | |
328 false, nullptr)) | |
329 .WillOnce(WithArgs<2>(Invoke(this, &QuicHeadersStreamTest::SaveIov))); | |
330 headers_stream_->WriteHeaders(stream_id, headers_.Clone(), fin, priority, | |
331 nullptr); | |
332 | |
333 // Parse the outgoing data and check that it matches was was written. | |
334 if (type == SYN_STREAM) { | |
335 EXPECT_CALL(visitor_, | |
336 OnHeaders(stream_id, kHasPriority, | |
337 Spdy3PriorityToHttp2Weight(priority), | |
338 /*parent_stream_id=*/0, | |
339 /*exclusive=*/false, fin, kFrameComplete)); | |
340 } else { | |
341 EXPECT_CALL(visitor_, | |
342 OnHeaders(stream_id, !kHasPriority, | |
343 /*priority=*/0, | |
344 /*parent_stream_id=*/0, | |
345 /*exclusive=*/false, fin, kFrameComplete)); | |
346 } | |
347 headers_handler_.reset(new TestHeadersHandler); | |
348 EXPECT_CALL(visitor_, OnHeaderFrameStart(stream_id)) | |
349 .WillOnce(Return(headers_handler_.get())); | |
350 EXPECT_CALL(visitor_, OnHeaderFrameEnd(stream_id, true)).Times(1); | |
351 if (fin) { | |
352 EXPECT_CALL(visitor_, OnStreamEnd(stream_id)); | |
353 } | |
354 framer_->ProcessInput(saved_data_.data(), saved_data_.length()); | |
355 EXPECT_FALSE(framer_->HasError()) | |
356 << SpdyFramer::ErrorCodeToString(framer_->error_code()); | |
357 | |
358 CheckHeaders(); | |
359 saved_data_.clear(); | |
360 } | |
361 | |
362 void CheckHeaders() { | |
363 EXPECT_EQ(headers_, headers_handler_->decoded_block()); | |
364 headers_handler_.reset(); | |
365 } | |
366 | |
367 Perspective perspective() const { return test_params_.perspective; } | |
368 | |
369 QuicVersion version() const { return test_params_.version; } | |
370 | |
371 QuicVersionVector GetVersion() { | |
372 QuicVersionVector versions; | |
373 versions.push_back(version()); | |
374 return versions; | |
375 } | |
376 | |
377 void TearDownLocalConnectionState() { | |
378 QuicConnectionPeer::TearDownLocalConnectionState(connection_); | |
379 } | |
380 | |
381 QuicStreamId NextPromisedStreamId() { return next_promised_stream_id_ += 2; } | |
382 | |
383 static const bool kFrameComplete = true; | |
384 static const bool kHasPriority = true; | |
385 | |
386 const TestParams test_params_; | |
387 MockQuicConnectionHelper helper_; | |
388 MockAlarmFactory alarm_factory_; | |
389 StrictMock<MockQuicConnection>* connection_; | |
390 StrictMock<MockQuicSpdySession> session_; | |
391 QuicHeadersStream* headers_stream_; | |
392 SpdyHeaderBlock headers_; | |
393 std::unique_ptr<TestHeadersHandler> headers_handler_; | |
394 string body_; | |
395 string saved_data_; | |
396 string saved_header_data_; | |
397 string saved_payloads_; | |
398 std::unique_ptr<SpdyFramer> framer_; | |
399 StrictMock<MockVisitor> visitor_; | |
400 std::unique_ptr<StrictMock<MockHpackDebugVisitor>> hpack_encoder_visitor_; | |
401 std::unique_ptr<StrictMock<MockHpackDebugVisitor>> hpack_decoder_visitor_; | |
402 QuicStreamFrame stream_frame_; | |
403 QuicStreamId next_promised_stream_id_; | |
404 }; | |
405 | |
406 // Run all tests with each version, perspective (client or server), | |
407 // HTTP/2 and HPACK decoder. | |
408 INSTANTIATE_TEST_CASE_P( | |
409 Tests, | |
410 QuicHeadersStreamTest, | |
411 ::testing::Combine( | |
412 ::testing::ValuesIn(QuicSupportedVersions()), | |
413 ::testing::Values(Perspective::IS_CLIENT, Perspective::IS_SERVER), | |
414 ::testing::Values(HTTP2_DECODER_SPDY, | |
415 HTTP2_DECODER_NESTED_SPDY, | |
416 HTTP2_DECODER_NEW), | |
417 ::testing::Values(HPACK_DECODER_SPDY, HPACK_DECODER_NEW))); | |
418 | |
419 TEST_P(QuicHeadersStreamTest, StreamId) { | |
420 EXPECT_EQ(3u, headers_stream_->id()); | |
421 } | |
422 | |
423 TEST_P(QuicHeadersStreamTest, WriteHeaders) { | |
424 for (QuicStreamId stream_id = kClientDataStreamId1; | |
425 stream_id < kClientDataStreamId3; stream_id += 2) { | |
426 for (bool fin : kFins) { | |
427 if (perspective() == Perspective::IS_SERVER) { | |
428 WriteHeadersAndExpectSynReply(stream_id, fin); | |
429 } else { | |
430 for (SpdyPriority priority = 0; priority < 7; ++priority) { | |
431 // TODO(rch): implement priorities correctly. | |
432 WriteHeadersAndExpectSynStream(stream_id, fin, 0); | |
433 } | |
434 } | |
435 } | |
436 } | |
437 } | |
438 | |
439 TEST_P(QuicHeadersStreamTest, WritePushPromises) { | |
440 for (QuicStreamId stream_id = kClientDataStreamId1; | |
441 stream_id < kClientDataStreamId3; stream_id += 2) { | |
442 QuicStreamId promised_stream_id = NextPromisedStreamId(); | |
443 if (perspective() == Perspective::IS_SERVER) { | |
444 // Write the headers and capture the outgoing data | |
445 EXPECT_CALL(session_, WritevData(headers_stream_, kHeadersStreamId, _, _, | |
446 false, nullptr)) | |
447 .WillOnce(WithArgs<2>(Invoke(this, &QuicHeadersStreamTest::SaveIov))); | |
448 headers_stream_->WritePushPromise(stream_id, promised_stream_id, | |
449 headers_.Clone(), nullptr); | |
450 | |
451 // Parse the outgoing data and check that it matches was was written. | |
452 EXPECT_CALL(visitor_, | |
453 OnPushPromise(stream_id, promised_stream_id, kFrameComplete)); | |
454 headers_handler_.reset(new TestHeadersHandler); | |
455 EXPECT_CALL(visitor_, OnHeaderFrameStart(stream_id)) | |
456 .WillOnce(Return(headers_handler_.get())); | |
457 EXPECT_CALL(visitor_, OnHeaderFrameEnd(stream_id, true)).Times(1); | |
458 framer_->ProcessInput(saved_data_.data(), saved_data_.length()); | |
459 EXPECT_FALSE(framer_->HasError()) | |
460 << SpdyFramer::ErrorCodeToString(framer_->error_code()); | |
461 CheckHeaders(); | |
462 saved_data_.clear(); | |
463 } else { | |
464 EXPECT_DFATAL( | |
465 headers_stream_->WritePushPromise(stream_id, promised_stream_id, | |
466 headers_.Clone(), nullptr), | |
467 "Client shouldn't send PUSH_PROMISE"); | |
468 } | |
469 } | |
470 } | |
471 | |
472 TEST_P(QuicHeadersStreamTest, ProcessRawData) { | |
473 for (QuicStreamId stream_id = kClientDataStreamId1; | |
474 stream_id < kClientDataStreamId3; stream_id += 2) { | |
475 for (bool fin : {false, true}) { | |
476 for (SpdyPriority priority = 0; priority < 7; ++priority) { | |
477 // Replace with "WriteHeadersAndSaveData" | |
478 SpdySerializedFrame frame; | |
479 if (perspective() == Perspective::IS_SERVER) { | |
480 SpdyHeadersIR headers_frame(stream_id, headers_.Clone()); | |
481 headers_frame.set_fin(fin); | |
482 headers_frame.set_has_priority(true); | |
483 headers_frame.set_weight(Spdy3PriorityToHttp2Weight(0)); | |
484 frame = framer_->SerializeFrame(headers_frame); | |
485 EXPECT_CALL(session_, OnStreamHeadersPriority(stream_id, 0)); | |
486 } else { | |
487 SpdyHeadersIR headers_frame(stream_id, headers_.Clone()); | |
488 headers_frame.set_fin(fin); | |
489 frame = framer_->SerializeFrame(headers_frame); | |
490 } | |
491 EXPECT_CALL(session_, | |
492 OnStreamHeaderList(stream_id, fin, frame.size(), _)) | |
493 .WillOnce(Invoke(this, &QuicHeadersStreamTest::SaveHeaderList)); | |
494 stream_frame_.data_buffer = frame.data(); | |
495 stream_frame_.data_length = frame.size(); | |
496 headers_stream_->OnStreamFrame(stream_frame_); | |
497 stream_frame_.offset += frame.size(); | |
498 CheckHeaders(); | |
499 } | |
500 } | |
501 } | |
502 } | |
503 | |
504 TEST_P(QuicHeadersStreamTest, ProcessPushPromise) { | |
505 if (perspective() == Perspective::IS_SERVER) | |
506 return; | |
507 for (QuicStreamId stream_id = kClientDataStreamId1; | |
508 stream_id < kClientDataStreamId3; stream_id += 2) { | |
509 QuicStreamId promised_stream_id = NextPromisedStreamId(); | |
510 SpdyPushPromiseIR push_promise(stream_id, promised_stream_id, | |
511 headers_.Clone()); | |
512 SpdySerializedFrame frame(framer_->SerializeFrame(push_promise)); | |
513 if (perspective() == Perspective::IS_SERVER) { | |
514 EXPECT_CALL(*connection_, | |
515 CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, | |
516 "PUSH_PROMISE not supported.", _)) | |
517 .WillRepeatedly(InvokeWithoutArgs( | |
518 this, &QuicHeadersStreamTest::TearDownLocalConnectionState)); | |
519 } else { | |
520 EXPECT_CALL(session_, OnPromiseHeaderList(stream_id, promised_stream_id, | |
521 frame.size(), _)) | |
522 .WillOnce( | |
523 Invoke(this, &QuicHeadersStreamTest::SavePromiseHeaderList)); | |
524 } | |
525 stream_frame_.data_buffer = frame.data(); | |
526 stream_frame_.data_length = frame.size(); | |
527 headers_stream_->OnStreamFrame(stream_frame_); | |
528 if (perspective() == Perspective::IS_CLIENT) { | |
529 stream_frame_.offset += frame.size(); | |
530 CheckHeaders(); | |
531 } | |
532 } | |
533 } | |
534 | |
535 TEST_P(QuicHeadersStreamTest, EmptyHeaderHOLBlockedTime) { | |
536 if (!FLAGS_quic_measure_headers_hol_blocking_time) { | |
537 return; | |
538 } | |
539 EXPECT_CALL(session_, OnHeadersHeadOfLineBlocking(_)).Times(0); | |
540 testing::InSequence seq; | |
541 bool fin = true; | |
542 for (int stream_num = 0; stream_num < 10; stream_num++) { | |
543 QuicStreamId stream_id = QuicClientDataStreamId(stream_num); | |
544 // Replace with "WriteHeadersAndSaveData" | |
545 SpdySerializedFrame frame; | |
546 if (perspective() == Perspective::IS_SERVER) { | |
547 SpdyHeadersIR headers_frame(stream_id, headers_.Clone()); | |
548 headers_frame.set_fin(fin); | |
549 headers_frame.set_has_priority(true); | |
550 headers_frame.set_weight(Spdy3PriorityToHttp2Weight(0)); | |
551 frame = framer_->SerializeFrame(headers_frame); | |
552 EXPECT_CALL(session_, OnStreamHeadersPriority(stream_id, 0)); | |
553 } else { | |
554 SpdyHeadersIR headers_frame(stream_id, headers_.Clone()); | |
555 headers_frame.set_fin(fin); | |
556 frame = framer_->SerializeFrame(headers_frame); | |
557 } | |
558 EXPECT_CALL(session_, OnStreamHeaderList(stream_id, fin, frame.size(), _)) | |
559 .Times(1); | |
560 stream_frame_.data_buffer = frame.data(); | |
561 stream_frame_.data_length = frame.size(); | |
562 headers_stream_->OnStreamFrame(stream_frame_); | |
563 connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); | |
564 stream_frame_.offset += frame.size(); | |
565 } | |
566 } | |
567 | |
568 TEST_P(QuicHeadersStreamTest, NonEmptyHeaderHOLBlockedTime) { | |
569 QuicStreamId stream_id; | |
570 bool fin = true; | |
571 QuicStreamFrame stream_frames[10]; | |
572 SpdySerializedFrame frames[10]; | |
573 // First create all the frames in order | |
574 { | |
575 InSequence seq; | |
576 for (int stream_num = 0; stream_num < 10; ++stream_num) { | |
577 stream_id = QuicClientDataStreamId(stream_num); | |
578 if (perspective() == Perspective::IS_SERVER) { | |
579 SpdyHeadersIR headers_frame(stream_id, headers_.Clone()); | |
580 headers_frame.set_fin(fin); | |
581 headers_frame.set_has_priority(true); | |
582 headers_frame.set_weight(Spdy3PriorityToHttp2Weight(0)); | |
583 frames[stream_num] = framer_->SerializeFrame(headers_frame); | |
584 EXPECT_CALL(session_, OnStreamHeadersPriority(stream_id, 0)).Times(1); | |
585 } else { | |
586 SpdyHeadersIR headers_frame(stream_id, headers_.Clone()); | |
587 headers_frame.set_fin(fin); | |
588 frames[stream_num] = framer_->SerializeFrame(headers_frame); | |
589 } | |
590 stream_frames[stream_num].stream_id = stream_frame_.stream_id; | |
591 stream_frames[stream_num].offset = stream_frame_.offset; | |
592 stream_frames[stream_num].data_buffer = frames[stream_num].data(); | |
593 stream_frames[stream_num].data_length = frames[stream_num].size(); | |
594 DVLOG(1) << "make frame for stream " << stream_num << " offset " | |
595 << stream_frames[stream_num].offset; | |
596 stream_frame_.offset += frames[stream_num].size(); | |
597 EXPECT_CALL(session_, OnStreamHeaderList(stream_id, fin, _, _)).Times(1); | |
598 } | |
599 } | |
600 | |
601 // Actually writing the frames in reverse order will cause HOL blocking. | |
602 EXPECT_CALL(session_, OnHeadersHeadOfLineBlocking(_)).Times(9); | |
603 | |
604 for (int stream_num = 9; stream_num >= 0; --stream_num) { | |
605 DVLOG(1) << "OnStreamFrame for stream " << stream_num << " offset " | |
606 << stream_frames[stream_num].offset; | |
607 headers_stream_->OnStreamFrame(stream_frames[stream_num]); | |
608 connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); | |
609 } | |
610 } | |
611 | |
612 TEST_P(QuicHeadersStreamTest, ProcessLargeRawData) { | |
613 // We want to create a frame that is more than the SPDY Framer's max control | |
614 // frame size, which is 16K, but less than the HPACK decoders max decode | |
615 // buffer size, which is 32K. | |
616 headers_["key0"] = string(1 << 13, '.'); | |
617 headers_["key1"] = string(1 << 13, '.'); | |
618 headers_["key2"] = string(1 << 13, '.'); | |
619 for (QuicStreamId stream_id = kClientDataStreamId1; | |
620 stream_id < kClientDataStreamId3; stream_id += 2) { | |
621 for (bool fin : {false, true}) { | |
622 for (SpdyPriority priority = 0; priority < 7; ++priority) { | |
623 // Replace with "WriteHeadersAndSaveData" | |
624 SpdySerializedFrame frame; | |
625 if (perspective() == Perspective::IS_SERVER) { | |
626 SpdyHeadersIR headers_frame(stream_id, headers_.Clone()); | |
627 headers_frame.set_fin(fin); | |
628 headers_frame.set_has_priority(true); | |
629 headers_frame.set_weight(Spdy3PriorityToHttp2Weight(0)); | |
630 frame = framer_->SerializeFrame(headers_frame); | |
631 EXPECT_CALL(session_, OnStreamHeadersPriority(stream_id, 0)); | |
632 } else { | |
633 SpdyHeadersIR headers_frame(stream_id, headers_.Clone()); | |
634 headers_frame.set_fin(fin); | |
635 frame = framer_->SerializeFrame(headers_frame); | |
636 } | |
637 EXPECT_CALL(session_, | |
638 OnStreamHeaderList(stream_id, fin, frame.size(), _)) | |
639 .WillOnce(Invoke(this, &QuicHeadersStreamTest::SaveHeaderList)); | |
640 stream_frame_.data_buffer = frame.data(); | |
641 stream_frame_.data_length = frame.size(); | |
642 headers_stream_->OnStreamFrame(stream_frame_); | |
643 stream_frame_.offset += frame.size(); | |
644 CheckHeaders(); | |
645 } | |
646 } | |
647 } | |
648 } | |
649 | |
650 TEST_P(QuicHeadersStreamTest, ProcessBadData) { | |
651 const char kBadData[] = "blah blah blah"; | |
652 EXPECT_CALL(*connection_, | |
653 CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, _, _)) | |
654 .Times(::testing::AnyNumber()); | |
655 stream_frame_.data_buffer = kBadData; | |
656 stream_frame_.data_length = strlen(kBadData); | |
657 headers_stream_->OnStreamFrame(stream_frame_); | |
658 } | |
659 | |
660 TEST_P(QuicHeadersStreamTest, ProcessSpdyDataFrame) { | |
661 SpdyDataIR data(2, "ping"); | |
662 SpdySerializedFrame frame(framer_->SerializeFrame(data)); | |
663 | |
664 EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, | |
665 "SPDY DATA frame received.", _)) | |
666 .WillOnce(InvokeWithoutArgs( | |
667 this, &QuicHeadersStreamTest::TearDownLocalConnectionState)); | |
668 stream_frame_.data_buffer = frame.data(); | |
669 stream_frame_.data_length = frame.size(); | |
670 headers_stream_->OnStreamFrame(stream_frame_); | |
671 } | |
672 | |
673 TEST_P(QuicHeadersStreamTest, ProcessSpdyDataFrameForceHolBlocking) { | |
674 if (version() <= QUIC_VERSION_35) { | |
675 return; | |
676 } | |
677 QuicSpdySessionPeer::SetForceHolBlocking(&session_, true); | |
678 SpdyDataIR data(2, "ping"); | |
679 SpdySerializedFrame frame(framer_->SerializeFrame(data)); | |
680 EXPECT_CALL(session_, OnStreamFrameData(2, _, 4, false)); | |
681 stream_frame_.data_buffer = frame.data(); | |
682 stream_frame_.data_length = frame.size(); | |
683 headers_stream_->OnStreamFrame(stream_frame_); | |
684 } | |
685 | |
686 TEST_P(QuicHeadersStreamTest, ProcessSpdyDataFrameEmptyWithFin) { | |
687 if (version() <= QUIC_VERSION_35) { | |
688 return; | |
689 } | |
690 QuicSpdySessionPeer::SetForceHolBlocking(&session_, true); | |
691 SpdyDataIR data(2, ""); | |
692 data.set_fin(true); | |
693 SpdySerializedFrame frame(framer_->SerializeFrame(data)); | |
694 EXPECT_CALL(session_, OnStreamFrameData(2, _, 0, true)); | |
695 stream_frame_.data_buffer = frame.data(); | |
696 stream_frame_.data_length = frame.size(); | |
697 headers_stream_->OnStreamFrame(stream_frame_); | |
698 } | |
699 | |
700 TEST_P(QuicHeadersStreamTest, ProcessSpdyRstStreamFrame) { | |
701 SpdyRstStreamIR data(2, RST_STREAM_PROTOCOL_ERROR); | |
702 SpdySerializedFrame frame(framer_->SerializeFrame(data)); | |
703 EXPECT_CALL(*connection_, | |
704 CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, | |
705 "SPDY RST_STREAM frame received.", _)) | |
706 .WillOnce(InvokeWithoutArgs( | |
707 this, &QuicHeadersStreamTest::TearDownLocalConnectionState)); | |
708 stream_frame_.data_buffer = frame.data(); | |
709 stream_frame_.data_length = frame.size(); | |
710 headers_stream_->OnStreamFrame(stream_frame_); | |
711 } | |
712 | |
713 TEST_P(QuicHeadersStreamTest, ProcessSpdySettingsFrame) { | |
714 FLAGS_quic_respect_http2_settings_frame = false; | |
715 SpdySettingsIR data; | |
716 data.AddSetting(SETTINGS_HEADER_TABLE_SIZE, true, true, 0); | |
717 SpdySerializedFrame frame(framer_->SerializeFrame(data)); | |
718 EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, | |
719 "SPDY SETTINGS frame received.", _)) | |
720 .WillOnce(InvokeWithoutArgs( | |
721 this, &QuicHeadersStreamTest::TearDownLocalConnectionState)); | |
722 stream_frame_.data_buffer = frame.data(); | |
723 stream_frame_.data_length = frame.size(); | |
724 headers_stream_->OnStreamFrame(stream_frame_); | |
725 } | |
726 | |
727 TEST_P(QuicHeadersStreamTest, RespectHttp2SettingsFrameSupportedFields) { | |
728 FLAGS_quic_respect_http2_settings_frame = true; | |
729 const uint32_t kTestHeaderTableSize = 1000; | |
730 SpdySettingsIR data; | |
731 // Respect supported settings frames SETTINGS_HEADER_TABLE_SIZE. | |
732 data.AddSetting(SETTINGS_HEADER_TABLE_SIZE, true, true, kTestHeaderTableSize); | |
733 SpdySerializedFrame frame(framer_->SerializeFrame(data)); | |
734 stream_frame_.data_buffer = frame.data(); | |
735 stream_frame_.data_length = frame.size(); | |
736 headers_stream_->OnStreamFrame(stream_frame_); | |
737 EXPECT_EQ(kTestHeaderTableSize, | |
738 QuicHeadersStreamPeer::GetSpdyFramer(headers_stream_) | |
739 .header_encoder_table_size()); | |
740 } | |
741 | |
742 TEST_P(QuicHeadersStreamTest, RespectHttp2SettingsFrameUnsupportedFields) { | |
743 FLAGS_quic_respect_http2_settings_frame = true; | |
744 SpdySettingsIR data; | |
745 // Does not support SETTINGS_MAX_HEADER_LIST_SIZE, | |
746 // SETTINGS_MAX_CONCURRENT_STREAMS, SETTINGS_INITIAL_WINDOW_SIZE, | |
747 // SETTINGS_ENABLE_PUSH and SETTINGS_MAX_FRAME_SIZE. | |
748 data.AddSetting(SETTINGS_MAX_HEADER_LIST_SIZE, true, true, 2000); | |
749 data.AddSetting(SETTINGS_MAX_CONCURRENT_STREAMS, true, true, 100); | |
750 data.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, true, true, 100); | |
751 data.AddSetting(SETTINGS_ENABLE_PUSH, true, true, 1); | |
752 data.AddSetting(SETTINGS_MAX_FRAME_SIZE, true, true, 1250); | |
753 SpdySerializedFrame frame(framer_->SerializeFrame(data)); | |
754 EXPECT_CALL( | |
755 *connection_, | |
756 CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, | |
757 "Unsupported field of HTTP/2 SETTINGS frame: " + | |
758 base::IntToString(SETTINGS_MAX_HEADER_LIST_SIZE), | |
759 _)); | |
760 EXPECT_CALL( | |
761 *connection_, | |
762 CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, | |
763 "Unsupported field of HTTP/2 SETTINGS frame: " + | |
764 base::IntToString(SETTINGS_MAX_CONCURRENT_STREAMS), | |
765 _)); | |
766 EXPECT_CALL( | |
767 *connection_, | |
768 CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, | |
769 "Unsupported field of HTTP/2 SETTINGS frame: " + | |
770 base::IntToString(SETTINGS_INITIAL_WINDOW_SIZE), | |
771 _)); | |
772 EXPECT_CALL(*connection_, | |
773 CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, | |
774 "Unsupported field of HTTP/2 SETTINGS frame: " + | |
775 base::IntToString(SETTINGS_ENABLE_PUSH), | |
776 _)); | |
777 EXPECT_CALL(*connection_, | |
778 CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, | |
779 "Unsupported field of HTTP/2 SETTINGS frame: " + | |
780 base::IntToString(SETTINGS_MAX_FRAME_SIZE), | |
781 _)); | |
782 stream_frame_.data_buffer = frame.data(); | |
783 stream_frame_.data_length = frame.size(); | |
784 headers_stream_->OnStreamFrame(stream_frame_); | |
785 } | |
786 | |
787 TEST_P(QuicHeadersStreamTest, ProcessSpdyPingFrame) { | |
788 SpdyPingIR data(1); | |
789 SpdySerializedFrame frame(framer_->SerializeFrame(data)); | |
790 EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, | |
791 "SPDY PING frame received.", _)) | |
792 .WillOnce(InvokeWithoutArgs( | |
793 this, &QuicHeadersStreamTest::TearDownLocalConnectionState)); | |
794 stream_frame_.data_buffer = frame.data(); | |
795 stream_frame_.data_length = frame.size(); | |
796 headers_stream_->OnStreamFrame(stream_frame_); | |
797 } | |
798 | |
799 TEST_P(QuicHeadersStreamTest, ProcessSpdyGoAwayFrame) { | |
800 SpdyGoAwayIR data(1, GOAWAY_PROTOCOL_ERROR, "go away"); | |
801 SpdySerializedFrame frame(framer_->SerializeFrame(data)); | |
802 EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, | |
803 "SPDY GOAWAY frame received.", _)) | |
804 .WillOnce(InvokeWithoutArgs( | |
805 this, &QuicHeadersStreamTest::TearDownLocalConnectionState)); | |
806 stream_frame_.data_buffer = frame.data(); | |
807 stream_frame_.data_length = frame.size(); | |
808 headers_stream_->OnStreamFrame(stream_frame_); | |
809 } | |
810 | |
811 TEST_P(QuicHeadersStreamTest, ProcessSpdyWindowUpdateFrame) { | |
812 SpdyWindowUpdateIR data(1, 1); | |
813 SpdySerializedFrame frame(framer_->SerializeFrame(data)); | |
814 EXPECT_CALL(*connection_, | |
815 CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, | |
816 "SPDY WINDOW_UPDATE frame received.", _)) | |
817 .WillOnce(InvokeWithoutArgs( | |
818 this, &QuicHeadersStreamTest::TearDownLocalConnectionState)); | |
819 stream_frame_.data_buffer = frame.data(); | |
820 stream_frame_.data_length = frame.size(); | |
821 headers_stream_->OnStreamFrame(stream_frame_); | |
822 } | |
823 | |
824 TEST_P(QuicHeadersStreamTest, NoConnectionLevelFlowControl) { | |
825 EXPECT_FALSE(ReliableQuicStreamPeer::StreamContributesToConnectionFlowControl( | |
826 headers_stream_)); | |
827 } | |
828 | |
829 TEST_P(QuicHeadersStreamTest, HpackDecoderDebugVisitor) { | |
830 if (FLAGS_use_nested_spdy_framer_decoder) | |
831 return; | |
832 | |
833 StrictMock<MockHpackDebugVisitor>* hpack_decoder_visitor = | |
834 hpack_decoder_visitor_.get(); | |
835 headers_stream_->SetHpackDecoderDebugVisitor( | |
836 std::move(hpack_decoder_visitor_)); | |
837 | |
838 // Create some headers we expect to generate entries in HPACK's | |
839 // dynamic table, in addition to content-length. | |
840 headers_["key0"] = string(1 << 1, '.'); | |
841 headers_["key1"] = string(1 << 2, '.'); | |
842 headers_["key2"] = string(1 << 3, '.'); | |
843 { | |
844 testing::InSequence seq; | |
845 // Number of indexed representations generated in headers below. | |
846 for (int i = 1; i < 28; i++) { | |
847 EXPECT_CALL(*hpack_decoder_visitor, | |
848 OnUseEntry(QuicTime::Delta::FromMilliseconds(i))) | |
849 .Times(4); | |
850 } | |
851 } | |
852 for (QuicStreamId stream_id = kClientDataStreamId1; | |
853 stream_id < kClientDataStreamId3; stream_id += 2) { | |
854 for (bool fin : {false, true}) { | |
855 for (SpdyPriority priority = 0; priority < 7; ++priority) { | |
856 // Replace with "WriteHeadersAndSaveData" | |
857 SpdySerializedFrame frame; | |
858 if (perspective() == Perspective::IS_SERVER) { | |
859 SpdyHeadersIR headers_frame(stream_id, headers_.Clone()); | |
860 headers_frame.set_fin(fin); | |
861 headers_frame.set_has_priority(true); | |
862 headers_frame.set_weight(Spdy3PriorityToHttp2Weight(0)); | |
863 frame = framer_->SerializeFrame(headers_frame); | |
864 EXPECT_CALL(session_, OnStreamHeadersPriority(stream_id, 0)); | |
865 } else { | |
866 SpdyHeadersIR headers_frame(stream_id, headers_.Clone()); | |
867 headers_frame.set_fin(fin); | |
868 frame = framer_->SerializeFrame(headers_frame); | |
869 } | |
870 EXPECT_CALL(session_, | |
871 OnStreamHeaderList(stream_id, fin, frame.size(), _)) | |
872 .WillOnce(Invoke(this, &QuicHeadersStreamTest::SaveHeaderList)); | |
873 stream_frame_.data_buffer = frame.data(); | |
874 stream_frame_.data_length = frame.size(); | |
875 connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); | |
876 headers_stream_->OnStreamFrame(stream_frame_); | |
877 stream_frame_.offset += frame.size(); | |
878 CheckHeaders(); | |
879 } | |
880 } | |
881 } | |
882 } | |
883 | |
884 TEST_P(QuicHeadersStreamTest, HpackEncoderDebugVisitor) { | |
885 StrictMock<MockHpackDebugVisitor>* hpack_encoder_visitor = | |
886 hpack_encoder_visitor_.get(); | |
887 headers_stream_->SetHpackEncoderDebugVisitor( | |
888 std::move(hpack_encoder_visitor_)); | |
889 | |
890 if (perspective() == Perspective::IS_SERVER) { | |
891 testing::InSequence seq; | |
892 for (int i = 1; i < 4; i++) { | |
893 EXPECT_CALL(*hpack_encoder_visitor, | |
894 OnUseEntry(QuicTime::Delta::FromMilliseconds(i))); | |
895 } | |
896 } else { | |
897 testing::InSequence seq; | |
898 for (int i = 1; i < 28; i++) { | |
899 EXPECT_CALL(*hpack_encoder_visitor, | |
900 OnUseEntry(QuicTime::Delta::FromMilliseconds(i))); | |
901 } | |
902 } | |
903 for (QuicStreamId stream_id = kClientDataStreamId1; | |
904 stream_id < kClientDataStreamId3; stream_id += 2) { | |
905 for (bool fin : {false, true}) { | |
906 if (perspective() == Perspective::IS_SERVER) { | |
907 WriteHeadersAndExpectSynReply(stream_id, fin); | |
908 connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); | |
909 } else { | |
910 for (SpdyPriority priority = 0; priority < 7; ++priority) { | |
911 // TODO(rch): implement priorities correctly. | |
912 WriteHeadersAndExpectSynStream(stream_id, fin, 0); | |
913 connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); | |
914 } | |
915 } | |
916 } | |
917 } | |
918 } | |
919 | |
920 TEST_P(QuicHeadersStreamTest, WritevStreamData) { | |
921 QuicStreamId id = kClientDataStreamId1; | |
922 QuicStreamOffset offset = 0; | |
923 struct iovec iov; | |
924 string data; | |
925 | |
926 // This test will issue a write that will require fragmenting into | |
927 // multiple HTTP/2 DATA frames. | |
928 const int kMinDataFrames = 4; | |
929 const size_t data_len = | |
930 kSpdyInitialFrameSizeLimit * kMinDataFrames + 1024; | |
931 // Set headers stream send window large enough for data written below. | |
932 headers_stream_->flow_controller()->UpdateSendWindowOffset(data_len * 2 * 4); | |
933 test::GenerateBody(&data, data_len); | |
934 | |
935 for (bool fin : {true, false}) { | |
936 for (bool use_ack_listener : {true, false}) { | |
937 scoped_refptr<ForceHolAckListener> ack_listener; | |
938 if (use_ack_listener) { | |
939 ack_listener = new ForceHolAckListener(); | |
940 } | |
941 EXPECT_CALL(session_, | |
942 WritevData(headers_stream_, kHeadersStreamId, _, _, false, _)) | |
943 .WillRepeatedly(WithArgs<2, 5>(Invoke( | |
944 this, &QuicHeadersStreamTest::SaveIovAndNotifyAckListener))); | |
945 | |
946 QuicConsumedData consumed_data = headers_stream_->WritevStreamData( | |
947 id, MakeIOVector(data, &iov), offset, fin, ack_listener.get()); | |
948 | |
949 EXPECT_EQ(consumed_data.bytes_consumed, data_len); | |
950 EXPECT_EQ(consumed_data.fin_consumed, fin); | |
951 // Now process the written data with the SPDY framer, and verify | |
952 // that the original data is unchanged. | |
953 EXPECT_CALL(visitor_, OnDataFrameHeader(id, _, _)) | |
954 .Times(AtLeast(kMinDataFrames)); | |
955 EXPECT_CALL(visitor_, OnStreamFrameData(id, _, _)) | |
956 .WillRepeatedly(WithArgs<1, 2>( | |
957 Invoke(this, &QuicHeadersStreamTest::SavePayload))); | |
958 if (fin) { | |
959 EXPECT_CALL(visitor_, OnStreamEnd(id)); | |
960 } | |
961 framer_->ProcessInput(saved_data_.data(), saved_data_.length()); | |
962 EXPECT_EQ(saved_payloads_, data); | |
963 | |
964 if (use_ack_listener) { | |
965 // Notice, acked bytes doesn't include extra bytes used by | |
966 // HTTP/2 DATA frame headers. | |
967 EXPECT_EQ(ack_listener->total_acked_bytes(), data_len); | |
968 } | |
969 saved_data_.clear(); | |
970 saved_payloads_.clear(); | |
971 } | |
972 } | |
973 } | |
974 | |
975 } // namespace | |
976 } // namespace test | |
977 } // namespace net | |
OLD | NEW |