File: WebTransportFlowControl.h

package info (click to toggle)
firefox 147.0-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 4,683,324 kB
  • sloc: cpp: 7,607,156; javascript: 6,532,492; ansic: 3,775,158; python: 1,415,368; xml: 634,556; asm: 438,949; java: 186,241; sh: 62,751; makefile: 18,079; objc: 13,092; perl: 12,808; yacc: 4,583; cs: 3,846; pascal: 3,448; lex: 1,720; ruby: 1,003; php: 436; lisp: 258; awk: 247; sql: 66; sed: 54; csh: 10; exp: 6
file content (339 lines) | stat: -rw-r--r-- 9,132 bytes parent folder | download | duplicates (10)
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
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef mozilla_net_WebTransportFlowControl_h
#define mozilla_net_WebTransportFlowControl_h

#include "Capsule.h"
#include "CapsuleEncoder.h"
#include "mozilla/Assertions.h"
#include "mozilla/Maybe.h"
#include "mozilla/Result.h"
#include "mozilla/net/neqo_glue_ffi_generated.h"
#include "WebTransportStreamBase.h"

namespace mozilla::net {

// This is based on `fc::SenderFlowControl` in neqo. Ideally, we would reuse it,
// but `SenderFlowControl` is in a private crate and tightly integrated with
// other internal crates in neqo.
class SenderFlowControlBase {
 public:
  explicit SenderFlowControlBase(uint64_t aInitial) : mLimit(aInitial) {}

  bool Update(uint64_t aNewLimit) {
    MOZ_ASSERT(aNewLimit < UINT64_MAX);
    if (aNewLimit > mLimit) {
      mLimit = aNewLimit;
      mBlockedCapsule = false;
      return true;
    }
    return false;
  }

  void Consume(uint64_t aCount) {
    MOZ_ASSERT(mUsed + aCount <= mLimit);
    mUsed += aCount;
  }

  uint64_t Available() const { return mLimit - mUsed; }

  uint64_t Used() const { return mUsed; }

  void Blocked() {
    if (mLimit >= mBlockedAt) {
      mBlockedAt = mLimit + 1;
      mBlockedCapsule = true;
    }
  }

  // Return whether a blocking Capsule needs to be sent.
  // This is `Some` with the active limit if `blocked` has been called,
  // if a blocking frame has not been sent (or it has been lost), and
  // if the blocking condition remains.
  mozilla::Maybe<uint64_t> BlockedNeeded() const {
    if (mBlockedCapsule && mLimit < mBlockedAt) {
      return Some(mBlockedAt - 1);
    }
    return Nothing();
  }

  void BlockedSent() { mBlockedCapsule = false; }

 protected:
  uint64_t mLimit = 0;
  uint64_t mUsed = 0;
  uint64_t mBlockedAt = 0;
  bool mBlockedCapsule = false;
};

// Flow control for stream creation.
class SenderFlowControlStreamType : public SenderFlowControlBase {
 public:
  SenderFlowControlStreamType(WebTransportStreamType aType, uint64_t aInitial)
      : SenderFlowControlBase(aInitial), mType(aType) {}

  Maybe<CapsuleEncoder> CreateStreamsBlockedCapsule();

 private:
  WebTransportStreamType mType;
};

// Flow control for stream data.
class SenderFlowControlStreamId : public SenderFlowControlBase {
 public:
  SenderFlowControlStreamId(StreamId aId, uint64_t aInitial)
      : SenderFlowControlBase(aInitial), mId(aId) {}

  Maybe<CapsuleEncoder> CreateStreamDataBlockedCapsule();

 private:
  StreamId mId;
};

// Flow control for session data.
class SenderFlowControlSession : public SenderFlowControlBase {
 public:
  explicit SenderFlowControlSession(uint64_t aInitial)
      : SenderFlowControlBase(aInitial) {}

  Maybe<CapsuleEncoder> CreateSessionDataBlockedCapsule();
};

class LocalStreamLimits {
 public:
  LocalStreamLimits()
      : mBidirectional(WebTransportStreamType::BiDi, 0),
        mUnidirectional(WebTransportStreamType::UniDi, 0) {}

  mozilla::Maybe<StreamId> TakeStreamId(WebTransportStreamType aStreamType) {
    SenderFlowControlStreamType& fc =
        (aStreamType == WebTransportStreamType::BiDi) ? mBidirectional
                                                      : mUnidirectional;

    if (fc.Available() > 0) {
      uint64_t newId = fc.Used();
      fc.Consume(1);
      uint64_t typeBit = (aStreamType == WebTransportStreamType::BiDi) ? 0 : 2;
      return Some(StreamId((newId << 2) + typeBit));
    } else {
      fc.Blocked();
      return Nothing();
    }
  }

  const SenderFlowControlStreamType& operator[](
      WebTransportStreamType aStreamType) const {
    if (aStreamType == WebTransportStreamType::BiDi) {
      return mBidirectional;
    }

    MOZ_ASSERT(aStreamType == WebTransportStreamType::UniDi);
    return mUnidirectional;
  }

  SenderFlowControlStreamType& operator[](WebTransportStreamType aStreamType) {
    if (aStreamType == WebTransportStreamType::BiDi) {
      return mBidirectional;
    }

    MOZ_ASSERT(aStreamType == WebTransportStreamType::UniDi);
    return mUnidirectional;
  }

 private:
  SenderFlowControlStreamType mBidirectional;
  SenderFlowControlStreamType mUnidirectional;
};

class ReceiverFlowControlBase {
 public:
  explicit ReceiverFlowControlBase(uint64_t aMax)
      : mMaxActive(aMax), mMaxAllowed(aMax) {}

