• 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 <set>
14 #include <string>
15 #include <vector>
16 
17 #include "base/memory/raw_ptr.h"
18 #include "base/memory/weak_ptr.h"
19 #include "net/base/host_port_pair.h"
20 #include "net/base/ip_endpoint.h"
21 #include "net/base/load_timing_info.h"
22 #include "net/base/net_errors.h"
23 #include "net/base/net_export.h"
24 #include "net/base/network_change_notifier.h"
25 #include "net/base/proxy_server.h"
26 #include "net/dns/public/host_resolver_results.h"
27 #include "net/log/net_log_source.h"
28 #include "net/proxy_resolution/proxy_config.h"
29 #include "net/socket/connect_job.h"
30 #include "net/socket/ssl_client_socket.h"
31 #include "net/spdy/http2_push_promise_index.h"
32 #include "net/spdy/server_push_delegate.h"
33 #include "net/spdy/spdy_session_key.h"
34 #include "net/ssl/ssl_config_service.h"
35 #include "net/third_party/quiche/src/quiche/quic/core/quic_versions.h"
36 #include "net/third_party/quiche/src/quiche/spdy/core/spdy_protocol.h"
37 #include "third_party/abseil-cpp/absl/types/optional.h"
38 
39 namespace net {
40 
41 class ClientSocketHandle;
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 absl::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 |client_socket_handle| on a
172   // subsequent event loop iteration, so it may be closed immediately afterwards
173   // if the first read of |client_socket_handle| fails.
174   int CreateAvailableSessionFromSocketHandle(
175       const SpdySessionKey& key,
176       std::unique_ptr<ClientSocketHandle> client_socket_handle,
177       const NetLogWithSource& net_log,
178       base::WeakPtr<SpdySession>* session);
179 
180   // Just like the above method, except it takes a SocketStream instead of a
181   // ClientSocketHandle, and separate connect timing information. When this
182   // constructor is used, there is no socket pool beneath the SpdySession.
183   // Instead, the session takes exclusive ownership of the underting socket, and
184   // destroying the session will directly destroy the socket, as opposed to
185   // disconnected it and then returning it to the socket pool. This is intended
186   // for use with H2 proxies, which are layered beneath the socket pools and
187   // can have sockets above them for tunnels, which are put in a socket pool.
188   base::WeakPtr<SpdySession> CreateAvailableSessionFromSocket(
189       const SpdySessionKey& key,
190       std::unique_ptr<StreamSocket> socket_stream,
191       const LoadTimingInfo::ConnectTiming& connect_timing,
192       const NetLogWithSource& net_log);
193 
194   // If there is an available session for |key|, return it.
195   // Otherwise if there is a session to pool to based on IP address:
196   //   * if |enable_ip_based_pooling == true|,
197   //     then mark it as available for |key| and return it;
198   //   * if |enable_ip_based_pooling == false|,
199   //     then remove it from the available sessions, and return nullptr.
200   // Otherwise return nullptr.
201   base::WeakPtr<SpdySession> FindAvailableSession(
202       const SpdySessionKey& key,
203       bool enable_ip_based_pooling,
204       bool is_websocket,
205       const NetLogWithSource& net_log);
206 
207   // Returns true if there is an available session for |key|.
208   bool HasAvailableSession(const SpdySessionKey& key, bool is_websocket) const;
209 
210   // Just like FindAvailableSession.
211   //
212   // Additionally, if it returns nullptr, populates |spdy_session_request| with
213   // a request that will invoke |delegate| once a matching SPDY session becomes
214   // available through the creation of a new SpdySession (as opposed to by
215   // creating an alias for an existing session with a new host).
216   //
217   // |is_blocking_request_for_session| will be set to |true| if there is not
218   // another "blocking" request already pending. For example, the first request
219   // created will be considered "blocking", but subsequent requests will not as
220   // long as the "blocking" request is not destroyed.  Once the "blocking"
221   // request is destroyed, the next created request will be marked "blocking".
222   //
223   // If a request is created, that request is not the "blocking" request, and
224   // |on_blocking_request_destroyed_callback| is non-null, then
225   // |on_blocking_request_destroyed_callback| will be invoked asynchronously
226   // when the "blocking" request is destroyed. The callback associated with the
227   // "blocking" request is never invoked.
228   //
229   // |delegate|, |spdy_session_request|, and |is_blocking_request_for_session|
230   // must all be non-null.
231   //
232   // TODO(mmenke): Merge this into FindAvailableSession().
233   // TODO(mmenke): Don't invoke |on_blocking_request_destroyed_callback| when
234   // all requests for a session have been successfully responded to.
235   base::WeakPtr<SpdySession> RequestSession(
236       const SpdySessionKey& key,
237       bool enable_ip_based_pooling,
238       bool is_websocket,
239       const NetLogWithSource& net_log,
240       base::RepeatingClosure on_blocking_request_destroyed_callback,
241       SpdySessionRequest::Delegate* delegate,
242       std::unique_ptr<SpdySessionRequest>* spdy_session_request,
243       bool* is_blocking_request_for_session);
244 
245   // Invoked when a host resolution completes. Returns
246   // OnHostResolutionCallbackResult::kMayBeDeletedAsync if there's a SPDY
247   // session that's a suitable alias for |key|, setting up the alias if needed.
248   OnHostResolutionCallbackResult OnHostResolutionComplete(
249       const SpdySessionKey& key,
250       bool is_websocket,
251       const std::vector<HostResolverEndpointResult>& endpoint_results,
252       const std::set<std::string>& aliases);
253 
254   // Remove all mappings and aliases for the given session, which must
255   // still be available. Except for in tests, this must be called by
256   // the given session itself.
257   void MakeSessionUnavailable(
258       const base::WeakPtr<SpdySession>& available_session);
259 
260   // Removes an unavailable session from the pool.  Except for in
261   // tests, this must be called by the given session itself.
262   void RemoveUnavailableSession(
263       const base::WeakPtr<SpdySession>& unavailable_session);
264 
265   // Note that the next three methods close sessions, potentially notifing
266   // delegates of error or synchronously invoking callbacks, which might trigger
267   // retries, thus opening new sessions.
268 
269   // Close only the currently existing SpdySessions with |error|.
270   // Let any new ones created while this method is running continue to
271   // live.
272   void CloseCurrentSessions(Error error);
273 
274   // Close only the currently existing SpdySessions that are idle.
275   // Let any new ones created while this method is running continue to
276   // live.
277   void CloseCurrentIdleSessions(const std::string& description);
278 
279   // Repeatedly close all SpdySessions until all of them (including new ones
280   // created in the process of closing the current ones, and new ones created in
281   // the process of closing those new ones, etc.) are unavailable.
282   void CloseAllSessions();
283 
284   // Mark all current sessions as going away.
285   void MakeCurrentSessionsGoingAway(Error error);
286 
287   // Creates a Value summary of the state of the spdy session pool.
288   std::unique_ptr<base::Value> SpdySessionPoolInfoToValue() const;
289 
http_server_properties()290   HttpServerProperties* http_server_properties() {
291     return http_server_properties_;
292   }
293 
push_promise_index()294   Http2PushPromiseIndex* push_promise_index() { return &push_promise_index_; }
295 
set_server_push_delegate(ServerPushDelegate * push_delegate)296   void set_server_push_delegate(ServerPushDelegate* push_delegate) {
297     push_delegate_ = push_delegate;
298   }
299 
300   // NetworkChangeNotifier::IPAddressObserver methods:
301 
302   // We flush all idle sessions and release references to the active ones so
303   // they won't get re-used.  The active ones will either complete successfully
304   // or error out due to the IP address change.
305   void OnIPAddressChanged() override;
306 
307   // SSLClientContext::Observer methods:
308 
309   // We perform the same flushing as described above when SSL settings change.
310   void OnSSLConfigChanged(
311       SSLClientContext::SSLConfigChangeType change_type) override;
312 
313   // Makes all sessions using |server|'s SSL configuration unavailable, meaning
314   // they will not be used to service new streams. Does not close any existing
315   // streams.
316   void OnSSLConfigForServerChanged(const HostPortPair& server) override;
317 
set_network_quality_estimator(NetworkQualityEstimator * network_quality_estimator)318   void set_network_quality_estimator(
319       NetworkQualityEstimator* network_quality_estimator) {
320     network_quality_estimator_ = network_quality_estimator;
321   }
322 
323   // Returns the stored DNS aliases for the session key.
324   std::set<std::string> GetDnsAliasesForSessionKey(
325       const SpdySessionKey& key) const;
326 
327  private:
328   friend class SpdySessionPoolPeer;  // For testing.
329 
330   using SessionSet = std::set<SpdySession*>;
331   using WeakSessionList = std::vector<base::WeakPtr<SpdySession>>;
332   using AvailableSessionMap =
333       std::map<SpdySessionKey, base::WeakPtr<SpdySession>>;
334   using AliasMap = std::multimap<IPEndPoint, SpdySessionKey>;
335   using DnsAliasesBySessionKeyMap =
336       std::map<SpdySessionKey, std::set<std::string>>;
337   using RequestSet = std::set<SpdySessionRequest*>;
338 
339   struct RequestInfoForKey {
340     RequestInfoForKey();
341     ~RequestInfoForKey();
342 
343     // Whether one of the requests in |RequestSet| has its
344     // is_blocking_request_for_session() bit set.
345     bool has_blocking_request = false;
346 
347     RequestSet request_set;
348 
349     // Set of callbacks watching for the blocking request to be destroyed.
350     std::list<base::RepeatingClosure> deferred_callbacks;
351   };
352 
353   using SpdySessionRequestMap = std::map<SpdySessionKey, RequestInfoForKey>;
354 
355   // Removes |request| from |spdy_session_request_map_|.
356   void RemoveRequestForSpdySession(SpdySessionRequest* request);
357 
358   // Returns true iff |session| is in |available_sessions_|.
359   bool IsSessionAvailable(const base::WeakPtr<SpdySession>& session) const;
360 
361   // Map the given key to the given session. There must not already be a
362   // mapping for `key`. Also adds an entry for `key` and `dns_aliases` in
363   // `dns_aliases_by_session_key_`. If there are already DNS aliases for the
364   // given key, replaces them.
365   void MapKeyToAvailableSession(const SpdySessionKey& key,
366                                 const base::WeakPtr<SpdySession>& session,
367                                 std::set<std::string> dns_aliases);
368 
369   // Returns an iterator into |available_sessions_| for the given key,
370   // which may be equal to |available_sessions_.end()|.
371   AvailableSessionMap::iterator LookupAvailableSessionByKey(
372       const SpdySessionKey& key);
373 
374   // Remove the mapping of the given key, which must exist. Also erases the
375   // key-value pair of SpdySessionKey and DNS aliases from the
376   // `dns_aliases_by_session_key_` map.
377   void UnmapKey(const SpdySessionKey& key);
378 
379   // Remove all aliases for |key| from the aliases table.
380   void RemoveAliases(const SpdySessionKey& key);
381 
382   // Get a copy of the current sessions as a list of WeakPtrs. Used by
383   // CloseCurrentSessionsHelper() below.
384   WeakSessionList GetCurrentSessions() const;
385 
386   // Close only the currently existing SpdySessions with |error|.  Let
387   // any new ones created while this method is running continue to
388   // live. If |idle_only| is true only idle sessions are closed.
389   void CloseCurrentSessionsHelper(Error error,
390                                   const std::string& description,
391                                   bool idle_only);
392 
393   // Creates a new session. The session must be initialized before
394   // InsertSession() is invoked.
395   std::unique_ptr<SpdySession> CreateSession(const SpdySessionKey& key,
396                                              NetLog* net_log);
397   // Adds a new session previously created with CreateSession to the pool.
398   // |source_net_log| is the NetLog for the object that created the session.
399   base::WeakPtr<SpdySession> InsertSession(
400       const SpdySessionKey& key,
401       std::unique_ptr<SpdySession> new_session,
402       const NetLogWithSource& source_net_log,
403       std::set<std::string> dns_aliases);
404 
405   // If a session with the specified |key| exists, invokes
406   // OnSpdySessionAvailable on all matching members of
407   // |spdy_session_request_map_|, removing them from the map. Regardless of
408   // whether or not such key exists, invokes all corresponding callbacks
409   // currently in |spdy_session_pending_request_map_|.
410   void UpdatePendingRequests(const SpdySessionKey& key);
411 
412   // Removes the SpdySessionRequest at |request_set_iterator| from the
413   // RequestSet at |request_map_iterator| and calls OnRemovedFromPool() on the
414   // request. If the RequestSet becomes empty, also removes it from
415   // |spdy_session_request_map_|.
416   void RemoveRequestInternal(
417       SpdySessionRequestMap::iterator request_map_iterator,
418       RequestSet::iterator request_set_iterator);
419 
420   raw_ptr<HttpServerProperties> http_server_properties_;
421 
422   raw_ptr<TransportSecurityState> transport_security_state_;
423 
424   // The set of all sessions. This is a superset of the sessions in
425   // |available_sessions_|.
426   //
427   // |sessions_| owns all its SpdySession objects.
428   SessionSet sessions_;
429 
430   // This is a map of available sessions by key. A session may appear
431   // more than once in this map if it has aliases.
432   AvailableSessionMap available_sessions_;
433 
434   // A map of IPEndPoint aliases for sessions.
435   AliasMap aliases_;
436 
437   // A map of DNS alias vectors by session keys.
438   DnsAliasesBySessionKeyMap dns_aliases_by_session_key_;
439 
440   // The index of all unclaimed pushed streams of all SpdySessions in this pool.
441   Http2PushPromiseIndex push_promise_index_;
442 
443   const raw_ptr<SSLClientContext> ssl_client_context_;
444   const raw_ptr<HostResolver> resolver_;
445 
446   // Versions of QUIC which may be used.
447   const quic::ParsedQuicVersionVector quic_supported_versions_;
448 
449   // Defaults to true. May be controlled via SpdySessionPoolPeer for tests.
450   bool enable_sending_initial_data_ = true;
451   bool enable_ping_based_connection_checking_;
452 
453   const bool is_http2_enabled_;
454   const bool is_quic_enabled_;
455 
456   size_t session_max_recv_window_size_;
457 
458   // Maximum number of capped frames that can be queued at any time.
459   int session_max_queued_capped_frames_;
460 
461   // Settings that are sent in the initial SETTINGS frame
462   // (if |enable_sending_initial_data_| is true),
463   // and also control SpdySession parameters like initial receive window size
464   // and maximum HPACK dynamic table size.
465   const spdy::SettingsMap initial_settings_;
466 
467   // If true, a setting parameter with reserved identifier will be sent in every
468   // initial SETTINGS frame, see
469   // https://tools.ietf.org/html/draft-bishop-httpbis-grease-00.
470   // The setting identifier and value will be drawn independently for each
471   // connection to prevent tracking of the client.
472   const bool enable_http2_settings_grease_;
473 
474   // If set, an HTTP/2 frame with a reserved frame type will be sent after
475   // every HTTP/2 SETTINGS frame and before every HTTP/2 DATA frame. See
476   // https://tools.ietf.org/html/draft-bishop-httpbis-grease-00.
477   const absl::optional<GreasedHttp2Frame> greased_http2_frame_;
478 
479   // If set, the HEADERS frame carrying a request without body will not have the
480   // END_STREAM flag set.  The stream will be closed by a subsequent empty DATA
481   // frame with END_STREAM.  Does not affect bidirectional or proxy streams.
482   // If unset, the HEADERS frame will have the END_STREAM flag set on.
483   // This is useful in conjuction with |greased_http2_frame_| so that a frame
484   // of reserved type can be sent out even on requests without a body.
485   const bool http2_end_stream_with_data_frame_;
486 
487   // If true, enable sending PRIORITY_UPDATE frames until SETTINGS frame
488   // arrives.  After SETTINGS frame arrives, do not send PRIORITY_UPDATE frames
489   // any longer if SETTINGS_DEPRECATE_HTTP2_PRIORITIES is missing or has zero 0,
490   // but continue and also stop sending HTTP/2-style priority information in
491   // HEADERS frames and PRIORITY frames if it has value 1.
492   const bool enable_priority_update_;
493 
494   // If set, sessions will be marked as going away upon relevant network changes
495   // (instead of being closed).
496   const bool go_away_on_ip_change_;
497 
498   SpdySessionRequestMap spdy_session_request_map_;
499 
500   TimeFunc time_func_;
501   raw_ptr<ServerPushDelegate> push_delegate_ = nullptr;
502 
503   raw_ptr<NetworkQualityEstimator> network_quality_estimator_;
504 
505   const bool cleanup_sessions_on_ip_address_changed_;
506 
507   base::WeakPtrFactory<SpdySessionPool> weak_ptr_factory_{this};
508 };
509 
510 }  // namespace net
511 
512 #endif  // NET_SPDY_SPDY_SESSION_POOL_H_
513