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 |