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