// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/quic/quic_http_stream.h"

#include <stdint.h>

#include <memory>
#include <utility>

#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "net/base/chunked_upload_data_stream.h"
#include "net/base/elements_upload_data_stream.h"
#include "net/base/load_timing_info.h"
#include "net/base/load_timing_info_test_util.h"
#include "net/base/net_errors.h"
#include "net/base/test_completion_callback.h"
#include "net/base/upload_bytes_element_reader.h"
#include "net/http/http_response_headers.h"
#include "net/http/transport_security_state.h"
#include "net/log/net_log_event_type.h"
#include "net/log/test_net_log.h"
#include "net/log/test_net_log_util.h"
#include "net/quic/crypto/proof_verifier_chromium.h"
#include "net/quic/mock_crypto_client_stream_factory.h"
#include "net/quic/quic_chromium_alarm_factory.h"
#include "net/quic/quic_chromium_connection_helper.h"
#include "net/quic/quic_chromium_packet_reader.h"
#include "net/quic/quic_chromium_packet_writer.h"
#include "net/quic/quic_http_utils.h"
#include "net/quic/quic_server_info.h"
#include "net/quic/quic_stream_factory.h"
#include "net/quic/quic_test_packet_maker.h"
#include "net/quic/test_task_runner.h"
#include "net/socket/socket_performance_watcher.h"
#include "net/socket/socket_test_util.h"
#include "net/spdy/spdy_http_utils.h"
#include "net/test/cert_test_util.h"
#include "net/test/gtest_util.h"
#include "net/test/test_data_directory.h"
#include "net/test/test_with_scoped_task_environment.h"
#include "net/third_party/quic/core/congestion_control/send_algorithm_interface.h"
#include "net/third_party/quic/core/crypto/crypto_protocol.h"
#include "net/third_party/quic/core/crypto/quic_decrypter.h"
#include "net/third_party/quic/core/crypto/quic_encrypter.h"
#include "net/third_party/quic/core/http/spdy_utils.h"
#include "net/third_party/quic/core/quic_connection.h"
#include "net/third_party/quic/core/quic_write_blocked_list.h"
#include "net/third_party/quic/core/tls_client_handshaker.h"
#include "net/third_party/quic/platform/api/quic_string_piece.h"
#include "net/third_party/quic/test_tools/crypto_test_utils.h"
#include "net/third_party/quic/test_tools/mock_clock.h"
#include "net/third_party/quic/test_tools/mock_random.h"
#include "net/third_party/quic/test_tools/quic_connection_peer.h"
#include "net/third_party/quic/test_tools/quic_spdy_session_peer.h"
#include "net/third_party/quic/test_tools/quic_test_utils.h"
#include "net/third_party/spdy/core/spdy_frame_builder.h"
#include "net/third_party/spdy/core/spdy_framer.h"
#include "net/third_party/spdy/core/spdy_protocol.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

using std::string;
using testing::_;
using testing::AnyNumber;
using testing::Return;

namespace net {
namespace test {
namespace {

const char kUploadData[] = "Really nifty data!";
const char kDefaultServerHostName[] = "www.example.org";
const uint16_t kDefaultServerPort = 443;

class TestQuicConnection : public quic::QuicConnection {
 public:
  TestQuicConnection(const quic::ParsedQuicVersionVector& versions,
                     quic::QuicConnectionId connection_id,
                     IPEndPoint address,
                     QuicChromiumConnectionHelper* helper,
                     QuicChromiumAlarmFactory* alarm_factory,
                     quic::QuicPacketWriter* writer)
      : quic::QuicConnection(
            connection_id,
            quic::QuicSocketAddress(quic::QuicSocketAddressImpl(address)),
            helper,
            alarm_factory,
            writer,
            true /* owns_writer */,
            quic::Perspective::IS_CLIENT,
            versions) {}

  void SetSendAlgorithm(quic::SendAlgorithmInterface* send_algorithm) {
    quic::test::QuicConnectionPeer::SetSendAlgorithm(this, send_algorithm);
  }
};

// UploadDataStream that always returns errors on data read.
class ReadErrorUploadDataStream : public UploadDataStream {
 public:
  enum class FailureMode { SYNC, ASYNC };

  explicit ReadErrorUploadDataStream(FailureMode mode)
      : UploadDataStream(true, 0), async_(mode), weak_factory_(this) {}
  ~ReadErrorUploadDataStream() override {}

 private:
  void CompleteRead() { UploadDataStream::OnReadCompleted(ERR_FAILED); }

  // UploadDataStream implementation:
  int InitInternal(const NetLogWithSource& net_log) override { return OK; }

  int ReadInternal(IOBuffer* buf, int buf_len) override {
    if (async_ == FailureMode::ASYNC) {
      base::ThreadTaskRunnerHandle::Get()->PostTask(
          FROM_HERE, base::Bind(&ReadErrorUploadDataStream::CompleteRead,
                                weak_factory_.GetWeakPtr()));
      return ERR_IO_PENDING;
    }
    return ERR_FAILED;
  }

  void ResetInternal() override {}

  const FailureMode async_;

  base::WeakPtrFactory<ReadErrorUploadDataStream> weak_factory_;

  DISALLOW_COPY_AND_ASSIGN(ReadErrorUploadDataStream);
};

// A helper class that will delete |stream| when the callback is invoked.
class DeleteStreamCallback : public TestCompletionCallbackBase {
 public:
  explicit DeleteStreamCallback(std::unique_ptr<QuicHttpStream> stream)
      : stream_(std::move(stream)) {}

  CompletionOnceCallback callback() {
    return base::BindOnce(&DeleteStreamCallback::DeleteStream,
                          base::Unretained(this));
  }

 private:
  void DeleteStream(int result) {
    stream_.reset();
    SetResult(result);
  }

  std::unique_ptr<QuicHttpStream> stream_;
};

}  // namespace

class QuicHttpStreamPeer {
 public:
  static QuicChromiumClientStream::Handle* GetQuicChromiumClientStream(
      QuicHttpStream* stream) {
    return stream->stream_.get();
  }
};

class QuicHttpStreamTest : public ::testing::TestWithParam<
                               std::tuple<quic::QuicTransportVersion, bool>>,
                           public WithScopedTaskEnvironment {
 public:
  void CloseStream(QuicHttpStream* stream, int /*rv*/) { stream->Close(false); }

 protected:
  static const bool kFin = true;
  static const bool kIncludeVersion = true;
  static const bool kIncludeCongestionFeedback = true;

  // Holds a packet to be written to the wire, and the IO mode that should
  // be used by the mock socket when performing the write.
  struct PacketToWrite {
    PacketToWrite(IoMode mode, quic::QuicReceivedPacket* packet)
        : mode(mode), packet(packet) {}
    PacketToWrite(IoMode mode, int rv) : mode(mode), packet(nullptr), rv(rv) {}
    IoMode mode;
    quic::QuicReceivedPacket* packet;
    int rv;
  };

  QuicHttpStreamTest()
      : version_(std::get<0>(GetParam())),
        client_headers_include_h2_stream_dependency_(std::get<1>(GetParam())),
        crypto_config_(quic::test::crypto_test_utils::ProofVerifierForTesting(),
                       quic::TlsClientHandshaker::CreateSslCtx()),
        read_buffer_(base::MakeRefCounted<IOBufferWithSize>(4096)),
        promise_id_(GetNthServerInitiatedStreamId(0)),
        stream_id_(GetNthClientInitiatedStreamId(0)),
        connection_id_(2),
        client_maker_(version_,
                      connection_id_,
                      &clock_,
                      kDefaultServerHostName,
                      quic::Perspective::IS_CLIENT,
                      client_headers_include_h2_stream_dependency_),
        server_maker_(version_,
                      connection_id_,
                      &clock_,
                      kDefaultServerHostName,
                      quic::Perspective::IS_SERVER,
                      false),
        random_generator_(0),
        response_offset_(0) {
    IPAddress ip(192, 0, 2, 33);
    peer_addr_ = IPEndPoint(ip, 443);
    self_addr_ = IPEndPoint(ip, 8435);
    clock_.AdvanceTime(quic::QuicTime::Delta::FromMilliseconds(20));
    request_.traffic_annotation =
        MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
  }

  ~QuicHttpStreamTest() {
    session_->CloseSessionOnError(ERR_ABORTED, quic::QUIC_INTERNAL_ERROR);
    for (size_t i = 0; i < writes_.size(); i++) {
      delete writes_[i].packet;
    }
  }

  // Adds a packet to the list of expected writes.
  void AddWrite(std::unique_ptr<quic::QuicReceivedPacket> packet) {
    writes_.push_back(PacketToWrite(SYNCHRONOUS, packet.release()));
  }

  void AddWrite(IoMode mode, int rv) {
    writes_.push_back(PacketToWrite(mode, rv));
  }

  // Returns the packet to be written at position |pos|.
  quic::QuicReceivedPacket* GetWrite(size_t pos) { return writes_[pos].packet; }

  bool AtEof() {
    return socket_data_->AllReadDataConsumed() &&
           socket_data_->AllWriteDataConsumed();
  }

  void ProcessPacket(std::unique_ptr<quic::QuicReceivedPacket> packet) {
    connection_->ProcessUdpPacket(
        quic::QuicSocketAddress(quic::QuicSocketAddressImpl(self_addr_)),
        quic::QuicSocketAddress(quic::QuicSocketAddressImpl(peer_addr_)),
        *packet);
  }

  // Configures the test fixture to use the list of expected writes.
  void Initialize() {
    mock_writes_.reset(new MockWrite[writes_.size()]);
    for (size_t i = 0; i < writes_.size(); i++) {
      if (writes_[i].packet == nullptr) {
        mock_writes_[i] = MockWrite(writes_[i].mode, writes_[i].rv, i);
      } else {
        mock_writes_[i] = MockWrite(writes_[i].mode, writes_[i].packet->data(),
                                    writes_[i].packet->length());
      }
    }

    socket_data_.reset(new StaticSocketDataProvider(
        base::span<MockRead>(),
        base::make_span(mock_writes_.get(), writes_.size())));

    std::unique_ptr<MockUDPClientSocket> socket(new MockUDPClientSocket(
        socket_data_.get(), net_log_.bound().net_log()));
    socket->Connect(peer_addr_);
    runner_ = new TestTaskRunner(&clock_);
    send_algorithm_ = new quic::test::MockSendAlgorithm();
    EXPECT_CALL(*send_algorithm_, InRecovery()).WillRepeatedly(Return(false));
    EXPECT_CALL(*send_algorithm_, InSlowStart()).WillRepeatedly(Return(false));
    EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
        .Times(testing::AtLeast(1));
    EXPECT_CALL(*send_algorithm_, GetCongestionWindow())
        .WillRepeatedly(Return(quic::kMaxPacketSize));
    EXPECT_CALL(*send_algorithm_, PacingRate(_))
        .WillRepeatedly(Return(quic::QuicBandwidth::Zero()));
    EXPECT_CALL(*send_algorithm_, CanSend(_)).WillRepeatedly(Return(true));
    EXPECT_CALL(*send_algorithm_, BandwidthEstimate())
        .WillRepeatedly(Return(quic::QuicBandwidth::Zero()));
    EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)).Times(AnyNumber());
    EXPECT_CALL(*send_algorithm_, OnApplicationLimited(_)).Times(AnyNumber());
    EXPECT_CALL(*send_algorithm_, GetCongestionControlType())
        .Times(AnyNumber());
    helper_.reset(
        new QuicChromiumConnectionHelper(&clock_, &random_generator_));
    alarm_factory_.reset(new QuicChromiumAlarmFactory(runner_.get(), &clock_));

