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

#include "net/quic/quic_chromium_client_session.h"

#include "base/base64.h"
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/rand_util.h"
#include "base/run_loop.h"
#include "base/strings/strcat.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/run_until.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_tick_clock.h"
#include "base/time/default_tick_clock.h"
#include "build/build_config.h"
#include "net/base/connection_endpoint_metadata.h"
#include "net/base/connection_migration_information.h"
#include "net/base/ech_mode.h"
#include "net/base/features.h"
#include "net/base/net_errors.h"
#include "net/base/network_anonymization_key.h"
#include "net/base/privacy_mode.h"
#include "net/base/proxy_chain.h"
#include "net/base/proxy_server.h"
#include "net/base/schemeful_site.h"
#include "net/base/session_usage.h"
#include "net/base/test_completion_callback.h"
#include "net/cert/cert_verify_result.h"
#include "net/dns/public/host_resolver_results.h"
#include "net/dns/public/secure_dns_policy.h"
#include "net/http/transport_security_state.h"
#include "net/http/transport_security_state_test_util.h"
#include "net/log/net_log.h"
#include "net/log/net_log_source.h"
#include "net/net_buildflags.h"
#include "net/quic/address_utils.h"
#include "net/quic/crypto/proof_verifier_chromium.h"
#include "net/quic/mock_crypto_client_stream_factory.h"
#include "net/quic/mock_quic_data.h"
#include "net/quic/quic_chromium_alarm_factory.h"
#include "net/quic/quic_chromium_client_session_peer.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_connectivity_monitor.h"
#include "net/quic/quic_context.h"
#include "net/quic/quic_crypto_client_config_handle.h"
#include "net/quic/quic_crypto_client_stream_factory.h"
#include "net/quic/quic_http_utils.h"
#include "net/quic/quic_server_info.h"
#include "net/quic/quic_session_alias_key.h"
#include "net/quic/quic_session_key.h"
#include "net/quic/quic_test_packet_maker.h"
#include "net/quic/test_quic_crypto_client_config_handle.h"
#include "net/socket/datagram_client_socket.h"
#include "net/socket/socket_test_util.h"
#include "net/spdy/multiplexed_session_creation_initiator.h"
#include "net/spdy/spdy_test_util_common.h"
#include "net/ssl/ssl_config_service_defaults.h"
#include "net/ssl/test_static_ech_mode_getter.h"
#include "net/test/cert_test_util.h"
#include "net/test/gtest_util.h"
#include "net/test/ssl_test_util.h"
#include "net/test/test_data_directory.h"
#include "net/test/test_with_task_environment.h"
#include "net/third_party/quiche/src/quiche/common/http/http_header_block.h"
#include "net/third_party/quiche/src/quiche/http2/test_tools/spdy_test_utils.h"
#include "net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_12_encrypter.h"
#include "net/third_party/quiche/src/quiche/quic/core/crypto/crypto_protocol.h"
#include "net/third_party/quiche/src/quiche/quic/core/crypto/quic_decrypter.h"
#include "net/third_party/quiche/src/quiche/quic/core/crypto/quic_encrypter.h"
#include "net/third_party/quiche/src/quiche/quic/core/http/http_constants.h"
#include "net/third_party/quiche/src/quiche/quic/core/quic_connection_id.h"
#include "net/third_party/quiche/src/quiche/quic/core/quic_packet_writer.h"
#include "net/third_party/quiche/src/quiche/quic/core/quic_tag.h"
#include "net/third_party/quiche/src/quiche/quic/core/quic_utils.h"
#include "net/third_party/quiche/src/quiche/quic/platform/api/quic_flags.h"
#include "net/third_party/quiche/src/quiche/quic/platform/api/quic_test.h"
#include "net/third_party/quiche/src/quiche/quic/test_tools/crypto_test_utils.h"
#include "net/third_party/quiche/src/quiche/quic/test_tools/mock_connection_id_generator.h"
#include "net/third_party/quiche/src/quiche/quic/test_tools/qpack/qpack_test_utils.h"
#include "net/third_party/quiche/src/quiche/quic/test_tools/quic_connection_peer.h"
#include "net/third_party/quiche/src/quiche/quic/test_tools/quic_session_peer.h"
#include "net/third_party/quiche/src/quiche/quic/test_tools/quic_stream_peer.h"
#include "net/third_party/quiche/src/quiche/quic/test_tools/quic_test_utils.h"
#include "net/third_party/quiche/src/quiche/quic/test_tools/simple_quic_framer.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/boringssl/src/include/openssl/ssl.h"
#include "url/gurl.h"
#include "url/scheme_host_port.h"
#include "url/url_constants.h"

#if BUILDFLAG(ENABLE_WEBSOCKETS)
#include "net/websockets/websocket_quic_spdy_stream.h"
#endif  // BUILDFLAG(ENABLE_WEBSOCKETS)

using testing::_;

namespace net::test {
namespace {

const IPEndPoint kIpEndPoint = IPEndPoint(IPAddress::IPv4AllZeros(), 0);
const char kServerHostname[] = "test.example.com";
const uint16_t kServerPort = 443;
const size_t kMaxReadersPerQuicSession = 5;

const handles::NetworkHandle kDefaultNetworkForTests = 1;
const handles::NetworkHandle kNewNetworkForTests = 2;

#if BUILDFLAG(ENABLE_WEBSOCKETS)
quic::QuicStreamId ActivateWebSocketStream(QuicChromiumClientSession* session) {
  quic::QuicStreamId stream_id =
      quic::test::QuicSessionPeer::GetNextOutgoingBidirectionalStreamId(
          session);
  auto websocket_stream = std::make_unique<WebSocketQuicSpdyStream>(
      stream_id, session, quic::BIDIRECTIONAL);
  quic::test::QuicSessionPeer::ActivateStream(session,
                                              std::move(websocket_stream));
  return stream_id;
}
#endif  // BUILDFLAG(ENABLE_WEBSOCKETS)

class TestingQuicConnection : public quic::QuicConnection {
 public:
  using quic::QuicConnection::QuicConnection;

  void OnKeepAliveTimeout() override {
    keep_alive_timeout_callback_.Run();
    quic::QuicConnection::OnKeepAliveTimeout();
  }

  void SetKeepAliveTimeoutCallback(base::RepeatingCallback<void()> callback) {
    keep_alive_timeout_callback_ = std::move(callback);
  }

 private:
  base::RepeatingCallback<void()> keep_alive_timeout_callback_;
};

class TestSSLConfigServiceDefaults : public SSLConfigServiceDefaults {
 public:
  EchMode GetEchMode(std::string_view hostname) const override {
    if (ech_mode_getter_) {
      return ech_mode_getter_->GetEchMode(hostname);
    }
    return EchMode::kOpportunistic;
  }

  void SetEchModeGetter(std::unique_ptr<EchModeGetter> ech_mode_getter) {
    ech_mode_getter_ = std::move(ech_mode_getter);
  }

 private:
  std::unique_ptr<EchModeGetter> ech_mode_getter_;
};

// A subclass of QuicChromiumClientSession that allows OnPathDegrading to be
// mocked.
class TestingQuicChromiumClientSession : public QuicChromiumClientSession {
 public:
  using QuicChromiumClientSession::QuicChromiumClientSession;

  MOCK_METHOD(void, OnPathDegrading, (), (override));
  MOCK_METHOD(void, RegisterQuicConnectionClosePayload, (), (override));
  MOCK_METHOD(void, UnregisterQuicConnectionClosePayload, (), (override));

  void ReallyOnPathDegrading() { QuicChromiumClientSession::OnPathDegrading(); }

  bool OnPacket(const quic::QuicReceivedPacket& packet,
                const quic::QuicSocketAddress& local_address,
                const quic::QuicSocketAddress& peer_address) override {
    bool result = QuicChromiumClientSession::OnPacket(packet, local_address,
                                                      peer_address);
    if (on_packet_callback_) {
      on_packet_callback_.Run();
    }
    return result;
  }

  void set_on_packet_callback(base::RepeatingClosure callback) {
    on_packet_callback_ = std::move(callback);
  }

 private:
  base::RepeatingClosure on_packet_callback_;
};

class QuicChromiumClientSessionTest
    : public ::testing::TestWithParam<quic::ParsedQuicVersion>,
      public WithTaskEnvironment {
 public:
  QuicChromiumClientSessionTest()
      : version_(GetParam()),
        config_(quic::test::DefaultQuicConfig()),
        crypto_config_(
            quic::test::crypto_test_utils::ProofVerifierForTesting()),
        default_read_(
            std::make_unique<MockRead>(SYNCHRONOUS, ERR_IO_PENDING, 0)),
        socket_data_(std::make_unique<SequencedSocketData>(
            base::span_from_ref(*default_read_),
            base::span<MockWrite>())),
        helper_(&clock_, &random_),
        transport_security_state_(std::make_unique<TransportSecurityState>()),
        session_key_(kServerHostname,
                     kServerPort,
                     PRIVACY_MODE_DISABLED,
                     ProxyChain::Direct(),
                     SessionUsage::kDestination,
                     SocketTag(),
                     NetworkAnonymizationKey(),
                     SecureDnsPolicy::kAllow,
                     /*require_dns_https_alpn=*/false,
                     /*disable_cert_verification_network_fetches=*/false,
                     handles::kInvalidNetworkHandle),
        destination_(url::kHttpsScheme, kServerHostname, kServerPort),
        default_network_(handles::kInvalidNetworkHandle),
        client_maker_(version_,
                      quic::QuicUtils::CreateRandomConnectionId(&random_),
                      &clock_,
                      kServerHostname,
                      quic::Perspective::IS_CLIENT),
        server_maker_(version_,
                      quic::QuicUtils::CreateRandomConnectionId(&random_),
                      &clock_,
                      kServerHostname,
                      quic::Perspective::IS_SERVER,
                      false) {
    FLAGS_quic_enable_http3_grease_randomness = false;
    quic::QuicEnableVersion(version_);
    // Advance the time, because timers do not like uninitialized times.
    clock_.AdvanceTime(quic::QuicTime::Delta::FromSeconds(1));
  }

  void ResetHandleOnError(
      std::unique_ptr<QuicChromiumClientSession::Handle>* handle,
      int net_error) {
    EXPECT_NE(OK, net_error);
    handle->reset();
  }

 protected:
  void Initialize() {
    InitializeInternal(&crypto_client_stream_factory_,
                       /*migrate_session_on_network_change_v2=*/false,
                       MultiplexedSessionCreationInitiator::kUnknown,
                       QuicConnectionReuseDetails(), kQuicYieldAfterPacketsRead,
                       quic::QuicTime::Delta::FromMilliseconds(
                           kQuicYieldAfterDurationMilliseconds));
  }

  void Initialize(bool migrate_session_on_network_change_v2) {
    InitializeInternal(&crypto_client_stream_factory_,
                       migrate_session_on_network_change_v2,
                       MultiplexedSessionCreationInitiator::kUnknown,
                       QuicConnectionReuseDetails(), kQuicYieldAfterPacketsRead,
                       quic::QuicTime::Delta::FromMilliseconds(
                           kQuicYieldAfterDurationMilliseconds));
  }

  void Initialize(
      MultiplexedSessionCreationInitiator session_creation_initiator,
      QuicConnectionReuseDetails reuse_details) {
    InitializeInternal(&crypto_client_stream_factory_,
                       /*migrate_session_on_network_change_v2=*/false,
                       session_creation_initiator, reuse_details,
                       kQuicYieldAfterPacketsRead,
                       quic::QuicTime::Delta::FromMilliseconds(
                           kQuicYieldAfterDurationMilliseconds));
  }

  void InitializeWithoutMockCrypto() {
    InitializeInternal(QuicCryptoClientStreamFactory::GetDefaultFactory(),
                       /*migrate_session_on_network_change_v2=*/false,
                       MultiplexedSessionCreationInitiator::kUnknown,
                       QuicConnectionReuseDetails(), kQuicYieldAfterPacketsRead,
                       quic::QuicTime::Delta::FromMilliseconds(
                           kQuicYieldAfterDurationMilliseconds));
  }

  void InitializeInternal(
      QuicCryptoClientStreamFactory* crypto_client_stream_factory,
      bool migrate_session_on_network_change_v2,
      MultiplexedSessionCreationInitiator session_creation_initiator,
      QuicConnectionReuseDetails reuse_details,
      int yield_after_packets,
      quic::QuicTime::Delta yield_after_duration) {
    if (socket_data_) {
      socket_factory_.AddSocketDataProvider(socket_data_.get());
    }
    std::unique_ptr<DatagramClientSocket> socket =
        socket_factory_.CreateDatagramClientSocket(
            DatagramSocket::DEFAULT_BIND, handles::kInvalidNetworkHandle,
            NetLog::Get(), NetLogSource());
    if (default_network_ != handles::kInvalidNetworkHandle) {
      socket->ConnectUsingNetwork(default_network_, kIpEndPoint);
    } else {
      socket->Connect(kIpEndPoint);
    }
    QuicChromiumPacketWriter* writer = new net::QuicChromiumPacketWriter(
        socket.get(), base::SingleThreadTaskRunner::GetCurrentDefault().get());
    auto* connection = new TestingQuicConnection(
        quic::QuicUtils::CreateRandomConnectionId(&random_),
        quic::QuicSocketAddress(), ToQuicSocketAddress(kIpEndPoint), &helper_,
        &alarm_factory_, writer, true, quic::Perspective::IS_CLIENT,
        quic::test::SupportedVersions(version_), connection_id_generator_);
    ping_alarm_ = quic::test::QuicTestAlarmProxy(
        quic::test::QuicConnectionPeer::GetPingAlarm(connection));
    connection->SetKeepAliveTimeoutCallback(
        base::BindRepeating(&QuicChromiumClientSessionTest::OnKeepAliveTimeout,
                            base::Unretained(this)));
    session_ = std::make_unique<TestingQuicChromiumClientSession>(
        connection, std::move(socket),
        /*stream_factory=*/nullptr, crypto_client_stream_factory, &clock_,
        transport_security_state_.get(), &ssl_config_service_,
        base::WrapUnique(static_cast<QuicServerInfo*>(nullptr)),
        QuicSessionAliasKey(url::SchemeHostPort(), session_key_),
        /*require_confirmation=*/false, migrate_session_early_v2_,
        migrate_session_on_network_change_v2, default_network_,
        quic::QuicTime::Delta::FromMilliseconds(
            kDefaultRetransmittableOnWireTimeout.InMilliseconds()),
        /*migrate_idle_session=*/false, allow_port_migration_,
        kDefaultIdleSessionMigrationPeriod, /*multi_port_probing_interval=*/0,
        kMaxTimeOnNonDefaultNetwork,
        kMaxMigrationsToNonDefaultNetworkOnWriteError,
        kMaxMigrationsToNonDefaultNetworkOnPathDegrading, yield_after_packets,
        yield_after_duration,
        /*cert_verify_flags=*/0, config_,
        std::make_unique<TestQuicCryptoClientConfigHandle>(&crypto_config_),
        "CONNECTION_UNKNOWN", base::TimeTicks::Now(), base::TimeTicks::Now(),
        /*resolution_details=*/std::nullopt, tick_clock_,
        base::SingleThreadTaskRunner::GetCurrentDefault().get(),
        /*socket_performance_watcher=*/nullptr, ConnectionEndpointMetadata(),
        /*enable_origin_frame=*/true, /*allow_server_preferred_address=*/true,
        session_creation_initiator,
        NetLogWithSource::Make(NetLogSourceType::NONE), reuse_details);
    if (connectivity_monitor_) {
      connectivity_monitor_->SetInitialDefaultNetwork(default_network_);
      session_->AddConnectivityObserver(connectivity_monitor_.get());
    }

    scoped_refptr<X509Certificate> cert(
        ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem"));
    verify_details_.cert_verify_result.verified_cert = cert;
    verify_details_.cert_verify_result.is_issued_by_known_root = true;
    session_->Initialize();
    // Blackhole QPACK decoder stream instead of constructing mock writes.
    session_->qpack_decoder()->set_qpack_stream_sender_delegate(
        &noop_qpack_stream_sender_delegate_);
    session_->StartReading();
    writer->set_delegate(session_.get());
  }

  void TearDown() override {
    if (session_) {
      if (connectivity_monitor_) {
        session_->RemoveConnectivityObserver(connectivity_monitor_.get());
      }
      if (session_->connection()->connected()) {
        EXPECT_CALL(*session_, UnregisterQuicConnectionClosePayload());
      }
      session_->CloseSessionOnError(
          ERR_ABORTED, quic::QUIC_INTERNAL_ERROR,
          quic::ConnectionCloseBehavior::SILENT_CLOSE);
    }
  }

  void CompleteCryptoHandshake() {
    ASSERT_THAT(session_->CryptoConnect(callback_.callback()), IsOk());
  }

  void OnKeepAliveTimeout() { keep_alive_timeouts_++; }

  std::unique_ptr<QuicChromiumPacketWriter> CreateQuicChromiumPacketWriter(
      DatagramClientSocket* socket,
      QuicChromiumClientSession* session) const {
    auto writer = std::make_unique<QuicChromiumPacketWriter>(
        socket, base::SingleThreadTaskRunner::GetCurrentDefault().get());
    writer->set_delegate(session);
    return writer;
  }

  quic::QuicStreamId GetNthClientInitiatedBidirectionalStreamId(int n) {
    return quic::test::GetNthClientInitiatedBidirectionalStreamId(
        version_.transport_version, n);
  }

  quic::QuicStreamId GetNthServerInitiatedUnidirectionalStreamId(int n) {
    return quic::test::GetNthServerInitiatedUnidirectionalStreamId(
        version_.transport_version, n);
  }

  size_t GetMaxAllowedOutgoingBidirectionalStreams() {
    return quic::test::QuicSessionPeer::ietf_streamid_manager(session_.get())
        ->max_outgoing_bidirectional_streams();
  }

  const quic::ParsedQuicVersion version_;
  quic::test::QuicFlagSaver flags_;  // Save/restore all QUIC flag values.
  quic::QuicConfig config_;
  quic::QuicCryptoClientConfig crypto_config_;
  NetLogWithSource net_log_with_source_{
      NetLogWithSource::Make(NetLog::Get(), NetLogSourceType::NONE)};
  MockClientSocketFactory socket_factory_;
  std::unique_ptr<MockRead> default_read_;
  std::unique_ptr<SequencedSocketData> socket_data_;
  quic::MockClock clock_;
  quic::test::MockRandom random_{0};
  QuicChromiumConnectionHelper helper_;
  quic::test::MockAlarmFactory alarm_factory_;
  std::unique_ptr<TransportSecurityState> transport_security_state_;
  MockCryptoClientStreamFactory crypto_client_stream_factory_;
  TestSSLConfigServiceDefaults ssl_config_service_;
  QuicSessionKey session_key_;
  url::SchemeHostPort destination_;
  std::unique_ptr<TestingQuicChromiumClientSession> session_;
  handles::NetworkHandle default_network_;
  std::unique_ptr<QuicConnectivityMonitor> connectivity_monitor_;
  raw_ptr<quic::QuicConnectionVisitorInterface> visitor_;
  TestCompletionCallback callback_;
  QuicTestPacketMaker client_maker_;
  QuicTestPacketMaker server_maker_;
  ProofVerifyDetailsChromium verify_details_;
  bool migrate_session_early_v2_ = false;
  bool allow_port_migration_ = false;
  quic::test::MockConnectionIdGenerator connection_id_generator_;
  quic::test::NoopQpackStreamSenderDelegate noop_qpack_stream_sender_delegate_;
  int keep_alive_timeouts_ = 0;
  std::optional<quic::test::QuicTestAlarmProxy> ping_alarm_;
  base::SimpleTestTickClock test_tick_clock_;
  raw_ptr<const base::TickClock> tick_clock_ =
      base::DefaultTickClock::GetInstance();
};

INSTANTIATE_TEST_SUITE_P(VersionIncludeStreamDependencySequence,
                         QuicChromiumClientSessionTest,
                         ::testing::ValuesIn(AllSupportedQuicVersions()),
                         ::testing::PrintToStringParamName());

// Basic test of ProofVerifyDetailsChromium is converted to SSLInfo retrieved
// through QuicChromiumClientSession::GetSSLInfo(). Doesn't test some of the
// more complicated fields.
TEST_P(QuicChromiumClientSessionTest, GetSSLInfo1) {
  MockQuicData quic_data(version_);
  quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);

  Initialize();

  ProofVerifyDetailsChromium details;
  details.is_fatal_cert_error = false;
  details.cert_verify_result.verified_cert =
      ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
  details.cert_verify_result.is_issued_by_known_root = true;
  details.cert_verify_result.policy_compliance =
      ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS;

  CompleteCryptoHandshake();
  session_->OnProofVerifyDetailsAvailable(details);

  SSLInfo ssl_info;
  ASSERT_TRUE(session_->GetSSLInfo(&ssl_info));
  EXPECT_TRUE(ssl_info.is_valid());

  EXPECT_EQ(details.is_fatal_cert_error, ssl_info.is_fatal_cert_error);
  EXPECT_TRUE(ssl_info.cert->EqualsIncludingChain(
      details.cert_verify_result.verified_cert.get()));
  EXPECT_EQ(details.cert_verify_result.cert_status, ssl_info.cert_status);
  EXPECT_EQ(details.cert_verify_result.is_issued_by_known_root,
            ssl_info.is_issued_by_known_root);
  EXPECT_EQ(details.cert_verify_result.policy_compliance,
            ssl_info.ct_policy_compliance);
}

// Just like GetSSLInfo1, but uses different values.
TEST_P(QuicChromiumClientSessionTest, GetSSLInfo2) {
  MockQuicData quic_data(version_);
  quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);

