/* This file is part of gnix (https://codeberg.org/metamuffin/gnix) which is licensed under the GNU Affero General Public License (version 3); see /COPYING. Copyright (C) 2025 metamuffin */ use super::{Node, NodeContext, NodeKind, NodeRequest, NodeResponse}; use crate::{config::DynNode, error::ServiceError}; use anyhow::Result; use futures::Future; use http::{ uri::{Authority, Scheme}, HeaderValue, Response, StatusCode, Uri, }; use http_body_util::BodyExt; use serde::Deserialize; use std::{pin::Pin, str::FromStr, sync::Arc}; pub struct UpgradeInsecureKind; #[derive(Deserialize)] pub struct UpgradeInsecure([DynNode; 1]); impl NodeKind for UpgradeInsecureKind { fn name(&self) -> &'static str { "upgrade_insecure" } fn instanciate(&self, config: serde_yml::Value) -> Result> { Ok(Arc::new(serde_yml::from_value::(config)?)) } } impl Node for UpgradeInsecure { fn handle<'a>( &'a self, context: &'a mut NodeContext, request: NodeRequest, ) -> Pin> + Send + Sync + 'a>> { Box::pin(async move { if request.headers().contains_key("upgrade-insecure-requests") && !context.secure { let mut parts = http::uri::Parts::default(); parts.scheme = Some(Scheme::HTTPS); parts.authority = Some( Authority::from_str( request .headers() .get("host") .ok_or(ServiceError::NoHost)? .to_str() .map_err(|_| ServiceError::InvalidUri)?, ) .map_err(|_| ServiceError::InvalidUri)?, ); parts.path_and_query = request.uri().path_and_query().cloned(); let uri = Uri::from_parts(parts).map_err(|_| ServiceError::InvalidUri)?; let mut resp = Response::new("".to_string()).map(|b| b.map_err(|e| match e {}).boxed()); *resp.status_mut() = StatusCode::MOVED_PERMANENTLY; resp.headers_mut().insert( "Location", HeaderValue::from_str(&uri.to_string()) .map_err(|_| ServiceError::InvalidUri)?, ); resp.headers_mut().insert( "Vary", HeaderValue::from_static("Upgrade-Insecure-Requests"), ); return Ok(resp); } self.0[0].handle(context, request).await }) } }