| Index: net/quic/quic_http_stream_test.cc
|
| diff --git a/net/quic/quic_http_stream_test.cc b/net/quic/quic_http_stream_test.cc
|
| index 01796c4d8f0bf2e7117905bf6381f3367d2a1620..329d583c731da6cc23f5af98175768b9ece25288 100644
|
| --- a/net/quic/quic_http_stream_test.cc
|
| +++ b/net/quic/quic_http_stream_test.cc
|
| @@ -139,10 +139,12 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<QuicVersion> {
|
| use_closing_stream_(false),
|
| crypto_config_(CryptoTestUtils::ProofVerifierForTesting()),
|
| read_buffer_(new IOBufferWithSize(4096)),
|
| - connection_id_(2),
|
| + promise_id_(kServerDataStreamId1),
|
| stream_id_(kClientDataStreamId1),
|
| + connection_id_(2),
|
| maker_(GetParam(), connection_id_, &clock_, kDefaultServerHostName),
|
| - random_generator_(0) {
|
| + random_generator_(0),
|
| + response_offset_(0) {
|
| IPAddress ip(192, 0, 2, 33);
|
| peer_addr_ = IPEndPoint(ip, 443);
|
| self_addr_ = IPEndPoint(ip, 8435);
|
| @@ -247,6 +249,25 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<QuicVersion> {
|
| stream_.reset(use_closing_stream_
|
| ? new AutoClosingStream(session_->GetWeakPtr())
|
| : new QuicHttpStream(session_->GetWeakPtr()));
|
| +
|
| + promised_stream_.reset(use_closing_stream_
|
| + ? new AutoClosingStream(session_->GetWeakPtr())
|
| + : new QuicHttpStream(session_->GetWeakPtr()));
|
| +
|
| + push_promise_[":path"] = "/bar";
|
| + push_promise_[":authority"] = "www.example.org";
|
| + push_promise_[":version"] = "HTTP/1.1";
|
| + push_promise_[":method"] = "GET";
|
| + push_promise_[":scheme"] = "https";
|
| +
|
| + promised_response_[":status"] = "200 OK";
|
| + promised_response_[":version"] = "HTTP/1.1";
|
| + promised_response_["content-type"] = "text/plain";
|
| +
|
| + promise_url_ = SpdyUtils::GetUrlFromHeaderBlock(push_promise_);
|
| +
|
| + serialized_push_promise_ =
|
| + SpdyUtils::SerializeUncompressedHeaders(push_promise_);
|
| }
|
|
|
| void SetRequest(const std::string& method,
|
| @@ -260,37 +281,69 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<QuicVersion> {
|
| response_data_ = body;
|
| }
|
|
|
| - scoped_ptr<QuicEncryptedPacket> ConstructDataPacket(
|
| + scoped_ptr<QuicEncryptedPacket> InnerConstructDataPacket(
|
| QuicPacketNumber packet_number,
|
| + QuicStreamId stream_id,
|
| bool should_include_version,
|
| bool fin,
|
| QuicStreamOffset offset,
|
| base::StringPiece data) {
|
| - return maker_.MakeDataPacket(packet_number, stream_id_,
|
| + return maker_.MakeDataPacket(packet_number, stream_id,
|
| should_include_version, fin, offset, data);
|
| }
|
|
|
| - scoped_ptr<QuicEncryptedPacket> ConstructRequestHeadersPacket(
|
| + scoped_ptr<QuicEncryptedPacket> ConstructDataPacket(
|
| QuicPacketNumber packet_number,
|
| + bool should_include_version,
|
| + bool fin,
|
| + QuicStreamOffset offset,
|
| + base::StringPiece data) {
|
| + return InnerConstructDataPacket(packet_number, stream_id_,
|
| + should_include_version, fin, offset, data);
|
| + }
|
| +
|
| + scoped_ptr<QuicEncryptedPacket> InnerConstructRequestHeadersPacket(
|
| + QuicPacketNumber packet_number,
|
| + QuicStreamId stream_id,
|
| + bool should_include_version,
|
| bool fin,
|
| RequestPriority request_priority,
|
| size_t* spdy_headers_frame_length) {
|
| SpdyPriority priority =
|
| ConvertRequestPriorityToQuicPriority(request_priority);
|
| return maker_.MakeRequestHeadersPacket(
|
| - packet_number, stream_id_, kIncludeVersion, fin, priority,
|
| + packet_number, stream_id, should_include_version, fin, priority,
|
| request_headers_, spdy_headers_frame_length);
|
| }
|
|
|
| - scoped_ptr<QuicEncryptedPacket> ConstructResponseHeadersPacket(
|
| + scoped_ptr<QuicEncryptedPacket> ConstructRequestHeadersPacket(
|
| QuicPacketNumber packet_number,
|
| bool fin,
|
| + RequestPriority request_priority,
|
| size_t* spdy_headers_frame_length) {
|
| - return maker_.MakeResponseHeadersPacket(
|
| - packet_number, stream_id_, !kIncludeVersion, fin, response_headers_,
|
| + return InnerConstructRequestHeadersPacket(
|
| + packet_number, stream_id_, kIncludeVersion, fin, request_priority,
|
| spdy_headers_frame_length);
|
| }
|
|
|
| + scoped_ptr<QuicEncryptedPacket> InnerConstructResponseHeadersPacket(
|
| + QuicPacketNumber packet_number,
|
| + QuicStreamId stream_id,
|
| + bool fin,
|
| + size_t* spdy_headers_frame_length) {
|
| + return maker_.MakeResponseHeadersPacket(
|
| + packet_number, stream_id, !kIncludeVersion, fin, response_headers_,
|
| + spdy_headers_frame_length, &response_offset_);
|
| + }
|
| +
|
| + scoped_ptr<QuicEncryptedPacket> ConstructResponseHeadersPacket(
|
| + QuicPacketNumber packet_number,
|
| + bool fin,
|
| + size_t* spdy_headers_frame_length) {
|
| + return InnerConstructResponseHeadersPacket(packet_number, stream_id_, fin,
|
| + spdy_headers_frame_length);
|
| + }
|
| +
|
| scoped_ptr<QuicEncryptedPacket> ConstructRstStreamPacket(
|
| QuicPacketNumber packet_number) {
|
| return maker_.MakeRstPacket(
|
| @@ -304,6 +357,12 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<QuicVersion> {
|
| QUIC_STREAM_CANCELLED);
|
| }
|
|
|
| + scoped_ptr<QuicEncryptedPacket> ConstructRstStreamVaryMismatchPacket(
|
| + QuicPacketNumber packet_number) {
|
| + return maker_.MakeRstPacket(packet_number, !kIncludeVersion, promise_id_,
|
| + QUIC_PROMISE_VARY_MISMATCH);
|
| + }
|
| +
|
| scoped_ptr<QuicEncryptedPacket> ConstructAckAndRstStreamPacket(
|
| QuicPacketNumber packet_number) {
|
| return maker_.MakeAckAndRstPacket(packet_number, !kIncludeVersion,
|
| @@ -319,6 +378,14 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<QuicVersion> {
|
| !kIncludeCongestionFeedback);
|
| }
|
|
|
| + void ReceivePromise(QuicStreamId id) {
|
| + QuicChromiumClientStream* stream =
|
| + QuicHttpStreamPeer::GetQuicChromiumClientStream(stream_.get());
|
| + stream->OnStreamHeaders(serialized_push_promise_);
|
| +
|
| + stream->OnPromiseHeadersComplete(id, serialized_push_promise_.size());
|
| + }
|
| +
|
| BoundNetLog net_log_;
|
| bool use_closing_stream_;
|
| MockSendAlgorithm* send_algorithm_;
|
| @@ -343,9 +410,17 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<QuicVersion> {
|
| std::string response_data_;
|
| QuicClientPushPromiseIndex push_promise_index_;
|
|
|
| + // For server push testing
|
| + scoped_ptr<QuicHttpStream> promised_stream_;
|
| + SpdyHeaderBlock push_promise_;
|
| + SpdyHeaderBlock promised_response_;
|
| + const QuicStreamId promise_id_;
|
| + string promise_url_;
|
| + string serialized_push_promise_;
|
| + const QuicStreamId stream_id_;
|
| +
|
| private:
|
| const QuicConnectionId connection_id_;
|
| - const QuicStreamId stream_id_;
|
| QuicTestPacketMaker maker_;
|
| IPEndPoint self_addr_;
|
| IPEndPoint peer_addr_;
|
| @@ -354,6 +429,7 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<QuicVersion> {
|
| MockCryptoClientStreamFactory crypto_client_stream_factory_;
|
| scoped_ptr<StaticSocketDataProvider> socket_data_;
|
| std::vector<PacketToWrite> writes_;
|
| + QuicStreamOffset response_offset_;
|
| };
|
|
|
| INSTANTIATE_TEST_CASE_P(Version,
|
| @@ -1029,5 +1105,425 @@ TEST_P(QuicHttpStreamTest, SessionClosedBeforeSendBodyComplete) {
|
| stream_->SendRequest(headers_, &response_, callback_.callback()));
|
| }
|
|
|
| +TEST_P(QuicHttpStreamTest, ServerPushGetRequest) {
|
| + SetRequest("GET", "/", DEFAULT_PRIORITY);
|
| + Initialize();
|
| +
|
| + // Initialize the first stream, for receiving the promise on.
|
| + request_.method = "GET";
|
| + request_.url = GURL("http://www.example.org/");
|
| +
|
| + EXPECT_EQ(OK, stream_->InitializeStream(&request_, DEFAULT_PRIORITY, net_log_,
|
| + callback_.callback()));
|
| +
|
| + // TODO(ckrasic) - could do this via constructing a PUSH_PROMISE
|
| + // packet, but does it matter?
|
| + ReceivePromise(promise_id_);
|
| + EXPECT_NE(session_->GetPromisedByUrl(promise_url_), nullptr);
|
| +
|
| + request_.url = GURL(promise_url_);
|
| +
|
| + // Make the second stream that will exercise the first step of the
|
| + // server push rendezvous mechanism.
|
| + EXPECT_EQ(OK,
|
| + promised_stream_->InitializeStream(&request_, DEFAULT_PRIORITY,
|
| + net_log_, callback_.callback()));
|
| +
|
| + // Receive the promised response headers.
|
| + response_headers_ = promised_response_;
|
| + size_t spdy_response_headers_frame_length;
|
| + ProcessPacket(InnerConstructResponseHeadersPacket(
|
| + 1, promise_id_, false, &spdy_response_headers_frame_length));
|
| +
|
| + // Receive the promised response body.
|
| + const char kResponseBody[] = "Hello world!";
|
| + ProcessPacket(
|
| + InnerConstructDataPacket(2, promise_id_, false, kFin, 0, kResponseBody));
|
| +
|
| + // Now sending a matching request will have successful rendezvous
|
| + // with the promised stream.
|
| + EXPECT_EQ(OK, promised_stream_->SendRequest(headers_, &response_,
|
| + callback_.callback()));
|
| +
|
| + EXPECT_EQ(
|
| + QuicHttpStreamPeer::GetQuicChromiumClientStream(promised_stream_.get())
|
| + ->id(),
|
| + promise_id_);
|
| +
|
| + // The headers will be immediately available.
|
| + EXPECT_EQ(OK, promised_stream_->ReadResponseHeaders(callback_.callback()));
|
| +
|
| + // As will be the body.
|
| + EXPECT_EQ(
|
| + static_cast<int>(strlen(kResponseBody)),
|
| + promised_stream_->ReadResponseBody(
|
| + read_buffer_.get(), read_buffer_->size(), callback_.callback()));
|
| + EXPECT_TRUE(promised_stream_->IsResponseBodyComplete());
|
| + EXPECT_TRUE(AtEof());
|
| +
|
| + EXPECT_EQ(0, stream_->GetTotalSentBytes());
|
| + EXPECT_EQ(0, stream_->GetTotalReceivedBytes());
|
| + EXPECT_EQ(0, promised_stream_->GetTotalSentBytes());
|
| + EXPECT_EQ(static_cast<int64_t>(spdy_response_headers_frame_length +
|
| + strlen(kResponseBody)),
|
| + promised_stream_->GetTotalReceivedBytes());
|
| +}
|
| +
|
| +TEST_P(QuicHttpStreamTest, ServerPushGetRequestSlowResponse) {
|
| + SetRequest("GET", "/", DEFAULT_PRIORITY);
|
| + Initialize();
|
| +
|
| + // Initialize the first stream, for receiving the promise on.
|
| + request_.method = "GET";
|
| + request_.url = GURL("http://www.example.org/");
|
| +
|
| + EXPECT_EQ(OK, stream_->InitializeStream(&request_, DEFAULT_PRIORITY, net_log_,
|
| + callback_.callback()));
|
| +
|
| + // TODO(ckrasic) - could do this via constructing a PUSH_PROMISE
|
| + // packet, but does it matter?
|
| + ReceivePromise(promise_id_);
|
| + EXPECT_NE(session_->GetPromisedByUrl(promise_url_), nullptr);
|
| +
|
| + request_.url = GURL(promise_url_);
|
| +
|
| + // Make the second stream that will exercise the first step of the
|
| + // server push rendezvous mechanism.
|
| + EXPECT_EQ(OK,
|
| + promised_stream_->InitializeStream(&request_, DEFAULT_PRIORITY,
|
| + net_log_, callback_.callback()));
|
| +
|
| + // Now sending a matching request will rendezvous with the promised
|
| + // stream, but pending secondary validation.
|
| + EXPECT_EQ(ERR_IO_PENDING, promised_stream_->SendRequest(
|
| + headers_, &response_, callback_.callback()));
|
| +
|
| + // Receive the promised response headers.
|
| + response_headers_ = promised_response_;
|
| + size_t spdy_response_headers_frame_length;
|
| + ProcessPacket(InnerConstructResponseHeadersPacket(
|
| + 1, promise_id_, false, &spdy_response_headers_frame_length));
|
| +
|
| + // Receive the promised response body.
|
| + const char kResponseBody[] = "Hello world!";
|
| + ProcessPacket(
|
| + InnerConstructDataPacket(2, promise_id_, false, kFin, 0, kResponseBody));
|
| +
|
| + base::MessageLoop::current()->RunUntilIdle();
|
| +
|
| + // Rendezvous should have succeeded now, so the promised stream
|
| + // should point at our push stream, and we should be able read
|
| + // headers and data from it.
|
| + EXPECT_EQ(OK, callback_.WaitForResult());
|
| +
|
| + EXPECT_EQ(
|
| + QuicHttpStreamPeer::GetQuicChromiumClientStream(promised_stream_.get())
|
| + ->id(),
|
| + promise_id_);
|
| +
|
| + EXPECT_EQ(OK, promised_stream_->ReadResponseHeaders(callback_.callback()));
|
| +
|
| + EXPECT_EQ(
|
| + static_cast<int>(strlen(kResponseBody)),
|
| + promised_stream_->ReadResponseBody(
|
| + read_buffer_.get(), read_buffer_->size(), callback_.callback()));
|
| +
|
| + // Callback should return
|
| + EXPECT_TRUE(promised_stream_->IsResponseBodyComplete());
|
| + EXPECT_TRUE(AtEof());
|
| +
|
| + EXPECT_EQ(0, stream_->GetTotalSentBytes());
|
| + EXPECT_EQ(0, stream_->GetTotalReceivedBytes());
|
| + EXPECT_EQ(0, promised_stream_->GetTotalSentBytes());
|
| + EXPECT_EQ(static_cast<int64_t>(spdy_response_headers_frame_length +
|
| + strlen(kResponseBody)),
|
| + promised_stream_->GetTotalReceivedBytes());
|
| +}
|
| +
|
| +TEST_P(QuicHttpStreamTest, ServerPushCrossOriginOK) {
|
| + SetRequest("GET", "/", DEFAULT_PRIORITY);
|
| + Initialize();
|
| +
|
| + // Initialize the first stream, for receiving the promise on.
|
| + request_.method = "GET";
|
| + request_.url = GURL("http://www.example.org/");
|
| +
|
| + EXPECT_EQ(OK, stream_->InitializeStream(&request_, DEFAULT_PRIORITY, net_log_,
|
| + callback_.callback()));
|
| +
|
| + // TODO(ckrasic) - could do this via constructing a PUSH_PROMISE
|
| + // packet, but does it matter?
|
| +
|
| + push_promise_[":authority"] = "mail.example.org";
|
| + promise_url_ = SpdyUtils::GetUrlFromHeaderBlock(push_promise_);
|
| + serialized_push_promise_ =
|
| + SpdyUtils::SerializeUncompressedHeaders(push_promise_);
|
| +
|
| + ReceivePromise(promise_id_);
|
| + EXPECT_NE(session_->GetPromisedByUrl(promise_url_), nullptr);
|
| +
|
| + request_.url = GURL(promise_url_);
|
| +
|
| + // Make the second stream that will exercise the first step of the
|
| + // server push rendezvous mechanism.
|
| + EXPECT_EQ(OK,
|
| + promised_stream_->InitializeStream(&request_, DEFAULT_PRIORITY,
|
| + net_log_, callback_.callback()));
|
| +
|
| + // Receive the promised response headers.
|
| + response_headers_ = promised_response_;
|
| + size_t spdy_response_headers_frame_length;
|
| + ProcessPacket(InnerConstructResponseHeadersPacket(
|
| + 1, promise_id_, false, &spdy_response_headers_frame_length));
|
| +
|
| + // Receive the promised response body.
|
| + const char kResponseBody[] = "Hello world!";
|
| + ProcessPacket(
|
| + InnerConstructDataPacket(2, promise_id_, false, kFin, 0, kResponseBody));
|
| +
|
| + // Now sending a matching request will have successful rendezvous
|
| + // with the promised stream.
|
| + EXPECT_EQ(OK, promised_stream_->SendRequest(headers_, &response_,
|
| + callback_.callback()));
|
| +
|
| + EXPECT_EQ(
|
| + QuicHttpStreamPeer::GetQuicChromiumClientStream(promised_stream_.get())
|
| + ->id(),
|
| + promise_id_);
|
| +
|
| + // The headers will be immediately available.
|
| + EXPECT_EQ(OK, promised_stream_->ReadResponseHeaders(callback_.callback()));
|
| +
|
| + // As will be the body.
|
| + EXPECT_EQ(
|
| + static_cast<int>(strlen(kResponseBody)),
|
| + promised_stream_->ReadResponseBody(
|
| + read_buffer_.get(), read_buffer_->size(), callback_.callback()));
|
| + EXPECT_TRUE(promised_stream_->IsResponseBodyComplete());
|
| + EXPECT_TRUE(AtEof());
|
| +
|
| + EXPECT_EQ(0, stream_->GetTotalSentBytes());
|
| + EXPECT_EQ(0, stream_->GetTotalReceivedBytes());
|
| + EXPECT_EQ(0, promised_stream_->GetTotalSentBytes());
|
| + EXPECT_EQ(static_cast<int64_t>(spdy_response_headers_frame_length +
|
| + strlen(kResponseBody)),
|
| + promised_stream_->GetTotalReceivedBytes());
|
| +}
|
| +
|
| +TEST_P(QuicHttpStreamTest, ServerPushCrossOriginFail) {
|
| + SetRequest("GET", "/", DEFAULT_PRIORITY);
|
| + Initialize();
|
| +
|
| + // Initialize the first stream, for receiving the promise on.
|
| + request_.method = "GET";
|
| + request_.url = GURL("http://www.example.org/");
|
| +
|
| + EXPECT_EQ(OK, stream_->InitializeStream(&request_, DEFAULT_PRIORITY, net_log_,
|
| + callback_.callback()));
|
| +
|
| + // TODO(ckrasic) - could do this via constructing a PUSH_PROMISE
|
| + // packet, but does it matter?
|
| + push_promise_[":authority"] = "www.notexample.org";
|
| + promise_url_ = SpdyUtils::GetUrlFromHeaderBlock(push_promise_);
|
| + serialized_push_promise_ =
|
| + SpdyUtils::SerializeUncompressedHeaders(push_promise_);
|
| +
|
| + ReceivePromise(promise_id_);
|
| + // The promise will have been rejected because the cert doesn't
|
| + // match.
|
| + EXPECT_EQ(session_->GetPromisedByUrl(promise_url_), nullptr);
|
| +}
|
| +
|
| +TEST_P(QuicHttpStreamTest, ServerPushVaryCheckOK) {
|
| + SetRequest("GET", "/", DEFAULT_PRIORITY);
|
| + Initialize();
|
| +
|
| + // Initialize the first stream, for receiving the promise on.
|
| + request_.method = "GET";
|
| + request_.url = GURL("http://www.example.org/");
|
| +
|
| + EXPECT_EQ(OK, stream_->InitializeStream(&request_, DEFAULT_PRIORITY, net_log_,
|
| + callback_.callback()));
|
| +
|
| + push_promise_["accept-encoding"] = "gzip";
|
| + serialized_push_promise_ =
|
| + SpdyUtils::SerializeUncompressedHeaders(push_promise_);
|
| +
|
| + // TODO(ckrasic) - could do this via constructing a PUSH_PROMISE
|
| + // packet, but does it matter?
|
| + ReceivePromise(promise_id_);
|
| + EXPECT_NE(session_->GetPromisedByUrl(promise_url_), nullptr);
|
| +
|
| + request_.url = GURL(promise_url_);
|
| +
|
| + // Make the second stream that will exercise the first step of the
|
| + // server push rendezvous mechanism.
|
| + EXPECT_EQ(OK,
|
| + promised_stream_->InitializeStream(&request_, DEFAULT_PRIORITY,
|
| + net_log_, callback_.callback()));
|
| +
|
| + headers_.SetHeader("accept-encoding", "gzip");
|
| +
|
| + // Now sending a matching request will rendezvous with the promised
|
| + // stream, but pending secondary validation.
|
| + EXPECT_EQ(ERR_IO_PENDING, promised_stream_->SendRequest(
|
| + headers_, &response_, callback_.callback()));
|
| +
|
| + // Receive the promised response headers.
|
| + promised_response_["vary"] = "accept-encoding";
|
| + response_headers_ = promised_response_;
|
| + size_t spdy_response_headers_frame_length;
|
| + ProcessPacket(InnerConstructResponseHeadersPacket(
|
| + 1, promise_id_, false, &spdy_response_headers_frame_length));
|
| +
|
| + // Receive the promised response body.
|
| + const char kResponseBody[] = "Hello world!";
|
| + ProcessPacket(
|
| + InnerConstructDataPacket(2, promise_id_, false, kFin, 0, kResponseBody));
|
| +
|
| + base::MessageLoop::current()->RunUntilIdle();
|
| +
|
| + // Rendezvous should have succeeded now, so the promised stream
|
| + // should point at our push stream, and we should be able read
|
| + // headers and data from it.
|
| + EXPECT_EQ(OK, callback_.WaitForResult());
|
| +
|
| + EXPECT_EQ(
|
| + QuicHttpStreamPeer::GetQuicChromiumClientStream(promised_stream_.get())
|
| + ->id(),
|
| + promise_id_);
|
| +
|
| + EXPECT_EQ(OK, promised_stream_->ReadResponseHeaders(callback_.callback()));
|
| +
|
| + EXPECT_EQ(
|
| + static_cast<int>(strlen(kResponseBody)),
|
| + promised_stream_->ReadResponseBody(
|
| + read_buffer_.get(), read_buffer_->size(), callback_.callback()));
|
| +
|
| + // Callback should return
|
| + EXPECT_TRUE(promised_stream_->IsResponseBodyComplete());
|
| + EXPECT_TRUE(AtEof());
|
| +
|
| + EXPECT_EQ(0, stream_->GetTotalSentBytes());
|
| + EXPECT_EQ(0, stream_->GetTotalReceivedBytes());
|
| + EXPECT_EQ(0, promised_stream_->GetTotalSentBytes());
|
| + EXPECT_EQ(static_cast<int64_t>(spdy_response_headers_frame_length +
|
| + strlen(kResponseBody)),
|
| + promised_stream_->GetTotalReceivedBytes());
|
| +}
|
| +
|
| +TEST_P(QuicHttpStreamTest, ServerPushVaryCheckFail) {
|
| + SetRequest("GET", "/", DEFAULT_PRIORITY);
|
| + request_headers_[":scheme"] = "https";
|
| + request_headers_[":path"] = "/bar";
|
| + request_headers_["accept-encoding"] = "sdch";
|
| +
|
| + size_t spdy_request_header_frame_length;
|
| + AddWrite(ConstructRstStreamVaryMismatchPacket(1));
|
| + AddWrite(InnerConstructRequestHeadersPacket(
|
| + 2, stream_id_ + 2, !kIncludeVersion, kFin, DEFAULT_PRIORITY,
|
| + &spdy_request_header_frame_length));
|
| + AddWrite(ConstructAckPacket(3, 3, 1));
|
| + AddWrite(ConstructRstStreamCancelledPacket(4));
|
| + Initialize();
|
| +
|
| + // Initialize the first stream, for receiving the promise on.
|
| + request_.method = "GET";
|
| + request_.url = GURL("http://www.example.org/");
|
| +
|
| + EXPECT_EQ(OK, stream_->InitializeStream(&request_, DEFAULT_PRIORITY, net_log_,
|
| + callback_.callback()));
|
| +
|
| + push_promise_["accept-encoding"] = "gzip";
|
| + serialized_push_promise_ =
|
| + SpdyUtils::SerializeUncompressedHeaders(push_promise_);
|
| +
|
| + // TODO(ckrasic) - could do this via constructing a PUSH_PROMISE
|
| + // packet, but does it matter?
|
| + ReceivePromise(promise_id_);
|
| + EXPECT_NE(session_->GetPromisedByUrl(promise_url_), nullptr);
|
| +
|
| + request_.url = GURL(promise_url_);
|
| +
|
| + // Make the second stream that will exercise the first step of the
|
| + // server push rendezvous mechanism.
|
| + EXPECT_EQ(OK,
|
| + promised_stream_->InitializeStream(&request_, DEFAULT_PRIORITY,
|
| + net_log_, callback_.callback()));
|
| +
|
| + headers_.SetHeader("accept-encoding", "sdch");
|
| +
|
| + // Now sending a matching request will rendezvous with the promised
|
| + // stream, but pending secondary validation.
|
| + EXPECT_EQ(ERR_IO_PENDING, promised_stream_->SendRequest(
|
| + headers_, &response_, callback_.callback()));
|
| +
|
| + // Receive the promised response headers.
|
| + promised_response_["vary"] = "accept-encoding";
|
| + response_headers_ = promised_response_;
|
| + size_t spdy_response_headers_frame_length;
|
| + ProcessPacket(InnerConstructResponseHeadersPacket(
|
| + 1, promise_id_, false, &spdy_response_headers_frame_length));
|
| +
|
| + base::MessageLoop::current()->RunUntilIdle();
|
| +
|
| + // Rendezvous should have failed due to vary mismatch, so the
|
| + // promised stream should have been aborted, and instead we have a
|
| + // new, regular client initiated stream.
|
| + EXPECT_EQ(OK, callback_.WaitForResult());
|
| +
|
| + // Not a server-initiated stream.
|
| + EXPECT_NE(
|
| + QuicHttpStreamPeer::GetQuicChromiumClientStream(promised_stream_.get())
|
| + ->id(),
|
| + promise_id_);
|
| +
|
| + // Instead, a new client-initiated stream.
|
| + EXPECT_EQ(
|
| + QuicHttpStreamPeer::GetQuicChromiumClientStream(promised_stream_.get())
|
| + ->id(),
|
| + stream_id_ + 2);
|
| +
|
| + // After rendezvous failure, the push stream has been cancelled.
|
| + EXPECT_EQ(session_->GetPromisedByUrl(promise_url_), nullptr);
|
| +
|
| + // The rest of the test verifies that the retried as
|
| + // client-initiated version of |promised_stream_| works as intended.
|
| +
|
| + // Ack the request.
|
| + ProcessPacket(ConstructAckPacket(2, 0, 0));
|
| +
|
| + SetResponse("404 Not Found", std::string());
|
| + size_t spdy_response_header_frame_length;
|
| + ProcessPacket(InnerConstructResponseHeadersPacket(
|
| + 3, stream_id_ + 2, kFin, &spdy_response_header_frame_length));
|
| +
|
| + base::MessageLoop::current()->RunUntilIdle();
|
| +
|
| + EXPECT_EQ(OK, promised_stream_->ReadResponseHeaders(callback_.callback()));
|
| + ASSERT_TRUE(response_.headers.get());
|
| + EXPECT_EQ(404, response_.headers->response_code());
|
| + EXPECT_TRUE(response_.headers->HasHeaderValue("Content-Type", "text/plain"));
|
| + EXPECT_FALSE(response_.response_time.is_null());
|
| + EXPECT_FALSE(response_.request_time.is_null());
|
| +
|
| + // There is no body, so this should return immediately.
|
| + EXPECT_EQ(
|
| + 0, promised_stream_->ReadResponseBody(
|
| + read_buffer_.get(), read_buffer_->size(), callback_.callback()));
|
| + EXPECT_TRUE(promised_stream_->IsResponseBodyComplete());
|
| +
|
| + stream_->Close(true);
|
| +
|
| + EXPECT_TRUE(AtEof());
|
| +
|
| + // QuicHttpStream::GetTotalSent/ReceivedBytes currently only includes the
|
| + // headers and payload.
|
| + EXPECT_EQ(static_cast<int64_t>(spdy_request_header_frame_length),
|
| + promised_stream_->GetTotalSentBytes());
|
| + EXPECT_EQ(static_cast<int64_t>(spdy_response_header_frame_length),
|
| + promised_stream_->GetTotalReceivedBytes());
|
| +}
|
| +
|
| } // namespace test
|
| } // namespace net
|
|
|