  Initialize();

  ProofVerifyDetailsChromium details;
  details.is_fatal_cert_error = false;
  details.cert_verify_result.verified_cert =
      ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
  details.cert_verify_result.is_issued_by_known_root = false;
  details.cert_verify_result.policy_compliance =
      ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS;

  CompleteCryptoHandshake();
  session_->OnProofVerifyDetailsAvailable(details);

  SSLInfo ssl_info;
  ASSERT_TRUE(session_->GetSSLInfo(&ssl_info));
  EXPECT_TRUE(ssl_info.is_valid());

  EXPECT_EQ(details.is_fatal_cert_error, ssl_info.is_fatal_cert_error);
  EXPECT_TRUE(ssl_info.cert->EqualsIncludingChain(
      details.cert_verify_result.verified_cert.get()));
  EXPECT_EQ(details.cert_verify_result.cert_status, ssl_info.cert_status);
  EXPECT_EQ(details.cert_verify_result.is_issued_by_known_root,
            ssl_info.is_issued_by_known_root);
  EXPECT_EQ(details.cert_verify_result.policy_compliance,
            ssl_info.ct_policy_compliance);
}

TEST_P(QuicChromiumClientSessionTest, IsFatalErrorNotSetForNonFatalError) {
  MockQuicData quic_data(version_);
  quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);

  Initialize();

  SSLInfo ssl_info;
  ProofVerifyDetailsChromium details;
  details.cert_verify_result.verified_cert =
      ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
  details.cert_verify_result.cert_status = CERT_STATUS_DATE_INVALID;
  details.is_fatal_cert_error = false;
  CompleteCryptoHandshake();
  session_->OnProofVerifyDetailsAvailable(details);

  ASSERT_TRUE(session_->GetSSLInfo(&ssl_info));
  EXPECT_FALSE(ssl_info.is_fatal_cert_error);
}

TEST_P(QuicChromiumClientSessionTest, IsFatalErrorSetForFatalError) {
  MockQuicData quic_data(version_);
  quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);
  Initialize();

  SSLInfo ssl_info;
  ProofVerifyDetailsChromium details;
  details.cert_verify_result.verified_cert =
      ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
  details.cert_verify_result.cert_status = CERT_STATUS_DATE_INVALID;
  details.is_fatal_cert_error = true;
  CompleteCryptoHandshake();
  session_->OnProofVerifyDetailsAvailable(details);
  ASSERT_TRUE(session_->GetSSLInfo(&ssl_info));
  EXPECT_TRUE(ssl_info.is_fatal_cert_error);
}

TEST_P(QuicChromiumClientSessionTest, CryptoConnect) {
  MockQuicData quic_data(version_);
  quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);
  Initialize();
  CompleteCryptoHandshake();
}

TEST_P(QuicChromiumClientSessionTest, Handle) {
  MockQuicData quic_data(version_);
  quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);

  Initialize();

  NetLogWithSource session_net_log = session_->net_log();
  EXPECT_EQ(NetLogSourceType::QUIC_SESSION, session_net_log.source().type);
  EXPECT_EQ(NetLog::Get(), session_net_log.net_log());

  // Set Migration information to the session so that it will be propagated to
  // the handler on init.
  auto migration_info = ConnectionMigrationInformation(
      ConnectionMigrationInformation::NetworkEventCount(
          /*default_network_change=*/1, /*network_disconnected=*/1,
          /*network_connected=*/1, /*path_degrading=*/1));
  session_->SetConnectionMigrationInformationForTesting(migration_info);

  std::unique_ptr<QuicChromiumClientSession::Handle> handle =
      session_->CreateHandle(destination_);
  EXPECT_TRUE(handle->IsConnected());
  EXPECT_FALSE(handle->OneRttKeysAvailable());
  EXPECT_EQ(version_, handle->GetQuicVersion());
  EXPECT_EQ(session_key_.server_id(), handle->server_id());
  EXPECT_EQ(session_net_log.source().type, handle->net_log().source().type);
  EXPECT_EQ(session_net_log.source().id, handle->net_log().source().id);
  EXPECT_EQ(session_net_log.net_log(), handle->net_log().net_log());
  IPEndPoint address;
  EXPECT_EQ(OK, handle->GetPeerAddress(&address));
  EXPECT_EQ(kIpEndPoint, address);
  EXPECT_TRUE(handle->CreatePacketBundler().get() != nullptr);

  auto base_migration_info = migration_info;
  EXPECT_EQ(handle->GetConnectionMigrationInfoSinceInit(),
            migration_info - base_migration_info);

  migration_info.event_count.network_connected_num++;
  session_->OnNetworkConnected(kDefaultNetworkForTests);
  EXPECT_EQ(handle->GetConnectionMigrationInfoSinceInit(),
            migration_info - base_migration_info);

  CompleteCryptoHandshake();

  EXPECT_TRUE(handle->OneRttKeysAvailable());

  // Request a stream and verify that a stream was created.
  TestCompletionCallback callback;
  ASSERT_EQ(OK, handle->RequestStream(/*requires_confirmation=*/false,
                                      callback.callback(),
                                      TRAFFIC_ANNOTATION_FOR_TESTS));
  EXPECT_TRUE(handle->ReleaseStream() != nullptr);

  EXPECT_CALL(*session_, UnregisterQuicConnectionClosePayload());
  quic_data.Resume();
  EXPECT_TRUE(quic_data.AllReadDataConsumed());
  EXPECT_TRUE(quic_data.AllWriteDataConsumed());

  // Veirfy that the handle works correctly after the session is closed.
  EXPECT_FALSE(handle->IsConnected());
  EXPECT_TRUE(handle->OneRttKeysAvailable());
  EXPECT_EQ(version_, handle->GetQuicVersion());
  EXPECT_EQ(session_key_.server_id(), handle->server_id());
  EXPECT_EQ(session_net_log.source().type, handle->net_log().source().type);
  EXPECT_EQ(session_net_log.source().id, handle->net_log().source().id);
  EXPECT_EQ(session_net_log.net_log(), handle->net_log().net_log());
  EXPECT_EQ(ERR_CONNECTION_CLOSED, handle->GetPeerAddress(&address));
  EXPECT_TRUE(handle->CreatePacketBundler().get() == nullptr);
  EXPECT_EQ(handle->GetConnectionMigrationInfoSinceInit(),
            migration_info - base_migration_info);
  {
    // Verify that CreateHandle() works even after the session is closed.
    std::unique_ptr<QuicChromiumClientSession::Handle> handle2 =
        session_->CreateHandle(destination_);
    EXPECT_FALSE(handle2->IsConnected());
    EXPECT_TRUE(handle2->OneRttKeysAvailable());
    ASSERT_EQ(ERR_CONNECTION_CLOSED,
              handle2->RequestStream(/*requires_confirmation=*/false,
                                     callback.callback(),
                                     TRAFFIC_ANNOTATION_FOR_TESTS));
  }

  session_.reset();

  // Verify that the handle works correctly after the session is deleted.
  EXPECT_FALSE(handle->IsConnected());
  EXPECT_TRUE(handle->OneRttKeysAvailable());
  EXPECT_EQ(version_, handle->GetQuicVersion());
  EXPECT_EQ(session_key_.server_id(), handle->server_id());
  EXPECT_EQ(session_net_log.source().type, handle->net_log().source().type);
  EXPECT_EQ(session_net_log.source().id, handle->net_log().source().id);
  EXPECT_EQ(session_net_log.net_log(), handle->net_log().net_log());
  EXPECT_EQ(ERR_CONNECTION_CLOSED, handle->GetPeerAddress(&address));
  EXPECT_TRUE(handle->CreatePacketBundler().get() == nullptr);
  ASSERT_EQ(
      ERR_CONNECTION_CLOSED,
      handle->RequestStream(/*requires_confirmation=*/false,
                            callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS));
  EXPECT_EQ(handle->GetConnectionMigrationInfoSinceInit(),
            migration_info - base_migration_info);
}

TEST_P(QuicChromiumClientSessionTest, StreamRequest) {
  MockQuicData quic_data(version_);
  quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);

  Initialize();
  CompleteCryptoHandshake();

  // Request a stream and verify that a stream was created.
  std::unique_ptr<QuicChromiumClientSession::Handle> handle =
      session_->CreateHandle(destination_);
  TestCompletionCallback callback;
  ASSERT_EQ(OK, handle->RequestStream(/*requires_confirmation=*/false,
                                      callback.callback(),
                                      TRAFFIC_ANNOTATION_FOR_TESTS));
  EXPECT_TRUE(handle->ReleaseStream() != nullptr);

  EXPECT_CALL(*session_, UnregisterQuicConnectionClosePayload());
  quic_data.Resume();
  EXPECT_TRUE(quic_data.AllReadDataConsumed());
  EXPECT_TRUE(quic_data.AllWriteDataConsumed());
}

TEST_P(QuicChromiumClientSessionTest, ConfirmationRequiredStreamRequest) {
  MockQuicData quic_data(version_);
  quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);

  Initialize();
  CompleteCryptoHandshake();

  // Request a stream and verify that a stream was created.
  std::unique_ptr<QuicChromiumClientSession::Handle> handle =
      session_->CreateHandle(destination_);
  TestCompletionCallback callback;
  ASSERT_EQ(OK, handle->RequestStream(/*requires_confirmation=*/true,
                                      callback.callback(),
                                      TRAFFIC_ANNOTATION_FOR_TESTS));
  EXPECT_TRUE(handle->ReleaseStream() != nullptr);

  EXPECT_CALL(*session_, UnregisterQuicConnectionClosePayload());
  quic_data.Resume();
  EXPECT_TRUE(quic_data.AllReadDataConsumed());
  EXPECT_TRUE(quic_data.AllWriteDataConsumed());
}

TEST_P(QuicChromiumClientSessionTest, StreamRequestBeforeConfirmation) {
  MockQuicData quic_data(version_);
  quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);

  Initialize();

  // Request a stream and verify that a stream was created.
  std::unique_ptr<QuicChromiumClientSession::Handle> handle =
      session_->CreateHandle(destination_);
  TestCompletionCallback callback;
  ASSERT_EQ(
      ERR_IO_PENDING,
      handle->RequestStream(/*requires_confirmation=*/true, callback.callback(),
                            TRAFFIC_ANNOTATION_FOR_TESTS));

  CompleteCryptoHandshake();

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

  EXPECT_TRUE(handle->ReleaseStream() != nullptr);

  EXPECT_CALL(*session_, UnregisterQuicConnectionClosePayload());
  quic_data.Resume();
  EXPECT_TRUE(quic_data.AllReadDataConsumed());
  EXPECT_TRUE(quic_data.AllWriteDataConsumed());
}

TEST_P(QuicChromiumClientSessionTest, CancelStreamRequestBeforeRelease) {
  MockQuicData quic_data(version_);
  int packet_num = 1;
  quic_data.AddWrite(SYNCHRONOUS,
                     client_maker_.MakeInitialSettingsPacket(packet_num++));
  quic_data.AddWrite(
      SYNCHRONOUS,
      client_maker_.Packet(packet_num++)
          .AddStopSendingFrame(GetNthClientInitiatedBidirectionalStreamId(0),
                               quic::QUIC_STREAM_CANCELLED)
          .AddRstStreamFrame(GetNthClientInitiatedBidirectionalStreamId(0),
                             quic::QUIC_STREAM_CANCELLED)
          .Build());
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);

  Initialize();
  CompleteCryptoHandshake();

  // Request a stream and cancel it without releasing the stream.
  std::unique_ptr<QuicChromiumClientSession::Handle> handle =
      session_->CreateHandle(destination_);
  TestCompletionCallback callback;
  ASSERT_EQ(OK, handle->RequestStream(/*requires_confirmation=*/false,
                                      callback.callback(),
                                      TRAFFIC_ANNOTATION_FOR_TESTS));
  handle.reset();

  EXPECT_CALL(*session_, UnregisterQuicConnectionClosePayload());
  quic_data.Resume();
  EXPECT_TRUE(quic_data.AllReadDataConsumed());
  EXPECT_TRUE(quic_data.AllWriteDataConsumed());
}

TEST_P(QuicChromiumClientSessionTest, AsyncStreamRequest) {
  MockQuicData quic_data(version_);
  uint64_t packet_num = 1;
  quic_data.AddWrite(SYNCHRONOUS,
                     client_maker_.MakeInitialSettingsPacket(packet_num++));
  // The open stream limit is set to 50 by
  // MockCryptoClientStream::SetConfigNegotiated() so when the 51st stream is
  // requested, a STREAMS_BLOCKED will be sent, indicating that it's blocked
  // at the limit of 50.
  quic_data.AddWrite(
      SYNCHRONOUS,
      client_maker_.Packet(packet_num++)
          .AddStreamsBlockedFrame(/*control_frame_id=*/1, /*stream_count=*/50,
                                  /*unidirectional=*/false)
          .Build());
  // Similarly, requesting the 52nd stream will also send a STREAMS_BLOCKED.
  quic_data.AddWrite(
      SYNCHRONOUS,
      client_maker_.Packet(packet_num++)
          .AddStreamsBlockedFrame(/*control_frame_id=*/1, /*stream_count=*/50,
                                  /*unidirectional=*/false)
          .Build());
  quic_data.AddWrite(
      SYNCHRONOUS,
      client_maker_.Packet(packet_num++)
          .AddRstStreamFrame(GetNthClientInitiatedBidirectionalStreamId(0),
                             quic::QUIC_STREAM_CANCELLED)
          .Build());
  quic_data.AddWrite(
      SYNCHRONOUS,
      client_maker_.Packet(packet_num++)
          .AddRstStreamFrame(GetNthClientInitiatedBidirectionalStreamId(1),
                             quic::QUIC_STREAM_CANCELLED)
          .Build());
  // After the STREAMS_BLOCKED is sent, receive a MAX_STREAMS to increase
  // the limit to 100.
  quic_data.AddRead(ASYNC, server_maker_.Packet(1)
                               .AddMaxStreamsFrame(/*control_frame_id=*/1,
                                                   /*stream_count=*/100,
                                                   /*unidirectional=*/false)
                               .Build());
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);
  Initialize();
  CompleteCryptoHandshake();

  // Open the maximum number of streams so that subsequent requests cannot
  // proceed immediately.
  EXPECT_EQ(GetMaxAllowedOutgoingBidirectionalStreams(), 50u);
  for (size_t i = 0; i < 50; i++) {
    QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get());
  }
  EXPECT_EQ(session_->GetNumActiveStreams(), 50u);

  // Request a stream and verify that it's pending.
  std::unique_ptr<QuicChromiumClientSession::Handle> handle =
      session_->CreateHandle(destination_);
  TestCompletionCallback callback;
  ASSERT_EQ(
      ERR_IO_PENDING,
      handle->RequestStream(/*requires_confirmation=*/false,
                            callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS));
  // Request a second stream and verify that it's also pending.
  std::unique_ptr<QuicChromiumClientSession::Handle> handle2 =
      session_->CreateHandle(destination_);
  TestCompletionCallback callback2;
  ASSERT_EQ(ERR_IO_PENDING,
            handle2->RequestStream(/*requires_confirmation=*/false,
                                   callback2.callback(),
                                   TRAFFIC_ANNOTATION_FOR_TESTS));

  // Close two stream to open up sending credits.
  quic::QuicRstStreamFrame rst(quic::kInvalidControlFrameId,
                               GetNthClientInitiatedBidirectionalStreamId(0),
                               quic::QUIC_STREAM_CANCELLED, 0);
  session_->OnRstStream(rst);
  quic::QuicRstStreamFrame rst2(quic::kInvalidControlFrameId,
                                GetNthClientInitiatedBidirectionalStreamId(1),
                                quic::QUIC_STREAM_CANCELLED, 0);
  session_->OnRstStream(rst2);
  // To close the streams completely, we need to also receive STOP_SENDING
  // frames.
  quic::QuicStopSendingFrame stop_sending(
      quic::kInvalidControlFrameId,
      GetNthClientInitiatedBidirectionalStreamId(0),
      quic::QUIC_STREAM_CANCELLED);
  session_->OnStopSendingFrame(stop_sending);
  quic::QuicStopSendingFrame stop_sending2(
      quic::kInvalidControlFrameId,
      GetNthClientInitiatedBidirectionalStreamId(1),
      quic::QUIC_STREAM_CANCELLED);
  session_->OnStopSendingFrame(stop_sending2);

  EXPECT_FALSE(callback.have_result());
  EXPECT_FALSE(callback2.have_result());

  // Pump the message loop to read the packet containing the MAX_STREAMS frame.
  base::RunLoop().RunUntilIdle();

  // Make sure that both requests were unblocked.
  ASSERT_TRUE(callback.have_result());
  EXPECT_THAT(callback.WaitForResult(), IsOk());
  EXPECT_TRUE(handle->ReleaseStream() != nullptr);
  ASSERT_TRUE(callback2.have_result());
  EXPECT_THAT(callback2.WaitForResult(), IsOk());
  EXPECT_TRUE(handle2->ReleaseStream() != nullptr);

  EXPECT_CALL(*session_, UnregisterQuicConnectionClosePayload());
  quic_data.Resume();
  EXPECT_TRUE(quic_data.AllReadDataConsumed());
  EXPECT_TRUE(quic_data.AllWriteDataConsumed());
}