    connection_ = new TestQuicConnection(
        quic::test::SupportedVersions(
            quic::ParsedQuicVersion(quic::PROTOCOL_QUIC_CRYPTO, version_)),
        connection_id_, peer_addr_, helper_.get(), alarm_factory_.get(),
        new QuicChromiumPacketWriter(
            socket.get(), base::ThreadTaskRunnerHandle::Get().get()));
    connection_->set_visitor(&visitor_);
    connection_->SetSendAlgorithm(send_algorithm_);

    // Load a certificate that is valid for *.example.org
    scoped_refptr<X509Certificate> test_cert(
        ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"));
    EXPECT_TRUE(test_cert.get());

    verify_details_.cert_verify_result.verified_cert = test_cert;
    verify_details_.cert_verify_result.is_issued_by_known_root = true;
    crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details_);

    base::TimeTicks dns_end = base::TimeTicks::Now();
    base::TimeTicks dns_start = dns_end - base::TimeDelta::FromMilliseconds(1);
    session_.reset(new QuicChromiumClientSession(
        connection_, std::move(socket),
        /*stream_factory=*/nullptr, &crypto_client_stream_factory_, &clock_,
        &transport_security_state_, /*ssl_config_service=*/nullptr,
        base::WrapUnique(static_cast<QuicServerInfo*>(nullptr)),
        QuicSessionKey(kDefaultServerHostName, kDefaultServerPort,
                       PRIVACY_MODE_DISABLED, SocketTag()),
        /*require_confirmation=*/false, /*migrate_session_early_v2=*/false,
        /*migrate_session_on_network_change_v2=*/false,
        /*go_away_on_path_degrading*/ false,
        /*default_network=*/NetworkChangeNotifier::kInvalidNetworkHandle,
        base::TimeDelta::FromSeconds(kMaxTimeOnNonDefaultNetworkSecs),
        kMaxMigrationsToNonDefaultNetworkOnWriteError,
        kMaxMigrationsToNonDefaultNetworkOnPathDegrading,
        kQuicYieldAfterPacketsRead,
        quic::QuicTime::Delta::FromMilliseconds(
            kQuicYieldAfterDurationMilliseconds),
        client_headers_include_h2_stream_dependency_, /*cert_verify_flags=*/0,
        quic::test::DefaultQuicConfig(), &crypto_config_, "CONNECTION_UNKNOWN",
        dns_start, dns_end, &push_promise_index_, nullptr,
        base::ThreadTaskRunnerHandle::Get().get(),
        /*socket_performance_watcher=*/nullptr, net_log_.bound().net_log()));
    session_->Initialize();
    TestCompletionCallback callback;

    session_->CryptoConnect(callback.callback());
    stream_ = std::make_unique<QuicHttpStream>(
        session_->CreateHandle(HostPortPair("www.example.org", 443)));
    promised_stream_ = std::make_unique<QuicHttpStream>(
        session_->CreateHandle(HostPortPair("www.example.org", 443)));
    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_ = quic::SpdyUtils::GetPromisedUrlFromHeaders(push_promise_);
  }

  void SetRequest(const string& method,
                  const string& path,
                  RequestPriority priority) {
    request_headers_ = client_maker_.GetRequestHeaders(method, "https", path);
  }

  void SetResponse(const string& status, const string& body) {
    response_headers_ = server_maker_.GetResponseHeaders(status);
    response_data_ = body;
  }

  std::unique_ptr<quic::QuicReceivedPacket> InnerConstructDataPacket(
      quic::QuicPacketNumber packet_number,
      quic::QuicStreamId stream_id,
      bool should_include_version,
      bool fin,
      quic::QuicStreamOffset offset,
      quic::QuicStringPiece data,
      QuicTestPacketMaker* maker) {
    return maker->MakeDataPacket(packet_number, stream_id,
                                 should_include_version, fin, offset, data);
  }

  std::unique_ptr<quic::QuicReceivedPacket> ConstructClientDataPacket(
      quic::QuicPacketNumber packet_number,
      bool should_include_version,
      bool fin,
      quic::QuicStreamOffset offset,
      quic::QuicStringPiece data) {
    return InnerConstructDataPacket(packet_number, stream_id_,
                                    should_include_version, fin, offset, data,
                                    &client_maker_);
  }

  std::unique_ptr<quic::QuicReceivedPacket> ConstructServerDataPacket(
      quic::QuicPacketNumber packet_number,
      bool should_include_version,
      bool fin,
      quic::QuicStreamOffset offset,
      quic::QuicStringPiece data) {
    return InnerConstructDataPacket(packet_number, stream_id_,
                                    should_include_version, fin, offset, data,
                                    &server_maker_);
  }

  std::unique_ptr<quic::QuicReceivedPacket> InnerConstructRequestHeadersPacket(
      quic::QuicPacketNumber packet_number,
      quic::QuicStreamId stream_id,
      bool should_include_version,
      bool fin,
      RequestPriority request_priority,
      size_t* spdy_headers_frame_length,
      quic::QuicStreamOffset* offset) {
    return InnerConstructRequestHeadersPacket(
        packet_number, stream_id, should_include_version, fin, request_priority,
        0, spdy_headers_frame_length, offset);
  }

  std::unique_ptr<quic::QuicReceivedPacket> InnerConstructRequestHeadersPacket(
      quic::QuicPacketNumber packet_number,
      quic::QuicStreamId stream_id,
      bool should_include_version,
      bool fin,
      RequestPriority request_priority,
      quic::QuicStreamId parent_stream_id,
      size_t* spdy_headers_frame_length,
      quic::QuicStreamOffset* offset) {
    spdy::SpdyPriority priority =
        ConvertRequestPriorityToQuicPriority(request_priority);
    return client_maker_.MakeRequestHeadersPacket(
        packet_number, stream_id, should_include_version, fin, priority,
        std::move(request_headers_), parent_stream_id,
        spdy_headers_frame_length, offset);
  }

  std::unique_ptr<quic::QuicReceivedPacket>
  ConstructRequestHeadersAndDataFramesPacket(
      quic::QuicPacketNumber packet_number,
      quic::QuicStreamId stream_id,
      bool should_include_version,
      bool fin,
      RequestPriority request_priority,
      quic::QuicStreamId parent_stream_id,
      quic::QuicStreamOffset* offset,
      size_t* spdy_headers_frame_length,
      const std::vector<std::string>& data_writes) {
    spdy::SpdyPriority priority =
        ConvertRequestPriorityToQuicPriority(request_priority);
    return client_maker_.MakeRequestHeadersAndMultipleDataFramesPacket(
        packet_number, stream_id, should_include_version, fin, priority,
        std::move(request_headers_), parent_stream_id, offset,
        spdy_headers_frame_length, data_writes);
  }

  std::unique_ptr<quic::QuicReceivedPacket> ConstructRequestAndRstPacket(
      quic::QuicPacketNumber packet_number,
      quic::QuicStreamId stream_id,
      bool should_include_version,
      bool fin,
      RequestPriority request_priority,
      quic::QuicStreamId parent_stream_id,
      size_t* spdy_headers_frame_length,
      quic::QuicStreamOffset* header_stream_offset,
      quic::QuicRstStreamErrorCode error_code,
      size_t bytes_written) {
    spdy::SpdyPriority priority =
        ConvertRequestPriorityToQuicPriority(request_priority);
    return client_maker_.MakeRequestHeadersAndRstPacket(
        packet_number, stream_id, should_include_version, fin, priority,
        std::move(request_headers_), parent_stream_id,
        spdy_headers_frame_length, header_stream_offset, error_code,
        bytes_written);
  }

  std::unique_ptr<quic::QuicReceivedPacket> ConstructRequestHeadersPacket(
      quic::QuicPacketNumber packet_number,
      bool fin,
      RequestPriority request_priority,
      size_t* spdy_headers_frame_length) {
    return InnerConstructRequestHeadersPacket(
        packet_number, stream_id_, kIncludeVersion, fin, request_priority,
        spdy_headers_frame_length, nullptr);
  }

  std::unique_ptr<quic::QuicReceivedPacket> InnerConstructResponseHeadersPacket(
      quic::QuicPacketNumber packet_number,
      quic::QuicStreamId stream_id,
      bool fin,
      size_t* spdy_headers_frame_length) {
    return server_maker_.MakeResponseHeadersPacket(
        packet_number, stream_id, !kIncludeVersion, fin,
        std::move(response_headers_), spdy_headers_frame_length,
        &response_offset_);
  }

  std::unique_ptr<quic::QuicReceivedPacket> ConstructResponseHeadersPacket(
      quic::QuicPacketNumber packet_number,
      bool fin,
      size_t* spdy_headers_frame_length) {
    return InnerConstructResponseHeadersPacket(packet_number, stream_id_, fin,
                                               spdy_headers_frame_length);
  }

  std::unique_ptr<quic::QuicReceivedPacket>
  ConstructResponseHeadersPacketWithOffset(quic::QuicPacketNumber packet_number,
                                           bool fin,
                                           size_t* spdy_headers_frame_length,
                                           quic::QuicStreamOffset* offset) {
    return server_maker_.MakeResponseHeadersPacket(
        packet_number, stream_id_, !kIncludeVersion, fin,
        std::move(response_headers_), spdy_headers_frame_length, offset);
  }

  std::unique_ptr<quic::QuicReceivedPacket> ConstructResponseTrailersPacket(
      quic::QuicPacketNumber packet_number,
      bool fin,
      spdy::SpdyHeaderBlock trailers,
      size_t* spdy_headers_frame_length,
      quic::QuicStreamOffset* offset) {
    return server_maker_.MakeResponseHeadersPacket(
        packet_number, stream_id_, !kIncludeVersion, fin, std::move(trailers),
        spdy_headers_frame_length, offset);
  }

  std::unique_ptr<quic::QuicReceivedPacket> ConstructClientRstStreamPacket(
      quic::QuicPacketNumber packet_number) {
    return client_maker_.MakeRstPacket(packet_number, true, stream_id_,
                                       quic::QUIC_RST_ACKNOWLEDGEMENT);
  }

  std::unique_ptr<quic::QuicReceivedPacket>
  ConstructClientRstStreamCancelledPacket(
      quic::QuicPacketNumber packet_number) {
    return client_maker_.MakeRstPacket(packet_number, !kIncludeVersion,
                                       stream_id_, quic::QUIC_STREAM_CANCELLED);
  }

  std::unique_ptr<quic::QuicReceivedPacket>
  ConstructClientRstStreamVaryMismatchPacket(
      quic::QuicPacketNumber packet_number) {
    return client_maker_.MakeRstPacket(packet_number, !kIncludeVersion,
                                       promise_id_,
                                       quic::QUIC_PROMISE_VARY_MISMATCH);
  }

  std::unique_ptr<quic::QuicReceivedPacket>
  ConstructClientRstStreamVaryMismatchAndRequestHeadersPacket(
      quic::QuicPacketNumber packet_number,
      quic::QuicStreamId stream_id,
      bool should_include_version,
      bool fin,
      RequestPriority request_priority,
      quic::QuicStreamId parent_stream_id,
      size_t* spdy_headers_frame_length,
      quic::QuicStreamOffset* offset) {
    spdy::SpdyPriority priority =
        ConvertRequestPriorityToQuicPriority(request_priority);
    return client_maker_.MakeRstAndRequestHeadersPacket(
        packet_number, should_include_version, promise_id_,
        quic::QUIC_PROMISE_VARY_MISMATCH, stream_id, fin, priority,
        std::move(request_headers_), parent_stream_id,
        spdy_headers_frame_length, offset);
  }

  std::unique_ptr<quic::QuicReceivedPacket> ConstructAckAndRstStreamPacket(
      quic::QuicPacketNumber packet_number,
      quic::QuicPacketNumber largest_received,
      quic::QuicPacketNumber smallest_received,
      quic::QuicPacketNumber least_unacked) {
    return client_maker_.MakeAckAndRstPacket(
        packet_number, !kIncludeVersion, stream_id_,
        quic::QUIC_STREAM_CANCELLED, largest_received, smallest_received,
        least_unacked, !kIncludeCongestionFeedback);
  }