  void Retire(uint64_t aRetired) {
    if (aRetired <= mRetired) {
      return;
    }
    mRetired = aRetired;
    if (mRetired + mMaxActive / 2 > mMaxAllowed) {
      mCapsulePending = true;
    }
  }

  void SendFlowControlUpdate() {
    if (mRetired + mMaxActive > mMaxAllowed) {
      mCapsulePending = true;
    }
  }

  bool CapsuleNeeded() const { return mCapsulePending; }
  uint64_t NextLimit() const { return mRetired + mMaxActive; }
  uint64_t MaxActive() const { return mMaxActive; }

  void SetMaxActive(uint64_t aMax) {
    mCapsulePending |= (mMaxActive < aMax);
    mMaxActive = aMax;
  }

  uint64_t Retired() const { return mRetired; }
  uint64_t Consumed() const { return mConsumed; }

  void CapsuleSent(uint64_t aNewMax) {
    mMaxAllowed = aNewMax;
    mCapsulePending = false;
  }

 protected:
  uint64_t mMaxActive = 0;
  uint64_t mMaxAllowed = 0;
  uint64_t mConsumed = 0;
  uint64_t mRetired = 0;
  bool mCapsulePending = false;
};

class ReceiverFlowControlStreamId : public ReceiverFlowControlBase {
 public:
  ReceiverFlowControlStreamId(StreamId aId, uint64_t aMax)
      : ReceiverFlowControlBase(aMax), mId(aId) {}

  Maybe<CapsuleEncoder> CreateMaxStreamDataCapsule();

  Result<uint64_t, nsresult> SetConsumed(uint64_t aConsumed) {
    if (aConsumed <= mConsumed) {
      return 0;
    }

    if (aConsumed > mMaxAllowed) {
      return Err(NS_ERROR_NOT_AVAILABLE);
    }

    uint64_t newConsumed = aConsumed - mConsumed;
    mConsumed = aConsumed;
    return newConsumed;
  }

  void AddRetired(uint64_t aCount) {
    MOZ_ASSERT(mRetired + aCount <= mConsumed);

    mRetired += aCount;
    if (mRetired + mMaxActive / 2 > mMaxAllowed) {
      mCapsulePending = true;
    }
  }

 private:
  StreamId mId;
};

class ReceiverFlowControlSession : public ReceiverFlowControlBase {
 public:
  explicit ReceiverFlowControlSession(uint64_t aMax)
      : ReceiverFlowControlBase(aMax) {}

  Maybe<CapsuleEncoder> CreateMaxDataCapsule();

  // Return false when exceeding the flow control limit.
  bool Consume(uint64_t aCount) {
    if (mConsumed + aCount > mMaxAllowed) {
      return false;
    }

    mConsumed += aCount;
    return true;
  }

  void AddRetired(uint64_t aCount) {
    MOZ_ASSERT(mRetired + aCount <= mConsumed);

    mRetired += aCount;
    if (mRetired + mMaxActive / 2 > mMaxAllowed) {
      mCapsulePending = true;
    }
  }
};

class ReceiverFlowControlStreamType : public ReceiverFlowControlBase {
 public:
  ReceiverFlowControlStreamType(WebTransportStreamType aStreamType,
                                uint64_t aMax)
      : ReceiverFlowControlBase(aMax), mType(aStreamType) {}

  Maybe<CapsuleEncoder> CreateMaxStreamsCapsule();

  bool CheckAllowed(uint64_t aNewEnd) const { return aNewEnd < mMaxAllowed; }

  void AddRetired(uint64_t aCount) {
    mRetired += aCount;
    if (aCount > 0) {
      SendFlowControlUpdate();
    }
  }

 private:
  WebTransportStreamType mType = WebTransportStreamType::BiDi;
};

class RemoteStreamLimit {
 public:
  RemoteStreamLimit(WebTransportStreamType aStreamType, uint64_t aMaxStreams)
      : mStreamsFC(aStreamType, aMaxStreams) {
    uint64_t typeBit = (aStreamType == WebTransportStreamType::BiDi) ? 0 : 2;
    // Server initiated stream starts with 1.
    mNextStreamId = StreamId(typeBit + 1);
  }

  bool IsAllowed(StreamId aStreamId) const {
    uint64_t streamIndex = aStreamId >> 2;
    return mStreamsFC.CheckAllowed(streamIndex);
  }

  Result<bool, nsresult> IsNewStream(StreamId aStreamId) const {
    if (!IsAllowed(aStreamId)) {
      return Err(NS_ERROR_NOT_AVAILABLE);
    }

    return aStreamId >= mNextStreamId;
  }

  StreamId TakeStreamId() {
    StreamId newStream = mNextStreamId;
    mNextStreamId.Next();
    MOZ_ASSERT(IsAllowed(newStream));
    return newStream;
  }

  ReceiverFlowControlStreamType& FlowControl() { return mStreamsFC; }
  const ReceiverFlowControlStreamType& FlowControl() const {
    return mStreamsFC;
  }

 private:
  ReceiverFlowControlStreamType mStreamsFC;
  StreamId mNextStreamId{1u};
};

class RemoteStreamLimits {
 public:
  RemoteStreamLimits(uint64_t aBidiMax, uint64_t aUniMax)
      : mBidi(WebTransportStreamType::BiDi, aBidiMax),
        mUni(WebTransportStreamType::UniDi, aUniMax) {}

  RemoteStreamLimit& operator[](WebTransportStreamType aType) {
    return aType == WebTransportStreamType::BiDi ? mBidi : mUni;
  }

  const RemoteStreamLimit& operator[](WebTransportStreamType aType) const {
    return aType == WebTransportStreamType::BiDi ? mBidi : mUni;
  }

 private:
  RemoteStreamLimit mBidi;
  RemoteStreamLimit mUni;
};

}  // namespace mozilla::net

#endif