use std::{
collections::{BinaryHeap, hash_map},
io,
};
use bytes::Bytes;
use thiserror::Error;
use tracing::trace;
use super::spaces::Retransmits;
use crate::{
Dir, StreamId, VarInt,
connection::streams::state::{get_or_insert_recv, get_or_insert_send},
frame,
};
mod recv;
use recv::Recv;
pub use recv::{Chunks, ReadError, ReadableError};
mod send;
pub(crate) use send::{ByteSlice, BytesArray};
use send::{BytesSource, Send, SendState};
pub use send::{FinishError, WriteError, Written};
mod state;
#[allow(unreachable_pub)] pub use state::StreamsState;
pub struct Streams<'a> {
pub(super) state: &'a mut StreamsState,
pub(super) conn_state: &'a super::State,
}
#[allow(clippy::needless_lifetimes)] impl<'a> Streams<'a> {
#[cfg(fuzzing)]
pub fn new(state: &'a mut StreamsState, conn_state: &'a super::State) -> Self {
Self { state, conn_state }
}
pub fn open(&mut self, dir: Dir) -> Option<StreamId> {
if self.conn_state.is_closed() {
return None;
}
if self.state.next[dir as usize] >= self.state.max[dir as usize] {
return None;
}
self.state.next[dir as usize] += 1;
let id = StreamId::new(self.state.side, dir, self.state.next[dir as usize] - 1);
self.state.insert(false, id);
self.state.send_streams += 1;
Some(id)
}
pub fn accept(&mut self, dir: Dir) -> Option<StreamId> {
if self.state.next_remote[dir as usize] == self.state.next_reported_remote[dir as usize] {
return None;
}
let x = self.state.next_reported_remote[dir as usize];
self.state.next_reported_remote[dir as usize] = x + 1;
if dir == Dir::Bi {
self.state.send_streams += 1;
}
Some(StreamId::new(!self.state.side, dir, x))
}
#[cfg(fuzzing)]
pub fn state(&mut self) -> &mut StreamsState {
self.state
}
pub fn send_streams(&self) -> usize {
self.state.send_streams
}
pub fn remote_open_streams(&self, dir: Dir) -> u64 {
self.state.next_remote[dir as usize]
- (self.state.max_remote[dir as usize]
- self.state.allocated_remote_count[dir as usize])
}
}
pub struct RecvStream<'a> {
pub(super) id: StreamId,
pub(super) state: &'a mut StreamsState,
pub(super) pending: &'a mut Retransmits,
}
impl RecvStream<'_> {
pub fn read(&mut self, ordered: bool) -> Result<Chunks<'_>, ReadableError> {
Chunks::new(self.id, ordered, self.state, self.pending)
}
pub fn stop(&mut self, error_code: VarInt) -> Result<(), ClosedStream> {
let mut entry = match self.state.recv.entry(self.id) {
hash_map::Entry::Occupied(s) => s,
hash_map::Entry::Vacant(_) => return Err(ClosedStream { _private: () }),
};
let stream = get_or_insert_recv(self.state.stream_receive_window)(entry.get_mut());
let (read_credits, stop_sending) = stream.stop()?;
if stop_sending.should_transmit() {
self.pending.stop_sending.push(frame::StopSending {
id: self.id,
error_code,
});
}
if !stream.final_offset_unknown() {
let recv = entry.remove().expect("must have recv when stopping");
self.state.stream_recv_freed(self.id, recv);
}
if self.state.add_read_credits(read_credits).should_transmit() {
self.pending.max_data = true;
}
Ok(())
}
pub fn received_reset(&mut self) -> Result<Option<VarInt>, ClosedStream> {
let hash_map::Entry::Occupied(entry) = self.state.recv.entry(self.id) else {
return Err(ClosedStream { _private: () });
};
let Some(s) = entry.get().as_ref().and_then(|s| s.as_open_recv()) else {
return Ok(None);
};
if s.stopped {
return Err(ClosedStream { _private: () });
}
let Some(code) = s.reset_code() else {
return Ok(None);
};
let (_, recv) = entry.remove_entry();
self.state
.stream_recv_freed(self.id, recv.expect("must have recv on reset"));
self.state.queue_max_stream_id(self.pending);
Ok(Some(code))
}
}
pub struct SendStream<'a> {
pub(super) id: StreamId,
pub(super) state: &'a mut StreamsState,
pub(super) pending: &'a mut Retransmits,
pub(super) conn_state: &'a super::State,
}
#[allow(clippy::needless_lifetimes)] impl<'a> SendStream<'a> {
#[cfg(fuzzing)]
pub fn new(
id: StreamId,
state: &'a mut StreamsState,
pending: &'a mut Retransmits,
conn_state: &'a super::State,
) -> Self {
Self {
id,
state,
pending,
conn_state,
}
}
pub fn write(&mut self, data: &[u8]) -> Result<usize, WriteError> {
Ok(self.write_source(&mut ByteSlice::from_slice(data))?.bytes)
}
pub fn write_chunks(&mut self, data: &mut [Bytes]) -> Result<Written, WriteError> {
self.write_source(&mut BytesArray::from_chunks(data))
}
fn write_source<'b, B: BytesSource<'b>>(
&mut self,
source: &'b mut B,
) -> Result<Written, WriteError> {
if self.conn_state.is_closed() {
trace!(%self.id, "write blocked; connection draining");
return Err(WriteError::Blocked);
}
let limit = self.state.write_limit();
let max_send_data = self.state.max_send_data(self.id);
let stream = self
.state
.send
.get_mut(&self.id)
.map(get_or_insert_send(max_send_data))
.ok_or(WriteError::ClosedStream)?;
if limit == 0 {
trace!(
stream = %self.id, max_data = self.state.max_data, data_sent = self.state.data_sent,
"write blocked by connection-level flow control or send window"
);
if !stream.connection_blocked {
stream.connection_blocked = true;
self.state.connection_blocked.push(self.id);
}
return Err(WriteError::Blocked);
}
let was_pending = stream.is_pending();
let written = stream.write(source, limit)?;
self.state.data_sent += written.bytes as u64;
self.state.unacked_data += written.bytes as u64;
trace!(stream = %self.id, "wrote {} bytes", written.bytes);
if !was_pending {
self.state.pending.push_pending(self.id, stream.priority);
}
Ok(written)
}
pub fn stopped(&self) -> Result<Option<VarInt>, ClosedStream> {
match self.state.send.get(&self.id).as_ref() {
Some(Some(s)) => Ok(s.stop_reason),
Some(None) => Ok(None),
None => Err(ClosedStream { _private: () }),
}
}
pub fn finish(&mut self) -> Result<(), FinishError> {
let max_send_data = self.state.max_send_data(self.id);
let stream = self
.state
.send
.get_mut(&self.id)
.map(get_or_insert_send(max_send_data))
.ok_or(FinishError::ClosedStream)?;
let was_pending = stream.is_pending();
stream.finish()?;
if !was_pending {
self.state.pending.push_pending(self.id, stream.priority);
}
Ok(())
}
pub fn reset(&mut self, error_code: VarInt) -> Result<(), ClosedStream> {
let max_send_data = self.state.max_send_data(self.id);
let stream = self
.state
.send
.get_mut(&self.id)
.map(get_or_insert_send(max_send_data))
.ok_or(ClosedStream { _private: () })?;
if matches!(stream.state, SendState::ResetSent) {
return Err(ClosedStream { _private: () });
}
self.state.unacked_data -= stream.pending.unacked();
stream.reset();
self.pending.reset_stream.push((self.id, error_code));
Ok(())
}
pub fn set_priority(&mut self, priority: i32) -> Result<(), ClosedStream> {
let max_send_data = self.state.max_send_data(self.id);
let stream = self
.state
.send
.get_mut(&self.id)
.map(get_or_insert_send(max_send_data))
.ok_or(ClosedStream { _private: () })?;
stream.priority = priority;
Ok(())
}
pub fn priority(&self) -> Result<i32, ClosedStream> {
let stream = self
.state
.send
.get(&self.id)
.ok_or(ClosedStream { _private: () })?;
Ok(stream.as_ref().map(|s| s.priority).unwrap_or_default())
}
}
struct PendingStreamsQueue {
streams: BinaryHeap<PendingStream>,
next: Option<PendingStream>,
recency: u64,
}
impl PendingStreamsQueue {
fn new() -> Self {
Self {
streams: BinaryHeap::new(),
next: None,
recency: u64::MAX,
}
}
fn reinsert_pending(&mut self, id: StreamId, priority: i32) {
assert!(self.next.is_none());
self.next = Some(PendingStream {
priority,
recency: self.recency, id,
});
}
fn push_pending(&mut self, id: StreamId, priority: i32) {
self.recency -= 1;
self.streams.push(PendingStream {
priority,
recency: self.recency,
id,
});
}
fn pop(&mut self) -> Option<PendingStream> {
self.next.take().or_else(|| self.streams.pop())
}
fn clear(&mut self) {
self.next = None;
self.streams.clear();
}
fn iter(&self) -> impl Iterator<Item = &PendingStream> {
self.next.iter().chain(self.streams.iter())
}
#[cfg(test)]
fn len(&self) -> usize {
self.streams.len() + self.next.is_some() as usize
}
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
struct PendingStream {
priority: i32,
recency: u64,
id: StreamId,
}
#[derive(Debug, PartialEq, Eq)]
pub enum StreamEvent {
Opened {
dir: Dir,
},
Readable {
id: StreamId,
},
Writable {
id: StreamId,
},
Finished {
id: StreamId,
},
Stopped {
id: StreamId,
error_code: VarInt,
},
Available {
dir: Dir,
},
}
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
#[must_use = "A frame might need to be enqueued"]
pub struct ShouldTransmit(bool);
impl ShouldTransmit {
pub fn should_transmit(self) -> bool {
self.0
}
}
#[derive(Debug, Default, Error, Clone, PartialEq, Eq)]
#[error("closed stream")]
pub struct ClosedStream {
_private: (),
}
impl From<ClosedStream> for io::Error {
fn from(x: ClosedStream) -> Self {
Self::new(io::ErrorKind::NotConnected, x)
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
enum StreamHalf {
Send,
Recv,
}
pub(super) trait BytesOrSlice<'a>: AsRef<[u8]> + 'a {
fn len(&self) -> usize {
self.as_ref().len()
}
fn is_empty(&self) -> bool {
self.as_ref().is_empty()
}
fn into_bytes(self) -> Bytes;
}
impl BytesOrSlice<'_> for Bytes {
fn into_bytes(self) -> Bytes {
self
}
}
impl<'a> BytesOrSlice<'a> for &'a [u8] {
fn into_bytes(self) -> Bytes {
Bytes::copy_from_slice(self)
}
}