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

            
6
use std::io::Error;
7
use std::pin::Pin;
8
use std::task::{Context, Poll};
9

            
10
use futures::{AsyncRead, Stream};
11
use pin_project::pin_project;
12
use tor_basic_utils::assert_val_impl_trait;
13
use tor_cell::relaycell::flow_ctrl::XonKBpsEwma;
14

            
15
use crate::stream::StreamTarget;
16
use 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]
24
pub(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

            
36
impl<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
132
    pub(crate) fn new(ctrl: XonXoffReaderCtrl<T>, reader: R) -> Self {
42
132
        Self {
43
132
            ctrl,
44
132
            reader,
45
132
            pending_drain_rate_update: false,
46
132
        }
47
132
    }
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
180
    pub(crate) fn inner_mut(&mut self) -> &mut R {
62
180
        &mut self.reader
63
180
    }
64
}
65

            
66
impl<R: AsyncRead + BufferIsEmpty, T: DrainRateNotifier> AsyncRead for XonXoffReader<R, T> {
67
322
    fn poll_read(
68
322
        self: Pin<&mut Self>,
69
322
        cx: &mut Context<'_>,
70
322
        buf: &mut [u8],
71
322
    ) -> Poll<Result<usize, Error>> {
72
322
        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
322
        assert_val_impl_trait!(
77
322
            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
322
        if let Poll::Ready(Some(())) = self_
83
322
            .ctrl
84
322
            .as_mut()
85
322
            .project()
86
322
            .drain_rate_request_stream
87
322
            .poll_next(cx)
88
12
        {
89
12
            // a drain rate update was requested, so we need to send a drain rate update once we
90
12
            // have no more bytes buffered
91
12
            *self_.pending_drain_rate_update = true;
92
310
        }
93

            
94
        // try reading from the inner reader
95
322
        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
322
        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
8
            self_
102
8
                .ctrl
103
8
                .drain_rate_notifier
104
8
                .notify(XonKBpsEwma::Unlimited)?;
105
8
            *self_.pending_drain_rate_update = false;
106
314
        }
107

            
108
322
        res
109
322
    }
110
}
111

            
112
/// Something that sends drain rate updates to the flow control logic (the `XonXoffFlowCtrl`).
113
pub(crate) trait DrainRateNotifier {
114
    /// Send the drain rate update.
115
    fn notify(&mut self, rate: XonKBpsEwma) -> Result<(), Error>;
116
}
117

            
118
impl 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]
136
pub(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

            
147
impl<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
156
    pub(crate) fn new(
153
156
        drain_rate_request_stream: NotifyReceiver<DrainRateRequest>,
154
156
        drain_rate_notifier: T,
155
156
    ) -> Self {
156
156
        Self {
157
156
            drain_rate_request_stream,
158
156
            drain_rate_notifier,
159
156
        }
160
156
    }
161
}
162

            
163
/// Used by the [`XonXoffReader`] to decide when to send a drain rate update
164
/// (typically resulting in an XON message).
165
pub(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)]
177
pub(crate) struct DrainRateRequest;
178

            
179
#[cfg(test)]
180
// This module (and `XonXoffReader`) are always available,
181
// but the flow control code logic that it uses requires the "flowctl-cc" feature.
182
#[cfg(feature = "flowctl-cc")]
183
// We use some tokio-specific types here to make the test easier to write.
184
#[cfg(feature = "tokio")]
185
mod test {
186
    // @@ begin test lint list maintained by maint/add_warning @@
187
    #![allow(clippy::bool_assert_comparison)]
188
    #![allow(clippy::clone_on_copy)]
189
    #![allow(clippy::dbg_macro)]
190
    #![allow(clippy::mixed_attributes_style)]
191
    #![allow(clippy::print_stderr)]
192
    #![allow(clippy::print_stdout)]
193
    #![allow(clippy::single_char_pattern)]
194
    #![allow(clippy::unwrap_used)]
195
    #![allow(clippy::unchecked_time_subtraction)]
196
    #![allow(clippy::useless_vec)]
197
    #![allow(clippy::needless_pass_by_value)]
198
    #![allow(clippy::string_slice)] // See arti#2571
199
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
200

            
201
    use super::*;
202

            
203
    use std::sync::Arc;
204
    use std::sync::atomic::{AtomicU64, Ordering};
205

            
206
    use crate::stream::flow_ctrl::params::FlowCtrlParameters;
207
    use crate::stream::flow_ctrl::state::{
208
        FlowCtrlHooks, StreamRateLimit, WithSidechannelMitigations,
209
    };
210
    use crate::stream::flow_ctrl::xon_xoff::state::XonXoffFlowCtrl;
211
    use crate::util::notify::NotifySender;
212

            
213
    use futures::channel::mpsc::{self, TryRecvError};
214
    use futures::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
215
    use tokio_crate::io::{DuplexStream, duplex};
216
    use tokio_util::compat::{Compat, TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt};
217

            
218
    /// The type that will be stored by the [`XonXoffReader`] and used to send drain rate updates.
219
    ///
220
    /// This essentially mocks what the [`StreamTarget`] would do.
221
    struct TestingDrainRateUpdates(mpsc::UnboundedSender<XonKBpsEwma>);
222

            
223
    impl TestingDrainRateUpdates {
224
        pub(crate) fn new(sender: mpsc::UnboundedSender<XonKBpsEwma>) -> Self {
225
            Self(sender)
226
        }
227
    }
228

            
229
    impl DrainRateNotifier for TestingDrainRateUpdates {
230
        fn notify(&mut self, rate: XonKBpsEwma) -> Result<(), Error> {
231
            self.0.unbounded_send(rate).unwrap();
232
            Ok(())
233
        }
234
    }
235

            
236
    /// The writer for a data stream that tracks the length.
237
    #[pin_project::pin_project]
238
    struct WriterWithLength<W> {
239
        #[pin]
240
        writer: W,
241
        length: Arc<AtomicU64>,
242
    }
243

            
244
    /// The reader for a data stream that tracks the length.
245
    #[pin_project::pin_project]
246
    struct ReaderWithLength<R> {
247
        #[pin]
248
        reader: R,
249
        length: Arc<AtomicU64>,
250
    }
251

            
252
    /// Wraps a writer and reader to track the queue length.
253
    fn with_length<W, R>(writer: W, reader: R) -> (WriterWithLength<W>, ReaderWithLength<R>) {
254
        let length = Arc::new(AtomicU64::new(0));
255

            
256
        let writer = WriterWithLength {
257
            writer,
258
            length: Arc::clone(&length),
259
        };
260
        let reader = ReaderWithLength { reader, length };
261

            
262
        (writer, reader)
263
    }
264

            
265
    impl<W> WriterWithLength<W> {
266
        /// Amount of bytes queued.
267
        pub(crate) fn len(&self) -> u64 {
268
            self.length.load(Ordering::Acquire)
269
        }
270
    }
271

            
272
    impl<R> BufferIsEmpty for ReaderWithLength<R> {
273
        fn is_empty(self: Pin<&mut Self>) -> bool {
274
            self.length.load(Ordering::Acquire) == 0
275
        }
276
    }
277

            
278
    impl<W: AsyncWrite> AsyncWrite for WriterWithLength<W> {
279
        fn poll_write(
280
            self: Pin<&mut Self>,
281
            cx: &mut Context<'_>,
282
            buf: &[u8],
283
        ) -> Poll<std::io::Result<usize>> {
284
            let self_ = self.project();
285

            
286
            let rv = self_.writer.poll_write(cx, buf);
287

            
288
            // NOTE: There's a race condition here since we don't write to the writer and update the
289
            // length as one atomic operation.
290
            // But this is good enough for our test where the mock runtime is deterministic and
291
            // single-threaded.
292
            //
293
            // We ignore the possibility of overflowing the 64-bit integer here.
294
            if let Poll::Ready(Ok(len)) = rv {
295
                let len: u64 = len.try_into().expect("usize should fit into u64");
296
                // The effect of `poll_write()` above will be visible after another thread checks
297
                // the length with `load(Acquire)`.
298
                self_.length.fetch_add(len, Ordering::Release);
299
            }
300

            
301
            rv
302
        }
303

            
304
        fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
305
            self.project().writer.poll_flush(cx)
306
        }
307

            
308
        fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
309
            self.project().writer.poll_close(cx)
310
        }