  std::unique_ptr<quic::QuicReceivedPacket> ConstructClientRstStreamErrorPacket(
      quic::QuicPacketNumber packet_number,
      bool include_version) {
    return client_maker_.MakeRstPacket(packet_number, include_version,
                                       stream_id_,
                                       quic::QUIC_ERROR_PROCESSING_STREAM);
  }

  std::unique_ptr<quic::QuicReceivedPacket> ConstructAckAndRstStreamPacket(
      quic::QuicPacketNumber packet_number) {
    return ConstructAckAndRstStreamPacket(packet_number, 2, 1, 1);
  }

  std::unique_ptr<quic::QuicReceivedPacket> ConstructClientAckPacket(
      quic::QuicPacketNumber packet_number,
      quic::QuicPacketNumber largest_received,
      quic::QuicPacketNumber smallest_received,
      quic::QuicPacketNumber least_unacked) {
    return client_maker_.MakeAckPacket(packet_number, largest_received,
                                       smallest_received, least_unacked,
                                       !kIncludeCongestionFeedback);
  }

  std::unique_ptr<quic::QuicReceivedPacket> ConstructServerAckPacket(
      quic::QuicPacketNumber packet_number,
      quic::QuicPacketNumber largest_received,
      quic::QuicPacketNumber smallest_received,
      quic::QuicPacketNumber least_unacked) {
    return server_maker_.MakeAckPacket(packet_number, largest_received,
                                       smallest_received, least_unacked,
                                       !kIncludeCongestionFeedback);
  }

  std::unique_ptr<quic::QuicReceivedPacket> ConstructClientPriorityPacket(
      quic::QuicPacketNumber packet_number,
      bool should_include_version,
      quic::QuicStreamId id,
      quic::QuicStreamId parent_stream_id,
      RequestPriority request_priority,
      quic::QuicStreamOffset* header_stream_offset) {
    return client_maker_.MakePriorityPacket(
        packet_number, should_include_version, id, parent_stream_id,
        ConvertRequestPriorityToQuicPriority(request_priority),
        header_stream_offset);
  }

  std::unique_ptr<quic::QuicReceivedPacket> ConstructInitialSettingsPacket(
      quic::QuicStreamOffset* offset) {
    return client_maker_.MakeInitialSettingsPacket(1, offset);
  }

  void ReceivePromise(quic::QuicStreamId id) {
    auto headers = quic::test::AsHeaderList(push_promise_);
    QuicChromiumClientStream::Handle* stream =
        QuicHttpStreamPeer::GetQuicChromiumClientStream(stream_.get());
    stream->OnPromiseHeaderList(id, headers.uncompressed_header_bytes(),
                                headers);
  }

  void ExpectLoadTimingValid(const LoadTimingInfo& load_timing_info,
                             bool session_reused) {
    EXPECT_EQ(session_reused, load_timing_info.socket_reused);
    if (session_reused) {
      ExpectConnectTimingHasNoTimes(load_timing_info.connect_timing);
    } else {
      ExpectConnectTimingHasTimes(
          load_timing_info.connect_timing,
          CONNECT_TIMING_HAS_SSL_TIMES | CONNECT_TIMING_HAS_DNS_TIMES);
    }
    ExpectLoadTimingHasOnlyConnectionTimes(load_timing_info);
  }

  quic::QuicStreamId GetNthClientInitiatedStreamId(int n) {
    return quic::test::GetNthClientInitiatedStreamId(version_, n);
  }

  quic::QuicStreamId GetNthServerInitiatedStreamId(int n) {
    return quic::test::GetNthServerInitiatedStreamId(version_, n);
  }

  const quic::QuicTransportVersion version_;
  const bool client_headers_include_h2_stream_dependency_;

  BoundTestNetLog net_log_;
  quic::test::MockSendAlgorithm* send_algorithm_;
  scoped_refptr<TestTaskRunner> runner_;
  std::unique_ptr<MockWrite[]> mock_writes_;
  quic::MockClock clock_;
  TestQuicConnection* connection_;
  std::unique_ptr<QuicChromiumConnectionHelper> helper_;
  std::unique_ptr<QuicChromiumAlarmFactory> alarm_factory_;
  testing::StrictMock<quic::test::MockQuicConnectionVisitor> visitor_;
  std::unique_ptr<UploadDataStream> upload_data_stream_;
  std::unique_ptr<QuicHttpStream> stream_;
  TransportSecurityState transport_security_state_;
  std::unique_ptr<QuicChromiumClientSession> session_;
  quic::QuicCryptoClientConfig crypto_config_;
  TestCompletionCallback callback_;
  HttpRequestInfo request_;
  HttpRequestHeaders headers_;
  HttpResponseInfo response_;
  scoped_refptr<IOBufferWithSize> read_buffer_;
  spdy::SpdyHeaderBlock request_headers_;
  spdy::SpdyHeaderBlock response_headers_;
  string request_data_;
  string response_data_;
  quic::QuicClientPushPromiseIndex push_promise_index_;

  // For server push testing
  std::unique_ptr<QuicHttpStream> promised_stream_;
  spdy::SpdyHeaderBlock push_promise_;
  spdy::SpdyHeaderBlock promised_response_;
  const quic::QuicStreamId promise_id_;
  string promise_url_;
  const quic::QuicStreamId stream_id_;

  const quic::QuicConnectionId connection_id_;
  QuicTestPacketMaker client_maker_;
  QuicTestPacketMaker server_maker_;
  IPEndPoint self_addr_;
  IPEndPoint peer_addr_;
  quic::test::MockRandom random_generator_;
  ProofVerifyDetailsChromium verify_details_;
  MockCryptoClientStreamFactory crypto_client_stream_factory_;
  std::unique_ptr<StaticSocketDataProvider> socket_data_;
  std::vector<PacketToWrite> writes_;
  quic::QuicStreamOffset response_offset_;
};

INSTANTIATE_TEST_CASE_P(
    VersionIncludeStreamDependencySequence,
    QuicHttpStreamTest,
    ::testing::Combine(
        ::testing::ValuesIn(quic::AllSupportedTransportVersions()),
        ::testing::Bool()));

TEST_P(QuicHttpStreamTest, RenewStreamForAuth) {
  Initialize();
  EXPECT_EQ(nullptr, stream_->RenewStreamForAuth());
}

TEST_P(QuicHttpStreamTest, CanReuseConnection) {
  Initialize();
  EXPECT_FALSE(stream_->CanReuseConnection());
}

TEST_P(QuicHttpStreamTest, DisableConnectionMigrationForStream) {
  request_.load_flags |= LOAD_DISABLE_CONNECTION_MIGRATION_TO_CELLULAR;
  Initialize();
  EXPECT_EQ(OK,
            stream_->InitializeStream(&request_, false, DEFAULT_PRIORITY,
                                      net_log_.bound(), callback_.callback()));
  QuicChromiumClientStream::Handle* client_stream =
      QuicHttpStreamPeer::GetQuicChromiumClientStream(stream_.get());
  EXPECT_FALSE(client_stream->can_migrate_to_cellular_network());
}

TEST_P(QuicHttpStreamTest, GetRequest) {
  SetRequest("GET", "/", DEFAULT_PRIORITY);
  size_t spdy_request_header_frame_length;
  quic::QuicStreamOffset header_stream_offset = 0;
  AddWrite(ConstructInitialSettingsPacket(&header_stream_offset));
  AddWrite(InnerConstructRequestHeadersPacket(
      2, GetNthClientInitiatedStreamId(0), kIncludeVersion, kFin,
      DEFAULT_PRIORITY, &spdy_request_header_frame_length,
      &header_stream_offset));

  Initialize();

  request_.method = "GET";
  request_.url = GURL("https://www.example.org/");

  // Make sure getting load timing from the stream early does not crash.
  LoadTimingInfo load_timing_info;
  EXPECT_TRUE(stream_->GetLoadTimingInfo(&load_timing_info));

  EXPECT_EQ(OK,
            stream_->InitializeStream(&request_, true, DEFAULT_PRIORITY,
                                      net_log_.bound(), callback_.callback()));
  EXPECT_EQ(OK,
            stream_->SendRequest(headers_, &response_, callback_.callback()));

  // Ack the request.
  ProcessPacket(ConstructServerAckPacket(1, 0, 0, 0));

  EXPECT_THAT(stream_->ReadResponseHeaders(callback_.callback()),
              IsError(ERR_IO_PENDING));

  SetResponse("404 Not Found", string());
  size_t spdy_response_header_frame_length;
  ProcessPacket(ConstructResponseHeadersPacket(
      2, kFin, &spdy_response_header_frame_length));

  // Now that the headers have been processed, the callback will return.
  EXPECT_THAT(callback_.WaitForResult(), IsOk());
  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,
            stream_->ReadResponseBody(read_buffer_.get(), read_buffer_->size(),
                                      callback_.callback()));
  EXPECT_TRUE(stream_->IsResponseBodyComplete());
  EXPECT_TRUE(AtEof());

  EXPECT_TRUE(stream_->GetLoadTimingInfo(&load_timing_info));
  ExpectLoadTimingValid(load_timing_info, /*session_reused=*/false);

  // QuicHttpStream::GetTotalSent/ReceivedBytes currently only includes the
  // headers and payload.
  EXPECT_EQ(static_cast<int64_t>(spdy_request_header_frame_length),
            stream_->GetTotalSentBytes());
  EXPECT_EQ(static_cast<int64_t>(spdy_response_header_frame_length),
            stream_->GetTotalReceivedBytes());
}

TEST_P(QuicHttpStreamTest, LoadTimingTwoRequests) {
  SetRequest("GET", "/", DEFAULT_PRIORITY);
  size_t spdy_request_header_frame_length;

  quic::QuicStreamOffset offset = 0;
  AddWrite(ConstructInitialSettingsPacket(&offset));
  AddWrite(InnerConstructRequestHeadersPacket(
      2, GetNthClientInitiatedStreamId(0), kIncludeVersion, kFin,
      DEFAULT_PRIORITY, &spdy_request_header_frame_length, &offset));

  // SetRequest() again for second request as |request_headers_| was moved.
  SetRequest("GET", "/", DEFAULT_PRIORITY);
  AddWrite(InnerConstructRequestHeadersPacket(
      3, GetNthClientInitiatedStreamId(1), kIncludeVersion, kFin,
      DEFAULT_PRIORITY, GetNthClientInitiatedStreamId(0),
      &spdy_request_header_frame_length, &offset));
  AddWrite(ConstructClientAckPacket(4, 3, 1, 1));  // Ack the responses.

  Initialize();

  request_.method = "GET";
  request_.url = GURL("https://www.example.org/");
  // Start first request.
  EXPECT_EQ(OK,
            stream_->InitializeStream(&request_, true, DEFAULT_PRIORITY,
                                      net_log_.bound(), callback_.callback()));
  EXPECT_EQ(OK,
            stream_->SendRequest(headers_, &response_, callback_.callback()));

  // Start a second request.
  QuicHttpStream stream2(
      session_->CreateHandle(HostPortPair("www.example.org", 443)));
  TestCompletionCallback callback2;
  EXPECT_EQ(OK,
            stream2.InitializeStream(&request_, true, DEFAULT_PRIORITY,
                                     net_log_.bound(), callback2.callback()));
  EXPECT_EQ(OK,
            stream2.SendRequest(headers_, &response_, callback2.callback()));

  // Ack both requests.
  ProcessPacket(ConstructServerAckPacket(1, 0, 0, 0));

  EXPECT_THAT(stream_->ReadResponseHeaders(callback_.callback()),
              IsError(ERR_IO_PENDING));
  size_t spdy_response_header_frame_length;
  SetResponse("200 OK", string());
  ProcessPacket(InnerConstructResponseHeadersPacket(
      2, GetNthClientInitiatedStreamId(0), kFin,
      &spdy_response_header_frame_length));

  // Now that the headers have been processed, the callback will return.
  EXPECT_THAT(callback_.WaitForResult(), IsOk());
  EXPECT_EQ(200, response_.headers->response_code());

  // There is no body, so this should return immediately.
  EXPECT_EQ(0,
            stream_->ReadResponseBody(read_buffer_.get(), read_buffer_->size(),
                                      callback_.callback()));
  EXPECT_TRUE(stream_->IsResponseBodyComplete());

  LoadTimingInfo load_timing_info;
  EXPECT_TRUE(stream_->GetLoadTimingInfo(&load_timing_info));
  ExpectLoadTimingValid(load_timing_info, /*session_reused=*/false);

  // SetResponse() again for second request as |response_headers_| was moved.
  SetResponse("200 OK", string());
  EXPECT_THAT(stream2.ReadResponseHeaders(callback2.callback()),
              IsError(ERR_IO_PENDING));

  ProcessPacket(InnerConstructResponseHeadersPacket(
      3, GetNthClientInitiatedStreamId(1), kFin,
      &spdy_response_header_frame_length));

  EXPECT_THAT(callback2.WaitForResult(), IsOk());

  // There is no body, so this should return immediately.
  EXPECT_EQ(0,
            stream2.ReadResponseBody(read_buffer_.get(), read_buffer_->size(),
                                     callback2.callback()));
  EXPECT_TRUE(stream2.IsResponseBodyComplete());

  LoadTimingInfo load_timing_info2;
  EXPECT_TRUE(stream2.GetLoadTimingInfo(&load_timing_info2));
  ExpectLoadTimingValid(load_timing_info2, /*session_reused=*/true);
}

