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::task::Poll;
5use std::{collections::VecDeque, fmt, future, future::Future, marker, mem, pin, rc::Rc};
6
7use ntex_service::{BindCtx, Ctx, Middleware, ReadyNotifier, Service, pipeline::PipelineState};
8
9use crate::{channel::oneshot, future::OnDropFn, task::LocalWaker};
10
11#[derive(Copy, Clone, Debug)]
12/// Middleware that buffers requests while the wrapped service is not ready.
13///
14/// See [`BufferService`] for how buffered requests are released.
15///
16/// The default buffer capacity is 16 requests.
17pub struct Buffer<St: Clone, Req, Res, Err> {
18    buf_size: usize,
19    cancel_on_shutdown: bool,
20    st: marker::PhantomData<fn(St, Req) -> Result<Res, Err>>,
21}
22
23impl<St: Clone, Req, Res, Err> Buffer<St, Req, Res, Err> {
24    /// Sets the maximum number of buffered requests.
25    ///
26    /// The default capacity is 16.
27    #[must_use]
28    pub fn buf_size(mut self, size: usize) -> Self {
29        self.buf_size = size;
30        self
31    }
32
33    /// Cancel all buffered requests on shutdown.
34    ///
35    /// By default, buffered requests are released during service shutdown.
36    #[must_use]
37    pub fn cancel_on_shutdown(mut self) -> Self {
38        self.cancel_on_shutdown = true;
39        self
40    }
41}
42
43impl<St: Clone, Req, Res, Err> Default for Buffer<St, Req, Res, Err> {
44    fn default() -> Self {
45        Self {
46            buf_size: 16,
47            cancel_on_shutdown: false,
48            st: marker::PhantomData,
49        }
50    }
51}
52
53impl<S, St, Req, Res, Err> Middleware<S, St> for Buffer<St, Req, Res, Err>
54where
55    S: Service<St, Req, Res = Res, Error = Err> + 'static,
56    St: Clone + 'static,
57    Req: 'static,
58    Res: 'static,
59    Err: 'static,
60{
61    type Service = BufferService<St, Req, Res, Err>;
62
63    fn create(&self, _: &St, service: S) -> Self::Service {
64        let service = BufferService::new(self.buf_size, PipelineState::new(service));
65        if self.cancel_on_shutdown {
66            service.cancel_on_shutdown()
67        } else {
68            service
69        }
70    }
71}
72
73/// Errors returned by [`BufferService`].
74#[derive(Clone, Copy, Debug, PartialEq, Eq)]
75pub enum BufferServiceError<E> {
76    /// Error returned by the wrapped service.
77    Service(E),
78    /// The buffered request was canceled before it could be processed.
79    RequestCanceled,
80}
81
82impl<E> From<E> for BufferServiceError<E> {
83    fn from(err: E) -> Self {
84        BufferServiceError::Service(err)
85    }
86}
87
88impl<E: fmt::Display> fmt::Display for BufferServiceError<E> {
89    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90        match self {
91            BufferServiceError::Service(e) => fmt::Display::fmt(e, f),
92            BufferServiceError::RequestCanceled => f.write_str("buffer service request canceled"),
93        }
94    }
95}
96
97impl<E: fmt::Display + fmt::Debug> std::error::Error for BufferServiceError<E> {}
98
99/// A service that buffers requests while its wrapped service is not ready.
100///
101/// The service is ready while the buffer has room; it does not depend on the
102/// readiness of the wrapped service. Requests that arrive while the wrapped
103/// service is not ready, or while earlier requests are still waiting, are
104/// buffered and released in order. Each released request waits for the wrapped
105/// service's readiness, then releases the next one before calling the wrapped
106/// service. The wrapped service must notify synchronously when a call consumes
107/// its capacity, as required by readiness caching.
108///
109/// The buffer size is a soft limit: callers that call without a readiness
110/// check, or after an outdated one, are still buffered.
111/// Queue capacity changes invalidate cached readiness for every pipeline sharing
112/// this service, including when a queued request is canceled.
113///
114/// Errors of the wrapped service's readiness are returned to the waiting
115/// request. Shutdown waits until all buffered requests have completed their
116/// calls, unless [`cancel_on_shutdown`](Self::cancel_on_shutdown) is set, and
117/// shuts down the wrapped service afterwards.
118///
119/// The default buffer capacity is 16 requests. A capacity of 0 is treated as 1.
120pub struct BufferService<St, Req, Res, Err> {
121    shared: Rc<Shared>,
122    service: PipelineState<St, Req, Res, Err>,
123    cancel_on_shutdown: bool,
124}
125
126struct Shared {
127    size: usize,
128    buf: RefCell<VecDeque<oneshot::Sender<Token>>>,
129    // a released request holds the token while it waits for inner readiness
130    has_token: Cell<bool>,
131    // buffered requests in the inner call
132    in_flight: Cell<usize>,
133    canceled: Cell<bool>,
134    notifier: ReadyNotifier,
135    token_waker: LocalWaker,
136    drained: LocalWaker,
137}
138
139impl Shared {
140    fn has_room(&self) -> bool {
141        self.buf.borrow().len() < self.size
142    }
143
144    fn is_drained(&self) -> bool {
145        !self.has_token.get() && self.in_flight.get() == 0 && self.buf.borrow().is_empty()
146    }
147
148    /// Passes the token to the next waiting request.
149    fn release_next(self: &Rc<Self>) {
150        loop {
151            let (sender, freed) = {
152                let mut buf = self.buf.borrow_mut();
153                let full = buf.len() >= self.size;
154                let sender = buf.pop_front();
155                (sender, full && buf.len() < self.size)
156            };
157            let Some(sender) = sender else {
158                self.has_token.set(false);
159                self.drained.wake();
160                return;
161            };
162            let token = Token(Some(self.clone()));
163            if freed {
164                self.notifier.notify();
165            }
166            match sender.send(token) {
167                Ok(()) => return,
168                // the request is gone
169                Err(token) => token.disarm(),
170            }
171        }
172    }
173
174    /// Removes dropped requests from the buffer.
175    fn prune(&self) {
176        let freed = {
177            let mut buf = self.buf.borrow_mut();
178            let full = buf.len() >= self.size;
179            buf.retain(|sender| !sender.is_canceled());
180            full && buf.len() < self.size
181        };
182        if freed {
183            self.notifier.notify();
184        }
185    }
186}
187
188/// The right to wait for inner readiness; dropping it releases the next request.
189struct Token(Option<Rc<Shared>>);
190
191impl Token {
192    fn disarm(mut self) {
193        self.0 = None;
194    }
195}
196
197impl Drop for Token {
198    fn drop(&mut self) {
199        if let Some(shared) = self.0.take() {
200            shared.release_next();
201        }
202    }
203}
204
205/// Removes a dropped buffered request from the buffer.
206struct Waiter<'a> {
207    shared: &'a Shared,
208    rx: Option<oneshot::Receiver<Token>>,
209}
210
211impl Drop for Waiter<'_> {
212    fn drop(&mut self) {
213        if let Some(rx) = self.rx.take() {
214            // drops a token that was sent but not received
215            drop(rx);
216            self.shared.prune();
217        }
218    }
219}
220
221struct InFlight<'a>(&'a Shared);
222
223impl Drop for InFlight<'_> {
224    fn drop(&mut self) {
225        self.0.in_flight.set(self.0.in_flight.get() - 1);
226        self.0.drained.wake();
227    }
228}
229
230impl<St, Req, Res, Err> BufferService<St, Req, Res, Err>
231where
232    St: Clone + 'static,
233{
234    #[must_use]
235    /// Creates a buffering service with the specified capacity.
236    pub fn new(size: usize, service: PipelineState<St, Req, Res, Err>) -> Self {
237        let size = size.max(1);
238        Self {
239            shared: Rc::new(Shared {
240                size,
241                buf: RefCell::new(VecDeque::with_capacity(size)),
242                has_token: Cell::new(false),
243                in_flight: Cell::new(0),
244                canceled: Cell::new(false),
245                notifier: ReadyNotifier::new(),
246                token_waker: LocalWaker::new(),
247                drained: LocalWaker::new(),
248            }),
249            service,
250            cancel_on_shutdown: false,
251        }
252    }
253
254    #[must_use]
255    /// Cancels pending buffered requests instead of releasing them on shutdown.
256    pub fn cancel_on_shutdown(self) -> Self {
257        Self {
258            cancel_on_shutdown: true,
259            ..self
260        }
261    }
262}
263
264impl<St, Req, Res, Err> fmt::Debug for BufferService<St, Req, Res, Err> {
265    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
266        f.debug_struct("BufferService")
267            .field("size", &self.shared.size)
268            .field("cancel_on_shutdown", &self.cancel_on_shutdown)
269            .field("buffered", &self.shared.buf.borrow().len())
270            .field("in_flight", &self.shared.in_flight.get())
271            .field("service", &self.service)
272            .finish()
273    }
274}
275
276impl<St, Req, Res, Err> Service<St, Req> for BufferService<St, Req, Res, Err>
277where
278    St: Clone + 'static,
279    Req: 'static,
280    Res: 'static,
281    Err: 'static,
282{
283    type Res = Res;
284    type Error = BufferServiceError<Err>;
285
286    fn bind(&self, ctx: BindCtx<'_>) {
287        // readiness depends on the buffer only, the inner pipeline is not bound
288        ctx.subscribe(&self.shared.notifier);
289    }
290
291    async fn ready(&self, _: Ctx<'_, Self, St>) -> Result<(), Self::Error> {
292        if self.shared.has_room() {
293            Ok(())
294        } else {
295            log::trace!("Buffer limit exceeded");
296            // releasing capacity notifies and restarts the check
297            future::pending().await
298        }
299    }
300
301    async fn shutdown(&self, ctx: Ctx<'_, Self, St>) {
302        let shared = &self.shared;
303        if self.cancel_on_shutdown {
304            shared.canceled.set(true);
305            let buf = mem::take(&mut *shared.buf.borrow_mut());
306            let freed = buf.len() >= shared.size;
307            let _cancel = OnDropFn::new(|| {
308                shared.token_waker.wake();
309                // buffered requests get `RequestCanceled`
310                drop(buf);
311            });
312            if freed {
313                shared.notifier.notify();
314            }
315        }
316
317        future::poll_fn(|cx| {
318            if shared.is_drained() {
319                return Poll::Ready(());
320            }
321            shared.drained.register(cx.waker());
322            if shared.is_drained() {
323                Poll::Ready(())
324            } else {
325                Poll::Pending
326            }
327        })
328        .await;
329
330        self.service.shutdown(ctx.st()).await;
331    }
332
333    async fn call(&self, req: Req, ctx: Ctx<'_, Self, St>) -> Result<Res, Self::Error> {
334        let shared = &self.shared;
335        let idle = !shared.has_token.get() && shared.buf.borrow().is_empty();
336        if idle && self.service.is_ready() {
337            return Ok(self.service.call(req, ctx.st()).await?);
338        }
339
340        let (token, mut buffered) = if idle {
341            shared.has_token.set(true);
342            (Token(Some(shared.clone())), false)
343        } else {
344            let (tx, rx) = oneshot::channel();
345            let full = {
346                let mut buf = shared.buf.borrow_mut();
347                buf.push_back(tx);
348                buf.len() == shared.size
349            };
350            let mut waiter = Waiter {
351                shared,
352                rx: Some(rx),
353            };
354            if full {
355                shared.notifier.notify();
356            }
357            let result = future::poll_fn(|cx| waiter.rx.as_ref().unwrap().poll_recv(cx)).await;
358            waiter.rx = None;
359            let token = result.map_err(|_| {
360                log::trace!("Buffered service request canceled");
361                BufferServiceError::RequestCanceled
362            })?;
363            (token, true)
364        };
365
366        // the token holder waits for inner readiness, dropping it releases the next request
367        let mut ready = pin::pin!(self.service.ready(ctx.st()));
368        future::poll_fn(|cx| {
369            if shared.canceled.get() {
370                return Poll::Ready(Err(BufferServiceError::RequestCanceled));
371            }
372            if let Poll::Ready(result) = ready.as_mut().poll(cx) {
373                Poll::Ready(result.map_err(BufferServiceError::Service))
374            } else {
375                buffered = true;
376                shared.token_waker.register(cx.waker());
377                Poll::Pending
378            }
379        })
380        .await?;
381
382        let _in_flight = buffered.then(|| {
383            shared.in_flight.set(shared.in_flight.get() + 1);
384            InFlight(shared)
385        });
386        drop(token);
387
388        Ok(self.service.call(req, ctx.st()).await?)
389    }
390}
391
392#[cfg(test)]
393mod tests {
394    #![allow(clippy::unused_async_trait_impl)]
395    use ntex_service::{Pipeline, ReadyCallback, apply, fn_factory};
396    use std::{cell::RefCell, pin::Pin, time::Duration};
397    use std::{panic::AssertUnwindSafe, panic::catch_unwind, task::Context};
398
399    use super::*;
400    use crate::future::lazy;
401
402    #[derive(Debug, Clone)]
403    struct TestService(Rc<Inner>);
404
405    #[derive(Debug, Default)]
406    struct Inner {
407        ready: Cell<bool>,
408        waker: LocalWaker,
409        changed: ReadyNotifier,
410        count: Cell<usize>,
411    }
412
413    impl Inner {
414        fn new(ready: bool) -> Rc<Self> {
415            let inner = Rc::new(Self::default());
416            inner.ready.set(ready);
417            inner
418        }
419
420        fn set_ready(&self) {
421            self.ready.set(true);
422            self.waker.wake();
423        }
424    }
425
426    impl Service<(), ()> for TestService {
427        type Res = ();
428        type Error = ();
429
430        fn bind(&self, ctx: BindCtx<'_>) {
431            ctx.subscribe(&self.0.changed);
432        }
433
434        async fn ready(&self, _: Ctx<'_, Self, ()>) -> Result<(), Self::Error> {
435            future::poll_fn(|cx| {
436                self.0.waker.register(cx.waker());
437                if self.0.ready.get() {
438                    Poll::Ready(Ok(()))
439                } else {
440                    Poll::Pending
441                }
442            })
443            .await
444        }
445
446        async fn call(&self, _r: (), _: Ctx<'_, Self, ()>) -> Result<(), ()> {
447            // each call consumes readiness
448            self.0.ready.set(false);
449            self.0.changed.notify();
450            self.0.count.set(self.0.count.get() + 1);
451            Ok(())
452        }
453    }
454
455    fn spawn_call(
456        srv: &Pipeline<(), (), BufferServiceError<()>>,
457    ) -> Rc<RefCell<Option<Result<(), BufferServiceError<()>>>>> {
458        let result = Rc::new(RefCell::new(None));
459        let result2 = result.clone();
460        let srv = srv.bind();
461        ntex::rt::spawn(async move {
462            *result2.borrow_mut() = Some(srv.call(()).await);
463        });
464        result
465    }
466
467    async fn settle() {
468        crate::time::sleep(Duration::from_millis(25)).await;
469    }
470
471    #[ntex::test]
472    async fn test_service() {
473        let inner = Inner::new(false);
474        let svc = BufferService::new(2, PipelineState::new(TestService(inner.clone())));
475        assert!(format!("{svc:?}").contains("BufferService"));
476
477        let srv = Pipeline::new((), svc);
478        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
479
480        // the first request waits for inner readiness, the others are buffered
481        let _r1 = spawn_call(&srv);
482        let _r2 = spawn_call(&srv);
483        settle().await;
484        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
485        let _r3 = spawn_call(&srv);
486        settle().await;
487        assert_eq!(inner.count.get(), 0);
488        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Pending);
489
490        // requests are released one per inner readiness
491        inner.set_ready();
492        settle().await;
493        assert_eq!(inner.count.get(), 1);
494        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
495
496        inner.set_ready();
497        settle().await;
498        assert_eq!(inner.count.get(), 2);
499
500        inner.set_ready();
501        settle().await;
502        assert_eq!(inner.count.get(), 3);
503        assert!(lazy(|cx| srv.poll_shutdown(cx)).await.is_ready());
504
505        let err = BufferServiceError::from("test");
506        assert!(format!("{err}").contains("test"));
507        assert!(format!("{:?}", Buffer::<(), (), (), ()>::default()).contains("Buffer"));
508    }
509
510    #[ntex::test]
511    async fn sequential_calls_with_ready_service() {
512        let inner = Rc::new(PendingInner::default());
513        let srv = Pipeline::new(
514            (),
515            BufferService::new(2, PipelineState::new(ReadyService(inner.clone()))),
516        );
517
518        for _ in 0..3 {
519            crate::time::timeout(Duration::from_secs(1), srv.call(()))
520                .await
521                .unwrap()
522                .unwrap();
523        }
524        assert_eq!(inner.count.get(), 3);
525    }
526
527    #[ntex::test]
528    #[allow(clippy::redundant_clone)]
529    async fn test_middleware() {
530        let inner = Inner::new(false);
531        let inner2 = inner.clone();
532
533        let srv = apply(
534            Buffer::default().buf_size(1),
535            fn_factory(async move |(): &()| Ok::<_, ()>(TestService(inner2.clone()))),
536        );
537
538        let srv = srv.pipeline(()).await.unwrap();
539        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
540
541        let srv1 = srv.bind();
542        ntex::rt::spawn(async move {
543            let _ = srv1.call(()).await;
544        });
545        let srv1 = srv.bind();
546        ntex::rt::spawn(async move {
547            let _ = srv1.call(()).await;
548        });
549        settle().await;
550        assert_eq!(inner.count.get(), 0);
551        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Pending);
552
553        inner.set_ready();
554        settle().await;
555        assert_eq!(inner.count.get(), 1);
556        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
557
558        inner.set_ready();
559        settle().await;
560        assert_eq!(inner.count.get(), 2);
561    }
562
563    #[ntex::test]
564    async fn middleware_cancels_buffered_requests_on_shutdown() {
565        let inner = Inner::new(false);
566        let inner2 = inner.clone();
567
568        let srv = apply(
569            Buffer::default().buf_size(1).cancel_on_shutdown(),
570            fn_factory(async move |(): &()| Ok::<_, ()>(TestService(inner2.clone()))),
571        )
572        .pipeline(())
573        .await
574        .unwrap();
575        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
576
577        // one request waits for inner readiness, one is buffered
578        let r1 = spawn_call(&srv);
579        let r2 = spawn_call(&srv);
580        settle().await;
581        crate::time::timeout(Duration::from_secs(1), srv.shutdown())
582            .await
583            .unwrap();
584        settle().await;
585
586        assert!(matches!(
587            *r1.borrow(),
588            Some(Err(BufferServiceError::RequestCanceled))
589        ));
590        assert!(matches!(
591            *r2.borrow(),
592            Some(Err(BufferServiceError::RequestCanceled))
593        ));
594        assert_eq!(inner.count.get(), 0);
595    }
596
597    #[derive(Default)]
598    struct PendingInner {
599        started: Cell<bool>,
600        completed: Cell<bool>,
601        shutdown: Cell<bool>,
602        count: Cell<usize>,
603    }
604
605    struct ReadyService(Rc<PendingInner>);
606
607    impl Service<(), ()> for ReadyService {
608        type Res = ();
609        type Error = ();
610
611        async fn call(&self, (): (), _: Ctx<'_, Self, ()>) -> Result<(), ()> {
612            self.0.count.set(self.0.count.get() + 1);
613            Ok(())
614        }
615    }
616
617    struct PendingService {
618        inner: Rc<PendingInner>,
619        release: RefCell<Option<oneshot::Receiver<()>>>,
620    }
621
622    impl Service<(), ()> for PendingService {
623        type Res = ();
624        type Error = ();
625
626        async fn call(&self, (): (), _: Ctx<'_, Self, ()>) -> Result<(), ()> {
627            self.inner.started.set(true);
628            let release = self.release.borrow_mut().take().unwrap();
629            release.recv().await.unwrap();
630            self.inner.completed.set(true);
631            Ok(())
632        }
633
634        async fn shutdown(&self, _: Ctx<'_, Self, ()>) {
635            self.inner.shutdown.set(true);
636        }
637    }
638
639    #[ntex::test]
640    async fn shutdown_with_in_flight_request() {
641        let inner = Rc::new(PendingInner::default());
642        let (release_tx, release_rx) = oneshot::channel();
643        let service = PendingService {
644            inner: inner.clone(),
645            release: RefCell::new(Some(release_rx)),
646        };
647        let srv = Pipeline::new((), BufferService::new(1, PipelineState::new(service)));
648
649        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
650
651        let srv2 = srv.bind();
652        ntex::rt::spawn(async move {
653            srv2.call(()).await.unwrap();
654        });
655        settle().await;
656
657        // the request was not buffered
658        assert!(inner.started.get());
659        assert!(!inner.completed.get());
660        assert!(lazy(|cx| srv.poll_shutdown(cx)).await.is_ready());
661        assert!(inner.shutdown.get());
662        assert!(!inner.completed.get());
663
664        release_tx.send(()).unwrap();
665        settle().await;
666
667        assert!(inner.completed.get());
668    }
669
670    #[derive(Default)]
671    struct DrainInner {
672        ready: Cell<bool>,
673        waker: LocalWaker,
674        active: Cell<usize>,
675        count: Cell<usize>,
676        active_on_shutdown: Cell<Option<usize>>,
677    }
678
679    struct DrainService(Rc<DrainInner>);
680
681    impl Service<(), ()> for DrainService {
682        type Res = ();
683        type Error = ();
684
685        async fn ready(&self, _: Ctx<'_, Self, ()>) -> Result<(), ()> {
686            future::poll_fn(|cx| {
687                self.0.waker.register(cx.waker());
688                if self.0.ready.get() {
689                    Poll::Ready(Ok(()))
690                } else {
691                    Poll::Pending
692                }
693            })
694            .await
695        }
696
697        async fn call(&self, (): (), _: Ctx<'_, Self, ()>) -> Result<(), ()> {
698            let inner = &self.0;
699            inner.active.set(inner.active.get() + 1);
700            crate::time::sleep(Duration::from_millis(25)).await;
701            inner.active.set(inner.active.get() - 1);
702            inner.count.set(inner.count.get() + 1);
703            Ok(())
704        }
705
706        async fn shutdown(&self, _: Ctx<'_, Self, ()>) {
707            self.0.active_on_shutdown.set(Some(self.0.active.get()));
708        }
709    }
710
711    #[ntex::test]
712    async fn shutdown_drains_buffer() {
713        let inner = Rc::new(DrainInner::default());
714        let srv = Pipeline::new(
715            (),
716            BufferService::new(4, PipelineState::new(DrainService(inner.clone()))),
717        );
718
719        for _ in 0..2 {
720            srv.ready().await.unwrap();
721            let srv = srv.bind();
722            ntex::rt::spawn(async move {
723                srv.call(()).await.unwrap();
724            });
725        }
726        crate::time::sleep(Duration::from_millis(10)).await;
727        assert_eq!(inner.count.get(), 0);
728
729        inner.ready.set(true);
730        inner.waker.wake();
731        srv.shutdown().await;
732
733        assert_eq!(inner.count.get(), 2);
734        assert_eq!(inner.active_on_shutdown.get(), Some(0));
735    }
736
737    #[test]
738    fn request_canceled_display() {
739        let err = BufferServiceError::<&str>::RequestCanceled;
740        assert_eq!(err.to_string(), "buffer service request canceled");
741    }
742
743    #[ntex::test]
744    async fn dropped_requests_are_skipped() {
745        let inner = Inner::new(false);
746        let srv = Pipeline::new(
747            (),
748            BufferService::new(1, PipelineState::new(TestService(inner.clone()))),
749        );
750        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
751
752        // the first request waits for inner readiness, the second is buffered
753        let mut fut1 = srv.call_static(());
754        assert!(lazy(|cx| Pin::new(&mut fut1).poll(cx)).await.is_pending());
755        let mut fut2 = srv.call_static(());
756        assert!(lazy(|cx| Pin::new(&mut fut2).poll(cx)).await.is_pending());
757        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Pending);
758
759        // dropping a buffered request frees its slot
760        drop(fut2);
761        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
762        let r3 = spawn_call(&srv);
763        settle().await;
764
765        // dropping the waiting request releases the next one
766        drop(fut1);
767        inner.set_ready();
768        settle().await;
769        assert!(matches!(*r3.borrow(), Some(Ok(()))));
770        assert_eq!(inner.count.get(), 1);
771
772        // a released request that is dropped before it runs releases the next one
773        let mut fut4 = srv.call_static(());
774        assert!(lazy(|cx| Pin::new(&mut fut4).poll(cx)).await.is_pending());
775        let mut fut5 = srv.call_static(());
776        assert!(lazy(|cx| Pin::new(&mut fut5).poll(cx)).await.is_pending());
777        drop(fut4);
778        drop(fut5);
779        inner.set_ready();
780        crate::time::timeout(Duration::from_secs(1), srv.call(()))
781            .await
782            .unwrap()
783            .unwrap();
784        assert_eq!(inner.count.get(), 2);
785        crate::time::timeout(Duration::from_secs(1), srv.shutdown())
786            .await
787            .unwrap();
788    }
789
790    #[ntex::test]
791    async fn shutdown_skips_dropped_buffered_request() {
792        let inner = Inner::new(false);
793        let srv = Pipeline::new(
794            (),
795            BufferService::new(2, PipelineState::new(TestService(inner.clone()))),
796        );
797        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
798
799        let mut fut1 = srv.call_static(());
800        assert!(lazy(|cx| Pin::new(&mut fut1).poll(cx)).await.is_pending());
801        let mut fut2 = srv.call_static(());
802        assert!(lazy(|cx| Pin::new(&mut fut2).poll(cx)).await.is_pending());
803        drop(fut2);
804        drop(fut1);
805
806        crate::time::timeout(Duration::from_secs(1), srv.shutdown())
807            .await
808            .unwrap();
809        assert_eq!(inner.count.get(), 0);
810    }
811
812    #[derive(Default)]
813    struct FailInner {
814        fail: Cell<bool>,
815        changed: ReadyNotifier,
816    }
817
818    struct FailService(Rc<FailInner>);
819
820    impl Service<(), ()> for FailService {
821        type Res = ();
822        type Error = ();
823
824        fn bind(&self, ctx: BindCtx<'_>) {
825            ctx.subscribe(&self.0.changed);
826        }
827
828        async fn ready(&self, _: Ctx<'_, Self, ()>) -> Result<(), ()> {
829            if self.0.fail.get() {
830                Err(())
831            } else {
832                future::pending().await
833            }
834        }
835
836        async fn call(&self, (): (), _: Ctx<'_, Self, ()>) -> Result<(), ()> {
837            unreachable!("FailService is never ready")
838        }
839    }
840
841    #[ntex::test]
842    async fn inner_ready_error_is_returned_to_requests() {
843        let inner = Rc::new(FailInner::default());
844        let srv = Pipeline::new(
845            (),
846            BufferService::new(2, PipelineState::new(FailService(inner.clone()))),
847        );
848        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
849
850        let r1 = spawn_call(&srv);
851        let r2 = spawn_call(&srv);
852        settle().await;
853        assert!(r1.borrow().is_none());
854
855        inner.fail.set(true);
856        inner.changed.notify();
857        crate::time::timeout(Duration::from_secs(1), srv.shutdown())
858            .await
859            .unwrap();
860        settle().await;
861
862        assert!(matches!(
863            *r1.borrow(),
864            Some(Err(BufferServiceError::Service(())))
865        ));
866        assert!(matches!(
867            *r2.borrow(),
868            Some(Err(BufferServiceError::Service(())))
869        ));
870    }
871
872    #[ntex::test]
873    async fn shared_buffer_capacity_notifies_all_roots() {
874        let inner = Inner::new(false);
875        let buffer = Rc::new(BufferService::new(
876            1,
877            PipelineState::new(TestService(inner.clone())),
878        ));
879        let first = Pipeline::new((), buffer.clone());
880        let second = Pipeline::new((), buffer);
881        first.ready().await.unwrap();
882        second.ready().await.unwrap();
883        let mut cx = Context::from_waker(std::task::Waker::noop());
884
885        let mut active = Box::pin(first.call(()));
886        assert!(active.as_mut().poll(&mut cx).is_pending());
887        let mut queued = Box::pin(second.call(()));
888        assert!(queued.as_mut().poll(&mut cx).is_pending());
889        assert!(first.poll_ready(&mut cx).is_pending());
890        assert!(second.poll_ready(&mut cx).is_pending());
891
892        drop(queued);
893        assert_eq!(first.poll_ready(&mut cx), Poll::Ready(Ok(())));
894        assert_eq!(second.poll_ready(&mut cx), Poll::Ready(Ok(())));
895        inner.set_ready();
896        assert_eq!(active.as_mut().poll(&mut cx), Poll::Ready(Ok(())));
897    }
898
899    #[ntex::test]
900    async fn notification_panics_do_not_strand_buffered_requests() {
901        let inner = Inner::new(false);
902        let buffer = Rc::new(BufferService::new(
903            1,
904            PipelineState::new(TestService(inner.clone())),
905        ));
906        let srv = Pipeline::new((), buffer.clone());
907        let panic_once = Rc::new(Cell::new(false));
908        let flag = panic_once.clone();
909        srv.on_notify(&ReadyCallback::new(move || {
910            assert!(!flag.replace(false), "notification panic");
911        }));
912        let mut cx = Context::from_waker(std::task::Waker::noop());
913        let mut active = Box::pin(srv.call(()));
914        assert!(active.as_mut().poll(&mut cx).is_pending());
915
916        panic_once.set(true);
917        let mut queued = Box::pin(srv.call(()));
918        assert!(catch_unwind(AssertUnwindSafe(|| queued.as_mut().poll(&mut cx))).is_err());
919        drop(queued);
920        assert!(buffer.shared.buf.borrow().is_empty());
921        assert_eq!(srv.poll_ready(&mut cx), Poll::Ready(Ok(())));
922
923        let mut queued = Box::pin(srv.call(()));
924        assert!(queued.as_mut().poll(&mut cx).is_pending());
925        panic_once.set(true);
926        assert!(catch_unwind(AssertUnwindSafe(|| drop(active))).is_err());
927        assert_eq!(
928            queued.as_mut().poll(&mut cx),
929            Poll::Ready(Err(BufferServiceError::RequestCanceled))
930        );
931        assert!(buffer.shared.is_drained());
932
933        inner.set_ready();
934        assert_eq!(srv.call(()).await, Ok(()));
935    }
936
937    #[ntex::test]
938    async fn shutdown_notification_panic_still_cancels_waiters() {
939        let inner = Inner::new(false);
940        let buffer = Rc::new(
941            BufferService::new(1, PipelineState::new(TestService(inner))).cancel_on_shutdown(),
942        );
943        let srv = Pipeline::new((), buffer.clone());
944        let mut cx = Context::from_waker(std::task::Waker::noop());
945        let mut active = Box::pin(srv.call(()));
946        assert!(active.as_mut().poll(&mut cx).is_pending());
947        let mut queued = Box::pin(srv.call(()));
948        assert!(queued.as_mut().poll(&mut cx).is_pending());
949        srv.on_notify(&ReadyCallback::new(|| panic!("shutdown notification")));
950
951        assert!(catch_unwind(AssertUnwindSafe(|| srv.poll_shutdown(&mut cx))).is_err());
952        assert_eq!(
953            active.as_mut().poll(&mut cx),
954            Poll::Ready(Err(BufferServiceError::RequestCanceled))
955        );
956        assert_eq!(
957            queued.as_mut().poll(&mut cx),
958            Poll::Ready(Err(BufferServiceError::RequestCanceled))
959        );
960        assert!(buffer.shared.is_drained());
961    }
962}