311
    }
312

            
313
    impl<R: AsyncRead> AsyncRead for ReaderWithLength<R> {
314
        fn poll_read(
315
            self: Pin<&mut Self>,
316
            cx: &mut Context<'_>,
317
            buf: &mut [u8],
318
        ) -> Poll<std::io::Result<usize>> {
319
            let self_ = self.project();
320

            
321
            let rv = self_.reader.poll_read(cx, buf);
322

            
323
            // NOTE: There's a race condition here since we don't read from the reader and update
324
            // the length as one atomic operation.
325
            // But this is good enough for our test where the mock runtime is deterministic and
326
            // single-threaded.
327
            //
328
            // We ignore the possibility of underflowing the integer here.
329
            if let Poll::Ready(Ok(len)) = rv {
330
                let len: u64 = len.try_into().expect("usize should fit into u64");
331
                // The effect of `poll_read()` above will be visible after another thread checks
332
                // the length with `load(Acquire)`.
333
                self_.length.fetch_sub(len, Ordering::Release);
334
            }
335

            
336
            rv
337
        }
338
    }
339

            
340
    /// Set up all of the flow control stuff needed to test the [`XonXoffReader`].
341
    ///
342
    /// Returns:
343
    ///
344
    /// 1. The stream writer (as would be held by the circuit/stream reactor).
