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