Skip to main content

tor_proto/stream/flow_ctrl/xon_xoff/
reader.rs

1//! A wrapper for an [`AsyncRead`] to support XON/XOFF flow control.
2//!
3//! This allows any `AsyncRead` that implements [`BufferIsEmpty`] to be used with XON/XOFF flow
4//! control.
5
6use std::io::Error;
7use std::pin::Pin;
8use std::task::{Context, Poll};
9
10use futures::{AsyncRead, Stream};
11use pin_project::pin_project;
12use tor_basic_utils::assert_val_impl_trait;
13use tor_cell::relaycell::flow_ctrl::XonKBpsEwma;
14
15use crate::stream::StreamTarget;
16use crate::util::notify::NotifyReceiver;
17
18/// A wrapper for an [`AsyncRead`] to support XON/XOFF flow control.
19///
20/// This reader will take care of communicating with the circuit reactor to handle XON/XOFF-related
21/// events.
22#[derive(Debug)]
23#[pin_project]
24pub(crate) struct XonXoffReader<R, T: DrainRateNotifier = StreamTarget> {
25    /// How we communicate with the circuit reactor.
26    #[pin]
27    ctrl: XonXoffReaderCtrl<T>,
28    /// The inner reader.
29    #[pin]
30    reader: R,
31    /// Have we received a drain rate request notification from the reactor,
32    /// but haven't yet sent a drain rate update back to the reactor?
33    pending_drain_rate_update: bool,
34}
35
36impl<R, T: DrainRateNotifier> XonXoffReader<R, T> {
37    /// Create a new [`XonXoffReader`].
38    ///
39    /// The reader must implement [`BufferIsEmpty`], which allows the `XonXoffReader` to check if
40    /// the incoming stream buffer is empty or not.
41    pub(crate) fn new(ctrl: XonXoffReaderCtrl<T>, reader: R) -> Self {
42        Self {
43            ctrl,
44            reader,
45            pending_drain_rate_update: false,
46        }
47    }
48
49    /// Get a reference to the inner [`AsyncRead`].
50    ///
51    /// NOTE: This will bypass the [`XonXoffReader`] and may cause incorrect behaviour depending on
52    /// how you use the returned reader (for example if it uses interior mutability).
53    pub(crate) fn inner(&self) -> &R {
54        &self.reader
55    }
56
57    /// Get a mutable reference to the inner [`AsyncRead`].
58    ///
59    /// NOTE: This will bypass the [`XonXoffReader`] and may cause incorrect behaviour depending on
60    /// how you use the returned reader (for example if you read bytes directly).
61    pub(crate) fn inner_mut(&mut self) -> &mut R {
62        &mut self.reader
63    }
64}
65
66impl<R: AsyncRead + BufferIsEmpty, T: DrainRateNotifier> AsyncRead for XonXoffReader<R, T> {
67    fn poll_read(
68        self: Pin<&mut Self>,
69        cx: &mut Context<'_>,
70        buf: &mut [u8],
71    ) -> Poll<Result<usize, Error>> {
72        let mut self_ = self.project();
73
74        // ensure that `drain_rate_request_stream` is a `FusedStream`,
75        // which means that we don't need to worry about calling `poll_next()` repeatedly
76        assert_val_impl_trait!(
77            self_.ctrl.drain_rate_request_stream,
78            futures::stream::FusedStream,
79        );
80
81        // check if the circuit reactor has requested a drain rate update
82        if let Poll::Ready(Some(())) = self_
83            .ctrl
84            .as_mut()
85            .project()
86            .drain_rate_request_stream
87            .poll_next(cx)
88        {
89            // a drain rate update was requested, so we need to send a drain rate update once we
90            // have no more bytes buffered
91            *self_.pending_drain_rate_update = true;
92        }
93
94        // try reading from the inner reader
95        let res = self_.reader.as_mut().poll_read(cx, buf);
96
97        // if we need to send a drain rate update and the stream buffer is empty, inform the reactor
98        if *self_.pending_drain_rate_update && self_.reader.is_empty() {
99            // TODO(arti#534): in the future we want to do rate estimation, but for now we'll just
100            // send an "unlimited" drain rate
101            self_
102                .ctrl
103                .drain_rate_notifier
104                .notify(XonKBpsEwma::Unlimited)?;
105            *self_.pending_drain_rate_update = false;
106        }
107
108        res
109    }
110}
111
112/// Something that sends drain rate updates to the flow control logic (the `XonXoffFlowCtrl`).
113pub(crate) trait DrainRateNotifier {
114    /// Send the drain rate update.
115    fn notify(&mut self, rate: XonKBpsEwma) -> Result<(), Error>;
116}
117
118impl DrainRateNotifier for StreamTarget {
119    fn notify(&mut self, rate: XonKBpsEwma) -> Result<(), Error> {
120        self.drain_rate_update(rate).map_err(Into::into)
121    }
122}
123
124/// The control structure for a stream that partakes in XON/XOFF flow control.
125///
126/// Used to construct an [`XonXoffReader`].
127///
128/// This contains a mechanism for us to be asked for our drain rate,
129/// and a mechanism of sending the drain rate in response.
130///
131/// The `DrainRateNotifier` is typically a `StreamTarget`,
132/// which sends the drain rate to the circuit reactor so that it can be sent in an XON message.
133/// We make this a trait to make unit testing possible.
134#[derive(Debug)]
135#[pin_project]
136pub(crate) struct XonXoffReaderCtrl<T: DrainRateNotifier = StreamTarget> {
137    /// Receive notifications when the reactor requests a new drain rate.
138    /// When we do, we should begin waiting for the receive buffer to clear.
139    /// Then when the buffer clears, we should send a new drain rate update to the reactor.
140    #[pin]
141    drain_rate_request_stream: NotifyReceiver<DrainRateRequest>,
142    /// An abstract handle to the reactor for this stream.
143    /// This allows us to send drain rate updates to the circuit reactor.
144    drain_rate_notifier: T,
145}
146
147impl<T: DrainRateNotifier> XonXoffReaderCtrl<T> {
148    /// Create a new [`XonXoffReaderCtrl`].
149    ///
150    /// The `drain_rate_request_stream` informs us when we need to send our drain rate,
151    /// and `drain_rate_notifier` allows us to send that drain rate.
152    pub(crate) fn new(
153        drain_rate_request_stream: NotifyReceiver<DrainRateRequest>,
154        drain_rate_notifier: T,
155    ) -> Self {
156        Self {
157            drain_rate_request_stream,
158            drain_rate_notifier,
159        }
160    }
161}
162
163/// Used by the [`XonXoffReader`] to decide when to send a drain rate update
164/// (typically resulting in an XON message).
165pub(crate) trait BufferIsEmpty {
166    /// Returns `true` if there are no incoming bytes buffered on this stream.
167    ///
168    /// This takes a `&mut` so that implementers can
169    /// [`unobtrusive_peek()`](tor_async_utils::peekable_stream::UnobtrusivePeekableStream::unobtrusive_peek)
170    /// a stream if necessary.
171    fn is_empty(self: Pin<&mut Self>) -> bool;
172}
173
174/// A marker type for a [`NotifySender`](crate::util::notify::NotifySender)
175/// indicating that notifications are for new drain rate requests.
176#[derive(Debug)]
177pub(crate) struct DrainRateRequest;
178
179#[cfg(test)]
180// We use some tokio-specific types here to make the test easier to write.
181#[cfg(feature = "tokio")]
182mod test {
183    // @@ begin test lint list maintained by maint/add_warning @@
184    #![allow(clippy::bool_assert_comparison)]
185    #![allow(clippy::clone_on_copy)]
186    #![allow(clippy::dbg_macro)]
187    #![allow(clippy::mixed_attributes_style)]
188    #![allow(clippy::print_stderr)]
189    #![allow(clippy::print_stdout)]
190    #![allow(clippy::single_char_pattern)]
191    #![allow(clippy::unwrap_used)]
192    #![allow(clippy::unchecked_time_subtraction)]
193    #![allow(clippy::useless_vec)]
194    #![allow(clippy::needless_pass_by_value)]
195    #![allow(clippy::string_slice)] // See arti#2571
196    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
197
198    use super::*;
199
200    use std::sync::Arc;
201    use std::sync::atomic::{AtomicU64, Ordering};
202
203    use crate::stream::flow_ctrl::params::FlowCtrlParameters;
204    use crate::stream::flow_ctrl::state::{
205        FlowCtrlHooks, StreamRateLimit, WithSidechannelMitigations,
206    };
207    use crate::stream::flow_ctrl::xon_xoff::state::XonXoffFlowCtrl;
208    use crate::util::notify::NotifySender;
209
210    use futures::channel::mpsc::{self, TryRecvError};
211    use futures::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
212    use tokio_crate::io::{DuplexStream, duplex};
213    use tokio_util::compat::{Compat, TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt};
214
215    /// The type that will be stored by the [`XonXoffReader`] and used to send drain rate updates.
216    ///
217    /// This essentially mocks what the [`StreamTarget`] would do.
218    struct TestingDrainRateUpdates(mpsc::UnboundedSender<XonKBpsEwma>);
219
220    impl TestingDrainRateUpdates {
221        pub(crate) fn new(sender: mpsc::UnboundedSender<XonKBpsEwma>) -> Self {
222            Self(sender)
223        }
224    }
225
226    impl DrainRateNotifier for TestingDrainRateUpdates {
227        fn notify(&mut self, rate: XonKBpsEwma) -> Result<(), Error> {
228            self.0.unbounded_send(rate).unwrap();
229            Ok(())
230        }
231    }
232
233    /// The writer for a data stream that tracks the length.
234    #[pin_project::pin_project]
235    struct WriterWithLength<W> {
236        #[pin]
237        writer: W,
238        length: Arc<AtomicU64>,
239    }
240
241    /// The reader for a data stream that tracks the length.
242    #[pin_project::pin_project]
243    struct ReaderWithLength<R> {
244        #[pin]
245        reader: R,
246        length: Arc<AtomicU64>,
247    }
248
249    /// Wraps a writer and reader to track the queue length.
250    fn with_length<W, R>(writer: W, reader: R) -> (WriterWithLength<W>, ReaderWithLength<R>) {
251        let length = Arc::new(AtomicU64::new(0));
252
253        let writer = WriterWithLength {
254            writer,
255            length: Arc::clone(&length),
256        };
257        let reader = ReaderWithLength { reader, length };
258
259        (writer, reader)
260    }
261
262    impl<W> WriterWithLength<W> {
263        /// Amount of bytes queued.
264        pub(crate) fn len(&self) -> u64 {
265            self.length.load(Ordering::Acquire)
266        }
267    }
268
269    impl<R> BufferIsEmpty for ReaderWithLength<R> {
270        fn is_empty(self: Pin<&mut Self>) -> bool {
271            self.length.load(Ordering::Acquire) == 0
272        }
273    }
274
275    impl<W: AsyncWrite> AsyncWrite for WriterWithLength<W> {
276        fn poll_write(
277            self: Pin<&mut Self>,
278            cx: &mut Context<'_>,
279            buf: &[u8],
280        ) -> Poll<std::io::Result<usize>> {
281            let self_ = self.project();
282
283            let rv = self_.writer.poll_write(cx, buf);
284
285            // NOTE: There's a race condition here since we don't write to the writer and update the
286            // length as one atomic operation.
287            // But this is good enough for our test where the mock runtime is deterministic and
288            // single-threaded.
289            //
290            // We ignore the possibility of overflowing the 64-bit integer here.
291            if let Poll::Ready(Ok(len)) = rv {
292                let len: u64 = len.try_into().expect("usize should fit into u64");
293                // The effect of `poll_write()` above will be visible after another thread checks
294                // the length with `load(Acquire)`.
295                self_.length.fetch_add(len, Ordering::Release);
296            }
297
298            rv
299        }
300
301        fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
302            self.project().writer.poll_flush(cx)
303        }
304
305        fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
306            self.project().writer.poll_close(cx)
307        }
308    }
309
310    impl<R: AsyncRead> AsyncRead for ReaderWithLength<R> {
311        fn poll_read(
312            self: Pin<&mut Self>,
313            cx: &mut Context<'_>,
314            buf: &mut [u8],
315        ) -> Poll<std::io::Result<usize>> {
316            let self_ = self.project();
317
318            let rv = self_.reader.poll_read(cx, buf);
319
320            // NOTE: There's a race condition here since we don't read from the reader and update
321            // the length as one atomic operation.
322            // But this is good enough for our test where the mock runtime is deterministic and
323            // single-threaded.
324            //
325            // We ignore the possibility of underflowing the integer here.
326            if let Poll::Ready(Ok(len)) = rv {
327                let len: u64 = len.try_into().expect("usize should fit into u64");
328                // The effect of `poll_read()` above will be visible after another thread checks
329                // the length with `load(Acquire)`.
330                self_.length.fetch_sub(len, Ordering::Release);
331            }
332
333            rv
334        }
335    }
336
337    /// Set up all of the flow control stuff needed to test the [`XonXoffReader`].
338    ///
339    /// Returns:
340    ///
341    /// 1. The stream writer (as would be held by the circuit/stream reactor).
342    /// 2. The stream reader (as would be held in a user-facing `DataStream`).
343    /// 3. An MPSC receiver of drain rate updates.
344    /// 4. The flow control logic.
345    #[allow(clippy::type_complexity)]
346    fn init_flow_ctrl(
347        with_sidechannel_mitigations: WithSidechannelMitigations,
348    ) -> (
349        WriterWithLength<Compat<DuplexStream>>,
350        XonXoffReader<ReaderWithLength<Compat<DuplexStream>>, TestingDrainRateUpdates>,
351        mpsc::UnboundedReceiver<XonKBpsEwma>,
352        XonXoffFlowCtrl,
353    ) {
354        let params = FlowCtrlParameters::defaults_for_tests();
355
356        // For the flow control logic to send rate limit changes to the stream writer.
357        // We don't use this in this test, but the `XonXoffFlowCtrl` needs the tx side.
358        let (rate_limit_tx, _rate_limit_rx) = postage::watch::channel_with(StreamRateLimit::MAX);
359
360        // For the flow control logic to request a new drain rate update from the stream reader.
361        let mut drain_rate_request_tx = NotifySender::new_typed();
362        let drain_rate_request_rx = drain_rate_request_tx.subscribe();
363
364        // The flow control logic.
365        let flow_ctrl = XonXoffFlowCtrl::new(
366            Arc::new(params),
367            with_sidechannel_mitigations,
368            rate_limit_tx,
369            drain_rate_request_tx,
370        );
371
372        // For the `XonXoffReader` to send a drain rate update.
373        let (drain_rate_sender, drain_rate_receiver) = mpsc::unbounded();
374        let drain_rate_updates = TestingDrainRateUpdates::new(drain_rate_sender);
375
376        // All of the information needed to build a `XonXoffReader`.
377        let reader_ctrl = XonXoffReaderCtrl::new(drain_rate_request_rx, drain_rate_updates);
378
379        // This is the stream queue for incoming data.
380        // So the `reader` is the stream reader and the `writer` would be within the reactor.
381        //
382        // In arti this stream should be unbounded, so here we use a max size of `usize::MAX`.
383        let (writer, reader) = duplex(/* max_buf_size= */ usize::MAX);
384        let writer = writer.compat_write();
385        let reader = reader.compat();
386
387        // Make the reader+writer pair track the length of the buffer so that it can support
388        // `BufferIsEmpty`.
389        let (writer, reader) = with_length(writer, reader);
390
391        // The reader for incoming stream data, with XON/XOFF support.
392        let reader = XonXoffReader::new(reader_ctrl, reader);
393
394        (writer, reader, drain_rate_receiver, flow_ctrl)
395    }
396
397    /// Buffer `num_bytes` as if the bytes arrived on the stream.
398    ///
399    /// Returns whether the flow control logic wanted to send an XOFF.
400    async fn buffer_incoming_data(
401        writer: &mut WriterWithLength<impl AsyncWrite + Unpin>,
402        mut num_bytes: usize,
403        flow_ctrl: &mut XonXoffFlowCtrl,
404    ) -> bool {
405        let mut wants_to_send_xoff = false;
406
407        // Write the requested number of bytes.
408        while num_bytes > 0 {
409            // Write 100_000 bytes at a time.
410            let buf_size = num_bytes.min(100_000);
411            writer.write_all(&vec![0; buf_size]).await.unwrap();
412            num_bytes -= buf_size;
413
414            // Inform the flow control logic.
415            let xoff = flow_ctrl.maybe_send_xoff(writer.len() as usize).unwrap();
416            wants_to_send_xoff |= xoff.is_some();
417        }
418
419        wants_to_send_xoff
420    }
421
422    /// Read `num_bytes` from the stream.
423    async fn read_incoming_data(mut reader: impl AsyncRead + Unpin, mut num_bytes: usize) {
424        // Read the requested number of bytes.
425        while num_bytes > 0 {
426            // Read 100_000 bytes at a time.
427            let buf_size = num_bytes.min(100_000);
428            reader.read_exact(&mut vec![0; buf_size]).await.unwrap();
429            num_bytes -= buf_size;
430        }
431    }
432
433    /// This test is meant to test the drain rate update.
434    /// It adds a lot of data to the stream queue so that it triggers sending an XOFF
435    /// and sends a drain rate request to the [`XonXoffReader`],
436    /// then it reads from the stream until it's empty
437    /// and the `XonXoffReader` sends a drain rate update.
438    /// The flow control logic receives the drain rate update and sends an XON.
439    #[test]
440    fn drain_rate_update() {
441        tor_rtmock::MockRuntime::test_with_various(|_rt| async move {
442            // This is the stream queue for incoming data.
443            // So the `reader` is the stream reader and the `writer` would be within the reactor.
444            let (mut writer, mut reader, mut drain_rate_receiver, mut flow_ctrl) =
445                init_flow_ctrl(WithSidechannelMitigations::Enabled);
446
447            // Data has arrived on the stream.
448            // We always consider sending an XOFF when a stream has received data.
449            // The amount of incoming data wasn't very large,
450            // so we don't expect that it would actually want to send an XOFF.
451            let wants_to_send_xoff =
452                buffer_incoming_data(&mut writer, 10_000, &mut flow_ctrl).await;
453            assert!(!wants_to_send_xoff);
454
455            // We didn't want to send an XOFF,
456            // so the stream reader will never have been asked for a drain rate update.
457            assert!(!reader.pending_drain_rate_update);
458
459            // The stream reader reads all of the incoming data.
460            read_incoming_data(&mut reader, 10_000).await;
461
462            // Check `pending_drain_rate_update` again,
463            // and also ensure that we didn't send a drain rate update.
464            assert!(!reader.pending_drain_rate_update);
465            assert_eq!(drain_rate_receiver.try_recv(), Err(TryRecvError::Empty));
466
467            // Data has arrived on the stream.
468            // We always consider sending an XOFF when a stream has received data.
469            // The amount of incoming data was large,
470            // so we expect that it would want to send an XOFF.
471            let wants_to_send_xoff =
472                buffer_incoming_data(&mut writer, 800_000, &mut flow_ctrl).await;
473            assert!(wants_to_send_xoff);
474
475            // The above code should have sent an XOFF and asked the reader for a drain rate update,
476            // but the reader hasn't realized this yet.
477            assert!(!reader.pending_drain_rate_update);
478            assert_eq!(drain_rate_receiver.try_recv(), Err(TryRecvError::Empty));
479
480            // The reader won't realize it was asked for a drain rate update until after it's tried
481            // reading once.
482            let _ = reader.read(&mut [0; 0]).await.unwrap();
483            assert!(reader.pending_drain_rate_update);
484
485            // The drain rate update is only sent once we've drained the buffer,
486            // so an update should not have been sent yet.
487            assert_eq!(drain_rate_receiver.try_recv(), Err(TryRecvError::Empty));
488
489            // Read most (but not all) of the data on the stream.
490            read_incoming_data(&mut reader, 700_000).await;
491
492            // We haven't read *all* of the data,
493            // so should still not have sent a drain rate update.
494            assert!(!Pin::new(reader.inner_mut()).is_empty());
495            assert!(reader.pending_drain_rate_update);
496            assert_eq!(drain_rate_receiver.try_recv(), Err(TryRecvError::Empty));
497
498            // Read the last of the data on the stream.
499            read_incoming_data(&mut reader, 100_000).await;
500
501            // Now that the buffer is empty,
502            // we should have sent a drain rate update.
503            assert!(Pin::new(reader.inner_mut()).is_empty());
504            assert!(!reader.pending_drain_rate_update);
505            let xon_rate = drain_rate_receiver.try_recv().unwrap();
506            assert_eq!(xon_rate, XonKBpsEwma::Unlimited);
507
508            // The buffer is still empty,
509            // so the flow control logic should want to send an XON.
510            let xon = flow_ctrl
511                .maybe_send_xon(xon_rate, writer.len() as usize)
512                .unwrap()
513                .unwrap();
514            assert_eq!(xon.kbytes_per_sec_ewma(), xon_rate);
515        });
516    }
517
518    /// Like the `drain_rate_update()` test,
519    /// this test causes the `XonXoffReader` to send a drain rate update.
520    /// But in this case the buffer refills again past the high-water mark
521    /// before the drain rate update can be processed by the flow control logic,
522    /// so it *does not* send an XON.
523    /// Instead it re-requests a drain rate from the `XonXoffReader`.
524    #[test]
525    fn drain_rate_update_then_buffer_refill() {
526        tor_rtmock::MockRuntime::test_with_various(|_rt| async move {
527            // This is the stream queue for incoming data.
528            // So the `reader` is the stream reader and the `writer` would be within the reactor.
529            let (mut writer, mut reader, mut drain_rate_receiver, mut flow_ctrl) =
530                init_flow_ctrl(WithSidechannelMitigations::Enabled);
531
532            // Data has arrived on the stream.
533            // We always consider sending an XOFF when a stream has received data.
534            // The amount of incoming data was large,
535            // so we expect that it would want to send an XOFF.
536            let wants_to_send_xoff =
537                buffer_incoming_data(&mut writer, 800_000, &mut flow_ctrl).await;
538            assert!(wants_to_send_xoff);
539
540            // Read all of the data on the stream.
541            read_incoming_data(&mut reader, 700_000).await;
542            assert!(reader.pending_drain_rate_update);
543            read_incoming_data(&mut reader, 100_000).await;
544
545            // Now that the buffer is empty,
546            // we should have sent a drain rate update.
547            assert!(Pin::new(reader.inner_mut()).is_empty());
548            assert!(!reader.pending_drain_rate_update);
549
550            // Before this drain rate update can make it to the
551            // flow control logic with `maybe_send_xon()`,
552            // the buffer fills again past the high-water mark.
553            let wants_to_send_xoff =
554                buffer_incoming_data(&mut writer, 800_000, &mut flow_ctrl).await;
555            assert!(!wants_to_send_xoff);
556
557            // Now the drain rate update makes it to the flow control logic.
558            // Since the buffer is past the high-water mark,
559            // we won't want to send an XON.
560            let xon_rate = drain_rate_receiver.try_recv().unwrap();
561            assert_eq!(xon_rate, XonKBpsEwma::Unlimited);
562            let xon = flow_ctrl
563                .maybe_send_xon(xon_rate, writer.len() as usize)
564                .unwrap();
565            assert!(xon.is_none());
566
567            // Instead the reader will have been asked for a drain rate update again,
568            // which restarts the entire process.
569            assert!(!reader.pending_drain_rate_update);
570            let _ = reader.read(&mut [0; 0]).await.unwrap();
571            assert!(reader.pending_drain_rate_update);
572        });
573    }
574}