345
    /// 2. The stream reader (as would be held in a user-facing `DataStream`).
346
    /// 3. An MPSC receiver of drain rate updates.
347
    /// 4. The flow control logic.
348
    #[allow(clippy::type_complexity)]
349
    fn init_flow_ctrl(
350
        with_sidechannel_mitigations: WithSidechannelMitigations,
351
    ) -> (
352
        WriterWithLength<Compat<DuplexStream>>,
353
        XonXoffReader<ReaderWithLength<Compat<DuplexStream>>, TestingDrainRateUpdates>,
354
        mpsc::UnboundedReceiver<XonKBpsEwma>,
355
        XonXoffFlowCtrl,
356
    ) {
357
        let params = FlowCtrlParameters::defaults_for_tests();
358

            
359
        // For the flow control logic to send rate limit changes to the stream writer.
360
        // We don't use this in this test, but the `XonXoffFlowCtrl` needs the tx side.
361
        let (rate_limit_tx, _rate_limit_rx) = postage::watch::channel_with(StreamRateLimit::MAX);
362

            
363
        // For the flow control logic to request a new drain rate update from the stream reader.
364
        let mut drain_rate_request_tx = NotifySender::new_typed();
365
        let drain_rate_request_rx = drain_rate_request_tx.subscribe();
366

            
367
        // The flow control logic.
368
        let flow_ctrl = XonXoffFlowCtrl::new(
369
            Arc::new(params),
370
            with_sidechannel_mitigations,
371
            rate_limit_tx,
372
            drain_rate_request_tx,
373
        );
374

            
375
        // For the `XonXoffReader` to send a drain rate update.
376
        let (drain_rate_sender, drain_rate_receiver) = mpsc::unbounded();
377
        let drain_rate_updates = TestingDrainRateUpdates::new(drain_rate_sender);
378

            
379
        // All of the information needed to build a `XonXoffReader`.
380
        let reader_ctrl = XonXoffReaderCtrl::new(drain_rate_request_rx, drain_rate_updates);
381

            
382
        // This is the stream queue for incoming data.
383
        // So the `reader` is the stream reader and the `writer` would be within the reactor.
384
        //
385
        // In arti this stream should be unbounded, so here we use a max size of `usize::MAX`.
386
        let (writer, reader) = duplex(/* max_buf_size= */ usize::MAX);
387
        let writer = writer.compat_write();
388
        let reader = reader.compat();
389

            
390
        // Make the reader+writer pair track the length of the buffer so that it can support
391
        // `BufferIsEmpty`.
392
        let (writer, reader) = with_length(writer, reader);
393

            
394
        // The reader for incoming stream data, with XON/XOFF support.
395
        let reader = XonXoffReader::new(reader_ctrl, reader);
396

            
397
        (writer, reader, drain_rate_receiver, flow_ctrl)
398
    }
399

            
400
    /// Buffer `num_bytes` as if the bytes arrived on the stream.
401
    ///
402
    /// Returns whether the flow control logic wanted to send an XOFF.
