1#![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)]
11pub 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 #[must_use]
27 pub fn buf_size(mut self, size: usize) -> Self {
28 self.buf_size = size;
29 self
30 }
31
32 #[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#[derive(Clone, Copy, Debug, PartialEq, Eq)]
74pub enum BufferServiceError<E> {
75 Service(E),
77 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
98pub 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 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 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 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 if self.service.poll_ready(cx, ctx.st())?.is_pending() {
179 if buffer.len() < self.size {
180 self.ready.set(false);
182 Poll::Ready(Ok(()))
183 } else {
184 log::trace!("Buffer limit exceeded");
185 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 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 loop {
210 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 }
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 let _task_guard = rx.recv().await.map_err(|_| {
253 log::trace!("Buffered service request canceled");
254 BufferServiceError::RequestCanceled
255 })?;
256
257 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 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 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 assert!(lazy(|cx| Pin::new(&mut fut).poll(cx)).await.is_pending());
733 }
734}