Skip to main content

ntex_io/
ioref.rs

1use std::{any, fmt, hash, io, ptr};
2
3use ntex_bytes::{BytePage, BytePages, BytesMut};
4use ntex_codec::{Decoder, Encoder};
5use ntex_service::cfg::SharedCfg;
6use ntex_util::time::Seconds;
7
8use crate::ops::{Id, Iops, TimerHandle};
9use crate::{Decoded, Filter, FilterBuf, Flags, IoConfig, IoContext, IoRef, types};
10
11impl IoRef {
12    #[inline]
13    /// Gets the ID.
14    pub fn id(&self) -> Id {
15        self.0.id()
16    }
17
18    #[inline]
19    /// Gets the I/O tag.
20    pub fn tag(&self) -> &'static str {
21        self.0.tag()
22    }
23
24    #[doc(hidden)]
25    /// Gets the current state flags.
26    pub fn flags(&self) -> Flags {
27        self.0.flags.clone()
28    }
29
30    #[inline]
31    /// Gets the current filter.
32    pub(crate) fn filter(&self) -> &dyn Filter {
33        self.0.filter()
34    }
35
36    #[inline]
37    /// Gets the configuration.
38    pub fn cfg(&self) -> &IoConfig {
39        &self.0.cfg
40    }
41
42    #[inline]
43    /// Gets the shared configuration.
44    pub fn shared(&self) -> SharedCfg {
45        self.0.cfg.shared()
46    }
47
48    #[inline]
49    /// Checks whether the I/O stream is closed.
50    pub fn is_closed(&self) -> bool {
51        self.0.flags.is_closed()
52    }
53
54    #[inline]
55    /// Checks whether write back-pressure is enabled.
56    pub fn is_wr_backpressure(&self) -> bool {
57        self.0.flags.is_wr_backpressure()
58    }
59
60    /// Gracefully closes the connection.
61    ///
62    /// Initiates the I/O stream shutdown process.
63    pub fn close(&self) {
64        self.0.start_shutdown();
65    }
66
67    /// Force-closes the connection.
68    ///
69    /// The dispatcher does not wait for incomplete responses. The I/O stream is
70    /// terminated without any graceful period.
71    pub fn terminate(&self) {
72        log::trace!("{}: Terminate io stream object", self.tag());
73        self.0.terminate_connection(None);
74    }
75
76    /// Queries filter-specific data.
77    pub fn query<T: 'static>(&self) -> types::QueryItem<T> {
78        types::QueryItem::new(self.filter().query(any::TypeId::of::<T>()))
79    }
80
81    #[inline]
82    /// Encodes the item into the write buffer.
83    pub fn encode<U>(&self, item: U::Item, codec: &U) -> Result<(), <U as Encoder>::Error>
84    where
85        U: Encoder,
86    {
87        self.with_write_buf(|buf| codec.encodev(item, buf))
88            .unwrap_or_else(|_| Ok(()))
89    }
90
91    #[inline]
92    /// Encodes the slice into the write buffer.
93    pub fn encode_slice(&self, src: &[u8]) -> io::Result<()> {
94        self.with_write_buf(|buf| buf.extend_from_slice(src))
95    }
96
97    #[inline]
98    /// Writes bytes to the write buffer.
99    pub fn encode_bytes<B>(&self, src: B) -> io::Result<()>
100    where
101        BytePage: From<B>,
102    {
103        self.with_write_buf(|buf| buf.append(src))
104    }
105
106    /// Attempts to decode a frame from the read buffer.
107    pub fn decode<U>(
108        &self,
109        codec: &U,
110    ) -> Result<Option<<U as Decoder>::Item>, <U as Decoder>::Error>
111    where
112        U: Decoder,
113    {
114        self.0.buffer.with_read_dst(self, |buf| {
115            let res = codec.decode(buf);
116            self.0.flags.unset_read_ready();
117            self.update_read_destination(buf);
118            res
119        })
120    }
121
122    /// Attempts to decode a frame from the read buffer.
123    pub fn decode_item<U>(
124        &self,
125        codec: &U,
126    ) -> Result<Decoded<<U as Decoder>::Item>, <U as Decoder>::Error>
127    where
128        U: Decoder,
129    {
130        self.0.buffer.with_read_dst(self, |buf| {
131            let len = buf.len();
132            let res = codec.decode(buf).map(|item| Decoded {
133                item,
134                remains: buf.len(),
135                consumed: len - buf.len(),
136            });
137            self.0.flags.unset_read_ready();
138            self.update_read_destination(buf);
139            res
140        })
141    }
142
143    /// Sends the write buffer to the I/O layer.
144    ///
145    /// Requires the underlying runtime to implement `.write()`;
146    /// otherwise, no action is taken.
147    pub fn send_buf(&self) -> io::Result<()> {
148        // try send bytes
149        self.consolidate_write_state(true);
150
151        if self.0.flags.is_stopping_any()
152            && let Some(err) = self.0.error.take()
153        {
154            Err(err)
155        } else {
156            Ok(())
157        }
158    }
159
160    pub(crate) fn ops_send_buf(&self) {
161        let st = &self.0;
162        #[cfg(feature = "trace")]
163        log::trace!(
164            "{}: ops-send == buf:{} flags:{:?}",
165            st.tag(),
166            st.buffer.write_buf_size(),
167            st.flags
168        );
169
170        if st.flags.is_wr_send_scheduled() {
171            st.flags.unset_wr_send_scheduled();
172
173            if st.flags.is_write_paused() {
174                // call `Handle::write()`.
175                // if write task is not paused, io write is pending
176                // need to wake write task for io completeion
177                if self.call_write() == WakeWriteTask::Yes {
178                    st.wake_write_task();
179                    st.flags.unset_write_paused();
180                }
181            } else {
182                st.wake_write_task();
183            }
184        }
185    }
186
187    /// Get access to filter buffer
188    pub fn with_buf<F, R>(&self, f: F) -> io::Result<R>
189    where
190        F: FnOnce(&mut FilterBuf<'_>) -> R,
191    {
192        self.with_callbacks(|cb| cb.before_processing(self));
193        let result = self.0.buffer.with_filter(self, |ctx| ctx.with_buffer(f));
194        self.with_callbacks(|cb| cb.after_processing(self));
195
196        self.consolidate_write_state(false);
197        Ok(result)
198    }
199
200    /// Get mut access to read buffer
201    pub fn with_read_buf<F, R>(&self, f: F) -> R
202    where
203        F: FnOnce(&mut BytesMut) -> R,
204    {
205        self.0.buffer.with_read_dst(self, |buf| {
206            let res = f(buf);
207            self.update_read_destination(buf);
208            res
209        })
210    }
211
212    /// Get mut access to source write buffer
213    pub fn with_write_buf<F, R>(&self, f: F) -> io::Result<R>
214    where
215        F: FnOnce(&mut BytePages) -> R,
216    {
217        let st = &self.0;
218
219        if st.flags.is_stopping_any() {
220            if st.flags.is_closed() {
221                Err(st.error_or_disconnected())
222            } else {
223                Err(io::Error::other("I/O stream is closing"))
224            }
225        } else {
226            let result = st.buffer.with_write_src(f);
227            self.consolidate_write_state(false);
228            Ok(result)
229        }
230    }
231
232    #[inline]
233    /// Get mut access to src read buffer
234    pub fn with_read_src_buf<F, R>(&self, f: F) -> R
235    where
236        F: FnOnce(&mut BytesMut) -> R,
237    {
238        self.0.buffer.with_read_src(self, f)
239    }
240
241    #[inline]
242    /// Get mut access to dest write buffer
243    pub fn with_write_dst_buf<F, R>(&self, f: F) -> R
244    where
245        F: FnOnce(&mut BytePages) -> R,
246    {
247        self.0.buffer.with_write_dst(f)
248    }
249
250    pub(crate) fn consolidate_write_state(&self, force: bool) {
251        let st = &self.0;
252
253        // wake write task if needsed
254        let size = st.buffer.write_buf_size();
255
256        #[cfg(feature = "trace")]
257        log::trace!("{}: write-upd == buf:{size} flags:{:?}", st.tag(), st.flags);
258
259        if size > 0 && st.flags.is_write_paused() {
260            // The app encodes data in response to incoming data,
261            // continuing to fill the write buffer until all data
262            // has been processed. Only then can the runtime wake
263            // the write task to send the buffered data.
264            //
265            // By that time, the buffer may have accumulated a large
266            // amount of data, causing it to be sent in large bursts,
267            // which introduces latency. To prevent this behavior and
268            // flatten data delivery to the peer, IoRef can initiate
269            // out-of-order writes based on a configured threshold.
270            if st.flags.is_direct_wr_enabled() && (force || size >= st.cfg.write_buf_threshold()) {
271                // Send data in-place
272                if self.call_write() == WakeWriteTask::Yes {
273                    #[cfg(feature = "trace")]
274                    log::trace!(
275                        "{}: write-upd == schedule(more):{} flags:{:?}",
276                        st.tag(),
277                        st.buffer.write_buf_size(),
278                        st.flags
279                    );
280                    if !st.flags.is_wr_send_scheduled() {
281                        // More data needs to be sent
282                        st.flags.set_wr_send_scheduled();
283                        Iops::schedule_write(st.id());
284                    }
285                } else {
286                    st.flags.unset_wr_send_scheduled();
287                }
288            } else if !st.flags.is_wr_send_scheduled() {
289                #[cfg(feature = "trace")]
290                log::trace!("{}: write-upd == schedule(too small)", st.tag());
291                st.flags.set_wr_send_scheduled();
292                Iops::schedule_write(st.id());
293            }
294        }
295        // Enable backpressure
296        if !st.flags.is_wr_backpressure() && st.is_wr_backpressure_needed(size) {
297            st.flags.set_wr_backpressure();
298            st.wake_dispatch_task();
299        }
300    }
301
302    fn update_read_destination(&self, buf: &mut BytesMut) {
303        let st = &self.0;
304
305        #[cfg(feature = "trace")]
306        log::trace!(
307            "{}: read-upd == buf:{} flags:{:?}",
308            st.tag(),
309            buf.len(),
310            st.flags
311        );
312
313        if st.flags.is_rd_backpressure() {
314            // back-pressure is still eanbled
315            if st.is_rd_backpressure_needed(buf.len()) {
316                return;
317            }
318            st.flags.unset_all_read_flags();
319        } else {
320            st.flags.unset_read_ready();
321        }
322
323        if st.flags.is_read_paused() {
324            st.wake_read_task();
325            st.flags.unset_read_paused();
326        }
327    }
328
329    /// Make sure buffer has enough free space
330    pub fn resize_read_buf(&self, buf: &mut BytesMut) {
331        self.0.cfg.read_buf().resize(buf);
332    }
333
334    /// Wakeup dispatcher
335    pub fn notify_dispatcher(&self) {
336        log::trace!("{}: Timer, notify dispatcher", self.tag());
337        self.0.wake_dispatch_task();
338    }
339
340    /// Wakeup dispatcher and send keep-alive error
341    pub fn notify_timeout(&self) {
342        self.0.notify_timeout();
343    }
344
345    /// Current timer handle
346    pub fn timer_handle(&self) -> TimerHandle {
347        self.0.timeout.get()
348    }
349
350    /// Start timer
351    pub fn start_timer(&self, timeout: Seconds) -> TimerHandle {
352        let cur_hnd = self.0.timeout.get();
353
354        if timeout.is_zero() {
355            if cur_hnd.is_set() {
356                self.0.timeout.set(TimerHandle::ZERO);
357                cur_hnd.unregister(self);
358            }
359            TimerHandle::ZERO
360        } else if cur_hnd.is_set() {
361            let hnd = cur_hnd.update(timeout, self);
362            if hnd != cur_hnd {
363                log::trace!("{}: Update timer {:?}", self.tag(), timeout);
364                self.0.timeout.set(hnd);
365            }
366            hnd
367        } else {
368            log::trace!("{}: Start timer {:?}", self.tag(), timeout);
369            let hnd = TimerHandle::register(timeout, self);
370            self.0.timeout.set(hnd);
371            hnd
372        }
373    }
374
375    /// Stop timer
376    pub fn stop_timer(&self) {
377        let hnd = self.0.timeout.get();
378        if hnd.is_set() {
379            log::trace!("{}: Stop timer", self.tag());
380            self.0.timeout.set(TimerHandle::ZERO);
381            hnd.unregister(self);
382        }
383    }
384
385    /// Notify when io stream get disconnected
386    pub fn on_disconnect(&self) -> crate::OnDisconnect {
387        crate::OnDisconnect::new(self.0.clone())
388    }
389
390    #[doc(hidden)]
391    /// Register filter callbacks
392    pub fn register_filter_callbacks<F: crate::IoCallbacks + 'static>(&self, f: F) {
393        self.0.extensions.register_filter_callbacks(f);
394    }
395
396    /// Call handle write method, returns true if
397    /// `write-paused` is still set
398    fn call_write(&self) -> WakeWriteTask {
399        if let Some(hnd) = self.0.handle.take() {
400            self.0.flags.unset_write_paused();
401            #[cfg(feature = "trace")]
402            log::trace!(
403                "{}: call-write ({}), flags:{:?}",
404                self.tag(),
405                self.0.buffer.write_buf_size(),
406                self.0.flags
407            );
408            let ctx = unsafe { &*(ptr::from_ref(self).cast::<IoContext>()) };
409            hnd.write(ctx);
410            self.0.handle.set(Some(hnd));
411        }
412        if self.0.flags.is_write_paused() {
413            WakeWriteTask::No
414        } else {
415            WakeWriteTask::Yes
416        }
417    }
418
419    pub(crate) fn call_notify(&self) {
420        if let Some(hnd) = self.0.handle.take() {
421            let ctx = unsafe { &*(ptr::from_ref(self).cast::<IoContext>()) };
422            hnd.notify(ctx);
423            self.0.handle.set(Some(hnd));
424        }
425    }
426
427    pub(crate) fn with_callbacks<F>(&self, f: F)
428    where
429        F: FnOnce(&dyn crate::IoCallbacks),
430    {
431        self.0.extensions.with_callbacks(f);
432    }
433}
434
435#[derive(Copy, Clone, PartialEq, Eq, Debug)]
436enum WakeWriteTask {
437    Yes,
438    No,
439}
440
441impl Eq for IoRef {}
442
443impl PartialEq for IoRef {
444    #[inline]
445    fn eq(&self, other: &Self) -> bool {
446        self.0.eq(&other.0)
447    }
448}
449
450impl hash::Hash for IoRef {
451    #[inline]
452    fn hash<H: hash::Hasher>(&self, state: &mut H) {
453        self.0.hash(state);
454    }
455}
456
457impl fmt::Debug for IoRef {
458    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
459        f.debug_struct("IoRef")
460            .field("state", self.0.as_ref())
461            .finish()
462    }
463}
464
465#[cfg(test)]
466mod tests {
467    use std::cell::{Cell, RefCell};
468    use std::{future::Future, future::poll_fn, pin::Pin, rc::Rc, task::Poll};
469
470    use ntex_bytes::Bytes;
471    use ntex_codec::BytesCodec;
472    use ntex_util::{future::lazy, time::Millis, time::sleep};
473
474    use super::*;
475    use crate::{FilterCtx, Io, testing::IoTest};
476
477    const BIN: &[u8] = b"GET /test HTTP/1\r\n\r\n";
478    const TEXT: &str = "GET /test HTTP/1\r\n\r\n";
479
480    #[ntex::test]
481    async fn utils() {
482        let (client, server) = IoTest::create();
483        client.remote_buffer_cap(1024);
484        client.write(TEXT);
485
486        let state = Io::from(server);
487        assert_eq!(state.get_ref(), state.get_ref());
488
489        let msg = state.recv(&BytesCodec).await.unwrap().unwrap();
490        assert_eq!(msg, Bytes::from_static(BIN));
491        assert_eq!(state.get_ref(), state.as_ref().clone());
492        assert!(format!("{state:?}").find("Io {").is_some());
493        assert!(format!("{:?}", state.get_ref()).find("IoRef {").is_some());
494
495        let res = poll_fn(|cx| Poll::Ready(state.poll_recv(&BytesCodec, cx))).await;
496        assert!(res.is_pending());
497        client.write(TEXT);
498        sleep(Millis(50)).await;
499        let res = poll_fn(|cx| Poll::Ready(state.poll_recv(&BytesCodec, cx))).await;
500        if let Poll::Ready(msg) = res {
501            assert_eq!(msg.unwrap(), Bytes::from_static(BIN));
502        }
503
504        client.read_error(io::Error::other("err"));
505        let msg = state.recv(&BytesCodec).await;
506        assert!(msg.is_err());
507        assert!(state.flags().is_terminated());
508
509        let (client, server) = IoTest::create();
510        client.remote_buffer_cap(1024);
511        let state = Io::from(server);
512
513        client.read_error(io::Error::other("err"));
514        let res = poll_fn(|cx| Poll::Ready(state.poll_recv(&BytesCodec, cx))).await;
515        if let Poll::Ready(msg) = res {
516            assert!(msg.is_err());
517            assert!(state.flags().is_terminated());
518        }
519
520        let (client, server) = IoTest::create();
521        client.remote_buffer_cap(1024);
522        let state = Io::from(server);
523        assert_eq!(0, state.with_write_dst_buf(|b| b.len()));
524        state.encode_slice(b"test").unwrap();
525        assert_eq!(4, state.with_write_dst_buf(|b| b.len()));
526        let buf = client.read().await.unwrap();
527        assert_eq!(buf, Bytes::from_static(b"test"));
528
529        client.write(b"test");
530        state.read_ready().await.unwrap();
531        let buf = state.decode(&BytesCodec).unwrap().unwrap();
532        assert_eq!(buf, Bytes::from_static(b"test"));
533
534        client.write_error(io::Error::other("err"));
535        state
536            .send(Bytes::from_static(b"test"), &BytesCodec)
537            .await
538            .unwrap();
539        assert!(state.flags().is_terminated());
540
541        let res = state.send(Bytes::from_static(b"test"), &BytesCodec).await;
542        assert!(res.is_err());
543
544        let (client, server) = IoTest::create();
545        client.remote_buffer_cap(1024);
546        let state = Io::from(server);
547        state.terminate();
548        assert!(state.flags().is_stopping());
549        assert!(state.flags().is_terminated());
550    }
551
552    #[ntex::test]
553    #[allow(clippy::unit_cmp)]
554    async fn on_disconnect() {
555        let (client, server) = IoTest::create();
556        let state = Io::from(server);
557        let mut waiter = state.on_disconnect();
558        assert_eq!(
559            lazy(|cx| Pin::new(&mut waiter).poll(cx)).await,
560            Poll::Pending
561        );
562        let mut waiter2 = waiter.clone();
563        assert_eq!(
564            lazy(|cx| Pin::new(&mut waiter2).poll(cx)).await,
565            Poll::Pending
566        );
567        client.close().await;
568        assert_eq!(waiter.await, ());
569        assert_eq!(waiter2.await, ());
570
571        let mut waiter = state.on_disconnect();
572        assert_eq!(
573            lazy(|cx| Pin::new(&mut waiter).poll(cx)).await,
574            Poll::Ready(())
575        );
576
577        let (client, server) = IoTest::create();
578        let state = Io::from(server);
579        let mut waiter = state.on_disconnect();
580        assert_eq!(
581            lazy(|cx| Pin::new(&mut waiter).poll(cx)).await,
582            Poll::Pending
583        );
584        client.read_error(io::Error::other("err"));
585        assert_eq!(waiter.await, ());
586    }
587
588    #[ntex::test]
589    async fn write_to_closed_io() {
590        let (client, server) = IoTest::create();
591        let state = Io::from(server);
592        client.close().await;
593
594        assert!(state.is_closed());
595        assert!(state.encode_slice(TEXT.as_bytes()).is_err());
596        assert!(state.encode_bytes(Bytes::from_static(BIN)).is_err());
597        assert!(
598            state
599                .with_write_buf(|buf| buf.extend_from_slice(BIN))
600                .is_err()
601        );
602    }
603
604    #[derive(Debug)]
605    struct Counter<F> {
606        layer: F,
607        idx: usize,
608        in_bytes: Rc<Cell<usize>>,
609        out_bytes: Rc<Cell<usize>>,
610        read_order: Rc<RefCell<Vec<usize>>>,
611        write_order: Rc<RefCell<Vec<usize>>>,
612    }
613
614    impl<F: Filter> Filter for Counter<F> {
615        fn process_read_buf(&self, ctx: &mut FilterCtx<'_>) -> io::Result<()> {
616            self.read_order.borrow_mut().push(self.idx);
617            let result = self.layer.process_read_buf(ctx);
618            self.in_bytes
619                .set(self.in_bytes.get() + ctx.new_read_bytes());
620            result
621        }
622
623        fn process_write_buf(&self, ctx: &mut FilterCtx<'_>) -> io::Result<()> {
624            self.write_order.borrow_mut().push(self.idx);
625            ctx.with_buffer(|buf| {
626                buf.with_write_buffers(|src, _| {
627                    self.out_bytes.set(self.out_bytes.get() + src.len());
628                });
629            });
630            self.layer.process_write_buf(ctx)
631        }
632
633        crate::forward_ready!(layer);
634        crate::forward_query!(layer);
635        crate::forward_shutdown!(layer);
636    }
637
638    #[ntex::test]
639    async fn filter() {
640        let in_bytes = Rc::new(Cell::new(0));
641        let out_bytes = Rc::new(Cell::new(0));
642        let read_order = Rc::new(RefCell::new(Vec::new()));
643        let write_order = Rc::new(RefCell::new(Vec::new()));
644
645        let (client, server) = IoTest::create();
646        let io = Io::from(server)
647            .map_filter(|layer| Counter {
648                layer,
649                idx: 1,
650                in_bytes: in_bytes.clone(),
651                out_bytes: out_bytes.clone(),
652                read_order: read_order.clone(),
653                write_order: write_order.clone(),
654            })
655            .seal();
656
657        client.remote_buffer_cap(1024);
658        client.write(TEXT);
659        let msg = io.recv(&BytesCodec).await.unwrap().unwrap();
660        assert_eq!(msg, Bytes::from_static(BIN));
661
662        io.send(Bytes::from_static(b"test"), &BytesCodec)
663            .await
664            .unwrap();
665        let buf = client.read().await.unwrap();
666        assert_eq!(buf, Bytes::from_static(b"test"));
667
668        client.write(TEXT);
669        let msg = io.recv(&BytesCodec).await.unwrap().unwrap();
670        assert_eq!(msg, Bytes::from_static(BIN));
671
672        assert_eq!(in_bytes.get(), BIN.len() * 2);
673        assert_eq!(out_bytes.get(), 8);
674    }
675
676    #[ntex::test]
677    async fn boxed_filter() {
678        let in_bytes = Rc::new(Cell::new(0));
679        let out_bytes = Rc::new(Cell::new(0));
680        let read_order = Rc::new(RefCell::new(Vec::new()));
681        let write_order = Rc::new(RefCell::new(Vec::new()));
682
683        let (client, server) = IoTest::create();
684        let state = Io::from(server)
685            .map_filter(|layer| Counter {
686                layer,
687                idx: 2,
688                in_bytes: in_bytes.clone(),
689                out_bytes: out_bytes.clone(),
690                read_order: read_order.clone(),
691                write_order: write_order.clone(),
692            })
693            .map_filter(|layer| Counter {
694                layer,
695                idx: 1,
696                in_bytes: in_bytes.clone(),
697                out_bytes: out_bytes.clone(),
698                read_order: read_order.clone(),
699                write_order: write_order.clone(),
700            });
701        let state = state.seal();
702
703        client.remote_buffer_cap(1024);
704        client.write(TEXT);
705        let msg = state.recv(&BytesCodec).await.unwrap().unwrap();
706        assert_eq!(msg, Bytes::from_static(BIN));
707
708        state
709            .send(Bytes::from_static(b"test"), &BytesCodec)
710            .await
711            .unwrap();
712        let buf = client.read().await.unwrap();
713        assert_eq!(buf, Bytes::from_static(b"test"));
714
715        assert_eq!(in_bytes.get(), BIN.len() * 2);
716        assert_eq!(out_bytes.get(), 16);
717        assert_eq!(state.0.buffer.with_write_dst(|b| b.len()), 0);
718
719        // refs
720        assert_eq!(Rc::strong_count(&in_bytes), 3);
721        drop(state);
722        assert_eq!(Rc::strong_count(&in_bytes), 1);
723        assert_eq!(*read_order.borrow(), &[1, 2][..]);
724        assert_eq!(*write_order.borrow(), &[1, 2, 1, 2, 1, 2][..]);
725    }
726}