// Regression test for https://crbug.com/1021938.
// When the connection is closed, there may be tasks queued in the message loop
// to read the last packet, reading that packet should not crash.
TEST_P(QuicChromiumClientSessionTest, ReadAfterConnectionClose) {
  MockQuicData quic_data(version_);
  quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
  // The open stream limit is set to 50 by
  // MockCryptoClientStream::SetConfigNegotiated() so when the 51st stream is
  // requested, a STREAMS_BLOCKED will be sent, indicating that it's blocked
  // at the limit of 50.
  quic_data.AddWrite(
      SYNCHRONOUS,
      client_maker_.Packet(2)
          .AddStreamsBlockedFrame(/*control_frame_id=*/1, /*stream_count=*/50,
                                  /*unidirectional=*/false)
          .Build());
  quic_data.AddWrite(
      SYNCHRONOUS,
      client_maker_.Packet(3)
          .AddStreamsBlockedFrame(/*control_frame_id=*/1, /*stream_count=*/50,
                                  /*unidirectional=*/false)
          .Build());
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  // This packet will be read after connection is closed.
  quic_data.AddRead(
      ASYNC, server_maker_.Packet(1)
                 .AddConnectionCloseFrame(
                     quic::QUIC_CRYPTO_VERSION_NOT_SUPPORTED, "Time to panic!")
                 .Build());
  quic_data.AddSocketDataToFactory(&socket_factory_);

  Initialize();
  CompleteCryptoHandshake();

  // Open the maximum number of streams so that a subsequent request
  // can not proceed immediately.
  const size_t kMaxOpenStreams = GetMaxAllowedOutgoingBidirectionalStreams();
  for (size_t i = 0; i < kMaxOpenStreams; i++) {
    QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get());
  }
  EXPECT_EQ(kMaxOpenStreams, session_->GetNumActiveStreams());

  // Request two streams which will both be pending.
  // In V99 each will generate a max stream id for each attempt.
  std::unique_ptr<QuicChromiumClientSession::Handle> handle =
      session_->CreateHandle(destination_);
  std::unique_ptr<QuicChromiumClientSession::Handle> handle2 =
      session_->CreateHandle(destination_);

  ASSERT_EQ(
      ERR_IO_PENDING,
      handle->RequestStream(
          /*requires_confirmation=*/false,
          base::BindOnce(&QuicChromiumClientSessionTest::ResetHandleOnError,
                         base::Unretained(this), &handle2),
          TRAFFIC_ANNOTATION_FOR_TESTS));

  TestCompletionCallback callback2;
  ASSERT_EQ(ERR_IO_PENDING,
            handle2->RequestStream(/*requires_confirmation=*/false,
                                   callback2.callback(),
                                   TRAFFIC_ANNOTATION_FOR_TESTS));

  EXPECT_CALL(*session_, UnregisterQuicConnectionClosePayload());
  session_->connection()->CloseConnection(
      quic::QUIC_NETWORK_IDLE_TIMEOUT, "Timed out",
      quic::ConnectionCloseBehavior::SILENT_CLOSE);

  // Pump the message loop to read the connection close packet.
  base::RunLoop().RunUntilIdle();
  EXPECT_FALSE(handle2.get());
  quic_data.Resume();
  EXPECT_TRUE(quic_data.AllReadDataConsumed());
  EXPECT_TRUE(quic_data.AllWriteDataConsumed());
}

TEST_P(QuicChromiumClientSessionTest, ClosedWithAsyncStreamRequest) {
  MockQuicData quic_data(version_);
  quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
  // The open stream limit is set to 50 by
  // MockCryptoClientStream::SetConfigNegotiated() so when the 51st stream is
  // requested, a STREAMS_BLOCKED will be sent, indicating that it's blocked
  // at the limit of 50.
  quic_data.AddWrite(
      SYNCHRONOUS,
      client_maker_.Packet(2)
          .AddStreamsBlockedFrame(/*control_frame_id=*/1, /*stream_count=*/50,
                                  /*unidirectional=*/false)
          .Build());
  quic_data.AddWrite(
      SYNCHRONOUS,
      client_maker_.Packet(3)
          .AddStreamsBlockedFrame(/*control_frame_id=*/1, /*stream_count=*/50,
                                  /*unidirectional=*/false)
          .Build());
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);

  Initialize();
  CompleteCryptoHandshake();

  // Open the maximum number of streams so that a subsequent request
  // can not proceed immediately.
  const size_t kMaxOpenStreams = GetMaxAllowedOutgoingBidirectionalStreams();
  for (size_t i = 0; i < kMaxOpenStreams; i++) {
    QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get());
  }
  EXPECT_EQ(kMaxOpenStreams, session_->GetNumActiveStreams());

  // Request two streams which will both be pending.
  // In V99 each will generate a max stream id for each attempt.
  std::unique_ptr<QuicChromiumClientSession::Handle> handle =
      session_->CreateHandle(destination_);
  std::unique_ptr<QuicChromiumClientSession::Handle> handle2 =
      session_->CreateHandle(destination_);

  ASSERT_EQ(
      ERR_IO_PENDING,
      handle->RequestStream(
          /*requires_confirmation=*/false,
          base::BindOnce(&QuicChromiumClientSessionTest::ResetHandleOnError,
                         base::Unretained(this), &handle2),
          TRAFFIC_ANNOTATION_FOR_TESTS));

  TestCompletionCallback callback2;
  ASSERT_EQ(ERR_IO_PENDING,
            handle2->RequestStream(/*requires_confirmation=*/false,
                                   callback2.callback(),
                                   TRAFFIC_ANNOTATION_FOR_TESTS));

  EXPECT_CALL(*session_, UnregisterQuicConnectionClosePayload);
  session_->connection()->CloseConnection(
      quic::QUIC_NETWORK_IDLE_TIMEOUT, "Timed out",
      quic::ConnectionCloseBehavior::SILENT_CLOSE);

  // Pump the message loop to read the connection close packet.
  base::RunLoop().RunUntilIdle();
  EXPECT_FALSE(handle2.get());
  quic_data.Resume();
  EXPECT_TRUE(quic_data.AllReadDataConsumed());
  EXPECT_TRUE(quic_data.AllWriteDataConsumed());
}

// Tests that the stream wait time is correctly calculated when the
// MAX_STREAMS frame increases the limit.
TEST_P(QuicChromiumClientSessionTest, PendingStreamWaitTime) {
  constexpr size_t kMaxStreams = 50;
  constexpr size_t kIncreasedMaxStreams = 100;
  constexpr base::TimeDelta kPendingWaitTime = base::Milliseconds(50);

  test_tick_clock_.Advance(base::Milliseconds(5));
  tick_clock_ = &test_tick_clock_;
  MockQuicData quic_data(version_);
  uint64_t packet_num = 1;
  quic_data.AddWrite(SYNCHRONOUS,
                     client_maker_.MakeInitialSettingsPacket(packet_num++));
  // The open stream limit is set to kMaxStreams, so when the 51st stream is
  // requested, a STREAMS_BLOCKED will be sent.
  quic_data.AddWrite(SYNCHRONOUS,
                     client_maker_.Packet(packet_num++)
                         .AddStreamsBlockedFrame(/*control_frame_id=*/1,
                                                 /*stream_count=*/kMaxStreams,
                                                 /*unidirectional=*/false)
                         .Build());
  // This node receives the RST_STREAM+STOP_SENDING.
  quic_data.AddWrite(
      SYNCHRONOUS,
      client_maker_.Packet(packet_num++)
          .AddRstStreamFrame(GetNthClientInitiatedBidirectionalStreamId(0),
                             quic::QUIC_STREAM_CANCELLED)
          .Build());
  // After the STREAMS_BLOCKED is sent, receive a MAX_STREAMS to increase
  // the limit to kIncreasedMaxStreams.
  quic_data.AddRead(
      ASYNC, server_maker_.Packet(1)
                 .AddMaxStreamsFrame(/*control_frame_id=*/1,
                                     /*stream_count=*/kIncreasedMaxStreams,
                                     /*unidirectional=*/false)
                 .Build());
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);
  Initialize();
  CompleteCryptoHandshake();

  // Open the maximum number of streams so that subsequent requests cannot
  // proceed immediately.
  EXPECT_EQ(GetMaxAllowedOutgoingBidirectionalStreams(), kMaxStreams);
  for (size_t i = 0; i < kMaxStreams; i++) {
    QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get());
  }
  EXPECT_EQ(session_->GetNumActiveStreams(), kMaxStreams);

  // Request a stream and verify that it's pending.
  std::unique_ptr<QuicChromiumClientSession::Handle> handle =
      session_->CreateHandle(destination_);
  TestCompletionCallback callback;
  ASSERT_EQ(
      ERR_IO_PENDING,
      handle->RequestStream(/*requires_confirmation=*/false,
                            callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS));

  // Advance the clock by kPendingWaitTime to simulate wait time.
  test_tick_clock_.Advance(kPendingWaitTime);

  quic::QuicRstStreamFrame rst(quic::kInvalidControlFrameId,
                               GetNthClientInitiatedBidirectionalStreamId(0),
                               quic::QUIC_STREAM_CANCELLED, 0);
  session_->OnRstStream(rst);
  quic::QuicStopSendingFrame stop_sending(
      quic::kInvalidControlFrameId,
      GetNthClientInitiatedBidirectionalStreamId(0),
      quic::QUIC_STREAM_CANCELLED);
  session_->OnStopSendingFrame(stop_sending);

  EXPECT_FALSE(callback.have_result());

  // Wait for the request to be unblocked.
  EXPECT_THAT(callback.WaitForResult(), IsOk());
  std::unique_ptr<QuicChromiumClientStream::Handle> stream =
      handle->ReleaseStream();
  ASSERT_TRUE(stream);
  EXPECT_EQ(kPendingWaitTime, stream->max_stream_limit_pending_delay());

  EXPECT_CALL(*session_, UnregisterQuicConnectionClosePayload());
  quic_data.Resume();
  EXPECT_TRUE(quic_data.AllReadDataConsumed());
  EXPECT_TRUE(quic_data.AllWriteDataConsumed());
}

TEST_P(QuicChromiumClientSessionTest, CancelPendingStreamRequest) {
  MockQuicData quic_data(version_);
  quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
  // The open stream limit is set to 50 by
  // MockCryptoClientStream::SetConfigNegotiated() so when the 51st stream is
  // requested, a STREAMS_BLOCKED will be sent.
  quic_data.AddWrite(
      SYNCHRONOUS,
      client_maker_.Packet(2)
          .AddStreamsBlockedFrame(/*control_frame_id=*/1, /*stream_count=*/50,
                                  /*unidirectional=*/false)
          .Build());
  // This node receives the RST_STREAM+STOP_SENDING, it responds
  // with only a RST_STREAM.
  quic_data.AddWrite(
      SYNCHRONOUS,
      client_maker_.Packet(3)
          .AddRstStreamFrame(GetNthClientInitiatedBidirectionalStreamId(0),
                             quic::QUIC_STREAM_CANCELLED)
          .Build());
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);

  Initialize();
  CompleteCryptoHandshake();

  // Open the maximum number of streams so that a subsequent request
  // can not proceed immediately.
  const size_t kMaxOpenStreams = GetMaxAllowedOutgoingBidirectionalStreams();
  for (size_t i = 0; i < kMaxOpenStreams; i++) {
    QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get());
  }
  EXPECT_EQ(kMaxOpenStreams, session_->GetNumActiveStreams());

  // Request a stream and verify that it's pending.
  std::unique_ptr<QuicChromiumClientSession::Handle> handle =
      session_->CreateHandle(destination_);
  TestCompletionCallback callback;
  ASSERT_EQ(
      ERR_IO_PENDING,
      handle->RequestStream(/*requires_confirmation=*/false,
                            callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS));

  // Cancel the pending stream request.
  handle.reset();

  // Close a stream and ensure that no new stream is created.
  quic::QuicRstStreamFrame rst(quic::kInvalidControlFrameId,
                               GetNthClientInitiatedBidirectionalStreamId(0),
                               quic::QUIC_STREAM_CANCELLED, 0);
  session_->OnRstStream(rst);
  // We require a STOP_SENDING as well as a RESET_STREAM to fully close the
  // stream.
  quic::QuicStopSendingFrame stop_sending(
      quic::kInvalidControlFrameId,
      GetNthClientInitiatedBidirectionalStreamId(0),
      quic::QUIC_STREAM_CANCELLED);
  session_->OnStopSendingFrame(stop_sending);
  EXPECT_EQ(kMaxOpenStreams - 1, session_->GetNumActiveStreams());

  EXPECT_CALL(*session_, UnregisterQuicConnectionClosePayload);
  quic_data.Resume();
  EXPECT_TRUE(quic_data.AllReadDataConsumed());
  EXPECT_TRUE(quic_data.AllWriteDataConsumed());
}

TEST_P(QuicChromiumClientSessionTest, ConnectionCloseBeforeStreamRequest) {
  MockQuicData quic_data(version_);
  int packet_num = 1;
  quic_data.AddWrite(SYNCHRONOUS,
                     client_maker_.MakeInitialSettingsPacket(packet_num++));
  quic_data.AddWrite(SYNCHRONOUS,
                     client_maker_.Packet(packet_num++).AddPingFrame().Build());
  quic_data.AddRead(
      ASYNC, server_maker_.Packet(1)
                 .AddConnectionCloseFrame(
                     quic::QUIC_CRYPTO_VERSION_NOT_SUPPORTED, "Time to panic!")
                 .Build());

  quic_data.AddSocketDataToFactory(&socket_factory_);

  Initialize();
  CompleteCryptoHandshake();

  // Send a ping so that client has outgoing traffic before receiving packets.
  session_->connection()->SendPing();

  // Pump the message loop to read the connection close packet.
  EXPECT_CALL(*session_, UnregisterQuicConnectionClosePayload);
  base::RunLoop().RunUntilIdle();

  // Request a stream and verify that it failed.
  std::unique_ptr<QuicChromiumClientSession::Handle> handle =
      session_->CreateHandle(destination_);
  TestCompletionCallback callback;
  ASSERT_EQ(
      ERR_CONNECTION_CLOSED,
      handle->RequestStream(/*requires_confirmation=*/false,
                            callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS));

  EXPECT_TRUE(quic_data.AllReadDataConsumed());
  EXPECT_TRUE(quic_data.AllWriteDataConsumed());
}

TEST_P(QuicChromiumClientSessionTest, ConnectionCloseBeforeHandshakeConfirmed) {
  // Force the connection close packet to use long headers with connection ID.
  server_maker_.SetEncryptionLevel(quic::ENCRYPTION_INITIAL);

  MockQuicData quic_data(version_);
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(
      ASYNC, server_maker_.Packet(1)
                 .AddConnectionCloseFrame(
                     quic::QUIC_CRYPTO_VERSION_NOT_SUPPORTED, "Time to panic!")
                 .Build());
  quic_data.AddSocketDataToFactory(&socket_factory_);

  Initialize();

  // Request a stream and verify that it's pending.
  std::unique_ptr<QuicChromiumClientSession::Handle> handle =
      session_->CreateHandle(destination_);
  TestCompletionCallback callback;
  ASSERT_EQ(
      ERR_IO_PENDING,
      handle->RequestStream(/*requires_confirmation=*/true, callback.callback(),
                            TRAFFIC_ANNOTATION_FOR_TESTS));

  // Close the connection and verify that the StreamRequest completes with
  // an error.
  quic_data.Resume();
  base::RunLoop().RunUntilIdle();

  EXPECT_THAT(callback.WaitForResult(), IsError(ERR_CONNECTION_CLOSED));

  EXPECT_TRUE(quic_data.AllReadDataConsumed());
  EXPECT_TRUE(quic_data.AllWriteDataConsumed());
}

TEST_P(QuicChromiumClientSessionTest, ConnectionCloseWithPendingStreamRequest) {
  MockQuicData quic_data(version_);
  int packet_num = 1;
  quic_data.AddWrite(SYNCHRONOUS,
                     client_maker_.MakeInitialSettingsPacket(packet_num++));
  quic_data.AddWrite(SYNCHRONOUS,
                     client_maker_.Packet(packet_num++).AddPingFrame().Build());
  quic_data.AddWrite(
      SYNCHRONOUS,
      client_maker_.Packet(packet_num++)
          .AddStreamsBlockedFrame(/*control_frame_id=*/1, /*stream_count=*/50,
                                  /*unidirectional=*/false)
          .Build());
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(
      ASYNC, server_maker_.Packet(1)
                 .AddConnectionCloseFrame(
                     quic::QUIC_CRYPTO_VERSION_NOT_SUPPORTED, "Time to panic!")
                 .Build());
  quic_data.AddSocketDataToFactory(&socket_factory_);

  Initialize();
  CompleteCryptoHandshake();

  // Send a ping so that client has outgoing traffic before receiving packets.
  session_->connection()->SendPing();

  // Open the maximum number of streams so that a subsequent request
  // can not proceed immediately.
  const size_t kMaxOpenStreams = GetMaxAllowedOutgoingBidirectionalStreams();
  for (size_t i = 0; i < kMaxOpenStreams; i++) {
    QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get());
  }
  EXPECT_EQ(kMaxOpenStreams, session_->GetNumActiveStreams());

  // Request a stream and verify that it's pending.
  std::unique_ptr<QuicChromiumClientSession::Handle> handle =
      session_->CreateHandle(destination_);
  TestCompletionCallback callback;
  ASSERT_EQ(
      ERR_IO_PENDING,
      handle->RequestStream(/*requires_confirmation=*/false,
                            callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS));

  // Close the connection and verify that the StreamRequest completes with
  // an error.
  EXPECT_CALL(*session_, UnregisterQuicConnectionClosePayload());
  quic_data.Resume();
  base::RunLoop().RunUntilIdle();

  EXPECT_THAT(callback.WaitForResult(), IsError(ERR_CONNECTION_CLOSED));

  EXPECT_TRUE(quic_data.AllReadDataConsumed());
  EXPECT_TRUE(quic_data.AllWriteDataConsumed());
}

TEST_P(QuicChromiumClientSessionTest, MaxNumStreams) {
  MockQuicData quic_data(version_);
  quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
  // Initial configuration is 50 dynamic streams. Taking into account
  // the static stream (headers), expect to block on when hitting the limit
  // of 50 streams
  quic_data.AddWrite(
      SYNCHRONOUS,
      client_maker_.Packet(2)
          .AddStreamsBlockedFrame(/*control_frame_id=*/1, /*stream_count=*/50,
                                  /*unidirectional=*/false)
          .Build());
  quic_data.AddWrite(
      SYNCHRONOUS,
      client_maker_.Packet(3)
          .AddStopSendingFrame(GetNthClientInitiatedBidirectionalStreamId(0),
                               quic::QUIC_RST_ACKNOWLEDGEMENT)
          .AddRstStreamFrame(GetNthClientInitiatedBidirectionalStreamId(0),
                             quic::QUIC_RST_ACKNOWLEDGEMENT)
          .Build());
  // For the second CreateOutgoingStream that fails because of hitting the
  // stream count limit.
  quic_data.AddWrite(
      SYNCHRONOUS,
      client_maker_.Packet(4)
          .AddStreamsBlockedFrame(/*control_frame_id=*/1, /*stream_count=*/50,
                                  /*unidirectional=*/false)
          .Build());
  quic_data.AddRead(ASYNC, server_maker_.Packet(1)
                               .AddMaxStreamsFrame(/*control_frame_id=*/1,
                                                   /*stream_count=*/50 + 2,
                                                   /*unidirectional=*/false)
                               .Build());
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);

  Initialize();
  CompleteCryptoHandshake();
  const size_t kMaxOpenStreams = GetMaxAllowedOutgoingBidirectionalStreams();

  std::vector<QuicChromiumClientStream*> streams;
  for (size_t i = 0; i < kMaxOpenStreams; i++) {
    QuicChromiumClientStream* stream =
        QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get());
    EXPECT_TRUE(stream);
    streams.push_back(stream);
  }
  // This stream, the 51st dynamic stream, can not be opened.
  EXPECT_FALSE(
      QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get()));

  EXPECT_EQ(kMaxOpenStreams, session_->GetNumActiveStreams());

  // Close a stream and ensure I can now open a new one.
  quic::QuicStreamId stream_id = streams[0]->id();
  session_->ResetStream(stream_id, quic::QUIC_RST_ACKNOWLEDGEMENT);

  // Pump data, bringing in the max-stream-id
  base::RunLoop().RunUntilIdle();

  EXPECT_FALSE(
      QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get()));
  quic::QuicRstStreamFrame rst1(quic::kInvalidControlFrameId, stream_id,
                                quic::QUIC_STREAM_NO_ERROR, 0);
  session_->OnRstStream(rst1);
  EXPECT_EQ(kMaxOpenStreams - 1, session_->GetNumActiveStreams());
  base::RunLoop().RunUntilIdle();
  EXPECT_TRUE(
      QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get()));
}

