tor_proto/stream/flow_ctrl/state.rs
1//! Code for implementing flow control (stream-level).
2
3use std::sync::Arc;
4
5use postage::watch;
6use tor_cell::relaycell::flow_ctrl::{Xoff, Xon, XonKbpsEwma};
7use tor_cell::relaycell::msg::AnyRelayMsg;
8use tor_cell::relaycell::{RelayMsg, UnparsedRelayMsg};
9
10use super::params::FlowCtrlParameters;
11use super::window::state::{HalfStreamWindowFlowCtrl, WindowFlowCtrl};
12use super::xon_xoff::reader::DrainRateRequest;
13#[cfg(feature = "flowctl-cc")]
14use super::xon_xoff::state::{HalfStreamXonXoffFlowCtrl, XonXoffFlowCtrl};
15
16use crate::Result;
17use crate::congestion::sendme;
18use crate::util::notify::NotifySender;
19
20/// Private internals of [`StreamFlowCtrl`].
21#[enum_dispatch::enum_dispatch]
22#[derive(Debug)]
23enum StreamFlowCtrlInner {
24 /// "legacy" sendme-window-based flow control.
25 Window(WindowFlowCtrl),
26 /// XON/XOFF flow control.
27 #[cfg(feature = "flowctl-cc")]
28 XonXoff(XonXoffFlowCtrl),
29}
30
31/// Manages the circuit reactor's flow control for a stream.
32///
33/// Note that the flow control logic can be distributed across multiple parts of Arti.
34/// For example some flow control logic will exist in the circuit reactor,
35/// but other logic will exist in the stream's `DataStream`.
36/// So this doesn't include all flow control logic.
37#[derive(Debug)]
38pub(crate) struct StreamFlowCtrl {
39 /// Private internal enum.
40 inner: StreamFlowCtrlInner,
41}
42
43impl StreamFlowCtrl {
44 /// Returns a new sendme-window-based [`StreamFlowCtrl`].
45 pub(crate) fn new_window(window: sendme::StreamSendWindow) -> Self {
46 Self {
47 inner: StreamFlowCtrlInner::Window(WindowFlowCtrl::new(window)),
48 }
49 }
50
51 /// Returns a new xon/xoff-based [`StreamFlowCtrl`].
52 #[cfg(feature = "flowctl-cc")]
53 pub(crate) fn new_xon_xoff(
54 params: Arc<FlowCtrlParameters>,
55 use_sidechannel_mitigations: bool,
56 rate_limit_updater: watch::Sender<StreamRateLimit>,
57 drain_rate_requester: NotifySender<DrainRateRequest>,
58 ) -> Self {
59 Self {
60 inner: StreamFlowCtrlInner::XonXoff(XonXoffFlowCtrl::new(
61 params,
62 use_sidechannel_mitigations,
63 rate_limit_updater,
64 drain_rate_requester,
65 )),
66 }
67 }
68
69 /// Once this end of the stream is closed and the stream becomes a
70 /// half-stream (`HalfStream`),
71 /// this method will turn the flow control object into a version
72 /// that is designed to be used for half-streams.
73 pub(crate) fn half_stream(self) -> HalfStreamFlowCtrl {
74 let inner = match self.inner {
75 StreamFlowCtrlInner::Window(x) => {
76 HalfStreamFlowCtrlInner::Window(HalfStreamWindowFlowCtrl::new(x))
77 }
78 #[cfg(feature = "flowctl-cc")]
79 StreamFlowCtrlInner::XonXoff(x) => {
80 HalfStreamFlowCtrlInner::XonXoff(HalfStreamXonXoffFlowCtrl::new(x))
81 }
82 };
83
84 HalfStreamFlowCtrl { inner }
85 }
86}
87
88// forward all trait methods to the inner enum
89impl FlowCtrlHooks for StreamFlowCtrl {
90 fn can_send<M: RelayMsg>(&self, msg: &M) -> bool {
91 self.inner.can_send(msg)
92 }
93
94 fn about_to_send(&mut self, msg: &AnyRelayMsg) -> Result<()> {
95 self.inner.about_to_send(msg)
96 }
97
98 fn put_for_incoming_sendme(&mut self, msg: UnparsedRelayMsg) -> Result<()> {
99 self.inner.put_for_incoming_sendme(msg)
100 }
101
102 fn handle_incoming_xon(&mut self, msg: UnparsedRelayMsg) -> Result<()> {
103 self.inner.handle_incoming_xon(msg)
104 }
105
106 fn handle_incoming_xoff(&mut self, msg: UnparsedRelayMsg) -> Result<()> {
107 self.inner.handle_incoming_xoff(msg)
108 }
109
110 fn maybe_send_xon(&mut self, rate: XonKbpsEwma, buffer_len: usize) -> Result<Option<Xon>> {
111 self.inner.maybe_send_xon(rate, buffer_len)
112 }
113
114 fn maybe_send_xoff(&mut self, buffer_len: usize) -> Result<Option<Xoff>> {
115 self.inner.maybe_send_xoff(buffer_len)
116 }
117
118 fn inbound_queue_max_len(&self) -> usize {
119 self.inner.inbound_queue_max_len()
120 }
121}
122
123/// Methods that can be called on a [`StreamFlowCtrl`].
124///
125/// We use a trait so that we can use `enum_dispatch` on the inner [`StreamFlowCtrlInner`] enum.
126#[enum_dispatch::enum_dispatch(StreamFlowCtrlInner)]
127pub(crate) trait FlowCtrlHooks {
128 /// Whether this stream is ready to send `msg`.
129 fn can_send<M: RelayMsg>(&self, msg: &M) -> bool;
130
131 /// Inform the flow control code that we're about to send `msg`.
132 /// Returns an error if the message should not be sent,
133 /// and the circuit should be closed.
134 // TODO: Consider having this method wrap the message in a type that
135 // "proves" we've applied flow control. This would make it easier to apply
136 // flow control earlier, e.g. in `OpenStreamEntStream`, without introducing
137 // ambiguity in the sending function as to whether flow control has already
138 // been applied or not.
139 fn about_to_send(&mut self, msg: &AnyRelayMsg) -> Result<()>;
140
141 /// Handle an incoming sendme.
142 ///
143 /// On success, return the number of cells left in the window.
144 ///
145 /// On failure, return an error: the caller should close the stream or
146 /// circuit with a protocol error.
147 ///
148 /// Takes the [`UnparsedRelayMsg`] so that we don't even try to decode it if we're not using the
149 /// correct type of flow control.
150 fn put_for_incoming_sendme(&mut self, msg: UnparsedRelayMsg) -> Result<()>;
151
152 /// Handle an incoming XON message.
153 ///
154 /// Takes the [`UnparsedRelayMsg`] so that we don't even try to decode it if we're not using the
155 /// correct type of flow control.
156 fn handle_incoming_xon(&mut self, msg: UnparsedRelayMsg) -> Result<()>;
157
158 /// Handle an incoming XOFF message.
159 ///
160 /// Takes the [`UnparsedRelayMsg`] so that we don't even try to decode it if we're not using the
161 /// correct type of flow control.
162 fn handle_incoming_xoff(&mut self, msg: UnparsedRelayMsg) -> Result<()>;
163
164 /// Check if we should send an XON message.
165 ///
166 /// If we should, then returns the XON message that should be sent.
167 /// Returns an error if XON/XOFF messages aren't supported for this type of flow control.
168 fn maybe_send_xon(&mut self, rate: XonKbpsEwma, buffer_len: usize) -> Result<Option<Xon>>;
169
170 /// Check if we should send an XOFF message.
171 ///
172 /// If we should, then returns the XOFF message that should be sent.
173 /// Returns an error if XON/XOFF messages aren't supported for this type of flow control.
174 fn maybe_send_xoff(&mut self, buffer_len: usize) -> Result<Option<Xoff>>;
175
176 /// The max queue length that should be used for stream messages incoming from the Tor network.
177 ///
178 /// This is the queue length between the user-facing stream reader (`DataReader`)
179 /// and the circuit reactor.
180 ///
181 /// If the queue would ever exceed this many messages, the stream should be closed.
182 fn inbound_queue_max_len(&self) -> usize;
183}
184
185/// Manages flow control for a half-stream (`HalfStream`).
186#[derive(Debug)]
187pub(crate) struct HalfStreamFlowCtrl {
188 /// Private internal enum.
189 inner: HalfStreamFlowCtrlInner,
190}
191
192/// Private internals of [`HalfStreamFlowCtrl`].
193#[enum_dispatch::enum_dispatch]
194#[derive(Debug)]
195enum HalfStreamFlowCtrlInner {
196 /// "legacy" sendme-window-based flow control.
197 Window(HalfStreamWindowFlowCtrl),
198 /// XON/XOFF flow control.
199 #[cfg(feature = "flowctl-cc")]
200 XonXoff(HalfStreamXonXoffFlowCtrl),
201}
202
203/// Methods that can be called on a [`HalfStreamFlowCtrl`].
204///
205/// We use a trait so that we can use `enum_dispatch` on the inner [`HalfStreamFlowCtrlInner`] enum.
206/// While this may seem unnecessary since this trait currently only has two methods,
207/// it's consistent with the [`FlowCtrlHooks`] trait above.
208#[enum_dispatch::enum_dispatch(HalfStreamFlowCtrlInner)]
209pub(crate) trait HalfStreamFlowCtrlHooks {
210 /// Handle some number of dropped stream messages.
211 ///
212 /// We don't know what kinds of stream messages were dropped, only the number of them.
213 ///
214 /// This method exists because currently the stream entry may drop some incoming stream
215 /// messages and they would never be processed by this flow control object otherwise.
216 fn handle_incoming_dropped(&mut self, msg_count: u16) -> Result<()>;
217
218 /// Handle an incoming message.
219 ///
220 /// If it's a flow control message, it will be consumed and `None` will be returned.
221 /// Otherwise the original message will be returned.
222 ///
223 /// Takes the [`UnparsedRelayMsg`] so that we don't even try to decode it if we're not using the
224 /// correct type of flow control.
225 fn handle_incoming_msg(&mut self, msg: UnparsedRelayMsg) -> Result<Option<UnparsedRelayMsg>>;
226}
227
228// forward all trait methods to the inner enum
229impl HalfStreamFlowCtrlHooks for HalfStreamFlowCtrl {
230 fn handle_incoming_dropped(&mut self, msg_count: u16) -> Result<()> {
231 self.inner.handle_incoming_dropped(msg_count)
232 }
233
234 fn handle_incoming_msg(&mut self, msg: UnparsedRelayMsg) -> Result<Option<UnparsedRelayMsg>> {
235 self.inner.handle_incoming_msg(msg)
236 }
237}
238
239/// A newtype wrapper for a tor stream rate limit that makes the units explicit.
240#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
241pub(crate) struct StreamRateLimit {
242 /// The rate in bytes/s.
243 rate: u64,
244}
245
246impl StreamRateLimit {
247 /// A maximum rate limit.
248 pub(crate) const MAX: Self = Self::new_bytes_per_sec(u64::MAX);
249
250 /// A rate limit of 0.
251 pub(crate) const ZERO: Self = Self::new_bytes_per_sec(0);
252
253 /// A new [`StreamRateLimit`] with `rate` bytes/s.
254 pub(crate) const fn new_bytes_per_sec(rate: u64) -> Self {
255 Self { rate }
256 }
257
258 /// The rate in bytes/s.
259 pub(crate) const fn bytes_per_sec(&self) -> u64 {
260 self.rate
261 }
262}
263
264impl std::fmt::Display for StreamRateLimit {
265 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
266 write!(f, "{} bytes/s", self.rate)
267 }
268}