Skip to main content

ntex_dispatcher/
lib.rs

1//! Service dispatcher for framed I/O transports.
2//!
3//! [`Dispatcher`] reads frames from an `ntex-io` transport using an
4//! `ntex-codec` decoder and forwards them to an `ntex-service` pipeline as
5//! [`DispatchItem`] values. The service may return an encoded response, report
6//! a service error, or return `None` when no response is required.
7//!
8//! The dispatcher also reports write backpressure through [`Control`] messages
9//! and delivers disconnect, codec, keep-alive, frame-read, and write
10//! failures through [`Reason`] before shutting down the service.
11#![deny(clippy::pedantic)]
12#![allow(clippy::cast_possible_truncation)]
13use std::task::{Context, Poll, ready};
14use std::time::Instant;
15use std::{cell::Cell, fmt, future::Future, future::poll_fn, io, mem, pin::Pin, rc::Rc};
16
17use ntex_codec::{Decoder, Encoder};
18use ntex_io::{Decoded, IoBoxed, IoStatusUpdate, RecvError};
19use ntex_service::pipeline::{Pipeline, PipelineCall};
20use ntex_util::{spawn, time::Seconds, time::Sleep, time::sleep};
21
22mod timer;
23
24use self::timer::{Timer, Timers};
25
26type Response<U> = <U as Encoder>::Item;
27
28/// Event delivered to the dispatcher service.
29pub enum DispatchItem<U: Encoder + Decoder> {
30    /// A frame decoded from the transport.
31    Item(<U as Decoder>::Item),
32    /// A transport flow-control notification.
33    Control(Control),
34    /// The dispatcher is stopping for the specified reason.
35    Stop(Reason<U>),
36}
37
38#[derive(Copy, Clone, Debug, PartialEq, Eq)]
39/// Write-side flow-control notification.
40pub enum Control {
41    /// Write backpressure has been enabled.
42    WBackPressureEnabled,
43    /// Write backpressure has been disabled.
44    WBackPressureDisabled,
45}
46
47/// Reason a dispatcher is stopping.
48pub enum Reason<U: Encoder + Decoder> {
49    /// Service error
50    Service,
51    /// The transport disconnected.
52    ///
53    /// The value contains the underlying I/O error when one was available.
54    /// If the peer closed its side cleanly while undecodable bytes were left
55    /// in the read buffer, the stream was truncated and the value contains an
56    /// [`io::ErrorKind::UnexpectedEof`] error.
57    Io(Option<io::Error>),
58    /// A service response could not be encoded.
59    Encoder(<U as Encoder>::Error),
60    /// Incoming bytes could not be decoded.
61    Decoder(<U as Decoder>::Error),
62    /// The connection exceeded its keep-alive timeout.
63    KeepAlive,
64    /// A complete frame was not received within the configured read deadline.
65    ReadTimeout,
66    /// Write backpressure stayed enabled for longer than the configured
67    /// write timeout.
68    WriteTimeout,
69}
70
71/// Reports a truncated stream, the peer closed cleanly in the middle of a frame.
72fn truncated(io: &IoBoxed) -> Option<io::Error> {
73    if io.is_read_eof() && io.read_dst_size() != 0 {
74        Some(io::Error::new(
75            io::ErrorKind::UnexpectedEof,
76            "bytes remaining on stream",
77        ))
78    } else {
79        None
80    }
81}
82
83/// Future that dispatches decoded transport frames to a service.
84///
85/// The service receives [`DispatchItem`] values and returns
86/// `Option<U::Item>`, where `Some(item)` is encoded and written to the
87/// transport and `None` produces no response.
88///
89/// Multiple service calls may be in flight concurrently. When the
90/// transport applies write backpressure, the dispatcher pauses normal
91/// reads and emits [`Control::WBackPressureEnabled`]. It emits
92/// [`Control::WBackPressureDisabled`] before resuming normal processing.
93///
94/// Before shutdown, transport and codec failures are delivered to the
95/// service as [`DispatchItem::Stop`]. The future resolves to `Err` only
96/// when the service itself fails. Graceful and protocol stops drain service
97/// calls before shutdown; transport failures abandon pending calls after
98/// delivering the stop notification so they cannot block teardown.
99pub struct Dispatcher<U, Err>
100where
101    U: Encoder + Decoder + 'static,
102    Err: 'static,
103{
104    inner: DispatcherInner<U, Err>,
105}
106
107// The dispatcher never pins its fields, all futures it polls are `Unpin`.
108impl<U: Encoder + Decoder, Err> Unpin for Dispatcher<U, Err> {}
109
110impl<U: Encoder + Decoder, Err> fmt::Debug for Dispatcher<U, Err> {
111    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112        f.debug_struct("Dispatcher").finish_non_exhaustive()
113    }
114}
115
116type Call<U, Err> = PipelineCall<DispatchItem<U>, Option<Response<U>>, Err>;
117type Service<U, Err> = Pipeline<DispatchItem<U>, Option<Response<U>>, Err>;
118
119struct DispatcherInner<U, Err>
120where
121    U: Encoder + Decoder + 'static,
122{
123    st: DispatcherState<U, Err>,
124    error: Option<Err>,
125    shared: Rc<DispatcherShared<U, Err>>,
126    response: Option<Call<U, Err>>,
127    timers: Timers,
128    max_inflight: u32,
129}
130
131pub(crate) struct DispatcherShared<U, Err>
132where
133    U: Encoder + Decoder,
134{
135    io: IoBoxed,
136    codec: U,
137    service: Service<U, Err>,
138    keepalive: bool,
139    error: Cell<Option<DispatcherError<Err, <U as Encoder>::Error>>>,
140    inflight: Cell<u32>,
141}
142
143#[derive(Debug)]
144enum DispatcherState<U: Encoder + Decoder, Err> {
145    Processing,
146    Backpressure,
147    Stop(StopCall<U, Err>),
148    Shutdown,
149    ShutdownIo,
150}
151
152#[derive(Debug)]
153struct StopCall<U: Encoder + Decoder, Err> {
154    fut: Call<U, Err>,
155    // started while the stop call waits for service readiness
156    timeout: Option<Sleep>,
157}
158
159#[derive(Debug)]
160enum DispatcherError<S, U> {
161    Encoder(U),
162    Service(S),
163}
164
165enum PollService {
166    /// Write backpressure is enabled while the service is not ready.
167    Backpressure,
168    Continue,
169    Ready,
170}
171
172impl<U, Err> Dispatcher<U, Err>
173where
174    U: Decoder + Encoder + 'static,
175    Err: 'static,
176{
177    /// Creates a dispatcher for an I/O transport, codec, and service pipeline.
178    ///
179    /// Keep-alive and frame-read timeout behavior is taken from the transport's
180    /// `ntex_io::IoConfig`.
181    pub fn new<Io>(io: Io, codec: U, service: Service<U, Err>) -> Dispatcher<U, Err>
182    where
183        IoBoxed: From<Io>,
184    {
185        let io = IoBoxed::from(io);
186        let shared = Rc::new(DispatcherShared {
187            keepalive: !io.cfg().keepalive_timeout().is_zero(),
188            io,
189            codec,
190            service,
191            error: Cell::new(None),
192            inflight: Cell::new(0),
193        });
194
195        Dispatcher {
196            inner: DispatcherInner {
197                timers: Timers::new(&shared.io),
198                shared,
199                response: None,
200                error: None,
201                st: DispatcherState::Processing,
202                max_inflight: u32::MAX,
203            },
204        }
205    }
206
207    #[must_use]
208    /// Sets the maximum number of concurrent service calls.
209    ///
210    /// The dispatcher stops reading frames while `max` calls are in flight
211    /// and resumes once a call completes. By default the number of calls
212    /// is not limited.
213    ///
214    /// # Panics
215    ///
216    /// Panics if `max` is zero.
217    pub fn max_inflight(mut self, max: u32) -> Self {
218        assert!(max > 0, "max_inflight must be greater than zero");
219        self.inner.max_inflight = max;
220        self
221    }
222}
223
224impl<U, Err> DispatcherShared<U, Err>
225where
226    U: Encoder + Decoder + 'static,
227    Err: 'static,
228{
229    fn call(&self, item: DispatchItem<U>) -> Call<U, Err> {
230        self.inflight.set(self.inflight.get() + 1);
231        self.service.call_static(item)
232    }
233
234    fn handle_result(&self, item: Result<Option<Response<U>>, Err>, wake: bool) {
235        match item {
236            Ok(Some(val)) => {
237                if let Err(err) = self.io.encode(val, &self.codec) {
238                    self.error.set(Some(DispatcherError::Encoder(err)));
239                }
240            }
241            Err(err) => self.error.set(Some(DispatcherError::Service(err))),
242            Ok(None) => (),
243        }
244        self.inflight.set(self.inflight.get() - 1);
245        if wake {
246            self.io.notify_dispatcher();
247        }
248    }
249}
250
251impl<U, Err> Future for Dispatcher<U, Err>
252where
253    U: Decoder + Encoder + 'static,
254    Err: 'static,
255{
256    type Output = Result<(), Err>;
257
258    #[allow(clippy::too_many_lines)]
259    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
260        let inner = &mut self.get_mut().inner;
261
262        // handle service response future
263        if let Some(fut) = inner.response.as_mut()
264            && let Poll::Ready(item) = Pin::new(fut).poll(cx)
265        {
266            inner.shared.handle_result(item, false);
267            inner.response = None;
268        }
269
270        loop {
271            match inner.st {
272                DispatcherState::Processing => {
273                    let item = match ready!(inner.poll_service(cx)) {
274                        PollService::Ready => {
275                            // decode incoming bytes if buffer is ready
276                            match inner.shared.io.poll_recv_decode(&inner.shared.codec, cx) {
277                                Ok(decoded) => {
278                                    inner.update_timer(&decoded);
279                                    if let Some(el) = decoded.item {
280                                        DispatchItem::Item(el)
281                                    } else {
282                                        return Poll::Pending;
283                                    }
284                                }
285                                Err(RecvError::Timeout) if inner.timers.active != Timer::Write => {
286                                    // input received together with a read timer wins over
287                                    // its expiry, the buffered input is decoded first
288                                    match inner.shared.io.decode_item(&inner.shared.codec) {
289                                        Ok(decoded) => {
290                                            let timer = inner.timers.active;
291                                            inner.update_timer(&decoded);
292                                            if let Some(el) = decoded.item {
293                                                DispatchItem::Item(el)
294                                            } else {
295                                                // a timer armed for the received input has
296                                                // not expired
297                                                if inner.timers.active == timer
298                                                    && let Err(ctl) = inner.handle_timeout()
299                                                {
300                                                    inner.st = inner.stop(ctl);
301                                                }
302                                                continue;
303                                            }
304                                        }
305                                        Err(err) => {
306                                            log::trace!(
307                                                "{}: Decoder error, stopping dispatcher: {:?}",
308                                                inner.shared.io.tag(),
309                                                err
310                                            );
311                                            inner.st = inner.stop(Reason::Decoder(err));
312                                            continue;
313                                        }
314                                    }
315                                }
316                                Err(RecvError::Timeout) => {
317                                    if let Err(ctl) = inner.handle_timeout() {
318                                        inner.st = inner.stop(ctl);
319                                    }
320                                    continue;
321                                }
322                                Err(RecvError::WriteBackpressure) => {
323                                    // instruct write task to notify dispatcher when data is flushed
324                                    inner.start_write_timer();
325                                    inner.st = DispatcherState::Backpressure;
326                                    DispatchItem::Control(Control::WBackPressureEnabled)
327                                }
328                                Err(RecvError::Decoder(err)) => {
329                                    log::trace!(
330                                        "{}: Decoder error, stopping dispatcher: {:?}",
331                                        inner.shared.io.tag(),
332                                        err
333                                    );
334                                    inner.st = inner.stop(Reason::Decoder(err));
335                                    continue;
336                                }
337                                Err(RecvError::PeerGone(err)) => {
338                                    let err = err.or_else(|| truncated(&inner.shared.io));
339                                    log::trace!(
340                                        "{}: Peer is gone, stopping dispatcher: {:?}",
341                                        inner.shared.io.tag(),
342                                        err
343                                    );
344                                    inner.st = inner.stop(Reason::Io(err));
345                                    continue;
346                                }
347                            }
348                        }
349                        PollService::Backpressure => {
350                            DispatchItem::Control(Control::WBackPressureEnabled)
351                        }
352                        PollService::Continue => continue,
353                    };
354
355                    inner.call_service(cx, item);
356                }
357                // handle write back-pressure
358                DispatcherState::Backpressure => {
359                    match ready!(inner.poll_service(cx)) {
360                        PollService::Ready | PollService::Backpressure => {}
361                        PollService::Continue => continue,
362                    }
363
364                    // check write timeout
365                    if let Poll::Ready(IoStatusUpdate::Timeout) =
366                        inner.shared.io.poll_status_update(cx)
367                        && let Err(reason) = inner.handle_timeout()
368                    {
369                        inner.st = inner.stop(reason);
370                        continue;
371                    }
372                    // suspends or resumes the write timeout on a filter pause
373                    inner.start_write_timer();
374
375                    let item = if let Err(err) = ready!(inner.shared.io.poll_flush(cx, false)) {
376                        inner.st = inner.stop(Reason::Io(Some(err)));
377                        continue;
378                    } else {
379                        // Stops the write timeout when write backpressure is disabled.
380                        inner.stop_timer();
381                        inner.st = DispatcherState::Processing;
382                        DispatchItem::Control(Control::WBackPressureDisabled)
383                    };
384                    inner.call_service(cx, item);
385                }
386                // deliver stop to service
387                DispatcherState::Stop(ref mut stop) => {
388                    // service may relay on poll_ready for response results
389                    let ready = inner.shared.service.poll_ready(cx);
390
391                    if let Poll::Ready(result) = Pin::new(&mut stop.fut).poll(cx) {
392                        inner.shared.handle_result(result, false);
393                        // the dispatcher returns a service error from the stop call,
394                        // unless it is stopping because of an earlier one
395                        if let Some(DispatcherError::Service(err)) = inner.shared.error.take()
396                            && inner.error.is_none()
397                        {
398                            inner.error = Some(err);
399                        }
400                        inner.shared.io.stop_timer();
401                        inner.st = DispatcherState::Shutdown;
402                        continue;
403                    }
404                    if ready.is_ready() {
405                        return Poll::Pending;
406                    }
407
408                    // the stop item waits for service readiness, it is delivered
409                    // in the background and io is closed without waiting for it
410                    let timeout = stop
411                        .timeout
412                        .get_or_insert_with(|| sleep(inner.shared.io.cfg().shutdown_timeout()));
413                    ready!(timeout.poll_elapsed(cx));
414                    log::trace!(
415                        "{}: Service is not ready for stop, shutdown io",
416                        inner.shared.io.tag()
417                    );
418
419                    let DispatcherState::Stop(stop) =
420                        mem::replace(&mut inner.st, DispatcherState::ShutdownIo)
421                    else {
422                        unreachable!()
423                    };
424                    inner.shared.io.stop_timer();
425
426                    // the task owns the readiness check from now on
427                    let shared = inner.shared.clone();
428                    let mut fut = Some(stop.fut);
429                    spawn(poll_fn(move |cx| {
430                        let _ = shared.service.poll_ready(cx);
431                        if let Some(f) = fut.as_mut() {
432                            let result = ready!(Pin::new(f).poll(cx));
433                            fut = None;
434                            shared.handle_result(result, false);
435                        }
436                        if shared.inflight.get() != 0 {
437                            shared.io.register_dispatch(cx);
438                            return Poll::Pending;
439                        }
440                        shared.service.poll_shutdown(cx)
441                    }));
442                }
443                // drain service responses and shutdown service
444                DispatcherState::Shutdown => {
445                    // service may relay on poll_ready for response results
446                    let _ = inner.shared.service.poll_ready(cx);
447
448                    if inner.shared.inflight.get() != 0 {
449                        inner.shared.io.register_dispatch(cx);
450                        return Poll::Pending;
451                    }
452
453                    ready!(inner.shared.service.poll_shutdown(cx));
454                    log::trace!(
455                        "{}: Service shutdown is completed, stop",
456                        inner.shared.io.tag()
457                    );
458                    inner.st = DispatcherState::ShutdownIo;
459                }
460                // shutdown io
461                DispatcherState::ShutdownIo => {
462                    let _ = ready!(inner.shared.io.poll_shutdown(cx));
463
464                    return Poll::Ready(if let Some(err) = inner.error.take() {
465                        Err(err)
466                    } else {
467                        Ok(())
468                    });
469                }
470            }
471        }
472    }
473}
474
475impl<U, Err> DispatcherInner<U, Err>
476where
477    U: Decoder + Encoder + 'static,
478    Err: 'static,
479{
480    fn stop(&self, reason: Reason<U>) -> DispatcherState<U, Err> {
481        DispatcherState::Stop(StopCall {
482            fut: self.shared.call(DispatchItem::Stop(reason)),
483            timeout: None,
484        })
485    }
486
487    fn call_service(&mut self, cx: &mut Context<'_>, item: DispatchItem<U>) {
488        let mut fut = self.shared.call(item);
489
490        // the first poll consumes the pipeline readiness checked by `poll_service()`
491        if let Poll::Ready(result) = Pin::new(&mut fut).poll(cx) {
492            self.shared.handle_result(result, false);
493        } else if self.response.is_none() {
494            self.response = Some(fut);
495        } else {
496            let shared = self.shared.clone();
497            spawn(async move {
498                let result = fut.await;
499                shared.handle_result(result, true);
500            });
501        }
502    }
503
504    fn check_error(&mut self) -> PollService {
505        // check for errors
506        if let Some(err) = self.shared.error.take() {
507            log::trace!(
508                "{}: Error occurred, stopping dispatcher",
509                self.shared.io.tag()
510            );
511            match err {
512                DispatcherError::Encoder(err) => {
513                    self.st = self.stop(Reason::Encoder(err));
514                }
515                DispatcherError::Service(err) => {
516                    self.error = Some(err);
517                    self.st = self.stop(Reason::Service);
518                }
519            }
520            PollService::Continue
521        } else {
522            PollService::Ready
523        }
524    }
525
526    fn poll_service(&mut self, cx: &mut Context<'_>) -> Poll<PollService> {
527        // wait until an in-flight call completes and the service becomes ready
528        let ready = if self.shared.inflight.get() >= self.max_inflight {
529            Poll::Pending
530        } else {
531            self.shared.service.poll_ready(cx)
532        };
533        match ready {
534            Poll::Ready(Ok(())) => Poll::Ready(self.check_error()),
535            // pause io read task
536            Poll::Pending => {
537                log::trace!(
538                    "{}: Service is not ready, register dispatcher",
539                    self.shared.io.tag()
540                );
541
542                // the write timeout keeps running while the service is paused
543                if self.timers.active != Timer::Write {
544                    self.stop_timer();
545                }
546                self.timers.reset_read(self.shared.io.cfg());
547
548                match ready!(self.shared.io.poll_read_pause(cx)) {
549                    IoStatusUpdate::Timeout => {
550                        if let Err(reason) = self.handle_timeout() {
551                            log::trace!(
552                                "{}: Timeout during pause, stopping dispatcher: {:?}",
553                                self.shared.io.tag(),
554                                reason
555                            );
556                            self.st = self.stop(reason);
557                        }
558                        Poll::Ready(PollService::Continue)
559                    }
560                    // frames that were already received are dispatched once
561                    // the service is ready, as in the processing state
562                    IoStatusUpdate::PeerGone(_) if self.shared.io.read_dst_size() != 0 => {
563                        log::trace!(
564                            "{}: Peer is gone during pause, wait for service",
565                            self.shared.io.tag()
566                        );
567                        Poll::Pending
568                    }
569                    IoStatusUpdate::PeerGone(err) => {
570                        log::trace!(
571                            "{}: Peer is gone during pause, stopping dispatcher: {:?}",
572                            self.shared.io.tag(),
573                            err
574                        );
575                        self.st = self.stop(Reason::Io(err));
576                        Poll::Ready(PollService::Continue)
577                    }
578                    IoStatusUpdate::WriteBackpressure => {
579                        if !matches!(self.st, DispatcherState::Backpressure) {
580                            self.start_write_timer();
581                        }
582                        self.st = DispatcherState::Backpressure;
583                        Poll::Ready(PollService::Backpressure)
584                    }
585                }
586            }
587            // handle service readiness error
588            Poll::Ready(Err(err)) => {
589                log::trace!(
590                    "{}: Service readiness check failed, stopping",
591                    self.shared.io.tag()
592                );
593                self.st = self.stop(Reason::Service);
594                self.error = Some(err);
595                Poll::Ready(PollService::Continue)
596            }
597        }
598    }
599
600    /// Starts the write timeout when write backpressure is enabled.
601    ///
602    /// Frames are not decoded during backpressure, so read-side timers are
603    /// stopped when no write timeout is configured. The timeout does not run
604    /// while the filter chain pauses writes, the peer is not charged for it,
605    /// and starts over once writes resume.
606    fn start_write_timer(&mut self) {
607        let timeout = self.shared.io.cfg().write_timeout();
608        if timeout.is_zero() || self.shared.io.is_write_filter_paused() {
609            self.stop_timer();
610        } else if self.timers.active != Timer::Write {
611            self.timers.active = Timer::Write;
612            self.shared.io.start_timer(timeout);
613        }
614    }
615
616    fn update_timer(&mut self, decoded: &Decoded<<U as Decoder>::Item>) {
617        let item = decoded.item.is_some();
618        self.timers.update_read(
619            self.shared.io.cfg(),
620            item,
621            decoded.remains as u32,
622            decoded.consumed as u32,
623        );
624
625        // the filter chain pauses reading, the peer is not charged for it
626        if !item && self.shared.io.is_read_filter_paused() {
627            if self.timers.active != Timer::Write {
628                self.stop_timer();
629            }
630            self.timers.reset_read(self.shared.io.cfg());
631            return;
632        }
633
634        // keep-alive and frame read timers do not apply while a frame is handled
635        let handling = item || self.shared.inflight.get() != 0;
636        let timer = self
637            .timers
638            .select(self.shared.io.cfg(), self.shared.keepalive, handling);
639        self.set_timer(timer);
640    }
641
642    /// Stops the dispatcher timer, if it is armed.
643    fn stop_timer(&mut self) {
644        if self.timers.active != Timer::Stopped {
645            self.timers.active = Timer::Stopped;
646            self.shared.io.stop_timer();
647        }
648    }
649
650    /// Arms the dispatcher timer for a read-side purpose, an armed timer
651    /// with the same purpose keeps running.
652    ///
653    /// The keep-alive timer is suspended instead of stopped, re-arming it is
654    /// cheaper than registering it again.
655    fn set_timer(&mut self, timer: Timer) {
656        if self.timers.active == timer {
657            return;
658        }
659        let io = &self.shared.io;
660        self.timers.active = match (self.timers.active, timer) {
661            (Timer::KeepAlive | Timer::Suspended, Timer::Stopped) => Timer::Suspended,
662            (_, Timer::KeepAlive) => {
663                log::trace!(
664                    "{}: Start keep-alive timer {:?}",
665                    io.tag(),
666                    io.cfg().keepalive_timeout()
667                );
668                let hnd = io.start_timer(io.cfg().keepalive_timeout());
669                self.timers.keepalive = Some(hnd.instant());
670                Timer::KeepAlive
671            }
672            (_, Timer::FrameRead) if let Some(params) = io.cfg().frame_read_rate() => {
673                io.start_timer(params.timeout);
674                Timer::FrameRead
675            }
676            _ => {
677                io.stop_timer();
678                Timer::Stopped
679            }
680        };
681    }
682
683    fn handle_timeout(&mut self) -> Result<(), Reason<U>> {
684        match self.timers.active {
685            Timer::FrameRead => {
686                let (Some(params), Some(p)) = (
687                    self.shared.io.cfg().frame_read_rate(),
688                    self.timers.read.progress(),
689                ) else {
690                    self.timers.active = Timer::Stopped;
691                    return Ok(());
692                };
693
694                // read rate, start timer for next period
695                if p.consumed > params.rate {
696                    let total = p.consumed;
697                    p.consumed = 0;
698
699                    if !params.max_timeout.is_zero() {
700                        p.max_timeout = Seconds(p.max_timeout.0.saturating_sub(params.timeout.0));
701                    }
702
703                    if params.max_timeout.is_zero() || !p.max_timeout.is_zero() {
704                        log::trace!(
705                            "{}: Frame read rate {:?}, extend timer",
706                            self.shared.io.tag(),
707                            total
708                        );
709                        self.shared.io.start_timer(params.timeout);
710                        return Ok(());
711                    }
712                    log::trace!(
713                        "{}: Max payload timeout has been reached",
714                        self.shared.io.tag()
715                    );
716                }
717                Err(Reason::ReadTimeout)
718            }
719            // backpressure can be released unnoticed while the service is paused
720            Timer::Write if !self.shared.io.is_wr_backpressure() => {
721                self.timers.active = Timer::Stopped;
722                Ok(())
723            }
724            // the filter chain paused writes before the dispatcher noticed
725            Timer::Write if self.shared.io.is_write_filter_paused() => {
726                self.stop_timer();
727                Ok(())
728            }
729            Timer::Write => {
730                log::trace!(
731                    "{}: Write backpressure timeout, stopping dispatcher",
732                    self.shared.io.tag()
733                );
734                Err(Reason::WriteTimeout)
735            }
736            Timer::KeepAlive => {
737                log::trace!(
738                    "{}: Keep-alive error, stopping dispatcher",
739                    self.shared.io.tag()
740                );
741                Err(Reason::KeepAlive)
742            }
743            // the suspended keep-alive timer has expired, a timeout before its
744            // expiry is external
745            Timer::Suspended
746                if self
747                    .timers
748                    .keepalive
749                    .is_none_or(|expiry| Instant::now() >= expiry) =>
750            {
751                self.timers.active = Timer::Stopped;
752                Ok(())
753            }
754            // external timeout, applies to idle connection
755            Timer::Stopped | Timer::Suspended if self.shared.inflight.get() == 0 => {
756                log::trace!(
757                    "{}: Idle timeout, stopping dispatcher",
758                    self.shared.io.tag()
759                );
760                Err(Reason::KeepAlive)
761            }
762            Timer::Stopped | Timer::Suspended => Ok(()),
763        }
764    }
765}
766
767impl<U> fmt::Debug for DispatchItem<U>
768where
769    U: Encoder + Decoder,
770    <U as Decoder>::Item: fmt::Debug,
771{
772    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
773        match self {
774            DispatchItem::Item(item) => f.debug_tuple("DispatchItem::Item").field(item).finish(),
775            DispatchItem::Control(e) => f.debug_tuple("DispatchItem::Control").field(e).finish(),
776            DispatchItem::Stop(e) => f.debug_tuple("DispatchItem::Stop").field(e).finish(),
777        }
778    }
779}
780
781impl<U> fmt::Debug for Reason<U>
782where
783    U: Encoder + Decoder,
784    <U as Encoder>::Error: fmt::Debug,
785    <U as Decoder>::Error: fmt::Debug,
786{
787    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
788        match self {
789            Reason::Service => f.write_str("Reason::Service"),
790            Reason::Io(err) => f.debug_tuple("Reason::Io").field(err).finish(),
791            Reason::Encoder(err) => f.debug_tuple("Reason::Encoder").field(err).finish(),
792            Reason::Decoder(err) => f.debug_tuple("Reason::Decoder").field(err).finish(),
793            Reason::KeepAlive => f.write_str("Reason::KeepAlive"),
794            Reason::ReadTimeout => f.write_str("Reason::ReadTimeout"),
795            Reason::WriteTimeout => f.write_str("Reason::WriteTimeout"),
796        }
797    }
798}
799
800#[cfg(test)]
801#[allow(clippy::unused_async_trait_impl)]
802mod tests {
803    use std::sync::{Arc, Mutex, atomic::AtomicBool, atomic::Ordering::Relaxed};
804    use std::{cell::RefCell, io};
805
806    use ntex_bytes::{BytePageSize, BytePages, Bytes, BytesMut};
807    use ntex_codec::BytesCodec;
808    use ntex_io::{Io, IoConfig, IoRef, testing::IoTest};
809    use ntex_service::{Ctx, Pipeline, Service, cfg::SharedCfg};
810    use ntex_util::time::{Millis, sleep, timeout};
811    use ntex_util::{channel::condition::Condition, channel::oneshot, future::lazy};
812    use rand::Rng;
813
814    use super::*;
815
816    /// Waits until `f` returns `true`, up to `max`.
817    async fn wait_until(max: Millis, f: impl Fn() -> bool) {
818        for _ in 0..max.0 / 50 {
819            if f() {
820                break;
821            }
822            sleep(Millis(50)).await;
823        }
824    }
825
826    /// Waits until the peer is closed, up to `max`.
827    async fn wait_closed(client: &IoTest, max: Millis) {
828        wait_until(max, || client.is_closed()).await;
829    }
830
831    pub(crate) struct State(IoRef);
832
833    impl State {
834        fn io(&self) -> &IoRef {
835            &self.0
836        }
837
838        fn close(&self) {
839            self.0.close();
840        }
841    }
842
843    #[derive(Copy, Clone)]
844    struct BCodec(usize);
845
846    impl Encoder for BCodec {
847        type Item = Bytes;
848        type Error = io::Error;
849
850        fn encode(&self, item: Bytes, dst: &mut BytePages) -> Result<(), Self::Error> {
851            dst.append(item);
852            Ok(())
853        }
854    }
855
856    impl Decoder for BCodec {
857        type Item = Bytes;
858        type Error = io::Error;
859
860        fn decode(&self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
861            if src.len() < self.0 {
862                Ok(None)
863            } else {
864                Ok(Some(src.split_to(self.0)))
865            }
866        }
867    }
868
869    impl<U, Err> Dispatcher<U, Err>
870    where
871        U: Decoder + Encoder + 'static,
872    {
873        /// Construct new `Dispatcher` instance
874        pub(crate) fn debug<S>(io: Io, codec: U, service: S) -> (Self, State)
875        where
876            S: Service<(), DispatchItem<U>, Res = Option<Response<U>>, Error = Err> + 'static,
877        {
878            let st = State(io.get_ref());
879            (Dispatcher::new(io, codec, Pipeline::new((), service)), st)
880        }
881    }
882
883    #[ntex::test]
884    async fn basics() {
885        let (client, server) = IoTest::create();
886        client.remote_buffer_cap(1024);
887        client.write("GET /test HTTP/1\r\n\r\n");
888
889        let (disp, _) = Dispatcher::debug(
890            Io::from(server),
891            BytesCodec,
892            ntex_service::fn_service(|msg: DispatchItem<BytesCodec>| async move {
893                sleep(Millis(50)).await;
894                if let DispatchItem::Item(msg) = msg {
895                    Ok::<_, ()>(Some(msg))
896                } else {
897                    Ok(None)
898                }
899            }),
900        );
901        spawn(async move {
902            let _ = disp.await;
903        });
904
905        sleep(Millis(25)).await;
906        let buf = client.read().await.unwrap();
907        assert_eq!(buf, Bytes::from_static(b"GET /test HTTP/1\r\n\r\n"));
908
909        client.write("GET /test HTTP/1\r\n\r\n");
910        let buf = client.read().await.unwrap();
911        assert_eq!(buf, Bytes::from_static(b"GET /test HTTP/1\r\n\r\n"));
912
913        client.close().await;
914        sleep(Millis(75)).await;
915        assert!(client.is_server_dropped());
916    }
917
918    #[ntex::test]
919    async fn sink() {
920        let (client, server) = IoTest::create();
921        client.remote_buffer_cap(1024);
922        client.write("GET /test HTTP/1\r\n\r\n");
923
924        let (disp, st) = Dispatcher::debug(
925            Io::from(server),
926            BytesCodec,
927            ntex_service::fn_service(|msg: DispatchItem<BytesCodec>| async move {
928                if let DispatchItem::Item(msg) = msg {
929                    Ok::<_, ()>(Some(msg))
930                } else if let DispatchItem::Stop(_) = msg {
931                    Ok(None)
932                } else {
933                    panic!()
934                }
935            }),
936        );
937        spawn(async move {
938            let _ = disp.await;
939        });
940
941        let buf = client.read().await.unwrap();
942        assert_eq!(buf, Bytes::from_static(b"GET /test HTTP/1\r\n\r\n"));
943
944        assert!(
945            st.io()
946                .encode(Bytes::from_static(b"test"), &BytesCodec)
947                .is_ok()
948        );
949        let buf = client.read().await.unwrap();
950        assert_eq!(buf, Bytes::from_static(b"test"));
951
952        st.close();
953        wait_until(Millis(2000), || client.is_server_dropped()).await;
954        assert!(client.is_server_dropped());
955    }
956
957    #[ntex::test]
958    async fn err_in_service() {
959        let (client, server) = IoTest::create();
960        client.remote_buffer_cap(0);
961        client.write("GET /test HTTP/1\r\n\r\n");
962
963        let (disp, state) = Dispatcher::debug(
964            Io::new(server, SharedCfg::new("SRV")),
965            BytesCodec,
966            ntex_service::fn_service(|_: DispatchItem<BytesCodec>| async move {
967                Err::<Option<Bytes>, _>(())
968            }),
969        );
970        state
971            .io()
972            .encode(Bytes::from_static(b"GET /test HTTP/1\r\n\r\n"), &BytesCodec)
973            .unwrap();
974        spawn(async move {
975            let _ = disp.await;
976        });
977
978        // buffer should be flushed
979        client.remote_buffer_cap(1024);
980        let buf = client.read().await.unwrap();
981        assert_eq!(buf, Bytes::from_static(b"GET /test HTTP/1\r\n\r\n"));
982
983        // write side must be closed, dispatcher waiting for read side to close
984        wait_closed(&client, Millis(1000)).await;
985        assert!(client.is_closed());
986
987        // close read side
988        client.close().await;
989
990        // dispatcher is closed
991        assert!(client.is_server_dropped());
992    }
993
994    #[ntex::test]
995    #[allow(clippy::items_after_statements)]
996    async fn err_in_service_ready() {
997        let (client, server) = IoTest::create();
998        client.remote_buffer_cap(0);
999        client.write("GET /test HTTP/1\r\n\r\n");
1000
1001        let counter = Rc::new(Cell::new(0));
1002
1003        struct Srv(Rc<Cell<usize>>);
1004
1005        impl Service<(), DispatchItem<BytesCodec>> for Srv {
1006            type Res = Option<Response<BytesCodec>>;
1007            type Error = &'static str;
1008
1009            async fn ready(&self, _: Ctx<'_, Self>) -> Result<(), Self::Error> {
1010                self.0.set(self.0.get() + 1);
1011                Err("test")
1012            }
1013
1014            async fn call(
1015                &self,
1016                _: DispatchItem<BytesCodec>,
1017                _: Ctx<'_, Self>,
1018            ) -> Result<Self::Res, Self::Error> {
1019                Ok(None)
1020            }
1021        }
1022
1023        let (disp, state) = Dispatcher::debug(Io::from(server), BytesCodec, Srv(counter.clone()));
1024        spawn(async move {
1025            let res = disp.await;
1026            assert_eq!(res, Err("test"));
1027        });
1028
1029        state
1030            .io()
1031            .encode(Bytes::from_static(b"GET /test HTTP/1\r\n\r\n"), &BytesCodec)
1032            .unwrap();
1033
1034        // buffer should be flushed
1035        client.remote_buffer_cap(1024);
1036        let buf = client.read().await.unwrap();
1037        assert_eq!(buf, Bytes::from_static(b"GET /test HTTP/1\r\n\r\n"));
1038
1039        // write side must be closed, dispatcher waiting for read side to close
1040        wait_closed(&client, Millis(1000)).await;
1041        assert!(client.is_closed());
1042
1043        // close read side
1044        client.close().await;
1045        assert!(client.is_server_dropped());
1046
1047        // service must be checked for readiness all the time,
1048        // the stop call checks readiness as well
1049        assert_eq!(counter.get(), 4);
1050    }
1051
1052    #[ntex::test]
1053    async fn write_backpressure() {
1054        let (client, server) = IoTest::create();
1055        // do not allow to write to socket
1056        client.remote_buffer_cap(0);
1057        client.write("GET /test HTTP/1\r\n\r\n");
1058
1059        let data = Arc::new(Mutex::new(RefCell::new(Vec::new())));
1060        let data2 = data.clone();
1061
1062        let io = Io::new(
1063            server,
1064            SharedCfg::new("TEST").add(
1065                IoConfig::new()
1066                    .set_read_size(BytePageSize::Size8, BytePageSize::Size8)
1067                    .set_read_backpressure(BytePageSize::Size8.capacity())
1068                    .set_write_backpressure(16 * 1024),
1069            ),
1070        );
1071
1072        let (disp, state) = Dispatcher::debug(
1073            io,
1074            BytesCodec,
1075            ntex_service::fn_service(move |msg: DispatchItem<BytesCodec>| {
1076                let data = data2.clone();
1077                async move {
1078                    match msg {
1079                        DispatchItem::Item(_) => {
1080                            data.lock().unwrap().borrow_mut().push(0);
1081                            let bytes = rand::rng()
1082                                .sample_iter(&rand::distr::Alphanumeric)
1083                                .take(65_536)
1084                                .map(char::from)
1085                                .collect::<String>();
1086                            return Ok::<_, ()>(Some(Bytes::from(bytes)));
1087                        }
1088                        DispatchItem::Control(Control::WBackPressureEnabled) => {
1089                            data.lock().unwrap().borrow_mut().push(1);
1090                        }
1091                        DispatchItem::Control(Control::WBackPressureDisabled) => {
1092                            data.lock().unwrap().borrow_mut().push(2);
1093                        }
1094                        _ => (),
1095                    }
1096                    Ok(None)
1097                }
1098            }),
1099        );
1100
1101        spawn(async move {
1102            let _ = disp.await;
1103        });
1104
1105        let buf = client.read_any();
1106        assert_eq!(buf, Bytes::from_static(b""));
1107        client.write("GET /test HTTP/1\r\n\r\n");
1108        sleep(Millis(25)).await;
1109
1110        // buf must be consumed
1111        assert_eq!(client.remote_buffer(|buf| buf.len()), 0);
1112
1113        // response message
1114        assert_eq!(state.io().with_write_src(|buf| buf.len()).unwrap(), 65536);
1115
1116        client.remote_buffer_cap(10240);
1117        sleep(Millis(50)).await;
1118        assert_eq!(state.io().with_write_src(|buf| buf.len()).unwrap(), 55296);
1119
1120        client.remote_buffer_cap(48056);
1121        sleep(Millis(50)).await;
1122        assert_eq!(state.io().with_write_src(|buf| buf.len()).unwrap(), 7240);
1123
1124        // backpressure disabled
1125        assert_eq!(&data.lock().unwrap().borrow()[..], &[0, 1, 2]);
1126    }
1127
1128    #[ntex::test]
1129    async fn disconnect_during_read_backpressure() {
1130        let (client, server) = IoTest::create();
1131        client.remote_buffer_cap(0);
1132
1133        let (disp, state) = Dispatcher::debug(
1134            Io::new(
1135                server,
1136                SharedCfg::new("TEST").add(
1137                    IoConfig::new()
1138                        .set_keepalive_timeout(Seconds::ZERO)
1139                        .set_read_size(BytePageSize::Size4, BytePageSize::Size4)
1140                        .set_read_backpressure(BytePageSize::Size4.capacity()),
1141                ),
1142            ),
1143            BytesCodec,
1144            ntex_util::services::inflight::InFlightService::new(
1145                1,
1146                ntex_service::fn_service(move |msg: DispatchItem<BytesCodec>| async move {
1147                    if let DispatchItem::Item(_) = msg {
1148                        sleep(Millis(500)).await;
1149                        Ok::<_, ()>(None)
1150                    } else {
1151                        Ok(None)
1152                    }
1153                }),
1154            ),
1155        );
1156
1157        let (tx, rx) = ntex::channel::oneshot::channel();
1158        ntex::rt::spawn(async move {
1159            let _ = disp.await;
1160            let _ = tx.send(());
1161        });
1162
1163        let bytes = rand::rng()
1164            .sample_iter(&rand::distr::Alphanumeric)
1165            .take(BytePageSize::Size4.capacity())
1166            .map(char::from)
1167            .collect::<String>();
1168        client.write(bytes.clone());
1169        sleep(Millis(25)).await;
1170        client.write(bytes);
1171        sleep(Millis(25)).await;
1172
1173        // close read side
1174        state.close();
1175        let _ = rx.recv().await;
1176    }
1177
1178    #[ntex::test]
1179    async fn application_close_waits_for_pending_service_call() {
1180        let started = Arc::new(AtomicBool::new(false));
1181        let started2 = started.clone();
1182        let release = Condition::<()>::new();
1183        let release2 = release.clone();
1184        let (client, server) = IoTest::create();
1185        client.remote_buffer_cap(1024);
1186
1187        let (mut disp, state) = Dispatcher::debug(
1188            Io::from(server),
1189            BytesCodec,
1190            ntex_service::fn_service(move |msg: DispatchItem<BytesCodec>| {
1191                let started = started2.clone();
1192                let waiter = release2.wait();
1193                async move {
1194                    if matches!(msg, DispatchItem::Item(_)) {
1195                        started.store(true, Relaxed);
1196                        let _ = waiter.ready().await;
1197                    }
1198                    Ok::<_, ()>(None)
1199                }
1200            }),
1201        );
1202
1203        client.write("request");
1204        for _ in 0..100 {
1205            assert!(lazy(|cx| Pin::new(&mut disp).poll(cx)).await.is_pending());
1206            if started.load(Relaxed) {
1207                break;
1208            }
1209            sleep(Millis(10)).await;
1210        }
1211        assert!(started.load(Relaxed));
1212
1213        // the service call is pending until it is released
1214        state.close();
1215        assert!(
1216            timeout(Millis(50), poll_fn(|cx| Pin::new(&mut disp).poll(cx)))
1217                .await
1218                .is_err()
1219        );
1220        release.notify(());
1221        timeout(Millis(1000), poll_fn(|cx| Pin::new(&mut disp).poll(cx)))
1222            .await
1223            .expect("dispatcher did not drain pending service call")
1224            .unwrap();
1225    }
1226
1227    #[ntex::test]
1228    async fn max_inflight_calls() {
1229        let calls = Rc::new(Cell::new(0));
1230        let calls2 = calls.clone();
1231        let release = Condition::<()>::new();
1232        let release2 = release.clone();
1233        let (client, server) = IoTest::create();
1234        client.remote_buffer_cap(1024);
1235
1236        let (disp, _) = Dispatcher::debug(
1237            Io::from(server),
1238            BytesCodec,
1239            ntex_service::fn_service(move |msg: DispatchItem<BytesCodec>| {
1240                let waiter = release2.wait();
1241                if matches!(msg, DispatchItem::Item(_)) {
1242                    calls2.set(calls2.get() + 1);
1243                }
1244                async move {
1245                    let _ = waiter.ready().await;
1246                    Ok::<_, ()>(None)
1247                }
1248            }),
1249        );
1250        ntex::rt::spawn(disp.max_inflight(2));
1251
1252        for msg in ["a", "b", "c"] {
1253            client.write(msg);
1254            sleep(Millis(25)).await;
1255        }
1256        assert_eq!(calls.get(), 2);
1257
1258        release.notify(());
1259        sleep(Millis(25)).await;
1260        assert_eq!(calls.get(), 3);
1261    }
1262
1263    #[ntex::test]
1264    async fn keepalive() {
1265        let (client, server) = IoTest::create();
1266        client.remote_buffer_cap(1024);
1267        client.write("GET /test HTTP/1\r\n\r\n");
1268
1269        let data = Arc::new(Mutex::new(RefCell::new(Vec::new())));
1270        let data2 = data.clone();
1271
1272        let cfg = SharedCfg::new("DBG").add(
1273            IoConfig::new()
1274                .set_shutdown_timeout(Seconds(1))
1275                .set_keepalive_timeout(Seconds(1)),
1276        );
1277
1278        let (disp, state) = Dispatcher::debug(
1279            Io::new(server, cfg),
1280            BytesCodec,
1281            ntex_service::fn_service(move |msg: DispatchItem<BytesCodec>| {
1282                let data = data2.clone();
1283                async move {
1284                    match msg {
1285                        DispatchItem::Item(bytes) => {
1286                            data.lock().unwrap().borrow_mut().push(0);
1287                            return Ok::<_, ()>(Some(bytes));
1288                        }
1289                        DispatchItem::Stop(Reason::KeepAlive) => {
1290                            data.lock().unwrap().borrow_mut().push(1);
1291                        }
1292                        _ => (),
1293                    }
1294                    Ok(None)
1295                }
1296            }),
1297        );
1298        spawn(async move {
1299            let _ = disp.await;
1300        });
1301
1302        let buf = client.read().await.unwrap();
1303        assert_eq!(buf, Bytes::from_static(b"GET /test HTTP/1\r\n\r\n"));
1304        wait_until(Millis(3000), || {
1305            !state.0.is_active() && client.is_closed() && data.lock().unwrap().borrow().len() == 2
1306        })
1307        .await;
1308
1309        // write side must be closed, dispatcher should fail with keep-alive
1310        assert!(!state.0.is_active());
1311        assert!(client.is_closed());
1312        assert_eq!(&data.lock().unwrap().borrow()[..], &[0, 1]);
1313    }
1314
1315    #[ntex::test]
1316    async fn keepalive2() {
1317        let (client, server) = IoTest::create();
1318        client.remote_buffer_cap(1024);
1319
1320        let data = Arc::new(Mutex::new(RefCell::new(Vec::new())));
1321        let data2 = data.clone();
1322
1323        let cfg = SharedCfg::new("DBG").add(
1324            IoConfig::new()
1325                .set_keepalive_timeout(Seconds(1))
1326                .set_frame_read_rate(Seconds(1), Seconds(2), 2),
1327        );
1328
1329        let (disp, state) = Dispatcher::debug(
1330            Io::new(server, cfg),
1331            BCodec(8),
1332            ntex_service::fn_service(move |msg: DispatchItem<BCodec>| {
1333                let data = data2.clone();
1334                async move {
1335                    match msg {
1336                        DispatchItem::Item(bytes) => {
1337                            data.lock().unwrap().borrow_mut().push(0);
1338                            return Ok::<_, ()>(Some(bytes));
1339                        }
1340                        DispatchItem::Stop(Reason::KeepAlive) => {
1341                            data.lock().unwrap().borrow_mut().push(1);
1342                        }
1343                        _ => (),
1344                    }
1345                    Ok(None)
1346                }
1347            }),
1348        );
1349        spawn(async move {
1350            let _ = disp.await;
1351        });
1352
1353        client.write("12345678");
1354        let buf = client.read().await.unwrap();
1355        assert_eq!(buf, Bytes::from_static(b"12345678"));
1356        wait_until(Millis(3000), || {
1357            !state.0.is_active() && client.is_closed() && data.lock().unwrap().borrow().len() == 2
1358        })
1359        .await;
1360
1361        // write side must be closed, dispatcher should fail with keep-alive
1362        assert!(!state.0.is_active());
1363        assert!(client.is_closed());
1364        assert_eq!(&data.lock().unwrap().borrow()[..], &[0, 1]);
1365    }
1366
1367    /// Update keep-alive timer after receiving frame
1368    #[ntex::test]
1369    async fn keepalive3() {
1370        let (client, server) = IoTest::create();
1371        client.remote_buffer_cap(1024);
1372
1373        let data = Arc::new(Mutex::new(RefCell::new(Vec::new())));
1374        let data2 = data.clone();
1375
1376        let cfg = SharedCfg::new("DBG").add(
1377            IoConfig::new()
1378                .set_keepalive_timeout(Seconds(1))
1379                .set_frame_read_rate(Seconds(1), Seconds(2), 2),
1380        );
1381
1382        let (disp, _) = Dispatcher::debug(
1383            Io::new(server, cfg),
1384            BCodec(1),
1385            ntex_service::fn_service(move |msg: DispatchItem<BCodec>| {
1386                let data = data2.clone();
1387                async move {
1388                    match msg {
1389                        DispatchItem::Item(bytes) => {
1390                            data.lock().unwrap().borrow_mut().push(0);
1391                            return Ok::<_, ()>(Some(bytes));
1392                        }
1393                        DispatchItem::Stop(Reason::KeepAlive) => {
1394                            data.lock().unwrap().borrow_mut().push(1);
1395                        }
1396                        _ => (),
1397                    }
1398                    Ok(None)
1399                }
1400            }),
1401        );
1402        spawn(async move {
1403            let _ = disp.await;
1404        });
1405
1406        client.write("1");
1407        let buf = client.read().await.unwrap();
1408        assert_eq!(buf, Bytes::from_static(b"1"));
1409        sleep(Millis(500)).await;
1410
1411        client.write("2");
1412        let buf = client.read().await.unwrap();
1413        assert_eq!(buf, Bytes::from_static(b"2"));
1414
1415        sleep(Millis(500)).await;
1416        client.write("3");
1417        let buf = client.read().await.unwrap();
1418        assert_eq!(buf, Bytes::from_static(b"3"));
1419
1420        sleep(Millis(500)).await;
1421        assert!(!client.is_closed());
1422        assert_eq!(&data.lock().unwrap().borrow()[..], &[0, 0, 0]);
1423    }
1424
1425    #[ntex::test]
1426    async fn read_timeout() {
1427        let (client, server) = IoTest::create();
1428        client.remote_buffer_cap(1024);
1429
1430        let data = Arc::new(Mutex::new(RefCell::new(Vec::new())));
1431        let data2 = data.clone();
1432
1433        let io = Io::new(
1434            server,
1435            SharedCfg::new("TEST").add(
1436                IoConfig::new()
1437                    .set_keepalive_timeout(Seconds::ZERO)
1438                    .set_frame_read_rate(Seconds(1), Seconds(2), 2),
1439            ),
1440        );
1441
1442        let (disp, state) = Dispatcher::debug(
1443            io,
1444            BCodec(8),
1445            ntex_service::fn_service(move |msg: DispatchItem<BCodec>| {
1446                let data = data2.clone();
1447                async move {
1448                    match msg {
1449                        DispatchItem::Item(bytes) => {
1450                            data.lock().unwrap().borrow_mut().push(0);
1451                            return Ok::<_, ()>(Some(bytes));
1452                        }
1453                        DispatchItem::Stop(Reason::ReadTimeout) => {
1454                            data.lock().unwrap().borrow_mut().push(1);
1455                        }
1456                        _ => (),
1457                    }
1458                    Ok(None)
1459                }
1460            }),
1461        );
1462        spawn(async move {
1463            let _ = disp.await;
1464        });
1465
1466        client.write("12345678");
1467        let buf = client.read().await.unwrap();
1468        assert_eq!(buf, Bytes::from_static(b"12345678"));
1469
1470        // the first period receives 3 bytes and is extended, the second one
1471        // receives 1 byte and fails
1472        client.write("1");
1473        sleep(Millis(500)).await;
1474        assert!(state.0.is_active());
1475        client.write("23");
1476        sleep(Millis(1000)).await;
1477        assert!(state.0.is_active());
1478        client.write("4");
1479        wait_until(Millis(3000), || {
1480            !state.0.is_active() && client.is_closed() && data.lock().unwrap().borrow().len() == 2
1481        })
1482        .await;
1483
1484        // write side must be closed, dispatcher should fail with keep-alive
1485        assert!(!state.0.is_active());
1486        assert!(client.is_closed());
1487        assert_eq!(&data.lock().unwrap().borrow()[..], &[0, 1]);
1488    }
1489
1490    #[ntex::test]
1491    async fn idle_timeout() {
1492        let (client, server) = IoTest::create();
1493        client.remote_buffer_cap(1024);
1494
1495        let data = Arc::new(Mutex::new(RefCell::new(Vec::new())));
1496        let data2 = data.clone();
1497
1498        let io = Io::new(
1499            server,
1500            SharedCfg::new("DBG").add(IoConfig::new().set_keepalive_timeout(Seconds::ZERO)),
1501        );
1502        let ioref = io.get_ref();
1503
1504        let (disp, state) = Dispatcher::debug(
1505            io,
1506            BCodec(1),
1507            ntex_service::fn_service(move |msg: DispatchItem<BCodec>| {
1508                let ioref = ioref.clone();
1509                ntex::rt::spawn(async move {
1510                    sleep(Millis(500)).await;
1511                    ioref.notify_timeout();
1512                });
1513                let data = data2.clone();
1514                async move {
1515                    match msg {
1516                        DispatchItem::Item(bytes) => {
1517                            data.lock().unwrap().borrow_mut().push(0);
1518                            return Ok::<_, ()>(Some(bytes));
1519                        }
1520                        DispatchItem::Stop(Reason::ReadTimeout) => {
1521                            data.lock().unwrap().borrow_mut().push(1);
1522                        }
1523                        _ => (),
1524                    }
1525                    Ok(None)
1526                }
1527            }),
1528        );
1529        spawn(async move {
1530            let _ = disp.await;
1531        });
1532
1533        client.write("1");
1534        let buf = client.read().await.unwrap();
1535        assert_eq!(buf, Bytes::from_static(b"1"));
1536
1537        wait_until(Millis(2000), || !state.0.is_active() && client.is_closed()).await;
1538        assert!(!state.0.is_active());
1539        assert!(client.is_closed());
1540    }
1541
1542    #[ntex::test]
1543    async fn unhandled_data() {
1544        let handled = Arc::new(AtomicBool::new(false));
1545        let handled2 = handled.clone();
1546
1547        let (client, server) = IoTest::create();
1548        client.remote_buffer_cap(1024);
1549        client.write("GET /test HTTP/1\r\n\r\n");
1550
1551        let (disp, _) = Dispatcher::debug(
1552            Io::from(server),
1553            BytesCodec,
1554            ntex_service::fn_service(move |msg: DispatchItem<BytesCodec>| {
1555                handled2.store(true, Relaxed);
1556                async move {
1557                    sleep(Millis(50)).await;
1558                    if let DispatchItem::Item(msg) = msg {
1559                        Ok::<_, ()>(Some(msg))
1560                    } else if let DispatchItem::Stop(_) = msg {
1561                        Ok::<_, ()>(None)
1562                    } else {
1563                        panic!()
1564                    }
1565                }
1566            }),
1567        );
1568        client.close().await;
1569        spawn(async move {
1570            let _ = disp.await;
1571        });
1572        sleep(Millis(50)).await;
1573
1574        assert!(handled.load(Relaxed));
1575    }
1576
1577    async fn stop_reason_at_eof(input: &'static str) -> Option<io::ErrorKind> {
1578        let reason = Rc::new(RefCell::new(None));
1579        let reason2 = reason.clone();
1580
1581        let (client, server) = IoTest::create();
1582        client.remote_buffer_cap(1024);
1583        client.write(input);
1584
1585        let (disp, _) = Dispatcher::debug(
1586            Io::from(server),
1587            BCodec(8),
1588            ntex_service::fn_service(move |msg: DispatchItem<BCodec>| {
1589                if let DispatchItem::Stop(Reason::Io(err)) = msg {
1590                    *reason2.borrow_mut() = Some(err.map(|e| e.kind()));
1591                }
1592                async move { Ok::<_, ()>(None) }
1593            }),
1594        );
1595        spawn(async move {
1596            let _ = disp.await;
1597        });
1598        sleep(Millis(25)).await;
1599        client.close().await;
1600        sleep(Millis(50)).await;
1601
1602        reason.borrow_mut().take().expect("dispatcher did not stop")
1603    }
1604
1605    /// Frames buffered while the service is not ready are dispatched even
1606    /// when the transport fails during the pause.
1607    #[ntex::test]
1608    async fn peer_gone_during_pause_dispatches_buffered_frames() {
1609        struct Srv(Condition, Rc<RefCell<Vec<String>>>);
1610
1611        impl Service<(), DispatchItem<BCodec>> for Srv {
1612            type Res = Option<Bytes>;
1613            type Error = ();
1614
1615            async fn ready(&self, _: Ctx<'_, Self>) -> Result<(), Self::Error> {
1616                if self.1.borrow().len() == 1 {
1617                    self.0.wait().await;
1618                }
1619                Ok(())
1620            }
1621
1622            async fn call(
1623                &self,
1624                msg: DispatchItem<BCodec>,
1625                _: Ctx<'_, Self>,
1626            ) -> Result<Option<Bytes>, Self::Error> {
1627                match msg {
1628                    DispatchItem::Item(msg) => {
1629                        self.1
1630                            .borrow_mut()
1631                            .push(String::from_utf8_lossy(&msg).into_owned());
1632                        return Ok(Some(msg));
1633                    }
1634                    DispatchItem::Stop(Reason::Io(err)) => self
1635                        .1
1636                        .borrow_mut()
1637                        .push(format!("stop: {:?}", err.map(|e| e.kind()))),
1638                    _ => (),
1639                }
1640                Ok(None)
1641            }
1642        }
1643
1644        let gate = Condition::new();
1645        let items = Rc::new(RefCell::new(Vec::new()));
1646        let (client, server) = IoTest::create();
1647        client.remote_buffer_cap(1024);
1648        // the response to the first frame fails while the service is paused
1649        client.write_error(io::Error::new(io::ErrorKind::ConnectionReset, "reset"));
1650        client.write("1234567887654321");
1651
1652        let (disp, _) = Dispatcher::debug(
1653            Io::from(server),
1654            BCodec(8),
1655            Srv(gate.clone(), items.clone()),
1656        );
1657        spawn(async move {
1658            let _ = disp.await;
1659        });
1660        sleep(Millis(25)).await;
1661
1662        gate.notify(());
1663        sleep(Millis(50)).await;
1664
1665        assert_eq!(
1666            *items.borrow(),
1667            ["12345678", "87654321", "stop: Some(ConnectionReset)"]
1668        );
1669    }
1670
1671    #[ntex::test]
1672    async fn stop_call_error_is_returned() {
1673        let (client, server) = IoTest::create();
1674        client.remote_buffer_cap(1024);
1675
1676        let (disp, _) = Dispatcher::debug(
1677            Io::from(server),
1678            BCodec(8),
1679            ntex_service::fn_service(async move |msg: DispatchItem<BCodec>| match msg {
1680                DispatchItem::Stop(_) => Err("stop"),
1681                _ => Ok(None),
1682            }),
1683        );
1684        client.close().await;
1685        assert_eq!(disp.await, Err("stop"));
1686    }
1687
1688    #[ntex::test]
1689    async fn earlier_service_error_is_kept() {
1690        let (client, server) = IoTest::create();
1691        client.remote_buffer_cap(1024);
1692        client.write("12345678");
1693
1694        let (disp, _) = Dispatcher::debug(
1695            Io::from(server),
1696            BCodec(8),
1697            ntex_service::fn_service(async move |msg: DispatchItem<BCodec>| match msg {
1698                DispatchItem::Item(_) => Err("item"),
1699                DispatchItem::Stop(_) => Err("stop"),
1700                DispatchItem::Control(_) => Ok(None),
1701            }),
1702        );
1703        assert_eq!(disp.await, Err("item"));
1704    }
1705
1706    #[ntex::test]
1707    async fn peer_eof_reports_truncated_frame() {
1708        assert_eq!(
1709            stop_reason_at_eof("123").await,
1710            Some(io::ErrorKind::UnexpectedEof)
1711        );
1712    }
1713
1714    #[ntex::test]
1715    async fn peer_eof_after_whole_frame_is_clean() {
1716        assert_eq!(stop_reason_at_eof("12345678").await, None);
1717    }
1718
1719    /// Service becomes not ready and write backpressure is enabled
1720    #[ntex::test]
1721    async fn service_is_not_ready_and_backpressure() {
1722        struct Srv(
1723            Cell<Option<oneshot::Receiver<()>>>,
1724            Rc<Cell<usize>>,
1725            Cell<bool>,
1726        );
1727
1728        impl Service<(), DispatchItem<BytesCodec>> for Srv {
1729            type Res = Option<Bytes>;
1730            type Error = ();
1731
1732            async fn ready(&self, _: Ctx<'_, Self>) -> Result<(), Self::Error> {
1733                if self.2.get()
1734                    && let Some(rx) = self.0.take()
1735                {
1736                    let _ = rx.await;
1737                }
1738                Ok(())
1739            }
1740
1741            async fn call(
1742                &self,
1743                msg: DispatchItem<BytesCodec>,
1744                _: Ctx<'_, Self>,
1745            ) -> Result<Option<Bytes>, Self::Error> {
1746                if let DispatchItem::Item(msg) = msg {
1747                    self.2.set(true);
1748                    return Ok::<_, ()>(Some(msg));
1749                } else if let DispatchItem::Control(Control::WBackPressureEnabled) = msg {
1750                    self.1.set(self.1.get() + 1);
1751                }
1752                Ok::<_, ()>(None)
1753            }
1754        }
1755
1756        let (ctx, rx) = oneshot::channel();
1757        let cnt = Rc::new(Cell::new(0));
1758        let (client, server) = IoTest::create();
1759        client.remote_buffer_cap(0);
1760
1761        let (disp, _) = Dispatcher::debug(
1762            Io::new(
1763                server,
1764                SharedCfg::new("DBG").add(IoConfig::new().set_write_backpressure(2)),
1765            ),
1766            BytesCodec,
1767            Srv(Cell::new(Some(rx)), cnt.clone(), Cell::new(false)),
1768        );
1769        let (tx, rx) = ntex::channel::oneshot::channel();
1770        ntex_util::spawn(async move {
1771            let _ = disp.await;
1772            let _ = tx.send(());
1773        });
1774
1775        client.write("123456789");
1776        client.remote_buffer_cap(9);
1777        sleep(Millis(125)).await;
1778        let _ = ctx.send(());
1779        client.write("123456789");
1780        sleep(Millis(125)).await;
1781        client.remote_buffer_cap(16);
1782        let res = client.read().await;
1783        assert_eq!(res.unwrap(), Bytes::from_static(b"123456789"));
1784        client.close().await;
1785        let _ = rx.await;
1786        assert_eq!(cnt.get(), 2);
1787    }
1788
1789    /// A completed frame stops the read timer, so it cannot close an idle
1790    /// connection when keep-alive is disabled.
1791    #[ntex::test]
1792    async fn read_timer_stopped_after_frame() {
1793        let (client, server) = IoTest::create();
1794        client.remote_buffer_cap(1024);
1795
1796        let data = Arc::new(Mutex::new(RefCell::new(Vec::new())));
1797        let data2 = data.clone();
1798
1799        let io = Io::new(
1800            server,
1801            SharedCfg::new("TEST").add(
1802                IoConfig::new()
1803                    .set_keepalive_timeout(Seconds::ZERO)
1804                    .set_frame_read_rate(Seconds(1), Seconds(5), 2),
1805            ),
1806        );
1807        let disp = Dispatcher::new(
1808            io,
1809            BCodec(8),
1810            Pipeline::new(
1811                (),
1812                ntex_service::fn_service(move |msg: DispatchItem<BCodec>| {
1813                    let data = data2.clone();
1814                    async move {
1815                        match msg {
1816                            DispatchItem::Item(bytes) => {
1817                                data.lock().unwrap().borrow_mut().push(0);
1818                                return Ok::<_, ()>(Some(bytes));
1819                            }
1820                            DispatchItem::Stop(_) => data.lock().unwrap().borrow_mut().push(1),
1821                            DispatchItem::Control(_) => (),
1822                        }
1823                        Ok(None)
1824                    }
1825                }),
1826            ),
1827        );
1828        spawn(async move {
1829            let _ = disp.await;
1830        });
1831
1832        client.write("1234");
1833        sleep(Millis(200)).await;
1834        client.write("5678");
1835        let buf = client.read().await.unwrap();
1836        assert_eq!(buf, Bytes::from_static(b"12345678"));
1837
1838        sleep(Millis(2500)).await;
1839        assert!(!client.is_closed());
1840        assert_eq!(&data.lock().unwrap().borrow()[..], &[0]);
1841    }
1842
1843    /// Drops buffered bytes up to and including `#`; frames are 8 bytes.
1844    struct DropCodec;
1845
1846    impl Encoder for DropCodec {
1847        type Item = Bytes;
1848        type Error = io::Error;
1849
1850        fn encode(&self, item: Bytes, dst: &mut BytePages) -> Result<(), Self::Error> {
1851            dst.append(item);
1852            Ok(())
1853        }
1854    }
1855
1856    impl Decoder for DropCodec {
1857        type Item = Bytes;
1858        type Error = io::Error;
1859
1860        fn decode(&self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
1861            if let Some(n) = src.iter().position(|b| *b == b'#') {
1862                let _ = src.split_to(n + 1);
1863            }
1864            if src.len() < 8 {
1865                Ok(None)
1866            } else {
1867                Ok(Some(src.split_to(8)))
1868            }
1869        }
1870    }
1871
1872    /// Bytes consumed by the codec without producing a frame count as read
1873    /// progress.
1874    #[ntex::test]
1875    async fn read_rate_counts_consumed_bytes() {
1876        let (client, server) = IoTest::create();
1877        client.remote_buffer_cap(1024);
1878
1879        let data = Arc::new(Mutex::new(RefCell::new(Vec::new())));
1880        let data2 = data.clone();
1881
1882        let io = Io::new(
1883            server,
1884            SharedCfg::new("TEST").add(
1885                IoConfig::new()
1886                    .set_keepalive_timeout(Seconds::ZERO)
1887                    .set_frame_read_rate(Seconds(1), Seconds(10), 2),
1888            ),
1889        );
1890        let disp = Dispatcher::new(
1891            io,
1892            DropCodec,
1893            Pipeline::new(
1894                (),
1895                ntex_service::fn_service(move |msg: DispatchItem<DropCodec>| {
1896                    let data = data2.clone();
1897                    async move {
1898                        if let DispatchItem::Stop(Reason::ReadTimeout) = msg {
1899                            data.lock().unwrap().borrow_mut().push(1);
1900                        }
1901                        Ok::<_, ()>(None)
1902                    }
1903                }),
1904            ),
1905        );
1906        spawn(async move {
1907            let _ = disp.await;
1908        });
1909
1910        client.write("123");
1911        // several periods are extended by consumed bytes only
1912        for _ in 0..4 {
1913            sleep(Millis(350)).await;
1914            client.write("abc#");
1915        }
1916        // a period without consumed bytes must still be running, without the
1917        // consumed bytes the timer would have expired at 2s
1918        sleep(Millis(1000)).await;
1919        assert!(!client.is_closed());
1920        assert!(data.lock().unwrap().borrow().is_empty());
1921
1922        // no progress, the frame read timer expires
1923        wait_closed(&client, Millis(4500)).await;
1924        assert!(client.is_closed());
1925        assert_eq!(&data.lock().unwrap().borrow()[..], &[1]);
1926    }
1927
1928    fn first_frame_dispatcher(
1929        server: IoTest,
1930        data: Arc<Mutex<RefCell<Vec<usize>>>>,
1931    ) -> Dispatcher<BCodec, ()> {
1932        let io = Io::new(
1933            server,
1934            SharedCfg::new("TEST").add(
1935                IoConfig::new()
1936                    .set_keepalive_timeout(Seconds(30))
1937                    .set_frame_read_rate(Seconds(1), Seconds(2), 2),
1938            ),
1939        );
1940        Dispatcher::new(
1941            io,
1942            BCodec(8),
1943            Pipeline::new(
1944                (),
1945                ntex_service::fn_service(move |msg: DispatchItem<BCodec>| {
1946                    let data = data.clone();
1947                    async move {
1948                        match msg {
1949                            DispatchItem::Item(bytes) => {
1950                                data.lock().unwrap().borrow_mut().push(0);
1951                                return Ok::<_, ()>(Some(bytes));
1952                            }
1953                            DispatchItem::Stop(Reason::ReadTimeout) => {
1954                                data.lock().unwrap().borrow_mut().push(1);
1955                            }
1956                            DispatchItem::Stop(Reason::KeepAlive) => {
1957                                data.lock().unwrap().borrow_mut().push(2);
1958                            }
1959                            _ => (),
1960                        }
1961                        Ok(None)
1962                    }
1963                }),
1964            ),
1965        )
1966    }
1967
1968    /// Frame read-rate tracking starts when the connection arrives, so a
1969    /// silent peer is closed before the keep-alive timeout.
1970    #[ntex::test]
1971    async fn first_frame_read_rate_silent_peer() {
1972        let (client, server) = IoTest::create();
1973        client.remote_buffer_cap(1024);
1974
1975        let data = Arc::new(Mutex::new(RefCell::new(Vec::new())));
1976        let disp = first_frame_dispatcher(server, data.clone());
1977        spawn(async move {
1978            let _ = disp.await;
1979        });
1980
1981        wait_until(Millis(5000), || {
1982            client.is_closed() && data.lock().unwrap().borrow().len() == 1
1983        })
1984        .await;
1985        assert!(client.is_closed());
1986        assert_eq!(&data.lock().unwrap().borrow()[..], &[1]);
1987    }
1988
1989    /// Completing the first frame stops frame read-rate tracking, and the
1990    /// keep-alive timer governs the idle connection.
1991    #[ntex::test]
1992    async fn first_frame_read_rate_then_keepalive() {
1993        let (client, server) = IoTest::create();
1994        client.remote_buffer_cap(1024);
1995
1996        let data = Arc::new(Mutex::new(RefCell::new(Vec::new())));
1997        let disp = first_frame_dispatcher(server, data.clone());
1998        spawn(async move {
1999            let _ = disp.await;
2000        });
2001
2002        sleep(Millis(200)).await;
2003        client.write("12345678");
2004        let buf = client.read().await.unwrap();
2005        assert_eq!(buf, Bytes::from_static(b"12345678"));
2006
2007        // without stopping the frame read timer the connection would be
2008        // closed after the 1s period and its 1s extension
2009        sleep(Millis(2500)).await;
2010        assert!(!client.is_closed());
2011        assert_eq!(&data.lock().unwrap().borrow()[..], &[0]);
2012    }
2013
2014    /// Service whose readiness waits on a gate installed by the test.
2015    struct GateSrv(Rc<RefCell<Option<oneshot::Receiver<()>>>>, Rc<Cell<bool>>);
2016
2017    impl Service<(), DispatchItem<BCodec>> for GateSrv {
2018        type Res = Option<Bytes>;
2019        type Error = ();
2020
2021        async fn ready(&self, _: Ctx<'_, Self>) -> Result<(), Self::Error> {
2022            let rx = self.0.borrow_mut().take();
2023            if let Some(rx) = rx {
2024                let _ = rx.await;
2025            }
2026            Ok(())
2027        }
2028
2029        async fn call(
2030            &self,
2031            msg: DispatchItem<BCodec>,
2032            _: Ctx<'_, Self>,
2033        ) -> Result<Option<Bytes>, Self::Error> {
2034            if let DispatchItem::Stop(Reason::ReadTimeout) = msg {
2035                self.1.set(true);
2036            }
2037            Ok(None)
2038        }
2039    }
2040
2041    /// A service pause restarts frame read-rate tracking with a fresh budget,
2042    /// repeated pauses do not exhaust it.
2043    #[ntex::test]
2044    async fn read_rate_budget_reset_on_service_pause() {
2045        let (client, server) = IoTest::create();
2046        client.remote_buffer_cap(1024);
2047
2048        let gate = Rc::new(RefCell::new(None));
2049        let timed_out = Rc::new(Cell::new(false));
2050        let io = Io::new(
2051            server,
2052            SharedCfg::new("TEST").add(
2053                IoConfig::new()
2054                    .set_keepalive_timeout(Seconds::ZERO)
2055                    .set_frame_read_rate(Seconds(1), Seconds(2), 2),
2056            ),
2057        );
2058        let disp = Dispatcher::new(
2059            io,
2060            BCodec(1024),
2061            Pipeline::new((), GateSrv(gate.clone(), timed_out.clone())),
2062        );
2063        spawn(async move {
2064            let _ = disp.await;
2065        });
2066
2067        // the budget allows one extension, at least two 1s periods, which
2068        // covers a 1.5s cycle. Without a restore by the pause it lasts less
2069        // than two 2s periods, two cycles take longer.
2070        client.write("abc");
2071        for _ in 0..2 {
2072            for _ in 0..2 {
2073                sleep(Millis(700)).await;
2074                client.write("abc");
2075            }
2076            let (tx, rx) = oneshot::channel();
2077            *gate.borrow_mut() = Some(rx);
2078            client.write("abc");
2079            sleep(Millis(100)).await;
2080            let _ = tx.send(());
2081        }
2082        assert!(!client.is_closed());
2083        assert!(!timed_out.get());
2084    }
2085
2086    /// Frame read-rate tracking restarts after a service pause, a peer that
2087    /// stops sending is still closed.
2088    #[ntex::test]
2089    async fn read_rate_restarts_after_service_pause() {
2090        let (client, server) = IoTest::create();
2091        client.remote_buffer_cap(1024);
2092
2093        let gate = Rc::new(RefCell::new(None));
2094        let timed_out = Rc::new(Cell::new(false));
2095        let io = Io::new(
2096            server,
2097            SharedCfg::new("TEST").add(
2098                IoConfig::new()
2099                    .set_keepalive_timeout(Seconds::ZERO)
2100                    .set_frame_read_rate(Seconds(1), Seconds::ZERO, 2),
2101            ),
2102        );
2103        let disp = Dispatcher::new(
2104            io,
2105            BCodec(1024),
2106            Pipeline::new((), GateSrv(gate.clone(), timed_out.clone())),
2107        );
2108        spawn(async move {
2109            let _ = disp.await;
2110        });
2111
2112        client.write("abc");
2113        sleep(Millis(100)).await;
2114        let (tx, rx) = oneshot::channel();
2115        *gate.borrow_mut() = Some(rx);
2116        client.write("abc");
2117        sleep(Millis(1500)).await;
2118        let _ = tx.send(());
2119
2120        // no more data after the pause, the data sent during the pause
2121        // extends the first period
2122        sleep(Millis(1500)).await;
2123        assert!(!client.is_closed());
2124        wait_closed(&client, Millis(3500)).await;
2125        assert!(client.is_closed());
2126        assert!(timed_out.get());
2127    }
2128
2129    /// Frame read-rate tracking starts when the codec consumes partial frame
2130    /// data into its own state and leaves nothing buffered.
2131    #[ntex::test]
2132    async fn read_rate_starts_for_consumed_partial_frame() {
2133        let (client, server) = IoTest::create();
2134        client.remote_buffer_cap(1024);
2135
2136        let data = Arc::new(Mutex::new(RefCell::new(Vec::new())));
2137        let data2 = data.clone();
2138
2139        let io = Io::new(
2140            server,
2141            SharedCfg::new("TEST").add(
2142                IoConfig::new()
2143                    .set_keepalive_timeout(Seconds(30))
2144                    .set_frame_read_rate(Seconds(1), Seconds(2), 2),
2145            ),
2146        );
2147        let disp = Dispatcher::new(
2148            io,
2149            DropCodec,
2150            Pipeline::new(
2151                (),
2152                ntex_service::fn_service(move |msg: DispatchItem<DropCodec>| {
2153                    let data = data2.clone();
2154                    async move {
2155                        match msg {
2156                            DispatchItem::Item(_) => data.lock().unwrap().borrow_mut().push(0),
2157                            DispatchItem::Stop(Reason::ReadTimeout) => {
2158                                data.lock().unwrap().borrow_mut().push(1);
2159                            }
2160                            _ => (),
2161                        }
2162                        Ok::<_, ()>(None)
2163                    }
2164                }),
2165            ),
2166        );
2167        spawn(async move {
2168            let _ = disp.await;
2169        });
2170
2171        client.write("12345678");
2172        sleep(Millis(100)).await;
2173        // the codec consumes the whole input without producing a frame
2174        client.write("1#");
2175        wait_until(Millis(5000), || {
2176            client.is_closed() && data.lock().unwrap().borrow().len() == 2
2177        })
2178        .await;
2179        assert!(client.is_closed());
2180        assert_eq!(&data.lock().unwrap().borrow()[..], &[0, 1]);
2181    }
2182
2183    /// Service that is not ready while a frame is handled.
2184    struct BusySrv(Rc<Cell<bool>>, IoRef);
2185
2186    impl Service<(), DispatchItem<BCodec>> for BusySrv {
2187        type Res = Option<Bytes>;
2188        type Error = ();
2189
2190        async fn ready(&self, _: Ctx<'_, Self>) -> Result<(), Self::Error> {
2191            while self.0.get() {
2192                sleep(Millis(50)).await;
2193            }
2194            Ok(())
2195        }
2196
2197        async fn call(
2198            &self,
2199            msg: DispatchItem<BCodec>,
2200            _: Ctx<'_, Self>,
2201        ) -> Result<Option<Bytes>, Self::Error> {
2202            if let DispatchItem::Item(bytes) = msg {
2203                self.0.set(true);
2204                let ioref = self.1.clone();
2205                spawn(async move {
2206                    sleep(Millis(300)).await;
2207                    ioref.notify_timeout();
2208                });
2209                sleep(Millis(800)).await;
2210                self.0.set(false);
2211                return Ok(Some(bytes));
2212            }
2213            Ok(None)
2214        }
2215    }
2216
2217    /// An external timeout does not stop the dispatcher while the service is
2218    /// paused and a frame is handled.
2219    #[ntex::test]
2220    async fn notify_timeout_ignored_during_pause_while_handling() {
2221        let (client, server) = IoTest::create();
2222        client.remote_buffer_cap(1024);
2223
2224        let io = Io::new(
2225            server,
2226            SharedCfg::new("TEST").add(IoConfig::new().set_keepalive_timeout(Seconds(5))),
2227        );
2228        let srv = BusySrv(Rc::new(Cell::new(false)), io.get_ref());
2229        let disp = Dispatcher::new(io, BCodec(1), Pipeline::new((), srv));
2230        spawn(async move {
2231            let _ = disp.await;
2232        });
2233
2234        client.write("1");
2235        // the timeout is delivered at 300ms, the frame is handled for 800ms
2236        sleep(Millis(600)).await;
2237        assert!(!client.is_closed());
2238        let buf = client.read().await.unwrap();
2239        assert_eq!(buf, Bytes::from_static(b"1"));
2240        // the timeout is not delivered after the service is ready again
2241        sleep(Millis(300)).await;
2242        assert!(!client.is_closed());
2243    }
2244
2245    /// An external timeout stops an idle dispatcher while the service is
2246    /// paused, the same as when it is ready.
2247    #[ntex::test]
2248    async fn notify_timeout_during_idle_pause() {
2249        let (client, server) = IoTest::create();
2250        client.remote_buffer_cap(1024);
2251
2252        let gate = Rc::new(RefCell::new(None));
2253        let io = Io::new(
2254            server,
2255            SharedCfg::new("TEST").add(IoConfig::new().set_keepalive_timeout(Seconds::ZERO)),
2256        );
2257        let ioref = io.get_ref();
2258        let (tx, rx) = oneshot::channel();
2259        *gate.borrow_mut() = Some(rx);
2260        let disp = Dispatcher::new(
2261            io,
2262            BCodec(1),
2263            Pipeline::new((), GateSrv(gate, Rc::new(Cell::new(false)))),
2264        );
2265        spawn(async move {
2266            let _ = disp.await;
2267        });
2268
2269        sleep(Millis(300)).await;
2270        ioref.notify_timeout();
2271        sleep(Millis(300)).await;
2272        // the stop item is delivered once the service is ready
2273        let _ = tx.send(());
2274        wait_closed(&client, Millis(1000)).await;
2275        assert!(client.is_closed());
2276    }
2277
2278    fn keepalive_dispatcher(
2279        server: IoTest,
2280        delay: Millis,
2281        data: Arc<Mutex<RefCell<Vec<usize>>>>,
2282    ) -> Dispatcher<BCodec, ()> {
2283        keepalive_io_dispatcher(keepalive_io(server), delay, data)
2284    }
2285
2286    fn keepalive_io(server: IoTest) -> Io {
2287        Io::new(
2288            server,
2289            SharedCfg::new("TEST").add(IoConfig::new().set_keepalive_timeout(Seconds(1))),
2290        )
2291    }
2292
2293    fn keepalive_io_dispatcher(
2294        io: Io,
2295        delay: Millis,
2296        data: Arc<Mutex<RefCell<Vec<usize>>>>,
2297    ) -> Dispatcher<BCodec, ()> {
2298        Dispatcher::new(
2299            io,
2300            BCodec(8),
2301            Pipeline::new(
2302                (),
2303                ntex_service::fn_service(move |msg: DispatchItem<BCodec>| {
2304                    let data = data.clone();
2305                    async move {
2306                        match msg {
2307                            DispatchItem::Item(bytes) => {
2308                                sleep(delay).await;
2309                                data.lock().unwrap().borrow_mut().push(0);
2310                                return Ok::<_, ()>(Some(bytes));
2311                            }
2312                            DispatchItem::Stop(Reason::KeepAlive) => {
2313                                data.lock().unwrap().borrow_mut().push(1);
2314                            }
2315                            _ => (),
2316                        }
2317                        Ok(None)
2318                    }
2319                }),
2320            ),
2321        )
2322    }
2323
2324    /// The keep-alive timer is not active while a frame is handled, it starts
2325    /// once the response is done.
2326    #[ntex::test]
2327    async fn keepalive_inactive_during_frame_handling() {
2328        let (client, server) = IoTest::create();
2329        client.remote_buffer_cap(1024);
2330
2331        let data = Arc::new(Mutex::new(RefCell::new(Vec::new())));
2332        // an active 1s keep-alive would expire in less than 2s
2333        let disp = keepalive_dispatcher(server, Millis(2200), data.clone());
2334        spawn(async move {
2335            let _ = disp.await;
2336        });
2337
2338        client.write("12345678");
2339        sleep(Millis(2400)).await;
2340        assert!(!client.is_closed());
2341        assert_eq!(&data.lock().unwrap().borrow()[..], &[0]);
2342
2343        wait_closed(&client, Millis(3000)).await;
2344        assert!(client.is_closed());
2345        assert_eq!(&data.lock().unwrap().borrow()[..], &[0, 1]);
2346    }
2347
2348    /// The keep-alive timer is not active while a partial frame is read.
2349    #[ntex::test]
2350    async fn keepalive_inactive_during_frame_read() {
2351        let (client, server) = IoTest::create();
2352        client.remote_buffer_cap(1024);
2353
2354        let data = Arc::new(Mutex::new(RefCell::new(Vec::new())));
2355        let disp = keepalive_dispatcher(server, Millis(0), data.clone());
2356        spawn(async move {
2357            let _ = disp.await;
2358        });
2359
2360        // keep-alive is armed for the idle connection, then a frame starts,
2361        // the 1s keep-alive would expire in less than 2s
2362        sleep(Millis(200)).await;
2363        client.write("1234");
2364        sleep(Millis(2200)).await;
2365        assert!(!client.is_closed());
2366
2367        client.write("5678");
2368        let buf = client.read().await.unwrap();
2369        assert_eq!(buf, Bytes::from_static(b"12345678"));
2370
2371        wait_closed(&client, Millis(3000)).await;
2372        assert!(client.is_closed());
2373        assert_eq!(&data.lock().unwrap().borrow()[..], &[0, 1]);
2374    }
2375
2376    /// The keep-alive timer stays armed while a frame is handled, it is not
2377    /// registered again for every frame.
2378    #[ntex::test]
2379    async fn keepalive_timer_kept_during_frame_handling() {
2380        let (client, server) = IoTest::create();
2381        client.remote_buffer_cap(1024);
2382
2383        let io = Io::new(
2384            server,
2385            SharedCfg::new("TEST").add(IoConfig::new().set_keepalive_timeout(Seconds(5))),
2386        );
2387        let ioref = io.get_ref();
2388        let handles = Rc::new(RefCell::new(Vec::new()));
2389        let handles2 = handles.clone();
2390        let disp = Dispatcher::new(
2391            io,
2392            BCodec(1),
2393            Pipeline::new(
2394                (),
2395                ntex_service::fn_service(move |msg: DispatchItem<BCodec>| {
2396                    let handles = handles2.clone();
2397                    let ioref = ioref.clone();
2398                    async move {
2399                        if let DispatchItem::Item(bytes) = msg {
2400                            handles.borrow_mut().push(ioref.timer_handle());
2401                            return Ok::<_, ()>(Some(bytes));
2402                        }
2403                        Ok(None)
2404                    }
2405                }),
2406            ),
2407        );
2408        spawn(async move {
2409            let _ = disp.await;
2410        });
2411
2412        for frame in ["1", "2"] {
2413            sleep(Millis(50)).await;
2414            client.write(frame);
2415            let buf = client.read().await.unwrap();
2416            assert_eq!(buf, Bytes::from(frame));
2417        }
2418        assert_eq!(handles.borrow().len(), 2);
2419        assert!(handles.borrow().iter().all(ntex_io::TimerHandle::is_set));
2420        assert!(!client.is_closed());
2421    }
2422
2423    /// An external timeout stops the dispatcher while a frame is read and
2424    /// the keep-alive timer is suspended, the same as when it is stopped.
2425    #[ntex::test]
2426    async fn notify_timeout_during_frame_read() {
2427        let (client, server) = IoTest::create();
2428        client.remote_buffer_cap(1024);
2429
2430        let io = Io::new(
2431            server,
2432            SharedCfg::new("TEST").add(IoConfig::new().set_keepalive_timeout(Seconds(5))),
2433        );
2434        let ioref = io.get_ref();
2435        let data = Rc::new(RefCell::new(Vec::new()));
2436        let data2 = data.clone();
2437        let disp = Dispatcher::new(
2438            io,
2439            BCodec(8),
2440            Pipeline::new(
2441                (),
2442                ntex_service::fn_service(move |msg: DispatchItem<BCodec>| {
2443                    if let DispatchItem::Stop(Reason::KeepAlive) = msg {
2444                        data2.borrow_mut().push(1);
2445                    }
2446                    async move { Ok::<_, ()>(None) }
2447                }),
2448            ),
2449        );
2450        spawn(async move {
2451            let _ = disp.await;
2452        });
2453
2454        sleep(Millis(100)).await;
2455        client.write("1234");
2456        sleep(Millis(100)).await;
2457        assert!(!client.is_closed());
2458
2459        ioref.notify_timeout();
2460        wait_closed(&client, Millis(1000)).await;
2461        assert!(client.is_closed());
2462        assert_eq!(&data.borrow()[..], &[1]);
2463    }
2464
2465    /// Service that answers every frame with `size` bytes and records events.
2466    struct WriteSrv {
2467        size: usize,
2468        gate: Gate,
2469        events: Events,
2470    }
2471
2472    impl Service<(), DispatchItem<BCodec>> for WriteSrv {
2473        type Res = Option<Bytes>;
2474        type Error = ();
2475
2476        async fn ready(&self, _: Ctx<'_, Self>) -> Result<(), Self::Error> {
2477            let rx = self.gate.borrow_mut().take();
2478            if let Some(rx) = rx {
2479                let _ = rx.await;
2480            }
2481            Ok(())
2482        }
2483
2484        async fn call(
2485            &self,
2486            msg: DispatchItem<BCodec>,
2487            _: Ctx<'_, Self>,
2488        ) -> Result<Option<Bytes>, Self::Error> {
2489            let ev = match msg {
2490                DispatchItem::Item(_) => {
2491                    self.events.borrow_mut().push("item");
2492                    return Ok(Some(Bytes::from(vec![b'x'; self.size])));
2493                }
2494                DispatchItem::Control(Control::WBackPressureEnabled) => "bp-on",
2495                DispatchItem::Control(Control::WBackPressureDisabled) => "bp-off",
2496                DispatchItem::Stop(Reason::WriteTimeout) => "write-timeout",
2497                DispatchItem::Stop(Reason::KeepAlive) => "keepalive",
2498                DispatchItem::Stop(Reason::ReadTimeout) => "read-timeout",
2499                DispatchItem::Stop(_) => "stop",
2500            };
2501            self.events.borrow_mut().push(ev);
2502            Ok(None)
2503        }
2504    }
2505
2506    type Gate = Rc<RefCell<Option<oneshot::Receiver<()>>>>;
2507    type Events = Rc<RefCell<Vec<&'static str>>>;
2508
2509    fn write_dispatcher(
2510        server: IoTest,
2511        cfg: IoConfig,
2512        size: usize,
2513    ) -> (Dispatcher<BCodec, ()>, Gate, Events) {
2514        let gate = Rc::new(RefCell::new(None));
2515        let events = Rc::new(RefCell::new(Vec::new()));
2516        let io = Io::new(
2517            server,
2518            SharedCfg::new("TEST").add(cfg.set_write_backpressure(1024)),
2519        );
2520        let disp = Dispatcher::new(
2521            io,
2522            BCodec(8),
2523            Pipeline::new(
2524                (),
2525                WriteSrv {
2526                    size,
2527                    gate: gate.clone(),
2528                    events: events.clone(),
2529                },
2530            ),
2531        );
2532        (disp, gate, events)
2533    }
2534
2535    /// A peer that stops reading during write backpressure is closed with a
2536    /// write timeout.
2537    #[ntex::test]
2538    async fn write_timeout_peer_not_reading() {
2539        let (client, server) = IoTest::create();
2540        client.remote_buffer_cap(0);
2541
2542        let (disp, _, events) = write_dispatcher(
2543            server,
2544            IoConfig::new()
2545                .set_keepalive_timeout(Seconds(1))
2546                .set_write_timeout(Seconds(2)),
2547            8192,
2548        );
2549        spawn(async move {
2550            let _ = disp.await;
2551        });
2552
2553        client.write("12345678");
2554        sleep(Millis(1500)).await;
2555        assert!(!client.is_closed());
2556        wait_closed(&client, Millis(4000)).await;
2557        assert!(client.is_closed());
2558        assert_eq!(&events.borrow()[..], &["item", "bp-on", "write-timeout"]);
2559    }
2560
2561    /// The write timeout covers the whole backpressure period, a peer that
2562    /// keeps reading too slowly to release backpressure is closed.
2563    #[ntex::test]
2564    async fn write_timeout_slow_reader() {
2565        let (client, server) = IoTest::create();
2566        client.remote_buffer_cap(0);
2567
2568        let (disp, _, events) =
2569            write_dispatcher(server, IoConfig::new().set_write_timeout(Seconds(2)), 65536);
2570        spawn(async move {
2571            let _ = disp.await;
2572        });
2573
2574        client.write("12345678");
2575        for _ in 0..24 {
2576            sleep(Millis(250)).await;
2577            client.remote_buffer_cap(64);
2578            let _ = client.read_any();
2579            if client.is_closed() {
2580                break;
2581            }
2582        }
2583        assert!(client.is_closed());
2584        wait_until(Millis(1000), || events.borrow().len() == 3).await;
2585        assert_eq!(&events.borrow()[..], &["item", "bp-on", "write-timeout"]);
2586    }
2587
2588    /// The write timeout keeps running while the service is not ready during
2589    /// write backpressure. The stop item waits for readiness, after the
2590    /// shutdown timeout it is delivered in the background and io is closed.
2591    #[ntex::test]
2592    async fn write_timeout_during_service_pause() {
2593        let (client, server) = IoTest::create();
2594        client.remote_buffer_cap(0);
2595
2596        let (disp, gate, events) =
2597            write_dispatcher(server, IoConfig::new().set_write_timeout(Seconds(1)), 8192);
2598        spawn(async move {
2599            let _ = disp.await;
2600        });
2601
2602        client.write("12345678");
2603        sleep(Millis(100)).await;
2604        assert_eq!(&events.borrow()[..], &["item", "bp-on"]);
2605
2606        // the service is not ready for a while
2607        let (tx, rx) = oneshot::channel::<()>();
2608        *gate.borrow_mut() = Some(rx);
2609
2610        // io is closed without waiting for service readiness
2611        wait_closed(&client, Millis(5000)).await;
2612        assert!(client.is_closed());
2613        assert_eq!(&events.borrow()[..], &["item", "bp-on"]);
2614
2615        // the stop item is delivered once the service is ready
2616        drop(tx);
2617        wait_until(Millis(1000), || events.borrow().len() == 3).await;
2618        assert_eq!(&events.borrow()[..], &["item", "bp-on", "write-timeout"]);
2619    }
2620
2621    /// Backpressure released while the service is not ready ends the write
2622    /// timeout, even though the dispatcher observes the release later.
2623    #[ntex::test]
2624    async fn write_timeout_released_during_service_pause() {
2625        let (client, server) = IoTest::create();
2626        client.remote_buffer_cap(0);
2627
2628        let (disp, gate, events) =
2629            write_dispatcher(server, IoConfig::new().set_write_timeout(Seconds(1)), 8192);
2630        spawn(async move {
2631            let _ = disp.await;
2632        });
2633
2634        client.write("12345678");
2635        sleep(Millis(100)).await;
2636        assert_eq!(&events.borrow()[..], &["item", "bp-on"]);
2637
2638        // the service is not ready while the peer drains the output
2639        let (tx, rx) = oneshot::channel::<()>();
2640        *gate.borrow_mut() = Some(rx);
2641        client.remote_buffer_cap(65536);
2642        sleep(Millis(100)).await;
2643        assert_eq!(client.read_any().len(), 8192);
2644        // a write timeout that was not stopped would fire within 2s
2645        sleep(Millis(2300)).await;
2646
2647        drop(tx);
2648        sleep(Millis(100)).await;
2649        assert!(!client.is_closed());
2650        assert_eq!(&events.borrow()[..], &["item", "bp-on", "bp-off"]);
2651    }
2652
2653    /// Keep-alive starts again once write backpressure is released.
2654    #[ntex::test]
2655    async fn keepalive_after_write_backpressure() {
2656        let (client, server) = IoTest::create();
2657        client.remote_buffer_cap(0);
2658
2659        let (disp, _, events) = write_dispatcher(
2660            server,
2661            IoConfig::new()
2662                .set_keepalive_timeout(Seconds(1))
2663                .set_write_timeout(Seconds(1)),
2664            8192,
2665        );
2666        spawn(async move {
2667            let _ = disp.await;
2668        });
2669
2670        client.write("12345678");
2671        wait_until(Millis(2000), || events.borrow().len() == 2).await;
2672        client.remote_buffer_cap(65536);
2673        sleep(Millis(100)).await;
2674        assert_eq!(client.read_any().len(), 8192);
2675        assert_eq!(&events.borrow()[..], &["item", "bp-on", "bp-off"]);
2676
2677        wait_closed(&client, Millis(4000)).await;
2678        assert!(client.is_closed());
2679        assert_eq!(
2680            &events.borrow()[..],
2681            &["item", "bp-on", "bp-off", "keepalive"]
2682        );
2683    }
2684
2685    /// The write timeout is stopped once write backpressure is released, even
2686    /// though output is still outstanding.
2687    #[ntex::test]
2688    async fn write_timeout_ends_at_backpressure_release() {
2689        let (client, server) = IoTest::create();
2690        client.remote_buffer_cap(0);
2691
2692        let (disp, _, events) =
2693            write_dispatcher(server, IoConfig::new().set_write_timeout(Seconds(1)), 8192);
2694        spawn(async move {
2695            let _ = disp.await;
2696        });
2697
2698        client.write("12345678");
2699        sleep(Millis(250)).await;
2700        // drain below the release threshold, then stop reading
2701        client.remote_buffer_cap(7900);
2702        sleep(Millis(100)).await;
2703        assert_eq!(client.read_any().len(), 7900);
2704        assert_eq!(&events.borrow()[..], &["item", "bp-on", "bp-off"]);
2705
2706        // a write timeout that was not stopped would fire within 2s
2707        sleep(Millis(2500)).await;
2708        assert!(!client.is_closed());
2709        assert_eq!(&events.borrow()[..], &["item", "bp-on", "bp-off"]);
2710    }
2711
2712    /// Each backpressure period starts a fresh write timeout.
2713    #[ntex::test]
2714    async fn write_timeout_restarts_per_backpressure_period() {
2715        let (client, server) = IoTest::create();
2716        client.remote_buffer_cap(0);
2717
2718        let (disp, _, events) =
2719            write_dispatcher(server, IoConfig::new().set_write_timeout(Seconds(2)), 8192);
2720        spawn(async move {
2721            let _ = disp.await;
2722        });
2723
2724        client.write("12345678");
2725        for _ in 0..2 {
2726            // release backpressure within the timeout
2727            sleep(Millis(1200)).await;
2728            client.remote_buffer_cap(65536);
2729            sleep(Millis(100)).await;
2730            assert_eq!(client.read_any().len(), 8192);
2731            client.remote_buffer_cap(0);
2732            client.write("12345678");
2733        }
2734        sleep(Millis(100)).await;
2735        assert!(!client.is_closed());
2736        assert_eq!(
2737            &events.borrow()[..],
2738            &[
2739                "item", "bp-on", "bp-off", "item", "bp-on", "bp-off", "item", "bp-on"
2740            ]
2741        );
2742    }
2743
2744    /// Service with a slow item handler and a large response.
2745    struct SlowWriteSrv(Events);
2746
2747    impl Service<(), DispatchItem<BCodec>> for SlowWriteSrv {
2748        type Res = Option<Bytes>;
2749        type Error = ();
2750
2751        async fn call(
2752            &self,
2753            msg: DispatchItem<BCodec>,
2754            _: Ctx<'_, Self>,
2755        ) -> Result<Option<Bytes>, Self::Error> {
2756            let ev = match msg {
2757                DispatchItem::Item(_) => {
2758                    sleep(Millis(300)).await;
2759                    self.0.borrow_mut().push("item");
2760                    return Ok(Some(Bytes::from(vec![b'x'; 8192])));
2761                }
2762                DispatchItem::Control(Control::WBackPressureEnabled) => "bp-on",
2763                DispatchItem::Control(Control::WBackPressureDisabled) => "bp-off",
2764                DispatchItem::Stop(Reason::ReadTimeout) => "read-timeout",
2765                DispatchItem::Stop(_) => "stop",
2766            };
2767            self.0.borrow_mut().push(ev);
2768            Ok(None)
2769        }
2770    }
2771
2772    /// Without a write timeout, the frame read timer does not run during
2773    /// write backpressure, while no frames are decoded.
2774    #[ntex::test]
2775    async fn read_rate_stopped_during_write_backpressure() {
2776        let (client, server) = IoTest::create();
2777        client.remote_buffer_cap(0);
2778
2779        let events: Events = Rc::new(RefCell::new(Vec::new()));
2780        let io = Io::new(
2781            server,
2782            SharedCfg::new("TEST").add(
2783                IoConfig::new()
2784                    .set_write_backpressure(1024)
2785                    .set_frame_read_rate(Seconds(1), Seconds::ZERO, 0),
2786            ),
2787        );
2788        let disp = Dispatcher::new(
2789            io,
2790            BCodec(8),
2791            Pipeline::new((), SlowWriteSrv(events.clone())),
2792        );
2793        spawn(async move {
2794            let _ = disp.await;
2795        });
2796
2797        // a partial frame arrives while the first frame is handled, then
2798        // the response enables write backpressure
2799        client.write("12345678");
2800        sleep(Millis(100)).await;
2801        client.write("1");
2802        sleep(Millis(400)).await;
2803        assert_eq!(&events.borrow()[..], &["item", "bp-on"]);
2804
2805        // the peer keeps sending partial frame bytes, a running 1s timer
2806        // would expire in less than 2s
2807        for _ in 0..5 {
2808            sleep(Millis(500)).await;
2809            client.write("1");
2810        }
2811        assert_eq!(&events.borrow()[..], &["item", "bp-on"]);
2812    }
2813
2814    /// Codec that fails to encode, and to decode input containing `!`.
2815    struct ErrCodec;
2816
2817    impl Encoder for ErrCodec {
2818        type Item = Bytes;
2819        type Error = io::Error;
2820
2821        fn encode(&self, _: Bytes, _: &mut BytePages) -> Result<(), Self::Error> {
2822            Err(io::Error::other("encode"))
2823        }
2824    }
2825
2826    impl Decoder for ErrCodec {
2827        type Item = Bytes;
2828        type Error = io::Error;
2829
2830        fn decode(&self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
2831            if src.contains(&b'!') {
2832                Err(io::Error::other("decode"))
2833            } else if src.is_empty() {
2834                Ok(None)
2835            } else {
2836                Ok(Some(src.split_to(src.len())))
2837            }
2838        }
2839    }
2840
2841    /// Runs an `ErrCodec` dispatcher that echoes items, returns the stop
2842    /// reason delivered to the service.
2843    async fn err_codec_stop(input: &'static str) -> String {
2844        let (client, server) = IoTest::create();
2845        client.remote_buffer_cap(1024);
2846        client.write(input);
2847
2848        let reason = Rc::new(RefCell::new(String::new()));
2849        let reason2 = reason.clone();
2850        let (disp, _) = Dispatcher::debug(
2851            Io::from(server),
2852            ErrCodec,
2853            ntex_service::fn_service(move |msg: DispatchItem<ErrCodec>| {
2854                let reason = reason2.clone();
2855                async move {
2856                    match msg {
2857                        DispatchItem::Item(item) => return Ok::<_, ()>(Some(item)),
2858                        DispatchItem::Stop(r) => *reason.borrow_mut() = format!("{r:?}"),
2859                        DispatchItem::Control(_) => (),
2860                    }
2861                    Ok(None)
2862                }
2863            }),
2864        );
2865        assert!(format!("{disp:?}").contains("Dispatcher"));
2866        assert_eq!(timeout(Millis(1000), disp).await, Ok(Ok(())));
2867        reason.take()
2868    }
2869
2870    #[ntex::test]
2871    async fn encoder_error_stops_dispatcher() {
2872        let reason = err_codec_stop("data").await;
2873        assert!(reason.starts_with("Reason::Encoder("), "{reason}");
2874        assert!(reason.contains("encode"), "{reason}");
2875    }
2876
2877    #[ntex::test]
2878    async fn decoder_error_stops_dispatcher() {
2879        let reason = err_codec_stop("!").await;
2880        assert!(reason.starts_with("Reason::Decoder("), "{reason}");
2881        assert!(reason.contains("decode"), "{reason}");
2882    }
2883
2884    #[test]
2885    fn debug_formats() {
2886        let item = DispatchItem::<BCodec>::Item(Bytes::from_static(b"x"));
2887        assert_eq!(format!("{item:?}"), "DispatchItem::Item(b\"x\")");
2888        let item = DispatchItem::<BCodec>::Control(Control::WBackPressureEnabled);
2889        assert_eq!(
2890            format!("{item:?}"),
2891            "DispatchItem::Control(WBackPressureEnabled)"
2892        );
2893        let item = DispatchItem::<BCodec>::Stop(Reason::KeepAlive);
2894        assert_eq!(format!("{item:?}"), "DispatchItem::Stop(Reason::KeepAlive)");
2895
2896        for (reason, expected) in [
2897            (Reason::<BCodec>::Service, "Reason::Service"),
2898            (Reason::Io(None), "Reason::Io(None)"),
2899            (Reason::KeepAlive, "Reason::KeepAlive"),
2900            (Reason::ReadTimeout, "Reason::ReadTimeout"),
2901            (Reason::WriteTimeout, "Reason::WriteTimeout"),
2902        ] {
2903            assert_eq!(format!("{reason:?}"), expected);
2904        }
2905        let reason = Reason::<BCodec>::Encoder(io::Error::other("e"));
2906        assert!(format!("{reason:?}").starts_with("Reason::Encoder("));
2907        let reason = Reason::<BCodec>::Decoder(io::Error::other("d"));
2908        assert!(format!("{reason:?}").starts_with("Reason::Decoder("));
2909    }
2910
2911    /// A write failure while waiting for backpressure release stops the
2912    /// dispatcher with an io error.
2913    #[ntex::test]
2914    async fn write_error_during_backpressure() {
2915        let (client, server) = IoTest::create();
2916        client.remote_buffer_cap(0);
2917
2918        let events = Rc::new(RefCell::new(Vec::new()));
2919        let events2 = events.clone();
2920        let io = Io::new(
2921            server,
2922            SharedCfg::new("TEST").add(IoConfig::new().set_write_backpressure(1024)),
2923        );
2924        let (disp, _) = Dispatcher::debug(
2925            io,
2926            BCodec(1),
2927            ntex_service::fn_service(move |msg: DispatchItem<BCodec>| {
2928                let events = events2.clone();
2929                async move {
2930                    let ev = match msg {
2931                        DispatchItem::Item(_) => {
2932                            events.borrow_mut().push("item".to_string());
2933                            return Ok::<_, ()>(Some(Bytes::from(vec![b'x'; 4096])));
2934                        }
2935                        DispatchItem::Control(c) => format!("{c:?}"),
2936                        DispatchItem::Stop(Reason::Io(Some(err))) => format!("io: {err}"),
2937                        DispatchItem::Stop(r) => format!("{r:?}"),
2938                    };
2939                    events.borrow_mut().push(ev);
2940                    Ok(None)
2941                }
2942            }),
2943        );
2944        let hnd = spawn(disp);
2945
2946        client.write("1");
2947        sleep(Millis(100)).await;
2948        assert_eq!(&events.borrow()[..], &["item", "WBackPressureEnabled"]);
2949
2950        client.write_error(io::Error::other("write failed"));
2951        client.remote_buffer_cap(1024);
2952        assert!(matches!(timeout(Millis(1000), hnd).await, Ok(Ok(Ok(())))));
2953        assert_eq!(
2954            &events.borrow()[..],
2955            &["item", "WBackPressureEnabled", "io: write failed"]
2956        );
2957    }
2958
2959    /// The frame read rate is satisfied, but the cumulative `max_timeout`
2960    /// budget expires.
2961    #[ntex::test]
2962    async fn read_rate_max_timeout_reached() {
2963        let (client, server) = IoTest::create();
2964        client.remote_buffer_cap(1024);
2965
2966        let data = Arc::new(Mutex::new(RefCell::new(Vec::new())));
2967        let data2 = data.clone();
2968        let io = Io::new(
2969            server,
2970            SharedCfg::new("TEST").add(
2971                IoConfig::new()
2972                    .set_keepalive_timeout(Seconds::ZERO)
2973                    .set_frame_read_rate(Seconds(1), Seconds(2), 2),
2974            ),
2975        );
2976        let (disp, _) = Dispatcher::debug(
2977            io,
2978            BCodec(1024),
2979            ntex_service::fn_service(move |msg: DispatchItem<BCodec>| {
2980                let data = data2.clone();
2981                async move {
2982                    if let DispatchItem::Stop(Reason::ReadTimeout) = msg {
2983                        data.lock().unwrap().borrow_mut().push(1);
2984                    }
2985                    Ok::<_, ()>(None)
2986                }
2987            }),
2988        );
2989        spawn(async move {
2990            let _ = disp.await;
2991        });
2992
2993        let start = std::time::Instant::now();
2994        for _ in 0..12 {
2995            client.write("abc");
2996            sleep(Millis(300)).await;
2997            if client.is_closed() {
2998                break;
2999            }
3000        }
3001        wait_closed(&client, Millis(1000)).await;
3002        assert!(client.is_closed());
3003        assert!(start.elapsed() < std::time::Duration::from_millis(3500));
3004        assert_eq!(&data.lock().unwrap().borrow()[..], &[1]);
3005    }
3006
3007    #[derive(Default)]
3008    struct GateDir {
3009        blocked: Cell<bool>,
3010        waker: RefCell<Option<std::task::Waker>>,
3011    }
3012
3013    impl GateDir {
3014        fn poll(
3015            &self,
3016            res: Poll<ntex_io::Readiness>,
3017            cx: &Context<'_>,
3018        ) -> Poll<ntex_io::Readiness> {
3019            match res {
3020                Poll::Ready(ntex_io::Readiness::Ready) if self.blocked.get() => {
3021                    *self.waker.borrow_mut() = Some(cx.waker().clone());
3022                    Poll::Pending
3023                }
3024                res => res,
3025            }
3026        }
3027
3028        fn set(&self, blocked: bool) {
3029            self.blocked.set(blocked);
3030            if !blocked && let Some(waker) = self.waker.borrow_mut().take() {
3031                waker.wake();
3032            }
3033        }
3034    }
3035
3036    /// Gate control, a blocked filter chain is not ready for reads or writes.
3037    #[derive(Clone, Default)]
3038    struct GateCtl(Rc<(GateDir, GateDir)>);
3039
3040    impl GateCtl {
3041        fn block(&self) {
3042            self.0.0.set(true);
3043        }
3044
3045        fn unblock(&self) {
3046            self.0.0.set(false);
3047        }
3048
3049        fn block_write(&self) {
3050            self.0.1.set(true);
3051        }
3052
3053        fn unblock_write(&self) {
3054            self.0.1.set(false);
3055        }
3056    }
3057
3058    struct GateFilter<F>(F, GateCtl);
3059
3060    impl<F: ntex_io::Filter> ntex_io::Filter for GateFilter<F> {
3061        fn query(&self, id: std::any::TypeId) -> Option<Box<dyn std::any::Any>> {
3062            self.0.query(id)
3063        }
3064
3065        fn process_read_buf(&self, ctx: &mut ntex_io::FilterCtx<'_>) -> io::Result<()> {
3066            self.0.process_read_buf(ctx)
3067        }
3068
3069        fn process_write_buf(&self, ctx: &mut ntex_io::FilterCtx<'_>) -> io::Result<()> {
3070            self.0.process_write_buf(ctx)
3071        }
3072
3073        fn shutdown(&self, ctx: &mut ntex_io::FilterCtx<'_>) -> io::Result<Poll<()>> {
3074            self.0.shutdown(ctx)
3075        }
3076
3077        fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll<ntex_io::Readiness> {
3078            self.1.0.0.poll(self.0.poll_read_ready(cx), cx)
3079        }
3080
3081        fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll<ntex_io::Readiness> {
3082            self.1.0.1.poll(self.0.poll_write_ready(cx), cx)
3083        }
3084    }
3085
3086    /// Echo dispatcher over a gated filter chain, records items as `0` and
3087    /// stop reasons as `1`.
3088    fn gated_dispatcher(
3089        server: IoTest,
3090        cfg: IoConfig,
3091        data: Rc<RefCell<Vec<u8>>>,
3092    ) -> (GateCtl, IoRef) {
3093        let gate = GateCtl::default();
3094        let g = gate.clone();
3095        let io =
3096            Io::new(server, SharedCfg::new("TEST").add(cfg)).map_filter(move |f| GateFilter(f, g));
3097        let ioref = io.get_ref();
3098
3099        let disp = Dispatcher::new(
3100            io,
3101            BCodec(8),
3102            Pipeline::new(
3103                (),
3104                ntex_service::fn_service(move |msg: DispatchItem<BCodec>| {
3105                    let data = data.clone();
3106                    async move {
3107                        match msg {
3108                            DispatchItem::Item(bytes) => {
3109                                data.borrow_mut().push(0);
3110                                return Ok::<_, ()>(Some(bytes));
3111                            }
3112                            DispatchItem::Stop(Reason::ReadTimeout | Reason::KeepAlive) => {
3113                                data.borrow_mut().push(1);
3114                            }
3115                            _ => (),
3116                        }
3117                        Ok(None)
3118                    }
3119                }),
3120            ),
3121        );
3122        spawn(async move {
3123            let _ = disp.await;
3124        });
3125        (gate, ioref)
3126    }
3127
3128    #[ntex::test]
3129    async fn filter_pause_suspends_read_timeout() {
3130        let (client, server) = IoTest::create();
3131        client.remote_buffer_cap(1024);
3132        let data = Rc::new(RefCell::new(Vec::new()));
3133        let (gate, io) = gated_dispatcher(
3134            server,
3135            IoConfig::new()
3136                .set_keepalive_timeout(Seconds::ZERO)
3137                .set_frame_read_rate(Seconds(1), Seconds(2), 2),
3138            data.clone(),
3139        );
3140
3141        // a partial frame arms the frame read timer
3142        client.write("1234");
3143        sleep(Millis(100)).await;
3144
3145        // the peer sends the rest of the frame while the filter is not ready
3146        gate.block();
3147        client.write("5678");
3148        sleep(Millis(100)).await;
3149        assert!(io.is_read_filter_paused());
3150
3151        // the frame read timer does not run during the pause
3152        sleep(Millis(3000)).await;
3153        assert!(io.is_active());
3154        assert!(data.borrow().is_empty());
3155
3156        gate.unblock();
3157        let buf = client.read().await.unwrap();
3158        assert_eq!(buf, Bytes::from_static(b"12345678"));
3159        assert!(!io.is_read_filter_paused());
3160        assert_eq!(&data.borrow()[..], &[0]);
3161    }
3162
3163    #[ntex::test]
3164    async fn filter_pause_suspends_keepalive() {
3165        let (client, server) = IoTest::create();
3166        client.remote_buffer_cap(1024);
3167        let data = Rc::new(RefCell::new(Vec::new()));
3168        let (gate, io) = gated_dispatcher(
3169            server,
3170            IoConfig::new()
3171                .set_shutdown_timeout(Seconds(1))
3172                .set_keepalive_timeout(Seconds(1)),
3173            data.clone(),
3174        );
3175
3176        // the peer sends a frame while the filter is not ready
3177        sleep(Millis(100)).await;
3178        gate.block();
3179        client.write("12345678");
3180        sleep(Millis(100)).await;
3181        assert!(io.is_read_filter_paused());
3182
3183        // keep-alive does not run during the pause
3184        sleep(Millis(2000)).await;
3185        assert!(io.is_active());
3186        assert!(data.borrow().is_empty());
3187
3188        gate.unblock();
3189        let buf = client.read().await.unwrap();
3190        assert_eq!(buf, Bytes::from_static(b"12345678"));
3191
3192        // keep-alive is armed again once reads resume
3193        wait_until(Millis(3000), || {
3194            !io.is_active() && client.is_closed() && data.borrow().len() == 2
3195        })
3196        .await;
3197        assert!(!io.is_active());
3198        assert!(client.is_closed());
3199        assert_eq!(&data.borrow()[..], &[0, 1]);
3200    }
3201
3202    /// `write_dispatcher` over a gated filter chain.
3203    fn gated_write_dispatcher(
3204        server: IoTest,
3205        cfg: IoConfig,
3206        size: usize,
3207    ) -> (GateCtl, IoRef, Events) {
3208        let gate = GateCtl::default();
3209        let g = gate.clone();
3210        let events = Rc::new(RefCell::new(Vec::new()));
3211        let io = Io::new(
3212            server,
3213            SharedCfg::new("TEST").add(cfg.set_write_backpressure(1024)),
3214        )
3215        .map_filter(move |f| GateFilter(f, g));
3216        let ioref = io.get_ref();
3217        let disp = Dispatcher::new(
3218            io,
3219            BCodec(8),
3220            Pipeline::new(
3221                (),
3222                WriteSrv {
3223                    size,
3224                    gate: Rc::new(RefCell::new(None)),
3225                    events: events.clone(),
3226                },
3227            ),
3228        );
3229        spawn(async move {
3230            let _ = disp.await;
3231        });
3232        (gate, ioref, events)
3233    }
3234
3235    #[ntex::test]
3236    async fn filter_pause_suspends_write_timeout() {
3237        let (client, server) = IoTest::create();
3238        client.remote_buffer_cap(65536);
3239        let (gate, io, events) =
3240            gated_write_dispatcher(server, IoConfig::new().set_write_timeout(Seconds(1)), 8192);
3241
3242        // the response waits for the filter, write backpressure is enabled
3243        gate.block_write();
3244        client.write("12345678");
3245        sleep(Millis(100)).await;
3246        assert!(io.is_write_filter_paused());
3247        assert_eq!(&events.borrow()[..], &["item", "bp-on"]);
3248
3249        // the write timeout does not run during the pause
3250        sleep(Millis(2500)).await;
3251        assert!(io.is_active());
3252        assert_eq!(&events.borrow()[..], &["item", "bp-on"]);
3253
3254        gate.unblock_write();
3255        let mut received = 0;
3256        while received < 8192 {
3257            received += client.read().await.unwrap().len();
3258        }
3259        assert!(!io.is_write_filter_paused());
3260        wait_until(Millis(1000), || events.borrow().len() == 3).await;
3261        assert_eq!(&events.borrow()[..], &["item", "bp-on", "bp-off"]);
3262    }
3263
3264    #[ntex::test]
3265    async fn write_timeout_restarts_after_filter_pause() {
3266        let (client, server) = IoTest::create();
3267        client.remote_buffer_cap(0);
3268        let (gate, io, events) =
3269            gated_write_dispatcher(server, IoConfig::new().set_write_timeout(Seconds(1)), 8192);
3270
3271        gate.block_write();
3272        client.write("12345678");
3273        sleep(Millis(1500)).await;
3274        assert!(io.is_write_filter_paused());
3275        assert!(io.is_active());
3276
3277        // the peer does not read, the timeout starts over once writes resume
3278        gate.unblock_write();
3279        sleep(Millis(500)).await;
3280        assert!(!io.is_write_filter_paused());
3281        assert!(io.is_active());
3282        wait_closed(&client, Millis(2000)).await;
3283        assert!(client.is_closed());
3284        assert_eq!(&events.borrow()[..], &["item", "bp-on", "write-timeout"]);
3285    }
3286}