1 // Copyright 2024 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef NET_HTTP_HTTP_STREAM_POOL_ATTEMPT_MANAGER_H_ 6 #define NET_HTTP_HTTP_STREAM_POOL_ATTEMPT_MANAGER_H_ 7 8 #include <memory> 9 #include <optional> 10 #include <set> 11 #include <vector> 12 13 #include "base/containers/flat_set.h" 14 #include "base/containers/unique_ptr_adapters.h" 15 #include "base/memory/raw_ptr.h" 16 #include "base/memory/scoped_refptr.h" 17 #include "base/memory/weak_ptr.h" 18 #include "base/time/time.h" 19 #include "base/timer/timer.h" 20 #include "base/types/expected.h" 21 #include "net/base/completion_once_callback.h" 22 #include "net/base/load_states.h" 23 #include "net/base/load_timing_info.h" 24 #include "net/base/net_error_details.h" 25 #include "net/base/priority_queue.h" 26 #include "net/base/request_priority.h" 27 #include "net/dns/host_resolver.h" 28 #include "net/dns/public/resolve_error_info.h" 29 #include "net/http/http_stream_pool.h" 30 #include "net/http/http_stream_pool_job.h" 31 #include "net/http/http_stream_request.h" 32 #include "net/log/net_log_with_source.h" 33 #include "net/socket/connection_attempts.h" 34 #include "net/socket/next_proto.h" 35 #include "net/socket/stream_attempt.h" 36 #include "net/socket/stream_socket_handle.h" 37 #include "net/socket/tls_stream_attempt.h" 38 #include "net/spdy/multiplexed_session_creation_initiator.h" 39 #include "net/ssl/ssl_cert_request_info.h" 40 #include "net/third_party/quiche/src/quiche/quic/core/quic_versions.h" 41 #include "url/gurl.h" 42 43 namespace net { 44 45 class HttpNetworkSession; 46 class NetLog; 47 class HttpStreamKey; 48 49 // Maintains in-flight Jobs. Peforms DNS resolution. 50 class HttpStreamPool::AttemptManager 51 : public HostResolver::ServiceEndpointRequest::Delegate { 52 public: 53 // Time to delay connection attempts more than one when the destination is 54 // known to support HTTP/2, to avoid unnecessary socket connection 55 // establishments. See https://crbug.com/718576 56 static constexpr base::TimeDelta kSpdyThrottleDelay = base::Milliseconds(300); 57 58 // `group` must outlive `this`. 59 AttemptManager(Group* group, NetLog* net_log); 60 61 AttemptManager(const AttemptManager&) = delete; 62 AttemptManager& operator=(const AttemptManager&) = delete; 63 64 ~AttemptManager() override; 65 group()66 Group* group() { return group_; } 67 service_endpoint_request()68 HostResolver::ServiceEndpointRequest* service_endpoint_request() { 69 return service_endpoint_request_.get(); 70 } 71 is_service_endpoint_request_finished()72 bool is_service_endpoint_request_finished() const { 73 return service_endpoint_request_finished_; 74 } 75 dns_resolution_start_time()76 base::TimeTicks dns_resolution_start_time() const { 77 return dns_resolution_start_time_; 78 } 79 dns_resolution_end_time()80 base::TimeTicks dns_resolution_end_time() const { 81 return dns_resolution_end_time_; 82 } 83 84 const NetLogWithSource& net_log(); 85 86 // Starts a Job. Will call one of Job::Delegate methods to notify results. 87 void StartJob(Job* job, 88 RequestPriority priority, 89 const std::vector<SSLConfig::CertAndStatus>& allowed_bad_certs, 90 quic::ParsedQuicVersion quic_version, 91 const NetLogWithSource& net_log); 92 93 // Creates idle streams or sessions for `num_streams` be opened. 94 // Note that this method finishes synchronously, or `callback` is called, once 95 // `this` has enough streams/sessions for `num_streams` be opened. This means 96 // that when there are two preconnect requests with `num_streams = 1`, all 97 // callbacks are invoked when one stream/session is established (not two). 98 int Preconnect(size_t num_streams, 99 quic::ParsedQuicVersion quic_version, 100 CompletionOnceCallback callback); 101 102 // HostResolver::ServiceEndpointRequest::Delegate implementation: 103 void OnServiceEndpointsUpdated() override; 104 void OnServiceEndpointRequestFinished(int rv) override; 105 106 // Tries to process a single pending request/preconnect. 107 void ProcessPendingJob(); 108 109 // Returns the number of total jobs in this manager. JobCount()110 size_t JobCount() const { return jobs_.size(); } 111 112 // Returns the number of in-flight attempts. InFlightAttemptCount()113 size_t InFlightAttemptCount() const { return in_flight_attempts_.size(); } 114 115 // Cancels all in-flight attempts. 116 void CancelInFlightAttempts(StreamCloseReason reason); 117 118 // Called when `job` is going to be destroyed. 119 void OnJobComplete(Job* job); 120 121 // Cancels all jobs. 122 void CancelJobs(int error); 123 124 // Returns the number of pending jobs/preconnects. The number is 125 // calculated by subtracting the number of in-flight attempts (excluding slow 126 // attempts) from the number of total jobs. 127 size_t PendingJobCount() const; 128 size_t PendingPreconnectCount() const; 129 130 // Returns the current load state. 131 LoadState GetLoadState() const; 132 133 // Called when the priority of `job` is set. 134 void SetJobPriority(Job* job, RequestPriority priority); 135 136 // Returns the highest priority in `jobs_` when there is at least one job. 137 // Otherwise, returns IDLE assuming this manager is doing preconnects. 138 RequestPriority GetPriority() const; 139 140 // Returns true when `this` is blocked by the pool's stream limit. 141 bool IsStalledByPoolLimit(); 142 143 // Returns whether attempts is "SVCB-optional". See 144 // https://www.rfc-editor.org/rfc/rfc9460.html#section-3-4 145 // Note that the result can be changed over time while the DNS resolution is 146 // still ongoing. 147 bool IsSvcbOptional(); 148 149 // Called when the server required HTTP/1.1. Clears the current SPDY session 150 // if exists. Subsequent jobs will fail while `this` is alive. 151 void OnRequiredHttp11(); 152 153 // Called when the QuicTask owned by `this` is completed. 154 void OnQuicTaskComplete(int rv, NetErrorDetails details); 155 156 // Retrieves information on the current state of `this` as a base::Value. 157 base::Value::Dict GetInfoAsValue(); 158 GetQuicTaskResultForTesting()159 std::optional<int> GetQuicTaskResultForTesting() { return quic_task_result_; } 160 161 MultiplexedSessionCreationInitiator 162 CalculateMultiplexedSessionCreationInitiator(); 163 164 private: 165 // Represents failure of connection attempts. Used to notify job of completion 166 // for failure cases. 167 enum class FailureKind { 168 kStreamFailed, 169 kCertifcateError, 170 kNeedsClientAuth, 171 }; 172 173 // Represents reasons if future connection attempts could be blocked or not. 174 enum class CanAttemptResult { 175 kAttempt, 176 kNoPendingJob, 177 kBlockedStreamAttempt, 178 kThrottledForSpdy, 179 kReachedGroupLimit, 180 kReachedPoolLimit, 181 }; 182 183 // The state of TCP/TLS connection attempts. 184 enum class TcpBasedAttemptState { 185 kNotStarted, 186 kAttempting, 187 kSucceededAtLeastOnce, 188 kAllAttemptsFailed, 189 }; 190 191 using JobQueue = PriorityQueue<raw_ptr<Job>>; 192 193 class InFlightAttempt; 194 struct PreconnectEntry; 195 196 static std::string_view TcpBasedAttemptStateToString( 197 TcpBasedAttemptState state); 198 199 const HttpStreamKey& stream_key() const; 200 201 const SpdySessionKey& spdy_session_key() const; 202 203 const QuicSessionAliasKey& quic_session_alias_key() const; 204 205 HttpNetworkSession* http_network_session(); 206 SpdySessionPool* spdy_session_pool(); 207 QuicSessionPool* quic_session_pool(); 208 209 HttpStreamPool* pool(); 210 const HttpStreamPool* pool() const; 211 212 int WaitForSSLConfigReady(CompletionOnceCallback callback); 213 214 base::expected<SSLConfig, TlsStreamAttempt::GetSSLConfigError> GetSSLConfig( 215 InFlightAttempt* attempt); 216 217 bool UsingTls() const; 218 219 bool RequiresHTTP11(); 220 221 void StartInternal(RequestPriority priority); 222 223 void ResolveServiceEndpoint(RequestPriority initial_priority); 224 225 void RestrictAllowedProtocols(NextProtoSet allowed_alpns); 226 227 void MaybeChangeServiceEndpointRequestPriority(); 228 229 // Called when service endpoint results have changed or finished. 230 void ProcessServiceEndpointChanges(); 231 232 // Returns true when there is an active SPDY session that can be used for 233 // on-going jobs after service endpoint results has changed. May notify jobs 234 // of stream ready. 235 bool CanUseExistingSessionAfterEndpointChanges(); 236 237 // Runs the stream attempt delay timer if stream attempts are blocked and the 238 // timer is not running. 239 void MaybeRunStreamAttemptDelayTimer(); 240 241 // Calculate SSLConfig if it's not calculated yet and `this` has received 242 // enough information to calculate it. 243 void MaybeCalculateSSLConfig(); 244 245 // Attempts QUIC sessions if QUIC can be used and `this` is ready to start 246 // cryptographic connection handshakes. 247 void MaybeAttemptQuic(); 248 249 // Attempts connections if there are pending jobs and IPEndPoints that 250 // haven't failed. If `max_attempts` is given, attempts connections up to 251 // `max_attempts`. 252 void MaybeAttemptConnection( 253 std::optional<size_t> max_attempts = std::nullopt); 254 255 // Returns true if there are pending jobs and the pool and the group 256 // haven't reached stream limits. If the pool reached the stream limit, may 257 // close idle sockets in other groups. Also may cancel preconnects or trigger 258 // `spdy_throttle_timer_`. 259 bool IsConnectionAttemptReady(); 260 261 // Actual implementation of IsConnectionAttemptReady(), without having side 262 // effects. 263 CanAttemptResult CanAttemptConnection(); 264 265 // Returns true only when there are no jobs that ignore the pool and group 266 // limits. 267 bool ShouldRespectLimits() const; 268 269 // Returns true only when there are no jobs that disable IP based pooling. 270 bool IsIpBasedPoolingEnabled() const; 271 272 // Returns true only when there are no jobs that disable alternative services. 273 bool IsAlternativeServiceEnabled() const; 274 275 // Returns true when connection attempts should be throttled because there is 276 // an in-flight attempt and the destination is known to support HTTP/2. 277 bool ShouldThrottleAttemptForSpdy(); 278 279 // Helper method to calculate pending jobs/preconnects. 280 size_t PendingCountInternal(size_t pending_count) const; 281 282 std::optional<IPEndPoint> GetIPEndPointToAttempt(); 283 std::optional<IPEndPoint> FindPreferredIPEndpoint( 284 const std::vector<IPEndPoint>& ip_endpoints); 285 286 // Calculate the failure kind to notify jobs of failure. Used to call one of 287 // the job's methods. 288 FailureKind DetermineFailureKind(); 289 290 // Notifies a failure to all jobs. 291 void NotifyFailure(); 292 293 // Notifies a failure to a single job. Used by NotifyFailure(). 294 void NotifyJobOfFailure(); 295 296 // Notifies all preconnects of completion. 297 void NotifyPreconnectsComplete(int rv); 298 299 // Called after completion of a connection attempt to decrement stream 300 // counts in preconnect entries. Invokes the callback of an entry when the 301 // entry's stream counts is less than or equal to `active_stream_count` 302 // (i.e., `this` has enough streams). 303 void ProcessPreconnectsAfterAttemptComplete(int rv, 304 size_t active_stream_count); 305 306 // Creates a text based stream and notifies the highest priority job. 307 void CreateTextBasedStreamAndNotify( 308 std::unique_ptr<StreamSocket> stream_socket, 309 StreamSocketHandle::SocketReuseType reuse_type, 310 LoadTimingInfo::ConnectTiming connect_timing); 311 312 void CreateSpdyStreamAndNotify(); 313 314 void CreateQuicStreamAndNotify(); 315 316 void NotifyStreamReady(std::unique_ptr<HttpStream> stream, 317 NextProto negotiated_protocol); 318 319 // Called when a SPDY session is ready to use. Cancels in-flight attempts. 320 // Closes idle streams. Completes preconnects. 321 void HandleSpdySessionReady(); 322 323 // Called when a QUIC session is ready to use. Cancels in-flight attempts. 324 // Closes idle streams. Completes preconnects. 325 void HandleQuicSessionReady(); 326 327 // Extracts an entry from `jobs_` of which priority is highest. The ownership 328 // of the entry is moved to `notified_jobs_`. 329 Job* ExtractFirstJobToNotify(); 330 331 // Remove the pointeee of `job_pointer` from `jobs_`. May cancel in-flight 332 // attempts when there are no limit ignoring jobs after removing the job and 333 // in-flight attempts count is larger than the limit. 334 raw_ptr<Job> RemoveJobFromQueue(JobQueue::Pointer job_pointer); 335 336 void OnInFlightAttemptComplete(InFlightAttempt* raw_attempt, int rv); 337 void OnInFlightAttemptTcpHandshakeComplete(InFlightAttempt* raw_attempt, 338 int rv); 339 void OnInFlightAttemptSlow(InFlightAttempt* raw_attempt); 340 341 void HandleAttemptFailure(std::unique_ptr<InFlightAttempt> in_flight_attempt, 342 int rv); 343 344 void OnSpdyThrottleDelayPassed(); 345 346 // Returns the delay for TCP-based stream attempts in favor of QUIC. 347 base::TimeDelta GetStreamAttemptDelay(); 348 349 // Updates whether stream attempts should be blocked or not. May cancel 350 // `stream_attempt_delay_timer_`. 351 void UpdateStreamAttemptState(); 352 353 // Called when `stream_attempt_delay_timer_` is fired. 354 void OnStreamAttemptDelayPassed(); 355 356 // If the destination is forced to use QUIC and the QUIC version is unknown, 357 // try the preferred QUIC version that is supported by default. 358 void MaybeUpdateQuicVersionWhenForced(quic::ParsedQuicVersion& quic_version); 359 360 bool CanUseTcpBasedProtocols(); 361 362 bool CanUseQuic(); 363 364 bool CanUseExistingQuicSession(); 365 366 bool IsEchEnabled() const; 367 368 // Returns true when `endpoint` can be used to attempt TCP/TLS connections. 369 bool IsEndpointUsableForTcpBasedAttempt(const ServiceEndpoint& endpoint, 370 bool svcb_optional); 371 372 // Mark QUIC brokenness if QUIC attempts failed but TCP/TLS attempts succeeded 373 // or not attempted. 374 void MaybeMarkQuicBroken(); 375 376 base::Value::Dict GetStatesAsNetLogParams(); 377 378 bool CanComplete() const; 379 380 void MaybeComplete(); 381 382 const raw_ptr<Group> group_; 383 384 const NetLogWithSource net_log_; 385 386 NextProtoSet allowed_alpns_ = NextProtoSet::All(); 387 388 // Holds jobs that are waiting for notifications. 389 JobQueue jobs_; 390 // Holds jobs that are already notified results. We need to keep them to avoid 391 // dangling pointers. 392 std::set<raw_ptr<Job>> notified_jobs_; 393 394 base::flat_set<raw_ptr<Job>> limit_ignoring_jobs_; 395 396 base::flat_set<raw_ptr<Job>> ip_based_pooling_disabling_jobs_; 397 398 base::flat_set<raw_ptr<Job>> alternative_service_disabling_jobs_; 399 400 // Holds preconnect requests. 401 std::set<std::unique_ptr<PreconnectEntry>, base::UniquePtrComparator> 402 preconnects_; 403 404 std::unique_ptr<HostResolver::ServiceEndpointRequest> 405 service_endpoint_request_; 406 bool service_endpoint_request_finished_ = false; 407 base::TimeTicks dns_resolution_start_time_; 408 base::TimeTicks dns_resolution_end_time_; 409 410 // Set to true when `this` cannot handle further jobs. Used to ensure that 411 // `this` doesn't accept further jobs while notifying the failure to the 412 // existing jobs. 413 bool is_failing_ = false; 414 415 // Set to true when `CancelJobs()` is called. 416 bool is_canceling_jobs_ = false; 417 418 NetErrorDetails net_error_details_; 419 ResolveErrorInfo resolve_error_info_; 420 ConnectionAttempts connection_attempts_; 421 422 // Set to an error from the latest stream attempt failure or network change 423 // events. Used to notify delegates when all attempts failed. 424 int error_to_notify_ = ERR_FAILED; 425 426 // Set to a SSLInfo when an attempt has failed with a certificate error. Used 427 // to notify jobs. 428 std::optional<SSLInfo> cert_error_ssl_info_; 429 430 // Set to a SSLCertRequestInfo when an attempt has requested a client cert. 431 // Used to notify jobs. 432 scoped_refptr<SSLCertRequestInfo> client_auth_cert_info_; 433 434 // Allowed bad certificates from the newest job. 435 std::vector<SSLConfig::CertAndStatus> allowed_bad_certs_; 436 // SSLConfig for all TLS connection attempts. Calculated after the service 437 // endpoint request is ready to proceed cryptographic handshakes. 438 // TODO(crbug.com/40812426): We need to have separate SSLConfigs when we 439 // support multiple HTTPS RR that have different service endpoints. 440 std::optional<SSLConfig> ssl_config_; 441 std::vector<CompletionOnceCallback> ssl_config_waiting_callbacks_; 442 443 std::set<std::unique_ptr<InFlightAttempt>, base::UniquePtrComparator> 444 in_flight_attempts_; 445 // The number of in-flight attempts that are treated as slow. 446 size_t slow_attempt_count_ = 0; 447 448 base::OneShotTimer spdy_throttle_timer_; 449 bool spdy_throttle_delay_passed_ = false; 450 451 // When true, try to use IPv6 for the next attempt first. 452 bool prefer_ipv6_ = true; 453 // Updated when a stream attempt failed. Used to calculate next IPEndPoint to 454 // attempt. 455 std::set<IPEndPoint> failed_ip_endpoints_; 456 // Updated when a stream attempt is considered slow. Used to calculate next 457 // IPEndPoint to attempt. 458 std::set<IPEndPoint> slow_ip_endpoints_; 459 460 // The current state of TCP/TLS connection attempts. 461 TcpBasedAttemptState tcp_based_attempt_state_ = 462 TcpBasedAttemptState::kNotStarted; 463 464 // Initialized when one of an attempt is negotiated to use HTTP/2. 465 base::WeakPtr<SpdySession> spdy_session_; 466 467 // QUIC version that is known to be used for the destination, usually coming 468 // from Alt-Svc. 469 quic::ParsedQuicVersion quic_version_ = 470 quic::ParsedQuicVersion::Unsupported(); 471 // Created when attempting QUIC sessions. 472 std::unique_ptr<QuicTask> quic_task_; 473 // Set when `quic_task_` is completed. 474 std::optional<int> quic_task_result_; 475 476 // The delay for TCP-based stream attempts in favor of QUIC. 477 base::TimeDelta stream_attempt_delay_; 478 // Set to true when stream attempts should be blocked. 479 bool should_block_stream_attempt_ = false; 480 base::OneShotTimer stream_attempt_delay_timer_; 481 482 base::WeakPtrFactory<AttemptManager> weak_ptr_factory_{this}; 483 }; 484 485 } // namespace net 486 487 #endif // NET_HTTP_HTTP_STREAM_POOL_ATTEMPT_MANAGER_H_ 488