use std::convert::Infallible; use std::future::Future; use std::marker::PhantomData; use std::pin::Pin; use std::task::{Context, Poll}; use std::time::Duration; use actix_rt::time::{delay_until, Delay, Instant}; use actix_service::{Service, ServiceFactory}; use futures::future::{ok, Ready}; use super::time::{LowResTime, LowResTimeService}; pub struct KeepAlive { f: F, ka: Duration, time: LowResTime, _t: PhantomData<(R, E)>, } impl KeepAlive where F: Fn() -> E + Clone, { pub fn new(ka: Duration, time: LowResTime, f: F) -> Self { KeepAlive { f, ka, time, _t: PhantomData, } } } impl Clone for KeepAlive where F: Clone, { fn clone(&self) -> Self { KeepAlive { f: self.f.clone(), ka: self.ka, time: self.time.clone(), _t: PhantomData, } } } impl ServiceFactory for KeepAlive where F: Fn() -> E + Clone, { type Request = R; type Response = R; type Error = E; type InitError = Infallible; type Config = (); type Service = KeepAliveService; type Future = Ready>; fn new_service(&self, _: ()) -> Self::Future { ok(KeepAliveService::new( self.ka, self.time.timer(), self.f.clone(), )) } } pub struct KeepAliveService { f: F, ka: Duration, time: LowResTimeService, delay: Delay, expire: Instant, _t: PhantomData<(R, E)>, } impl KeepAliveService where F: Fn() -> E, { pub fn new(ka: Duration, time: LowResTimeService, f: F) -> Self { let expire = Instant::from_std(time.now() + ka); KeepAliveService { f, ka, time, expire, delay: delay_until(expire), _t: PhantomData, } } } impl Service for KeepAliveService where F: Fn() -> E, { type Request = R; type Response = R; type Error = E; type Future = Ready>; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { match Pin::new(&mut self.delay).poll(cx) { Poll::Ready(_) => { let now = Instant::from_std(self.time.now()); if self.expire <= now { Poll::Ready(Err((self.f)())) } else { self.delay.reset(self.expire); let _ = Pin::new(&mut self.delay).poll(cx); Poll::Ready(Ok(())) } } Poll::Pending => Poll::Ready(Ok(())), } } fn call(&mut self, req: R) -> Self::Future { self.expire = Instant::from_std(self.time.now() + self.ka); ok(req) } }