403
    async fn buffer_incoming_data(
404
        writer: &mut WriterWithLength<impl AsyncWrite + Unpin>,
405
        mut num_bytes: usize,
406
        flow_ctrl: &mut XonXoffFlowCtrl,
407
    ) -> bool {
408
        let mut wants_to_send_xoff = false;
409

            
410
        // Write the requested number of bytes.
411
        while num_bytes > 0 {
412
            // Write 100_000 bytes at a time.
413
            let buf_size = num_bytes.min(100_000);
414
            writer.write_all(&vec![0; buf_size]).await.unwrap();
415
            num_bytes -= buf_size;
416

            
417
            // Inform the flow control logic.
418
            let xoff = flow_ctrl.maybe_send_xoff(writer.len() as usize).unwrap();
419
            wants_to_send_xoff |= xoff.is_some();
420
        }
421

            
422
        wants_to_send_xoff
423
    }
424

            
425
    /// Read `num_bytes` from the stream.
426
    async fn read_incoming_data(mut reader: impl AsyncRead + Unpin, mut num_bytes: usize) {
427
        // Read the requested number of bytes.
428
        while num_bytes > 0 {
429
            // Read 100_000 bytes at a time.
430
            let buf_size = num_bytes.min(100_000);
431
            reader.read_exact(&mut vec![0; buf_size]).await.unwrap();
432
            num_bytes -= buf_size;
433
        }
434
    }
435

            
436
    /// This test is meant to test the drain rate update.
437
    /// It adds a lot of data to the stream queue so that it triggers sending an XOFF
438
    /// and sends a drain rate request to the [`XonXoffReader`],
439
    /// then it reads from the stream until it's empty
440
    /// and the `XonXoffReader` sends a drain rate update.
441
    /// The flow control logic receives the drain rate update and sends an XON.
442
    #[test]
443
    fn drain_rate_update() {
444
        tor_rtmock::MockRuntime::test_with_various(|_rt| async move {
445
            // This is the stream queue for incoming data.
446
            // So the `reader` is the stream reader and the `writer` would be within the reactor.
447
            let (mut writer, mut reader, mut drain_rate_receiver, mut flow_ctrl) =
448
                init_flow_ctrl(WithSidechannelMitigations::Enabled);
449

            
450
            // Data has arrived on the stream.
451
            // We always consider sending an XOFF when a stream has received data.
452
            // The amount of incoming data wasn't very large,
453
            // so we don't expect that it would actually want to send an XOFF.
454
            let wants_to_send_xoff =
455
                buffer_incoming_data(&mut writer, 10_000, &mut flow_ctrl).await;
456
            assert!(!wants_to_send_xoff);
457

            
458
            // We didn't want to send an XOFF,
459
            // so the stream reader will never have been asked for a drain rate update.
460
            assert!(!reader.pending_drain_rate_update);
461

            
462
            // The stream reader reads all of the incoming data.
463
            read_incoming_data(&mut reader, 10_000).await;
464

            
465
            // Check `pending_drain_rate_update` again,
466
            // and also ensure that we didn't send a drain rate update.
467
            assert!(!reader.pending_drain_rate_update);
468
            assert_eq!(drain_rate_receiver.try_recv(), Err(TryRecvError::Empty));
469

            
470
            // Data has arrived on the stream.
471
            // We always consider sending an XOFF when a stream has received data.
472
            // The amount of incoming data was large,
473
            // so we expect that it would want to send an XOFF.
474
            let wants_to_send_xoff =
475
                buffer_incoming_data(&mut writer, 800_000, &mut flow_ctrl).await;
476
            assert!(wants_to_send_xoff);
477

            
478
            // The above code should have sent an XOFF and asked the reader for a drain rate update,
479
            // but the reader hasn't realized this yet.
480
            assert!(!reader.pending_drain_rate_update);
481
            assert_eq!(drain_rate_receiver.try_recv(), Err(TryRecvError::Empty));
482

            
483
            // The reader won't realize it was asked for a drain rate update until after it's tried
484
            // reading once.
485
            let _ = reader.read(&mut [0; 0]).await.unwrap();
486
            assert!(reader.pending_drain_rate_update);
487

            
488
            // The drain rate update is only sent once we've drained the buffer,
489
            // so an update should not have been sent yet.
490
            assert_eq!(drain_rate_receiver.try_recv(), Err(TryRecvError::Empty));
491

            
492
            // Read most (but not all) of the data on the stream.
493
            read_incoming_data(&mut reader, 700_000).await;
494

            
495
            // We haven't read *all* of the data,
496
            // so should still not have sent a drain rate update.
497
            assert!(!Pin::new(reader.inner_mut()).is_empty());
498
            assert!(reader.pending_drain_rate_update);
499
            assert_eq!(drain_rate_receiver.try_recv(), Err(TryRecvError::Empty));
500

            
501
            // Read the last of the data on the stream.
502
            read_incoming_data(&mut reader, 100_000).await;
503

            
504
            // Now that the buffer is empty,
505
            // we should have sent a drain rate update.
506
            assert!(Pin::new(reader.inner_mut()).is_empty());
507
            assert!(!reader.pending_drain_rate_update);
508
            let xon_rate = drain_rate_receiver.try_recv().unwrap();
509
            assert_eq!(xon_rate, XonKBpsEwma::Unlimited);
510

            
511
            // The buffer is still empty,
512
            // so the flow control logic should want to send an XON.
513
            let xon = flow_ctrl
514
                .maybe_send_xon(xon_rate, writer.len() as usize)
515
                .unwrap()
516
                .unwrap();
517
            assert_eq!(xon.kbytes_per_sec_ewma(), xon_rate);
518
        });
519
    }
