summaryrefslogtreecommitdiff
path: root/chromium/services/network/websocket_throttler.cc
blob: d380982555665c607137d384461ec5d9253b8671 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// Copyright 2018 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 "services/network/websocket_throttler.h"

#include <algorithm>

#include "base/rand_util.h"

namespace network {

constexpr int WebSocketPerProcessThrottler::kMaxPendingWebSocketConnections;

WebSocketPerProcessThrottler::PendingConnection::PendingConnection(
    base::WeakPtr<WebSocketPerProcessThrottler> throttler)
    : throttler_(std::move(throttler)) {
  DCHECK(throttler_);
  ++throttler_->num_pending_connections_;
}
WebSocketPerProcessThrottler::PendingConnection::PendingConnection(
    PendingConnection&& other)
    : throttler_(std::move(other.throttler_)) {
  other.throttler_ = nullptr;
}
WebSocketPerProcessThrottler::PendingConnection::~PendingConnection() {
  if (!throttler_)
    return;

  --throttler_->num_pending_connections_;
  ++throttler_->num_current_failed_connections_;
}

void WebSocketPerProcessThrottler::PendingConnection::OnCompleteHandshake() {
  DCHECK(throttler_);

  --throttler_->num_pending_connections_;
  ++throttler_->num_current_succeeded_connections_;
  throttler_ = nullptr;
}

WebSocketPerProcessThrottler::WebSocketPerProcessThrottler() {}
WebSocketPerProcessThrottler::~WebSocketPerProcessThrottler() {}

base::TimeDelta WebSocketPerProcessThrottler::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);
}

WebSocketPerProcessThrottler::PendingConnection
WebSocketPerProcessThrottler::IssuePendingConnectionTracker() {
  return PendingConnection(weak_factory_.GetWeakPtr());
}

bool WebSocketPerProcessThrottler::IsClean() const {
  return num_pending_connections_ == 0 &&
         num_current_succeeded_connections_ == 0 &&
         num_previous_succeeded_connections_ == 0 &&
         num_current_failed_connections_ == 0 &&
         num_previous_succeeded_connections_ == 0;
}

void WebSocketPerProcessThrottler::Roll() {
  num_previous_succeeded_connections_ = num_current_succeeded_connections_;
  num_previous_failed_connections_ = num_current_failed_connections_;

  num_current_succeeded_connections_ = 0;
  num_current_failed_connections_ = 0;
}

WebSocketThrottler::WebSocketThrottler() {}
WebSocketThrottler::~WebSocketThrottler() {}

bool WebSocketThrottler::HasTooManyPendingConnections(int process_id) const {
  auto it = per_process_throttlers_.find(process_id);
  if (it == per_process_throttlers_.end())
    return false;

  return it->second->HasTooManyPendingConnections();
}

base::TimeDelta WebSocketThrottler::CalculateDelay(int process_id) const {
  auto it = per_process_throttlers_.find(process_id);
  if (it == per_process_throttlers_.end())
    return base::TimeDelta();

  return it->second->CalculateDelay();
}

WebSocketThrottler::PendingConnection
WebSocketThrottler::IssuePendingConnectionTracker(int process_id) {
  auto it = per_process_throttlers_.find(process_id);
  if (it == per_process_throttlers_.end()) {
    it = per_process_throttlers_
             .insert(std::make_pair(
                 process_id, std::make_unique<WebSocketPerProcessThrottler>()))
             .first;
  }

  if (!throttling_period_timer_.IsRunning()) {
    throttling_period_timer_.Start(FROM_HERE, base::TimeDelta::FromMinutes(2),
                                   this, &WebSocketThrottler::OnTimer);
  }
  return it->second->IssuePendingConnectionTracker();
}

void WebSocketThrottler::OnTimer() {
  auto it = per_process_throttlers_.begin();
  while (it != per_process_throttlers_.end()) {
    it->second->Roll();
    if (it->second->IsClean()) {
      // We don't need the entry. Erase it.
      it = per_process_throttlers_.erase(it);
    } else {
      ++it;
    }
  }
  if (per_process_throttlers_.empty())
    throttling_period_timer_.Stop();
}

}  // namespace network