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