| Index: net/tools/quic/quic_simple_server_session_test.cc
|
| diff --git a/net/tools/quic/quic_simple_server_session_test.cc b/net/tools/quic/quic_simple_server_session_test.cc
|
| index efe8552823a2a96b2eaf4602d17ec78e6a89ddf7..6dd65dad5a98ea900afcaa38cfa64566e81d66e7 100644
|
| --- a/net/tools/quic/quic_simple_server_session_test.cc
|
| +++ b/net/tools/quic/quic_simple_server_session_test.cc
|
| @@ -31,6 +31,7 @@
|
| #include "testing/gtest/include/gtest/gtest.h"
|
|
|
| using net::test::CryptoTestUtils;
|
| +using net::test::GenerateBody;
|
| using net::test::MockConnection;
|
| using net::test::MockConnectionHelper;
|
| using net::test::QuicConfigPeer;
|
| @@ -176,54 +177,6 @@ class QuicSimpleServerSessionTest
|
| FLAGS_quic_cede_correctly = false;
|
| }
|
|
|
| - // Given |num_resources|, create this number of fake push resources and push
|
| - // them by sending PUSH_PROMISE for all and sending push responses for as much
|
| - // as possible(limited by kMaxStreamsForTest).
|
| - // If |num_resources| > kMaxStreamsForTest, the left over will be queued.
|
| - void PromisePushResources(size_t num_resources) {
|
| - // Assume encryption already established.
|
| - MockQuicCryptoServerStream* crypto_stream =
|
| - new MockQuicCryptoServerStream(&crypto_config_, session_.get());
|
| - crypto_stream->set_encryption_established(true);
|
| - QuicSimpleServerSessionPeer::SetCryptoStream(session_.get(), crypto_stream);
|
| -
|
| - QuicInMemoryCachePeer::ResetForTests();
|
| -
|
| - string request_url = "mail.google.com/";
|
| - SpdyHeaderBlock request_headers;
|
| - string resource_host = "www.google.com";
|
| - string partial_push_resource_path = "/server_push_src";
|
| - string partial_push_response_body =
|
| - "Push resource body " + partial_push_resource_path;
|
| - list<QuicInMemoryCache::ServerPushInfo> push_resources;
|
| - string scheme = "http";
|
| - for (unsigned int i = 1; i <= num_resources; ++i) {
|
| - string path = partial_push_resource_path + base::UintToString(i);
|
| - string url = scheme + "://" + resource_host + path;
|
| - GURL resource_url = GURL(url);
|
| - string body = partial_push_response_body + base::UintToString(i);
|
| - SpdyHeaderBlock response_headers;
|
| - QuicInMemoryCache::GetInstance()->AddSimpleResponse(resource_host, path,
|
| - 200, body);
|
| - push_resources.push_back(QuicInMemoryCache::ServerPushInfo(
|
| - resource_url, response_headers, kDefaultPriority, body));
|
| - // PUSH_PROMISED are sent for all the resources.
|
| - EXPECT_CALL(*headers_stream_,
|
| - WritePushPromise(kClientDataStreamId1, i * 2, _, nullptr));
|
| - if (i <= kMaxStreamsForTest) {
|
| - // |kMaxStreamsForTest| promised responses should be sent.
|
| - EXPECT_CALL(*headers_stream_,
|
| - WriteHeaders(i * 2, _, false, kDefaultPriority, nullptr));
|
| - // Mock that SendStreamData() returns less than supposed to send to keep
|
| - // the stream open.
|
| - EXPECT_CALL(*connection_, SendStreamData(i * 2, _, 0, true, _, nullptr))
|
| - .WillOnce(Return(QuicConsumedData(0, false)));
|
| - }
|
| - }
|
| - session_->PromisePushResources(request_url, push_resources,
|
| - kClientDataStreamId1, request_headers);
|
| - }
|
| -
|
| StrictMock<MockQuicServerSessionVisitor> owner_;
|
| MockConnectionHelper helper_;
|
| StrictMock<MockConnectionWithSendStreamData>* connection_;
|
| @@ -403,7 +356,121 @@ TEST_P(QuicSimpleServerSessionTest, OnStreamFrameWithEvenStreamId) {
|
| session_->OnStreamFrame(frame);
|
| }
|
|
|
| -TEST_P(QuicSimpleServerSessionTest, TestPromisePushResources) {
|
| +TEST_P(QuicSimpleServerSessionTest, GetEvenIncomingError) {
|
| + // Tests that calling GetOrCreateDynamicStream() on an outgoing stream not
|
| + // promised yet should result close connection.
|
| + EXPECT_CALL(*connection_,
|
| + SendConnectionCloseWithDetails(QUIC_INVALID_STREAM_ID,
|
| + "Data for nonexistent stream"));
|
| + EXPECT_EQ(nullptr,
|
| + QuicSessionPeer::GetOrCreateDynamicStream(session_.get(), 4));
|
| +}
|
| +
|
| +// In order to test the case where server push stream creation goes beyond
|
| +// limit, server push streams need to be hanging there instead of
|
| +// immediately closing after sending back response.
|
| +// To achieve this goal, this class resets flow control windows so that large
|
| +// responses will not be sent fully in order to prevent push streams from being
|
| +// closed immediately.
|
| +// Also adjust connection-level flow control window to ensure a large response
|
| +// can cause stream-level flow control blocked but not connection-level.
|
| +class QuicSimpleServerSessionServerPushTest
|
| + : public QuicSimpleServerSessionTest {
|
| + protected:
|
| + const size_t kStreamFlowControlWindowSize = 32 * 1024; // 32KB.
|
| +
|
| + QuicSimpleServerSessionServerPushTest() : QuicSimpleServerSessionTest() {
|
| + // This flag has to be true for negotiation of max number of outgoing
|
| + // streams to work correctly.
|
| + FLAGS_quic_different_max_num_open_streams = true;
|
| +
|
| + config_.SetMaxStreamsPerConnection(kMaxStreamsForTest, kMaxStreamsForTest);
|
| +
|
| + // Reset stream level flow control window to be 32KB.
|
| + QuicConfigPeer::SetReceivedInitialStreamFlowControlWindow(
|
| + &config_, kStreamFlowControlWindowSize);
|
| + // Reset connection level flow control window to be 1.5 MB which is large
|
| + // enough that it won't block any stream to write before stream level flow
|
| + // control blocks it.
|
| + QuicConfigPeer::SetReceivedInitialSessionFlowControlWindow(
|
| + &config_, kInitialSessionFlowControlWindowForTest);
|
| +
|
| + connection_ = new StrictMock<MockConnectionWithSendStreamData>(
|
| + &helper_, Perspective::IS_SERVER, SupportedVersions(GetParam()));
|
| + session_.reset(new QuicSimpleServerSession(config_, connection_, &owner_,
|
| + &crypto_config_));
|
| + session_->Initialize();
|
| + // Needed to make new session flow control window work.
|
| + session_->OnConfigNegotiated();
|
| +
|
| + visitor_ = QuicConnectionPeer::GetVisitor(connection_);
|
| + headers_stream_ = new MockQuicHeadersStream(session_.get());
|
| + QuicSpdySessionPeer::SetHeadersStream(session_.get(), headers_stream_);
|
| +
|
| + // Assume encryption already established.
|
| + MockQuicCryptoServerStream* crypto_stream =
|
| + new MockQuicCryptoServerStream(&crypto_config_, session_.get());
|
| + crypto_stream->set_encryption_established(true);
|
| + QuicSimpleServerSessionPeer::SetCryptoStream(session_.get(), crypto_stream);
|
| + }
|
| +
|
| + // Given |num_resources|, create this number of fake push resources and push
|
| + // them by sending PUSH_PROMISE for all and sending push responses for as much
|
| + // as possible(limited by kMaxStreamsForTest).
|
| + // If |num_resources| > kMaxStreamsForTest, the left over will be queued.
|
| + void PromisePushResources(size_t num_resources) {
|
| + // To prevent push streams from being closed the response need to be larger
|
| + // than stream flow control window so stream won't send the full body.
|
| + size_t body_size = 2 * kStreamFlowControlWindowSize; // 64KB.
|
| +
|
| + config_.SetMaxStreamsPerConnection(kMaxStreamsForTest, kMaxStreamsForTest);
|
| +
|
| + QuicInMemoryCachePeer::ResetForTests();
|
| +
|
| + string request_url = "mail.google.com/";
|
| + SpdyHeaderBlock request_headers;
|
| + string resource_host = "www.google.com";
|
| + string partial_push_resource_path = "/server_push_src";
|
| + list<QuicInMemoryCache::ServerPushInfo> push_resources;
|
| + string scheme = "http";
|
| + for (unsigned int i = 1; i <= num_resources; ++i) {
|
| + QuicStreamId stream_id = i * 2;
|
| + string path = partial_push_resource_path + base::UintToString(i);
|
| + string url = scheme + "://" + resource_host + path;
|
| + GURL resource_url = GURL(url);
|
| + string body;
|
| + GenerateBody(&body, body_size);
|
| + SpdyHeaderBlock response_headers;
|
| + QuicInMemoryCache::GetInstance()->AddSimpleResponse(resource_host, path,
|
| + 200, body);
|
| + push_resources.push_back(QuicInMemoryCache::ServerPushInfo(
|
| + resource_url, response_headers, kDefaultPriority, body));
|
| + // PUSH_PROMISED are sent for all the resources.
|
| + EXPECT_CALL(*headers_stream_, WritePushPromise(kClientDataStreamId1,
|
| + stream_id, _, nullptr));
|
| + if (i <= kMaxStreamsForTest) {
|
| + // |kMaxStreamsForTest| promised responses should be sent.
|
| + EXPECT_CALL(*headers_stream_, WriteHeaders(stream_id, _, false,
|
| + kDefaultPriority, nullptr));
|
| + // Since flow control window is smaller than response body, not the
|
| + // whole body will be sent.
|
| + EXPECT_CALL(*connection_,
|
| + SendStreamData(stream_id, _, 0, false, _, nullptr))
|
| + .WillOnce(
|
| + Return(QuicConsumedData(kStreamFlowControlWindowSize, false)));
|
| + EXPECT_CALL(*connection_, SendBlocked(stream_id));
|
| + }
|
| + }
|
| + session_->PromisePushResources(request_url, push_resources,
|
| + kClientDataStreamId1, request_headers);
|
| + }
|
| +};
|
| +
|
| +INSTANTIATE_TEST_CASE_P(Tests,
|
| + QuicSimpleServerSessionServerPushTest,
|
| + ::testing::ValuesIn(QuicSupportedVersions()));
|
| +
|
| +TEST_P(QuicSimpleServerSessionServerPushTest, TestPromisePushResources) {
|
| // Tests that given more than kMaxOpenStreamForTest resources, all their
|
| // PUSH_PROMISE's will be sent out and only |kMaxOpenStreamForTest| streams
|
| // will be opened and send push response.
|
| @@ -412,7 +479,7 @@ TEST_P(QuicSimpleServerSessionTest, TestPromisePushResources) {
|
| EXPECT_EQ(kMaxStreamsForTest, session_->GetNumOpenOutgoingStreams());
|
| }
|
|
|
| -TEST_P(QuicSimpleServerSessionTest,
|
| +TEST_P(QuicSimpleServerSessionServerPushTest,
|
| HandlePromisedPushRequestsAfterStreamDraining) {
|
| // Tests that after promised stream queued up, when an opened stream is marked
|
| // draining, a queued promised stream will become open and send push response.
|
| @@ -425,25 +492,17 @@ TEST_P(QuicSimpleServerSessionTest,
|
| EXPECT_CALL(*headers_stream_, WriteHeaders(next_out_going_stream_id, _, false,
|
| kDefaultPriority, nullptr));
|
| EXPECT_CALL(*connection_,
|
| - SendStreamData(next_out_going_stream_id, _, 0, true, _, nullptr))
|
| - .WillOnce(Return(QuicConsumedData(0, false)));
|
| + SendStreamData(next_out_going_stream_id, _, 0, false, _, nullptr))
|
| + .WillOnce(Return(QuicConsumedData(kStreamFlowControlWindowSize, false)));
|
| + EXPECT_CALL(*connection_, SendBlocked(next_out_going_stream_id));
|
| session_->StreamDraining(2);
|
| // Number of open outgoing streams should still be the same, because a new
|
| // stream is opened. And the queue should be empty.
|
| EXPECT_EQ(kMaxStreamsForTest, session_->GetNumOpenOutgoingStreams());
|
| }
|
|
|
| -TEST_P(QuicSimpleServerSessionTest, GetEvenIncomingError) {
|
| - // Tests that calling GetOrCreateDynamicStream() on an outgoing stream not
|
| - // promised yet should result close connection.
|
| - EXPECT_CALL(*connection_,
|
| - SendConnectionCloseWithDetails(QUIC_INVALID_STREAM_ID,
|
| - "Data for nonexistent stream"));
|
| - EXPECT_EQ(nullptr,
|
| - QuicSessionPeer::GetOrCreateDynamicStream(session_.get(), 4));
|
| -}
|
| -
|
| -TEST_P(QuicSimpleServerSessionTest, ResetPromisedStreamToCancelServerPush) {
|
| +TEST_P(QuicSimpleServerSessionServerPushTest,
|
| + ResetPromisedStreamToCancelServerPush) {
|
| // Tests that after all resources are promised, a RST frame from client can
|
| // prevent a promised resource to be send out.
|
|
|
| @@ -467,8 +526,9 @@ TEST_P(QuicSimpleServerSessionTest, ResetPromisedStreamToCancelServerPush) {
|
| EXPECT_CALL(*headers_stream_, WriteHeaders(stream_not_reset, _, false,
|
| kDefaultPriority, nullptr));
|
| EXPECT_CALL(*connection_,
|
| - SendStreamData(stream_not_reset, _, 0, true, _, nullptr))
|
| - .WillOnce(Return(QuicConsumedData(0, false)));
|
| + SendStreamData(stream_not_reset, _, 0, false, _, nullptr))
|
| + .WillOnce(Return(QuicConsumedData(kStreamFlowControlWindowSize, false)));
|
| + EXPECT_CALL(*connection_, SendBlocked(stream_not_reset));
|
| EXPECT_CALL(*headers_stream_, WriteHeaders(stream_got_reset, _, false,
|
| kDefaultPriority, nullptr))
|
| .Times(0);
|
| @@ -477,7 +537,8 @@ TEST_P(QuicSimpleServerSessionTest, ResetPromisedStreamToCancelServerPush) {
|
| session_->StreamDraining(4);
|
| }
|
|
|
| -TEST_P(QuicSimpleServerSessionTest, CloseStreamToHandleMorePromisedStream) {
|
| +TEST_P(QuicSimpleServerSessionServerPushTest,
|
| + CloseStreamToHandleMorePromisedStream) {
|
| // Tests that closing a open outgoing stream can trigger a promised resource
|
| // in the queue to be send out.
|
| size_t num_resources = kMaxStreamsForTest + 1;
|
| @@ -492,9 +553,10 @@ TEST_P(QuicSimpleServerSessionTest, CloseStreamToHandleMorePromisedStream) {
|
| EXPECT_CALL(*headers_stream_, WriteHeaders(stream_to_open, _, false,
|
| kDefaultPriority, nullptr));
|
| EXPECT_CALL(*connection_,
|
| - SendStreamData(stream_to_open, _, 0, true, _, nullptr))
|
| - .WillOnce(Return(QuicConsumedData(0, false)));
|
| + SendStreamData(stream_to_open, _, 0, false, _, nullptr))
|
| + .WillOnce(Return(QuicConsumedData(kStreamFlowControlWindowSize, false)));
|
|
|
| + EXPECT_CALL(*connection_, SendBlocked(stream_to_open));
|
| QuicRstStreamFrame rst(stream_got_reset, QUIC_STREAM_CANCELLED, 0);
|
| visitor_->OnRstStream(rst);
|
| }
|
|
|