| Index: net/socket/websocket_endpoint_lock_manager.cc
|
| diff --git a/net/socket/websocket_endpoint_lock_manager.cc b/net/socket/websocket_endpoint_lock_manager.cc
|
| index e578bb2435b314cb18fb223e749b7093ec4c2c60..1bccb1df36be0fce33cc24ae54d146e57586e31e 100644
|
| --- a/net/socket/websocket_endpoint_lock_manager.cc
|
| +++ b/net/socket/websocket_endpoint_lock_manager.cc
|
| @@ -6,12 +6,23 @@
|
|
|
| #include <utility>
|
|
|
| +#include "base/bind.h"
|
| #include "base/logging.h"
|
| +#include "base/message_loop/message_loop.h"
|
| #include "net/base/net_errors.h"
|
| #include "net/base/net_log.h"
|
|
|
| namespace net {
|
|
|
| +namespace {
|
| +
|
| +// This delay prevents DoS attacks.
|
| +// TODO(ricea): Replace this with randomised truncated exponential backoff.
|
| +// See crbug.com/377613.
|
| +const int kUnlockDelayInMs = 10;
|
| +
|
| +} // namespace
|
| +
|
| WebSocketEndpointLockManager::Waiter::~Waiter() {
|
| if (next()) {
|
| DCHECK(previous());
|
| @@ -65,23 +76,31 @@ void WebSocketEndpointLockManager::UnlockSocket(StreamSocket* socket) {
|
| << lock_info_it->first.ToString() << " ("
|
| << socket_lock_info_map_.size() << " socket(s) left)";
|
| socket_lock_info_map_.erase(socket_it);
|
| - DCHECK(socket == lock_info_it->second.socket);
|
| + DCHECK_EQ(socket, lock_info_it->second.socket);
|
| lock_info_it->second.socket = NULL;
|
| - UnlockEndpointByIterator(lock_info_it);
|
| + UnlockEndpointAfterDelay(lock_info_it->first);
|
| }
|
|
|
| void WebSocketEndpointLockManager::UnlockEndpoint(const IPEndPoint& endpoint) {
|
| LockInfoMap::iterator lock_info_it = lock_info_map_.find(endpoint);
|
| if (lock_info_it == lock_info_map_.end())
|
| return;
|
| -
|
| - UnlockEndpointByIterator(lock_info_it);
|
| + if (lock_info_it->second.socket)
|
| + EraseSocket(lock_info_it);
|
| + UnlockEndpointAfterDelay(endpoint);
|
| }
|
|
|
| bool WebSocketEndpointLockManager::IsEmpty() const {
|
| return lock_info_map_.empty() && socket_lock_info_map_.empty();
|
| }
|
|
|
| +base::TimeDelta WebSocketEndpointLockManager::SetUnlockDelayForTesting(
|
| + base::TimeDelta new_delay) {
|
| + base::TimeDelta old_delay = unlock_delay_;
|
| + unlock_delay_ = new_delay;
|
| + return old_delay;
|
| +}
|
| +
|
| WebSocketEndpointLockManager::LockInfo::LockInfo() : socket(NULL) {}
|
| WebSocketEndpointLockManager::LockInfo::~LockInfo() {
|
| DCHECK(!socket);
|
| @@ -92,17 +111,37 @@ WebSocketEndpointLockManager::LockInfo::LockInfo(const LockInfo& rhs)
|
| DCHECK(!rhs.queue);
|
| }
|
|
|
| -WebSocketEndpointLockManager::WebSocketEndpointLockManager() {}
|
| +WebSocketEndpointLockManager::WebSocketEndpointLockManager()
|
| + : unlock_delay_(base::TimeDelta::FromMilliseconds(kUnlockDelayInMs)),
|
| + pending_unlock_count_(0),
|
| + weak_factory_(this) {
|
| +}
|
|
|
| WebSocketEndpointLockManager::~WebSocketEndpointLockManager() {
|
| - DCHECK(lock_info_map_.empty());
|
| + DCHECK_EQ(lock_info_map_.size(), pending_unlock_count_);
|
| DCHECK(socket_lock_info_map_.empty());
|
| }
|
|
|
| -void WebSocketEndpointLockManager::UnlockEndpointByIterator(
|
| - LockInfoMap::iterator lock_info_it) {
|
| - if (lock_info_it->second.socket)
|
| - EraseSocket(lock_info_it);
|
| +void WebSocketEndpointLockManager::UnlockEndpointAfterDelay(
|
| + const IPEndPoint& endpoint) {
|
| + DVLOG(3) << "Delaying " << unlock_delay_.InMilliseconds()
|
| + << "ms before unlocking endpoint " << endpoint.ToString();
|
| + ++pending_unlock_count_;
|
| + base::MessageLoop::current()->PostDelayedTask(
|
| + FROM_HERE,
|
| + base::Bind(&WebSocketEndpointLockManager::DelayedUnlockEndpoint,
|
| + weak_factory_.GetWeakPtr(), endpoint),
|
| + unlock_delay_);
|
| +}
|
| +
|
| +void WebSocketEndpointLockManager::DelayedUnlockEndpoint(
|
| + const IPEndPoint& endpoint) {
|
| + LockInfoMap::iterator lock_info_it = lock_info_map_.find(endpoint);
|
| + DCHECK_GT(pending_unlock_count_, 0U);
|
| + --pending_unlock_count_;
|
| + if (lock_info_it == lock_info_map_.end())
|
| + return;
|
| + DCHECK(!lock_info_it->second.socket);
|
| LockInfo::WaiterQueue* queue = lock_info_it->second.queue.get();
|
| DCHECK(queue);
|
| if (queue->empty()) {
|
| @@ -115,7 +154,6 @@ void WebSocketEndpointLockManager::UnlockEndpointByIterator(
|
| << " and activating next waiter";
|
| Waiter* next_job = queue->head()->value();
|
| next_job->RemoveFromList();
|
| - // This must be last to minimise the excitement caused by re-entrancy.
|
| next_job->GotEndpointLock();
|
| }
|
|
|
|
|