| 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 9aa7a5e2d3c64f22374cad9430d120d3e44a4aea..ab4e68b76c1c17a6e722d2e38ae2438f14478bb0 100644
|
| --- a/content/browser/renderer_host/websocket_dispatcher_host.cc
|
| +++ b/content/browser/renderer_host/websocket_dispatcher_host.cc
|
| @@ -9,6 +9,8 @@
|
|
|
| #include "base/callback.h"
|
| #include "base/logging.h"
|
| +#include "base/numerics/safe_conversions.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 +25,10 @@ namespace {
|
| // fully-qualified every time.
|
| typedef WebSocketDispatcherHost::WebSocketHostState WebSocketHostState;
|
|
|
| +// Max number of pending connections per WebSocketDispatcherHost
|
| +// used for per-renderer WebSocket throttling.
|
| +const int kMaxPendingWebSocketConnections = 255;
|
| +
|
| } // namespace
|
|
|
| WebSocketDispatcherHost::WebSocketDispatcherHost(
|
| @@ -33,7 +39,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,
|
| @@ -42,10 +53,18 @@ WebSocketDispatcherHost::WebSocketDispatcherHost(
|
| : BrowserMessageFilter(WebSocketMsgStart),
|
| process_id_(process_id),
|
| get_context_callback_(get_context_callback),
|
| - websocket_host_factory_(websocket_host_factory) {}
|
| + websocket_host_factory_(websocket_host_factory),
|
| + num_pending_connections_(0),
|
| + num_current_succeeded_connections_(0),
|
| + num_previous_succeeded_connections_(0),
|
| + num_current_failed_connections_(0),
|
| + num_previous_failed_connections_(0) {}
|
|
|
| -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 +92,24 @@ 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 (!throttling_period_timer_.IsRunning())
|
| + throttling_period_timer_.Start(
|
| + FROM_HERE,
|
| + base::TimeDelta::FromMinutes(2),
|
| + this,
|
| + &WebSocketDispatcherHost::ThrottlingPeriodTimerCallback);
|
| }
|
| if (!host) {
|
| DVLOG(1) << "Received invalid routing ID " << routing_id
|
| @@ -112,6 +147,14 @@ WebSocketHostState WebSocketDispatcherHost::SendAddChannelResponse(
|
| int routing_id,
|
| const std::string& selected_protocol,
|
| const std::string& extensions) {
|
| + // 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_;
|
| +
|
| return SendOrDrop(new WebSocketMsg_AddChannelResponse(
|
| routing_id, selected_protocol, extensions));
|
| }
|
| @@ -192,8 +235,55 @@ 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_;
|
| + }
|
| +
|
| delete it->second;
|
| hosts_.erase(it);
|
| +
|
| + DCHECK_LE(base::checked_cast<size_t>(num_pending_connections_),
|
| + hosts_.size());
|
| +}
|
| +
|
| +int64_t WebSocketDispatcherHost::num_failed_connections() const {
|
| + return num_previous_failed_connections_ +
|
| + num_current_failed_connections_;
|
| +}
|
| +
|
| +int64_t WebSocketDispatcherHost::num_succeeded_connections() const {
|
| + return num_previous_succeeded_connections_ +
|
| + num_current_succeeded_connections_;
|
| +}
|
| +
|
| +// Calculate delay as described in
|
| +// 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_failed_connections();
|
| + int64_t s = num_succeeded_connections();
|
| + int p = num_pending_connections();
|
| + return base::TimeDelta::FromMilliseconds(
|
| + base::RandInt(1000, 5000) *
|
| + (1 << std::min(p + f / (s + 1), INT64_C(16))) / 65536);
|
| +}
|
| +
|
| +void WebSocketDispatcherHost::ThrottlingPeriodTimerCallback() {
|
| + 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) {
|
| + throttling_period_timer_.Stop();
|
| + }
|
| }
|
|
|
| } // namespace content
|
|
|