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