use super::{NegotiationConfig, body::ErrorBody, future::ErrorServiceFuture};
use bytes::Buf;
use http::{Request, Response, header};
use http_body::Body;
use std::{
marker::PhantomData,
task::{Context, Poll},
};
use tower::{BoxError, Service};
#[derive(Debug)]
pub struct ErrorService<S> {
inner: S,
config: NegotiationConfig,
poll_ready_error: Option<BoxError>,
}
impl<S: Clone> Clone for ErrorService<S> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
config: self.config.clone(),
poll_ready_error: None,
}
}
}
impl<S> ErrorService<S> {
pub(super) fn new(inner: S, config: NegotiationConfig) -> Self {
Self {
inner,
config,
poll_ready_error: None,
}
}
}
impl<S, ReqBody, ResBody> Service<Request<ReqBody>> for ErrorService<S>
where
S: Service<Request<ReqBody>, Response = Response<ResBody>>,
S::Error: Into<BoxError>,
ResBody: Body,
ResBody::Data: Buf,
ResBody::Error: Into<BoxError>,
{
type Response = Response<ErrorBody<ResBody>>;
type Error = std::convert::Infallible;
type Future = ErrorServiceFuture<S::Future, ResBody>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
match self.inner.poll_ready(cx) {
Poll::Ready(Ok(())) => Poll::Ready(Ok(())),
Poll::Ready(Err(e)) => {
self.poll_ready_error = Some(e.into());
Poll::Ready(Ok(()))
}
Poll::Pending => Poll::Pending,
}
}
fn call(&mut self, req: Request<ReqBody>) -> Self::Future {
let accept = req
.headers()
.get(header::ACCEPT)
.and_then(|v| v.to_str().ok())
.map(String::from);
let poll_ready_error = std::mem::take(&mut self.poll_ready_error);
ErrorServiceFuture {
inner: self.inner.call(req),
config: self.config.clone(),
accept,
poll_ready_error,
_marker: PhantomData,
}
}
}