373 lines
16 KiB
C++
373 lines
16 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_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_
|