Index: net/spdy/spdy_session_unittest.cc |
diff --git a/net/spdy/spdy_session_unittest.cc b/net/spdy/spdy_session_unittest.cc |
index 1f7ca60daf75cd4a5577812870b720fbd2408592..dd8679174efd403905fd7440e2e8340cb1ce6b44 100644 |
--- a/net/spdy/spdy_session_unittest.cc |
+++ b/net/spdy/spdy_session_unittest.cc |
@@ -282,8 +282,7 @@ TEST_P(SpdySessionTest, PendingStreamCancellingAnother) { |
EXPECT_EQ(ERR_ABORTED, callback1.WaitForResult()); |
} |
-// A session receiving a GOAWAY frame with no active streams should |
-// immediately close. |
+// A session receiving a GOAWAY frame with no active streams should close. |
TEST_P(SpdySessionTest, GoAwayWithNoActiveStreams) { |
session_deps_.host_resolver->set_synchronous_mode(true); |
@@ -307,9 +306,8 @@ TEST_P(SpdySessionTest, GoAwayWithNoActiveStreams) { |
// Read and process the GOAWAY frame. |
data.RunFor(1); |
- |
EXPECT_FALSE(HasSpdySession(spdy_session_pool_, key_)); |
- |
+ base::RunLoop().RunUntilIdle(); |
EXPECT_TRUE(session == NULL); |
} |
@@ -409,12 +407,13 @@ TEST_P(SpdySessionTest, GoAwayWithActiveStreams) { |
EXPECT_EQ(NULL, spdy_stream2.get()); |
EXPECT_TRUE(session->IsStreamActive(1)); |
- EXPECT_FALSE(session->IsClosed()); |
+ EXPECT_TRUE(session->IsGoingAway()); |
// Should close the session. |
spdy_stream1->Close(); |
EXPECT_EQ(NULL, spdy_stream1.get()); |
+ base::MessageLoop::current()->RunUntilIdle(); |
EXPECT_TRUE(session == NULL); |
} |
@@ -489,13 +488,12 @@ TEST_P(SpdySessionTest, GoAwayTwice) { |
EXPECT_FALSE(session->IsStreamActive(3)); |
EXPECT_EQ(NULL, spdy_stream2.get()); |
EXPECT_TRUE(session->IsStreamActive(1)); |
- |
- EXPECT_FALSE(session->IsClosed()); |
+ EXPECT_TRUE(session->IsGoingAway()); |
// Read and process the second GOAWAY frame, which should close the |
// session. |
data.RunFor(1); |
- |
+ base::MessageLoop::current()->RunUntilIdle(); |
EXPECT_TRUE(session == NULL); |
} |
@@ -568,12 +566,79 @@ TEST_P(SpdySessionTest, GoAwayWithActiveStreamsThenClose) { |
EXPECT_FALSE(session->IsStreamActive(3)); |
EXPECT_EQ(NULL, spdy_stream2.get()); |
EXPECT_TRUE(session->IsStreamActive(1)); |
- |
- EXPECT_FALSE(session->IsClosed()); |
+ EXPECT_TRUE(session->IsGoingAway()); |
session->CloseSessionOnError(ERR_ABORTED, "Aborting session"); |
- |
EXPECT_EQ(NULL, spdy_stream1.get()); |
+ |
+ base::MessageLoop::current()->RunUntilIdle(); |
+ EXPECT_TRUE(session == NULL); |
+} |
+ |
+// Process a joint read buffer which causes the session to begin draining, and |
+// then processes a GOAWAY. The session should gracefully drain. Regression test |
+// for crbug.com/379469 |
+TEST_P(SpdySessionTest, GoAwayWhileDraining) { |
+ session_deps_.host_resolver->set_synchronous_mode(true); |
+ |
+ scoped_ptr<SpdyFrame> req( |
+ spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, MEDIUM, true)); |
+ MockWrite writes[] = { |
+ CreateMockWrite(*req, 0), |
+ }; |
+ |
+ scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1)); |
+ scoped_ptr<SpdyFrame> goaway(spdy_util_.ConstructSpdyGoAway(1)); |
+ scoped_ptr<SpdyFrame> body(spdy_util_.ConstructSpdyBodyFrame(1, true)); |
+ size_t joint_size = goaway->size() * 2 + body->size(); |
+ |
+ // Compose interleaved |goaway| and |body| frames into a single read. |
+ scoped_ptr<char[]> buffer(new char[joint_size]); |
+ { |
+ size_t out = 0; |
+ memcpy(&buffer[out], goaway->data(), goaway->size()); |
+ out += goaway->size(); |
+ memcpy(&buffer[out], body->data(), body->size()); |
+ out += body->size(); |
+ memcpy(&buffer[out], goaway->data(), goaway->size()); |
+ out += goaway->size(); |
+ ASSERT_EQ(out, joint_size); |
+ } |
+ SpdyFrame joint_frames(buffer.get(), joint_size, false); |
+ |
+ MockRead reads[] = { |
+ CreateMockRead(*resp, 1), CreateMockRead(joint_frames, 2), |
+ MockRead(ASYNC, 0, 3) // EOF |
+ }; |
+ |
+ MockConnect connect_data(SYNCHRONOUS, OK); |
+ DeterministicSocketData data( |
+ reads, arraysize(reads), writes, arraysize(writes)); |
+ data.set_connect_data(connect_data); |
+ session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data); |
+ |
+ CreateDeterministicNetworkSession(); |
+ base::WeakPtr<SpdySession> session = |
+ CreateInsecureSpdySession(http_session_, key_, BoundNetLog()); |
+ |
+ GURL url(kDefaultURL); |
+ base::WeakPtr<SpdyStream> spdy_stream = CreateStreamSynchronously( |
+ SPDY_REQUEST_RESPONSE_STREAM, session, url, MEDIUM, BoundNetLog()); |
+ test::StreamDelegateDoNothing delegate(spdy_stream); |
+ spdy_stream->SetDelegate(&delegate); |
+ |
+ scoped_ptr<SpdyHeaderBlock> headers( |
+ spdy_util_.ConstructGetHeaderBlock(url.spec())); |
+ spdy_stream->SendRequestHeaders(headers.Pass(), NO_MORE_DATA_TO_SEND); |
+ EXPECT_TRUE(spdy_stream->HasUrlFromHeaders()); |
+ |
+ data.RunFor(3); |
+ base::MessageLoop::current()->RunUntilIdle(); |
+ |
+ // Stream and session closed gracefully. |
+ EXPECT_TRUE(delegate.StreamIsClosed()); |
+ EXPECT_EQ(OK, delegate.WaitForClose()); |
+ EXPECT_EQ(kUploadData, delegate.TakeReceivedData()); |
EXPECT_TRUE(session == NULL); |
} |
@@ -702,7 +767,7 @@ TEST_P(SpdySessionTest, SynStreamAfterGoAway) { |
// Read and process the SYN_STREAM frame, the subsequent RST_STREAM, |
// and EOF. |
data.RunFor(3); |
- |
+ base::MessageLoop::current()->RunUntilIdle(); |
EXPECT_TRUE(session == NULL); |
} |
@@ -761,7 +826,7 @@ TEST_P(SpdySessionTest, NetworkChangeWithActiveStreams) { |
// pre-existing stream is still active. |
EXPECT_FALSE(HasSpdySession(spdy_session_pool_, key_)); |
- EXPECT_FALSE(session->IsClosed()); |
+ EXPECT_TRUE(session->IsGoingAway()); |
EXPECT_TRUE(session->IsStreamActive(1)); |
@@ -770,6 +835,7 @@ TEST_P(SpdySessionTest, NetworkChangeWithActiveStreams) { |
#endif |
EXPECT_EQ(NULL, spdy_stream.get()); |
+ base::MessageLoop::current()->RunUntilIdle(); |
EXPECT_TRUE(session == NULL); |
} |
@@ -1042,6 +1108,7 @@ TEST_P(SpdySessionTest, StreamIdSpaceExhausted) { |
EXPECT_EQ(kUploadData, delegate2.TakeReceivedData()); |
// Session was destroyed. |
+ base::MessageLoop::current()->RunUntilIdle(); |
EXPECT_FALSE(session.get()); |
} |
@@ -1195,6 +1262,7 @@ TEST_P(SpdySessionTest, DeleteExpiredPushStreams) { |
// Read and process EOF. |
data.RunFor(1); |
+ base::MessageLoop::current()->RunUntilIdle(); |
EXPECT_TRUE(session == NULL); |
} |
@@ -1203,14 +1271,18 @@ TEST_P(SpdySessionTest, FailedPing) { |
MockConnect connect_data(SYNCHRONOUS, OK); |
MockRead reads[] = { |
- MockRead(ASYNC, 0, 0, 0) // EOF |
+ MockRead(SYNCHRONOUS, ERR_IO_PENDING) // Stall forever. |
}; |
scoped_ptr<SpdyFrame> write_ping(spdy_util_.ConstructSpdyPing(1, false)); |
- DeterministicSocketData data(reads, arraysize(reads), NULL, 0); |
+ MockWrite writes[] = { |
+ CreateMockWrite(*write_ping), |
+ }; |
+ StaticSocketDataProvider data( |
+ reads, arraysize(reads), writes, arraysize(writes)); |
data.set_connect_data(connect_data); |
- session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data); |
+ session_deps_.socket_factory->AddSocketDataProvider(&data); |
- CreateDeterministicNetworkSession(); |
+ CreateNetworkSession(); |
base::WeakPtr<SpdySession> session = |
CreateInsecureSpdySession(http_session_, key_, BoundNetLog()); |
@@ -1232,7 +1304,7 @@ TEST_P(SpdySessionTest, FailedPing) { |
EXPECT_TRUE(session->check_ping_status_pending()); |
// Assert session is not closed. |
- EXPECT_FALSE(session->IsClosed()); |
+ EXPECT_TRUE(session->IsAvailable()); |
EXPECT_LT(0u, session->num_active_streams() + session->num_created_streams()); |
EXPECT_TRUE(HasSpdySession(spdy_session_pool_, key_)); |
@@ -1241,6 +1313,7 @@ TEST_P(SpdySessionTest, FailedPing) { |
base::TimeTicks now = base::TimeTicks::Now(); |
session->last_activity_time_ = now - base::TimeDelta::FromSeconds(1); |
session->CheckPingStatus(now); |
+ base::MessageLoop::current()->RunUntilIdle(); |
EXPECT_TRUE(session == NULL); |
EXPECT_FALSE(HasSpdySession(spdy_session_pool_, key_)); |
@@ -1304,6 +1377,11 @@ TEST_P(SpdySessionTest, OnSettings) { |
EXPECT_EQ(OK, stream_releaser.WaitForResult()); |
data.RunFor(1); |
+ if (spdy_util_.spdy_version() >= SPDY4) { |
+ // Allow the SETTINGS+ACK to write, so the session finishes draining. |
+ data.RunFor(1); |
+ } |
+ base::MessageLoop::current()->RunUntilIdle(); |
EXPECT_TRUE(session == NULL); |
} |
@@ -1583,7 +1661,7 @@ TEST_P(SpdySessionTest, Initialize) { |
EXPECT_NE(log.bound().source().id, socket_source.id); |
} |
-TEST_P(SpdySessionTest, CloseSessionOnError) { |
+TEST_P(SpdySessionTest, NetLogOnSessionGoaway) { |
session_deps_.host_resolver->set_synchronous_mode(true); |
MockConnect connect_data(SYNCHRONOUS, OK); |
@@ -1625,6 +1703,53 @@ TEST_P(SpdySessionTest, CloseSessionOnError) { |
CapturingNetLog::CapturedEntry entry = entries[pos]; |
int error_code = 0; |
ASSERT_TRUE(entry.GetNetErrorCode(&error_code)); |
+ EXPECT_EQ(OK, error_code); |
+ } else { |
+ ADD_FAILURE(); |
+ } |
+} |
+ |
+TEST_P(SpdySessionTest, NetLogOnSessionEOF) { |
+ session_deps_.host_resolver->set_synchronous_mode(true); |
+ |
+ MockConnect connect_data(SYNCHRONOUS, OK); |
+ MockRead reads[] = { |
+ MockRead(SYNCHRONOUS, 0, 0) // EOF |
+ }; |
+ |
+ StaticSocketDataProvider data(reads, arraysize(reads), NULL, 0); |
+ data.set_connect_data(connect_data); |
+ session_deps_.socket_factory->AddSocketDataProvider(&data); |
+ |
+ CreateNetworkSession(); |
+ |
+ CapturingBoundNetLog log; |
+ base::WeakPtr<SpdySession> session = |
+ CreateInsecureSpdySession(http_session_, key_, log.bound()); |
+ EXPECT_TRUE(HasSpdySession(spdy_session_pool_, key_)); |
+ |
+ // Flush the read completion task. |
+ base::MessageLoop::current()->RunUntilIdle(); |
+ |
+ EXPECT_FALSE(HasSpdySession(spdy_session_pool_, key_)); |
+ EXPECT_TRUE(session == NULL); |
+ |
+ // Check that the NetLog was filled reasonably. |
+ net::CapturingNetLog::CapturedEntryList entries; |
+ log.GetEntries(&entries); |
+ EXPECT_LT(0u, entries.size()); |
+ |
+ // Check that we logged SPDY_SESSION_CLOSE correctly. |
+ int pos = |
+ net::ExpectLogContainsSomewhere(entries, |
+ 0, |
+ net::NetLog::TYPE_SPDY_SESSION_CLOSE, |
+ net::NetLog::PHASE_NONE); |
+ |
+ if (pos < static_cast<int>(entries.size())) { |
+ CapturingNetLog::CapturedEntry entry = entries[pos]; |
+ int error_code = 0; |
+ ASSERT_TRUE(entry.GetNetErrorCode(&error_code)); |
EXPECT_EQ(ERR_CONNECTION_CLOSED, error_code); |
} else { |
ADD_FAILURE(); |
@@ -1857,6 +1982,7 @@ TEST_P(SpdySessionTest, CloseSessionWithTwoCreatedSelfClosingStreams) { |
EXPECT_TRUE(delegate1.StreamIsClosed()); |
EXPECT_TRUE(delegate2.StreamIsClosed()); |
+ base::MessageLoop::current()->RunUntilIdle(); |
EXPECT_TRUE(session == NULL); |
} |
@@ -1930,6 +2056,7 @@ TEST_P(SpdySessionTest, CloseSessionWithTwoCreatedMutuallyClosingStreams) { |
EXPECT_TRUE(delegate1.StreamIsClosed()); |
EXPECT_TRUE(delegate2.StreamIsClosed()); |
+ base::MessageLoop::current()->RunUntilIdle(); |
EXPECT_TRUE(session == NULL); |
} |
@@ -2011,6 +2138,7 @@ TEST_P(SpdySessionTest, CloseSessionWithTwoActivatedSelfClosingStreams) { |
EXPECT_TRUE(delegate1.StreamIsClosed()); |
EXPECT_TRUE(delegate2.StreamIsClosed()); |
+ base::MessageLoop::current()->RunUntilIdle(); |
EXPECT_TRUE(session == NULL); |
} |
@@ -2094,6 +2222,7 @@ TEST_P(SpdySessionTest, CloseSessionWithTwoActivatedMutuallyClosingStreams) { |
EXPECT_TRUE(delegate1.StreamIsClosed()); |
EXPECT_TRUE(delegate2.StreamIsClosed()); |
+ base::MessageLoop::current()->RunUntilIdle(); |
EXPECT_TRUE(session == NULL); |
} |
@@ -2124,12 +2253,14 @@ TEST_P(SpdySessionTest, CloseActivatedStreamThatClosesSession) { |
scoped_ptr<SpdyFrame> req( |
spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, MEDIUM, true)); |
+ scoped_ptr<SpdyFrame> rst( |
+ spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_CANCEL)); |
MockWrite writes[] = { |
- CreateMockWrite(*req, 0), |
+ CreateMockWrite(*req, 0), CreateMockWrite(*rst, 1), |
}; |
MockRead reads[] = { |
- MockRead(ASYNC, 0, 1) // EOF |
+ MockRead(ASYNC, 0, 2) // EOF |
}; |
DeterministicSocketData data(reads, arraysize(reads), |
writes, arraysize(writes)); |
@@ -2166,8 +2297,12 @@ TEST_P(SpdySessionTest, CloseActivatedStreamThatClosesSession) { |
// session). |
spdy_stream->Cancel(); |
+ data.RunFor(1); |
+ base::MessageLoop::current()->RunUntilIdle(); |
+ |
EXPECT_EQ(NULL, spdy_stream.get()); |
EXPECT_TRUE(delegate.StreamIsClosed()); |
+ |
EXPECT_TRUE(session == NULL); |
} |
@@ -4308,6 +4443,7 @@ TEST_P(SpdySessionTest, SendWindowSizeIncreaseWithDeletedSession) { |
// Close the session (since we can't do it from within the delegate |
// method, since it's in the stream's loop). |
session->CloseSessionOnError(ERR_CONNECTION_CLOSED, "Closing session"); |
+ base::RunLoop().RunUntilIdle(); |
EXPECT_TRUE(session == NULL); |
EXPECT_FALSE(HasSpdySession(spdy_session_pool_, key_)); |