summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/base/net_log_event_type_list.h14
-rw-r--r--net/http/http_proxy_client_socket_pool.cc11
-rw-r--r--net/http/http_proxy_client_socket_pool.h5
-rw-r--r--net/socket/client_socket_pool.h15
-rw-r--r--net/socket/client_socket_pool_base.cc107
-rw-r--r--net/socket/client_socket_pool_base.h108
-rw-r--r--net/socket/client_socket_pool_base_unittest.cc366
-rw-r--r--net/socket/socks_client_socket_pool.cc11
-rw-r--r--net/socket/socks_client_socket_pool.h5
-rw-r--r--net/socket/ssl_client_socket_pool.cc11
-rw-r--r--net/socket/ssl_client_socket_pool.h5
-rw-r--r--net/socket/tcp_client_socket_pool.cc20
-rw-r--r--net/socket/tcp_client_socket_pool.h5
13 files changed, 646 insertions, 37 deletions
diff --git a/net/base/net_log_event_type_list.h b/net/base/net_log_event_type_list.h
index 9c3264f..b0b4d4b 100644
--- a/net/base/net_log_event_type_list.h
+++ b/net/base/net_log_event_type_list.h
@@ -412,6 +412,13 @@ EVENT_TYPE(SOCKET_POOL_REUSED_AN_EXISTING_SOCKET)
// }
EVENT_TYPE(TCP_CLIENT_SOCKET_POOL_REQUESTED_SOCKET)
+// This event simply describes the host:port that were requested from the
+// socket pool. Its parameters are:
+// {
+// "host_and_port": <String encoding the host and port>
+// }
+EVENT_TYPE(TCP_CLIENT_SOCKET_POOL_REQUESTED_SOCKETS)
+
// A backup socket is created due to slow connect
EVENT_TYPE(SOCKET_BACKUP_CREATED)
@@ -434,6 +441,13 @@ EVENT_TYPE(SOCKET_POOL_BOUND_TO_CONNECT_JOB)
// }
EVENT_TYPE(SOCKET_POOL_BOUND_TO_SOCKET)
+// The start/end of a client socket pool request for multiple sockets.
+// The event parameters are:
+// {
+// "num_sockets": <Number of sockets we're trying to ensure are connected>
+// }
+EVENT_TYPE(SOCKET_POOL_CONNECTING_N_SOCKETS)
+
// ------------------------------------------------------------------------
// URLRequest
// ------------------------------------------------------------------------
diff --git a/net/http/http_proxy_client_socket_pool.cc b/net/http/http_proxy_client_socket_pool.cc
index 0f55e14..4a5495b 100644
--- a/net/http/http_proxy_client_socket_pool.cc
+++ b/net/http/http_proxy_client_socket_pool.cc
@@ -299,6 +299,17 @@ int HttpProxyClientSocketPool::RequestSocket(const std::string& group_name,
handle, callback, net_log);
}
+void HttpProxyClientSocketPool::RequestSockets(
+ const std::string& group_name,
+ const void* params,
+ int num_sockets,
+ const BoundNetLog& net_log) {
+ const scoped_refptr<HttpProxySocketParams>* casted_params =
+ static_cast<const scoped_refptr<HttpProxySocketParams>*>(params);
+
+ base_.RequestSockets(group_name, *casted_params, num_sockets, net_log);
+}
+
void HttpProxyClientSocketPool::CancelRequest(
const std::string& group_name,
ClientSocketHandle* handle) {
diff --git a/net/http/http_proxy_client_socket_pool.h b/net/http/http_proxy_client_socket_pool.h
index 3ee215a..f95a054 100644
--- a/net/http/http_proxy_client_socket_pool.h
+++ b/net/http/http_proxy_client_socket_pool.h
@@ -162,6 +162,11 @@ class HttpProxyClientSocketPool : public ClientSocketPool {
CompletionCallback* callback,
const BoundNetLog& net_log);
+ virtual void RequestSockets(const std::string& group_name,
+ const void* params,
+ int num_sockets,
+ const BoundNetLog& net_log);
+
virtual void CancelRequest(const std::string& group_name,
ClientSocketHandle* handle);
diff --git a/net/socket/client_socket_pool.h b/net/socket/client_socket_pool.h
index ad5f1ae..d097c0f 100644
--- a/net/socket/client_socket_pool.h
+++ b/net/socket/client_socket_pool.h
@@ -67,6 +67,21 @@ class ClientSocketPool {
CompletionCallback* callback,
const BoundNetLog& net_log) = 0;
+ // RequestSockets is used to request that |num_sockets| be connected in the
+ // connection group for |group_name|. If the connection group already has
+ // |num_sockets| idle sockets / active sockets / currently connecting sockets,
+ // then this function doesn't do anything. Otherwise, it will start up as
+ // many connections as necessary to reach |num_sockets| total sockets for the
+ // group. 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 void RequestSockets(const std::string& group_name,
+ const void* params,
+ int num_sockets,
+ const BoundNetLog& net_log) = 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 CompletionCallback is
diff --git a/net/socket/client_socket_pool_base.cc b/net/socket/client_socket_pool_base.cc
index 994e1d2..13d8b68 100644
--- a/net/socket/client_socket_pool_base.cc
+++ b/net/socket/client_socket_pool_base.cc
@@ -44,7 +44,8 @@ ConnectJob::ConnectJob(const std::string& group_name,
timeout_duration_(timeout_duration),
delegate_(delegate),
net_log_(net_log),
- idle_(true) {
+ idle_(true),
+ preconnect_state_(NOT_PRECONNECT) {
DCHECK(!group_name.empty());
DCHECK(delegate);
net_log.BeginEvent(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB, NULL);
@@ -54,6 +55,13 @@ ConnectJob::~ConnectJob() {
net_log().EndEvent(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB, NULL);
}
+void ConnectJob::Initialize(bool is_preconnect) {
+ if (is_preconnect)
+ preconnect_state_ = UNUSED_PRECONNECT;
+ else
+ preconnect_state_ = NOT_PRECONNECT;
+}
+
int ConnectJob::Connect() {
if (timeout_duration_ != base::TimeDelta())
timer_.Start(timeout_duration_, this, &ConnectJob::OnTimeout);
@@ -72,6 +80,11 @@ int ConnectJob::Connect() {
return rv;
}
+void ConnectJob::UseForNormalRequest() {
+ DCHECK_EQ(UNUSED_PRECONNECT, preconnect_state_);
+ preconnect_state_ = USED_PRECONNECT;
+}
+
void ConnectJob::set_socket(ClientSocket* socket) {
if (socket) {
net_log().AddEvent(NetLog::TYPE_CONNECT_JOB_SET_SOCKET,
@@ -122,10 +135,12 @@ ClientSocketPoolBaseHelper::Request::Request(
ClientSocketHandle* handle,
CompletionCallback* callback,
RequestPriority priority,
+ Flags flags,
const BoundNetLog& net_log)
: handle_(handle),
callback_(callback),
priority_(priority),
+ flags_(flags),
net_log_(net_log) {}
ClientSocketPoolBaseHelper::Request::~Request() {}
@@ -193,6 +208,9 @@ ClientSocketPoolBaseHelper::RemoveRequestFromQueue(
int ClientSocketPoolBaseHelper::RequestSocket(
const std::string& group_name,
const Request* request) {
+ CHECK(request->callback());
+ CHECK(request->handle());
+
request->net_log().BeginEvent(NetLog::TYPE_SOCKET_POOL, NULL);
Group* group = GetOrCreateGroup(group_name);
@@ -207,19 +225,57 @@ int ClientSocketPoolBaseHelper::RequestSocket(
return rv;
}
+void ClientSocketPoolBaseHelper::RequestSockets(
+ const std::string& group_name,
+ const Request& request,
+ int num_sockets) {
+ DCHECK(!request.callback());
+ DCHECK(!request.handle());
+
+ if (num_sockets > max_sockets_per_group_) {
+ NOTREACHED();
+ num_sockets = max_sockets_per_group_;
+ }
+
+ request.net_log().BeginEvent(
+ NetLog::TYPE_SOCKET_POOL_CONNECTING_N_SOCKETS,
+ new NetLogIntegerParameter("num_sockets", num_sockets));
+
+ Group* group = GetOrCreateGroup(group_name);
+
+ for (int num_iterations_left = num_sockets;
+ group->NumActiveSocketSlots() < num_sockets &&
+ num_iterations_left > 0 ; num_iterations_left--) {
+ int rv = RequestSocketInternal(group_name, &request);
+ if (rv < 0 && rv != ERR_IO_PENDING) {
+ // We're encountering a synchronous error. Give up.
+ break;
+ }
+ }
+
+ if (group->IsEmpty())
+ RemoveGroup(group_name);
+
+ request.net_log().EndEvent(
+ NetLog::TYPE_SOCKET_POOL_CONNECTING_N_SOCKETS, NULL);
+}
+
int ClientSocketPoolBaseHelper::RequestSocketInternal(
const std::string& group_name,
const Request* request) {
DCHECK_GE(request->priority(), 0);
- CompletionCallback* const callback = request->callback();
- CHECK(callback);
ClientSocketHandle* const handle = request->handle();
- CHECK(handle);
+ const bool preconnecting = !handle;
Group* group = GetOrCreateGroup(group_name);
- // Try to reuse a socket.
- if (AssignIdleSocketToGroup(request, group))
- return OK;
+ if (!(request->flags() & NO_IDLE_SOCKETS)) {
+ // Try to reuse a socket.
+ if (AssignIdleSocketToGroup(request, group))
+ return OK;
+ }
+
+ if (!preconnecting && group->TryToUsePreconnectConnectJob())
+ return ERR_IO_PENDING;
// Can we make another active socket now?
if (!group->HasAvailableSocketSlot(max_sockets_per_group_)) {
@@ -244,18 +300,25 @@ int ClientSocketPoolBaseHelper::RequestSocketInternal(
scoped_ptr<ConnectJob> connect_job(
connect_job_factory_->NewConnectJob(group_name, *request, this));
+ connect_job->Initialize(preconnecting);
int rv = connect_job->Connect();
if (rv == OK) {
LogBoundConnectJobToRequest(connect_job->net_log().source(), request);
- HandOutSocket(connect_job->ReleaseSocket(), false /* not reused */,
- handle, base::TimeDelta(), group, request->net_log());
+ if (!preconnecting) {
+ HandOutSocket(connect_job->ReleaseSocket(), false /* not reused */,
+ handle, base::TimeDelta(), group, request->net_log());
+ } else {
+ AddIdleSocket(connect_job->ReleaseSocket(), group);
+ }
} else if (rv == ERR_IO_PENDING) {
// If we don't have any sockets in this group, set a timer for potentially
// creating a new one. If the SYN is lost, this backup socket may complete
// before the slow socket, improving end user latency.
- if (group->IsEmpty() && !group->HasBackupJob() &&
- connect_backup_jobs_enabled_)
+ if (connect_backup_jobs_enabled_ &&
+ group->IsEmpty() && !group->HasBackupJob() &&
+ handle) {
group->StartBackupSocketTimer(group_name, this);
+ }
connecting_socket_count_++;
@@ -357,10 +420,9 @@ void ClientSocketPoolBaseHelper::CancelRequest(
RequestQueue::iterator it = group->mutable_pending_requests()->begin();
for (; it != group->pending_requests().end(); ++it) {
if ((*it)->handle() == handle) {
- const Request* req = RemoveRequestFromQueue(it, group);
+ scoped_ptr<const Request> req(RemoveRequestFromQueue(it, group));
req->net_log().AddEvent(NetLog::TYPE_CANCELLED, NULL);
req->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL, NULL);
- delete req;
// We let the job run, unless we're at the socket limit.
if (group->jobs().size() && ReachedMaxSocketsLimit()) {
@@ -468,7 +530,7 @@ DictionaryValue* ClientSocketPoolBaseHelper::GetInfoAsValue(
group_dict->Set("idle_sockets", idle_socket_list);
ListValue* connect_jobs_list = new ListValue();
- std::set<const ConnectJob*>::const_iterator job = group->jobs().begin();
+ std::set<ConnectJob*>::const_iterator job = group->jobs().begin();
for (job = group->jobs().begin(); job != group->jobs().end(); job++) {
int source_id = (*job)->net_log().source().id;
connect_jobs_list->Append(Value::CreateIntegerValue(source_id));
@@ -725,7 +787,7 @@ void ClientSocketPoolBaseHelper::Flush() {
AbortAllRequests();
}
-void ClientSocketPoolBaseHelper::RemoveConnectJob(const ConnectJob* job,
+void ClientSocketPoolBaseHelper::RemoveConnectJob(ConnectJob* job,
Group* group) {
CHECK_GT(connecting_socket_count_, 0);
connecting_socket_count_--;
@@ -837,10 +899,9 @@ void ClientSocketPoolBaseHelper::AbortAllRequests() {
pending_requests.swap(*group->mutable_pending_requests());
for (RequestQueue::iterator it2 = pending_requests.begin();
it2 != pending_requests.end(); ++it2) {
- const Request* request = *it2;
+ scoped_ptr<const Request> request(*it2);
InvokeUserCallbackLater(
request->handle(), request->callback(), ERR_ABORTED);
- delete request;
}
// Delete group if no longer needed.
@@ -934,6 +995,18 @@ void ClientSocketPoolBaseHelper::Group::StartBackupSocketTimer(
pool->ConnectRetryIntervalMs());
}
+bool ClientSocketPoolBaseHelper::Group::TryToUsePreconnectConnectJob() {
+ for (std::set<ConnectJob*>::iterator it = jobs_.begin();
+ it != jobs_.end(); ++it) {
+ ConnectJob* job = *it;
+ if (job->is_unused_preconnect()) {
+ job->UseForNormalRequest();
+ return true;
+ }
+ }
+ return false;
+}
+
void ClientSocketPoolBaseHelper::Group::OnBackupSocketTimerFired(
std::string group_name,
ClientSocketPoolBaseHelper* pool) {
diff --git a/net/socket/client_socket_pool_base.h b/net/socket/client_socket_pool_base.h
index a39eeaa..6c626f7 100644
--- a/net/socket/client_socket_pool_base.h
+++ b/net/socket/client_socket_pool_base.h
@@ -76,6 +76,16 @@ class ConnectJob {
// Accessors
const std::string& group_name() const { return group_name_; }
const BoundNetLog& net_log() { return net_log_; }
+ bool is_preconnect() const { return preconnect_state_ != NOT_PRECONNECT; }
+ bool is_unused_preconnect() const {
+ return preconnect_state_ == UNUSED_PRECONNECT;
+ }
+
+ // Initialized by the ClientSocketPoolBaseHelper.
+ // TODO(willchan): Move most of the constructor arguments over here. We
+ // shouldn't give the ConnectJobFactory (subclasses) the ability to screw up
+ // the initialization.
+ void Initialize(bool is_preconnect);
// Releases |socket_| to the client. On connection error, this should return
// NULL.
@@ -89,6 +99,10 @@ class ConnectJob {
// if it succeeded.
int Connect();
+ // Precondition: is_unused_preconnect() must be true. Marks the job as a
+ // used preconnect job.
+ void UseForNormalRequest();
+
virtual LoadState GetLoadState() const = 0;
// If Connect returns an error (or OnConnectJobComplete reports an error
@@ -105,6 +119,12 @@ class ConnectJob {
void ResetTimer(base::TimeDelta remainingTime);
private:
+ enum PreconnectState {
+ NOT_PRECONNECT,
+ UNUSED_PRECONNECT,
+ USED_PRECONNECT,
+ };
+
virtual int ConnectInternal() = 0;
void LogConnectStart();
@@ -122,6 +142,7 @@ class ConnectJob {
BoundNetLog net_log_;
// A ConnectJob is idle until Connect() has been called.
bool idle_;
+ PreconnectState preconnect_state_;
DISALLOW_COPY_AND_ASSIGN(ConnectJob);
};
@@ -137,11 +158,20 @@ class ClientSocketPoolBaseHelper
: public ConnectJob::Delegate,
public NetworkChangeNotifier::Observer {
public:
+ // Used to specify specific behavior for the ClientSocketPool.
+ enum Flag {
+ NORMAL = 0, // Normal behavior.
+ NO_IDLE_SOCKETS = 0x1, // Do not return an idle socket. Create a new one.
+ };
+
+ typedef uint32 Flags;
+
class Request {
public:
Request(ClientSocketHandle* handle,
CompletionCallback* callback,
RequestPriority priority,
+ Flags flags,
const BoundNetLog& net_log);
virtual ~Request();
@@ -149,12 +179,14 @@ class ClientSocketPoolBaseHelper
ClientSocketHandle* handle() const { return handle_; }
CompletionCallback* callback() const { return callback_; }
RequestPriority priority() const { return priority_; }
+ const Flags flags() const { return flags_; }
const BoundNetLog& net_log() const { return net_log_; }
private:
ClientSocketHandle* const handle_;
CompletionCallback* const callback_;
const RequestPriority priority_;
+ const Flags flags_;
BoundNetLog net_log_;
DISALLOW_COPY_AND_ASSIGN(Request);
@@ -190,6 +222,11 @@ class ClientSocketPoolBaseHelper
// heap allocated.
int RequestSocket(const std::string& group_name, const Request* request);
+ // See ClientSocketPool::RequestSocket for documentation on this function.
+ void RequestSockets(const std::string& group_name,
+ const Request& request,
+ int num_sockets);
+
// See ClientSocketPool::CancelRequest for documentation on this function.
void CancelRequest(const std::string& group_name,
ClientSocketHandle* handle);
@@ -234,6 +271,10 @@ class ClientSocketPoolBaseHelper
return group_map_.find(group_name)->second->jobs().size();
}
+ int NumActiveSocketsInGroup(const std::string& group_name) const {
+ return group_map_.find(group_name)->second->active_socket_count();
+ }
+
bool HasGroup(const std::string& group_name) const;
// Closes all idle sockets if |force| is true. Else, only closes idle
@@ -271,7 +312,7 @@ class ClientSocketPoolBaseHelper
bool ShouldCleanup(base::TimeTicks now, base::TimeDelta timeout) const;
};
- typedef std::deque<const Request*> RequestQueue;
+ typedef std::deque<const Request* > RequestQueue;
typedef std::map<const ClientSocketHandle*, const Request*> RequestMap;
// A Group is allocated per group_name when there are idle sockets or pending
@@ -288,8 +329,12 @@ class ClientSocketPoolBaseHelper
}
bool HasAvailableSocketSlot(int max_sockets_per_group) const {
- return active_socket_count_ + static_cast<int>(jobs_.size()) <
- max_sockets_per_group;
+ return NumActiveSocketSlots() < max_sockets_per_group;
+ }
+
+ int NumActiveSocketSlots() const {
+ return active_socket_count_ + static_cast<int>(jobs_.size()) +
+ static_cast<int>(idle_sockets_.size());
}
bool IsStalled(int max_sockets_per_group) const {
@@ -311,19 +356,18 @@ class ClientSocketPoolBaseHelper
void StartBackupSocketTimer(const std::string& group_name,
ClientSocketPoolBaseHelper* pool);
- // Called when the backup socket timer fires.
- void OnBackupSocketTimerFired(
- std::string group_name,
- ClientSocketPoolBaseHelper* pool);
+ // Searches |jobs_| to see if there's a preconnect ConnectJob, and if so,
+ // uses it. Returns true on success. Otherwise, returns false.
+ bool TryToUsePreconnectConnectJob();
- void AddJob(const ConnectJob* job) { jobs_.insert(job); }
- void RemoveJob(const ConnectJob* job) { jobs_.erase(job); }
+ void AddJob(ConnectJob* job) { jobs_.insert(job); }
+ void RemoveJob(ConnectJob* job) { jobs_.erase(job); }
void RemoveAllJobs();
void IncrementActiveSocketCount() { active_socket_count_++; }
void DecrementActiveSocketCount() { active_socket_count_--; }
- const std::set<const ConnectJob*>& jobs() const { return jobs_; }
+ const std::set<ConnectJob*>& jobs() const { return jobs_; }
const std::list<IdleSocket>& idle_sockets() const { return idle_sockets_; }
const RequestQueue& pending_requests() const { return pending_requests_; }
int active_socket_count() const { return active_socket_count_; }
@@ -331,8 +375,13 @@ class ClientSocketPoolBaseHelper
std::list<IdleSocket>* mutable_idle_sockets() { return &idle_sockets_; }
private:
+ // Called when the backup socket timer fires.
+ void OnBackupSocketTimerFired(
+ std::string group_name,
+ ClientSocketPoolBaseHelper* pool);
+
std::list<IdleSocket> idle_sockets_;
- std::set<const ConnectJob*> jobs_;
+ std::set<ConnectJob*> jobs_;
RequestQueue pending_requests_;
int active_socket_count_; // number of active sockets used by clients
// A factory to pin the backup_job tasks.
@@ -341,7 +390,7 @@ class ClientSocketPoolBaseHelper
typedef std::map<std::string, Group*> GroupMap;
- typedef std::set<const ConnectJob*> ConnectJobSet;
+ typedef std::set<ConnectJob*> ConnectJobSet;
struct CallbackResultPair {
CallbackResultPair() : callback(NULL), result(OK) {}
@@ -381,7 +430,7 @@ class ClientSocketPoolBaseHelper
}
// Removes |job| from |connect_job_set_|. Also updates |group| if non-NULL.
- void RemoveConnectJob(const ConnectJob* job, Group* group);
+ void RemoveConnectJob(ConnectJob* job, Group* group);
// Tries to see if we can handle any more requests for |group|.
void OnAvailableSocketSlot(const std::string& group_name, Group* group);
@@ -505,16 +554,17 @@ class ClientSocketPoolBase {
Request(ClientSocketHandle* handle,
CompletionCallback* callback,
RequestPriority priority,
+ internal::ClientSocketPoolBaseHelper::Flags flags,
const scoped_refptr<SocketParams>& params,
const BoundNetLog& net_log)
: internal::ClientSocketPoolBaseHelper::Request(
- handle, callback, priority, net_log),
+ handle, callback, priority, flags, net_log),
params_(params) {}
const scoped_refptr<SocketParams>& params() const { return params_; }
private:
- scoped_refptr<SocketParams> params_;
+ const scoped_refptr<SocketParams> params_;
};
class ConnectJobFactory {
@@ -556,18 +606,36 @@ class ClientSocketPoolBase {
// These member functions simply forward to ClientSocketPoolBaseHelper.
// RequestSocket bundles up the parameters into a Request and then forwards to
- // ClientSocketPoolBaseHelper::RequestSocket(). Note that the memory
- // ownership is transferred in the asynchronous (ERR_IO_PENDING) case.
+ // ClientSocketPoolBaseHelper::RequestSocket().
int RequestSocket(const std::string& group_name,
const scoped_refptr<SocketParams>& params,
RequestPriority priority,
ClientSocketHandle* handle,
CompletionCallback* callback,
const BoundNetLog& net_log) {
- Request* request = new Request(handle, callback, priority, params, net_log);
+ Request* request =
+ new Request(handle, callback, priority,
+ internal::ClientSocketPoolBaseHelper::NORMAL,
+ params, net_log);
return helper_.RequestSocket(group_name, request);
}
+ // RequestSockets bundles up the parameters into a Request and then forwards
+ // to ClientSocketPoolBaseHelper::RequestSockets(). Note that it assigns the
+ // priority to LOWEST and specifies the NO_IDLE_SOCKETS flag.
+ void RequestSockets(const std::string& group_name,
+ const scoped_refptr<SocketParams>& params,
+ int num_sockets,
+ const BoundNetLog& net_log) {
+ const Request request(NULL /* no handle */,
+ NULL /* no callback */,
+ LOWEST,
+ internal::ClientSocketPoolBaseHelper::NO_IDLE_SOCKETS,
+ params,
+ net_log);
+ helper_.RequestSockets(group_name, request, num_sockets);
+ }
+
void CancelRequest(const std::string& group_name,
ClientSocketHandle* handle) {
return helper_.CancelRequest(group_name, handle);
@@ -599,6 +667,10 @@ class ClientSocketPoolBase {
return helper_.NumConnectJobsInGroup(group_name);
}
+ int NumActiveSocketsInGroup(const std::string& group_name) const {
+ return helper_.NumActiveSocketsInGroup(group_name);
+ }
+
bool HasGroup(const std::string& group_name) const {
return helper_.HasGroup(group_name);
}
diff --git a/net/socket/client_socket_pool_base_unittest.cc b/net/socket/client_socket_pool_base_unittest.cc
index 87f2ae4..0f06160 100644
--- a/net/socket/client_socket_pool_base_unittest.cc
+++ b/net/socket/client_socket_pool_base_unittest.cc
@@ -382,6 +382,16 @@ class TestClientSocketPool : public ClientSocketPool {
handle, callback, net_log);
}
+ virtual void RequestSockets(const std::string& group_name,
+ const void* params,
+ int num_sockets,
+ const BoundNetLog& net_log) {
+ const scoped_refptr<TestSocketParams>* casted_params =
+ static_cast<const scoped_refptr<TestSocketParams>*>(params);
+
+ base_.RequestSockets(group_name, *casted_params, num_sockets, net_log);
+ }
+
virtual void CancelRequest(
const std::string& group_name,
ClientSocketHandle* handle) {
@@ -434,6 +444,10 @@ class TestClientSocketPool : public ClientSocketPool {
return base_.NumConnectJobsInGroup(group_name);
}
+ int NumActiveSocketsInGroup(const std::string& group_name) const {
+ return base_.NumActiveSocketsInGroup(group_name);
+ }
+
bool HasGroup(const std::string& group_name) const {
return base_.HasGroup(group_name);
}
@@ -565,7 +579,9 @@ TEST_F(ClientSocketPoolBaseTest, ConnectJob_NoTimeoutOnSynchronousCompletion) {
TestConnectJobDelegate delegate;
ClientSocketHandle ignored;
TestClientSocketPoolBase::Request request(
- &ignored, NULL, kDefaultPriority, params_, BoundNetLog());
+ &ignored, NULL, kDefaultPriority,
+ internal::ClientSocketPoolBaseHelper::NORMAL,
+ params_, BoundNetLog());
scoped_ptr<TestConnectJob> job(
new TestConnectJob(TestConnectJob::kMockJob,
"a",
@@ -583,7 +599,9 @@ TEST_F(ClientSocketPoolBaseTest, ConnectJob_TimedOut) {
CapturingNetLog log(CapturingNetLog::kUnbounded);
TestClientSocketPoolBase::Request request(
- &ignored, NULL, kDefaultPriority, params_, BoundNetLog());
+ &ignored, NULL, kDefaultPriority,
+ internal::ClientSocketPoolBaseHelper::NORMAL,
+ params_, BoundNetLog());
// Deleted by TestConnectJobDelegate.
TestConnectJob* job =
new TestConnectJob(TestConnectJob::kMockPendingJob,
@@ -2630,6 +2648,350 @@ TEST_F(ClientSocketPoolBaseTest, PreferUsedSocketToUnusedSocket) {
EXPECT_FALSE(handle3.socket()->WasEverUsed());
}
+TEST_F(ClientSocketPoolBaseTest, RequestSockets) {
+ CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+ connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
+
+ pool_->RequestSockets("a", &params_, 2, BoundNetLog());
+
+ ASSERT_TRUE(pool_->HasGroup("a"));
+ EXPECT_EQ(2, pool_->NumConnectJobsInGroup("a"));
+ EXPECT_EQ(0, pool_->IdleSocketCountInGroup("a"));
+
+ ClientSocketHandle handle1;
+ TestCompletionCallback callback1;
+ EXPECT_EQ(ERR_IO_PENDING, handle1.Init("a",
+ params_,
+ kDefaultPriority,
+ &callback1,
+ pool_.get(),
+ BoundNetLog()));
+
+ ClientSocketHandle handle2;
+ TestCompletionCallback callback2;
+ EXPECT_EQ(ERR_IO_PENDING, handle2.Init("a",
+ params_,
+ kDefaultPriority,
+ &callback2,
+ pool_.get(),
+ BoundNetLog()));
+
+ EXPECT_EQ(2, pool_->NumConnectJobsInGroup("a"));
+ EXPECT_EQ(0, pool_->IdleSocketCountInGroup("a"));
+
+ EXPECT_EQ(OK, callback1.WaitForResult());
+ EXPECT_EQ(OK, callback2.WaitForResult());
+ handle1.Reset();
+ handle2.Reset();
+
+ EXPECT_EQ(0, pool_->NumConnectJobsInGroup("a"));
+ EXPECT_EQ(2, pool_->IdleSocketCountInGroup("a"));
+}
+
+TEST_F(ClientSocketPoolBaseTest, RequestSocketsWhenAlreadyHaveAConnectJob) {
+ CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+ connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
+
+ ClientSocketHandle handle1;
+ TestCompletionCallback callback1;
+ EXPECT_EQ(ERR_IO_PENDING, handle1.Init("a",
+ params_,
+ kDefaultPriority,
+ &callback1,
+ pool_.get(),
+ BoundNetLog()));
+
+ ASSERT_TRUE(pool_->HasGroup("a"));
+ EXPECT_EQ(1, pool_->NumConnectJobsInGroup("a"));
+ EXPECT_EQ(0, pool_->IdleSocketCountInGroup("a"));
+
+ pool_->RequestSockets("a", &params_, 2, BoundNetLog());
+
+ EXPECT_EQ(2, pool_->NumConnectJobsInGroup("a"));
+ EXPECT_EQ(0, pool_->IdleSocketCountInGroup("a"));
+
+ ClientSocketHandle handle2;
+ TestCompletionCallback callback2;
+ EXPECT_EQ(ERR_IO_PENDING, handle2.Init("a",
+ params_,
+ kDefaultPriority,
+ &callback2,
+ pool_.get(),
+ BoundNetLog()));
+
+ EXPECT_EQ(2, pool_->NumConnectJobsInGroup("a"));
+ EXPECT_EQ(0, pool_->IdleSocketCountInGroup("a"));
+
+ EXPECT_EQ(OK, callback1.WaitForResult());
+ EXPECT_EQ(OK, callback2.WaitForResult());
+ handle1.Reset();
+ handle2.Reset();
+
+ EXPECT_EQ(0, pool_->NumConnectJobsInGroup("a"));
+ EXPECT_EQ(2, pool_->IdleSocketCountInGroup("a"));
+}
+
+TEST_F(ClientSocketPoolBaseTest,
+ RequestSocketsWhenAlreadyHaveMultipleConnectJob) {
+ CreatePool(4, 4);
+ connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
+
+ ClientSocketHandle handle1;
+ TestCompletionCallback callback1;
+ EXPECT_EQ(ERR_IO_PENDING, handle1.Init("a",
+ params_,
+ kDefaultPriority,
+ &callback1,
+ pool_.get(),
+ BoundNetLog()));
+
+ ClientSocketHandle handle2;
+ TestCompletionCallback callback2;
+ EXPECT_EQ(ERR_IO_PENDING, handle2.Init("a",
+ params_,
+ kDefaultPriority,
+ &callback2,
+ pool_.get(),
+ BoundNetLog()));
+
+ ClientSocketHandle handle3;
+ TestCompletionCallback callback3;
+ EXPECT_EQ(ERR_IO_PENDING, handle3.Init("a",
+ params_,
+ kDefaultPriority,
+ &callback3,
+ pool_.get(),
+ BoundNetLog()));
+
+ ASSERT_TRUE(pool_->HasGroup("a"));
+ EXPECT_EQ(3, pool_->NumConnectJobsInGroup("a"));
+ EXPECT_EQ(0, pool_->IdleSocketCountInGroup("a"));
+
+ pool_->RequestSockets("a", &params_, 2, BoundNetLog());
+
+ EXPECT_EQ(3, pool_->NumConnectJobsInGroup("a"));
+ EXPECT_EQ(0, pool_->IdleSocketCountInGroup("a"));
+
+ EXPECT_EQ(OK, callback1.WaitForResult());
+ EXPECT_EQ(OK, callback2.WaitForResult());
+ EXPECT_EQ(OK, callback3.WaitForResult());
+ handle1.Reset();
+ handle2.Reset();
+ handle3.Reset();
+
+ EXPECT_EQ(0, pool_->NumConnectJobsInGroup("a"));
+ EXPECT_EQ(3, pool_->IdleSocketCountInGroup("a"));
+}
+
+TEST_F(ClientSocketPoolBaseTest, RequestSocketsAtMaxSocketLimit) {
+ CreatePool(kDefaultMaxSockets, kDefaultMaxSockets);
+ connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
+
+ ASSERT_FALSE(pool_->HasGroup("a"));
+
+ pool_->RequestSockets("a", &params_, kDefaultMaxSockets,
+ BoundNetLog());
+
+ ASSERT_TRUE(pool_->HasGroup("a"));
+ EXPECT_EQ(kDefaultMaxSockets, pool_->NumConnectJobsInGroup("a"));
+
+ ASSERT_FALSE(pool_->HasGroup("b"));
+
+ pool_->RequestSockets("b", &params_, kDefaultMaxSockets,
+ BoundNetLog());
+
+ ASSERT_FALSE(pool_->HasGroup("b"));
+}
+
+TEST_F(ClientSocketPoolBaseTest, RequestSocketsHitMaxSocketLimit) {
+ CreatePool(kDefaultMaxSockets, kDefaultMaxSockets);
+ connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
+
+ ASSERT_FALSE(pool_->HasGroup("a"));
+
+ pool_->RequestSockets("a", &params_, kDefaultMaxSockets - 1,
+ BoundNetLog());
+
+ ASSERT_TRUE(pool_->HasGroup("a"));
+ EXPECT_EQ(kDefaultMaxSockets - 1, pool_->NumConnectJobsInGroup("a"));
+
+ ASSERT_FALSE(pool_->HasGroup("b"));
+
+ pool_->RequestSockets("b", &params_, kDefaultMaxSockets,
+ BoundNetLog());
+
+ ASSERT_TRUE(pool_->HasGroup("b"));
+ EXPECT_EQ(1, pool_->NumConnectJobsInGroup("b"));
+}
+
+TEST_F(ClientSocketPoolBaseTest, RequestSocketsCountIdleSockets) {
+ CreatePool(4, 4);
+ connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
+
+ ClientSocketHandle handle1;
+ TestCompletionCallback callback1;
+ EXPECT_EQ(ERR_IO_PENDING, handle1.Init("a",
+ params_,
+ kDefaultPriority,
+ &callback1,
+ pool_.get(),
+ BoundNetLog()));
+ ASSERT_EQ(OK, callback1.WaitForResult());
+ handle1.Reset();
+
+ ASSERT_TRUE(pool_->HasGroup("a"));
+ EXPECT_EQ(0, pool_->NumConnectJobsInGroup("a"));
+ EXPECT_EQ(1, pool_->IdleSocketCountInGroup("a"));
+
+ pool_->RequestSockets("a", &params_, 2, BoundNetLog());
+
+ EXPECT_EQ(1, pool_->NumConnectJobsInGroup("a"));
+ EXPECT_EQ(1, pool_->IdleSocketCountInGroup("a"));
+}
+
+TEST_F(ClientSocketPoolBaseTest, RequestSocketsCountActiveSockets) {
+ CreatePool(4, 4);
+ connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
+
+ ClientSocketHandle handle1;
+ TestCompletionCallback callback1;
+ EXPECT_EQ(ERR_IO_PENDING, handle1.Init("a",
+ params_,
+ kDefaultPriority,
+ &callback1,
+ pool_.get(),
+ BoundNetLog()));
+ ASSERT_EQ(OK, callback1.WaitForResult());
+
+ ASSERT_TRUE(pool_->HasGroup("a"));
+ EXPECT_EQ(0, pool_->NumConnectJobsInGroup("a"));
+ EXPECT_EQ(0, pool_->IdleSocketCountInGroup("a"));
+ EXPECT_EQ(1, pool_->NumActiveSocketsInGroup("a"));
+
+ pool_->RequestSockets("a", &params_, 2, BoundNetLog());
+
+ EXPECT_EQ(1, pool_->NumConnectJobsInGroup("a"));
+ EXPECT_EQ(0, pool_->IdleSocketCountInGroup("a"));
+ EXPECT_EQ(1, pool_->NumActiveSocketsInGroup("a"));
+}
+
+TEST_F(ClientSocketPoolBaseTest, RequestSocketsSynchronous) {
+ CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+ connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
+
+ pool_->RequestSockets("a", &params_, kDefaultMaxSocketsPerGroup,
+ BoundNetLog());
+
+ ASSERT_TRUE(pool_->HasGroup("a"));
+ EXPECT_EQ(0, pool_->NumConnectJobsInGroup("a"));
+ EXPECT_EQ(kDefaultMaxSocketsPerGroup, pool_->IdleSocketCountInGroup("a"));
+
+ pool_->RequestSockets("b", &params_, kDefaultMaxSocketsPerGroup,
+ BoundNetLog());
+
+ EXPECT_EQ(0, pool_->NumConnectJobsInGroup("b"));
+ EXPECT_EQ(kDefaultMaxSocketsPerGroup, pool_->IdleSocketCountInGroup("b"));
+}
+
+TEST_F(ClientSocketPoolBaseTest, RequestSocketsMultipleTimesDoesNothing) {
+ CreatePool(4, 4);
+ connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
+
+ pool_->RequestSockets("a", &params_, 2, BoundNetLog());
+
+ ASSERT_TRUE(pool_->HasGroup("a"));
+ EXPECT_EQ(2, pool_->NumConnectJobsInGroup("a"));
+ EXPECT_EQ(0, pool_->IdleSocketCountInGroup("a"));
+
+ pool_->RequestSockets("a", &params_, 2, BoundNetLog());
+ EXPECT_EQ(2, pool_->NumConnectJobsInGroup("a"));
+ EXPECT_EQ(0, pool_->IdleSocketCountInGroup("a"));
+
+ ClientSocketHandle handle1;
+ TestCompletionCallback callback1;
+ EXPECT_EQ(ERR_IO_PENDING, handle1.Init("a",
+ params_,
+ kDefaultPriority,
+ &callback1,
+ pool_.get(),
+ BoundNetLog()));
+ ASSERT_EQ(OK, callback1.WaitForResult());
+
+ ClientSocketHandle handle2;
+ TestCompletionCallback callback2;
+ int rv = handle2.Init("a",
+ params_,
+ kDefaultPriority,
+ &callback2,
+ pool_.get(),
+ BoundNetLog());
+ if (rv != OK) {
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ EXPECT_EQ(OK, callback2.WaitForResult());
+ }
+
+ handle1.Reset();
+ handle2.Reset();
+
+ EXPECT_EQ(2, pool_->IdleSocketCountInGroup("a"));
+
+ pool_->RequestSockets("a", &params_, 2, BoundNetLog());
+ EXPECT_EQ(0, pool_->NumConnectJobsInGroup("a"));
+ EXPECT_EQ(2, pool_->IdleSocketCountInGroup("a"));
+}
+
+TEST_F(ClientSocketPoolBaseTest, RequestSocketsDifferentNumSockets) {
+ CreatePool(4, 4);
+ connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
+
+ pool_->RequestSockets("a", &params_, 1, BoundNetLog());
+
+ ASSERT_TRUE(pool_->HasGroup("a"));
+ EXPECT_EQ(1, pool_->NumConnectJobsInGroup("a"));
+ EXPECT_EQ(0, pool_->IdleSocketCountInGroup("a"));
+
+ pool_->RequestSockets("a", &params_, 2, BoundNetLog());
+ EXPECT_EQ(2, pool_->NumConnectJobsInGroup("a"));
+ EXPECT_EQ(0, pool_->IdleSocketCountInGroup("a"));
+
+ pool_->RequestSockets("a", &params_, 3, BoundNetLog());
+ EXPECT_EQ(3, pool_->NumConnectJobsInGroup("a"));
+ EXPECT_EQ(0, pool_->IdleSocketCountInGroup("a"));
+
+ pool_->RequestSockets("a", &params_, 1, BoundNetLog());
+ EXPECT_EQ(3, pool_->NumConnectJobsInGroup("a"));
+ EXPECT_EQ(0, pool_->IdleSocketCountInGroup("a"));
+}
+
+TEST_F(ClientSocketPoolBaseTest, PreconnectJobsTakenByNormalRequests) {
+ CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+ connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
+
+ pool_->RequestSockets("a", &params_, 1, BoundNetLog());
+
+ ASSERT_TRUE(pool_->HasGroup("a"));
+ EXPECT_EQ(1, pool_->NumConnectJobsInGroup("a"));
+ EXPECT_EQ(0, pool_->IdleSocketCountInGroup("a"));
+
+ ClientSocketHandle handle1;
+ TestCompletionCallback callback1;
+ EXPECT_EQ(ERR_IO_PENDING, handle1.Init("a",
+ params_,
+ kDefaultPriority,
+ &callback1,
+ pool_.get(),
+ BoundNetLog()));
+
+ EXPECT_EQ(1, pool_->NumConnectJobsInGroup("a"));
+ EXPECT_EQ(0, pool_->IdleSocketCountInGroup("a"));
+
+ ASSERT_EQ(OK, callback1.WaitForResult());
+
+ handle1.Reset();
+
+ EXPECT_EQ(1, pool_->IdleSocketCountInGroup("a"));
+}
+
} // namespace
} // namespace net
diff --git a/net/socket/socks_client_socket_pool.cc b/net/socket/socks_client_socket_pool.cc
index cae333a..6d9e814c 100644
--- a/net/socket/socks_client_socket_pool.cc
+++ b/net/socket/socks_client_socket_pool.cc
@@ -207,6 +207,17 @@ int SOCKSClientSocketPool::RequestSocket(const std::string& group_name,
handle, callback, net_log);
}
+void SOCKSClientSocketPool::RequestSockets(
+ const std::string& group_name,
+ const void* params,
+ int num_sockets,
+ const BoundNetLog& net_log) {
+ const scoped_refptr<SOCKSSocketParams>* casted_params =
+ static_cast<const scoped_refptr<SOCKSSocketParams>*>(params);
+
+ base_.RequestSockets(group_name, *casted_params, num_sockets, net_log);
+}
+
void SOCKSClientSocketPool::CancelRequest(const std::string& group_name,
ClientSocketHandle* handle) {
base_.CancelRequest(group_name, handle);
diff --git a/net/socket/socks_client_socket_pool.h b/net/socket/socks_client_socket_pool.h
index a257d0f..96eb4cf 100644
--- a/net/socket/socks_client_socket_pool.h
+++ b/net/socket/socks_client_socket_pool.h
@@ -122,6 +122,11 @@ class SOCKSClientSocketPool : public ClientSocketPool {
CompletionCallback* callback,
const BoundNetLog& net_log);
+ virtual void RequestSockets(const std::string& group_name,
+ const void* params,
+ int num_sockets,
+ const BoundNetLog& net_log);
+
virtual void CancelRequest(const std::string& group_name,
ClientSocketHandle* handle);
diff --git a/net/socket/ssl_client_socket_pool.cc b/net/socket/ssl_client_socket_pool.cc
index ea0a177..22d921a 100644
--- a/net/socket/ssl_client_socket_pool.cc
+++ b/net/socket/ssl_client_socket_pool.cc
@@ -428,6 +428,17 @@ int SSLClientSocketPool::RequestSocket(const std::string& group_name,
handle, callback, net_log);
}
+void SSLClientSocketPool::RequestSockets(
+ const std::string& group_name,
+ const void* params,
+ int num_sockets,
+ const BoundNetLog& net_log) {
+ const scoped_refptr<SSLSocketParams>* casted_params =
+ static_cast<const scoped_refptr<SSLSocketParams>*>(params);
+
+ base_.RequestSockets(group_name, *casted_params, num_sockets, net_log);
+}
+
void SSLClientSocketPool::CancelRequest(const std::string& group_name,
ClientSocketHandle* handle) {
base_.CancelRequest(group_name, handle);
diff --git a/net/socket/ssl_client_socket_pool.h b/net/socket/ssl_client_socket_pool.h
index 2c75e03..935a0d7 100644
--- a/net/socket/ssl_client_socket_pool.h
+++ b/net/socket/ssl_client_socket_pool.h
@@ -183,6 +183,11 @@ class SSLClientSocketPool : public ClientSocketPool,
CompletionCallback* callback,
const BoundNetLog& net_log);
+ virtual void RequestSockets(const std::string& group_name,
+ const void* params,
+ int num_sockets,
+ const BoundNetLog& net_log);
+
virtual void CancelRequest(const std::string& group_name,
ClientSocketHandle* handle);
diff --git a/net/socket/tcp_client_socket_pool.cc b/net/socket/tcp_client_socket_pool.cc
index bea4dff..f11ae25 100644
--- a/net/socket/tcp_client_socket_pool.cc
+++ b/net/socket/tcp_client_socket_pool.cc
@@ -231,6 +231,26 @@ int TCPClientSocketPool::RequestSocket(
callback, net_log);
}
+void TCPClientSocketPool::RequestSockets(
+ const std::string& group_name,
+ const void* params,
+ int num_sockets,
+ const BoundNetLog& net_log) {
+ const scoped_refptr<TCPSocketParams>* casted_params =
+ static_cast<const scoped_refptr<TCPSocketParams>*>(params);
+
+ if (net_log.IsLoggingAll()) {
+ // TODO(eroman): Split out the host and port parameters.
+ net_log.AddEvent(
+ NetLog::TYPE_TCP_CLIENT_SOCKET_POOL_REQUESTED_SOCKETS,
+ new NetLogStringParameter(
+ "host_and_port",
+ casted_params->get()->destination().host_port_pair().ToString()));
+ }
+
+ base_.RequestSockets(group_name, *casted_params, num_sockets, net_log);
+}
+
void TCPClientSocketPool::CancelRequest(
const std::string& group_name,
ClientSocketHandle* handle) {
diff --git a/net/socket/tcp_client_socket_pool.h b/net/socket/tcp_client_socket_pool.h
index 4837468..08f0634f 100644
--- a/net/socket/tcp_client_socket_pool.h
+++ b/net/socket/tcp_client_socket_pool.h
@@ -131,6 +131,11 @@ class TCPClientSocketPool : public ClientSocketPool {
CompletionCallback* callback,
const BoundNetLog& net_log);
+ virtual void RequestSockets(const std::string& group_name,
+ const void* params,
+ int num_sockets,
+ const BoundNetLog& net_log);
+
virtual void CancelRequest(const std::string& group_name,
ClientSocketHandle* handle);