| 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 f2e451d7d68bc9a5f4de9d20f691efca7dc1075c..47c3d5fdc91282ff2b05c15238938949aa2a1c9a 100644
|
| --- a/net/tools/quic/quic_simple_server_session_test.cc
|
| +++ b/net/tools/quic/quic_simple_server_session_test.cc
|
| @@ -4,7 +4,10 @@
|
|
|
| #include "net/tools/quic/quic_simple_server_session.h"
|
|
|
| +#include <algorithm>
|
| +
|
| #include "base/macros.h"
|
| +#include "base/strings/string_number_conversions.h"
|
| #include "net/quic/crypto/quic_crypto_server_config.h"
|
| #include "net/quic/crypto/quic_random.h"
|
| #include "net/quic/proto/cached_network_parameters.pb.h"
|
| @@ -23,6 +26,7 @@
|
| #include "net/test/gtest_util.h"
|
| #include "net/tools/quic/quic_simple_server_stream.h"
|
| #include "net/tools/quic/test_tools/mock_quic_server_session_visitor.h"
|
| +#include "net/tools/quic/test_tools/quic_in_memory_cache_peer.h"
|
| #include "testing/gmock/include/gmock/gmock.h"
|
| #include "testing/gtest/include/gtest/gtest.h"
|
|
|
| @@ -47,10 +51,69 @@ using net::test::kInitialStreamFlowControlWindowForTest;
|
| using std::string;
|
| using testing::StrictMock;
|
| using testing::_;
|
| +using testing::InSequence;
|
| +using testing::Return;
|
|
|
| namespace net {
|
| namespace tools {
|
| namespace test {
|
| +namespace {
|
| +typedef QuicSimpleServerSession::PromisedStreamInfo PromisedStreamInfo;
|
| +} // namespace
|
| +
|
| +class MockQuicHeadersStream : public QuicHeadersStream {
|
| + public:
|
| + explicit MockQuicHeadersStream(QuicSpdySession* session)
|
| + : QuicHeadersStream(session) {}
|
| +
|
| + MOCK_METHOD4(WritePushPromise,
|
| + size_t(QuicStreamId original_stream_id,
|
| + QuicStreamId promised_stream_id,
|
| + const SpdyHeaderBlock& headers,
|
| + QuicAckListenerInterface* ack_listener));
|
| +
|
| + MOCK_METHOD5(WriteHeaders,
|
| + size_t(QuicStreamId stream_id,
|
| + const SpdyHeaderBlock& headers,
|
| + bool fin,
|
| + SpdyPriority priority,
|
| + QuicAckListenerInterface* ack_listener));
|
| +};
|
| +
|
| +class MockQuicCryptoServerStream : public QuicCryptoServerStream {
|
| + public:
|
| + explicit MockQuicCryptoServerStream(
|
| + const QuicCryptoServerConfig* crypto_config,
|
| + QuicSession* session)
|
| + : QuicCryptoServerStream(crypto_config, session) {}
|
| + ~MockQuicCryptoServerStream() override {}
|
| +
|
| + MOCK_METHOD1(SendServerConfigUpdate,
|
| + void(const CachedNetworkParameters* cached_network_parameters));
|
| +
|
| + void set_encryption_established(bool has_established) {
|
| + encryption_established_ = has_established;
|
| + }
|
| +
|
| + private:
|
| + DISALLOW_COPY_AND_ASSIGN(MockQuicCryptoServerStream);
|
| +};
|
| +
|
| +class MockConnectionWithSendStreamData : public MockConnection {
|
| + public:
|
| + MockConnectionWithSendStreamData(MockConnectionHelper* helper,
|
| + Perspective perspective,
|
| + const QuicVersionVector& supported_versions)
|
| + : MockConnection(helper, perspective, supported_versions) {}
|
| +
|
| + MOCK_METHOD6(SendStreamData,
|
| + QuicConsumedData(QuicStreamId id,
|
| + QuicIOVector iov,
|
| + QuicStreamOffset offset,
|
| + bool fin,
|
| + FecProtection fec_protection,
|
| + QuicAckListenerInterface* listern));
|
| +};
|
|
|
| class QuicSimpleServerSessionPeer {
|
| public:
|
| @@ -70,6 +133,15 @@ class QuicSimpleServerSessionPeer {
|
| SpdyPriority priority) {
|
| return s->CreateOutgoingDynamicStream(priority);
|
| }
|
| +
|
| + static std::deque<PromisedStreamInfo>* promised_streams(
|
| + QuicSimpleServerSession* s) {
|
| + return &(s->promised_streams_);
|
| + }
|
| +
|
| + static QuicStreamId hightest_promised_stream_id(QuicSimpleServerSession* s) {
|
| + return s->highest_promised_stream_id_;
|
| + }
|
| };
|
|
|
| namespace {
|
| @@ -89,7 +161,7 @@ class QuicSimpleServerSessionTest
|
| config_.SetInitialSessionFlowControlWindowToSend(
|
| kInitialSessionFlowControlWindowForTest);
|
|
|
| - connection_ = new StrictMock<MockConnection>(
|
| + connection_ = new StrictMock<MockConnectionWithSendStreamData>(
|
| &helper_, Perspective::IS_SERVER, SupportedVersions(GetParam()));
|
| session_.reset(new QuicSimpleServerSession(config_, connection_, &owner_,
|
| &crypto_config_));
|
| @@ -99,16 +171,67 @@ class QuicSimpleServerSessionTest
|
| QuicCryptoServerConfig::ConfigOptions()));
|
| session_->Initialize();
|
| visitor_ = QuicConnectionPeer::GetVisitor(connection_);
|
| + headers_stream_ = new MockQuicHeadersStream(session_.get());
|
| + QuicSpdySessionPeer::SetHeadersStream(session_.get(), headers_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) {
|
| + // 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<MockConnection>* connection_;
|
| + StrictMock<MockConnectionWithSendStreamData>* connection_;
|
| QuicConfig config_;
|
| QuicCryptoServerConfig crypto_config_;
|
| scoped_ptr<QuicSimpleServerSession> session_;
|
| scoped_ptr<CryptoHandshakeMessage> handshake_message_;
|
| QuicConnectionVisitorInterface* visitor_;
|
| + MockQuicHeadersStream* headers_stream_;
|
| };
|
|
|
| INSTANTIATE_TEST_CASE_P(Tests,
|
| @@ -122,7 +245,7 @@ TEST_P(QuicSimpleServerSessionTest, CloseStreamDueToReset) {
|
| session_->OnStreamFrame(data1);
|
| EXPECT_EQ(1u, session_->GetNumOpenIncomingStreams());
|
|
|
| - // Send a reset (and expect the peer to send a RST in response).
|
| + // Receive a reset (and send a RST in response).
|
| QuicRstStreamFrame rst1(kClientDataStreamId1, QUIC_ERROR_PROCESSING_STREAM,
|
| 0);
|
| EXPECT_CALL(*connection_,
|
| @@ -212,25 +335,6 @@ TEST_P(QuicSimpleServerSessionTest, CreateIncomingDynamicStream) {
|
| EXPECT_EQ(kClientDataStreamId1, stream->id());
|
| }
|
|
|
| -class MockQuicCryptoServerStream : public QuicCryptoServerStream {
|
| - public:
|
| - explicit MockQuicCryptoServerStream(
|
| - const QuicCryptoServerConfig* crypto_config,
|
| - QuicSession* session)
|
| - : QuicCryptoServerStream(crypto_config, session) {}
|
| - ~MockQuicCryptoServerStream() override {}
|
| -
|
| - MOCK_METHOD1(SendServerConfigUpdate,
|
| - void(const CachedNetworkParameters* cached_network_parameters));
|
| -
|
| - void set_encryption_established(bool has_established) {
|
| - encryption_established_ = has_established;
|
| - }
|
| -
|
| - private:
|
| - DISALLOW_COPY_AND_ASSIGN(MockQuicCryptoServerStream);
|
| -};
|
| -
|
| TEST_P(QuicSimpleServerSessionTest, CreateOutgoingDynamicStreamDisconnected) {
|
| // Tests that outgoing stream creation fails when connection is not connected.
|
| size_t initial_num_open_stream = session_->GetNumOpenOutgoingStreams();
|
| @@ -290,6 +394,110 @@ TEST_P(QuicSimpleServerSessionTest, CreateOutgoingDynamicStreamUptoLimit) {
|
| EXPECT_EQ(2u, session_->GetNumOpenIncomingStreams());
|
| }
|
|
|
| +TEST_P(QuicSimpleServerSessionTest, OnStreamFrameWithEvenStreamId) {
|
| + QuicStreamFrame frame(2, false, 0, StringPiece());
|
| + EXPECT_CALL(*connection_, SendConnectionCloseWithDetails(
|
| + QUIC_INVALID_STREAM_ID,
|
| + "Client sent data on server push stream"));
|
| + session_->OnStreamFrame(frame);
|
| +}
|
| +
|
| +TEST_P(QuicSimpleServerSessionTest, 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.
|
| + size_t num_resources = kMaxStreamsForTest + 5;
|
| + PromisePushResources(num_resources);
|
| + EXPECT_EQ(kMaxStreamsForTest, session_->GetNumOpenOutgoingStreams());
|
| +}
|
| +
|
| +TEST_P(QuicSimpleServerSessionTest,
|
| + 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.
|
| + size_t num_resources = kMaxStreamsForTest + 1;
|
| + PromisePushResources(num_resources);
|
| + QuicStreamId next_out_going_stream_id = num_resources * 2;
|
| +
|
| + // After an open stream is marked draining, a new stream is expected to be
|
| + // created and a response sent on the stream.
|
| + 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)));
|
| + 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) {
|
| + // Tests that after all resources are promised, a RST frame from client can
|
| + // prevent a promised resource to be send out.
|
| +
|
| + // Having two extra resources to be send later. One of them will be reset, so
|
| + // when opened stream become close, only one will become open.
|
| + size_t num_resources = kMaxStreamsForTest + 2;
|
| + PromisePushResources(num_resources);
|
| +
|
| + // Reset the last stream in the queue. It should be marked cancelled.
|
| + QuicStreamId stream_got_reset = num_resources * 2;
|
| + QuicRstStreamFrame rst(stream_got_reset, QUIC_STREAM_CANCELLED, 0);
|
| + EXPECT_CALL(*connection_,
|
| + SendRstStream(stream_got_reset, QUIC_RST_ACKNOWLEDGEMENT, 0));
|
| + visitor_->OnRstStream(rst);
|
| +
|
| + // When the first 2 streams becomes draining, the two queued up stream could
|
| + // be created. But since one of them was marked cancelled due to RST frame,
|
| + // only one queued resource will be sent out.
|
| + QuicStreamId stream_not_reset = (kMaxStreamsForTest + 1) * 2;
|
| + InSequence s;
|
| + 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)));
|
| + EXPECT_CALL(*headers_stream_, WriteHeaders(stream_got_reset, _, false,
|
| + kDefaultPriority, nullptr))
|
| + .Times(0);
|
| +
|
| + session_->StreamDraining(2);
|
| + session_->StreamDraining(4);
|
| +}
|
| +
|
| +TEST_P(QuicSimpleServerSessionTest, 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;
|
| + PromisePushResources(num_resources);
|
| + QuicStreamId stream_to_open = num_resources * 2;
|
| +
|
| + // Resetting 1st open stream will close the stream and give space for extra
|
| + // stream to be opened.
|
| + QuicStreamId stream_got_reset = 2;
|
| + EXPECT_CALL(*connection_,
|
| + SendRstStream(stream_got_reset, QUIC_RST_ACKNOWLEDGEMENT, _));
|
| + 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)));
|
| +
|
| + QuicRstStreamFrame rst(stream_got_reset, QUIC_STREAM_CANCELLED, 0);
|
| + visitor_->OnRstStream(rst);
|
| +}
|
| +
|
| } // namespace
|
| } // namespace test
|
| } // namespace tools
|
|
|