Skip to main content

ntex_io/
lib.rs

1//! Asynchronous I/O abstractions for the ntex ecosystem.
2//!
3//! [`Io`] wraps an underlying [`IoStream`] and coordinates buffered reads,
4//! writes, backpressure, timeouts, and shutdown. Protocol transforms can be
5//! composed through [`Filter`] layers, while [`Framed`] combines an I/O stream
6//! with an `ntex-codec` encoder and decoder.
7//!
8//! Use [`IoConfig`] to configure buffer thresholds and connection timeouts.
9#![deny(clippy::pedantic)]
10#![allow(
11    clippy::missing_fields_in_debug,
12    clippy::missing_errors_doc,
13    clippy::missing_panics_doc,
14    clippy::must_use_candidate
15)]
16use std::io::{Error as IoError, Result as IoResult};
17use std::{any::Any, any::TypeId, fmt, task::Poll};
18
19pub mod cfg;
20pub mod testing;
21pub mod types;
22
23mod buf;
24mod ctx;
25mod filter;
26mod filterptr;
27mod flags;
28mod framed;
29mod io;
30mod ioref;
31mod macros;
32mod ops;
33mod seal;
34mod utils;
35
36use ntex_codec::Decoder;
37
38pub use self::buf::{FilterBuf, FilterCtx};
39pub use self::cfg::IoConfig;
40pub use self::ctx::IoContext;
41pub use self::filter::{Base, Filter, Layer};
42pub use self::framed::Framed;
43pub use self::io::{Io, IoRef, OnDisconnect};
44pub use self::ops::{Id, TimerHandle};
45pub use self::seal::{IoBoxed, Sealed};
46pub use self::utils::Decoded;
47
48#[doc(hidden)]
49pub use self::flags::Flags;
50
51/// Filter readiness state.
52#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
53pub enum Readiness {
54    /// The I/O task may proceed with I/O operations.
55    Ready,
56    /// Initiates a graceful I/O shutdown.
57    Shutdown,
58    /// Immediately terminates the I/O stream.
59    Terminate,
60}
61
62impl Readiness {
63    /// Merges two readiness states.
64    pub fn merge(val1: Poll<Readiness>, val2: Poll<Readiness>) -> Poll<Readiness> {
65        match val1 {
66            Poll::Pending => Poll::Pending,
67            Poll::Ready(Readiness::Ready) => val2,
68            Poll::Ready(Readiness::Terminate) => Poll::Ready(Readiness::Terminate),
69            Poll::Ready(Readiness::Shutdown) => {
70                if val2 == Poll::Ready(Readiness::Terminate) {
71                    Poll::Ready(Readiness::Terminate)
72                } else {
73                    Poll::Ready(Readiness::Shutdown)
74                }
75            }
76        }
77    }
78}
79
80/// A processing layer that transforms an I/O stream's read and write buffers.
81#[allow(unused_variables)]
82pub trait FilterLayer: fmt::Debug + 'static {
83    /// Accesses internal filter information.
84    fn query(&self, id: TypeId) -> Option<Box<dyn Any>> {
85        None
86    }
87
88    /// Processes incoming read-buffer data.
89    fn process_read_buf(&self, buf: &FilterBuf<'_>) -> IoResult<()>;
90
91    /// Processes outgoing write-buffer data.
92    fn process_write_buf(&self, buf: &FilterBuf<'_>) -> IoResult<()>;
93
94    /// Performs a graceful shutdown of the filter.
95    fn shutdown(&self, buf: &FilterBuf<'_>) -> IoResult<Poll<()>> {
96        Ok(Poll::Ready(()))
97    }
98}
99
100/// An underlying transport that can be managed by [`Io`].
101pub trait IoStream {
102    /// Starts transport-specific I/O tasks and returns their control handle.
103    fn start(self, _: IoContext) -> Box<dyn Handle>;
104}
105
106#[doc(hidden)]
107/// Callbacks invoked around filter-chain processing.
108pub trait IoCallbacks {
109    /// Called before processing the read or write filter chain.
110    fn before_processing(&self, io: &IoRef);
111
112    /// Called after processing the read or write filter chain.
113    fn after_processing(&self, io: &IoRef);
114}
115
116/// Control handle for transport-specific I/O tasks.
117pub trait Handle {
118    /// Queries transport-specific information by type.
119    fn query(&self, _: TypeId) -> Option<Box<dyn Any>> {
120        None
121    }
122
123    #[inline]
124    /// Requests that the transport start a write operation.
125    fn write(&self, _: &IoContext) {}
126
127    #[inline]
128    /// Notifies the I/O context that readiness has changed.
129    fn notify(&self, ctx: &IoContext) {
130        ctx.notify();
131    }
132}
133
134/// Current status of the I/O state.
135#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
136pub enum IoTaskStatus {
137    /// Continue performing I/O operations.
138    Io,
139    /// Pause I/O processing temporarily.
140    Pause,
141    /// Stop the I/O task.
142    Stop,
143}
144
145/// I/O status update events.
146#[derive(Debug)]
147pub enum IoStatusUpdate {
148    /// Keep-alive timeout has occurred.
149    KeepAlive,
150    /// Write backpressure is currently active.
151    WriteBackpressure,
152    /// Peer has disconnected.
153    PeerGone(Option<IoError>),
154}
155
156/// Errors that can occur while receiving data.
157pub enum RecvError<U: Decoder> {
158    /// A keep-alive timeout occurred.
159    KeepAlive,
160    /// Write backpressure is currently active.
161    WriteBackpressure,
162    /// Failed to decode an incoming frame.
163    Decoder(U::Error),
164    /// The peer has disconnected.
165    PeerGone(Option<IoError>),
166}
167
168impl<U> fmt::Debug for RecvError<U>
169where
170    U: Decoder,
171    <U as Decoder>::Error: fmt::Debug,
172{
173    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
174        match *self {
175            RecvError::KeepAlive => {
176                write!(fmt, "RecvError::KeepAlive")
177            }
178            RecvError::WriteBackpressure => {
179                write!(fmt, "RecvError::WriteBackpressure")
180            }
181            RecvError::Decoder(ref e) => {
182                write!(fmt, "RecvError::Decoder({e:?})")
183            }
184            RecvError::PeerGone(ref e) => {
185                write!(fmt, "RecvError::PeerGone({e:?})")
186            }
187        }
188    }
189}
190
191#[cfg(test)]
192mod tests {
193    use super::*;
194    use ntex_codec::BytesCodec;
195    use std::io;
196
197    #[test]
198    fn test_fmt() {
199        assert!(format!("{:?}", IoStatusUpdate::KeepAlive).contains("KeepAlive"));
200        assert!(format!("{:?}", RecvError::<BytesCodec>::KeepAlive).contains("KeepAlive"));
201        assert!(
202            format!("{:?}", RecvError::<BytesCodec>::WriteBackpressure)
203                .contains("WriteBackpressure")
204        );
205        assert!(
206            format!(
207                "{:?}",
208                RecvError::<BytesCodec>::Decoder(io::Error::other("err"))
209            )
210            .contains("RecvError::Decoder")
211        );
212        assert!(
213            format!(
214                "{:?}",
215                RecvError::<BytesCodec>::PeerGone(Some(io::Error::other("err")))
216            )
217            .contains("RecvError::PeerGone")
218        );
219    }
220}