Skip to main content

ntex_io/
io.rs

1use std::cell::{Cell, UnsafeCell};
2use std::task::{Context, Poll};
3use std::{fmt, future::poll_fn, hash, io, marker, mem, ops, ptr, rc::Rc};
4
5use ntex_bytes::{BytePageSize, BytesMut};
6use ntex_codec::{Decoder, Encoder};
7use ntex_service::cfg::{Cfg, SharedCfg};
8use ntex_util::{future::Either, task::LocalWaker, time::Sleep};
9
10use crate::buf::Stack;
11use crate::cfg::IoConfig;
12use crate::ctx::IoContext;
13use crate::filter::{Base, Filter, Layer};
14use crate::filterptr::FilterPtr;
15use crate::flags::Flags;
16use crate::ops::{Id, IoManager, TimerHandle};
17use crate::seal::{IoBoxed, Sealed};
18use crate::utils::{Extensions, WriteDeadline, write_timed_out};
19use crate::waiters::{TAG_WRITE, WriteGuard};
20use crate::{Decoded, FilterLayer, Handle, IoStatusUpdate, IoStream, RecvError};
21
22/// Read batches in a row small enough for a smaller page before the read page
23/// size shrinks.
24const RD_SHRINK_BATCHES: u8 = 4;
25
26/// Buffered, filterable interface to an underlying I/O stream.
27///
28/// `Io` is the main handle for a connection. The runtime fills its read buffer
29/// and drains its write buffer, while protocol code uses this handle to decode
30/// requests and encode responses.
31///
32/// Reads and writes go through buffers rather than directly to the socket.
33/// When the read buffer grows too large, ntex pauses the transport.
34/// [`read_more`](Self::read_more) can resume it, so calling that method asks
35/// for more input; it does more than check whether data is already available.
36///
37/// If the peer cleanly closes its read side, any buffered input remains
38/// available and responses can still be written. [`shutdown`](Self::shutdown)
39/// closes the connection gracefully and waits for the runtime to finish.
40/// [`terminate`](IoRef::terminate) closes it immediately.
41///
42/// The `F` parameter keeps track of the installed filters. Adding or mapping a
43/// filter changes the type. Use [`seal`](Self::seal) or [`boxed`](Self::boxed)
44/// when the concrete filter type does not need to be exposed.
45///
46/// Use [`get_ref`](Self::get_ref) to share access to the connection. The
47/// returned [`IoRef`] points to the same buffers and state; it does not create
48/// another connection. Dropping `Io` terminates the connection even if an
49/// `IoRef` is still alive.
50pub struct Io<F = Base>(UnsafeCell<IoRef>, marker::PhantomData<F>);
51
52/// A cheap, cloneable handle to an [`Io`] connection.
53///
54/// All clones point to the same connection. Changes to its buffers,
55/// configuration, timers, errors, or shutdown state are visible through every
56/// clone. Cloning `IoRef` never clones the socket.
57///
58/// Use it to inspect the connection, access its buffers, queue output, or start
59/// graceful or immediate shutdown. These methods are also available directly
60/// on `Io`, which dereferences to `IoRef`.
61///
62/// Keeping an `IoRef` alive does not keep the connection open after its `Io`
63/// owner is dropped. Like `Io`, it stays on the local runtime thread and is
64/// neither `Send` nor `Sync`.
65#[derive(Clone)]
66pub struct IoRef(pub(super) Rc<IoState>);
67
68pub(crate) struct IoState {
69    filter: FilterPtr,
70    pub(super) id: Cell<Id>,
71    pub(super) cfg: Cfg<IoConfig>,
72    pub(super) flags: Flags,
73    pub(super) error: Cell<Option<io::Error>>,
74    pub(super) read_task: LocalWaker,
75    pub(super) write_task: LocalWaker,
76    dispatch_task: LocalWaker,
77    pub(super) buffer: Stack,
78    pub(super) handle: Cell<Option<Box<dyn Handle>>>,
79    pub(super) timeout: Cell<TimerHandle>,
80    pub(super) shutdown_timeout: Cell<Option<Sleep>>,
81    /// Bytes handed to the transport that have not reached the peer yet.
82    ///
83    /// Completion based transports take ownership of write pages and keep them
84    /// until the operation completes, so those bytes are no longer in
85    /// `buffer`. They are still outstanding output and must be accounted for.
86    pub(super) wr_inflight: Cell<u32>,
87    /// Page size of new read buffers.
88    ///
89    /// It adapts to the input between the configured min and max read sizes,
90    /// see [`IoState::track_read`].
91    rd_size: Cell<BytePageSize>,
92    /// Input read since the last read that did not fill its buffer.
93    rd_batch: Cell<u32>,
94    /// Consecutive read batches small enough for a smaller page.
95    rd_small: Cell<u8>,
96    pub(super) extensions: Extensions,
97}
98
99impl IoState {
100    pub(super) fn id(&self) -> Id {
101        self.id.get()
102    }
103
104    pub(super) fn tag(&self) -> &'static str {
105        self.cfg.tag()
106    }
107
108    /// Checks whether the `Io` that owned the filter chain has been dropped.
109    pub(super) fn is_io_dropped(&self) -> bool {
110        !self.filter.is_set()
111    }
112
113    pub(super) fn filter(&self) -> &dyn Filter {
114        self.filter.get()
115    }
116
117    pub(super) fn notify_timeout(&self) {
118        if self.flags.check_dispatcher_timeout_unset() {
119            self.wake_dispatch_task();
120            log::trace!("{}: Timer, notify dispatcher", self.cfg.tag());
121        }
122    }
123
124    /// Wakes the disconnect waiters and all public waker slots.
125    pub(super) fn notify_disconnect(&self) {
126        self.extensions.wake_all();
127    }
128
129    /// Get the current I/O error.
130    pub(super) fn error(&self) -> Option<io::Error> {
131        if let Some(err) = self.error.take() {
132            let cloned = if let Some(code) = err.raw_os_error() {
133                io::Error::from_raw_os_error(code)
134            } else {
135                io::Error::new(err.kind(), format!("{err}"))
136            };
137            self.error.set(Some(cloned));
138            Some(err)
139        } else {
140            None
141        }
142    }
143
144    /// Returns the current I/O error, or creates a `NotConnected` error.
145    pub(super) fn error_or_disconnected(&self) -> io::Error {
146        self.error()
147            .unwrap_or_else(|| io::Error::new(io::ErrorKind::NotConnected, "Disconnected"))
148    }
149
150    pub(super) fn filters_stopped(&self) {
151        // the shutdown deadline carries over into the transport shutdown
152        // phase, so that a single `shutdown_timeout` bounds both phases
153        self.wake_read_task();
154        self.wake_write_task();
155        self.wake_dispatch_task();
156        self.wake_write_waiters();
157        self.flags.enter_transport_shutdown();
158    }
159
160    fn set_error(&self, err: Option<io::Error>) {
161        if let Some(err) = err {
162            if let Some(current) = self.error.take() {
163                self.error.set(Some(current));
164            } else {
165                self.error.set(Some(err));
166            }
167        }
168    }
169
170    pub(super) fn set_shutdown_error(&self, err: io::Error) {
171        self.set_error(Some(err));
172    }
173
174    /// Force-closes the connection, aborting it instead of closing gracefully.
175    ///
176    /// This is the only path that makes the transport report
177    /// [`Readiness::Terminate`](crate::Readiness::Terminate). Terminations that
178    /// come from a failure rather than from an explicit request go through
179    /// [`terminate_connection`](Self::terminate_connection) and let the
180    /// transport close the connection gracefully.
181    pub(super) fn force_close_connection(&self) {
182        self.begin_terminate(None, true);
183    }
184
185    pub(super) fn terminate_connection(&self, err: Option<io::Error>) {
186        self.begin_terminate(err, false);
187    }
188
189    /// Moves the connection onto the termination path and runs the teardown
190    /// work, which only the first call to get here performs.
191    fn begin_terminate(&self, err: Option<io::Error>, force: bool) {
192        self.set_error(err);
193        if self.flags.begin_terminate(force) {
194            log::trace!("{}: Terminate io", self.cfg.tag());
195            // buffers held by the transport are gone with it
196            self.wr_inflight.set(0);
197            self.wake_read_task();
198            self.wake_write_task();
199            self.wake_dispatch_task();
200            self.wake_write_waiters();
201            self.handle.take();
202        }
203    }
204
205    pub(super) fn stop_connection(&self, err: Option<io::Error>) {
206        if !self.flags.is_closed() {
207            log::trace!("{}: Stop io with error {:?}", self.cfg.tag(), err);
208            self.set_error(err);
209            self.flags.set_stopped();
210            // buffers held by the transport are gone with it
211            self.wr_inflight.set(0);
212            self.wake_read_task();
213            self.wake_write_task();
214            self.wake_dispatch_task();
215            self.wake_write_waiters();
216            self.notify_disconnect();
217            self.handle.take();
218        }
219    }
220
221    /// Gracefully shuts down the read and write I/O tasks.
222    pub(super) fn start_shutdown(&self) {
223        if self.flags.is_active() {
224            log::trace!("{}: Initiate io shutdown {:?}", self.cfg.tag(), self.flags);
225            self.flags.enter_filters_stopping();
226            self.wake_read_task();
227            self.wake_write_task();
228        }
229    }
230
231    /// Acquires an empty read buffer of the connection's read page size from
232    /// the thread-local page cache.
233    pub(super) fn get_read_buf(&self) -> BytesMut {
234        BytesMut::with_page_size(self.rd_size.get())
235    }
236
237    /// Returns the page size of new read buffers.
238    #[cfg(test)]
239    pub(super) fn read_size(&self) -> BytePageSize {
240        self.rd_size.get()
241    }
242
243    /// Resets the read page size to the configured min.
244    fn reset_read_size(&self) {
245        self.rd_size.set(self.cfg.read_size_min());
246        self.rd_batch.set(0);
247        self.rd_small.set(0);
248    }
249
250    /// Adapts the read page size to a transport read of `nbytes`.
251    ///
252    /// Reads that fill their buffer most likely leave more input behind, so
253    /// they and the read that follows them form one batch. A batch larger
254    /// than the page capacity grows the page size to fit it, up to the
255    /// configured max. After [`RD_SHRINK_BATCHES`] batches in a row that fit
256    /// in half of the next smaller page, the page size shrinks by one step,
257    /// down to the configured min.
258    pub(super) fn track_read(&self, nbytes: usize, full: bool) {
259        let batch = self.rd_batch.get().saturating_add(as_u32(nbytes));
260        let size = self.rd_size.get();
261        if full {
262            self.rd_batch.set(batch);
263            if batch as usize > size.capacity() {
264                self.grow_read_size(batch as usize);
265            }
266            return;
267        }
268
269        self.rd_batch.set(0);
270        let batch = batch as usize;
271        if batch == 0 {
272            // nothing was read since the previous batch ended
273        } else if batch > size.capacity() {
274            self.grow_read_size(batch);
275        } else if size != self.cfg.read_size_min() && batch < size.prev().capacity() / 2 {
276            let small = self.rd_small.get() + 1;
277            if small < RD_SHRINK_BATCHES {
278                self.rd_small.set(small);
279            } else {
280                let min = self.cfg.read_size_min();
281                let prev = size.prev();
282                self.rd_size.set(if prev.capacity() < min.capacity() {
283                    min
284                } else {
285                    prev
286                });
287                self.rd_small.set(0);
288            }
289        } else {
290            self.rd_small.set(0);
291        }
292    }
293
294    fn grow_read_size(&self, batch: usize) {
295        let max = self.cfg.read_size_max();
296        let size = BytePageSize::for_capacity(batch);
297        let size = if size == BytePageSize::Unset || size.capacity() > max.capacity() {
298            max
299        } else {
300            size
301        };
302        if size.capacity() > self.rd_size.get().capacity() {
303            self.rd_size.set(size);
304        }
305        self.rd_small.set(0);
306    }
307
308    pub(super) fn is_rd_backpressure_needed(&self, size: usize) -> bool {
309        size >= self.cfg.read_backpressure()
310    }
311
312    pub(super) fn is_wr_backpressure_needed(&self, size: usize) -> bool {
313        size >= self.cfg.write_backpressure()
314    }
315
316    pub(super) fn should_disable_rd_backpressure(&self, size: usize) -> bool {
317        size <= self.cfg.read_half()
318    }
319
320    pub(super) fn should_disable_wr_backpressure(&self, size: usize) -> bool {
321        size <= self.cfg.write_half()
322    }
323
324    /// Total output that has not reached the peer yet.
325    ///
326    /// This is the buffered output plus whatever the transport has taken
327    /// ownership of but not written out. Flush completion, write
328    /// back-pressure and the shutdown drain are all decided on this value,
329    /// because until it reaches zero the peer has not seen everything.
330    pub(super) fn write_outstanding(&self) -> usize {
331        self.buffer.write_buf_size() + self.wr_inflight.get() as usize
332    }
333
334    /// Output that has reached the transport-facing buffer but not the peer.
335    ///
336    /// Output a filter holds back, for example application data during a TLS
337    /// renegotiation, is excluded. It can not drain until more input is read,
338    /// so it must not keep reads paused.
339    pub(super) fn transport_outstanding(&self) -> usize {
340        self.buffer.write_dst_size() + self.wr_inflight.get() as usize
341    }
342
343    /// Records bytes taken by, or returned from, the transport.
344    pub(super) fn track_wr_inflight(&self, before: usize, after: usize) {
345        let inflight = self.wr_inflight.get();
346        if after < before {
347            self.wr_inflight
348                .set(inflight.saturating_add(as_u32(before - after)));
349        } else {
350            // the transport returned unwritten output to the buffer
351            self.wr_inflight
352                .set(inflight.saturating_sub(as_u32(after - before)));
353        }
354    }
355
356    /// Records output that reached the peer.
357    pub(super) fn wr_inflight_written(&self, written: usize) {
358        self.wr_inflight
359            .set(self.wr_inflight.get().saturating_sub(as_u32(written)));
360    }
361
362    pub(super) fn wake_read_task(&self) {
363        self.read_task.wake();
364    }
365
366    pub(super) fn wake_write_task(&self) {
367        #[cfg(feature = "trace")]
368        log::trace!("{}: Wake write task, flags:{:?}", self.tag(), self.flags);
369        self.write_task.wake();
370    }
371
372    pub(super) fn wake_dispatch_task(&self) {
373        self.dispatch_task.wake();
374    }
375
376    pub(super) fn wake_write_waiters(&self) {
377        self.extensions.wake(TAG_WRITE);
378    }
379
380    /// Returns `Some` once more output can be written or the connection is gone.
381    pub(super) fn check_write_ready(&self) -> Option<io::Result<()>> {
382        if self.flags.is_peer_gone() {
383            Some(Err(self.error_or_disconnected()))
384        } else if !self.flags.is_wr_backpressure()
385            || self.should_disable_wr_backpressure(self.write_outstanding())
386        {
387            Some(Ok(()))
388        } else {
389            None
390        }
391    }
392
393    pub(super) async fn write_ready(&self) -> io::Result<()> {
394        if let Some(res) = self.check_write_ready() {
395            return res;
396        }
397
398        let waiter = WriteGuard::new(&self.extensions);
399        let mut deadline = WriteDeadline::new(self.cfg.write_timeout());
400        poll_fn(|cx| {
401            if let Some(res) = self.check_write_ready() {
402                Poll::Ready(res)
403            } else if deadline.poll_expired(cx) {
404                Poll::Ready(Err(write_timed_out()))
405            } else {
406                // a woken waiter registers the waker again
407                waiter.register(cx);
408                Poll::Pending
409            }
410        })
411        .await
412    }
413
414    /// Waits for `f`, bounded by the configured write timeout.
415    pub(super) async fn with_write_timeout<T, F>(&self, mut f: F) -> io::Result<T>
416    where
417        F: FnMut(&mut Context<'_>) -> Poll<io::Result<T>>,
418    {
419        let mut deadline = WriteDeadline::new(self.cfg.write_timeout());
420        poll_fn(|cx| match f(cx) {
421            Poll::Ready(res) => Poll::Ready(res),
422            Poll::Pending if deadline.poll_expired(cx) => Poll::Ready(Err(write_timed_out())),
423            Poll::Pending => Poll::Pending,
424        })
425        .await
426    }
427}
428
429impl Eq for IoState {}
430
431impl PartialEq for IoState {
432    #[inline]
433    fn eq(&self, other: &Self) -> bool {
434        ptr::eq(self, other)
435    }
436}
437
438impl hash::Hash for IoState {
439    #[inline]
440    fn hash<H: hash::Hasher>(&self, state: &mut H) {
441        (ptr::from_ref(self) as usize).hash(state);
442    }
443}
444
445impl fmt::Debug for IoState {
446    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
447        let err = self.error.take();
448        let res = f
449            .debug_struct("IoState")
450            .field("id", &self.id)
451            .field("flags", &self.flags)
452            .field("filter", &self.filter.is_set())
453            .field("timeout", &self.timeout)
454            .field("error", &err)
455            .field("buffer", &self.buffer)
456            .field("cfg", &self.cfg)
457            .finish();
458        self.error.set(err);
459        res
460    }
461}
462
463impl Io {
464    /// Creates a new `Io` instance.
465    pub fn new<I: IoStream, T: Into<SharedCfg>>(io: I, cfg: T) -> Self {
466        let cfg = cfg.into().get::<IoConfig>();
467        let size = cfg.write_size();
468        let flags = Flags::new(cfg.write_buf_threshold() > 0);
469
470        let inner = Rc::new(IoState {
471            cfg,
472            flags,
473            id: Cell::new(Id::default()),
474            filter: FilterPtr::null(),
475            error: Cell::new(None),
476            dispatch_task: LocalWaker::new(),
477            read_task: LocalWaker::new(),
478            write_task: LocalWaker::new(),
479            buffer: Stack::new(size),
480            handle: Cell::new(None),
481            timeout: Cell::new(TimerHandle::default()),
482            shutdown_timeout: Cell::new(None),
483            wr_inflight: Cell::new(0),
484            rd_size: Cell::new(BytePageSize::Size4),
485            rd_batch: Cell::new(0),
486            rd_small: Cell::new(0),
487            extensions: Extensions::default(),
488        });
489        inner.reset_read_size();
490        inner.filter.set(Base::new(IoRef(inner.clone())));
491
492        let ioref = IoRef(inner);
493        ioref.0.id.set(IoManager::register(&ioref));
494
495        // start io tasks
496        let hnd = io.start(IoContext::new(ioref.clone()));
497        ioref.0.handle.set(Some(hnd));
498
499        Io(UnsafeCell::new(ioref), marker::PhantomData)
500    }
501}
502
503impl<I: IoStream> From<I> for Io {
504    #[inline]
505    fn from(io: I) -> Io {
506        Io::new(io, SharedCfg::default())
507    }
508}
509
510impl IoRef {
511    fn create_empty() -> IoRef {
512        IoRef(Rc::new(IoState {
513            id: Cell::new(Id::default()),
514            cfg: SharedCfg::default().get::<IoConfig>(),
515            filter: FilterPtr::null(),
516            flags: Flags::new_stopped(),
517            error: Cell::new(None),
518            dispatch_task: LocalWaker::new(),
519            read_task: LocalWaker::new(),
520            write_task: LocalWaker::new(),
521            buffer: Stack::new(BytePageSize::Size16),
522            handle: Cell::new(None),
523            timeout: Cell::new(TimerHandle::default()),
524            shutdown_timeout: Cell::new(None),
525            wr_inflight: Cell::new(0),
526            rd_size: Cell::new(BytePageSize::Size4),
527            rd_batch: Cell::new(0),
528            rd_small: Cell::new(0),
529            extensions: Extensions::default(),
530        }))
531    }
532}
533
534impl<F> Io<F> {
535    #[inline]
536    /// Returns a cloneable reference to this connection's shared state.
537    pub fn get_ref(&self) -> IoRef {
538        self.io_ref().clone()
539    }
540
541    #[inline]
542    #[must_use]
543    /// Transfers the live connection state into a new `Io` object.
544    ///
545    /// This does not clone the connection. `self` is replaced with a stopped
546    /// placeholder and should no longer be used for I/O.
547    ///
548    /// # Safety
549    ///
550    /// No reference derived from `self` may be alive across this call, the
551    /// filter returned by [`Io::filter`], the `IoRef` it dereferences to and
552    /// the config returned by [`IoRef::cfg`] all borrow the transferred state,
553    /// which is dropped together with the returned `Io`.
554    pub unsafe fn take(&self) -> Self {
555        Self(UnsafeCell::new(self.take_io_ref()), marker::PhantomData)
556    }
557
558    fn take_io_ref(&self) -> IoRef {
559        unsafe { mem::replace(&mut *self.0.get(), IoRef::create_empty()) }
560    }
561
562    /// Panics if the filter chain is in use, it cannot be changed then.
563    #[track_caller]
564    fn check_not_borrowed(&self) {
565        if self.st().buffer.is_borrowed() {
566            let tag = self.tag();
567            // the state is leaked, dropping `self` while unwinding would
568            // drop the filter that is in use
569            mem::forget(self.take_io_ref());
570            panic!("{tag}: filter chain is changed while it is in use");
571        }
572    }
573
574    fn st(&self) -> &IoState {
575        unsafe { &(*self.0.get()).0 }
576    }
577
578    fn io_ref(&self) -> &IoRef {
579        unsafe { &*self.0.get() }
580    }
581
582    #[inline]
583    /// Replaces this connection's shared I/O configuration.
584    ///
585    /// The write-buffer page size and eager-write enablement are updated
586    /// immediately, the read page size restarts at the new min read size.
587    /// Existing allocated buffers and an already registered timer are not
588    /// recreated.
589    ///
590    /// # Safety
591    ///
592    /// No reference obtained from [`IoRef::cfg`] for this connection may be
593    /// live when this method is called or used afterward. Replacing the
594    /// configuration may release the allocation backing those references.
595    pub unsafe fn set_config<T: Into<SharedCfg>>(&self, cfg: T) {
596        let cfg = cfg.into().get::<IoConfig>();
597        let page_size = cfg.write_size();
598        if self.cfg().write_size() != page_size {
599            self.st().buffer.set_page_size(page_size);
600        }
601        self.st()
602            .flags
603            .set_direct_wr_enabled(cfg.write_buf_threshold() > 0);
604        unsafe {
605            self.st().cfg.replace(cfg);
606        }
607        self.st().reset_read_size();
608    }
609}
610
611impl<F: FilterLayer, T: Filter> Io<Layer<F, T>> {
612    #[inline]
613    /// Returns a reference to a filter.
614    pub fn filter(&self) -> &F {
615        &self.st().filter.filter::<Layer<F, T>>().0
616    }
617}
618
619impl<F: Filter> Io<F> {
620    #[inline]
621    /// Converts the current I/O stream into a sealed version.
622    ///
623    /// # Panics
624    ///
625    /// Panics if called while the connection's filters or buffers are in use,
626    /// for example from a closure, codec or filter invoked by this connection.
627    pub fn seal(self) -> Io<Sealed> {
628        self.check_not_borrowed();
629        let state = self.take_io_ref();
630        state.0.filter.seal::<F>();
631
632        Io(UnsafeCell::new(state), marker::PhantomData)
633    }
634
635    #[inline]
636    /// Converts the current I/O stream into a boxed version.
637    pub fn boxed(self) -> IoBoxed {
638        self.seal().into()
639    }
640
641    #[inline]
642    /// Adds a new processing layer to the current filter chain.
643    ///
644    /// # Panics
645    ///
646    /// Panics if called while the connection's filters or buffers are in use,
647    /// for example from a closure, codec or filter invoked by this connection.
648    pub fn add_filter<U>(self, nf: U) -> Io<Layer<U, F>>
649    where
650        U: FilterLayer,
651    {
652        self.check_not_borrowed();
653        self.with_callbacks(|cb| cb.before_processing(&self));
654
655        // Write buffer processing may be delayed,
656        // call the filter chain to process pending writes
657        if let Err(e) = self.st().buffer.process_write_buf_no_cb(&self) {
658            self.st().terminate_connection(Some(e));
659        }
660
661        let state = self.take_io_ref();
662
663        // Add the buffers layer
664        state.0.buffer.add_layer(state.0.cfg.write_size());
665
666        // Replace current filter
667        state.0.filter.add_filter::<F, U>(nf);
668
669        let io = Io(UnsafeCell::new(state), marker::PhantomData);
670
671        // push read data into new filter
672        if let Err(e) = io.st().buffer.process_read_buf_no_cb(&io) {
673            io.st().terminate_connection(Some(e));
674        }
675        io.with_callbacks(|cb| cb.after_processing(&io));
676
677        io
678    }
679
680    #[allow(clippy::items_after_statements)]
681    /// Wraps the current layer with a wrapper.
682    ///
683    /// # Panics
684    ///
685    /// Panics if called while the connection's filters or buffers are in use,
686    /// for example from a closure, codec or filter invoked by this connection.
687    pub fn map_filter<U, R>(self, f: U) -> Io<R>
688    where
689        U: FnOnce(F) -> R,
690        R: Filter,
691    {
692        self.check_not_borrowed();
693        self.with_callbacks(|cb| cb.before_processing(&self));
694
695        // Write buffer processing may be delayed,
696        // call the filter chain to process pending writes
697        if let Err(e) = self.st().buffer.process_write_buf(&self) {
698            self.st().terminate_connection(Some(e));
699        }
700
701        // `f` owns the filter, if it unwinds the filter chain is gone and the
702        // connection must be torn down here, `Drop for Io` skips it without
703        // a filter
704        struct Guard<'a>(&'a IoRef);
705
706        impl Drop for Guard<'_> {
707            fn drop(&mut self) {
708                let st = &self.0.0;
709                st.force_close_connection();
710                st.buffer.release();
711                drop(st.extensions.take_callbacks());
712            }
713        }
714
715        let guard = Guard(self.io_ref());
716        self.st().filter.map_filter::<F, U, R>(f);
717        mem::forget(guard);
718
719        let state = self.take_io_ref();
720        let io = Io(UnsafeCell::new(state), marker::PhantomData);
721        io.with_callbacks(|cb| cb.after_processing(&io));
722        io
723    }
724}
725
726impl<F> Io<F> {
727    /// Reads and decodes the next item from the incoming stream.
728    ///
729    /// Returns `Ok(None)` when the connection closed before another item could
730    /// be decoded and nothing was left undecoded, whether the peer
731    /// disconnected or the shutdown was started locally.
732    ///
733    /// If the peer closed its write half while the codec still held a partial
734    /// item, the stream was truncated and this returns
735    /// [`io::ErrorKind::UnexpectedEof`] in [`Either::Right`] rather than
736    /// `Ok(None)`, so that a cut-off frame is not mistaken for a clean end of
737    /// stream. Undecodable bytes left after a locally started shutdown are not
738    /// treated as truncation.
739    ///
740    /// Codec errors are returned in [`Either::Left`]. Dispatcher timeouts and
741    /// connection errors are returned in [`Either::Right`]; a connection error
742    /// may originate from the transport, a filter, or shutdown.
743    ///
744    /// If write backpressure prevents further reads, this method first waits
745    /// for the write buffer to fall below its configured threshold. A
746    /// dispatcher timeout that fires during this wait is returned as well.
747    pub async fn recv<U>(&self, codec: &U) -> Result<Option<U::Item>, Either<U::Error, io::Error>>
748    where
749        U: Decoder,
750    {
751        loop {
752            return match poll_fn(|cx| self.poll_recv(codec, cx)).await {
753                Ok(item) => Ok(Some(item)),
754                Err(RecvError::Timeout) => Err(Either::Right(io::Error::new(
755                    io::ErrorKind::TimedOut,
756                    "Timeout",
757                ))),
758                Err(RecvError::WriteBackpressure) => {
759                    let timed_out = poll_fn(|cx| {
760                        if self.st().flags.check_dispatcher_timeout() {
761                            Poll::Ready(Ok(true))
762                        } else {
763                            self.poll_flush(cx, false).map_ok(|()| false)
764                        }
765                    })
766                    .await
767                    .map_err(Either::Right)?;
768
769                    if timed_out {
770                        Err(Either::Right(io::Error::new(
771                            io::ErrorKind::TimedOut,
772                            "Timeout",
773                        )))
774                    } else {
775                        continue;
776                    }
777                }
778                Err(RecvError::Decoder(err)) => Err(Either::Left(err)),
779                Err(RecvError::PeerGone(Some(err))) => Err(Either::Right(err)),
780                Err(RecvError::PeerGone(None)) => {
781                    let st = self.st();
782                    if st.flags.is_read_eof() && st.buffer.read_dst_size() != 0 {
783                        Err(Either::Right(io::Error::new(
784                            io::ErrorKind::UnexpectedEof,
785                            "bytes remaining on stream",
786                        )))
787                    } else {
788                        Ok(None)
789                    }
790                }
791            };
792        }
793    }
794
795    /// Reads exactly enough bytes from this I/O stream to fill `dst`.
796    ///
797    /// If there is not enough data available, waits for incoming data.
798    /// If clean EOF or an error-free shutdown occurs before `dst` is filled,
799    /// this returns [`io::ErrorKind::UnexpectedEof`]. Transport errors are
800    /// passed through unchanged.
801    ///
802    /// Each wait goes through [`read_more`](Self::read_more), so this releases
803    /// read backpressure unconditionally rather than waiting for the read
804    /// buffer to drain to half the high watermark.
805    pub async fn read_exact(&self, dst: &mut [u8]) -> io::Result<()> {
806        loop {
807            let completed = self.with_read_dst(|buf| {
808                if buf.len() >= dst.len() {
809                    let _ = io::Read::read(buf, dst).expect("Cannot fail");
810                    true
811                } else {
812                    false
813                }
814            });
815            if completed {
816                return Ok(());
817            }
818            // No more bytes will arrive after clean EOF or shutdown.
819            if self.read_more().await?.is_none() {
820                return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "Disconnected"));
821            }
822        }
823    }
824
825    #[inline]
826    /// Waits until application-facing data is available and allows the
827    /// transport to read more data.
828    ///
829    /// If reads are paused or under backpressure, calling this method resumes
830    /// the read task. This is not a passive check of the current buffer, and
831    /// read backpressure is released however much data is still buffered.
832    ///
833    /// Returns `Ok(Some(()))` when input that has not been reported yet is
834    /// available, `Ok(None)` when no further input will be reported, and `Err`
835    /// if the transport failed. See [`poll_read_more`](Self::poll_read_more)
836    /// for what `None` means after a clean EOF.
837    pub async fn read_more(&self) -> io::Result<Option<()>> {
838        poll_fn(|cx| self.poll_read_more(cx)).await
839    }
840
841    #[inline]
842    /// Waits for the next read from the transport.
843    ///
844    /// Use this when a filter needs more source bytes. Unlike
845    /// [`read_more`](Self::read_more), data already waiting in the application
846    /// buffer does not complete this wait. If the read task is paused, this
847    /// method wakes it.
848    ///
849    /// Returns `Some(())` when the transport provides more input. If clean EOF
850    /// leaves final data in the application buffer, it returns `Some(())` once
851    /// and `None` afterward. A transport error is returned unchanged.
852    pub async fn read_notify(&self) -> io::Result<Option<()>> {
853        poll_fn(|cx| self.poll_read_notify(cx)).await
854    }
855
856    #[inline]
857    /// Encodes an item and sends it to the peer, fully flushing the write buffer.
858    ///
859    /// The flush is bounded by the
860    /// [write timeout](crate::IoConfig::set_write_timeout), if one is set;
861    /// when it expires this returns [`io::ErrorKind::TimedOut`] in
862    /// [`Either::Right`].
863    pub async fn send<U>(&self, item: U::Item, codec: &U) -> Result<(), Either<U::Error, io::Error>>
864    where
865        U: Encoder,
866    {
867        self.encode(item, codec).map_err(Either::Left)?;
868
869        self.st()
870            .with_write_timeout(|cx| self.poll_flush(cx, true))
871            .await
872            .map_err(Either::Right)?;
873
874        Ok(())
875    }
876
877    #[inline]
878    /// Wakes the write task and requests a flush of queued output.
879    ///
880    /// This is the asynchronous counterpart to `poll_flush`. A full flush
881    /// completes once all output has reached the peer, including output a
882    /// transport has taken ownership of but not yet written.
883    ///
884    /// The wait is bounded by the
885    /// [write timeout](crate::IoConfig::set_write_timeout), if one is set;
886    /// when it expires this returns [`io::ErrorKind::TimedOut`].
887    pub async fn flush(&self, full: bool) -> io::Result<()> {
888        self.st()
889            .with_write_timeout(|cx| self.poll_flush(cx, full))
890            .await
891    }
892
893    #[inline]
894    /// Gracefully shuts down the I/O stream.
895    ///
896    /// Shutdown runs in two phases, bounded together by a single
897    /// [`IoConfig::set_shutdown_timeout`]. First the filters shut down while
898    /// both directions stay open, so a filter can emit its closing data and
899    /// read the peer's. Then the transport drains the remaining output, pauses
900    /// the read side, and closes the connection.
901    ///
902    /// This completes once the transport backend has finished its shutdown
903    /// operation, not merely once the output has been drained.
904    ///
905    /// If the shutdown deadline expires in either phase, this returns an
906    /// [`io::ErrorKind::TimedOut`] error once the transport has stopped.
907    ///
908    /// [`IoConfig::set_shutdown_timeout`]: crate::IoConfig::set_shutdown_timeout
909    pub async fn shutdown(&self) -> io::Result<()> {
910        poll_fn(|cx| self.poll_shutdown(cx)).await
911    }
912
913    #[inline]
914    /// Polls for application-facing data and allows the transport to read more.
915    ///
916    /// If reads are paused or under backpressure, this resumes the read task.
917    /// It therefore changes the read state and should not be used as a passive
918    /// buffer check.
919    ///
920    /// The release is unconditional: unlike consumption through
921    /// [`IoRef::decode`], [`IoRef::with_buf`], [`IoRef::with_read_src`] or
922    /// [`IoRef::with_read_dst`], which waits for the read buffer to fall to at
923    /// most half the high watermark, this releases read backpressure however
924    /// much data is still buffered. Asking for more input
925    /// is taken as the dispatcher declaring itself able to accept it. This
926    /// also resumes reads paused because output produced by reading, for
927    /// example replies to peer pings, has not drained yet.
928    ///
929    /// # Returns
930    ///
931    /// - `Poll::Pending` while waiting for more data.
932    /// - `Poll::Ready(Ok(Some(())))` when input that has not been reported yet
933    ///   is available.
934    /// - `Poll::Ready(Ok(None))` when no further input will be reported: after
935    ///   a clean EOF once the available input has been reported, or when the
936    ///   stream closes without an error. Clean EOF leaves the write half open.
937    ///
938    ///   This reports arrivals, not buffer contents. Input that has already
939    ///   been reported stays in the read buffer and remains decodable through
940    ///   [`IoRef::decode`] and [`IoRef::with_read_dst`], so `None` does not
941    ///   imply that the read buffer is empty.
942    /// - `Poll::Ready(Err(e))` if the transport failed.
943    pub fn poll_read_more(&self, cx: &mut Context<'_>) -> Poll<io::Result<Option<()>>> {
944        let st = self.st();
945
946        if st.flags.is_peer_gone() {
947            if let Some(err) = st.error() {
948                Poll::Ready(Err(err))
949            } else {
950                Poll::Ready(Ok(None))
951            }
952        } else {
953            let ready = st.flags.is_read_ready();
954
955            if st.flags.is_read_eof() && !ready {
956                return Poll::Ready(Ok(None));
957            }
958
959            // If the dispatcher requests more data but no read occurs,
960            // restart the read task.
961            if st.flags.is_read_paused_or_backpressure() || st.flags.is_read_wr_backpressure() {
962                st.flags.unset_read_ready_and_backpressure();
963                st.flags.unset_read_paused();
964                st.flags.unset_read_wr_backpressure();
965                st.wake_read_task();
966                if ready {
967                    Poll::Ready(Ok(Some(())))
968                } else {
969                    st.dispatch_task.register(cx.waker());
970                    Poll::Pending
971                }
972            } else if ready {
973                Poll::Ready(Ok(Some(())))
974            } else {
975                st.dispatch_task.register(cx.waker());
976                Poll::Pending
977            }
978        }
979    }
980
981    #[inline]
982    /// Polls for the next read from the transport.
983    ///
984    /// This is the polling version of [`read_notify`](Self::read_notify).
985    /// Existing application data does not make it ready. When another transport
986    /// read is needed, this wakes the read task and registers the current waker.
987    ///
988    /// `Some(())` means that more input arrived. Clean EOF may produce one last
989    /// `Some(())` when filters leave final application data; later polls return
990    /// `None`. Transport errors are returned unchanged.
991    ///
992    /// Paused or back-pressured reads are resumed through
993    /// [`poll_read_more`](Self::poll_read_more), which releases read
994    /// backpressure unconditionally.
995    pub fn poll_read_notify(&self, cx: &mut Context<'_>) -> Poll<io::Result<Option<()>>> {
996        let st = self.st();
997        if st.flags.is_stopping_or_terminating() {
998            if let Some(err) = st.error() {
999                Poll::Ready(Err(err))
1000            } else {
1001                Poll::Ready(Ok(None))
1002            }
1003        } else if st.flags.is_read_eof() {
1004            let notified = st.flags.take_read_notified();
1005            if notified && st.flags.is_read_ready() {
1006                Poll::Ready(Ok(Some(())))
1007            } else {
1008                Poll::Ready(Ok(None))
1009            }
1010        } else if st.flags.take_read_notified() {
1011            Poll::Ready(Ok(Some(())))
1012        } else {
1013            st.flags.set_read_notify();
1014            // Resumes the read task if reads are paused. The result is
1015            // discarded on purpose: buffered application data does not
1016            // complete this wait, and the eof and closed cases are handled
1017            // by the branches above.
1018            let _ = self.poll_read_more(cx);
1019            st.dispatch_task.register(cx.waker());
1020            Poll::Pending
1021        }
1022    }
1023
1024    #[inline]
1025    /// Decodes the next item from the incoming byte stream.
1026    ///
1027    /// Returns `Poll::Pending` when the codec needs more input, after going
1028    /// through [`poll_read_more`](Self::poll_read_more), which wakes the read
1029    /// task and releases read backpressure unconditionally.
1030    ///
1031    /// An error return does not register the waker.
1032    pub fn poll_recv<U>(
1033        &self,
1034        codec: &U,
1035        cx: &mut Context<'_>,
1036    ) -> Poll<Result<U::Item, RecvError<U>>>
1037    where
1038        U: Decoder,
1039    {
1040        let decoded = self.poll_recv_decode(codec, cx)?;
1041
1042        if let Some(item) = decoded.item {
1043            Poll::Ready(Ok(item))
1044        } else {
1045            Poll::Pending
1046        }
1047    }
1048
1049    #[inline]
1050    /// Attempts to decode an item and reports buffer progress.
1051    ///
1052    /// `Decoded::consumed` is the number of bytes consumed by this decode
1053    /// attempt and `Decoded::remains` is the number left in the
1054    /// application-facing read buffer. If the codec needs more input, this
1055    /// returns `Ok` with `item` set to `None` after arranging for `cx` to be
1056    /// woken when progress is possible.
1057    ///
1058    /// An error return does not register the waker. While the connection is
1059    /// open, an expired dispatcher timer and then active write backpressure are
1060    /// reported before the read buffer is decoded, so an error never follows a
1061    /// decode attempt and `Decoded` is never lost. The caller decides whether
1062    /// input received together with a timeout is decoded first, see
1063    /// [`IoRef::decode_item`](crate::IoRef::decode_item). Once the connection
1064    /// is closing neither is reported, the buffered input is decoded and
1065    /// `RecvError::PeerGone` is returned when no item is left.
1066    ///
1067    /// When the codec needs more input this goes through
1068    /// [`poll_read_more`](Self::poll_read_more), which releases read
1069    /// backpressure unconditionally.
1070    pub fn poll_recv_decode<U>(
1071        &self,
1072        codec: &U,
1073        cx: &mut Context<'_>,
1074    ) -> Result<Decoded<U::Item>, RecvError<U>>
1075    where
1076        U: Decoder,
1077    {
1078        let st = self.st();
1079        st.flags.unset_read_ready();
1080
1081        let closed = st.flags.is_stopping() || st.flags.is_terminating();
1082        if !closed {
1083            if st.flags.check_dispatcher_timeout() {
1084                return Err(RecvError::Timeout);
1085            } else if st.flags.is_wr_backpressure() {
1086                return Err(RecvError::WriteBackpressure);
1087            }
1088        }
1089
1090        let decoded = self
1091            .decode_item(codec)
1092            .map_err(|err| RecvError::Decoder(err))?;
1093
1094        if decoded.item.is_some() {
1095            Ok(decoded)
1096        } else if st.flags.is_stopping() || st.flags.is_terminating() {
1097            Err(RecvError::PeerGone(st.error()))
1098        } else {
1099            match self.poll_read_more(cx) {
1100                Poll::Pending | Poll::Ready(Ok(Some(()))) => {
1101                    #[cfg(feature = "trace")]
1102                    if decoded.remains != 0 {
1103                        log::trace!("{}: Not enough data to decode next frame", self.tag());
1104                    }
1105                    Ok(decoded)
1106                }
1107                Poll::Ready(Err(e)) => Err(RecvError::PeerGone(Some(e))),
1108                Poll::Ready(Ok(None)) => Err(RecvError::PeerGone(None)),
1109            }
1110        }
1111    }
1112
1113    #[inline]
1114    /// Wakes the write task and instructs it to flush data.
1115    ///
1116    /// A full flush waits until all output has reached the peer.
1117    ///
1118    /// Otherwise this returns immediately while the outstanding size is below
1119    /// the configured high watermark. Reaching that watermark enables write
1120    /// backpressure, and the call then waits until the outstanding size falls
1121    /// to half of it.
1122    ///
1123    /// Output that a completion based transport has taken ownership of counts
1124    /// as outstanding until it reaches the peer, so a full flush does not
1125    /// complete while a write is still in flight.
1126    pub fn poll_flush(&self, cx: &mut Context<'_>, full: bool) -> Poll<io::Result<()>> {
1127        let st = self.st();
1128
1129        // flush filter state
1130        st.buffer.process_write_buf_force(self)?;
1131        self.consolidate_write_state(false)?;
1132
1133        let len = st.write_outstanding();
1134        if len > 0 {
1135            if st.flags.is_peer_gone() {
1136                return Poll::Ready(Err(st.error_or_disconnected()));
1137            } else if full {
1138                st.flags.set_wants_write_flush();
1139                st.dispatch_task.register(cx.waker());
1140                return Poll::Pending;
1141            } else if st.flags.is_wr_backpressure() {
1142                if !st.should_disable_wr_backpressure(len) {
1143                    st.dispatch_task.register(cx.waker());
1144                    return Poll::Pending;
1145                }
1146            } else if st.is_wr_backpressure_needed(len) {
1147                st.flags.set_wr_backpressure();
1148                st.dispatch_task.register(cx.waker());
1149                return Poll::Pending;
1150            }
1151        }
1152        if st.flags.is_peer_gone() && !st.flags.is_write_flush() {
1153            Poll::Ready(Err(st.error_or_disconnected()))
1154        } else {
1155            st.flags.unset_wr_backpressure_and_flush();
1156            Poll::Ready(Ok(()))
1157        }
1158    }
1159
1160    #[inline]
1161    /// Polls graceful shutdown through transport completion.
1162    ///
1163    /// `Poll::Ready` is returned only after the transport backend marks the
1164    /// connection stopped, not merely when filter shutdown and flushing finish.
1165    pub fn poll_shutdown(&self, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
1166        let st = self.st();
1167
1168        if st.flags.is_closed() {
1169            if let Some(err) = st.error() {
1170                Poll::Ready(Err(err))
1171            } else {
1172                Poll::Ready(Ok(()))
1173            }
1174        } else {
1175            if !st.flags.is_terminating() && !st.flags.is_stopping_filters() {
1176                st.start_shutdown();
1177            }
1178            // Reads must keep running during shutdown so that the transport can
1179            // observe a peer EOF and filters can complete their shutdown
1180            // handshake. `BUF_R_READY` is deliberately left alone: it marks
1181            // input the dispatcher has not consumed yet.
1182            //
1183            // Only the pause is cleared. Backpressure means the read buffer is
1184            // full, which the shutdown must still respect: clearing it on every
1185            // poll would let a peer that keeps sending grow the buffer without
1186            // bound, and would hide the blocked shutdown detection in
1187            // `poll_filters_shutdown`, which tests for exactly that flag.
1188            //
1189            // The tasks are woken only when this changes their state:
1190            // `start_shutdown` wakes both. Waking them on every poll would let
1191            // a caller that is polled spuriously keep the runtime busy with
1192            // wakeups; compio then never polls for I/O completions.
1193            if st.flags.is_read_paused() {
1194                st.flags.unset_read_paused();
1195                st.wake_read_task();
1196            }
1197            st.dispatch_task.register(cx.waker());
1198            Poll::Pending
1199        }
1200    }
1201
1202    #[inline]
1203    /// Pauses the read task and polls for a status update.
1204    ///
1205    /// The transport stops reading until the pause is cancelled. There is no
1206    /// explicit resume: the pause is cancelled implicitly by any operation that
1207    /// touches the read buffer or asks for more input, namely
1208    /// [`read_more`](Self::read_more), [`poll_read_more`](Self::poll_read_more),
1209    /// [`IoRef::decode`], [`IoRef::decode_item`], and [`IoRef::with_read_dst`].
1210    /// Releasing read backpressure cancels it as well. Because those methods
1211    /// are available through every [`IoRef`] clone, the pause holds only while
1212    /// no other holder touches the read buffer.
1213    ///
1214    /// See [`poll_status_update`](Self::poll_status_update) for the reported
1215    /// updates.
1216    pub fn poll_read_pause(&self, cx: &mut Context<'_>) -> Poll<IoStatusUpdate> {
1217        let st = self.st();
1218        if !st.flags.is_read_paused() {
1219            st.wake_read_task();
1220            st.flags.set_read_paused();
1221        }
1222        self.poll_status_update(cx)
1223    }
1224
1225    #[inline]
1226    /// Polls for available status updates.
1227    ///
1228    /// `Timeout` consumes the pending dispatcher-timeout notification.
1229    /// `WriteBackpressure` is reported while backpressure is active. The poll
1230    /// that observes the write buffer falling below its release threshold
1231    /// releases backpressure and reports no status update, matching
1232    /// [`poll_flush`](Self::poll_flush). `PeerGone` is returned once the
1233    /// connection has closed, whether the peer disconnected, the transport
1234    /// failed, or the shutdown was started locally.
1235    pub fn poll_status_update(&self, cx: &mut Context<'_>) -> Poll<IoStatusUpdate> {
1236        let st = self.st();
1237        st.dispatch_task.register(cx.waker());
1238        if st.flags.is_peer_gone() {
1239            Poll::Ready(IoStatusUpdate::PeerGone(st.error()))
1240        } else if st.flags.check_dispatcher_timeout() {
1241            Poll::Ready(IoStatusUpdate::Timeout)
1242        } else if st.flags.is_wr_backpressure() {
1243            // write backpressure is enabled and outstanding output is smaller than half
1244            if st.should_disable_wr_backpressure(st.write_outstanding()) {
1245                st.flags.unset_wr_backpressure();
1246                Poll::Pending
1247            } else {
1248                Poll::Ready(IoStatusUpdate::WriteBackpressure)
1249            }
1250        } else {
1251            Poll::Pending
1252        }
1253    }
1254
1255    #[inline]
1256    /// Registers a dispatch task.
1257    pub fn register_dispatch(&self, cx: &mut Context<'_>) {
1258        self.st().dispatch_task.register(cx.waker());
1259    }
1260}
1261
1262impl<F> AsRef<IoRef> for Io<F> {
1263    #[inline]
1264    fn as_ref(&self) -> &IoRef {
1265        self.io_ref()
1266    }
1267}
1268
1269impl<F> Eq for Io<F> {}
1270
1271impl<F> PartialEq for Io<F> {
1272    #[inline]
1273    fn eq(&self, other: &Self) -> bool {
1274        self.io_ref().eq(other.io_ref())
1275    }
1276}
1277
1278impl<F> hash::Hash for Io<F> {
1279    #[inline]
1280    fn hash<H: hash::Hasher>(&self, state: &mut H) {
1281        self.io_ref().hash(state);
1282    }
1283}
1284
1285impl<F> fmt::Debug for Io<F> {
1286    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1287        f.debug_struct("Io").field("state", self.st()).finish()
1288    }
1289}
1290
1291impl<F> ops::Deref for Io<F> {
1292    type Target = IoRef;
1293
1294    #[inline]
1295    fn deref(&self) -> &Self::Target {
1296        self.io_ref()
1297    }
1298}
1299
1300impl<F> Drop for Io<F> {
1301    fn drop(&mut self) {
1302        let st = self.st();
1303        self.stop_timer();
1304
1305        // code run by the filter chain dropped the `Io`, the filter is in use
1306        let in_use = st.filter.is_set() && st.buffer.is_borrowed();
1307
1308        if st.filter.is_set() {
1309            if in_use {
1310                // the filter cannot be dropped, it is leaked
1311                st.force_close_connection();
1312                st.filter.leak();
1313            } else {
1314                // filter is unsafe and must be dropped explicitly,
1315                // and won't be dropped without special attention
1316                if !st.flags.is_closed() {
1317                    log::trace!("{}: Io is dropped, terminate connection", st.tag());
1318                }
1319
1320                if st.write_outstanding() == 0 {
1321                    // Everything the application wrote has reached the transport,
1322                    // so the connection can end with a normal FIN and the peer
1323                    // sees a clean end of stream.
1324                    st.terminate_connection(None);
1325                } else {
1326                    // Output is still buffered and the filter chain is about to go
1327                    // away, so it can never be delivered. Abort instead, so that
1328                    // the peer cannot mistake a truncated stream for a complete
1329                    // one.
1330                    st.force_close_connection();
1331                }
1332                st.filter.drop_filter::<F>();
1333            }
1334
1335            // Nothing can consume buffered input or deliver buffered output
1336            // anymore, but the state may outlive the `Io` for a while, held by
1337            // the transport while it closes or by other `IoRef` handles.
1338            st.buffer.release();
1339
1340            // Callbacks may hold an `IoRef` to this connection, which would keep
1341            // the state alive through a reference cycle. They are dropped outside
1342            // the extensions borrow, because their destructor may use the `IoRef`.
1343            drop(st.extensions.take_callbacks());
1344        }
1345
1346        IoManager::unregister(self.io_ref());
1347
1348        // a panic while unwinding would abort
1349        assert!(
1350            !in_use || std::thread::panicking(),
1351            "{}: Io is dropped while its filter is in use",
1352            st.tag()
1353        );
1354    }
1355}
1356
1357/// Saturating conversion used for the in-flight write counter.
1358fn as_u32(v: usize) -> u32 {
1359    u32::try_from(v).unwrap_or(u32::MAX)
1360}
1361
1362#[cfg(test)]
1363mod tests {
1364    use std::{cell::Cell, rc::Rc};
1365
1366    use ntex_bytes::{BufMut, BytePages, Bytes, BytesMut};
1367    use ntex_codec::BytesCodec;
1368    use ntex_util::{future::lazy, time::Millis, time::sleep, time::timeout};
1369
1370    use super::*;
1371    use crate::waiters::{TAG_DISCONNECT, WaiterEntry};
1372    use crate::{
1373        FilterBuf, IoContext, IoTaskStatus, Readiness, Waiter, ops::Iops, testing::IoTest,
1374    };
1375    use std::pin::Pin;
1376
1377    const BIN: &[u8] = b"GET /test HTTP/1\r\n\r\n";
1378    const TEXT: &str = "GET /test HTTP/1\r\n\r\n";
1379    const BIN2: &[u8] = b"12345678901234561234567890123456";
1380
1381    #[ntex::test]
1382    async fn test_basics() {
1383        let (client, server) = IoTest::create();
1384        client.remote_buffer_cap(1024);
1385
1386        let server = Io::from(server);
1387        assert!(server.eq(&server));
1388        assert!(server.io_ref().eq(server.io_ref()));
1389    }
1390
1391    #[ntex::test]
1392    async fn test_recv() {
1393        let (client, server) = IoTest::create();
1394        client.remote_buffer_cap(1024);
1395
1396        let server = Io::new(server, SharedCfg::new("SRV"));
1397
1398        server.st().notify_timeout();
1399        let err = server.recv(&BytesCodec).await.err().unwrap();
1400        assert!(format!("{err:?}").contains("Timeout"));
1401
1402        client.write(TEXT);
1403        server.st().flags.set_wr_backpressure();
1404        let item = server.recv(&BytesCodec).await.ok().unwrap().unwrap();
1405        assert_eq!(item, TEXT);
1406    }
1407
1408    fn read_size_io(min: BytePageSize, max: BytePageSize) -> Io {
1409        Io::new(
1410            IoTest::create().1,
1411            SharedCfg::new("SRV").add(IoConfig::default().set_read_size(min, max)),
1412        )
1413    }
1414
1415    /// The read page size starts at the min and grows to fit read batches,
1416    /// up to the max.
1417    #[ntex::test]
1418    async fn read_size_grows_with_batch() {
1419        let io = Io::new(IoTest::create().1, SharedCfg::new("SRV"));
1420        let st = io.st();
1421        assert_eq!(st.read_size(), BytePageSize::Size4);
1422        assert_eq!(st.get_read_buf().capacity(), BytePageSize::Size4.capacity());
1423
1424        // a read that does not fill its buffer ends the batch
1425        st.track_read(BytePageSize::Size4.capacity() - 1, false);
1426        assert_eq!(st.read_size(), BytePageSize::Size4);
1427
1428        // full reads continue the batch, it grows the page once it no longer
1429        // fits
1430        st.track_read(3000, true);
1431        assert_eq!(st.read_size(), BytePageSize::Size4);
1432        st.track_read(3000, true);
1433        assert_eq!(st.read_size(), BytePageSize::Size8);
1434        st.track_read(10_000, false);
1435        assert_eq!(st.read_size(), BytePageSize::Size16);
1436        assert_eq!(
1437            st.get_read_buf().capacity(),
1438            BytePageSize::Size16.capacity()
1439        );
1440
1441        // zero-byte reads and errors do not change anything
1442        st.track_read(0, false);
1443        assert_eq!(st.read_size(), BytePageSize::Size16);
1444
1445        // capped at the max
1446        for _ in 0..10 {
1447            st.track_read(60_000, true);
1448        }
1449        assert_eq!(st.read_size(), BytePageSize::Size64);
1450        st.track_read(1, false);
1451        assert_eq!(st.read_size(), BytePageSize::Size64);
1452    }
1453
1454    /// The read page size shrinks one step after several small batches in a
1455    /// row, down to the min.
1456    #[ntex::test]
1457    async fn read_size_shrinks_after_small_batches() {
1458        let io = read_size_io(BytePageSize::Size8, BytePageSize::Size64);
1459        let st = io.st();
1460        assert_eq!(st.read_size(), BytePageSize::Size8);
1461        st.track_read(40_000, false);
1462        assert_eq!(st.read_size(), BytePageSize::Size48);
1463
1464        // fits in half of the smaller page, but not enough in a row
1465        for _ in 0..RD_SHRINK_BATCHES - 1 {
1466            st.track_read(100, false);
1467        }
1468        assert_eq!(st.read_size(), BytePageSize::Size48);
1469        // a batch that needs the larger page restarts the count
1470        st.track_read(20_000, false);
1471        for _ in 0..RD_SHRINK_BATCHES - 1 {
1472            st.track_read(100, false);
1473        }
1474        assert_eq!(st.read_size(), BytePageSize::Size48);
1475        st.track_read(100, false);
1476        assert_eq!(st.read_size(), BytePageSize::Size32);
1477
1478        // down to the min, not below
1479        for _ in 0..RD_SHRINK_BATCHES * 10 {
1480            st.track_read(100, false);
1481        }
1482        assert_eq!(st.read_size(), BytePageSize::Size8);
1483    }
1484
1485    /// Equal min and max read page sizes disable the adaptation.
1486    #[ntex::test]
1487    async fn read_size_fixed() {
1488        let io = read_size_io(BytePageSize::Size16, BytePageSize::Size16);
1489        let st = io.st();
1490        assert_eq!(st.read_size(), BytePageSize::Size16);
1491        for _ in 0..10 {
1492            st.track_read(60_000, true);
1493        }
1494        assert_eq!(st.read_size(), BytePageSize::Size16);
1495        for _ in 0..RD_SHRINK_BATCHES * 10 {
1496            st.track_read(1, false);
1497        }
1498        assert_eq!(st.read_size(), BytePageSize::Size16);
1499    }
1500
1501    /// Replacing the configuration restarts the read page size at the new
1502    /// min.
1503    #[ntex::test]
1504    async fn read_size_reset_by_set_config() {
1505        let io = Io::new(IoTest::create().1, SharedCfg::new("SRV"));
1506        io.st().track_read(60_000, false);
1507        assert_eq!(io.st().read_size(), BytePageSize::Size64);
1508
1509        unsafe {
1510            io.set_config(
1511                SharedCfg::new("SRV2").add(
1512                    IoConfig::default().set_read_size(BytePageSize::Size8, BytePageSize::Size16),
1513                ),
1514            );
1515        }
1516        assert_eq!(io.st().read_size(), BytePageSize::Size8);
1517        io.st().track_read(60_000, false);
1518        assert_eq!(io.st().read_size(), BytePageSize::Size16);
1519    }
1520
1521    /// Transport reads drive the read page size, read backpressure does not
1522    /// depend on it.
1523    #[ntex::test]
1524    async fn read_size_follows_transport_reads() {
1525        let (client, server) = IoTest::create();
1526        let io = Io::new(server, SharedCfg::new("SRV"));
1527        assert_eq!(io.st().read_size(), BytePageSize::Size4);
1528
1529        let data = vec![7u8; 100 * 1024];
1530        client.write(&data);
1531        let mut received = 0;
1532        while received < data.len() {
1533            let item = io.recv(&BytesCodec).await.unwrap().unwrap();
1534            received += item.len();
1535        }
1536        assert_eq!(received, data.len());
1537        assert!(io.st().read_size().capacity() > BytePageSize::Size4.capacity());
1538        assert_eq!(
1539            io.cfg().read_backpressure(),
1540            BytePageSize::Size32.capacity()
1541        );
1542
1543        // small messages shrink it back
1544        for _ in 0..RD_SHRINK_BATCHES * 10 {
1545            client.write(b"x");
1546            assert_eq!(io.recv(&BytesCodec).await.unwrap().unwrap(), &b"x"[..]);
1547        }
1548        assert_eq!(io.st().read_size(), BytePageSize::Size4);
1549    }
1550    /// Callbacks that hold an `IoRef` must not keep the connection state alive
1551    /// after the `Io` is dropped.
1552    #[ntex::test]
1553    async fn test_drop_releases_callbacks() {
1554        struct Cb(#[allow(dead_code)] IoRef);
1555        impl crate::IoCallbacks for Cb {
1556            fn before_processing(&self, _: &IoRef) {}
1557            fn after_processing(&self, _: &IoRef) {}
1558        }
1559
1560        let (client, server) = IoTest::create();
1561        let server = Io::new(server, SharedCfg::new("SRV"));
1562        server.register_filter_callbacks(Cb(server.get_ref()));
1563        let state = Rc::downgrade(&server.io_ref().0);
1564
1565        drop(server);
1566        client.close().await;
1567        sleep(Millis(50)).await;
1568        assert!(state.upgrade().is_none());
1569    }
1570
1571    /// Callbacks registered through a leftover `IoRef` once the `Io` is
1572    /// dropped, or the connection is closed, must not be stored.
1573    #[ntex::test]
1574    async fn test_callbacks_not_registered_after_drop_or_close() {
1575        struct Cb(#[allow(dead_code)] IoRef, Rc<Cell<usize>>);
1576        impl crate::IoCallbacks for Cb {
1577            fn before_processing(&self, _: &IoRef) {
1578                self.1.set(self.1.get() + 1);
1579            }
1580            fn after_processing(&self, _: &IoRef) {}
1581        }
1582
1583        // dropped, teardown still in progress
1584        let (client, server) = IoTest::create();
1585        let server = Io::new(server, SharedCfg::new("SRV"));
1586        let io = server.get_ref();
1587        let state = Rc::downgrade(&io.0);
1588        drop(server);
1589        io.register_filter_callbacks(Cb(io.clone(), Rc::default()));
1590        drop(io);
1591        client.close().await;
1592        sleep(Millis(50)).await;
1593        assert!(state.upgrade().is_none());
1594
1595        // closed, `Io` still alive
1596        let (client, server) = IoTest::create();
1597        let server = Io::new(server, SharedCfg::new("SRV"));
1598        client.close().await;
1599        server.close();
1600        let _ = server.shutdown().await;
1601        assert!(server.is_closed());
1602        let calls = Rc::new(Cell::new(0));
1603        server.register_filter_callbacks(Cb(server.get_ref(), calls.clone()));
1604        server.with_callbacks(|cb| cb.before_processing(&server));
1605        assert_eq!(calls.get(), 0);
1606    }
1607
1608    #[ntex::test]
1609    async fn test_stop_timer_clears_timeout_notification() {
1610        let (_client, server) = IoTest::create();
1611        let server = Io::new(server, SharedCfg::new("SRV"));
1612
1613        server.start_timer(ntex_util::time::Seconds(10));
1614        server.notify_timeout();
1615        server.stop_timer();
1616
1617        assert!(lazy(|cx| server.poll_status_update(cx)).await.is_pending());
1618    }
1619
1620    #[ntex::test]
1621    async fn test_read() {
1622        let (client, server) = IoTest::create();
1623        client.remote_buffer_cap(1024);
1624
1625        let server = Io::new(server, SharedCfg::new("SRV"));
1626
1627        client.write(b"1234");
1628        let mut buf: [u8; 4] = [0, 0, 0, 0];
1629        server.read_exact(&mut buf).await.unwrap();
1630        assert_eq!(&buf, b"1234");
1631
1632        // disconnect during read
1633        let fut = ntex_rt::spawn(async move {
1634            let mut buf: [u8; 4] = [0, 0, 0, 0];
1635            let err = server.read_exact(&mut buf).await.unwrap_err();
1636            (server, err)
1637        });
1638        client.close().await;
1639        let (server, err) = fut.await.unwrap();
1640        assert_eq!(err.kind(), io::ErrorKind::UnexpectedEof);
1641
1642        let err = server.read_exact(&mut [0]).await.unwrap_err();
1643        assert_eq!(err.kind(), io::ErrorKind::UnexpectedEof);
1644    }
1645
1646    #[ntex::test]
1647    async fn test_read_partial_eof() {
1648        let (client, server) = IoTest::create();
1649        client.remote_buffer_cap(1024);
1650
1651        let server = Io::new(server, SharedCfg::new("SRV"));
1652
1653        client.write(b"12");
1654        client.close().await;
1655
1656        let err = server.read_exact(&mut [0; 4]).await.unwrap_err();
1657        assert_eq!(err.kind(), io::ErrorKind::UnexpectedEof);
1658
1659        let mut buf = [0; 2];
1660        server.read_exact(&mut buf).await.unwrap();
1661        assert_eq!(&buf, b"12");
1662
1663        let err = server.read_exact(&mut [0]).await.unwrap_err();
1664        assert_eq!(err.kind(), io::ErrorKind::UnexpectedEof);
1665    }
1666
1667    #[ntex::test]
1668    async fn test_send() {
1669        let (client, server) = IoTest::create();
1670        client.remote_buffer_cap(1024);
1671
1672        let server = Io::from(server);
1673        assert!(server.eq(&server));
1674
1675        server
1676            .send(Bytes::from_static(BIN), &BytesCodec)
1677            .await
1678            .ok()
1679            .unwrap();
1680        let item = client.read_any();
1681        assert_eq!(item, TEXT);
1682    }
1683
1684    #[ntex::test]
1685    async fn read() {
1686        let io = Io::new(
1687            IoTest::create().0,
1688            SharedCfg::new("SRV").add(
1689                IoConfig::default()
1690                    .set_read_size(BytePageSize::Size4, BytePageSize::Size4)
1691                    .set_read_backpressure(BytePageSize::Size4.capacity()),
1692            ),
1693        );
1694        assert!(lazy(|cx| io.poll_read_more(cx)).await.is_pending());
1695        assert!(io.st().dispatch_task.is_set());
1696
1697        let high = BytePageSize::Size4.capacity();
1698        let half = high / 2;
1699        let data: Vec<u8> = (0..=u8::MAX).cycle().take(high + 2).collect();
1700        let ctx = IoContext::new(io.get_ref());
1701
1702        // Ready
1703        assert_eq!(
1704            lazy(|cx| ctx.poll_read_ready(cx)).await,
1705            Poll::Ready(Readiness::Ready)
1706        );
1707        assert!(io.st().read_task.is_set());
1708        assert!(!io.st().flags.is_read_ready());
1709        assert!(!io.st().flags.is_rd_backpressure());
1710        assert!(!io.is_rd_backpressure());
1711        assert!(!io.is_wr_backpressure());
1712
1713        // == Enable backpressure
1714        ctx.release_read_buf(
1715            BytesMut::copy_from_slice(&data),
1716            Poll::Ready(Ok(data.len())),
1717        );
1718
1719        // dispatcher is woken
1720        assert!(!io.st().dispatch_task.is_set());
1721        // read task is paused
1722        assert!(io.st().flags.is_read_paused());
1723        // read buffer is ready
1724        assert!(io.st().flags.is_read_ready());
1725        // read backpressure is enabled
1726        assert!(io.st().flags.is_rd_backpressure());
1727        assert!(io.is_rd_backpressure());
1728        assert!(!io.is_wr_backpressure());
1729        // read task paused
1730        assert_eq!(lazy(|cx| ctx.poll_read_ready(cx)).await, Poll::Pending);
1731
1732        // read one byte
1733        assert_eq!(io.with_read_dst(|buf| buf.split_to(1)), &data[..1]);
1734        // read buffer is ready
1735        assert!(io.st().flags.is_read_ready());
1736        // read backpressure is enabled
1737        assert!(io.st().flags.is_rd_backpressure());
1738
1739        // read task is set
1740        assert!(io.st().read_task.is_set());
1741
1742        // read one more byte
1743        assert_eq!(io.with_read_dst(|buf| buf.split_to(1)), &data[1..2]);
1744        // read backpressure is enabled
1745        assert!(io.st().flags.is_rd_backpressure());
1746
1747        // dropping below the high watermark does not release backpressure
1748        assert_eq!(io.with_read_dst(|buf| buf.split_to(1)), &data[2..3]);
1749        assert!(io.st().flags.is_rd_backpressure());
1750        assert!(io.st().flags.is_read_paused());
1751
1752        // reaching half of the high watermark releases backpressure
1753        let n = high - 1 - half;
1754        assert_eq!(io.with_read_dst(|buf| buf.split_to(n)), &data[3..3 + n]);
1755        // read task is not paused anymore
1756        assert!(!io.st().flags.is_read_paused());
1757        // read buffer is not ready
1758        assert!(!io.st().flags.is_read_ready());
1759        // read backpressure is disabled
1760        assert!(!io.st().flags.is_rd_backpressure());
1761        // read task is woken
1762        assert!(!io.st().read_task.is_set());
1763        assert_eq!(
1764            lazy(|cx| ctx.poll_read_ready(cx)).await,
1765            Poll::Ready(Readiness::Ready)
1766        );
1767
1768        // register dispatcher task
1769        lazy(|cx| io.register_dispatch(cx)).await;
1770
1771        // == Enable backpressure, half in buffer + half more
1772        ctx.release_read_buf(
1773            BytesMut::copy_from_slice(vec![b'a'; half]),
1774            Poll::Ready(Ok(half)),
1775        );
1776
1777        // dispatcher is woken
1778        assert!(!io.st().dispatch_task.is_set());
1779        // read task is paused
1780        assert!(io.st().flags.is_read_paused());
1781        // read buffer is ready
1782        assert!(io.st().flags.is_read_ready());
1783        // read backpressure is enabled
1784        assert!(io.st().flags.is_rd_backpressure());
1785        // read task paused
1786        assert_eq!(lazy(|cx| ctx.poll_read_ready(cx)).await, Poll::Pending);
1787
1788        // read half, buf size is half of high watermark
1789        assert_eq!(io.with_read_dst(|buf| buf.split_to(half)), &data[3 + n..]);
1790        // read backpressure is disabled
1791        assert!(!io.st().flags.is_rd_backpressure());
1792
1793        // register dispatcher task
1794        lazy(|cx| io.register_dispatch(cx)).await;
1795
1796        // == No backpressure, half in buffer + half - 1 more
1797        ctx.release_read_buf(
1798            BytesMut::copy_from_slice(vec![b'b'; half - 1]),
1799            Poll::Ready(Ok(half - 1)),
1800        );
1801
1802        // read task is paused
1803        assert!(!io.st().flags.is_read_paused());
1804        // read buffer is ready
1805        assert!(io.st().flags.is_read_ready());
1806        // read backpressure is enabled
1807        assert!(!io.st().flags.is_rd_backpressure());
1808        // read task ready
1809        assert_eq!(
1810            lazy(|cx| ctx.poll_read_ready(cx)).await,
1811            Poll::Ready(Readiness::Ready)
1812        );
1813
1814        // read everything
1815        let item = io.with_read_dst(BytesMut::take);
1816        assert_eq!(item.len(), high - 1);
1817        assert_eq!(&item[..half], &vec![b'a'; half][..]);
1818        assert_eq!(&item[half..], &vec![b'b'; half - 1][..]);
1819        // read task is paused
1820        assert!(!io.st().flags.is_read_paused());
1821        // read buffer is ready
1822        assert!(!io.st().flags.is_read_ready());
1823        // read task is not woken
1824        assert!(io.st().read_task.is_set());
1825
1826        // == Terminate
1827        io.terminate();
1828        // read task is woken
1829        assert!(!io.st().read_task.is_set());
1830        // read task ready
1831        assert_eq!(
1832            lazy(|cx| ctx.poll_read_ready(cx)).await,
1833            Poll::Ready(Readiness::Terminate)
1834        );
1835    }
1836
1837    #[ntex::test]
1838    async fn only_force_close_reports_terminate() {
1839        // a transport failure ends the connection, but it is closed gracefully
1840        let io = Io::new(IoTest::create().0, SharedCfg::new("SRV"));
1841        let ctx = IoContext::new(io.get_ref());
1842        ctx.stop(Some(io::Error::other("transport failed")));
1843        assert!(io.st().flags.is_terminating());
1844        assert!(!io.st().flags.is_force_closing());
1845        assert_eq!(
1846            lazy(|cx| ctx.poll_read_ready(cx)).await,
1847            Poll::Ready(Readiness::Close)
1848        );
1849        assert_eq!(
1850            lazy(|cx| ctx.poll_write_ready(cx)).await,
1851            Poll::Ready(Readiness::Close)
1852        );
1853
1854        // a failure during the transport shutdown phase does not abort either
1855        let io = Io::new(IoTest::create().0, SharedCfg::new("SRV"));
1856        let ctx = IoContext::new(io.get_ref());
1857        io.close();
1858        io.st().filters_stopped();
1859        assert!(io.st().flags.is_stopping());
1860        ctx.stop(Some(io::Error::other("transport failed")));
1861        assert_eq!(
1862            lazy(|cx| ctx.poll_write_ready(cx)).await,
1863            Poll::Ready(Readiness::Close)
1864        );
1865
1866        // only an explicit force close aborts
1867        let io = Io::new(IoTest::create().0, SharedCfg::new("SRV"));
1868        let ctx = IoContext::new(io.get_ref());
1869        io.terminate();
1870        assert!(io.st().flags.is_force_closing());
1871        assert_eq!(
1872            lazy(|cx| ctx.poll_read_ready(cx)).await,
1873            Poll::Ready(Readiness::Terminate)
1874        );
1875        assert_eq!(
1876            lazy(|cx| ctx.poll_write_ready(cx)).await,
1877            Poll::Ready(Readiness::Terminate)
1878        );
1879    }
1880
1881    #[ntex::test]
1882    async fn drop_closes_gracefully_once_output_is_flushed() {
1883        // Nothing is left to deliver, so the transport ends the connection with
1884        // a normal FIN and the peer sees a clean end of stream.
1885        let io = Io::new(IoTest::create().0, SharedCfg::new("SRV"));
1886        let ioref = io.get_ref();
1887        let ctx = IoContext::new(io.get_ref());
1888        assert_eq!(io.st().write_outstanding(), 0);
1889        drop(io);
1890        assert!(!ioref.0.flags.is_force_closing());
1891        assert_eq!(
1892            lazy(|cx| ctx.poll_read_ready(cx)).await,
1893            Poll::Ready(Readiness::Close)
1894        );
1895        assert_eq!(
1896            lazy(|cx| ctx.poll_write_ready(cx)).await,
1897            Poll::Ready(Readiness::Close)
1898        );
1899    }
1900
1901    #[ntex::test]
1902    async fn drop_aborts_when_output_would_be_lost() {
1903        // The filter chain goes away with the `Io`, so buffered output can
1904        // never be delivered. Aborting keeps a truncated stream distinguishable
1905        // from a complete one.
1906        let io = Io::new(IoTest::create().0, SharedCfg::new("SRV"));
1907        let ioref = io.get_ref();
1908        let ctx = IoContext::new(io.get_ref());
1909        io.encode_slice(b"not delivered").unwrap();
1910        assert_ne!(io.st().write_outstanding(), 0);
1911        drop(io);
1912        assert!(ioref.0.flags.is_force_closing());
1913        assert_eq!(
1914            lazy(|cx| ctx.poll_read_ready(cx)).await,
1915            Poll::Ready(Readiness::Terminate)
1916        );
1917        assert_eq!(
1918            lazy(|cx| ctx.poll_write_ready(cx)).await,
1919            Poll::Ready(Readiness::Terminate)
1920        );
1921    }
1922
1923    #[ntex::test]
1924    async fn drop_releases_buffers() {
1925        // The state can outlive the `Io`, held by the transport while it closes
1926        // or by other handles, but nothing can use its buffers anymore.
1927        let io = Io::new(IoTest::create().0, SharedCfg::new("SRV"));
1928        let ioref = io.get_ref();
1929        let ctx = IoContext::new(io.get_ref());
1930        ctx.release_read_buf(BytesMut::copy_from_slice(b"unread"), Poll::Ready(Ok(6)));
1931        io.encode_slice(b"not delivered").unwrap();
1932        assert_eq!(ioref.0.buffer.read_dst_size(), 6);
1933        assert_ne!(ioref.0.buffer.write_buf_size(), 0);
1934
1935        drop(io);
1936        assert_eq!(ioref.0.buffer.read_dst_size(), 0);
1937        assert!(ioref.0.buffer.get_read_buf().is_none());
1938        assert_eq!(ioref.0.buffer.write_buf_size(), 0);
1939
1940        // input of a read that was in flight is discarded
1941        ctx.release_read_buf(BytesMut::copy_from_slice(b"late"), Poll::Ready(Ok(4)));
1942        assert!(ioref.0.buffer.get_read_buf().is_none());
1943        ctx.with_read_buf(|buf| {
1944            buf.extend_from_slice(b"late");
1945            Poll::Ready(Ok(4))
1946        });
1947        assert!(ioref.0.buffer.get_read_buf().is_none());
1948        assert_eq!(ioref.0.buffer.read_dst_size(), 0);
1949    }
1950
1951    #[ntex::test]
1952    async fn force_close_survives_filter_replacement() {
1953        // Dropping `Io` swaps the chain for `NullFilter`, which cannot see the
1954        // io state, so an explicit terminate must still be honoured afterwards.
1955        let io = Io::new(IoTest::create().0, SharedCfg::new("SRV"));
1956        let ctx = IoContext::new(io.get_ref());
1957        io.terminate();
1958        drop(io);
1959        assert_eq!(
1960            lazy(|cx| ctx.poll_read_ready(cx)).await,
1961            Poll::Ready(Readiness::Terminate)
1962        );
1963        assert_eq!(
1964            lazy(|cx| ctx.poll_write_ready(cx)).await,
1965            Poll::Ready(Readiness::Terminate)
1966        );
1967    }
1968
1969    #[ntex::test]
1970    async fn read_notify() {
1971        let io = Io::new(
1972            IoTest::create().0,
1973            SharedCfg::new("SRV").add(
1974                IoConfig::default()
1975                    .set_read_size(BytePageSize::Size4, BytePageSize::Size4)
1976                    .set_read_backpressure(BytePageSize::Size4.capacity()),
1977            ),
1978        );
1979        assert!(!io.st().flags.is_read_notify());
1980        assert!(lazy(|cx| io.poll_read_notify(cx)).await.is_pending());
1981        assert!(io.st().dispatch_task.is_set());
1982        assert!(io.st().flags.is_read_notify());
1983
1984        let ctx = IoContext::new(io.get_ref());
1985
1986        // incoming bytes
1987        ctx.release_read_buf(BytesMut::copy_from_slice(b"1"), Poll::Ready(Ok(1)));
1988
1989        assert!(!io.st().dispatch_task.is_set());
1990        // rd buffer is ready
1991        assert!(io.st().flags.is_read_ready());
1992        assert!(io.st().flags.is_read_notify());
1993        // dispatcher is notified
1994        assert!(io.st().flags.is_read_notified());
1995        let res = lazy(|cx| io.poll_read_notify(cx)).await;
1996        assert!(matches!(res, Poll::Ready(Ok(Some(())))));
1997
1998        // disapcher is not set
1999        assert!(!io.st().dispatch_task.is_set());
2000        // rd buffer is ready
2001        assert!(io.st().flags.is_read_ready());
2002
2003        // == start notification process again
2004        assert!(lazy(|cx| io.poll_read_notify(cx)).await.is_pending());
2005        assert!(io.st().dispatch_task.is_set());
2006        assert!(io.st().flags.is_read_notify());
2007        assert!(io.st().flags.is_read_ready());
2008        // read task ready
2009        assert_eq!(
2010            lazy(|cx| ctx.poll_read_ready(cx)).await,
2011            Poll::Ready(Readiness::Ready)
2012        );
2013
2014        // == enable packpressure
2015        let len = BytePageSize::Size4.capacity() - 1;
2016        ctx.release_read_buf(
2017            BytesMut::copy_from_slice(vec![b'2'; len]),
2018            Poll::Ready(Ok(len)),
2019        );
2020        // read backpressure is enabled
2021        assert!(io.st().flags.is_rd_backpressure());
2022
2023        // rd buffer is ready
2024        assert!(io.st().flags.is_read_ready());
2025        assert!(io.st().flags.is_read_notify());
2026        // dispatcher is notified
2027        assert!(io.st().flags.is_read_notified());
2028        let res = lazy(|cx| io.poll_read_notify(cx)).await;
2029        assert!(matches!(res, Poll::Ready(Ok(Some(())))));
2030        // read task paused
2031        assert_eq!(lazy(|cx| ctx.poll_read_ready(cx)).await, Poll::Pending);
2032        // read task is set
2033        assert!(io.st().read_task.is_set());
2034
2035        // == start notification process again
2036        assert!(lazy(|cx| io.poll_read_notify(cx)).await.is_pending());
2037        // read flags active
2038        assert!(!io.st().flags.is_rd_backpressure());
2039        assert!(!io.st().flags.is_read_ready());
2040        assert!(!io.st().flags.is_read_paused());
2041        // read task is woken
2042        assert!(!io.st().read_task.is_set());
2043        // read task ready
2044        assert_eq!(
2045            lazy(|cx| ctx.poll_read_ready(cx)).await,
2046            Poll::Ready(Readiness::Ready)
2047        );
2048
2049        // incoming bytes
2050        ctx.release_read_buf(BytesMut::copy_from_slice(b"1"), Poll::Ready(Ok(1)));
2051        assert!(!io.st().dispatch_task.is_set());
2052        // rd buffer is ready
2053        assert!(io.st().flags.is_read_ready());
2054        assert!(io.st().flags.is_read_notify());
2055        assert!(io.st().flags.is_read_paused());
2056        assert!(io.st().flags.is_rd_backpressure());
2057        // dispatcher is notified
2058        assert!(io.st().flags.is_read_notified());
2059        assert!(matches!(
2060            lazy(|cx| io.poll_read_notify(cx)).await,
2061            Poll::Ready(Ok(Some(())))
2062        ));
2063
2064        // == Terminate
2065        io.terminate();
2066        let res = lazy(|cx| io.poll_read_notify(cx)).await;
2067        assert!(matches!(res, Poll::Ready(Ok(None))), "{res:?}");
2068    }
2069
2070    #[ntex::test]
2071    async fn read_more() {
2072        let (client, server) = IoTest::create();
2073        client.remote_buffer_cap(1024);
2074
2075        let io = Io::from(server);
2076        assert!(lazy(|cx| io.poll_read_more(cx)).await.is_pending());
2077
2078        client.write(TEXT);
2079        assert_eq!(io.read_more().await.unwrap(), Some(()));
2080        assert!(matches!(
2081            lazy(|cx| io.poll_read_more(cx)).await,
2082            Poll::Ready(Ok(Some(())))
2083        ));
2084
2085        let item = io.with_read_dst(BytesMut::take);
2086        assert_eq!(item, Bytes::from_static(BIN));
2087
2088        client.write(TEXT);
2089        sleep(Millis(50)).await;
2090        assert!(lazy(|cx| io.poll_read_more(cx)).await.is_ready());
2091        assert!(lazy(|cx| io.poll_read_more(cx)).await.is_ready());
2092    }
2093
2094    #[ntex::test]
2095    async fn read_backpressure() {
2096        let (client, server) = IoTest::create();
2097
2098        let io = Io::new(
2099            server,
2100            SharedCfg::new("SRV").add(
2101                IoConfig::default()
2102                    .set_read_size(BytePageSize::Size4, BytePageSize::Size4)
2103                    .set_read_backpressure(BytePageSize::Size4.capacity()),
2104            ),
2105        );
2106        assert!(lazy(|cx| io.poll_read_more(cx)).await.is_pending());
2107
2108        client.write(vec![b'x'; BytePageSize::Size4.capacity()]);
2109        sleep(Millis(50)).await;
2110        assert!(io.flags().is_read_ready());
2111        assert!(io.flags().is_rd_backpressure());
2112        let _item = io.recv(&BytesCodec).await.ok().unwrap().unwrap();
2113        assert!(!io.flags().is_read_ready());
2114        assert!(!io.flags().is_rd_backpressure());
2115
2116        client.write(vec![b'x'; BytePageSize::Size4.capacity()]);
2117        sleep(Millis(50)).await;
2118        assert!(io.flags().is_read_ready());
2119        assert!(io.flags().is_rd_backpressure());
2120        assert_eq!(io.read_more().await.unwrap(), Some(()));
2121    }
2122
2123    #[ntex::test]
2124    async fn read_src_releases_read_backpressure() {
2125        let (client, server) = IoTest::create();
2126
2127        let io = Io::new(
2128            server,
2129            SharedCfg::new("SRV").add(
2130                IoConfig::default()
2131                    .set_read_size(BytePageSize::Size4, BytePageSize::Size4)
2132                    .set_read_backpressure(BytePageSize::Size4.capacity()),
2133            ),
2134        );
2135        assert!(lazy(|cx| io.poll_read_more(cx)).await.is_pending());
2136
2137        client.write(vec![b'x'; BytePageSize::Size4.capacity()]);
2138        sleep(Millis(50)).await;
2139        assert!(io.flags().is_rd_backpressure());
2140
2141        // On a filterless Io the transport-facing source aliases the
2142        // application-facing read destination.
2143        let len = io.get_ref().with_read_src(|buf| {
2144            let len = buf.len();
2145            buf.clear();
2146            len
2147        });
2148        assert!(len > 0);
2149        assert!(!io.flags().is_rd_backpressure());
2150        assert!(!io.flags().is_read_paused());
2151
2152        // reads resume
2153        client.write(BIN2);
2154        sleep(Millis(50)).await;
2155        assert!(io.flags().is_read_ready());
2156    }
2157
2158    #[ntex::test]
2159    async fn with_buf_releases_read_backpressure() {
2160        let (client, server) = IoTest::create();
2161
2162        let io = Io::new(
2163            server,
2164            SharedCfg::new("SRV").add(
2165                IoConfig::default()
2166                    .set_read_size(BytePageSize::Size4, BytePageSize::Size4)
2167                    .set_read_backpressure(BytePageSize::Size4.capacity()),
2168            ),
2169        );
2170        assert!(lazy(|cx| io.poll_read_more(cx)).await.is_pending());
2171
2172        client.write(vec![b'x'; BytePageSize::Size4.capacity()]);
2173        sleep(Millis(50)).await;
2174        assert!(io.flags().is_rd_backpressure());
2175
2176        let len = io
2177            .get_ref()
2178            .with_buf(|buf| {
2179                buf.with_read_buffers(|_, dst| {
2180                    let len = dst.len();
2181                    dst.clear();
2182                    len
2183                })
2184            })
2185            .unwrap();
2186        assert!(len > 0);
2187        assert!(!io.flags().is_rd_backpressure());
2188        assert!(!io.flags().is_read_paused());
2189
2190        // reads resume
2191        client.write(BIN2);
2192        sleep(Millis(50)).await;
2193        assert!(io.flags().is_read_ready());
2194    }
2195
2196    #[ntex::test]
2197    async fn write() {
2198        let io = Io::new(
2199            IoTest::create().0,
2200            SharedCfg::new("SRV").add(IoConfig::default().set_write_backpressure(8)),
2201        );
2202        assert!(lazy(|cx| io.poll_status_update(cx)).await.is_pending());
2203        assert!(io.st().dispatch_task.is_set());
2204        assert!(io.st().flags.is_direct_wr_enabled());
2205
2206        let ctx = IoContext::new(io.get_ref());
2207
2208        // == No write work
2209        assert_eq!(lazy(|cx| ctx.poll_write_ready(cx)).await, Poll::Pending);
2210        assert!(io.st().write_task.is_set());
2211        assert!(io.st().flags.is_write_paused());
2212        assert!(!io.st().flags.is_wr_backpressure());
2213
2214        // write
2215        io.with_write_src(|buf| buf.put_slice(b"1234")).unwrap();
2216        assert_eq!(lazy(|cx| ctx.poll_write_ready(cx)).await, Poll::Pending);
2217        // write task is paused
2218        assert!(io.st().flags.is_write_paused());
2219        // send-buf op is scheduled
2220        assert!(io.st().flags.is_wr_send_scheduled());
2221        // back-pressure is not enabled
2222        assert!(!io.st().flags.is_wr_backpressure());
2223        // dispatch is not woken up
2224        assert!(io.st().dispatch_task.is_set());
2225
2226        // == enable wr backpressure
2227        io.with_write_src(|buf| buf.put_slice(b"5678")).unwrap();
2228        // back-pressure is enabled
2229        assert!(io.st().flags.is_wr_backpressure());
2230        // dispatch is woken up
2231        assert!(!io.st().dispatch_task.is_set());
2232        // write task is set
2233        assert!(io.st().write_task.is_set());
2234        // dispatcher gets WriteBackpressure
2235        assert!(matches!(
2236            lazy(|cx| io.poll_status_update(cx)).await,
2237            Poll::Ready(IoStatusUpdate::WriteBackpressure)
2238        ));
2239        // flush write buffer
2240        assert!(lazy(|cx| io.poll_flush(cx, false)).await.is_pending());
2241        // full flush is not enabled
2242        assert!(!io.st().flags.is_write_flush());
2243
2244        // run send-buf ops
2245        Iops::run();
2246        // send-buf op is not scheduled
2247        assert!(!io.st().flags.is_wr_send_scheduled());
2248        // write task is not paused
2249        assert!(!io.st().flags.is_write_paused());
2250        // write task has been woken up
2251        assert!(!io.st().write_task.is_set());
2252        // write task can proceed
2253        assert_eq!(
2254            lazy(|cx| ctx.poll_write_ready(cx)).await,
2255            Poll::Ready(Readiness::Ready)
2256        );
2257
2258        // wrote 4 bytes to io
2259        assert_eq!(ctx.with_write_dst(|buf| buf.split_to(4).freeze()), b"1234");
2260        // continue to write
2261        assert_eq!(ctx.update_write_status(Ok(4)), IoTaskStatus::Io);
2262        // write task can proceed
2263        assert_eq!(
2264            lazy(|cx| ctx.poll_write_ready(cx)).await,
2265            Poll::Ready(Readiness::Ready)
2266        );
2267        // write task is not paused
2268        assert!(!io.st().flags.is_write_paused());
2269        // back-pressure is enabled
2270        assert!(io.st().flags.is_wr_backpressure());
2271        // the write buf dropped below the release threshold, back-pressure is
2272        // released and no further WriteBackpressure is reported
2273        assert!(lazy(|cx| io.poll_status_update(cx)).await.is_pending());
2274        // back-pressure is disabled
2275        assert!(!io.st().flags.is_wr_backpressure());
2276        assert!(lazy(|cx| io.poll_status_update(cx)).await.is_pending());
2277        // write buffer is flushed
2278        assert!(matches!(
2279            lazy(|cx| io.poll_flush(cx, false)).await,
2280            Poll::Ready(Ok(()))
2281        ));
2282
2283        // full flush write buffer
2284        io.with_write_src(|buf| buf.put_slice(b"1234")).unwrap();
2285        assert!(lazy(|cx| io.poll_flush(cx, true)).await.is_pending());
2286        // full flush is enabled
2287        assert!(io.st().flags.is_write_flush());
2288        // back-pressure is enabled
2289        assert!(io.st().flags.is_wr_backpressure());
2290
2291        // wrote all data
2292        Iops::run();
2293        assert_eq!(ctx.with_write_dst(BytePages::freeze), b"56781234");
2294        // write task is not paused, so send-buf op is not scheduled
2295        assert!(!io.st().flags.is_wr_send_scheduled());
2296        // update status, no more work
2297        assert_eq!(ctx.update_write_status(Ok(8)), IoTaskStatus::Pause);
2298        // write task is paused
2299        assert!(io.st().flags.is_write_paused());
2300        // flush is still enabled
2301        assert!(io.st().flags.is_write_flush());
2302        // back-pressure is still enabled
2303        assert!(io.st().flags.is_wr_backpressure());
2304        // dispatch is woken up
2305        assert!(!io.st().dispatch_task.is_set());
2306
2307        // write buffer is flushed
2308        assert!(matches!(
2309            lazy(|cx| io.poll_flush(cx, false)).await,
2310            Poll::Ready(Ok(()))
2311        ));
2312        // full flush is disabled
2313        assert!(!io.st().flags.is_write_flush());
2314        // back-pressure is disabled
2315        assert!(!io.st().flags.is_wr_backpressure());
2316
2317        // == Terminate
2318        io.terminate();
2319        // read task is woken
2320        assert!(!io.st().write_task.is_set());
2321        // write task ready
2322        assert_eq!(
2323            lazy(|cx| ctx.poll_write_ready(cx)).await,
2324            Poll::Ready(Readiness::Terminate)
2325        );
2326        // flush returns error
2327        let Poll::Ready(Err(err)) = lazy(|cx| io.poll_flush(cx, false)).await else {
2328            panic!()
2329        };
2330        assert_eq!(err.kind(), io::ErrorKind::NotConnected);
2331        // statis returns error
2332        assert!(matches!(
2333            lazy(|cx| io.poll_status_update(cx)).await,
2334            Poll::Ready(IoStatusUpdate::PeerGone(None))
2335        ));
2336    }
2337
2338    #[ntex::test]
2339    async fn local_shutdown_reports_peer_gone_without_error() {
2340        let (client, server) = IoTest::create();
2341        client.remote_buffer_cap(1024);
2342        let io = Io::from(server);
2343
2344        // purely local graceful shutdown, the peer does nothing
2345        io.shutdown().await.unwrap();
2346
2347        assert!(!io.is_active());
2348        assert!(matches!(
2349            lazy(|cx| io.poll_status_update(cx)).await,
2350            Poll::Ready(IoStatusUpdate::PeerGone(None))
2351        ));
2352    }
2353
2354    #[ntex::test]
2355    async fn set_config_updates_eager_write_support() {
2356        let io = Io::new(
2357            IoTest::create().0,
2358            SharedCfg::new("SRV").add(IoConfig::new().set_write_buf_threshold(0)),
2359        );
2360        assert!(!io.st().flags.is_direct_wr_enabled());
2361
2362        // SAFETY: no reference returned by `io.cfg()` is retained.
2363        unsafe {
2364            io.set_config(SharedCfg::new("SRV").add(IoConfig::new().set_write_buf_threshold(1024)));
2365        }
2366        assert!(io.st().flags.is_direct_wr_enabled());
2367        assert_eq!(io.cfg().write_buf_threshold(), 1024);
2368
2369        // SAFETY: the previous `io.cfg()` reference was limited to the
2370        // assertion statement and is no longer live.
2371        unsafe {
2372            io.set_config(SharedCfg::new("SRV").add(IoConfig::new().set_write_buf_threshold(0)));
2373        }
2374        assert!(!io.st().flags.is_direct_wr_enabled());
2375        assert_eq!(io.cfg().write_buf_threshold(), 0);
2376    }
2377
2378    #[ntex::test]
2379    async fn eager_write_uses_updated_buffer_size() {
2380        #[derive(Debug)]
2381        struct DirectWrite;
2382
2383        impl IoStream for DirectWrite {
2384            fn start(self, _: IoContext) -> Box<dyn Handle> {
2385                Box::new(self)
2386            }
2387        }
2388
2389        impl Handle for DirectWrite {
2390            fn write(&self, ctx: &IoContext) {
2391                let n = ctx.with_write_dst(|buf| {
2392                    let n = buf.len();
2393                    buf.clear();
2394                    n
2395                });
2396                let _ = ctx.update_write_status(Ok(n));
2397            }
2398        }
2399
2400        let io = Io::new(
2401            DirectWrite,
2402            SharedCfg::new("SRV").add(
2403                IoConfig::new()
2404                    .set_write_buf_threshold(1)
2405                    .set_write_backpressure(8),
2406            ),
2407        );
2408
2409        io.encode_slice(BIN2).unwrap();
2410
2411        assert_eq!(io.st().buffer.write_buf_size(), 0);
2412        assert!(io.flags().is_write_paused());
2413        assert!(!io.flags().is_wr_backpressure());
2414        assert!(!io.st().flags.is_wr_send_scheduled());
2415    }
2416
2417    #[ntex::test]
2418    async fn eager_write_reports_transport_error() {
2419        #[derive(Debug)]
2420        struct FailedWrite;
2421
2422        impl IoStream for FailedWrite {
2423            fn start(self, _: IoContext) -> Box<dyn Handle> {
2424                Box::new(self)
2425            }
2426        }
2427
2428        impl Handle for FailedWrite {
2429            fn write(&self, ctx: &IoContext) {
2430                ctx.update_write_status(Err(io::Error::new(
2431                    io::ErrorKind::ConnectionReset,
2432                    "connection reset",
2433                )));
2434            }
2435        }
2436
2437        let io = Io::new(
2438            FailedWrite,
2439            SharedCfg::new("SRV").add(IoConfig::new().set_write_buf_threshold(1)),
2440        );
2441
2442        let err = io.encode_slice(BIN2).unwrap_err();
2443        assert_eq!(err.kind(), io::ErrorKind::ConnectionReset);
2444        assert_eq!(err.to_string(), "connection reset");
2445        assert!(io.st().flags.is_terminating());
2446    }
2447
2448    #[ntex::test]
2449    async fn terminate_during_eager_write_releases_transport() {
2450        #[derive(Debug)]
2451        struct FailedWrite(Rc<Cell<bool>>);
2452
2453        impl Drop for FailedWrite {
2454            fn drop(&mut self) {
2455                self.0.set(true);
2456            }
2457        }
2458
2459        impl IoStream for FailedWrite {
2460            fn start(self, _: IoContext) -> Box<dyn Handle> {
2461                Box::new(self)
2462            }
2463        }
2464
2465        impl Handle for FailedWrite {
2466            fn write(&self, ctx: &IoContext) {
2467                ctx.update_write_status(Err(io::Error::new(
2468                    io::ErrorKind::ConnectionReset,
2469                    "connection reset",
2470                )));
2471            }
2472        }
2473
2474        let dropped = Rc::new(Cell::new(false));
2475        let io = Io::new(
2476            FailedWrite(dropped.clone()),
2477            SharedCfg::new("SRV").add(IoConfig::new().set_write_buf_threshold(1)),
2478        );
2479
2480        io.encode_slice(BIN2).unwrap_err();
2481        assert!(io.st().flags.is_terminating());
2482
2483        // the handle is taken for the duration of the direct write, so the
2484        // terminate it triggered could not release the transport itself
2485        assert!(dropped.get());
2486        assert!(io.st().handle.take().is_none());
2487    }
2488
2489    #[ntex::test]
2490    async fn write_backpressure() {
2491        let (client, server) = IoTest::create();
2492        client.remote_buffer_cap(0);
2493
2494        let io = Io::new(
2495            server,
2496            SharedCfg::new("SRV").add(IoConfig::default().set_write_backpressure(16)),
2497        );
2498        assert!(lazy(|cx| io.poll_read_more(cx)).await.is_pending());
2499        assert!(io.flags().is_write_paused());
2500        assert!(!io.flags().is_wr_backpressure());
2501        assert!(!io.is_wr_backpressure());
2502
2503        io.encode_slice(BIN2).unwrap();
2504        assert!(Iops::is_registered(&io));
2505        assert!(io.flags().is_wr_backpressure());
2506
2507        client.remote_buffer_cap(1024);
2508        let item = client.read().await.unwrap();
2509        assert_eq!(item, BIN2);
2510        assert!(io.flags().is_wr_backpressure());
2511        // the write buf drained, back-pressure is released and no status
2512        // update is reported, same as poll_flush() below
2513        assert!(lazy(|cx| io.poll_status_update(cx)).await.is_pending());
2514        assert!(!io.flags().is_wr_backpressure());
2515        assert!(matches!(
2516            lazy(|cx| io.poll_flush(cx, false)).await,
2517            Poll::Ready(Ok(()))
2518        ));
2519        assert!(!io.flags().is_wr_backpressure());
2520    }
2521
2522    #[ntex::test]
2523    async fn partial_flush_keeps_write_backpressure_until_half_watermark() {
2524        let io = Io::new(
2525            IoTest::create().0,
2526            SharedCfg::new("SRV").add(IoConfig::default().set_write_backpressure(8)),
2527        );
2528        let ctx = IoContext::new(io.get_ref());
2529
2530        io.encode_slice(b"12345678").unwrap();
2531        assert!(io.flags().is_wr_backpressure());
2532
2533        assert_eq!(ctx.with_write_dst(|buf| buf.split_to(1).len()), 1);
2534        assert_eq!(ctx.update_write_status(Ok(1)), IoTaskStatus::Io);
2535        assert!(lazy(|cx| io.poll_flush(cx, false)).await.is_pending());
2536        assert!(io.flags().is_wr_backpressure());
2537
2538        assert_eq!(ctx.with_write_dst(|buf| buf.split_to(3).len()), 3);
2539        assert_eq!(ctx.update_write_status(Ok(3)), IoTaskStatus::Io);
2540        assert!(matches!(
2541            lazy(|cx| io.poll_flush(cx, false)).await,
2542            Poll::Ready(Ok(()))
2543        ));
2544        assert!(!io.flags().is_wr_backpressure());
2545    }
2546
2547    #[ntex::test]
2548    async fn write_ready_waits_for_release_threshold() {
2549        let io = Io::new(
2550            IoTest::create().0,
2551            SharedCfg::new("SRV").add(IoConfig::default().set_write_backpressure(8)),
2552        );
2553        let ctx = IoContext::new(io.get_ref());
2554
2555        // no back-pressure
2556        assert!(io.write_ready().await.is_ok());
2557
2558        io.encode_slice(b"12345678").unwrap();
2559        assert!(io.flags().is_wr_backpressure());
2560
2561        // several producers wait at once
2562        let done = Rc::new(Cell::new(0));
2563        for _ in 0..2 {
2564            let (io, done) = (io.get_ref(), done.clone());
2565            ntex_util::spawn(async move {
2566                io.write_ready().await.unwrap();
2567                done.set(done.get() + 1);
2568            });
2569        }
2570        sleep(Millis(10)).await;
2571        assert_eq!(done.get(), 0);
2572
2573        // above the release threshold
2574        assert_eq!(ctx.with_write_dst(|buf| buf.split_to(1).len()), 1);
2575        assert_eq!(ctx.update_write_status(Ok(1)), IoTaskStatus::Io);
2576        sleep(Millis(10)).await;
2577        assert_eq!(done.get(), 0);
2578
2579        // the write task wakes the producers, the dispatcher does not run
2580        assert_eq!(ctx.with_write_dst(|buf| buf.split_to(3).len()), 3);
2581        assert_eq!(ctx.update_write_status(Ok(3)), IoTaskStatus::Io);
2582        sleep(Millis(10)).await;
2583        assert_eq!(done.get(), 2);
2584        assert!(io.flags().is_wr_backpressure());
2585        assert!(io.write_ready().await.is_ok());
2586    }
2587
2588    #[ntex::test]
2589    async fn write_ready_released_during_full_flush() {
2590        let io = Io::new(
2591            IoTest::create().0,
2592            SharedCfg::new("SRV").add(IoConfig::default().set_write_backpressure(8)),
2593        );
2594        let ctx = IoContext::new(io.get_ref());
2595
2596        io.encode_slice(b"12345678").unwrap();
2597        assert!(lazy(|cx| io.poll_flush(cx, true)).await.is_pending());
2598        assert!(io.flags().is_write_flush());
2599
2600        let done = Rc::new(Cell::new(false));
2601        let (io2, done2) = (io.get_ref(), done.clone());
2602        ntex_util::spawn(async move {
2603            io2.write_ready().await.unwrap();
2604            done2.set(true);
2605        });
2606        sleep(Millis(10)).await;
2607        assert!(!done.get());
2608
2609        assert_eq!(ctx.with_write_dst(|buf| buf.split_to(4).len()), 4);
2610        assert_eq!(ctx.update_write_status(Ok(4)), IoTaskStatus::Io);
2611        sleep(Millis(10)).await;
2612        assert!(done.get());
2613    }
2614
2615    #[ntex::test]
2616    async fn write_ready_fails_on_disconnect() {
2617        let (client, server) = IoTest::create();
2618        client.remote_buffer_cap(0);
2619        let io = Io::new(
2620            server,
2621            SharedCfg::new("SRV").add(IoConfig::default().set_write_backpressure(8)),
2622        );
2623        io.encode_slice(b"12345678").unwrap();
2624        assert!(io.flags().is_wr_backpressure());
2625
2626        let res = Rc::new(Cell::new(None));
2627        let (io2, res2) = (io.get_ref(), res.clone());
2628        ntex_util::spawn(async move {
2629            res2.set(Some(io2.write_ready().await.is_err()));
2630        });
2631        sleep(Millis(10)).await;
2632        assert_eq!(res.get(), None);
2633
2634        io.terminate();
2635        sleep(Millis(10)).await;
2636        assert_eq!(res.get(), Some(true));
2637        assert!(io.write_ready().await.is_err());
2638    }
2639
2640    #[ntex::test]
2641    async fn waiter() {
2642        let (client, server) = IoTest::create();
2643        let io = Io::from(server);
2644        let (mut s1, mut s2, mut s3) = (Waiter::new(&io, 7), Waiter::new(&io, 7), io.waiter(8));
2645        assert!(lazy(|cx| Pin::new(&mut s1).poll(cx)).await.is_pending());
2646        assert!(lazy(|cx| Pin::new(&mut s2).poll(cx)).await.is_pending());
2647        assert!(lazy(|cx| Pin::new(&mut s3).poll(cx)).await.is_pending());
2648        assert!(lazy(|cx| Pin::new(&mut s1).poll(cx)).await.is_pending());
2649        assert_eq!(io.get_ref().0.extensions.wakers_len(), 3);
2650
2651        io.wake(7);
2652        assert!(lazy(|cx| Pin::new(&mut s1).poll(cx)).await.is_ready());
2653        assert!(lazy(|cx| Pin::new(&mut s2).poll(cx)).await.is_ready());
2654        assert!(lazy(|cx| Pin::new(&mut s3).poll(cx)).await.is_pending());
2655        assert_eq!(io.get_ref().0.extensions.wakers_len(), 1);
2656
2657        drop(s3);
2658        assert_eq!(io.get_ref().0.extensions.wakers_len(), 0);
2659
2660        // a static waiter keeps the registration
2661        assert!(lazy(|cx| Pin::new(&mut s1).poll(cx)).await.is_pending());
2662        let mut s1 = s1.into_static();
2663        assert_eq!(io.get_ref().0.extensions.wakers_len(), 1);
2664
2665        // a cloned waiter is not registered
2666        let s4 = s1.clone();
2667        assert_eq!(io.get_ref().0.extensions.wakers_len(), 1);
2668        drop(s4);
2669        assert_eq!(io.get_ref().0.extensions.wakers_len(), 1);
2670
2671        // a waiter is woken on disconnect
2672        let res = Rc::new(Cell::new(false));
2673        let res2 = res.clone();
2674        ntex_util::spawn(async move {
2675            (&mut s1).await;
2676            res2.set(true);
2677        });
2678        sleep(Millis(10)).await;
2679        assert_eq!(io.get_ref().0.extensions.wakers_len(), 1);
2680        client.close().await;
2681        timeout(Millis(1000), io.shutdown())
2682            .await
2683            .expect("stream shutdown did not complete")
2684            .unwrap();
2685        sleep(Millis(10)).await;
2686        assert!(res.get(), "waiter was not woken on disconnect");
2687        (&mut s2).await;
2688        drop(s2);
2689        assert_eq!(io.get_ref().0.extensions.wakers_len(), 0);
2690    }
2691
2692    #[ntex::test]
2693    async fn waiter_poll_ready() {
2694        let (_client, server) = IoTest::create();
2695        let io = Io::from(server);
2696        let waiter = io.waiter(3);
2697        let ext = &io.get_ref().0.extensions;
2698
2699        // not registered, the wake is missed
2700        io.wake(3);
2701        assert!(lazy(|cx| waiter.poll_ready(cx)).await.is_pending());
2702        assert!(lazy(|cx| waiter.poll_ready(cx)).await.is_pending());
2703        assert_eq!(ext.wakers_len(), 1);
2704
2705        // a wake between polls is reported once, then the waiter registers again
2706        io.wake(3);
2707        assert!(lazy(|cx| waiter.poll_ready(cx)).await.is_ready());
2708        assert_eq!(ext.wakers_len(), 0);
2709        assert!(lazy(|cx| waiter.poll_ready(cx)).await.is_pending());
2710        assert_eq!(ext.wakers_len(), 1);
2711
2712        io.wake(3);
2713        waiter.await;
2714        assert_eq!(ext.wakers_len(), 0);
2715    }
2716
2717    #[ntex::test]
2718    async fn wake_reserved_tag_is_ignored() {
2719        let (_client, server) = IoTest::create();
2720        let io = Io::from(server);
2721        let mut waiter = io.on_disconnect();
2722        assert!(lazy(|cx| Pin::new(&mut waiter).poll(cx)).await.is_pending());
2723        io.wake(TAG_DISCONNECT);
2724        io.wake(TAG_WRITE);
2725        assert!(lazy(|cx| Pin::new(&mut waiter).poll(cx)).await.is_pending());
2726        assert_eq!(io.get_ref().0.extensions.wakers_len(), 1);
2727    }
2728
2729    #[cfg(debug_assertions)]
2730    #[ntex::test]
2731    #[should_panic(expected = "reserved")]
2732    async fn waiter_reserved_tag() {
2733        let (_client, server) = IoTest::create();
2734        let io = Io::from(server);
2735        let _waiter = Waiter::new(&io, TAG_DISCONNECT);
2736    }
2737
2738    #[cfg(debug_assertions)]
2739    #[ntex::test]
2740    #[should_panic(expected = "reserved")]
2741    async fn waiter_reserved_tag_ioref() {
2742        let (_client, server) = IoTest::create();
2743        let io = Io::from(server);
2744        let _waiter = io.waiter(TAG_WRITE);
2745    }
2746
2747    #[ntex::test]
2748    async fn dropped_waiters_are_removed() {
2749        let (client, server) = IoTest::create();
2750        client.remote_buffer_cap(0);
2751        let io = Io::new(
2752            server,
2753            SharedCfg::new("SRV").add(IoConfig::default().set_write_backpressure(8)),
2754        );
2755        let ext = &io.get_ref().0.extensions;
2756
2757        for _ in 0..4 {
2758            let waiter = io.on_disconnect();
2759            assert!(lazy(|cx| waiter.poll_ready(cx)).await.is_pending());
2760            assert!(lazy(|cx| waiter.poll_ready(cx)).await.is_pending());
2761            assert_eq!(ext.wakers_len(), 1);
2762        }
2763        assert_eq!(ext.wakers_len(), 0);
2764
2765        io.encode_slice(b"12345678").unwrap();
2766        assert!(io.flags().is_wr_backpressure());
2767        for _ in 0..4 {
2768            let mut fut = std::pin::pin!(io.write_ready());
2769            assert!(lazy(|cx| fut.as_mut().poll(cx)).await.is_pending());
2770            assert!(lazy(|cx| fut.as_mut().poll(cx)).await.is_pending());
2771            assert_eq!(ext.wakers_len(), 1);
2772        }
2773        assert_eq!(ext.wakers_len(), 0);
2774
2775        // woken waiters are removed
2776        let waiter = io.on_disconnect();
2777        assert!(lazy(|cx| waiter.poll_ready(cx)).await.is_pending());
2778        let mut fut = std::pin::pin!(io.write_ready());
2779        assert!(lazy(|cx| fut.as_mut().poll(cx)).await.is_pending());
2780        assert_eq!(ext.wakers_len(), 2);
2781        io.terminate();
2782        assert_eq!(ext.wakers_len(), 1);
2783        assert!(lazy(|cx| fut.as_mut().poll(cx)).await.is_ready());
2784
2785        // the backend reports the teardown
2786        sleep(Millis(50)).await;
2787        assert_eq!(ext.wakers_len(), 0);
2788        assert!(lazy(|cx| waiter.poll_ready(cx)).await.is_ready());
2789    }
2790
2791    #[test]
2792    fn woken_waiter_slot_is_reused() {
2793        let ext = Extensions::default();
2794        let waker = std::task::Waker::noop();
2795        let (a, b) = (WaiterEntry::new(TAG_WRITE), WaiterEntry::new(TAG_WRITE));
2796
2797        ext.register_waker(&a, waker);
2798        ext.wake(TAG_WRITE);
2799
2800        // `b` reuses the slot of the woken `a`
2801        ext.register_waker(&b, waker);
2802        ext.register_waker(&a, waker);
2803        assert_eq!(ext.wakers_len(), 2);
2804        ext.remove_waker(&a);
2805        assert_eq!(ext.wakers_len(), 1);
2806
2807        // the removed stale id of `a` does not touch `b`
2808        ext.wake(TAG_WRITE);
2809        ext.register_waker(&b, waker);
2810        ext.register_waker(&a, waker);
2811        ext.wake(TAG_WRITE);
2812        ext.register_waker(&b, waker);
2813        ext.remove_waker(&a);
2814        assert_eq!(ext.wakers_len(), 1);
2815        ext.remove_waker(&b);
2816        assert_eq!(ext.wakers_len(), 0);
2817    }
2818
2819    #[ntex::test]
2820    async fn full_flush_waits_for_inflight_write() {
2821        // A completion based transport takes ownership of the write pages, so
2822        // the write buffer empties before the bytes reach the peer. A full
2823        // flush must not complete until they have.
2824        let io = Io::new(
2825            IoTest::create().0,
2826            SharedCfg::new("SRV").add(IoConfig::default()),
2827        );
2828        let ctx = IoContext::new(io.get_ref());
2829
2830        io.encode_slice(b"12345678").unwrap();
2831
2832        let page = ctx.with_write_dst(BytePages::take).unwrap();
2833        assert_eq!(page.len(), 8);
2834        // the buffer is empty, the output is in flight
2835        assert_eq!(io.st().buffer.write_buf_size(), 0);
2836        assert_eq!(io.st().write_outstanding(), 8);
2837
2838        assert!(lazy(|cx| io.poll_flush(cx, true)).await.is_pending());
2839
2840        // the transport reports the write
2841        assert_eq!(ctx.update_write_status(Ok(page.len())), IoTaskStatus::Pause);
2842        assert_eq!(io.st().write_outstanding(), 0);
2843        assert!(matches!(
2844            lazy(|cx| io.poll_flush(cx, true)).await,
2845            Poll::Ready(Ok(()))
2846        ));
2847    }
2848
2849    #[ntex::test]
2850    async fn write_backpressure_counts_inflight_output() {
2851        // Output owned by the transport is still outstanding, so it keeps
2852        // back-pressure in place.
2853        let io = Io::new(
2854            IoTest::create().0,
2855            SharedCfg::new("SRV").add(IoConfig::default().set_write_backpressure(8)),
2856        );
2857        let ctx = IoContext::new(io.get_ref());
2858
2859        io.encode_slice(b"12345678").unwrap();
2860        assert!(io.flags().is_wr_backpressure());
2861
2862        let page = ctx.with_write_dst(BytePages::take).unwrap();
2863        assert_eq!(io.st().buffer.write_buf_size(), 0);
2864
2865        // nothing reached the peer yet, so back-pressure stays enabled
2866        assert!(lazy(|cx| io.poll_flush(cx, false)).await.is_pending());
2867        assert!(io.flags().is_wr_backpressure());
2868
2869        assert_eq!(ctx.update_write_status(Ok(page.len())), IoTaskStatus::Pause);
2870        assert!(matches!(
2871            lazy(|cx| io.poll_flush(cx, false)).await,
2872            Poll::Ready(Ok(()))
2873        ));
2874        assert!(!io.flags().is_wr_backpressure());
2875    }
2876
2877    #[ntex::test]
2878    async fn transport_shutdown_waits_for_inflight_write() {
2879        // The transport shutdown phase reports `Close` once the output has
2880        // been drained. Output the transport already owns has not been
2881        // drained until it is reported as written.
2882        let io = Io::new(
2883            IoTest::create().0,
2884            SharedCfg::new("SRV").add(IoConfig::default()),
2885        );
2886        let ctx = IoContext::new(io.get_ref());
2887
2888        io.encode_slice(b"12345678").unwrap();
2889        let page = ctx.with_write_dst(BytePages::take).unwrap();
2890
2891        // enter the transport shutdown phase
2892        io.st().flags.enter_filters_stopping();
2893        io.st().filters_stopped();
2894        assert!(io.st().flags.is_stopping());
2895
2896        // nothing left to submit, but the output is still in flight
2897        assert_eq!(lazy(|cx| ctx.poll_write_ready(cx)).await, Poll::Pending);
2898
2899        assert_eq!(ctx.update_write_status(Ok(page.len())), IoTaskStatus::Pause);
2900        assert_eq!(
2901            lazy(|cx| ctx.poll_write_ready(cx)).await,
2902            Poll::Ready(Readiness::Close)
2903        );
2904    }
2905
2906    #[ntex::test]
2907    async fn shutdown_flushes_write_buf_with_read_backpressure() {
2908        // Graceful shutdown must flush the pending write buffer even if
2909        // the peer keeps sending data (read backpressure is enabled).
2910        let (client, server) = IoTest::create();
2911        // remote side does not accept any data yet, write task stalls
2912        client.remote_buffer_cap(0);
2913
2914        let io = Io::new(
2915            server,
2916            SharedCfg::new("SRV").add(
2917                IoConfig::default()
2918                    .set_read_size(BytePageSize::Size4, BytePageSize::Size4)
2919                    .set_read_backpressure(BytePageSize::Size4.capacity())
2920                    .set_shutdown_timeout(ntex_util::time::Seconds(2)),
2921            ),
2922        );
2923
2924        // queue response data; remote is stalled so it stays in the write buffer
2925        io.encode_slice(b"response-tail").unwrap();
2926        sleep(Millis(50)).await;
2927        assert_eq!(io.st().buffer.write_buf_size(), 13);
2928
2929        // peer keeps sending, crossing the read high watermark,
2930        // read task gets paused with back-pressure enabled
2931        client.write(vec![b'x'; BytePageSize::Size4.capacity()]);
2932        sleep(Millis(50)).await;
2933        assert!(io.flags().is_read_paused());
2934        assert!(io.flags().is_rd_backpressure());
2935        assert_eq!(io.st().buffer.write_buf_size(), 13);
2936
2937        // start graceful shutdown while the write buffer is not empty
2938        io.close();
2939        sleep(Millis(50)).await;
2940
2941        // peer starts draining the connection
2942        client.remote_buffer_cap(1024);
2943
2944        // all previously queued data must be written before io stream is closed.
2945        // Without the fix graceful shutdown completes immediately (read is paused
2946        // with back-pressure), dropping the buffered write data, so the read here
2947        // returns nothing instead of the queued response tail.
2948        let data = ntex_util::time::timeout(Millis(2000), client.read())
2949            .await
2950            .expect("write buffer was dropped during shutdown")
2951            .unwrap();
2952        assert_eq!(&data[..], b"response-tail");
2953
2954        // the connection still closes gracefully afterwards (within the
2955        // shutdown timeout) instead of hanging
2956        ntex_util::time::timeout(Millis(4000), io.on_disconnect())
2957            .await
2958            .expect("io stream did not disconnect after flush");
2959    }
2960
2961    #[ntex::test]
2962    async fn peer_eof_allows_response_before_shutdown() {
2963        let (client, server) = IoTest::create();
2964        client.remote_buffer_cap(1024);
2965        let io = Io::from(server);
2966
2967        client.write("request");
2968        client.close().await;
2969
2970        assert_eq!(
2971            timeout(Millis(1000), io.recv(&BytesCodec))
2972                .await
2973                .expect("request was not decoded")
2974                .unwrap(),
2975            Some(Bytes::from_static(b"request"))
2976        );
2977        assert!(
2978            timeout(Millis(1000), io.recv(&BytesCodec))
2979                .await
2980                .expect("EOF was not reported")
2981                .unwrap()
2982                .is_none()
2983        );
2984        assert!(!io.st().flags.is_closed());
2985
2986        io.encode(Bytes::from_static(b"response"), &BytesCodec)
2987            .unwrap();
2988        timeout(Millis(1000), io.shutdown())
2989            .await
2990            .expect("shutdown did not complete")
2991            .unwrap();
2992
2993        assert_eq!(
2994            timeout(Millis(1000), client.read())
2995                .await
2996                .expect("response was not flushed")
2997                .unwrap(),
2998            b"response"[..]
2999        );
3000    }
3001
3002    #[ntex::test]
3003    async fn shutdown_waits_for_transport_stop() {
3004        #[derive(Debug)]
3005        struct DormantTransport;
3006
3007        impl IoStream for DormantTransport {
3008            fn start(self, _: IoContext) -> Box<dyn Handle> {
3009                Box::new(self)
3010            }
3011        }
3012
3013        impl Handle for DormantTransport {}
3014
3015        let io = Io::from(DormantTransport);
3016        let ctx = IoContext::new(io.get_ref());
3017        let waiter = io.on_disconnect();
3018        io.st().flags.enter_transport_shutdown();
3019
3020        assert!(lazy(|cx| io.poll_shutdown(cx)).await.is_pending());
3021        assert!(lazy(|cx| waiter.poll_ready(cx)).await.is_pending());
3022
3023        ctx.stopped(None);
3024        assert!(matches!(
3025            lazy(|cx| io.poll_shutdown(cx)).await,
3026            Poll::Ready(Ok(()))
3027        ));
3028        assert!(lazy(|cx| waiter.poll_ready(cx)).await.is_ready());
3029    }
3030
3031    /// The transport can report the connection gone straight from the active
3032    /// state, so being stopped reports every earlier state as reached. That
3033    /// must not be mistaken for having gone through a graceful shutdown and
3034    /// discard input that arrived before the connection went away.
3035    #[ntex::test]
3036    async fn a_stopped_connection_keeps_its_buffered_input() {
3037        let (client, server) = IoTest::create();
3038        client.remote_buffer_cap(1024);
3039        client.write(TEXT);
3040
3041        let io = Io::from(server);
3042        io.read_more().await.unwrap().unwrap();
3043        assert_eq!(io.with_read_dst(|buf| buf.len()), BIN.len());
3044
3045        IoContext::new(io.get_ref()).stopped(None);
3046        assert!(io.is_closed() && !io.is_active());
3047        // the shutdown states report as reached even though none was run
3048        assert!(io.st().flags.is_stopping() && !io.st().flags.is_shutting_down_filters());
3049        assert_eq!(
3050            io.with_read_dst(|buf| buf.len()),
3051            BIN.len(),
3052            "input received before the transport went away was discarded"
3053        );
3054    }
3055
3056    #[ntex::test]
3057    async fn transport_shutdown_drain_wakes_write_task() {
3058        #[derive(Debug)]
3059        struct DormantTransport;
3060
3061        impl IoStream for DormantTransport {
3062            fn start(self, _: IoContext) -> Box<dyn Handle> {
3063                Box::new(self)
3064            }
3065        }
3066
3067        impl Handle for DormantTransport {}
3068
3069        let io = Io::from(DormantTransport);
3070        let ctx = IoContext::new(io.get_ref());
3071
3072        // enter the transport shutdown phase with output still queued
3073        io.encode_slice(b"tail").unwrap();
3074        io.st().flags.enter_filters_stopping();
3075        io.st().flags.enter_transport_shutdown();
3076        assert_eq!(io.st().buffer.write_buf_size(), 4);
3077
3078        // the write task is asked to drain it
3079        assert!(matches!(
3080            lazy(|cx| ctx.poll_write_ready(cx)).await,
3081            Poll::Ready(Readiness::Ready)
3082        ));
3083        assert!(io.st().write_task.is_set());
3084
3085        // the transport writes everything out
3086        let res = ctx.with_write_dst(|buf| {
3087            let mut written = 0;
3088            while let Some(page) = buf.take() {
3089                written += page.len();
3090            }
3091            Ok(written)
3092        });
3093        assert_eq!(ctx.update_write_status(res), IoTaskStatus::Pause);
3094        assert_eq!(io.st().write_outstanding(), 0);
3095
3096        // `poll_write_ready` reports `Close` only on another poll, so the
3097        // drain must have woken the write task for the shutdown to complete
3098        assert!(!io.st().write_task.is_set());
3099        assert!(matches!(
3100            lazy(|cx| ctx.poll_write_ready(cx)).await,
3101            Poll::Ready(Readiness::Close)
3102        ));
3103    }
3104
3105    #[ntex::test]
3106    async fn termination_waits_for_transport_stop() {
3107        #[derive(Debug)]
3108        struct DormantTransport;
3109
3110        impl IoStream for DormantTransport {
3111            fn start(self, _: IoContext) -> Box<dyn Handle> {
3112                Box::new(self)
3113            }
3114        }
3115
3116        impl Handle for DormantTransport {}
3117
3118        let io = Io::from(DormantTransport);
3119        let ctx = IoContext::new(io.get_ref());
3120        let waiter = io.on_disconnect();
3121        ctx.stop(Some(io::Error::new(
3122            io::ErrorKind::ConnectionReset,
3123            "connection reset",
3124        )));
3125
3126        assert!(io.st().flags.is_terminating());
3127        assert!(!io.st().flags.is_closed());
3128        assert!(lazy(|cx| io.poll_shutdown(cx)).await.is_pending());
3129        assert!(lazy(|cx| waiter.poll_ready(cx)).await.is_pending());
3130
3131        ctx.stopped(None);
3132        let Poll::Ready(Err(err)) = lazy(|cx| io.poll_shutdown(cx)).await else {
3133            panic!("shutdown did not report termination error");
3134        };
3135        assert_eq!(err.kind(), io::ErrorKind::ConnectionReset);
3136        assert!(lazy(|cx| waiter.poll_ready(cx)).await.is_ready());
3137    }
3138
3139    #[ntex::test]
3140    async fn send_buf_retains_termination_error() {
3141        #[derive(Debug)]
3142        struct DormantTransport;
3143
3144        impl IoStream for DormantTransport {
3145            fn start(self, _: IoContext) -> Box<dyn Handle> {
3146                Box::new(self)
3147            }
3148        }
3149
3150        impl Handle for DormantTransport {}
3151
3152        let io = Io::from(DormantTransport);
3153        let ctx = IoContext::new(io.get_ref());
3154        ctx.stop(Some(io::Error::new(
3155            io::ErrorKind::ConnectionReset,
3156            "connection reset",
3157        )));
3158
3159        let err = io.get_ref().send_buf().unwrap_err();
3160        assert_eq!(err.kind(), io::ErrorKind::ConnectionReset);
3161        assert_eq!(err.to_string(), "connection reset");
3162
3163        ctx.stopped(None);
3164        let err = io.shutdown().await.unwrap_err();
3165        assert_eq!(err.kind(), io::ErrorKind::ConnectionReset);
3166        assert_eq!(err.to_string(), "connection reset");
3167    }
3168
3169    #[ntex::test]
3170    async fn read_readiness_reports_termination_error() {
3171        #[derive(Debug)]
3172        struct DormantTransport;
3173
3174        impl IoStream for DormantTransport {
3175            fn start(self, _: IoContext) -> Box<dyn Handle> {
3176                Box::new(self)
3177            }
3178        }
3179
3180        impl Handle for DormantTransport {}
3181
3182        let io = Io::from(DormantTransport);
3183        let ctx = IoContext::new(io.get_ref());
3184        ctx.stop(Some(io::Error::new(
3185            io::ErrorKind::ConnectionReset,
3186            "connection reset",
3187        )));
3188
3189        let Poll::Ready(Err(err)) = lazy(|cx| io.poll_read_more(cx)).await else {
3190            panic!("read request did not report termination error");
3191        };
3192        assert_eq!(err.kind(), io::ErrorKind::ConnectionReset);
3193        assert_eq!(err.to_string(), "connection reset");
3194
3195        let Poll::Ready(Err(err)) = lazy(|cx| io.poll_read_notify(cx)).await else {
3196            panic!("read notification did not report termination error");
3197        };
3198        assert_eq!(err.kind(), io::ErrorKind::ConnectionReset);
3199        assert_eq!(err.to_string(), "connection reset");
3200
3201        let err = io.read_exact(&mut [0]).await.unwrap_err();
3202        assert_eq!(err.kind(), io::ErrorKind::ConnectionReset);
3203        assert_eq!(err.to_string(), "connection reset");
3204    }
3205
3206    /// Reads pause under back-pressure, so a filter shutdown that waits for
3207    /// input cannot complete while it holds, even once the dispatcher has
3208    /// taken the buffered input. Readiness must not ask for reads that would
3209    /// pause: a readiness based transport would then never re-arm read
3210    /// interest.
3211    #[ntex::test]
3212    async fn filter_shutdown_is_blocked_by_read_backpressure() {
3213        #[derive(Debug)]
3214        struct PendingShutdown;
3215
3216        impl FilterLayer for PendingShutdown {
3217            fn process_read_buf(&self, _: &FilterBuf<'_>) -> io::Result<()> {
3218                Ok(())
3219            }
3220
3221            fn process_write_buf(&self, _: &FilterBuf<'_>) -> io::Result<()> {
3222                Ok(())
3223            }
3224
3225            fn shutdown(&self, _: &FilterBuf<'_>) -> io::Result<Poll<()>> {
3226                Ok(Poll::Pending)
3227            }
3228        }
3229
3230        let (_client, server) = IoTest::create();
3231        let io = Io::new(server, SharedCfg::new("SRV")).add_filter(PendingShutdown);
3232
3233        // The dispatcher decoded part of the input: read readiness is cleared,
3234        // but the buffer is still above the back-pressure release mark.
3235        io.st().flags.set_read_ready_and_backpressure();
3236        io.st().flags.unset_read_ready();
3237        // shutdown clears the read pause and keeps the back-pressure
3238        assert!(lazy(|cx| io.poll_shutdown(cx)).await.is_pending());
3239
3240        let ctx = IoContext::new(io.get_ref());
3241        assert_eq!(lazy(|cx| ctx.poll_read_ready(cx)).await, Poll::Pending);
3242        assert!(io.st().flags.is_stopping());
3243    }
3244
3245    #[ntex::test]
3246    async fn intermediate_filter_output_reaches_transport() {
3247        // A filter that emits output while processing reads, the way a TLS
3248        // layer emits handshake records.
3249        #[derive(Debug)]
3250        struct Emit;
3251
3252        impl FilterLayer for Emit {
3253            fn process_read_buf(&self, buf: &FilterBuf<'_>) -> io::Result<()> {
3254                buf.with_read_buffers(|src, dst| {
3255                    if let Some(src) = src {
3256                        dst.extend_from_slice(src);
3257                        src.clear();
3258                    }
3259                });
3260                buf.with_write_buffers(|_, dst| dst.extend_from_slice(b"pong"));
3261                Ok(())
3262            }
3263
3264            fn process_write_buf(&self, buf: &FilterBuf<'_>) -> io::Result<()> {
3265                buf.with_write_buffers(BytePages::move_to);
3266                Ok(())
3267            }
3268
3269            fn shutdown(&self, _: &FilterBuf<'_>) -> io::Result<Poll<()>> {
3270                Ok(Poll::Ready(()))
3271            }
3272        }
3273
3274        #[derive(Debug)]
3275        struct Passthrough;
3276
3277        impl FilterLayer for Passthrough {
3278            fn process_read_buf(&self, buf: &FilterBuf<'_>) -> io::Result<()> {
3279                buf.with_read_buffers(|src, dst| {
3280                    if let Some(src) = src {
3281                        dst.extend_from_slice(src);
3282                        src.clear();
3283                    }
3284                });
3285                Ok(())
3286            }
3287
3288            fn process_write_buf(&self, buf: &FilterBuf<'_>) -> io::Result<()> {
3289                buf.with_write_buffers(BytePages::move_to);
3290                Ok(())
3291            }
3292
3293            fn shutdown(&self, _: &FilterBuf<'_>) -> io::Result<Poll<()>> {
3294                Ok(Poll::Ready(()))
3295            }
3296        }
3297
3298        let (client, server) = IoTest::create();
3299        client.remote_buffer_cap(1024);
3300
3301        // Two layers, so the bytes `Emit` writes during read processing land in
3302        // an intermediate buffer that `Stack::write_buf_size()` does not count.
3303        // Only the forced write-chain pass moves them out to the transport.
3304        let io = Io::from(server).add_filter(Passthrough).add_filter(Emit);
3305
3306        client.write("ping");
3307        let _ = io.recv(&BytesCodec).await.unwrap();
3308        sleep(Millis(50)).await;
3309        assert!(client.read_any().starts_with(b"pong"));
3310    }
3311
3312    #[ntex::test]
3313    async fn read_pauses_while_read_output_is_not_drained() {
3314        // A filter that answers every input chunk itself, like WebSocket pong
3315        // replies, without passing anything to the application.
3316        #[derive(Debug)]
3317        struct Reply;
3318
3319        impl FilterLayer for Reply {
3320            fn process_read_buf(&self, buf: &FilterBuf<'_>) -> io::Result<()> {
3321                let data = buf.with_read_buffers(|src, _| src.as_mut().map(BytesMut::take));
3322                if let Some(data) = data {
3323                    buf.with_write_buffers(|_, dst| dst.extend_from_slice(&data));
3324                }
3325                Ok(())
3326            }
3327
3328            fn process_write_buf(&self, buf: &FilterBuf<'_>) -> io::Result<()> {
3329                buf.with_write_buffers(BytePages::move_to);
3330                Ok(())
3331            }
3332
3333            fn shutdown(&self, _: &FilterBuf<'_>) -> io::Result<Poll<()>> {
3334                Ok(Poll::Ready(()))
3335            }
3336        }
3337
3338        // The transport is driven by the test through `IoContext`.
3339        #[derive(Debug)]
3340        struct Manual;
3341
3342        impl IoStream for Manual {
3343            fn start(self, _: IoContext) -> Box<dyn Handle> {
3344                Box::new(self)
3345            }
3346        }
3347
3348        impl Handle for Manual {}
3349
3350        let io = Io::new(
3351            Manual,
3352            SharedCfg::new("SRV").add(IoConfig::default().set_write_backpressure(8)),
3353        )
3354        .add_filter(Reply);
3355        let ctx = IoContext::new(io.get_ref());
3356        let read = |data: &'static [u8]| {
3357            ctx.with_read_buf(|buf| {
3358                buf.extend_from_slice(data);
3359                Poll::Ready(Ok(data.len()))
3360            })
3361        };
3362
3363        // the replies are below the high watermark
3364        assert_eq!(read(b"ping"), IoTaskStatus::Io);
3365        assert!(!io.st().flags.is_read_wr_backpressure());
3366
3367        // the replies reach the high watermark, reads pause
3368        assert_eq!(read(b"ping"), IoTaskStatus::Pause);
3369        assert!(io.st().flags.is_read_wr_backpressure());
3370        assert_eq!(lazy(|cx| ctx.poll_read_ready(cx)).await, Poll::Pending);
3371        assert!(io.st().read_task.is_set());
3372
3373        // the transport takes the replies, in-flight output is not drained
3374        let _ = ctx.with_write_dst(|buf| buf.split_to(8));
3375        assert_eq!(io.st().write_outstanding(), 8);
3376        let _ = ctx.update_write_status(Ok(0));
3377        assert!(io.st().flags.is_read_wr_backpressure());
3378
3379        // above half of the high watermark reads stay paused
3380        let _ = ctx.update_write_status(Ok(3));
3381        assert!(io.st().flags.is_read_wr_backpressure());
3382        assert!(io.st().read_task.is_set());
3383
3384        // drained to half, reading resumes
3385        let _ = ctx.update_write_status(Ok(1));
3386        assert!(!io.st().flags.is_read_wr_backpressure());
3387        assert!(!io.st().read_task.is_set());
3388        assert_eq!(
3389            lazy(|cx| ctx.poll_read_ready(cx)).await,
3390            Poll::Ready(Readiness::Ready)
3391        );
3392
3393        // output that was not produced by reading does not pause reads
3394        io.encode_slice(b"12345678").unwrap();
3395        assert!(io.st().write_outstanding() >= 8);
3396        assert_eq!(
3397            lazy(|cx| ctx.poll_read_ready(cx)).await,
3398            Poll::Ready(Readiness::Ready)
3399        );
3400        assert!(!io.st().flags.is_read_wr_backpressure());
3401    }
3402
3403    #[ntex::test]
3404    async fn read_output_pause_ignores_held_back_output() {
3405        // Holds application output until a handshake completes, like TLS
3406        // during a renegotiation.
3407        #[derive(Debug, Default)]
3408        struct Reneg(Cell<bool>);
3409
3410        impl FilterLayer for Reneg {
3411            fn process_read_buf(&self, buf: &FilterBuf<'_>) -> io::Result<()> {
3412                let data = buf.with_read_buffers(|src, _| src.as_mut().map(BytesMut::take));
3413                match data.as_deref() {
3414                    Some(b"hello") => {
3415                        buf.with_write_buffers(|_, dst| dst.extend_from_slice(b"handshake"));
3416                    }
3417                    Some(b"done") => {
3418                        self.0.set(true);
3419                        buf.with_write_buffers(BytePages::move_to);
3420                    }
3421                    _ => (),
3422                }
3423                Ok(())
3424            }
3425
3426            fn process_write_buf(&self, buf: &FilterBuf<'_>) -> io::Result<()> {
3427                if self.0.get() {
3428                    buf.with_write_buffers(BytePages::move_to);
3429                }
3430                Ok(())
3431            }
3432
3433            fn shutdown(&self, _: &FilterBuf<'_>) -> io::Result<Poll<()>> {
3434                Ok(Poll::Ready(()))
3435            }
3436        }
3437
3438        #[derive(Debug)]
3439        struct Manual;
3440
3441        impl IoStream for Manual {
3442            fn start(self, _: IoContext) -> Box<dyn Handle> {
3443                Box::new(self)
3444            }
3445        }
3446
3447        impl Handle for Manual {}
3448
3449        let io = Io::new(
3450            Manual,
3451            SharedCfg::new("SRV").add(IoConfig::default().set_write_backpressure(8)),
3452        )
3453        .add_filter(Reneg::default());
3454        let ctx = IoContext::new(io.get_ref());
3455        let read = |data: &'static [u8]| {
3456            ctx.with_read_buf(|buf| {
3457                buf.extend_from_slice(data);
3458                Poll::Ready(Ok(data.len()))
3459            })
3460        };
3461
3462        // application output is held back by the filter
3463        io.encode_slice(b"12345678").unwrap();
3464        assert_eq!(io.st().write_outstanding(), 8);
3465
3466        // the handshake reply reaches the high watermark, reads pause
3467        assert_eq!(read(b"hello"), IoTaskStatus::Pause);
3468        assert!(io.st().flags.is_read_wr_backpressure());
3469
3470        // the reply drains, the held back output does not keep reads paused
3471        let _ = ctx.with_write_dst(|buf| buf.split_to(9));
3472        let _ = ctx.update_write_status(Ok(9));
3473        assert_eq!(io.st().write_outstanding(), 8);
3474        assert!(!io.st().flags.is_read_wr_backpressure());
3475        assert_eq!(
3476            lazy(|cx| ctx.poll_read_ready(cx)).await,
3477            Poll::Ready(Readiness::Ready)
3478        );
3479
3480        // the handshake completes and the held back output moves
3481        let _ = read(b"done");
3482        assert_eq!(ctx.with_write_dst(|buf| buf.len()), 8);
3483    }
3484
3485    #[ntex::test]
3486    async fn read_more_lifts_read_output_pause() {
3487        #[derive(Debug)]
3488        struct Reply;
3489
3490        impl FilterLayer for Reply {
3491            fn process_read_buf(&self, buf: &FilterBuf<'_>) -> io::Result<()> {
3492                let data = buf.with_read_buffers(|src, _| src.as_mut().map(BytesMut::take));
3493                if let Some(data) = data {
3494                    buf.with_write_buffers(|_, dst| dst.extend_from_slice(&data));
3495                }
3496                Ok(())
3497            }
3498
3499            fn process_write_buf(&self, buf: &FilterBuf<'_>) -> io::Result<()> {
3500                buf.with_write_buffers(BytePages::move_to);
3501                Ok(())
3502            }
3503
3504            fn shutdown(&self, _: &FilterBuf<'_>) -> io::Result<Poll<()>> {
3505                Ok(Poll::Ready(()))
3506            }
3507        }
3508
3509        #[derive(Debug)]
3510        struct Manual;
3511
3512        impl IoStream for Manual {
3513            fn start(self, _: IoContext) -> Box<dyn Handle> {
3514                Box::new(self)
3515            }
3516        }
3517
3518        impl Handle for Manual {}
3519
3520        let io = Io::new(
3521            Manual,
3522            SharedCfg::new("SRV").add(IoConfig::default().set_write_backpressure(8)),
3523        )
3524        .add_filter(Reply);
3525        let ctx = IoContext::new(io.get_ref());
3526        let read = |data: &'static [u8]| {
3527            ctx.with_read_buf(|buf| {
3528                buf.extend_from_slice(data);
3529                Poll::Ready(Ok(data.len()))
3530            })
3531        };
3532
3533        // read_more
3534        assert_eq!(read(b"pingping"), IoTaskStatus::Pause);
3535        assert_eq!(lazy(|cx| ctx.poll_read_ready(cx)).await, Poll::Pending);
3536        assert!(lazy(|cx| io.poll_read_more(cx)).await.is_pending());
3537        assert!(!io.st().flags.is_read_wr_backpressure());
3538        assert!(!io.st().read_task.is_set());
3539        assert_eq!(
3540            lazy(|cx| ctx.poll_read_ready(cx)).await,
3541            Poll::Ready(Readiness::Ready)
3542        );
3543
3544        // read_notify
3545        assert_eq!(read(b"ping"), IoTaskStatus::Pause);
3546        assert_eq!(lazy(|cx| ctx.poll_read_ready(cx)).await, Poll::Pending);
3547        assert!(lazy(|cx| io.poll_read_notify(cx)).await.is_pending());
3548        assert!(!io.st().flags.is_read_wr_backpressure());
3549        assert!(!io.st().read_task.is_set());
3550        assert_eq!(
3551            lazy(|cx| ctx.poll_read_ready(cx)).await,
3552            Poll::Ready(Readiness::Ready)
3553        );
3554    }
3555
3556    #[ntex::test]
3557    async fn read_pause_on_read_output_does_not_block_filter_shutdown() {
3558        #[derive(Debug)]
3559        struct Reply;
3560
3561        impl FilterLayer for Reply {
3562            fn process_read_buf(&self, buf: &FilterBuf<'_>) -> io::Result<()> {
3563                let data = buf.with_read_buffers(|src, _| src.as_mut().map(BytesMut::take));
3564                if let Some(data) = data {
3565                    buf.with_write_buffers(|_, dst| dst.extend_from_slice(&data));
3566                }
3567                Ok(())
3568            }
3569
3570            fn process_write_buf(&self, buf: &FilterBuf<'_>) -> io::Result<()> {
3571                buf.with_write_buffers(BytePages::move_to);
3572                Ok(())
3573            }
3574
3575            fn shutdown(&self, _: &FilterBuf<'_>) -> io::Result<Poll<()>> {
3576                // waits for input, like a TLS close_notify
3577                Ok(Poll::Pending)
3578            }
3579        }
3580
3581        #[derive(Debug)]
3582        struct Manual;
3583
3584        impl IoStream for Manual {
3585            fn start(self, _: IoContext) -> Box<dyn Handle> {
3586                Box::new(self)
3587            }
3588        }
3589
3590        impl Handle for Manual {}
3591
3592        let io = Io::new(
3593            Manual,
3594            SharedCfg::new("SRV").add(IoConfig::default().set_write_backpressure(8)),
3595        )
3596        .add_filter(Reply);
3597        let ctx = IoContext::new(io.get_ref());
3598        let read = |data: &'static [u8]| {
3599            ctx.with_read_buf(|buf| {
3600                buf.extend_from_slice(data);
3601                Poll::Ready(Ok(data.len()))
3602            })
3603        };
3604
3605        assert_eq!(read(b"pingping"), IoTaskStatus::Pause);
3606        assert!(io.st().flags.is_read_wr_backpressure());
3607
3608        // the filters are shutting down and may need input to complete
3609        assert!(lazy(|cx| io.poll_shutdown(cx)).await.is_pending());
3610        assert!(io.st().flags.is_stopping_filters());
3611        assert_eq!(
3612            lazy(|cx| ctx.poll_read_ready(cx)).await,
3613            Poll::Ready(Readiness::Ready)
3614        );
3615        assert_eq!(read(b"ping"), IoTaskStatus::Io);
3616    }
3617
3618    #[ntex::test]
3619    async fn peer_eof_completes_filter_shutdown() {
3620        #[derive(Debug)]
3621        struct PendingShutdown;
3622
3623        impl FilterLayer for PendingShutdown {
3624            fn process_read_buf(&self, _: &FilterBuf<'_>) -> io::Result<()> {
3625                Ok(())
3626            }
3627
3628            fn process_write_buf(&self, buf: &FilterBuf<'_>) -> io::Result<()> {
3629                buf.with_write_buffers(BytePages::move_to);
3630                Ok(())
3631            }
3632
3633            fn shutdown(&self, buf: &FilterBuf<'_>) -> io::Result<Poll<()>> {
3634                // waits for input that can never arrive after a clean eof
3635                buf.with_write_buffers(BytePages::move_to);
3636                Ok(Poll::Pending)
3637            }
3638        }
3639
3640        let (client, server) = IoTest::create();
3641        client.remote_buffer_cap(1024);
3642        let io = Io::new(
3643            server,
3644            SharedCfg::new("SRV")
3645                .add(IoConfig::default().set_shutdown_timeout(ntex_util::time::Seconds(30))),
3646        )
3647        .add_filter(PendingShutdown);
3648
3649        io.encode_slice(b"bye").unwrap();
3650
3651        // peer closes cleanly, no further input can arrive
3652        let peer = client.clone();
3653        drop(client);
3654        assert!(io.read_more().await.unwrap().is_none());
3655        assert!(io.st().flags.is_read_eof());
3656
3657        // the shutdown completes without waiting for the shutdown timeout
3658        timeout(Millis(1000), io.shutdown())
3659            .await
3660            .expect("transport shutdown did not complete")
3661            .unwrap();
3662        assert!(io.st().flags.is_closed());
3663
3664        // buffered output still reached the peer
3665        assert_eq!(peer.read_any(), Bytes::from_static(b"bye"));
3666    }
3667
3668    /// A filter that forwards input and never finishes its own shutdown.
3669    #[derive(Debug)]
3670    struct StuckShutdown(Cell<bool>);
3671
3672    impl FilterLayer for StuckShutdown {
3673        fn process_read_buf(&self, buf: &FilterBuf<'_>) -> io::Result<()> {
3674            buf.with_read_buffers(|src, dst| {
3675                if let Some(src) = src {
3676                    dst.extend_from_slice(src);
3677                    src.clear();
3678                }
3679            });
3680            Ok(())
3681        }
3682
3683        fn process_write_buf(&self, buf: &FilterBuf<'_>) -> io::Result<()> {
3684            buf.with_write_buffers(BytePages::move_to);
3685            Ok(())
3686        }
3687
3688        fn shutdown(&self, _: &FilterBuf<'_>) -> io::Result<Poll<()>> {
3689            self.0.set(true);
3690            Ok(Poll::Pending)
3691        }
3692    }
3693
3694    #[ntex::test]
3695    async fn transport_shutdown_pauses_read_task() {
3696        let (client, server) = IoTest::create();
3697        client.remote_buffer_cap(1024);
3698        let io = Io::new(server, SharedCfg::new("SRV"));
3699
3700        client.write("before");
3701        sleep(Millis(25)).await;
3702        assert_eq!(
3703            io.recv(&BytesCodec).await.unwrap().unwrap(),
3704            b"before".as_ref()
3705        );
3706
3707        // stall the output so the transport shutdown phase does not complete
3708        client.remote_buffer_cap(0);
3709        io.get_ref().with_write_dst(|b| b.extend_from_slice(b"out"));
3710
3711        // enter the transport shutdown phase
3712        io.st().flags.enter_filters_stopping();
3713        io.st().flags.enter_transport_shutdown();
3714        io.st().wake_read_task();
3715
3716        // the filters are done, so input is left in the transport; it is
3717        // discarded by the transport itself right before it closes
3718        client.write("after");
3719        sleep(Millis(50)).await;
3720        assert!(!io.st().flags.is_closed());
3721        assert_eq!(client.remote_buffer(|buf| buf.len()), 5);
3722    }
3723
3724    /// Counts the wakeups delivered through the waker it provides.
3725    #[derive(Default)]
3726    struct WakeCounter(std::sync::atomic::AtomicUsize);
3727
3728    impl std::task::Wake for WakeCounter {
3729        fn wake(self: std::sync::Arc<Self>) {
3730            self.wake_by_ref();
3731        }
3732
3733        fn wake_by_ref(self: &std::sync::Arc<Self>) {
3734            self.0.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
3735        }
3736    }
3737
3738    impl WakeCounter {
3739        fn count(&self) -> usize {
3740            self.0.load(std::sync::atomic::Ordering::Relaxed)
3741        }
3742    }
3743
3744    #[ntex::test]
3745    async fn repeated_shutdown_polls_do_not_wake_tasks() {
3746        use std::{sync::Arc, task::Waker};
3747
3748        let (_client, server) = IoTest::create();
3749        let io = Io::new(server, SharedCfg::new("SRV")).add_filter(StuckShutdown(Cell::new(false)));
3750
3751        let waker = Waker::noop();
3752        let mut cx = Context::from_waker(waker);
3753        assert!(io.poll_shutdown(&mut cx).is_pending());
3754        assert!(io.st().flags.is_stopping_filters());
3755
3756        // Polling again without any state change must not wake the transport
3757        // tasks. A runtime that polls the caller spuriously, as compio does
3758        // for `block_on`, would otherwise be kept busy with wakeups.
3759        let rd = Arc::new(WakeCounter::default());
3760        let wr = Arc::new(WakeCounter::default());
3761        io.st().read_task.register(&Waker::from(rd.clone()));
3762        io.st().write_task.register(&Waker::from(wr.clone()));
3763        for _ in 0..3 {
3764            assert!(io.poll_shutdown(&mut cx).is_pending());
3765        }
3766        assert_eq!(rd.count(), 0);
3767        assert_eq!(wr.count(), 0);
3768
3769        // Paused reads are resumed, the filter may need the input.
3770        io.st().flags.set_read_paused();
3771        assert!(io.poll_shutdown(&mut cx).is_pending());
3772        assert!(!io.st().flags.is_read_paused());
3773        assert_eq!(rd.count(), 1);
3774    }
3775
3776    #[ntex::test]
3777    async fn filter_shutdown_applies_read_backpressure() {
3778        let (client, server) = IoTest::create();
3779        client.remote_buffer_cap(1024 * 1024);
3780        let io = Io::new(
3781            server,
3782            SharedCfg::new("SRV").add(
3783                IoConfig::default()
3784                    .set_read_size(BytePageSize::Size4, BytePageSize::Size4)
3785                    .set_read_backpressure(BytePageSize::Size4.capacity())
3786                    .set_shutdown_timeout(ntex_util::time::Seconds(30)),
3787            ),
3788        )
3789        .add_filter(StuckShutdown(Cell::new(false)));
3790
3791        let ioref = io.get_ref();
3792        let high = BytePageSize::Size4.capacity();
3793        ntex::rt::spawn(async move {
3794            let _ = io.shutdown().await;
3795        });
3796        sleep(Millis(50)).await;
3797
3798        // The filter never completes, so the connection stays in the filter
3799        // shutdown phase while the peer keeps sending.
3800        for _ in 0..40 {
3801            client.write("A".repeat(1024));
3802            sleep(Millis(5)).await;
3803        }
3804        sleep(Millis(100)).await;
3805
3806        // Reads are backpressured instead of draining the peer without bound.
3807        let buffered = ioref.with_read_dst(|buf| buf.len());
3808        assert!(
3809            buffered <= high * 2,
3810            "read buffer grew to {buffered} with a high watermark of {high}"
3811        );
3812        assert!(
3813            client.remote_buffer(|buf| !buf.is_empty()),
3814            "peer send buffer was drained despite read backpressure"
3815        );
3816    }
3817
3818    #[ntex::test]
3819    async fn filter_shutdown_blocked_by_unconsumed_input() {
3820        // The dispatcher stops consuming with a full read buffer, so the filter
3821        // cannot receive the input it is waiting for. The shutdown gives up on
3822        // the filter handshake promptly instead of reading without bound until
3823        // the deadline.
3824        let (client, server) = IoTest::create();
3825        client.remote_buffer_cap(1024 * 1024);
3826        let io = Io::new(
3827            server,
3828            SharedCfg::new("SRV").add(
3829                IoConfig::default()
3830                    .set_read_size(BytePageSize::Size4, BytePageSize::Size4)
3831                    .set_read_backpressure(BytePageSize::Size4.capacity())
3832                    .set_shutdown_timeout(ntex_util::time::Seconds(30)),
3833            ),
3834        )
3835        .add_filter(StuckShutdown(Cell::new(false)));
3836
3837        client.write("A".repeat(4096));
3838        sleep(Millis(50)).await;
3839        assert!(
3840            io.get_ref().is_rd_backpressure(),
3841            "read backpressure was not active before the shutdown"
3842        );
3843
3844        // Completes well inside the 30 second deadline, so it is the blocked
3845        // detection that ends the phase rather than the timeout.
3846        let err = timeout(Millis(3000), io.shutdown())
3847            .await
3848            .expect("shutdown did not complete")
3849            .unwrap_err();
3850        assert_eq!(err.kind(), io::ErrorKind::Other);
3851    }
3852
3853    #[derive(Debug)]
3854    struct Passthrough;
3855
3856    impl FilterLayer for Passthrough {
3857        fn process_read_buf(&self, buf: &FilterBuf<'_>) -> io::Result<()> {
3858            buf.with_read_buffers(|src, dst| {
3859                if let Some(src) = src {
3860                    dst.extend_from_slice(src);
3861                    src.clear();
3862                }
3863            });
3864            Ok(())
3865        }
3866
3867        fn process_write_buf(&self, buf: &FilterBuf<'_>) -> io::Result<()> {
3868            buf.with_write_buffers(BytePages::move_to);
3869            Ok(())
3870        }
3871    }
3872
3873    /// A filter that sends "bye" on shutdown and waits for the peer's "ack".
3874    #[derive(Debug, Default)]
3875    struct AckShutdown {
3876        sent: Cell<bool>,
3877        acked: Cell<bool>,
3878    }
3879
3880    impl FilterLayer for AckShutdown {
3881        fn process_read_buf(&self, buf: &FilterBuf<'_>) -> io::Result<()> {
3882            buf.with_read_buffers(|src, dst| {
3883                if let Some(src) = src {
3884                    if self.sent.get() && &src[..] == b"ack" {
3885                        self.acked.set(true);
3886                    } else {
3887                        dst.extend_from_slice(src);
3888                    }
3889                    src.clear();
3890                }
3891            });
3892            Ok(())
3893        }
3894
3895        fn process_write_buf(&self, buf: &FilterBuf<'_>) -> io::Result<()> {
3896            buf.with_write_buffers(BytePages::move_to);
3897            Ok(())
3898        }
3899
3900        fn shutdown(&self, buf: &FilterBuf<'_>) -> io::Result<Poll<()>> {
3901            if !self.sent.replace(true) {
3902                buf.with_write_buffers(|_, dst| dst.extend_from_slice(b"bye"));
3903            }
3904            Ok(if self.acked.get() {
3905                Poll::Ready(())
3906            } else {
3907                Poll::Pending
3908            })
3909        }
3910    }
3911
3912    async fn filter_shutdown_waits_for_peer<F: Filter>(client: IoTest, io: Io<F>) {
3913        client.remote_buffer_cap(1024);
3914        let io = io.add_filter(AckShutdown::default());
3915        let done = Rc::new(Cell::new(false));
3916        let done2 = done.clone();
3917        ntex::rt::spawn(async move {
3918            io.shutdown().await.unwrap();
3919            done2.set(true);
3920        });
3921        sleep(Millis(50)).await;
3922
3923        // the shutdown output reached the peer while the filter is pending
3924        assert_eq!(client.read_any(), Bytes::from_static(b"bye"));
3925        assert!(!done.get());
3926
3927        // the peer's input completes the filter shutdown
3928        client.write("ack");
3929        sleep(Millis(50)).await;
3930        assert!(done.get());
3931    }
3932
3933    #[ntex::test]
3934    async fn filter_shutdown_completes_on_peer_input() {
3935        let (client, server) = IoTest::create();
3936        let io = Io::new(server, SharedCfg::new("SRV"));
3937        filter_shutdown_waits_for_peer(client, io).await;
3938    }
3939
3940    #[ntex::test]
3941    async fn filter_shutdown_output_passes_inner_filters() {
3942        let (client, server) = IoTest::create();
3943        let io = Io::new(server, SharedCfg::new("SRV")).add_filter(Passthrough);
3944        filter_shutdown_waits_for_peer(client, io).await;
3945    }
3946
3947    #[ntex::test]
3948    async fn filter_failure_output_passes_inner_filters() {
3949        /// Answers any input with "err", closes and fails.
3950        #[derive(Debug)]
3951        struct FailOnInput;
3952
3953        impl FilterLayer for FailOnInput {
3954            fn process_read_buf(&self, buf: &FilterBuf<'_>) -> io::Result<()> {
3955                if buf.with_read_src(|src| src.take().is_some()) {
3956                    buf.with_write_buffers(|_, dst| dst.extend_from_slice(b"err"));
3957                    buf.io().close();
3958                    Err(io::Error::new(io::ErrorKind::InvalidData, "failed"))
3959                } else {
3960                    Ok(())
3961                }
3962            }
3963
3964            fn process_write_buf(&self, buf: &FilterBuf<'_>) -> io::Result<()> {
3965                buf.with_write_buffers(BytePages::move_to);
3966                Ok(())
3967            }
3968        }
3969
3970        let (client, server) = IoTest::create();
3971        client.remote_buffer_cap(1024);
3972        let io = Io::new(server, SharedCfg::new("SRV"))
3973            .add_filter(Passthrough)
3974            .add_filter(FailOnInput);
3975
3976        client.write("input");
3977        let err = io.recv(&BytesCodec).await.unwrap_err();
3978        assert_eq!(err.into_inner().kind(), io::ErrorKind::InvalidData);
3979        sleep(Millis(50)).await;
3980
3981        assert_eq!(client.read_any(), Bytes::from_static(b"err"));
3982        assert!(io.is_closed());
3983    }
3984
3985    #[ntex::test]
3986    async fn filter_shutdown_timeout_is_reported() {
3987        #[derive(Debug)]
3988        struct PendingShutdown;
3989
3990        impl FilterLayer for PendingShutdown {
3991            fn process_read_buf(&self, _: &FilterBuf<'_>) -> io::Result<()> {
3992                Ok(())
3993            }
3994
3995            fn process_write_buf(&self, _: &FilterBuf<'_>) -> io::Result<()> {
3996                Ok(())
3997            }
3998
3999            fn shutdown(&self, _: &FilterBuf<'_>) -> io::Result<Poll<()>> {
4000                Ok(Poll::Pending)
4001            }
4002        }
4003
4004        let (_client, server) = IoTest::create();
4005        let io = Io::new(
4006            server,
4007            SharedCfg::new("SRV")
4008                .add(IoConfig::default().set_shutdown_timeout(ntex_util::time::Seconds(1))),
4009        )
4010        .add_filter(PendingShutdown);
4011
4012        let err = timeout(Millis(3000), io.shutdown())
4013            .await
4014            .expect("transport shutdown did not complete")
4015            .unwrap_err();
4016        assert_eq!(err.kind(), io::ErrorKind::TimedOut);
4017        assert!(io.st().flags.is_closed());
4018        assert!(!io.st().flags.is_terminating());
4019    }
4020
4021    #[ntex::test]
4022    async fn blocked_filter_shutdown_flushes_buffered_output() {
4023        #[derive(Debug)]
4024        struct ClosingShutdown(Cell<bool>);
4025
4026        impl FilterLayer for ClosingShutdown {
4027            fn process_read_buf(&self, _: &FilterBuf<'_>) -> io::Result<()> {
4028                Ok(())
4029            }
4030
4031            fn process_write_buf(&self, buf: &FilterBuf<'_>) -> io::Result<()> {
4032                buf.with_write_buffers(BytePages::move_to);
4033                Ok(())
4034            }
4035
4036            fn shutdown(&self, buf: &FilterBuf<'_>) -> io::Result<Poll<()>> {
4037                // emit a closing record, like a tls close_notify
4038                if !self.0.replace(true) {
4039                    buf.with_write_buffers(|_, dst| dst.extend_from_slice(b"bye"));
4040                }
4041                Ok(Poll::Pending)
4042            }
4043        }
4044
4045        let (client, server) = IoTest::create();
4046        // the peer cannot accept the closing record yet
4047        client.remote_buffer_cap(0);
4048
4049        let io = Io::new(
4050            server,
4051            SharedCfg::new("SRV").add(
4052                IoConfig::default()
4053                    .set_read_size(BytePageSize::Size4, BytePageSize::Size4)
4054                    .set_read_backpressure(BytePageSize::Size4.capacity())
4055                    .set_shutdown_timeout(ntex_util::time::Seconds(10)),
4056            ),
4057        )
4058        .add_filter(ClosingShutdown(Cell::new(false)));
4059
4060        io.st().flags.set_read_ready_and_backpressure();
4061        io.close();
4062        sleep(Millis(50)).await;
4063
4064        // the filter shutdown is blocked, so the transport shutdown phase
4065        // starts right away and takes over draining the closing record
4066        assert!(io.st().flags.is_stopping());
4067
4068        // let the peer accept the buffered bytes
4069        client.remote_buffer_cap(1024);
4070        assert_eq!(
4071            timeout(Millis(1000), client.read())
4072                .await
4073                .expect("closing record was not written")
4074                .unwrap(),
4075            Bytes::from_static(b"bye")
4076        );
4077
4078        let err = timeout(Millis(1000), io.shutdown())
4079            .await
4080            .expect("transport shutdown did not complete")
4081            .unwrap_err();
4082        assert_eq!(err.kind(), io::ErrorKind::Other);
4083    }
4084
4085    #[ntex::test]
4086    async fn one_deadline_bounds_both_shutdown_phases() {
4087        #[derive(Debug)]
4088        struct ClosingShutdown(Cell<bool>);
4089
4090        impl FilterLayer for ClosingShutdown {
4091            fn process_read_buf(&self, _: &FilterBuf<'_>) -> io::Result<()> {
4092                Ok(())
4093            }
4094
4095            fn process_write_buf(&self, buf: &FilterBuf<'_>) -> io::Result<()> {
4096                buf.with_write_buffers(BytePages::move_to);
4097                Ok(())
4098            }
4099
4100            fn shutdown(&self, buf: &FilterBuf<'_>) -> io::Result<Poll<()>> {
4101                if !self.0.replace(true) {
4102                    buf.with_write_buffers(|_, dst| dst.extend_from_slice(b"bye"));
4103                }
4104                Ok(Poll::Pending)
4105            }
4106        }
4107
4108        let (client, server) = IoTest::create();
4109        // the peer never accepts the closing record, so neither the filter
4110        // shutdown nor the transport drain can ever complete
4111        client.remote_buffer_cap(0);
4112
4113        let io = Io::new(
4114            server,
4115            SharedCfg::new("SRV").add(
4116                IoConfig::default()
4117                    .set_read_size(BytePageSize::Size4, BytePageSize::Size4)
4118                    .set_read_backpressure(BytePageSize::Size4.capacity())
4119                    .set_shutdown_timeout(ntex_util::time::Seconds(1)),
4120            ),
4121        )
4122        .add_filter(ClosingShutdown(Cell::new(false)));
4123
4124        let start = std::time::Instant::now();
4125        io.close();
4126
4127        timeout(Millis(5000), io.shutdown())
4128            .await
4129            .expect("transport shutdown did not complete")
4130            .unwrap_err();
4131        assert!(io.st().flags.is_closed());
4132
4133        // both phases stall, yet a single shutdown timeout covers them: a
4134        // per-phase deadline would take twice as long
4135        let elapsed = start.elapsed();
4136        assert!(
4137            elapsed < std::time::Duration::from_millis(1600),
4138            "shutdown took {elapsed:?}, the deadline did not span both phases"
4139        );
4140    }
4141
4142    #[ntex::test]
4143    async fn blocked_filter_shutdown_is_reported() {
4144        #[derive(Debug)]
4145        struct PendingShutdown;
4146
4147        impl FilterLayer for PendingShutdown {
4148            fn process_read_buf(&self, _: &FilterBuf<'_>) -> io::Result<()> {
4149                Ok(())
4150            }
4151
4152            fn process_write_buf(&self, _: &FilterBuf<'_>) -> io::Result<()> {
4153                Ok(())
4154            }
4155
4156            fn shutdown(&self, _: &FilterBuf<'_>) -> io::Result<Poll<()>> {
4157                Ok(Poll::Pending)
4158            }
4159        }
4160
4161        let (_client, server) = IoTest::create();
4162        let io = Io::new(
4163            server,
4164            SharedCfg::new("SRV").add(
4165                IoConfig::default()
4166                    .set_read_size(BytePageSize::Size4, BytePageSize::Size4)
4167                    .set_read_backpressure(BytePageSize::Size4.capacity())
4168                    .set_shutdown_timeout(ntex_util::time::Seconds(10)),
4169            ),
4170        )
4171        .add_filter(PendingShutdown);
4172
4173        io.st().flags.set_read_ready_and_backpressure();
4174        io.close();
4175        sleep(Millis(50)).await;
4176
4177        let err = timeout(Millis(1000), io.shutdown())
4178            .await
4179            .expect("transport shutdown did not complete")
4180            .unwrap_err();
4181        assert_eq!(err.kind(), io::ErrorKind::Other);
4182        assert!(io.st().flags.is_closed());
4183        assert!(!io.st().flags.is_terminating());
4184    }
4185
4186    #[ntex::test]
4187    async fn shutdown() {
4188        // layer drops all unprocessed data after filter shutdown
4189        #[derive(Debug)]
4190        struct F;
4191
4192        impl FilterLayer for F {
4193            fn process_read_buf(&self, _: &FilterBuf<'_>) -> io::Result<()> {
4194                Ok(())
4195            }
4196            fn process_write_buf(&self, _: &FilterBuf<'_>) -> io::Result<()> {
4197                Ok(())
4198            }
4199        }
4200
4201        let io = Io::new(
4202            IoTest::create().0,
4203            SharedCfg::new("SRV").add(IoConfig::default().set_write_backpressure(8)),
4204        );
4205        let st = io.st();
4206        assert!(lazy(|cx| io.poll_status_update(cx)).await.is_pending());
4207        assert!(st.dispatch_task.is_set());
4208        assert!(!st.flags.is_peer_gone());
4209        assert!(!st.flags.is_stopping_filters());
4210
4211        let ctx = IoContext::new(io.get_ref());
4212
4213        // == init shutdown
4214        io.close();
4215        assert!(!st.flags.is_peer_gone());
4216        assert!(st.flags.is_stopping_filters());
4217        // encoding is not allowed in shutting down stage
4218        let err = io.with_write_src(|_| 1).unwrap_err();
4219        assert_eq!(err.kind(), io::ErrorKind::Other);
4220
4221        let io = io.add_filter(F);
4222        let layer = Layer::new(F, Base::new(io.get_ref()));
4223
4224        let st = io.st();
4225        st.buffer.with_write_src(|p| p.put_slice(b"123"));
4226        assert_eq!(st.buffer.write_buf_size(), 3);
4227        let res = st.buffer.with_filter(io.as_ref(), |f| layer.shutdown(f));
4228        assert!(matches!(res, Ok(Poll::Ready(()))));
4229        assert_eq!(st.buffer.write_buf_size(), 0);
4230
4231        // == terminate
4232        ctx.stop(None);
4233        assert!(st.flags.is_peer_gone());
4234        assert!(st.flags.is_terminating());
4235        assert!(!st.flags.is_closed());
4236        assert!(st.flags.is_stopping_filters());
4237
4238        let err = io.with_write_src(|_| 1).unwrap_err();
4239        assert_eq!(err.kind(), io::ErrorKind::NotConnected);
4240
4241        ctx.stopped(None);
4242        assert!(st.flags.is_closed());
4243    }
4244
4245    struct FixedSize(usize);
4246
4247    impl Decoder for FixedSize {
4248        type Item = Bytes;
4249        type Error = io::Error;
4250
4251        fn decode(&self, src: &mut BytesMut) -> Result<Option<Bytes>, io::Error> {
4252            if src.len() < self.0 {
4253                Ok(None)
4254            } else {
4255                Ok(Some(src.split_to(self.0)))
4256            }
4257        }
4258    }
4259
4260    #[ntex::test]
4261    async fn recv_reports_timeout_during_write_backpressure() {
4262        let (client, server) = IoTest::create();
4263        client.remote_buffer_cap(0);
4264        let io = Io::new(
4265            server,
4266            SharedCfg::new("SRV").add(IoConfig::default().set_write_backpressure(16)),
4267        );
4268        io.encode_slice(BIN2).unwrap();
4269        assert!(io.flags().is_wr_backpressure());
4270
4271        let ioref = io.get_ref();
4272        let (res, ()) =
4273            ntex_util::future::join(timeout(Millis(1000), io.recv(&BytesCodec)), async move {
4274                sleep(Millis(25)).await;
4275                ioref.0.notify_timeout();
4276            })
4277            .await;
4278
4279        let Err(Either::Right(err)) = res.expect("recv ignored the timeout") else {
4280            panic!("expected a timeout error")
4281        };
4282        assert_eq!(err.kind(), io::ErrorKind::TimedOut);
4283        assert!(io.flags().is_wr_backpressure());
4284    }
4285
4286    #[ntex::test]
4287    async fn write_timeout_bounds_output_waits() {
4288        let (client, server) = IoTest::create();
4289        client.remote_buffer_cap(0);
4290        let io = Io::new(
4291            server,
4292            SharedCfg::new("SRV").add(
4293                IoConfig::default()
4294                    .set_write_backpressure(16)
4295                    .set_write_timeout(ntex_util::time::Seconds(1)),
4296            ),
4297        );
4298        io.encode_slice(BIN2).unwrap();
4299        assert!(io.flags().is_wr_backpressure());
4300
4301        let ioref = io.get_ref();
4302        let ((send, flush), ready) = timeout(
4303            Millis(3000),
4304            ntex_util::future::join(
4305                ntex_util::future::join(
4306                    io.send(Bytes::from_static(b"item"), &BytesCodec),
4307                    io.flush(false),
4308                ),
4309                ioref.write_ready(),
4310            ),
4311        )
4312        .await
4313        .expect("output waits are not bounded by the write timeout");
4314
4315        let Err(Either::Right(err)) = send else {
4316            panic!("expected a transport error")
4317        };
4318        assert_eq!(err.kind(), io::ErrorKind::TimedOut);
4319        assert_eq!(flush.unwrap_err().kind(), io::ErrorKind::TimedOut);
4320        assert_eq!(ready.unwrap_err().kind(), io::ErrorKind::TimedOut);
4321
4322        // the connection is left open, the caller decides
4323        assert!(!io.is_closed());
4324        client.remote_buffer_cap(1024);
4325        io.flush(true).await.unwrap();
4326    }
4327
4328    #[ntex::test]
4329    async fn recv_reports_truncated_stream() {
4330        let (client, server) = IoTest::create();
4331        client.remote_buffer_cap(1024);
4332        let io = Io::new(server, SharedCfg::new("SRV"));
4333
4334        // a partial item, then the peer goes away
4335        client.write("123");
4336        sleep(Millis(25)).await;
4337        client.close().await;
4338
4339        let err = io.recv(&FixedSize(8)).await.err().unwrap();
4340        let Either::Right(err) = err else {
4341            panic!("expected a transport error")
4342        };
4343        assert_eq!(err.kind(), io::ErrorKind::UnexpectedEof);
4344
4345        // the partial item is still buffered, it is not discarded
4346        assert_eq!(io.with_read_dst(|b| b.len()), 3);
4347    }
4348
4349    #[ntex::test]
4350    async fn recv_reports_clean_eof() {
4351        let (client, server) = IoTest::create();
4352        client.remote_buffer_cap(1024);
4353        let io = Io::new(server, SharedCfg::new("SRV"));
4354
4355        // a whole item, then the peer goes away
4356        client.write("12345678");
4357        sleep(Millis(25)).await;
4358        client.close().await;
4359
4360        assert_eq!(io.recv(&FixedSize(8)).await.unwrap().unwrap(), "12345678");
4361        assert!(io.recv(&FixedSize(8)).await.unwrap().is_none());
4362    }
4363
4364    /// Fixed size frames, the last frame may be shorter once at eof.
4365    struct FixedSizeEof(usize, std::cell::Cell<usize>);
4366
4367    impl Decoder for FixedSizeEof {
4368        type Item = Bytes;
4369        type Error = io::Error;
4370
4371        fn decode(&self, src: &mut BytesMut) -> Result<Option<Bytes>, io::Error> {
4372            FixedSize(self.0).decode(src)
4373        }
4374
4375        fn decode_eof(&self, src: &mut BytesMut) -> Result<Option<Bytes>, io::Error> {
4376            self.1.set(self.1.get() + 1);
4377            if src.is_empty() {
4378                Ok(None)
4379            } else {
4380                let len = src.len().min(self.0);
4381                Ok(Some(src.split_to(len)))
4382            }
4383        }
4384    }
4385
4386    #[ntex::test]
4387    async fn recv_uses_decode_eof_after_eof() {
4388        let (client, server) = IoTest::create();
4389        client.remote_buffer_cap(1024);
4390        let io = Io::new(server, SharedCfg::new("SRV"));
4391        let codec = FixedSizeEof(8, std::cell::Cell::new(0));
4392
4393        client.write("12345678");
4394        sleep(Millis(25)).await;
4395        assert_eq!(io.recv(&codec).await.unwrap().unwrap(), "12345678");
4396        // not at eof yet
4397        assert_eq!(codec.1.get(), 0);
4398
4399        // a partial frame, then the peer goes away
4400        client.write("123");
4401        sleep(Millis(25)).await;
4402        client.close().await;
4403
4404        assert_eq!(io.recv(&codec).await.unwrap().unwrap(), "123");
4405        assert!(io.recv(&codec).await.unwrap().is_none());
4406        assert!(codec.1.get() >= 2);
4407        assert_eq!(io.with_read_dst(|b| b.len()), 0);
4408    }
4409
4410    #[ntex::test]
4411    async fn recv_local_shutdown_is_not_truncation() {
4412        let (client, server) = IoTest::create();
4413        client.remote_buffer_cap(1024);
4414        let io = Io::new(server, SharedCfg::new("SRV"));
4415
4416        // undecodable input is left buffered, but the peer never closed and
4417        // the shutdown is started locally, so this is not a truncated stream
4418        client.write("123");
4419        sleep(Millis(25)).await;
4420        io.close();
4421        sleep(Millis(25)).await;
4422
4423        assert!(io.recv(&FixedSize(8)).await.unwrap().is_none());
4424        assert_eq!(io.with_read_dst(|b| b.len()), 3);
4425    }
4426
4427    #[ntex::test]
4428    async fn read_pause_stops_transport_reads() {
4429        let (client, server) = IoTest::create();
4430        client.remote_buffer_cap(1024);
4431        let io = Io::new(server, SharedCfg::new("SRV"));
4432
4433        assert!(lazy(|cx| io.poll_read_pause(cx)).await.is_pending());
4434        assert!(io.flags().is_read_paused());
4435        assert!(lazy(|cx| io.poll_read_pause(cx)).await.is_pending());
4436
4437        // the transport does not read while paused
4438        client.write("data");
4439        sleep(Millis(25)).await;
4440        assert_eq!(io.st().buffer.read_dst_size(), 0);
4441
4442        // status updates are still reported
4443        io.st().notify_timeout();
4444        assert!(matches!(
4445            lazy(|cx| io.poll_read_pause(cx)).await,
4446            Poll::Ready(IoStatusUpdate::Timeout)
4447        ));
4448
4449        // waiting for input cancels the pause
4450        assert_eq!(io.read_notify().await.unwrap(), Some(()));
4451        assert!(!io.flags().is_read_paused());
4452        assert_eq!(io.with_read_dst(BytesMut::take), b"data");
4453    }
4454
4455    /// Querying the read destination size leaves the read state in place.
4456    #[ntex::test]
4457    async fn read_dst_size_keeps_read_state() {
4458        let (client, server) = IoTest::create();
4459        client.remote_buffer_cap(1024);
4460        let io = Io::new(server, SharedCfg::new("SRV"));
4461
4462        // an installed read pause is not cancelled
4463        assert!(lazy(|cx| io.poll_read_pause(cx)).await.is_pending());
4464        assert_eq!(io.read_dst_size(), 0);
4465        assert!(io.flags().is_read_paused());
4466
4467        // read readiness is not cleared
4468        client.write("data");
4469        assert_eq!(io.read_notify().await.unwrap(), Some(()));
4470        io.st().flags.set_read_ready();
4471        assert_eq!(io.read_dst_size(), 4);
4472        assert!(io.flags().is_read_ready());
4473        assert_eq!(io.with_read_dst(BytesMut::take), b"data");
4474        assert!(!io.flags().is_read_ready());
4475    }
4476
4477    struct Failing;
4478
4479    impl Decoder for Failing {
4480        type Item = Bytes;
4481        type Error = &'static str;
4482
4483        fn decode(&self, _: &mut BytesMut) -> Result<Option<Bytes>, &'static str> {
4484            Err("invalid frame")
4485        }
4486    }
4487
4488    #[ntex::test]
4489    async fn recv_reports_decoder_error() {
4490        let (client, server) = IoTest::create();
4491        client.remote_buffer_cap(1024);
4492        let io = Io::new(server, SharedCfg::new("SRV"));
4493
4494        client.write("data");
4495        let Err(Either::Left(err)) = io.recv(&Failing).await else {
4496            panic!("expected a decoder error")
4497        };
4498        assert_eq!(err, "invalid frame");
4499    }
4500
4501    /// An expired timer is reported before decoding, the buffered input is
4502    /// left for the next attempt.
4503    #[ntex::test]
4504    async fn poll_recv_decode_reports_timeout_before_decoding() {
4505        let (client, server) = IoTest::create();
4506        client.remote_buffer_cap(1024);
4507        let io = Io::new(server, SharedCfg::new("SRV"));
4508
4509        client.write("data");
4510        sleep(Millis(25)).await;
4511        io.st().notify_timeout();
4512
4513        let res = lazy(|cx| io.poll_recv_decode(&BytesCodec, cx)).await;
4514        assert!(matches!(res, Err(RecvError::Timeout)));
4515        assert_eq!(io.st().buffer.read_dst_size(), 4);
4516
4517        let decoded = lazy(|cx| io.poll_recv_decode(&BytesCodec, cx))
4518            .await
4519            .unwrap();
4520        assert_eq!(decoded.item.unwrap(), "data");
4521        assert_eq!((decoded.consumed, decoded.remains), (4, 0));
4522    }
4523
4524    /// Write backpressure is reported before decoding, the buffered input is
4525    /// left for the next attempt.
4526    #[ntex::test]
4527    async fn poll_recv_decode_reports_write_backpressure_before_decoding() {
4528        let (client, server) = IoTest::create();
4529        client.remote_buffer_cap(1024);
4530        let io = Io::new(server, SharedCfg::new("SRV"));
4531
4532        client.write("data");
4533        sleep(Millis(25)).await;
4534        io.st().flags.set_wr_backpressure();
4535
4536        let res = lazy(|cx| io.poll_recv_decode(&BytesCodec, cx)).await;
4537        assert!(matches!(res, Err(RecvError::WriteBackpressure)));
4538        assert_eq!(io.st().buffer.read_dst_size(), 4);
4539
4540        io.flush(false).await.unwrap();
4541        let decoded = lazy(|cx| io.poll_recv_decode(&BytesCodec, cx))
4542            .await
4543            .unwrap();
4544        assert_eq!(decoded.item.unwrap(), "data");
4545    }
4546
4547    /// A closing connection reports neither an expired timer nor write
4548    /// backpressure, the buffered input is decoded before the disconnect.
4549    #[ntex::test]
4550    async fn poll_recv_decode_closing_decodes_buffered_input() {
4551        let (client, server) = IoTest::create();
4552        client.remote_buffer_cap(1024);
4553        let io = Io::new(server, SharedCfg::new("SRV"));
4554
4555        client.write("data");
4556        sleep(Millis(25)).await;
4557        io.close();
4558        sleep(Millis(25)).await;
4559        io.st().notify_timeout();
4560        io.st().flags.set_wr_backpressure();
4561
4562        let decoded = lazy(|cx| io.poll_recv_decode(&BytesCodec, cx))
4563            .await
4564            .unwrap();
4565        assert_eq!(decoded.item.unwrap(), "data");
4566
4567        let res = lazy(|cx| io.poll_recv_decode(&BytesCodec, cx)).await;
4568        assert!(matches!(res, Err(RecvError::PeerGone(None))));
4569    }
4570
4571    #[ntex::test]
4572    async fn poll_flush_enables_write_backpressure() {
4573        let (client, server) = IoTest::create();
4574        client.remote_buffer_cap(0);
4575        let io = Io::new(
4576            server,
4577            SharedCfg::new("SRV").add(IoConfig::default().set_write_backpressure(16)),
4578        );
4579
4580        // output buffered without a state update
4581        io.with_write_dst(|buf| buf.extend_from_slice(BIN2));
4582        assert!(!io.flags().is_wr_backpressure());
4583
4584        assert!(lazy(|cx| io.poll_flush(cx, false)).await.is_pending());
4585        assert!(io.flags().is_wr_backpressure());
4586
4587        client.remote_buffer_cap(1024);
4588        assert_eq!(client.read().await.unwrap(), BIN2);
4589        io.flush(false).await.unwrap();
4590        assert!(!io.flags().is_wr_backpressure());
4591    }
4592
4593    /// Filter chain wrapper that is not ready for reads or writes while
4594    /// blocked.
4595    struct Gate<F>(F, Rc<Cell<bool>>, Rc<Cell<bool>>);
4596
4597    impl<F: Filter> Filter for Gate<F> {
4598        fn query(&self, id: std::any::TypeId) -> Option<Box<dyn std::any::Any>> {
4599            self.0.query(id)
4600        }
4601
4602        fn process_read_buf(&self, ctx: &mut crate::FilterCtx<'_>) -> io::Result<()> {
4603            self.0.process_read_buf(ctx)
4604        }
4605
4606        fn process_write_buf(&self, ctx: &mut crate::FilterCtx<'_>) -> io::Result<()> {
4607            self.0.process_write_buf(ctx)
4608        }
4609
4610        fn shutdown(&self, ctx: &mut crate::FilterCtx<'_>) -> io::Result<Poll<()>> {
4611            self.0.shutdown(ctx)
4612        }
4613
4614        fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll<Readiness> {
4615            match self.0.poll_read_ready(cx) {
4616                Poll::Ready(Readiness::Ready) if self.1.get() => Poll::Pending,
4617                res => res,
4618            }
4619        }
4620
4621        fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll<Readiness> {
4622            match self.0.poll_write_ready(cx) {
4623                Poll::Ready(Readiness::Ready) if self.2.get() => Poll::Pending,
4624                res => res,
4625            }
4626        }
4627    }
4628
4629    #[derive(Debug)]
4630    struct GateTransport;
4631
4632    impl IoStream for GateTransport {
4633        fn start(self, _: IoContext) -> Box<dyn Handle> {
4634            Box::new(self)
4635        }
4636    }
4637
4638    impl Handle for GateTransport {}
4639
4640    #[ntex::test]
4641    async fn filter_pause_pauses_reading() {
4642        let blocked = Rc::new(Cell::new(false));
4643        let b = blocked.clone();
4644        let io = Io::new(GateTransport, SharedCfg::default())
4645            .map_filter(move |f| Gate(f, b, Rc::default()));
4646        let ctx = IoContext::new(io.get_ref());
4647
4648        assert_eq!(
4649            lazy(|cx| ctx.poll_read_ready(cx)).await,
4650            Poll::Ready(Readiness::Ready)
4651        );
4652        assert!(!io.is_read_filter_paused());
4653
4654        // the filter is not ready while the io state allows reads
4655        assert!(lazy(|cx| io.poll_read_more(cx)).await.is_pending());
4656        blocked.set(true);
4657        assert!(lazy(|cx| ctx.poll_read_ready(cx)).await.is_pending());
4658        assert!(io.is_read_filter_paused());
4659        assert!(!io.st().dispatch_task.is_set(), "dispatcher is notified");
4660
4661        // a continued pause does not notify again
4662        assert!(lazy(|cx| io.poll_read_more(cx)).await.is_pending());
4663        assert!(lazy(|cx| ctx.poll_read_ready(cx)).await.is_pending());
4664        assert!(io.st().dispatch_task.is_set());
4665
4666        // a read already in flight is accepted, but reading stays paused
4667        let mut buf = ctx.take_read_buf();
4668        buf.extend_from_slice(b"12");
4669        assert_eq!(
4670            ctx.release_read_buf(buf, Poll::Ready(Ok(2))),
4671            IoTaskStatus::Pause
4672        );
4673        assert_eq!(io.with_read_dst(BytesMut::take), b"12");
4674        assert_eq!(
4675            ctx.with_read_buf(|_| Poll::<io::Result<usize>>::Pending),
4676            IoTaskStatus::Pause
4677        );
4678
4679        // the filter is ready again
4680        assert!(lazy(|cx| io.poll_read_more(cx)).await.is_pending());
4681        blocked.set(false);
4682        assert_eq!(
4683            lazy(|cx| ctx.poll_read_ready(cx)).await,
4684            Poll::Ready(Readiness::Ready)
4685        );
4686        assert!(!io.is_read_filter_paused());
4687        assert!(!io.st().dispatch_task.is_set(), "dispatcher is notified");
4688        assert_eq!(
4689            ctx.with_read_buf(|_| Poll::<io::Result<usize>>::Pending),
4690            IoTaskStatus::Io
4691        );
4692    }
4693
4694    #[ntex::test]
4695    async fn io_state_pause_is_not_filter_pause() {
4696        let blocked = Rc::new(Cell::new(true));
4697        let b = blocked.clone();
4698        let io = Io::new(GateTransport, SharedCfg::default())
4699            .map_filter(move |f| Gate(f, b, Rc::default()));
4700        let ctx = IoContext::new(io.get_ref());
4701
4702        // the dispatcher paused reads, the filter pause is not reported
4703        io.st().flags.set_read_paused();
4704        assert!(lazy(|cx| ctx.poll_read_ready(cx)).await.is_pending());
4705        assert!(!io.is_read_filter_paused());
4706
4707        // the dispatcher resumes reads while the filter is still not ready
4708        io.st().flags.unset_read_paused();
4709        assert!(lazy(|cx| ctx.poll_read_ready(cx)).await.is_pending());
4710        assert!(io.is_read_filter_paused());
4711
4712        // the io state pauses reads again, the filter pause is kept
4713        io.st().flags.set_read_paused();
4714        assert!(lazy(|cx| ctx.poll_read_ready(cx)).await.is_pending());
4715        assert!(io.is_read_filter_paused());
4716
4717        // a ready chain ends the filter pause
4718        blocked.set(false);
4719        io.st().flags.unset_read_paused();
4720        assert_eq!(
4721            lazy(|cx| ctx.poll_read_ready(cx)).await,
4722            Poll::Ready(Readiness::Ready)
4723        );
4724        assert!(!io.is_read_filter_paused());
4725    }
4726
4727    #[ntex::test]
4728    async fn filter_pause_pauses_writing() {
4729        let blocked = Rc::new(Cell::new(true));
4730        let b = blocked.clone();
4731        let io = Io::new(GateTransport, SharedCfg::default())
4732            .map_filter(move |f| Gate(f, Rc::default(), b));
4733        let ctx = IoContext::new(io.get_ref());
4734
4735        // no output is waiting, the filter pause is not reported
4736        assert!(lazy(|cx| ctx.poll_write_ready(cx)).await.is_pending());
4737        assert!(!io.is_write_filter_paused());
4738
4739        // output waits for the filter
4740        io.encode_slice(b"1234").unwrap();
4741        sleep(Millis(10)).await;
4742        assert!(!io.st().flags.is_write_paused());
4743        assert!(lazy(|cx| io.poll_read_more(cx)).await.is_pending());
4744        assert!(lazy(|cx| ctx.poll_write_ready(cx)).await.is_pending());
4745        assert!(io.is_write_filter_paused());
4746        assert!(!io.st().dispatch_task.is_set(), "dispatcher is notified");
4747
4748        // a write already in flight completes, writing stays paused
4749        ctx.with_write_dst(|dst| {
4750            let mut page = dst.take().unwrap();
4751            page.advance_to(2);
4752            dst.prepend(page);
4753        });
4754        assert_eq!(ctx.update_write_status(Ok(2)), IoTaskStatus::Pause);
4755        assert!(!io.st().flags.is_write_paused());
4756        assert_eq!(io.st().buffer.write_buf_size(), 2);
4757
4758        // the filter is ready again
4759        assert!(lazy(|cx| io.poll_read_more(cx)).await.is_pending());
4760        blocked.set(false);
4761        assert_eq!(
4762            lazy(|cx| ctx.poll_write_ready(cx)).await,
4763            Poll::Ready(Readiness::Ready)
4764        );
4765        assert!(!io.is_write_filter_paused());
4766        assert!(!io.st().dispatch_task.is_set(), "dispatcher is notified");
4767        assert_eq!(ctx.update_write_status(Ok(0)), IoTaskStatus::Io);
4768    }
4769}