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())
}