Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(491)

Unified Diff: net/spdy/spdy_session_spdy3_unittest.cc

Issue 13834009: SPDY - Re-land greedy read support for SpdySession (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: histograms to track bytes_read during init Created 7 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: net/spdy/spdy_session_spdy3_unittest.cc
diff --git a/net/spdy/spdy_session_spdy3_unittest.cc b/net/spdy/spdy_session_spdy3_unittest.cc
index 519f1d3e643dacc116dca89d3df0bff24cd8f014..f376f656ff0e05cdc9d921d5e6a39e38d1211c4f 100644
--- a/net/spdy/spdy_session_spdy3_unittest.cc
+++ b/net/spdy/spdy_session_spdy3_unittest.cc
@@ -4,14 +4,18 @@
#include "net/spdy/spdy_session.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/base/io_buffer.h"
#include "net/base/ip_endpoint.h"
#include "net/base/net_log_unittest.h"
#include "net/base/request_priority.h"
#include "net/base/test_data_directory.h"
+#include "net/base/test_data_stream.h"
#include "net/dns/host_cache.h"
#include "net/spdy/spdy_http_utils.h"
#include "net/spdy/spdy_io_buffer.h"
#include "net/spdy/spdy_session_pool.h"
+#include "net/spdy/spdy_session_test_util.h"
#include "net/spdy/spdy_stream.h"
#include "net/spdy/spdy_stream_test_util.h"
#include "net/spdy/spdy_test_util_common.h"
@@ -1685,6 +1689,348 @@ TEST_F(SpdySessionSpdy3Test, UpdateStreamsSendWindowSize) {
spdy_stream2 = NULL;
}
+// Test that SpdySession::DoRead reads data from the socket without yielding.
+// This test makes 32k - 1 bytes of data available on the socket for reading. It
+// then verifies that it has read all the available data without yielding.
+TEST_F(SpdySessionSpdy3Test, ReadDataWithoutYielding) {
+ MockConnect connect_data(SYNCHRONOUS, OK);
+ BufferedSpdyFramer framer(3, false);
+
+ scoped_ptr<SpdyFrame> req1(ConstructSpdyGet(NULL, 0, false, 1, MEDIUM));
+ MockWrite writes[] = {
+ CreateMockWrite(*req1, 0),
+ };
+
+ // Build buffer of size kMaxReadBytes / 4 (-spdy_data_frame_size).
+ ASSERT_EQ(32 * 1024, kMaxReadBytes);
+ const int kPayloadSize =
+ kMaxReadBytes / 4 - framer.GetControlFrameHeaderSize();
+ TestDataStream test_stream;
+ scoped_refptr<net::IOBuffer> payload(new net::IOBuffer(kPayloadSize));
+ char* payload_data = payload->data();
+ test_stream.GetBytes(payload_data, kPayloadSize);
+
+ scoped_ptr<SpdyFrame> partial_data_frame(
+ framer.CreateDataFrame(1, payload_data, kPayloadSize, DATA_FLAG_NONE));
+ scoped_ptr<SpdyFrame> finish_data_frame(
+ framer.CreateDataFrame(1, payload_data, kPayloadSize - 1, DATA_FLAG_FIN));
+
+ scoped_ptr<SpdyFrame> resp1(ConstructSpdyGetSynReply(NULL, 0, 1));
+
+ // Write 1 byte less than kMaxReadBytes to check that DoRead reads up to 32k
+ // bytes.
+ MockRead reads[] = {
+ CreateMockRead(*resp1, 1),
+ CreateMockRead(*partial_data_frame, 2),
+ CreateMockRead(*partial_data_frame, 3, SYNCHRONOUS),
+ CreateMockRead(*partial_data_frame, 4, SYNCHRONOUS),
+ CreateMockRead(*finish_data_frame, 5, SYNCHRONOUS),
+ MockRead(ASYNC, 0, 6) // EOF
+ };
+
+ // Create SpdySession and SpdyStream and send the request.
+ DeterministicSocketData data(reads, arraysize(reads),
+ writes, arraysize(writes));
+ data.set_connect_data(connect_data);
+ session_deps_.host_resolver->set_synchronous_mode(true);
+ session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
+
+ SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
+ session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
+
+ CreateDeterministicNetworkSession();
+
+ scoped_refptr<SpdySession> session = CreateInitializedSession();
+
+ GURL url1("http://www.google.com");
+ scoped_refptr<SpdyStream> spdy_stream1 =
+ CreateStreamSynchronously(session, url1, MEDIUM, BoundNetLog());
+ ASSERT_TRUE(spdy_stream1.get() != NULL);
+ EXPECT_EQ(0u, spdy_stream1->stream_id());
+
+ spdy_stream1->set_spdy_headers(ConstructGetHeaderBlock(url1.spec()));
+ EXPECT_TRUE(spdy_stream1->HasUrl());
+ spdy_stream1->SendRequest(false);
+
+ // Set up the TaskObserver to verify SpdySession::DoRead doesn't post a task.
+ SpdySessionTestTaskObserver observer("spdy_session.cc", "DoRead");
+
+ // Run until 1st read.
+ EXPECT_EQ(0u, spdy_stream1->stream_id());
+ data.RunFor(2);
+ EXPECT_EQ(1u, spdy_stream1->stream_id());
+ EXPECT_EQ(0u, observer.executed_count());
+
+ // Read all the data and verify SpdySession::DoRead has not posted a task.
+ data.RunFor(4);
+
+ // Verify task observer's executed_count is zero, which indicates DoRead read
+ // all the available data.
+ EXPECT_EQ(0u, observer.executed_count());
+ EXPECT_TRUE(data.at_write_eof());
+ EXPECT_TRUE(data.at_read_eof());
+}
+
+// Test that SpdySession::DoRead yields while reading the data. This test makes
+// 32k + 1 bytes of data available on the socket for reading. It then verifies
+// that DoRead has yielded even though there is data available for it to read
+// (i.e, socket()->Read didn't return ERR_IO_PENDING during socket reads).
+TEST_F(SpdySessionSpdy3Test, TestYieldingDuringReadData) {
+ MockConnect connect_data(SYNCHRONOUS, OK);
+ BufferedSpdyFramer framer(3, false);
+
+ scoped_ptr<SpdyFrame> req1(ConstructSpdyGet(NULL, 0, false, 1, MEDIUM));
+ MockWrite writes[] = {
+ CreateMockWrite(*req1, 0),
+ };
+
+ // Build buffer of size kMaxReadBytes / 4 (-spdy_data_frame_size).
+ ASSERT_EQ(32 * 1024, kMaxReadBytes);
+ const int kPayloadSize =
+ kMaxReadBytes / 4 - framer.GetControlFrameHeaderSize();
+ TestDataStream test_stream;
+ scoped_refptr<net::IOBuffer> payload(new net::IOBuffer(kPayloadSize));
+ char* payload_data = payload->data();
+ test_stream.GetBytes(payload_data, kPayloadSize);
+
+ scoped_ptr<SpdyFrame> partial_data_frame(
+ framer.CreateDataFrame(1, payload_data, kPayloadSize, DATA_FLAG_NONE));
+ scoped_ptr<SpdyFrame> finish_data_frame(
+ framer.CreateDataFrame(1, "h", 1, DATA_FLAG_FIN));
+
+ scoped_ptr<SpdyFrame> resp1(ConstructSpdyGetSynReply(NULL, 0, 1));
+
+ // Write 1 byte more than kMaxReadBytes to check that DoRead yields.
+ MockRead reads[] = {
+ CreateMockRead(*resp1, 1),
+ CreateMockRead(*partial_data_frame, 2),
+ CreateMockRead(*partial_data_frame, 3, SYNCHRONOUS),
+ CreateMockRead(*partial_data_frame, 4, SYNCHRONOUS),
+ CreateMockRead(*partial_data_frame, 5, SYNCHRONOUS),
+ CreateMockRead(*finish_data_frame, 6, SYNCHRONOUS),
+ MockRead(ASYNC, 0, 7) // EOF
+ };
+
+ // Create SpdySession and SpdyStream and send the request.
+ DeterministicSocketData data(reads, arraysize(reads),
+ writes, arraysize(writes));
+ data.set_connect_data(connect_data);
+ session_deps_.host_resolver->set_synchronous_mode(true);
+ session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
+
+ SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
+ session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
+
+ CreateDeterministicNetworkSession();
+
+ scoped_refptr<SpdySession> session = CreateInitializedSession();
+
+ GURL url1("http://www.google.com");
+ scoped_refptr<SpdyStream> spdy_stream1 =
+ CreateStreamSynchronously(session, url1, MEDIUM, BoundNetLog());
+ ASSERT_TRUE(spdy_stream1.get() != NULL);
+ EXPECT_EQ(0u, spdy_stream1->stream_id());
+
+ spdy_stream1->set_spdy_headers(ConstructGetHeaderBlock(url1.spec()));
+ EXPECT_TRUE(spdy_stream1->HasUrl());
+ spdy_stream1->SendRequest(false);
+
+ // Set up the TaskObserver to verify SpdySession::DoRead posts a task.
+ SpdySessionTestTaskObserver observer("spdy_session.cc", "DoRead");
+
+ // Run until 1st read.
+ EXPECT_EQ(0u, spdy_stream1->stream_id());
+ data.RunFor(2);
+ EXPECT_EQ(1u, spdy_stream1->stream_id());
+ EXPECT_EQ(0u, observer.executed_count());
+
+ // Read all the data and verify SpdySession::DoRead has posted a task.
+ data.RunFor(6);
+
+ // Verify task observer's executed_count is 1, which indicates DoRead has
+ // posted only one task and thus yielded though there is data available for it
+ // to read.
+ EXPECT_EQ(1u, observer.executed_count());
+ EXPECT_TRUE(data.at_write_eof());
+ EXPECT_TRUE(data.at_read_eof());
+}
+
+// Test that SpdySession::DoRead() tests interactions of yielding + async,
+// by doing the following MockReads.
+//
+// MockRead of SYNCHRONOUS 8K, SYNCHRONOUS 8K, SYNCHRONOUS 8K, SYNCHRONOUS 2K
+// ASYNC 8K, SYNCHRONOUS 8K, SYNCHRONOUS 8K, SYNCHRONOUS 8K, SYNCHRONOUS 2K.
+//
+// The above reads 26K synchronously. Since that is less that 32K, we will
+// attempt to read again. However, that DoRead() will return ERR_IO_PENDING
+// (because of async read), so DoRead() will yield. When we come back, DoRead()
+// will read the results from the async read, and rest of the data
+// synchronously.
+TEST_F(SpdySessionSpdy3Test, TestYieldingDuringAsyncReadData) {
+ MockConnect connect_data(SYNCHRONOUS, OK);
+ BufferedSpdyFramer framer(3, false);
+
+ scoped_ptr<SpdyFrame> req1(ConstructSpdyGet(NULL, 0, false, 1, MEDIUM));
+ MockWrite writes[] = {
+ CreateMockWrite(*req1, 0),
+ };
+
+ // Build buffer of size kMaxReadBytes / 4 (-spdy_data_frame_size).
+ ASSERT_EQ(32 * 1024, kMaxReadBytes);
+ TestDataStream test_stream;
+ const int kEightKPayloadSize =
+ kMaxReadBytes / 4 - framer.GetControlFrameHeaderSize();
+ scoped_refptr<net::IOBuffer> eightk_payload(
+ new net::IOBuffer(kEightKPayloadSize));
+ char* eightk_payload_data = eightk_payload->data();
+ test_stream.GetBytes(eightk_payload_data, kEightKPayloadSize);
+
+ // Build buffer of 2k size.
+ TestDataStream test_stream2;
+ const int kTwoKPayloadSize = kEightKPayloadSize - 6 * 1024;
+ scoped_refptr<net::IOBuffer> twok_payload(
+ new net::IOBuffer(kTwoKPayloadSize));
+ char* twok_payload_data = twok_payload->data();
+ test_stream2.GetBytes(twok_payload_data, kTwoKPayloadSize);
+
+ scoped_ptr<SpdyFrame> eightk_data_frame(framer.CreateDataFrame(
+ 1, eightk_payload_data, kEightKPayloadSize, DATA_FLAG_NONE));
+ scoped_ptr<SpdyFrame> twok_data_frame(framer.CreateDataFrame(
+ 1, twok_payload_data, kTwoKPayloadSize, DATA_FLAG_NONE));
+ scoped_ptr<SpdyFrame> finish_data_frame(framer.CreateDataFrame(
+ 1, "h", 1, DATA_FLAG_FIN));
+
+ scoped_ptr<SpdyFrame> resp1(ConstructSpdyGetSynReply(NULL, 0, 1));
+
+ MockRead reads[] = {
+ CreateMockRead(*resp1, 1),
+ CreateMockRead(*eightk_data_frame, 2),
+ CreateMockRead(*eightk_data_frame, 3, SYNCHRONOUS),
+ CreateMockRead(*eightk_data_frame, 4, SYNCHRONOUS),
+ CreateMockRead(*twok_data_frame, 5, SYNCHRONOUS),
+ CreateMockRead(*eightk_data_frame, 6, ASYNC),
+ CreateMockRead(*eightk_data_frame, 7, SYNCHRONOUS),
+ CreateMockRead(*eightk_data_frame, 8, SYNCHRONOUS),
+ CreateMockRead(*eightk_data_frame, 9, SYNCHRONOUS),
+ CreateMockRead(*twok_data_frame, 10, SYNCHRONOUS),
+ CreateMockRead(*finish_data_frame, 11, SYNCHRONOUS),
+ MockRead(ASYNC, 0, 12) // EOF
+ };
+
+ // Create SpdySession and SpdyStream and send the request.
+ DeterministicSocketData data(reads, arraysize(reads),
+ writes, arraysize(writes));
+ data.set_connect_data(connect_data);
+ session_deps_.host_resolver->set_synchronous_mode(true);
+ session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
+
+ SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
+ session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
+
+ CreateDeterministicNetworkSession();
+
+ scoped_refptr<SpdySession> session = CreateInitializedSession();
+
+ GURL url1("http://www.google.com");
+ scoped_refptr<SpdyStream> spdy_stream1 =
+ CreateStreamSynchronously(session, url1, MEDIUM, BoundNetLog());
+ ASSERT_TRUE(spdy_stream1.get() != NULL);
+ EXPECT_EQ(0u, spdy_stream1->stream_id());
+
+ spdy_stream1->set_spdy_headers(ConstructGetHeaderBlock(url1.spec()));
+ EXPECT_TRUE(spdy_stream1->HasUrl());
+ spdy_stream1->SendRequest(false);
+
+ // Set up the TaskObserver to monitor SpdySession::DoRead posting of tasks.
+ SpdySessionTestTaskObserver observer("spdy_session.cc", "DoRead");
+
+ // Run until 1st read.
+ EXPECT_EQ(0u, spdy_stream1->stream_id());
+ data.RunFor(2);
+ EXPECT_EQ(1u, spdy_stream1->stream_id());
+ EXPECT_EQ(0u, observer.executed_count());
+
+ // Read all the data and verify SpdySession::DoRead has posted a task.
+ data.RunFor(12);
+
+ // Verify task observer's executed_count is 1, which indicates DoRead has
+ // posted only one task and thus yielded though there is data available for
+ // it to read.
+ EXPECT_EQ(1u, observer.executed_count());
+ EXPECT_TRUE(data.at_write_eof());
+ EXPECT_TRUE(data.at_read_eof());
+}
+
+// Send a GoAway frame when SpdySession is in DoLoop. If scoped_refptr to
+// <SpdySession> is deleted from SpdySession::DoLoop(), we get a crash because
+// GoAway could delete the SpdySession from the SpdySessionPool and the last
+// reference to SpdySession.
+TEST_F(SpdySessionSpdy3Test, GoAwayWhileInDoLoop) {
+ MockConnect connect_data(SYNCHRONOUS, OK);
+ BufferedSpdyFramer framer(3, false);
+
+ scoped_ptr<SpdyFrame> req1(ConstructSpdyGet(NULL, 0, false, 1, MEDIUM));
+ MockWrite writes[] = {
+ CreateMockWrite(*req1, 0),
+ };
+
+ scoped_ptr<SpdyFrame> resp1(ConstructSpdyGetSynReply(NULL, 0, 1));
+ scoped_ptr<SpdyFrame> body1(ConstructSpdyBodyFrame(1, true));
+ scoped_ptr<SpdyFrame> goaway(ConstructSpdyGoAway());
+
+ MockRead reads[] = {
+ CreateMockRead(*resp1, 1),
+ CreateMockRead(*body1, 2),
+ CreateMockRead(*goaway, 3),
+ MockRead(ASYNC, 0, 4) // EOF
+ };
+
+ // Create SpdySession and SpdyStream and send the request.
+ DeterministicSocketData data(reads, arraysize(reads),
+ writes, arraysize(writes));
+ data.set_connect_data(connect_data);
+ session_deps_.host_resolver->set_synchronous_mode(true);
+ session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
+
+ SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
+ session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl);
+
+ CreateDeterministicNetworkSession();
+
+ scoped_refptr<SpdySession> session = CreateInitializedSession();
+
+ GURL url1("http://www.google.com");
+ scoped_refptr<SpdyStream> spdy_stream1 =
+ CreateStreamSynchronously(session, url1, MEDIUM, BoundNetLog());
+ ASSERT_TRUE(spdy_stream1.get() != NULL);
+ EXPECT_EQ(0u, spdy_stream1->stream_id());
+
+ spdy_stream1->set_spdy_headers(ConstructGetHeaderBlock(url1.spec()));
+ EXPECT_TRUE(spdy_stream1->HasUrl());
+ spdy_stream1->SendRequest(false);
+
+ // Run until 1st read.
+ EXPECT_EQ(0u, spdy_stream1->stream_id());
+ data.RunFor(1);
+ EXPECT_EQ(1u, spdy_stream1->stream_id());
+
+ // Drop the reference to the session.
+ session = NULL;
+
+ // Run until GoAway.
+ data.RunFor(2);
+
+ // Drop the reference to the stream which deletes its reference to the
+ // SpdySession. Only references to SpdySession are held by DoLoop and
+ // SpdySessionPool. If DoLoop doesn't hold the reference, we get a crash if
+ // SpdySession is deleted from the SpdySessionPool.
+ spdy_stream1 = NULL;
+
+ data.RunFor(2);
+ EXPECT_TRUE(data.at_write_eof());
+ EXPECT_TRUE(data.at_read_eof());
+}
+
// Within this framework, a SpdySession should be initialized with
// flow control enabled only for streams and with protocol version 3
// by default.

Powered by Google App Engine
This is Rietveld 408576698