use super::ip_limiter::{ConnectionPermit, ProvideIpLimiter, SystemIpLimiter};
use crate::tokio_net::nodelay_keepalive;
use axum::extract::Request;
use axum_server::accept::Accept;
use std::future::Future;
use std::io::{self, ErrorKind};
use std::marker::PhantomData;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::io::{AsyncRead, AsyncWrite};
use tokio::net::TcpStream;
use tokio::sync::mpsc;
use tower::Service;
#[derive(Clone, Copy, Debug, Default)]
pub struct AxumServerAcceptor<S, I, P = SystemIpLimiter>(I, PhantomData<S>, P);
impl<S, I> AxumServerAcceptor<S, I, SystemIpLimiter> {
pub fn new(inner: I) -> Self {
Self::new_with(inner, SystemIpLimiter)
}
}
impl<S, I, P: ProvideIpLimiter> AxumServerAcceptor<S, I, P> {
pub fn new_with(inner: I, provide: P) -> Self {
Self(inner, PhantomData, provide)
}
}
#[pin_project::pin_project(project = FutureOrImmediateProj)]
pub enum FutureOrImmediate<F: Future> {
Future(#[pin] F),
Immediate(Option<F::Output>),
}
impl<F: Future> Future for FutureOrImmediate<F> {
type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
match this {
FutureOrImmediateProj::Future(future) => future.poll(cx),
FutureOrImmediateProj::Immediate(immediate) => Poll::Ready(
immediate
.take()
.expect("FutureOrImmediate polled after returned immediate"),
),
}
}
}
impl<
S,
P: ProvideIpLimiter,
I: Accept<KillSwitchStream<TcpStream, P>, AddExtension<S, KillSwitch>>,
> axum_server::accept::Accept<TcpStream, S> for AxumServerAcceptor<S, I, P>
{
type Future = FutureOrImmediate<I::Future>;
type Service =
<I as Accept<KillSwitchStream<TcpStream, P>, AddExtension<S, KillSwitch>>>::Service;
type Stream =
<I as Accept<KillSwitchStream<TcpStream, P>, AddExtension<S, KillSwitch>>>::Stream;
fn accept(&self, stream: TcpStream, service: S) -> FutureOrImmediate<I::Future> {
let Some(_permit) = stream
.peer_addr()
.ok()
.map(|s| s.ip())
.and_then(|ip| ConnectionPermit::new_with(ip, "TCP connection", self.2.clone()))
else {
return FutureOrImmediate::Immediate(Some(Err(io::Error::from(
ErrorKind::ConnectionRefused,
))));
};
nodelay_keepalive(&stream, 10, 2);
let (kill, killed) = mpsc::channel::<()>(1);
FutureOrImmediate::Future(self.0.accept(
KillSwitchStream {
stream,
killed,
_permit,
},
AddExtension {
service,
value: KillSwitch { kill },
},
))
}
}
#[derive(Clone)]
pub struct KillSwitch {
kill: mpsc::Sender<()>,
}
impl KillSwitch {
pub fn kill(&self) {
let _ = self.kill.try_send(());
}
}
#[derive(Clone)]
pub struct AddExtension<S, T> {
pub(crate) service: S,
pub(crate) value: T,
}
impl<ResBody, S, T> Service<Request<ResBody>> for AddExtension<S, T>
where
S: Service<Request<ResBody>>,
T: Clone + Send + Sync + 'static,
{
type Error = S::Error;
type Future = S::Future;
type Response = S::Response;
#[inline]
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.service.poll_ready(cx)
}
fn call(&mut self, mut req: Request<ResBody>) -> Self::Future {
req.extensions_mut().insert(self.value.clone());
self.service.call(req)
}
}
#[pin_project::pin_project]
pub struct KillSwitchStream<S, P: ProvideIpLimiter = SystemIpLimiter> {
#[pin]
stream: S,
#[pin]
killed: mpsc::Receiver<()>,
_permit: ConnectionPermit<P>,
}
#[inline(always)]
fn check_killed(mut killed: Pin<&mut mpsc::Receiver<()>>, cx: &mut Context<'_>) -> io::Result<()> {
if !killed.is_closed() && matches!(killed.poll_recv(cx), Poll::Ready(Some(_))) {
Err(io::Error::from(io::ErrorKind::ConnectionAborted))
} else {
Ok(())
}
}
impl<S: AsyncRead, P: ProvideIpLimiter> AsyncRead for KillSwitchStream<S, P> {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut tokio::io::ReadBuf<'_>,
) -> Poll<std::io::Result<()>> {
let this = self.project();
check_killed(this.killed, cx)?;
this.stream.poll_read(cx, buf)
}
}
impl<S: AsyncWrite, P: ProvideIpLimiter> AsyncWrite for KillSwitchStream<S, P> {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<std::io::Result<usize>> {
let this = self.project();
this.stream.poll_write(cx, buf)
}
fn is_write_vectored(&self) -> bool {
self.stream.is_write_vectored()
}
fn poll_write_vectored(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &[std::io::IoSlice<'_>],
) -> Poll<std::io::Result<usize>> {
let this = self.project();
this.stream.poll_write_vectored(cx, bufs)
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
let this = self.project();
this.stream.poll_flush(cx)
}
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
self.project().stream.poll_shutdown(cx)
}
}