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