// Regression test for crbug.com/968621.
TEST_P(QuicChromiumClientSessionTest, PendingStreamOnRst) {
  MockQuicData quic_data(version_);
  int packet_num = 1;
  quic_data.AddWrite(ASYNC,
                     client_maker_.MakeInitialSettingsPacket(packet_num++));
  quic_data.AddWrite(
      ASYNC,
      client_maker_.Packet(packet_num++)
          .AddStopSendingFrame(GetNthServerInitiatedUnidirectionalStreamId(0),
                               quic::QUIC_RST_ACKNOWLEDGEMENT)
          .Build());
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);

  Initialize();
  CompleteCryptoHandshake();

  quic::QuicStreamFrame data(GetNthServerInitiatedUnidirectionalStreamId(0),
                             false, 1, std::string_view("SP"));
  session_->OnStreamFrame(data);
  EXPECT_EQ(0u, session_->GetNumActiveStreams());
  quic::QuicRstStreamFrame rst(quic::kInvalidControlFrameId,
                               GetNthServerInitiatedUnidirectionalStreamId(0),
                               quic::QUIC_STREAM_CANCELLED, 0);
  session_->OnRstStream(rst);
}

// Regression test for crbug.com/971361.
TEST_P(QuicChromiumClientSessionTest, ClosePendingStream) {
  MockQuicData quic_data(version_);
  int packet_num = 1;
  quic_data.AddWrite(ASYNC,
                     client_maker_.MakeInitialSettingsPacket(packet_num++));
  quic_data.AddWrite(
      ASYNC,
      client_maker_.Packet(packet_num++)
          .AddStopSendingFrame(GetNthServerInitiatedUnidirectionalStreamId(0),
                               quic::QUIC_RST_ACKNOWLEDGEMENT)
          .Build());
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);

  Initialize();
  CompleteCryptoHandshake();

  quic::QuicStreamId id = GetNthServerInitiatedUnidirectionalStreamId(0);
  quic::QuicStreamFrame data(id, false, 1, std::string_view("SP"));
  session_->OnStreamFrame(data);
  EXPECT_EQ(0u, session_->GetNumActiveStreams());
  session_->ResetStream(id, quic::QUIC_STREAM_NO_ERROR);
}

TEST_P(QuicChromiumClientSessionTest, MaxNumStreamsViaRequest) {
  MockQuicData quic_data(version_);
  quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
  quic_data.AddWrite(
      SYNCHRONOUS,
      client_maker_.Packet(2)
          .AddStreamsBlockedFrame(/*control_frame_id=*/1, /*stream_count=*/50,
                                  /*unidirectional=*/false)
          .Build());
  quic_data.AddWrite(
      SYNCHRONOUS,
      client_maker_.Packet(3)
          .AddStopSendingFrame(GetNthClientInitiatedBidirectionalStreamId(0),
                               quic::QUIC_RST_ACKNOWLEDGEMENT)
          .AddRstStreamFrame(GetNthClientInitiatedBidirectionalStreamId(0),
                             quic::QUIC_RST_ACKNOWLEDGEMENT)
          .Build());
  quic_data.AddRead(ASYNC, server_maker_.Packet(1)
                               .AddMaxStreamsFrame(/*control_frame_id=*/1,
                                                   /*stream_count=*/52,
                                                   /*unidirectional=*/false)
                               .Build());
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);

  Initialize();
  CompleteCryptoHandshake();
  const size_t kMaxOpenStreams = GetMaxAllowedOutgoingBidirectionalStreams();
  std::vector<QuicChromiumClientStream*> streams;
  for (size_t i = 0; i < kMaxOpenStreams; i++) {
    QuicChromiumClientStream* stream =
        QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get());
    EXPECT_TRUE(stream);
    streams.push_back(stream);
  }

  std::unique_ptr<QuicChromiumClientSession::Handle> handle =
      session_->CreateHandle(destination_);
  TestCompletionCallback callback;
  ASSERT_EQ(
      ERR_IO_PENDING,
      handle->RequestStream(/*requires_confirmation=*/false,
                            callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS));

  // Close a stream and ensure I can now open a new one.
  quic::QuicStreamId stream_id = streams[0]->id();
  session_->ResetStream(stream_id, quic::QUIC_RST_ACKNOWLEDGEMENT);
  quic::QuicRstStreamFrame rst1(quic::kInvalidControlFrameId, stream_id,
                                quic::QUIC_STREAM_NO_ERROR, 0);
  session_->OnRstStream(rst1);
  // Pump data, bringing in the max-stream-id
  base::RunLoop().RunUntilIdle();
  ASSERT_TRUE(callback.have_result());
  EXPECT_THAT(callback.WaitForResult(), IsOk());
  EXPECT_TRUE(handle->ReleaseStream() != nullptr);
}

TEST_P(QuicChromiumClientSessionTest, GoAwayReceived) {
  MockQuicData quic_data(version_);
  quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);
  Initialize();
  CompleteCryptoHandshake();

  // After receiving a GoAway, I should no longer be able to create outgoing
  // streams.
  session_->OnHttp3GoAway(0);
  EXPECT_EQ(nullptr, QuicChromiumClientSessionPeer::CreateOutgoingStream(
                         session_.get()));
}

TEST_P(QuicChromiumClientSessionTest, CanPool) {
  MockQuicData quic_data(version_);
  quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);
  Initialize();
  // Load a cert that is valid for:
  //   www.example.org
  //   mail.example.org
  //   www.example.com

  ProofVerifyDetailsChromium details;
  details.cert_verify_result.verified_cert =
      ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
  ASSERT_TRUE(details.cert_verify_result.verified_cert.get());

  CompleteCryptoHandshake();
  session_->OnProofVerifyDetailsAvailable(details);

  EXPECT_TRUE(session_->CanPool(
      "www.example.org",
      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, ProxyChain::Direct(),
                     SessionUsage::kDestination, SocketTag(),
                     NetworkAnonymizationKey(), SecureDnsPolicy::kAllow,
                     /*require_dns_https_alpn=*/false,
                     /*disable_cert_verification_network_fetches=*/false,
                     handles::kInvalidNetworkHandle)));
  EXPECT_FALSE(session_->CanPool(
      "www.example.org",
      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, ProxyChain::Direct(),
                     SessionUsage::kDestination, SocketTag(),
                     NetworkAnonymizationKey(), SecureDnsPolicy::kAllow,
                     /*require_dns_https_alpn=*/false,
                     /*disable_cert_verification_network_fetches=*/false,
                     /*target_network=*/1)));
  EXPECT_FALSE(session_->CanPool(
      "www.example.org",
      QuicSessionKey("foo", 1234, PRIVACY_MODE_ENABLED, ProxyChain::Direct(),
                     SessionUsage::kDestination, SocketTag(),
                     NetworkAnonymizationKey(), SecureDnsPolicy::kAllow,
                     /*require_dns_https_alpn=*/false,
                     /*disable_cert_verification_network_fetches=*/false,
                     handles::kInvalidNetworkHandle)));
  EXPECT_FALSE(session_->CanPool(
      "www.example.org",
      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, ProxyChain::Direct(),
                     SessionUsage::kDestination, SocketTag(),
                     NetworkAnonymizationKey(), SecureDnsPolicy::kDisable,
                     /*require_dns_https_alpn=*/false,
                     /*disable_cert_verification_network_fetches=*/false,
                     handles::kInvalidNetworkHandle)));
#if BUILDFLAG(IS_ANDROID)
  SocketTag tag1(SocketTag::UNSET_UID, 0x12345678);
  SocketTag tag2(getuid(), 0x87654321);
  EXPECT_FALSE(session_->CanPool(
      "www.example.org",
      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, ProxyChain::Direct(),
                     SessionUsage::kDestination, tag1,
                     NetworkAnonymizationKey(), SecureDnsPolicy::kAllow,
                     /*require_dns_https_alpn=*/false,
                     /*disable_cert_verification_network_fetches=*/false,
                     handles::kInvalidNetworkHandle)));
  EXPECT_FALSE(session_->CanPool(
      "www.example.org",
      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, ProxyChain::Direct(),
                     SessionUsage::kDestination, tag2,
                     NetworkAnonymizationKey(), SecureDnsPolicy::kAllow,
                     /*require_dns_https_alpn=*/false,
                     /*disable_cert_verification_network_fetches=*/false,
                     handles::kInvalidNetworkHandle)));
#endif
  EXPECT_FALSE(session_->CanPool(
      "www.example.org",
      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED,
                     ProxyChain::FromSchemeHostAndPort(ProxyServer::SCHEME_QUIC,
                                                       "bar", 443),
                     SessionUsage::kDestination, SocketTag(),
                     NetworkAnonymizationKey(), SecureDnsPolicy::kAllow,
                     /*require_dns_https_alpn=*/false,
                     /*disable_cert_verification_network_fetches=*/false,
                     handles::kInvalidNetworkHandle)));
  // Note that this isn't a session key we would actually use because if the
  // session usage is `kProxy` then `disable_cert_verification_network_fetches`
  // should be true to prevent possible deadlocks, but we use it here for
  // testing.
  EXPECT_FALSE(session_->CanPool(
      "www.example.org",
      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, ProxyChain::Direct(),
                     SessionUsage::kProxy, SocketTag(),
                     NetworkAnonymizationKey(), SecureDnsPolicy::kAllow,
                     /*require_dns_https_alpn=*/false,
                     /*disable_cert_verification_network_fetches=*/false,
                     handles::kInvalidNetworkHandle)));

  EXPECT_TRUE(session_->CanPool(
      "mail.example.org",
      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, ProxyChain::Direct(),
                     SessionUsage::kDestination, SocketTag(),
                     NetworkAnonymizationKey(), SecureDnsPolicy::kAllow,
                     /*require_dns_https_alpn=*/false,
                     /*disable_cert_verification_network_fetches=*/false,
                     handles::kInvalidNetworkHandle)));
  EXPECT_TRUE(session_->CanPool(
      "mail.example.com",
      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, ProxyChain::Direct(),
                     SessionUsage::kDestination, SocketTag(),
                     NetworkAnonymizationKey(), SecureDnsPolicy::kAllow,
                     /*require_dns_https_alpn=*/false,
                     /*disable_cert_verification_network_fetches=*/false,
                     handles::kInvalidNetworkHandle)));
  EXPECT_FALSE(session_->CanPool(
      "mail.google.com",
      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, ProxyChain::Direct(),
                     SessionUsage::kDestination, SocketTag(),
                     NetworkAnonymizationKey(), SecureDnsPolicy::kAllow,
                     /*require_dns_https_alpn=*/false,
                     /*disable_cert_verification_network_fetches=*/false,
                     handles::kInvalidNetworkHandle)));

  const SchemefulSite kSiteFoo(GURL("http://foo.test/"));

  // Check that NetworkAnonymizationKey is respected when feature is enabled.
  {
    base::test::ScopedFeatureList feature_list;
    feature_list.InitAndDisableFeature(
        features::kPartitionConnectionsByNetworkIsolationKey);
    EXPECT_TRUE(session_->CanPool(
        "mail.example.com",
        QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, ProxyChain::Direct(),
                       SessionUsage::kDestination, SocketTag(),
                       NetworkAnonymizationKey::CreateSameSite(kSiteFoo),
                       SecureDnsPolicy::kAllow,
                       /*require_dns_https_alpn=*/false,
                       /*disable_cert_verification_network_fetches=*/false,
                       handles::kInvalidNetworkHandle)));
  }
  {
    base::test::ScopedFeatureList feature_list;
    feature_list.InitAndEnableFeature(
        features::kPartitionConnectionsByNetworkIsolationKey);
    EXPECT_FALSE(session_->CanPool(
        "mail.example.com",
        QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, ProxyChain::Direct(),
                       SessionUsage::kDestination, SocketTag(),
                       NetworkAnonymizationKey::CreateSameSite(kSiteFoo),
                       SecureDnsPolicy::kAllow,
                       /*require_dns_https_alpn=*/false,
                       /*disable_cert_verification_network_fetches=*/false,
                       handles::kInvalidNetworkHandle)));
  }

  EXPECT_FALSE(session_->CanPool(
      "www.example.org",
      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, ProxyChain::Direct(),
                     SessionUsage::kDestination, SocketTag(),
                     NetworkAnonymizationKey(), SecureDnsPolicy::kDisable,
                     /*require_dns_https_alpn=*/false,
                     /*disable_cert_verification_network_fetches=*/false,
                     handles::kInvalidNetworkHandle)));
  EXPECT_FALSE(session_->CanPool(
      "www.example.org",
      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, ProxyChain::Direct(),
                     SessionUsage::kDestination, SocketTag(),
                     NetworkAnonymizationKey(), SecureDnsPolicy::kAllow,
                     /*require_dns_https_alpn=*/true,
                     /*disable_cert_verification_network_fetches=*/false,
                     handles::kInvalidNetworkHandle)));
  EXPECT_FALSE(session_->CanPool(
      "www.example.org",
      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, ProxyChain::Direct(),
                     SessionUsage::kDestination, SocketTag(),
                     NetworkAnonymizationKey(), SecureDnsPolicy::kAllow,
                     /*require_dns_https_alpn=*/false,
                     /*disable_cert_verification_network_fetches=*/true,
                     handles::kInvalidNetworkHandle)));
}

// Much as above, but uses a non-empty NetworkAnonymizationKey.
TEST_P(QuicChromiumClientSessionTest, CanPoolWithNetworkAnonymizationKey) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndEnableFeature(
      features::kPartitionConnectionsByNetworkIsolationKey);

  SchemefulSite site_foo(GURL("http://foo.test/"));
  SchemefulSite site_bar(GURL("http://bar.test/"));
  const auto kNetworkAnonymizationKey1 =
      NetworkAnonymizationKey::CreateSameSite(std::move(site_foo));
  const auto kNetworkAnonymizationKey2 =
      NetworkAnonymizationKey::CreateSameSite(std::move(site_bar));

  session_key_ = QuicSessionKey(
      kServerHostname, kServerPort, PRIVACY_MODE_DISABLED, ProxyChain::Direct(),
      SessionUsage::kDestination, SocketTag(), kNetworkAnonymizationKey1,
      SecureDnsPolicy::kAllow,
      /*require_dns_https_alpn=*/false,
      /*disable_cert_verification_network_fetches=*/false,
      handles::kInvalidNetworkHandle);

  MockQuicData quic_data(version_);
  quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);
  Initialize();
  // Load a cert that is valid for:
  //   www.example.org
  //   mail.example.org
  //   www.example.com

  ProofVerifyDetailsChromium details;
  details.cert_verify_result.verified_cert =
      ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
  ASSERT_TRUE(details.cert_verify_result.verified_cert.get());

  CompleteCryptoHandshake();
  session_->OnProofVerifyDetailsAvailable(details);

  EXPECT_TRUE(session_->CanPool(
      "www.example.org",
      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, ProxyChain::Direct(),
                     SessionUsage::kDestination, SocketTag(),
                     kNetworkAnonymizationKey1, SecureDnsPolicy::kAllow,
                     /*require_dns_https_alpn=*/false,
                     /*disable_cert_verification_network_fetches=*/false,
                     handles::kInvalidNetworkHandle)));
  EXPECT_FALSE(session_->CanPool(
      "www.example.org",
      QuicSessionKey("foo", 1234, PRIVACY_MODE_ENABLED, ProxyChain::Direct(),
                     SessionUsage::kDestination, SocketTag(),
                     kNetworkAnonymizationKey1, SecureDnsPolicy::kAllow,
                     /*require_dns_https_alpn=*/false,
                     /*disable_cert_verification_network_fetches=*/false,
                     handles::kInvalidNetworkHandle)));
  EXPECT_FALSE(session_->CanPool(
      "www.example.org",
      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED,
                     ProxyChain::FromSchemeHostAndPort(ProxyServer::SCHEME_QUIC,
                                                       "bar", 443),
                     SessionUsage::kDestination, SocketTag(),
                     kNetworkAnonymizationKey1, SecureDnsPolicy::kAllow,
                     /*require_dns_https_alpn=*/false,
                     /*disable_cert_verification_network_fetches=*/false,
                     handles::kInvalidNetworkHandle)));
  // Note that this isn't a session key we would actually use because if the
  // session usage is `kProxy` then `disable_cert_verification_network_fetches`
  // should be true to prevent possible deadlocks, but we use it here for
  // testing.
  EXPECT_FALSE(session_->CanPool(
      "www.example.org",
      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, ProxyChain::Direct(),
                     SessionUsage::kProxy, SocketTag(),
                     kNetworkAnonymizationKey1, SecureDnsPolicy::kAllow,
                     /*require_dns_https_alpn=*/false,
                     /*disable_cert_verification_network_fetches=*/false,
                     handles::kInvalidNetworkHandle)));
#if BUILDFLAG(IS_ANDROID)
  SocketTag tag1(SocketTag::UNSET_UID, 0x12345678);
  SocketTag tag2(getuid(), 0x87654321);
  EXPECT_FALSE(session_->CanPool(
      "www.example.org",
      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, ProxyChain::Direct(),
                     SessionUsage::kDestination, tag1,
                     kNetworkAnonymizationKey1, SecureDnsPolicy::kAllow,
                     /*require_dns_https_alpn=*/false,
                     /*disable_cert_verification_network_fetches=*/false,
                     handles::kInvalidNetworkHandle)));
  EXPECT_FALSE(session_->CanPool(
      "www.example.org",
      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, ProxyChain::Direct(),
                     SessionUsage::kDestination, tag2,
                     kNetworkAnonymizationKey1, SecureDnsPolicy::kAllow,
                     /*require_dns_https_alpn=*/false,
                     /*disable_cert_verification_network_fetches=*/false,
                     handles::kInvalidNetworkHandle)));
#endif
  EXPECT_TRUE(session_->CanPool(
      "mail.example.org",
      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, ProxyChain::Direct(),
                     SessionUsage::kDestination, SocketTag(),
                     kNetworkAnonymizationKey1, SecureDnsPolicy::kAllow,
                     /*require_dns_https_alpn=*/false,
                     /*disable_cert_verification_network_fetches=*/false,
                     handles::kInvalidNetworkHandle)));
  EXPECT_TRUE(session_->CanPool(
      "mail.example.com",
      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, ProxyChain::Direct(),
                     SessionUsage::kDestination, SocketTag(),
                     kNetworkAnonymizationKey1, SecureDnsPolicy::kAllow,
                     /*require_dns_https_alpn=*/false,
                     /*disable_cert_verification_network_fetches=*/false,
                     handles::kInvalidNetworkHandle)));
  EXPECT_FALSE(session_->CanPool(
      "mail.google.com",
      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, ProxyChain::Direct(),
                     SessionUsage::kDestination, SocketTag(),
                     kNetworkAnonymizationKey1, SecureDnsPolicy::kAllow,
                     /*require_dns_https_alpn=*/false,
                     /*disable_cert_verification_network_fetches=*/false,
                     handles::kInvalidNetworkHandle)));

  EXPECT_FALSE(session_->CanPool(
      "mail.example.com",
      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, ProxyChain::Direct(),
                     SessionUsage::kDestination, SocketTag(),
                     kNetworkAnonymizationKey2, SecureDnsPolicy::kAllow,
                     /*require_dns_https_alpn=*/false,
                     /*disable_cert_verification_network_fetches=*/false,
                     handles::kInvalidNetworkHandle)));
  EXPECT_FALSE(session_->CanPool(
      "mail.example.com",
      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, ProxyChain::Direct(),
                     SessionUsage::kDestination, SocketTag(),
                     NetworkAnonymizationKey(), SecureDnsPolicy::kAllow,
                     /*require_dns_https_alpn=*/false,
                     /*disable_cert_verification_network_fetches=*/false,
                     handles::kInvalidNetworkHandle)));

  EXPECT_FALSE(session_->CanPool(
      "www.example.org",
      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, ProxyChain::Direct(),
                     SessionUsage::kDestination, SocketTag(),
                     kNetworkAnonymizationKey1, SecureDnsPolicy::kDisable,
                     /*require_dns_https_alpn=*/false,
                     /*disable_cert_verification_network_fetches=*/false,
                     handles::kInvalidNetworkHandle)));
  EXPECT_FALSE(session_->CanPool(
      "www.example.org",
      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, ProxyChain::Direct(),
                     SessionUsage::kDestination, SocketTag(),
                     kNetworkAnonymizationKey1, SecureDnsPolicy::kAllow,
                     /*require_dns_https_alpn=*/true,
                     /*disable_cert_verification_network_fetches=*/false,
                     handles::kInvalidNetworkHandle)));
  EXPECT_FALSE(session_->CanPool(
      "www.example.org",
      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, ProxyChain::Direct(),
                     SessionUsage::kDestination, SocketTag(),
                     kNetworkAnonymizationKey1, SecureDnsPolicy::kAllow,
                     /*require_dns_https_alpn=*/false,
                     /*disable_cert_verification_network_fetches=*/true,
                     handles::kInvalidNetworkHandle)));
}