// QuicHttpStream does not currently support trailers. It should ignore
// trailers upon receiving them.
TEST_P(QuicHttpStreamTest, GetRequestWithTrailers) {
  SetRequest("GET", "/", DEFAULT_PRIORITY);
  size_t spdy_request_header_frame_length;
  quic::QuicStreamOffset header_stream_offset = 0;
  AddWrite(ConstructInitialSettingsPacket(&header_stream_offset));
  AddWrite(InnerConstructRequestHeadersPacket(
      2, GetNthClientInitiatedStreamId(0), kIncludeVersion, kFin,
      DEFAULT_PRIORITY, &spdy_request_header_frame_length,
      &header_stream_offset));
  AddWrite(ConstructClientAckPacket(3, 3, 1, 1));  // Ack the data packet.

  Initialize();

  request_.method = "GET";
  request_.url = GURL("https://www.example.org/");

  EXPECT_EQ(OK,
            stream_->InitializeStream(&request_, true, DEFAULT_PRIORITY,
                                      net_log_.bound(), callback_.callback()));

  EXPECT_EQ(OK,
            stream_->SendRequest(headers_, &response_, callback_.callback()));
  // Ack the request.
  ProcessPacket(ConstructServerAckPacket(1, 0, 0, 0));

  EXPECT_THAT(stream_->ReadResponseHeaders(callback_.callback()),
              IsError(ERR_IO_PENDING));

  SetResponse("200 OK", string());

  // Send the response headers.
  size_t spdy_response_header_frame_length;
  quic::QuicStreamOffset offset = 0;
  ProcessPacket(ConstructResponseHeadersPacketWithOffset(
      2, !kFin, &spdy_response_header_frame_length, &offset));
  // Now that the headers have been processed, the callback will return.
  EXPECT_THAT(callback_.WaitForResult(), IsOk());
  ASSERT_TRUE(response_.headers.get());
  EXPECT_EQ(200, 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());

  // Send the response body.
  const char kResponseBody[] = "Hello world!";
  ProcessPacket(
      ConstructServerDataPacket(3, false, !kFin, /*offset=*/0, kResponseBody));
  spdy::SpdyHeaderBlock trailers;
  size_t spdy_trailers_frame_length;
  trailers["foo"] = "bar";
  trailers[quic::kFinalOffsetHeaderKey] =
      base::IntToString(strlen(kResponseBody));
  ProcessPacket(ConstructResponseTrailersPacket(
      4, kFin, std::move(trailers), &spdy_trailers_frame_length, &offset));

  // Make sure trailers are processed.
  base::RunLoop().RunUntilIdle();

  EXPECT_EQ(static_cast<int>(strlen(kResponseBody)),
            stream_->ReadResponseBody(read_buffer_.get(), read_buffer_->size(),
                                      callback_.callback()));
  EXPECT_TRUE(stream_->IsResponseBodyComplete());

  EXPECT_EQ(OK,
            stream_->ReadResponseBody(read_buffer_.get(), read_buffer_->size(),
                                      callback_.callback()));

  EXPECT_TRUE(stream_->IsResponseBodyComplete());
  EXPECT_TRUE(AtEof());

  // QuicHttpStream::GetTotalSent/ReceivedBytes currently only includes the
  // headers and payload.
  EXPECT_EQ(static_cast<int64_t>(spdy_request_header_frame_length),
            stream_->GetTotalSentBytes());
  EXPECT_EQ(
      static_cast<int64_t>(spdy_response_header_frame_length +
                           strlen(kResponseBody) + +spdy_trailers_frame_length),
      stream_->GetTotalReceivedBytes());
  // Check that NetLog was filled as expected.
  TestNetLogEntry::List entries;
  net_log_.GetEntries(&entries);
  size_t pos = ExpectLogContainsSomewhere(
      entries, /*min_offset=*/0,
      NetLogEventType::QUIC_CHROMIUM_CLIENT_STREAM_SEND_REQUEST_HEADERS,
      NetLogEventPhase::NONE);
  pos = ExpectLogContainsSomewhere(
      entries, /*min_offset=*/pos,
      NetLogEventType::QUIC_CHROMIUM_CLIENT_STREAM_SEND_REQUEST_HEADERS,
      NetLogEventPhase::NONE);
  ExpectLogContainsSomewhere(
      entries, /*min_offset=*/pos,
      NetLogEventType::QUIC_CHROMIUM_CLIENT_STREAM_SEND_REQUEST_HEADERS,
      NetLogEventPhase::NONE);
}

// Regression test for http://crbug.com/288128
TEST_P(QuicHttpStreamTest, GetRequestLargeResponse) {
  SetRequest("GET", "/", DEFAULT_PRIORITY);
  size_t spdy_request_headers_frame_length;
  quic::QuicStreamOffset header_stream_offset = 0;
  AddWrite(ConstructInitialSettingsPacket(&header_stream_offset));
  AddWrite(InnerConstructRequestHeadersPacket(
      2, GetNthClientInitiatedStreamId(0), kIncludeVersion, kFin,
      DEFAULT_PRIORITY, &spdy_request_headers_frame_length,
      &header_stream_offset));
  Initialize();

  request_.method = "GET";
  request_.url = GURL("https://www.example.org/");

  EXPECT_EQ(OK,
            stream_->InitializeStream(&request_, true, DEFAULT_PRIORITY,
                                      net_log_.bound(), callback_.callback()));
  EXPECT_EQ(OK,
            stream_->SendRequest(headers_, &response_, callback_.callback()));

  // Ack the request.
  ProcessPacket(ConstructServerAckPacket(1, 0, 0, 0));

  EXPECT_THAT(stream_->ReadResponseHeaders(callback_.callback()),
              IsError(ERR_IO_PENDING));

  response_headers_[":status"] = "200 OK";
  response_headers_[":version"] = "HTTP/1.1";
  response_headers_["content-type"] = "text/plain";
  response_headers_["big6"] = string(1000, 'x');  // Lots of x's.

  size_t spdy_response_headers_frame_length;
  ProcessPacket(ConstructResponseHeadersPacket(
      2, kFin, &spdy_response_headers_frame_length));

  // Now that the headers have been processed, the callback will return.
  EXPECT_THAT(callback_.WaitForResult(), IsOk());
  ASSERT_TRUE(response_.headers.get());
  EXPECT_EQ(200, response_.headers->response_code());
  EXPECT_TRUE(response_.headers->HasHeaderValue("Content-Type", "text/plain"));

  // There is no body, so this should return immediately.
  EXPECT_EQ(0,
            stream_->ReadResponseBody(read_buffer_.get(), read_buffer_->size(),
                                      callback_.callback()));
  EXPECT_TRUE(stream_->IsResponseBodyComplete());
  EXPECT_TRUE(AtEof());

  // QuicHttpStream::GetTotalSent/ReceivedBytes currently only includes the
  // headers and payload.
  EXPECT_EQ(static_cast<int64_t>(spdy_request_headers_frame_length),
            stream_->GetTotalSentBytes());
  EXPECT_EQ(static_cast<int64_t>(spdy_response_headers_frame_length),
            stream_->GetTotalReceivedBytes());
}

// Regression test for http://crbug.com/409101
TEST_P(QuicHttpStreamTest, SessionClosedBeforeSendRequest) {
  SetRequest("GET", "/", DEFAULT_PRIORITY);
  Initialize();

  request_.method = "GET";
  request_.url = GURL("https://www.example.org/");

  EXPECT_EQ(OK,
            stream_->InitializeStream(&request_, true, DEFAULT_PRIORITY,
                                      net_log_.bound(), callback_.callback()));

  session_->connection()->CloseConnection(
      quic::QUIC_NO_ERROR, "test", quic::ConnectionCloseBehavior::SILENT_CLOSE);

  EXPECT_EQ(ERR_CONNECTION_CLOSED,
            stream_->SendRequest(headers_, &response_, callback_.callback()));

  EXPECT_EQ(0, stream_->GetTotalSentBytes());
  EXPECT_EQ(0, stream_->GetTotalReceivedBytes());
}

// Regression test for http://crbug.com/584441
TEST_P(QuicHttpStreamTest, GetSSLInfoAfterSessionClosed) {
  SetRequest("GET", "/", DEFAULT_PRIORITY);
  Initialize();

  request_.method = "GET";
  request_.url = GURL("https://www.example.org/");

  EXPECT_EQ(OK,
            stream_->InitializeStream(&request_, true, DEFAULT_PRIORITY,
                                      net_log_.bound(), callback_.callback()));

  SSLInfo ssl_info;
  EXPECT_FALSE(ssl_info.is_valid());
  stream_->GetSSLInfo(&ssl_info);
  EXPECT_TRUE(ssl_info.is_valid());

  session_->connection()->CloseConnection(
      quic::QUIC_NO_ERROR, "test", quic::ConnectionCloseBehavior::SILENT_CLOSE);

  SSLInfo ssl_info2;
  stream_->GetSSLInfo(&ssl_info2);
  EXPECT_TRUE(ssl_info2.is_valid());
}

TEST_P(QuicHttpStreamTest, GetAlternativeService) {
  SetRequest("GET", "/", DEFAULT_PRIORITY);
  Initialize();

  request_.method = "GET";
  request_.url = GURL("https://www.example.org/");

  EXPECT_EQ(OK,
            stream_->InitializeStream(&request_, true, DEFAULT_PRIORITY,
                                      net_log_.bound(), callback_.callback()));

  AlternativeService alternative_service;
  EXPECT_TRUE(stream_->GetAlternativeService(&alternative_service));
  EXPECT_EQ(AlternativeService(kProtoQUIC, "www.example.org", 443),
            alternative_service);

  session_->connection()->CloseConnection(
      quic::QUIC_NO_ERROR, "test", quic::ConnectionCloseBehavior::SILENT_CLOSE);

  AlternativeService alternative_service2;
  EXPECT_TRUE(stream_->GetAlternativeService(&alternative_service2));
  EXPECT_EQ(AlternativeService(kProtoQUIC, "www.example.org", 443),
            alternative_service2);
}

