| Index: content/browser/websockets/websocket_manager.cc
|
| diff --git a/content/browser/websockets/websocket_manager.cc b/content/browser/websockets/websocket_manager.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..757bb139ddcb48bcd84fd68801b369dffdc35ee6
|
| --- /dev/null
|
| +++ b/content/browser/websockets/websocket_manager.cc
|
| @@ -0,0 +1,133 @@
|
| +// Copyright 2013 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 "content/browser/websockets/websocket_manager.h"
|
| +
|
| +#include <stddef.h>
|
| +
|
| +#include <algorithm>
|
| +#include <string>
|
| +#include <vector>
|
| +
|
| +#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/public/browser/browser_thread.h"
|
| +#include "net/url_request/url_request_context_getter.h"
|
| +
|
| +namespace content {
|
| +
|
| +namespace {
|
| +
|
| +// Max number of pending connections per WebSocketManager used for per-renderer
|
| +// WebSocket throttling.
|
| +const int kMaxPendingWebSocketConnections = 255;
|
| +
|
| +} // namespace
|
| +
|
| +WebSocketManager::WebSocketManager(int process_id,
|
| + StoragePartition* storage_partition)
|
| + : process_id_(process_id),
|
| + storage_partition_(storage_partition),
|
| + num_pending_connections_(0),
|
| + num_current_succeeded_connections_(0),
|
| + num_previous_succeeded_connections_(0),
|
| + num_current_failed_connections_(0),
|
| + num_previous_failed_connections_(0) {}
|
| +
|
| +WebSocketManager::~WebSocketManager() {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| +
|
| + for (auto impl : impls_) {
|
| + impl->GoAway();
|
| + delete impl;
|
| + }
|
| +}
|
| +
|
| +void WebSocketManager::CreateWebSocket(int render_frame_id,
|
| + mojom::WebSocketRequest request) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| +
|
| + if (num_pending_connections_ >= kMaxPendingWebSocketConnections) {
|
| + // Too many websockets! By returning here, we let |request| die, which
|
| + // will be observed by the client as Mojo connection error.
|
| + return;
|
| + }
|
| +
|
| + // Keep all WebSocketImpls alive until either the client drops its
|
| + // connection (see OnLostConnectionToClient) or we need to shutdown.
|
| +
|
| + impls_.insert(new WebSocketImpl(this,
|
| + std::move(request),
|
| + render_frame_id,
|
| + CalculateDelay()));
|
| + ++num_pending_connections_;
|
| +
|
| + if (!throttling_period_timer_.IsRunning()) {
|
| + throttling_period_timer_.Start(
|
| + FROM_HERE,
|
| + base::TimeDelta::FromMinutes(2),
|
| + this,
|
| + &WebSocketManager::ThrottlingPeriodTimerCallback);
|
| + }
|
| +}
|
| +
|
| +// Calculate delay as described in the per-renderer WebSocket throttling
|
| +// design doc: https://goo.gl/tldFNn
|
| +base::TimeDelta WebSocketManager::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), INT64_C(16))) / 65536);
|
| +}
|
| +
|
| +void WebSocketManager::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();
|
| + }
|
| +}
|
| +
|
| +int WebSocketManager::GetClientProcessId() {
|
| + return process_id_;
|
| +}
|
| +
|
| +StoragePartition* WebSocketManager::GetStoragePartition() {
|
| + return storage_partition_;
|
| +}
|
| +
|
| +void WebSocketManager::OnReceivedResponseFromServer(WebSocketImpl* impl) {
|
| + // The server accepted this WebSocket connection.
|
| + impl->OnHandshakeSucceeded();
|
| + --num_pending_connections_;
|
| + DCHECK_GE(num_pending_connections_, 0);
|
| + ++num_current_succeeded_connections_;
|
| +}
|
| +
|
| +void WebSocketManager::OnLostConnectionToClient(WebSocketImpl* impl) {
|
| + // The client is no longer interested in this WebSocket.
|
| + if (!impl->handshake_succeeded()) {
|
| + // Update throttling counters (failure).
|
| + --num_pending_connections_;
|
| + DCHECK_GE(num_pending_connections_, 0);
|
| + ++num_current_failed_connections_;
|
| + }
|
| + impls_.erase(impl);
|
| + delete impl;
|
| +}
|
| +
|
| +} // namespace content
|
|
|