TEST_P(QuicChromiumClientSessionTest, ConnectionNotPooledWithDifferentPin) {
  base::test::ScopedFeatureList scoped_feature_list_;
  scoped_feature_list_.InitAndEnableFeature(
      net::features::kStaticKeyPinningEnforcement);
  // Configure the TransportSecurityStateSource so that kPreloadedPKPHost will
  // have static PKP pins set.
  ScopedTransportSecurityStateSource scoped_security_state_source;

  // |net::test_default::kHSTSSource| defines pins for kPreloadedPKPHost.
  // (This hostname must be in the spdy_pooling.pem SAN.)
  const char kPreloadedPKPHost[] = "www.example.org";
  // A hostname without any static state.  (This hostname isn't in
  // spdy_pooling.pem SAN, but that's okay because the
  // ProofVerifyDetailsChromium are faked.)
  const char kNoPinsHost[] = "no-pkp.example.org";

  MockQuicData quic_data(version_);
  quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);
  Initialize();

  transport_security_state_->EnableStaticPinsForTesting();
  transport_security_state_->SetPinningListAlwaysTimelyForTesting(true);

  ProofVerifyDetailsChromium details;
  details.cert_verify_result.verified_cert =
      ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
  details.cert_verify_result.is_issued_by_known_root = true;
  uint8_t bad_pin = 3;
  details.cert_verify_result.public_key_hashes.push_back(
      GetTestHashValue(bad_pin));

  ASSERT_TRUE(details.cert_verify_result.verified_cert.get());

  CompleteCryptoHandshake();
  session_->OnProofVerifyDetailsAvailable(details);
  QuicChromiumClientSessionPeer::SetHostname(session_.get(), kNoPinsHost);

  EXPECT_FALSE(session_->CanPool(
      kPreloadedPKPHost,
      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, ProxyChain::Direct(),
                     SessionUsage::kDestination, SocketTag(),
                     NetworkAnonymizationKey(), SecureDnsPolicy::kAllow,
                     /*require_dns_https_alpn=*/false,
                     /*disable_cert_verification_network_fetches=*/false,
                     handles::kInvalidNetworkHandle)));
}

TEST_P(QuicChromiumClientSessionTest, ConnectionPooledWithMatchingPin) {
  ScopedTransportSecurityStateSource scoped_security_state_source;

  MockQuicData quic_data(version_);
  quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);
  Initialize();

  transport_security_state_->EnableStaticPinsForTesting();

  ProofVerifyDetailsChromium details;
  details.cert_verify_result.verified_cert =
      ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
  details.cert_verify_result.is_issued_by_known_root = true;
  std::optional<HashValue> primary_pin = HashValue::FromString(
      "sha256/Nn8jk5By4Vkq6BeOVZ7R7AC6XUUBZsWmUbJR1f1Y5FY=");
  ASSERT_TRUE(primary_pin.has_value());
  details.cert_verify_result.public_key_hashes.push_back(
      primary_pin->sha256hashvalue());

  ASSERT_TRUE(details.cert_verify_result.verified_cert.get());

  CompleteCryptoHandshake();
  session_->OnProofVerifyDetailsAvailable(details);
  QuicChromiumClientSessionPeer::SetHostname(session_.get(), "www.example.org");

  EXPECT_TRUE(session_->CanPool(
      "mail.example.org",
      QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, ProxyChain::Direct(),
                     SessionUsage::kDestination, SocketTag(),
                     NetworkAnonymizationKey(), SecureDnsPolicy::kAllow,
                     /*require_dns_https_alpn=*/false,
                     /*disable_cert_verification_network_fetches=*/false,
                     handles::kInvalidNetworkHandle)));
}

TEST_P(QuicChromiumClientSessionTest, MigrateToSocket) {
  quic::QuicConnectionId cid_on_new_path =
      quic::test::TestConnectionId(12345678);
  MockQuicData quic_data(version_);
  int packet_num = 1;
  int peer_packet_num = 1;
  socket_data_.reset();
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddWrite(ASYNC,
                     client_maker_.MakeInitialSettingsPacket(packet_num++));
  quic_data.AddRead(ASYNC, server_maker_.Packet(peer_packet_num++)
                               .AddNewConnectionIdFrame(cid_on_new_path,
                                                        /*sequence_number=*/1u,
                                                        /*retire_prior_to=*/0u)
                               .Build());
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);
  Initialize();
  CompleteCryptoHandshake();

  // Make new connection ID available after handshake completion.
  quic_data.Resume();
  base::RunLoop().RunUntilIdle();

  char data[] = "ABCD";
  MockQuicData quic_data2(version_);
  client_maker_.set_connection_id(cid_on_new_path);
  quic_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
  quic_data2.AddWrite(SYNCHRONOUS,
                      client_maker_.Packet(packet_num++)
                          .AddAckFrame(/*first_received=*/1,
                                       /*largest_received=*/peer_packet_num - 1,
                                       /*smallest_received=*/1)
                          .AddPingFrame()
                          .Build());
  quic_data2.AddWrite(
      SYNCHRONOUS,
      client_maker_.Packet(packet_num++)
          .AddStreamFrame(GetNthClientInitiatedBidirectionalStreamId(0), false,
                          std::string_view(data))
          .Build());
  quic_data2.AddSocketDataToFactory(&socket_factory_);
  // Create connected socket.
  std::unique_ptr<DatagramClientSocket> new_socket =
      socket_factory_.CreateDatagramClientSocket(DatagramSocket::RANDOM_BIND,
                                                 handles::kInvalidNetworkHandle,
                                                 NetLog::Get(), NetLogSource());
  EXPECT_THAT(new_socket->Connect(kIpEndPoint), IsOk());

  // Create reader and writer.
  auto new_reader = std::make_unique<QuicChromiumPacketReader>(
      std::move(new_socket), &clock_, session_.get(),
      kQuicYieldAfterPacketsRead,
      quic::QuicTime::Delta::FromMilliseconds(
          kQuicYieldAfterDurationMilliseconds),
      net_log_with_source_);
  new_reader->StartReading();
  std::unique_ptr<QuicChromiumPacketWriter> new_writer(
      CreateQuicChromiumPacketWriter(new_reader->socket(), session_.get()));

  IPEndPoint local_address;
  new_reader->socket()->GetLocalAddress(&local_address);
  IPEndPoint peer_address;
  new_reader->socket()->GetPeerAddress(&peer_address);
  // Migrate session.
  EXPECT_CALL(*session_, UnregisterQuicConnectionClosePayload());
  EXPECT_CALL(*session_, RegisterQuicConnectionClosePayload());
  EXPECT_TRUE(session_->MigrateToSocket(
      ToQuicSocketAddress(local_address), ToQuicSocketAddress(peer_address),
      std::move(new_reader), std::move(new_writer)));
  // Spin message loop to complete migration.
  base::RunLoop().RunUntilIdle();

  // Write data to session.
  QuicChromiumClientStream* stream =
      QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get());
  quic::test::QuicStreamPeer::SendBuffer(stream).SaveStreamData(data);
  quic::test::QuicStreamPeer::SetStreamBytesWritten(4, stream);
  session_->WritevData(stream->id(), 4, 0, quic::NO_FIN,
                       quic::NOT_RETRANSMISSION,
                       quic::ENCRYPTION_FORWARD_SECURE);

  EXPECT_TRUE(quic_data2.AllReadDataConsumed());
  EXPECT_TRUE(quic_data2.AllWriteDataConsumed());
}

TEST_P(QuicChromiumClientSessionTest, MigrateToSocketMaxReaders) {
  MockQuicData quic_data(version_);
  socket_data_.reset();
  int packet_num = 1;
  int peer_packet_num = 1;
  quic::QuicConnectionId next_cid = quic::QuicUtils::CreateRandomConnectionId(
      quiche::QuicheRandom::GetInstance());
  uint64_t next_cid_sequence_number = 1u;
  quic_data.AddWrite(SYNCHRONOUS,
                     client_maker_.MakeInitialSettingsPacket(packet_num++));
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC,
                    server_maker_.Packet(peer_packet_num++)
                        .AddNewConnectionIdFrame(
                            next_cid, next_cid_sequence_number,
                            /*retire_prior_to=*/next_cid_sequence_number - 1)
                        .Build());
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);
  Initialize();
  CompleteCryptoHandshake();

  // Make connection ID available for the first migration.
  quic_data.Resume();

  /* Migration succeeds when maximum number of readers is not reached.*/
  for (size_t i = 0; i < kMaxReadersPerQuicSession - 1; ++i) {
    MockQuicData quic_data2(version_);
    client_maker_.set_connection_id(next_cid);
    quic_data2.AddWrite(
        SYNCHRONOUS, client_maker_.Packet(packet_num++)
                         .AddAckFrame(/*first_received=*/1,
                                      /*largest_received=*/peer_packet_num - 1,
                                      /*smallest_received=*/1)
                         .AddPingFrame()
                         .Build());
    quic_data2.AddRead(ASYNC, ERR_IO_PENDING);
    quic_data2.AddWrite(
        ASYNC, client_maker_.Packet(packet_num++)
                   .AddRetireConnectionIdFrame(
                       /*sequence_number=*/next_cid_sequence_number - 1)
                   .Build());
    next_cid = quic::QuicUtils::CreateRandomConnectionId(
        quiche::QuicheRandom::GetInstance());
    ++next_cid_sequence_number;
    quic_data2.AddRead(ASYNC,
                       server_maker_.Packet(peer_packet_num++)
                           .AddNewConnectionIdFrame(
                               next_cid, next_cid_sequence_number,
                               /*retire_prior_to=*/next_cid_sequence_number - 1)
                           .Build());
    quic_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // Hanging read.
    quic_data2.AddSocketDataToFactory(&socket_factory_);

    // Create connected socket.
    std::unique_ptr<DatagramClientSocket> new_socket =
        socket_factory_.CreateDatagramClientSocket(
            DatagramSocket::RANDOM_BIND, handles::kInvalidNetworkHandle,
            NetLog::Get(), NetLogSource());
    EXPECT_THAT(new_socket->Connect(kIpEndPoint), IsOk());

    // Create reader and writer.
    auto new_reader = std::make_unique<QuicChromiumPacketReader>(
        std::move(new_socket), &clock_, session_.get(),
        kQuicYieldAfterPacketsRead,
        quic::QuicTime::Delta::FromMilliseconds(
            kQuicYieldAfterDurationMilliseconds),
        net_log_with_source_);
    new_reader->StartReading();
    std::unique_ptr<QuicChromiumPacketWriter> new_writer(
        CreateQuicChromiumPacketWriter(new_reader->socket(), session_.get()));

    IPEndPoint local_address;
    new_reader->socket()->GetLocalAddress(&local_address);
    IPEndPoint peer_address;
    new_reader->socket()->GetPeerAddress(&peer_address);
    // Migrate session.
    EXPECT_CALL(*session_, UnregisterQuicConnectionClosePayload());
    EXPECT_CALL(*session_, RegisterQuicConnectionClosePayload());
    EXPECT_TRUE(session_->MigrateToSocket(
        ToQuicSocketAddress(local_address), ToQuicSocketAddress(peer_address),
        std::move(new_reader), std::move(new_writer)));
    // Spin message loop to complete migration.
    base::RunLoop().RunUntilIdle();
    EXPECT_CALL(*session_, RegisterQuicConnectionClosePayload());
    alarm_factory_.FireAlarm(
        quic::test::QuicConnectionPeer::GetRetirePeerIssuedConnectionIdAlarm(
            session_->connection()));
    // Make new connection ID available for subsequent migration.
    quic_data2.Resume();
    base::RunLoop().RunUntilIdle();
    EXPECT_TRUE(quic_data2.AllReadDataConsumed());
    EXPECT_TRUE(quic_data2.AllWriteDataConsumed());
  }

  /* Migration fails when maximum number of readers is reached.*/
  MockQuicData quic_data2(version_);
  quic_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // Hanging read.
  quic_data2.AddSocketDataToFactory(&socket_factory_);
  // Create connected socket.
  std::unique_ptr<DatagramClientSocket> new_socket =
      socket_factory_.CreateDatagramClientSocket(DatagramSocket::RANDOM_BIND,
                                                 handles::kInvalidNetworkHandle,
                                                 NetLog::Get(), NetLogSource());
  EXPECT_THAT(new_socket->Connect(kIpEndPoint), IsOk());

  // Create reader and writer.
  auto new_reader = std::make_unique<QuicChromiumPacketReader>(
      std::move(new_socket), &clock_, session_.get(),
      kQuicYieldAfterPacketsRead,
      quic::QuicTime::Delta::FromMilliseconds(
          kQuicYieldAfterDurationMilliseconds),
      net_log_with_source_);
  new_reader->StartReading();
  std::unique_ptr<QuicChromiumPacketWriter> new_writer(
      CreateQuicChromiumPacketWriter(new_reader->socket(), session_.get()));

  IPEndPoint local_address;
  new_reader->socket()->GetLocalAddress(&local_address);
  IPEndPoint peer_address;
  new_reader->socket()->GetPeerAddress(&peer_address);
  EXPECT_FALSE(session_->MigrateToSocket(
      ToQuicSocketAddress(local_address), ToQuicSocketAddress(peer_address),
      std::move(new_reader), std::move(new_writer)));
  EXPECT_TRUE(quic_data2.AllReadDataConsumed());
  EXPECT_TRUE(quic_data2.AllWriteDataConsumed());
}

TEST_P(QuicChromiumClientSessionTest, MigrateToSocketReadError) {
  MockQuicData quic_data(version_);
  socket_data_.reset();
  int packet_num = 1;
  int peer_packet_num = 1;

  quic::QuicConnectionId cid_on_new_path =
      quic::test::TestConnectionId(12345678);
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddWrite(ASYNC,
                     client_maker_.MakeInitialSettingsPacket(packet_num++));
  quic_data.AddRead(ASYNC, server_maker_.Packet(peer_packet_num++)
                               .AddNewConnectionIdFrame(cid_on_new_path,
                                                        /*sequence_number=*/1u,
                                                        /*retire_prior_to=*/0u)
                               .Build());
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_NETWORK_CHANGED);

  quic_data.AddSocketDataToFactory(&socket_factory_);
  Initialize();
  CompleteCryptoHandshake();

  // Make new connection ID available after handshake completion.
  quic_data.Resume();
  base::RunLoop().RunUntilIdle();

  MockQuicData quic_data2(version_);
  client_maker_.set_connection_id(cid_on_new_path);
  quic_data2.AddWrite(SYNCHRONOUS,
                      client_maker_.Packet(packet_num++)
                          .AddAckFrame(/*first_received=*/1,
                                       /*largest_received=*/peer_packet_num - 1,
                                       /*smallest_received=*/1)
                          .AddPingFrame()
                          .Build());
  quic_data2.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data2.AddRead(ASYNC, server_maker_.Packet(1).AddPingFrame().Build());
  quic_data2.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data2.AddRead(ASYNC, ERR_NETWORK_CHANGED);
  quic_data2.AddSocketDataToFactory(&socket_factory_);

  // Create connected socket.
  std::unique_ptr<DatagramClientSocket> new_socket =
      socket_factory_.CreateDatagramClientSocket(DatagramSocket::RANDOM_BIND,
                                                 handles::kInvalidNetworkHandle,
                                                 NetLog::Get(), NetLogSource());
  EXPECT_THAT(new_socket->Connect(kIpEndPoint), IsOk());

  // Create reader and writer.
  auto new_reader = std::make_unique<QuicChromiumPacketReader>(
      std::move(new_socket), &clock_, session_.get(),
      kQuicYieldAfterPacketsRead,
      quic::QuicTime::Delta::FromMilliseconds(
          kQuicYieldAfterDurationMilliseconds),
      net_log_with_source_);
  new_reader->StartReading();
  std::unique_ptr<QuicChromiumPacketWriter> new_writer(
      CreateQuicChromiumPacketWriter(new_reader->socket(), session_.get()));

  IPEndPoint local_address;
  new_reader->socket()->GetLocalAddress(&local_address);
  IPEndPoint peer_address;
  new_reader->socket()->GetPeerAddress(&peer_address);
  // Store old socket and migrate session.
  EXPECT_CALL(*session_, UnregisterQuicConnectionClosePayload());
  EXPECT_CALL(*session_, RegisterQuicConnectionClosePayload());
  EXPECT_TRUE(session_->MigrateToSocket(
      ToQuicSocketAddress(local_address), ToQuicSocketAddress(peer_address),
      std::move(new_reader), std::move(new_writer)));
  // Spin message loop to complete migration.
  base::RunLoop().RunUntilIdle();
  EXPECT_TRUE(
      quic::test::QuicConnectionPeer::GetRetirePeerIssuedConnectionIdAlarm(
          session_->connection())
          ->IsSet());

  // Read error on old socket does not impact session.
  quic_data.Resume();
  EXPECT_TRUE(session_->connection()->connected());
  quic_data2.Resume();

  // Read error on new socket causes session close.
  EXPECT_CALL(*session_, UnregisterQuicConnectionClosePayload());
  EXPECT_TRUE(session_->connection()->connected());
  quic_data2.Resume();
  EXPECT_FALSE(session_->connection()->connected());

  EXPECT_TRUE(quic_data.AllReadDataConsumed());
  EXPECT_TRUE(quic_data.AllWriteDataConsumed());
  EXPECT_TRUE(quic_data2.AllReadDataConsumed());
  EXPECT_TRUE(quic_data2.AllWriteDataConsumed());
}

TEST_P(QuicChromiumClientSessionTest, RetransmittableOnWireTimeout) {
  migrate_session_early_v2_ = true;

  MockQuicData quic_data(version_);
  int packet_num = 1;
  quic_data.AddWrite(SYNCHRONOUS,
                     client_maker_.MakeInitialSettingsPacket(packet_num++));
  quic_data.AddWrite(SYNCHRONOUS,
                     client_maker_.Packet(packet_num++).AddPingFrame().Build());

  quic_data.AddRead(
      ASYNC, server_maker_.Packet(1).AddAckFrame(1, packet_num - 1, 1).Build());

  quic_data.AddWrite(SYNCHRONOUS,
                     client_maker_.Packet(packet_num++).AddPingFrame().Build());
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);

  Initialize();
  CompleteCryptoHandshake();

  // Open a stream since the connection only sends PINGs to keep a
  // retransmittable packet on the wire if there's an open stream.
  EXPECT_TRUE(
      QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get()));

  quic::test::QuicTestAlarmProxy alarm(
      quic::test::QuicConnectionPeer::GetPingAlarm(session_->connection()));
  EXPECT_FALSE(alarm.IsSet());

  // Send PING, which will be ACKed by the server. After the ACK, there will be
  // no retransmittable packets on the wire, so the alarm should be set.
  session_->connection()->SendPing();
  base::RunLoop().RunUntilIdle();
  EXPECT_TRUE(alarm.IsSet());
  EXPECT_EQ(
      clock_.ApproximateNow() + quic::QuicTime::Delta::FromMilliseconds(200),
      alarm.deadline());

  // Advance clock and simulate the alarm firing. This should cause a PING to be
  // sent.
  clock_.AdvanceTime(quic::QuicTime::Delta::FromMilliseconds(200));
  alarm.Fire();
  base::RunLoop().RunUntilIdle();

  EXPECT_CALL(*session_, UnregisterQuicConnectionClosePayload());
  quic_data.Resume();
  EXPECT_TRUE(quic_data.AllReadDataConsumed());
  EXPECT_TRUE(quic_data.AllWriteDataConsumed());
}