TEST_P(QuicHttpStreamTest, LogGranularQuicConnectionError) {
  SetRequest("GET", "/", DEFAULT_PRIORITY);
  size_t spdy_request_headers_frame_length;
  quic::QuicStreamOffset header_stream_offset = 0;
  AddWrite(ConstructInitialSettingsPacket(&header_stream_offset));
  AddWrite(InnerConstructRequestHeadersPacket(
      2, GetNthClientInitiatedStreamId(0), kIncludeVersion, kFin,
      DEFAULT_PRIORITY, &spdy_request_headers_frame_length,
      &header_stream_offset));
  AddWrite(ConstructAckAndRstStreamPacket(3));
  Initialize();

  request_.method = "GET";
  request_.url = GURL("https://www.example.org/");

  EXPECT_EQ(OK,
            stream_->InitializeStream(&request_, true, DEFAULT_PRIORITY,
                                      net_log_.bound(), callback_.callback()));
  EXPECT_EQ(OK,
            stream_->SendRequest(headers_, &response_, callback_.callback()));

  // Ack the request.
  ProcessPacket(ConstructServerAckPacket(1, 0, 0, 0));
  EXPECT_THAT(stream_->ReadResponseHeaders(callback_.callback()),
              IsError(ERR_IO_PENDING));

  quic::QuicConnectionCloseFrame frame;
  frame.error_code = quic::QUIC_PEER_GOING_AWAY;
  session_->connection()->OnConnectionCloseFrame(frame);

  NetErrorDetails details;
  EXPECT_EQ(quic::QUIC_NO_ERROR, details.quic_connection_error);
  stream_->PopulateNetErrorDetails(&details);
  EXPECT_EQ(quic::QUIC_PEER_GOING_AWAY, details.quic_connection_error);
}

TEST_P(QuicHttpStreamTest, LogGranularQuicErrorIfHandshakeNotConfirmed) {
  // By default the test setup defaults handshake to be confirmed. Manually set
  // it to be not confirmed.
  crypto_client_stream_factory_.set_handshake_mode(
      MockCryptoClientStream::ZERO_RTT);

  SetRequest("GET", "/", DEFAULT_PRIORITY);
  size_t spdy_request_headers_frame_length;
  quic::QuicStreamOffset header_stream_offset = 0;
  client_maker_.SetEncryptionLevel(quic::ENCRYPTION_INITIAL);
  client_maker_.SetLongHeaderType(quic::ZERO_RTT_PROTECTED);
  AddWrite(InnerConstructRequestHeadersPacket(
      1, GetNthClientInitiatedStreamId(0), kIncludeVersion, kFin,
      DEFAULT_PRIORITY, &spdy_request_headers_frame_length,
      &header_stream_offset));
  Initialize();

  request_.method = "GET";
  request_.url = GURL("https://www.example.org/");

  EXPECT_EQ(OK,
            stream_->InitializeStream(&request_, true, DEFAULT_PRIORITY,
                                      net_log_.bound(), callback_.callback()));
  EXPECT_EQ(OK,
            stream_->SendRequest(headers_, &response_, callback_.callback()));

  // Ack the request.
  ProcessPacket(ConstructServerAckPacket(1, 0, 0, 0));
  EXPECT_THAT(stream_->ReadResponseHeaders(callback_.callback()),
              IsError(ERR_IO_PENDING));

  quic::QuicConnectionCloseFrame frame;
  frame.error_code = quic::QUIC_PEER_GOING_AWAY;
  session_->connection()->OnConnectionCloseFrame(frame);

  NetErrorDetails details;
  stream_->PopulateNetErrorDetails(&details);
  EXPECT_EQ(quic::QUIC_PEER_GOING_AWAY, details.quic_connection_error);
}

// Regression test for http://crbug.com/409871
TEST_P(QuicHttpStreamTest, SessionClosedBeforeReadResponseHeaders) {
  SetRequest("GET", "/", DEFAULT_PRIORITY);
  size_t spdy_request_headers_frame_length;
  quic::QuicStreamOffset header_stream_offset = 0;
  AddWrite(ConstructInitialSettingsPacket(&header_stream_offset));
  AddWrite(InnerConstructRequestHeadersPacket(
      2, GetNthClientInitiatedStreamId(0), kIncludeVersion, kFin,
      DEFAULT_PRIORITY, &spdy_request_headers_frame_length,
      &header_stream_offset));
  Initialize();

  request_.method = "GET";
  request_.url = GURL("https://www.example.org/");

  EXPECT_EQ(OK,
            stream_->InitializeStream(&request_, true, DEFAULT_PRIORITY,
                                      net_log_.bound(), callback_.callback()));

  EXPECT_EQ(OK,
            stream_->SendRequest(headers_, &response_, callback_.callback()));

  session_->connection()->CloseConnection(
      quic::QUIC_NO_ERROR, "test", quic::ConnectionCloseBehavior::SILENT_CLOSE);

  EXPECT_NE(OK, stream_->ReadResponseHeaders(callback_.callback()));

  // QuicHttpStream::GetTotalSent/ReceivedBytes currently only includes the
  // headers and payload.
  EXPECT_EQ(static_cast<int64_t>(spdy_request_headers_frame_length),
            stream_->GetTotalSentBytes());
  EXPECT_EQ(0, stream_->GetTotalReceivedBytes());
}

TEST_P(QuicHttpStreamTest, SendPostRequest) {
  SetRequest("POST", "/", DEFAULT_PRIORITY);
  size_t spdy_request_headers_frame_length;
  quic::QuicStreamOffset header_stream_offset = 0;
  AddWrite(ConstructInitialSettingsPacket(&header_stream_offset));

  AddWrite(ConstructRequestHeadersAndDataFramesPacket(
      2, GetNthClientInitiatedStreamId(0), kIncludeVersion, kFin,
      DEFAULT_PRIORITY, 0, &header_stream_offset,
      &spdy_request_headers_frame_length, {kUploadData}));

  AddWrite(ConstructClientAckPacket(3, 3, 1, 1));

  Initialize();

  std::vector<std::unique_ptr<UploadElementReader>> element_readers;
  element_readers.push_back(std::make_unique<UploadBytesElementReader>(
      kUploadData, strlen(kUploadData)));
  upload_data_stream_ =
      std::make_unique<ElementsUploadDataStream>(std::move(element_readers), 0);
  request_.method = "POST";
  request_.url = GURL("https://www.example.org/");
  request_.upload_data_stream = upload_data_stream_.get();
  ASSERT_THAT(request_.upload_data_stream->Init(CompletionOnceCallback(),
                                                NetLogWithSource()),
              IsOk());

  EXPECT_EQ(OK,
            stream_->InitializeStream(&request_, false, DEFAULT_PRIORITY,
                                      net_log_.bound(), callback_.callback()));
  EXPECT_EQ(OK,
            stream_->SendRequest(headers_, &response_, callback_.callback()));

  // Ack both packets in the request.
  ProcessPacket(ConstructServerAckPacket(1, 0, 0, 0));

  // Send the response headers (but not the body).
  SetResponse("200 OK", string());
  size_t spdy_response_headers_frame_length;
  ProcessPacket(ConstructResponseHeadersPacket(
      2, !kFin, &spdy_response_headers_frame_length));

  // The headers have already arrived.
  EXPECT_THAT(stream_->ReadResponseHeaders(callback_.callback()), IsOk());
  ASSERT_TRUE(response_.headers.get());
  EXPECT_EQ(200, response_.headers->response_code());
  EXPECT_TRUE(response_.headers->HasHeaderValue("Content-Type", "text/plain"));

  // Send the response body.
  const char kResponseBody[] = "Hello world!";
  ProcessPacket(ConstructServerDataPacket(3, false, kFin, 0, kResponseBody));
  // Since the body has already arrived, this should return immediately.
  EXPECT_EQ(static_cast<int>(strlen(kResponseBody)),
            stream_->ReadResponseBody(read_buffer_.get(), read_buffer_->size(),
                                      callback_.callback()));
  EXPECT_EQ(0,
            stream_->ReadResponseBody(read_buffer_.get(), read_buffer_->size(),
                                      callback_.callback()));

  EXPECT_TRUE(stream_->IsResponseBodyComplete());
  EXPECT_TRUE(AtEof());

  // QuicHttpStream::GetTotalSent/ReceivedBytes currently only includes the
  // headers and payload.
  EXPECT_EQ(static_cast<int64_t>(spdy_request_headers_frame_length +
                                 strlen(kUploadData)),
            stream_->GetTotalSentBytes());
  EXPECT_EQ(static_cast<int64_t>(spdy_response_headers_frame_length +
                                 strlen(kResponseBody)),
            stream_->GetTotalReceivedBytes());
}

TEST_P(QuicHttpStreamTest, SendPostRequestAndReceiveSoloFin) {
  SetRequest("POST", "/", DEFAULT_PRIORITY);
  size_t spdy_request_headers_frame_length;
  quic::QuicStreamOffset header_stream_offset = 0;
  AddWrite(ConstructInitialSettingsPacket(&header_stream_offset));
  AddWrite(ConstructRequestHeadersAndDataFramesPacket(
      2, GetNthClientInitiatedStreamId(0), kIncludeVersion, kFin,
      DEFAULT_PRIORITY, 0, &header_stream_offset,
      &spdy_request_headers_frame_length, {kUploadData}));
  AddWrite(ConstructClientAckPacket(3, 3, 1, 1));

  Initialize();

  std::vector<std::unique_ptr<UploadElementReader>> element_readers;
  element_readers.push_back(std::make_unique<UploadBytesElementReader>(
      kUploadData, strlen(kUploadData)));
  upload_data_stream_ =
      std::make_unique<ElementsUploadDataStream>(std::move(element_readers), 0);
  request_.method = "POST";
  request_.url = GURL("https://www.example.org/");
  request_.upload_data_stream = upload_data_stream_.get();
  ASSERT_THAT(request_.upload_data_stream->Init(CompletionOnceCallback(),
                                                NetLogWithSource()),
              IsOk());

  EXPECT_EQ(OK,
            stream_->InitializeStream(&request_, false, DEFAULT_PRIORITY,
                                      net_log_.bound(), callback_.callback()));
  EXPECT_EQ(OK,
            stream_->SendRequest(headers_, &response_, callback_.callback()));

  // Ack both packets in the request.
  ProcessPacket(ConstructServerAckPacket(1, 0, 0, 0));

  // Send the response headers (but not the body).
  SetResponse("200 OK", string());
  size_t spdy_response_headers_frame_length;
  ProcessPacket(ConstructResponseHeadersPacket(
      2, !kFin, &spdy_response_headers_frame_length));

  // The headers have already arrived.
  EXPECT_THAT(stream_->ReadResponseHeaders(callback_.callback()), IsOk());
  ASSERT_TRUE(response_.headers.get());
  EXPECT_EQ(200, response_.headers->response_code());
  EXPECT_TRUE(response_.headers->HasHeaderValue("Content-Type", "text/plain"));

  // Send the response body.
  const char kResponseBody[] = "Hello world!";
  ProcessPacket(ConstructServerDataPacket(3, false, !kFin, 0, kResponseBody));
  // Since the body has already arrived, this should return immediately.
  EXPECT_EQ(static_cast<int>(strlen(kResponseBody)),
            stream_->ReadResponseBody(read_buffer_.get(), read_buffer_->size(),
                                      callback_.callback()));
  ProcessPacket(ConstructServerDataPacket(4, false, kFin,
                                          arraysize(kResponseBody) - 1, ""));
  EXPECT_EQ(0,
            stream_->ReadResponseBody(read_buffer_.get(), read_buffer_->size(),
                                      callback_.callback()));

  EXPECT_TRUE(stream_->IsResponseBodyComplete());
  EXPECT_TRUE(AtEof());

  // QuicHttpStream::GetTotalSent/ReceivedBytes currently only includes the
  // headers and payload.
  EXPECT_EQ(static_cast<int64_t>(spdy_request_headers_frame_length +
                                 strlen(kUploadData)),
            stream_->GetTotalSentBytes());
  EXPECT_EQ(static_cast<int64_t>(spdy_response_headers_frame_length +
                                 strlen(kResponseBody)),
            stream_->GetTotalReceivedBytes());
}

