1 // Copyright (c) 2009 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 // A ClientSocketPoolBase is used to restrict the number of sockets open at 6 // a time. It also maintains a list of idle persistent sockets for reuse. 7 // Subclasses of ClientSocketPool should compose ClientSocketPoolBase to handle 8 // the core logic of (1) restricting the number of active (connected or 9 // connecting) sockets per "group" (generally speaking, the hostname), (2) 10 // maintaining a per-group list of idle, persistent sockets for reuse, and (3) 11 // limiting the total number of active sockets in the system. 12 // 13 // ClientSocketPoolBase abstracts socket connection details behind ConnectJob, 14 // ConnectJobFactory, and SocketParams. When a socket "slot" becomes available, 15 // the ClientSocketPoolBase will ask the ConnectJobFactory to create a 16 // ConnectJob with a SocketParams. Subclasses of ClientSocketPool should 17 // implement their socket specific connection by subclassing ConnectJob and 18 // implementing ConnectJob::ConnectInternal(). They can control the parameters 19 // passed to each new ConnectJob instance via their ConnectJobFactory subclass 20 // and templated SocketParams parameter. 21 // 22 #ifndef NET_SOCKET_CLIENT_SOCKET_POOL_BASE_H_ 23 #define NET_SOCKET_CLIENT_SOCKET_POOL_BASE_H_ 24 25 #include <deque> 26 #include <map> 27 #include <set> 28 #include <string> 29 30 #include "base/basictypes.h" 31 #include "base/ref_counted.h" 32 #include "base/scoped_ptr.h" 33 #include "base/time.h" 34 #include "base/timer.h" 35 #include "net/base/address_list.h" 36 #include "net/base/completion_callback.h" 37 #include "net/base/load_log.h" 38 #include "net/base/load_states.h" 39 #include "net/base/net_errors.h" 40 #include "net/base/network_change_notifier.h" 41 #include "net/base/request_priority.h" 42 #include "net/socket/client_socket.h" 43 #include "net/socket/client_socket_pool.h" 44 45 namespace net { 46 47 class ClientSocketHandle; 48 49 // ConnectJob provides an abstract interface for "connecting" a socket. 50 // The connection may involve host resolution, tcp connection, ssl connection, 51 // etc. 52 class ConnectJob { 53 public: 54 class Delegate { 55 public: 56 Delegate() {} 57 virtual ~Delegate() {} 58 59 // Alerts the delegate that the connection completed. 60 virtual void OnConnectJobComplete(int result, ConnectJob* job) = 0; 61 62 private: 63 DISALLOW_COPY_AND_ASSIGN(Delegate); 64 }; 65 66 // A |timeout_duration| of 0 corresponds to no timeout. 67 ConnectJob(const std::string& group_name, 68 base::TimeDelta timeout_duration, 69 Delegate* delegate, 70 LoadLog* load_log); 71 virtual ~ConnectJob(); 72 73 // Accessors 74 const std::string& group_name() const { return group_name_; } 75 LoadLog* load_log() { return load_log_; } 76 77 // Releases |socket_| to the client. On connection error, this should return 78 // NULL. 79 ClientSocket* ReleaseSocket() { return socket_.release(); } 80 81 // Begins connecting the socket. Returns OK on success, ERR_IO_PENDING if it 82 // cannot complete synchronously without blocking, or another net error code 83 // on error. In asynchronous completion, the ConnectJob will notify 84 // |delegate_| via OnConnectJobComplete. In both asynchronous and synchronous 85 // completion, ReleaseSocket() can be called to acquire the connected socket 86 // if it succeeded. 87 int Connect(); 88 89 virtual LoadState GetLoadState() const = 0; 90 91 protected: 92 void set_socket(ClientSocket* socket) { socket_.reset(socket); } 93 ClientSocket* socket() { return socket_.get(); } 94 void NotifyDelegateOfCompletion(int rv); 95 96 private: 97 virtual int ConnectInternal() = 0; 98 99 // Alerts the delegate that the ConnectJob has timed out. 100 void OnTimeout(); 101 102 const std::string group_name_; 103 const base::TimeDelta timeout_duration_; 104 // Timer to abort jobs that take too long. 105 base::OneShotTimer<ConnectJob> timer_; 106 Delegate* delegate_; 107 scoped_ptr<ClientSocket> socket_; 108 scoped_refptr<LoadLog> load_log_; 109 110 DISALLOW_COPY_AND_ASSIGN(ConnectJob); 111 }; 112 113 namespace internal { 114 115 // ClientSocketPoolBaseHelper is an internal class that implements almost all 116 // the functionality from ClientSocketPoolBase without using templates. 117 // ClientSocketPoolBase adds templated definitions built on top of 118 // ClientSocketPoolBaseHelper. This class is not for external use, please use 119 // ClientSocketPoolBase instead. 120 class ClientSocketPoolBaseHelper 121 : public base::RefCounted<ClientSocketPoolBaseHelper>, 122 public ConnectJob::Delegate, 123 public NetworkChangeNotifier::Observer { 124 public: 125 class Request { 126 public: 127 Request(ClientSocketHandle* handle, 128 CompletionCallback* callback, 129 RequestPriority priority, 130 LoadLog* load_log) 131 : handle_(handle), callback_(callback), priority_(priority), 132 load_log_(load_log) {} 133 134 virtual ~Request() {} 135 136 ClientSocketHandle* handle() const { return handle_; } 137 CompletionCallback* callback() const { return callback_; } 138 RequestPriority priority() const { return priority_; } 139 LoadLog* load_log() const { return load_log_.get(); } 140 141 private: 142 ClientSocketHandle* const handle_; 143 CompletionCallback* const callback_; 144 const RequestPriority priority_; 145 const scoped_refptr<LoadLog> load_log_; 146 147 DISALLOW_COPY_AND_ASSIGN(Request); 148 }; 149 150 class ConnectJobFactory { 151 public: 152 ConnectJobFactory() {} 153 virtual ~ConnectJobFactory() {} 154 155 virtual ConnectJob* NewConnectJob( 156 const std::string& group_name, 157 const Request& request, 158 ConnectJob::Delegate* delegate, 159 LoadLog* load_log) const = 0; 160 161 private: 162 DISALLOW_COPY_AND_ASSIGN(ConnectJobFactory); 163 }; 164 165 ClientSocketPoolBaseHelper( 166 int max_sockets, 167 int max_sockets_per_group, 168 base::TimeDelta unused_idle_socket_timeout, 169 base::TimeDelta used_idle_socket_timeout, 170 ConnectJobFactory* connect_job_factory, 171 NetworkChangeNotifier* network_change_notifier); 172 173 // See ClientSocketPool::RequestSocket for documentation on this function. 174 // Note that |request| must be heap allocated. If ERR_IO_PENDING is returned, 175 // then ClientSocketPoolBaseHelper takes ownership of |request|. 176 int RequestSocket(const std::string& group_name, const Request* request); 177 178 // See ClientSocketPool::CancelRequest for documentation on this function. 179 void CancelRequest(const std::string& group_name, 180 const ClientSocketHandle* handle); 181 182 // See ClientSocketPool::ReleaseSocket for documentation on this function. 183 void ReleaseSocket(const std::string& group_name, 184 ClientSocket* socket); 185 186 // See ClientSocketPool::CloseIdleSockets for documentation on this function. 187 void CloseIdleSockets(); 188 189 // See ClientSocketPool::IdleSocketCount() for documentation on this function. 190 int idle_socket_count() const { 191 return idle_socket_count_; 192 } 193 194 // See ClientSocketPool::IdleSocketCountInGroup() for documentation on this 195 // function. 196 int IdleSocketCountInGroup(const std::string& group_name) const; 197 198 // See ClientSocketPool::GetLoadState() for documentation on this function. 199 LoadState GetLoadState(const std::string& group_name, 200 const ClientSocketHandle* handle) const; 201 202 // ConnectJob::Delegate methods: 203 virtual void OnConnectJobComplete(int result, ConnectJob* job); 204 205 // NetworkChangeNotifier::Observer methods: 206 virtual void OnIPAddressChanged(); 207 208 // For testing. 209 bool may_have_stalled_group() const { return may_have_stalled_group_; } 210 211 int NumConnectJobsInGroup(const std::string& group_name) const { 212 return group_map_.find(group_name)->second.jobs.size(); 213 } 214 215 // Closes all idle sockets if |force| is true. Else, only closes idle 216 // sockets that timed out or can't be reused. Made public for testing. 217 void CleanupIdleSockets(bool force); 218 219 private: 220 friend class base::RefCounted<ClientSocketPoolBaseHelper>; 221 222 ~ClientSocketPoolBaseHelper(); 223 224 // Entry for a persistent socket which became idle at time |start_time|. 225 struct IdleSocket { 226 IdleSocket() : socket(NULL), used(false) {} 227 ClientSocket* socket; 228 base::TimeTicks start_time; 229 bool used; // Indicates whether or not the socket has been used yet. 230 231 // An idle socket should be removed if it can't be reused, or has been idle 232 // for too long. |now| is the current time value (TimeTicks::Now()). 233 // |timeout| is the length of time to wait before timing out an idle socket. 234 // 235 // An idle socket can't be reused if it is disconnected or has received 236 // data unexpectedly (hence no longer idle). The unread data would be 237 // mistaken for the beginning of the next response if we were to reuse the 238 // socket for a new request. 239 bool ShouldCleanup(base::TimeTicks now, base::TimeDelta timeout) const; 240 }; 241 242 typedef std::deque<const Request*> RequestQueue; 243 typedef std::map<const ClientSocketHandle*, const Request*> RequestMap; 244 245 // A Group is allocated per group_name when there are idle sockets or pending 246 // requests. Otherwise, the Group object is removed from the map. 247 // |active_socket_count| tracks the number of sockets held by clients. Of 248 // this number of sockets held by clients, some of them may be released soon, 249 // since ReleaseSocket() was called of them, but the DoReleaseSocket() task 250 // has not run yet for them. |num_releasing_sockets| tracks these values, 251 // which is useful for not starting up new ConnectJobs when sockets may become 252 // available really soon. 253 struct Group { 254 Group() : active_socket_count(0), num_releasing_sockets(0) {} 255 256 bool IsEmpty() const { 257 return active_socket_count == 0 && idle_sockets.empty() && jobs.empty() && 258 pending_requests.empty(); 259 } 260 261 bool HasAvailableSocketSlot(int max_sockets_per_group) const { 262 return active_socket_count + static_cast<int>(jobs.size()) < 263 max_sockets_per_group; 264 } 265 266 bool HasReleasingSockets() const { 267 return num_releasing_sockets > 0; 268 } 269 270 RequestPriority TopPendingPriority() const { 271 return pending_requests.front()->priority(); 272 } 273 274 std::deque<IdleSocket> idle_sockets; 275 std::set<const ConnectJob*> jobs; 276 RequestQueue pending_requests; 277 int active_socket_count; // number of active sockets used by clients 278 // Number of sockets being released within one loop through the MessageLoop. 279 int num_releasing_sockets; 280 }; 281 282 typedef std::map<std::string, Group> GroupMap; 283 284 typedef std::set<const ConnectJob*> ConnectJobSet; 285 286 static void InsertRequestIntoQueue(const Request* r, 287 RequestQueue* pending_requests); 288 static const Request* RemoveRequestFromQueue(RequestQueue::iterator it, 289 RequestQueue* pending_requests); 290 291 // Called when the number of idle sockets changes. 292 void IncrementIdleCount(); 293 void DecrementIdleCount(); 294 295 // Called via PostTask by ReleaseSocket. 296 void DoReleaseSocket(const std::string& group_name, ClientSocket* socket); 297 298 // Scans the group map for groups which have an available socket slot and 299 // at least one pending request. Returns number of groups found, and if found 300 // at least one, fills |group| and |group_name| with data of the stalled group 301 // having highest priority. 302 int FindTopStalledGroup(Group** group, std::string* group_name); 303 304 // Called when timer_ fires. This method scans the idle sockets removing 305 // sockets that timed out or can't be reused. 306 void OnCleanupTimerFired() { 307 CleanupIdleSockets(false); 308 } 309 310 // Removes |job| from |connect_job_set_|. Also updates |group| if non-NULL. 311 void RemoveConnectJob(const ConnectJob* job, Group* group); 312 313 // Same as OnAvailableSocketSlot except it looks up the Group first to see if 314 // it's there. 315 void MaybeOnAvailableSocketSlot(const std::string& group_name); 316 317 // Might delete the Group from |group_map_|. 318 void OnAvailableSocketSlot(const std::string& group_name, Group* group); 319 320 // Process a request from a group's pending_requests queue. 321 void ProcessPendingRequest(const std::string& group_name, Group* group); 322 323 // Assigns |socket| to |handle| and updates |group|'s counters appropriately. 324 void HandOutSocket(ClientSocket* socket, 325 bool reused, 326 ClientSocketHandle* handle, 327 base::TimeDelta time_idle, 328 Group* group); 329 330 // Adds |socket| to the list of idle sockets for |group|. |used| indicates 331 // whether or not the socket has previously been used. 332 void AddIdleSocket(ClientSocket* socket, bool used, Group* group); 333 334 // Iterates through |connect_job_map_|, canceling all ConnectJobs. 335 // Afterwards, it iterates through all groups and deletes them if they are no 336 // longer needed. 337 void CancelAllConnectJobs(); 338 339 // Returns true if we can't create any more sockets due to the total limit. 340 // TODO(phajdan.jr): Also take idle sockets into account. 341 bool ReachedMaxSocketsLimit() const; 342 343 GroupMap group_map_; 344 345 // Timer used to periodically prune idle sockets that timed out or can't be 346 // reused. 347 base::RepeatingTimer<ClientSocketPoolBaseHelper> timer_; 348 349 // The total number of idle sockets in the system. 350 int idle_socket_count_; 351 352 // Number of connecting sockets across all groups. 353 int connecting_socket_count_; 354 355 // Number of connected sockets we handed out across all groups. 356 int handed_out_socket_count_; 357 358 // The maximum total number of sockets. See ReachedMaxSocketsLimit. 359 const int max_sockets_; 360 361 // The maximum number of sockets kept per group. 362 const int max_sockets_per_group_; 363 364 // The time to wait until closing idle sockets. 365 const base::TimeDelta unused_idle_socket_timeout_; 366 const base::TimeDelta used_idle_socket_timeout_; 367 368 // Until the maximum number of sockets limit is reached, a group can only 369 // have pending requests if it exceeds the "max sockets per group" limit. 370 // 371 // This means when a socket is released, the only pending requests that can 372 // be started next belong to the same group. 373 // 374 // However once the |max_sockets_| limit is reached, this stops being true: 375 // groups can now have pending requests without having first reached the 376 // |max_sockets_per_group_| limit. So choosing the next request involves 377 // selecting the highest priority request across *all* groups. 378 // 379 // Since reaching the maximum number of sockets is an edge case, we make note 380 // of when it happens, and thus avoid doing the slower "scan all groups" 381 // in the common case. 382 bool may_have_stalled_group_; 383 384 const scoped_ptr<ConnectJobFactory> connect_job_factory_; 385 386 NetworkChangeNotifier* const network_change_notifier_; 387 }; 388 389 } // namespace internal 390 391 // The maximum duration, in seconds, to keep unused idle persistent sockets 392 // alive. 393 // TODO(willchan): Change this timeout after getting histogram data on how 394 // long it should be. 395 static const int kUnusedIdleSocketTimeout = 10; 396 // The maximum duration, in seconds, to keep used idle persistent sockets alive. 397 static const int kUsedIdleSocketTimeout = 300; // 5 minutes 398 399 template <typename SocketParams> 400 class ClientSocketPoolBase { 401 public: 402 class Request : public internal::ClientSocketPoolBaseHelper::Request { 403 public: 404 Request(ClientSocketHandle* handle, 405 CompletionCallback* callback, 406 RequestPriority priority, 407 const SocketParams& params, 408 LoadLog* load_log) 409 : internal::ClientSocketPoolBaseHelper::Request( 410 handle, callback, priority, load_log), 411 params_(params) {} 412 413 const SocketParams& params() const { return params_; } 414 415 private: 416 SocketParams params_; 417 }; 418 419 class ConnectJobFactory { 420 public: 421 ConnectJobFactory() {} 422 virtual ~ConnectJobFactory() {} 423 424 virtual ConnectJob* NewConnectJob( 425 const std::string& group_name, 426 const Request& request, 427 ConnectJob::Delegate* delegate, 428 LoadLog* load_log) const = 0; 429 430 private: 431 DISALLOW_COPY_AND_ASSIGN(ConnectJobFactory); 432 }; 433 434 // |max_sockets| is the maximum number of sockets to be maintained by this 435 // ClientSocketPool. |max_sockets_per_group| specifies the maximum number of 436 // sockets a "group" can have. |unused_idle_socket_timeout| specifies how 437 // long to leave an unused idle socket open before closing it. 438 // |used_idle_socket_timeout| specifies how long to leave a previously used 439 // idle socket open before closing it. 440 ClientSocketPoolBase( 441 int max_sockets, 442 int max_sockets_per_group, 443 base::TimeDelta unused_idle_socket_timeout, 444 base::TimeDelta used_idle_socket_timeout, 445 ConnectJobFactory* connect_job_factory, 446 NetworkChangeNotifier* network_change_notifier) 447 : helper_(new internal::ClientSocketPoolBaseHelper( 448 max_sockets, max_sockets_per_group, 449 unused_idle_socket_timeout, used_idle_socket_timeout, 450 new ConnectJobFactoryAdaptor(connect_job_factory), 451 network_change_notifier)) {} 452 453 virtual ~ClientSocketPoolBase() {} 454 455 // These member functions simply forward to ClientSocketPoolBaseHelper. 456 457 // RequestSocket bundles up the parameters into a Request and then forwards to 458 // ClientSocketPoolBaseHelper::RequestSocket(). Note that the memory 459 // ownership is transferred in the asynchronous (ERR_IO_PENDING) case. 460 int RequestSocket(const std::string& group_name, 461 const SocketParams& params, 462 RequestPriority priority, 463 ClientSocketHandle* handle, 464 CompletionCallback* callback, 465 LoadLog* load_log) { 466 scoped_ptr<Request> request( 467 new Request(handle, callback, priority, params, load_log)); 468 LoadLog::BeginEvent(load_log, LoadLog::TYPE_SOCKET_POOL); 469 int rv = helper_->RequestSocket(group_name, request.get()); 470 if (rv == ERR_IO_PENDING) 471 request.release(); 472 else 473 LoadLog::EndEvent(load_log, LoadLog::TYPE_SOCKET_POOL); 474 return rv; 475 } 476 477 void CancelRequest(const std::string& group_name, 478 const ClientSocketHandle* handle) { 479 return helper_->CancelRequest(group_name, handle); 480 } 481 482 void ReleaseSocket(const std::string& group_name, ClientSocket* socket) { 483 return helper_->ReleaseSocket(group_name, socket); 484 } 485 486 void CloseIdleSockets() { return helper_->CloseIdleSockets(); } 487 488 int idle_socket_count() const { return helper_->idle_socket_count(); } 489 490 int IdleSocketCountInGroup(const std::string& group_name) const { 491 return helper_->IdleSocketCountInGroup(group_name); 492 } 493 494 LoadState GetLoadState(const std::string& group_name, 495 const ClientSocketHandle* handle) const { 496 return helper_->GetLoadState(group_name, handle); 497 } 498 499 virtual void OnConnectJobComplete(int result, ConnectJob* job) { 500 return helper_->OnConnectJobComplete(result, job); 501 } 502 503 // For testing. 504 bool may_have_stalled_group() const { 505 return helper_->may_have_stalled_group(); 506 } 507 508 int NumConnectJobsInGroup(const std::string& group_name) const { 509 return helper_->NumConnectJobsInGroup(group_name); 510 } 511 512 void CleanupIdleSockets(bool force) { 513 return helper_->CleanupIdleSockets(force); 514 } 515 516 private: 517 // This adaptor class exists to bridge the 518 // internal::ClientSocketPoolBaseHelper::ConnectJobFactory and 519 // ClientSocketPoolBase::ConnectJobFactory types, allowing clients to use the 520 // typesafe ClientSocketPoolBase::ConnectJobFactory, rather than having to 521 // static_cast themselves. 522 class ConnectJobFactoryAdaptor 523 : public internal::ClientSocketPoolBaseHelper::ConnectJobFactory { 524 public: 525 typedef typename ClientSocketPoolBase<SocketParams>::ConnectJobFactory 526 ConnectJobFactory; 527 528 explicit ConnectJobFactoryAdaptor( 529 ConnectJobFactory* connect_job_factory) 530 : connect_job_factory_(connect_job_factory) {} 531 virtual ~ConnectJobFactoryAdaptor() {} 532 533 virtual ConnectJob* NewConnectJob( 534 const std::string& group_name, 535 const internal::ClientSocketPoolBaseHelper::Request& request, 536 ConnectJob::Delegate* delegate, 537 LoadLog* load_log) const { 538 const Request* casted_request = static_cast<const Request*>(&request); 539 return connect_job_factory_->NewConnectJob( 540 group_name, *casted_request, delegate, load_log); 541 } 542 543 const scoped_ptr<ConnectJobFactory> connect_job_factory_; 544 }; 545 546 // One might ask why ClientSocketPoolBaseHelper is also refcounted if its 547 // containing ClientSocketPool is already refcounted. The reason is because 548 // DoReleaseSocket() posts a task. If ClientSocketPool gets deleted between 549 // the posting of the task and the execution, then we'll hit the DCHECK that 550 // |ClientSocketPoolBaseHelper::group_map_| is empty. 551 scoped_refptr<internal::ClientSocketPoolBaseHelper> helper_; 552 553 DISALLOW_COPY_AND_ASSIGN(ClientSocketPoolBase); 554 }; 555 556 } // namespace net 557 558 #endif // NET_SOCKET_CLIENT_SOCKET_POOL_BASE_H_ 559