Home | History | Annotate | Download | only in socket
      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