TEST_P(QuicHttpStreamTest, SendChunkedPostRequest) {
  SetRequest("POST", "/", DEFAULT_PRIORITY);
  size_t chunk_size = strlen(kUploadData);
  size_t spdy_request_headers_frame_length;
  quic::QuicStreamOffset header_stream_offset = 0;
  AddWrite(ConstructInitialSettingsPacket(&header_stream_offset));
  AddWrite(ConstructRequestHeadersAndDataFramesPacket(
      2, GetNthClientInitiatedStreamId(0), kIncludeVersion, !kFin,
      DEFAULT_PRIORITY, 0, &header_stream_offset,
      &spdy_request_headers_frame_length, {kUploadData}));
  AddWrite(ConstructClientDataPacket(3, kIncludeVersion, kFin, chunk_size,
                                     kUploadData));
  AddWrite(ConstructClientAckPacket(4, 3, 1, 1));
  Initialize();

  upload_data_stream_ = std::make_unique<ChunkedUploadDataStream>(0);
  auto* chunked_upload_stream =
      static_cast<ChunkedUploadDataStream*>(upload_data_stream_.get());
  chunked_upload_stream->AppendData(kUploadData, chunk_size, false);

  request_.method = "POST";
  request_.url = GURL("https://www.example.org/");
  request_.upload_data_stream = upload_data_stream_.get();
  ASSERT_EQ(OK, request_.upload_data_stream->Init(
                    TestCompletionCallback().callback(), NetLogWithSource()));

  ASSERT_EQ(OK,
            stream_->InitializeStream(&request_, false, DEFAULT_PRIORITY,
                                      net_log_.bound(), callback_.callback()));
  ASSERT_EQ(ERR_IO_PENDING,
            stream_->SendRequest(headers_, &response_, callback_.callback()));

  chunked_upload_stream->AppendData(kUploadData, chunk_size, true);
  EXPECT_THAT(callback_.WaitForResult(), IsOk());

  // Ack both packets in the request.
  ProcessPacket(ConstructServerAckPacket(1, 0, 0, 0));

  // Send the response headers (but not the body).
  SetResponse("200 OK", string());
  size_t spdy_response_headers_frame_length;
  ProcessPacket(ConstructResponseHeadersPacket(
      2, !kFin, &spdy_response_headers_frame_length));

  // The headers have already arrived.
  EXPECT_THAT(stream_->ReadResponseHeaders(callback_.callback()), IsOk());
  ASSERT_TRUE(response_.headers.get());
  EXPECT_EQ(200, response_.headers->response_code());
  EXPECT_TRUE(response_.headers->HasHeaderValue("Content-Type", "text/plain"));

  // Send the response body.
  const char kResponseBody[] = "Hello world!";
  ProcessPacket(ConstructServerDataPacket(
      3, false, kFin, response_data_.length(), kResponseBody));

  // Since the body has already arrived, this should return immediately.
  ASSERT_EQ(static_cast<int>(strlen(kResponseBody)),
            stream_->ReadResponseBody(read_buffer_.get(), read_buffer_->size(),
                                      callback_.callback()));

  EXPECT_TRUE(stream_->IsResponseBodyComplete());
  EXPECT_TRUE(AtEof());

  // QuicHttpStream::GetTotalSent/ReceivedBytes currently only includes the
  // headers and payload.
  EXPECT_EQ(static_cast<int64_t>(spdy_request_headers_frame_length +
                                 strlen(kUploadData) * 2),
            stream_->GetTotalSentBytes());
  EXPECT_EQ(static_cast<int64_t>(spdy_response_headers_frame_length +
                                 strlen(kResponseBody)),
            stream_->GetTotalReceivedBytes());
}

TEST_P(QuicHttpStreamTest, SendChunkedPostRequestWithFinalEmptyDataPacket) {
  SetRequest("POST", "/", DEFAULT_PRIORITY);
  size_t chunk_size = strlen(kUploadData);
  size_t spdy_request_headers_frame_length;
  quic::QuicStreamOffset header_stream_offset = 0;
  AddWrite(ConstructInitialSettingsPacket(&header_stream_offset));
  AddWrite(ConstructRequestHeadersAndDataFramesPacket(
      2, GetNthClientInitiatedStreamId(0), kIncludeVersion, !kFin,
      DEFAULT_PRIORITY, 0, &header_stream_offset,
      &spdy_request_headers_frame_length, {kUploadData}));
  AddWrite(ConstructClientDataPacket(3, kIncludeVersion, kFin, chunk_size, ""));
  AddWrite(ConstructClientAckPacket(4, 3, 1, 1));
  Initialize();

  upload_data_stream_ = std::make_unique<ChunkedUploadDataStream>(0);
  auto* chunked_upload_stream =
      static_cast<ChunkedUploadDataStream*>(upload_data_stream_.get());
  chunked_upload_stream->AppendData(kUploadData, chunk_size, false);

  request_.method = "POST";
  request_.url = GURL("https://www.example.org/");
  request_.upload_data_stream = upload_data_stream_.get();
  ASSERT_EQ(OK, request_.upload_data_stream->Init(
                    TestCompletionCallback().callback(), NetLogWithSource()));

  ASSERT_EQ(OK,
            stream_->InitializeStream(&request_, false, DEFAULT_PRIORITY,
                                      net_log_.bound(), callback_.callback()));
  ASSERT_EQ(ERR_IO_PENDING,
            stream_->SendRequest(headers_, &response_, callback_.callback()));

  chunked_upload_stream->AppendData(nullptr, 0, true);
  EXPECT_THAT(callback_.WaitForResult(), IsOk());

  ProcessPacket(ConstructServerAckPacket(1, 0, 0, 0));

  // Send the response headers (but not the body).
  SetResponse("200 OK", string());
  size_t spdy_response_headers_frame_length;
  ProcessPacket(ConstructResponseHeadersPacket(
      2, !kFin, &spdy_response_headers_frame_length));

  // The headers have already arrived.
  EXPECT_THAT(stream_->ReadResponseHeaders(callback_.callback()), IsOk());
  ASSERT_TRUE(response_.headers.get());
  EXPECT_EQ(200, response_.headers->response_code());
  EXPECT_TRUE(response_.headers->HasHeaderValue("Content-Type", "text/plain"));

  // Send the response body.
  const char kResponseBody[] = "Hello world!";
  ProcessPacket(ConstructServerDataPacket(
      3, false, kFin, response_data_.length(), kResponseBody));

  // The body has arrived, but it is delivered asynchronously
  ASSERT_EQ(static_cast<int>(strlen(kResponseBody)),
            stream_->ReadResponseBody(read_buffer_.get(), read_buffer_->size(),
                                      callback_.callback()));
  EXPECT_TRUE(stream_->IsResponseBodyComplete());
  EXPECT_TRUE(AtEof());

  // QuicHttpStream::GetTotalSent/ReceivedBytes currently only includes the
  // headers and payload.
  EXPECT_EQ(static_cast<int64_t>(spdy_request_headers_frame_length +
                                 strlen(kUploadData)),
            stream_->GetTotalSentBytes());
  EXPECT_EQ(static_cast<int64_t>(spdy_response_headers_frame_length +
                                 strlen(kResponseBody)),
            stream_->GetTotalReceivedBytes());
}

TEST_P(QuicHttpStreamTest, SendChunkedPostRequestWithOneEmptyDataPacket) {
  SetRequest("POST", "/", DEFAULT_PRIORITY);
  size_t spdy_request_headers_frame_length;
  quic::QuicStreamOffset header_stream_offset = 0;
  AddWrite(ConstructInitialSettingsPacket(&header_stream_offset));
  AddWrite(InnerConstructRequestHeadersPacket(
      2, GetNthClientInitiatedStreamId(0), kIncludeVersion, !kFin,
      DEFAULT_PRIORITY, &spdy_request_headers_frame_length,
      &header_stream_offset));
  AddWrite(ConstructClientDataPacket(3, kIncludeVersion, kFin, 0, ""));
  AddWrite(ConstructClientAckPacket(4, 3, 1, 1));
  Initialize();

  upload_data_stream_ = std::make_unique<ChunkedUploadDataStream>(0);
  auto* chunked_upload_stream =
      static_cast<ChunkedUploadDataStream*>(upload_data_stream_.get());

  request_.method = "POST";
  request_.url = GURL("https://www.example.org/");
  request_.upload_data_stream = upload_data_stream_.get();
  ASSERT_EQ(OK, request_.upload_data_stream->Init(
                    TestCompletionCallback().callback(), NetLogWithSource()));

  ASSERT_EQ(OK,
            stream_->InitializeStream(&request_, false, DEFAULT_PRIORITY,
                                      net_log_.bound(), callback_.callback()));
  ASSERT_EQ(ERR_IO_PENDING,
            stream_->SendRequest(headers_, &response_, callback_.callback()));

  chunked_upload_stream->AppendData(nullptr, 0, true);
  EXPECT_THAT(callback_.WaitForResult(), IsOk());

  ProcessPacket(ConstructServerAckPacket(1, 0, 0, 0));

  // Send the response headers (but not the body).
  SetResponse("200 OK", string());
  size_t spdy_response_headers_frame_length;
  ProcessPacket(ConstructResponseHeadersPacket(
      2, !kFin, &spdy_response_headers_frame_length));

  // The headers have already arrived.
  EXPECT_THAT(stream_->ReadResponseHeaders(callback_.callback()), IsOk());
  ASSERT_TRUE(response_.headers.get());
  EXPECT_EQ(200, response_.headers->response_code());
  EXPECT_TRUE(response_.headers->HasHeaderValue("Content-Type", "text/plain"));

  // Send the response body.
  const char kResponseBody[] = "Hello world!";
  ProcessPacket(ConstructServerDataPacket(
      3, false, kFin, response_data_.length(), kResponseBody));

  // The body has arrived, but it is delivered asynchronously
  ASSERT_EQ(static_cast<int>(strlen(kResponseBody)),
            stream_->ReadResponseBody(read_buffer_.get(), read_buffer_->size(),
                                      callback_.callback()));

  EXPECT_TRUE(stream_->IsResponseBodyComplete());
  EXPECT_TRUE(AtEof());

  // QuicHttpStream::GetTotalSent/ReceivedBytes currently only includes the
  // headers and payload.
  EXPECT_EQ(static_cast<int64_t>(spdy_request_headers_frame_length),
            stream_->GetTotalSentBytes());
  EXPECT_EQ(static_cast<int64_t>(spdy_response_headers_frame_length +
                                 strlen(kResponseBody)),
            stream_->GetTotalReceivedBytes());
}

