diff --git a/src/types/request.rs b/src/types/request.rs index 85fabd4..9f81145 100644 --- a/src/types/request.rs +++ b/src/types/request.rs @@ -4,6 +4,7 @@ use std::convert::TryInto; use std::{ collections::HashMap, convert::TryFrom, + path::Path, }; use anyhow::*; use percent_encoding::percent_decode_str; @@ -25,6 +26,8 @@ pub struct Request { manager: UserManager, #[cfg(feature = "scgi_srv")] headers: HashMap, + #[cfg(feature = "scgi_srv")] + script_path: Option, } impl Request { @@ -37,7 +40,7 @@ impl Request { manager: UserManager, ) -> Result { #[cfg(feature = "scgi_srv")] - let (mut uri, certificate) = ( + let (mut uri, certificate, script_path) = ( URIReference::try_from( format!( "{}{}", @@ -58,6 +61,9 @@ impl Request { .try_into() .expect("Received certificate fingerprint of invalid lenght from upstream") }), + headers.get("SCRIPT_PATH") + .or_else(|| headers.get("SCRIPT_NAME")) + .cloned() ); uri.normalize(); @@ -83,6 +89,8 @@ impl Request { trailing_segments: None, #[cfg(feature = "scgi_srv")] headers, + #[cfg(feature = "scgi_srv")] + script_path, #[cfg(feature="user_management")] manager, }) @@ -192,6 +200,40 @@ impl Request { pub fn user_manager(&self) -> &UserManager { &self.manager } + + /// Attempt to rewrite an absolute URL against the base path of the SCGI script + /// + /// When writing an SCGI script, you cannot assume that your script is mounted on the + /// base path of "/". For example, a request to the gemini server for "/app/path" + /// might be received by your script as "/path" if your script is mounted on "/app/". + /// In this situation, if you linked to "/", you would be sending users to "/", which + /// is not handled by your app, instead of "/app/", where you probably intended to + /// send the user. + /// + /// This method attempts to infer where the script is mounted, and rewrite an absolute + /// url relative to that. For example, if the application was mounted on "/app/", and + /// you passed "/path", the result would be "/app/path". + /// + /// When running in `gemini_srv` mode, the application is always mounted at the base + /// path, so this will always return the path unchanged. + /// + /// Not all SCGI clients will correctly report the application's path, so this may + /// fail if unable to infer the correct path. If this is the case, None will be + /// returned. Currently, the SCGI headers checked are: + /// + /// * `SCRIPT_PATH` (Used by mollybrown) + /// * `SCRIPT_NAME` (Used by GLV-1.12556) + pub fn rewrite_path(&self, path: impl AsRef) -> Option { + #[cfg(feature = "scgi_srv")] { + self.script_path.as_ref().map(|base| { + let base: &Path = base.as_ref(); + base.join(path.as_ref()).display().to_string() + }) + } + #[cfg(feature = "gemini_srv")] { + Some(path.to_string()) + } + } } impl ops::Deref for Request {