#[derive(Debug, Default)] pubstruct RecvStreams {
streams: BTreeMap<StreamId, RecvStream>,
keep_alive: Weak<()>, /// Set when any stream has ended; cleared by `remove_ended`.
has_ended: bool,
}
/// Read from a stream, noting when it ends. /// /// # Errors /// When the stream does not exist or has no more data. pubfn read(&mutself, stream_id: StreamId, data: &mut [u8]) -> Res<(usize, bool)> { let (n, fin) = self.get_mut(stream_id)?.read(data)?; self.set_ended(fin);
Ok((n, fin))
}
/// Stop sending on a stream, noting when it ends. /// /// # Errors /// When the stream does not exist. pubfn stop_sending(&mutself, stream_id: StreamId, err: AppError) -> Res<()> { let ended = self.get_mut(stream_id)?.stop_sending(err); self.set_ended(ended);
Ok(())
}
/// Reset a stream, noting if it ended. /// /// # Errors /// When flow control is violated. pubfn reset(
&mutself,
stream_id: StreamId,
application_error_code: AppError,
final_size: u64,
) -> Res<()> { iflet Ok(rs) = self.get_mut(stream_id) { let ended = rs.reset(application_error_code, final_size)?; self.set_ended(ended);
}
Ok(())
}
/// Note whether a stop-sending ack ended the stream. pubfn stop_sending_acked(&mutself, stream_id: StreamId) { iflet Ok(rs) = self.get_mut(stream_id) { let ended = rs.stop_sending_acked(); self.set_ended(ended);
}
}
pubfn remove_ended(&mutself, send_streams: &SendStreams, role: Role) -> (u64, u64) { if !self.has_ended { return (0, 0);
} self.has_ended = false; // Note: retained ended bidi streams (send counterpart alive) will be re-flagged // when their send side is removed via `cleanup_closed_streams`. letmut removed_bidi = 0; letmut removed_uni = 0; self.streams.retain(|id, s| { let dead = s.is_ended() && (id.is_uni() || !send_streams.exists(*id)); if dead && id.is_remote_initiated(role) { if id.is_bidi() {
removed_bidi += 1;
} else {
removed_uni += 1;
}
}
!dead
});
(removed_bidi, removed_uni)
}
}
/// Holds data not yet read by application. Orders and dedupes data ranges /// from incoming STREAM frames. #[derive(Debug, Default)] pubstruct RxStreamOrderer {
data_ranges: BTreeMap<u64, Vec<u8>>, // (start_offset, data)
retired: u64, // Number of bytes the application has read
received: u64, // The number of bytes stored in `data_ranges`
}
/// Process an incoming stream frame off the wire. This may result in data /// being available to upper layers if frame is not out of order (ooo) or /// if the frame fills a gap. /// # Panics /// Only when `u64` values cannot be converted to `usize`, which only /// happens on 32-bit machines that hold far too much data at the same time. pubfn inbound_frame(&mutself, mut new_start: u64, mut new_data: &[u8]) {
qtrace!("Inbound data offset={new_start} len={}", new_data.len());
// Get entry before where new entry would go, so we can see if we already // have the new bytes. // Avoid copies and duplicated data. let new_end = new_start + u64::try_from(new_data.len()).expect("usize fits in u64");
if new_end <= self.retired { // Range already read by application, this frame is very late and unneeded. return;
}
if new_start < self.retired {
new_data =
&new_data[usize::try_from(self.retired - new_start).expect("u64 fits in usize")..];
new_start = self.retired;
}
if new_data.is_empty() { // No data to insert return;
}
let extend = iflet Some((&prev_start, prev_vec)) = self.data_ranges.range_mut(..=new_start).next_back()
{ let prev_end = prev_start + u64::try_from(prev_vec.len()).expect("usize fits in u64"); if new_end > prev_end { // PPPPPP -> PPPPPP // NNNNNN NN // NNNNNNNN NN // Add a range containing only new data // (In-order frames will take this path, with no overlap) let overlap = prev_end.saturating_sub(new_start);
qtrace!("New frame {new_start}-{new_end} received, overlap: {overlap}");
new_start += overlap;
new_data = &new_data[usize::try_from(overlap).expect("u64 fits in usize")..]; // If it is small enough, extend the previous buffer. // This can't always extend, because otherwise the buffer could end up // growing indefinitely without being released.
prev_vec.len() < 4096 && prev_end == new_start
} else { // PPPPPP -> PPPPPP // NNNN // NNNN // Do nothing
qtrace!("Dropping frame with already-received range {new_start}-{new_end}"); return;
}
} else {
qtrace!("New frame {new_start}-{new_end} received"); false
};
letmut to_add = new_data; ifself
.data_ranges
.last_entry()
.is_some_and(|e| *e.key() >= new_start)
{ // Is this at the end (common case)? If so, nothing to do in this block // Common case: // PPPPPP -> PPPPPP // NNNNNNN NNNNNNN // or // PPPPPP -> PPPPPP // NNNNNNN NNNNNNN // // Not the common case, handle possible overlap with next entries // PPPPPP AAA -> PPPPPP // NNNNNNN NNNNNNN // or // PPPPPP AAAA -> PPPPPP AAAA // NNNNNNN NNNNN // or (this is where to_remove is used) // PPPPPP AA -> PPPPPP // NNNNNNN NNNNNNN
letmut to_remove = SmallVec::<[_; 8]>::new();
for (&next_start, next_data) inself.data_ranges.range_mut(new_start..) { let next_end =
next_start + u64::try_from(next_data.len()).expect("usize fits in u64"); let overlap = new_end.saturating_sub(next_start); if overlap == 0 { // Fills in the hole, exactly (probably common) break;
} elseif next_end >= new_end {
qtrace!( "New frame {new_start}-{new_end} overlaps with next frame by {overlap}, truncating"
); let truncate_to =
new_data.len() - usize::try_from(overlap).expect("u64 fits in usize");
to_add = &new_data[..truncate_to]; break;
}
qtrace!( "New frame {new_start}-{new_end} spans entire next frame {next_start}-{next_end}, replacing"
);
to_remove.push(next_start); // Continue, since we may have more overlaps
}
for start in to_remove { self.data_ranges.remove(&start);
}
}
if !to_add.is_empty() { self.received += u64::try_from(to_add.len()).expect("usize fits in u64"); if extend { iflet Some((_, buf)) = self.data_ranges.range_mut(..=new_start).next_back() {
buf.extend_from_slice(to_add);
}
} else { self.data_ranges.insert(new_start, to_add.to_vec());
}
}
}
/// Are any bytes readable? #[must_use] pubfn data_ready(&self) -> bool { self.data_ranges
.keys()
.next()
.is_some_and(|&start| start <= self.retired)
}
/// How many bytes are readable? fn bytes_ready(&self) -> usize { letmut prev_end = self.retired; self.data_ranges
.iter()
.map(|(start_offset, data)| { // All ranges don't overlap but we could have partially // retired some of the first entry's data. let data_len = data.len() as u64 - self.retired.saturating_sub(*start_offset);
(start_offset, data_len)
})
.take_while(|(start_offset, data_len)| { if **start_offset <= prev_end {
prev_end += data_len; true
} else { false
}
}) // Accumulate, but saturate at usize::MAX.
.fold(0, |acc: usize, (_, data_len)| {
acc.saturating_add(usize::try_from(data_len).unwrap_or(usize::MAX))
})
}
/// Bytes read by the application. #[must_use] pubconstfn retired(&self) -> u64 { self.retired
}
/// Data bytes buffered. Could be more than `bytes_readable` if there are /// ranges missing. fn buffered(&self) -> u64 { self.data_ranges
.iter()
.map(|(&start, data)| data.len() as u64 - (self.retired.saturating_sub(start)))
.sum()
}
/// Copy received data (if any) into the buffer. Returns bytes copied. fn read(&mutself, buf: &mut [u8]) -> usize {
qtrace!("Reading {} bytes, {} available", buf.len(), self.buffered()); letmut copied = 0;
for (&range_start, range_data) in &mutself.data_ranges { letmut keep = false; ifself.retired >= range_start { // Frame data has new contiguous bytes. let copy_offset = usize::try_from(max(range_start, self.retired) - range_start)
.expect("u64 fits in usize");
assert!(range_data.len() >= copy_offset); let available = range_data.len() - copy_offset; let space = buf.len() - copied; let copy_bytes = if available > space {
keep = true;
space
} else {
available
};
if copy_bytes > 0 { let copy_slc = &range_data[copy_offset..copy_offset + copy_bytes];
buf[copied..copied + copy_bytes].copy_from_slice(copy_slc);
copied += copy_bytes; self.retired += u64::try_from(copy_bytes).expect("usize fits in u64");
}
} else { // The data in the buffer isn't contiguous.
keep = true;
} if keep { letmut keep = self.data_ranges.split_off(&range_start);
mem::swap(&mutself.data_ranges, &mut keep); return copied;
}
}
self.data_ranges.clear();
copied
}
/// Extend the given Vector with any available data. pubfn read_to_end(&mutself, buf: &mut Vec<u8>) -> usize { let orig_len = buf.len();
buf.resize(orig_len + self.bytes_ready(), 0); self.read(&mut buf[orig_len..])
}
}
/// QUIC receiving states, based on -transport 3.2. #[derive(Debug, Display)] // Because a dead_code warning is easier than clippy::unused_self, see https://github.com/rust-lang/rust/issues/68408 enum RecvStreamState {
Recv {
fc: ReceiverFlowControl<StreamId>,
session_fc: Rc<RefCell<ReceiverFlowControl<()>>>,
recv_buf: RxStreamOrderer,
},
SizeKnown {
fc: ReceiverFlowControl<StreamId>,
session_fc: Rc<RefCell<ReceiverFlowControl<()>>>,
recv_buf: RxStreamOrderer,
},
DataRecvd {
fc: ReceiverFlowControl<StreamId>,
session_fc: Rc<RefCell<ReceiverFlowControl<()>>>,
recv_buf: RxStreamOrderer,
},
DataRead {
final_received: u64,
final_read: u64,
},
AbortReading {
fc: ReceiverFlowControl<StreamId>,
session_fc: Rc<RefCell<ReceiverFlowControl<()>>>,
final_size_reached: bool,
frame_needed: bool,
err: AppError,
final_received: u64,
final_read: u64,
},
WaitForReset {
fc: ReceiverFlowControl<StreamId>,
session_fc: Rc<RefCell<ReceiverFlowControl<()>>>,
final_received: u64,
final_read: u64,
},
ResetRecvd {
final_received: u64,
final_read: u64,
}, // Defined by spec but we don't use it: ResetRead
}
if !final_size_ok { return Err(Error::FinalSize);
}
let new_bytes_consumed = fc.set_consumed(consumed)?;
session_fc.borrow_mut().consume(new_bytes_consumed)?; if retire_data { // Let's also retire this data since the stream has been aborted
RecvStream::flow_control_retire_data(fc.consumed() - fc.retired(), fc, session_fc);
}
Ok(())
}
}
// See https://www.w3.org/TR/webtransport/#receive-stream-stats #[derive(Debug, Clone, Copy)] pubstruct Stats { // An indicator of progress on how many of the server application’s bytes // intended for this stream have been received so far. // Only sequential bytes up to, but not including, the first missing byte, // are counted. This number can only increase. pub bytes_received: u64, // The total number of bytes the application has successfully read from this // stream. This number can only increase, and is always less than or equal // to bytes_received. pub bytes_read: u64,
}
match new_state { // Receiving all data, or receiving or requesting RESET_STREAM // is cause to stop keepalives.
RecvStreamState::DataRecvd { .. }
| RecvStreamState::AbortReading { .. }
| RecvStreamState::ResetRecvd { .. } => { self.keep_alive = None;
} // Once all the data is read, generate an event.
RecvStreamState::DataRead { .. } => { self.conn_events.recv_stream_complete(self.stream_id);
}
_ => {}
}
/// # Errors /// When the incoming data violates flow control limits. /// # Panics /// Only when `u64` values are so big that they can't fit in a `usize`, which /// only happens on a 32-bit machine that has far too much unread data. pubfn inbound_stream_frame(&mutself, fin: bool, offset: u64, data: &[u8]) -> Res<()> { // We should post a DataReadable event only once when we change from no-data-ready to // data-ready. Therefore remember the state before processing a new frame. let already_data_ready = self.data_ready(); let new_end = offset + u64::try_from(data.len())?;
match &mutself.state {
RecvStreamState::Recv {
recv_buf,
fc,
session_fc,
} => {
recv_buf.inbound_frame(offset, data); if fin { let all_recv =
fc.consumed() == recv_buf.retired() + recv_buf.bytes_ready() as u64; let buf = mem::replace(recv_buf, RxStreamOrderer::new()); let fc_copy = mem::take(fc); let session_fc_copy = mem::take(session_fc); if all_recv { self.set_state(RecvStreamState::DataRecvd {
fc: fc_copy,
session_fc: session_fc_copy,
recv_buf: buf,
});
} else { self.set_state(RecvStreamState::SizeKnown {
fc: fc_copy,
session_fc: session_fc_copy,
recv_buf: buf,
});
}
}
}
RecvStreamState::SizeKnown {
recv_buf,
fc,
session_fc,
} => {
recv_buf.inbound_frame(offset, data); if fc.consumed() == recv_buf.retired() + recv_buf.bytes_ready() as u64 { let buf = mem::replace(recv_buf, RxStreamOrderer::new()); let fc_copy = mem::take(fc); let session_fc_copy = mem::take(session_fc); self.set_state(RecvStreamState::DataRecvd {
fc: fc_copy,
session_fc: session_fc_copy,
recv_buf: buf,
});
}
}
RecvStreamState::DataRecvd { .. }
| RecvStreamState::DataRead { .. }
| RecvStreamState::AbortReading { .. }
| RecvStreamState::WaitForReset { .. }
| RecvStreamState::ResetRecvd { .. } => {
qtrace!("data received when we are in state {}", self.state);
}
}
if !already_data_ready && (self.data_ready() || self.needs_to_inform_app_about_fin()) { self.conn_events.recv_stream_readable(self.stream_id);
}
Ok(())
}
/// # Errors /// When the reset occurs at an invalid point. /// /// # Returns /// `true` when the stream transitions to `ResetRecvd` (ended). /// `false` if the stream is already in a terminal state and the reset is a no-op. pubfn reset(&mutself, application_error_code: AppError, final_size: u64) -> Res<bool> { self.state.flow_control_consume_data(final_size, true)?; match &mutself.state {
RecvStreamState::Recv {
fc,
session_fc,
recv_buf,
}
| RecvStreamState::SizeKnown {
fc,
session_fc,
recv_buf,
} => { // make flow control consumes new data that not really exist. Self::flow_control_retire_data(final_size - fc.retired(), fc, session_fc); self.conn_events
.recv_stream_reset(self.stream_id, application_error_code); let received = recv_buf.received(); let read = recv_buf.retired(); self.set_state(RecvStreamState::ResetRecvd {
final_received: received,
final_read: read,
});
Ok(true)
}
RecvStreamState::AbortReading {
fc,
session_fc,
final_received,
final_read,
..
}
| RecvStreamState::WaitForReset {
fc,
session_fc,
final_received,
final_read,
} => { // make flow control consumes new data that not really exist. Self::flow_control_retire_data(final_size - fc.retired(), fc, session_fc); self.conn_events
.recv_stream_reset(self.stream_id, application_error_code); let received = *final_received; let read = *final_read; self.set_state(RecvStreamState::ResetRecvd {
final_received: received,
final_read: read,
});
Ok(true)
}
_ => Ok(false), // Ignore reset if in DataRecvd, DataRead, or ResetRecvd
}
}
/// Send a flow control update. /// This is used when a peer declares that they are blocked. /// This sends `MAX_STREAM_DATA` if there is any increase possible. pubconstfn send_flowc_update(&mutself) { iflet RecvStreamState::Recv { fc, .. } = &mutself.state {
fc.send_flowc_update();
}
}
// App got all data but did not get the fin signal. constfn needs_to_inform_app_about_fin(&self) -> bool {
matches!(self.state, RecvStreamState::DataRecvd { .. })
}
/// # Returns /// `true` if the stream transitions to `ResetRecvd` (ended) because /// the final size was already known. #[must_use] pubfn stop_sending_acked(&mutself) -> bool { iflet RecvStreamState::AbortReading {
fc,
session_fc,
final_size_reached,
final_received,
final_read,
..
} = &mutself.state
{ let received = *final_received; let read = *final_read; if *final_size_reached { // We already know the final_size of the stream therefore we // do not need to wait for RESET. self.set_state(RecvStreamState::ResetRecvd {
final_received: received,
final_read: read,
}); returntrue;
} let fc_copy = mem::take(fc); let session_fc_copy = mem::take(session_fc); self.set_state(RecvStreamState::WaitForReset {
fc: fc_copy,
session_fc: session_fc_copy,
final_received: received,
final_read: read,
});
} false
}
letmut s = RxStreamOrderer::default(); for r in ranges { let data = &ZEROES[..usize::try_from(r.end - r.start).unwrap()];
s.inbound_frame(r.start, data);
}
/// A buffer of exactly 4096 bytes has reached the extension limit and must not be extended. #[test] fn inbound_frame_no_extend_at_4096() { letmut s = RxStreamOrderer::default(); // Fill to the extend threshold.
s.inbound_frame(0, &[0u8; 4096]);
assert_eq!(s.data_ranges[&0].len(), 4096); // The next byte must not be merged; the threshold has been reached.
s.inbound_frame(4096, &[1u8]);
assert_eq!(
s.data_ranges.len(), 2, "a 4096-byte buffer must not be extended further"
);
}
/// A buffer of 4095 bytes IS extended when the next frame is contiguous. #[test] fn inbound_frame_extends_below_4096() { letmut s = RxStreamOrderer::default();
s.inbound_frame(0, &[0u8; 4095]);
s.inbound_frame(4095, &[1u8]);
assert_eq!(s.data_ranges.len(), 1);
assert_eq!(s.data_ranges[&0].len(), 4096);
}
/// Reading exactly `available` bytes frees the range so the next read can proceed. #[test] fn read_exact_available_removes_range() { letmut s = RxStreamOrderer::default();
s.inbound_frame(0, &[1u8; 5]);
s.inbound_frame(5, &[2u8; 5]);
#[test] #[expect(
clippy::single_range_in_vec_init,
reason = "Because that lint makes no sense here."
)] fn recv_noncontiguous() { // Non-contiguous with the start, no data available.
recv_ranges(&[10..20], 0);
}
/// Overlaps with the start of a 10..20 range of bytes. #[test] fn recv_overlap_start() { // Overlap the start, with a larger new value. // More overlap than not.
recv_ranges(&[10..20, 4..18, 0..4], 20); // Overlap the start, with a larger new value. // Less overlap than not.
recv_ranges(&[10..20, 2..15, 0..2], 20); // Overlap the start, with a smaller new value. // More overlap than not.
recv_ranges(&[10..20, 8..14, 0..8], 20); // Overlap the start, with a smaller new value. // Less overlap than not.
recv_ranges(&[10..20, 6..13, 0..6], 20);
// Again with some of the first range split in two.
recv_ranges(&[10..11, 11..20, 4..18, 0..4], 20);
recv_ranges(&[10..11, 11..20, 2..15, 0..2], 20);
recv_ranges(&[10..11, 11..20, 8..14, 0..8], 20);
recv_ranges(&[10..11, 11..20, 6..13, 0..6], 20);
// Again with a gap in the first range.
recv_ranges(&[10..11, 12..20, 4..18, 0..4], 20);
recv_ranges(&[10..11, 12..20, 2..15, 0..2], 20);
recv_ranges(&[10..11, 12..20, 8..14, 0..8], 20);
recv_ranges(&[10..11, 12..20, 6..13, 0..6], 20);
}
/// Overlaps with the end of a 10..20 range of bytes. #[test] fn recv_overlap_end() { // Overlap the end, with a larger new value. // More overlap than not.
recv_ranges(&[10..20, 12..25, 0..10], 25); // Overlap the end, with a larger new value. // Less overlap than not.
recv_ranges(&[10..20, 17..33, 0..10], 33); // Overlap the end, with a smaller new value. // More overlap than not.
recv_ranges(&[10..20, 15..21, 0..10], 21); // Overlap the end, with a smaller new value. // Less overlap than not.
recv_ranges(&[10..20, 17..25, 0..10], 25);
// Again with some of the first range split in two.
recv_ranges(&[10..19, 19..20, 12..25, 0..10], 25);
recv_ranges(&[10..19, 19..20, 17..33, 0..10], 33);
recv_ranges(&[10..19, 19..20, 15..21, 0..10], 21);
recv_ranges(&[10..19, 19..20, 17..25, 0..10], 25);
// Again with a gap in the first range.
recv_ranges(&[10..18, 19..20, 12..25, 0..10], 25);
recv_ranges(&[10..18, 19..20, 17..33, 0..10], 33);
recv_ranges(&[10..18, 19..20, 15..21, 0..10], 21);
recv_ranges(&[10..18, 19..20, 17..25, 0..10], 25);
}
/// Complete overlaps with the start of a 10..20 range of bytes. #[test] fn recv_overlap_complete() { // Complete overlap, more at the end.
recv_ranges(&[10..20, 9..23, 0..9], 23); // Complete overlap, more at the start.
recv_ranges(&[10..20, 3..23, 0..3], 23); // Complete overlap, to end.
recv_ranges(&[10..20, 5..20, 0..5], 20); // Complete overlap, from start.
recv_ranges(&[10..20, 10..27, 0..10], 27); // Complete overlap, from 0 and more.
recv_ranges(&[10..20, 0..23], 23);
// Again with the first range split in two.
recv_ranges(&[10..14, 14..20, 9..23, 0..9], 23);
recv_ranges(&[10..14, 14..20, 3..23, 0..3], 23);
recv_ranges(&[10..14, 14..20, 5..20, 0..5], 20);
recv_ranges(&[10..14, 14..20, 10..27, 0..10], 27);
recv_ranges(&[10..14, 14..20, 0..23], 23);
// Again with the a gap in the first range.
recv_ranges(&[10..13, 14..20, 9..23, 0..9], 23);
recv_ranges(&[10..13, 14..20, 3..23, 0..3], 23);
recv_ranges(&[10..13, 14..20, 5..20, 0..5], 20);
recv_ranges(&[10..13, 14..20, 10..27, 0..10], 27);
recv_ranges(&[10..13, 14..20, 0..23], 23);
}
/// An overlap with no new bytes. #[test] fn recv_overlap_duplicate() {
recv_ranges(&[10..20, 11..12, 0..10], 20);
recv_ranges(&[10..20, 10..15, 0..10], 20);
recv_ranges(&[10..20, 14..20, 0..10], 20); // Now with the first range split.
recv_ranges(&[10..14, 14..20, 10..15, 0..10], 20);
recv_ranges(&[10..15, 16..20, 21..25, 10..25, 0..10], 25);
}
/// Reading exactly one chunk works, when the next chunk starts immediately. #[test] fn stop_reading_at_chunk() { const CHUNK_SIZE: usize = 10; const EXTRA_SIZE: usize = 3; letmut s = RxStreamOrderer::new();
// Add three chunks.
s.inbound_frame(0, &[0; CHUNK_SIZE]); let offset = u64::try_from(CHUNK_SIZE).unwrap();
s.inbound_frame(offset, &[0; EXTRA_SIZE]); let offset = u64::try_from(CHUNK_SIZE + EXTRA_SIZE).unwrap();
s.inbound_frame(offset, &[0; EXTRA_SIZE]);
// Read, providing only enough space for the first. letmut buf = [0; 100]; let count = s.read(&mut buf[..CHUNK_SIZE]);
assert_eq!(count, CHUNK_SIZE); let count = s.read(&mut buf[..]);
assert_eq!(count, EXTRA_SIZE * 2);
}
#[test] fn recv_overlap_while_reading() { letmut s = RxStreamOrderer::new();
// Add a chunk
s.inbound_frame(0, &[0; 150]);
assert_eq!(s.data_ranges[&0].len(), 150); // Read, providing only enough space for the first 100. letmut buf = [0; 100]; let count = s.read(&mut buf[..]);
assert_eq!(count, 100);
assert_eq!(s.retired, 100);
// Add a second frame that overlaps. // This shouldn't truncate the first frame, as we're already // Reading from it.
s.inbound_frame(120, &[0; 60]);
assert_eq!(s.data_ranges[&0].len(), 180); // Read second part of first frame and all of the second frame let count = s.read(&mut buf[..]);
assert_eq!(count, 80);
}
/// Reading exactly one chunk works, when there is a gap. #[test] fn stop_reading_at_gap() { const CHUNK_SIZE: usize = 10; const EXTRA_SIZE: usize = 3; letmut s = RxStreamOrderer::new();
// Add three chunks.
s.inbound_frame(0, &[0; CHUNK_SIZE]); let offset = u64::try_from(CHUNK_SIZE + EXTRA_SIZE).unwrap();
s.inbound_frame(offset, &[0; EXTRA_SIZE]);
// Read, providing only enough space for the first chunk. letmut buf = [0; 100]; let count = s.read(&mut buf[..CHUNK_SIZE]);
assert_eq!(count, CHUNK_SIZE);
// Now fill the gap and ensure that everything can be read. let offset = u64::try_from(CHUNK_SIZE).unwrap();
s.inbound_frame(offset, &[0; EXTRA_SIZE]); let count = s.read(&mut buf[..]);
assert_eq!(count, EXTRA_SIZE * 2);
}
/// Reading exactly one chunk works, when there is a gap. #[test] fn stop_reading_in_chunk() { const CHUNK_SIZE: usize = 10; const EXTRA_SIZE: usize = 3; letmut s = RxStreamOrderer::new();
// Add two chunks.
s.inbound_frame(0, &[0; CHUNK_SIZE]); let offset = u64::try_from(CHUNK_SIZE).unwrap();
s.inbound_frame(offset, &[0; EXTRA_SIZE]);
// Read, providing only enough space for some of the first chunk. letmut buf = [0; 100]; let count = s.read(&mut buf[..CHUNK_SIZE - EXTRA_SIZE]);
assert_eq!(count, CHUNK_SIZE - EXTRA_SIZE);
let count = s.read(&mut buf[..]);
assert_eq!(count, EXTRA_SIZE * 2);
}
/// Read one byte at a time. #[test] fn read_byte_at_a_time() { const CHUNK_SIZE: usize = 10; const EXTRA_SIZE: usize = 3; letmut s = RxStreamOrderer::new();
// Add two chunks.
s.inbound_frame(0, &[0; CHUNK_SIZE]); let offset = u64::try_from(CHUNK_SIZE).unwrap();
s.inbound_frame(offset, &[0; EXTRA_SIZE]);
// test receiving a contig frame and reading it works
s.inbound_stream_frame(false, 0, &[1; 10]).unwrap();
assert!(s.data_ready());
check_stats(&s, 10, 0);
// test receiving a noncontig frame
s.inbound_stream_frame(false, 12, &[2; 12]).unwrap();
assert!(!s.data_ready());
assert_eq!(s.read(&mut buf).unwrap(), (0, false));
assert_eq!(s.state.recv_buf().unwrap().retired(), 10);
assert_eq!(s.state.recv_buf().unwrap().buffered(), 12);
check_stats(&s, 22, 10);
// another frame that overlaps the first
s.inbound_stream_frame(false, 14, &[3; 8]).unwrap();
assert!(!s.data_ready());
assert_eq!(s.state.recv_buf().unwrap().retired(), 10);
assert_eq!(s.state.recv_buf().unwrap().buffered(), 12);
check_stats(&s, 22, 10);
// fill in the gap, but with a FIN
s.inbound_stream_frame(true, 10, &[4; 6]).unwrap_err();
assert!(!s.data_ready());
assert_eq!(s.read(&mut buf).unwrap(), (0, false));
assert_eq!(s.state.recv_buf().unwrap().retired(), 10);
assert_eq!(s.state.recv_buf().unwrap().buffered(), 12);
check_stats(&s, 22, 10);
// fill in the gap
s.inbound_stream_frame(false, 10, &[5; 10]).unwrap();
assert!(s.data_ready());
assert_eq!(s.state.recv_buf().unwrap().retired(), 10);
assert_eq!(s.state.recv_buf().unwrap().buffered(), 14);
check_stats(&s, 24, 10);
// a legit FIN
s.inbound_stream_frame(true, 24, &[6; 18]).unwrap();
assert_eq!(s.state.recv_buf().unwrap().retired(), 10);
assert_eq!(s.state.recv_buf().unwrap().buffered(), 32);
assert!(s.data_ready());
assert_eq!(s.read(&mut buf).unwrap(), (32, true));
check_stats(&s, 42, 42);
// Stream now no longer readable (is in DataRead state)
s.read(&mut buf).unwrap_err();
}
// Insertion before an existing chunk causes truncation of the new chunk.
s.inbound_frame(0, &[7; 6]);
check_chunks(&s, &[(0, 1), (1, 6)]);
// New data at the end causes the tail to be added to the first chunk, // replacing later chunks entirely.
s.inbound_frame(0, &[9; 8]);
check_chunks(&s, &[(0, 8)]);
// read some so there's an offset into the first frame letmut buf = [0u8; 10];
rx_ord.read(&mut buf[..2]);
assert_eq!(rx_ord.bytes_ready(), 4);
assert_eq!(rx_ord.buffered(), 4);
assert_eq!(rx_ord.retired(), 2);
#[test] fn session_flow_control() { let (mut s, session_fc) = create_stream_session_flow_control();
s.inbound_stream_frame(false, 0, &[0; SESSION_WINDOW])
.unwrap();
assert!(!session_fc.borrow().frame_needed()); // The buffer is big enough to hold SESSION_WINDOW, this will make sure that we always // read everything from he stream. letmut buf = [0; 2 * SESSION_WINDOW];
s.read(&mut buf).unwrap();
assert!(session_fc.borrow().frame_needed()); // consume it letmut builder =
packet::Builder::short(Encoder::default(), false, None::<&[u8]>, packet::LIMIT); letmut token = recovery::Tokens::new();
session_fc.borrow_mut().write_frames(
&mut builder,
&mut token,
&mut FrameStats::default(),
now(),
Duration::from_millis(100),
);
// Switch to SizeKnown state
s.inbound_stream_frame(true, 2 * u64::try_from(SESSION_WINDOW).unwrap() - 1, &[e='color: green'>0])
.unwrap();
assert!(!session_fc.borrow().frame_needed()); // Receive new data that can be read.
s.inbound_stream_frame( false,
u64::try_from(SESSION_WINDOW).unwrap(),
&[0; SESSION_WINDOW / 2 + 1],
)
.unwrap();
assert!(!session_fc.borrow().frame_needed());
s.read(&mut buf).unwrap();
assert!(session_fc.borrow().frame_needed()); // consume it letmut builder =
packet::Builder::short(Encoder::default(), false, None::<&[u8]>, packet::LIMIT); letmut token = recovery::Tokens::new();
session_fc.borrow_mut().write_frames(
&mut builder,
&mut token,
&mut FrameStats::default(),
now(),
Duration::from_millis(100),
);
// Test DataRecvd state let session_fc = Rc::new(RefCell::new(ReceiverFlowControl::new(
(),
u64::try_from(SESSION_WINDOW).unwrap(),
))); letmut s = RecvStream::new(
StreamId::from(567),
INITIAL_LOCAL_MAX_STREAM_DATA as u64,
Rc::clone(&session_fc),
ConnectionEvents::default(),
);
// Read when there is no more date to be read will not change fc.
assert_eq!(s1.read(&mut buf).unwrap(), (0, false));
check_fc(&fc.borrow(), SW / 2, SW / 2);
check_fc(s1.fc().unwrap(), SW / 4, SW / 4);
check_fc(s2.fc().unwrap(), SW / 4, SW / 4);
// Receiving more data on a stream.
s1.inbound_stream_frame(false, SW / 4, &[0; SW_US / 4])
.unwrap();
check_fc(&fc.borrow(), SW * 3 / 4, SW / 2);
check_fc(s1.fc().unwrap(), SW / 2, SW / 4);
check_fc(s2.fc().unwrap(), SW / 4, SW / 4);
/// Test consuming the flow control in `RecvStreamState::Recv` - duplicate data #[test] fn fc_state_recv_4() { const SW: u64 = 1024; const SW_US: usize = 1024; let fc = Rc::new(RefCell::new(ReceiverFlowControl::new((), SW))); letmut s = create_stream_with_fc(Rc::clone(&fc), SW * 3 / 4);
// Receiving duplicate frames (already consumed data) will not cause an error or // change fc.
s.inbound_stream_frame(false, 0, &[0; SW_US / 8]).unwrap();
check_fc(&fc.borrow(), SW / 4, 0);
check_fc(s.fc().unwrap(), SW / 4, 0);
}
/// Test consuming the flow control in `RecvStreamState::Recv` - filling a gap in the /// data stream. #[test] fn fc_state_recv_5() { const SW: u64 = 1024; const SW_US: usize = 1024; let fc = Rc::new(RefCell::new(ReceiverFlowControl::new((), SW))); letmut s = create_stream_with_fc(Rc::clone(&fc), SW * 3 / 4);
// Receive out of order data.
s.inbound_stream_frame(false, SW / 8, &[0; SW_US / 8])
.unwrap();
check_fc(&fc.borrow(), SW / 4, 0);
check_fc(s.fc().unwrap(), SW / 4, 0);
// Filling in the gap will not change fc.
s.inbound_stream_frame(false, 0, &[0; SW_US / 8]).unwrap();
check_fc(&fc.borrow(), SW / 4, 0);
check_fc(s.fc().unwrap(), SW / 4, 0);
}
/// Test consuming the flow control in `RecvStreamState::Recv` - receiving frame past /// the flow control will cause an error. #[test] fn fc_state_recv_6() { const SW: u64 = 1024; const SW_US: usize = 1024; let fc = Rc::new(RefCell::new(ReceiverFlowControl::new((), SW))); letmut s = create_stream_with_fc(Rc::clone(&fc), SW * 3 / 4);
// Receiving frame past the flow control will cause an error.
assert_eq!(
s.inbound_stream_frame(false, 0, &[0; SW_US * 3 / 4 + 1]),
Err(Error::FlowControl)
);
}
/// Test that the flow controls will send updates. #[expect(
clippy::too_many_lines,
clippy::cast_possible_truncation,
reason = "This is test code."
)] #[test] fn fc_state_recv_7() { const CONNECTION_WINDOW: u64 = 1024; const CONNECTION_WINDOW_US: usize = CONNECTION_WINDOW as usize;
// Receive data up to but not over the fc update trigger point.
s.inbound_stream_frame(false, 0, &[0; STREAM_WINDOW_US / WINDOW_UPDATE_FRACTION_US])
.unwrap(); letmut buf = [1; CONNECTION_WINDOW_US];
assert_eq!(
s.read(&mut buf).unwrap(),
(STREAM_WINDOW_US / WINDOW_UPDATE_FRACTION_US, false)
);
check_fc(
&fc.borrow(),
STREAM_WINDOW / WINDOW_UPDATE_FRACTION,
STREAM_WINDOW / WINDOW_UPDATE_FRACTION,
);
check_fc(
s.fc().unwrap(),
STREAM_WINDOW / WINDOW_UPDATE_FRACTION,
STREAM_WINDOW / WINDOW_UPDATE_FRACTION,
);
// Still no fc update needed.
assert!(!fc.borrow().frame_needed());
assert!(!s.fc().unwrap().frame_needed());
// Receive one more byte that will cause a fc update after it is read.
s.inbound_stream_frame(false, STREAM_WINDOW / WINDOW_UPDATE_FRACTION, &[0])
.unwrap();
check_fc(
&fc.borrow(),
STREAM_WINDOW / WINDOW_UPDATE_FRACTION + 1,
STREAM_WINDOW / WINDOW_UPDATE_FRACTION,
);
check_fc(
s.fc().unwrap(),
STREAM_WINDOW / WINDOW_UPDATE_FRACTION + 1,
STREAM_WINDOW / WINDOW_UPDATE_FRACTION,
); // Only consuming data does not cause a fc update to be sent.
assert!(!fc.borrow().frame_needed());
assert!(!s.fc().unwrap().frame_needed());
assert_eq!(s.read(&mut buf).unwrap(), (1, false));
check_fc(
&fc.borrow(),
STREAM_WINDOW / WINDOW_UPDATE_FRACTION + 1,
STREAM_WINDOW / WINDOW_UPDATE_FRACTION + 1,
);
check_fc(
s.fc().unwrap(),
STREAM_WINDOW / WINDOW_UPDATE_FRACTION + 1,
STREAM_WINDOW / WINDOW_UPDATE_FRACTION + 1,
); // Data are retired and the stream fc will send an update.
assert!(!fc.borrow().frame_needed());
assert!(s.fc().unwrap().frame_needed());
/// Test flow control in `RecvStreamState::SizeKnown` #[test] fn fc_state_size_known() { const SW: u64 = 1024; const SW_US: usize = 1024; let fc = Rc::new(RefCell::new(ReceiverFlowControl::new((), SW)));
letmut s = create_stream_with_fc(Rc::clone(&fc), SW);
// Receiving duplicate frames (already consumed data) will not cause an error or // change fc.
s.inbound_stream_frame(true, SW / 4, &[0; SW_US / 4])
.unwrap();
check_fc(&fc.borrow(), SW / 2, 0);
check_fc(s.fc().unwrap(), SW / 2, 0);
// The stream can still receive duplicate data without a fin bit.
s.inbound_stream_frame(false, SW / 4, &[0; SW_US / 4])
.unwrap();
check_fc(&fc.borrow(), SW / 2, 0);
check_fc(s.fc().unwrap(), SW / 2, 0);
// Receiving frame past the final size of a stream will return an error.
assert_eq!(
s.inbound_stream_frame(true, SW / 4, &[0; SW_US / 4 + 1]),
Err(Error::FinalSize)
);
check_fc(&fc.borrow(), SW / 2, 0);
check_fc(s.fc().unwrap(), SW / 2, 0);
// Add new data to the gap will not change fc.
s.inbound_stream_frame(false, SW / 8, &[0; SW_US / 8])
.unwrap();
check_fc(&fc.borrow(), SW / 2, 0);
check_fc(s.fc().unwrap(), SW / 2, 0);
// Fill the gap
s.inbound_stream_frame(false, 0, &[0; SW_US / 8]).unwrap();
check_fc(&fc.borrow(), SW / 2, 0);
check_fc(s.fc().unwrap(), SW / 2, 0);
// Read all data letmut buf = [1; SW_US];
assert_eq!(s.read(&mut buf).unwrap(), (SW_US / 2, true)); // the stream does not have fc any more. We can only check the session fc.
check_fc(&fc.borrow(), SW / 2, SW / 2);
assert!(s.fc().is_none());
}
/// Test flow control in `RecvStreamState::DataRecvd` #[test] fn fc_state_data_recv() { const SW: u64 = 1024; const SW_US: usize = 1024; let fc = Rc::new(RefCell::new(ReceiverFlowControl::new((), SW)));
letmut s = create_stream_with_fc(Rc::clone(&fc), SW);
// Receiving duplicate frames (already consumed data) will not cause an error or // change fc.
s.inbound_stream_frame(true, SW / 4, &[0; SW_US / 4])
.unwrap();
check_fc(&fc.borrow(), SW / 2, 0);
check_fc(s.fc().unwrap(), SW / 2, 0);
// The stream can still receive duplicate data without a fin bit.
s.inbound_stream_frame(false, SW / 4, &[0; SW_US / 4])
.unwrap();
check_fc(&fc.borrow(), SW / 2, 0);
check_fc(s.fc().unwrap(), SW / 2, 0);
// Receiving frame past the final size of a stream will return an error.
assert_eq!(
s.inbound_stream_frame(true, SW / 4, &[0; SW_US / 4 + 1]),
Err(Error::FinalSize)
);
check_fc(&fc.borrow(), SW / 2, 0);
check_fc(s.fc().unwrap(), SW / 2, 0);
// Read all data letmut buf = [1; SW_US];
assert_eq!(s.read(&mut buf).unwrap(), (SW_US / 2, true)); // the stream does not have fc any more. We can only check the session fc.
check_fc(&fc.borrow(), SW / 2, SW / 2);
assert!(s.fc().is_none());
}
/// Test flow control in `RecvStreamState::DataRead` #[test] fn fc_state_data_read() { const SW: u64 = 1024; const SW_US: usize = 1024; let fc = Rc::new(RefCell::new(ReceiverFlowControl::new((), SW)));
letmut buf = [1; SW_US];
assert_eq!(s.read(&mut buf).unwrap(), (SW_US / 2, true)); // the stream does not have fc any more. We can only check the session fc.
check_fc(&fc.borrow(), SW / 2, SW / 2);
assert!(s.fc().is_none());
// Receiving duplicate frames (already consumed data) will not cause an error or // change fc.
s.inbound_stream_frame(true, 0, &[0; SW_US / 2]).unwrap(); // the stream does not have fc any more. We can only check the session fc.
check_fc(&fc.borrow(), SW / 2, SW / 2);
assert!(s.fc().is_none());
// Receiving frame past the final size of a stream or the stream's fc limit // will NOT return an error.
s.inbound_stream_frame(true, 0, &[0; SW_US / 2 + 1])
.unwrap();
s.inbound_stream_frame(true, 0, &[0; SW_US * 3 / 4 + 1])
.unwrap();
check_fc(&fc.borrow(), SW / 2, SW / 2);
assert!(s.fc().is_none());
}
/// Test flow control in `RecvStreamState::AbortReading` and final size is known #[test] fn fc_state_abort_reading_1() { const SW: u64 = 1024; const SW_US: usize = 1024; let fc = Rc::new(RefCell::new(ReceiverFlowControl::new((), SW)));
assert!(!s.stop_sending(Error::None.code())); // All data will de retired
check_fc(&fc.borrow(), SW / 2, SW / 2);
check_fc(s.fc().unwrap(), SW / 2, SW / 2);
// Receiving duplicate frames (already consumed data) will not cause an error or // change fc.
s.inbound_stream_frame(true, 0, &[0; SW_US / 2]).unwrap();
check_fc(&fc.borrow(), SW / 2, SW / 2);
check_fc(s.fc().unwrap(), SW / 2, SW / 2);
// The stream can still receive duplicate data without a fin bit.
s.inbound_stream_frame(false, SW / 4, &[0; SW_US / 4])
.unwrap();
check_fc(&fc.borrow(), SW / 2, SW / 2);
check_fc(s.fc().unwrap(), SW / 2, SW / 2);
// Receiving frame past the final size of a stream will return an error.
assert_eq!(
s.inbound_stream_frame(true, SW / 4, &[0; SW_US / 4 + 1]),
Err(Error::FinalSize)
);
check_fc(&fc.borrow(), SW / 2, SW / 2);
check_fc(s.fc().unwrap(), SW / 2, SW / 2);
}
/// Test flow control in `RecvStreamState::AbortReading` and final size is unknown #[test] fn fc_state_abort_reading_2() { const SW: u64 = 1024; const SW_US: usize = 1024; let fc = Rc::new(RefCell::new(ReceiverFlowControl::new((), SW)));
assert!(!s.stop_sending(Error::None.code())); // All data will de retired
check_fc(&fc.borrow(), SW / 2, SW / 2);
check_fc(s.fc().unwrap(), SW / 2, SW / 2);
// Receiving duplicate frames (already consumed data) will not cause an error or // change fc.
s.inbound_stream_frame(false, 0, &[0; SW_US / 2]).unwrap();
check_fc(&fc.borrow(), SW / 2, SW / 2);
check_fc(s.fc().unwrap(), SW / 2, SW / 2);
// Receiving data past the flow control limit will cause an error.
assert_eq!(
s.inbound_stream_frame(false, 0, &[0; SW_US * 3 / 4 + 1]),
Err(Error::FlowControl)
);
// The stream can still receive duplicate data without a fin bit.
s.inbound_stream_frame(false, SW / 4, &[0; SW_US / 4])
.unwrap();
check_fc(&fc.borrow(), SW / 2, SW / 2);
check_fc(s.fc().unwrap(), SW / 2, SW / 2);
// Receiving more data will case the data to be retired. // The stream can still receive duplicate data without a fin bit.
s.inbound_stream_frame(false, SW / 2, &[0; 10]).unwrap();
check_fc(&fc.borrow(), SW / 2 + 10, SW / 2 + 10);
check_fc(s.fc().unwrap(), SW / 2 + 10, SW / 2 + 10);
// We can still receive the final size.
s.inbound_stream_frame(true, SW / 2, &[0; 20]).unwrap();
check_fc(&fc.borrow(), SW / 2 + 20, SW / 2 + 20);
check_fc(s.fc().unwrap(), SW / 2 + 20, SW / 2 + 20);
// Receiving frame past the final size of a stream will return an error.
assert_eq!(
s.inbound_stream_frame(true, SW / 2, &[0; 21]),
Err(Error::FinalSize)
);
check_fc(&fc.borrow(), SW / 2 + 20, SW / 2 + 20);
check_fc(s.fc().unwrap(), SW / 2 + 20, SW / 2 + 20);
}
/// Test flow control in `RecvStreamState::WaitForReset` #[test] fn fc_state_wait_for_reset() { const SW: u64 = 1024; const SW_US: usize = 1024; let fc = Rc::new(RefCell::new(ReceiverFlowControl::new((), SW)));
// Receiving duplicate frames (already consumed data) will not cause an error or // change fc.
s.inbound_stream_frame(false, 0, &[0; SW_US / 2]).unwrap();
check_fc(&fc.borrow(), SW / 2, SW / 2);
check_fc(s.fc().unwrap(), SW / 2, SW / 2);
// Receiving data past the flow control limit will cause an error.
assert_eq!(
s.inbound_stream_frame(false, 0, &[0; SW_US * 3 / 4 + 1]),
Err(Error::FlowControl)
);
// The stream can still receive duplicate data without a fin bit.
s.inbound_stream_frame(false, SW / 4, &[0; SW_US / 4])
.unwrap();
check_fc(&fc.borrow(), SW / 2, SW / 2);
check_fc(s.fc().unwrap(), SW / 2, SW / 2);
// Receiving more data will case the data to be retired. // The stream can still receive duplicate data without a fin bit.
s.inbound_stream_frame(false, SW / 2, &[0; 10]).unwrap();
check_fc(&fc.borrow(), SW / 2 + 10, SW / 2 + 10);
check_fc(s.fc().unwrap(), SW / 2 + 10, SW / 2 + 10);
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.52 Sekunden
(vorverarbeitet am 2026-08-25)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.