1// Copyright 2014 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_WEBSOCKET_TRANSPORT_CLIENT_SOCKET_POOL_H_
6#define NET_SOCKET_WEBSOCKET_TRANSPORT_CLIENT_SOCKET_POOL_H_
7
8#include <list>
9#include <map>
10#include <set>
11#include <string>
12
13#include "base/basictypes.h"
14#include "base/memory/ref_counted.h"
15#include "base/memory/scoped_ptr.h"
16#include "base/memory/weak_ptr.h"
17#include "base/time/time.h"
18#include "base/timer/timer.h"
19#include "net/base/net_export.h"
20#include "net/base/net_log.h"
21#include "net/socket/client_socket_pool.h"
22#include "net/socket/client_socket_pool_base.h"
23#include "net/socket/transport_client_socket_pool.h"
24
25namespace net {
26
27class ClientSocketFactory;
28class ClientSocketPoolHistograms;
29class HostResolver;
30class NetLog;
31class WebSocketEndpointLockManager;
32class WebSocketTransportConnectSubJob;
33
34// WebSocketTransportConnectJob handles the host resolution necessary for socket
35// creation and the TCP connect. WebSocketTransportConnectJob also has fallback
36// logic for IPv6 connect() timeouts (which may happen due to networks / routers
37// with broken IPv6 support). Those timeouts take 20s, so rather than make the
38// user wait 20s for the timeout to fire, we use a fallback timer
39// (kIPv6FallbackTimerInMs) and start a connect() to an IPv4 address if the
40// timer fires. Then we race the IPv4 connect(s) against the IPv6 connect(s) and
41// use the socket that completes successfully first or fails last.
42class NET_EXPORT_PRIVATE WebSocketTransportConnectJob : public ConnectJob {
43 public:
44  WebSocketTransportConnectJob(
45      const std::string& group_name,
46      RequestPriority priority,
47      const scoped_refptr<TransportSocketParams>& params,
48      base::TimeDelta timeout_duration,
49      const CompletionCallback& callback,
50      ClientSocketFactory* client_socket_factory,
51      HostResolver* host_resolver,
52      ClientSocketHandle* handle,
53      Delegate* delegate,
54      NetLog* pool_net_log,
55      const BoundNetLog& request_net_log);
56  virtual ~WebSocketTransportConnectJob();
57
58  // Unlike normal socket pools, the WebSocketTransportClientPool uses
59  // early-binding of sockets.
60  ClientSocketHandle* handle() const { return handle_; }
61
62  // Stash the callback from RequestSocket() here for convenience.
63  const CompletionCallback& callback() const { return callback_; }
64
65  const BoundNetLog& request_net_log() const { return request_net_log_; }
66
67  // ConnectJob methods.
68  virtual LoadState GetLoadState() const OVERRIDE;
69
70 private:
71  friend class WebSocketTransportConnectSubJob;
72  friend class TransportConnectJobHelper;
73  friend class WebSocketEndpointLockManager;
74
75  // Although it is not strictly necessary, it makes the code simpler if each
76  // subjob knows what type it is.
77  enum SubJobType { SUB_JOB_IPV4, SUB_JOB_IPV6 };
78
79  int DoResolveHost();
80  int DoResolveHostComplete(int result);
81  int DoTransportConnect();
82  int DoTransportConnectComplete(int result);
83
84  // Called back from a SubJob when it completes.
85  void OnSubJobComplete(int result, WebSocketTransportConnectSubJob* job);
86
87  // Called from |fallback_timer_|.
88  void StartIPv4JobAsync();
89
90  // Begins the host resolution and the TCP connect.  Returns OK on success
91  // and ERR_IO_PENDING if it cannot immediately service the request.
92  // Otherwise, it returns a net error code.
93  virtual int ConnectInternal() OVERRIDE;
94
95  TransportConnectJobHelper helper_;
96
97  // The addresses are divided into IPv4 and IPv6, which are performed partially
98  // in parallel. If the list of IPv6 addresses is non-empty, then the IPv6 jobs
99  // go first, followed after |kIPv6FallbackTimerInMs| by the IPv4
100  // addresses. First sub-job to establish a connection wins.
101  scoped_ptr<WebSocketTransportConnectSubJob> ipv4_job_;
102  scoped_ptr<WebSocketTransportConnectSubJob> ipv6_job_;
103
104  base::OneShotTimer<WebSocketTransportConnectJob> fallback_timer_;
105  TransportConnectJobHelper::ConnectionLatencyHistogram race_result_;
106  ClientSocketHandle* const handle_;
107  CompletionCallback callback_;
108  BoundNetLog request_net_log_;
109
110  bool had_ipv4_;
111  bool had_ipv6_;
112
113  DISALLOW_COPY_AND_ASSIGN(WebSocketTransportConnectJob);
114};
115
116class NET_EXPORT_PRIVATE WebSocketTransportClientSocketPool
117    : public TransportClientSocketPool {
118 public:
119  WebSocketTransportClientSocketPool(int max_sockets,
120                                     int max_sockets_per_group,
121                                     ClientSocketPoolHistograms* histograms,
122                                     HostResolver* host_resolver,
123                                     ClientSocketFactory* client_socket_factory,
124                                     NetLog* net_log);
125
126  virtual ~WebSocketTransportClientSocketPool();
127
128  // Allow another connection to be started to the IPEndPoint that this |handle|
129  // is connected to. Used when the WebSocket handshake completes successfully.
130  // This only works if the socket is connected, however the caller does not
131  // need to explicitly check for this. Instead, ensure that dead sockets are
132  // returned to ReleaseSocket() in a timely fashion.
133  static void UnlockEndpoint(ClientSocketHandle* handle);
134
135  // ClientSocketPool implementation.
136  virtual int RequestSocket(const std::string& group_name,
137                            const void* resolve_info,
138                            RequestPriority priority,
139                            ClientSocketHandle* handle,
140                            const CompletionCallback& callback,
141                            const BoundNetLog& net_log) OVERRIDE;
142  virtual void RequestSockets(const std::string& group_name,
143                              const void* params,
144                              int num_sockets,
145                              const BoundNetLog& net_log) OVERRIDE;
146  virtual void CancelRequest(const std::string& group_name,
147                             ClientSocketHandle* handle) OVERRIDE;
148  virtual void ReleaseSocket(const std::string& group_name,
149                             scoped_ptr<StreamSocket> socket,
150                             int id) OVERRIDE;
151  virtual void FlushWithError(int error) OVERRIDE;
152  virtual void CloseIdleSockets() OVERRIDE;
153  virtual int IdleSocketCount() const OVERRIDE;
154  virtual int IdleSocketCountInGroup(
155      const std::string& group_name) const OVERRIDE;
156  virtual LoadState GetLoadState(
157      const std::string& group_name,
158      const ClientSocketHandle* handle) const OVERRIDE;
159  virtual base::DictionaryValue* GetInfoAsValue(
160      const std::string& name,
161      const std::string& type,
162      bool include_nested_pools) const OVERRIDE;
163  virtual base::TimeDelta ConnectionTimeout() const OVERRIDE;
164  virtual ClientSocketPoolHistograms* histograms() const OVERRIDE;
165
166  // HigherLayeredPool implementation.
167  virtual bool IsStalled() const OVERRIDE;
168
169 private:
170  class ConnectJobDelegate : public ConnectJob::Delegate {
171   public:
172    explicit ConnectJobDelegate(WebSocketTransportClientSocketPool* owner);
173    virtual ~ConnectJobDelegate();
174
175    virtual void OnConnectJobComplete(int result, ConnectJob* job) OVERRIDE;
176
177   private:
178    WebSocketTransportClientSocketPool* owner_;
179
180    DISALLOW_COPY_AND_ASSIGN(ConnectJobDelegate);
181  };
182
183  // Store the arguments from a call to RequestSocket() that has stalled so we
184  // can replay it when there are available socket slots.
185  struct StalledRequest {
186    StalledRequest(const scoped_refptr<TransportSocketParams>& params,
187                   RequestPriority priority,
188                   ClientSocketHandle* handle,
189                   const CompletionCallback& callback,
190                   const BoundNetLog& net_log);
191    ~StalledRequest();
192    const scoped_refptr<TransportSocketParams> params;
193    const RequestPriority priority;
194    ClientSocketHandle* const handle;
195    const CompletionCallback callback;
196    const BoundNetLog net_log;
197  };
198  friend class ConnectJobDelegate;
199  typedef std::map<const ClientSocketHandle*, WebSocketTransportConnectJob*>
200      PendingConnectsMap;
201  // This is a list so that we can remove requests from the middle, and also
202  // so that iterators are not invalidated unless the corresponding request is
203  // removed.
204  typedef std::list<StalledRequest> StalledRequestQueue;
205  typedef std::map<const ClientSocketHandle*, StalledRequestQueue::iterator>
206      StalledRequestMap;
207
208  void OnConnectJobComplete(int result, WebSocketTransportConnectJob* job);
209  void InvokeUserCallbackLater(ClientSocketHandle* handle,
210                               const CompletionCallback& callback,
211                               int rv);
212  void InvokeUserCallback(ClientSocketHandle* handle,
213                          const CompletionCallback& callback,
214                          int rv);
215  bool ReachedMaxSocketsLimit() const;
216  void HandOutSocket(scoped_ptr<StreamSocket> socket,
217                     const LoadTimingInfo::ConnectTiming& connect_timing,
218                     ClientSocketHandle* handle,
219                     const BoundNetLog& net_log);
220  void AddJob(ClientSocketHandle* handle,
221              scoped_ptr<WebSocketTransportConnectJob> connect_job);
222  bool DeleteJob(ClientSocketHandle* handle);
223  const WebSocketTransportConnectJob* LookupConnectJob(
224      const ClientSocketHandle* handle) const;
225  void ActivateStalledRequest();
226  bool DeleteStalledRequest(ClientSocketHandle* handle);
227
228  ConnectJobDelegate connect_job_delegate_;
229  std::set<const ClientSocketHandle*> pending_callbacks_;
230  PendingConnectsMap pending_connects_;
231  StalledRequestQueue stalled_request_queue_;
232  StalledRequestMap stalled_request_map_;
233  ClientSocketPoolHistograms* const histograms_;
234  NetLog* const pool_net_log_;
235  ClientSocketFactory* const client_socket_factory_;
236  HostResolver* const host_resolver_;
237  const int max_sockets_;
238  int handed_out_socket_count_;
239  bool flushing_;
240
241  base::WeakPtrFactory<WebSocketTransportClientSocketPool> weak_factory_;
242
243  DISALLOW_COPY_AND_ASSIGN(WebSocketTransportClientSocketPool);
244};
245
246}  // namespace net
247
248#endif  // NET_SOCKET_WEBSOCKET_TRANSPORT_CLIENT_SOCKET_POOL_H_
249