TEST_P(QuicHttpStreamTest, DestroyedEarly) {
  SetRequest("GET", "/", DEFAULT_PRIORITY);
  size_t spdy_request_headers_frame_length;
  quic::QuicStreamOffset header_stream_offset = 0;
  AddWrite(ConstructInitialSettingsPacket(&header_stream_offset));
  AddWrite(InnerConstructRequestHeadersPacket(
      2, GetNthClientInitiatedStreamId(0), kIncludeVersion, kFin,
      DEFAULT_PRIORITY, &spdy_request_headers_frame_length,
      &header_stream_offset));
  AddWrite(ConstructAckAndRstStreamPacket(3));
  Initialize();

  request_.method = "GET";
  request_.url = GURL("https://www.example.org/");

  EXPECT_EQ(OK,
            stream_->InitializeStream(&request_, true, DEFAULT_PRIORITY,
                                      net_log_.bound(), callback_.callback()));
  EXPECT_EQ(OK,
            stream_->SendRequest(headers_, &response_, callback_.callback()));

  // Ack the request.
  ProcessPacket(ConstructServerAckPacket(1, 0, 0, 0));
  EXPECT_THAT(stream_->ReadResponseHeaders(
                  base::Bind(&QuicHttpStreamTest::CloseStream,
                             base::Unretained(this), stream_.get())),
              IsError(ERR_IO_PENDING));

  // Send the response with a body.
  SetResponse("404 OK", "hello world!");
  // In the course of processing this packet, the QuicHttpStream close itself.
  size_t response_size = 0;
  ProcessPacket(ConstructResponseHeadersPacket(2, !kFin, &response_size));

  base::RunLoop().RunUntilIdle();

  EXPECT_TRUE(AtEof());

  // QuicHttpStream::GetTotalSent/ReceivedBytes currently only includes the
  // headers and payload.
  EXPECT_EQ(static_cast<int64_t>(spdy_request_headers_frame_length),
            stream_->GetTotalSentBytes());
  // The stream was closed after receiving the headers.
  EXPECT_EQ(static_cast<int64_t>(response_size),
            stream_->GetTotalReceivedBytes());
}

TEST_P(QuicHttpStreamTest, Priority) {
  SetRequest("GET", "/", MEDIUM);
  size_t spdy_request_headers_frame_length;
  quic::QuicStreamOffset header_stream_offset = 0;
  AddWrite(ConstructInitialSettingsPacket(&header_stream_offset));
  AddWrite(InnerConstructRequestHeadersPacket(
      2, GetNthClientInitiatedStreamId(0), kIncludeVersion, kFin, MEDIUM,
      &spdy_request_headers_frame_length, &header_stream_offset));
  Initialize();

  request_.method = "GET";
  request_.url = GURL("https://www.example.org/");

  EXPECT_EQ(OK,
            stream_->InitializeStream(&request_, true, MEDIUM, net_log_.bound(),
                                      callback_.callback()));

  EXPECT_EQ(OK,
            stream_->SendRequest(headers_, &response_, callback_.callback()));

  // Ack the request.
  ProcessPacket(ConstructServerAckPacket(1, 0, 0, 0));
  EXPECT_THAT(stream_->ReadResponseHeaders(callback_.callback()),
              IsError(ERR_IO_PENDING));

  // Send the response with a body.
  SetResponse("404 OK", "hello world!");
  size_t response_size = 0;
  ProcessPacket(ConstructResponseHeadersPacket(2, kFin, &response_size));

  EXPECT_EQ(OK, callback_.WaitForResult());

  EXPECT_TRUE(AtEof());

  // QuicHttpStream::GetTotalSent/ReceivedBytes currently only includes the
  // headers and payload.
  EXPECT_EQ(static_cast<int64_t>(spdy_request_headers_frame_length),
            stream_->GetTotalSentBytes());
  EXPECT_EQ(static_cast<int64_t>(response_size),
            stream_->GetTotalReceivedBytes());
}

TEST_P(QuicHttpStreamTest, SessionClosedDuringDoLoop) {
  SetRequest("POST", "/", DEFAULT_PRIORITY);
  size_t spdy_request_headers_frame_length;
  quic::QuicStreamOffset header_stream_offset = 0;
  AddWrite(ConstructInitialSettingsPacket(&header_stream_offset));
  AddWrite(ConstructRequestHeadersAndDataFramesPacket(
      2, GetNthClientInitiatedStreamId(0), kIncludeVersion, !kFin,
      DEFAULT_PRIORITY, 0, &header_stream_offset,
      &spdy_request_headers_frame_length, {kUploadData}));
  // Second data write will result in a synchronous failure which will close
  // the session.
  AddWrite(SYNCHRONOUS, ERR_FAILED);
  Initialize();

  upload_data_stream_ = std::make_unique<ChunkedUploadDataStream>(0);
  auto* chunked_upload_stream =
      static_cast<ChunkedUploadDataStream*>(upload_data_stream_.get());

  request_.method = "POST";
  request_.url = GURL("https://www.example.org/");
  request_.upload_data_stream = upload_data_stream_.get();
  ASSERT_EQ(OK, request_.upload_data_stream->Init(
                    TestCompletionCallback().callback(), NetLogWithSource()));

  size_t chunk_size = strlen(kUploadData);
  chunked_upload_stream->AppendData(kUploadData, chunk_size, false);
  ASSERT_EQ(OK,
            stream_->InitializeStream(&request_, false, DEFAULT_PRIORITY,
                                      net_log_.bound(), callback_.callback()));
  QuicHttpStream* stream = stream_.get();
  DeleteStreamCallback delete_stream_callback(std::move(stream_));
  // SendRequest() completes asynchronously after the final chunk is added.
  // Error does not surface yet since packet write is triggered by a packet
  // flusher that tries to bundle request body writes.
  ASSERT_EQ(ERR_IO_PENDING,
            stream->SendRequest(headers_, &response_, callback_.callback()));
  chunked_upload_stream->AppendData(kUploadData, chunk_size, true);
  int rv = callback_.WaitForResult();
  EXPECT_EQ(OK, rv);
  // Error will be surfaced once an attempt to read the response occurs.
  ASSERT_EQ(ERR_QUIC_PROTOCOL_ERROR,
            stream->ReadResponseHeaders(callback_.callback()));
}

TEST_P(QuicHttpStreamTest, SessionClosedBeforeSendHeadersComplete) {
  SetRequest("POST", "/", DEFAULT_PRIORITY);
  quic::QuicStreamOffset header_stream_offset = 0;
  AddWrite(ConstructInitialSettingsPacket(&header_stream_offset));
  AddWrite(SYNCHRONOUS, ERR_FAILED);
  Initialize();

  upload_data_stream_ = std::make_unique<ChunkedUploadDataStream>(0);
  auto* chunked_upload_stream =
      static_cast<ChunkedUploadDataStream*>(upload_data_stream_.get());

  request_.method = "POST";
  request_.url = GURL("https://www.example.org/");
  request_.upload_data_stream = upload_data_stream_.get();
  ASSERT_EQ(OK, request_.upload_data_stream->Init(
                    TestCompletionCallback().callback(), NetLogWithSource()));

  ASSERT_EQ(OK,
            stream_->InitializeStream(&request_, false, DEFAULT_PRIORITY,
                                      net_log_.bound(), callback_.callback()));
  ASSERT_EQ(ERR_IO_PENDING,
            stream_->SendRequest(headers_, &response_, callback_.callback()));

  // Error will be surfaced once |upload_data_stream| triggers the next write.
  size_t chunk_size = strlen(kUploadData);
  chunked_upload_stream->AppendData(kUploadData, chunk_size, true);
  ASSERT_EQ(ERR_QUIC_PROTOCOL_ERROR, callback_.WaitForResult());

  EXPECT_LE(0, stream_->GetTotalSentBytes());
  EXPECT_EQ(0, stream_->GetTotalReceivedBytes());
}

TEST_P(QuicHttpStreamTest, SessionClosedBeforeSendHeadersCompleteReadResponse) {
  SetRequest("POST", "/", DEFAULT_PRIORITY);
  quic::QuicStreamOffset header_stream_offset = 0;
  AddWrite(ConstructInitialSettingsPacket(&header_stream_offset));
  AddWrite(SYNCHRONOUS, ERR_FAILED);
  Initialize();

  upload_data_stream_ = std::make_unique<ChunkedUploadDataStream>(0);
  auto* chunked_upload_stream =
      static_cast<ChunkedUploadDataStream*>(upload_data_stream_.get());

  request_.method = "POST";
  request_.url = GURL("https://www.example.org/");
  request_.upload_data_stream = upload_data_stream_.get();

  size_t chunk_size = strlen(kUploadData);
  chunked_upload_stream->AppendData(kUploadData, chunk_size, true);

  ASSERT_EQ(OK, request_.upload_data_stream->Init(
                    TestCompletionCallback().callback(), NetLogWithSource()));

  ASSERT_EQ(OK,
            stream_->InitializeStream(&request_, false, DEFAULT_PRIORITY,
                                      net_log_.bound(), callback_.callback()));
  ASSERT_EQ(OK,
            stream_->SendRequest(headers_, &response_, callback_.callback()));

  // Error will be surfaced once an attempt to read the response occurs.
  ASSERT_EQ(ERR_QUIC_PROTOCOL_ERROR,
            stream_->ReadResponseHeaders(callback_.callback()));

  EXPECT_LE(0, stream_->GetTotalSentBytes());
  EXPECT_EQ(0, stream_->GetTotalReceivedBytes());
}

TEST_P(QuicHttpStreamTest, SessionClosedBeforeSendBodyComplete) {
  SetRequest("POST", "/", DEFAULT_PRIORITY);
  size_t spdy_request_headers_frame_length;
  quic::QuicStreamOffset header_stream_offset = 0;
  AddWrite(ConstructInitialSettingsPacket(&header_stream_offset));
  AddWrite(InnerConstructRequestHeadersPacket(
      2, GetNthClientInitiatedStreamId(0), kIncludeVersion, !kFin,
      DEFAULT_PRIORITY, &spdy_request_headers_frame_length,
      &header_stream_offset));
  AddWrite(SYNCHRONOUS, ERR_FAILED);
  Initialize();

  upload_data_stream_ = std::make_unique<ChunkedUploadDataStream>(0);
  auto* chunked_upload_stream =
      static_cast<ChunkedUploadDataStream*>(upload_data_stream_.get());

  request_.method = "POST";
  request_.url = GURL("https://www.example.org/");
  request_.upload_data_stream = upload_data_stream_.get();
  ASSERT_EQ(OK, request_.upload_data_stream->Init(
                    TestCompletionCallback().callback(), NetLogWithSource()));

  ASSERT_EQ(OK,
            stream_->InitializeStream(&request_, false, DEFAULT_PRIORITY,
                                      net_log_.bound(), callback_.callback()));
  ASSERT_EQ(ERR_IO_PENDING,
            stream_->SendRequest(headers_, &response_, callback_.callback()));

  size_t chunk_size = strlen(kUploadData);
  chunked_upload_stream->AppendData(kUploadData, chunk_size, true);
  // Error does not surface yet since packet write is triggered by a packet
  // flusher that tries to bundle request body writes.
  ASSERT_EQ(OK, callback_.WaitForResult());
  // Error will be surfaced once an attempt to read the response occurs.
  ASSERT_EQ(ERR_QUIC_PROTOCOL_ERROR,
            stream_->ReadResponseHeaders(callback_.callback()));

  EXPECT_LE(0, stream_->GetTotalSentBytes());
  EXPECT_EQ(0, stream_->GetTotalReceivedBytes());
}

