Chromium Code Reviews| Index: net/dns/dns_session.cc | 
| diff --git a/net/dns/dns_session.cc b/net/dns/dns_session.cc | 
| index fb6c4b4c5ba269157bf7ca29cc7d5c06052ccda1..287303a1ec6dce0a6d21c0f95f7a80ca3cdc30b5 100644 | 
| --- a/net/dns/dns_session.cc | 
| +++ b/net/dns/dns_session.cc | 
| @@ -6,26 +6,111 @@ | 
| #include "base/basictypes.h" | 
| #include "base/bind.h" | 
| +#include "base/rand_util.h" | 
| +#include "base/stl_util.h" | 
| #include "base/time.h" | 
| #include "net/base/ip_endpoint.h" | 
| +#include "net/base/net_errors.h" | 
| #include "net/dns/dns_config_service.h" | 
| #include "net/socket/client_socket_factory.h" | 
| namespace net { | 
| +DnsSession::SocketLease::SocketLease(scoped_refptr<DnsSession> session, | 
| + int server_index, | 
| + scoped_ptr<DatagramClientSocket> socket) | 
| + : session_(session), server_index_(server_index), socket_(socket.Pass()) {} | 
| + | 
| +DnsSession::SocketLease::~SocketLease() { | 
| + session_->FreeSocket(server_index_, socket_.Pass()); | 
| +} | 
| + | 
| +#if defined(OS_WIN) | 
| + | 
| +// Don't request specific (random) ports on Windows, since that will trigger | 
| +// firewall prompts. | 
| +static const DatagramSocket::BindType kBindType = DatagramSocket::DEFAULT_BIND; | 
| +// Keep a buffer of the sockets the OS gives us to give some entropy in source | 
| +// ports. | 
| +static const size_t kMaxPoolSize = 256; | 
| +// TODO(ttuttle): I want 1024, but we get ERR_INSUFFICIENT_RESOURCES if we | 
| +// try to allocate that many. | 
| + | 
| +#else | 
| + | 
| +static const DatagramSocket::BindType kBindType = DatagramSocket::RANDOM_BIND; | 
| +static const size_t kMaxPoolSize = 0; | 
| + | 
| +#endif | 
| + | 
| DnsSession::DnsSession(const DnsConfig& config, | 
| ClientSocketFactory* factory, | 
| const RandIntCallback& rand_int_callback, | 
| NetLog* net_log) | 
| : config_(config), | 
| socket_factory_(factory), | 
| - rand_callback_(base::Bind(rand_int_callback, 0, kuint16max)), | 
| + rand_int_callback_(rand_int_callback), | 
| net_log_(net_log), | 
| + pools_(0), | 
| server_index_(0) { | 
| + CreateDefaultPools(kMaxPoolSize); | 
| +} | 
| + | 
| +DnsSession::DnsSession(const DnsConfig& config, | 
| + ClientSocketFactory* socket_factory, | 
| + const RandIntCallback& rand_int_callback, | 
| + int pool_size, | 
| + NetLog* net_log) | 
| + : config_(config), | 
| + socket_factory_(socket_factory), | 
| + rand_int_callback_(rand_int_callback), | 
| + net_log_(net_log), | 
| + pools_(0), | 
| + server_index_(0) { | 
| + CreateDefaultPools(pool_size); | 
| +} | 
| + | 
| +DnsSession::DnsSession(const DnsConfig& config, | 
| + ClientSocketFactory* socket_factory, | 
| + const RandIntCallback& rand_int_callback, | 
| + const SocketPoolFactoryCallback& pool_factory_callback, | 
| + NetLog* net_log) | 
| + : config_(config), | 
| + socket_factory_(socket_factory), | 
| + rand_int_callback_(rand_int_callback), | 
| + net_log_(net_log), | 
| + pools_(0), | 
| + server_index_(0) { | 
| + CreatePools(pool_factory_callback); | 
| +} | 
| + | 
| +void DnsSession::CreatePools( | 
| + const SocketPoolFactoryCallback& pool_factory_callback) { | 
| + CHECK(pools_.empty()); | 
| + | 
| + for (unsigned i = 0; i < config_.nameservers.size(); i++) { | 
| + EndPointSocketFactoryCallback socket_factory = | 
| + base::Bind(&DnsSession::CreateConnectedSocket, | 
| + this, | 
| + config_.nameservers[i]); | 
| + SocketPool* pool = pool_factory_callback.Run(socket_factory); | 
| + pools_.push_back(pool); | 
| + } | 
| +} | 
| + | 
| +void DnsSession::CreateDefaultPools(int pool_size) { | 
| + CreatePools(base::Bind(SocketPoolImpl::CreateSocketPool, | 
| + pool_size, | 
| + rand_int_callback_)); | 
| +} | 
| + | 
| +DnsSession::~DnsSession() { | 
| + STLDeleteElements(&pools_); | 
| } | 
| int DnsSession::NextQueryId() const { | 
| - return rand_callback_.Run(); | 
| + /* NB: This callback is also used for selecting sockets from the pool. */ | 
| 
 
szym
2012/09/10 19:31:00
I think the RNG used for selecting sockets from th
 
 | 
| + return rand_int_callback_.Run(0, kuint16max); | 
| } | 
| int DnsSession::NextFirstServerIndex() { | 
| @@ -41,7 +126,107 @@ base::TimeDelta DnsSession::NextTimeout(int attempt) { | 
| return config_.timeout * (1 << (attempt / config_.nameservers.size())); | 
| } | 
| -DnsSession::~DnsSession() {} | 
| +DatagramClientSocket* DnsSession::CreateConnectedSocket( | 
| + const IPEndPoint& endpoint) { | 
| + DatagramClientSocket* socket; | 
| + | 
| + socket = socket_factory_->CreateDatagramClientSocket( | 
| + kBindType, | 
| + base::Bind(&base::RandInt), | 
| + net_log_, | 
| + NetLog::Source()); | 
| + if (!socket) | 
| + return NULL; | 
| + | 
| + int rv = socket->Connect(endpoint); | 
| + if (rv != OK) { | 
| + delete socket; | 
| + return NULL; | 
| + } | 
| + | 
| + return socket; | 
| +} | 
| + | 
| +DnsSession::SocketPoolImpl::SocketPoolImpl( | 
| + unsigned pool_size, | 
| + const RandIntCallback& rand_int_callback, | 
| + const EndPointSocketFactoryCallback& factory_callback) | 
| + : pool_(0), | 
| + pool_size_(pool_size), | 
| + rand_int_callback_(rand_int_callback), | 
| + factory_callback_(factory_callback) { | 
| + FillPool(); | 
| +} | 
| + | 
| +DnsSession::SocketPoolImpl::~SocketPoolImpl() { | 
| + STLDeleteElements(&pool_); | 
| +} | 
| + | 
| +// static | 
| +DnsSession::SocketPool* DnsSession::SocketPoolImpl::CreateSocketPool( | 
| + unsigned pool_size, | 
| + const RandIntCallback& rand_int_callback, | 
| + const EndPointSocketFactoryCallback& factory_callback) { | 
| + SocketPoolImpl* socket_pool = new SocketPoolImpl(pool_size, | 
| + rand_int_callback, | 
| + factory_callback); | 
| + return static_cast<SocketPool*>(socket_pool); | 
| +} | 
| + | 
| +scoped_ptr<DatagramClientSocket> DnsSession::SocketPoolImpl::GetSocket() { | 
| + DatagramClientSocket* socket = NULL; | 
| + | 
| + if (!pool_.empty()) { | 
| + unsigned index = rand_int_callback_.Run(0, pool_.size() - 1); | 
| + socket = pool_[index]; | 
| + pool_[index] = pool_.back(); | 
| + pool_.pop_back(); | 
| + } | 
| + | 
| + if (!socket) | 
| + socket = factory_callback_.Run(); | 
| + | 
| + return scoped_ptr<DatagramClientSocket>(socket); | 
| +} | 
| + | 
| +void DnsSession::SocketPoolImpl::PutSocket( | 
| + scoped_ptr<DatagramClientSocket> socket) { | 
| + // Do nothing, so the socket is destroyed. | 
| +} | 
| + | 
| +void DnsSession::SocketPoolImpl::FillPool() { | 
| + while (pool_.size() < pool_size_) { | 
| + DatagramClientSocket* socket = factory_callback_.Run(); | 
| + if (!socket) | 
| + return; | 
| + pool_.push_back(socket); | 
| + } | 
| +} | 
| + | 
| +// Allocate a socket, already connected to the server address. | 
| +scoped_ptr<DnsSession::SocketLease> DnsSession::AllocateSocket(int index) { | 
| + CHECK(index >= 0); | 
| 
 
szym
2012/09/10 19:31:00
Consider making |index| unsigned.
 
Deprecated (see juliatuttle)
2012/09/14 15:28:18
Done.
 
 | 
| + CHECK(index < (int)config_.nameservers.size()); | 
| + DCHECK(pools_.size() == config_.nameservers.size()); | 
| + | 
| + scoped_ptr<DatagramClientSocket> socket(pools_[index]->GetSocket()); | 
| + | 
| + if (socket.get()) | 
| 
 
szym
2012/09/10 19:31:00
nit: needs braces.
 
 | 
| + return scoped_ptr<SocketLease>(new SocketLease(this, index, socket.Pass())); | 
| + else | 
| + return scoped_ptr<SocketLease>(NULL); | 
| +} | 
| + | 
| +// Release a socket. | 
| +void DnsSession::FreeSocket(int index, | 
| + scoped_ptr<DatagramClientSocket> socket) { | 
| + CHECK(index >= 0); | 
| + CHECK(index < (int)config_.nameservers.size()); | 
| + DCHECK(pools_.size() == config_.nameservers.size()); | 
| + | 
| + pools_[index]->PutSocket(socket.Pass()); | 
| +} | 
| + | 
| } // namespace net |