| // 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 <memory> | 
 | #include <utility> | 
 |  | 
 | #include "base/bind.h" | 
 | #include "base/memory/ptr_util.h" | 
 | #include "base/run_loop.h" | 
 | #include "base/stl_util.h" | 
 | #include "base/strings/string_number_conversions.h" | 
 | #include "base/threading/thread_task_runner_handle.h" | 
 | #include "base/time/default_tick_clock.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_utils.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/quiche/src/spdy/core/spdy_frame_builder.h" | 
 | #include "net/third_party/quiche/src/spdy/core/spdy_framer.h" | 
 | #include "net/third_party/quiche/src/spdy/core/spdy_protocol.h" | 
 | #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" | 
 | #include "starboard/string.h" | 
 | #include "starboard/types.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::BindOnce(&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_(GetNthServerInitiatedUnidirectionalStreamId(0)), | 
 |         stream_id_(GetNthClientInitiatedBidirectionalStreamId(0)), | 
 |         connection_id_(quic::test::TestConnectionId(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, | 
 |                                   quic::ConnectionCloseBehavior::SILENT_CLOSE); | 
 |     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, | 
 |         /*default_network=*/NetworkChangeNotifier::kInvalidNetworkHandle, | 
 |         quic::QuicTime::Delta::FromMilliseconds( | 
 |             kDefaultRetransmittableOnWireTimeoutMillisecs), | 
 |         /*migrate_idle_session=*/false, | 
 |         base::TimeDelta::FromSeconds(kDefaultIdleSessionMigrationPeriodSeconds), | 
 |         base::TimeDelta::FromSeconds(kMaxTimeOnNonDefaultNetworkSecs), | 
 |         kMaxMigrationsToNonDefaultNetworkOnWriteError, | 
 |         kMaxMigrationsToNonDefaultNetworkOnPathDegrading, | 
 |         kQuicYieldAfterPacketsRead, | 
 |         quic::QuicTime::Delta::FromMilliseconds( | 
 |             kQuicYieldAfterDurationMilliseconds), | 
 |         /*go_away_on_path_degrading*/ false, | 
 |         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::DefaultTickClock::GetInstance(), | 
 |         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( | 
 |       uint64_t 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( | 
 |       uint64_t 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> | 
 |   ConstructClientMultipleDataFramesPacket( | 
 |       uint64_t packet_number, | 
 |       bool should_include_version, | 
 |       bool fin, | 
 |       quic::QuicStreamOffset offset, | 
 |       const std::vector<std::string>& data) { | 
 |     return client_maker_.MakeMultipleDataFramesPacket( | 
 |         packet_number, stream_id_, should_include_version, fin, offset, data); | 
 |   } | 
 |  | 
 |   std::unique_ptr<quic::QuicReceivedPacket> ConstructServerDataPacket( | 
 |       uint64_t 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( | 
 |       uint64_t 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( | 
 |       uint64_t 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( | 
 |       uint64_t 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( | 
 |       uint64_t 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( | 
 |       uint64_t 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( | 
 |       uint64_t 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( | 
 |       uint64_t 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(uint64_t 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( | 
 |       uint64_t 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( | 
 |       uint64_t packet_number) { | 
 |     return client_maker_.MakeRstPacket(packet_number, true, stream_id_, | 
 |                                        quic::QUIC_RST_ACKNOWLEDGEMENT); | 
 |   } | 
 |  | 
 |   std::unique_ptr<quic::QuicReceivedPacket> | 
 |   ConstructClientRstStreamCancelledPacket(uint64_t packet_number) { | 
 |     return client_maker_.MakeRstPacket(packet_number, !kIncludeVersion, | 
 |                                        stream_id_, quic::QUIC_STREAM_CANCELLED); | 
 |   } | 
 |  | 
 |   std::unique_ptr<quic::QuicReceivedPacket> | 
 |   ConstructClientRstStreamVaryMismatchPacket(uint64_t packet_number) { | 
 |     return client_maker_.MakeRstPacket(packet_number, !kIncludeVersion, | 
 |                                        promise_id_, | 
 |                                        quic::QUIC_PROMISE_VARY_MISMATCH); | 
 |   } | 
 |  | 
 |   std::unique_ptr<quic::QuicReceivedPacket> | 
 |   ConstructClientRstStreamVaryMismatchAndRequestHeadersPacket( | 
 |       uint64_t 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( | 
 |       uint64_t packet_number, | 
 |       uint64_t largest_received, | 
 |       uint64_t smallest_received, | 
 |       uint64_t 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( | 
 |       uint64_t 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( | 
 |       uint64_t packet_number) { | 
 |     return ConstructAckAndRstStreamPacket(packet_number, 2, 1, 2); | 
 |   } | 
 |  | 
 |   std::unique_ptr<quic::QuicReceivedPacket> ConstructClientAckPacket( | 
 |       uint64_t packet_number, | 
 |       uint64_t largest_received, | 
 |       uint64_t smallest_received, | 
 |       uint64_t least_unacked) { | 
 |     return client_maker_.MakeAckPacket(packet_number, largest_received, | 
 |                                        smallest_received, least_unacked, | 
 |                                        !kIncludeCongestionFeedback); | 
 |   } | 
 |  | 
 |   std::unique_ptr<quic::QuicReceivedPacket> ConstructServerAckPacket( | 
 |       uint64_t packet_number, | 
 |       uint64_t largest_received, | 
 |       uint64_t smallest_received, | 
 |       uint64_t least_unacked) { | 
 |     return server_maker_.MakeAckPacket(packet_number, largest_received, | 
 |                                        smallest_received, least_unacked, | 
 |                                        !kIncludeCongestionFeedback); | 
 |   } | 
 |  | 
 |   std::unique_ptr<quic::QuicReceivedPacket> ConstructClientPriorityPacket( | 
 |       uint64_t 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); | 
 |   } | 
 |  | 
 |   quic::QuicString ConstructDataHeader(size_t body_len) { | 
 |     if (version_ != quic::QUIC_VERSION_99) { | 
 |       return ""; | 
 |     } | 
 |     quic::HttpEncoder encoder; | 
 |     std::unique_ptr<char[]> buffer; | 
 |     auto header_length = encoder.SerializeDataFrameHeader(body_len, &buffer); | 
 |     return quic::QuicString(buffer.get(), header_length); | 
 |   } | 
 |  | 
 |   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 GetNthClientInitiatedBidirectionalStreamId(int n) { | 
 |     return quic::test::GetNthClientInitiatedBidirectionalStreamId(version_, n); | 
 |   } | 
 |  | 
 |   quic::QuicStreamId GetNthServerInitiatedUnidirectionalStreamId(int n) { | 
 |     return quic::test::GetNthServerInitiatedUnidirectionalStreamId(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_SUITE_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, GetNthClientInitiatedBidirectionalStreamId(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, 1, 1, 1)); | 
 |  | 
 |   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, GetNthClientInitiatedBidirectionalStreamId(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, GetNthClientInitiatedBidirectionalStreamId(1), kIncludeVersion, kFin, | 
 |       DEFAULT_PRIORITY, GetNthClientInitiatedBidirectionalStreamId(0), | 
 |       &spdy_request_header_frame_length, &offset)); | 
 |   AddWrite(ConstructClientAckPacket(4, 3, 1, 2));  // 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, 1, 1, 1)); | 
 |  | 
 |   EXPECT_THAT(stream_->ReadResponseHeaders(callback_.callback()), | 
 |               IsError(ERR_IO_PENDING)); | 
 |   size_t spdy_response_header_frame_length; | 
 |   SetResponse("200 OK", string()); | 
 |   ProcessPacket(InnerConstructResponseHeadersPacket( | 
 |       2, GetNthClientInitiatedBidirectionalStreamId(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, GetNthClientInitiatedBidirectionalStreamId(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, GetNthClientInitiatedBidirectionalStreamId(0), kIncludeVersion, kFin, | 
 |       DEFAULT_PRIORITY, &spdy_request_header_frame_length, | 
 |       &header_stream_offset)); | 
 |   AddWrite(ConstructClientAckPacket(3, 3, 1, 2));  // 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, 1, 1, 1)); | 
 |  | 
 |   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!"; | 
 |   quic::QuicString header = | 
 |       ConstructDataHeader(strlen(kResponseBody)); | 
 |   ProcessPacket(ConstructServerDataPacket(3, false, !kFin, /*offset=*/0, | 
 |                                           header + kResponseBody)); | 
 |   spdy::SpdyHeaderBlock trailers; | 
 |   size_t spdy_trailers_frame_length; | 
 |   trailers["foo"] = "bar"; | 
 |   trailers[quic::kFinalOffsetHeaderKey] = | 
 |       base::NumberToString(strlen(kResponseBody) + header.length()); | 
 |   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) + | 
 |                                  header.length() + +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, GetNthClientInitiatedBidirectionalStreamId(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, 1, 1, 1)); | 
 |  | 
 |   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, GetNthClientInitiatedBidirectionalStreamId(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, 1, 1, 1)); | 
 |   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_ZERO_RTT); | 
 |   AddWrite(InnerConstructRequestHeadersPacket( | 
 |       1, GetNthClientInitiatedBidirectionalStreamId(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, 1, 1, 1)); | 
 |   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, GetNthClientInitiatedBidirectionalStreamId(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)); | 
 |  | 
 |   quic::QuicString header = ConstructDataHeader(strlen(kUploadData)); | 
 |   if (version_ != quic::QUIC_VERSION_99) { | 
 |     AddWrite(ConstructRequestHeadersAndDataFramesPacket( | 
 |         2, GetNthClientInitiatedBidirectionalStreamId(0), kIncludeVersion, kFin, | 
 |         DEFAULT_PRIORITY, 0, &header_stream_offset, | 
 |         &spdy_request_headers_frame_length, {kUploadData})); | 
 |   } else { | 
 |     AddWrite(ConstructRequestHeadersAndDataFramesPacket( | 
 |         2, GetNthClientInitiatedBidirectionalStreamId(0), kIncludeVersion, kFin, | 
 |         DEFAULT_PRIORITY, 0, &header_stream_offset, | 
 |         &spdy_request_headers_frame_length, {header, kUploadData})); | 
 |   } | 
 |  | 
 |   AddWrite(ConstructClientAckPacket(3, 3, 1, 2)); | 
 |  | 
 |   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, 1, 1, 1)); | 
 |  | 
 |   // 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!"; | 
 |   quic::QuicString header2 = | 
 |       ConstructDataHeader(strlen(kResponseBody)); | 
 |   ProcessPacket( | 
 |       ConstructServerDataPacket(3, false, kFin, 0, header2 + 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) + header.length()), | 
 |       stream_->GetTotalSentBytes()); | 
 |   EXPECT_EQ( | 
 |       static_cast<int64_t>(spdy_response_headers_frame_length + | 
 |                            strlen(kResponseBody) + header2.length()), | 
 |       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)); | 
 |   quic::QuicString header = ConstructDataHeader(strlen(kUploadData)); | 
 |   if (version_ != quic::QUIC_VERSION_99) { | 
 |     AddWrite(ConstructRequestHeadersAndDataFramesPacket( | 
 |         2, GetNthClientInitiatedBidirectionalStreamId(0), kIncludeVersion, kFin, | 
 |         DEFAULT_PRIORITY, 0, &header_stream_offset, | 
 |         &spdy_request_headers_frame_length, {kUploadData})); | 
 |   } else { | 
 |     AddWrite(ConstructRequestHeadersAndDataFramesPacket( | 
 |         2, GetNthClientInitiatedBidirectionalStreamId(0), kIncludeVersion, kFin, | 
 |         DEFAULT_PRIORITY, 0, &header_stream_offset, | 
 |         &spdy_request_headers_frame_length, {header, kUploadData})); | 
 |   } | 
 |  | 
 |   AddWrite(ConstructClientAckPacket(3, 3, 1, 2)); | 
 |  | 
 |   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, 1, 1, 1)); | 
 |  | 
 |   // 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!"; | 
 |   quic::QuicString header2 = | 
 |       ConstructDataHeader(strlen(kResponseBody)); | 
 |   ProcessPacket( | 
 |       ConstructServerDataPacket(3, false, !kFin, 0, header2 + 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, base::size(kResponseBody) - 1 + header2.length(), "")); | 
 |   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) + header.length()), | 
 |       stream_->GetTotalSentBytes()); | 
 |   EXPECT_EQ( | 
 |       static_cast<int64_t>(spdy_response_headers_frame_length + | 
 |                            strlen(kResponseBody) + header2.length()), | 
 |       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)); | 
 |   quic::QuicString header = ConstructDataHeader(chunk_size); | 
 |   if (version_ == quic::QUIC_VERSION_99) { | 
 |     AddWrite(ConstructRequestHeadersAndDataFramesPacket( | 
 |         2, GetNthClientInitiatedBidirectionalStreamId(0), kIncludeVersion, | 
 |         !kFin, DEFAULT_PRIORITY, 0, &header_stream_offset, | 
 |         &spdy_request_headers_frame_length, {header, kUploadData})); | 
 |     AddWrite(ConstructClientMultipleDataFramesPacket( | 
 |         3, kIncludeVersion, kFin, header.length() + chunk_size, | 
 |         {header, kUploadData})); | 
 |   } else { | 
 |     AddWrite(ConstructRequestHeadersAndDataFramesPacket( | 
 |         2, GetNthClientInitiatedBidirectionalStreamId(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, 2)); | 
 |   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, 1, 1, 1)); | 
 |  | 
 |   // 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!"; | 
 |   quic::QuicString header2 = | 
 |       ConstructDataHeader(strlen(kResponseBody)); | 
 |   ProcessPacket(ConstructServerDataPacket( | 
 |       3, false, kFin, response_data_.length(), header2 + 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 + | 
 |                                  header.length() * 2), | 
 |             stream_->GetTotalSentBytes()); | 
 |   EXPECT_EQ( | 
 |       static_cast<int64_t>(spdy_response_headers_frame_length + | 
 |                            strlen(kResponseBody) + header2.length()), | 
 |       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)); | 
 |   quic::QuicString header = ConstructDataHeader(chunk_size); | 
 |  | 
 |   if (version_ != quic::QUIC_VERSION_99) { | 
 |     AddWrite(ConstructRequestHeadersAndDataFramesPacket( | 
 |         2, GetNthClientInitiatedBidirectionalStreamId(0), kIncludeVersion, | 
 |         !kFin, DEFAULT_PRIORITY, 0, &header_stream_offset, | 
 |         &spdy_request_headers_frame_length, {kUploadData})); | 
 |   } else { | 
 |     AddWrite(ConstructRequestHeadersAndDataFramesPacket( | 
 |         2, GetNthClientInitiatedBidirectionalStreamId(0), kIncludeVersion, | 
 |         !kFin, DEFAULT_PRIORITY, 0, &header_stream_offset, | 
 |         &spdy_request_headers_frame_length, {header, kUploadData})); | 
 |   } | 
 |   AddWrite(ConstructClientDataPacket(3, kIncludeVersion, kFin, | 
 |                                      chunk_size + header.length(), "")); | 
 |   AddWrite(ConstructClientAckPacket(4, 3, 1, 2)); | 
 |   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, 1, 1, 1)); | 
 |  | 
 |   // 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!"; | 
 |   quic::QuicString header2 = | 
 |       ConstructDataHeader(strlen(kResponseBody)); | 
 |   ProcessPacket(ConstructServerDataPacket( | 
 |       3, false, kFin, response_data_.length(), header2 + 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) + header.length()), | 
 |       stream_->GetTotalSentBytes()); | 
 |   EXPECT_EQ( | 
 |       static_cast<int64_t>(spdy_response_headers_frame_length + | 
 |                            strlen(kResponseBody) + header2.length()), | 
 |       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, GetNthClientInitiatedBidirectionalStreamId(0), kIncludeVersion, !kFin, | 
 |       DEFAULT_PRIORITY, &spdy_request_headers_frame_length, | 
 |       &header_stream_offset)); | 
 |   AddWrite(ConstructClientDataPacket(3, kIncludeVersion, kFin, 0, "")); | 
 |   AddWrite(ConstructClientAckPacket(4, 3, 1, 2)); | 
 |   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, 1, 1, 1)); | 
 |  | 
 |   // 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!"; | 
 |   quic::QuicString header = | 
 |       ConstructDataHeader(strlen(kResponseBody)); | 
 |   ProcessPacket(ConstructServerDataPacket( | 
 |       3, false, kFin, response_data_.length(), header + 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) + header.length()), | 
 |       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, GetNthClientInitiatedBidirectionalStreamId(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, 1, 1, 1)); | 
 |   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, GetNthClientInitiatedBidirectionalStreamId(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, 1, 1, 1)); | 
 |   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)); | 
 |   quic::QuicString header = ConstructDataHeader(strlen(kUploadData)); | 
 |   if (version_ != quic::QUIC_VERSION_99) { | 
 |     AddWrite(ConstructRequestHeadersAndDataFramesPacket( | 
 |         2, GetNthClientInitiatedBidirectionalStreamId(0), kIncludeVersion, | 
 |         !kFin, DEFAULT_PRIORITY, 0, &header_stream_offset, | 
 |         &spdy_request_headers_frame_length, {kUploadData})); | 
 |   } else { | 
 |     AddWrite(ConstructRequestHeadersAndDataFramesPacket( | 
 |         2, GetNthClientInitiatedBidirectionalStreamId(0), kIncludeVersion, | 
 |         !kFin, DEFAULT_PRIORITY, 0, &header_stream_offset, | 
 |         &spdy_request_headers_frame_length, {header, 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, GetNthClientInitiatedBidirectionalStreamId(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)); | 
 |   quic::QuicString header = ConstructDataHeader(strlen(kUploadData)); | 
 |   if (version_ != quic::QUIC_VERSION_99) { | 
 |     AddWrite(ConstructRequestHeadersAndDataFramesPacket( | 
 |         2, GetNthClientInitiatedBidirectionalStreamId(0), kIncludeVersion, | 
 |         !kFin, DEFAULT_PRIORITY, 0, &header_stream_offset, | 
 |         &spdy_request_headers_frame_length, {kUploadData})); | 
 |   } else { | 
 |     AddWrite(ConstructRequestHeadersAndDataFramesPacket( | 
 |         2, GetNthClientInitiatedBidirectionalStreamId(0), kIncludeVersion, | 
 |         !kFin, DEFAULT_PRIORITY, 0, &header_stream_offset, | 
 |         &spdy_request_headers_frame_length, {header, 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!"; | 
 |   quic::QuicString header = | 
 |       ConstructDataHeader(strlen(kResponseBody)); | 
 |   ProcessPacket(InnerConstructDataPacket( | 
 |       2, promise_id_, false, kFin, 0, header + 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) + header.length()), | 
 |       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!"; | 
 |   quic::QuicString header = | 
 |       ConstructDataHeader(strlen(kResponseBody)); | 
 |   ProcessPacket(InnerConstructDataPacket( | 
 |       2, promise_id_, false, kFin, 0, header + 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) + header.length()), | 
 |       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!"; | 
 |   quic::QuicString header = | 
 |       ConstructDataHeader(strlen(kResponseBody)); | 
 |   ProcessPacket(InnerConstructDataPacket( | 
 |       2, promise_id_, false, kFin, 0, header + 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) + header.length()), | 
 |       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!"; | 
 |   quic::QuicString header = | 
 |       ConstructDataHeader(strlen(kResponseBody)); | 
 |   ProcessPacket(InnerConstructDataPacket( | 
 |       2, promise_id_, false, kFin, 0, header + 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) + header.length()), | 
 |       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)); | 
 |  | 
 |   uint64_t 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_ + quic::QuicUtils::StreamIdDelta(version_), !kIncludeVersion, | 
 |       kFin, DEFAULT_PRIORITY, promise_id_, &spdy_request_header_frame_length, | 
 |       &header_stream_offset)); | 
 |   AddWrite(ConstructClientAckPacket(client_packet_number++, 3, 1, 2)); | 
 |   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_ + quic::QuicUtils::StreamIdDelta(version_)); | 
 |  | 
 |   // 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, 1, 1, 1)); | 
 |  | 
 |   SetResponse("404 Not Found", string()); | 
 |   size_t spdy_response_header_frame_length; | 
 |   ProcessPacket(InnerConstructResponseHeadersPacket( | 
 |       3, stream_id_ + quic::QuicUtils::StreamIdDelta(version_), 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, GetNthClientInitiatedBidirectionalStreamId(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, GetNthClientInitiatedBidirectionalStreamId(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, 1, 1, 1)); | 
 |   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 |