520

            
521
    /// Like the `drain_rate_update()` test,
522
    /// this test causes the `XonXoffReader` to send a drain rate update.
523
    /// But in this case the buffer refills again past the high-water mark
524
    /// before the drain rate update can be processed by the flow control logic,
525
    /// so it *does not* send an XON.
526
    /// Instead it re-requests a drain rate from the `XonXoffReader`.
527
    #[test]
528
    fn drain_rate_update_then_buffer_refill() {
529
        tor_rtmock::MockRuntime::test_with_various(|_rt| async move {
530
            // This is the stream queue for incoming data.
531
            // So the `reader` is the stream reader and the `writer` would be within the reactor.
532
            let (mut writer, mut reader, mut drain_rate_receiver, mut flow_ctrl) =
533
                init_flow_ctrl(WithSidechannelMitigations::Enabled);
534

            
535
            // Data has arrived on the stream.
536
            // We always consider sending an XOFF when a stream has received data.
537
            // The amount of incoming data was large,
538
            // so we expect that it would want to send an XOFF.
539
            let wants_to_send_xoff =
540
                buffer_incoming_data(&mut writer, 800_000, &mut flow_ctrl).await;
541
            assert!(wants_to_send_xoff);
542

            
543
            // Read all of the data on the stream.
544
            read_incoming_data(&mut reader, 700_000).await;
545
            assert!(reader.pending_drain_rate_update);
546
            read_incoming_data(&mut reader, 100_000).await;
547

            
548
            // Now that the buffer is empty,
549
            // we should have sent a drain rate update.
550
            assert!(Pin::new(reader.inner_mut()).is_empty());
551
            assert!(!reader.pending_drain_rate_update);
552

            
553
            // Before this drain rate update can make it to the
554
            // flow control logic with `maybe_send_xon()`,
555
            // the buffer fills again past the high-water mark.
556
            let wants_to_send_xoff =
557
                buffer_incoming_data(&mut writer, 800_000, &mut flow_ctrl).await;
558
            assert!(!wants_to_send_xoff);
559

            
560
            // Now the drain rate update makes it to the flow control logic.
561
            // Since the buffer is past the high-water mark,
562
            // we won't want to send an XON.
563
            let xon_rate = drain_rate_receiver.try_recv().unwrap();
564
            assert_eq!(xon_rate, XonKBpsEwma::Unlimited);
565
            let xon = flow_ctrl
566
                .maybe_send_xon(xon_rate, writer.len() as usize)
567
                .unwrap();
568
            assert!(xon.is_none());
569

            
570
            // Instead the reader will have been asked for a drain rate update again,
571
            // which restarts the entire process.
572
            assert!(!reader.pending_drain_rate_update);
573
            let _ = reader.read(&mut [0; 0]).await.unwrap();
574
            assert!(reader.pending_drain_rate_update);
575
        });
576
    }
577
}