Skip to main content

ntex_io/
ctx.rs

1use std::{fmt, io, task::Context, task::Poll};
2
3use ntex_bytes::{BytePages, BytesMut};
4use ntex_util::time::{Seconds, sleep};
5
6use crate::filter::{read_readiness, write_readiness};
7use crate::{Flags, Id, IoRef, IoTaskStatus, Readiness, io::IoState};
8
9/// Connection context shared with transport read and write tasks.
10///
11/// Transport implementations obtain buffers from this context, perform
12/// nonblocking I/O, and return completion through
13/// [`release_read_buf`](Self::release_read_buf) and
14/// [`update_write_status`](Self::update_write_status). Their return value tells
15/// the task whether to continue, pause until notified, or stop.
16///
17/// # Shutdown
18///
19/// A transport task runs until [`poll_read_ready`](Self::poll_read_ready) or
20/// [`poll_write_ready`](Self::poll_write_ready) reports [`Readiness::Close`] or
21/// [`Readiness::Terminate`], or until a status update returns
22/// [`IoTaskStatus::Stop`]. All of those imply that the connection is already
23/// closing or closed.
24///
25/// Graceful shutdown runs in two phases. In the first the filters shut down
26/// while both directions stay open. In the second, buffered output is drained
27/// into the transport while the read side is paused;
28/// [`Readiness::Close`] is reported only once nothing is left to write. A
29/// single shutdown timeout bounds both phases. If it elapses during filter
30/// shutdown, that phase is skipped and transport shutdown begins. If it
31/// elapses during transport shutdown, the connection closes and any remaining
32/// output is discarded.
33///
34/// So by the time the loop exits there is nothing left to drain, whether the
35/// connection was shut down gracefully or terminated. A task must never attempt
36/// a final flush on the way out; it should release the transport immediately
37/// and report the outcome through [`stopped`](Self::stopped). The two variants
38/// differ only in how the transport is released: [`Readiness::Close`] closes
39/// both directions gracefully, while [`Readiness::Terminate`] skips the
40/// graceful close so that an aborted connection stays distinguishable from one
41/// that ended normally. `Terminate` is reported for an explicit
42/// [`IoRef::terminate`](crate::IoRef::terminate), and when [`Io`](crate::Io) is
43/// dropped while output it accepted has not reached the transport, because the
44/// filter chain goes away with it and that output can never be delivered. Every
45/// other way a connection can end, an expired shutdown timeout included,
46/// reports `Close`.
47// `repr(transparent)` is required: `IoRef::call_write` casts `&IoRef` to `&IoContext`.
48#[repr(transparent)]
49pub struct IoContext(IoRef);
50
51impl IoContext {
52    pub(crate) fn new(io: IoRef) -> Self {
53        Self(io)
54    }
55
56    fn st(&self) -> &IoState {
57        &self.0.0
58    }
59
60    #[inline]
61    /// Gets the ID.
62    pub fn id(&self) -> Id {
63        self.0.id()
64    }
65
66    #[inline]
67    /// Gets the I/O tag.
68    pub fn tag(&self) -> &'static str {
69        self.0.tag()
70    }
71
72    #[doc(hidden)]
73    /// Gets the state flags. (for debug purpose only)
74    pub fn flags(&self) -> Flags {
75        self.0.flags()
76    }
77
78    #[inline]
79    /// Gets the configured shutdown timeout.
80    ///
81    /// A backend whose own teardown can stall, such as one waiting for
82    /// cancelled operations to complete, can bound it with this, as the
83    /// graceful shutdown before it is bounded.
84    pub fn shutdown_timeout(&self) -> Seconds {
85        self.0.cfg().shutdown_timeout()
86    }
87
88    #[inline]
89    /// Checks readiness for read operations.
90    ///
91    /// Resolves to [`Readiness::Ready`], [`Readiness::Close`] or
92    /// [`Readiness::Terminate`], or stays `Pending`. Reads continue through the
93    /// filter shutdown phase so that filters can complete theirs, and are
94    /// paused for the transport shutdown phase, so `Close` is resolved here
95    /// only once the connection is terminated.
96    ///
97    /// A filter that is not ready while the io state allows reads pauses
98    /// them, see [`IoRef::is_read_filter_paused`]. The dispatcher is notified
99    /// when the pause starts and when it ends.
100    pub fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll<Readiness> {
101        let st = self.st();
102        if st.flags.is_force_closing() {
103            // The filter chain is replaced by `NullFilter` when `Io` is
104            // dropped, so the force-close decision is made here rather than in
105            // the chain: it has to survive that replacement.
106            return Poll::Ready(Readiness::Terminate);
107        }
108        self.poll_filters_shutdown(cx);
109        let res = {
110            let _borrow = st.buffer.borrow();
111            self.0.filter().poll_read_ready(cx)
112        };
113
114        if res.is_pending() {
115            // A pause the io state accounts for is known to the dispatcher. A
116            // filter pause is kept until the chain is ready again.
117            if !st.flags.is_read_filter_paused()
118                && read_readiness(st) == Poll::Ready(Readiness::Ready)
119            {
120                log::trace!("{}: Filter is not ready, pause reading", st.tag());
121                st.flags.set_read_filter_paused();
122                st.wake_dispatch_task();
123            }
124        } else if st.flags.is_read_filter_paused() {
125            log::trace!("{}: Filter is ready, resume reading", st.tag());
126            st.flags.unset_read_filter_paused();
127            st.wake_dispatch_task();
128        }
129        res
130    }
131
132    #[inline]
133    /// Checks readiness for write operations.
134    ///
135    /// Resolves to [`Readiness::Ready`], [`Readiness::Close`] or
136    /// [`Readiness::Terminate`], or stays `Pending`. Unlike the read path this
137    /// reports `Close` at the end of a graceful shutdown as well, once buffered
138    /// output has been drained, so the task must not flush again.
139    ///
140    /// A filter that is not ready while output is waiting pauses writes, see
141    /// [`IoRef::is_write_filter_paused`]. The dispatcher is notified when the
142    /// pause starts and when it ends.
143    pub fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll<Readiness> {
144        let st = self.st();
145        if st.flags.is_force_closing() {
146            // see `poll_read_ready`
147            return Poll::Ready(Readiness::Terminate);
148        }
149        self.poll_shutdown_deadline(cx);
150        let res = {
151            let _borrow = st.buffer.borrow();
152            self.0.filter().poll_write_ready(cx)
153        };
154
155        if res.is_pending() {
156            // see `poll_read_ready`
157            if !st.flags.is_write_filter_paused()
158                && write_readiness(st) == Poll::Ready(Readiness::Ready)
159            {
160                log::trace!("{}: Filter is not ready, pause writing", st.tag());
161                st.flags.set_write_filter_paused();
162                st.wake_dispatch_task();
163            }
164        } else if st.flags.is_write_filter_paused() {
165            log::trace!("{}: Filter is ready, resume writing", st.tag());
166            st.flags.unset_write_filter_paused();
167            st.wake_dispatch_task();
168        }
169        res
170    }
171
172    /// Stops I/O processing without graceful filter shutdown.
173    ///
174    /// Pending application work is not drained. Unlike
175    /// [`IoRef::terminate`](crate::IoRef::terminate), this does not request an
176    /// aborted transport release: the transport observes
177    /// [`Readiness::Close`] and closes both directions gracefully. Call
178    /// [`stopped`](Self::stopped) afterwards, once transport teardown has
179    /// actually finished.
180    pub fn stop(&self, e: Option<io::Error>) {
181        self.st().terminate_connection(e);
182    }
183
184    /// Marks backend transport teardown as complete.
185    pub fn stopped(&self, e: Option<io::Error>) {
186        self.st().stop_connection(e);
187    }
188
189    /// Takes a buffer for the next transport read.
190    ///
191    /// The returned buffer must be released exactly once through
192    /// [`release_read_buf`](Self::release_read_buf), even when the read
193    /// fails or would otherwise stop the task.
194    ///
195    /// This hands out a buffer of its own while the dispatcher still has input
196    /// to consume, because the read buffer is moved out of the io state until
197    /// it is released and the dispatcher would not find it. A transport whose
198    /// read completes without suspending can avoid that with
199    /// [`with_read_buf`](Self::with_read_buf).
200    pub fn take_read_buf(&self) -> BytesMut {
201        let st = self.st();
202
203        if st.flags.is_read_ready() {
204            // The dispatcher has not consumed the read buffer yet, so it must
205            // stay in place and the read goes to a buffer of its own.
206            st.get_read_buf()
207        } else if let Some(mut buf) = st.buffer.get_read_buf() {
208            buf.reserve_more();
209            buf
210        } else {
211            st.get_read_buf()
212        }
213    }
214
215    /// Releases a transport read buffer and reports the read result.
216    ///
217    /// This is the counterpart of [`take_read_buf`](Self::take_read_buf); every
218    /// buffer it hands out must come back here exactly once.
219    ///
220    /// `Poll::Ready(Ok(n))` reports that `n` bytes were appended to `buf`.
221    /// Zero marks the transport read side as closed and invokes the read filter
222    /// chain once with no new bytes. This lets filters emit final buffered data
223    /// or report truncated input. Further transport reads are parked, but
224    /// buffered input remains decodable and the write side remains usable until
225    /// graceful shutdown. `Poll::Ready(Err(_))` terminates the connection.
226    /// `Poll::Pending` returns the buffer after a nonblocking operation made no
227    /// progress or a submitted operation was canceled for reissue.
228    ///
229    /// The returned [`IoTaskStatus`] instructs the read task to continue
230    /// immediately, pause until notified, or stop.
231    pub fn release_read_buf(&self, buf: BytesMut, status: Poll<io::Result<usize>>) -> IoTaskStatus {
232        let st = self.st();
233        let orig = st.buffer.read_dst_size();
234
235        #[cfg(feature = "trace")]
236        log::trace!(
237            "{}: read-status == {status:?} orig:{orig:?} flags:{:?}",
238            st.tag(),
239            st.flags
240        );
241
242        // Transport shutdown phase, the filters are shut down and the
243        // connection is about to be closed. The read task is paused, but a read
244        // issued before the transition can still complete here; its input is
245        // discarded because nothing can consume it anymore.
246        if st.flags.is_stopping() {
247            let mut buf = buf;
248            buf.clear();
249            st.buffer.set_read_buf(buf);
250            stopping_read_status(st, &status)
251        } else {
252            let mut buf = buf;
253            track_read(st, &buf, &status);
254            if st.is_io_dropped() {
255                // the `Io` is gone, nothing can consume this input anymore
256                buf.clear();
257            }
258            // release read buffer
259            st.buffer.set_read_buf(buf);
260
261            self.process_read_status(orig, status)
262        }
263    }
264
265    /// Reads into the read buffer in place and reports the read result.
266    ///
267    /// This is the counterpart of [`take_read_buf`](Self::take_read_buf) and
268    /// [`release_read_buf`](Self::release_read_buf) for a transport whose
269    /// read completes without suspending. `f` reads into the buffer it is
270    /// given and reports the same status `release_read_buf` takes, with the
271    /// same meaning.
272    ///
273    /// The buffer is not moved out of the io state for the duration of the
274    /// call, so no temporary buffer is taken from the pool and no append is
275    /// needed to put the result back. A transport that keeps the buffer across
276    /// a suspension point cannot use this: the buffer would be missing while
277    /// the dispatcher looks for input, so it must take one of its own through
278    /// `take_read_buf` instead.
279    ///
280    /// `f` must not read from this io again. Nested read access is unsupported
281    /// and may terminate the connection or lose nested buffer changes.
282    pub fn with_read_buf<F>(&self, f: F) -> IoTaskStatus
283    where
284        F: FnOnce(&mut BytesMut) -> Poll<io::Result<usize>>,
285    {
286        let st = self.st();
287        let orig = st.buffer.read_dst_size();
288        let stopping = st.flags.is_stopping();
289        let discard = stopping || st.is_io_dropped();
290
291        let status = st.buffer.with_read_src(&self.0, |buf| {
292            buf.reserve_more();
293            let status = f(buf);
294            if !stopping {
295                track_read(st, buf, &status);
296            }
297            if discard {
298                // the filters are done or the `Io` is gone, nothing can
299                // consume this input anymore
300                buf.clear();
301            }
302            status
303        });
304
305        #[cfg(feature = "trace")]
306        log::trace!(
307            "{}: rd-status = {status:?} orig:{orig:?} flags:{:?}",
308            st.tag(),
309            st.flags
310        );
311
312        if stopping {
313            stopping_read_status(st, &status)
314        } else {
315            self.process_read_status(orig, status)
316        }
317    }
318
319    /// Processes input that reached the transport-facing read buffer.
320    ///
321    /// `orig` is the size of the destination read buffer before the read.
322    fn process_read_status(&self, orig: usize, status: Poll<io::Result<usize>>) -> IoTaskStatus {
323        let st = self.st();
324
325        // process read buf
326        let result = match status {
327            Poll::Pending => Ok(()),
328            Poll::Ready(status) => status.and_then(|nbytes| {
329                if nbytes == 0 {
330                    if st.flags.is_read_eof() {
331                        // A clean eof is reported to the filter chain exactly
332                        // once, no matter how often the transport reports it.
333                        return Ok(());
334                    }
335                    st.flags.set_read_eof();
336                    st.wake_dispatch_task();
337                }
338
339                st.buffer.process_read_buf(&self.0).and_then(|status| {
340                    let size = st.buffer.read_dst_size();
341
342                    // The destination read buffer has new data, wake up the dispatcher
343                    if size > orig {
344                        if st.is_rd_backpressure_needed(size) {
345                            log::trace!("{}: Read buf({size}), enable back-pressure", st.tag());
346                            st.flags.set_read_ready_and_backpressure();
347                        } else {
348                            st.flags.set_read_ready();
349                        }
350                        #[cfg(feature = "trace")]
351                        log::trace!("{}: New {size} bytes available", st.tag());
352                        st.wake_dispatch_task();
353                    }
354
355                    if st.flags.is_read_notify() {
356                        // If the "notify" flag is set, we must wake the
357                        // dispatcher task whenever data is read from the source.
358                        st.wake_dispatch_task();
359                        st.flags.set_read_notified();
360                    }
361
362                    // A filter may write data while processing reads, for
363                    // example a TLS handshake record. Such output can land in
364                    // an intermediate buffer that `write_buf_size()` does not
365                    // account for, so the write chain is forced from the
366                    // outermost layer to move it to the transport.
367                    if status.wants_write {
368                        st.buffer.process_write_buf_force(&self.0)?;
369                        self.0.consolidate_write_state(false)?;
370
371                        // Output produced by reading, for example replies to
372                        // peer pings, must not grow without bound while the
373                        // peer does not read. Reads pause until it drains or
374                        // the dispatcher asks for more input.
375                        if st.is_wr_backpressure_needed(st.transport_outstanding()) {
376                            log::trace!("{}: Write buf is full, pause reading", st.tag());
377                            st.flags.set_read_wr_backpressure();
378                        }
379                    }
380
381                    // The input may be what a filter waits for to complete its
382                    // shutdown, e.g. the peer's TLS close_notify, which is
383                    // polled by the read task. Only actual input wakes it, a
384                    // pending read would make the read task spin until the
385                    // shutdown deadline.
386                    if st.flags.is_shutting_down_filters() {
387                        st.wake_read_task();
388                    }
389                    Ok(())
390                })
391            }),
392        };
393
394        if let Err(err) = result {
395            // A read failure while the filters are shutting down does not
396            // terminate the connection: the filter handshake cannot complete,
397            // but buffered output is still drained by the transport shutdown
398            // phase.
399            if st.flags.is_stopping_filters() {
400                // Output the filters produced before the failure, for example
401                // a close notification, may sit in an intermediate buffer that
402                // the transport shutdown phase does not drain.
403                let _ = st.buffer.process_write_buf_force(&self.0);
404                stop_filters(st, Some(err));
405                IoTaskStatus::Pause
406            } else {
407                st.terminate_connection(Some(err));
408                IoTaskStatus::Stop
409            }
410        } else if st.flags.is_aborted() {
411            IoTaskStatus::Stop
412        } else if st.flags.is_read_eof()
413            || st.flags.is_read_paused_or_backpressure()
414            || st.flags.is_read_filter_paused()
415            || (st.flags.is_read_wr_backpressure() && !st.flags.is_stopping_filters())
416        {
417            IoTaskStatus::Pause
418        } else {
419            IoTaskStatus::Io
420        }
421    }
422
423    /// Provides mutable access to the transport-facing write destination.
424    ///
425    /// This holds the encoded bytes that are ready to be written out.
426    ///
427    /// Pending filter output is processed before `f` is invoked. The transport
428    /// may write bytes out directly, or take ownership of pages and write them
429    /// later; any page it removes is counted as in-flight output until it is
430    /// either returned to this buffer or reported as written through
431    /// [`update_write_status`](Self::update_write_status).
432    ///
433    /// # Panics
434    ///
435    /// Panics if the closure accesses the transport-facing write buffer again.
436    pub fn with_write_dst<F, R>(&self, f: F) -> R
437    where
438        F: FnOnce(&mut BytePages) -> R,
439    {
440        let st = self.st();
441
442        // Write buffer processing may be delayed
443        if let Err(e) = st.buffer.process_write_buf(&self.0) {
444            st.terminate_connection(Some(e));
445        }
446
447        let before = st.buffer.write_buf_size();
448        let result = st.buffer.with_write_dst(|buffer| f(buffer));
449        st.track_wr_inflight(before, st.buffer.write_buf_size());
450
451        result
452    }
453
454    /// Updates the write status.
455    ///
456    /// `Ok(n)` reports that the write attempt completed without error and that
457    /// `n` bytes reached the peer; `n` is zero when the attempt moved nothing.
458    /// Any page the transport is still holding stays counted as outstanding
459    /// output, so it must either be returned to the write buffer or reported
460    /// here. An error terminates the connection. The returned
461    /// [`IoTaskStatus`] instructs the write task to continue, pause until
462    /// notified, or stop.
463    pub fn update_write_status(&self, status: io::Result<usize>) -> IoTaskStatus {
464        let st = &self.st();
465
466        #[cfg(feature = "trace")]
467        log::trace!(
468            "{}: write-status == {status:?} buf:{} inflight:{} flags:{:?}",
469            st.tag(),
470            st.buffer.write_buf_size(),
471            st.wr_inflight.get(),
472            st.flags
473        );
474
475        match status {
476            Ok(written) => {
477                st.wr_inflight_written(written);
478
479                let len = st.buffer.write_buf_size();
480                let outstanding = st.write_outstanding();
481
482                // Full flush is active
483                if st.flags.is_write_flush() {
484                    // All output must reach the peer, including in-flight pages
485                    if outstanding == 0 {
486                        st.wake_dispatch_task();
487                    }
488                } else if st.flags.is_wr_backpressure()
489                    && st.should_disable_wr_backpressure(outstanding)
490                {
491                    // Write backpressure is active and outstanding output is
492                    // below the threshold
493                    st.wake_dispatch_task();
494                }
495
496                // Producers waiting for the write back-pressure release, the
497                // flag itself is released by the dispatcher. Repeated calls
498                // until then are cheap, a notified waiter's waker is consumed.
499                if st.flags.is_wr_backpressure() && st.should_disable_wr_backpressure(outstanding) {
500                    st.wake_write_waiters();
501                }
502
503                // Reads paused by their own output resume once it drains
504                if st.flags.is_read_wr_backpressure()
505                    && st.should_disable_wr_backpressure(st.transport_outstanding())
506                {
507                    st.flags.unset_read_wr_backpressure();
508                    st.wake_read_task();
509                }
510
511                if st.flags.is_aborted() {
512                    IoTaskStatus::Stop
513                } else if len == 0 {
514                    // Nothing left to submit, pause the write task. In-flight
515                    // pages are not actionable here, their completion wakes
516                    // the task again.
517                    st.flags.set_write_paused();
518                    if st.flags.is_stopping_filters() {
519                        st.wake_read_task();
520                    }
521                    if st.flags.is_stopping() && outstanding == 0 {
522                        // The transport shutdown phase ends once buffered
523                        // output is drained, but only `poll_write_ready`
524                        // reports that, so the write task has to run once more
525                        // to observe it. A backend that drives both directions
526                        // from a single task is covered by the read wake above,
527                        // one that splits them is not.
528                        st.wake_write_task();
529                    }
530                    IoTaskStatus::Pause
531                } else {
532                    st.flags.unset_write_paused();
533                    if st.flags.is_write_filter_paused() {
534                        // the write task waits for the filter chain, it is
535                        // not paused for lack of output
536                        IoTaskStatus::Pause
537                    } else {
538                        IoTaskStatus::Io
539                    }
540                }
541            }
542            Err(err) => {
543                st.terminate_connection(Some(err));
544                IoTaskStatus::Stop
545            }
546        }
547    }
548
549    /// Drives the filter shutdown phase.
550    ///
551    /// This is polled from [`poll_read_ready`](Self::poll_read_ready), so the
552    /// read task advances the phase, and does nothing unless it is active.
553    /// Both directions stay open here: a filter may emit its closing data and
554    /// still read the peer's.
555    ///
556    /// The phase ends once every filter reports ready and its output has
557    /// reached the transport, and the transport shutdown phase begins. It is
558    /// also ended early when the filters cannot finish: after a clean read EOF,
559    /// because no further input can arrive, which is a normal close rather than
560    /// an error; when reads are paused or back-pressured, which is reported as
561    /// a blocked shutdown; and when the shutdown timeout elapses. An I/O error
562    /// terminates the connection instead.
563    ///
564    /// The deadline is kept once it has expired so that
565    /// [`poll_shutdown_deadline`](Self::poll_shutdown_deadline) sees it
566    /// expired, which is what makes one `shutdown_timeout` bound both
567    /// phases.
568    fn poll_filters_shutdown(&self, cx: &mut Context<'_>) {
569        let st = &self.st();
570        if !st.flags.is_shutting_down_filters() {
571            return;
572        }
573
574        // process filter shutdown
575        let ready = match st.buffer.process_shutdown(&self.0) {
576            Ok(Poll::Ready(())) => true,
577            Ok(Poll::Pending) => false,
578            Err(err) => {
579                st.terminate_connection(Some(err));
580                return;
581            }
582        };
583        if self.0.consolidate_write_state(true).is_err() {
584            return;
585        }
586
587        // all pending output has reached the transport
588        let flushed = st.flags.is_write_paused() && !st.flags.is_wr_send_scheduled();
589
590        #[cfg(feature = "trace")]
591        log::trace!(
592            "{}: shutdown filters, done:{ready:?} flushed:{flushed:?} wr-buf:{:?}, flags:{:?}",
593            st.tag(),
594            st.buffer.write_buf_size(),
595            st.flags,
596        );
597
598        // filters are shutdown and write task is paused
599        if ready && flushed {
600            st.filters_stopped();
601            return;
602        }
603
604        // After a clean read EOF no further input can arrive, so a filter that
605        // is waiting for the peer can never finish. The peer closing first is
606        // a normal close, so this is not reported as an error.
607        let eof = !ready && st.flags.is_read_eof();
608
609        // If the read buffer is not consumed it is unlikely that the filter
610        // will ever complete its shutdown. Back-pressure counts on its own,
611        // even once the dispatcher has taken the buffered input: reads pause
612        // under it, so the transport would neither read the input the filter
613        // waits for nor arm read interest for it.
614        let blocked =
615            !ready && !eof && (st.flags.is_read_paused() || st.flags.is_rd_backpressure());
616
617        // The filter shutdown cannot complete. Move on to the transport
618        // shutdown phase, which drains whatever output has been produced so far
619        // and then closes the connection.
620        if eof || blocked {
621            if eof {
622                log::debug!("{}: Peer closed before filter shutdown completed", st.tag());
623            }
624            stop_filters(st, blocked.then(blocked_err));
625            return;
626        }
627
628        // filter shutdown timeout
629        let timeout = st
630            .shutdown_timeout
631            .take()
632            .unwrap_or_else(|| sleep(st.cfg.shutdown_timeout()));
633        if timeout.poll_elapsed(cx).is_ready() {
634            stop_filters(
635                st,
636                Some(io::Error::new(
637                    io::ErrorKind::TimedOut,
638                    "filter shutdown timed out",
639                )),
640            );
641        }
642        // the deadline is put back even once it has elapsed, so that the
643        // transport shutdown phase sees it expired instead of starting a
644        // second one
645        st.shutdown_timeout.set(Some(timeout));
646    }
647
648    /// Polls the shutdown deadline during the transport shutdown phase.
649    ///
650    /// The deadline is created when filter shutdown starts and is not reset
651    /// here, so a single `shutdown_timeout` bounds both shutdown phases. When
652    /// it elapses the connection is terminated and any output that has not
653    /// reached the transport is lost.
654    fn poll_shutdown_deadline(&self, cx: &mut Context<'_>) {
655        let st = &self.st();
656        if !st.flags.is_stopping() {
657            return;
658        }
659
660        // Nothing is left to drain, so the connection closes cleanly on the
661        // next readiness check and the deadline does not apply. Without this
662        // the shutdown would be reported as timed out whenever the filter
663        // phase happened to consume the whole deadline.
664        if st.write_outstanding() == 0 {
665            return;
666        }
667
668        let timeout = st
669            .shutdown_timeout
670            .take()
671            .unwrap_or_else(|| sleep(st.cfg.shutdown_timeout()));
672        if timeout.poll_elapsed(cx).is_ready() {
673            let len = st.write_outstanding();
674            if len != 0 {
675                log::warn!(
676                    "{}: Shutdown timed out, discarding {len} bytes of buffered output",
677                    st.tag()
678                );
679            }
680            st.terminate_connection(Some(io::Error::new(
681                io::ErrorKind::TimedOut,
682                "io shutdown timed out",
683            )));
684        } else {
685            st.shutdown_timeout.set(Some(timeout));
686        }
687    }
688}
689
690fn blocked_err() -> io::Error {
691    io::Error::other("filter shutdown blocked by unread buffered data")
692}
693
694/// Leaves the filter shutdown phase after an incomplete shutdown.
695///
696/// Output that has not reached the transport is not lost: the transport
697/// shutdown phase drains it before closing the connection.
698///
699/// The error is recorded here rather than when the failure is first
700/// detected: while an error is set, `IoRef::consolidate_write_state()`
701/// short-circuits, which would stop the write buffer from draining.
702fn stop_filters(st: &IoState, err: Option<io::Error>) {
703    if let Some(err) = err {
704        st.set_shutdown_error(err);
705    }
706    st.filters_stopped();
707}
708
709/// Adapts the connection's read page size to a transport read.
710fn track_read(st: &IoState, buf: &BytesMut, status: &Poll<io::Result<usize>>) {
711    match status {
712        Poll::Ready(Ok(n)) => st.track_read(*n, *n != 0 && buf.len() == buf.capacity()),
713        Poll::Pending => st.track_read(0, false),
714        Poll::Ready(Err(_)) => {}
715    }
716}
717
718/// Reports a read that completed during the transport shutdown phase.
719///
720/// Neither a clean eof nor an error terminates the connection: the write
721/// side keeps draining until it completes or the shutdown deadline elapses.
722fn stopping_read_status(st: &IoState, status: &Poll<io::Result<usize>>) -> IoTaskStatus {
723    match status {
724        Poll::Ready(Ok(n)) if *n != 0 => IoTaskStatus::Io,
725        Poll::Ready(_) => {
726            st.flags.set_read_eof();
727            IoTaskStatus::Pause
728        }
729        Poll::Pending => IoTaskStatus::Pause,
730    }
731}
732
733impl Clone for IoContext {
734    fn clone(&self) -> Self {
735        Self(self.0.clone())
736    }
737}
738
739impl fmt::Debug for IoContext {
740    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
741        f.debug_struct("IoContext").field("io", &self.0).finish()
742    }
743}
744
745#[cfg(test)]
746mod tests {
747    use super::*;
748    use crate::{FilterBuf, FilterLayer, Io, testing::IoTest};
749    use ntex_util::future::lazy;
750
751    #[ntex::test]
752    async fn ctx_basics() {
753        let (_, server) = IoTest::create();
754
755        let state = Io::from(server);
756        let ctx = IoContext::new(state.get_ref());
757        let _ = ctx.flags();
758        assert_ne!(ctx.id(), Id::default());
759        assert!(format!("{ctx:?}").contains("IoContext"));
760    }
761
762    #[ntex::test]
763    async fn pending_read_completion_is_not_eof() {
764        let (_, server) = IoTest::create();
765        let state = Io::from(server);
766        let ctx = IoContext::new(state.get_ref());
767
768        assert!(lazy(|cx| state.poll_read_more(cx)).await.is_pending());
769        assert_ne!(
770            ctx.release_read_buf(ctx.take_read_buf(), Poll::Pending),
771            IoTaskStatus::Stop
772        );
773        assert!(lazy(|cx| state.poll_read_more(cx)).await.is_pending());
774
775        assert_eq!(
776            ctx.release_read_buf(ctx.take_read_buf(), Poll::Ready(Ok(0))),
777            IoTaskStatus::Pause
778        );
779        assert!(matches!(
780            lazy(|cx| state.poll_read_more(cx)).await,
781            Poll::Ready(Ok(None))
782        ));
783    }
784
785    #[derive(Debug)]
786    struct FinishOnEof;
787
788    impl FilterLayer for FinishOnEof {
789        fn process_read_buf(&self, buf: &FilterBuf<'_>) -> io::Result<()> {
790            if buf.io().is_read_eof() {
791                buf.with_read_buffers(|_, dst| dst.extend_from_slice(b"final"));
792            }
793            Ok(())
794        }
795
796        fn process_write_buf(&self, _: &FilterBuf<'_>) -> io::Result<()> {
797            Ok(())
798        }
799    }
800
801    #[ntex::test]
802    async fn eof_reports_available_input_once() {
803        let (_, server) = IoTest::create();
804        let state = Io::from(server);
805        let ctx = IoContext::new(state.get_ref());
806
807        // data arrives, then a clean eof
808        ctx.release_read_buf(BytesMut::copy_from_slice(b"12345"), Poll::Ready(Ok(5)));
809        ctx.release_read_buf(ctx.take_read_buf(), Poll::Ready(Ok(0)));
810
811        // the buffered input is reported once
812        assert!(matches!(
813            lazy(|cx| state.poll_read_more(cx)).await,
814            Poll::Ready(Ok(Some(())))
815        ));
816
817        // accessing the buffer marks the input as reported
818        assert_eq!(state.with_read_dst(|b| b.len()), 5);
819        assert!(matches!(
820            lazy(|cx| state.poll_read_more(cx)).await,
821            Poll::Ready(Ok(None))
822        ));
823
824        // "no further input" does not mean the read buffer is empty, the
825        // remaining bytes are still decodable
826        assert_eq!(state.with_read_dst(BytesMut::take), b"12345");
827    }
828
829    #[ntex::test]
830    async fn shutdown_keeps_unconsumed_input_visible() {
831        let (_, server) = IoTest::create();
832        let state = Io::from(server);
833        let ctx = IoContext::new(state.get_ref());
834
835        // input arrives but the dispatcher has not consumed it yet
836        ctx.release_read_buf(BytesMut::copy_from_slice(b"12345"), Poll::Ready(Ok(5)));
837        assert!(ctx.flags().is_read_ready());
838
839        // starting a shutdown must not discard the "input available" signal
840        assert!(lazy(|cx| state.poll_shutdown(cx)).await.is_pending());
841        assert!(ctx.flags().is_read_ready());
842
843        // so the read task is handed a fresh buffer instead of the one the
844        // dispatcher still has to decode
845        assert!(ctx.take_read_buf().is_empty());
846        assert_eq!(state.with_read_dst(BytesMut::take), b"12345");
847    }
848
849    #[ntex::test]
850    async fn clean_eof_is_processed_by_filters_once() {
851        let (_, server) = IoTest::create();
852        let state = Io::from(server).add_filter(FinishOnEof);
853        let ctx = IoContext::new(state.get_ref());
854
855        for _ in 0..3 {
856            assert_eq!(
857                ctx.release_read_buf(ctx.take_read_buf(), Poll::Ready(Ok(0))),
858                IoTaskStatus::Pause
859            );
860            assert!(state.is_read_eof());
861        }
862        assert_eq!(state.with_read_dst(BytesMut::take), b"final");
863    }
864
865    #[ntex::test]
866    async fn clean_eof_is_processed_by_filters() {
867        let (_, server) = IoTest::create();
868        let state = Io::from(server).add_filter(FinishOnEof);
869        let ctx = IoContext::new(state.get_ref());
870
871        assert!(lazy(|cx| state.poll_read_notify(cx)).await.is_pending());
872        assert_eq!(
873            ctx.release_read_buf(ctx.take_read_buf(), Poll::Ready(Ok(0))),
874            IoTaskStatus::Pause
875        );
876        assert!(state.is_read_eof());
877        assert!(matches!(
878            lazy(|cx| state.poll_read_notify(cx)).await,
879            Poll::Ready(Ok(Some(())))
880        ));
881        assert!(matches!(
882            lazy(|cx| state.poll_read_notify(cx)).await,
883            Poll::Ready(Ok(None))
884        ));
885        assert_eq!(state.with_read_dst(BytesMut::take), b"final");
886        assert!(matches!(
887            lazy(|cx| state.poll_read_more(cx)).await,
888            Poll::Ready(Ok(None))
889        ));
890    }
891
892    #[derive(Debug)]
893    struct RejectEof;
894
895    impl FilterLayer for RejectEof {
896        fn process_read_buf(&self, buf: &FilterBuf<'_>) -> io::Result<()> {
897            if buf.io().is_read_eof() {
898                Err(io::Error::new(
899                    io::ErrorKind::UnexpectedEof,
900                    "truncated filtered stream",
901                ))
902            } else {
903                Ok(())
904            }
905        }
906
907        fn process_write_buf(&self, _: &FilterBuf<'_>) -> io::Result<()> {
908            Ok(())
909        }
910    }
911
912    #[ntex::test]
913    async fn clean_eof_filter_error_terminates_connection() {
914        let (_, server) = IoTest::create();
915        let state = Io::from(server).add_filter(RejectEof);
916        let ctx = IoContext::new(state.get_ref());
917
918        assert_eq!(
919            ctx.release_read_buf(ctx.take_read_buf(), Poll::Ready(Ok(0))),
920            IoTaskStatus::Stop
921        );
922        assert!(state.is_read_eof());
923        assert!(state.flags().is_terminating());
924    }
925
926    #[ntex::test]
927    async fn filter_shutdown_does_not_spin_on_pending_read() {
928        use crate::{Handle, IoStream};
929        use std::{cell::Cell, future::poll_fn, rc::Rc};
930
931        // transport that never makes progress in either direction
932        struct Stalled(Rc<Cell<usize>>);
933
934        impl IoStream for Stalled {
935            fn start(self, ctx: IoContext) -> Box<dyn Handle> {
936                let polls = self.0.clone();
937                ntex_util::spawn(async move {
938                    poll_fn(|cx| {
939                        polls.set(polls.get() + 1);
940                        if let Poll::Ready(Readiness::Ready) = ctx.poll_read_ready(cx) {
941                            let _ = ctx.with_read_buf(|_| Poll::Pending);
942                        }
943                        match ctx.poll_write_ready(cx) {
944                            Poll::Ready(Readiness::Ready) => {
945                                let _ = ctx.update_write_status(Ok(0));
946                                Poll::Pending
947                            }
948                            Poll::Ready(_) => Poll::Ready(()),
949                            Poll::Pending => Poll::Pending,
950                        }
951                    })
952                    .await;
953                    ctx.stopped(None);
954                });
955                Box::new(Stalled(self.0))
956            }
957        }
958
959        impl Handle for Stalled {}
960
961        let polls = Rc::new(Cell::new(0));
962        let io = Io::new(
963            Stalled(polls.clone()),
964            ntex_service::cfg::SharedCfg::default(),
965        );
966        io.encode_slice(b"data").unwrap();
967        ntex_util::time::sleep(ntex_util::time::Millis(20)).await;
968
969        // graceful shutdown cannot flush the output, it waits for the deadline
970        io.close();
971        let start = polls.get();
972        ntex_util::time::sleep(ntex_util::time::Millis(100)).await;
973        assert!(
974            polls.get() - start < 10,
975            "io task polled {} times",
976            polls.get() - start
977        );
978    }
979
980    /// Transport driven by the test through an `IoContext`.
981    struct Manual;
982
983    impl crate::IoStream for Manual {
984        fn start(self, _: IoContext) -> Box<dyn crate::Handle> {
985            Box::new(Manual)
986        }
987    }
988
989    impl crate::Handle for Manual {}
990
991    #[ntex::test]
992    async fn take_read_buf_reuses_consumed_buffer() {
993        let io = Io::new(Manual, ntex_service::cfg::SharedCfg::default());
994        let ctx = IoContext::new(io.get_ref());
995        assert_eq!(ctx.shutdown_timeout(), io.cfg().shutdown_timeout());
996        assert!(io.query::<u32>().get().is_none());
997
998        let mut buf = ctx.take_read_buf();
999        buf.extend_from_slice(b"12345");
1000        assert_eq!(
1001            ctx.release_read_buf(buf, Poll::Ready(Ok(5))),
1002            IoTaskStatus::Io
1003        );
1004        assert_eq!(io.with_read_dst(|b| b.split_to(3)), b"123");
1005
1006        // the dispatcher consumed the input, the partly filled buffer is
1007        // handed to the transport
1008        let mut buf = ctx.take_read_buf();
1009        assert_eq!(buf, b"45");
1010        buf.reserve_more();
1011        assert!(buf.capacity() - buf.len() >= io.get_ref().0.read_size().low());
1012        buf.extend_from_slice(b"6");
1013        assert_eq!(
1014            ctx.release_read_buf(buf, Poll::Ready(Ok(1))),
1015            IoTaskStatus::Io
1016        );
1017        assert_eq!(io.with_read_dst(BytesMut::take), b"456");
1018    }
1019
1020    #[ntex::test]
1021    async fn reads_are_discarded_in_transport_shutdown_phase() {
1022        let io = Io::new(Manual, ntex_service::cfg::SharedCfg::default());
1023        let ctx = IoContext::new(io.get_ref());
1024
1025        // no filter work and no output, the filter phase ends at once
1026        io.close();
1027        assert!(lazy(|cx| ctx.poll_read_ready(cx)).await.is_pending());
1028        assert!(ctx.flags().is_stopping());
1029
1030        // input is discarded, reads keep going until eof
1031        let mut buf = ctx.take_read_buf();
1032        buf.extend_from_slice(b"12345");
1033        assert_eq!(
1034            ctx.release_read_buf(buf, Poll::Ready(Ok(5))),
1035            IoTaskStatus::Io
1036        );
1037        assert_eq!(
1038            ctx.with_read_buf(|buf| {
1039                buf.extend_from_slice(b"678");
1040                Poll::Ready(Ok(3))
1041            }),
1042            IoTaskStatus::Io
1043        );
1044        assert_eq!(io.with_read_dst(|b| b.len()), 0);
1045
1046        assert_eq!(
1047            ctx.release_read_buf(ctx.take_read_buf(), Poll::Pending),
1048            IoTaskStatus::Pause
1049        );
1050        assert_eq!(ctx.with_read_buf(|_| Poll::Pending), IoTaskStatus::Pause);
1051        assert!(!io.is_read_eof());
1052
1053        // a read error does not terminate the connection
1054        assert_eq!(
1055            ctx.release_read_buf(
1056                ctx.take_read_buf(),
1057                Poll::Ready(Err(io::Error::other("err")))
1058            ),
1059            IoTaskStatus::Pause
1060        );
1061        assert!(io.is_read_eof());
1062        assert!(!ctx.flags().is_terminating());
1063
1064        assert_eq!(
1065            ctx.with_read_buf(|_| Poll::Ready(Ok(0))),
1066            IoTaskStatus::Pause
1067        );
1068
1069        // nothing left to drain, the transport closes the connection
1070        assert_eq!(
1071            lazy(|cx| ctx.poll_write_ready(cx)).await,
1072            Poll::Ready(Readiness::Close)
1073        );
1074        ctx.stopped(None);
1075        assert!(io.is_closed());
1076    }
1077
1078    #[derive(Debug)]
1079    struct FailShutdown;
1080
1081    impl FilterLayer for FailShutdown {
1082        fn process_read_buf(&self, _: &FilterBuf<'_>) -> io::Result<()> {
1083            Ok(())
1084        }
1085
1086        fn process_write_buf(&self, _: &FilterBuf<'_>) -> io::Result<()> {
1087            Ok(())
1088        }
1089
1090        fn shutdown(&self, _: &FilterBuf<'_>) -> io::Result<Poll<()>> {
1091            Err(io::Error::other("shutdown failed"))
1092        }
1093    }
1094
1095    #[ntex::test]
1096    async fn filter_shutdown_error_terminates_connection() {
1097        let io = Io::new(Manual, ntex_service::cfg::SharedCfg::default()).add_filter(FailShutdown);
1098        let ctx = IoContext::new(io.get_ref());
1099        // neither the layer nor the transport provide values
1100        assert!(io.query::<u32>().get().is_none());
1101
1102        io.close();
1103        assert_eq!(
1104            lazy(|cx| ctx.poll_read_ready(cx)).await,
1105            Poll::Ready(Readiness::Close)
1106        );
1107        assert!(ctx.flags().is_terminating());
1108        ctx.stopped(None);
1109        let err = io.shutdown().await.unwrap_err();
1110        assert_eq!(err.to_string(), "shutdown failed");
1111    }
1112
1113    #[ntex::test]
1114    async fn read_after_termination_stops_read_task() {
1115        let io = Io::new(Manual, ntex_service::cfg::SharedCfg::default());
1116        let ctx = IoContext::new(io.get_ref());
1117        let ctx2 = ctx.clone();
1118        assert_eq!(ctx.id(), ctx2.id());
1119
1120        ctx.stop(Some(io::Error::other("failed")));
1121        let mut buf = ctx2.take_read_buf();
1122        buf.extend_from_slice(b"1");
1123        assert_eq!(
1124            ctx2.release_read_buf(buf, Poll::Ready(Ok(1))),
1125            IoTaskStatus::Stop
1126        );
1127    }
1128}