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(); |
} |