blob: b98f9fdae9a5d99bdcfc737f60541e96788c16e4 [file] [log] [blame]
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/socket/socks_client_socket_pool.h"
#include "base/time.h"
#include "googleurl/src/gurl.h"
#include "net/base/net_errors.h"
#include "net/socket/client_socket_factory.h"
#include "net/socket/client_socket_handle.h"
#include "net/socket/client_socket_pool_base.h"
#include "net/socket/socks5_client_socket.h"
#include "net/socket/socks_client_socket.h"
namespace net {
// SOCKSConnectJobs will time out after this many seconds. Note this is on
// top of the timeout for the transport socket.
static const int kSOCKSConnectJobTimeoutInSeconds = 30;
SOCKSConnectJob::SOCKSConnectJob(
const std::string& group_name,
const SOCKSSocketParams& socks_params,
const base::TimeDelta& timeout_duration,
const scoped_refptr<TCPClientSocketPool>& tcp_pool,
const scoped_refptr<HostResolver>& host_resolver,
Delegate* delegate,
const BoundNetLog& net_log)
: ConnectJob(group_name, timeout_duration, delegate, net_log),
socks_params_(socks_params),
tcp_pool_(tcp_pool),
resolver_(host_resolver),
ALLOW_THIS_IN_INITIALIZER_LIST(
callback_(this, &SOCKSConnectJob::OnIOComplete)) {}
SOCKSConnectJob::~SOCKSConnectJob() {
// We don't worry about cancelling the tcp socket since the destructor in
// scoped_ptr<ClientSocketHandle> tcp_socket_handle_ will take care of it.
}
LoadState SOCKSConnectJob::GetLoadState() const {
switch (next_state_) {
case kStateTCPConnect:
case kStateTCPConnectComplete:
return tcp_socket_handle_->GetLoadState();
case kStateSOCKSConnect:
case kStateSOCKSConnectComplete:
return LOAD_STATE_CONNECTING;
default:
NOTREACHED();
return LOAD_STATE_IDLE;
}
}
int SOCKSConnectJob::ConnectInternal() {
next_state_ = kStateTCPConnect;
return DoLoop(OK);
}
void SOCKSConnectJob::OnIOComplete(int result) {
int rv = DoLoop(result);
if (rv != ERR_IO_PENDING)
NotifyDelegateOfCompletion(rv); // Deletes |this|
}
int SOCKSConnectJob::DoLoop(int result) {
DCHECK_NE(next_state_, kStateNone);
int rv = result;
do {
State state = next_state_;
next_state_ = kStateNone;
switch (state) {
case kStateTCPConnect:
DCHECK_EQ(OK, rv);
rv = DoTCPConnect();
break;
case kStateTCPConnectComplete:
rv = DoTCPConnectComplete(rv);
break;
case kStateSOCKSConnect:
DCHECK_EQ(OK, rv);
rv = DoSOCKSConnect();
break;
case kStateSOCKSConnectComplete:
rv = DoSOCKSConnectComplete(rv);
break;
default:
NOTREACHED() << "bad state";
rv = ERR_FAILED;
break;
}
} while (rv != ERR_IO_PENDING && next_state_ != kStateNone);
return rv;
}
int SOCKSConnectJob::DoTCPConnect() {
next_state_ = kStateTCPConnectComplete;
tcp_socket_handle_.reset(new ClientSocketHandle());
return tcp_socket_handle_->Init(group_name(), socks_params_.tcp_params(),
socks_params_.destination().priority(),
&callback_, tcp_pool_, net_log());
}
int SOCKSConnectJob::DoTCPConnectComplete(int result) {
if (result != OK)
return result;
// Reset the timer to just the length of time allowed for SOCKS handshake
// so that a fast TCP connection plus a slow SOCKS failure doesn't take
// longer to timeout than it should.
ResetTimer(base::TimeDelta::FromSeconds(kSOCKSConnectJobTimeoutInSeconds));
next_state_ = kStateSOCKSConnect;
return result;
}
int SOCKSConnectJob::DoSOCKSConnect() {
next_state_ = kStateSOCKSConnectComplete;
// Add a SOCKS connection on top of the tcp socket.
if (socks_params_.is_socks_v5()) {
socket_.reset(new SOCKS5ClientSocket(tcp_socket_handle_.release(),
socks_params_.destination()));
} else {
socket_.reset(new SOCKSClientSocket(tcp_socket_handle_.release(),
socks_params_.destination(),
resolver_));
}
return socket_->Connect(&callback_);
}
int SOCKSConnectJob::DoSOCKSConnectComplete(int result) {
if (result != OK) {
socket_->Disconnect();
return result;
}
set_socket(socket_.release());
return result;
}
ConnectJob* SOCKSClientSocketPool::SOCKSConnectJobFactory::NewConnectJob(
const std::string& group_name,
const PoolBase::Request& request,
ConnectJob::Delegate* delegate,
const BoundNetLog& net_log) const {
return new SOCKSConnectJob(group_name, request.params(), ConnectionTimeout(),
tcp_pool_, host_resolver_, delegate, net_log);
}
base::TimeDelta
SOCKSClientSocketPool::SOCKSConnectJobFactory::ConnectionTimeout() const {
return tcp_pool_->ConnectionTimeout() +
base::TimeDelta::FromSeconds(kSOCKSConnectJobTimeoutInSeconds);
}
SOCKSClientSocketPool::SOCKSClientSocketPool(
int max_sockets,
int max_sockets_per_group,
const std::string& name,
const scoped_refptr<HostResolver>& host_resolver,
const scoped_refptr<TCPClientSocketPool>& tcp_pool,
NetworkChangeNotifier* network_change_notifier)
: base_(max_sockets, max_sockets_per_group, name,
base::TimeDelta::FromSeconds(kUnusedIdleSocketTimeout),
base::TimeDelta::FromSeconds(kUsedIdleSocketTimeout),
new SOCKSConnectJobFactory(tcp_pool, host_resolver),
network_change_notifier) {}
SOCKSClientSocketPool::~SOCKSClientSocketPool() {}
int SOCKSClientSocketPool::RequestSocket(
const std::string& group_name,
const void* socket_params,
RequestPriority priority,
ClientSocketHandle* handle,
CompletionCallback* callback,
const BoundNetLog& net_log) {
const SOCKSSocketParams* casted_socket_params =
static_cast<const SOCKSSocketParams*>(socket_params);
return base_.RequestSocket(group_name, *casted_socket_params, priority,
handle, callback, net_log);
}
void SOCKSClientSocketPool::CancelRequest(
const std::string& group_name,
const ClientSocketHandle* handle) {
base_.CancelRequest(group_name, handle);
}
void SOCKSClientSocketPool::ReleaseSocket(
const std::string& group_name,
ClientSocket* socket) {
base_.ReleaseSocket(group_name, socket);
}
void SOCKSClientSocketPool::CloseIdleSockets() {
base_.CloseIdleSockets();
}
int SOCKSClientSocketPool::IdleSocketCountInGroup(
const std::string& group_name) const {
return base_.IdleSocketCountInGroup(group_name);
}
LoadState SOCKSClientSocketPool::GetLoadState(
const std::string& group_name, const ClientSocketHandle* handle) const {
return base_.GetLoadState(group_name, handle);
}
} // namespace net