• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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