TEST_P(QuicHttpStreamTest, SessionClosedBeforeSendBundledBodyComplete) {
  SetRequest("POST", "/", DEFAULT_PRIORITY);
  size_t spdy_request_headers_frame_length;
  quic::QuicStreamOffset header_stream_offset = 0;
  AddWrite(ConstructInitialSettingsPacket(&header_stream_offset));
  AddWrite(ConstructRequestHeadersAndDataFramesPacket(
      2, GetNthClientInitiatedStreamId(0), kIncludeVersion, !kFin,
      DEFAULT_PRIORITY, 0, &header_stream_offset,
      &spdy_request_headers_frame_length, {kUploadData}));
  AddWrite(SYNCHRONOUS, ERR_FAILED);
  Initialize();

  upload_data_stream_ = std::make_unique<ChunkedUploadDataStream>(0);
  auto* chunked_upload_stream =
      static_cast<ChunkedUploadDataStream*>(upload_data_stream_.get());

  request_.method = "POST";
  request_.url = GURL("https://www.example.org/");
  request_.upload_data_stream = upload_data_stream_.get();

  size_t chunk_size = strlen(kUploadData);
  chunked_upload_stream->AppendData(kUploadData, chunk_size, false);

  ASSERT_EQ(OK, request_.upload_data_stream->Init(
                    TestCompletionCallback().callback(), NetLogWithSource()));

  ASSERT_EQ(OK,
            stream_->InitializeStream(&request_, false, DEFAULT_PRIORITY,
                                      net_log_.bound(), callback_.callback()));
  ASSERT_EQ(ERR_IO_PENDING,
            stream_->SendRequest(headers_, &response_, callback_.callback()));

  chunked_upload_stream->AppendData(kUploadData, chunk_size, true);

  // Error does not surface yet since packet write is triggered by a packet
  // flusher that tries to bundle request body writes.
  ASSERT_EQ(OK, callback_.WaitForResult());
  // Error will be surfaced once an attempt to read the response occurs.
  ASSERT_EQ(ERR_QUIC_PROTOCOL_ERROR,
            stream_->ReadResponseHeaders(callback_.callback()));

  EXPECT_LE(0, stream_->GetTotalSentBytes());
  EXPECT_EQ(0, stream_->GetTotalReceivedBytes());
}

TEST_P(QuicHttpStreamTest, ServerPushGetRequest) {
  SetRequest("GET", "/", DEFAULT_PRIORITY);
  Initialize();

  // Initialize the first stream, for receiving the promise on.
  request_.method = "GET";
  request_.url = GURL("https://www.example.org/");

  EXPECT_EQ(OK,
            stream_->InitializeStream(&request_, true, DEFAULT_PRIORITY,
                                      net_log_.bound(), 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_, true, DEFAULT_PRIORITY, net_log_.bound(),
                    callback_.callback()));

  // Receive the promised response headers.
  response_headers_ = promised_response_.Clone();
  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, &server_maker_));

  // 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_THAT(promised_stream_->ReadResponseHeaders(callback_.callback()),
              IsOk());

  // 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("https://www.example.org/");

  EXPECT_EQ(OK,
            stream_->InitializeStream(&request_, true, DEFAULT_PRIORITY,
                                      net_log_.bound(), 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_, true, DEFAULT_PRIORITY, net_log_.bound(),
                    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_.Clone();
  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, &server_maker_));

  base::RunLoop().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_THAT(callback_.WaitForResult(), IsOk());

  EXPECT_EQ(
      QuicHttpStreamPeer::GetQuicChromiumClientStream(promised_stream_.get())
          ->id(),
      promise_id_);

  EXPECT_THAT(promised_stream_->ReadResponseHeaders(callback_.callback()),
              IsOk());

  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());
}

// Verify fix for crbug.com/637349
TEST_P(QuicHttpStreamTest, ServerPushCancelHttpStreamBeforeResponse) {
  SetRequest("GET", "/", DEFAULT_PRIORITY);
  Initialize();

  // Initialize the first stream, for receiving the promise on.
  request_.method = "GET";
  request_.url = GURL("https://www.example.org/");

  EXPECT_EQ(OK,
            stream_->InitializeStream(&request_, true, DEFAULT_PRIORITY,
                                      net_log_.bound(), 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_, true, DEFAULT_PRIORITY, net_log_.bound(),
                    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()));

  base::RunLoop().RunUntilIdle();

  // Cause of FinalValidation() crash as per bug.
  promised_stream_.reset();

  // Receive the promised response headers.
  response_headers_ = promised_response_.Clone();
  size_t spdy_response_headers_frame_length;
  ProcessPacket(InnerConstructResponseHeadersPacket(
      1, promise_id_, false, &spdy_response_headers_frame_length));
}

TEST_P(QuicHttpStreamTest, ServerPushCrossOriginOK) {
  SetRequest("GET", "/", DEFAULT_PRIORITY);
  Initialize();

  // Initialize the first stream, for receiving the promise on.
  request_.method = "GET";
  request_.url = GURL("https://www.example.org/");

  EXPECT_EQ(OK,
            stream_->InitializeStream(&request_, true, DEFAULT_PRIORITY,
                                      net_log_.bound(), 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_ = quic::SpdyUtils::GetPromisedUrlFromHeaders(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_, true, DEFAULT_PRIORITY, net_log_.bound(),
                    callback_.callback()));

  // Receive the promised response headers.
  response_headers_ = promised_response_.Clone();
  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, &server_maker_));

  // 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_THAT(promised_stream_->ReadResponseHeaders(callback_.callback()),
              IsOk());

  // 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("https://www.example.org/");

  EXPECT_EQ(OK,
            stream_->InitializeStream(&request_, true, DEFAULT_PRIORITY,
                                      net_log_.bound(), 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_ = quic::SpdyUtils::GetPromisedUrlFromHeaders(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("https://www.example.org/");

  EXPECT_EQ(OK,
            stream_->InitializeStream(&request_, true, DEFAULT_PRIORITY,
                                      net_log_.bound(), callback_.callback()));

  push_promise_["accept-encoding"] = "gzip";

  // 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_, true, DEFAULT_PRIORITY, net_log_.bound(),
                    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_.Clone();
  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, &server_maker_));

  base::RunLoop().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_THAT(callback_.WaitForResult(), IsOk());

  EXPECT_EQ(
      QuicHttpStreamPeer::GetQuicChromiumClientStream(promised_stream_.get())
          ->id(),
      promise_id_);

  EXPECT_THAT(promised_stream_->ReadResponseHeaders(callback_.callback()),
              IsOk());

  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;
  quic::QuicStreamOffset header_stream_offset = 0;
  AddWrite(ConstructInitialSettingsPacket(&header_stream_offset));

  quic::QuicPacketNumber client_packet_number = 2;
  if (client_headers_include_h2_stream_dependency_ &&
      version_ >= quic::QUIC_VERSION_43) {
    AddWrite(ConstructClientPriorityPacket(
        client_packet_number++, kIncludeVersion, promise_id_, 0,
        DEFAULT_PRIORITY, &header_stream_offset));
  }
  AddWrite(ConstructClientRstStreamVaryMismatchAndRequestHeadersPacket(
      client_packet_number++, stream_id_ + 2, !kIncludeVersion, kFin,
      DEFAULT_PRIORITY, promise_id_, &spdy_request_header_frame_length,
      &header_stream_offset));
  AddWrite(ConstructClientAckPacket(client_packet_number++, 3, 1, 1));
  AddWrite(ConstructClientRstStreamCancelledPacket(client_packet_number++));

  Initialize();

  // Initialize the first stream, for receiving the promise on.
  request_.method = "GET";
  request_.url = GURL("https://www.example.org/");

  EXPECT_EQ(OK,
            stream_->InitializeStream(&request_, true, DEFAULT_PRIORITY,
                                      net_log_.bound(), callback_.callback()));

  push_promise_["accept-encoding"] = "gzip";

  // 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_, true, DEFAULT_PRIORITY, net_log_.bound(),
                    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_.Clone();
  size_t spdy_response_headers_frame_length;
  ProcessPacket(InnerConstructResponseHeadersPacket(
      1, promise_id_, false, &spdy_response_headers_frame_length));

  base::RunLoop().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_THAT(callback_.WaitForResult(), IsOk());

  // 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(ConstructServerAckPacket(2, 0, 0, 0));

  SetResponse("404 Not Found", string());
  size_t spdy_response_header_frame_length;
  ProcessPacket(InnerConstructResponseHeadersPacket(
      3, stream_id_ + 2, kFin, &spdy_response_header_frame_length));

  base::RunLoop().RunUntilIdle();

  EXPECT_THAT(promised_stream_->ReadResponseHeaders(callback_.callback()),
              IsOk());
  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());
}

TEST_P(QuicHttpStreamTest, DataReadErrorSynchronous) {
  SetRequest("POST", "/", DEFAULT_PRIORITY);
  size_t spdy_request_headers_frame_length;
  quic::QuicStreamOffset header_stream_offset = 0;
  AddWrite(ConstructInitialSettingsPacket(&header_stream_offset));
  AddWrite(ConstructRequestAndRstPacket(
      2, GetNthClientInitiatedStreamId(0), kIncludeVersion, !kFin,
      DEFAULT_PRIORITY, 0, &spdy_request_headers_frame_length,
      &header_stream_offset, quic::QUIC_ERROR_PROCESSING_STREAM, 0));

  Initialize();

  upload_data_stream_ = std::make_unique<ReadErrorUploadDataStream>(
      ReadErrorUploadDataStream::FailureMode::SYNC);
  request_.method = "POST";
  request_.url = GURL("https://www.example.org/");
  request_.upload_data_stream = upload_data_stream_.get();
  ASSERT_EQ(OK, request_.upload_data_stream->Init(
                    TestCompletionCallback().callback(), NetLogWithSource()));

  EXPECT_EQ(OK,
            stream_->InitializeStream(&request_, false, DEFAULT_PRIORITY,
                                      net_log_.bound(), callback_.callback()));

  int result = stream_->SendRequest(headers_, &response_, callback_.callback());
  EXPECT_THAT(result, IsError(ERR_FAILED));

  EXPECT_TRUE(AtEof());

  // QuicHttpStream::GetTotalSent/ReceivedBytes includes only headers.
  EXPECT_EQ(static_cast<int64_t>(spdy_request_headers_frame_length),
            stream_->GetTotalSentBytes());
  EXPECT_EQ(0, stream_->GetTotalReceivedBytes());
}

TEST_P(QuicHttpStreamTest, DataReadErrorAsynchronous) {
  SetRequest("POST", "/", DEFAULT_PRIORITY);
  size_t spdy_request_headers_frame_length;
  quic::QuicStreamOffset header_stream_offset = 0;
  AddWrite(ConstructInitialSettingsPacket(&header_stream_offset));
  AddWrite(InnerConstructRequestHeadersPacket(
      2, GetNthClientInitiatedStreamId(0), kIncludeVersion, !kFin,
      DEFAULT_PRIORITY, &spdy_request_headers_frame_length,
      &header_stream_offset));
  AddWrite(ConstructClientRstStreamErrorPacket(3, !kIncludeVersion));

  Initialize();

  upload_data_stream_ = std::make_unique<ReadErrorUploadDataStream>(
      ReadErrorUploadDataStream::FailureMode::ASYNC);
  request_.method = "POST";
  request_.url = GURL("https://www.example.org/");
  request_.upload_data_stream = upload_data_stream_.get();
  ASSERT_EQ(OK, request_.upload_data_stream->Init(
                    TestCompletionCallback().callback(), NetLogWithSource()));

  EXPECT_EQ(OK,
            stream_->InitializeStream(&request_, false, DEFAULT_PRIORITY,
                                      net_log_.bound(), callback_.callback()));

  int result = stream_->SendRequest(headers_, &response_, callback_.callback());

  ProcessPacket(ConstructServerAckPacket(1, 0, 0, 0));
  SetResponse("200 OK", string());

  EXPECT_THAT(result, IsError(ERR_IO_PENDING));
  EXPECT_THAT(callback_.GetResult(result), IsError(ERR_FAILED));

  EXPECT_TRUE(AtEof());

  // QuicHttpStream::GetTotalSent/ReceivedBytes includes only headers.
  EXPECT_EQ(static_cast<int64_t>(spdy_request_headers_frame_length),
            stream_->GetTotalSentBytes());
  EXPECT_EQ(0, stream_->GetTotalReceivedBytes());
}

}  // namespace test
}  // namespace net
