httproxide 0.2.0

Rusted HTTP router reverse-proxy
Documentation
use std::convert::Infallible;

use axum::error_handling::HandleError;
use bytes::Bytes;
use http::{Request, Response, StatusCode};
use tower::util::BoxCloneService;
use tower::{Service, ServiceExt};
use tower_http::map_response_body::MapResponseBody;

use crate::config::TargetConfig;
use crate::layer::layers_config;
use crate::service::service_config;

pub type ReqBody = axum::body::BoxBody;
pub type ResBody = axum::body::BoxBody;
pub type Target<A = ReqBody, B = ResBody> = BoxCloneService<Request<A>, Response<B>, Infallible>;

async fn handle_error(err: impl Into<anyhow::Error>) -> (StatusCode, String) {
    let err = err.into();
    log::warn!("unhandled error: {:?}", err);
    (
        StatusCode::INTERNAL_SERVER_ERROR,
        format!("Something went wrong"),
    )
}

pub trait IntoTarget {
    fn into_target(self) -> Target;
}

impl<S, B> IntoTarget for S
where
    S: Service<Request<ReqBody>, Response = Response<B>> + Clone + Sized + Send + 'static,
    B: http_body::Body<Data = Bytes> + Send + 'static,
    B::Error: Into<axum::BoxError>,
    S::Error: Into<anyhow::Error> + Send,
    S::Future: Send + 'static,
{
    fn into_target(self) -> Target {
        let s = self;
        let s = MapResponseBody::new(s, axum::body::boxed);
        let s = HandleError::<_, _, ()>::new(s, handle_error);
        ServiceExt::<Request<ReqBody>>::boxed_clone(s)
    }
}

pub fn target_config(cfg: TargetConfig) -> anyhow::Result<Target> {
    let mut service = service_config(cfg.service)?;
    service = layers_config(service, cfg.layers)?;
    Ok(service)
}