blob: 61b408621c05dbe0cced5d938b0b9469a52dca02 [file] [log] [blame]
// Copyright (c) 2012 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/websockets/websocket_throttle.h"
#include <string>
#include "base/hash_tables.h"
#include "base/memory/ref_counted.h"
#include "base/memory/singleton.h"
#include "base/message_loop.h"
#include "base/string_number_conversions.h"
#include "base/string_util.h"
#include "base/stringprintf.h"
#include "net/base/io_buffer.h"
#include "net/socket_stream/socket_stream.h"
#include "net/websockets/websocket_job.h"
namespace net {
static std::string IPEndPointToHashkey(const IPEndPoint& endpoint) {
return base::StringPrintf("%d:%s",
endpoint.GetFamily(),
base::HexEncode(&endpoint.address()[0],
endpoint.address().size()).c_str());
}
WebSocketThrottle::WebSocketThrottle() {
}
WebSocketThrottle::~WebSocketThrottle() {
DCHECK(queue_.empty());
DCHECK(addr_map_.empty());
}
// static
WebSocketThrottle* WebSocketThrottle::GetInstance() {
return Singleton<WebSocketThrottle>::get();
}
void WebSocketThrottle::PutInQueue(WebSocketJob* job) {
queue_.push_back(job);
const AddressList& address_list = job->address_list();
base::hash_set<std::string> address_set;
for (AddressList::const_iterator addr_iter = address_list.begin();
addr_iter != address_list.end();
++addr_iter) {
std::string addrkey = IPEndPointToHashkey(*addr_iter);
// If |addrkey| is already processed, don't do it again.
if (address_set.find(addrkey) != address_set.end())
continue;
address_set.insert(addrkey);
ConnectingAddressMap::iterator iter = addr_map_.find(addrkey);
if (iter == addr_map_.end()) {
ConnectingQueue* queue = new ConnectingQueue();
queue->push_back(job);
addr_map_[addrkey] = queue;
} else {
iter->second->push_back(job);
job->SetWaiting();
DVLOG(1) << "Waiting on " << addrkey;
}
}
}
void WebSocketThrottle::RemoveFromQueue(WebSocketJob* job) {
bool in_queue = false;
for (ConnectingQueue::iterator iter = queue_.begin();
iter != queue_.end();
++iter) {
if (*iter == job) {
queue_.erase(iter);
in_queue = true;
break;
}
}
if (!in_queue)
return;
const AddressList& address_list = job->address_list();
base::hash_set<std::string> address_set;
for (AddressList::const_iterator addr_iter = address_list.begin();
addr_iter != address_list.end();
++addr_iter) {
std::string addrkey = IPEndPointToHashkey(*addr_iter);
// If |addrkey| is already processed, don't do it again.
if (address_set.find(addrkey) != address_set.end())
continue;
address_set.insert(addrkey);
ConnectingAddressMap::iterator iter = addr_map_.find(addrkey);
DCHECK(iter != addr_map_.end());
ConnectingQueue* queue = iter->second;
// Job may not be front of queue when job is closed early while waiting.
for (ConnectingQueue::iterator iter = queue->begin();
iter != queue->end();
++iter) {
if (*iter == job) {
queue->erase(iter);
break;
}
}
if (queue->empty()) {
delete queue;
addr_map_.erase(iter);
}
}
}
void WebSocketThrottle::WakeupSocketIfNecessary() {
for (ConnectingQueue::iterator iter = queue_.begin();
iter != queue_.end();
++iter) {
WebSocketJob* job = *iter;
if (!job->IsWaiting())
continue;
bool should_wakeup = true;
const AddressList& address_list = job->address_list();
for (AddressList::const_iterator addr_iter = address_list.begin();
addr_iter != address_list.end();
++addr_iter) {
std::string addrkey = IPEndPointToHashkey(*addr_iter);
ConnectingAddressMap::iterator iter = addr_map_.find(addrkey);
DCHECK(iter != addr_map_.end());
ConnectingQueue* queue = iter->second;
if (job != queue->front()) {
should_wakeup = false;
break;
}
}
if (should_wakeup)
job->Wakeup();
}
}
} // namespace net