httproxide 0.2.0

Rusted HTTP router reverse-proxy
Documentation
use std::convert::Infallible;
use std::pin::Pin;
use std::task::{Context, Poll};

use axum::body::BoxBody;
use bytes::Bytes;
use http::{HeaderMap, Request, Response};
use http_body::Body;
use tower::layer::layer_fn;
use tower::{Layer, Service, ServiceExt};

use crate::layer::ApplyLayer;
use crate::target::{IntoTarget, ReqBody, Target};

#[derive(Default)]
pub enum AutomaticBody {
    #[default]
    Automatic,
    Boxed(BoxBody),
}

impl Body for AutomaticBody {
    type Data = Bytes;
    type Error = axum::Error;
    fn poll_data(
        self: Pin<&mut Self>,
        _: &mut Context,
    ) -> Poll<Option<Result<Self::Data, Self::Error>>> {
        panic!()
    }
    fn poll_trailers(
        self: Pin<&mut Self>,
        _: &mut Context,
    ) -> Poll<Result<Option<HeaderMap>, Self::Error>> {
        panic!()
    }
}

pub fn to_automatic(inner: impl IntoTarget) -> Target<ReqBody, AutomaticBody> {
    inner
        .into_target()
        .map_response(|resp| {
            let (parts, body) = resp.into_parts();
            Response::from_parts(parts, AutomaticBody::Boxed(body))
        })
        .boxed_clone()
}

pub fn from_automatic(inner: Target<ReqBody, AutomaticBody>) -> impl IntoTarget {
    inner.map_response(|resp| {
        let (parts, body) = resp.into_parts();
        let new_body = match body {
            AutomaticBody::Automatic => {
                let mut text = parts.status.as_u16().to_string();
                if let Some(reason) = parts.status.canonical_reason() {
                    text = format!("{} {}", text, reason);
                }
                axum::body::boxed(hyper::Body::from(text))
            }
            AutomaticBody::Boxed(b) => b,
        };
        Response::from_parts(parts, new_body)
    })
}

pub fn add_automatic_body<L, S>(inner: L) -> impl ApplyLayer
where
    L: Layer<Target<ReqBody, AutomaticBody>, Service = S>,
    S: Service<Request<ReqBody>, Response = Response<AutomaticBody>, Error = Infallible>
        + Clone
        + Sized
        + Send
        + 'static,
    S::Future: Send + 'static,
{
    layer_fn(move |s| from_automatic(inner.layer(to_automatic(s)).boxed_clone()))
}