httproxide 0.2.0

Rusted HTTP router reverse-proxy
Documentation
use anyhow::anyhow;
use axum::handler::HandlerWithoutStateExt;
use axum::response::Redirect as AxumRedirect;
use http::uri::Uri;
use http::StatusCode;
use serde::{Deserialize, Serialize};
use tower_http::services::Redirect as TowerRedirect;

use crate::target::{IntoTarget, ResBody};

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum RedirectMode {
    Permanent,
    Temporary,
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct RedirectConfig {
    redirect_mode: RedirectMode,
    target: String,
}

pub fn simple_from_config(config: RedirectConfig) -> anyhow::Result<impl IntoTarget> {
    Ok(match config.redirect_mode {
        RedirectMode::Temporary => TowerRedirect::<ResBody>::temporary(config.target.try_into()?),
        RedirectMode::Permanent => TowerRedirect::<ResBody>::permanent(config.target.try_into()?),
    })
}

pub fn host_from_config(config: RedirectConfig) -> anyhow::Result<impl IntoTarget> {
    let uri: Uri = config.target.parse()?;
    let parts = uri.into_parts();
    let scheme = parts.scheme.ok_or(anyhow!("missing scheme"))?;
    let authority = parts.authority.ok_or(anyhow!("missing authority"))?;
    let redirect = match config.redirect_mode {
        RedirectMode::Temporary => AxumRedirect::temporary,
        RedirectMode::Permanent => AxumRedirect::permanent,
    };
    let handler = move |uri: Uri| async move {
        let mut parts = uri.into_parts();
        parts.scheme = Some(scheme);
        parts.authority = Some(authority);
        match Uri::from_parts(parts) {
            Ok(new_uri) => Ok(redirect(new_uri.to_string().as_str())),
            Err(e) => {
                log::warn!("https forward failed: {}", e);
                Err((StatusCode::BAD_REQUEST, "https forward failed".to_string()))
            }
        }
    };
    Ok(handler.into_service())
}