[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 1 | // 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 | #ifndef NET_SOCKET_CLIENT_SOCKET_POOL_BASE_H_ |
| 6 | #define NET_SOCKET_CLIENT_SOCKET_POOL_BASE_H_ |
| 7 | |
| 8 | #include <deque> |
| 9 | #include <map> |
[email protected] | 5fc08e3 | 2009-07-15 17:09:57 | [diff] [blame] | 10 | #include <set> |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 11 | #include <string> |
| 12 | |
| 13 | #include "base/basictypes.h" |
| 14 | #include "base/scoped_ptr.h" |
| 15 | #include "base/time.h" |
| 16 | #include "base/timer.h" |
| 17 | #include "net/base/address_list.h" |
| 18 | #include "net/base/completion_callback.h" |
| 19 | #include "net/base/host_resolver.h" |
| 20 | #include "net/base/load_states.h" |
[email protected] | 2ab05b5 | 2009-07-01 23:57:58 | [diff] [blame] | 21 | #include "net/socket/client_socket.h" |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 22 | #include "net/socket/client_socket_pool.h" |
| 23 | |
| 24 | namespace net { |
| 25 | |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 26 | class ClientSocketHandle; |
| 27 | class ClientSocketPoolBase; |
| 28 | |
| 29 | // ConnectJob provides an abstract interface for "connecting" a socket. |
| 30 | // The connection may involve host resolution, tcp connection, ssl connection, |
| 31 | // etc. |
| 32 | class ConnectJob { |
| 33 | public: |
[email protected] | ab83889 | 2009-06-30 18:49:05 | [diff] [blame] | 34 | class Delegate { |
| 35 | public: |
| 36 | Delegate() {} |
| 37 | virtual ~Delegate() {} |
| 38 | |
[email protected] | 2ab05b5 | 2009-07-01 23:57:58 | [diff] [blame] | 39 | // Alerts the delegate that the connection completed. |
| 40 | virtual void OnConnectJobComplete(int result, ConnectJob* job) = 0; |
[email protected] | ab83889 | 2009-06-30 18:49:05 | [diff] [blame] | 41 | |
| 42 | private: |
| 43 | DISALLOW_COPY_AND_ASSIGN(Delegate); |
| 44 | }; |
| 45 | |
[email protected] | 2ab05b5 | 2009-07-01 23:57:58 | [diff] [blame] | 46 | ConnectJob(const std::string& group_name, |
| 47 | const ClientSocketHandle* key_handle, |
| 48 | Delegate* delegate); |
| 49 | virtual ~ConnectJob(); |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 50 | |
[email protected] | 2ab05b5 | 2009-07-01 23:57:58 | [diff] [blame] | 51 | // Accessors |
| 52 | const std::string& group_name() const { return group_name_; } |
[email protected] | ab83889 | 2009-06-30 18:49:05 | [diff] [blame] | 53 | LoadState load_state() const { return load_state_; } |
[email protected] | 2ab05b5 | 2009-07-01 23:57:58 | [diff] [blame] | 54 | const ClientSocketHandle* key_handle() const { return key_handle_; } |
| 55 | |
[email protected] | 8e12ae0 | 2009-07-02 16:15:04 | [diff] [blame] | 56 | // Releases |socket_| to the client. On connection error, this should return |
| 57 | // NULL. |
[email protected] | 2ab05b5 | 2009-07-01 23:57:58 | [diff] [blame] | 58 | ClientSocket* ReleaseSocket() { return socket_.release(); } |
[email protected] | ab83889 | 2009-06-30 18:49:05 | [diff] [blame] | 59 | |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 60 | // Begins connecting the socket. Returns OK on success, ERR_IO_PENDING if it |
| 61 | // cannot complete synchronously without blocking, or another net error code |
[email protected] | 2ab05b5 | 2009-07-01 23:57:58 | [diff] [blame] | 62 | // on error. In asynchronous completion, the ConnectJob will notify |
| 63 | // |delegate_| via OnConnectJobComplete. In both asynchronous and synchronous |
| 64 | // completion, ReleaseSocket() can be called to acquire the connected socket |
| 65 | // if it succeeded. |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 66 | virtual int Connect() = 0; |
| 67 | |
[email protected] | ab83889 | 2009-06-30 18:49:05 | [diff] [blame] | 68 | protected: |
| 69 | void set_load_state(LoadState load_state) { load_state_ = load_state; } |
[email protected] | 2ab05b5 | 2009-07-01 23:57:58 | [diff] [blame] | 70 | void set_socket(ClientSocket* socket) { socket_.reset(socket); } |
| 71 | ClientSocket* socket() { return socket_.get(); } |
| 72 | Delegate* delegate() { return delegate_; } |
[email protected] | ab83889 | 2009-06-30 18:49:05 | [diff] [blame] | 73 | |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 74 | private: |
[email protected] | 2ab05b5 | 2009-07-01 23:57:58 | [diff] [blame] | 75 | const std::string group_name_; |
| 76 | // Temporarily needed until we switch to late binding. |
| 77 | const ClientSocketHandle* const key_handle_; |
| 78 | Delegate* const delegate_; |
[email protected] | ab83889 | 2009-06-30 18:49:05 | [diff] [blame] | 79 | LoadState load_state_; |
[email protected] | 2ab05b5 | 2009-07-01 23:57:58 | [diff] [blame] | 80 | scoped_ptr<ClientSocket> socket_; |
[email protected] | ab83889 | 2009-06-30 18:49:05 | [diff] [blame] | 81 | |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 82 | DISALLOW_COPY_AND_ASSIGN(ConnectJob); |
| 83 | }; |
| 84 | |
| 85 | // A ClientSocketPoolBase is used to restrict the number of sockets open at |
| 86 | // a time. It also maintains a list of idle persistent sockets. |
| 87 | // |
[email protected] | ab83889 | 2009-06-30 18:49:05 | [diff] [blame] | 88 | class ClientSocketPoolBase |
| 89 | : public base::RefCounted<ClientSocketPoolBase>, |
| 90 | public ConnectJob::Delegate { |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 91 | public: |
| 92 | // A Request is allocated per call to RequestSocket that results in |
| 93 | // ERR_IO_PENDING. |
| 94 | struct Request { |
| 95 | // HostResolver::RequestInfo has no default constructor, so fudge something. |
[email protected] | 3a6bc904 | 2009-07-06 18:32:21 | [diff] [blame] | 96 | Request() |
| 97 | : handle(NULL), |
| 98 | callback(NULL), |
| 99 | priority(0), |
| 100 | resolve_info(std::string(), 0) {} |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 101 | |
| 102 | Request(ClientSocketHandle* handle, |
| 103 | CompletionCallback* callback, |
| 104 | int priority, |
[email protected] | ab83889 | 2009-06-30 18:49:05 | [diff] [blame] | 105 | const HostResolver::RequestInfo& resolve_info) |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 106 | : handle(handle), callback(callback), priority(priority), |
[email protected] | ab83889 | 2009-06-30 18:49:05 | [diff] [blame] | 107 | resolve_info(resolve_info) { |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 108 | } |
| 109 | |
| 110 | ClientSocketHandle* handle; |
| 111 | CompletionCallback* callback; |
| 112 | int priority; |
| 113 | HostResolver::RequestInfo resolve_info; |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 114 | }; |
| 115 | |
| 116 | class ConnectJobFactory { |
| 117 | public: |
| 118 | ConnectJobFactory() {} |
| 119 | virtual ~ConnectJobFactory() {} |
| 120 | |
| 121 | virtual ConnectJob* NewConnectJob( |
| 122 | const std::string& group_name, |
| 123 | const Request& request, |
[email protected] | ab83889 | 2009-06-30 18:49:05 | [diff] [blame] | 124 | ConnectJob::Delegate* delegate) const = 0; |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 125 | |
| 126 | private: |
| 127 | DISALLOW_COPY_AND_ASSIGN(ConnectJobFactory); |
| 128 | }; |
| 129 | |
| 130 | ClientSocketPoolBase(int max_sockets_per_group, |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 131 | ConnectJobFactory* connect_job_factory); |
| 132 | |
| 133 | ~ClientSocketPoolBase(); |
| 134 | |
| 135 | int RequestSocket(const std::string& group_name, |
| 136 | const HostResolver::RequestInfo& resolve_info, |
| 137 | int priority, |
| 138 | ClientSocketHandle* handle, |
| 139 | CompletionCallback* callback); |
| 140 | |
| 141 | void CancelRequest(const std::string& group_name, |
| 142 | const ClientSocketHandle* handle); |
| 143 | |
| 144 | void ReleaseSocket(const std::string& group_name, |
| 145 | ClientSocket* socket); |
| 146 | |
| 147 | void CloseIdleSockets(); |
| 148 | |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 149 | int idle_socket_count() const { |
| 150 | return idle_socket_count_; |
| 151 | } |
| 152 | |
[email protected] | c9d6a1d | 2009-07-14 16:15:20 | [diff] [blame] | 153 | int max_sockets_per_group() const { |
| 154 | return max_sockets_per_group_; |
| 155 | } |
| 156 | |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 157 | int IdleSocketCountInGroup(const std::string& group_name) const; |
| 158 | |
| 159 | LoadState GetLoadState(const std::string& group_name, |
| 160 | const ClientSocketHandle* handle) const; |
| 161 | |
[email protected] | 2ab05b5 | 2009-07-01 23:57:58 | [diff] [blame] | 162 | virtual void OnConnectJobComplete(int result, ConnectJob* job); |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 163 | |
[email protected] | 5fc08e3 | 2009-07-15 17:09:57 | [diff] [blame] | 164 | // Enables late binding of sockets. In this mode, socket requests are |
| 165 | // decoupled from socket connection jobs. A socket request may initiate a |
| 166 | // socket connection job, but there is no guarantee that that socket |
| 167 | // connection will service the request (for example, a released socket may |
| 168 | // service the request sooner, or a higher priority request may come in |
| 169 | // afterward and receive the socket from the job). |
| 170 | static void EnableLateBindingOfSockets(bool enabled); |
| 171 | |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 172 | private: |
| 173 | // Entry for a persistent socket which became idle at time |start_time|. |
| 174 | struct IdleSocket { |
[email protected] | 5fc08e3 | 2009-07-15 17:09:57 | [diff] [blame] | 175 | IdleSocket() : socket(NULL), used(false) {} |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 176 | ClientSocket* socket; |
| 177 | base::TimeTicks start_time; |
[email protected] | 5fc08e3 | 2009-07-15 17:09:57 | [diff] [blame] | 178 | bool used; // Indicates whether or not the socket has been used yet. |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 179 | |
| 180 | // An idle socket should be removed if it can't be reused, or has been idle |
| 181 | // for too long. |now| is the current time value (TimeTicks::Now()). |
| 182 | // |
| 183 | // An idle socket can't be reused if it is disconnected or has received |
| 184 | // data unexpectedly (hence no longer idle). The unread data would be |
| 185 | // mistaken for the beginning of the next response if we were to reuse the |
| 186 | // socket for a new request. |
| 187 | bool ShouldCleanup(base::TimeTicks now) const; |
| 188 | }; |
| 189 | |
| 190 | typedef std::deque<Request> RequestQueue; |
| 191 | typedef std::map<const ClientSocketHandle*, Request> RequestMap; |
| 192 | |
| 193 | // A Group is allocated per group_name when there are idle sockets or pending |
| 194 | // requests. Otherwise, the Group object is removed from the map. |
| 195 | struct Group { |
[email protected] | 2ab05b5 | 2009-07-01 23:57:58 | [diff] [blame] | 196 | Group() : active_socket_count(0) {} |
| 197 | |
| 198 | bool IsEmpty() const { |
[email protected] | 5fc08e3 | 2009-07-15 17:09:57 | [diff] [blame] | 199 | return active_socket_count == 0 && idle_sockets.empty() && jobs.empty(); |
[email protected] | 2ab05b5 | 2009-07-01 23:57:58 | [diff] [blame] | 200 | } |
| 201 | |
| 202 | bool HasAvailableSocketSlot(int max_sockets_per_group) const { |
[email protected] | 5fc08e3 | 2009-07-15 17:09:57 | [diff] [blame] | 203 | return active_socket_count + static_cast<int>(jobs.size()) < |
[email protected] | 2ab05b5 | 2009-07-01 23:57:58 | [diff] [blame] | 204 | max_sockets_per_group; |
| 205 | } |
| 206 | |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 207 | std::deque<IdleSocket> idle_sockets; |
[email protected] | 5fc08e3 | 2009-07-15 17:09:57 | [diff] [blame] | 208 | std::set<const ConnectJob*> jobs; |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 209 | RequestQueue pending_requests; |
| 210 | RequestMap connecting_requests; |
[email protected] | 2ab05b5 | 2009-07-01 23:57:58 | [diff] [blame] | 211 | int active_socket_count; // number of active sockets used by clients |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 212 | }; |
| 213 | |
| 214 | typedef std::map<std::string, Group> GroupMap; |
| 215 | |
| 216 | typedef std::map<const ClientSocketHandle*, ConnectJob*> ConnectJobMap; |
[email protected] | 5fc08e3 | 2009-07-15 17:09:57 | [diff] [blame] | 217 | typedef std::set<const ConnectJob*> ConnectJobSet; |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 218 | |
| 219 | static void InsertRequestIntoQueue(const Request& r, |
| 220 | RequestQueue* pending_requests); |
| 221 | |
| 222 | // Closes all idle sockets if |force| is true. Else, only closes idle |
| 223 | // sockets that timed out or can't be reused. |
| 224 | void CleanupIdleSockets(bool force); |
| 225 | |
| 226 | // Called when the number of idle sockets changes. |
| 227 | void IncrementIdleCount(); |
| 228 | void DecrementIdleCount(); |
| 229 | |
| 230 | // Called via PostTask by ReleaseSocket. |
| 231 | void DoReleaseSocket(const std::string& group_name, ClientSocket* socket); |
| 232 | |
| 233 | // Called when timer_ fires. This method scans the idle sockets removing |
| 234 | // sockets that timed out or can't be reused. |
| 235 | void OnCleanupTimerFired() { |
| 236 | CleanupIdleSockets(false); |
| 237 | } |
| 238 | |
| 239 | // Removes the ConnectJob corresponding to |handle| from the |
[email protected] | 5fc08e3 | 2009-07-15 17:09:57 | [diff] [blame] | 240 | // |connect_job_map_| or |connect_job_set_| depending on whether or not late |
| 241 | // binding is enabled. |job| must be non-NULL when late binding is |
| 242 | // enabled. Also updates |group| if non-NULL. |
| 243 | void RemoveConnectJob(const ClientSocketHandle* handle, |
| 244 | ConnectJob* job, |
| 245 | Group* group); |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 246 | |
[email protected] | 2ab05b5 | 2009-07-01 23:57:58 | [diff] [blame] | 247 | // Same as OnAvailableSocketSlot except it looks up the Group first to see if |
| 248 | // it's there. |
| 249 | void MaybeOnAvailableSocketSlot(const std::string& group_name); |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 250 | |
[email protected] | 2ab05b5 | 2009-07-01 23:57:58 | [diff] [blame] | 251 | // Might delete the Group from |group_map_|. |
| 252 | void OnAvailableSocketSlot(const std::string& group_name, Group* group); |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 253 | |
| 254 | // Process a request from a group's pending_requests queue. |
| 255 | void ProcessPendingRequest(const std::string& group_name, Group* group); |
| 256 | |
[email protected] | 2ab05b5 | 2009-07-01 23:57:58 | [diff] [blame] | 257 | // Assigns |socket| to |handle| and updates |group|'s counters appropriately. |
| 258 | void HandOutSocket(ClientSocket* socket, |
| 259 | bool reused, |
| 260 | ClientSocketHandle* handle, |
| 261 | Group* group); |
| 262 | |
[email protected] | 5fc08e3 | 2009-07-15 17:09:57 | [diff] [blame] | 263 | // Adds |socket| to the list of idle sockets for |group|. |used| indicates |
| 264 | // whether or not the socket has previously been used. |
| 265 | void AddIdleSocket(ClientSocket* socket, bool used, Group* group); |
| 266 | |
| 267 | // Iterates through |connect_job_map_|, canceling all ConnectJobs. |
| 268 | // Afterwards, it iterates through all groups and deletes them if they are no |
| 269 | // longer needed. |
| 270 | void CancelAllConnectJobs(); |
| 271 | |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 272 | GroupMap group_map_; |
| 273 | |
| 274 | ConnectJobMap connect_job_map_; |
| 275 | |
| 276 | // Timer used to periodically prune idle sockets that timed out or can't be |
| 277 | // reused. |
| 278 | base::RepeatingTimer<ClientSocketPoolBase> timer_; |
| 279 | |
| 280 | // The total number of idle sockets in the system. |
| 281 | int idle_socket_count_; |
| 282 | |
| 283 | // The maximum number of sockets kept per group. |
| 284 | const int max_sockets_per_group_; |
| 285 | |
[email protected] | ab83889 | 2009-06-30 18:49:05 | [diff] [blame] | 286 | const scoped_ptr<ConnectJobFactory> connect_job_factory_; |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 287 | |
[email protected] | 5fc08e3 | 2009-07-15 17:09:57 | [diff] [blame] | 288 | // Controls whether or not we use late binding of sockets. |
| 289 | static bool g_late_binding; |
| 290 | |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 291 | DISALLOW_COPY_AND_ASSIGN(ClientSocketPoolBase); |
| 292 | }; |
| 293 | |
| 294 | } // namespace net |
| 295 | |
| 296 | #endif // NET_SOCKET_CLIENT_SOCKET_POOL_BASE_H_ |