| // 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_SOCKET_CLIENT_SOCKET_POOL_H_ |
| #define NET_SOCKET_CLIENT_SOCKET_POOL_H_ |
| |
| #include <memory> |
| #include <string> |
| |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/time/time.h" |
| #include "base/values.h" |
| #include "net/base/completion_once_callback.h" |
| #include "net/base/load_states.h" |
| #include "net/base/net_export.h" |
| #include "net/base/network_anonymization_key.h" |
| #include "net/base/privacy_mode.h" |
| #include "net/base/request_priority.h" |
| #include "net/dns/host_resolver.h" |
| #include "net/dns/public/secure_dns_policy.h" |
| #include "net/http/http_request_info.h" |
| #include "net/log/net_log_capture_mode.h" |
| #include "net/socket/connect_job.h" |
| #include "net/socket/socket_tag.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| #include "url/scheme_host_port.h" |
| |
| namespace net { |
| |
| class ClientSocketHandle; |
| class ConnectJobFactory; |
| class HttpAuthController; |
| class HttpResponseInfo; |
| class NetLogWithSource; |
| struct NetworkTrafficAnnotationTag; |
| class ProxyServer; |
| struct SSLConfig; |
| class StreamSocket; |
| |
| // ClientSocketPools are layered. This defines an interface for lower level |
| // socket pools to communicate with higher layer pools. |
| class NET_EXPORT HigherLayeredPool { |
| public: |
| virtual ~HigherLayeredPool() = default; |
| |
| // Instructs the HigherLayeredPool to close an idle connection. Return true if |
| // one was closed. Closing an idle connection will call into the lower layer |
| // pool it came from, so must be careful of re-entrancy when using this. |
| virtual bool CloseOneIdleConnection() = 0; |
| }; |
| |
| // ClientSocketPools are layered. This defines an interface for higher level |
| // socket pools to communicate with lower layer pools. |
| class NET_EXPORT LowerLayeredPool { |
| public: |
| virtual ~LowerLayeredPool() = default; |
| |
| // Returns true if a there is currently a request blocked on the per-pool |
| // (not per-host) max socket limit, either in this pool, or one that it is |
| // layered on top of. |
| virtual bool IsStalled() const = 0; |
| |
| // Called to add or remove a higher layer pool on top of |this|. A higher |
| // layer pool may be added at most once to |this|, and must be removed prior |
| // to destruction of |this|. |
| virtual void AddHigherLayeredPool(HigherLayeredPool* higher_pool) = 0; |
| virtual void RemoveHigherLayeredPool(HigherLayeredPool* higher_pool) = 0; |
| }; |
| |
| // A ClientSocketPool is used to restrict the number of sockets open at a time. |
| // It also maintains a list of idle persistent sockets. |
| // |
| // Subclasses must also have an inner class SocketParams which is |
| // the type for the |params| argument in RequestSocket() and |
| // RequestSockets() below. |
| class NET_EXPORT ClientSocketPool : public LowerLayeredPool { |
| public: |
| // Indicates whether or not a request for a socket should respect the |
| // SocketPool's global and per-group socket limits. |
| enum class RespectLimits { DISABLED, ENABLED }; |
| |
| // ProxyAuthCallback is invoked when there is an auth challenge while |
| // connecting to a tunnel. When |restart_with_auth_callback| is invoked, the |
| // corresponding socket request is guaranteed not to be completed |
| // synchronously, nor will the ProxyAuthCallback be invoked against |
| // synchronously. |
| typedef base::RepeatingCallback<void( |
| const HttpResponseInfo& response, |
| HttpAuthController* auth_controller, |
| base::OnceClosure restart_with_auth_callback)> |
| ProxyAuthCallback; |
| |
| // Group ID for a socket request. Requests with the same group ID are |
| // considered indistinguishable. |
| class NET_EXPORT GroupId { |
| public: |
| GroupId(); |
| GroupId(url::SchemeHostPort destination, |
| PrivacyMode privacy_mode, |
| NetworkAnonymizationKey network_anonymization_key, |
| SecureDnsPolicy secure_dns_policy); |
| GroupId(const GroupId& group_id); |
| |
| ~GroupId(); |
| |
| GroupId& operator=(const GroupId& group_id); |
| GroupId& operator=(GroupId&& group_id); |
| |
| const url::SchemeHostPort& destination() const { return destination_; } |
| |
| PrivacyMode privacy_mode() const { return privacy_mode_; } |
| |
| const NetworkAnonymizationKey& network_anonymization_key() const { |
| return network_anonymization_key_; |
| } |
| |
| SecureDnsPolicy secure_dns_policy() const { return secure_dns_policy_; } |
| |
| // Returns the group ID as a string, for logging. |
| std::string ToString() const; |
| |
| bool operator==(const GroupId& other) const { |
| return std::tie(destination_, privacy_mode_, network_anonymization_key_, |
| secure_dns_policy_) == |
| std::tie(other.destination_, other.privacy_mode_, |
| other.network_anonymization_key_, |
| other.secure_dns_policy_); |
| } |
| |
| bool operator<(const GroupId& other) const { |
| return std::tie(destination_, privacy_mode_, network_anonymization_key_, |
| secure_dns_policy_) < |
| std::tie(other.destination_, other.privacy_mode_, |
| other.network_anonymization_key_, |
| other.secure_dns_policy_); |
| } |
| |
| private: |
| // The endpoint of the final destination (not the proxy). |
| url::SchemeHostPort destination_; |
| |
| // If this request is for a privacy mode / uncredentialed connection. |
| PrivacyMode privacy_mode_; |
| |
| // Used to separate requests made in different contexts. |
| NetworkAnonymizationKey network_anonymization_key_; |
| |
| // Controls the Secure DNS behavior to use when creating this socket. |
| SecureDnsPolicy secure_dns_policy_; |
| }; |
| |
| // Parameters that, in combination with GroupId, proxy, websocket information, |
| // and global state, are sufficient to create a ConnectJob. |
| // |
| // DO NOT ADD ANY FIELDS TO THIS CLASS. |
| // |
| // TODO(https://crbug.com/921369) In order to resolve longstanding issues |
| // related to pooling distinguishable sockets together, remove this class |
| // entirely. |
| class NET_EXPORT_PRIVATE SocketParams |
| : public base::RefCounted<SocketParams> { |
| public: |
| // For non-SSL requests / non-HTTPS proxies, the corresponding SSLConfig |
| // argument may be nullptr. |
| SocketParams(std::unique_ptr<SSLConfig> ssl_config_for_origin, |
| std::unique_ptr<SSLConfig> ssl_config_for_proxy); |
| |
| SocketParams(const SocketParams&) = delete; |
| SocketParams& operator=(const SocketParams&) = delete; |
| |
| // Creates a SocketParams object with none of the fields populated. This |
| // works for the HTTP case only. |
| static scoped_refptr<SocketParams> CreateForHttpForTesting(); |
| |
| const SSLConfig* ssl_config_for_origin() const { |
| return ssl_config_for_origin_.get(); |
| } |
| |
| const SSLConfig* ssl_config_for_proxy() const { |
| return ssl_config_for_proxy_.get(); |
| } |
| |
| private: |
| friend class base::RefCounted<SocketParams>; |
| ~SocketParams(); |
| |
| std::unique_ptr<SSLConfig> ssl_config_for_origin_; |
| std::unique_ptr<SSLConfig> ssl_config_for_proxy_; |
| }; |
| |
| ClientSocketPool(const ClientSocketPool&) = delete; |
| ClientSocketPool& operator=(const ClientSocketPool&) = delete; |
| |
| ~ClientSocketPool() override; |
| |
| // Requests a connected socket with a specified GroupId. |
| // |
| // There are five possible results from calling this function: |
| // 1) RequestSocket returns OK and initializes |handle| with a reused socket. |
| // 2) RequestSocket returns OK with a newly connected socket. |
| // 3) RequestSocket returns ERR_IO_PENDING. The handle will be added to a |
| // wait list until a socket is available to reuse or a new socket finishes |
| // connecting. |priority| will determine the placement into the wait list. |
| // 4) An error occurred early on, so RequestSocket returns an error code. |
| // 5) A recoverable error occurred while setting up the socket. An error |
| // code is returned, but the |handle| is initialized with the new socket. |
| // The caller must recover from the error before using the connection, or |
| // Disconnect the socket before releasing or resetting the |handle|. |
| // The current recoverable errors are: the errors accepted by |
| // IsCertificateError(err) and HTTPS_PROXY_TUNNEL_RESPONSE when reported by |
| // HttpProxyClientSocketPool. |
| // |
| // If this function returns OK, then |handle| is initialized upon return. |
| // The |handle|'s is_initialized method will return true in this case. If a |
| // StreamSocket was reused, then ClientSocketPool will call |
| // |handle|->set_reused(true). In either case, the socket will have been |
| // allocated and will be connected. A client might want to know whether or |
| // not the socket is reused in order to request a new socket if it encounters |
| // an error with the reused socket. |
| // |
| // If ERR_IO_PENDING is returned, then the callback will be used to notify the |
| // client of completion. |
| // |
| // Profiling information for the request is saved to |net_log| if non-NULL. |
| // |
| // If |respect_limits| is DISABLED, priority must be HIGHEST. |
| // |
| // |proxy_annotation_tag| is the annotation used for proxy-related reads and |
| // writes, and may be nullopt if (and only if) no proxy is in use. |
| // |
| // |proxy_auth_callback| will be invoked each time an auth challenge is seen |
| // while establishing a tunnel. It will be invoked asynchronously, once for |
| // each auth challenge seen. |
| virtual int RequestSocket( |
| const GroupId& group_id, |
| scoped_refptr<SocketParams> params, |
| const absl::optional<NetworkTrafficAnnotationTag>& proxy_annotation_tag, |
| RequestPriority priority, |
| const SocketTag& socket_tag, |
| RespectLimits respect_limits, |
| ClientSocketHandle* handle, |
| CompletionOnceCallback callback, |
| const ProxyAuthCallback& proxy_auth_callback, |
| const NetLogWithSource& net_log) = 0; |
| |
| // RequestSockets is used to request that |num_sockets| be connected in the |
| // connection group for |group_id|. If the connection group already has |
| // |num_sockets| idle sockets / active sockets / currently connecting sockets, |
| // then this function doesn't do anything and returns OK. Otherwise, it will |
| // start up as many connections as necessary to reach |num_sockets| total |
| // sockets for the group and returns ERR_IO_PENDING. And |callback| will be |
| // called with OK when the connection tasks are finished. |
| // It uses |params| to control how to connect the sockets. The |
| // ClientSocketPool will assign a priority to the new connections, if any. |
| // This priority will probably be lower than all others, since this method |
| // is intended to make sure ahead of time that |num_sockets| sockets are |
| // available to talk to a host. |
| virtual int RequestSockets( |
| const GroupId& group_id, |
| scoped_refptr<SocketParams> params, |
| const absl::optional<NetworkTrafficAnnotationTag>& proxy_annotation_tag, |
| int num_sockets, |
| CompletionOnceCallback callback, |
| const NetLogWithSource& net_log) = 0; |
| |
| // Called to change the priority of a RequestSocket call that returned |
| // ERR_IO_PENDING and has not yet asynchronously completed. The same handle |
| // parameter must be passed to this method as was passed to the |
| // RequestSocket call being modified. |
| // This function is a no-op if |priority| is the same as the current |
| // request priority. |
| virtual void SetPriority(const GroupId& group_id, |
| ClientSocketHandle* handle, |
| RequestPriority priority) = 0; |
| |
| // Called to cancel a RequestSocket call that returned ERR_IO_PENDING. The |
| // same handle parameter must be passed to this method as was passed to the |
| // RequestSocket call being cancelled. The associated callback is not run. |
| // If |cancel_connect_job| is true, and there are more ConnectJobs than |
| // requests, a ConnectJob will be canceled. If it's false, excess ConnectJobs |
| // may be allowed to continue, just in case there are new requests to the same |
| // endpoint. |
| virtual void CancelRequest(const GroupId& group_id, |
| ClientSocketHandle* handle, |
| bool cancel_connect_job) = 0; |
| |
| // Called to release a socket once the socket is no longer needed. If the |
| // socket still has an established connection, then it will be added to the |
| // set of idle sockets to be used to satisfy future RequestSocket calls. |
| // Otherwise, the StreamSocket is destroyed. |generation| is used to |
| // differentiate between updated versions of the same pool instance. The |
| // pool's generation will change when it flushes, so it can use this |
| // |generation| to discard sockets with mismatched ids. |
| virtual void ReleaseSocket(const GroupId& group_id, |
| std::unique_ptr<StreamSocket> socket, |
| int64_t generation) = 0; |
| |
| // This flushes all state from the ClientSocketPool. Pending socket requests |
| // are failed with |error|, while |reason| is logged to the NetLog. |
| // |
| // Active sockets being held by ClientSocketPool clients will be discarded |
| // when released back to the pool, though they will be closed with an error |
| // about being of the wrong generation, rather than |net_log_reason_utf8|. |
| virtual void FlushWithError(int error, const char* net_log_reason_utf8) = 0; |
| |
| // Called to close any idle connections held by the connection manager. |
| // |reason| is logged to NetLog for debugging purposes. |
| virtual void CloseIdleSockets(const char* net_log_reason_utf8) = 0; |
| |
| // Called to close any idle connections held by the connection manager. |
| // |reason| is logged to NetLog for debugging purposes. |
| virtual void CloseIdleSocketsInGroup(const GroupId& group_id, |
| const char* net_log_reason_utf8) = 0; |
| |
| // The total number of idle sockets in the pool. |
| virtual int IdleSocketCount() const = 0; |
| |
| // The total number of idle sockets in a connection group. |
| virtual size_t IdleSocketCountInGroup(const GroupId& group_id) const = 0; |
| |
| // Determine the LoadState of a connecting ClientSocketHandle. |
| virtual LoadState GetLoadState(const GroupId& group_id, |
| const ClientSocketHandle* handle) const = 0; |
| |
| // Retrieves information on the current state of the pool as a |
| // Value. |
| // If |include_nested_pools| is true, the states of any nested |
| // ClientSocketPools will be included. |
| virtual base::Value GetInfoAsValue(const std::string& name, |
| const std::string& type) const = 0; |
| |
| // Returns whether a connected (idle or handed out) or connecting socket |
| // exists for the group. This method is not supported for WebSockets. |
| virtual bool HasActiveSocket(const GroupId& group_id) const = 0; |
| |
| // Returns the maximum amount of time to wait before retrying a connect. |
| static const int kMaxConnectRetryIntervalMs = 250; |
| |
| static base::TimeDelta used_idle_socket_timeout(); |
| static void set_used_idle_socket_timeout(base::TimeDelta timeout); |
| |
| protected: |
| ClientSocketPool(bool is_for_websockets, |
| const CommonConnectJobParams* common_connect_job_params, |
| std::unique_ptr<ConnectJobFactory> connect_job_factory); |
| |
| void NetLogTcpClientSocketPoolRequestedSocket(const NetLogWithSource& net_log, |
| const GroupId& group_id); |
| |
| // Utility method to log a GroupId with a NetLog event. |
| static base::Value::Dict NetLogGroupIdParams(const GroupId& group_id); |
| |
| std::unique_ptr<ConnectJob> CreateConnectJob( |
| GroupId group_id, |
| scoped_refptr<SocketParams> socket_params, |
| const ProxyServer& proxy_server, |
| const absl::optional<NetworkTrafficAnnotationTag>& proxy_annotation_tag, |
| RequestPriority request_priority, |
| SocketTag socket_tag, |
| ConnectJob::Delegate* delegate); |
| |
| private: |
| const bool is_for_websockets_; |
| const raw_ptr<const CommonConnectJobParams> common_connect_job_params_; |
| const std::unique_ptr<ConnectJobFactory> connect_job_factory_; |
| }; |
| |
| } // namespace net |
| |
| #endif // NET_SOCKET_CLIENT_SOCKET_POOL_H_ |