blob: 0afe53e65ae890619ffc7d769174b0c91fc04369 [file] [log] [blame]
[email protected]ff579d42009-06-24 15:47:021// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/socket/client_socket_pool_base.h"
6
7#include "base/compiler_specific.h"
8#include "base/message_loop.h"
9#include "base/stl_util-inl.h"
10#include "base/time.h"
11#include "net/base/net_errors.h"
12#include "net/socket/client_socket_handle.h"
13
14using base::TimeDelta;
15
16namespace {
17
18// The timeout value, in seconds, used to clean up idle sockets that can't be
19// reused.
20//
21// Note: It's important to close idle sockets that have received data as soon
22// as possible because the received data may cause BSOD on Windows XP under
23// some conditions. See http://crbug.com/4606.
24const int kCleanupInterval = 10; // DO NOT INCREASE THIS TIMEOUT.
25
26// The maximum duration, in seconds, to keep idle persistent sockets alive.
27const int kIdleTimeout = 300; // 5 minutes.
28
29} // namespace
30
31namespace net {
32
[email protected]2ab05b52009-07-01 23:57:5833ConnectJob::ConnectJob(const std::string& group_name,
34 const ClientSocketHandle* key_handle,
35 Delegate* delegate)
36 : group_name_(group_name),
37 key_handle_(key_handle),
38 delegate_(delegate),
39 load_state_(LOAD_STATE_IDLE) {
40 DCHECK(!group_name.empty());
41 DCHECK(key_handle);
42 DCHECK(delegate);
43}
44
45ConnectJob::~ConnectJob() {}
46
[email protected]ff579d42009-06-24 15:47:0247ClientSocketPoolBase::ClientSocketPoolBase(
48 int max_sockets_per_group,
[email protected]ff579d42009-06-24 15:47:0249 ConnectJobFactory* connect_job_factory)
50 : idle_socket_count_(0),
51 max_sockets_per_group_(max_sockets_per_group),
[email protected]ff579d42009-06-24 15:47:0252 connect_job_factory_(connect_job_factory) {}
53
54ClientSocketPoolBase::~ClientSocketPoolBase() {
55 // Clean up any idle sockets. Assert that we have no remaining active
56 // sockets or pending requests. They should have all been cleaned up prior
57 // to the manager being destroyed.
58 CloseIdleSockets();
59 DCHECK(group_map_.empty());
60 DCHECK(connect_job_map_.empty());
61}
62
63// InsertRequestIntoQueue inserts the request into the queue based on
64// priority. Highest priorities are closest to the front. Older requests are
65// prioritized over requests of equal priority.
66//
67// static
68void ClientSocketPoolBase::InsertRequestIntoQueue(
69 const Request& r, RequestQueue* pending_requests) {
70 RequestQueue::iterator it = pending_requests->begin();
71 while (it != pending_requests->end() && r.priority <= it->priority)
72 ++it;
73 pending_requests->insert(it, r);
74}
75
76int ClientSocketPoolBase::RequestSocket(
77 const std::string& group_name,
78 const HostResolver::RequestInfo& resolve_info,
79 int priority,
80 ClientSocketHandle* handle,
81 CompletionCallback* callback) {
82 DCHECK(!resolve_info.hostname().empty());
83 DCHECK_GE(priority, 0);
[email protected]ab838892009-06-30 18:49:0584 DCHECK(callback);
[email protected]ff579d42009-06-24 15:47:0285 Group& group = group_map_[group_name];
86
[email protected]ff579d42009-06-24 15:47:0287 // Can we make another active socket now?
[email protected]2ab05b52009-07-01 23:57:5888 if (!group.HasAvailableSocketSlot(max_sockets_per_group_)) {
[email protected]ff579d42009-06-24 15:47:0289 CHECK(callback);
[email protected]ab838892009-06-30 18:49:0590 Request r(handle, callback, priority, resolve_info);
[email protected]ff579d42009-06-24 15:47:0291 InsertRequestIntoQueue(r, &group.pending_requests);
92 return ERR_IO_PENDING;
93 }
94
[email protected]ff579d42009-06-24 15:47:0295 while (!group.idle_sockets.empty()) {
96 IdleSocket idle_socket = group.idle_sockets.back();
97 group.idle_sockets.pop_back();
98 DecrementIdleCount();
99 if (idle_socket.socket->IsConnectedAndIdle()) {
100 // We found one we can reuse!
[email protected]2ab05b52009-07-01 23:57:58101 HandOutSocket(idle_socket.socket, true /* reuse */, handle, &group);
[email protected]ff579d42009-06-24 15:47:02102 return OK;
103 }
104 delete idle_socket.socket;
105 }
106
107 // We couldn't find a socket to reuse, so allocate and connect a new one.
108
109 CHECK(callback);
[email protected]ab838892009-06-30 18:49:05110 Request r(handle, callback, priority, resolve_info);
[email protected]2ab05b52009-07-01 23:57:58111 scoped_ptr<ConnectJob> connect_job(
112 connect_job_factory_->NewConnectJob(group_name, r, this));
[email protected]ff579d42009-06-24 15:47:02113
[email protected]2ab05b52009-07-01 23:57:58114 int rv = connect_job->Connect();
115 if (rv == OK) {
116 HandOutSocket(connect_job->ReleaseSocket(), false /* not reused */,
117 handle, &group);
118 } else if (rv == ERR_IO_PENDING) {
119 group.connecting_requests[handle] = r;
120 CHECK(!ContainsKey(connect_job_map_, handle));
121 connect_job_map_[handle] = connect_job.release();
122 } else {
123 if (group.IsEmpty())
124 group_map_.erase(group_name);
125 }
[email protected]ff579d42009-06-24 15:47:02126
[email protected]2ab05b52009-07-01 23:57:58127 return rv;
[email protected]ff579d42009-06-24 15:47:02128}
129
130void ClientSocketPoolBase::CancelRequest(const std::string& group_name,
131 const ClientSocketHandle* handle) {
132 CHECK(ContainsKey(group_map_, group_name));
133
134 Group& group = group_map_[group_name];
135
[email protected]ff579d42009-06-24 15:47:02136 // Search pending_requests for matching handle.
137 RequestQueue::iterator it = group.pending_requests.begin();
138 for (; it != group.pending_requests.end(); ++it) {
139 if (it->handle == handle) {
140 group.pending_requests.erase(it);
141 return;
142 }
143 }
144
145 // It's invalid to cancel a non-existent request.
146 CHECK(ContainsKey(group.connecting_requests, handle));
147
148 RequestMap::iterator map_it = group.connecting_requests.find(handle);
149 if (map_it != group.connecting_requests.end()) {
150 RemoveConnectJob(handle);
151 group.connecting_requests.erase(map_it);
[email protected]2ab05b52009-07-01 23:57:58152 OnAvailableSocketSlot(group_name, &group);
[email protected]ff579d42009-06-24 15:47:02153 }
154}
155
156void ClientSocketPoolBase::ReleaseSocket(const std::string& group_name,
157 ClientSocket* socket) {
158 // Run this asynchronously to allow the caller to finish before we let
159 // another to begin doing work. This also avoids nasty recursion issues.
160 // NOTE: We cannot refer to the handle argument after this method returns.
161 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
162 this, &ClientSocketPoolBase::DoReleaseSocket, group_name, socket));
163}
164
165void ClientSocketPoolBase::CloseIdleSockets() {
166 CleanupIdleSockets(true);
167}
168
169int ClientSocketPoolBase::IdleSocketCountInGroup(
170 const std::string& group_name) const {
171 GroupMap::const_iterator i = group_map_.find(group_name);
172 CHECK(i != group_map_.end());
173
174 return i->second.idle_sockets.size();
175}
176
177LoadState ClientSocketPoolBase::GetLoadState(
178 const std::string& group_name,
179 const ClientSocketHandle* handle) const {
180 if (!ContainsKey(group_map_, group_name)) {
181 NOTREACHED() << "ClientSocketPool does not contain group: " << group_name
182 << " for handle: " << handle;
183 return LOAD_STATE_IDLE;
184 }
185
186 // Can't use operator[] since it is non-const.
187 const Group& group = group_map_.find(group_name)->second;
188
189 // Search connecting_requests for matching handle.
190 RequestMap::const_iterator map_it = group.connecting_requests.find(handle);
191 if (map_it != group.connecting_requests.end()) {
[email protected]ab838892009-06-30 18:49:05192 ConnectJobMap::const_iterator job_it = connect_job_map_.find(handle);
193 if (job_it == connect_job_map_.end()) {
194 NOTREACHED();
195 return LOAD_STATE_IDLE;
196 }
197 return job_it->second->load_state();
[email protected]ff579d42009-06-24 15:47:02198 }
199
200 // Search pending_requests for matching handle.
201 RequestQueue::const_iterator it = group.pending_requests.begin();
202 for (; it != group.pending_requests.end(); ++it) {
203 if (it->handle == handle) {
[email protected]ff579d42009-06-24 15:47:02204 // TODO(wtc): Add a state for being on the wait list.
205 // See http://www.crbug.com/5077.
206 return LOAD_STATE_IDLE;
207 }
208 }
209
210 NOTREACHED();
211 return LOAD_STATE_IDLE;
212}
213
214bool ClientSocketPoolBase::IdleSocket::ShouldCleanup(
215 base::TimeTicks now) const {
216 bool timed_out = (now - start_time) >=
217 base::TimeDelta::FromSeconds(kIdleTimeout);
218 return timed_out || !socket->IsConnectedAndIdle();
219}
220
221void ClientSocketPoolBase::CleanupIdleSockets(bool force) {
222 if (idle_socket_count_ == 0)
223 return;
224
225 // Current time value. Retrieving it once at the function start rather than
226 // inside the inner loop, since it shouldn't change by any meaningful amount.
227 base::TimeTicks now = base::TimeTicks::Now();
228
229 GroupMap::iterator i = group_map_.begin();
230 while (i != group_map_.end()) {
231 Group& group = i->second;
232
233 std::deque<IdleSocket>::iterator j = group.idle_sockets.begin();
234 while (j != group.idle_sockets.end()) {
235 if (force || j->ShouldCleanup(now)) {
236 delete j->socket;
237 j = group.idle_sockets.erase(j);
238 DecrementIdleCount();
239 } else {
240 ++j;
241 }
242 }
243
244 // Delete group if no longer needed.
[email protected]2ab05b52009-07-01 23:57:58245 if (group.IsEmpty()) {
[email protected]ff579d42009-06-24 15:47:02246 CHECK(group.pending_requests.empty());
[email protected]ff579d42009-06-24 15:47:02247 group_map_.erase(i++);
248 } else {
249 ++i;
250 }
251 }
252}
253
254void ClientSocketPoolBase::IncrementIdleCount() {
255 if (++idle_socket_count_ == 1)
256 timer_.Start(TimeDelta::FromSeconds(kCleanupInterval), this,
257 &ClientSocketPoolBase::OnCleanupTimerFired);
258}
259
260void ClientSocketPoolBase::DecrementIdleCount() {
261 if (--idle_socket_count_ == 0)
262 timer_.Stop();
263}
264
265void ClientSocketPoolBase::DoReleaseSocket(const std::string& group_name,
266 ClientSocket* socket) {
267 GroupMap::iterator i = group_map_.find(group_name);
268 CHECK(i != group_map_.end());
269
270 Group& group = i->second;
271
272 CHECK(group.active_socket_count > 0);
[email protected]2ab05b52009-07-01 23:57:58273 group.active_socket_count--;
[email protected]ff579d42009-06-24 15:47:02274
275 const bool can_reuse = socket->IsConnectedAndIdle();
276 if (can_reuse) {
277 IdleSocket idle_socket;
278 idle_socket.socket = socket;
279 idle_socket.start_time = base::TimeTicks::Now();
280
281 group.idle_sockets.push_back(idle_socket);
282 IncrementIdleCount();
283 } else {
284 delete socket;
285 }
286
[email protected]2ab05b52009-07-01 23:57:58287 OnAvailableSocketSlot(group_name, &group);
[email protected]ff579d42009-06-24 15:47:02288}
289
[email protected]2ab05b52009-07-01 23:57:58290void ClientSocketPoolBase::OnConnectJobComplete(int result, ConnectJob* job) {
291 DCHECK_NE(ERR_IO_PENDING, result);
292 const std::string group_name = job->group_name();
[email protected]ff579d42009-06-24 15:47:02293 GroupMap::iterator group_it = group_map_.find(group_name);
294 CHECK(group_it != group_map_.end());
295 Group& group = group_it->second;
296
[email protected]ff579d42009-06-24 15:47:02297 RequestMap* request_map = &group.connecting_requests;
298
[email protected]2ab05b52009-07-01 23:57:58299 RequestMap::iterator it = request_map->find(job->key_handle());
[email protected]ff579d42009-06-24 15:47:02300 CHECK(it != request_map->end());
[email protected]2ab05b52009-07-01 23:57:58301 ClientSocketHandle* const handle = it->second.handle;
302 CompletionCallback* const callback = it->second.callback;
[email protected]ff579d42009-06-24 15:47:02303 request_map->erase(it);
[email protected]2ab05b52009-07-01 23:57:58304 DCHECK_EQ(handle, job->key_handle());
[email protected]ff579d42009-06-24 15:47:02305
[email protected]8024ddc2009-07-02 00:28:39306 scoped_ptr<ClientSocket> socket(job->ReleaseSocket());
[email protected]2ab05b52009-07-01 23:57:58307 RemoveConnectJob(job->key_handle());
308
309 if (result != OK) {
[email protected]8e12ae02009-07-02 16:15:04310 DCHECK(!socket.get());
[email protected]2ab05b52009-07-01 23:57:58311 callback->Run(result); // |group| is not necessarily valid after this.
312 // |group| may be invalid after the callback, we need to search
313 // |group_map_| again.
314 MaybeOnAvailableSocketSlot(group_name);
[email protected]ff579d42009-06-24 15:47:02315 } else {
[email protected]8024ddc2009-07-02 00:28:39316 HandOutSocket(socket.release(), false /* not reused */, handle, &group);
[email protected]2ab05b52009-07-01 23:57:58317 callback->Run(result);
[email protected]ff579d42009-06-24 15:47:02318 }
[email protected]ff579d42009-06-24 15:47:02319}
320
321void ClientSocketPoolBase::RemoveConnectJob(
322 const ClientSocketHandle* handle) {
323 ConnectJobMap::iterator it = connect_job_map_.find(handle);
324 CHECK(it != connect_job_map_.end());
325 delete it->second;
326 connect_job_map_.erase(it);
327}
328
[email protected]2ab05b52009-07-01 23:57:58329void ClientSocketPoolBase::MaybeOnAvailableSocketSlot(
330 const std::string& group_name) {
331 GroupMap::iterator it = group_map_.find(group_name);
332 if (it != group_map_.end()) {
333 Group& group = it->second;
334 if (group.HasAvailableSocketSlot(max_sockets_per_group_))
335 OnAvailableSocketSlot(group_name, &group);
336 }
337}
[email protected]ff579d42009-06-24 15:47:02338
[email protected]2ab05b52009-07-01 23:57:58339void ClientSocketPoolBase::OnAvailableSocketSlot(const std::string& group_name,
340 Group* group) {
[email protected]ff579d42009-06-24 15:47:02341 if (!group->pending_requests.empty()) {
342 ProcessPendingRequest(group_name, group);
343 // |group| may no longer be valid after this point. Be careful not to
344 // access it again.
[email protected]2ab05b52009-07-01 23:57:58345 } else if (group->IsEmpty()) {
[email protected]ff579d42009-06-24 15:47:02346 // Delete |group| if no longer needed. |group| will no longer be valid.
[email protected]ff579d42009-06-24 15:47:02347 group_map_.erase(group_name);
[email protected]ff579d42009-06-24 15:47:02348 }
349}
350
351void ClientSocketPoolBase::ProcessPendingRequest(const std::string& group_name,
352 Group* group) {
353 Request r = group->pending_requests.front();
354 group->pending_requests.pop_front();
355
356 int rv = RequestSocket(
357 group_name, r.resolve_info, r.priority, r.handle, r.callback);
358
[email protected]2ab05b52009-07-01 23:57:58359 if (rv != ERR_IO_PENDING) {
[email protected]ff579d42009-06-24 15:47:02360 r.callback->Run(rv);
[email protected]2ab05b52009-07-01 23:57:58361 if (rv != OK) {
362 // |group| may be invalid after the callback, we need to search
363 // |group_map_| again.
364 MaybeOnAvailableSocketSlot(group_name);
365 }
366 }
367}
368
369void ClientSocketPoolBase::HandOutSocket(
370 ClientSocket* socket,
371 bool reused,
372 ClientSocketHandle* handle,
373 Group* group) {
374 DCHECK(socket);
375 handle->set_socket(socket);
376 handle->set_is_reused(reused);
377 group->active_socket_count++;
[email protected]ff579d42009-06-24 15:47:02378}
379
380} // namespace net