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.
9 #include "base/memory/ref_counted.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/stl_util.h"
12 #include "base/strings/string_piece.h"
13 #include "net/base/completion_callback.h"
14 #include "net/base/net_log_unittest.h"
15 #include "net/base/request_priority.h"
16 #include "net/socket/next_proto.h"
17 #include "net/socket/socket_test_util.h"
18 #include "net/spdy/buffered_spdy_framer.h"
19 #include "net/spdy/spdy_http_utils.h"
20 #include "net/spdy/spdy_protocol.h"
21 #include "net/spdy/spdy_session.h"
22 #include "net/spdy/spdy_stream.h"
23 #include "net/spdy/spdy_stream_test_util.h"
24 #include "net/spdy/spdy_test_util_common.h"
25 #include "testing/gtest/include/gtest/gtest.h"
27 // TODO(ukai): factor out common part with spdy_http_stream_unittest.cc
35 const char kStreamUrl
[] = "http://www.google.com/";
36 const char kPostBody
[] = "\0hello!\xff";
37 const size_t kPostBodyLength
= arraysize(kPostBody
);
38 const base::StringPiece
kPostBodyStringPiece(kPostBody
, kPostBodyLength
);
40 class SpdyStreamTest
: public ::testing::Test
,
41 public ::testing::WithParamInterface
<NextProto
> {
43 // A function that takes a SpdyStream and the number of bytes which
44 // will unstall the next frame completely.
45 typedef base::Callback
<void(const base::WeakPtr
<SpdyStream
>&, int32
)>
49 : spdy_util_(GetParam()),
50 session_deps_(GetParam()),
53 base::WeakPtr
<SpdySession
> CreateDefaultSpdySession() {
54 SpdySessionKey
key(HostPortPair("www.google.com", 80),
55 ProxyServer::Direct(),
56 PRIVACY_MODE_DISABLED
);
57 return CreateInsecureSpdySession(session_
, key
, BoundNetLog());
60 void TearDown() override
{ base::MessageLoop::current()->RunUntilIdle(); }
62 void RunResumeAfterUnstallRequestResponseTest(
63 const UnstallFunction
& unstall_function
);
65 void RunResumeAfterUnstallBidirectionalTest(
66 const UnstallFunction
& unstall_function
);
68 // Add{Read,Write}() populates lists that are eventually passed to a
69 // SocketData class. |frame| must live for the whole test.
71 void AddRead(const SpdyFrame
& frame
) {
72 reads_
.push_back(CreateMockRead(frame
, offset_
++));
75 void AddWrite(const SpdyFrame
& frame
) {
76 writes_
.push_back(CreateMockWrite(frame
, offset_
++));
80 reads_
.push_back(MockRead(ASYNC
, 0, offset_
++));
83 MockRead
* GetReads() {
84 return vector_as_array(&reads_
);
87 size_t GetNumReads() const {
91 MockWrite
* GetWrites() {
92 return vector_as_array(&writes_
);
95 int GetNumWrites() const {
96 return writes_
.size();
99 SpdyTestUtil spdy_util_
;
100 SpdySessionDependencies session_deps_
;
101 scoped_refptr
<HttpNetworkSession
> session_
;
104 // Used by Add{Read,Write}() above.
105 std::vector
<MockWrite
> writes_
;
106 std::vector
<MockRead
> reads_
;
110 INSTANTIATE_TEST_CASE_P(NextProto
,
112 testing::Values(kProtoSPDY31
,
116 TEST_P(SpdyStreamTest
, SendDataAfterOpen
) {
117 GURL
url(kStreamUrl
);
119 session_
= SpdySessionDependencies::SpdyCreateSession(&session_deps_
);
121 scoped_ptr
<SpdyFrame
> req(
122 spdy_util_
.ConstructSpdyPost(
123 kStreamUrl
, 1, kPostBodyLength
, LOWEST
, NULL
, 0));
126 scoped_ptr
<SpdyFrame
> resp(
127 spdy_util_
.ConstructSpdyPostSynReply(NULL
, 0));
130 scoped_ptr
<SpdyFrame
> msg(
131 spdy_util_
.ConstructSpdyBodyFrame(1, kPostBody
, kPostBodyLength
, false));
134 scoped_ptr
<SpdyFrame
> echo(
135 spdy_util_
.ConstructSpdyBodyFrame(1, kPostBody
, kPostBodyLength
, false));
140 OrderedSocketData
data(GetReads(), GetNumReads(),
141 GetWrites(), GetNumWrites());
142 MockConnect
connect_data(SYNCHRONOUS
, OK
);
143 data
.set_connect_data(connect_data
);
145 session_deps_
.socket_factory
->AddSocketDataProvider(&data
);
147 base::WeakPtr
<SpdySession
> session(CreateDefaultSpdySession());
149 base::WeakPtr
<SpdyStream
> stream
=
150 CreateStreamSynchronously(
151 SPDY_BIDIRECTIONAL_STREAM
, session
, url
, LOWEST
, BoundNetLog());
152 ASSERT_TRUE(stream
.get() != NULL
);
154 StreamDelegateSendImmediate
delegate(stream
, kPostBodyStringPiece
);
155 stream
->SetDelegate(&delegate
);
157 EXPECT_FALSE(stream
->HasUrlFromHeaders());
159 scoped_ptr
<SpdyHeaderBlock
> headers(
160 spdy_util_
.ConstructPostHeaderBlock(kStreamUrl
, kPostBodyLength
));
161 EXPECT_EQ(ERR_IO_PENDING
,
162 stream
->SendRequestHeaders(headers
.Pass(), MORE_DATA_TO_SEND
));
163 EXPECT_TRUE(stream
->HasUrlFromHeaders());
164 EXPECT_EQ(kStreamUrl
, stream
->GetUrlFromHeaders().spec());
166 EXPECT_EQ(ERR_CONNECTION_CLOSED
, delegate
.WaitForClose());
168 EXPECT_TRUE(delegate
.send_headers_completed());
169 EXPECT_EQ("200", delegate
.GetResponseHeaderValue(spdy_util_
.GetStatusKey()));
170 EXPECT_EQ(std::string(kPostBody
, kPostBodyLength
),
171 delegate
.TakeReceivedData());
172 EXPECT_TRUE(data
.at_write_eof());
175 TEST_P(SpdyStreamTest
, PushedStream
) {
176 session_
= SpdySessionDependencies::SpdyCreateSession(&session_deps_
);
180 OrderedSocketData
data(GetReads(), GetNumReads(),
181 GetWrites(), GetNumWrites());
182 MockConnect
connect_data(SYNCHRONOUS
, OK
);
183 data
.set_connect_data(connect_data
);
185 session_deps_
.socket_factory
->AddSocketDataProvider(&data
);
187 base::WeakPtr
<SpdySession
> spdy_session(CreateDefaultSpdySession());
189 // Conjure up a stream.
190 SpdyStream
stream(SPDY_PUSH_STREAM
, spdy_session
, GURL(), DEFAULT_PRIORITY
,
191 SpdySession::GetInitialWindowSize(kProtoSPDY31
),
192 SpdySession::GetInitialWindowSize(kProtoSPDY31
),
194 stream
.set_stream_id(2);
195 EXPECT_FALSE(stream
.HasUrlFromHeaders());
197 // Set required request headers.
198 SpdyHeaderBlock request_headers
;
199 spdy_util_
.AddUrlToHeaderBlock(kStreamUrl
, &request_headers
);
200 stream
.OnPushPromiseHeadersReceived(request_headers
);
202 // Send some basic response headers.
203 SpdyHeaderBlock response
;
204 response
[spdy_util_
.GetStatusKey()] = "200";
205 response
[spdy_util_
.GetVersionKey()] = "OK";
206 stream
.OnInitialResponseHeadersReceived(
207 response
, base::Time::Now(), base::TimeTicks::Now());
209 // And some more headers.
210 // TODO(baranovich): not valid for HTTP 2.
211 SpdyHeaderBlock headers
;
212 headers
["alpha"] = "beta";
213 stream
.OnAdditionalResponseHeadersReceived(headers
);
215 EXPECT_TRUE(stream
.HasUrlFromHeaders());
216 EXPECT_EQ(kStreamUrl
, stream
.GetUrlFromHeaders().spec());
218 StreamDelegateDoNothing
delegate(stream
.GetWeakPtr());
219 stream
.SetDelegate(&delegate
);
221 base::MessageLoop::current()->RunUntilIdle();
223 EXPECT_EQ("200", delegate
.GetResponseHeaderValue(spdy_util_
.GetStatusKey()));
224 EXPECT_EQ("beta", delegate
.GetResponseHeaderValue("alpha"));
226 EXPECT_TRUE(spdy_session
== NULL
);
229 TEST_P(SpdyStreamTest
, StreamError
) {
230 GURL
url(kStreamUrl
);
232 session_
= SpdySessionDependencies::SpdyCreateSession(&session_deps_
);
234 scoped_ptr
<SpdyFrame
> req(
235 spdy_util_
.ConstructSpdyPost(
236 kStreamUrl
, 1, kPostBodyLength
, LOWEST
, NULL
, 0));
239 scoped_ptr
<SpdyFrame
> resp(
240 spdy_util_
.ConstructSpdyGetSynReply(NULL
, 0, 1));
243 scoped_ptr
<SpdyFrame
> msg(
244 spdy_util_
.ConstructSpdyBodyFrame(1, kPostBody
, kPostBodyLength
, false));
247 scoped_ptr
<SpdyFrame
> echo(
248 spdy_util_
.ConstructSpdyBodyFrame(1, kPostBody
, kPostBodyLength
, false));
253 CapturingBoundNetLog log
;
255 OrderedSocketData
data(GetReads(), GetNumReads(),
256 GetWrites(), GetNumWrites());
257 MockConnect
connect_data(SYNCHRONOUS
, OK
);
258 data
.set_connect_data(connect_data
);
260 session_deps_
.socket_factory
->AddSocketDataProvider(&data
);
262 base::WeakPtr
<SpdySession
> session(CreateDefaultSpdySession());
264 base::WeakPtr
<SpdyStream
> stream
=
265 CreateStreamSynchronously(
266 SPDY_BIDIRECTIONAL_STREAM
, session
, url
, LOWEST
, log
.bound());
267 ASSERT_TRUE(stream
.get() != NULL
);
269 StreamDelegateSendImmediate
delegate(stream
, kPostBodyStringPiece
);
270 stream
->SetDelegate(&delegate
);
272 EXPECT_FALSE(stream
->HasUrlFromHeaders());
274 scoped_ptr
<SpdyHeaderBlock
> headers(
275 spdy_util_
.ConstructPostHeaderBlock(kStreamUrl
, kPostBodyLength
));
276 EXPECT_EQ(ERR_IO_PENDING
,
277 stream
->SendRequestHeaders(headers
.Pass(), MORE_DATA_TO_SEND
));
278 EXPECT_TRUE(stream
->HasUrlFromHeaders());
279 EXPECT_EQ(kStreamUrl
, stream
->GetUrlFromHeaders().spec());
281 EXPECT_EQ(ERR_CONNECTION_CLOSED
, delegate
.WaitForClose());
283 const SpdyStreamId stream_id
= delegate
.stream_id();
285 EXPECT_TRUE(delegate
.send_headers_completed());
286 EXPECT_EQ("200", delegate
.GetResponseHeaderValue(spdy_util_
.GetStatusKey()));
287 EXPECT_EQ(std::string(kPostBody
, kPostBodyLength
),
288 delegate
.TakeReceivedData());
289 EXPECT_TRUE(data
.at_write_eof());
291 // Check that the NetLog was filled reasonably.
292 net::CapturingNetLog::CapturedEntryList entries
;
293 log
.GetEntries(&entries
);
294 EXPECT_LT(0u, entries
.size());
296 // Check that we logged SPDY_STREAM_ERROR correctly.
297 int pos
= net::ExpectLogContainsSomewhere(
298 entries
, 0, net::NetLog::TYPE_HTTP2_STREAM_ERROR
,
299 net::NetLog::PHASE_NONE
);
302 ASSERT_TRUE(entries
[pos
].GetIntegerValue("stream_id", &stream_id2
));
303 EXPECT_EQ(static_cast<int>(stream_id
), stream_id2
);
306 // Make sure that large blocks of data are properly split up into
307 // frame-sized chunks for a request/response (i.e., an HTTP-like)
309 TEST_P(SpdyStreamTest
, SendLargeDataAfterOpenRequestResponse
) {
310 GURL
url(kStreamUrl
);
312 session_
= SpdySessionDependencies::SpdyCreateSession(&session_deps_
);
314 scoped_ptr
<SpdyFrame
> req(
315 spdy_util_
.ConstructSpdyPost(
316 kStreamUrl
, 1, kPostBodyLength
, LOWEST
, NULL
, 0));
319 std::string
chunk_data(kMaxSpdyFrameChunkSize
, 'x');
320 scoped_ptr
<SpdyFrame
> chunk(
321 spdy_util_
.ConstructSpdyBodyFrame(
322 1, chunk_data
.data(), chunk_data
.length(), false));
326 scoped_ptr
<SpdyFrame
> last_chunk(
327 spdy_util_
.ConstructSpdyBodyFrame(
328 1, chunk_data
.data(), chunk_data
.length(), true));
329 AddWrite(*last_chunk
);
331 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyPostSynReply(NULL
, 0));
336 OrderedSocketData
data(GetReads(), GetNumReads(),
337 GetWrites(), GetNumWrites());
338 MockConnect
connect_data(SYNCHRONOUS
, OK
);
339 data
.set_connect_data(connect_data
);
341 session_deps_
.socket_factory
->AddSocketDataProvider(&data
);
343 base::WeakPtr
<SpdySession
> session(CreateDefaultSpdySession());
345 base::WeakPtr
<SpdyStream
> stream
=
346 CreateStreamSynchronously(
347 SPDY_REQUEST_RESPONSE_STREAM
, session
, url
, LOWEST
, BoundNetLog());
348 ASSERT_TRUE(stream
.get() != NULL
);
350 std::string
body_data(3 * kMaxSpdyFrameChunkSize
, 'x');
351 StreamDelegateWithBody
delegate(stream
, body_data
);
352 stream
->SetDelegate(&delegate
);
354 EXPECT_FALSE(stream
->HasUrlFromHeaders());
356 scoped_ptr
<SpdyHeaderBlock
> headers(
357 spdy_util_
.ConstructPostHeaderBlock(kStreamUrl
, kPostBodyLength
));
358 EXPECT_EQ(ERR_IO_PENDING
,
359 stream
->SendRequestHeaders(headers
.Pass(), MORE_DATA_TO_SEND
));
360 EXPECT_TRUE(stream
->HasUrlFromHeaders());
361 EXPECT_EQ(kStreamUrl
, stream
->GetUrlFromHeaders().spec());
363 EXPECT_EQ(ERR_CONNECTION_CLOSED
, delegate
.WaitForClose());
365 EXPECT_TRUE(delegate
.send_headers_completed());
366 EXPECT_EQ("200", delegate
.GetResponseHeaderValue(spdy_util_
.GetStatusKey()));
367 EXPECT_EQ(std::string(), delegate
.TakeReceivedData());
368 EXPECT_TRUE(data
.at_write_eof());
371 // Make sure that large blocks of data are properly split up into
372 // frame-sized chunks for a bidirectional (i.e., non-HTTP-like)
374 TEST_P(SpdyStreamTest
, SendLargeDataAfterOpenBidirectional
) {
375 GURL
url(kStreamUrl
);
377 session_
= SpdySessionDependencies::SpdyCreateSession(&session_deps_
);
379 scoped_ptr
<SpdyFrame
> req(
380 spdy_util_
.ConstructSpdyPost(
381 kStreamUrl
, 1, kPostBodyLength
, LOWEST
, NULL
, 0));
384 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyPostSynReply(NULL
, 0));
387 std::string
chunk_data(kMaxSpdyFrameChunkSize
, 'x');
388 scoped_ptr
<SpdyFrame
> chunk(
389 spdy_util_
.ConstructSpdyBodyFrame(
390 1, chunk_data
.data(), chunk_data
.length(), false));
397 OrderedSocketData
data(GetReads(), GetNumReads(),
398 GetWrites(), GetNumWrites());
399 MockConnect
connect_data(SYNCHRONOUS
, OK
);
400 data
.set_connect_data(connect_data
);
402 session_deps_
.socket_factory
->AddSocketDataProvider(&data
);
404 base::WeakPtr
<SpdySession
> session(CreateDefaultSpdySession());
406 base::WeakPtr
<SpdyStream
> stream
=
407 CreateStreamSynchronously(
408 SPDY_BIDIRECTIONAL_STREAM
, session
, url
, LOWEST
, BoundNetLog());
409 ASSERT_TRUE(stream
.get() != NULL
);
411 std::string
body_data(3 * kMaxSpdyFrameChunkSize
, 'x');
412 StreamDelegateSendImmediate
delegate(stream
, body_data
);
413 stream
->SetDelegate(&delegate
);
415 EXPECT_FALSE(stream
->HasUrlFromHeaders());
417 scoped_ptr
<SpdyHeaderBlock
> headers(
418 spdy_util_
.ConstructPostHeaderBlock(kStreamUrl
, kPostBodyLength
));
419 EXPECT_EQ(ERR_IO_PENDING
,
420 stream
->SendRequestHeaders(headers
.Pass(), MORE_DATA_TO_SEND
));
421 EXPECT_TRUE(stream
->HasUrlFromHeaders());
422 EXPECT_EQ(kStreamUrl
, stream
->GetUrlFromHeaders().spec());
424 EXPECT_EQ(ERR_CONNECTION_CLOSED
, delegate
.WaitForClose());
426 EXPECT_TRUE(delegate
.send_headers_completed());
427 EXPECT_EQ("200", delegate
.GetResponseHeaderValue(spdy_util_
.GetStatusKey()));
428 EXPECT_EQ(std::string(), delegate
.TakeReceivedData());
429 EXPECT_TRUE(data
.at_write_eof());
432 // Receiving a header with uppercase ASCII should result in a protocol
434 TEST_P(SpdyStreamTest
, UpperCaseHeaders
) {
435 GURL
url(kStreamUrl
);
438 SpdySessionDependencies::SpdyCreateSessionDeterministic(&session_deps_
);
440 scoped_ptr
<SpdyFrame
> syn(
441 spdy_util_
.ConstructSpdyGet(NULL
, 0, false, 1, LOWEST
, true));
444 const char* const kExtraHeaders
[] = {"X-UpperCase", "yes"};
445 scoped_ptr
<SpdyFrame
>
446 reply(spdy_util_
.ConstructSpdyGetSynReply(kExtraHeaders
, 1, 1));
449 scoped_ptr
<SpdyFrame
> rst(
450 spdy_util_
.ConstructSpdyRstStream(1, RST_STREAM_PROTOCOL_ERROR
));
455 DeterministicSocketData
data(GetReads(), GetNumReads(),
456 GetWrites(), GetNumWrites());
457 MockConnect
connect_data(SYNCHRONOUS
, OK
);
458 data
.set_connect_data(connect_data
);
460 session_deps_
.deterministic_socket_factory
->AddSocketDataProvider(&data
);
462 base::WeakPtr
<SpdySession
> session(CreateDefaultSpdySession());
464 base::WeakPtr
<SpdyStream
> stream
=
465 CreateStreamSynchronously(
466 SPDY_REQUEST_RESPONSE_STREAM
, session
, url
, LOWEST
, BoundNetLog());
467 ASSERT_TRUE(stream
.get() != NULL
);
469 StreamDelegateDoNothing
delegate(stream
);
470 stream
->SetDelegate(&delegate
);
472 EXPECT_FALSE(stream
->HasUrlFromHeaders());
474 scoped_ptr
<SpdyHeaderBlock
> headers(
475 spdy_util_
.ConstructGetHeaderBlock(kStreamUrl
));
476 EXPECT_EQ(ERR_IO_PENDING
,
477 stream
->SendRequestHeaders(headers
.Pass(), NO_MORE_DATA_TO_SEND
));
478 EXPECT_TRUE(stream
->HasUrlFromHeaders());
479 EXPECT_EQ(kStreamUrl
, stream
->GetUrlFromHeaders().spec());
483 EXPECT_EQ(ERR_SPDY_PROTOCOL_ERROR
, delegate
.WaitForClose());
486 // Receiving a header with uppercase ASCII should result in a protocol
487 // error even for a push stream.
488 TEST_P(SpdyStreamTest
, UpperCaseHeadersOnPush
) {
489 GURL
url(kStreamUrl
);
492 SpdySessionDependencies::SpdyCreateSessionDeterministic(&session_deps_
);
494 scoped_ptr
<SpdyFrame
> syn(
495 spdy_util_
.ConstructSpdyGet(NULL
, 0, false, 1, LOWEST
, true));
498 scoped_ptr
<SpdyFrame
>
499 reply(spdy_util_
.ConstructSpdyGetSynReply(NULL
, 0, 1));
502 const char* const extra_headers
[] = {"X-UpperCase", "yes"};
503 scoped_ptr
<SpdyFrame
>
504 push(spdy_util_
.ConstructSpdyPush(extra_headers
, 1, 2, 1, kStreamUrl
));
507 scoped_ptr
<SpdyFrame
> rst(
508 spdy_util_
.ConstructSpdyRstStream(2, RST_STREAM_PROTOCOL_ERROR
));
513 DeterministicSocketData
data(GetReads(), GetNumReads(),
514 GetWrites(), GetNumWrites());
515 MockConnect
connect_data(SYNCHRONOUS
, OK
);
516 data
.set_connect_data(connect_data
);
518 session_deps_
.deterministic_socket_factory
->AddSocketDataProvider(&data
);
520 base::WeakPtr
<SpdySession
> session(CreateDefaultSpdySession());
522 base::WeakPtr
<SpdyStream
> stream
=
523 CreateStreamSynchronously(
524 SPDY_REQUEST_RESPONSE_STREAM
, session
, url
, LOWEST
, BoundNetLog());
525 ASSERT_TRUE(stream
.get() != NULL
);
527 StreamDelegateDoNothing
delegate(stream
);
528 stream
->SetDelegate(&delegate
);
530 EXPECT_FALSE(stream
->HasUrlFromHeaders());
532 scoped_ptr
<SpdyHeaderBlock
> headers(
533 spdy_util_
.ConstructGetHeaderBlock(kStreamUrl
));
534 EXPECT_EQ(ERR_IO_PENDING
,
535 stream
->SendRequestHeaders(headers
.Pass(), NO_MORE_DATA_TO_SEND
));
536 EXPECT_TRUE(stream
->HasUrlFromHeaders());
537 EXPECT_EQ(kStreamUrl
, stream
->GetUrlFromHeaders().spec());
541 base::WeakPtr
<SpdyStream
> push_stream
;
542 EXPECT_EQ(OK
, session
->GetPushStream(url
, &push_stream
, BoundNetLog()));
543 EXPECT_FALSE(push_stream
);
547 EXPECT_EQ(ERR_CONNECTION_CLOSED
, delegate
.WaitForClose());
550 // Receiving a header with uppercase ASCII in a HEADERS frame should
551 // result in a protocol error.
552 TEST_P(SpdyStreamTest
, UpperCaseHeadersInHeadersFrame
) {
553 GURL
url(kStreamUrl
);
556 SpdySessionDependencies::SpdyCreateSessionDeterministic(&session_deps_
);
558 scoped_ptr
<SpdyFrame
> syn(
559 spdy_util_
.ConstructSpdyGet(NULL
, 0, false, 1, LOWEST
, true));
562 scoped_ptr
<SpdyFrame
>
563 reply(spdy_util_
.ConstructSpdyGetSynReply(NULL
, 0, 1));
566 scoped_ptr
<SpdyFrame
>
567 push(spdy_util_
.ConstructSpdyPush(NULL
, 0, 2, 1, kStreamUrl
));
570 scoped_ptr
<SpdyHeaderBlock
> late_headers(new SpdyHeaderBlock());
571 (*late_headers
)["X-UpperCase"] = "yes";
572 scoped_ptr
<SpdyFrame
> headers_frame(
573 spdy_util_
.ConstructSpdyControlFrame(late_headers
.Pass(),
580 AddRead(*headers_frame
);
582 scoped_ptr
<SpdyFrame
> rst(
583 spdy_util_
.ConstructSpdyRstStream(2, RST_STREAM_PROTOCOL_ERROR
));
588 DeterministicSocketData
data(GetReads(), GetNumReads(),
589 GetWrites(), GetNumWrites());
590 MockConnect
connect_data(SYNCHRONOUS
, OK
);
591 data
.set_connect_data(connect_data
);
593 session_deps_
.deterministic_socket_factory
->AddSocketDataProvider(&data
);
595 base::WeakPtr
<SpdySession
> session(CreateDefaultSpdySession());
597 base::WeakPtr
<SpdyStream
> stream
=
598 CreateStreamSynchronously(
599 SPDY_REQUEST_RESPONSE_STREAM
, session
, url
, LOWEST
, BoundNetLog());
600 ASSERT_TRUE(stream
.get() != NULL
);
602 StreamDelegateDoNothing
delegate(stream
);
603 stream
->SetDelegate(&delegate
);
605 EXPECT_FALSE(stream
->HasUrlFromHeaders());
607 scoped_ptr
<SpdyHeaderBlock
> headers(
608 spdy_util_
.ConstructGetHeaderBlock(kStreamUrl
));
609 EXPECT_EQ(ERR_IO_PENDING
,
610 stream
->SendRequestHeaders(headers
.Pass(), NO_MORE_DATA_TO_SEND
));
611 EXPECT_TRUE(stream
->HasUrlFromHeaders());
612 EXPECT_EQ(kStreamUrl
, stream
->GetUrlFromHeaders().spec());
616 base::WeakPtr
<SpdyStream
> push_stream
;
617 EXPECT_EQ(OK
, session
->GetPushStream(url
, &push_stream
, BoundNetLog()));
618 EXPECT_TRUE(push_stream
);
622 EXPECT_EQ(OK
, session
->GetPushStream(url
, &push_stream
, BoundNetLog()));
623 EXPECT_FALSE(push_stream
);
627 EXPECT_EQ(ERR_CONNECTION_CLOSED
, delegate
.WaitForClose());
630 // Receiving a duplicate header in a HEADERS frame should result in a
632 TEST_P(SpdyStreamTest
, DuplicateHeaders
) {
633 GURL
url(kStreamUrl
);
636 SpdySessionDependencies::SpdyCreateSessionDeterministic(&session_deps_
);
638 scoped_ptr
<SpdyFrame
> syn(
639 spdy_util_
.ConstructSpdyGet(NULL
, 0, false, 1, LOWEST
, true));
642 scoped_ptr
<SpdyFrame
>
643 reply(spdy_util_
.ConstructSpdyGetSynReply(NULL
, 0, 1));
646 scoped_ptr
<SpdyFrame
>
647 push(spdy_util_
.ConstructSpdyPush(NULL
, 0, 2, 1, kStreamUrl
));
650 scoped_ptr
<SpdyHeaderBlock
> late_headers(new SpdyHeaderBlock());
651 (*late_headers
)[spdy_util_
.GetStatusKey()] = "500 Server Error";
652 scoped_ptr
<SpdyFrame
> headers_frame(
653 spdy_util_
.ConstructSpdyControlFrame(late_headers
.Pass(),
660 AddRead(*headers_frame
);
662 scoped_ptr
<SpdyFrame
> rst(
663 spdy_util_
.ConstructSpdyRstStream(2, RST_STREAM_PROTOCOL_ERROR
));
668 DeterministicSocketData
data(GetReads(), GetNumReads(),
669 GetWrites(), GetNumWrites());
670 MockConnect
connect_data(SYNCHRONOUS
, OK
);
671 data
.set_connect_data(connect_data
);
673 session_deps_
.deterministic_socket_factory
->AddSocketDataProvider(&data
);
675 base::WeakPtr
<SpdySession
> session(CreateDefaultSpdySession());
677 base::WeakPtr
<SpdyStream
> stream
=
678 CreateStreamSynchronously(
679 SPDY_REQUEST_RESPONSE_STREAM
, session
, url
, LOWEST
, BoundNetLog());
680 ASSERT_TRUE(stream
.get() != NULL
);
682 StreamDelegateDoNothing
delegate(stream
);
683 stream
->SetDelegate(&delegate
);
685 EXPECT_FALSE(stream
->HasUrlFromHeaders());
687 scoped_ptr
<SpdyHeaderBlock
> headers(
688 spdy_util_
.ConstructGetHeaderBlock(kStreamUrl
));
689 EXPECT_EQ(ERR_IO_PENDING
,
690 stream
->SendRequestHeaders(headers
.Pass(), NO_MORE_DATA_TO_SEND
));
691 EXPECT_TRUE(stream
->HasUrlFromHeaders());
692 EXPECT_EQ(kStreamUrl
, stream
->GetUrlFromHeaders().spec());
696 base::WeakPtr
<SpdyStream
> push_stream
;
697 EXPECT_EQ(OK
, session
->GetPushStream(url
, &push_stream
, BoundNetLog()));
698 EXPECT_TRUE(push_stream
);
702 EXPECT_EQ(OK
, session
->GetPushStream(url
, &push_stream
, BoundNetLog()));
703 EXPECT_FALSE(push_stream
);
707 EXPECT_EQ(ERR_CONNECTION_CLOSED
, delegate
.WaitForClose());
710 // The tests below are only for SPDY/3 and above.
712 // Call IncreaseSendWindowSize on a stream with a large enough delta
713 // to overflow an int32. The SpdyStream should handle that case
715 TEST_P(SpdyStreamTest
, IncreaseSendWindowSizeOverflow
) {
717 SpdySessionDependencies::SpdyCreateSessionDeterministic(&session_deps_
);
719 scoped_ptr
<SpdyFrame
> req(
720 spdy_util_
.ConstructSpdyPost(
721 kStreamUrl
, 1, kPostBodyLength
, LOWEST
, NULL
, 0));
724 // Triggered by the overflowing call to IncreaseSendWindowSize
726 scoped_ptr
<SpdyFrame
> rst(
727 spdy_util_
.ConstructSpdyRstStream(1, RST_STREAM_FLOW_CONTROL_ERROR
));
732 CapturingBoundNetLog log
;
734 DeterministicSocketData
data(GetReads(), GetNumReads(),
735 GetWrites(), GetNumWrites());
736 MockConnect
connect_data(SYNCHRONOUS
, OK
);
737 data
.set_connect_data(connect_data
);
739 session_deps_
.deterministic_socket_factory
->AddSocketDataProvider(&data
);
741 base::WeakPtr
<SpdySession
> session(CreateDefaultSpdySession());
742 GURL
url(kStreamUrl
);
744 base::WeakPtr
<SpdyStream
> stream
=
745 CreateStreamSynchronously(
746 SPDY_BIDIRECTIONAL_STREAM
, session
, url
, LOWEST
, log
.bound());
747 ASSERT_TRUE(stream
.get() != NULL
);
748 StreamDelegateSendImmediate
delegate(stream
, kPostBodyStringPiece
);
749 stream
->SetDelegate(&delegate
);
751 scoped_ptr
<SpdyHeaderBlock
> headers(
752 spdy_util_
.ConstructPostHeaderBlock(kStreamUrl
, kPostBodyLength
));
753 EXPECT_EQ(ERR_IO_PENDING
,
754 stream
->SendRequestHeaders(headers
.Pass(), MORE_DATA_TO_SEND
));
755 EXPECT_TRUE(stream
->HasUrlFromHeaders());
756 EXPECT_EQ(kStreamUrl
, stream
->GetUrlFromHeaders().spec());
760 int32 old_send_window_size
= stream
->send_window_size();
761 ASSERT_GT(old_send_window_size
, 0);
762 int32 delta_window_size
= kint32max
- old_send_window_size
+ 1;
763 stream
->IncreaseSendWindowSize(delta_window_size
);
764 EXPECT_EQ(NULL
, stream
.get());
768 EXPECT_EQ(ERR_SPDY_PROTOCOL_ERROR
, delegate
.WaitForClose());
771 // Functions used with
772 // RunResumeAfterUnstall{RequestResponse,Bidirectional}Test().
774 void StallStream(const base::WeakPtr
<SpdyStream
>& stream
) {
775 // Reduce the send window size to 0 to stall.
776 while (stream
->send_window_size() > 0) {
777 stream
->DecreaseSendWindowSize(
778 std::min(kMaxSpdyFrameChunkSize
, stream
->send_window_size()));
782 void IncreaseStreamSendWindowSize(const base::WeakPtr
<SpdyStream
>& stream
,
783 int32 delta_window_size
) {
784 EXPECT_TRUE(stream
->send_stalled_by_flow_control());
785 stream
->IncreaseSendWindowSize(delta_window_size
);
786 EXPECT_FALSE(stream
->send_stalled_by_flow_control());
789 void AdjustStreamSendWindowSize(const base::WeakPtr
<SpdyStream
>& stream
,
790 int32 delta_window_size
) {
791 // Make sure that negative adjustments are handled properly.
792 EXPECT_TRUE(stream
->send_stalled_by_flow_control());
793 stream
->AdjustSendWindowSize(-delta_window_size
);
794 EXPECT_TRUE(stream
->send_stalled_by_flow_control());
795 stream
->AdjustSendWindowSize(+delta_window_size
);
796 EXPECT_TRUE(stream
->send_stalled_by_flow_control());
797 stream
->AdjustSendWindowSize(+delta_window_size
);
798 EXPECT_FALSE(stream
->send_stalled_by_flow_control());
801 // Given an unstall function, runs a test to make sure that a
802 // request/response (i.e., an HTTP-like) stream resumes after a stall
804 void SpdyStreamTest::RunResumeAfterUnstallRequestResponseTest(
805 const UnstallFunction
& unstall_function
) {
806 GURL
url(kStreamUrl
);
809 SpdySessionDependencies::SpdyCreateSessionDeterministic(&session_deps_
);
811 scoped_ptr
<SpdyFrame
> req(
812 spdy_util_
.ConstructSpdyPost(
813 kStreamUrl
, 1, kPostBodyLength
, LOWEST
, NULL
, 0));
816 scoped_ptr
<SpdyFrame
> body(
817 spdy_util_
.ConstructSpdyBodyFrame(1, kPostBody
, kPostBodyLength
, true));
820 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyGetSynReply(NULL
, 0, 1));
825 DeterministicSocketData
data(GetReads(), GetNumReads(),
826 GetWrites(), GetNumWrites());
827 MockConnect
connect_data(SYNCHRONOUS
, OK
);
828 data
.set_connect_data(connect_data
);
830 session_deps_
.deterministic_socket_factory
->AddSocketDataProvider(&data
);
832 base::WeakPtr
<SpdySession
> session(CreateDefaultSpdySession());
834 base::WeakPtr
<SpdyStream
> stream
=
835 CreateStreamSynchronously(
836 SPDY_REQUEST_RESPONSE_STREAM
, session
, url
, LOWEST
, BoundNetLog());
837 ASSERT_TRUE(stream
.get() != NULL
);
839 StreamDelegateWithBody
delegate(stream
, kPostBodyStringPiece
);
840 stream
->SetDelegate(&delegate
);
842 EXPECT_FALSE(stream
->HasUrlFromHeaders());
843 EXPECT_FALSE(stream
->send_stalled_by_flow_control());
845 scoped_ptr
<SpdyHeaderBlock
> headers(
846 spdy_util_
.ConstructPostHeaderBlock(kStreamUrl
, kPostBodyLength
));
847 EXPECT_EQ(ERR_IO_PENDING
,
848 stream
->SendRequestHeaders(headers
.Pass(), MORE_DATA_TO_SEND
));
849 EXPECT_TRUE(stream
->HasUrlFromHeaders());
850 EXPECT_EQ(kStreamUrl
, stream
->GetUrlFromHeaders().spec());
856 EXPECT_TRUE(stream
->send_stalled_by_flow_control());
858 unstall_function
.Run(stream
, kPostBodyLength
);
860 EXPECT_FALSE(stream
->send_stalled_by_flow_control());
864 EXPECT_EQ(ERR_CONNECTION_CLOSED
, delegate
.WaitForClose());
866 EXPECT_TRUE(delegate
.send_headers_completed());
867 EXPECT_EQ("200", delegate
.GetResponseHeaderValue(":status"));
868 EXPECT_EQ(std::string(), delegate
.TakeReceivedData());
869 EXPECT_TRUE(data
.at_write_eof());
872 TEST_P(SpdyStreamTest
, ResumeAfterSendWindowSizeIncreaseRequestResponse
) {
873 RunResumeAfterUnstallRequestResponseTest(
874 base::Bind(&IncreaseStreamSendWindowSize
));
877 TEST_P(SpdyStreamTest
, ResumeAfterSendWindowSizeAdjustRequestResponse
) {
878 RunResumeAfterUnstallRequestResponseTest(
879 base::Bind(&AdjustStreamSendWindowSize
));
882 // Given an unstall function, runs a test to make sure that a
883 // bidirectional (i.e., non-HTTP-like) stream resumes after a stall
885 void SpdyStreamTest::RunResumeAfterUnstallBidirectionalTest(
886 const UnstallFunction
& unstall_function
) {
887 GURL
url(kStreamUrl
);
890 SpdySessionDependencies::SpdyCreateSessionDeterministic(&session_deps_
);
892 scoped_ptr
<SpdyFrame
> req(
893 spdy_util_
.ConstructSpdyPost(
894 kStreamUrl
, 1, kPostBodyLength
, LOWEST
, NULL
, 0));
897 scoped_ptr
<SpdyFrame
> resp(spdy_util_
.ConstructSpdyGetSynReply(NULL
, 0, 1));
900 scoped_ptr
<SpdyFrame
> msg(
901 spdy_util_
.ConstructSpdyBodyFrame(1, kPostBody
, kPostBodyLength
, false));
904 scoped_ptr
<SpdyFrame
> echo(
905 spdy_util_
.ConstructSpdyBodyFrame(1, kPostBody
, kPostBodyLength
, false));
910 DeterministicSocketData
data(GetReads(), GetNumReads(),
911 GetWrites(), GetNumWrites());
912 MockConnect
connect_data(SYNCHRONOUS
, OK
);
913 data
.set_connect_data(connect_data
);
915 session_deps_
.deterministic_socket_factory
->AddSocketDataProvider(&data
);
917 base::WeakPtr
<SpdySession
> session(CreateDefaultSpdySession());
919 base::WeakPtr
<SpdyStream
> stream
=
920 CreateStreamSynchronously(
921 SPDY_BIDIRECTIONAL_STREAM
, session
, url
, LOWEST
, BoundNetLog());
922 ASSERT_TRUE(stream
.get() != NULL
);
924 StreamDelegateSendImmediate
delegate(stream
, kPostBodyStringPiece
);
925 stream
->SetDelegate(&delegate
);
927 EXPECT_FALSE(stream
->HasUrlFromHeaders());
929 scoped_ptr
<SpdyHeaderBlock
> headers(
930 spdy_util_
.ConstructPostHeaderBlock(kStreamUrl
, kPostBodyLength
));
931 EXPECT_EQ(ERR_IO_PENDING
,
932 stream
->SendRequestHeaders(headers
.Pass(), MORE_DATA_TO_SEND
));
933 EXPECT_TRUE(stream
->HasUrlFromHeaders());
934 EXPECT_EQ(kStreamUrl
, stream
->GetUrlFromHeaders().spec());
938 EXPECT_FALSE(stream
->send_stalled_by_flow_control());
944 EXPECT_TRUE(stream
->send_stalled_by_flow_control());
946 unstall_function
.Run(stream
, kPostBodyLength
);
948 EXPECT_FALSE(stream
->send_stalled_by_flow_control());
952 EXPECT_EQ(ERR_CONNECTION_CLOSED
, delegate
.WaitForClose());
954 EXPECT_TRUE(delegate
.send_headers_completed());
955 EXPECT_EQ("200", delegate
.GetResponseHeaderValue(":status"));
956 EXPECT_EQ(std::string(kPostBody
, kPostBodyLength
),
957 delegate
.TakeReceivedData());
958 EXPECT_TRUE(data
.at_write_eof());
961 TEST_P(SpdyStreamTest
, ResumeAfterSendWindowSizeIncreaseBidirectional
) {
962 RunResumeAfterUnstallBidirectionalTest(
963 base::Bind(&IncreaseStreamSendWindowSize
));
966 TEST_P(SpdyStreamTest
, ResumeAfterSendWindowSizeAdjustBidirectional
) {
967 RunResumeAfterUnstallBidirectionalTest(
968 base::Bind(&AdjustStreamSendWindowSize
));
971 // Test calculation of amount of bytes received from network.
972 TEST_P(SpdyStreamTest
, ReceivedBytes
) {
973 GURL
url(kStreamUrl
);
976 SpdySessionDependencies::SpdyCreateSessionDeterministic(&session_deps_
);
978 scoped_ptr
<SpdyFrame
> syn(
979 spdy_util_
.ConstructSpdyGet(NULL
, 0, false, 1, LOWEST
, true));
982 scoped_ptr
<SpdyFrame
>
983 reply(spdy_util_
.ConstructSpdyGetSynReply(NULL
, 0, 1));
986 scoped_ptr
<SpdyFrame
> msg(
987 spdy_util_
.ConstructSpdyBodyFrame(1, kPostBody
, kPostBodyLength
, false));
992 DeterministicSocketData
data(GetReads(), GetNumReads(),
993 GetWrites(), GetNumWrites());
994 MockConnect
connect_data(SYNCHRONOUS
, OK
);
995 data
.set_connect_data(connect_data
);
997 session_deps_
.deterministic_socket_factory
->AddSocketDataProvider(&data
);
999 base::WeakPtr
<SpdySession
> session(CreateDefaultSpdySession());
1001 base::WeakPtr
<SpdyStream
> stream
=
1002 CreateStreamSynchronously(
1003 SPDY_REQUEST_RESPONSE_STREAM
, session
, url
, LOWEST
, BoundNetLog());
1004 ASSERT_TRUE(stream
.get() != NULL
);
1006 StreamDelegateDoNothing
delegate(stream
);
1007 stream
->SetDelegate(&delegate
);
1009 EXPECT_FALSE(stream
->HasUrlFromHeaders());
1011 scoped_ptr
<SpdyHeaderBlock
> headers(
1012 spdy_util_
.ConstructGetHeaderBlock(kStreamUrl
));
1013 EXPECT_EQ(ERR_IO_PENDING
,
1014 stream
->SendRequestHeaders(headers
.Pass(), NO_MORE_DATA_TO_SEND
));
1015 EXPECT_TRUE(stream
->HasUrlFromHeaders());
1016 EXPECT_EQ(kStreamUrl
, stream
->GetUrlFromHeaders().spec());
1018 int64 reply_frame_len
= reply
->size();
1019 int64 data_header_len
= spdy_util_
.CreateFramer(false)
1020 ->GetDataFrameMinimumSize();
1021 int64 data_frame_len
= data_header_len
+ kPostBodyLength
;
1022 int64 response_len
= reply_frame_len
+ data_frame_len
;
1024 EXPECT_EQ(0, stream
->raw_received_bytes());
1025 data
.RunFor(1); // SYN
1026 EXPECT_EQ(0, stream
->raw_received_bytes());
1027 data
.RunFor(1); // REPLY
1028 EXPECT_EQ(reply_frame_len
, stream
->raw_received_bytes());
1029 data
.RunFor(1); // DATA
1030 EXPECT_EQ(response_len
, stream
->raw_received_bytes());
1031 data
.RunFor(1); // FIN
1033 EXPECT_EQ(ERR_CONNECTION_CLOSED
, delegate
.WaitForClose());