• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 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_SPDY_SPDY_SESSION_POOL_H_
6 #define NET_SPDY_SPDY_SESSION_POOL_H_
7 
8 #include <stddef.h>
9 
10 #include <list>
11 #include <map>
12 #include <memory>
13 #include <optional>
14 #include <set>
15 #include <string>
16 #include <vector>
17 
18 #include "base/memory/raw_ptr.h"
19 #include "base/memory/weak_ptr.h"
20 #include "base/types/expected.h"
21 #include "net/base/host_port_pair.h"
22 #include "net/base/ip_endpoint.h"
23 #include "net/base/load_timing_info.h"
24 #include "net/base/net_errors.h"
25 #include "net/base/net_export.h"
26 #include "net/base/network_change_notifier.h"
27 #include "net/base/proxy_server.h"
28 #include "net/dns/public/host_resolver_results.h"
29 #include "net/log/net_log_source.h"
30 #include "net/proxy_resolution/proxy_config.h"
31 #include "net/socket/connect_job.h"
32 #include "net/socket/ssl_client_socket.h"
33 #include "net/spdy/multiplexed_session_creation_initiator.h"
34 #include "net/spdy/spdy_session_key.h"
35 #include "net/ssl/ssl_config_service.h"
36 #include "net/third_party/quiche/src/quiche/http2/core/spdy_protocol.h"
37 #include "net/third_party/quiche/src/quiche/quic/core/quic_versions.h"
38 
39 namespace net {
40 
41 class StreamSocketHandle;
42 class HostResolver;
43 class HttpServerProperties;
44 class NetLogWithSource;
45 class NetworkQualityEstimator;
46 class SpdySession;
47 class StreamSocket;
48 class TransportSecurityState;
49 
50 // This is a very simple pool for open SpdySessions.
51 class NET_EXPORT SpdySessionPool
52     : public NetworkChangeNotifier::IPAddressObserver,
53       public SSLClientContext::Observer {
54  public:
55   typedef base::TimeTicks (*TimeFunc)();
56 
57   // Struct to hold randomly generated frame parameters to be used for sending
58   // frames on the wire to "grease" frame type.  Frame type has to be one of
59   // the reserved values defined in
60   // https://tools.ietf.org/html/draft-bishop-httpbis-grease-00.
61   struct GreasedHttp2Frame {
62     uint8_t type;
63     uint8_t flags;
64     std::string payload;
65   };
66 
67   // A request for a SpdySession with a particular SpdySessionKey. The
68   // SpdySessionPool's RequestSession() creates these. The Delegate's
69   // OnSpdySessionAvailable() method will be invoked when a matching SpdySession
70   // is added to the pool. The Delegate's OnSpdySessionAvailable() method will
71   // be invoked at most once for a single SpdySessionRequest.
72   //
73   // Destroying the request will stop watching the pool for such a session. The
74   // request must be destroyed before the SpdySessionPool is.
75   class NET_EXPORT_PRIVATE SpdySessionRequest {
76    public:
77     // Interface for watching for when a SpdySession with a provided key is
78     // created.
79     class NET_EXPORT_PRIVATE Delegate {
80      public:
81       Delegate();
82 
83       Delegate(const Delegate&) = delete;
84       Delegate& operator=(const Delegate&) = delete;
85 
86       virtual ~Delegate();
87 
88       // |spdy_session| will not be null.
89       virtual void OnSpdySessionAvailable(
90           base::WeakPtr<SpdySession> spdy_session) = 0;
91     };
92 
93     // Constructor - this is called by the SpdySessionPool.
94     SpdySessionRequest(const SpdySessionKey& key,
95                        bool enable_ip_based_pooling,
96                        bool is_websocket,
97                        bool is_blocking_request_for_session,
98                        Delegate* delegate,
99                        SpdySessionPool* spdy_session_pool);
100 
101     SpdySessionRequest(const SpdySessionRequest&) = delete;
102     SpdySessionRequest& operator=(const SpdySessionRequest&) = delete;
103 
104     ~SpdySessionRequest();
105 
106     // Called by SpdySessionPool to signal that the request has been removed
107     // from the SpdySessionPool.
108     void OnRemovedFromPool();
109 
key()110     const SpdySessionKey& key() const { return key_; }
enable_ip_based_pooling()111     bool enable_ip_based_pooling() const { return enable_ip_based_pooling_; }
is_websocket()112     bool is_websocket() const { return is_websocket_; }
is_blocking_request_for_session()113     bool is_blocking_request_for_session() const {
114       return is_blocking_request_for_session_;
115     }
delegate()116     Delegate* delegate() { return delegate_; }
117 
118     // The associated SpdySessionPool, or nullptr if OnRemovedFromPool() has
119     // been called.
spdy_session_pool()120     SpdySessionPool* spdy_session_pool() { return spdy_session_pool_; }
121 
122    private:
123     const SpdySessionKey key_;
124     const bool enable_ip_based_pooling_;
125     const bool is_websocket_;
126     const bool is_blocking_request_for_session_;
127     const raw_ptr<Delegate> delegate_;
128     raw_ptr<SpdySessionPool> spdy_session_pool_;
129   };
130 
131   SpdySessionPool(HostResolver* host_resolver,
132                   SSLClientContext* ssl_client_context,
133                   HttpServerProperties* http_server_properties,
134                   TransportSecurityState* transport_security_state,
135                   const quic::ParsedQuicVersionVector& quic_supported_versions,
136                   bool enable_ping_based_connection_checking,
137                   bool is_http_enabled,
138                   bool is_quic_enabled,
139                   size_t session_max_recv_window_size,
140                   int session_max_queued_capped_frames,
141                   const spdy::SettingsMap& initial_settings,
142                   bool enable_http2_settings_grease,
143                   const std::optional<GreasedHttp2Frame>& greased_http2_frame,
144                   bool http2_end_stream_with_data_frame,
145                   bool enable_priority_update,
146                   bool go_away_on_ip_change,
147                   SpdySessionPool::TimeFunc time_func,
148                   NetworkQualityEstimator* network_quality_estimator,
149                   bool cleanup_sessions_on_ip_address_changed);
150 
151   SpdySessionPool(const SpdySessionPool&) = delete;
152   SpdySessionPool& operator=(const SpdySessionPool&) = delete;
153 
154   ~SpdySessionPool() override;
155 
156   // In the functions below, a session is "available" if this pool has
157   // a reference to it and there is some SpdySessionKey for which
158   // FindAvailableSession() will return it. A session is "unavailable"
159   // if this pool has a reference to it but it won't be returned by
160   // FindAvailableSession() for any SpdySessionKey; for example, this
161   // can happen when a session receives a GOAWAY frame and is still
162   // processing existing streams.
163 
164   // Create a new SPDY session from an existing socket.  There must
165   // not already be a session for the given key.
166   //
167   // Returns OK on success and sets |*session| to point to the new SpdySession.
168   // Returns a net error code on failure, in which case the value of |*session|
169   // is undefined.
170   //
171   // Note that the SpdySession begins reading from |stream_socket_handle| on a
172   // subsequent event loop iteration, so it may be closed immediately afterwards
173   // if the first read of |stream_socket_handle| fails.
174   int CreateAvailableSessionFromSocketHandle(
175       const SpdySessionKey& key,
176       std::unique_ptr<StreamSocketHandle> stream_socket_handle,
177       const NetLogWithSource& net_log,
178       const MultiplexedSessionCreationInitiator session_creation_initiator,
179       base::WeakPtr<SpdySession>* session);
180 
181   // Just like the above method, except it takes a SocketStream instead of a
182   // StreamSocketHandle, and separate connect timing information. When this
183   // constructor is used, there is no socket pool beneath the SpdySession.
184   // Instead, the session takes exclusive ownership of the underting socket, and
185   // destroying the session will directly destroy the socket, as opposed to
186   // disconnected it and then returning it to the socket pool. This is intended
187   // for use with H2 proxies, which are layered beneath the socket pools and
188   // can have sockets above them for tunnels, which are put in a socket pool.
189   base::expected<base::WeakPtr<SpdySession>, int>
190   CreateAvailableSessionFromSocket(
191       const SpdySessionKey& key,
192       std::unique_ptr<StreamSocket> socket_stream,
193       const LoadTimingInfo::ConnectTiming& connect_timing,
194       const NetLogWithSource& net_log);
195 
196   // If there is an available session for |key|, return it.
197   // Otherwise if there is a session to pool to based on IP address:
198   //   * if |enable_ip_based_pooling == true|,
199   //     then mark it as available for |key| and return it;
200   //   * if |enable_ip_based_pooling == false|,
201   //     then remove it from the available sessions, and return nullptr.
202   // Otherwise return nullptr.
203   base::WeakPtr<SpdySession> FindAvailableSession(
204       const SpdySessionKey& key,
205       bool enable_ip_based_pooling,
206       bool is_websocket,
207       const NetLogWithSource& net_log);
208 
209   // Returns an available session if there is active session for `key` and the
210   // session can be used for IP addresses in `service_endpoint`. Should be
211   // called only when IP-based pooling is enabled.
212   base::WeakPtr<SpdySession> FindMatchingIpSessionForServiceEndpoint(
213       const SpdySessionKey& key,
214       const ServiceEndpoint& service_endpoint,
215       const std::set<std::string>& dns_aliases);
216 
217   // Returns true if there is an available session for |key|.
218   bool HasAvailableSession(const SpdySessionKey& key, bool is_websocket) const;
219 
220   // Just like FindAvailableSession.
221   //
222   // Additionally, if it returns nullptr, populates |spdy_session_request| with
223   // a request that will invoke |delegate| once a matching SPDY session becomes
224   // available through the creation of a new SpdySession (as opposed to by
225   // creating an alias for an existing session with a new host).
226   //
227   // |is_blocking_request_for_session| will be set to |true| if there is not
228   // another "blocking" request already pending. For example, the first request
229   // created will be considered "blocking", but subsequent requests will not as
230   // long as the "blocking" request is not destroyed.  Once the "blocking"
231   // request is destroyed, the next created request will be marked "blocking".
232   //
233   // If a request is created, that request is not the "blocking" request, and
234   // |on_blocking_request_destroyed_callback| is non-null, then
235   // |on_blocking_request_destroyed_callback| will be invoked asynchronously
236   // when the "blocking" request is destroyed. The callback associated with the
237   // "blocking" request is never invoked.
238   //
239   // |delegate|, |spdy_session_request|, and |is_blocking_request_for_session|
240   // must all be non-null.
241   //
242   // TODO(mmenke): Merge this into FindAvailableSession().
243   // TODO(mmenke): Don't invoke |on_blocking_request_destroyed_callback| when
244   // all requests for a session have been successfully responded to.
245   base::WeakPtr<SpdySession> RequestSession(
246       const SpdySessionKey& key,
247       bool enable_ip_based_pooling,
248       bool is_websocket,
249       const NetLogWithSource& net_log,
250       base::RepeatingClosure on_blocking_request_destroyed_callback,
251       SpdySessionRequest::Delegate* delegate,
252       std::unique_ptr<SpdySessionRequest>* spdy_session_request,
253       bool* is_blocking_request_for_session);
254 
255   // Invoked when a host resolution completes. Returns
256   // OnHostResolutionCallbackResult::kMayBeDeletedAsync if there's a SPDY
257   // session that's a suitable alias for |key|, setting up the alias if needed.
258   OnHostResolutionCallbackResult OnHostResolutionComplete(
259       const SpdySessionKey& key,
260       bool is_websocket,
261       const std::vector<HostResolverEndpointResult>& endpoint_results,
262       const std::set<std::string>& aliases);
263 
264   // Remove all mappings and aliases for the given session, which must
265   // still be available. Except for in tests, this must be called by
266   // the given session itself.
267   void MakeSessionUnavailable(
268       const base::WeakPtr<SpdySession>& available_session);
269 
270   // Removes an unavailable session from the pool.  Except for in
271   // tests, this must be called by the given session itself.
272   void RemoveUnavailableSession(
273       const base::WeakPtr<SpdySession>& unavailable_session);
274 
275   // Note that the next three methods close sessions, potentially notifing
276   // delegates of error or synchronously invoking callbacks, which might trigger
277   // retries, thus opening new sessions.
278 
279   // Close only the currently existing SpdySessions with |error|.
280   // Let any new ones created while this method is running continue to
281   // live.
282   void CloseCurrentSessions(Error error);
283 
284   // Close only the currently existing SpdySessions that are idle.
285   // Let any new ones created while this method is running continue to
286   // live.
287   void CloseCurrentIdleSessions(const std::string& description);
288 
289   // Repeatedly close all SpdySessions until all of them (including new ones
290   // created in the process of closing the current ones, and new ones created in
291   // the process of closing those new ones, etc.) are unavailable.
292   void CloseAllSessions();
293 
294   // Mark all current sessions as going away.
295   void MakeCurrentSessionsGoingAway(Error error);
296 
297   // Creates a Value summary of the state of the spdy session pool.
298   std::unique_ptr<base::Value> SpdySessionPoolInfoToValue() const;
299 
http_server_properties()300   HttpServerProperties* http_server_properties() {
301     return http_server_properties_;
302   }
303 
304   // NetworkChangeNotifier::IPAddressObserver methods:
305 
306   // We flush all idle sessions and release references to the active ones so
307   // they won't get re-used.  The active ones will either complete successfully
308   // or error out due to the IP address change.
309   void OnIPAddressChanged() override;
310 
311   // SSLClientContext::Observer methods:
312 
313   // We perform the same flushing as described above when SSL settings change.
314   void OnSSLConfigChanged(
315       SSLClientContext::SSLConfigChangeType change_type) override;
316 
317   // Makes all sessions using |server|'s SSL configuration unavailable, meaning
318   // they will not be used to service new streams. Does not close any existing
319   // streams.
320   void OnSSLConfigForServersChanged(
321       const base::flat_set<HostPortPair>& servers) override;
322 
set_network_quality_estimator(NetworkQualityEstimator * network_quality_estimator)323   void set_network_quality_estimator(
324       NetworkQualityEstimator* network_quality_estimator) {
325     network_quality_estimator_ = network_quality_estimator;
326   }
327 
328   // Returns the stored DNS aliases for the session key.
329   std::set<std::string> GetDnsAliasesForSessionKey(
330       const SpdySessionKey& key) const;
331 
332  private:
333   friend class SpdySessionPoolPeer;  // For testing.
334 
335   using SessionSet = std::set<raw_ptr<SpdySession>>;
336   using WeakSessionList = std::vector<base::WeakPtr<SpdySession>>;
337   using AvailableSessionMap =
338       std::map<SpdySessionKey, base::WeakPtr<SpdySession>>;
339   using AliasMap = std::multimap<IPEndPoint, SpdySessionKey>;
340   using DnsAliasesBySessionKeyMap =
341       std::map<SpdySessionKey, std::set<std::string>>;
342   using RequestSet = std::set<raw_ptr<SpdySessionRequest>>;
343 
344   struct RequestInfoForKey {
345     RequestInfoForKey();
346     ~RequestInfoForKey();
347 
348     // Whether one of the requests in |RequestSet| has its
349     // is_blocking_request_for_session() bit set.
350     bool has_blocking_request = false;
351 
352     RequestSet request_set;
353 
354     // Set of callbacks watching for the blocking request to be destroyed.
355     std::list<base::RepeatingClosure> deferred_callbacks;
356   };
357 
358   using SpdySessionRequestMap = std::map<SpdySessionKey, RequestInfoForKey>;
359 
360   // Removes |request| from |spdy_session_request_map_|.
361   void RemoveRequestForSpdySession(SpdySessionRequest* request);
362 
363   // Returns true iff |session| is in |available_sessions_|.
364   bool IsSessionAvailable(const base::WeakPtr<SpdySession>& session) const;
365 
366   // Map the given key to the given session. There must not already be a
367   // mapping for `key`. Also adds an entry for `key` and `dns_aliases` in
368   // `dns_aliases_by_session_key_`. If there are already DNS aliases for the
369   // given key, replaces them.
370   void MapKeyToAvailableSession(const SpdySessionKey& key,
371                                 const base::WeakPtr<SpdySession>& session,
372                                 std::set<std::string> dns_aliases);
373 
374   // Returns an iterator into |available_sessions_| for the given key,
375   // which may be equal to |available_sessions_.end()|.
376   AvailableSessionMap::iterator LookupAvailableSessionByKey(
377       const SpdySessionKey& key);
378 
379   // Remove the mapping of the given key, which must exist. Also erases the
380   // key-value pair of SpdySessionKey and DNS aliases from the
381   // `dns_aliases_by_session_key_` map.
382   void UnmapKey(const SpdySessionKey& key);
383 
384   // Remove all aliases for |key| from the aliases table.
385   void RemoveAliases(const SpdySessionKey& key);
386 
387   // Get a copy of the current sessions as a list of WeakPtrs. Used by
388   // CloseCurrentSessionsHelper() below.
389   WeakSessionList GetCurrentSessions() const;
390 
391   // Close only the currently existing SpdySessions with |error|.  Let
392   // any new ones created while this method is running continue to
393   // live. If |idle_only| is true only idle sessions are closed.
394   void CloseCurrentSessionsHelper(Error error,
395                                   const std::string& description,
396                                   bool idle_only);
397 
398   // Creates a new session. The session must be initialized before
399   // InsertSession() is invoked.
400   std::unique_ptr<SpdySession> CreateSession(
401       const SpdySessionKey& key,
402       NetLog* net_log,
403       const MultiplexedSessionCreationInitiator session_creation_initiator);
404   // Adds a new session previously created with CreateSession to the pool.
405   // |source_net_log| is the NetLog for the object that created the session.
406   base::expected<base::WeakPtr<SpdySession>, int> InsertSession(
407       const SpdySessionKey& key,
408       std::unique_ptr<SpdySession> new_session,
409       const NetLogWithSource& source_net_log,
410       std::set<std::string> dns_aliases,
411       bool perform_post_insertion_checks);
412 
413   // If a session with the specified |key| exists, invokes
414   // OnSpdySessionAvailable on all matching members of
415   // |spdy_session_request_map_|, removing them from the map. Regardless of
416   // whether or not such key exists, invokes all corresponding callbacks
417   // currently in |spdy_session_pending_request_map_|.
418   void UpdatePendingRequests(const SpdySessionKey& key);
419 
420   // Removes the SpdySessionRequest at |request_set_iterator| from the
421   // RequestSet at |request_map_iterator| and calls OnRemovedFromPool() on the
422   // request. If the RequestSet becomes empty, also removes it from
423   // |spdy_session_request_map_|.
424   void RemoveRequestInternal(
425       SpdySessionRequestMap::iterator request_map_iterator,
426       RequestSet::iterator request_set_iterator);
427 
428   // Helper method of `FindMatchingIpSessionForServiceEndpoint()`. This is
429   // basically a subset of OnHostResolutionComplete(), i.e.,:
430   // * Doesn't support SocketTag.
431   // * Assumes there is only one host resolution for `key` at the same time.
432   base::WeakPtr<SpdySession> FindMatchingIpSession(
433       const SpdySessionKey& key,
434       const std::vector<IPEndPoint>& ip_endpoints,
435       const std::set<std::string>& dns_aliases);
436 
437   raw_ptr<HttpServerProperties> http_server_properties_;
438 
439   raw_ptr<TransportSecurityState> transport_security_state_;
440 
441   // The set of all sessions. This is a superset of the sessions in
442   // |available_sessions_|.
443   //
444   // |sessions_| owns all its SpdySession objects.
445   SessionSet sessions_;
446 
447   // This is a map of available sessions by key. A session may appear
448   // more than once in this map if it has aliases.
449   AvailableSessionMap available_sessions_;
450 
451   // A map of IPEndPoint aliases for sessions.
452   AliasMap aliases_;
453 
454   // A map of DNS alias vectors by session keys.
455   DnsAliasesBySessionKeyMap dns_aliases_by_session_key_;
456 
457   const raw_ptr<SSLClientContext> ssl_client_context_;
458   const raw_ptr<HostResolver> resolver_;
459 
460   // Versions of QUIC which may be used.
461   const quic::ParsedQuicVersionVector quic_supported_versions_;
462 
463   // Defaults to true. May be controlled via SpdySessionPoolPeer for tests.
464   bool enable_sending_initial_data_ = true;
465   bool enable_ping_based_connection_checking_;
466 
467   const bool is_http2_enabled_;
468   const bool is_quic_enabled_;
469 
470   size_t session_max_recv_window_size_;
471 
472   // Maximum number of capped frames that can be queued at any time.
473   int session_max_queued_capped_frames_;
474 
475   // Settings that are sent in the initial SETTINGS frame
476   // (if |enable_sending_initial_data_| is true),
477   // and also control SpdySession parameters like initial receive window size
478   // and maximum HPACK dynamic table size.
479   const spdy::SettingsMap initial_settings_;
480 
481   // If true, a setting parameter with reserved identifier will be sent in every
482   // initial SETTINGS frame, see
483   // https://tools.ietf.org/html/draft-bishop-httpbis-grease-00.
484   // The setting identifier and value will be drawn independently for each
485   // connection to prevent tracking of the client.
486   const bool enable_http2_settings_grease_;
487 
488   // If set, an HTTP/2 frame with a reserved frame type will be sent after
489   // every HTTP/2 SETTINGS frame and before every HTTP/2 DATA frame. See
490   // https://tools.ietf.org/html/draft-bishop-httpbis-grease-00.
491   const std::optional<GreasedHttp2Frame> greased_http2_frame_;
492 
493   // If set, the HEADERS frame carrying a request without body will not have the
494   // END_STREAM flag set.  The stream will be closed by a subsequent empty DATA
495   // frame with END_STREAM.  Does not affect bidirectional or proxy streams.
496   // If unset, the HEADERS frame will have the END_STREAM flag set on.
497   // This is useful in conjuction with |greased_http2_frame_| so that a frame
498   // of reserved type can be sent out even on requests without a body.
499   const bool http2_end_stream_with_data_frame_;
500 
501   // If true, enable sending PRIORITY_UPDATE frames until SETTINGS frame
502   // arrives.  After SETTINGS frame arrives, do not send PRIORITY_UPDATE frames
503   // any longer if SETTINGS_DEPRECATE_HTTP2_PRIORITIES is missing or has zero 0,
504   // but continue and also stop sending HTTP/2-style priority information in
505   // HEADERS frames and PRIORITY frames if it has value 1.
506   const bool enable_priority_update_;
507 
508   // If set, sessions will be marked as going away upon relevant network changes
509   // (instead of being closed).
510   const bool go_away_on_ip_change_;
511 
512   SpdySessionRequestMap spdy_session_request_map_;
513 
514   TimeFunc time_func_;
515 
516   raw_ptr<NetworkQualityEstimator> network_quality_estimator_;
517 
518   const bool cleanup_sessions_on_ip_address_changed_;
519 
520   base::WeakPtrFactory<SpdySessionPool> weak_ptr_factory_{this};
521 };
522 
523 }  // namespace net
524 
525 #endif  // NET_SPDY_SPDY_SESSION_POOL_H_
526