Chromium Code Reviews| Index: content/browser/renderer_host/websocket_dispatcher_host.cc |
| diff --git a/content/browser/renderer_host/websocket_dispatcher_host.cc b/content/browser/renderer_host/websocket_dispatcher_host.cc |
| index d49199a7cb8f44e08ea03435a58905f63b618fc5..870b63070bad468c3868e4229bdb5d8f91fdd9fc 100644 |
| --- a/content/browser/renderer_host/websocket_dispatcher_host.cc |
| +++ b/content/browser/renderer_host/websocket_dispatcher_host.cc |
| @@ -9,6 +9,7 @@ |
| #include "base/callback.h" |
| #include "base/logging.h" |
| +#include "base/rand_util.h" |
| #include "base/stl_util.h" |
| #include "content/browser/child_process_security_policy_impl.h" |
| #include "content/browser/renderer_host/websocket_host.h" |
| @@ -23,6 +24,10 @@ namespace { |
| // fully-qualified every time. |
| typedef WebSocketDispatcherHost::WebSocketHostState WebSocketHostState; |
| +// Max of pending connections per WebSocketDispatcherHost |
|
Adam Rice
2015/03/04 08:20:50
Nitpick: "Max number of"
hiroshige
2015/03/04 11:13:15
Done.
|
| +// used for per-renderer WebSocket throttling. |
| +const int kMaxPendingWebSocketConnections = 255; |
| + |
| } // namespace |
| WebSocketDispatcherHost::WebSocketDispatcherHost( |
| @@ -33,7 +38,12 @@ WebSocketDispatcherHost::WebSocketDispatcherHost( |
| get_context_callback_(get_context_callback), |
| websocket_host_factory_( |
| base::Bind(&WebSocketDispatcherHost::CreateWebSocketHost, |
| - base::Unretained(this))) {} |
| + base::Unretained(this))), |
| + num_pending_connections_(0), |
| + num_current_succeeded_connections_(0), |
| + num_previous_succeeded_connections_(0), |
| + num_current_failed_connections_(0), |
| + num_previous_failed_connections_(0) {} |
| WebSocketDispatcherHost::WebSocketDispatcherHost( |
| int process_id, |
| @@ -44,8 +54,11 @@ WebSocketDispatcherHost::WebSocketDispatcherHost( |
| get_context_callback_(get_context_callback), |
| websocket_host_factory_(websocket_host_factory) {} |
| -WebSocketHost* WebSocketDispatcherHost::CreateWebSocketHost(int routing_id) { |
| - return new WebSocketHost(routing_id, this, get_context_callback_.Run()); |
| +WebSocketHost* WebSocketDispatcherHost::CreateWebSocketHost( |
| + int routing_id, |
| + base::TimeDelta delay) { |
| + return new WebSocketHost( |
| + routing_id, this, get_context_callback_.Run(), delay); |
| } |
| bool WebSocketDispatcherHost::OnMessageReceived(const IPC::Message& message) { |
| @@ -73,8 +86,23 @@ bool WebSocketDispatcherHost::OnMessageReceived(const IPC::Message& message) { |
| // little extreme. So for now just ignore the bogus request. |
| return true; // We handled the message (by ignoring it). |
| } |
| - host = websocket_host_factory_.Run(routing_id); |
| + if (num_pending_connections_ > kMaxPendingWebSocketConnections) { |
| + if(!Send(new WebSocketMsg_NotifyFailure(routing_id, |
| + "Error in connection establishment: net::ERR_INSUFFICIENT_RESOURCES" |
| + ))) { |
| + DVLOG(1) << "Sending of message type " |
| + << "WebSocketMsg_NotifyFailure failed."; |
| + } |
| + return true; |
| + } |
| + host = websocket_host_factory_.Run(routing_id, CalculateDelay()); |
| hosts_.insert(WebSocketHostTable::value_type(routing_id, host)); |
| + ++num_pending_connections_; |
| + if (!timer_.IsRunning()) |
| + timer_.Start(FROM_HERE, |
| + base::TimeDelta::FromMinutes(2), |
| + this, |
| + &WebSocketDispatcherHost::FinishThrottlingPeriod); |
| } |
| if (!host) { |
| DVLOG(1) << "Received invalid routing ID " << routing_id |
| @@ -121,6 +149,16 @@ WebSocketHostState WebSocketDispatcherHost::SendAddChannelResponse( |
| DeleteWebSocketHost(routing_id); |
| return WEBSOCKET_HOST_DELETED; |
| } |
| + |
| + // Update throttling counters (success). |
| + WebSocketHost* host = GetHost(routing_id); |
| + DCHECK(host); |
| + host->OnHandshakeSucceeded(); |
| + --num_pending_connections_; |
| + DCHECK_GE(num_pending_connections_, 0); |
| + ++num_current_succeeded_connections_; |
| + DCHECK_LT(num_current_succeeded_connections_, kint64max / 2); |
|
Adam Rice
2015/03/04 08:20:50
I don't think the "/ 2" is doing anything useful h
hiroshige
2015/03/04 11:13:15
I added "/ 2" here because this variable is used l
Adam Rice
2015/03/05 07:48:27
Yes, just remove it. Even if there is a bug in the
hiroshige
2015/03/06 06:04:29
Done.
|
| + |
| return WEBSOCKET_HOST_ALIVE; |
| } |
| @@ -200,8 +238,47 @@ WebSocketDispatcherHost::~WebSocketDispatcherHost() { |
| void WebSocketDispatcherHost::DeleteWebSocketHost(int routing_id) { |
| WebSocketHostTable::iterator it = hosts_.find(routing_id); |
| DCHECK(it != hosts_.end()); |
| + DCHECK(it->second); |
| + if (!it->second->handshake_succeeded()) { |
| + // Update throttling counters (failure). |
| + --num_pending_connections_; |
| + DCHECK_GE(num_pending_connections_, 0); |
| + ++num_current_failed_connections_; |
| + DCHECK_LT(num_current_failed_connections_, kint64max / 2); |
| + } |
| + |
| delete it->second; |
| hosts_.erase(it); |
| + |
| + DCHECK(static_cast<size_t>(num_pending_connections_) <= hosts_.size()); |
|
Adam Rice
2015/03/04 08:20:49
DCHECK_LE.
Also, you should use checked_cast<> fr
hiroshige
2015/03/04 11:13:15
Done.
|
| +} |
| + |
| +// Calculate delay according to |
|
Adam Rice
2015/03/04 08:20:49
I think "as described in" sounds nicer than "accor
hiroshige
2015/03/04 11:13:15
Done.
|
| +// the per-renderer WebSocket throttling design doc: |
| +// https://docs.google.com/document/d/1aw2oN5PKfk-1gLnBrlv1OwLA8K3-ykM2ckwX2lubTg4/edit?usp=sharing |
| +base::TimeDelta WebSocketDispatcherHost::CalculateDelay() const { |
| + int64_t f = num_previous_failed_connections_ + |
| + num_current_failed_connections_; |
| + int64_t s = num_previous_succeeded_connections_ + |
| + num_current_succeeded_connections_; |
| + int p = num_pending_connections_; |
| + return base::TimeDelta::FromMilliseconds( |
| + base::RandInt(1000, 5000) * |
| + (1 << std::min(p + f / (s + 1), static_cast<int64_t>(16))) / 65536); |
|
Adam Rice
2015/03/04 08:20:49
I think it is probably clearer to write 16LL rathe
hiroshige
2015/03/04 11:13:14
Sadly 16LL doesn't pass compilation;
std::min requ
Adam Rice
2015/03/05 07:48:27
Sorry. I just remembered how to do this properly.
hiroshige
2015/03/06 06:04:29
Done.
|
| +} |
| + |
| +void WebSocketDispatcherHost::FinishThrottlingPeriod() { |
| + num_previous_failed_connections_ = num_current_failed_connections_; |
| + num_current_failed_connections_ = 0; |
| + |
| + num_previous_succeeded_connections_ = num_current_succeeded_connections_; |
| + num_current_succeeded_connections_ = 0; |
| + |
| + if (num_pending_connections_ == 0 && |
| + num_previous_failed_connections_ == 0 && |
| + num_previous_succeeded_connections_ == 0) { |
| + timer_.Stop(); |
| + } |
| } |
| } // namespace content |