mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-12-03 02:36:09 +03:00
514 lines
21 KiB
C++
514 lines
21 KiB
C++
// Copyright 2012 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#ifndef NET_SPDY_SPDY_SESSION_POOL_H_
|
|
#define NET_SPDY_SPDY_SESSION_POOL_H_
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <list>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <set>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "base/memory/raw_ptr.h"
|
|
#include "base/memory/weak_ptr.h"
|
|
#include "net/base/host_port_pair.h"
|
|
#include "net/base/ip_endpoint.h"
|
|
#include "net/base/load_timing_info.h"
|
|
#include "net/base/net_errors.h"
|
|
#include "net/base/net_export.h"
|
|
#include "net/base/network_change_notifier.h"
|
|
#include "net/base/proxy_server.h"
|
|
#include "net/dns/public/host_resolver_results.h"
|
|
#include "net/log/net_log_source.h"
|
|
#include "net/proxy_resolution/proxy_config.h"
|
|
#include "net/socket/connect_job.h"
|
|
#include "net/socket/ssl_client_socket.h"
|
|
#include "net/spdy/http2_push_promise_index.h"
|
|
#include "net/spdy/server_push_delegate.h"
|
|
#include "net/spdy/spdy_session_key.h"
|
|
#include "net/ssl/ssl_config_service.h"
|
|
#include "net/third_party/quiche/src/quiche/quic/core/quic_versions.h"
|
|
#include "net/third_party/quiche/src/quiche/spdy/core/spdy_protocol.h"
|
|
#include "third_party/abseil-cpp/absl/types/optional.h"
|
|
|
|
namespace net {
|
|
|
|
class ClientSocketHandle;
|
|
class HostResolver;
|
|
class HttpServerProperties;
|
|
class NetLogWithSource;
|
|
class NetworkQualityEstimator;
|
|
class SpdySession;
|
|
class StreamSocket;
|
|
class TransportSecurityState;
|
|
|
|
// This is a very simple pool for open SpdySessions.
|
|
class NET_EXPORT SpdySessionPool
|
|
: public NetworkChangeNotifier::IPAddressObserver,
|
|
public SSLClientContext::Observer {
|
|
public:
|
|
typedef base::TimeTicks (*TimeFunc)();
|
|
|
|
// Struct to hold randomly generated frame parameters to be used for sending
|
|
// frames on the wire to "grease" frame type. Frame type has to be one of
|
|
// the reserved values defined in
|
|
// https://tools.ietf.org/html/draft-bishop-httpbis-grease-00.
|
|
struct GreasedHttp2Frame {
|
|
uint8_t type;
|
|
uint8_t flags;
|
|
std::string payload;
|
|
};
|
|
|
|
// A request for a SpdySession with a particular SpdySessionKey. The
|
|
// SpdySessionPool's RequestSession() creates these. The Delegate's
|
|
// OnSpdySessionAvailable() method will be invoked when a matching SpdySession
|
|
// is added to the pool. The Delegate's OnSpdySessionAvailable() method will
|
|
// be invoked at most once for a single SpdySessionRequest.
|
|
//
|
|
// Destroying the request will stop watching the pool for such a session. The
|
|
// request must be destroyed before the SpdySessionPool is.
|
|
class NET_EXPORT_PRIVATE SpdySessionRequest {
|
|
public:
|
|
// Interface for watching for when a SpdySession with a provided key is
|
|
// created.
|
|
class NET_EXPORT_PRIVATE Delegate {
|
|
public:
|
|
Delegate();
|
|
|
|
Delegate(const Delegate&) = delete;
|
|
Delegate& operator=(const Delegate&) = delete;
|
|
|
|
virtual ~Delegate();
|
|
|
|
// |spdy_session| will not be null.
|
|
virtual void OnSpdySessionAvailable(
|
|
base::WeakPtr<SpdySession> spdy_session) = 0;
|
|
};
|
|
|
|
// Constructor - this is called by the SpdySessionPool.
|
|
SpdySessionRequest(const SpdySessionKey& key,
|
|
bool enable_ip_based_pooling,
|
|
bool is_websocket,
|
|
bool is_blocking_request_for_session,
|
|
Delegate* delegate,
|
|
SpdySessionPool* spdy_session_pool);
|
|
|
|
SpdySessionRequest(const SpdySessionRequest&) = delete;
|
|
SpdySessionRequest& operator=(const SpdySessionRequest&) = delete;
|
|
|
|
~SpdySessionRequest();
|
|
|
|
// Called by SpdySessionPool to signal that the request has been removed
|
|
// from the SpdySessionPool.
|
|
void OnRemovedFromPool();
|
|
|
|
const SpdySessionKey& key() const { return key_; }
|
|
bool enable_ip_based_pooling() const { return enable_ip_based_pooling_; }
|
|
bool is_websocket() const { return is_websocket_; }
|
|
bool is_blocking_request_for_session() const {
|
|
return is_blocking_request_for_session_;
|
|
}
|
|
Delegate* delegate() { return delegate_; }
|
|
|
|
// The associated SpdySessionPool, or nullptr if OnRemovedFromPool() has
|
|
// been called.
|
|
SpdySessionPool* spdy_session_pool() { return spdy_session_pool_; }
|
|
|
|
private:
|
|
const SpdySessionKey key_;
|
|
const bool enable_ip_based_pooling_;
|
|
const bool is_websocket_;
|
|
const bool is_blocking_request_for_session_;
|
|
const raw_ptr<Delegate> delegate_;
|
|
raw_ptr<SpdySessionPool> spdy_session_pool_;
|
|
};
|
|
|
|
SpdySessionPool(HostResolver* host_resolver,
|
|
SSLClientContext* ssl_client_context,
|
|
HttpServerProperties* http_server_properties,
|
|
TransportSecurityState* transport_security_state,
|
|
const quic::ParsedQuicVersionVector& quic_supported_versions,
|
|
bool enable_ping_based_connection_checking,
|
|
bool is_http_enabled,
|
|
bool is_quic_enabled,
|
|
size_t session_max_recv_window_size,
|
|
int session_max_queued_capped_frames,
|
|
const spdy::SettingsMap& initial_settings,
|
|
bool enable_http2_settings_grease,
|
|
const absl::optional<GreasedHttp2Frame>& greased_http2_frame,
|
|
bool http2_end_stream_with_data_frame,
|
|
bool enable_priority_update,
|
|
bool go_away_on_ip_change,
|
|
SpdySessionPool::TimeFunc time_func,
|
|
NetworkQualityEstimator* network_quality_estimator,
|
|
bool cleanup_sessions_on_ip_address_changed);
|
|
|
|
SpdySessionPool(const SpdySessionPool&) = delete;
|
|
SpdySessionPool& operator=(const SpdySessionPool&) = delete;
|
|
|
|
~SpdySessionPool() override;
|
|
|
|
// In the functions below, a session is "available" if this pool has
|
|
// a reference to it and there is some SpdySessionKey for which
|
|
// FindAvailableSession() will return it. A session is "unavailable"
|
|
// if this pool has a reference to it but it won't be returned by
|
|
// FindAvailableSession() for any SpdySessionKey; for example, this
|
|
// can happen when a session receives a GOAWAY frame and is still
|
|
// processing existing streams.
|
|
|
|
// Create a new SPDY session from an existing socket. There must
|
|
// not already be a session for the given key.
|
|
//
|
|
// Returns OK on success and sets |*session| to point to the new SpdySession.
|
|
// Returns a net error code on failure, in which case the value of |*session|
|
|
// is undefined.
|
|
//
|
|
// Note that the SpdySession begins reading from |client_socket_handle| on a
|
|
// subsequent event loop iteration, so it may be closed immediately afterwards
|
|
// if the first read of |client_socket_handle| fails.
|
|
int CreateAvailableSessionFromSocketHandle(
|
|
const SpdySessionKey& key,
|
|
std::unique_ptr<ClientSocketHandle> client_socket_handle,
|
|
const NetLogWithSource& net_log,
|
|
base::WeakPtr<SpdySession>* session);
|
|
|
|
// Just like the above method, except it takes a SocketStream instead of a
|
|
// ClientSocketHandle, and separate connect timing information. When this
|
|
// constructor is used, there is no socket pool beneath the SpdySession.
|
|
// Instead, the session takes exclusive ownership of the underting socket, and
|
|
// destroying the session will directly destroy the socket, as opposed to
|
|
// disconnected it and then returning it to the socket pool. This is intended
|
|
// for use with H2 proxies, which are layered beneath the socket pools and
|
|
// can have sockets above them for tunnels, which are put in a socket pool.
|
|
base::WeakPtr<SpdySession> CreateAvailableSessionFromSocket(
|
|
const SpdySessionKey& key,
|
|
std::unique_ptr<StreamSocket> socket_stream,
|
|
const LoadTimingInfo::ConnectTiming& connect_timing,
|
|
const NetLogWithSource& net_log);
|
|
|
|
// If there is an available session for |key|, return it.
|
|
// Otherwise if there is a session to pool to based on IP address:
|
|
// * if |enable_ip_based_pooling == true|,
|
|
// then mark it as available for |key| and return it;
|
|
// * if |enable_ip_based_pooling == false|,
|
|
// then remove it from the available sessions, and return nullptr.
|
|
// Otherwise return nullptr.
|
|
base::WeakPtr<SpdySession> FindAvailableSession(
|
|
const SpdySessionKey& key,
|
|
bool enable_ip_based_pooling,
|
|
bool is_websocket,
|
|
const NetLogWithSource& net_log);
|
|
|
|
// Returns true if there is an available session for |key|.
|
|
bool HasAvailableSession(const SpdySessionKey& key, bool is_websocket) const;
|
|
|
|
// Just like FindAvailableSession.
|
|
//
|
|
// Additionally, if it returns nullptr, populates |spdy_session_request| with
|
|
// a request that will invoke |delegate| once a matching SPDY session becomes
|
|
// available through the creation of a new SpdySession (as opposed to by
|
|
// creating an alias for an existing session with a new host).
|
|
//
|
|
// |is_blocking_request_for_session| will be set to |true| if there is not
|
|
// another "blocking" request already pending. For example, the first request
|
|
// created will be considered "blocking", but subsequent requests will not as
|
|
// long as the "blocking" request is not destroyed. Once the "blocking"
|
|
// request is destroyed, the next created request will be marked "blocking".
|
|
//
|
|
// If a request is created, that request is not the "blocking" request, and
|
|
// |on_blocking_request_destroyed_callback| is non-null, then
|
|
// |on_blocking_request_destroyed_callback| will be invoked asynchronously
|
|
// when the "blocking" request is destroyed. The callback associated with the
|
|
// "blocking" request is never invoked.
|
|
//
|
|
// |delegate|, |spdy_session_request|, and |is_blocking_request_for_session|
|
|
// must all be non-null.
|
|
//
|
|
// TODO(mmenke): Merge this into FindAvailableSession().
|
|
// TODO(mmenke): Don't invoke |on_blocking_request_destroyed_callback| when
|
|
// all requests for a session have been successfully responded to.
|
|
base::WeakPtr<SpdySession> RequestSession(
|
|
const SpdySessionKey& key,
|
|
bool enable_ip_based_pooling,
|
|
bool is_websocket,
|
|
const NetLogWithSource& net_log,
|
|
base::RepeatingClosure on_blocking_request_destroyed_callback,
|
|
SpdySessionRequest::Delegate* delegate,
|
|
std::unique_ptr<SpdySessionRequest>* spdy_session_request,
|
|
bool* is_blocking_request_for_session);
|
|
|
|
// Invoked when a host resolution completes. Returns
|
|
// OnHostResolutionCallbackResult::kMayBeDeletedAsync if there's a SPDY
|
|
// session that's a suitable alias for |key|, setting up the alias if needed.
|
|
OnHostResolutionCallbackResult OnHostResolutionComplete(
|
|
const SpdySessionKey& key,
|
|
bool is_websocket,
|
|
const std::vector<HostResolverEndpointResult>& endpoint_results,
|
|
const std::set<std::string>& aliases);
|
|
|
|
// Remove all mappings and aliases for the given session, which must
|
|
// still be available. Except for in tests, this must be called by
|
|
// the given session itself.
|
|
void MakeSessionUnavailable(
|
|
const base::WeakPtr<SpdySession>& available_session);
|
|
|
|
// Removes an unavailable session from the pool. Except for in
|
|
// tests, this must be called by the given session itself.
|
|
void RemoveUnavailableSession(
|
|
const base::WeakPtr<SpdySession>& unavailable_session);
|
|
|
|
// Note that the next three methods close sessions, potentially notifing
|
|
// delegates of error or synchronously invoking callbacks, which might trigger
|
|
// retries, thus opening new sessions.
|
|
|
|
// Close only the currently existing SpdySessions with |error|.
|
|
// Let any new ones created while this method is running continue to
|
|
// live.
|
|
void CloseCurrentSessions(Error error);
|
|
|
|
// Close only the currently existing SpdySessions that are idle.
|
|
// Let any new ones created while this method is running continue to
|
|
// live.
|
|
void CloseCurrentIdleSessions(const std::string& description);
|
|
|
|
// Repeatedly close all SpdySessions until all of them (including new ones
|
|
// created in the process of closing the current ones, and new ones created in
|
|
// the process of closing those new ones, etc.) are unavailable.
|
|
void CloseAllSessions();
|
|
|
|
// Mark all current sessions as going away.
|
|
void MakeCurrentSessionsGoingAway(Error error);
|
|
|
|
// Creates a Value summary of the state of the spdy session pool.
|
|
std::unique_ptr<base::Value> SpdySessionPoolInfoToValue() const;
|
|
|
|
HttpServerProperties* http_server_properties() {
|
|
return http_server_properties_;
|
|
}
|
|
|
|
Http2PushPromiseIndex* push_promise_index() { return &push_promise_index_; }
|
|
|
|
void set_server_push_delegate(ServerPushDelegate* push_delegate) {
|
|
push_delegate_ = push_delegate;
|
|
}
|
|
|
|
// NetworkChangeNotifier::IPAddressObserver methods:
|
|
|
|
// We flush all idle sessions and release references to the active ones so
|
|
// they won't get re-used. The active ones will either complete successfully
|
|
// or error out due to the IP address change.
|
|
void OnIPAddressChanged() override;
|
|
|
|
// SSLClientContext::Observer methods:
|
|
|
|
// We perform the same flushing as described above when SSL settings change.
|
|
void OnSSLConfigChanged(
|
|
SSLClientContext::SSLConfigChangeType change_type) override;
|
|
|
|
// Makes all sessions using |server|'s SSL configuration unavailable, meaning
|
|
// they will not be used to service new streams. Does not close any existing
|
|
// streams.
|
|
void OnSSLConfigForServersChanged(
|
|
const base::flat_set<HostPortPair>& servers) override;
|
|
|
|
void set_network_quality_estimator(
|
|
NetworkQualityEstimator* network_quality_estimator) {
|
|
network_quality_estimator_ = network_quality_estimator;
|
|
}
|
|
|
|
// Returns the stored DNS aliases for the session key.
|
|
std::set<std::string> GetDnsAliasesForSessionKey(
|
|
const SpdySessionKey& key) const;
|
|
|
|
private:
|
|
friend class SpdySessionPoolPeer; // For testing.
|
|
|
|
using SessionSet = std::set<SpdySession*>;
|
|
using WeakSessionList = std::vector<base::WeakPtr<SpdySession>>;
|
|
using AvailableSessionMap =
|
|
std::map<SpdySessionKey, base::WeakPtr<SpdySession>>;
|
|
using AliasMap = std::multimap<IPEndPoint, SpdySessionKey>;
|
|
using DnsAliasesBySessionKeyMap =
|
|
std::map<SpdySessionKey, std::set<std::string>>;
|
|
using RequestSet = std::set<SpdySessionRequest*>;
|
|
|
|
struct RequestInfoForKey {
|
|
RequestInfoForKey();
|
|
~RequestInfoForKey();
|
|
|
|
// Whether one of the requests in |RequestSet| has its
|
|
// is_blocking_request_for_session() bit set.
|
|
bool has_blocking_request = false;
|
|
|
|
RequestSet request_set;
|
|
|
|
// Set of callbacks watching for the blocking request to be destroyed.
|
|
std::list<base::RepeatingClosure> deferred_callbacks;
|
|
};
|
|
|
|
using SpdySessionRequestMap = std::map<SpdySessionKey, RequestInfoForKey>;
|
|
|
|
// Removes |request| from |spdy_session_request_map_|.
|
|
void RemoveRequestForSpdySession(SpdySessionRequest* request);
|
|
|
|
// Returns true iff |session| is in |available_sessions_|.
|
|
bool IsSessionAvailable(const base::WeakPtr<SpdySession>& session) const;
|
|
|
|
// Map the given key to the given session. There must not already be a
|
|
// mapping for `key`. Also adds an entry for `key` and `dns_aliases` in
|
|
// `dns_aliases_by_session_key_`. If there are already DNS aliases for the
|
|
// given key, replaces them.
|
|
void MapKeyToAvailableSession(const SpdySessionKey& key,
|
|
const base::WeakPtr<SpdySession>& session,
|
|
std::set<std::string> dns_aliases);
|
|
|
|
// Returns an iterator into |available_sessions_| for the given key,
|
|
// which may be equal to |available_sessions_.end()|.
|
|
AvailableSessionMap::iterator LookupAvailableSessionByKey(
|
|
const SpdySessionKey& key);
|
|
|
|
// Remove the mapping of the given key, which must exist. Also erases the
|
|
// key-value pair of SpdySessionKey and DNS aliases from the
|
|
// `dns_aliases_by_session_key_` map.
|
|
void UnmapKey(const SpdySessionKey& key);
|
|
|
|
// Remove all aliases for |key| from the aliases table.
|
|
void RemoveAliases(const SpdySessionKey& key);
|
|
|
|
// Get a copy of the current sessions as a list of WeakPtrs. Used by
|
|
// CloseCurrentSessionsHelper() below.
|
|
WeakSessionList GetCurrentSessions() const;
|
|
|
|
// Close only the currently existing SpdySessions with |error|. Let
|
|
// any new ones created while this method is running continue to
|
|
// live. If |idle_only| is true only idle sessions are closed.
|
|
void CloseCurrentSessionsHelper(Error error,
|
|
const std::string& description,
|
|
bool idle_only);
|
|
|
|
// Creates a new session. The session must be initialized before
|
|
// InsertSession() is invoked.
|
|
std::unique_ptr<SpdySession> CreateSession(const SpdySessionKey& key,
|
|
NetLog* net_log);
|
|
// Adds a new session previously created with CreateSession to the pool.
|
|
// |source_net_log| is the NetLog for the object that created the session.
|
|
base::WeakPtr<SpdySession> InsertSession(
|
|
const SpdySessionKey& key,
|
|
std::unique_ptr<SpdySession> new_session,
|
|
const NetLogWithSource& source_net_log,
|
|
std::set<std::string> dns_aliases);
|
|
|
|
// If a session with the specified |key| exists, invokes
|
|
// OnSpdySessionAvailable on all matching members of
|
|
// |spdy_session_request_map_|, removing them from the map. Regardless of
|
|
// whether or not such key exists, invokes all corresponding callbacks
|
|
// currently in |spdy_session_pending_request_map_|.
|
|
void UpdatePendingRequests(const SpdySessionKey& key);
|
|
|
|
// Removes the SpdySessionRequest at |request_set_iterator| from the
|
|
// RequestSet at |request_map_iterator| and calls OnRemovedFromPool() on the
|
|
// request. If the RequestSet becomes empty, also removes it from
|
|
// |spdy_session_request_map_|.
|
|
void RemoveRequestInternal(
|
|
SpdySessionRequestMap::iterator request_map_iterator,
|
|
RequestSet::iterator request_set_iterator);
|
|
|
|
raw_ptr<HttpServerProperties> http_server_properties_;
|
|
|
|
raw_ptr<TransportSecurityState> transport_security_state_;
|
|
|
|
// The set of all sessions. This is a superset of the sessions in
|
|
// |available_sessions_|.
|
|
//
|
|
// |sessions_| owns all its SpdySession objects.
|
|
SessionSet sessions_;
|
|
|
|
// This is a map of available sessions by key. A session may appear
|
|
// more than once in this map if it has aliases.
|
|
AvailableSessionMap available_sessions_;
|
|
|
|
// A map of IPEndPoint aliases for sessions.
|
|
AliasMap aliases_;
|
|
|
|
// A map of DNS alias vectors by session keys.
|
|
DnsAliasesBySessionKeyMap dns_aliases_by_session_key_;
|
|
|
|
// The index of all unclaimed pushed streams of all SpdySessions in this pool.
|
|
Http2PushPromiseIndex push_promise_index_;
|
|
|
|
const raw_ptr<SSLClientContext> ssl_client_context_;
|
|
const raw_ptr<HostResolver> resolver_;
|
|
|
|
// Versions of QUIC which may be used.
|
|
const quic::ParsedQuicVersionVector quic_supported_versions_;
|
|
|
|
// Defaults to true. May be controlled via SpdySessionPoolPeer for tests.
|
|
bool enable_sending_initial_data_ = true;
|
|
bool enable_ping_based_connection_checking_;
|
|
|
|
const bool is_http2_enabled_;
|
|
const bool is_quic_enabled_;
|
|
|
|
size_t session_max_recv_window_size_;
|
|
|
|
// Maximum number of capped frames that can be queued at any time.
|
|
int session_max_queued_capped_frames_;
|
|
|
|
// Settings that are sent in the initial SETTINGS frame
|
|
// (if |enable_sending_initial_data_| is true),
|
|
// and also control SpdySession parameters like initial receive window size
|
|
// and maximum HPACK dynamic table size.
|
|
const spdy::SettingsMap initial_settings_;
|
|
|
|
// If true, a setting parameter with reserved identifier will be sent in every
|
|
// initial SETTINGS frame, see
|
|
// https://tools.ietf.org/html/draft-bishop-httpbis-grease-00.
|
|
// The setting identifier and value will be drawn independently for each
|
|
// connection to prevent tracking of the client.
|
|
const bool enable_http2_settings_grease_;
|
|
|
|
// If set, an HTTP/2 frame with a reserved frame type will be sent after
|
|
// every HTTP/2 SETTINGS frame and before every HTTP/2 DATA frame. See
|
|
// https://tools.ietf.org/html/draft-bishop-httpbis-grease-00.
|
|
const absl::optional<GreasedHttp2Frame> greased_http2_frame_;
|
|
|
|
// If set, the HEADERS frame carrying a request without body will not have the
|
|
// END_STREAM flag set. The stream will be closed by a subsequent empty DATA
|
|
// frame with END_STREAM. Does not affect bidirectional or proxy streams.
|
|
// If unset, the HEADERS frame will have the END_STREAM flag set on.
|
|
// This is useful in conjuction with |greased_http2_frame_| so that a frame
|
|
// of reserved type can be sent out even on requests without a body.
|
|
const bool http2_end_stream_with_data_frame_;
|
|
|
|
// If true, enable sending PRIORITY_UPDATE frames until SETTINGS frame
|
|
// arrives. After SETTINGS frame arrives, do not send PRIORITY_UPDATE frames
|
|
// any longer if SETTINGS_DEPRECATE_HTTP2_PRIORITIES is missing or has zero 0,
|
|
// but continue and also stop sending HTTP/2-style priority information in
|
|
// HEADERS frames and PRIORITY frames if it has value 1.
|
|
const bool enable_priority_update_;
|
|
|
|
// If set, sessions will be marked as going away upon relevant network changes
|
|
// (instead of being closed).
|
|
const bool go_away_on_ip_change_;
|
|
|
|
SpdySessionRequestMap spdy_session_request_map_;
|
|
|
|
TimeFunc time_func_;
|
|
raw_ptr<ServerPushDelegate> push_delegate_ = nullptr;
|
|
|
|
raw_ptr<NetworkQualityEstimator> network_quality_estimator_;
|
|
|
|
const bool cleanup_sessions_on_ip_address_changed_;
|
|
|
|
base::WeakPtrFactory<SpdySessionPool> weak_ptr_factory_{this};
|
|
};
|
|
|
|
} // namespace net
|
|
|
|
#endif // NET_SPDY_SPDY_SESSION_POOL_H_
|