Index: net/spdy/spdy_session_spdy3_unittest.cc |
=================================================================== |
--- net/spdy/spdy_session_spdy3_unittest.cc (revision 180807) |
+++ net/spdy/spdy_session_spdy3_unittest.cc (working copy) |
@@ -4,13 +4,21 @@ |
#include "net/spdy/spdy_session.h" |
+#include "base/location.h" |
+#include "base/memory/scoped_ptr.h" |
+#include "base/memory/scoped_vector.h" |
+#include "base/pending_task.h" |
+#include "base/string_util.h" |
#include "net/base/cert_test_util.h" |
#include "net/base/host_cache.h" |
+#include "net/base/io_buffer.h" |
#include "net/base/ip_endpoint.h" |
#include "net/base/net_log_unittest.h" |
#include "net/base/test_data_directory.h" |
+#include "net/base/test_data_stream.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_test_util_spdy3.h" |
#include "testing/platform_test.h" |
@@ -1748,4 +1756,304 @@ |
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 - SpdyDataFrame::size(); |
+ 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(); |
+ |
+ scoped_refptr<SpdyStream> spdy_stream1; |
+ TestCompletionCallback callback1; |
+ GURL url1("http://www.google.com"); |
+ EXPECT_EQ(OK, session->CreateStream(url1, MEDIUM, &spdy_stream1, |
+ BoundNetLog(), callback1.callback())); |
+ EXPECT_EQ(0u, spdy_stream1->stream_id()); |
+ |
+ scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock); |
+ (*headers)[":method"] = "GET"; |
+ (*headers)[":scheme"] = url1.scheme(); |
+ (*headers)[":host"] = url1.host(); |
+ (*headers)[":path"] = url1.path(); |
+ (*headers)[":version"] = "HTTP/1.1"; |
+ |
+ spdy_stream1->set_spdy_headers(headers.Pass()); |
+ 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 - SpdyDataFrame::size(); |
+ 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(); |
+ |
+ scoped_refptr<SpdyStream> spdy_stream1; |
+ TestCompletionCallback callback1; |
+ GURL url1("http://www.google.com"); |
+ EXPECT_EQ(OK, session->CreateStream(url1, MEDIUM, &spdy_stream1, |
+ BoundNetLog(), callback1.callback())); |
+ EXPECT_EQ(0u, spdy_stream1->stream_id()); |
+ |
+ scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock); |
+ (*headers)[":method"] = "GET"; |
+ (*headers)[":scheme"] = url1.scheme(); |
+ (*headers)[":host"] = url1.host(); |
+ (*headers)[":path"] = url1.path(); |
+ (*headers)[":version"] = "HTTP/1.1"; |
+ |
+ spdy_stream1->set_spdy_headers(headers.Pass()); |
+ 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 yields while reading the data. This test makes |
+// 4 reads of kMaxReadBytes / 4 (-spdy_data_frame_size), one read of |
+// kMaxReadBytes (-spdy_data_frame_size), and some reads of kMaxReadBytes + 8k |
+// (-spdy_data_frame_size), 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). Also verifies that SpdySession reads only |
+// SpdySession::kReadBufferSize data from the underlying transport. |
+// TODO(rtennti): Make this test work after fixes to DeterministicSocketData. |
+TEST_F(SpdySessionSpdy3Test, DISABLED_TestYieldingDuringLargeReadData) { |
+ 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 kSmallPayloadSize = kMaxReadBytes / 4 - SpdyDataFrame::size(); |
+ scoped_refptr<net::IOBuffer> small_payload( |
+ new net::IOBuffer(kSmallPayloadSize)); |
+ char* small_payload_data = small_payload->data(); |
+ test_stream.GetBytes(small_payload_data, kSmallPayloadSize); |
+ |
+ // Build buffer of size kMaxReadBytes - (-spdy_data_frame_size). |
+ TestDataStream test_stream1; |
+ const int kMaxReadBytesPayloadSize = kMaxReadBytes - SpdyDataFrame::size(); |
+ scoped_refptr<net::IOBuffer> max_bytes_payload( |
+ new net::IOBuffer(kMaxReadBytesPayloadSize)); |
+ char* max_bytes_payload_data = max_bytes_payload->data(); |
+ test_stream1.GetBytes(max_bytes_payload_data, kMaxReadBytesPayloadSize); |
+ |
+ // Build buffer of size kMaxReadBytes + kSmallPayloadSize |
+ // (-spdy_data_frame_size). |
+ TestDataStream test_stream2; |
+ const int kLargePayloadSize = |
+ kMaxReadBytes + kSmallPayloadSize - SpdyDataFrame::size(); |
+ scoped_refptr<net::IOBuffer> large_payload( |
+ new net::IOBuffer(kLargePayloadSize)); |
+ char* large_payload_data = large_payload->data(); |
+ test_stream2.GetBytes(large_payload_data, kLargePayloadSize); |
+ |
+ scoped_ptr<SpdyFrame> small_data_frame(framer.CreateDataFrame( |
+ 1, small_payload_data, kSmallPayloadSize, DATA_FLAG_NONE)); |
+ scoped_ptr<SpdyFrame> max_bytes_data_frame(framer.CreateDataFrame( |
+ 1, max_bytes_payload_data, kMaxReadBytesPayloadSize, DATA_FLAG_NONE)); |
+ scoped_ptr<SpdyFrame> large_data_frame(framer.CreateDataFrame( |
+ 1, large_payload_data, kLargePayloadSize, 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(*small_data_frame, 2), |
+ CreateMockRead(*small_data_frame, 3, SYNCHRONOUS), |
+ CreateMockRead(*small_data_frame, 4, SYNCHRONOUS), |
+ CreateMockRead(*small_data_frame, 5, SYNCHRONOUS), |
+ CreateMockRead(*max_bytes_data_frame, 6), |
+ CreateMockRead(*large_data_frame, 7, SYNCHRONOUS), |
+ CreateMockRead(*finish_data_frame, 8, SYNCHRONOUS), |
+ MockRead(ASYNC, 0, 9) // 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(); |
+ |
+ scoped_refptr<SpdyStream> spdy_stream1; |
+ TestCompletionCallback callback1; |
+ GURL url1("http://www.google.com"); |
+ EXPECT_EQ(OK, session->CreateStream(url1, MEDIUM, &spdy_stream1, |
+ BoundNetLog(), callback1.callback())); |
+ EXPECT_EQ(0u, spdy_stream1->stream_id()); |
+ |
+ scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock); |
+ (*headers)[":method"] = "GET"; |
+ (*headers)[":scheme"] = url1.scheme(); |
+ (*headers)[":host"] = url1.host(); |
+ (*headers)[":path"] = url1.path(); |
+ (*headers)[":version"] = "HTTP/1.1"; |
+ |
+ spdy_stream1->set_spdy_headers(headers.Pass()); |
+ 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(10); |
+ |
+ // 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()); |
+} |
+ |
} // namespace net |