tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

WebTransportFlowControl.h (9132B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #ifndef mozilla_net_WebTransportFlowControl_h
      7 #define mozilla_net_WebTransportFlowControl_h
      8 
      9 #include "Capsule.h"
     10 #include "CapsuleEncoder.h"
     11 #include "mozilla/Assertions.h"
     12 #include "mozilla/Maybe.h"
     13 #include "mozilla/Result.h"
     14 #include "mozilla/net/neqo_glue_ffi_generated.h"
     15 #include "WebTransportStreamBase.h"
     16 
     17 namespace mozilla::net {
     18 
     19 // This is based on `fc::SenderFlowControl` in neqo. Ideally, we would reuse it,
     20 // but `SenderFlowControl` is in a private crate and tightly integrated with
     21 // other internal crates in neqo.
     22 class SenderFlowControlBase {
     23 public:
     24  explicit SenderFlowControlBase(uint64_t aInitial) : mLimit(aInitial) {}
     25 
     26  bool Update(uint64_t aNewLimit) {
     27    MOZ_ASSERT(aNewLimit < UINT64_MAX);
     28    if (aNewLimit > mLimit) {
     29      mLimit = aNewLimit;
     30      mBlockedCapsule = false;
     31      return true;
     32    }
     33    return false;
     34  }
     35 
     36  void Consume(uint64_t aCount) {
     37    MOZ_ASSERT(mUsed + aCount <= mLimit);
     38    mUsed += aCount;
     39  }
     40 
     41  uint64_t Available() const { return mLimit - mUsed; }
     42 
     43  uint64_t Used() const { return mUsed; }
     44 
     45  void Blocked() {
     46    if (mLimit >= mBlockedAt) {
     47      mBlockedAt = mLimit + 1;
     48      mBlockedCapsule = true;
     49    }
     50  }
     51 
     52  // Return whether a blocking Capsule needs to be sent.
     53  // This is `Some` with the active limit if `blocked` has been called,
     54  // if a blocking frame has not been sent (or it has been lost), and
     55  // if the blocking condition remains.
     56  mozilla::Maybe<uint64_t> BlockedNeeded() const {
     57    if (mBlockedCapsule && mLimit < mBlockedAt) {
     58      return Some(mBlockedAt - 1);
     59    }
     60    return Nothing();
     61  }
     62 
     63  void BlockedSent() { mBlockedCapsule = false; }
     64 
     65 protected:
     66  uint64_t mLimit = 0;
     67  uint64_t mUsed = 0;
     68  uint64_t mBlockedAt = 0;
     69  bool mBlockedCapsule = false;
     70 };
     71 
     72 // Flow control for stream creation.
     73 class SenderFlowControlStreamType : public SenderFlowControlBase {
     74 public:
     75  SenderFlowControlStreamType(WebTransportStreamType aType, uint64_t aInitial)
     76      : SenderFlowControlBase(aInitial), mType(aType) {}
     77 
     78  Maybe<CapsuleEncoder> CreateStreamsBlockedCapsule();
     79 
     80 private:
     81  WebTransportStreamType mType;
     82 };
     83 
     84 // Flow control for stream data.
     85 class SenderFlowControlStreamId : public SenderFlowControlBase {
     86 public:
     87  SenderFlowControlStreamId(StreamId aId, uint64_t aInitial)
     88      : SenderFlowControlBase(aInitial), mId(aId) {}
     89 
     90  Maybe<CapsuleEncoder> CreateStreamDataBlockedCapsule();
     91 
     92 private:
     93  StreamId mId;
     94 };
     95 
     96 // Flow control for session data.
     97 class SenderFlowControlSession : public SenderFlowControlBase {
     98 public:
     99  explicit SenderFlowControlSession(uint64_t aInitial)
    100      : SenderFlowControlBase(aInitial) {}
    101 
    102  Maybe<CapsuleEncoder> CreateSessionDataBlockedCapsule();
    103 };
    104 
    105 class LocalStreamLimits {
    106 public:
    107  LocalStreamLimits()
    108      : mBidirectional(WebTransportStreamType::BiDi, 0),
    109        mUnidirectional(WebTransportStreamType::UniDi, 0) {}
    110 
    111  mozilla::Maybe<StreamId> TakeStreamId(WebTransportStreamType aStreamType) {
    112    SenderFlowControlStreamType& fc =
    113        (aStreamType == WebTransportStreamType::BiDi) ? mBidirectional
    114                                                      : mUnidirectional;
    115 
    116    if (fc.Available() > 0) {
    117      uint64_t newId = fc.Used();
    118      fc.Consume(1);
    119      uint64_t typeBit = (aStreamType == WebTransportStreamType::BiDi) ? 0 : 2;
    120      return Some(StreamId((newId << 2) + typeBit));
    121    } else {
    122      fc.Blocked();
    123      return Nothing();
    124    }
    125  }
    126 
    127  const SenderFlowControlStreamType& operator[](
    128      WebTransportStreamType aStreamType) const {
    129    if (aStreamType == WebTransportStreamType::BiDi) {
    130      return mBidirectional;
    131    }
    132 
    133    MOZ_ASSERT(aStreamType == WebTransportStreamType::UniDi);
    134    return mUnidirectional;
    135  }
    136 
    137  SenderFlowControlStreamType& operator[](WebTransportStreamType aStreamType) {
    138    if (aStreamType == WebTransportStreamType::BiDi) {
    139      return mBidirectional;
    140    }
    141 
    142    MOZ_ASSERT(aStreamType == WebTransportStreamType::UniDi);
    143    return mUnidirectional;
    144  }
    145 
    146 private:
    147  SenderFlowControlStreamType mBidirectional;
    148  SenderFlowControlStreamType mUnidirectional;
    149 };
    150 
    151 class ReceiverFlowControlBase {
    152 public:
    153  explicit ReceiverFlowControlBase(uint64_t aMax)
    154      : mMaxActive(aMax), mMaxAllowed(aMax) {}
    155 
    156  void Retire(uint64_t aRetired) {
    157    if (aRetired <= mRetired) {
    158      return;
    159    }
    160    mRetired = aRetired;
    161    if (mRetired + mMaxActive / 2 > mMaxAllowed) {
    162      mCapsulePending = true;
    163    }
    164  }
    165 
    166  void SendFlowControlUpdate() {
    167    if (mRetired + mMaxActive > mMaxAllowed) {
    168      mCapsulePending = true;
    169    }
    170  }
    171 
    172  bool CapsuleNeeded() const { return mCapsulePending; }
    173  uint64_t NextLimit() const { return mRetired + mMaxActive; }
    174  uint64_t MaxActive() const { return mMaxActive; }
    175 
    176  void SetMaxActive(uint64_t aMax) {
    177    mCapsulePending |= (mMaxActive < aMax);
    178    mMaxActive = aMax;
    179  }
    180 
    181  uint64_t Retired() const { return mRetired; }
    182  uint64_t Consumed() const { return mConsumed; }
    183 
    184  void CapsuleSent(uint64_t aNewMax) {
    185    mMaxAllowed = aNewMax;
    186    mCapsulePending = false;
    187  }
    188 
    189 protected:
    190  uint64_t mMaxActive = 0;
    191  uint64_t mMaxAllowed = 0;
    192  uint64_t mConsumed = 0;
    193  uint64_t mRetired = 0;
    194  bool mCapsulePending = false;
    195 };
    196 
    197 class ReceiverFlowControlStreamId : public ReceiverFlowControlBase {
    198 public:
    199  ReceiverFlowControlStreamId(StreamId aId, uint64_t aMax)
    200      : ReceiverFlowControlBase(aMax), mId(aId) {}
    201 
    202  Maybe<CapsuleEncoder> CreateMaxStreamDataCapsule();
    203 
    204  Result<uint64_t, nsresult> SetConsumed(uint64_t aConsumed) {
    205    if (aConsumed <= mConsumed) {
    206      return 0;
    207    }
    208 
    209    if (aConsumed > mMaxAllowed) {
    210      return Err(NS_ERROR_NOT_AVAILABLE);
    211    }
    212 
    213    uint64_t newConsumed = aConsumed - mConsumed;
    214    mConsumed = aConsumed;
    215    return newConsumed;
    216  }
    217 
    218  void AddRetired(uint64_t aCount) {
    219    MOZ_ASSERT(mRetired + aCount <= mConsumed);
    220 
    221    mRetired += aCount;
    222    if (mRetired + mMaxActive / 2 > mMaxAllowed) {
    223      mCapsulePending = true;
    224    }
    225  }
    226 
    227 private:
    228  StreamId mId;
    229 };
    230 
    231 class ReceiverFlowControlSession : public ReceiverFlowControlBase {
    232 public:
    233  explicit ReceiverFlowControlSession(uint64_t aMax)
    234      : ReceiverFlowControlBase(aMax) {}
    235 
    236  Maybe<CapsuleEncoder> CreateMaxDataCapsule();
    237 
    238  // Return false when exceeding the flow control limit.
    239  bool Consume(uint64_t aCount) {
    240    if (mConsumed + aCount > mMaxAllowed) {
    241      return false;
    242    }
    243 
    244    mConsumed += aCount;
    245    return true;
    246  }
    247 
    248  void AddRetired(uint64_t aCount) {
    249    MOZ_ASSERT(mRetired + aCount <= mConsumed);
    250 
    251    mRetired += aCount;
    252    if (mRetired + mMaxActive / 2 > mMaxAllowed) {
    253      mCapsulePending = true;
    254    }
    255  }
    256 };
    257 
    258 class ReceiverFlowControlStreamType : public ReceiverFlowControlBase {
    259 public:
    260  ReceiverFlowControlStreamType(WebTransportStreamType aStreamType,
    261                                uint64_t aMax)
    262      : ReceiverFlowControlBase(aMax), mType(aStreamType) {}
    263 
    264  Maybe<CapsuleEncoder> CreateMaxStreamsCapsule();
    265 
    266  bool CheckAllowed(uint64_t aNewEnd) const { return aNewEnd < mMaxAllowed; }
    267 
    268  void AddRetired(uint64_t aCount) {
    269    mRetired += aCount;
    270    if (aCount > 0) {
    271      SendFlowControlUpdate();
    272    }
    273  }
    274 
    275 private:
    276  WebTransportStreamType mType = WebTransportStreamType::BiDi;
    277 };
    278 
    279 class RemoteStreamLimit {
    280 public:
    281  RemoteStreamLimit(WebTransportStreamType aStreamType, uint64_t aMaxStreams)
    282      : mStreamsFC(aStreamType, aMaxStreams) {
    283    uint64_t typeBit = (aStreamType == WebTransportStreamType::BiDi) ? 0 : 2;
    284    // Server initiated stream starts with 1.
    285    mNextStreamId = StreamId(typeBit + 1);
    286  }
    287 
    288  bool IsAllowed(StreamId aStreamId) const {
    289    uint64_t streamIndex = aStreamId >> 2;
    290    return mStreamsFC.CheckAllowed(streamIndex);
    291  }
    292 
    293  Result<bool, nsresult> IsNewStream(StreamId aStreamId) const {
    294    if (!IsAllowed(aStreamId)) {
    295      return Err(NS_ERROR_NOT_AVAILABLE);
    296    }
    297 
    298    return aStreamId >= mNextStreamId;
    299  }
    300 
    301  StreamId TakeStreamId() {
    302    StreamId newStream = mNextStreamId;
    303    mNextStreamId.Next();
    304    MOZ_ASSERT(IsAllowed(newStream));
    305    return newStream;
    306  }
    307 
    308  ReceiverFlowControlStreamType& FlowControl() { return mStreamsFC; }
    309  const ReceiverFlowControlStreamType& FlowControl() const {
    310    return mStreamsFC;
    311  }
    312 
    313 private:
    314  ReceiverFlowControlStreamType mStreamsFC;
    315  StreamId mNextStreamId{1u};
    316 };
    317 
    318 class RemoteStreamLimits {
    319 public:
    320  RemoteStreamLimits(uint64_t aBidiMax, uint64_t aUniMax)
    321      : mBidi(WebTransportStreamType::BiDi, aBidiMax),
    322        mUni(WebTransportStreamType::UniDi, aUniMax) {}
    323 
    324  RemoteStreamLimit& operator[](WebTransportStreamType aType) {
    325    return aType == WebTransportStreamType::BiDi ? mBidi : mUni;
    326  }
    327 
    328  const RemoteStreamLimit& operator[](WebTransportStreamType aType) const {
    329    return aType == WebTransportStreamType::BiDi ? mBidi : mUni;
    330  }
    331 
    332 private:
    333  RemoteStreamLimit mBidi;
    334  RemoteStreamLimit mUni;
    335 };
    336 
    337 }  // namespace mozilla::net
    338 
    339 #endif