blob: ac4ccd18192386c3a818a1f7d96fd2d2daef3cec [file]
// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_HTTP_HTTP_STREAM_POOL_ATTEMPT_H_
#define NET_HTTP_HTTP_STREAM_POOL_ATTEMPT_H_
#include <memory>
#include <optional>
#include "base/containers/flat_set.h"
#include "base/memory/raw_ref.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "base/trace_event/trace_event.h"
#include "base/types/expected.h"
#include "net/base/ip_endpoint.h"
#include "net/base/load_timing_info.h"
#include "net/base/net_export.h"
#include "net/dns/host_resolver.h"
#include "net/dns/public/host_resolver_results.h"
#include "net/http/http_stream_pool.h"
#include "net/socket/tls_stream_attempt.h"
#include "net/ssl/ssl_cert_request_info.h"
#include "net/ssl/ssl_config.h"
namespace net {
class StreamSocket;
class HttpStreamKey;
// Represents a single in-flight attempt which can make at most two concurrent
// TCP-based inner attempts (one IPv4, one IPv6). Upon success, it creates an
// HttpStream or a SpdySession and notifies the Delegate. If all inner attempts
// fail, it notifies the Delegate of the failure.
//
// In the future, this may also handle QUIC inner attempts.
class HttpStreamPool::Attempt {
public:
// An interface to abstract dependencies so that we can have unittests without
// depending on AttemptManager. In production code AttemptManager will
// implement this interface. Unless otherwise specified, all methods of this
// interface must not delete the corresponding Attempt.
class NET_EXPORT_PRIVATE Delegate {
public:
virtual ~Delegate() = default;
// Returns the HttpStreamKey this attempt uses to make inner attempts.
virtual const HttpStreamKey& GetHttpStreamKey() const = 0;
// Returns the ServiceEndpointRequest associated with this attempt.
virtual HostResolver::ServiceEndpointRequest& GetServiceEndpointRequest()
const = 0;
// Returns true when the ServiceEndpointRequest is finished.
virtual bool IsServiceEndpointRequestFinished() const = 0;
// Returns true when endpoints are SVCB optional. See
// https://www.rfc-editor.org/rfc/rfc9460#section-3
virtual bool IsSvcbOptional() const = 0;
// Returns the base SSLConfig associated with this attempt.
virtual SSLConfig GetBaseSSLConfig() const = 0;
// Returns the ALPNs to be used with ALPN.
virtual const NextProtoVector& GetAlpnProtos() const = 0;
// Callback methods. Only one of these methods will be called. Once one of
// these methods is called, the Attempt must immediately be deleted, or it
// will enter an undefined state.
virtual void OnStreamSocketReady(
Attempt* attempt,
std::unique_ptr<StreamSocket> stream,
LoadTimingInfo::ConnectTiming connect_timing) = 0;
virtual void OnAttemptFailure(Attempt* attempt, int rv) = 0;
virtual void OnCertificateError(Attempt* attempt,
int rv,
SSLInfo ssl_info) = 0;
virtual void OnNeedsClientCertificate(
Attempt* attempt,
scoped_refptr<SSLCertRequestInfo> cert_info) = 0;
};
Attempt(Delegate& delegate,
const StreamAttemptParams& stream_attempt_params,
NetLogWithSource net_log);
~Attempt();
// Starts the attempt.
void Start();
// Processes the service endpoint changes. Note that calling this method
// may destroy `this` synchronously.
void ProcessServiceEndpointChanges();
const base::flat_set<IPEndPoint>& attempted_endpoints_for_testing() const {
return attempted_endpoints_;
}
private:
class TcpAttempt;
// Represents a result of a failed attempt.
struct AttemptResult {
int error;
IPEndPoint ip_endpoint;
};
// Tries to start an attempt. If all endpoints are failed, notifies the
// delegate with the most recent attempt failure, or ERR_NAME_NOT_RESOLVED if
// no endpoint is attempted. Otherwise, waits for further endpoints update,
// and/or completing in-flight attempts.
void MaybeAttempt();
// Starts an internal attempt for `ip_endpoint`.
void StartAttempt(IPEndPoint ip_endpoint);
// Returns true when the `endpoint` is usable in the sense of
// SVCB resource record. See
// https://www.rfc-editor.org/rfc/rfc9460.html#section-9.3.
bool IsEndpointUsable(const ServiceEndpoint& endpoint,
bool svcb_optional) const;
// Calculates the IPEndPoint to attempt. Returns std::nullopt when no endpoint
// is available.
std::optional<IPEndPoint> GetIPEndPointToAttempt() const;
// Returns the ServiceEndpoint to use for the TLS handshake. Returns an error
// when the endpoint is not usable. See IsEndpointUsable() for details.
base::expected<ServiceEndpoint, TlsStreamAttempt::GetServiceEndpointError>
GetServiceEndpointForTlsHandshake(const IPEndPoint& ip_endpoint) const;
void OnTcpAttemptSlow(TcpAttempt* attempt);
void OnTcpAttemptComplete(TcpAttempt* attempt, int rv);
void HandleSingleFailure(std::unique_ptr<TcpAttempt> attempt, int rv);
const raw_ref<Delegate> delegate_;
const raw_ref<const StreamAttemptParams> stream_attempt_params_;
const bool using_tls_;
const perfetto::Track track_;
const NetLogWithSource net_log_;
std::unique_ptr<TcpAttempt> ipv4_attempt_;
std::unique_ptr<TcpAttempt> ipv6_attempt_;
// Set to true when the associated ServiceEndpointRequest indicates that
// endpoints are ready for crypto (TLS) handshake.
bool is_crypto_ready_ = false;
// Set to true when any of the inner attempts is observed to be slow.
bool observed_slow_attempt_ = false;
std::optional<AttemptResult> most_recent_attempt_failure_;
// Contains IPEndPoints that have been attempted so far, including those
// that are currently being attempted.
base::flat_set<IPEndPoint> attempted_endpoints_;
base::WeakPtrFactory<Attempt> weak_ptr_factory_{this};
};
} // namespace net
#endif // NET_HTTP_HTTP_STREAM_POOL_ATTEMPT_H_