// Regression test for https://crbug.com/1043531.
TEST_P(QuicChromiumClientSessionTest, ResetOnEmptyResponseHeaders) {
  MockQuicData quic_data(version_);
  int packet_num = 1;
  quic_data.AddWrite(ASYNC,
                     client_maker_.MakeInitialSettingsPacket(packet_num++));
  quic_data.AddWrite(
      ASYNC,
      client_maker_.Packet(packet_num++)
          .AddStopSendingFrame(GetNthClientInitiatedBidirectionalStreamId(0),
                               quic::QUIC_STREAM_GENERAL_PROTOCOL_ERROR)
          .AddRstStreamFrame(GetNthClientInitiatedBidirectionalStreamId(0),
                             quic::QUIC_STREAM_GENERAL_PROTOCOL_ERROR)
          .Build());
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);
  Initialize();

  ProofVerifyDetailsChromium details;
  details.cert_verify_result.verified_cert =
      ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
  ASSERT_TRUE(details.cert_verify_result.verified_cert.get());

  CompleteCryptoHandshake();
  session_->OnProofVerifyDetailsAvailable(details);

  auto session_handle = session_->CreateHandle(destination_);
  TestCompletionCallback callback;
  EXPECT_EQ(OK, session_handle->RequestStream(/*requires_confirmation=*/false,
                                              callback.callback(),
                                              TRAFFIC_ANNOTATION_FOR_TESTS));

  auto stream_handle = session_handle->ReleaseStream();
  EXPECT_TRUE(stream_handle->IsOpen());

  auto* stream = quic::test::QuicSessionPeer::GetOrCreateStream(
      session_.get(), stream_handle->id());

  const quic::QuicHeaderList empty_response_headers;
  static_cast<quic::QuicSpdyStream*>(stream)->OnStreamHeaderList(
      /* fin = */ false, /* frame_len = */ 0, empty_response_headers);

  // QuicSpdyStream::OnStreamHeaderList() calls
  // QuicChromiumClientStream::OnInitialHeadersComplete() with the empty
  // header list, and QuicChromiumClientStream signals an error.
  quiche::HttpHeaderBlock header_block;
  int rv = stream_handle->ReadInitialHeaders(&header_block,
                                             CompletionOnceCallback());
  EXPECT_THAT(rv, IsError(net::ERR_QUIC_PROTOCOL_ERROR));

  EXPECT_CALL(*session_, UnregisterQuicConnectionClosePayload());
  base::RunLoop().RunUntilIdle();
  quic_data.Resume();
  EXPECT_TRUE(quic_data.AllReadDataConsumed());
  EXPECT_TRUE(quic_data.AllWriteDataConsumed());
}

// This test verifies that when handles::NetworkHandle is not supported and
// there is no network change, session reports to the connectivity monitor
// correctly on path degrading detection and recovery.
TEST_P(QuicChromiumClientSessionTest,
       DegradingWithoutNetworkChange_NoNetworkHandle) {
  // Add a connectivity monitor for testing.
  default_network_ = handles::kInvalidNetworkHandle;
  connectivity_monitor_ =
      std::make_unique<QuicConnectivityMonitor>(default_network_);

  Initialize();

  // Fire path degrading detection.
  session_->ReallyOnPathDegrading();
  EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions());

  session_->OnForwardProgressMadeAfterPathDegrading();
  EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions());

  // Fire again.
  session_->ReallyOnPathDegrading();
  EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions());

  // Close the session but keep the session around, the connectivity monitor
  // will not remove the tracking immediately.
  EXPECT_CALL(*session_, UnregisterQuicConnectionClosePayload());
  session_->CloseSessionOnError(ERR_ABORTED, quic::QUIC_INTERNAL_ERROR,
                                quic::ConnectionCloseBehavior::SILENT_CLOSE);
  EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions());

  // Delete the session will remove the degrading count in connectivity
  // monitor.
  session_.reset();
  EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions());
}

// This test verifies that when multi-port and port migration is enabled, path
// degrading won't trigger port migration.
TEST_P(QuicChromiumClientSessionTest, DegradingWithMultiPortEnabled) {
  // Default network is always set to handles::kInvalidNetworkHandle.
  default_network_ = handles::kInvalidNetworkHandle;
  connectivity_monitor_ =
      std::make_unique<QuicConnectivityMonitor>(default_network_);
  allow_port_migration_ = true;
  auto options = config_.SendConnectionOptions();
  config_.SetClientConnectionOptions(quic::QuicTagVector{quic::kMPQC});
  config_.SetConnectionOptionsToSend(options);

  Initialize();
  EXPECT_TRUE(session_->connection()->multi_port_stats());

  session_->ReallyOnPathDegrading();
  EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions());

  EXPECT_EQ(
      UNKNOWN_CAUSE,
      QuicChromiumClientSessionPeer::GetCurrentMigrationCause(session_.get()));
}

// This test verifies that when the handles::NetworkHandle is not supported, and
// there are speculated network change reported via OnIPAddressChange, session
// still reports to the connectivity monitor correctly on path degrading
// detection and recovery.
TEST_P(QuicChromiumClientSessionTest, DegradingWithIPAddressChange) {
  // Default network is always set to handles::kInvalidNetworkHandle.
  default_network_ = handles::kInvalidNetworkHandle;
  connectivity_monitor_ =
      std::make_unique<QuicConnectivityMonitor>(default_network_);

  Initialize();

  session_->ReallyOnPathDegrading();
  EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions());

  session_->OnForwardProgressMadeAfterPathDegrading();
  EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions());

  session_->ReallyOnPathDegrading();
  EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions());

  // When handles::NetworkHandle is not supported, network change is notified
  // via IP address change.
  connectivity_monitor_->OnIPAddressChanged();
  EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions());

  // When handles::NetworkHandle is not supported and IP address changes,
  // session either goes away or gets closed. When it goes away,
  // reporting to connectivity monitor is disabled.
  connectivity_monitor_->OnSessionGoingAwayOnIPAddressChange(session_.get());

  // Even if session detects recovery or degradation, this session is no longer
  // on the default network and connectivity monitor will not update.
  session_->OnForwardProgressMadeAfterPathDegrading();
  EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions());
  session_->ReallyOnPathDegrading();
  EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions());

  EXPECT_CALL(*session_, UnregisterQuicConnectionClosePayload());
  session_->CloseSessionOnError(ERR_ABORTED, quic::QUIC_INTERNAL_ERROR,
                                quic::ConnectionCloseBehavior::SILENT_CLOSE);
  EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions());

  session_.reset();
  EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions());
}

// This test verifies that when handles::NetworkHandle is supported but
// migration is not supported and there's no network change, session reports to
// connectivity monitor correctly on path degrading detection or recovery.
// Default network change is currently reported with valid
// handles::NetworkHandles while session's current network interface is tracked
// by |default_network_|.
TEST_P(QuicChromiumClientSessionTest,
       DegradingOnDeafultNetwork_WithoutMigration) {
  default_network_ = kDefaultNetworkForTests;
  connectivity_monitor_ =
      std::make_unique<QuicConnectivityMonitor>(default_network_);

  Initialize();

  session_->ReallyOnPathDegrading();
  EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions());

  session_->OnForwardProgressMadeAfterPathDegrading();
  EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions());

  session_->ReallyOnPathDegrading();
  EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions());

  // Close the session but keep the session around, the connectivity monitor
  // should not remove the count immediately.
  EXPECT_CALL(*session_, UnregisterQuicConnectionClosePayload());
  session_->CloseSessionOnError(ERR_ABORTED, quic::QUIC_INTERNAL_ERROR,
                                quic::ConnectionCloseBehavior::SILENT_CLOSE);
  EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions());

  // Delete the session will remove the degrading count in connectivity
  // monitor.
  session_.reset();
  EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions());
}

// This test verifies that when handles::NetworkHandle is supported but
// migrations is not supported and there is network changes, session reports to
// the connectivity monitor correctly on path degrading detection or recovery.
TEST_P(QuicChromiumClientSessionTest,
       DegradingWithDeafultNetworkChange_WithoutMigration) {
  default_network_ = kDefaultNetworkForTests;
  connectivity_monitor_ =
      std::make_unique<QuicConnectivityMonitor>(default_network_);

  Initialize();

  session_->ReallyOnPathDegrading();
  EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions());

  session_->OnForwardProgressMadeAfterPathDegrading();
  EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions());

  session_->ReallyOnPathDegrading();
  EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions());

  // Simulate the default network change.
  connectivity_monitor_->OnDefaultNetworkUpdated(kNewNetworkForTests);
  EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions());
  session_->OnNetworkMadeDefault(kNewNetworkForTests);

  // Session stays on the old default network, and recovers.
  session_->OnForwardProgressMadeAfterPathDegrading();
  EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions());

  // Session degrades again on the old default.
  session_->ReallyOnPathDegrading();
  EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions());

  // Simulate that default network switches back to the old default.
  connectivity_monitor_->OnDefaultNetworkUpdated(kDefaultNetworkForTests);
  EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions());
  session_->OnNetworkMadeDefault(kDefaultNetworkForTests);

  // Session recovers again on the (old) default.
  session_->OnForwardProgressMadeAfterPathDegrading();
  EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions());

  // Session degrades again on the (old) default.
  session_->ReallyOnPathDegrading();
  EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions());

  EXPECT_CALL(*session_, UnregisterQuicConnectionClosePayload());
  session_->CloseSessionOnError(ERR_ABORTED, quic::QUIC_INTERNAL_ERROR,
                                quic::ConnectionCloseBehavior::SILENT_CLOSE);
  EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions());

  session_.reset();
  EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions());
}

TEST_P(QuicChromiumClientSessionTest, WriteErrorDuringCryptoConnect) {
  // Add a connectivity monitor for testing.
  default_network_ = kDefaultNetworkForTests;
  connectivity_monitor_ =
      std::make_unique<QuicConnectivityMonitor>(default_network_);

  // Use unmocked crypto stream to do crypto connect.
  crypto_client_stream_factory_.set_handshake_mode(
      MockCryptoClientStream::COLD_START_WITH_CHLO_SENT);

  MockQuicData quic_data(version_);
  // Trigger a packet write error when sending packets in crypto connect.
  quic_data.AddWrite(SYNCHRONOUS, ERR_ADDRESS_UNREACHABLE);
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);

  Initialize();
  EXPECT_CALL(*session_, UnregisterQuicConnectionClosePayload());
  ASSERT_THAT(session_->CryptoConnect(callback_.callback()),
              IsError(ERR_QUIC_HANDSHAKE_FAILED));
  // Verify error count is properly recorded.
  EXPECT_EQ(1u, connectivity_monitor_->GetCountForWriteErrorCode(
                    ERR_ADDRESS_UNREACHABLE));
  EXPECT_EQ(0u, connectivity_monitor_->GetCountForWriteErrorCode(
                    ERR_CONNECTION_RESET));

  // Simulate a default network change, write error stats should be reset.
  connectivity_monitor_->OnDefaultNetworkUpdated(kNewNetworkForTests);
  EXPECT_EQ(0u, connectivity_monitor_->GetCountForWriteErrorCode(
                    ERR_ADDRESS_UNREACHABLE));
}

TEST_P(QuicChromiumClientSessionTest, WriteErrorAfterHandshakeConfirmed) {
  // Add a connectivity monitor for testing.
  default_network_ = handles::kInvalidNetworkHandle;
  connectivity_monitor_ =
      std::make_unique<QuicConnectivityMonitor>(default_network_);

  MockQuicData quic_data(version_);
  int packet_num = 1;
  quic_data.AddWrite(SYNCHRONOUS,
                     client_maker_.MakeInitialSettingsPacket(packet_num++));
  // When sending the PING packet, trigger a packet write error.
  quic_data.AddWrite(SYNCHRONOUS, ERR_CONNECTION_RESET);
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);

  Initialize();
  CompleteCryptoHandshake();

  // Send a ping so that client has outgoing traffic before receiving packets.
  EXPECT_CALL(*session_, UnregisterQuicConnectionClosePayload());
  session_->connection()->SendPing();

  // Verify error count is properly recorded.
  EXPECT_EQ(1u, connectivity_monitor_->GetCountForWriteErrorCode(
                    ERR_CONNECTION_RESET));
  EXPECT_EQ(0u, connectivity_monitor_->GetCountForWriteErrorCode(
                    ERR_ADDRESS_UNREACHABLE));

  connectivity_monitor_->OnIPAddressChanged();

  // If network handle is supported, IP Address change is a no-op. Otherwise it
  // clears all stats.
  size_t expected_error_count =
      NetworkChangeNotifier::AreNetworkHandlesSupported() ? 1u : 0u;
  EXPECT_EQ(
      expected_error_count,
      connectivity_monitor_->GetCountForWriteErrorCode(ERR_CONNECTION_RESET));
}

// Much like above, but checking that ECN marks are reported.
TEST_P(QuicChromiumClientSessionTest, ReportsReceivedEcn) {
  MockQuicData mock_quic_data(version_);
  int write_packet_num = 1, read_packet_num = 0;
  quic::QuicEcnCounts ecn(1, 0, 0);  // 1 ECT(0) packet received
  mock_quic_data.AddWrite(
      ASYNC, client_maker_.MakeInitialSettingsPacket(write_packet_num++));
  mock_quic_data.AddRead(
      ASYNC, server_maker_.MakeInitialSettingsPacket(read_packet_num++));
  server_maker_.set_ecn_codepoint(quic::ECN_ECT0);
  mock_quic_data.AddRead(
      ASYNC, server_maker_.Packet(read_packet_num++).AddPingFrame().Build());
  mock_quic_data.AddWrite(SYNCHRONOUS, client_maker_.Packet(write_packet_num++)
                                           .AddAckFrame(0, 1, 0, ecn)
                                           .Build());
  server_maker_.set_ecn_codepoint(quic::ECN_ECT1);
  mock_quic_data.AddRead(
      ASYNC, server_maker_.Packet(read_packet_num++).AddPingFrame().Build());
  server_maker_.set_ecn_codepoint(quic::ECN_CE);
  mock_quic_data.AddRead(
      ASYNC, server_maker_.Packet(read_packet_num++).AddPingFrame().Build());
  ecn.ect1 = 1;
  ecn.ce = 1;
  mock_quic_data.AddWrite(SYNCHRONOUS, client_maker_.Packet(write_packet_num++)
                                           .AddAckFrame(0, 3, 0, ecn)
                                           .Build());
  mock_quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);

  mock_quic_data.AddSocketDataToFactory(&socket_factory_);
  Initialize();
  CompleteCryptoHandshake();
  base::RunLoop().RunUntilIdle();
  EXPECT_TRUE(mock_quic_data.AllReadDataConsumed());
  EXPECT_TRUE(mock_quic_data.AllWriteDataConsumed());
}

TEST_P(QuicChromiumClientSessionTest, OnOriginFrame) {
  const std::string kExampleOrigin1 = "https://www.example.com";
  const std::string kExampleOrigin2 = "https://www.example.com:443";
  const std::string kExampleOrigin3 = "https://www.example.com:8443";
  const std::string kExampleOrigin4 = "http://www.example.com:8080";
  const std::string kInvalidOrigin1 = "https://www.example.com/";
  const std::string kInvalidOrigin2 = "www.example.com";

  GURL url1(base::StrCat({kExampleOrigin1, "/"}));
  url::SchemeHostPort origin1(url1);
  ASSERT_TRUE(origin1.IsValid());
  GURL url2(base::StrCat({kExampleOrigin2, "/"}));
  url::SchemeHostPort origin2(url2);
  ASSERT_TRUE(origin2.IsValid());
  GURL url3(base::StrCat({kExampleOrigin3, "/"}));
  url::SchemeHostPort origin3(url3);
  ASSERT_TRUE(origin3.IsValid());
  GURL url4(base::StrCat({kExampleOrigin4, "/"}));
  url::SchemeHostPort origin4(url4);
  ASSERT_TRUE(origin4.IsValid());

  quic::OriginFrame frame;

  Initialize();

  ASSERT_TRUE(session_->received_origins().empty());

  frame.origins.push_back(kExampleOrigin1);
  session_->OnOriginFrame(frame);
  EXPECT_EQ(1u, session_->received_origins().size());
  EXPECT_TRUE(session_->received_origins().count(origin1));
  EXPECT_TRUE(session_->received_origins().count(origin2));
  EXPECT_FALSE(session_->received_origins().count(origin3));
  EXPECT_FALSE(session_->received_origins().count(origin4));

  frame.origins.push_back(kExampleOrigin2);
  frame.origins.push_back(kInvalidOrigin1);
  frame.origins.push_back(kInvalidOrigin2);
  frame.origins.push_back(kExampleOrigin3);
  frame.origins.push_back(kExampleOrigin4);
  session_->OnOriginFrame(frame);
  EXPECT_EQ(3u, session_->received_origins().size());

  EXPECT_TRUE(session_->received_origins().count(origin1));
  EXPECT_TRUE(session_->received_origins().count(origin2));
  EXPECT_TRUE(session_->received_origins().count(origin3));
  EXPECT_TRUE(session_->received_origins().count(origin4));
}

TEST_P(QuicChromiumClientSessionTest, SettingEcn) {
  quic::QuicTagVector copt;
  copt.push_back(quic::kPRGC);  // Prague Cubic congestion control, uses ECT(1).
  config_.SetClientConnectionOptions(copt);
  MockQuicData quic_data(version_);
  char packet[] = {
      0x40, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x01,
      0x00, 0x00, 0x01, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
      0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
  };
  quic_data.AddReadPause();
  quic_data.AddWrite(ASYNC, std::make_unique<quic::QuicEncryptedPacket>(
                                packet, sizeof(packet)));
  quic_data.AddSocketDataToFactory(&socket_factory_);
  Initialize();
  auto* mock_socket = reinterpret_cast<const MockUDPClientSocket*>(
      session_->GetDefaultSocket());

  // The first packet write changes the socket ECN setting.
  EXPECT_EQ(mock_socket->outgoing_ecn(), ECN_NOT_ECT);
  session_->connection()->SetEncrypter(
      quic::ENCRYPTION_FORWARD_SECURE,
      std::make_unique<quic::test::TaggingEncrypter>(0x0a));
  session_->connection()->SendPing();
  EXPECT_EQ(mock_socket->outgoing_ecn(), ECN_ECT1);
}

TEST_P(QuicChromiumClientSessionTest,
       RegisterQuicConnectionClosePayloadOnTlsHandshakeConfirmed) {
  Initialize();

  // Call OnTlsHandshakeConfirmed and verify RegisterQuicConnectionClosePayload
  // is called.
  // Since it's not easy to trigger OnTlsHandshakeConfirmed by injecting read
  // data, this test directly calls OnTlsHandshakeConfirmed().
  EXPECT_CALL(*session_, RegisterQuicConnectionClosePayload());
  session_->OnTlsHandshakeConfirmed();
}

TEST_P(QuicChromiumClientSessionTest,
       RegisterQuicConnectionClosePayloadOnServerConnectionIdRetired) {
  MockQuicData quic_data(version_);
  int packet_num = 1;
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddWrite(ASYNC,
                     client_maker_.MakeInitialSettingsPacket(packet_num++));
  // Set retire_prior_to=1 to retire the currently active connection ID.
  quic::QuicConnectionId new_cid = quic::test::TestConnectionId(123456);
  quic_data.AddRead(ASYNC, server_maker_.Packet(1)
                               .AddNewConnectionIdFrame(new_cid,
                                                        /*sequence_number=*/1u,
                                                        /*retire_prior_to=*/1u)
                               .Build());

  client_maker_.set_connection_id(new_cid);
  quic_data.AddWrite(SYNCHRONOUS, client_maker_.Packet(packet_num++)
                                      .AddAckFrame(/*first_received=*/1,
                                                   /*largest_received=*/1,
                                                   /*smallest_received=*/1)
                                      .AddRetireConnectionIdFrame(
                                          /*sequence_number=*/0)
                                      .Build());

  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);
  Initialize();
  CompleteCryptoHandshake();

  // Make new connection ID available
  quic_data.Resume();
  base::RunLoop().RunUntilIdle();

  // Retire server connection ID and verify that
  // RegisterQuicConnectionClosePayload is called.
  EXPECT_CALL(*session_, RegisterQuicConnectionClosePayload());
  alarm_factory_.FireAlarm(
      quic::test::QuicConnectionPeer::GetRetirePeerIssuedConnectionIdAlarm(
          session_->connection()));

  // Close the connection and verify that UnregisterQuicConnectionClosePayload
  // is called.
  EXPECT_CALL(*session_, UnregisterQuicConnectionClosePayload());
  quic_data.Resume();
  EXPECT_TRUE(quic_data.AllReadDataConsumed());
  EXPECT_TRUE(quic_data.AllWriteDataConsumed());
}

