Summary:
This commit converts IntervalSet to use CHECKs instead of throwing exceptions and provides safe tryInsert methods that return quic::Expected for error handling.
**Core Problem Solved:**
IntervalSet was throwing `std::invalid_argument` exceptions in two scenarios:
1. When constructing an Interval with `start > end`
2. When interval bounds exceed the maximum allowed value
This change eliminates exceptions in favor of CHECKs (for internal validation) and Expected-based error handling (for caller validation).
**Implementation Details:**
**1. IntervalSet Core Changes:**
- Replaced `throw std::invalid_argument` with `CHECK_LE` in Interval constructor
- Replaced `throw std::invalid_argument` with `CHECK_LE` in `insert(start, end)`
- Added `IntervalSetError` enum for error classification
- Added `folly::Expected` include
**2. Safe API Layer:**
- Added `tryInsert(interval)` method returning `Expected<Unit, IntervalSetError>`
- Added `tryInsert(start, end)` method with pre-validation
- Added `tryInsert(point)` method
- Added static `Interval::tryCreate()` method for safe interval construction
**3. Updated Code:**
- **QuicWriteCodec.cpp**: Updated `fillFrameWithPacketReceiveTimestamps` to use `tryInsert`
- Returns `QuicError` if interval validation fails
- Maintains existing error handling patterns
- **QuicTransportFunctions.cpp**: Updated `implicitAckCryptoStream` to use `tryInsert`
- Logs errors and continues processing other packets
- Robust error handling for crypto stream implicit acks
Reviewed By: kvtsoy
Differential Revision: D76792362
fbshipit-source-id: 5bd7c22e69a91d60cc41c603a1f2380893f4c8a0
Summary: Continuing the theme, removing them from the CryptoFactory and translating to Expected.
Reviewed By: kvtsoy, jbeshay
Differential Revision: D74676120
fbshipit-source-id: 715b497e68a4e3004811038cba479c443d5398fd
Summary: Continuing the theme, this time the TransportParameters
Reviewed By: jbeshay
Differential Revision: D74491343
fbshipit-source-id: 15f8bc0014f7163e674eb7adaf81727cbf2f35d6
Summary:
This is an API break, but it should mostly be a manageable one. We want to be able to compile mvfst internally without exceptions, and folly::Optional is one dependency that makes this challenging. Additionally, we already have an imported secondary optional type for performance/struct size reasons, tiny-optional.
This second optional interface is mostly compatible in an API sense (including the use of std::nullopt) with std::optional. Thus our approach is to remove the dependency on folly::Optional, and offer a quic::Optional instead.
The next diff will properly vendor tiny-optional so that quic::Optional is an independent version of it.
Reviewed By: sharmafb, kvtsoy
Differential Revision: D74133131
fbshipit-source-id: 715f8bb5043ba3bb876cacfe54236887e0686b30
Summary: Add new QUIC version MVFST_EXPERIMENTAL5 with same salt as MVFST_EXPERIMENTAL4
Reviewed By: mjoras
Differential Revision: D73285467
fbshipit-source-id: 2d4b8828574f604dab1072847149a5cff2964209
Summary:
Previously,
* `RawBuf` was a typealias for `std::unique_ptr<folly::IOBuf>`
* `Buf` was a typealias for `folly::IOBuf`
In this diff,
* `Buf` is a typealias for `folly::IOBuf`
* `BufPtr` is a typealias for `std::unique_ptr<folly::IOBuf>`
Reviewed By: hanidamlaj
Differential Revision: D73206576
fbshipit-source-id: 454bf6ccfce3d6571e5e931889263ed98cc24af3
Summary: Continuing the theme. This removes it from client and server handshakes.
Reviewed By: kvtsoy
Differential Revision: D73335422
fbshipit-source-id: 262bad17c1ebd2bcef623b1185e38e6a63ec714b
Summary: As in title, this is more of a theme on adding an Expected return.
Reviewed By: kvtsoy
Differential Revision: D72579218
fbshipit-source-id: 25735535368838f1a4315667cd7e9e9b5df1c485
Summary: I started with the QuicStreamManager, but it turns out that the path from the manager up to the close path touches a LOT, and so this is a big diff. The strategy is basically the same everywhere, add a folly::Expected and check it on every function and enforce that with [[nodiscard]]
Reviewed By: kvtsoy
Differential Revision: D72347215
fbshipit-source-id: 452868b541754d2ecab646d6c3cbd6aacf317d7f
Summary:
Adds more logging for:
- Number of initial packets received
- Number of unique crypto frames received
- Time elapsed to get the last meaningful initial crypto frame.
- named groups, psk status, and ech status from TLS layer.
Reviewed By: mjoras
Differential Revision: D71910754
fbshipit-source-id: ea8524213ba296727079800bb167ec080143c10b
Summary: This is a step towards removing exceptions entirely from this path. Mostly involves a mass conversion to folly::Expected and adding a CodecResult that conveys the errors.
Reviewed By: kvtsoy
Differential Revision: D71704941
fbshipit-source-id: a35825b75bf53e252cef8cbf851e2e670209e8de
Summary: Cache the negotiated config for what ACK type to write and which fields to use once the peer transport parameters are available. This avoids computing the config with every ack frame being written.
Reviewed By: sharmafb
Differential Revision: D70004436
fbshipit-source-id: 79354f5137c77353c3a97d4c41782a700622e986
Summary:
This introduces a new frame type for acks (ACK_EXTENDED) that can carry optional fields depending on the features supported by the peer. The currently supported features set will include ECN count fields, and Receive Timstamp fields. This enables a quic connection to report both ECN counts and receive timestamps, which is not possible otherwise because they use different frame types.
Support for the extended ack as well as the set of features that can be included in it is negotiated through a new transport parameter (extended_ack_supported = 0xff0a004). Its value indicates which features are supported by the local transport. The value is an integer which is evaluated against the following bitmasks:
```
ECN_COUNTS = 0x01,
RECEIVE_TIMESTAMPS = 0x02,
```
This diff introduces the transport parameter and negotiates the supported features between the peers of the connection. The parameter is cached in the psk cache so the client can remember the server config. It is also encoded inside the 0-rtt ticket so the server can reject it if its local config has changed.
The following diffs add reading and writing the frame itself.
The ACK_EXTENDED frame itself will have the following format
```
ACK_EXTENDED Frame {
Type (i) = 0xB1
// Fields of the existing ACK (type=0x02) frame:
Largest Acknowledged (i),
ACK Delay (i),
ACK Range Count (i),
First ACK Range (i),
ACK Range (..) ...,
Extended Ack Features (i),
// Optional ECN counts (if bit 0 is set in Features)
[ECN Counts (..)],
// Optional Receive Timestamps (if bit 1 is set in Features)
[Receive Timestamps (..)]
}
// Fields from the existing ACK_ECN frame
ECN Counts {
ECT0 Count (i),
ECT1 Count (i),
ECN-CE Count (i),
}
// Fields from the existing ACK_RECEIVE_TIMESTAMPS frame
Receive Timestamps {
Timestamp Range Count (i),
Timestamp Ranges (..) ...,
}
Timestamp Range {
Gap (i),
Timestamp Delta Count (i),
Timestamp Delta (i) ...,
}
```
Reviewed By: sharmafb
Differential Revision: D68931151
fbshipit-source-id: 44c8c83d2f434abca97c4e85f0fa7502736cddc1
Summary: For now, we're going to throw an exception when we get a RESET_STREAM_AT frame, which would terminate the connection. Later on, this logic is going to be gated on whether we and the peer support reliable resets (and we get to know this through the transport parameters).
Reviewed By: jbeshay
Differential Revision: D67866393
fbshipit-source-id: 6b2888c026fadf0f2aa5dad52314ed8250959cf6
Summary: This diff adds the reliable_stream_reset transport parameter to mvfst.
Reviewed By: hanidamlaj
Differential Revision: D65383676
fbshipit-source-id: cb2f6a1a90004ea489447b67ed3cfc12ca90b804
Summary:
With normal resets, we transition from `StreamSendState::ResetSent` to `StreamSendState::Closed` when we get an ACK for a RESET_STREAM frame.
With reliable resets, this is going to be a little more complicated. We can't automatically transition from `StreamSendState::ResetSent` to `StreamSendState::Closed` when we get an ACK for a RESET_STREAM_AT frame because it's possible that the peer hasn't yet received all data until the reliable reset offset.
My idea is the following: Keep track of the `minReliableSizeAcked`, which is the lowest value of the reliable size in any RESET_STREAM_AT frame that was ACKed by the peer. We set it to 0 if a RESET_STREAM frame was ACKed by the peer.
Then, we can transition from `StreamSendState::ResetSent` to `StreamSendState::Closed` if any one of the following two events happen:
* We get an ACK for a RESET_STREAM_AT or a RESET_STREAM frame, and all data until the `minReliableSizeAcked` has been ACKed by the peer.
* We get an ACK for stream data, and this puts us in a state where all data until the `minReliableSizeAcked` has been ACKed by the peer.
Note: This diff doesn't have any functional change. The only change is that we're keeping track of the `minReliableSizeAcked`, but aren't using it anywhere.
Reviewed By: mjoras
Differential Revision: D66781199
fbshipit-source-id: 2aa5138a18f70e9801e59e747460558ba706939c
Summary:
Provide a more detailed reason why we dropped a packet to the stats
callback and qlog.
Reviewed By: hanidamlaj
Differential Revision: D66171930
fbshipit-source-id: 4cf10b149d1184afb7b268ac8f229555d2ffb75d
Summary:
You can find the format of the RST_STREAM_AT frame in the [RFC](https://datatracker.ietf.org/doc/html/draft-ietf-quic-reliable-stream-reset-06#name-reset_stream_at-frame). In this change, I'm adding RST_STREAM_AT to the FrameType enum and am also adding the functionality to decode such frames.
Note that we ignore the case when we receive RST_STREAM_AT frames (because we haven't implemented their handler yet).
Reviewed By: hanidamlaj
Differential Revision: D64836490
fbshipit-source-id: 5bd742b14c343ce2f26dd29d3a285a6fed60676a
Summary:
Timely reaction to congestion requires relaying any CE marks to the sender as soon as possible.
This change schedules an ack to be sent whenever incoming packets are received with CE marks. This will only happen when the readEcnOnIngress option is enabled.
Reviewed By: mjoras
Differential Revision: D58423959
fbshipit-source-id: 30f8cf8b11d0446985c2d87d7df67c24c0d5afdf
Summary: We have a lot of optionals that are either integral values or std::chrono::microseconds. These end up wasting memory, where we can instead store sentinel values to encode whether the value is there or not. This reduces the effective range of the type by one value, but that is an acceptable tradeoff.
Reviewed By: kvtsoy
Differential Revision: D57684368
fbshipit-source-id: b406b86011f9b8169b6e5e925265f4829928cc63
Summary:
The idea here is to make it so we can swap out the type we are using for optionality. In the near term we are going to try swapping towards one that more aggressively tries to save size.
For now there is no functional change and this is just a big aliasing diff.
Reviewed By: sharmafb
Differential Revision: D57633896
fbshipit-source-id: 6eae5953d47395b390016e59cf9d639f3b6c8cfe
Summary: Add another QUIC version alias
Reviewed By: sharmafb
Differential Revision: D57233430
fbshipit-source-id: 7536e5bb13b84ca3e2de818fdc05adf894831ccb
Summary:
Keep track of ECN values in incoming packets and update the AckState that is used for echoing the counts to the peer in ACK_ECN frames.
Reading and writing the ACK_ECN frames is in the next change.
Reviewed By: kvtsoy
Differential Revision: D54927783
fbshipit-source-id: 44471c6224ee8578aaacc0d1a1c54370ef6d2ffe
Summary: Adds new field `tosValue` to ReceivedUdpPacket so it is accessible in the rest of the read path.
Reviewed By: kvtsoy
Differential Revision: D54912161
fbshipit-source-id: ea4714fa2374d38e915fc850387e1094d1fb8adf
Summary: ReceivedUdpPacket has attached metadata (currently timings and later in the stack, tos values). Rather than passing the metadata separately in the read path for the client and the server, this propagates the ReceivedUdpPacket further up so this metadata is easier to use in the rest of the stack.
Reviewed By: mjoras
Differential Revision: D54912162
fbshipit-source-id: c980d5b276704f5bba780ac16a380bbb4e5bedad
Summary: This reduces the number of stats callbacks when processing multiple packets in rapid succession.
Reviewed By: mjoras
Differential Revision: D56315022
fbshipit-source-id: 750024301f28b21e3125c144ead6f115706736a4
Summary:
The current ackVisitor passed to processAckFrame() gets executed with every acked frame.
Not every check in that visitor needs to be performed for every frame.
This refactors it into two different visitors, one that is called once per acked packet and another that is called once per acked frame.
Reviewed By: mjoras
Differential Revision: D56090734
fbshipit-source-id: 7b0877684e439f9e88c0aae7a294245570cf8309
Summary: we should only increment numMigrations if the peer is attempting migration from a previously unseen addr
Reviewed By: kvtsoy, sharmafb
Differential Revision: D55884605
fbshipit-source-id: 9204ed785195a321d7f8f7497151b7cfe5f745f7
Summary: We currently cap the max send size at the default instead of the max we support (1452). This is pretty conservative and I don't think it's needed, and causes issues when trying to use MASQUE + UDP proxying with QUIC.
Reviewed By: hanidamlaj
Differential Revision: D53440189
fbshipit-source-id: 1d5bbdbb4cc1abb677f85dff6bdf2841cb01c5e1
Summary:
Allow the server/client transport to initiate periodic key update. It's defaulted to being disabled.
The new logic for initiating and verifying a key update was handled correctly by the peer is consolidated in QuicTransportFunctions.
Reviewed By: mjoras
Differential Revision: D53109624
fbshipit-source-id: 0c3a944978fc0e0a84252da953dc116aa7c26379
Summary:
This stack adds key update support to Mvfst client and server. This diff adds the main logic for detecting key updates in the QuicReadCodec. When an update is successful, the server transport reacts to it by updating the write phase and cipher.
The high level design is as follows:
- The QuicReadCodec is responsible for detecting incoming key update attempts by the peer, as well as tracking any ongoing locally-initiated key updates.
- Upon detecting a successful key update, the QuicReadCodec updates its state. The Server/Client transport reacts to this change by updating its write phase and cipher.
- A locally initiated key update starts with updating the write phase and key, and signaling the read codec that a key update has been initiated.
- The read codec keeps this in a pending state until a packet is successfully received in the new phase.
- Functions for syncing the read/write phase on incoming key updates, as well as initiating and verifying outgoing key updates are abstracted in QuicTransportFunctions and are used by both the client and server transports.
- Common handshake functions used for rotating the keys are now in HandshakeLayer that is shared by both client and server handshakes.
Reviewed By: mjoras
Differential Revision: D53016559
fbshipit-source-id: 134e965dabd62917193544a9655a4eb8868ab7f8