// Copyright 2019 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/socket/ssl_connect_job.h"

#include <memory>
#include <string>

#include "base/compiler_specific.h"
#include "base/containers/extend.h"
#include "base/functional/callback.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "net/base/auth.h"
#include "net/base/features.h"
#include "net/base/host_port_pair.h"
#include "net/base/load_timing_info.h"
#include "net/base/net_errors.h"
#include "net/base/network_anonymization_key.h"
#include "net/base/network_handle.h"
#include "net/base/network_isolation_key.h"
#include "net/base/proxy_chain.h"
#include "net/base/proxy_server.h"
#include "net/base/proxy_string_util.h"
#include "net/cert/mock_cert_verifier.h"
#include "net/dns/mock_host_resolver.h"
#include "net/dns/public/secure_dns_policy.h"
#include "net/http/http_auth_handler_factory.h"
#include "net/http/http_network_session.h"
#include "net/http/http_proxy_connect_job.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_server_properties.h"
#include "net/http/transport_security_state.h"
#include "net/log/net_log_source.h"
#include "net/log/net_log_with_source.h"
#include "net/log/test_net_log.h"
#include "net/log/test_net_log_util.h"
#include "net/proxy_resolution/configured_proxy_resolution_service.h"
#include "net/quic/quic_context.h"
#include "net/socket/connect_job_test_util.h"
#include "net/socket/connection_attempts.h"
#include "net/socket/next_proto.h"
#include "net/socket/socket_tag.h"
#include "net/socket/socket_test_util.h"
#include "net/socket/socks_connect_job.h"
#include "net/socket/transport_connect_job.h"
#include "net/ssl/ssl_config_service_defaults.h"
#include "net/ssl/ssl_connection_status_flags.h"
#include "net/ssl/test_ssl_config_service.h"
#include "net/ssl/test_static_ech_mode_getter.h"
#include "net/test/cert_builder.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_certificate_data.h"
#include "net/test/test_data_directory.h"
#include "net/test/test_with_task_environment.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "net/url_request/static_http_user_agent_settings.h"
#include "testing/gtest/include/gtest/gtest.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"

namespace net {
namespace {

IPAddress ParseIP(const std::string& ip) {
  IPAddress address;
  CHECK(address.AssignFromIPLiteral(ip));
  return address;
}

// Just check that all connect times are set to base::TimeTicks::Now(), for
// tests that don't update the mocked out time.
void CheckConnectTimesSet(const LoadTimingInfo::ConnectTiming& connect_timing) {
  EXPECT_EQ(base::TimeTicks::Now(), connect_timing.domain_lookup_start);
  EXPECT_EQ(base::TimeTicks::Now(), connect_timing.domain_lookup_end);
  EXPECT_EQ(base::TimeTicks::Now(), connect_timing.connect_start);
  EXPECT_EQ(base::TimeTicks::Now(), connect_timing.ssl_start);
  EXPECT_EQ(base::TimeTicks::Now(), connect_timing.ssl_end);
  EXPECT_EQ(base::TimeTicks::Now(), connect_timing.connect_end);
}

// Just check that all connect times are set to base::TimeTicks::Now(), except
// for DNS times, for tests that don't update the mocked out time and use a
// proxy.
void CheckConnectTimesExceptDnsSet(
    const LoadTimingInfo::ConnectTiming& connect_timing) {
  EXPECT_TRUE(connect_timing.domain_lookup_start.is_null());
  EXPECT_TRUE(connect_timing.domain_lookup_end.is_null());
  EXPECT_EQ(base::TimeTicks::Now(), connect_timing.connect_start);
  EXPECT_EQ(base::TimeTicks::Now(), connect_timing.ssl_start);
  EXPECT_EQ(base::TimeTicks::Now(), connect_timing.ssl_end);
  EXPECT_EQ(base::TimeTicks::Now(), connect_timing.connect_end);
}

const url::SchemeHostPort kHostHttps{url::kHttpsScheme, "host", 443};
const HostPortPair kHostHttp{"host", 80};
const ProxyServer kSocksProxyServer{ProxyServer::SCHEME_SOCKS5,
                                    HostPortPair("sockshost", 443)};
const ProxyServer kHttpProxyServer{ProxyServer::SCHEME_HTTP,
                                   HostPortPair("proxy", 443)};

const ProxyChain kHttpProxyChain{kHttpProxyServer};


// Test fixture runs with Happy Eyeballs v2 both enabled and disabled, based on
// the boolean test parameter.
//
// TODO(https://crbug.com/484073410): Remove the param, once HappyEyeballs v2 is
// enabled by default.
class SSLConnectJobTestBase : public ::testing::Test {
 public:
  SSLConnectJobTestBase()
      : proxy_resolution_service_(
            ConfiguredProxyResolutionService::CreateDirect()),
        ssl_config_service_(
            std::make_unique<TestSSLConfigService>(SSLContextConfig())),
        http_auth_handler_factory_(HttpAuthHandlerFactory::CreateDefault()),
        session_(CreateNetworkSession()),
        common_connect_job_params_(session_->CreateCommonConnectJobParams()) {}

  ~SSLConnectJobTestBase() override = default;

  scoped_refptr<TransportSocketParams> CreateDirectTransportSocketParams(
      SecureDnsPolicy secure_dns_policy) const {
    return base::MakeRefCounted<TransportSocketParams>(
        kHostHttps, NetworkAnonymizationKey(), secure_dns_policy,
        handles::kInvalidNetworkHandle, OnHostResolutionCallback(),
        /*supported_alpns=*/base::flat_set<std::string>({"h2", "http/1.1"}));
  }

  scoped_refptr<TransportSocketParams> CreateProxyTransportSocketParams(
      SecureDnsPolicy secure_dns_policy) const {
    return base::MakeRefCounted<TransportSocketParams>(
        kHttpProxyServer.host_port_pair(), NetworkAnonymizationKey(),
        secure_dns_policy, handles::kInvalidNetworkHandle,
        OnHostResolutionCallback(),
        /*supported_alpns=*/base::flat_set<std::string>({}));
  }

  scoped_refptr<SOCKSSocketParams> CreateSOCKSSocketParams(
      SecureDnsPolicy secure_dns_policy) {
    return base::MakeRefCounted<SOCKSSocketParams>(
        ConnectJobParams(CreateProxyTransportSocketParams(secure_dns_policy)),
        kSocksProxyServer.scheme() == ProxyServer::SCHEME_SOCKS5,
        kSocksProxyServer.host_port_pair(), NetworkAnonymizationKey(),
        TRAFFIC_ANNOTATION_FOR_TESTS);
  }

  scoped_refptr<HttpProxySocketParams> CreateHttpProxySocketParams(
      SecureDnsPolicy secure_dns_policy) {
    return base::MakeRefCounted<HttpProxySocketParams>(
        ConnectJobParams(CreateProxyTransportSocketParams(secure_dns_policy)),
        kHostHttp, kHttpProxyChain,
        /*proxy_server_index=*/0,
        /*tunnel=*/true, TRAFFIC_ANNOTATION_FOR_TESTS,
        NetworkAnonymizationKey(), secure_dns_policy,
        handles::kInvalidNetworkHandle);
  }

  std::unique_ptr<ConnectJob> CreateConnectJob(
      TestConnectJobDelegate* test_delegate,
      ProxyChain proxy_chain = ProxyChain::Direct(),
      RequestPriority priority = DEFAULT_PRIORITY,
      SecureDnsPolicy secure_dns_policy = SecureDnsPolicy::kAllow) {
    return std::make_unique<SSLConnectJob>(
        priority, SocketTag(), &common_connect_job_params_,
        CreateSSLSocketParams(proxy_chain, secure_dns_policy), test_delegate,
        /*net_log=*/nullptr);
  }

  scoped_refptr<SSLSocketParams> CreateSSLSocketParams(
      ProxyChain proxy_chain,
      SecureDnsPolicy secure_dns_policy) {
    return base::MakeRefCounted<SSLSocketParams>(
        proxy_chain == ProxyChain::Direct()
            ? ConnectJobParams(
                  CreateDirectTransportSocketParams(secure_dns_policy))
        : proxy_chain.is_single_proxy() &&
                proxy_chain.First().scheme() == ProxyServer::SCHEME_SOCKS5
            ? ConnectJobParams(CreateSOCKSSocketParams(secure_dns_policy))
        : proxy_chain.is_single_proxy() &&
                proxy_chain.First().scheme() == ProxyServer::SCHEME_HTTP
            ? ConnectJobParams(CreateHttpProxySocketParams(secure_dns_policy))
            : ConnectJobParams(),
        HostPortPair::FromSchemeHostPort(kHostHttps), SSLConfig(),
        NetworkAnonymizationKey());
  }

  void AddAuthToCache() {
    const std::u16string kFoo(u"foo");
    const std::u16string kBar(u"bar");
    session_->http_auth_cache()->Add(
        url::SchemeHostPort(GURL("http://proxy:443/")), HttpAuth::AUTH_PROXY,
        "MyRealm1", HttpAuth::AUTH_SCHEME_BASIC, NetworkAnonymizationKey(),
        "Basic realm=MyRealm1", AuthCredentials(kFoo, kBar), "/");
  }

  std::unique_ptr<HttpNetworkSession> CreateNetworkSession() {
    HttpNetworkSessionContext session_context;
    session_context.host_resolver = &host_resolver_;
    session_context.cert_verifier = &cert_verifier_;
    session_context.transport_security_state = &transport_security_state_;
    session_context.proxy_resolution_service = proxy_resolution_service_.get();
    session_context.client_socket_factory = &socket_factory_;
    session_context.ssl_config_service = ssl_config_service_.get();
    session_context.http_auth_handler_factory =
        http_auth_handler_factory_.get();
    session_context.http_server_properties = &http_server_properties_;
    session_context.net_log = NetLog::Get();
    session_context.http_user_agent_settings = &http_user_agent_settings_;
    session_context.quic_context = &quic_context_;
    return std::make_unique<HttpNetworkSession>(HttpNetworkSessionParams(),
                                                session_context);
  }

 protected:
  base::test::ScopedFeatureList scoped_feature_list_;
  base::test::TaskEnvironment task_environment_{
      base::test::TaskEnvironment::TimeSource::MOCK_TIME};

  MockClientSocketFactory socket_factory_;
  MockHostResolver host_resolver_{/*default_result=*/MockHostResolverBase::
                                      RuleResolver::GetLocalhostResult()};
  MockCertVerifier cert_verifier_;
  TransportSecurityState transport_security_state_;
  const std::unique_ptr<ProxyResolutionService> proxy_resolution_service_;
  const std::unique_ptr<TestSSLConfigService> ssl_config_service_;
  const std::unique_ptr<HttpAuthHandlerFactory> http_auth_handler_factory_;
  const StaticHttpUserAgentSettings http_user_agent_settings_ = {"*",
                                                                 "test-ua"};
  HttpServerProperties http_server_properties_;
  QuicContext quic_context_;
  const std::unique_ptr<HttpNetworkSession> session_;