// Test whether we arm pings after send a ping for periodic keep alive.
TEST_P(QuicChromiumClientSessionTest, SendPeriodicPings) {
  MockQuicData quic_data(version_);
  int packet_num = 1;
  quic_data.AddWrite(SYNCHRONOUS,
                     client_maker_.MakeInitialSettingsPacket(packet_num++));
  quic_data.AddWrite(SYNCHRONOUS,
                     client_maker_.Packet(packet_num++).AddPingFrame().Build());
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);

  Initialize();
  // Set the keep alive ping to a relatively short timeout.
  session_->connection()->set_keep_alive_ping_timeout(
      quic::QuicTime::Delta::FromSeconds(10));
  // Set connection keep alive true.
  session_->SetPeriodicConnectionKeepAlive(true);

  EXPECT_TRUE(ping_alarm_.has_value());
  EXPECT_FALSE(ping_alarm_->IsSet());
  CompleteCryptoHandshake();

  // Check that we do not have any outstanding streams, but have the alarm set.
  EXPECT_EQ(0u, session_->GetNumActiveStreams());
  EXPECT_TRUE(ping_alarm_->IsSet());

  EXPECT_EQ(quic::QuicTime::Delta::FromSeconds(10),
            ping_alarm_->deadline() - clock_.ApproximateNow());
  clock_.AdvanceTime(quic::QuicTime::Delta::FromSeconds(10));
  ping_alarm_->Fire();

  // Check if we call keep alive timeout even without any outstanding packets.
  CHECK_EQ(1, keep_alive_timeouts_);
}

// Tests that allow_extended_connect() returns false initially (before server
// sends SETTINGS_ENABLE_CONNECT_PROTOCOL=1), and returns true after receiving
// the setting. Extended CONNECT is required for WebTransport and WebSocket
// support over HTTP/3.
TEST_P(QuicChromiumClientSessionTest, AllowExtendedConnect) {
  MockQuicData quic_data(version_);
  quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);
  Initialize();
  CompleteCryptoHandshake();

  // Initially, allow_extended_connect() should return false because the server
  // hasn't sent SETTINGS_ENABLE_CONNECT_PROTOCOL=1 yet.
  EXPECT_FALSE(session_->allow_extended_connect());

  // Simulate receiving SETTINGS_ENABLE_CONNECT_PROTOCOL=1 from the server.
  session_->OnSetting(quic::SETTINGS_ENABLE_CONNECT_PROTOCOL, 1);

  // Now allow_extended_connect() should return true.
  EXPECT_TRUE(session_->allow_extended_connect());
}

#if BUILDFLAG(ENABLE_WEBSOCKETS)
// Mixed-stream case where GOAWAY visits both a regular HTTP stream and a
// WebSocket stream.
TEST_P(QuicChromiumClientSessionTest,
       OnHttp3GoAwayWithWebSocketStreamDoesNotCrash) {
  MockQuicData quic_data(version_);
  quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);
  Initialize();
  CompleteCryptoHandshake();

  session_->OnSetting(quic::SETTINGS_ENABLE_CONNECT_PROTOCOL, 1);
  ASSERT_TRUE(session_->allow_extended_connect());

  QuicChromiumClientStream* http_stream =
      QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get());
  ASSERT_TRUE(http_stream);
  quic::QuicStreamId http_stream_id = http_stream->id();
  quic::QuicStreamId websocket_stream_id =
      ActivateWebSocketStream(session_.get());

  EXPECT_NE(nullptr, quic::test::QuicSessionPeer::GetStream(session_.get(),
                                                            http_stream_id));
  EXPECT_NE(nullptr, quic::test::QuicSessionPeer::GetStream(
                         session_.get(), websocket_stream_id));

  // Use the smallest valid client-initiated bidirectional stream ID so GOAWAY
  // reaches both the HTTP stream and the WebSocket stream.
  session_->OnHttp3GoAway(/*id=*/0);

  // If the GOAWAY path did not crash, the session should now reject new
  // outgoing streams.
  EXPECT_TRUE(session_->goaway_received());
  EXPECT_EQ(nullptr, QuicChromiumClientSessionPeer::CreateOutgoingStream(
                         session_.get()));
}

// Covers the same mixed-stream setup for CloseSessionOnError(), which walks
// all active streams during session shutdown.
TEST_P(QuicChromiumClientSessionTest,
       CloseSessionOnErrorWithWebSocketStreamDoesNotCrash) {
  MockQuicData quic_data(version_);
  quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);
  Initialize();
  CompleteCryptoHandshake();

  session_->OnSetting(quic::SETTINGS_ENABLE_CONNECT_PROTOCOL, 1);

  ASSERT_TRUE(
      QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get()));
  ActivateWebSocketStream(session_.get());

  session_->CloseSessionOnError(ERR_ABORTED, quic::QUIC_INTERNAL_ERROR,
                                quic::ConnectionCloseBehavior::SILENT_CLOSE);
}
#endif  // BUILDFLAG(ENABLE_WEBSOCKETS)

TEST_P(QuicChromiumClientSessionTest,
       OnNetworkMadeDefault_Redundant_FeatureDisabled) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndDisableFeature(
      features::kQuicIgnoreRedundantOnNetworkMadeDefault);

  MockQuicData quic_data(version_);
  quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);

  // Initialize session with WiFi (kDefaultNetworkForTests) as default.
  default_network_ = kDefaultNetworkForTests;
  Initialize(/*migrate_session_on_network_change_v2=*/true);
  CompleteCryptoHandshake();

  base::HistogramTester histogram_tester;

  // Simulate redundant OnNetworkMadeDefault with the SAME network.
  session_->OnNetworkMadeDefault(kDefaultNetworkForTests);

  // Verify that ALREADY_MIGRATED IS logged because feature is disabled by
  // default.
  histogram_tester.ExpectBucketCount(
      "Net.QuicSession.ConnectionMigration.OnNetworkMadeDefault",
      MIGRATION_STATUS_ALREADY_MIGRATED, 1);
}

TEST_P(QuicChromiumClientSessionTest,
       OnNetworkMadeDefault_Redundant_FeatureEnabled) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndEnableFeature(
      features::kQuicIgnoreRedundantOnNetworkMadeDefault);

  MockQuicData quic_data(version_);
  quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);

  // Initialize session with WiFi (kDefaultNetworkForTests) as default.
  default_network_ = kDefaultNetworkForTests;
  Initialize(/*migrate_session_on_network_change_v2=*/true);
  CompleteCryptoHandshake();

  base::HistogramTester histogram_tester;

  // Simulate redundant OnNetworkMadeDefault with the SAME network.
  session_->OnNetworkMadeDefault(kDefaultNetworkForTests);

  // Verify that ALREADY_MIGRATED is NOT logged because feature is enabled.
  histogram_tester.ExpectBucketCount(
      "Net.QuicSession.ConnectionMigration.OnNetworkMadeDefault",
      MIGRATION_STATUS_ALREADY_MIGRATED, 0);

  // Reset default network in session to invalid using Peer.
  // This simulates that the default network was disconnected, but the session
  // is still running on it.
  QuicChromiumClientSessionPeer::SetDefaultNetwork(
      session_.get(), handles::kInvalidNetworkHandle);

  // Call OnNetworkMadeDefault with WiFi again.
  // Since default_network_ is now invalid, it should NOT return early.
  // Since GetCurrentNetwork() is WiFi, and new_network is WiFi,
  // it should detect we are already on WiFi and log ALREADY_MIGRATED.
  session_->OnNetworkMadeDefault(kDefaultNetworkForTests);

  // Verify that ALREADY_MIGRATED IS logged now.
  histogram_tester.ExpectBucketCount(
      "Net.QuicSession.ConnectionMigration.OnNetworkMadeDefault",
      MIGRATION_STATUS_ALREADY_MIGRATED, 1);
}

TEST_P(QuicChromiumClientSessionTest, NoMigrationForProxiedSessionOnHandshake) {
  ProxyChain proxy_chain(ProxyServer::SCHEME_HTTPS,
                         HostPortPair("proxy.example.com", 443));
  session_key_ = QuicSessionKey(
      kServerHostname, kServerPort, PRIVACY_MODE_DISABLED, proxy_chain,
      SessionUsage::kDestination, SocketTag(), NetworkAnonymizationKey(),
      SecureDnsPolicy::kAllow, /*require_dns_https_alpn=*/false,
      /*disable_cert_verification_network_fetches=*/false,
      handles::kInvalidNetworkHandle);

  // Initialize with kInvalidNetworkHandle so the test socket gets bound to it
  // (simulating QuicProxyDatagramClientSocket's behavior).
  default_network_ = handles::kInvalidNetworkHandle;
  Initialize(/*migrate_session_on_network_change_v2=*/true);

  // Now set the session's default_network_ to a valid handle, simulating the
  // physical user's network. This creates the exact mismatch condition for the
  // bug.
  QuicChromiumClientSessionPeer::SetDefaultNetwork(
      session_.get(), handles::kInvalidNetworkHandle + 1);

  QuicChromiumClientSessionPeer::OnCryptoHandshakeComplete(session_.get());

  // The timer MUST NOT be running for a proxied session.
  EXPECT_FALSE(
      QuicChromiumClientSessionPeer::IsMigrateBackToDefaultNetworkTimerRunning(
          session_.get()));
}

TEST_P(QuicChromiumClientSessionTest, GoingAwaySessionDoesNotKeepAlive) {
  MockQuicData quic_data(version_);
  quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);
  Initialize();
  CompleteCryptoHandshake();

  // Enable periodic ping.
  session_->SetPeriodicConnectionKeepAlive(true);
  EXPECT_TRUE(session_->ShouldKeepConnectionAlive());

  // Mark session as going away.
  session_->SetGoingAwayForTesting(true);

  // If there are no active streams, it should NOT keep connection alive.
  EXPECT_EQ(0u, session_->GetNumActiveStreams());
  EXPECT_FALSE(session_->ShouldKeepConnectionAlive());
}

TEST_P(QuicChromiumClientSessionTest,
       GoingAwaySessionWithActiveStreamsKeepsAlive) {
  MockQuicData quic_data(version_);
  quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);
  Initialize();
  CompleteCryptoHandshake();

  // Enable periodic ping.
  session_->SetPeriodicConnectionKeepAlive(true);
  EXPECT_TRUE(session_->ShouldKeepConnectionAlive());

  // Create an active stream.
  QuicChromiumClientStream* stream =
      QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get());
  EXPECT_TRUE(stream);
  EXPECT_EQ(1u, session_->GetNumActiveStreams());

  // Mark session as going away.
  session_->SetGoingAwayForTesting(true);

  // If there are active streams, it should STILL keep connection alive.
  EXPECT_TRUE(session_->ShouldKeepConnectionAlive());
}

TEST_P(QuicChromiumClientSessionTest, ResumedTicketAgeAccepted) {
  // MockClock starts at 0. Advance it to 10000.
  clock_.AdvanceTime(quic::QuicTime::Delta::FromSeconds(10000));

  crypto_client_stream_factory_.set_handshake_mode(
      MockCryptoClientStream::ZERO_RTT);

  // Since we are in ZERO_RTT, we MUST configure client_maker_ for ZERO_RTT.
  client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);

  MockQuicData quic_data(version_);
  quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);

  Initialize();

  auto* crypto_stream = crypto_client_stream_factory_.last_stream();
  crypto_stream->SetTicketCreationTime(
      quic::QuicWallTime::FromUNIXSeconds(2800));  // 2 hours in the past
  crypto_stream->SetEarlyDataReason(ssl_early_data_accepted);

  base::HistogramTester histogram_tester;

  CompleteCryptoHandshake();

  // The handshake is not complete yet, so no histograms should be logged.
  histogram_tester.ExpectTotalCount(
      "Net.QuicSession.ResumeAttemptTicketAge.All", 0);

  // Now, notify the session that 1-RTT keys are available (completing the
  // handshake).
  crypto_stream->NotifySessionOneRttKeyAvailable();

  histogram_tester.ExpectUniqueTimeSample(
      "Net.QuicSession.ResumeAttemptTicketAge.All", base::Seconds(7200), 1);
  histogram_tester.ExpectUniqueTimeSample(
      "Net.QuicSession.ResumeAttemptTicketAge.Accepted", base::Seconds(7200),
      1);
  histogram_tester.ExpectTotalCount(
      "Net.QuicSession.ResumeAttemptTicketAge.Rejected", 0);
}

TEST_P(QuicChromiumClientSessionTest, ResumedTicketAgeRejected) {
  // MockClock starts at 0. Advance it to 10000.
  clock_.AdvanceTime(quic::QuicTime::Delta::FromSeconds(10000));

  crypto_client_stream_factory_.set_handshake_mode(
      MockCryptoClientStream::ZERO_RTT);

  // Since we are in ZERO_RTT, we MUST configure client_maker_ for ZERO_RTT.
  client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);

  MockQuicData quic_data(version_);
  quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);

  Initialize();

  auto* crypto_stream = crypto_client_stream_factory_.last_stream();
  crypto_stream->SetTicketCreationTime(
      quic::QuicWallTime::FromUNIXSeconds(6400));  // 1 hour in the past
  crypto_stream->SetEarlyDataReason(ssl_early_data_peer_declined);

  base::HistogramTester histogram_tester;

  CompleteCryptoHandshake();

  // Handshake is not complete yet.
  histogram_tester.ExpectTotalCount(
      "Net.QuicSession.ResumeAttemptTicketAge.All", 0);

  // Notify the session that 1-RTT keys are available (completing the
  // handshake).
  crypto_stream->NotifySessionOneRttKeyAvailable();

  histogram_tester.ExpectUniqueTimeSample(
      "Net.QuicSession.ResumeAttemptTicketAge.All", base::Seconds(3600), 1);
  histogram_tester.ExpectUniqueTimeSample(
      "Net.QuicSession.ResumeAttemptTicketAge.Rejected", base::Seconds(3600),
      1);
  histogram_tester.ExpectTotalCount(
      "Net.QuicSession.ResumeAttemptTicketAge.Accepted", 0);
}

TEST_P(QuicChromiumClientSessionTest, ResumedTicketAgeNotAttempted) {
  // MockClock starts at 0. Advance it to 10000.
  clock_.AdvanceTime(quic::QuicTime::Delta::FromSeconds(10000));

  crypto_client_stream_factory_.set_handshake_mode(
      MockCryptoClientStream::ZERO_RTT);

  // Since we are in ZERO_RTT, we MUST configure client_maker_ for ZERO_RTT.
  client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);

  MockQuicData quic_data(version_);
  quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);

  Initialize();

  auto* crypto_stream = crypto_client_stream_factory_.last_stream();
  crypto_stream->SetTicketCreationTime(
      quic::QuicWallTime::FromUNIXSeconds(6400));  // 1 hour in the past
  crypto_stream->SetEarlyDataReason(ssl_early_data_disabled);

  base::HistogramTester histogram_tester;

  CompleteCryptoHandshake();

  // Handshake is not complete yet.
  histogram_tester.ExpectTotalCount(
      "Net.QuicSession.ResumeAttemptTicketAge.All", 0);

  // Notify the session that 1-RTT keys are available (completing the
  // handshake).
  crypto_stream->NotifySessionOneRttKeyAvailable();

  // 0-RTT was not attempted (disabled), so no ticket age histograms should be
  // logged.
  histogram_tester.ExpectTotalCount(
      "Net.QuicSession.ResumeAttemptTicketAge.All", 0);
  histogram_tester.ExpectTotalCount(
      "Net.QuicSession.ResumeAttemptTicketAge.Accepted", 0);
  histogram_tester.ExpectTotalCount(
      "Net.QuicSession.ResumeAttemptTicketAge.Rejected", 0);
}
TEST_P(QuicChromiumClientSessionTest, ReadMultiplePackets) {
  base::test::ScopedFeatureList scoped_feature_list;
  scoped_feature_list.InitAndEnableFeature(features::kQuicUseReadMultiple);

  base::MetricsSubSampler::ScopedAlwaysSampleForTesting always_sample;
  base::HistogramTester histogram_tester;

  socket_data_.reset();

  MockQuicData quic_data(version_);
  int packet_num = 1;
  int peer_packet_num = 1;

  quic_data.AddWrite(SYNCHRONOUS,
                     client_maker_.MakeInitialSettingsPacket(packet_num++));

  quic_data.AddRead(
      ASYNC, server_maker_.Packet(peer_packet_num++).AddPingFrame().Build());
  quic_data.AddRead(
      SYNCHRONOUS,
      server_maker_.Packet(peer_packet_num++).AddPingFrame().Build());

  quic_data.AddWrite(
      SYNCHRONOUS,
      client_maker_.Packet(packet_num++).AddAckFrame(1, 2, 1).Build());

  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);

  quic_data.AddSocketDataToFactory(&socket_factory_);

  Initialize();
  CompleteCryptoHandshake();

  EXPECT_TRUE(base::test::RunUntil([&]() {
    return session_->connection()->GetStats().packets_received == 2u;
  }));

  // Verify that the histogram logged 2 packets read.
  histogram_tester.ExpectUniqueSample("Net.QuicSession.ReadMultipleNumPackets",
                                      2, 1);

  // Verify that both packets were processed.
  EXPECT_EQ(session_->connection()->GetStats().packets_received, 2u);
}

TEST_P(QuicChromiumClientSessionTest, ReadMultiplePacketsWithError) {
  base::test::ScopedFeatureList scoped_feature_list;
  scoped_feature_list.InitAndEnableFeature(features::kQuicUseReadMultiple);

  base::MetricsSubSampler::ScopedAlwaysSampleForTesting always_sample;
  base::HistogramTester histogram_tester;

  socket_data_.reset();

  MockQuicData quic_data(version_);
  int packet_num = 1;
  int peer_packet_num = 1;

  quic_data.AddWrite(SYNCHRONOUS,
                     client_maker_.MakeInitialSettingsPacket(packet_num++));

  // First packet read asynchronously.
  quic_data.AddRead(
      ASYNC, server_maker_.Packet(peer_packet_num++).AddPingFrame().Build());
  // Next packet read synchronously.
  quic_data.AddRead(
      SYNCHRONOUS,
      server_maker_.Packet(peer_packet_num++).AddPingFrame().Build());

  // ACK for packets 1 and 2.
  quic_data.AddWrite(
      ASYNC, client_maker_.Packet(packet_num++).AddAckFrame(1, 2, 1).Build());

  // Third read returns an error asynchronously (waits for ACK 1-2).
  quic_data.AddRead(ASYNC, ERR_MSG_TOO_BIG);

  quic_data.AddSocketDataToFactory(&socket_factory_);

  Initialize();
  CompleteCryptoHandshake();

  // Run the loop.
  EXPECT_TRUE(base::test::RunUntil(
      [&]() { return !session_->connection()->connected(); }));

  // Verify that the connection is closed due to the error.
  EXPECT_FALSE(session_->connection()->connected());

  // Verify that the histogram logged the 2 packets read before the error.
  histogram_tester.ExpectUniqueSample("Net.QuicSession.ReadMultipleNumPackets",
                                      2, 1);

  // Verify that only 2 packets were processed (packet 3 was an error).
  EXPECT_EQ(session_->connection()->GetStats().packets_received, 2u);
  EXPECT_EQ(session_->error(), quic::QUIC_PACKET_READ_ERROR);
  EXPECT_EQ(session_->error_details(), ErrorToString(ERR_MSG_TOO_BIG));
}

