File: web_transport_throttle_context.h

package info (click to toggle)
chromium 139.0.7258.127-2
  • links: PTS, VCS
  • area: main
  • in suites: forky
  • size: 6,122,156 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (200 lines) | stat: -rw-r--r-- 7,156 bytes parent folder | download | duplicates (3)
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CONTENT_BROWSER_WEBTRANSPORT_WEB_TRANSPORT_THROTTLE_CONTEXT_H_
#define CONTENT_BROWSER_WEBTRANSPORT_WEB_TRANSPORT_THROTTLE_CONTEXT_H_

#include <stddef.h>

#include <functional>
#include <memory>
#include <queue>
#include <vector>

#include "base/containers/queue.h"
#include "base/functional/callback_forward.h"
#include "base/memory/weak_ptr.h"
#include "base/supports_user_data.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "content/common/content_export.h"
#include "net/base/ip_endpoint.h"

namespace content {

// Tracks a single "bucket" of pending handshakes. For frames and dedicated
// workers there is one object per Page. For shared and service workers there is
// one per profile.
class CONTENT_EXPORT WebTransportThrottleContext final
    : public base::SupportsUserData::Data {
 public:
  // Tracks an ongoing handshake. Passed to the caller of PerformThrottle(),
  // which should call one of the methods.
  class CONTENT_EXPORT Tracker final {
   public:
    // Only constructed by WebTransportThrottleContext.
    explicit Tracker(
        base::WeakPtr<WebTransportThrottleContext> throttle_context);

    Tracker(const Tracker&) = delete;
    Tracker& operator=(const Tracker&) = delete;

    // Destruction of the object without calling one of the methods is treated
    // like handshake failure.
    ~Tracker();

    // Collects information about a WebTransport handshake that is about to
    // start.
    void OnBeforeConnect(const net::IPEndPoint& server_address);

    // Records the successful end of a WebTransport handshake.
    void OnHandshakeEstablished();

    // Records a WebTransport handshake failure.
    void OnHandshakeFailed();

   private:
    base::WeakPtr<WebTransportThrottleContext> throttle_context_;
    net::IPEndPoint server_address_;
  };

  using ThrottleDoneCallback =
      base::OnceCallback<void(std::unique_ptr<Tracker>)>;

  static constexpr int kMaxPendingSessions = 64;

  enum class ThrottleResult {
    kOk,
    kTooManyPendingSessions,
  };

  WebTransportThrottleContext();
  WebTransportThrottleContext(const WebTransportThrottleContext&) = delete;
  WebTransportThrottleContext& operator=(const WebTransportThrottleContext&) =
      delete;
  ~WebTransportThrottleContext() override;

  // Attempts to start a new handshake. Returns kTooManyPendingSessions if there
  // are already too many pending handshakes. Otherwise, runs the
  // `on_throttle_done` closure, possibly synchronously, and returns kOk.
  // Immediately before the `on_throttle_done` closure is called the handshake
  // is considered started.
  ThrottleResult PerformThrottle(ThrottleDoneCallback on_throttle_done);

  // Called when a handshake fails. Adds handshake delays unless it is
  // explicitly suppressed.
  void MaybeQueueHandshakeFailurePenalty(
      const std::optional<net::IPEndPoint>& server_address);

  void OnPendingQueueReady();

  base::WeakPtr<WebTransportThrottleContext> GetWeakPtr();

 private:
  class PenaltyManager final {
   public:
    using FailedHandshakesMap = std::map<net::IPAddress, base::TimeTicks>;

    explicit PenaltyManager(WebTransportThrottleContext*);
    PenaltyManager(const PenaltyManager&) = delete;
    PenaltyManager& operator=(const PenaltyManager&) = delete;
    ~PenaltyManager();

    base::TimeDelta ComputeHandshakePenalty(
        const std::optional<net::IPEndPoint>& server_address);

    // Queues a pending handshake to be considered complete after `after`.
    void QueuePending(base::TimeDelta after);

    // If there are handshakes in `pending_queue_` that can now be considered
    // finished, remove them and decrement `pending_handshakes_`. Recalculates
    // the delay for the head of `throttled_connections_` and may trigger it to
    // start as a side-effect.
    void MaybeDecrementPending();

    // Start the timer based on the `pending_queue` top.
    void ProcessPendingQueue();

    void StopPendingQueueTimer();

    int PendingHandshakes() const { return pending_handshakes_; }
    void AddPendingHandshakes() { ++pending_handshakes_; }
    void RemovePendingHandshakes() { --pending_handshakes_; }
    bool PendingQueueTimerIsRunning() {
      return pending_queue_timer_.IsRunning();
    }
    bool PendingQueueIsEmpty() { return pending_queue_.empty(); }

   private:
    // Start the timer for removing items from `pending_queue_timer_`, to fire
    // after `after` has passed.
    void StartPendingQueueTimer(base::TimeDelta after);

    // Removes any obsolete item in the failed_handshakes_ map.
    void CleanupFailedHandshakes();

    // Checks if there is a previous, non-obsolete, item in the
    // failed_handshakes_ map for the given ip address and updates
    // the map with the current time.
    bool FailedHandshakeNeedsPenalty(const net::IPAddress ip_address);

    const raw_ptr<WebTransportThrottleContext> throttle_context_;

    int pending_handshakes_ = 0;

    // Sessions for which the handshake has completed but we are still counting
    // as "pending" for the purposes of throttling. An items is added to this
    // queue when the handshake completes, and removed when the timer expires.
    // The "top" of the queue is the timer that will expire first.
    std::priority_queue<base::TimeTicks,
                        std::vector<base::TimeTicks>,
                        std::greater<>>
        pending_queue_;

    // A timer that will fire the next time an entry should be removed from
    // `pending_queue_`. The timer doesn't run when `throttled_connections_` is
    // empty.
    base::OneShotTimer pending_queue_timer_;

    FailedHandshakesMap failed_handshakes_;

    // A timer to cleanup the obsolete failed_handshakes.
    base::RepeatingTimer failed_handshakes_timer_;
  };

  // Starts a connection immediately if there are none pending, or sets a timer
  // to start one later.
  void ScheduleThrottledConnection();

  // Calls the throttle done callback on the connection at the head of
  // `throttled_connections_`.
  void DoOnThrottleDone();

  // Starts a connection if there is one waiting, and schedules the timer for
  // the next connection.
  void StartOneConnection();

  // False when the `--webtransport-developer-mode` flag is specified, true
  // otherwise.
  const bool should_queue_handshake_failure_penalty_;

  PenaltyManager penalty_mgr_{this};

  base::queue<ThrottleDoneCallback> throttled_connections_;

  // The time that `throttled_connections_[0]` reached the front of the queue.
  // This is needed if it gets recheduled by ScheduleThrottledConnection().
  base::TimeTicks queue_head_time_;

  // A timer that will fire the next time a throttled connection should be
  // allowed to proceed. This is a reset when pending_handshakes_ is
  // decremented.
  base::OneShotTimer throttled_connections_timer_;

  base::WeakPtrFactory<WebTransportThrottleContext> weak_factory_{this};
};

}  // namespace content

#endif  // CONTENT_BROWSER_WEBTRANSPORT_WEB_TRANSPORT_THROTTLE_CONTEXT_H_