socks_client_socket_pool.cc revision dfffe33b592d92936093aae70ed06488fc7798ed
1// Copyright (c) 2011 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#include "net/socket/socks_client_socket_pool.h"
6
7#include "base/time.h"
8#include "base/values.h"
9#include "googleurl/src/gurl.h"
10#include "net/base/net_errors.h"
11#include "net/socket/client_socket_factory.h"
12#include "net/socket/client_socket_handle.h"
13#include "net/socket/client_socket_pool_base.h"
14#include "net/socket/socks5_client_socket.h"
15#include "net/socket/socks_client_socket.h"
16#include "net/socket/transport_client_socket_pool.h"
17
18namespace net {
19
20SOCKSSocketParams::SOCKSSocketParams(
21    const scoped_refptr<TransportSocketParams>& proxy_server,
22    bool socks_v5,
23    const HostPortPair& host_port_pair,
24    RequestPriority priority,
25    const GURL& referrer)
26    : transport_params_(proxy_server),
27      destination_(host_port_pair),
28      socks_v5_(socks_v5) {
29  if (transport_params_)
30    ignore_limits_ = transport_params_->ignore_limits();
31  else
32    ignore_limits_ = false;
33  // The referrer is used by the DNS prefetch system to correlate resolutions
34  // with the page that triggered them. It doesn't impact the actual addresses
35  // that we resolve to.
36  destination_.set_referrer(referrer);
37  destination_.set_priority(priority);
38}
39
40SOCKSSocketParams::~SOCKSSocketParams() {}
41
42// SOCKSConnectJobs will time out after this many seconds.  Note this is on
43// top of the timeout for the transport socket.
44static const int kSOCKSConnectJobTimeoutInSeconds = 30;
45
46SOCKSConnectJob::SOCKSConnectJob(
47    const std::string& group_name,
48    const scoped_refptr<SOCKSSocketParams>& socks_params,
49    const base::TimeDelta& timeout_duration,
50    TransportClientSocketPool* transport_pool,
51    HostResolver* host_resolver,
52    Delegate* delegate,
53    NetLog* net_log)
54    : ConnectJob(group_name, timeout_duration, delegate,
55                 BoundNetLog::Make(net_log, NetLog::SOURCE_CONNECT_JOB)),
56      socks_params_(socks_params),
57      transport_pool_(transport_pool),
58      resolver_(host_resolver),
59      ALLOW_THIS_IN_INITIALIZER_LIST(
60          callback_(this, &SOCKSConnectJob::OnIOComplete)) {
61}
62
63SOCKSConnectJob::~SOCKSConnectJob() {
64  // We don't worry about cancelling the tcp socket since the destructor in
65  // scoped_ptr<ClientSocketHandle> transport_socket_handle_ will take care of
66  // it.
67}
68
69LoadState SOCKSConnectJob::GetLoadState() const {
70  switch (next_state_) {
71    case STATE_TRANSPORT_CONNECT:
72    case STATE_TRANSPORT_CONNECT_COMPLETE:
73      return transport_socket_handle_->GetLoadState();
74    case STATE_SOCKS_CONNECT:
75    case STATE_SOCKS_CONNECT_COMPLETE:
76      return LOAD_STATE_CONNECTING;
77    default:
78      NOTREACHED();
79      return LOAD_STATE_IDLE;
80  }
81}
82
83void SOCKSConnectJob::OnIOComplete(int result) {
84  int rv = DoLoop(result);
85  if (rv != ERR_IO_PENDING)
86    NotifyDelegateOfCompletion(rv);  // Deletes |this|
87}
88
89int SOCKSConnectJob::DoLoop(int result) {
90  DCHECK_NE(next_state_, STATE_NONE);
91
92  int rv = result;
93  do {
94    State state = next_state_;
95    next_state_ = STATE_NONE;
96    switch (state) {
97      case STATE_TRANSPORT_CONNECT:
98        DCHECK_EQ(OK, rv);
99        rv = DoTransportConnect();
100        break;
101      case STATE_TRANSPORT_CONNECT_COMPLETE:
102        rv = DoTransportConnectComplete(rv);
103        break;
104      case STATE_SOCKS_CONNECT:
105        DCHECK_EQ(OK, rv);
106        rv = DoSOCKSConnect();
107        break;
108      case STATE_SOCKS_CONNECT_COMPLETE:
109        rv = DoSOCKSConnectComplete(rv);
110        break;
111      default:
112        NOTREACHED() << "bad state";
113        rv = ERR_FAILED;
114        break;
115    }
116  } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
117
118  return rv;
119}
120
121int SOCKSConnectJob::DoTransportConnect() {
122  next_state_ = STATE_TRANSPORT_CONNECT_COMPLETE;
123  transport_socket_handle_.reset(new ClientSocketHandle());
124  return transport_socket_handle_->Init(group_name(),
125                                        socks_params_->transport_params(),
126                                        socks_params_->destination().priority(),
127                                        &callback_,
128                                        transport_pool_,
129                                        net_log());
130}
131
132int SOCKSConnectJob::DoTransportConnectComplete(int result) {
133  if (result != OK)
134    return ERR_PROXY_CONNECTION_FAILED;
135
136  // Reset the timer to just the length of time allowed for SOCKS handshake
137  // so that a fast TCP connection plus a slow SOCKS failure doesn't take
138  // longer to timeout than it should.
139  ResetTimer(base::TimeDelta::FromSeconds(kSOCKSConnectJobTimeoutInSeconds));
140  next_state_ = STATE_SOCKS_CONNECT;
141  return result;
142}
143
144int SOCKSConnectJob::DoSOCKSConnect() {
145  next_state_ = STATE_SOCKS_CONNECT_COMPLETE;
146
147  // Add a SOCKS connection on top of the tcp socket.
148  if (socks_params_->is_socks_v5()) {
149    socket_.reset(new SOCKS5ClientSocket(transport_socket_handle_.release(),
150                                         socks_params_->destination()));
151  } else {
152    socket_.reset(new SOCKSClientSocket(transport_socket_handle_.release(),
153                                        socks_params_->destination(),
154                                        resolver_));
155  }
156  return socket_->Connect(&callback_
157#ifdef ANDROID
158                          , socks_params_->ignore_limits()
159#endif
160                         );
161}
162
163int SOCKSConnectJob::DoSOCKSConnectComplete(int result) {
164  if (result != OK) {
165    socket_->Disconnect();
166    return result;
167  }
168
169  set_socket(socket_.release());
170  return result;
171}
172
173int SOCKSConnectJob::ConnectInternal() {
174  next_state_ = STATE_TRANSPORT_CONNECT;
175  return DoLoop(OK);
176}
177
178ConnectJob* SOCKSClientSocketPool::SOCKSConnectJobFactory::NewConnectJob(
179    const std::string& group_name,
180    const PoolBase::Request& request,
181    ConnectJob::Delegate* delegate) const {
182  return new SOCKSConnectJob(group_name,
183                             request.params(),
184                             ConnectionTimeout(),
185                             transport_pool_,
186                             host_resolver_,
187                             delegate,
188                             net_log_);
189}
190
191base::TimeDelta
192SOCKSClientSocketPool::SOCKSConnectJobFactory::ConnectionTimeout() const {
193  return transport_pool_->ConnectionTimeout() +
194      base::TimeDelta::FromSeconds(kSOCKSConnectJobTimeoutInSeconds);
195}
196
197SOCKSClientSocketPool::SOCKSClientSocketPool(
198    int max_sockets,
199    int max_sockets_per_group,
200    ClientSocketPoolHistograms* histograms,
201    HostResolver* host_resolver,
202    TransportClientSocketPool* transport_pool,
203    NetLog* net_log)
204    : transport_pool_(transport_pool),
205      base_(max_sockets, max_sockets_per_group, histograms,
206            base::TimeDelta::FromSeconds(
207                ClientSocketPool::unused_idle_socket_timeout()),
208            base::TimeDelta::FromSeconds(kUsedIdleSocketTimeout),
209            new SOCKSConnectJobFactory(transport_pool,
210                                       host_resolver,
211                                       net_log)) {
212}
213
214SOCKSClientSocketPool::~SOCKSClientSocketPool() {}
215
216int SOCKSClientSocketPool::RequestSocket(const std::string& group_name,
217                                         const void* socket_params,
218                                         RequestPriority priority,
219                                         ClientSocketHandle* handle,
220                                         CompletionCallback* callback,
221                                         const BoundNetLog& net_log) {
222  const scoped_refptr<SOCKSSocketParams>* casted_socket_params =
223      static_cast<const scoped_refptr<SOCKSSocketParams>*>(socket_params);
224
225  return base_.RequestSocket(group_name, *casted_socket_params, priority,
226                             handle, callback, net_log);
227}
228
229void SOCKSClientSocketPool::RequestSockets(
230    const std::string& group_name,
231    const void* params,
232    int num_sockets,
233    const BoundNetLog& net_log) {
234  const scoped_refptr<SOCKSSocketParams>* casted_params =
235      static_cast<const scoped_refptr<SOCKSSocketParams>*>(params);
236
237  base_.RequestSockets(group_name, *casted_params, num_sockets, net_log);
238}
239
240void SOCKSClientSocketPool::CancelRequest(const std::string& group_name,
241                                          ClientSocketHandle* handle) {
242  base_.CancelRequest(group_name, handle);
243}
244
245void SOCKSClientSocketPool::ReleaseSocket(const std::string& group_name,
246                                          ClientSocket* socket, int id) {
247  base_.ReleaseSocket(group_name, socket, id);
248}
249
250void SOCKSClientSocketPool::Flush() {
251  base_.Flush();
252}
253
254void SOCKSClientSocketPool::CloseIdleSockets() {
255  base_.CloseIdleSockets();
256}
257
258int SOCKSClientSocketPool::IdleSocketCount() const {
259  return base_.idle_socket_count();
260}
261
262int SOCKSClientSocketPool::IdleSocketCountInGroup(
263    const std::string& group_name) const {
264  return base_.IdleSocketCountInGroup(group_name);
265}
266
267LoadState SOCKSClientSocketPool::GetLoadState(
268    const std::string& group_name, const ClientSocketHandle* handle) const {
269  return base_.GetLoadState(group_name, handle);
270}
271
272DictionaryValue* SOCKSClientSocketPool::GetInfoAsValue(
273    const std::string& name,
274    const std::string& type,
275    bool include_nested_pools) const {
276  DictionaryValue* dict = base_.GetInfoAsValue(name, type);
277  if (include_nested_pools) {
278    ListValue* list = new ListValue();
279    list->Append(transport_pool_->GetInfoAsValue("transport_socket_pool",
280                                                 "transport_socket_pool",
281                                                 false));
282    dict->Set("nested_pools", list);
283  }
284  return dict;
285}
286
287base::TimeDelta SOCKSClientSocketPool::ConnectionTimeout() const {
288  return base_.ConnectionTimeout();
289}
290
291ClientSocketPoolHistograms* SOCKSClientSocketPool::histograms() const {
292  return base_.histograms();
293};
294
295}  // namespace net
296