1// Copyright (c) 2012 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 <list>
27#include <map>
28#include <set>
29#include <string>
30#include <vector>
31
32#include "base/basictypes.h"
33#include "base/memory/ref_counted.h"
34#include "base/memory/scoped_ptr.h"
35#include "base/memory/weak_ptr.h"
36#include "base/time/time.h"
37#include "base/timer/timer.h"
38#include "net/base/address_list.h"
39#include "net/base/completion_callback.h"
40#include "net/base/load_states.h"
41#include "net/base/load_timing_info.h"
42#include "net/base/net_errors.h"
43#include "net/base/net_export.h"
44#include "net/base/net_log.h"
45#include "net/base/network_change_notifier.h"
46#include "net/base/request_priority.h"
47#include "net/socket/client_socket_pool.h"
48#include "net/socket/stream_socket.h"
49
50namespace net {
51
52class ClientSocketHandle;
53
54// ConnectJob provides an abstract interface for "connecting" a socket.
55// The connection may involve host resolution, tcp connection, ssl connection,
56// etc.
57class NET_EXPORT_PRIVATE ConnectJob {
58 public:
59  class NET_EXPORT_PRIVATE Delegate {
60   public:
61    Delegate() {}
62    virtual ~Delegate() {}
63
64    // Alerts the delegate that the connection completed.
65    virtual void OnConnectJobComplete(int result, ConnectJob* job) = 0;
66
67   private:
68    DISALLOW_COPY_AND_ASSIGN(Delegate);
69  };
70
71  // A |timeout_duration| of 0 corresponds to no timeout.
72  ConnectJob(const std::string& group_name,
73             base::TimeDelta timeout_duration,
74             Delegate* delegate,
75             const BoundNetLog& net_log);
76  virtual ~ConnectJob();
77
78  // Accessors
79  const std::string& group_name() const { return group_name_; }
80  const BoundNetLog& net_log() { return net_log_; }
81
82  // Releases |socket_| to the client.  On connection error, this should return
83  // NULL.
84  StreamSocket* ReleaseSocket() { return socket_.release(); }
85
86  // Begins connecting the socket.  Returns OK on success, ERR_IO_PENDING if it
87  // cannot complete synchronously without blocking, or another net error code
88  // on error.  In asynchronous completion, the ConnectJob will notify
89  // |delegate_| via OnConnectJobComplete.  In both asynchronous and synchronous
90  // completion, ReleaseSocket() can be called to acquire the connected socket
91  // if it succeeded.
92  int Connect();
93
94  virtual LoadState GetLoadState() const = 0;
95
96  // If Connect returns an error (or OnConnectJobComplete reports an error
97  // result) this method will be called, allowing the pool to add
98  // additional error state to the ClientSocketHandle (post late-binding).
99  virtual void GetAdditionalErrorState(ClientSocketHandle* handle) {}
100
101  const LoadTimingInfo::ConnectTiming& connect_timing() const {
102    return connect_timing_;
103  }
104
105  const BoundNetLog& net_log() const { return net_log_; }
106
107 protected:
108  void set_socket(StreamSocket* socket);
109  StreamSocket* socket() { return socket_.get(); }
110  void NotifyDelegateOfCompletion(int rv);
111  void ResetTimer(base::TimeDelta remainingTime);
112
113  // Connection establishment timing information.
114  LoadTimingInfo::ConnectTiming connect_timing_;
115
116 private:
117  virtual int ConnectInternal() = 0;
118
119  void LogConnectStart();
120  void LogConnectCompletion(int net_error);
121
122  // Alerts the delegate that the ConnectJob has timed out.
123  void OnTimeout();
124
125  const std::string group_name_;
126  const base::TimeDelta timeout_duration_;
127  // Timer to abort jobs that take too long.
128  base::OneShotTimer<ConnectJob> timer_;
129  Delegate* delegate_;
130  scoped_ptr<StreamSocket> socket_;
131  BoundNetLog net_log_;
132  // A ConnectJob is idle until Connect() has been called.
133  bool idle_;
134
135  DISALLOW_COPY_AND_ASSIGN(ConnectJob);
136};
137
138namespace internal {
139
140// ClientSocketPoolBaseHelper is an internal class that implements almost all
141// the functionality from ClientSocketPoolBase without using templates.
142// ClientSocketPoolBase adds templated definitions built on top of
143// ClientSocketPoolBaseHelper.  This class is not for external use, please use
144// ClientSocketPoolBase instead.
145class NET_EXPORT_PRIVATE ClientSocketPoolBaseHelper
146    : public ConnectJob::Delegate,
147      public NetworkChangeNotifier::IPAddressObserver {
148 public:
149  typedef uint32 Flags;
150
151  // Used to specify specific behavior for the ClientSocketPool.
152  enum Flag {
153    NORMAL = 0,  // Normal behavior.
154    NO_IDLE_SOCKETS = 0x1,  // Do not return an idle socket. Create a new one.
155  };
156
157  class NET_EXPORT_PRIVATE Request {
158   public:
159    Request(ClientSocketHandle* handle,
160            const CompletionCallback& callback,
161            RequestPriority priority,
162            bool ignore_limits,
163            Flags flags,
164            const BoundNetLog& net_log);
165
166    virtual ~Request();
167
168    ClientSocketHandle* handle() const { return handle_; }
169    const CompletionCallback& callback() const { return callback_; }
170    RequestPriority priority() const { return priority_; }
171    bool ignore_limits() const { return ignore_limits_; }
172    Flags flags() const { return flags_; }
173    const BoundNetLog& net_log() const { return net_log_; }
174
175   private:
176    ClientSocketHandle* const handle_;
177    CompletionCallback callback_;
178    const RequestPriority priority_;
179    bool ignore_limits_;
180    const Flags flags_;
181    BoundNetLog net_log_;
182
183    DISALLOW_COPY_AND_ASSIGN(Request);
184  };
185
186  class ConnectJobFactory {
187   public:
188    ConnectJobFactory() {}
189    virtual ~ConnectJobFactory() {}
190
191    virtual ConnectJob* NewConnectJob(
192        const std::string& group_name,
193        const Request& request,
194        ConnectJob::Delegate* delegate) const = 0;
195
196    virtual base::TimeDelta ConnectionTimeout() const = 0;
197
198   private:
199    DISALLOW_COPY_AND_ASSIGN(ConnectJobFactory);
200  };
201
202  ClientSocketPoolBaseHelper(
203      int max_sockets,
204      int max_sockets_per_group,
205      base::TimeDelta unused_idle_socket_timeout,
206      base::TimeDelta used_idle_socket_timeout,
207      ConnectJobFactory* connect_job_factory);
208
209  virtual ~ClientSocketPoolBaseHelper();
210
211  // Adds/Removes layered pools. It is expected in the destructor that no
212  // layered pools remain.
213  void AddLayeredPool(LayeredPool* pool);
214  void RemoveLayeredPool(LayeredPool* pool);
215
216  // See ClientSocketPool::RequestSocket for documentation on this function.
217  // ClientSocketPoolBaseHelper takes ownership of |request|, which must be
218  // heap allocated.
219  int RequestSocket(const std::string& group_name, const Request* request);
220
221  // See ClientSocketPool::RequestSocket for documentation on this function.
222  void RequestSockets(const std::string& group_name,
223                      const Request& request,
224                      int num_sockets);
225
226  // See ClientSocketPool::CancelRequest for documentation on this function.
227  void CancelRequest(const std::string& group_name,
228                     ClientSocketHandle* handle);
229
230  // See ClientSocketPool::ReleaseSocket for documentation on this function.
231  void ReleaseSocket(const std::string& group_name,
232                     StreamSocket* socket,
233                     int id);
234
235  // See ClientSocketPool::FlushWithError for documentation on this function.
236  void FlushWithError(int error);
237
238  // See ClientSocketPool::IsStalled for documentation on this function.
239  bool IsStalled() const;
240
241  // See ClientSocketPool::CloseIdleSockets for documentation on this function.
242  void CloseIdleSockets();
243
244  // See ClientSocketPool::IdleSocketCount() for documentation on this function.
245  int idle_socket_count() const {
246    return idle_socket_count_;
247  }
248
249  // See ClientSocketPool::IdleSocketCountInGroup() for documentation on this
250  // function.
251  int IdleSocketCountInGroup(const std::string& group_name) const;
252
253  // See ClientSocketPool::GetLoadState() for documentation on this function.
254  LoadState GetLoadState(const std::string& group_name,
255                         const ClientSocketHandle* handle) const;
256
257  base::TimeDelta ConnectRetryInterval() const {
258    // TODO(mbelshe): Make this tuned dynamically based on measured RTT.
259    //                For now, just use the max retry interval.
260    return base::TimeDelta::FromMilliseconds(
261        ClientSocketPool::kMaxConnectRetryIntervalMs);
262  }
263
264  int NumUnassignedConnectJobsInGroup(const std::string& group_name) const {
265    return group_map_.find(group_name)->second->unassigned_job_count();
266  }
267
268  int NumConnectJobsInGroup(const std::string& group_name) const {
269    return group_map_.find(group_name)->second->jobs().size();
270  }
271
272  int NumActiveSocketsInGroup(const std::string& group_name) const {
273    return group_map_.find(group_name)->second->active_socket_count();
274  }
275
276  bool HasGroup(const std::string& group_name) const;
277
278  // Called to enable/disable cleaning up idle sockets. When enabled,
279  // idle sockets that have been around for longer than a period defined
280  // by kCleanupInterval are cleaned up using a timer. Otherwise they are
281  // closed next time client makes a request. This may reduce network
282  // activity and power consumption.
283  static bool cleanup_timer_enabled();
284  static bool set_cleanup_timer_enabled(bool enabled);
285
286  // Closes all idle sockets if |force| is true.  Else, only closes idle
287  // sockets that timed out or can't be reused.  Made public for testing.
288  void CleanupIdleSockets(bool force);
289
290  // Closes one idle socket.  Picks the first one encountered.
291  // TODO(willchan): Consider a better algorithm for doing this.  Perhaps we
292  // should keep an ordered list of idle sockets, and close them in order.
293  // Requires maintaining more state.  It's not clear if it's worth it since
294  // I'm not sure if we hit this situation often.
295  bool CloseOneIdleSocket();
296
297  // Checks layered pools to see if they can close an idle connection.
298  bool CloseOneIdleConnectionInLayeredPool();
299
300  // See ClientSocketPool::GetInfoAsValue for documentation on this function.
301  base::DictionaryValue* GetInfoAsValue(const std::string& name,
302                                        const std::string& type) const;
303
304  base::TimeDelta ConnectionTimeout() const {
305    return connect_job_factory_->ConnectionTimeout();
306  }
307
308  static bool connect_backup_jobs_enabled();
309  static bool set_connect_backup_jobs_enabled(bool enabled);
310
311  void EnableConnectBackupJobs();
312
313  // ConnectJob::Delegate methods:
314  virtual void OnConnectJobComplete(int result, ConnectJob* job) OVERRIDE;
315
316  // NetworkChangeNotifier::IPAddressObserver methods:
317  virtual void OnIPAddressChanged() OVERRIDE;
318
319 private:
320  friend class base::RefCounted<ClientSocketPoolBaseHelper>;
321
322  // Entry for a persistent socket which became idle at time |start_time|.
323  struct IdleSocket {
324    IdleSocket() : socket(NULL) {}
325
326    // An idle socket should be removed if it can't be reused, or has been idle
327    // for too long. |now| is the current time value (TimeTicks::Now()).
328    // |timeout| is the length of time to wait before timing out an idle socket.
329    //
330    // An idle socket can't be reused if it is disconnected or has received
331    // data unexpectedly (hence no longer idle).  The unread data would be
332    // mistaken for the beginning of the next response if we were to reuse the
333    // socket for a new request.
334    bool ShouldCleanup(base::TimeTicks now, base::TimeDelta timeout) const;
335
336    StreamSocket* socket;
337    base::TimeTicks start_time;
338  };
339
340  typedef std::deque<const Request* > RequestQueue;
341  typedef std::map<const ClientSocketHandle*, const Request*> RequestMap;
342
343  // A Group is allocated per group_name when there are idle sockets or pending
344  // requests.  Otherwise, the Group object is removed from the map.
345  // |active_socket_count| tracks the number of sockets held by clients.
346  class Group {
347   public:
348    Group();
349    ~Group();
350
351    bool IsEmpty() const {
352      return active_socket_count_ == 0 && idle_sockets_.empty() &&
353          jobs_.empty() && pending_requests_.empty();
354    }
355
356    bool HasAvailableSocketSlot(int max_sockets_per_group) const {
357      return NumActiveSocketSlots() < max_sockets_per_group;
358    }
359
360    int NumActiveSocketSlots() const {
361      return active_socket_count_ + static_cast<int>(jobs_.size()) +
362          static_cast<int>(idle_sockets_.size());
363    }
364
365    bool IsStalledOnPoolMaxSockets(int max_sockets_per_group) const {
366      return HasAvailableSocketSlot(max_sockets_per_group) &&
367          pending_requests_.size() > jobs_.size();
368    }
369
370    RequestPriority TopPendingPriority() const {
371      return pending_requests_.front()->priority();
372    }
373
374    bool HasBackupJob() const { return weak_factory_.HasWeakPtrs(); }
375
376    void CleanupBackupJob() {
377      weak_factory_.InvalidateWeakPtrs();
378    }
379
380    // Set a timer to create a backup socket if it takes too long to create one.
381    void StartBackupSocketTimer(const std::string& group_name,
382                                ClientSocketPoolBaseHelper* pool);
383
384    // If there's a ConnectJob that's never been assigned to Request,
385    // decrements |unassigned_job_count_| and returns true.
386    // Otherwise, returns false.
387    bool TryToUseUnassignedConnectJob();
388
389    void AddJob(ConnectJob* job, bool is_preconnect);
390    void RemoveJob(ConnectJob* job);
391    void RemoveAllJobs();
392
393    void IncrementActiveSocketCount() { active_socket_count_++; }
394    void DecrementActiveSocketCount() { active_socket_count_--; }
395
396    int unassigned_job_count() const { return unassigned_job_count_; }
397    const std::set<ConnectJob*>& jobs() const { return jobs_; }
398    const std::list<IdleSocket>& idle_sockets() const { return idle_sockets_; }
399    const RequestQueue& pending_requests() const { return pending_requests_; }
400    int active_socket_count() const { return active_socket_count_; }
401    RequestQueue* mutable_pending_requests() { return &pending_requests_; }
402    std::list<IdleSocket>* mutable_idle_sockets() { return &idle_sockets_; }
403
404   private:
405    // Called when the backup socket timer fires.
406    void OnBackupSocketTimerFired(
407        std::string group_name,
408        ClientSocketPoolBaseHelper* pool);
409
410    // Checks that |unassigned_job_count_| does not execeed the number of
411    // ConnectJobs.
412    void SanityCheck();
413
414    // Total number of ConnectJobs that have never been assigned to a Request.
415    // Since jobs use late binding to requests, which ConnectJobs have or have
416    // not been assigned to a request are not tracked.  This is incremented on
417    // preconnect and decremented when a preconnect is assigned, or when there
418    // are fewer than |unassigned_job_count_| ConnectJobs.  Not incremented
419    // when a request is cancelled.
420    size_t unassigned_job_count_;
421
422    std::list<IdleSocket> idle_sockets_;
423    std::set<ConnectJob*> jobs_;
424    RequestQueue pending_requests_;
425    int active_socket_count_;  // number of active sockets used by clients
426    // A factory to pin the backup_job tasks.
427    base::WeakPtrFactory<Group> weak_factory_;
428  };
429
430  typedef std::map<std::string, Group*> GroupMap;
431
432  typedef std::set<ConnectJob*> ConnectJobSet;
433
434  struct CallbackResultPair {
435    CallbackResultPair();
436    CallbackResultPair(const CompletionCallback& callback_in, int result_in);
437    ~CallbackResultPair();
438
439    CompletionCallback callback;
440    int result;
441  };
442
443  typedef std::map<const ClientSocketHandle*, CallbackResultPair>
444      PendingCallbackMap;
445
446  // Inserts the request into the queue based on order they will receive
447  // sockets. Sockets which ignore the socket pool limits are first. Then
448  // requests are sorted by priority, with higher priorities closer to the
449  // front. Older requests are prioritized over requests of equal priority.
450  static void InsertRequestIntoQueue(const Request* r,
451                                     RequestQueue* pending_requests);
452  static const Request* RemoveRequestFromQueue(const RequestQueue::iterator& it,
453                                               Group* group);
454
455  Group* GetOrCreateGroup(const std::string& group_name);
456  void RemoveGroup(const std::string& group_name);
457  void RemoveGroup(GroupMap::iterator it);
458
459  // Called when the number of idle sockets changes.
460  void IncrementIdleCount();
461  void DecrementIdleCount();
462
463  // Start cleanup timer for idle sockets.
464  void StartIdleSocketTimer();
465
466  // Scans the group map for groups which have an available socket slot and
467  // at least one pending request. Returns true if any groups are stalled, and
468  // if so (and if both |group| and |group_name| are not NULL), fills |group|
469  // and |group_name| with data of the stalled group having highest priority.
470  bool FindTopStalledGroup(Group** group, std::string* group_name) const;
471
472  // Called when timer_ fires.  This method scans the idle sockets removing
473  // sockets that timed out or can't be reused.
474  void OnCleanupTimerFired() {
475    CleanupIdleSockets(false);
476  }
477
478  // Removes |job| from |connect_job_set_|.  Also updates |group| if non-NULL.
479  void RemoveConnectJob(ConnectJob* job, Group* group);
480
481  // Tries to see if we can handle any more requests for |group|.
482  void OnAvailableSocketSlot(const std::string& group_name, Group* group);
483
484  // Process a pending socket request for a group.
485  void ProcessPendingRequest(const std::string& group_name, Group* group);
486
487  // Assigns |socket| to |handle| and updates |group|'s counters appropriately.
488  void HandOutSocket(StreamSocket* socket,
489                     bool reused,
490                     const LoadTimingInfo::ConnectTiming& connect_timing,
491                     ClientSocketHandle* handle,
492                     base::TimeDelta time_idle,
493                     Group* group,
494                     const BoundNetLog& net_log);
495
496  // Adds |socket| to the list of idle sockets for |group|.
497  void AddIdleSocket(StreamSocket* socket, Group* group);
498
499  // Iterates through |group_map_|, canceling all ConnectJobs and deleting
500  // groups if they are no longer needed.
501  void CancelAllConnectJobs();
502
503  // Iterates through |group_map_|, posting |error| callbacks for all
504  // requests, and then deleting groups if they are no longer needed.
505  void CancelAllRequestsWithError(int error);
506
507  // Returns true if we can't create any more sockets due to the total limit.
508  bool ReachedMaxSocketsLimit() const;
509
510  // This is the internal implementation of RequestSocket().  It differs in that
511  // it does not handle logging into NetLog of the queueing status of
512  // |request|.
513  int RequestSocketInternal(const std::string& group_name,
514                            const Request* request);
515
516  // Assigns an idle socket for the group to the request.
517  // Returns |true| if an idle socket is available, false otherwise.
518  bool AssignIdleSocketToRequest(const Request* request, Group* group);
519
520  static void LogBoundConnectJobToRequest(
521      const NetLog::Source& connect_job_source, const Request* request);
522
523  // Same as CloseOneIdleSocket() except it won't close an idle socket in
524  // |group|.  If |group| is NULL, it is ignored.  Returns true if it closed a
525  // socket.
526  bool CloseOneIdleSocketExceptInGroup(const Group* group);
527
528  // Checks if there are stalled socket groups that should be notified
529  // for possible wakeup.
530  void CheckForStalledSocketGroups();
531
532  // Posts a task to call InvokeUserCallback() on the next iteration through the
533  // current message loop.  Inserts |callback| into |pending_callback_map_|,
534  // keyed by |handle|.
535  void InvokeUserCallbackLater(
536      ClientSocketHandle* handle, const CompletionCallback& callback, int rv);
537
538  // Invokes the user callback for |handle|.  By the time this task has run,
539  // it's possible that the request has been cancelled, so |handle| may not
540  // exist in |pending_callback_map_|.  We look up the callback and result code
541  // in |pending_callback_map_|.
542  void InvokeUserCallback(ClientSocketHandle* handle);
543
544  // Tries to close idle sockets in a higher level socket pool as long as this
545  // this pool is stalled.
546  void TryToCloseSocketsInLayeredPools();
547
548  GroupMap group_map_;
549
550  // Map of the ClientSocketHandles for which we have a pending Task to invoke a
551  // callback.  This is necessary since, before we invoke said callback, it's
552  // possible that the request is cancelled.
553  PendingCallbackMap pending_callback_map_;
554
555  // Timer used to periodically prune idle sockets that timed out or can't be
556  // reused.
557  base::RepeatingTimer<ClientSocketPoolBaseHelper> timer_;
558
559  // The total number of idle sockets in the system.
560  int idle_socket_count_;
561
562  // Number of connecting sockets across all groups.
563  int connecting_socket_count_;
564
565  // Number of connected sockets we handed out across all groups.
566  int handed_out_socket_count_;
567
568  // The maximum total number of sockets. See ReachedMaxSocketsLimit.
569  const int max_sockets_;
570
571  // The maximum number of sockets kept per group.
572  const int max_sockets_per_group_;
573
574  // Whether to use timer to cleanup idle sockets.
575  bool use_cleanup_timer_;
576
577  // The time to wait until closing idle sockets.
578  const base::TimeDelta unused_idle_socket_timeout_;
579  const base::TimeDelta used_idle_socket_timeout_;
580
581  const scoped_ptr<ConnectJobFactory> connect_job_factory_;
582
583  // TODO(vandebo) Remove when backup jobs move to TransportClientSocketPool
584  bool connect_backup_jobs_enabled_;
585
586  // A unique id for the pool.  It gets incremented every time we
587  // FlushWithError() the pool.  This is so that when sockets get released back
588  // to the pool, we can make sure that they are discarded rather than reused.
589  int pool_generation_number_;
590
591  std::set<LayeredPool*> higher_layer_pools_;
592
593  base::WeakPtrFactory<ClientSocketPoolBaseHelper> weak_factory_;
594
595  DISALLOW_COPY_AND_ASSIGN(ClientSocketPoolBaseHelper);
596};
597
598}  // namespace internal
599
600template <typename SocketParams>
601class ClientSocketPoolBase {
602 public:
603  class Request : public internal::ClientSocketPoolBaseHelper::Request {
604   public:
605    Request(ClientSocketHandle* handle,
606            const CompletionCallback& callback,
607            RequestPriority priority,
608            internal::ClientSocketPoolBaseHelper::Flags flags,
609            bool ignore_limits,
610            const scoped_refptr<SocketParams>& params,
611            const BoundNetLog& net_log)
612        : internal::ClientSocketPoolBaseHelper::Request(
613              handle, callback, priority, ignore_limits, flags, net_log),
614          params_(params) {}
615
616    const scoped_refptr<SocketParams>& params() const { return params_; }
617
618   private:
619    const scoped_refptr<SocketParams> params_;
620  };
621
622  class ConnectJobFactory {
623   public:
624    ConnectJobFactory() {}
625    virtual ~ConnectJobFactory() {}
626
627    virtual ConnectJob* NewConnectJob(
628        const std::string& group_name,
629        const Request& request,
630        ConnectJob::Delegate* delegate) const = 0;
631
632    virtual base::TimeDelta ConnectionTimeout() const = 0;
633
634   private:
635    DISALLOW_COPY_AND_ASSIGN(ConnectJobFactory);
636  };
637
638  // |max_sockets| is the maximum number of sockets to be maintained by this
639  // ClientSocketPool.  |max_sockets_per_group| specifies the maximum number of
640  // sockets a "group" can have.  |unused_idle_socket_timeout| specifies how
641  // long to leave an unused idle socket open before closing it.
642  // |used_idle_socket_timeout| specifies how long to leave a previously used
643  // idle socket open before closing it.
644  ClientSocketPoolBase(
645      int max_sockets,
646      int max_sockets_per_group,
647      ClientSocketPoolHistograms* histograms,
648      base::TimeDelta unused_idle_socket_timeout,
649      base::TimeDelta used_idle_socket_timeout,
650      ConnectJobFactory* connect_job_factory)
651      : histograms_(histograms),
652        helper_(max_sockets, max_sockets_per_group,
653                unused_idle_socket_timeout, used_idle_socket_timeout,
654                new ConnectJobFactoryAdaptor(connect_job_factory)) {}
655
656  virtual ~ClientSocketPoolBase() {}
657
658  // These member functions simply forward to ClientSocketPoolBaseHelper.
659  void AddLayeredPool(LayeredPool* pool) {
660    helper_.AddLayeredPool(pool);
661  }
662
663  void RemoveLayeredPool(LayeredPool* pool) {
664    helper_.RemoveLayeredPool(pool);
665  }
666
667  // RequestSocket bundles up the parameters into a Request and then forwards to
668  // ClientSocketPoolBaseHelper::RequestSocket().
669  int RequestSocket(const std::string& group_name,
670                    const scoped_refptr<SocketParams>& params,
671                    RequestPriority priority,
672                    ClientSocketHandle* handle,
673                    const CompletionCallback& callback,
674                    const BoundNetLog& net_log) {
675    Request* request =
676        new Request(handle, callback, priority,
677                    internal::ClientSocketPoolBaseHelper::NORMAL,
678                    params->ignore_limits(),
679                    params, net_log);
680    return helper_.RequestSocket(group_name, request);
681  }
682
683  // RequestSockets bundles up the parameters into a Request and then forwards
684  // to ClientSocketPoolBaseHelper::RequestSockets().  Note that it assigns the
685  // priority to DEFAULT_PRIORITY and specifies the NO_IDLE_SOCKETS flag.
686  void RequestSockets(const std::string& group_name,
687                      const scoped_refptr<SocketParams>& params,
688                      int num_sockets,
689                      const BoundNetLog& net_log) {
690    const Request request(NULL /* no handle */,
691                          CompletionCallback(),
692                          DEFAULT_PRIORITY,
693                          internal::ClientSocketPoolBaseHelper::NO_IDLE_SOCKETS,
694                          params->ignore_limits(),
695                          params,
696                          net_log);
697    helper_.RequestSockets(group_name, request, num_sockets);
698  }
699
700  void CancelRequest(const std::string& group_name,
701                     ClientSocketHandle* handle) {
702    return helper_.CancelRequest(group_name, handle);
703  }
704
705  void ReleaseSocket(const std::string& group_name, StreamSocket* socket,
706                     int id) {
707    return helper_.ReleaseSocket(group_name, socket, id);
708  }
709
710  void FlushWithError(int error) { helper_.FlushWithError(error); }
711
712  bool IsStalled() const { return helper_.IsStalled(); }
713
714  void CloseIdleSockets() { return helper_.CloseIdleSockets(); }
715
716  int idle_socket_count() const { return helper_.idle_socket_count(); }
717
718  int IdleSocketCountInGroup(const std::string& group_name) const {
719    return helper_.IdleSocketCountInGroup(group_name);
720  }
721
722  LoadState GetLoadState(const std::string& group_name,
723                         const ClientSocketHandle* handle) const {
724    return helper_.GetLoadState(group_name, handle);
725  }
726
727  virtual void OnConnectJobComplete(int result, ConnectJob* job) {
728    return helper_.OnConnectJobComplete(result, job);
729  }
730
731  int NumUnassignedConnectJobsInGroup(const std::string& group_name) const {
732    return helper_.NumUnassignedConnectJobsInGroup(group_name);
733  }
734
735  int NumConnectJobsInGroup(const std::string& group_name) const {
736    return helper_.NumConnectJobsInGroup(group_name);
737  }
738
739  int NumActiveSocketsInGroup(const std::string& group_name) const {
740    return helper_.NumActiveSocketsInGroup(group_name);
741  }
742
743  bool HasGroup(const std::string& group_name) const {
744    return helper_.HasGroup(group_name);
745  }
746
747  void CleanupIdleSockets(bool force) {
748    return helper_.CleanupIdleSockets(force);
749  }
750
751  base::DictionaryValue* GetInfoAsValue(const std::string& name,
752                                        const std::string& type) const {
753    return helper_.GetInfoAsValue(name, type);
754  }
755
756  base::TimeDelta ConnectionTimeout() const {
757    return helper_.ConnectionTimeout();
758  }
759
760  ClientSocketPoolHistograms* histograms() const {
761    return histograms_;
762  }
763
764  void EnableConnectBackupJobs() { helper_.EnableConnectBackupJobs(); }
765
766  bool CloseOneIdleSocket() { return helper_.CloseOneIdleSocket(); }
767
768  bool CloseOneIdleConnectionInLayeredPool() {
769    return helper_.CloseOneIdleConnectionInLayeredPool();
770  }
771
772 private:
773  // This adaptor class exists to bridge the
774  // internal::ClientSocketPoolBaseHelper::ConnectJobFactory and
775  // ClientSocketPoolBase::ConnectJobFactory types, allowing clients to use the
776  // typesafe ClientSocketPoolBase::ConnectJobFactory, rather than having to
777  // static_cast themselves.
778  class ConnectJobFactoryAdaptor
779      : public internal::ClientSocketPoolBaseHelper::ConnectJobFactory {
780   public:
781    typedef typename ClientSocketPoolBase<SocketParams>::ConnectJobFactory
782        ConnectJobFactory;
783
784    explicit ConnectJobFactoryAdaptor(ConnectJobFactory* connect_job_factory)
785        : connect_job_factory_(connect_job_factory) {}
786    virtual ~ConnectJobFactoryAdaptor() {}
787
788    virtual ConnectJob* NewConnectJob(
789        const std::string& group_name,
790        const internal::ClientSocketPoolBaseHelper::Request& request,
791        ConnectJob::Delegate* delegate) const {
792      const Request* casted_request = static_cast<const Request*>(&request);
793      return connect_job_factory_->NewConnectJob(
794          group_name, *casted_request, delegate);
795    }
796
797    virtual base::TimeDelta ConnectionTimeout() const {
798      return connect_job_factory_->ConnectionTimeout();
799    }
800
801    const scoped_ptr<ConnectJobFactory> connect_job_factory_;
802  };
803
804  // Histograms for the pool
805  ClientSocketPoolHistograms* const histograms_;
806  internal::ClientSocketPoolBaseHelper helper_;
807
808  DISALLOW_COPY_AND_ASSIGN(ClientSocketPoolBase);
809};
810
811}  // namespace net
812
813#endif  // NET_SOCKET_CLIENT_SOCKET_POOL_BASE_H_
814