1#![allow(clippy::type_complexity)]
3use std::cell::{Cell, RefCell};
4use std::{collections::VecDeque, fmt, future, marker, task, 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> {
15 buf_size: usize,
16 cancel_on_shutdown: bool,
17 st: marker::PhantomData<fn(St, Req) -> Result<Res, Err>>,
18}
19
20impl<St: Clone, Req, Res, Err> Buffer<St, Req, Res, Err> {
21 #[must_use]
25 pub fn buf_size(mut self, size: usize) -> Self {
26 self.buf_size = size;
27 self
28 }
29
30 #[must_use]
34 pub fn cancel_on_shutdown(mut self) -> Self {
35 self.cancel_on_shutdown = true;
36 self
37 }
38}
39
40impl<St: Clone, Req, Res, Err> Default for Buffer<St, Req, Res, Err> {
41 fn default() -> Self {
42 Self {
43 buf_size: 16,
44 cancel_on_shutdown: false,
45 st: marker::PhantomData,
46 }
47 }
48}
49
50impl<S, St, Req, Res, Err> Middleware<S, St> for Buffer<St, Req, Res, Err>
51where
52 S: Service<St, Req, Res = Res, Error = Err> + 'static,
53 St: Clone + 'static,
54 Req: 'static,
55 Res: 'static,
56 Err: 'static,
57{
58 type Service = BufferService<St, Req, Res, Err>;
59
60 fn create(&self, _: &St, service: S) -> Self::Service {
61 let service = BufferService::new(self.buf_size, PipelineState::new(service));
62 if self.cancel_on_shutdown {
63 service.cancel_on_shutdown()
64 } else {
65 service
66 }
67 }
68}
69
70#[derive(Clone, Copy, Debug, PartialEq, Eq)]
72pub enum BufferServiceError<E> {
73 Service(E),
75 RequestCanceled,
77}
78
79impl<E> From<E> for BufferServiceError<E> {
80 fn from(err: E) -> Self {
81 BufferServiceError::Service(err)
82 }
83}
84
85impl<E: fmt::Display> fmt::Display for BufferServiceError<E> {
86 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87 match self {
88 BufferServiceError::Service(e) => fmt::Display::fmt(e, f),
89 BufferServiceError::RequestCanceled => f.write_str("buffer service request canceled"),
90 }
91 }
92}
93
94impl<E: fmt::Display + fmt::Debug> std::error::Error for BufferServiceError<E> {}
95
96pub struct BufferService<St, Req, Res, Err> {
100 size: usize,
101 ready: Cell<bool>,
102 service: PipelineState<St, Req, Res, Err>,
103 buf: RefCell<VecDeque<oneshot::Sender<oneshot::Sender<()>>>>,
104 next_call: RefCell<Option<oneshot::Receiver<()>>>,
105 cancel_on_shutdown: bool,
106 readiness: Cell<Option<task::Waker>>,
107}
108
109impl<St, Req, Res, Err> BufferService<St, Req, Res, Err>
110where
111 St: Clone + 'static,
112{
113 #[must_use]
114 pub fn new(size: usize, service: PipelineState<St, Req, Res, Err>) -> Self {
116 Self {
117 size,
118 service,
119 ready: Cell::new(false),
120 buf: RefCell::new(VecDeque::with_capacity(size)),
121 next_call: RefCell::default(),
122 cancel_on_shutdown: false,
123 readiness: Cell::new(None),
124 }
125 }
126
127 #[must_use]
128 pub fn cancel_on_shutdown(self) -> Self {
130 Self {
131 cancel_on_shutdown: true,
132 ..self
133 }
134 }
135}
136
137impl<St, Req, Res, Err> fmt::Debug for BufferService<St, Req, Res, Err> {
138 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139 f.debug_struct("BufferService")
140 .field("size", &self.size)
141 .field("cancel_on_shutdown", &self.cancel_on_shutdown)
142 .field("ready", &self.ready)
143 .field("service", &self.service)
144 .field("buf", &self.buf)
145 .field("next_call", &self.next_call)
146 .finish()
147 }
148}
149
150impl<St, Req, Res, Err> Service<St, Req> for BufferService<St, Req, Res, Err>
151where
152 St: Clone + 'static,
153 Req: 'static,
154 Res: 'static,
155 Err: 'static,
156{
157 type Res = Res;
158 type Error = BufferServiceError<Err>;
159
160 async fn ready(&self, ctx: Ctx<'_, Self, St>) -> Result<(), Self::Error> {
161 let next_call = self.next_call.borrow_mut().take();
163 if let Some(next_call) = next_call {
164 let _ = next_call.recv().await;
165 }
166
167 ctx.poll_fn(|cx| {
168 let mut buffer = self.buf.borrow_mut();
169
170 if self.service.poll_ready(cx, ctx.st())?.is_pending() {
172 if buffer.len() < self.size {
173 self.ready.set(false);
175 Poll::Ready(Ok(()))
176 } else {
177 log::trace!("Buffer limit exceeded");
178 let _ = self.readiness.take().map(task::Waker::wake);
180 Poll::Pending
181 }
182 } else {
183 while let Some(sender) = buffer.pop_front() {
184 let (next_call_tx, next_call_rx) = oneshot::channel();
185 if sender.send(next_call_tx).is_err() || next_call_rx.poll_recv(cx).is_ready() {
186 continue;
188 }
189 self.next_call.borrow_mut().replace(next_call_rx);
190 self.ready.set(false);
191 return Poll::Ready(Ok(()));
192 }
193
194 self.ready.set(true);
195 Poll::Ready(Ok(()))
196 }
197 })
198 .await
199 }
200
201 async fn shutdown(&self, ctx: Ctx<'_, Self, St>) {
202 let next_call = self.next_call.borrow_mut().take();
204 if let Some(next_call) = next_call {
205 let _ = next_call.recv().await;
206 }
207
208 future::poll_fn(|cx| {
209 let mut buffer = self.buf.borrow_mut();
210 if self.cancel_on_shutdown {
211 buffer.clear();
212 }
213
214 if !buffer.is_empty() {
215 if task::ready!(self.service.poll_ready(cx, ctx.st())).is_err() {
216 log::error!("Buffered inner service failed while buffer flushing on shutdown");
217 return Poll::Ready(());
218 }
219
220 while let Some(sender) = buffer.pop_front() {
221 let (next_call_tx, next_call_rx) = oneshot::channel();
222 if sender.send(next_call_tx).is_err() || next_call_rx.poll_recv(cx).is_ready() {
223 continue;
225 }
226 self.next_call.borrow_mut().replace(next_call_rx);
227 if buffer.is_empty() {
228 break;
229 }
230 return Poll::Pending;
231 }
232 }
233 Poll::Ready(())
234 })
235 .await;
236
237 self.service.shutdown(ctx.st()).await;
238 }
239
240 async fn call(&self, req: Req, ctx: Ctx<'_, Self, St>) -> Result<Res, Self::Error> {
241 if self.ready.get() {
242 self.ready.set(false);
243 Ok(self.service.call_nowait(req, ctx.st()).await?)
244 } else {
245 let (tx, rx) = oneshot::channel();
246 self.buf.borrow_mut().push_back(tx);
247
248 let _task_guard = rx.recv().await.map_err(|_| {
250 log::trace!("Buffered service request canceled");
251 BufferServiceError::RequestCanceled
252 })?;
253
254 Ok(self.service.call(req, ctx.st()).await?)
256 }
257 }
258}
259
260#[cfg(test)]
261mod tests {
262 #![allow(clippy::unused_async_trait_impl)]
263 use ntex_service::{Pipeline, apply, fn_factory};
264 use std::{cell::RefCell, rc::Rc, time::Duration};
265
266 use super::*;
267 use crate::{future::lazy, task::LocalWaker};
268
269 #[derive(Debug, Clone)]
270 struct TestService(Rc<Inner>);
271
272 #[derive(Debug)]
273 struct Inner {
274 ready: Cell<bool>,
275 waker: LocalWaker,
276 count: Cell<usize>,
277 }
278
279 impl Service<(), ()> for TestService {
280 type Res = ();
281 type Error = ();
282
283 async fn ready(&self, ctx: Ctx<'_, Self, ()>) -> Result<(), Self::Error> {
284 ctx.poll_fn(|cx| {
285 self.0.waker.register(cx.waker());
286 if self.0.ready.get() {
287 Poll::Ready(Ok(()))
288 } else {
289 Poll::Pending
290 }
291 })
292 .await
293 }
294
295 async fn call(&self, _r: (), _: Ctx<'_, Self, ()>) -> Result<(), ()> {
296 self.0.ready.set(false);
297 self.0.count.set(self.0.count.get() + 1);
298 Ok(())
299 }
300 }
301
302 struct PendingService {
303 inner: Rc<PendingInner>,
304 release: RefCell<Option<oneshot::Receiver<()>>>,
305 }
306
307 struct PendingInner {
308 started: Cell<bool>,
309 completed: Cell<bool>,
310 shutdown: Cell<bool>,
311 }
312
313 impl Service<(), ()> for PendingService {
314 type Res = ();
315 type Error = ();
316
317 async fn call(&self, (): (), _: Ctx<'_, Self, ()>) -> Result<(), ()> {
318 self.inner.started.set(true);
319 let release = self.release.borrow_mut().take().unwrap();
320 release.recv().await.unwrap();
321 self.inner.completed.set(true);
322 Ok(())
323 }
324
325 async fn shutdown(&self, _: Ctx<'_, Self, ()>) {
326 self.inner.shutdown.set(true);
327 }
328 }
329
330 #[ntex::test]
331 async fn test_service() {
332 let inner = Rc::new(Inner {
333 ready: Cell::new(false),
334 waker: LocalWaker::default(),
335 count: Cell::new(0),
336 });
337
338 let svc = BufferService::new(2, PipelineState::new(TestService(inner.clone())));
339 assert!(format!("{svc:?}").contains("BufferService"));
340
341 let srv = Pipeline::new((), svc);
342 assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
343
344 let srv1 = srv.bind();
345 ntex::rt::spawn(async move {
346 let _ = srv1.call(()).await;
347 });
348 crate::time::sleep(Duration::from_millis(25)).await;
349 assert_eq!(inner.count.get(), 0);
350 assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
351
352 let srv1 = srv.bind();
353 ntex::rt::spawn(async move {
354 let _ = srv1.call(()).await;
355 });
356 crate::time::sleep(Duration::from_millis(25)).await;
357 assert_eq!(inner.count.get(), 0);
358 assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Pending);
359
360 inner.ready.set(true);
361 inner.waker.wake();
362 assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
363
364 crate::time::sleep(Duration::from_millis(25)).await;
365 assert_eq!(inner.count.get(), 1);
366
367 inner.ready.set(true);
368 inner.waker.wake();
369 assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
370
371 crate::time::sleep(Duration::from_millis(25)).await;
372 assert_eq!(inner.count.get(), 2);
373
374 let inner = Rc::new(Inner {
375 ready: Cell::new(true),
376 waker: LocalWaker::default(),
377 count: Cell::new(0),
378 });
379
380 let srv = Pipeline::new(
381 (),
382 BufferService::new(2, PipelineState::new(TestService(inner.clone()))),
383 );
384 assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
385
386 let _ = srv.call(()).await;
387 assert_eq!(inner.count.get(), 1);
388 assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
389 assert!(lazy(|cx| srv.poll_shutdown(cx)).await.is_ready());
390
391 let err = BufferServiceError::from("test");
392 assert!(format!("{err}").contains("test"));
393 assert!(format!("{:?}", Buffer::<(), (), (), ()>::default()).contains("Buffer"));
394 }
395
396 #[ntex::test]
397 #[allow(clippy::redundant_clone)]
398 async fn test_middleware() {
399 let inner = Rc::new(Inner {
400 ready: Cell::new(false),
401 waker: LocalWaker::default(),
402 count: Cell::new(0),
403 });
404 let inner2 = inner.clone();
405
406 let srv = apply(
407 Buffer::default().buf_size(2),
408 fn_factory(async move |(): &()| Ok::<_, ()>(TestService(inner2.clone()))),
409 );
410
411 let srv = srv.pipeline(()).await.unwrap();
412 assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
413
414 let srv1 = srv.bind();
415 ntex::rt::spawn(async move {
416 let _ = srv1.call(()).await;
417 });
418 crate::time::sleep(Duration::from_millis(25)).await;
419 assert_eq!(inner.count.get(), 0);
420 assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
421
422 let srv1 = srv.bind();
423 ntex::rt::spawn(async move {
424 let _ = srv1.call(()).await;
425 });
426 crate::time::sleep(Duration::from_millis(25)).await;
427 assert_eq!(inner.count.get(), 0);
428 assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Pending);
429
430 inner.ready.set(true);
431 inner.waker.wake();
432 assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
433
434 crate::time::sleep(Duration::from_millis(25)).await;
435 assert_eq!(inner.count.get(), 1);
436
437 inner.ready.set(true);
438 inner.waker.wake();
439 assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
440
441 crate::time::sleep(Duration::from_millis(25)).await;
442 assert_eq!(inner.count.get(), 2);
443 }
444
445 #[ntex::test]
446 #[allow(clippy::redundant_clone)]
447 async fn test_middleware2() {
448 let inner = Rc::new(Inner {
449 ready: Cell::new(false),
450 waker: LocalWaker::default(),
451 count: Cell::new(0),
452 });
453 let inner2 = inner.clone();
454
455 let srv = apply(
456 Buffer::default().buf_size(2),
457 fn_factory(async move |(): &()| Ok::<_, ()>(TestService(inner2.clone()))),
458 );
459
460 let srv = srv.pipeline(()).await.unwrap();
461 assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
462
463 let srv1 = srv.bind();
464 ntex::rt::spawn(async move {
465 let _ = srv1.call(()).await;
466 });
467 crate::time::sleep(Duration::from_millis(25)).await;
468 assert_eq!(inner.count.get(), 0);
469 assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
470
471 let srv1 = srv.bind();
472 ntex::rt::spawn(async move {
473 let _ = srv1.call(()).await;
474 });
475 crate::time::sleep(Duration::from_millis(25)).await;
476 assert_eq!(inner.count.get(), 0);
477 assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Pending);
478
479 inner.ready.set(true);
480 inner.waker.wake();
481 assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
482
483 crate::time::sleep(Duration::from_millis(25)).await;
484 assert_eq!(inner.count.get(), 1);
485
486 inner.ready.set(true);
487 inner.waker.wake();
488 assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
489
490 crate::time::sleep(Duration::from_millis(25)).await;
491 assert_eq!(inner.count.get(), 2);
492 }
493
494 #[ntex::test]
495 async fn middleware_cancels_buffered_requests_on_shutdown() {
496 let inner = Rc::new(Inner {
497 ready: Cell::new(false),
498 waker: LocalWaker::default(),
499 count: Cell::new(0),
500 });
501 let inner2 = inner.clone();
502
503 let srv = apply(
504 Buffer::default().buf_size(1).cancel_on_shutdown(),
505 fn_factory(async move |(): &()| Ok::<_, ()>(TestService(inner2.clone()))),
506 )
507 .pipeline(())
508 .await
509 .unwrap();
510
511 assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
512
513 let canceled = Rc::new(Cell::new(false));
514 let canceled2 = canceled.clone();
515 let srv2 = srv.bind();
516 ntex::rt::spawn(async move {
517 canceled2.set(matches!(
518 srv2.call(()).await,
519 Err(BufferServiceError::RequestCanceled)
520 ));
521 });
522
523 crate::time::sleep(Duration::from_millis(25)).await;
524 srv.shutdown().await;
525 crate::time::sleep(Duration::from_millis(25)).await;
526
527 assert!(canceled.get());
528 assert_eq!(inner.count.get(), 0);
529 }
530
531 #[ntex::test]
532 async fn shutdown_with_in_flight_request() {
533 let inner = Rc::new(PendingInner {
534 started: Cell::new(false),
535 completed: Cell::new(false),
536 shutdown: Cell::new(false),
537 });
538 let (release_tx, release_rx) = oneshot::channel();
539 let service = PendingService {
540 inner: inner.clone(),
541 release: RefCell::new(Some(release_rx)),
542 };
543 let srv = Pipeline::new((), BufferService::new(1, PipelineState::new(service)));
544
545 assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
546
547 let srv2 = srv.bind();
548 ntex::rt::spawn(async move {
549 srv2.call(()).await.unwrap();
550 });
551 crate::time::sleep(Duration::from_millis(25)).await;
552
553 assert!(inner.started.get());
554 assert!(!inner.completed.get());
555 assert!(lazy(|cx| srv.poll_shutdown(cx)).await.is_ready());
556 assert!(inner.shutdown.get());
557 assert!(!inner.completed.get());
558
559 release_tx.send(()).unwrap();
560 crate::time::sleep(Duration::from_millis(25)).await;
561
562 assert!(inner.completed.get());
563 }
564}