// This test verifies that QuicChromiumPacketReader is resilient to re-entrant
// calls to StartReading() during packet processing.
//
// Re-entrancy is an existing pattern in the QUIC session. During packet
// processing (inside `Visitor::OnPacket()`), the session may trigger events
// that result in a call back into `StartReading()`. Real-world cases include:
// 1) Connection Migration: Processing a packet might trigger a write (e.g.
// ACK)
//    which fails, or path degradation is detected, causing the session to
//    migrate to a new socket and call `StartReading()` on the readers.
// 2) Write Blocked Recovery: A write triggered by a read packet might succeed
//    or fail in a way that causes the session to reset or resume its read
//    loop.
// 3) Connectivity Probing: Receiving a probing packet might cause the session
//    to activate a probing reader and call `StartReading()`.
//
// The old single-read (recvmsg) implementation was naturally resilient to
// this because `read_pending_` remained `true` throughout the entire
// read-and-process cycle (including while yielded). Any re-entrant call to
// `StartReading()` would hit the `if (read_pending_) return;` guard and
// safely no-op.
//
// This test ensures that the new batched ReadMultiple (recvmmsg) path
// maintains this resilience by safely no-oping on re-entrant calls instead of
// attempting a corrupting concurrent read or crashing due to a non-empty
// packet queue.
TEST_P(QuicChromiumClientSessionTest,
       ReadMultiplePacketsReentrantStartReading) {
  base::test::ScopedFeatureList scoped_feature_list;
  scoped_feature_list.InitAndEnableFeature(features::kQuicUseReadMultiple);

  base::MetricsSubSampler::ScopedAlwaysSampleForTesting always_sample;

  socket_data_.reset();

  MockQuicData quic_data(version_);
  int packet_num = 1;
  int peer_packet_num = 1;

  quic_data.AddWrite(SYNCHRONOUS,
                     client_maker_.MakeInitialSettingsPacket(packet_num++));

  // We need at least 2 packets in the batch to trigger the scenario.
  quic_data.AddRead(
      ASYNC, server_maker_.Packet(peer_packet_num++).AddPingFrame().Build());
  quic_data.AddRead(
      SYNCHRONOUS,
      server_maker_.Packet(peer_packet_num++).AddPingFrame().Build());

  quic_data.AddWrite(
      SYNCHRONOUS,
      client_maker_.Packet(packet_num++).AddAckFrame(1, 2, 1).Build());

  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);

  quic_data.AddSocketDataToFactory(&socket_factory_);

  Initialize();
  CompleteCryptoHandshake();

  // Set up the re-entrant call. When the first packet is processed,
  // we call StartReading() which should be a no-op (return early)
  // because the reader is busy processing the batch.
  // If the fix is NOT in place, this will crash on
  // CHECK(pending_datagrams_.empty()).
  session_->set_on_packet_callback(
      base::BindLambdaForTesting([&]() { session_->StartReading(); }));

  EXPECT_TRUE(base::test::RunUntil([&]() {
    return session_->connection()->GetStats().packets_received == 2u;
  }));

  EXPECT_EQ(session_->connection()->GetStats().packets_received, 2u);
}

// Test that, if EchMode is kDisabled, ECH GREASE is disabled in QuicSSLConfig.
TEST_P(QuicChromiumClientSessionTest, ECHModeDisabled) {
  ssl_config_service_.SetEchModeGetter(
      std::make_unique<TestStaticEchModeGetter>(EchMode::kDisabled,
                                                kServerHostname));
  MockQuicData quic_data(version_);
  quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);
  Initialize();

  quic::QuicSSLConfig config = session_->GetSSLConfig();
  EXPECT_FALSE(config.ech_grease_enabled);
  EXPECT_FALSE(config.reject_unusable_ech_config);
  EXPECT_TRUE(config.ech_config_list.empty());

  CompleteCryptoHandshake();
}

// Test that, if EchMode is kStrict and ECH configs are missing, CryptoConnect
// returns ERR_STRICT_ECH_REQUIRED.
TEST_P(QuicChromiumClientSessionTest, ECHModeStrictMissingConfig) {
  ssl_config_service_.SetEchModeGetter(
      std::make_unique<TestStaticEchModeGetter>(EchMode::kStrict,
                                                kServerHostname));
  Initialize();

  // ech_config_list_ is empty by default in tests.
  EXPECT_THAT(session_->CryptoConnect(callback_.callback()),
              IsError(ERR_STRICT_ECH_REQUIRED));
}

// Test that, if EchMode is kStrict and ECH configs are available,
// QuicSSLConfig is correctly populated.
TEST_P(QuicChromiumClientSessionTest, ECHModeStrictWithConfigs) {
  ssl_config_service_.SetEchModeGetter(
      std::make_unique<TestStaticEchModeGetter>(EchMode::kStrict,
                                                kServerHostname));
  std::vector<uint8_t> ech_config_list = {1, 2, 3, 4};
  MockQuicData quic_data(version_);
  quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);
  Initialize();

  test::QuicChromiumClientSessionPeer::SetEchConfigList(session_.get(),
                                                        ech_config_list);

  quic::QuicSSLConfig config = session_->GetSSLConfig();
  EXPECT_TRUE(config.ech_grease_enabled);
  EXPECT_TRUE(config.reject_unusable_ech_config);
  EXPECT_EQ(config.ech_config_list,
            std::string(ech_config_list.begin(), ech_config_list.end()));

  CompleteCryptoHandshake();
}

// Test that, if EchMode is kStrict and ECH configs are unusable, CryptoConnect
// fails.
TEST_P(QuicChromiumClientSessionTest, ECHModeStrictUnusableConfig) {
  ssl_config_service_.SetEchModeGetter(
      std::make_unique<TestStaticEchModeGetter>(EchMode::kStrict,
                                                kServerHostname));
  std::vector<uint8_t> ech_config_list;
  bssl::UniquePtr<SSL_ECH_KEYS> keys =
      MakeTestEchKeys(kServerHostname, /*max_name_len=*/64, &ech_config_list);
  ASSERT_TRUE(keys);
  ASSERT_GT(ech_config_list.size(), 4u);
  ech_config_list[2] ^= 1;

  client_maker_.SetEncryptionLevel(quic::ENCRYPTION_INITIAL);
  MockQuicData quic_data(version_);
  quic_data.AddWrite(SYNCHRONOUS, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);

  InitializeWithoutMockCrypto();
  test::QuicChromiumClientSessionPeer::SetEchConfigList(session_.get(),
                                                        ech_config_list);

  EXPECT_THAT(session_->CryptoConnect(callback_.callback()),
              IsError(ERR_QUIC_HANDSHAKE_FAILED));
}

// Test that, if EchMode is kOpportunistic, ECH GREASE is enabled in
// QuicSSLConfig.
TEST_P(QuicChromiumClientSessionTest, ECHModeOpportunistic) {
  ssl_config_service_.SetEchModeGetter(
      std::make_unique<TestStaticEchModeGetter>(EchMode::kOpportunistic,
                                                kServerHostname));
  MockQuicData quic_data(version_);
  quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);
  Initialize();

  quic::QuicSSLConfig config = session_->GetSSLConfig();
  EXPECT_TRUE(config.ech_grease_enabled);
  EXPECT_FALSE(config.reject_unusable_ech_config);
  EXPECT_TRUE(config.ech_config_list.empty());

  CompleteCryptoHandshake();
}

// Test that, if EchMode is kOpportunistic and ECH configs are unusable,
// CryptoConnect silently succeeds without ECH.
TEST_P(QuicChromiumClientSessionTest, ECHModeOpportunisticUnusableConfig) {
  ssl_config_service_.SetEchModeGetter(
      std::make_unique<TestStaticEchModeGetter>(EchMode::kOpportunistic,
                                                kServerHostname));
  std::vector<uint8_t> ech_config_list;
  bssl::UniquePtr<SSL_ECH_KEYS> keys =
      MakeTestEchKeys(kServerHostname, /*max_name_len=*/64, &ech_config_list);
  ASSERT_TRUE(keys);
  ASSERT_GT(ech_config_list.size(), 4u);
  ech_config_list[2] ^= 1;

  MockQuicData quic_data(version_);
  quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
  quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
  quic_data.AddSocketDataToFactory(&socket_factory_);
  Initialize();
  test::QuicChromiumClientSessionPeer::SetEchConfigList(session_.get(),
                                                        ech_config_list);

  CompleteCryptoHandshake();
}

TEST_P(QuicChromiumClientSessionTest, GoogleSearchSessionMetricsUnused) {
  base::HistogramTester histogram_tester;
  QuicConnectionReuseDetails reuse_details;
  reuse_details.establishment_reason =
      QuicSessionEstablishmentReason::kSessionExistedAndWasPreconnect;
  Initialize(MultiplexedSessionCreationInitiator::kPreconnect, reuse_details);
  QuicChromiumClientSessionPeer::SetHostname(session_.get(), "www.google.com");
  session_.reset();
  histogram_tester.ExpectUniqueSample(
      "Net.QuicSession.GoogleSearch.SessionCreationInitiator.Unused",
      MultiplexedSessionCreationInitiator::kPreconnect, 1);
  histogram_tester.ExpectUniqueSample(
      "Net.QuicSession.GoogleSearch.EstablishmentReason.Unused",
      QuicSessionEstablishmentReason::kSessionExistedAndWasPreconnect, 1);
  histogram_tester.ExpectUniqueSample(
      "Net.QuicSession.GoogleSearch.EstablishmentReason.Preconnect.Unused",
      QuicSessionEstablishmentReason::kSessionExistedAndWasPreconnect, 1);
  histogram_tester.ExpectTotalCount(
      "Net.QuicSession.GoogleSearch.EstablishmentReason.NonPreconnect.Unused",
      0);
}

TEST_P(QuicChromiumClientSessionTest, GoogleSearchSessionMetricsUsed) {
  base::HistogramTester histogram_tester;
  QuicConnectionReuseDetails reuse_details;
  reuse_details.establishment_reason =
      QuicSessionEstablishmentReason::kSessionExistedButNotPreconnect;
  Initialize(MultiplexedSessionCreationInitiator::kUnknown, reuse_details);
  QuicChromiumClientSessionPeer::SetHostname(session_.get(), "www.google.com");
  QuicChromiumClientSessionPeer::SetNumTotalStreamsForTesting(session_.get(),
                                                              1);
  session_.reset();
  histogram_tester.ExpectUniqueSample(
      "Net.QuicSession.GoogleSearch.SessionCreationInitiator.Used",
      MultiplexedSessionCreationInitiator::kUnknown, 1);
  histogram_tester.ExpectUniqueSample(
      "Net.QuicSession.GoogleSearch.EstablishmentReason.Used",
      QuicSessionEstablishmentReason::kSessionExistedButNotPreconnect, 1);
  histogram_tester.ExpectUniqueSample(
      "Net.QuicSession.GoogleSearch.EstablishmentReason.NonPreconnect.Used",
      QuicSessionEstablishmentReason::kSessionExistedButNotPreconnect, 1);
  histogram_tester.ExpectTotalCount(
      "Net.QuicSession.GoogleSearch.EstablishmentReason.Preconnect.Used", 0);
}

TEST_P(QuicChromiumClientSessionTest, GoogleSearchSessionMetricsNoSession) {
  base::HistogramTester histogram_tester;
  QuicConnectionReuseDetails reuse_details;
  reuse_details.establishment_reason =
      QuicSessionEstablishmentReason::kNoSessionExisted;
  Initialize(MultiplexedSessionCreationInitiator::kUnknown, reuse_details);
  QuicChromiumClientSessionPeer::SetHostname(session_.get(), "www.google.com");
  session_.reset();
  histogram_tester.ExpectUniqueSample(
      "Net.QuicSession.GoogleSearch.EstablishmentReason.Unused",
      QuicSessionEstablishmentReason::kNoSessionExisted, 1);
  histogram_tester.ExpectUniqueSample(
      "Net.QuicSession.GoogleSearch.EstablishmentReason.NonPreconnect.Unused",
      QuicSessionEstablishmentReason::kNoSessionExisted, 1);
  histogram_tester.ExpectTotalCount(
      "Net.QuicSession.GoogleSearch.EstablishmentReason.Preconnect.Unused", 0);
}

TEST_P(QuicChromiumClientSessionTest,
       GoogleSearchSessionMetricsPreconnectUsed) {
  base::HistogramTester histogram_tester;
  QuicConnectionReuseDetails reuse_details;
  reuse_details.establishment_reason =
      QuicSessionEstablishmentReason::kSessionExistedAndWasPreconnect;
  Initialize(MultiplexedSessionCreationInitiator::kPreconnect, reuse_details);
  QuicChromiumClientSessionPeer::SetHostname(session_.get(), "www.google.com");
  QuicChromiumClientSessionPeer::SetNumTotalStreamsForTesting(session_.get(),
                                                              1);
  session_.reset();
  histogram_tester.ExpectUniqueSample(
      "Net.QuicSession.GoogleSearch.SessionCreationInitiator.Used",
      MultiplexedSessionCreationInitiator::kPreconnect, 1);
  histogram_tester.ExpectUniqueSample(
      "Net.QuicSession.GoogleSearch.EstablishmentReason.Used",
      QuicSessionEstablishmentReason::kSessionExistedAndWasPreconnect, 1);
  histogram_tester.ExpectUniqueSample(
      "Net.QuicSession.GoogleSearch.EstablishmentReason.Preconnect.Used",
      QuicSessionEstablishmentReason::kSessionExistedAndWasPreconnect, 1);
  histogram_tester.ExpectTotalCount(
      "Net.QuicSession.GoogleSearch.EstablishmentReason.NonPreconnect.Used", 0);
}

TEST_P(QuicChromiumClientSessionTest,
       GoogleSearchSessionMetricsNonReuseReason) {
  base::HistogramTester histogram_tester;
  QuicConnectionReuseDetails reuse_details;
  reuse_details.establishment_reason =
      QuicSessionEstablishmentReason::kNoSessionExisted;
  reuse_details.non_reuse_reason =
      QuicSessionNonReuseReason::kNoSessionExisted_KeyMismatch_SocketTag;
  Initialize(MultiplexedSessionCreationInitiator::kUnknown, reuse_details);
  QuicChromiumClientSessionPeer::SetHostname(session_.get(), "www.google.com");
  session_.reset();
  histogram_tester.ExpectUniqueSample(
      "Net.QuicSession.GoogleSearch.NonReuseReason.Unused",
      QuicSessionNonReuseReason::kNoSessionExisted_KeyMismatch_SocketTag, 1);
  histogram_tester.ExpectUniqueSample(
      "Net.QuicSession.GoogleSearch.NonReuseReason.NonPreconnect.Unused",
      QuicSessionNonReuseReason::kNoSessionExisted_KeyMismatch_SocketTag, 1);
  histogram_tester.ExpectTotalCount(
      "Net.QuicSession.GoogleSearch.NonReuseReason.Preconnect.Unused", 0);
  histogram_tester.ExpectUniqueSample(
      "Net.QuicSession.GoogleSearch.EstablishmentReason.Unused",
      QuicSessionEstablishmentReason::kNoSessionExisted, 1);
}

TEST_P(QuicChromiumClientSessionTest,
       GoogleSearchSessionMetricsNonReuseReason_MultipleFields) {
  base::HistogramTester histogram_tester;
  QuicConnectionReuseDetails reuse_details;
  reuse_details.establishment_reason =
      QuicSessionEstablishmentReason::kNoSessionExisted;
  reuse_details.non_reuse_reason =
      QuicSessionNonReuseReason::kNoSessionExisted_KeyMismatch_MultipleFields;
  Initialize(MultiplexedSessionCreationInitiator::kUnknown, reuse_details);
  QuicChromiumClientSessionPeer::SetHostname(session_.get(), "www.google.com");
  session_.reset();
  histogram_tester.ExpectUniqueSample(
      "Net.QuicSession.GoogleSearch.NonReuseReason.Unused",
      QuicSessionNonReuseReason::kNoSessionExisted_KeyMismatch_MultipleFields,
      1);
  histogram_tester.ExpectUniqueSample(
      "Net.QuicSession.GoogleSearch.NonReuseReason.NonPreconnect.Unused",
      QuicSessionNonReuseReason::kNoSessionExisted_KeyMismatch_MultipleFields,
      1);
  histogram_tester.ExpectTotalCount(
      "Net.QuicSession.GoogleSearch.NonReuseReason.Preconnect.Unused", 0);
  histogram_tester.ExpectUniqueSample(
      "Net.QuicSession.GoogleSearch.EstablishmentReason.Unused",
      QuicSessionEstablishmentReason::kNoSessionExisted, 1);
}

TEST_P(QuicChromiumClientSessionTest,
       GoogleSearchSessionMetricsNonReuseReason_MultipleReasons) {
  base::HistogramTester histogram_tester;
  QuicConnectionReuseDetails reuse_details;
  reuse_details.establishment_reason =
      QuicSessionEstablishmentReason::kSessionExistedBoth;
  reuse_details.non_reuse_reason =
      QuicSessionNonReuseReason::kSessionExisted_MultipleReasons;
  Initialize(MultiplexedSessionCreationInitiator::kUnknown, reuse_details);
  QuicChromiumClientSessionPeer::SetHostname(session_.get(), "www.google.com");
  session_.reset();
  histogram_tester.ExpectUniqueSample(
      "Net.QuicSession.GoogleSearch.NonReuseReason.Unused",
      QuicSessionNonReuseReason::kSessionExisted_MultipleReasons, 1);
  histogram_tester.ExpectUniqueSample(
      "Net.QuicSession.GoogleSearch.NonReuseReason.NonPreconnect.Unused",
      QuicSessionNonReuseReason::kSessionExisted_MultipleReasons, 1);
  histogram_tester.ExpectTotalCount(
      "Net.QuicSession.GoogleSearch.NonReuseReason.Preconnect.Unused", 0);
  histogram_tester.ExpectUniqueSample(
      "Net.QuicSession.GoogleSearch.EstablishmentReason.Unused",
      QuicSessionEstablishmentReason::kSessionExistedBoth, 1);
}

TEST_P(QuicChromiumClientSessionTest,
       GoogleSearchSessionMetricsNonReuseReason_PreconnectUsed) {
  base::HistogramTester histogram_tester;
  QuicConnectionReuseDetails reuse_details;
  reuse_details.establishment_reason =
      QuicSessionEstablishmentReason::kSessionExistedAndWasPreconnect;
  reuse_details.non_reuse_reason =
      QuicSessionNonReuseReason::kSessionExisted_ServerGoaway;
  Initialize(MultiplexedSessionCreationInitiator::kPreconnect, reuse_details);
  QuicChromiumClientSessionPeer::SetHostname(session_.get(), "www.google.com");
  QuicChromiumClientSessionPeer::SetNumTotalStreamsForTesting(session_.get(),
                                                              1);
  session_.reset();
  histogram_tester.ExpectUniqueSample(
      "Net.QuicSession.GoogleSearch.NonReuseReason.Used",
      QuicSessionNonReuseReason::kSessionExisted_ServerGoaway, 1);
  histogram_tester.ExpectUniqueSample(
      "Net.QuicSession.GoogleSearch.NonReuseReason.Preconnect.Used",
      QuicSessionNonReuseReason::kSessionExisted_ServerGoaway, 1);
  histogram_tester.ExpectTotalCount(
      "Net.QuicSession.GoogleSearch.NonReuseReason.NonPreconnect.Used", 0);
  histogram_tester.ExpectUniqueSample(
      "Net.QuicSession.GoogleSearch.EstablishmentReason.Used",
      QuicSessionEstablishmentReason::kSessionExistedAndWasPreconnect, 1);
  histogram_tester.ExpectUniqueSample(
      "Net.QuicSession.GoogleSearch.EstablishmentReason.Preconnect.Used",
      QuicSessionEstablishmentReason::kSessionExistedAndWasPreconnect, 1);
  histogram_tester.ExpectTotalCount(
      "Net.QuicSession.GoogleSearch.EstablishmentReason.NonPreconnect.Used", 0);
}

}  // namespace
}  // namespace net::test