  const CommonConnectJobParams common_connect_job_params_;
};

class SSLConnectJobTest : public SSLConnectJobTestBase,
                          public ::testing::WithParamInterface<bool> {
 public:
  SSLConnectJobTest() {
    if (IsUsingHappyEyeballsV2()) {
      scoped_feature_list_.InitAndEnableFeature(features::kHappyEyeballsV2);
    } else {
      scoped_feature_list_.InitAndDisableFeature(features::kHappyEyeballsV2);
    }
  }

  bool IsUsingHappyEyeballsV2() const { return GetParam(); }
};

INSTANTIATE_TEST_SUITE_P(All, SSLConnectJobTest, testing::Bool());

TEST_P(SSLConnectJobTest, TCPFail) {
  for (IoMode io_mode : {SYNCHRONOUS, ASYNC}) {
    SCOPED_TRACE(io_mode);
    host_resolver_.set_synchronous_mode(io_mode == SYNCHRONOUS);
    StaticSocketDataProvider data;
    data.set_connect_data(MockConnect(io_mode, ERR_CONNECTION_FAILED));
    socket_factory_.AddSocketDataProvider(&data);

    TestConnectJobDelegate test_delegate;
    std::unique_ptr<ConnectJob> ssl_connect_job =
        CreateConnectJob(&test_delegate);
    test_delegate.StartJobExpectingResult(
        ssl_connect_job.get(), ERR_CONNECTION_FAILED, io_mode == SYNCHRONOUS);
    EXPECT_FALSE(test_delegate.socket());
    EXPECT_FALSE(ssl_connect_job->IsSSLError());
    ConnectionAttempts connection_attempts =
        ssl_connect_job->GetConnectionAttempts();
    ASSERT_EQ(1u, connection_attempts.size());
    EXPECT_THAT(connection_attempts[0].result,
                test::IsError(ERR_CONNECTION_FAILED));
  }
}

TEST_P(SSLConnectJobTest, TCPTimeout) {
  const base::TimeDelta kTinyTime = base::Microseconds(1);

  // Make request hang.
  host_resolver_.set_ondemand_mode(true);

  TestConnectJobDelegate test_delegate;
  std::unique_ptr<ConnectJob> ssl_connect_job =
      CreateConnectJob(&test_delegate);
  ASSERT_THAT(ssl_connect_job->Connect(), test::IsError(ERR_IO_PENDING));

  // Right up until just before the TCP connection timeout, the job does not
  // time out.
  task_environment_.FastForwardBy(TransportConnectJob::ConnectionTimeout() -
                                  kTinyTime);
  EXPECT_FALSE(test_delegate.has_result());

  // But at the exact time of TCP connection timeout, the job fails.
  task_environment_.FastForwardBy(kTinyTime);
  EXPECT_TRUE(test_delegate.has_result());
  EXPECT_THAT(test_delegate.WaitForResult(), test::IsError(ERR_TIMED_OUT));
}

TEST_P(SSLConnectJobTest, SSLTimeoutSyncConnect) {
  const base::TimeDelta kTinyTime = base::Microseconds(1);

  // DNS lookup and transport connect complete synchronously, but SSL
  // negotiation hangs.
  host_resolver_.set_synchronous_mode(true);
  StaticSocketDataProvider data;
  data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
  socket_factory_.AddSocketDataProvider(&data);
  SSLSocketDataProvider ssl(SYNCHRONOUS, ERR_IO_PENDING);
  socket_factory_.AddSSLSocketDataProvider(&ssl);

  // Make request hang.
  TestConnectJobDelegate test_delegate;
  std::unique_ptr<ConnectJob> ssl_connect_job =
      CreateConnectJob(&test_delegate);
  ASSERT_THAT(ssl_connect_job->Connect(), test::IsError(ERR_IO_PENDING));

  // Right up until just before the SSL handshake timeout, the job does not time
  // out.
  task_environment_.FastForwardBy(SSLConnectJob::HandshakeTimeoutForTesting() -
                                  kTinyTime);
  EXPECT_FALSE(test_delegate.has_result());

  // But at the exact SSL handshake timeout time, the job fails.
  task_environment_.FastForwardBy(kTinyTime);
  EXPECT_TRUE(test_delegate.has_result());
  EXPECT_THAT(test_delegate.WaitForResult(), test::IsError(ERR_TIMED_OUT));
}

TEST_P(SSLConnectJobTest, SSLTimeoutAsyncTcpConnect) {
  const base::TimeDelta kTinyTime = base::Microseconds(1);

  // DNS lookup is asynchronous, and later SSL negotiation hangs.
  host_resolver_.set_ondemand_mode(true);
  StaticSocketDataProvider data;
  data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
  socket_factory_.AddSocketDataProvider(&data);
  SSLSocketDataProvider ssl(SYNCHRONOUS, ERR_IO_PENDING);
  socket_factory_.AddSSLSocketDataProvider(&ssl);

  TestConnectJobDelegate test_delegate;
  std::unique_ptr<ConnectJob> ssl_connect_job =
      CreateConnectJob(&test_delegate);
  // Connecting should hand on the TransportConnectJob connect.
  ASSERT_THAT(ssl_connect_job->Connect(), test::IsError(ERR_IO_PENDING));

  // Right up until just before the TCP connection timeout, the job does not
  // time out.
  task_environment_.FastForwardBy(TransportConnectJob::ConnectionTimeout() -
                                  kTinyTime);
  EXPECT_FALSE(test_delegate.has_result());

  // The DNS lookup completes, and a TCP connection is immediately establshed,
  // which cancels the TCP connection timer. The SSL handshake timer is started,
  // and the SSL handshake hangs.
  host_resolver_.ResolveOnlyRequestNow();
  EXPECT_FALSE(test_delegate.has_result());

  // Right up until just before the SSL handshake timeout, the job does not time
  // out.
  task_environment_.FastForwardBy(SSLConnectJob::HandshakeTimeoutForTesting() -
                                  kTinyTime);
  EXPECT_FALSE(test_delegate.has_result());

  // But at the exact SSL handshake timeout time, the job fails.
  task_environment_.FastForwardBy(kTinyTime);
  EXPECT_TRUE(test_delegate.has_result());
  EXPECT_THAT(test_delegate.WaitForResult(), test::IsError(ERR_TIMED_OUT));
}

TEST_P(SSLConnectJobTest, BasicDirectSync) {
  host_resolver_.set_synchronous_mode(true);
  StaticSocketDataProvider data;
  data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
  socket_factory_.AddSocketDataProvider(&data);
  SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
  socket_factory_.AddSSLSocketDataProvider(&ssl);

  TestConnectJobDelegate test_delegate;
  std::unique_ptr<ConnectJob> ssl_connect_job =
      CreateConnectJob(&test_delegate, ProxyChain::Direct(), MEDIUM);

  test_delegate.StartJobExpectingResult(ssl_connect_job.get(), OK,
                                        true /* expect_sync_result */);
  EXPECT_EQ(MEDIUM, host_resolver_.last_request_priority());

  ConnectionAttempts connection_attempts =
      ssl_connect_job->GetConnectionAttempts();
  EXPECT_EQ(0u, connection_attempts.size());
  CheckConnectTimesSet(ssl_connect_job->connect_timing());
}

TEST_P(SSLConnectJobTest, BasicDirectAsync) {
  host_resolver_.set_ondemand_mode(true);
  base::TimeTicks start_time = base::TimeTicks::Now();
  StaticSocketDataProvider data;
  data.set_connect_data(MockConnect(ASYNC, OK));
  socket_factory_.AddSocketDataProvider(&data);
  SSLSocketDataProvider ssl(ASYNC, OK);
  socket_factory_.AddSSLSocketDataProvider(&ssl);

  TestConnectJobDelegate test_delegate;
  std::unique_ptr<ConnectJob> ssl_connect_job =
      CreateConnectJob(&test_delegate, ProxyChain::Direct(), MEDIUM);
  EXPECT_THAT(ssl_connect_job->Connect(), test::IsError(ERR_IO_PENDING));
  EXPECT_TRUE(host_resolver_.has_pending_requests());
  EXPECT_EQ(MEDIUM, host_resolver_.last_request_priority());
  task_environment_.FastForwardBy(base::Seconds(5));

  base::TimeTicks resolve_complete_time = base::TimeTicks::Now();
  host_resolver_.ResolveAllPending();
  EXPECT_THAT(test_delegate.WaitForResult(), test::IsOk());

  ConnectionAttempts connection_attempts =
      ssl_connect_job->GetConnectionAttempts();
  EXPECT_EQ(0u, connection_attempts.size());

  // Check times. Since time is mocked out, all times will be the same, except
  // |dns_start|, which is the only one recorded before the FastForwardBy()
  // call. The test classes don't allow any other phases to be triggered on
  // demand, or delayed by a set interval.
  EXPECT_EQ(start_time, ssl_connect_job->connect_timing().domain_lookup_start);
  EXPECT_EQ(resolve_complete_time,
            ssl_connect_job->connect_timing().domain_lookup_end);
  EXPECT_EQ(resolve_complete_time,
            ssl_connect_job->connect_timing().connect_start);
  EXPECT_EQ(resolve_complete_time, ssl_connect_job->connect_timing().ssl_start);
  EXPECT_EQ(resolve_complete_time, ssl_connect_job->connect_timing().ssl_end);
  EXPECT_EQ(resolve_complete_time,
            ssl_connect_job->connect_timing().connect_end);
}

TEST_P(SSLConnectJobTest, DirectHasEstablishedConnection) {
  host_resolver_.set_ondemand_mode(true);
  StaticSocketDataProvider data;
  data.set_connect_data(MockConnect(ASYNC, OK));
  socket_factory_.AddSocketDataProvider(&data);

  // SSL negotiation hangs. Value returned after SSL negotiation is complete
  // doesn't matter, as HasEstablishedConnection() may only be used between job
  // start and job complete.
  SSLSocketDataProvider ssl(SYNCHRONOUS, ERR_IO_PENDING);
  socket_factory_.AddSSLSocketDataProvider(&ssl);

  TestConnectJobDelegate test_delegate;
  std::unique_ptr<ConnectJob> ssl_connect_job =
      CreateConnectJob(&test_delegate, ProxyChain::Direct(), MEDIUM);
  EXPECT_THAT(ssl_connect_job->Connect(), test::IsError(ERR_IO_PENDING));
  EXPECT_TRUE(host_resolver_.has_pending_requests());
  EXPECT_EQ(LOAD_STATE_RESOLVING_HOST, ssl_connect_job->GetLoadState());
  EXPECT_FALSE(ssl_connect_job->HasEstablishedConnection());

  // DNS resolution completes, and then the ConnectJob tries to connect the
  // socket, which should succeed asynchronously.
  host_resolver_.ResolveNow(1);
  EXPECT_EQ(LOAD_STATE_CONNECTING, ssl_connect_job->GetLoadState());
  EXPECT_FALSE(ssl_connect_job->HasEstablishedConnection());

  // Spinning the message loop causes the socket to finish connecting. The SSL
  // handshake should start and hang.
  base::RunLoop().RunUntilIdle();
  EXPECT_FALSE(test_delegate.has_result());
  EXPECT_EQ(LOAD_STATE_SSL_HANDSHAKE, ssl_connect_job->GetLoadState());
  EXPECT_TRUE(ssl_connect_job->HasEstablishedConnection());
}

TEST_P(SSLConnectJobTest, RequestPriority) {
  host_resolver_.set_ondemand_mode(true);
  for (int initial_priority = MINIMUM_PRIORITY;
       initial_priority <= MAXIMUM_PRIORITY; ++initial_priority) {
    SCOPED_TRACE(initial_priority);
    for (int new_priority = MINIMUM_PRIORITY; new_priority <= MAXIMUM_PRIORITY;
         ++new_priority) {
      SCOPED_TRACE(new_priority);
      if (initial_priority == new_priority) {
        continue;
      }
      TestConnectJobDelegate test_delegate;
      std::unique_ptr<ConnectJob> ssl_connect_job =
          CreateConnectJob(&test_delegate, ProxyChain::Direct(),
                           static_cast<RequestPriority>(initial_priority));
      EXPECT_THAT(ssl_connect_job->Connect(), test::IsError(ERR_IO_PENDING));
      EXPECT_TRUE(host_resolver_.has_pending_requests());
      int request_id = host_resolver_.num_resolve();
      EXPECT_EQ(initial_priority, host_resolver_.request_priority(request_id));

      ssl_connect_job->ChangePriority(
          static_cast<RequestPriority>(new_priority));
      EXPECT_EQ(new_priority, host_resolver_.request_priority(request_id));

      ssl_connect_job->ChangePriority(
          static_cast<RequestPriority>(initial_priority));
      EXPECT_EQ(initial_priority, host_resolver_.request_priority(request_id));
    }
  }
}

TEST_P(SSLConnectJobTest, SecureDnsPolicy) {
  for (auto secure_dns_policy :
       {SecureDnsPolicy::kAllow, SecureDnsPolicy::kDisable}) {
    TestConnectJobDelegate test_delegate;
    std::unique_ptr<ConnectJob> ssl_connect_job =
        CreateConnectJob(&test_delegate, ProxyChain::Direct(), DEFAULT_PRIORITY,
                         secure_dns_policy);

    EXPECT_THAT(ssl_connect_job->Connect(), test::IsError(ERR_IO_PENDING));
    EXPECT_EQ(secure_dns_policy, host_resolver_.last_secure_dns_policy());
  }
}

TEST_P(SSLConnectJobTest, DirectHostResolutionFailure) {
  host_resolver_.rules()->AddSimulatedTimeoutFailure("host");

  TestConnectJobDelegate test_delegate;
  std::unique_ptr<ConnectJob> ssl_connect_job =
      CreateConnectJob(&test_delegate, ProxyChain::Direct());
  test_delegate.StartJobExpectingResult(ssl_connect_job.get(),
                                        ERR_NAME_NOT_RESOLVED,
                                        false /* expect_sync_result */);
  EXPECT_THAT(ssl_connect_job->GetResolveErrorInfo().error,
              test::IsError(ERR_DNS_TIMED_OUT));
}

TEST_P(SSLConnectJobTest, DirectCertError) {
  StaticSocketDataProvider data;
  socket_factory_.AddSocketDataProvider(&data);
  SSLSocketDataProvider ssl(ASYNC, ERR_CERT_COMMON_NAME_INVALID);
  socket_factory_.AddSSLSocketDataProvider(&ssl);

  TestConnectJobDelegate test_delegate(
      TestConnectJobDelegate::SocketExpected::ALWAYS);
  std::unique_ptr<ConnectJob> ssl_connect_job =
      CreateConnectJob(&test_delegate);

  test_delegate.StartJobExpectingResult(ssl_connect_job.get(),
                                        ERR_CERT_COMMON_NAME_INVALID,
                                        false /* expect_sync_result */);
  EXPECT_TRUE(ssl_connect_job->IsSSLError());
  ConnectionAttempts connection_attempts =
      ssl_connect_job->GetConnectionAttempts();
  ASSERT_EQ(1u, connection_attempts.size());
  EXPECT_THAT(connection_attempts[0].result,
              test::IsError(ERR_CERT_COMMON_NAME_INVALID));
  CheckConnectTimesSet(ssl_connect_job->connect_timing());
}

TEST_P(SSLConnectJobTest, DirectIgnoreCertErrors) {
  session_->IgnoreCertificateErrorsForTesting();

  StaticSocketDataProvider data;
  socket_factory_.AddSocketDataProvider(&data);
  SSLSocketDataProvider ssl(ASYNC, OK);
  ssl.expected_ignore_certificate_errors = true;
  socket_factory_.AddSSLSocketDataProvider(&ssl);

  TestConnectJobDelegate test_delegate(
      TestConnectJobDelegate::SocketExpected::ALWAYS);
  std::unique_ptr<ConnectJob> ssl_connect_job =
      CreateConnectJob(&test_delegate);

  test_delegate.StartJobExpectingResult(ssl_connect_job.get(), OK,
                                        /*expect_sync_result=*/false);
}

TEST_P(SSLConnectJobTest, DirectSSLError) {
  StaticSocketDataProvider data;
  socket_factory_.AddSocketDataProvider(&data);
  SSLSocketDataProvider ssl(ASYNC, ERR_BAD_SSL_CLIENT_AUTH_CERT);
  socket_factory_.AddSSLSocketDataProvider(&ssl);

  TestConnectJobDelegate test_delegate;
  std::unique_ptr<ConnectJob> ssl_connect_job =
      CreateConnectJob(&test_delegate);

  test_delegate.StartJobExpectingResult(ssl_connect_job.get(),
                                        ERR_BAD_SSL_CLIENT_AUTH_CERT,
                                        false /* expect_sync_result */);
  ConnectionAttempts connection_attempts =
      ssl_connect_job->GetConnectionAttempts();
  ASSERT_EQ(1u, connection_attempts.size());
  EXPECT_THAT(connection_attempts[0].result,
              test::IsError(ERR_BAD_SSL_CLIENT_AUTH_CERT));
}

TEST_P(SSLConnectJobTest, DirectWithNPN) {
  StaticSocketDataProvider data;
  socket_factory_.AddSocketDataProvider(&data);
  SSLSocketDataProvider ssl(ASYNC, OK);
  ssl.next_proto = NextProto::kProtoHTTP11;
  socket_factory_.AddSSLSocketDataProvider(&ssl);

  TestConnectJobDelegate test_delegate;
  std::unique_ptr<ConnectJob> ssl_connect_job =
      CreateConnectJob(&test_delegate);

  test_delegate.StartJobExpectingResult(ssl_connect_job.get(), OK,
                                        false /* expect_sync_result */);
  CheckConnectTimesSet(ssl_connect_job->connect_timing());
}

TEST_P(SSLConnectJobTest, DirectGotHTTP2) {
  StaticSocketDataProvider data;
  socket_factory_.AddSocketDataProvider(&data);
  SSLSocketDataProvider ssl(ASYNC, OK);
  ssl.next_proto = NextProto::kProtoHTTP2;
  socket_factory_.AddSSLSocketDataProvider(&ssl);

  TestConnectJobDelegate test_delegate;
  std::unique_ptr<ConnectJob> ssl_connect_job =
      CreateConnectJob(&test_delegate);

  test_delegate.StartJobExpectingResult(ssl_connect_job.get(), OK,
                                        false /* expect_sync_result */);
  EXPECT_EQ(NextProto::kProtoHTTP2,
            test_delegate.socket()->GetNegotiatedProtocol());
  CheckConnectTimesSet(ssl_connect_job->connect_timing());
}

TEST_P(SSLConnectJobTest, SOCKSFail) {
  for (IoMode io_mode : {SYNCHRONOUS, ASYNC}) {
    SCOPED_TRACE(io_mode);
    host_resolver_.set_synchronous_mode(io_mode == SYNCHRONOUS);
    StaticSocketDataProvider data;
    data.set_connect_data(MockConnect(io_mode, ERR_CONNECTION_FAILED));
    socket_factory_.AddSocketDataProvider(&data);

    TestConnectJobDelegate test_delegate;
    std::unique_ptr<ConnectJob> ssl_connect_job = CreateConnectJob(
        &test_delegate, PacResultElementToProxyChain("SOCKS5 foo:333"));
    test_delegate.StartJobExpectingResult(ssl_connect_job.get(),
                                          ERR_PROXY_CONNECTION_FAILED,
                                          io_mode == SYNCHRONOUS);
    EXPECT_FALSE(ssl_connect_job->IsSSLError());

    ConnectionAttempts connection_attempts =
        ssl_connect_job->GetConnectionAttempts();
    EXPECT_EQ(0u, connection_attempts.size());
  }
}

TEST_P(SSLConnectJobTest, SOCKSHostResolutionFailure) {
  host_resolver_.rules()->AddSimulatedTimeoutFailure("proxy");

  TestConnectJobDelegate test_delegate;
  std::unique_ptr<ConnectJob> ssl_connect_job = CreateConnectJob(
      &test_delegate, PacResultElementToProxyChain("SOCKS5 foo:333"));
  test_delegate.StartJobExpectingResult(ssl_connect_job.get(),
                                        ERR_PROXY_CONNECTION_FAILED,
                                        false /* expect_sync_result */);
  EXPECT_THAT(ssl_connect_job->GetResolveErrorInfo().error,
              test::IsError(ERR_DNS_TIMED_OUT));
}

TEST_P(SSLConnectJobTest, SOCKSBasic) {
  for (IoMode io_mode : {SYNCHRONOUS, ASYNC}) {
    SCOPED_TRACE(io_mode);
    const uint8_t kSOCKS5Request[] = {0x05, 0x01, 0x00, 0x03, 0x09, 's',
                                      'o',  'c',  'k',  's',  'h',  'o',
                                      's',  't',  0x01, 0xBB};

    MockWrite writes[] = {
        MockWrite(io_mode, kSOCKS5GreetRequest),
        MockWrite(io_mode, base::as_byte_span(kSOCKS5Request)),
    };

    MockRead reads[] = {
        MockRead(io_mode, kSOCKS5GreetResponse),
        MockRead(io_mode, kSOCKS5OkResponse),
    };

    host_resolver_.set_synchronous_mode(io_mode == SYNCHRONOUS);
    StaticSocketDataProvider data(reads, writes);
    data.set_connect_data(MockConnect(io_mode, OK));
    socket_factory_.AddSocketDataProvider(&data);
    SSLSocketDataProvider ssl(io_mode, OK);
    socket_factory_.AddSSLSocketDataProvider(&ssl);

    TestConnectJobDelegate test_delegate;
    std::unique_ptr<ConnectJob> ssl_connect_job = CreateConnectJob(
        &test_delegate, PacResultElementToProxyChain("SOCKS5 foo:333"));
    test_delegate.StartJobExpectingResult(ssl_connect_job.get(), OK,
                                          io_mode == SYNCHRONOUS);
    CheckConnectTimesExceptDnsSet(ssl_connect_job->connect_timing());

    // Proxies should not set any DNS aliases.
    EXPECT_TRUE(test_delegate.socket()->GetDnsAliases().empty());
  }
}

TEST_P(SSLConnectJobTest, SOCKSHasEstablishedConnection) {
  const uint8_t kSOCKS5Request[] = {0x05, 0x01, 0x00, 0x03, 0x09, 's',
                                    'o',  'c',  'k',  's',  'h',  'o',
                                    's',  't',  0x01, 0xBB};

  MockWrite writes[] = {
      MockWrite(SYNCHRONOUS, /*seq=*/0, kSOCKS5GreetRequest),
      MockWrite(SYNCHRONOUS, /*seq=*/3, base::as_byte_span(kSOCKS5Request)),
  };

  MockRead reads[] = {
      // Pause so can probe current state.
      MockRead(ASYNC, ERR_IO_PENDING, /*seq=*/1),
      MockRead(ASYNC, /*seq=*/2, kSOCKS5GreetResponse),
      MockRead(SYNCHRONOUS, /*seq=*/4, kSOCKS5OkResponse),
  };

  host_resolver_.set_ondemand_mode(true);
  SequencedSocketData data(reads, writes);
  data.set_connect_data(MockConnect(ASYNC, OK));
  socket_factory_.AddSocketDataProvider(&data);

  // SSL negotiation hangs. Value returned after SSL negotiation is complete
  // doesn't matter, as HasEstablishedConnection() may only be used between job
  // start and job complete.
  SSLSocketDataProvider ssl(SYNCHRONOUS, ERR_IO_PENDING);
  socket_factory_.AddSSLSocketDataProvider(&ssl);

  TestConnectJobDelegate test_delegate;
  std::unique_ptr<ConnectJob> ssl_connect_job = CreateConnectJob(
      &test_delegate, PacResultElementToProxyChain("SOCKS5 foo:333"));
  EXPECT_THAT(ssl_connect_job->Connect(), test::IsError(ERR_IO_PENDING));
  EXPECT_TRUE(host_resolver_.has_pending_requests());
  EXPECT_EQ(LOAD_STATE_RESOLVING_HOST, ssl_connect_job->GetLoadState());
  EXPECT_FALSE(ssl_connect_job->HasEstablishedConnection());

  // DNS resolution completes, and then the ConnectJob tries to connect the
  // socket, which should succeed asynchronously.
  host_resolver_.ResolveNow(1);
  EXPECT_EQ(LOAD_STATE_CONNECTING, ssl_connect_job->GetLoadState());
  EXPECT_FALSE(ssl_connect_job->HasEstablishedConnection());

  // Spin the message loop until the first read of the handshake.
  // HasEstablishedConnection() should return true, as a TCP connection has been
  // successfully established by this point.
  data.RunUntilPaused();
  EXPECT_FALSE(test_delegate.has_result());
  EXPECT_EQ(LOAD_STATE_CONNECTING, ssl_connect_job->GetLoadState());
  EXPECT_TRUE(ssl_connect_job->HasEstablishedConnection());

  // Finish up the handshake, and spin the message loop until the SSL handshake
  // starts and hang.
  data.Resume();
  base::RunLoop().RunUntilIdle();
  EXPECT_FALSE(test_delegate.has_result());
  EXPECT_EQ(LOAD_STATE_SSL_HANDSHAKE, ssl_connect_job->GetLoadState());
  EXPECT_TRUE(ssl_connect_job->HasEstablishedConnection());
}

TEST_P(SSLConnectJobTest, SOCKSRequestPriority) {
  host_resolver_.set_ondemand_mode(true);
  for (int initial_priority = MINIMUM_PRIORITY;
       initial_priority <= MAXIMUM_PRIORITY; ++initial_priority) {
    SCOPED_TRACE(initial_priority);
    for (int new_priority = MINIMUM_PRIORITY; new_priority <= MAXIMUM_PRIORITY;
         ++new_priority) {
      SCOPED_TRACE(new_priority);
      if (initial_priority == new_priority) {
        continue;
      }
      TestConnectJobDelegate test_delegate;
      std::unique_ptr<ConnectJob> ssl_connect_job = CreateConnectJob(
          &test_delegate, PacResultElementToProxyChain("SOCKS5 foo:333"),
          static_cast<RequestPriority>(initial_priority));
      EXPECT_THAT(ssl_connect_job->Connect(), test::IsError(ERR_IO_PENDING));
      EXPECT_TRUE(host_resolver_.has_pending_requests());
      int request_id = host_resolver_.num_resolve();
      EXPECT_EQ(initial_priority, host_resolver_.request_priority(request_id));

      ssl_connect_job->ChangePriority(
          static_cast<RequestPriority>(new_priority));
      EXPECT_EQ(new_priority, host_resolver_.request_priority(request_id));

      ssl_connect_job->ChangePriority(
          static_cast<RequestPriority>(initial_priority));
      EXPECT_EQ(initial_priority, host_resolver_.request_priority(request_id));
    }
  }
}

TEST_P(SSLConnectJobTest, HttpProxyFail) {
  for (IoMode io_mode : {SYNCHRONOUS, ASYNC}) {
    SCOPED_TRACE(io_mode);
    host_resolver_.set_synchronous_mode(io_mode == SYNCHRONOUS);
    StaticSocketDataProvider data;
    data.set_connect_data(MockConnect(io_mode, ERR_CONNECTION_FAILED));
    socket_factory_.AddSocketDataProvider(&data);

    TestConnectJobDelegate test_delegate;
    std::unique_ptr<ConnectJob> ssl_connect_job = CreateConnectJob(
        &test_delegate, PacResultElementToProxyChain("PROXY foo:444"));
    test_delegate.StartJobExpectingResult(ssl_connect_job.get(),
                                          ERR_PROXY_CONNECTION_FAILED,
                                          io_mode == SYNCHRONOUS);

    EXPECT_FALSE(ssl_connect_job->IsSSLError());
    ConnectionAttempts connection_attempts =
        ssl_connect_job->GetConnectionAttempts();
    EXPECT_EQ(0u, connection_attempts.size());
  }
}

TEST_P(SSLConnectJobTest, HttpProxyHostResolutionFailure) {
  host_resolver_.rules()->AddSimulatedTimeoutFailure("proxy");

  TestConnectJobDelegate test_delegate;
  std::unique_ptr<ConnectJob> ssl_connect_job = CreateConnectJob(
      &test_delegate, PacResultElementToProxyChain("PROXY foo:444"));
  test_delegate.StartJobExpectingResult(ssl_connect_job.get(),
                                        ERR_PROXY_CONNECTION_FAILED,
                                        false /* expect_sync_result */);
  EXPECT_THAT(ssl_connect_job->GetResolveErrorInfo().error,
              test::IsError(ERR_DNS_TIMED_OUT));
}

TEST_P(SSLConnectJobTest, HttpProxyAuthChallenge) {
  MockWrite writes[] = {
      MockWrite(ASYNC, 0,
                "CONNECT host:80 HTTP/1.1\r\n"
                "Host: host:80\r\n"
                "Proxy-Connection: keep-alive\r\n"
                "User-Agent: test-ua\r\n\r\n"),
      MockWrite(ASYNC, 5,
                "CONNECT host:80 HTTP/1.1\r\n"
                "Host: host:80\r\n"
                "Proxy-Connection: keep-alive\r\n"
                "User-Agent: test-ua\r\n"
                "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
  };
  MockRead reads[] = {
      MockRead(ASYNC, 1, "HTTP/1.1 407 Proxy Authentication Required\r\n"),
      MockRead(ASYNC, 2, "Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"),
      MockRead(ASYNC, 3, "Content-Length: 10\r\n\r\n"),
      MockRead(ASYNC, 4, "0123456789"),
      MockRead(ASYNC, 6, "HTTP/1.1 200 Connection Established\r\n\r\n"),
  };
  StaticSocketDataProvider data(reads, writes);
  socket_factory_.AddSocketDataProvider(&data);
  SSLSocketDataProvider ssl(ASYNC, OK);
  socket_factory_.AddSSLSocketDataProvider(&ssl);

  TestConnectJobDelegate test_delegate;
  std::unique_ptr<ConnectJob> ssl_connect_job = CreateConnectJob(
      &test_delegate, PacResultElementToProxyChain("PROXY foo:444"));
  ASSERT_THAT(ssl_connect_job->Connect(), test::IsError(ERR_IO_PENDING));
  test_delegate.WaitForAuthChallenge(1);

  EXPECT_EQ(407, test_delegate.auth_response_info().headers->response_code());
  std::string proxy_authenticate;
  ASSERT_TRUE(test_delegate.auth_response_info().headers->EnumerateHeader(
      nullptr, "Proxy-Authenticate", &proxy_authenticate));
  EXPECT_EQ(proxy_authenticate, "Basic realm=\"MyRealm1\"");

  // While waiting for auth credentials to be provided, the Job should not time
  // out.
  task_environment_.FastForwardBy(base::Days(1));
  test_delegate.WaitForAuthChallenge(1);
  EXPECT_FALSE(test_delegate.has_result());

  // Respond to challenge.
  test_delegate.auth_controller()->ResetAuth(AuthCredentials(u"foo", u"bar"));
  test_delegate.RunAuthCallback();

  EXPECT_THAT(test_delegate.WaitForResult(), test::IsOk());

  // Proxies should not set any DNS aliases.
  EXPECT_TRUE(test_delegate.socket()->GetDnsAliases().empty());
}

TEST_P(SSLConnectJobTest, HttpProxyAuthWithCachedCredentials) {
  for (IoMode io_mode : {SYNCHRONOUS, ASYNC}) {
    SCOPED_TRACE(io_mode);
    host_resolver_.set_synchronous_mode(io_mode == SYNCHRONOUS);
    MockWrite writes[] = {
        MockWrite(io_mode,
                  "CONNECT host:80 HTTP/1.1\r\n"
                  "Host: host:80\r\n"
                  "Proxy-Connection: keep-alive\r\n"
                  "User-Agent: test-ua\r\n"
                  "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
    };
    MockRead reads[] = {
        MockRead(io_mode, "HTTP/1.1 200 Connection Established\r\n\r\n"),
    };
    StaticSocketDataProvider data(reads, writes);
    data.set_connect_data(MockConnect(io_mode, OK));
    socket_factory_.AddSocketDataProvider(&data);
    AddAuthToCache();
    SSLSocketDataProvider ssl(io_mode, OK);
    socket_factory_.AddSSLSocketDataProvider(&ssl);

    TestConnectJobDelegate test_delegate;
    std::unique_ptr<ConnectJob> ssl_connect_job = CreateConnectJob(
        &test_delegate, PacResultElementToProxyChain("PROXY foo:444"));
    test_delegate.StartJobExpectingResult(ssl_connect_job.get(), OK,
                                          io_mode == SYNCHRONOUS);
    CheckConnectTimesExceptDnsSet(ssl_connect_job->connect_timing());
    EXPECT_TRUE(test_delegate.socket()->GetDnsAliases().empty());
  }
}

TEST_P(SSLConnectJobTest, HttpProxyRequestPriority) {
  host_resolver_.set_ondemand_mode(true);
  for (int initial_priority = MINIMUM_PRIORITY;
       initial_priority <= MAXIMUM_PRIORITY; ++initial_priority) {
    SCOPED_TRACE(initial_priority);
    for (int new_priority = MINIMUM_PRIORITY; new_priority <= MAXIMUM_PRIORITY;
         ++new_priority) {
      SCOPED_TRACE(new_priority);
      if (initial_priority == new_priority) {
        continue;
      }
      TestConnectJobDelegate test_delegate;
      std::unique_ptr<ConnectJob> ssl_connect_job = CreateConnectJob(
          &test_delegate, PacResultElementToProxyChain("PROXY foo:444"),
          static_cast<RequestPriority>(initial_priority));
      EXPECT_THAT(ssl_connect_job->Connect(), test::IsError(ERR_IO_PENDING));
      EXPECT_TRUE(host_resolver_.has_pending_requests());
      int request_id = host_resolver_.num_resolve();
      EXPECT_EQ(initial_priority, host_resolver_.request_priority(request_id));

      ssl_connect_job->ChangePriority(
          static_cast<RequestPriority>(new_priority));
      EXPECT_EQ(new_priority, host_resolver_.request_priority(request_id));

      ssl_connect_job->ChangePriority(
          static_cast<RequestPriority>(initial_priority));
      EXPECT_EQ(initial_priority, host_resolver_.request_priority(request_id));
    }
  }
}

TEST_P(SSLConnectJobTest, HttpProxyAuthHasEstablishedConnection) {
  host_resolver_.set_ondemand_mode(true);
  MockWrite writes[] = {
      MockWrite(ASYNC, 0,
                "CONNECT host:80 HTTP/1.1\r\n"
                "Host: host:80\r\n"
                "Proxy-Connection: keep-alive\r\n"
                "User-Agent: test-ua\r\n\r\n"),
      MockWrite(ASYNC, 3,
                "CONNECT host:80 HTTP/1.1\r\n"
                "Host: host:80\r\n"
                "Proxy-Connection: keep-alive\r\n"
                "User-Agent: test-ua\r\n"
                "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
  };
  MockRead reads[] = {
      // Pause reading.
      MockRead(ASYNC, ERR_IO_PENDING, 1),
      MockRead(ASYNC, 2,
               "HTTP/1.1 407 Proxy Authentication Required\r\n"
               "Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"
               "Content-Length: 0\r\n\r\n"),
      // Pause reading.
      MockRead(ASYNC, ERR_IO_PENDING, 4),
      MockRead(ASYNC, 5, "HTTP/1.1 200 Connection Established\r\n\r\n"),
  };
  SequencedSocketData data(reads, writes);
  socket_factory_.AddSocketDataProvider(&data);
  SSLSocketDataProvider ssl(ASYNC, OK);
  socket_factory_.AddSSLSocketDataProvider(&ssl);

  TestConnectJobDelegate test_delegate;
  std::unique_ptr<ConnectJob> ssl_connect_job = CreateConnectJob(
      &test_delegate, PacResultElementToProxyChain("PROXY foo:444"));
  ASSERT_THAT(ssl_connect_job->Connect(), test::IsError(ERR_IO_PENDING));
  EXPECT_TRUE(host_resolver_.has_pending_requests());
  EXPECT_EQ(LOAD_STATE_RESOLVING_HOST, ssl_connect_job->GetLoadState());
  EXPECT_FALSE(ssl_connect_job->HasEstablishedConnection());

  // DNS resolution completes, and then the ConnectJob tries to connect the
  // socket, which should succeed asynchronously.
  host_resolver_.ResolveOnlyRequestNow();
  EXPECT_EQ(LOAD_STATE_CONNECTING, ssl_connect_job->GetLoadState());
  EXPECT_FALSE(ssl_connect_job->HasEstablishedConnection());

  // Spinning the message loop causes the connection to be established and the
  // nested HttpProxyConnectJob to start establishing a tunnel.
  base::RunLoop().RunUntilIdle();
  EXPECT_FALSE(test_delegate.has_result());
  EXPECT_EQ(LOAD_STATE_ESTABLISHING_PROXY_TUNNEL,
            ssl_connect_job->GetLoadState());
  EXPECT_TRUE(ssl_connect_job->HasEstablishedConnection());

  // Receive the auth challenge.
  data.Resume();
  test_delegate.WaitForAuthChallenge(1);
  EXPECT_FALSE(test_delegate.has_result());
  EXPECT_EQ(LOAD_STATE_IDLE, ssl_connect_job->GetLoadState());
  EXPECT_TRUE(ssl_connect_job->HasEstablishedConnection());

  // Respond to challenge.
  test_delegate.auth_controller()->ResetAuth(AuthCredentials(u"foo", u"bar"));
  test_delegate.RunAuthCallback();
  EXPECT_FALSE(test_delegate.has_result());
  EXPECT_EQ(LOAD_STATE_ESTABLISHING_PROXY_TUNNEL,
            ssl_connect_job->GetLoadState());
  EXPECT_TRUE(ssl_connect_job->HasEstablishedConnection());

  // Run until the next read pauses.
  base::RunLoop().RunUntilIdle();
  EXPECT_FALSE(test_delegate.has_result());
  EXPECT_EQ(LOAD_STATE_ESTABLISHING_PROXY_TUNNEL,
            ssl_connect_job->GetLoadState());
  EXPECT_TRUE(ssl_connect_job->HasEstablishedConnection());

  // Receive the connection established response, at which point SSL negotiation
  // finally starts.
  data.Resume();
  EXPECT_FALSE(test_delegate.has_result());
  EXPECT_EQ(LOAD_STATE_SSL_HANDSHAKE, ssl_connect_job->GetLoadState());
  EXPECT_TRUE(ssl_connect_job->HasEstablishedConnection());

  EXPECT_THAT(test_delegate.WaitForResult(), test::IsOk());
}

TEST_P(SSLConnectJobTest,
       HttpProxyAuthHasEstablishedConnectionWithProxyConnectionClose) {
  host_resolver_.set_ondemand_mode(true);
  MockWrite writes1[] = {
      MockWrite(ASYNC, 0,
                "CONNECT host:80 HTTP/1.1\r\n"
                "Host: host:80\r\n"
                "Proxy-Connection: keep-alive\r\n"
                "User-Agent: test-ua\r\n\r\n"),
  };
  MockRead reads1[] = {
      // Pause reading.
      MockRead(ASYNC, ERR_IO_PENDING, 1),
      MockRead(ASYNC, 2,
               "HTTP/1.1 407 Proxy Authentication Required\r\n"
               "Proxy-Connection: Close\r\n"
               "Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"
               "Content-Length: 0\r\n\r\n"),
  };
  SequencedSocketData data1(reads1, writes1);
  socket_factory_.AddSocketDataProvider(&data1);

  MockWrite writes2[] = {
      MockWrite(ASYNC, 0,
                "CONNECT host:80 HTTP/1.1\r\n"
                "Host: host:80\r\n"
                "Proxy-Connection: keep-alive\r\n"
                "User-Agent: test-ua\r\n"
                "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
  };
  MockRead reads2[] = {
      // Pause reading.
      MockRead(ASYNC, ERR_IO_PENDING, 1),
      MockRead(ASYNC, 2, "HTTP/1.1 200 Connection Established\r\n\r\n"),
  };
  SequencedSocketData data2(reads2, writes2);
  socket_factory_.AddSocketDataProvider(&data2);
  SSLSocketDataProvider ssl(ASYNC, OK);
  socket_factory_.AddSSLSocketDataProvider(&ssl);

  TestConnectJobDelegate test_delegate;
  std::unique_ptr<ConnectJob> ssl_connect_job = CreateConnectJob(
      &test_delegate, PacResultElementToProxyChain("PROXY foo:444"));
  ASSERT_THAT(ssl_connect_job->Connect(), test::IsError(ERR_IO_PENDING));
  EXPECT_TRUE(host_resolver_.has_pending_requests());
  EXPECT_EQ(LOAD_STATE_RESOLVING_HOST, ssl_connect_job->GetLoadState());
  EXPECT_FALSE(ssl_connect_job->HasEstablishedConnection());

  // DNS resolution completes, and then the ConnectJob tries to connect the
  // socket, which should succeed asynchronously.
  host_resolver_.ResolveOnlyRequestNow();
  EXPECT_EQ(LOAD_STATE_CONNECTING, ssl_connect_job->GetLoadState());
  EXPECT_FALSE(ssl_connect_job->HasEstablishedConnection());

  // Spinning the message loop causes the connection to be established and the
  // nested HttpProxyConnectJob to start establishing a tunnel.
  base::RunLoop().RunUntilIdle();
  EXPECT_FALSE(test_delegate.has_result());
  EXPECT_EQ(LOAD_STATE_ESTABLISHING_PROXY_TUNNEL,
            ssl_connect_job->GetLoadState());
  EXPECT_TRUE(ssl_connect_job->HasEstablishedConnection());

  // Receive the auth challenge.
  data1.Resume();
  test_delegate.WaitForAuthChallenge(1);
  EXPECT_FALSE(test_delegate.has_result());
  EXPECT_EQ(LOAD_STATE_IDLE, ssl_connect_job->GetLoadState());
  EXPECT_TRUE(ssl_connect_job->HasEstablishedConnection());

  // Respond to challenge.
  test_delegate.auth_controller()->ResetAuth(AuthCredentials(u"foo", u"bar"));
  test_delegate.RunAuthCallback();
  EXPECT_FALSE(test_delegate.has_result());
  EXPECT_EQ(LOAD_STATE_ESTABLISHING_PROXY_TUNNEL,
            ssl_connect_job->GetLoadState());
  EXPECT_TRUE(ssl_connect_job->HasEstablishedConnection());

  // Run until the next DNS lookup.
  base::RunLoop().RunUntilIdle();
  EXPECT_TRUE(host_resolver_.has_pending_requests());
  EXPECT_EQ(LOAD_STATE_RESOLVING_HOST, ssl_connect_job->GetLoadState());
  EXPECT_TRUE(ssl_connect_job->HasEstablishedConnection());

  // DNS resolution completes, and then the ConnectJob tries to connect the
  // socket, which should succeed asynchronously.
  host_resolver_.ResolveOnlyRequestNow();
  EXPECT_EQ(LOAD_STATE_CONNECTING, ssl_connect_job->GetLoadState());
  EXPECT_TRUE(ssl_connect_job->HasEstablishedConnection());

  // Spinning the message loop causes the connection to be established and the
  // nested HttpProxyConnectJob to start establishing a tunnel.
  base::RunLoop().RunUntilIdle();
  EXPECT_FALSE(test_delegate.has_result());
  EXPECT_EQ(LOAD_STATE_ESTABLISHING_PROXY_TUNNEL,
            ssl_connect_job->GetLoadState());
  EXPECT_TRUE(ssl_connect_job->HasEstablishedConnection());

  // Receive the connection established response, at which point SSL negotiation
  // finally starts.
  data2.Resume();
  EXPECT_FALSE(test_delegate.has_result());
  EXPECT_EQ(LOAD_STATE_SSL_HANDSHAKE, ssl_connect_job->GetLoadState());
  EXPECT_TRUE(ssl_connect_job->HasEstablishedConnection());

  EXPECT_THAT(test_delegate.WaitForResult(), test::IsOk());
}

TEST_P(SSLConnectJobTest, DnsAliases) {
  host_resolver_.set_synchronous_mode(true);

  // Resolve an AddressList with DNS aliases.
  std::vector<std::string> aliases({"alias1", "alias2", "host"});
  host_resolver_.rules()->AddIPLiteralRuleWithDnsAliases("host", "2.2.2.2",
                                                         std::move(aliases));
  StaticSocketDataProvider data;
  data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
  socket_factory_.AddSocketDataProvider(&data);
  SSLSocketDataProvider ssl(ASYNC, OK);
  socket_factory_.AddSSLSocketDataProvider(&ssl);
  TestConnectJobDelegate test_delegate;

  std::unique_ptr<ConnectJob> ssl_connect_job =
      CreateConnectJob(&test_delegate, ProxyChain::Direct(), MEDIUM);

  EXPECT_THAT(ssl_connect_job->Connect(), test::IsError(ERR_IO_PENDING));

  base::RunLoop().RunUntilIdle();

  // Verify that the elements of the alias list are those from the
  // parameter vector.
  EXPECT_THAT(test_delegate.socket()->GetDnsAliases(),
              testing::ElementsAre("alias1", "alias2", "host"));
}

TEST_P(SSLConnectJobTest, NoAdditionalDnsAliases) {
  host_resolver_.set_synchronous_mode(true);

  // Resolve an AddressList without additional DNS aliases. (The parameter
  // is an empty vector.)
  std::vector<std::string> aliases;
  host_resolver_.rules()->AddIPLiteralRuleWithDnsAliases("host", "2.2.2.2",
                                                         std::move(aliases));
  StaticSocketDataProvider data;
  data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
  socket_factory_.AddSocketDataProvider(&data);
  SSLSocketDataProvider ssl(ASYNC, OK);
  socket_factory_.AddSSLSocketDataProvider(&ssl);
  TestConnectJobDelegate test_delegate;

  std::unique_ptr<ConnectJob> ssl_connect_job =
      CreateConnectJob(&test_delegate, ProxyChain::Direct(), MEDIUM);

  EXPECT_THAT(ssl_connect_job->Connect(), test::IsError(ERR_IO_PENDING));

  base::RunLoop().RunUntilIdle();

  // Verify that the alias list only contains "host".
  EXPECT_THAT(test_delegate.socket()->GetDnsAliases(),
              testing::ElementsAre("host"));
}

// Test that when `kTLSTrustAnchorIDs` is enabled, `SSLConnectJob`
// unconditionally selects all Trust Anchor IDs that are allowed by the
// sub-flags.
TEST_P(SSLConnectJobTest, TrustAnchorIDs) {
  HostResolverEndpointResult endpoint;
  endpoint.metadata.trust_anchor_ids = {
      {0x01, 0x02, 0x03}, {0x04, 0x04}, {0x05, 0x05, 0x05}};
  endpoint.ip_endpoints = {IPEndPoint(ParseIP("1::"), 8441)};
  host_resolver_.rules()->AddRule(
      "host",
      MockHostResolverBase::RuleResolver::RuleResult(std::vector{endpoint}));

  SSLContextConfig config;
  config.trust_anchor_ids = {{0x01, 0x02, 0x03}, {0x02, 0x02}, {0x04, 0x04}};
  config.mtc_trust_anchor_ids = {{0x07, 0x08, 0x09}, {0x06, 0x06}};
  ssl_config_service_->UpdateSSLConfigAndNotify(config);

  for (bool trust_anchor_ids_enabled : {false, true}) {
    SCOPED_TRACE(trust_anchor_ids_enabled);
    for (bool non_mtc_enabled : {false, true}) {
      SCOPED_TRACE(non_mtc_enabled);
      for (bool mtc_enabled : {false, true}) {
        SCOPED_TRACE(mtc_enabled);

        std::vector<base::test::FeatureRef> enabled_features;
        std::vector<base::test::FeatureRef> disabled_features;

        if (trust_anchor_ids_enabled) {
          enabled_features.push_back(features::kTLSTrustAnchorIDs);
        } else {
          disabled_features.push_back(features::kTLSTrustAnchorIDs);
        }

        if (non_mtc_enabled) {
          enabled_features.push_back(features::kNonMtcTrustAnchorIDs);
        } else {
          disabled_features.push_back(features::kNonMtcTrustAnchorIDs);
        }

#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
        if (mtc_enabled) {
          enabled_features.push_back(features::kVerifyMTCs);
        } else {
          disabled_features.push_back(features::kVerifyMTCs);
        }
#endif

        base::test::ScopedFeatureList feature_list;
        feature_list.InitWithFeatures(enabled_features, disabled_features);

        StaticSocketDataProvider data;
        data.set_expected_addresses(AddressList(endpoint.ip_endpoints));
        data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
        socket_factory_.AddSocketDataProvider(&data);
        SSLSocketDataProvider ssl(ASYNC, OK);

        bool expect_any = false;
        std::vector<std::vector<uint8_t>> expected_ids;
        std::vector<std::string> expected_strings;
        if (trust_anchor_ids_enabled) {
          if (non_mtc_enabled) {
            expect_any = true;
            expected_strings.push_back("1.2.3");
            expected_strings.push_back("2.2");
            expected_strings.push_back("4.4");
            expected_ids.push_back({0x01, 0x02, 0x03});
            expected_ids.push_back({0x02, 0x02});
            expected_ids.push_back({0x04, 0x04});
          }
#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
          if (mtc_enabled) {
            expect_any = true;
            expected_strings.push_back("7.8.9");
            expected_strings.push_back("6.6");
            expected_ids.push_back({0x07, 0x08, 0x09});
            expected_ids.push_back({0x06, 0x06});
          }
#endif
        }

        if (expect_any) {
          ssl.expected_trust_anchor_ids = expected_ids;
        } else {
          ssl.expect_no_trust_anchor_ids = true;
        }
        socket_factory_.AddSSLSocketDataProvider(&ssl);

        base::HistogramTester histogram_tester;
        TestConnectJobDelegate test_delegate;
        RecordingNetLogObserver net_log_observer(
            common_connect_job_params_.net_log, NetLogCaptureMode::kDefault);
        std::unique_ptr<ConnectJob> ssl_connect_job =
            CreateConnectJob(&test_delegate, ProxyChain::Direct(), MEDIUM);
        EXPECT_THAT(ssl_connect_job->Connect(), test::IsError(ERR_IO_PENDING));
        EXPECT_THAT(test_delegate.WaitForResult(), test::IsOk());
        histogram_tester.ExpectTotalCount(
            "Net.SSL_Connection_Error_TrustAnchorIDs", 0);
        histogram_tester.ExpectTotalCount(
            "Net.SSL_Connection_Latency_TrustAnchorIDs", 0);
        histogram_tester.ExpectUniqueSample(
            "Net.SSL.TrustAnchorIDsResult",
            SSLClientSocket::TrustAnchorIDsResult::kNoDnsSuccessInitial, 1);
        auto events = net_log_observer.GetEntriesWithType(
            NetLogEventType::SSL_CONNECT_JOB_SSL_CONNECT);
        ASSERT_EQ(1u, events.size());
        EXPECT_FALSE(
            events[0].params.contains("selected_trust_anchor_ids_for_retry"));
        EXPECT_FALSE(events[0].params.contains("trust_anchor_ids_from_dns"));
        if (!expect_any) {
          EXPECT_FALSE(events[0].params.contains("selected_trust_anchor_ids"));
        } else {
          EXPECT_THAT(
              base::SplitString(GetStringValueFromParams(
                                    events[0], "selected_trust_anchor_ids"),
                                ", ", base::TRIM_WHITESPACE,
                                base::SPLIT_WANT_NONEMPTY),
              testing::UnorderedElementsAreArray(expected_strings));
        }
      }
    }
  }
}


// Test that `SSLConnectJob` passes the ECHConfigList from DNS to
// `SSLClientSocket`.
TEST_P(SSLConnectJobTest, EncryptedClientHello) {
  std::vector<uint8_t> ech_config_list1, ech_config_list2;
  ASSERT_TRUE(MakeTestEchKeys("public.example", /*max_name_len=*/128,
                              &ech_config_list1));
  ASSERT_TRUE(MakeTestEchKeys("public.example", /*max_name_len=*/128,
                              &ech_config_list2));

  // Configure two HTTPS RR routes, to test we pass the correct one.
  HostResolverEndpointResult endpoint1, endpoint2;
  endpoint1.ip_endpoints = {IPEndPoint(ParseIP("1::"), 8441)};
  endpoint1.metadata.supported_protocol_alpns = {"http/1.1"};
  endpoint1.metadata.ech_config_list = ech_config_list1;
  endpoint2.ip_endpoints = {IPEndPoint(ParseIP("2::"), 8442)};
  endpoint2.metadata.supported_protocol_alpns = {"http/1.1"};
  endpoint2.metadata.ech_config_list = ech_config_list2;
  host_resolver_.rules()->AddRule(
      "host", MockHostResolverBase::RuleResolver::RuleResult(
                  std::vector{endpoint1, endpoint2}));

  for (bool ech_enabled : {true, false}) {
    SCOPED_TRACE(ech_enabled);
    ssl_config_service_->SetEchModeGetter(
        std::make_unique<TestStaticEchModeGetter>(
            ech_enabled ? EchMode::kOpportunistic : EchMode::kDisabled,
            kHostHttps.host()));

    // The first connection attempt will be to `endpoint1`, which will fail.
    StaticSocketDataProvider data1;
    data1.set_expected_addresses(AddressList(endpoint1.ip_endpoints));
    data1.set_connect_data(MockConnect(SYNCHRONOUS, ERR_CONNECTION_REFUSED));
    socket_factory_.AddSocketDataProvider(&data1);
    // The second connection attempt will be to `endpoint2`, which will succeed.
    StaticSocketDataProvider data2;
    data2.set_expected_addresses(AddressList(endpoint2.ip_endpoints));
    data2.set_connect_data(MockConnect(SYNCHRONOUS, OK));
    socket_factory_.AddSocketDataProvider(&data2);
    // The handshake then succeeds.
    SSLSocketDataProvider ssl2(ASYNC, OK);
    // The ECH configuration should be passed if and only if the feature is
    // enabled.
    ssl2.expected_ech_config_list =
        ech_enabled ? ech_config_list2 : std::vector<uint8_t>{};
    socket_factory_.AddSSLSocketDataProvider(&ssl2);

    // The connection should ultimately succeed.
    base::HistogramTester histogram_tester;
    TestConnectJobDelegate test_delegate;
    std::unique_ptr<ConnectJob> ssl_connect_job =
        CreateConnectJob(&test_delegate, ProxyChain::Direct(), MEDIUM);
    EXPECT_THAT(ssl_connect_job->Connect(), test::IsError(ERR_IO_PENDING));
    EXPECT_THAT(test_delegate.WaitForResult(), test::IsOk());

    // The ECH result should only be recorded if ECH was actually enabled.
    if (ech_enabled) {
      histogram_tester.ExpectUniqueSample("Net.SSL.ECHResult",
                                          0 /* kSuccessInitial */, 1);
    } else {
      histogram_tester.ExpectTotalCount("Net.SSL.ECHResult", 0);
    }
  }
}

// Test that `SSLConnectJob` retries the connection if there was a stale ECH
// configuration.
TEST_P(SSLConnectJobTest, ECHStaleConfig) {
  std::vector<uint8_t> ech_config_list1, ech_config_list2, ech_config_list3;
  ASSERT_TRUE(MakeTestEchKeys("public.example", /*max_name_len=*/128,
                              &ech_config_list1));
  ASSERT_TRUE(MakeTestEchKeys("public.example", /*max_name_len=*/128,
                              &ech_config_list2));
  ASSERT_TRUE(MakeTestEchKeys("public.example", /*max_name_len=*/128,
                              &ech_config_list3));

  // Configure two HTTPS RR routes, to test the retry uses the correct one.
  HostResolverEndpointResult endpoint1, endpoint2;
  endpoint1.ip_endpoints = {IPEndPoint(ParseIP("1::"), 8441)};
  endpoint1.metadata.supported_protocol_alpns = {"http/1.1"};
  endpoint1.metadata.ech_config_list = ech_config_list1;
  endpoint2.ip_endpoints = {IPEndPoint(ParseIP("2::"), 8442)};
  endpoint2.metadata.supported_protocol_alpns = {"http/1.1"};
  endpoint2.metadata.ech_config_list = ech_config_list2;
  host_resolver_.rules()->AddRule(
      "host", MockHostResolverBase::RuleResolver::RuleResult(
                  std::vector{endpoint1, endpoint2}));

  // The first connection attempt will be to `endpoint1`, which will fail.
  StaticSocketDataProvider data1;
  data1.set_expected_addresses(AddressList(endpoint1.ip_endpoints));
  data1.set_connect_data(MockConnect(SYNCHRONOUS, ERR_CONNECTION_REFUSED));
  socket_factory_.AddSocketDataProvider(&data1);
  // The second connection attempt will be to `endpoint2`, which will succeed.
  StaticSocketDataProvider data2;
  data2.set_expected_addresses(AddressList(endpoint2.ip_endpoints));
  data2.set_connect_data(MockConnect(SYNCHRONOUS, OK));
  socket_factory_.AddSocketDataProvider(&data2);
  // The handshake will then fail, but then provide retry configs.
  SSLSocketDataProvider ssl2(ASYNC, ERR_ECH_NOT_NEGOTIATED);
  ssl2.expected_ech_config_list = ech_config_list2;
  ssl2.ech_retry_configs = ech_config_list3;
  socket_factory_.AddSSLSocketDataProvider(&ssl2);
  // The third connection attempt should skip `endpoint1` and retry with only
  // `endpoint2`.
  StaticSocketDataProvider data3;
  data3.set_expected_addresses(AddressList(endpoint2.ip_endpoints));
  data3.set_connect_data(MockConnect(SYNCHRONOUS, OK));
  socket_factory_.AddSocketDataProvider(&data3);
  // The handshake should be passed the retry configs.
  SSLSocketDataProvider ssl3(ASYNC, OK);
  ssl3.expected_ech_config_list = ech_config_list3;
  socket_factory_.AddSSLSocketDataProvider(&ssl3);

  // The connection should ultimately succeed.
  base::HistogramTester histogram_tester;
  TestConnectJobDelegate test_delegate;
  std::unique_ptr<ConnectJob> ssl_connect_job =
      CreateConnectJob(&test_delegate, ProxyChain::Direct(), MEDIUM);
  EXPECT_THAT(ssl_connect_job->Connect(), test::IsError(ERR_IO_PENDING));
  EXPECT_THAT(test_delegate.WaitForResult(), test::IsOk());

  histogram_tester.ExpectUniqueSample("Net.SSL.ECHResult",
                                      2 /* kSuccessRetry */, 1);
}

// Test that `SSLConnectJob` retries the connection given a secure rollback
// signal.
TEST_P(SSLConnectJobTest, ECHRollback) {
  std::vector<uint8_t> ech_config_list1, ech_config_list2;
  ASSERT_TRUE(MakeTestEchKeys("public.example", /*max_name_len=*/128,
                              &ech_config_list1));
  ASSERT_TRUE(MakeTestEchKeys("public.example", /*max_name_len=*/128,
                              &ech_config_list2));

  // Configure two HTTPS RR routes, to test the retry uses the correct one.
  HostResolverEndpointResult endpoint1, endpoint2;
  endpoint1.ip_endpoints = {IPEndPoint(ParseIP("1::"), 8441)};
  endpoint1.metadata.supported_protocol_alpns = {"http/1.1"};
  endpoint1.metadata.ech_config_list = ech_config_list1;
  endpoint2.ip_endpoints = {IPEndPoint(ParseIP("2::"), 8442)};
  endpoint2.metadata.supported_protocol_alpns = {"http/1.1"};
  endpoint2.metadata.ech_config_list = ech_config_list2;
  host_resolver_.rules()->AddRule(
      "host", MockHostResolverBase::RuleResolver::RuleResult(
                  std::vector{endpoint1, endpoint2}));

  // The first connection attempt will be to `endpoint1`, which will fail.
  StaticSocketDataProvider data1;
  data1.set_expected_addresses(AddressList(endpoint1.ip_endpoints));
  data1.set_connect_data(MockConnect(SYNCHRONOUS, ERR_CONNECTION_REFUSED));
  socket_factory_.AddSocketDataProvider(&data1);
  // The second connection attempt will be to `endpoint2`, which will succeed.
  StaticSocketDataProvider data2;
  data2.set_expected_addresses(AddressList(endpoint2.ip_endpoints));
  data2.set_connect_data(MockConnect(SYNCHRONOUS, OK));
  socket_factory_.AddSocketDataProvider(&data2);
  // The handshake will then fail, and provide no retry configs.
  SSLSocketDataProvider ssl2(ASYNC, ERR_ECH_NOT_NEGOTIATED);
  ssl2.expected_ech_config_list = ech_config_list2;
  ssl2.ech_retry_configs = std::vector<uint8_t>();
  socket_factory_.AddSSLSocketDataProvider(&ssl2);
  // The third connection attempt should skip `endpoint1` and retry with only
  // `endpoint2`.
  StaticSocketDataProvider data3;
  data3.set_expected_addresses(AddressList(endpoint2.ip_endpoints));
  data3.set_connect_data(MockConnect(SYNCHRONOUS, OK));
  socket_factory_.AddSocketDataProvider(&data3);
  // The handshake should not be passed ECH configs.
  SSLSocketDataProvider ssl3(ASYNC, OK);
  ssl3.expected_ech_config_list = std::vector<uint8_t>();
  socket_factory_.AddSSLSocketDataProvider(&ssl3);

  // The connection should ultimately succeed.
  base::HistogramTester histogram_tester;
  TestConnectJobDelegate test_delegate;
  std::unique_ptr<ConnectJob> ssl_connect_job =
      CreateConnectJob(&test_delegate, ProxyChain::Direct(), MEDIUM);
  EXPECT_THAT(ssl_connect_job->Connect(), test::IsError(ERR_IO_PENDING));
  EXPECT_THAT(test_delegate.WaitForResult(), test::IsOk());

  histogram_tester.ExpectUniqueSample("Net.SSL.ECHResult",
                                      4 /* kSuccessRollback */, 1);
}

// Test that `SSLConnectJob` fails the connection under strict ECH mode
// if the server returns an empty retry config.
TEST_P(SSLConnectJobTest, ECHStrictRollbackFail) {
  ssl_config_service_->SetEchModeGetter(
      std::make_unique<TestStaticEchModeGetter>(EchMode::kStrict,
                                                kHostHttps.host()));

  std::vector<uint8_t> ech_config_list;
  ASSERT_TRUE(MakeTestEchKeys("public.example", /*max_name_len=*/128,
                              &ech_config_list));

  HostResolverEndpointResult endpoint;
  endpoint.ip_endpoints = {IPEndPoint(ParseIP("1::"), 8441)};
  endpoint.metadata.supported_protocol_alpns = {"http/1.1"};
  endpoint.metadata.ech_config_list = ech_config_list;
  host_resolver_.rules()->AddRule(
      "host",
      MockHostResolverBase::RuleResolver::RuleResult(std::vector{endpoint}));

  // The first connection attempt will succeed at the TCP layer.
  StaticSocketDataProvider data1;
  data1.set_expected_addresses(AddressList(endpoint.ip_endpoints));
  data1.set_connect_data(MockConnect(SYNCHRONOUS, OK));
  socket_factory_.AddSocketDataProvider(&data1);
  // The handshake will then fail, and provide an empty retry config.
  SSLSocketDataProvider ssl1(ASYNC, ERR_ECH_NOT_NEGOTIATED);
  ssl1.expected_ech_config_list = ech_config_list;
  ssl1.ech_retry_configs = std::vector<uint8_t>();
  socket_factory_.AddSSLSocketDataProvider(&ssl1);
  // The connect job will restart and try the endpoint again.
  StaticSocketDataProvider data2;
  data2.set_expected_addresses(AddressList(endpoint.ip_endpoints));
  data2.set_connect_data(MockConnect(SYNCHRONOUS, OK));
  socket_factory_.AddSocketDataProvider(&data2);
  // The handshake should fail because it's strict mode and we got an empty
  // retry config.
  SSLSocketDataProvider ssl2(ASYNC, ERR_STRICT_ECH_REQUIRED);
  socket_factory_.AddSSLSocketDataProvider(&ssl2);

  // The connection should ultimately fail.
  base::HistogramTester histogram_tester;
  TestConnectJobDelegate test_delegate;
  std::unique_ptr<ConnectJob> ssl_connect_job =
      CreateConnectJob(&test_delegate, ProxyChain::Direct(), MEDIUM);
  EXPECT_THAT(ssl_connect_job->Connect(), test::IsError(ERR_IO_PENDING));
  EXPECT_THAT(test_delegate.WaitForResult(),
              test::IsError(ERR_STRICT_ECH_REQUIRED));

  histogram_tester.ExpectUniqueSample("Net.SSL.ECHResult",
                                      5 /* kErrorRollback */, 1);
}

// Test that `SSLConnectJob` will not retry more than once.
TEST_P(SSLConnectJobTest, ECHTooManyRetries) {
  std::vector<uint8_t> ech_config_list1, ech_config_list2, ech_config_list3;
  ASSERT_TRUE(MakeTestEchKeys("public.example", /*max_name_len=*/128,
                              &ech_config_list1));
  ASSERT_TRUE(MakeTestEchKeys("public.example", /*max_name_len=*/128,
                              &ech_config_list2));
  ASSERT_TRUE(MakeTestEchKeys("public.example", /*max_name_len=*/128,
                              &ech_config_list3));

  HostResolverEndpointResult endpoint;
  endpoint.ip_endpoints = {IPEndPoint(ParseIP("1::"), 8441)};
  endpoint.metadata.supported_protocol_alpns = {"http/1.1"};
  endpoint.metadata.ech_config_list = ech_config_list1;
  host_resolver_.rules()->AddRule(
      "host",
      MockHostResolverBase::RuleResolver::RuleResult(std::vector{endpoint}));

  // The first connection attempt will succeed.
  StaticSocketDataProvider data1;
  data1.set_connect_data(MockConnect(SYNCHRONOUS, OK));
  socket_factory_.AddSocketDataProvider(&data1);
  // The handshake will then fail, but provide retry configs.
  SSLSocketDataProvider ssl1(ASYNC, ERR_ECH_NOT_NEGOTIATED);
  ssl1.expected_ech_config_list = ech_config_list1;
  ssl1.ech_retry_configs = ech_config_list2;
  socket_factory_.AddSSLSocketDataProvider(&ssl1);
  // The second connection attempt will succeed.
  StaticSocketDataProvider data2;
  data2.set_connect_data(MockConnect(SYNCHRONOUS, OK));
  socket_factory_.AddSocketDataProvider(&data2);
  // The handshake will then fail, but provide new retry configs.
  SSLSocketDataProvider ssl2(ASYNC, ERR_ECH_NOT_NEGOTIATED);
  ssl2.expected_ech_config_list = ech_config_list2;
  ssl2.ech_retry_configs = ech_config_list3;
  socket_factory_.AddSSLSocketDataProvider(&ssl2);
  // There will be no third connection attempt.

  base::HistogramTester histogram_tester;
  TestConnectJobDelegate test_delegate;
  std::unique_ptr<ConnectJob> ssl_connect_job =
      CreateConnectJob(&test_delegate, ProxyChain::Direct(), MEDIUM);
  EXPECT_THAT(ssl_connect_job->Connect(), test::IsError(ERR_IO_PENDING));
  EXPECT_THAT(test_delegate.WaitForResult(),
              test::IsError(ERR_ECH_NOT_NEGOTIATED));

  histogram_tester.ExpectUniqueSample("Net.SSL.ECHResult", 3 /* kErrorRetry */,
                                      1);
}

// Test that `SSLConnectJob` will not retry for ECH given the wrong error.
TEST_P(SSLConnectJobTest, ECHWrongRetryError) {
  std::vector<uint8_t> ech_config_list1, ech_config_list2;
  ASSERT_TRUE(MakeTestEchKeys("public.example", /*max_name_len=*/128,
                              &ech_config_list1));
  ASSERT_TRUE(MakeTestEchKeys("public.example", /*max_name_len=*/128,
                              &ech_config_list2));

  HostResolverEndpointResult endpoint;
  endpoint.ip_endpoints = {IPEndPoint(ParseIP("1::"), 8441)};
  endpoint.metadata.supported_protocol_alpns = {"http/1.1"};
  endpoint.metadata.ech_config_list = ech_config_list1;
  host_resolver_.rules()->AddRule(
      "host",
      MockHostResolverBase::RuleResolver::RuleResult(std::vector{endpoint}));

  // The first connection attempt will succeed.
  StaticSocketDataProvider data1;
  data1.set_connect_data(MockConnect(SYNCHRONOUS, OK));
  socket_factory_.AddSocketDataProvider(&data1);
  // The handshake will then fail, but provide retry configs.
  SSLSocketDataProvider ssl1(ASYNC, ERR_FAILED);
  ssl1.expected_ech_config_list = ech_config_list1;
  ssl1.ech_retry_configs = ech_config_list2;
  socket_factory_.AddSSLSocketDataProvider(&ssl1);
  // There will be no second connection attempt.

  base::HistogramTester histogram_tester;
  TestConnectJobDelegate test_delegate;
  std::unique_ptr<ConnectJob> ssl_connect_job =
      CreateConnectJob(&test_delegate, ProxyChain::Direct(), MEDIUM);
  EXPECT_THAT(ssl_connect_job->Connect(), test::IsError(ERR_IO_PENDING));
  EXPECT_THAT(test_delegate.WaitForResult(), test::IsError(ERR_FAILED));

  histogram_tester.ExpectUniqueSample("Net.SSL.ECHResult",
                                      1 /* kErrorInitial */, 1);
}

// Test the legacy crypto callback can trigger after the ECH recovery flow.
TEST_P(SSLConnectJobTest, ECHRecoveryThenLegacyCrypto) {
  std::vector<uint8_t> ech_config_list1, ech_config_list2, ech_config_list3;
  ASSERT_TRUE(MakeTestEchKeys("public.example", /*max_name_len=*/128,
                              &ech_config_list1));
  ASSERT_TRUE(MakeTestEchKeys("public.example", /*max_name_len=*/128,
                              &ech_config_list2));
  ASSERT_TRUE(MakeTestEchKeys("public.example", /*max_name_len=*/128,
                              &ech_config_list3));

  // Configure two HTTPS RR routes, to test the retry uses the correct one.
  HostResolverEndpointResult endpoint1, endpoint2;
  endpoint1.ip_endpoints = {IPEndPoint(ParseIP("1::"), 8441)};
  endpoint1.metadata.supported_protocol_alpns = {"http/1.1"};
  endpoint1.metadata.ech_config_list = ech_config_list1;
  endpoint2.ip_endpoints = {IPEndPoint(ParseIP("2::"), 8442)};
  endpoint2.metadata.supported_protocol_alpns = {"http/1.1"};
  endpoint2.metadata.ech_config_list = ech_config_list2;
  host_resolver_.rules()->AddRule(
      "host", MockHostResolverBase::RuleResolver::RuleResult(
                  std::vector{endpoint1, endpoint2}));

  // The first connection attempt will be to `endpoint1`, which will fail.
  StaticSocketDataProvider data1;
  data1.set_expected_addresses(AddressList(endpoint1.ip_endpoints));
  data1.set_connect_data(MockConnect(SYNCHRONOUS, ERR_CONNECTION_REFUSED));
  socket_factory_.AddSocketDataProvider(&data1);
  // The second connection attempt will be to `endpoint2`, which will succeed.
  StaticSocketDataProvider data2;
  data2.set_expected_addresses(AddressList(endpoint2.ip_endpoints));
  data2.set_connect_data(MockConnect(SYNCHRONOUS, OK));
  socket_factory_.AddSocketDataProvider(&data2);
  // The handshake will then fail, and provide retry configs.
  SSLSocketDataProvider ssl2(ASYNC, ERR_ECH_NOT_NEGOTIATED);
  ssl2.expected_ech_config_list = ech_config_list2;
  ssl2.ech_retry_configs = ech_config_list3;
  socket_factory_.AddSSLSocketDataProvider(&ssl2);
  // The third connection attempt should skip `endpoint1` and retry with only
  // `endpoint2`.
  StaticSocketDataProvider data3;
  data3.set_expected_addresses(AddressList(endpoint2.ip_endpoints));
  data3.set_connect_data(MockConnect(SYNCHRONOUS, OK));
  socket_factory_.AddSocketDataProvider(&data3);
  // The handshake should be passed the retry configs. This will progress
  // further but trigger the legacy crypto fallback.
  SSLSocketDataProvider ssl3(ASYNC, ERR_SSL_PROTOCOL_ERROR);
  ssl3.expected_ech_config_list = ech_config_list3;
  socket_factory_.AddSSLSocketDataProvider(&ssl3);
  // The third connection attempt should still skip `endpoint1` and retry with
  // only `endpoint2`.
  StaticSocketDataProvider data4;
  data4.set_expected_addresses(AddressList(endpoint2.ip_endpoints));
  data4.set_connect_data(MockConnect(SYNCHRONOUS, OK));
  socket_factory_.AddSocketDataProvider(&data4);
  // The handshake should still be passed ECH retry configs. This time, the
  // connection enables legacy crypto and succeeds.
  SSLSocketDataProvider ssl4(ASYNC, OK);
  ssl4.expected_ech_config_list = ech_config_list3;
  socket_factory_.AddSSLSocketDataProvider(&ssl4);

  // The connection should ultimately succeed.
  base::HistogramTester histogram_tester;
  TestConnectJobDelegate test_delegate;
  std::unique_ptr<ConnectJob> ssl_connect_job =
      CreateConnectJob(&test_delegate, ProxyChain::Direct(), MEDIUM);
  EXPECT_THAT(ssl_connect_job->Connect(), test::IsError(ERR_IO_PENDING));
  EXPECT_THAT(test_delegate.WaitForResult(), test::IsOk());

  histogram_tester.ExpectUniqueSample("Net.SSL.ECHResult",
                                      2 /* kSuccessRetry */, 1);
}

// Test the ECH recovery flow can trigger after the legacy crypto fallback.
TEST_P(SSLConnectJobTest, LegacyCryptoThenECHRecovery) {
  std::vector<uint8_t> ech_config_list1, ech_config_list2, ech_config_list3;
  ASSERT_TRUE(MakeTestEchKeys("public.example", /*max_name_len=*/128,
                              &ech_config_list1));
  ASSERT_TRUE(MakeTestEchKeys("public.example", /*max_name_len=*/128,
                              &ech_config_list2));
  ASSERT_TRUE(MakeTestEchKeys("public.example", /*max_name_len=*/128,
                              &ech_config_list3));

  // Configure two HTTPS RR routes, to test the retry uses the correct one.
  HostResolverEndpointResult endpoint1, endpoint2;
  endpoint1.ip_endpoints = {IPEndPoint(ParseIP("1::"), 8441)};
  endpoint1.metadata.supported_protocol_alpns = {"http/1.1"};
  endpoint1.metadata.ech_config_list = ech_config_list1;
  endpoint2.ip_endpoints = {IPEndPoint(ParseIP("2::"), 8442)};
  endpoint2.metadata.supported_protocol_alpns = {"http/1.1"};
  endpoint2.metadata.ech_config_list = ech_config_list2;
  host_resolver_.rules()->AddRule(
      "host", MockHostResolverBase::RuleResolver::RuleResult(
                  std::vector{endpoint1, endpoint2}));

  // The first connection attempt will be to `endpoint1`, which will fail.
  StaticSocketDataProvider data1;
  data1.set_expected_addresses(AddressList(endpoint1.ip_endpoints));
  data1.set_connect_data(MockConnect(SYNCHRONOUS, ERR_CONNECTION_REFUSED));
  socket_factory_.AddSocketDataProvider(&data1);
  // The second connection attempt will be to `endpoint2`, which will succeed.
  StaticSocketDataProvider data2;
  data2.set_expected_addresses(AddressList(endpoint2.ip_endpoints));
  data2.set_connect_data(MockConnect(SYNCHRONOUS, OK));
  socket_factory_.AddSocketDataProvider(&data2);
  // The handshake will then fail, and trigger the legacy cryptography fallback.
  SSLSocketDataProvider ssl2(ASYNC, ERR_SSL_PROTOCOL_ERROR);
  ssl2.expected_ech_config_list = ech_config_list2;
  socket_factory_.AddSSLSocketDataProvider(&ssl2);
  // The third and fourth connection attempts proceed as before, but with legacy
  // cryptography enabled.
  StaticSocketDataProvider data3;
  data3.set_expected_addresses(AddressList(endpoint1.ip_endpoints));
  data3.set_connect_data(MockConnect(SYNCHRONOUS, ERR_CONNECTION_REFUSED));
  socket_factory_.AddSocketDataProvider(&data3);
  StaticSocketDataProvider data4;
  data4.set_expected_addresses(AddressList(endpoint2.ip_endpoints));
  data4.set_connect_data(MockConnect(SYNCHRONOUS, OK));
  socket_factory_.AddSocketDataProvider(&data4);
  // The handshake enables legacy crypto. Now ECH fails with retry configs.
  SSLSocketDataProvider ssl4(ASYNC, ERR_ECH_NOT_NEGOTIATED);
  ssl4.expected_ech_config_list = ech_config_list2;
  ssl4.ech_retry_configs = ech_config_list3;
  socket_factory_.AddSSLSocketDataProvider(&ssl4);
  // The fourth connection attempt should still skip `endpoint1` and retry with
  // only `endpoint2`.
  StaticSocketDataProvider data5;
  data5.set_expected_addresses(AddressList(endpoint2.ip_endpoints));
  data5.set_connect_data(MockConnect(SYNCHRONOUS, OK));
  socket_factory_.AddSocketDataProvider(&data5);
  // The handshake will now succeed with ECH retry configs and legacy
  // cryptography.
  SSLSocketDataProvider ssl5(ASYNC, OK);
  ssl5.expected_ech_config_list = ech_config_list3;
  socket_factory_.AddSSLSocketDataProvider(&ssl5);

  // The connection should ultimately succeed.
  base::HistogramTester histogram_tester;
  TestConnectJobDelegate test_delegate;
  std::unique_ptr<ConnectJob> ssl_connect_job =
      CreateConnectJob(&test_delegate, ProxyChain::Direct(), MEDIUM);
  EXPECT_THAT(ssl_connect_job->Connect(), test::IsError(ERR_IO_PENDING));
  EXPECT_THAT(test_delegate.WaitForResult(), test::IsOk());

  histogram_tester.ExpectUniqueSample("Net.SSL.ECHResult",
                                      2 /* kSuccessRetry */, 1);
}

TEST_P(SSLConnectJobTest, ServerPaddingNotRequested) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndDisableFeature(features::kAddTLSServerHandshakePadding);
  RecordingNetLogObserver net_log_observer(common_connect_job_params_.net_log,
                                           NetLogCaptureMode::kDefault);

  StaticSocketDataProvider data;
  socket_factory_.AddSocketDataProvider(&data);
  SSLSocketDataProvider ssl(ASYNC, OK);
  socket_factory_.AddSSLSocketDataProvider(&ssl);

  base::HistogramTester histogram_tester;
  TestConnectJobDelegate test_delegate;
  std::unique_ptr<ConnectJob> ssl_connect_job =
      CreateConnectJob(&test_delegate);

  test_delegate.StartJobExpectingResult(ssl_connect_job.get(), OK,
                                        /*expect_sync_result=*/false);
  histogram_tester.ExpectTotalCount("Net.SSL_Connection_Latency_ServerPadding",
                                    0);
  auto events = net_log_observer.GetEntriesWithType(
      NetLogEventType::SSL_CONNECT_JOB_SSL_CONNECT);
  ASSERT_EQ(1u, events.size());
  EXPECT_FALSE(
      GetOptionalIntegerValueFromParams(events[0], "requested_server_padding")
          .has_value());
}

TEST_P(SSLConnectJobTest, ServerPaddingRequest) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndEnableFeatureWithParameters(
      features::kAddTLSServerHandshakePadding,
      {{"AddTLSServerHandshakePaddingBytes", "128"}});
  RecordingNetLogObserver net_log_observer(common_connect_job_params_.net_log,
                                           NetLogCaptureMode::kDefault);

  StaticSocketDataProvider data;
  socket_factory_.AddSocketDataProvider(&data);
  SSLSocketDataProvider ssl(ASYNC, OK);
  ssl.expected_server_padding_to_request = 128;
  ssl.ssl_info.server_padding_received = true;
  socket_factory_.AddSSLSocketDataProvider(&ssl);

  base::HistogramTester histogram_tester;
  TestConnectJobDelegate test_delegate;
  std::unique_ptr<ConnectJob> ssl_connect_job =
      CreateConnectJob(&test_delegate);

  test_delegate.StartJobExpectingResult(ssl_connect_job.get(), OK,
                                        /*expect_sync_result=*/false);
  histogram_tester.ExpectTotalCount("Net.SSL_Connection_Latency_ServerPadding",
                                    1);
  auto events = net_log_observer.GetEntriesWithType(
      NetLogEventType::SSL_CONNECT_JOB_SSL_CONNECT);
  ASSERT_EQ(1u, events.size());
  EXPECT_EQ(128, GetOptionalIntegerValueFromParams(events[0],
                                                   "requested_server_padding"));
}

TEST_P(SSLConnectJobTest, ServerPaddingRequestZeroPadding) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndEnableFeatureWithParameters(
      features::kAddTLSServerHandshakePadding,
      {{"AddTLSServerHandshakePaddingBytes", "0"}});
  RecordingNetLogObserver net_log_observer(common_connect_job_params_.net_log,
                                           NetLogCaptureMode::kDefault);

  StaticSocketDataProvider data;
  socket_factory_.AddSocketDataProvider(&data);
  SSLSocketDataProvider ssl(ASYNC, OK);
  ssl.expected_server_padding_to_request = 0;
  ssl.ssl_info.server_padding_received = true;
  socket_factory_.AddSSLSocketDataProvider(&ssl);

  base::HistogramTester histogram_tester;
  TestConnectJobDelegate test_delegate;
  std::unique_ptr<ConnectJob> ssl_connect_job =
      CreateConnectJob(&test_delegate);

  test_delegate.StartJobExpectingResult(ssl_connect_job.get(), OK,
                                        /*expect_sync_result=*/false);
  histogram_tester.ExpectTotalCount("Net.SSL_Connection_Latency_ServerPadding",
                                    1);
  auto events = net_log_observer.GetEntriesWithType(
      NetLogEventType::SSL_CONNECT_JOB_SSL_CONNECT);
  ASSERT_EQ(1u, events.size());
  EXPECT_EQ(0, GetOptionalIntegerValueFromParams(events[0],
                                                 "requested_server_padding"));
}

TEST_P(SSLConnectJobTest, ServerPaddingRequestButNotReceived) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndEnableFeatureWithParameters(
      features::kAddTLSServerHandshakePadding,
      {{"AddTLSServerHandshakePaddingBytes", "0"}});
  RecordingNetLogObserver net_log_observer(common_connect_job_params_.net_log,
                                           NetLogCaptureMode::kDefault);

  StaticSocketDataProvider data;
  socket_factory_.AddSocketDataProvider(&data);
  SSLSocketDataProvider ssl(ASYNC, OK);
  ssl.expected_server_padding_to_request = 0;
  ssl.ssl_info.server_padding_received = false;
  socket_factory_.AddSSLSocketDataProvider(&ssl);

  base::HistogramTester histogram_tester;
  TestConnectJobDelegate test_delegate;
  std::unique_ptr<ConnectJob> ssl_connect_job =
      CreateConnectJob(&test_delegate);

  test_delegate.StartJobExpectingResult(ssl_connect_job.get(), OK,
                                        /*expect_sync_result=*/false);
  histogram_tester.ExpectTotalCount("Net.SSL_Connection_Latency_ServerPadding",
                                    0);
  auto events = net_log_observer.GetEntriesWithType(
      NetLogEventType::SSL_CONNECT_JOB_SSL_CONNECT);
  ASSERT_EQ(1u, events.size());
  EXPECT_EQ(0, GetOptionalIntegerValueFromParams(events[0],
                                                 "requested_server_padding"));
}

class SSLConnectJobOptimisticDnsTest
    : public SSLConnectJobTestBase,
      public ::testing::WithParamInterface<bool> {
 public:
  SSLConnectJobOptimisticDnsTest() {
    scoped_feature_list_.InitWithFeaturesAndParameters(
        {{features::kOptimisticDnsForTcp,
          {{features::kUseStaleConnectorsForOptimisticDns.name,
            IsUsingStaleConnectors() ? "true" : "false"}}},
         {features::kHappyEyeballsV2, {}}},
        /*disabled_features=*/{});
  }

  bool IsUsingStaleConnectors() const { return GetParam(); }
};

INSTANTIATE_TEST_SUITE_P(All, SSLConnectJobOptimisticDnsTest, testing::Bool());

TEST_P(SSLConnectJobOptimisticDnsTest, StaleDnsTlsFailure) {
  HostResolverEndpointResult endpoint;
  endpoint.ip_endpoints = {IPEndPoint(ParseIP("1::"), 8441)};
  host_resolver_.rules()->AddRule(
      "host",
      MockHostResolverBase::RuleResolver::RuleResult(std::vector{endpoint}));
  host_resolver_.set_is_stale_while_refreshing(true);

  // First connection attempt (Stale DNS) succeeds TCP but fails TLS with a cert
  // error.
  StaticSocketDataProvider data1;
  data1.set_expected_addresses(AddressList(endpoint.ip_endpoints));
  data1.set_connect_data(MockConnect(SYNCHRONOUS, OK));
  socket_factory_.AddSocketDataProvider(&data1);

  SSLSocketDataProvider ssl_fail(ASYNC, ERR_CERT_COMMON_NAME_INVALID);
  socket_factory_.AddSSLSocketDataProvider(&ssl_fail);

  // Second connection attempt (Fresh DNS) succeeds TCP and TLS.
  StaticSocketDataProvider data2;
  data2.set_expected_addresses(AddressList(endpoint.ip_endpoints));
  data2.set_connect_data(MockConnect(SYNCHRONOUS, OK));
  socket_factory_.AddSocketDataProvider(&data2);

  SSLSocketDataProvider ssl_success(ASYNC, OK);
  socket_factory_.AddSSLSocketDataProvider(&ssl_success);

  TestConnectJobDelegate test_delegate;
  std::unique_ptr<ConnectJob> ssl_connect_job =
      CreateConnectJob(&test_delegate, ProxyChain::Direct());

  EXPECT_THAT(ssl_connect_job->Connect(), test::IsError(ERR_IO_PENDING));
  EXPECT_THAT(test_delegate.WaitForResult(), test::IsOk());

  // Both sockets should be consumed (retry occurred).
  EXPECT_TRUE(data1.AllReadDataConsumed());
  EXPECT_TRUE(data1.AllWriteDataConsumed());
  EXPECT_TRUE(data2.AllReadDataConsumed());
  EXPECT_TRUE(data2.AllWriteDataConsumed());
}

TEST_P(SSLConnectJobOptimisticDnsTest, StaleDnsTlsFailureTwice) {
  HostResolverEndpointResult endpoint;
  endpoint.ip_endpoints = {IPEndPoint(ParseIP("1::"), 8441)};
  host_resolver_.rules()->AddRule(
      "host",
      MockHostResolverBase::RuleResolver::RuleResult(std::vector{endpoint}));
  host_resolver_.set_is_stale_while_refreshing(true);

  // First connection attempt (Stale DNS) succeeds TCP but fails TLS with a cert
  // error.
  StaticSocketDataProvider data1;
  data1.set_expected_addresses(AddressList(endpoint.ip_endpoints));
  data1.set_connect_data(MockConnect(SYNCHRONOUS, OK));
  socket_factory_.AddSocketDataProvider(&data1);

  SSLSocketDataProvider ssl_fail1(ASYNC, ERR_CERT_COMMON_NAME_INVALID);
  socket_factory_.AddSSLSocketDataProvider(&ssl_fail1);

  // Second connection attempt (Fresh DNS) succeeds TCP and also fails TLS.
  StaticSocketDataProvider data2;
  data2.set_expected_addresses(AddressList(endpoint.ip_endpoints));
  data2.set_connect_data(MockConnect(SYNCHRONOUS, OK));
  socket_factory_.AddSocketDataProvider(&data2);

  SSLSocketDataProvider ssl_fail2(ASYNC, ERR_CERT_COMMON_NAME_INVALID);
  socket_factory_.AddSSLSocketDataProvider(&ssl_fail2);

  TestConnectJobDelegate test_delegate(
      TestConnectJobDelegate::SocketExpected::ALWAYS);
  std::unique_ptr<ConnectJob> ssl_connect_job =
      CreateConnectJob(&test_delegate, ProxyChain::Direct());

  EXPECT_THAT(ssl_connect_job->Connect(), test::IsError(ERR_IO_PENDING));
  EXPECT_THAT(test_delegate.WaitForResult(),
              test::IsError(ERR_CERT_COMMON_NAME_INVALID));

  // Both sockets should be consumed (retry occurred and failed).
  EXPECT_TRUE(data1.AllReadDataConsumed());
  EXPECT_TRUE(data1.AllWriteDataConsumed());
  EXPECT_TRUE(data2.AllReadDataConsumed());
  EXPECT_TRUE(data2.AllWriteDataConsumed());
}

TEST_P(SSLConnectJobOptimisticDnsTest, StaleDnsEarlyDataDisabled) {
  HostResolverEndpointResult endpoint;
  endpoint.ip_endpoints = {IPEndPoint(ParseIP("1::"), 8441)};
  host_resolver_.rules()->AddRule(
      "host",
      MockHostResolverBase::RuleResolver::RuleResult(std::vector{endpoint}));
  host_resolver_.set_is_stale_while_refreshing(true);

  // First connection attempt (Stale DNS) succeeds TCP but fails TLS with a cert
  // error.
  StaticSocketDataProvider data1;
  data1.set_expected_addresses(AddressList(endpoint.ip_endpoints));
  data1.set_connect_data(MockConnect(SYNCHRONOUS, OK));
  socket_factory_.AddSocketDataProvider(&data1);

  SSLSocketDataProvider ssl_fail1(ASYNC, ERR_CERT_COMMON_NAME_INVALID);
  // Early data should be explicitly disabled on the stale connection attempt.
  ssl_fail1.expected_early_data_enabled = false;
  socket_factory_.AddSSLSocketDataProvider(&ssl_fail1);

  // Second connection attempt (Fresh DNS) succeeds TCP and TLS.
  StaticSocketDataProvider data2;
  data2.set_expected_addresses(AddressList(endpoint.ip_endpoints));
  data2.set_connect_data(MockConnect(SYNCHRONOUS, OK));
  socket_factory_.AddSocketDataProvider(&data2);

  SSLSocketDataProvider ssl_success(ASYNC, OK);
  // Early data should be re-enabled on the fresh retry attempt.
  ssl_success.expected_early_data_enabled = true;
  socket_factory_.AddSSLSocketDataProvider(&ssl_success);

  SSLConfig ssl_config;
  ssl_config.early_data_enabled = true;
  scoped_refptr<SSLSocketParams> ssl_params =
      base::MakeRefCounted<SSLSocketParams>(
          ConnectJobParams(
              CreateDirectTransportSocketParams(SecureDnsPolicy::kAllow)),
          HostPortPair::FromSchemeHostPort(kHostHttps), ssl_config,
          NetworkAnonymizationKey());

  TestConnectJobDelegate test_delegate;
  auto ssl_connect_job = std::make_unique<SSLConnectJob>(
      DEFAULT_PRIORITY, SocketTag(), &common_connect_job_params_, ssl_params,
      &test_delegate, /*net_log=*/nullptr);

  EXPECT_THAT(ssl_connect_job->Connect(), test::IsError(ERR_IO_PENDING));
  EXPECT_THAT(test_delegate.WaitForResult(), test::IsOk());

  // Both sockets should be consumed (retry occurred).
  EXPECT_TRUE(data1.AllReadDataConsumed());
  EXPECT_TRUE(data1.AllWriteDataConsumed());
  EXPECT_TRUE(data2.AllReadDataConsumed());
  EXPECT_TRUE(data2.AllWriteDataConsumed());
}

}  // namespace
}  // namespace net
