Skip to main content

ntex_util/services/
buffer.rs

1//! Service that buffers incoming requests.
2#![allow(clippy::type_complexity)]
3use std::cell::{Cell, RefCell};
4use std::{collections::VecDeque, fmt, marker, task::Poll};
5
6use ntex_service::{Ctx, Middleware, Service, pipeline::PipelineState};
7
8use crate::channel::oneshot;
9
10#[derive(Copy, Clone, Debug)]
11/// Middleware that buffers requests while the wrapped service is not ready.
12///
13/// See [`BufferService`] for how buffered requests are released.
14///
15/// The default buffer capacity is 16 requests.
16pub struct Buffer<St: Clone, Req, Res, Err> {
17    buf_size: usize,
18    cancel_on_shutdown: bool,
19    st: marker::PhantomData<fn(St, Req) -> Result<Res, Err>>,
20}
21
22impl<St: Clone, Req, Res, Err> Buffer<St, Req, Res, Err> {
23    /// Sets the maximum number of buffered requests.
24    ///
25    /// The default capacity is 16.
26    #[must_use]
27    pub fn buf_size(mut self, size: usize) -> Self {
28        self.buf_size = size;
29        self
30    }
31
32    /// Cancel all buffered requests on shutdown.
33    ///
34    /// By default, buffered requests are released during service shutdown.
35    #[must_use]
36    pub fn cancel_on_shutdown(mut self) -> Self {
37        self.cancel_on_shutdown = true;
38        self
39    }
40}
41
42impl<St: Clone, Req, Res, Err> Default for Buffer<St, Req, Res, Err> {
43    fn default() -> Self {
44        Self {
45            buf_size: 16,
46            cancel_on_shutdown: false,
47            st: marker::PhantomData,
48        }
49    }
50}
51
52impl<S, St, Req, Res, Err> Middleware<S, St> for Buffer<St, Req, Res, Err>
53where
54    S: Service<St, Req, Res = Res, Error = Err> + 'static,
55    St: Clone + 'static,
56    Req: 'static,
57    Res: 'static,
58    Err: 'static,
59{
60    type Service = BufferService<St, Req, Res, Err>;
61
62    fn create(&self, _: &St, service: S) -> Self::Service {
63        let service = BufferService::new(self.buf_size, PipelineState::new(service));
64        if self.cancel_on_shutdown {
65            service.cancel_on_shutdown()
66        } else {
67            service
68        }
69    }
70}
71
72/// Errors returned by [`BufferService`].
73#[derive(Clone, Copy, Debug, PartialEq, Eq)]
74pub enum BufferServiceError<E> {
75    /// Error returned by the wrapped service.
76    Service(E),
77    /// The buffered request was canceled before it could be processed.
78    RequestCanceled,
79}
80
81impl<E> From<E> for BufferServiceError<E> {
82    fn from(err: E) -> Self {
83        BufferServiceError::Service(err)
84    }
85}
86
87impl<E: fmt::Display> fmt::Display for BufferServiceError<E> {
88    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89        match self {
90            BufferServiceError::Service(e) => fmt::Display::fmt(e, f),
91            BufferServiceError::RequestCanceled => f.write_str("buffer service request canceled"),
92        }
93    }
94}
95
96impl<E: fmt::Display + fmt::Debug> std::error::Error for BufferServiceError<E> {}
97
98/// A service that buffers requests while its wrapped service is not ready.
99///
100/// Buffered requests are released in order, one at a time: the next one is
101/// released only after the previous one has completed its call or was
102/// dropped. Shutdown drains the buffer the same way, unless
103/// [`cancel_on_shutdown`](Self::cancel_on_shutdown) is set, and shuts down the
104/// wrapped service afterwards.
105///
106/// The default buffer capacity is 16 requests.
107pub struct BufferService<St, Req, Res, Err> {
108    size: usize,
109    ready: Cell<bool>,
110    service: PipelineState<St, Req, Res, Err>,
111    buf: RefCell<VecDeque<oneshot::Sender<oneshot::Sender<()>>>>,
112    next_call: RefCell<Option<oneshot::Receiver<()>>>,
113    cancel_on_shutdown: bool,
114}
115
116impl<St, Req, Res, Err> BufferService<St, Req, Res, Err>
117where
118    St: Clone + 'static,
119{
120    #[must_use]
121    /// Creates a buffering service with the specified capacity.
122    pub fn new(size: usize, service: PipelineState<St, Req, Res, Err>) -> Self {
123        Self {
124            size,
125            service,
126            ready: Cell::new(false),
127            buf: RefCell::new(VecDeque::with_capacity(size)),
128            next_call: RefCell::default(),
129            cancel_on_shutdown: false,
130        }
131    }
132
133    #[must_use]
134    /// Cancels pending buffered requests instead of releasing them on shutdown.
135    pub fn cancel_on_shutdown(self) -> Self {
136        Self {
137            cancel_on_shutdown: true,
138            ..self
139        }
140    }
141}
142
143impl<St, Req, Res, Err> fmt::Debug for BufferService<St, Req, Res, Err> {
144    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145        f.debug_struct("BufferService")
146            .field("size", &self.size)
147            .field("cancel_on_shutdown", &self.cancel_on_shutdown)
148            .field("ready", &self.ready)
149            .field("service", &self.service)
150            .field("buf", &self.buf)
151            .field("next_call", &self.next_call)
152            .finish()
153    }
154}
155
156impl<St, Req, Res, Err> Service<St, Req> for BufferService<St, Req, Res, Err>
157where
158    St: Clone + 'static,
159    Req: 'static,
160    Res: 'static,
161    Err: 'static,
162{
163    type Res = Res;
164    type Error = BufferServiceError<Err>;
165
166    async fn ready(&self, ctx: Ctx<'_, Self, St>) -> Result<(), Self::Error> {
167        // hold advancement until the last released task completes its call or
168        // is dropped
169        let next_call = self.next_call.borrow_mut().take();
170        if let Some(next_call) = next_call {
171            let _ = next_call.recv().await;
172        }
173
174        ctx.poll_fn(|cx| {
175            let mut buffer = self.buf.borrow_mut();
176
177            // handle inner service readiness
178            if self.service.poll_ready(cx, ctx.st())?.is_pending() {
179                if buffer.len() < self.size {
180                    // buffer next request
181                    self.ready.set(false);
182                    Poll::Ready(Ok(()))
183                } else {
184                    log::trace!("Buffer limit exceeded");
185                    // service is not ready, its readiness wakes this task
186                    Poll::Pending
187                }
188            } else {
189                while let Some(sender) = buffer.pop_front() {
190                    let (next_call_tx, next_call_rx) = oneshot::channel();
191                    if sender.send(next_call_tx).is_err() || next_call_rx.poll_recv(cx).is_ready() {
192                        // the task is gone
193                        continue;
194                    }
195                    self.next_call.borrow_mut().replace(next_call_rx);
196                    self.ready.set(false);
197                    return Poll::Ready(Ok(()));
198                }
199
200                self.ready.set(true);
201                Poll::Ready(Ok(()))
202            }
203        })
204        .await
205    }
206
207    async fn shutdown(&self, ctx: Ctx<'_, Self, St>) {
208        // buffered requests are released one at a time, as in `ready()`
209        loop {
210            // hold advancement until the last released task completes its call
211            // or is dropped
212            let next_call = self.next_call.borrow_mut().take();
213            if let Some(next_call) = next_call {
214                let _ = next_call.recv().await;
215            }
216
217            if self.cancel_on_shutdown {
218                self.buf.borrow_mut().clear();
219            }
220            if self.buf.borrow().is_empty() {
221                break;
222            }
223
224            if self.service.ready(ctx.st()).await.is_err() {
225                log::error!("Buffered inner service failed while buffer flushing on shutdown");
226                break;
227            }
228
229            let mut buffer = self.buf.borrow_mut();
230            while let Some(sender) = buffer.pop_front() {
231                let (next_call_tx, next_call_rx) = oneshot::channel();
232                if sender.send(next_call_tx).is_ok() {
233                    self.next_call.borrow_mut().replace(next_call_rx);
234                    break;
235                }
236                // the task is gone
237            }
238        }
239
240        self.service.shutdown(ctx.st()).await;
241    }
242
243    async fn call(&self, req: Req, ctx: Ctx<'_, Self, St>) -> Result<Res, Self::Error> {
244        if self.ready.get() {
245            self.ready.set(false);
246            Ok(self.service.call_nowait(req, ctx.st()).await?)
247        } else {
248            let (tx, rx) = oneshot::channel();
249            self.buf.borrow_mut().push_back(tx);
250
251            // released, the guard is held until the call completes
252            let _task_guard = rx.recv().await.map_err(|_| {
253                log::trace!("Buffered service request canceled");
254                BufferServiceError::RequestCanceled
255            })?;
256
257            // call service
258            Ok(self.service.call(req, ctx.st()).await?)
259        }
260    }
261}
262
263#[cfg(test)]
264mod tests {
265    #![allow(clippy::unused_async_trait_impl)]
266    use ntex_service::{Pipeline, apply, fn_factory};
267    use std::{cell::RefCell, pin::Pin, rc::Rc, time::Duration};
268
269    use super::*;
270    use crate::{future::lazy, task::LocalWaker};
271
272    #[derive(Debug, Clone)]
273    struct TestService(Rc<Inner>);
274
275    #[derive(Debug)]
276    struct Inner {
277        ready: Cell<bool>,
278        waker: LocalWaker,
279        count: Cell<usize>,
280    }
281
282    impl Service<(), ()> for TestService {
283        type Res = ();
284        type Error = ();
285
286        async fn ready(&self, ctx: Ctx<'_, Self, ()>) -> Result<(), Self::Error> {
287            ctx.poll_fn(|cx| {
288                self.0.waker.register(cx.waker());
289                if self.0.ready.get() {
290                    Poll::Ready(Ok(()))
291                } else {
292                    Poll::Pending
293                }
294            })
295            .await
296        }
297
298        async fn call(&self, _r: (), _: Ctx<'_, Self, ()>) -> Result<(), ()> {
299            self.0.ready.set(false);
300            self.0.count.set(self.0.count.get() + 1);
301            Ok(())
302        }
303    }
304
305    struct PendingService {
306        inner: Rc<PendingInner>,
307        release: RefCell<Option<oneshot::Receiver<()>>>,
308    }
309
310    struct PendingInner {
311        started: Cell<bool>,
312        completed: Cell<bool>,
313        shutdown: Cell<bool>,
314    }
315
316    impl Service<(), ()> for PendingService {
317        type Res = ();
318        type Error = ();
319
320        async fn call(&self, (): (), _: Ctx<'_, Self, ()>) -> Result<(), ()> {
321            self.inner.started.set(true);
322            let release = self.release.borrow_mut().take().unwrap();
323            release.recv().await.unwrap();
324            self.inner.completed.set(true);
325            Ok(())
326        }
327
328        async fn shutdown(&self, _: Ctx<'_, Self, ()>) {
329            self.inner.shutdown.set(true);
330        }
331    }
332
333    #[ntex::test]
334    async fn test_service() {
335        let inner = Rc::new(Inner {
336            ready: Cell::new(false),
337            waker: LocalWaker::default(),
338            count: Cell::new(0),
339        });
340
341        let svc = BufferService::new(2, PipelineState::new(TestService(inner.clone())));
342        assert!(format!("{svc:?}").contains("BufferService"));
343
344        let srv = Pipeline::new((), svc);
345        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
346
347        let srv1 = srv.bind();
348        ntex::rt::spawn(async move {
349            let _ = srv1.call(()).await;
350        });
351        crate::time::sleep(Duration::from_millis(25)).await;
352        assert_eq!(inner.count.get(), 0);
353        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
354
355        let srv1 = srv.bind();
356        ntex::rt::spawn(async move {
357            let _ = srv1.call(()).await;
358        });
359        crate::time::sleep(Duration::from_millis(25)).await;
360        assert_eq!(inner.count.get(), 0);
361        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Pending);
362
363        inner.ready.set(true);
364        inner.waker.wake();
365        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
366
367        crate::time::sleep(Duration::from_millis(25)).await;
368        assert_eq!(inner.count.get(), 1);
369
370        inner.ready.set(true);
371        inner.waker.wake();
372        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
373
374        crate::time::sleep(Duration::from_millis(25)).await;
375        assert_eq!(inner.count.get(), 2);
376
377        let inner = Rc::new(Inner {
378            ready: Cell::new(true),
379            waker: LocalWaker::default(),
380            count: Cell::new(0),
381        });
382
383        let srv = Pipeline::new(
384            (),
385            BufferService::new(2, PipelineState::new(TestService(inner.clone()))),
386        );
387        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
388
389        let _ = srv.call(()).await;
390        assert_eq!(inner.count.get(), 1);
391        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
392        assert!(lazy(|cx| srv.poll_shutdown(cx)).await.is_ready());
393
394        let err = BufferServiceError::from("test");
395        assert!(format!("{err}").contains("test"));
396        assert!(format!("{:?}", Buffer::<(), (), (), ()>::default()).contains("Buffer"));
397    }
398
399    #[ntex::test]
400    #[allow(clippy::redundant_clone)]
401    async fn test_middleware() {
402        let inner = Rc::new(Inner {
403            ready: Cell::new(false),
404            waker: LocalWaker::default(),
405            count: Cell::new(0),
406        });
407        let inner2 = inner.clone();
408
409        let srv = apply(
410            Buffer::default().buf_size(2),
411            fn_factory(async move |(): &()| Ok::<_, ()>(TestService(inner2.clone()))),
412        );
413
414        let srv = srv.pipeline(()).await.unwrap();
415        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
416
417        let srv1 = srv.bind();
418        ntex::rt::spawn(async move {
419            let _ = srv1.call(()).await;
420        });
421        crate::time::sleep(Duration::from_millis(25)).await;
422        assert_eq!(inner.count.get(), 0);
423        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
424
425        let srv1 = srv.bind();
426        ntex::rt::spawn(async move {
427            let _ = srv1.call(()).await;
428        });
429        crate::time::sleep(Duration::from_millis(25)).await;
430        assert_eq!(inner.count.get(), 0);
431        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Pending);
432
433        inner.ready.set(true);
434        inner.waker.wake();
435        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
436
437        crate::time::sleep(Duration::from_millis(25)).await;
438        assert_eq!(inner.count.get(), 1);
439
440        inner.ready.set(true);
441        inner.waker.wake();
442        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
443
444        crate::time::sleep(Duration::from_millis(25)).await;
445        assert_eq!(inner.count.get(), 2);
446    }
447
448    #[ntex::test]
449    #[allow(clippy::redundant_clone)]
450    async fn test_middleware2() {
451        let inner = Rc::new(Inner {
452            ready: Cell::new(false),
453            waker: LocalWaker::default(),
454            count: Cell::new(0),
455        });
456        let inner2 = inner.clone();
457
458        let srv = apply(
459            Buffer::default().buf_size(2),
460            fn_factory(async move |(): &()| Ok::<_, ()>(TestService(inner2.clone()))),
461        );
462
463        let srv = srv.pipeline(()).await.unwrap();
464        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
465
466        let srv1 = srv.bind();
467        ntex::rt::spawn(async move {
468            let _ = srv1.call(()).await;
469        });
470        crate::time::sleep(Duration::from_millis(25)).await;
471        assert_eq!(inner.count.get(), 0);
472        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
473
474        let srv1 = srv.bind();
475        ntex::rt::spawn(async move {
476            let _ = srv1.call(()).await;
477        });
478        crate::time::sleep(Duration::from_millis(25)).await;
479        assert_eq!(inner.count.get(), 0);
480        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Pending);
481
482        inner.ready.set(true);
483        inner.waker.wake();
484        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
485
486        crate::time::sleep(Duration::from_millis(25)).await;
487        assert_eq!(inner.count.get(), 1);
488
489        inner.ready.set(true);
490        inner.waker.wake();
491        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
492
493        crate::time::sleep(Duration::from_millis(25)).await;
494        assert_eq!(inner.count.get(), 2);
495    }
496
497    #[ntex::test]
498    async fn middleware_cancels_buffered_requests_on_shutdown() {
499        let inner = Rc::new(Inner {
500            ready: Cell::new(false),
501            waker: LocalWaker::default(),
502            count: Cell::new(0),
503        });
504        let inner2 = inner.clone();
505
506        let srv = apply(
507            Buffer::default().buf_size(1).cancel_on_shutdown(),
508            fn_factory(async move |(): &()| Ok::<_, ()>(TestService(inner2.clone()))),
509        )
510        .pipeline(())
511        .await
512        .unwrap();
513
514        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
515
516        let canceled = Rc::new(Cell::new(false));
517        let canceled2 = canceled.clone();
518        let srv2 = srv.bind();
519        ntex::rt::spawn(async move {
520            canceled2.set(matches!(
521                srv2.call(()).await,
522                Err(BufferServiceError::RequestCanceled)
523            ));
524        });
525
526        crate::time::sleep(Duration::from_millis(25)).await;
527        srv.shutdown().await;
528        crate::time::sleep(Duration::from_millis(25)).await;
529
530        assert!(canceled.get());
531        assert_eq!(inner.count.get(), 0);
532    }
533
534    #[ntex::test]
535    async fn shutdown_with_in_flight_request() {
536        let inner = Rc::new(PendingInner {
537            started: Cell::new(false),
538            completed: Cell::new(false),
539            shutdown: Cell::new(false),
540        });
541        let (release_tx, release_rx) = oneshot::channel();
542        let service = PendingService {
543            inner: inner.clone(),
544            release: RefCell::new(Some(release_rx)),
545        };
546        let srv = Pipeline::new((), BufferService::new(1, PipelineState::new(service)));
547
548        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
549
550        let srv2 = srv.bind();
551        ntex::rt::spawn(async move {
552            srv2.call(()).await.unwrap();
553        });
554        crate::time::sleep(Duration::from_millis(25)).await;
555
556        assert!(inner.started.get());
557        assert!(!inner.completed.get());
558        assert!(lazy(|cx| srv.poll_shutdown(cx)).await.is_ready());
559        assert!(inner.shutdown.get());
560        assert!(!inner.completed.get());
561
562        release_tx.send(()).unwrap();
563        crate::time::sleep(Duration::from_millis(25)).await;
564
565        assert!(inner.completed.get());
566    }
567
568    #[derive(Default)]
569    struct DrainInner {
570        ready: Cell<bool>,
571        waker: LocalWaker,
572        active: Cell<usize>,
573        max: Cell<usize>,
574        count: Cell<usize>,
575        active_on_shutdown: Cell<Option<usize>>,
576    }
577
578    struct DrainService(Rc<DrainInner>);
579
580    impl Service<(), ()> for DrainService {
581        type Res = ();
582        type Error = ();
583
584        async fn ready(&self, ctx: Ctx<'_, Self, ()>) -> Result<(), ()> {
585            ctx.poll_fn(|cx| {
586                self.0.waker.register(cx.waker());
587                if self.0.ready.get() {
588                    Poll::Ready(Ok(()))
589                } else {
590                    Poll::Pending
591                }
592            })
593            .await
594        }
595
596        async fn call(&self, (): (), _: Ctx<'_, Self, ()>) -> Result<(), ()> {
597            let inner = &self.0;
598            inner.active.set(inner.active.get() + 1);
599            inner.max.set(inner.max.get().max(inner.active.get()));
600            crate::time::sleep(Duration::from_millis(25)).await;
601            inner.active.set(inner.active.get() - 1);
602            inner.count.set(inner.count.get() + 1);
603            Ok(())
604        }
605
606        async fn shutdown(&self, _: Ctx<'_, Self, ()>) {
607            self.0.active_on_shutdown.set(Some(self.0.active.get()));
608        }
609    }
610
611    #[ntex::test]
612    async fn shutdown_drains_buffer_one_at_a_time() {
613        let inner = Rc::new(DrainInner::default());
614        let srv = Pipeline::new(
615            (),
616            BufferService::new(4, PipelineState::new(DrainService(inner.clone()))),
617        );
618
619        for _ in 0..2 {
620            srv.ready().await.unwrap();
621            let srv = srv.bind();
622            ntex::rt::spawn(async move {
623                srv.call(()).await.unwrap();
624            });
625        }
626        crate::time::sleep(Duration::from_millis(10)).await;
627        assert_eq!(inner.count.get(), 0);
628
629        inner.ready.set(true);
630        inner.waker.wake();
631        srv.shutdown().await;
632
633        assert_eq!(inner.count.get(), 2);
634        assert_eq!(inner.max.get(), 1);
635        assert_eq!(inner.active_on_shutdown.get(), Some(0));
636    }
637
638    struct FailService(Rc<Cell<bool>>);
639
640    impl Service<(), ()> for FailService {
641        type Res = ();
642        type Error = ();
643
644        async fn ready(&self, _: Ctx<'_, Self, ()>) -> Result<(), ()> {
645            if self.0.get() {
646                Err(())
647            } else {
648                std::future::pending().await
649            }
650        }
651
652        async fn call(&self, (): (), _: Ctx<'_, Self, ()>) -> Result<(), ()> {
653            unreachable!("FailService is never ready")
654        }
655    }
656
657    #[test]
658    fn request_canceled_display() {
659        let err = BufferServiceError::<&str>::RequestCanceled;
660        assert_eq!(err.to_string(), "buffer service request canceled");
661    }
662
663    #[ntex::test]
664    async fn dropped_buffered_request_is_skipped() {
665        let inner = Rc::new(Inner {
666            ready: Cell::new(false),
667            waker: LocalWaker::default(),
668            count: Cell::new(0),
669        });
670        let srv = Pipeline::new(
671            (),
672            BufferService::new(2, PipelineState::new(TestService(inner.clone()))),
673        );
674        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
675
676        // buffer a request, then drop it before release
677        let mut fut = srv.call_nowait(());
678        assert!(lazy(|cx| Pin::new(&mut fut).poll(cx)).await.is_pending());
679        drop(fut);
680
681        inner.ready.set(true);
682        inner.waker.wake();
683        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
684
685        // readiness goes to the next direct call
686        srv.call_nowait(()).await.unwrap();
687        assert_eq!(inner.count.get(), 1);
688    }
689
690    #[ntex::test]
691    async fn shutdown_skips_dropped_buffered_request() {
692        let inner = Rc::new(Inner {
693            ready: Cell::new(false),
694            waker: LocalWaker::default(),
695            count: Cell::new(0),
696        });
697        let srv = Pipeline::new(
698            (),
699            BufferService::new(2, PipelineState::new(TestService(inner.clone()))),
700        );
701        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
702
703        let mut fut = srv.call_nowait(());
704        assert!(lazy(|cx| Pin::new(&mut fut).poll(cx)).await.is_pending());
705        drop(fut);
706
707        inner.ready.set(true);
708        crate::time::timeout(Duration::from_secs(1), srv.shutdown())
709            .await
710            .unwrap();
711        assert_eq!(inner.count.get(), 0);
712    }
713
714    #[ntex::test]
715    async fn shutdown_stops_on_inner_ready_error() {
716        let fail = Rc::new(Cell::new(false));
717        let srv = Pipeline::new(
718            (),
719            BufferService::new(2, PipelineState::new(FailService(fail.clone()))),
720        );
721        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
722
723        let mut fut = srv.call_nowait(());
724        assert!(lazy(|cx| Pin::new(&mut fut).poll(cx)).await.is_pending());
725
726        fail.set(true);
727        crate::time::timeout(Duration::from_secs(1), srv.shutdown())
728            .await
729            .unwrap();
730
731        // the buffered request is never released
732        assert!(lazy(|cx| Pin::new(&mut fut).poll(cx)).await.is_pending());
733    }
734}