diff options
author | metamuffin <metamuffin@disroot.org> | 2024-11-11 14:54:15 +0100 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2024-11-11 14:54:15 +0100 |
commit | ef1ed0beb17649e7c65eee7081c605282bae8111 (patch) | |
tree | 7ac8d4bfda1a77e2e0ea5bb69f60bf16d2bf7fec | |
parent | a559aaf12f9b57ef1a1d440011e874e2c8d4f954 (diff) | |
download | gnix-ef1ed0beb17649e7c65eee7081c605282bae8111.tar gnix-ef1ed0beb17649e7c65eee7081c605282bae8111.tar.bz2 gnix-ef1ed0beb17649e7c65eee7081c605282bae8111.tar.zst |
paths rest capture group
-rw-r--r-- | readme.md | 22 | ||||
-rw-r--r-- | src/modules/auth/openid.rs | 1 | ||||
-rw-r--r-- | src/modules/paths.rs | 30 |
3 files changed, 40 insertions, 13 deletions
@@ -58,7 +58,8 @@ handler: !hosts ## Reference The configuration uses YAML formatting. When the configuration file is changed, -it will automatically be loaded and applied if valid. +it will automatically be loaded and applied if valid. Configuration errors are +reported in stderr and are only fatal at startup. - **section `http`** - Optional section. Omit to disable unencrypted http. @@ -86,7 +87,7 @@ it will automatically be loaded and applied if valid. connections. excess connections are rejected. Default: 256 - **section `handler`** - - A module to handle all requests. Usually an instance of `hosts`. + - A module to handle all requests. Usually an instance of `hosts` or `paths`. - `watch_config`: boolean if to watch the configuration file for changes and apply them accordingly. Default: true (Note: This will watch the entire parent @@ -103,14 +104,17 @@ themselves; in that case the request is passed on. - Takes a map from hostname (string) to handler (module) - **module `paths`** - - Routes requests by matching the path against regexes. - - Takes a map from a global regex that is applied to the path (string) to - handler (module) + - Routes requests by matching the path against regexes. The first matching + regex determines the module to handle this request. If the regex has an + unnamed capture group, its value will become the downstream request URI. + - Takes a map from regex (string) to handler (module) OR a sequence of such + maps. The priority is assigned first by position in the sequence then + alphabetically in the map. - **module `proxy`** - Forwards the request as-is to some other server. the `x-real-ip` header is - injected into the request. Connection upgrades are handled by direct - forwarding of network traffic. + added to the request. Connection upgrades are handled by direct forwarding + of network traffic. - `backend`: socket address (string) to the backend server - **module `files`** @@ -127,8 +131,8 @@ themselves; in that case the request is passed on. - **module `file`** - Replies with static content. - - `path`: file path to the content. _This will be loaded into memory once!_ - (string) + - `path`: file path to the content. _This will be loaded into memory at + startup!_ (string) - `content`: inline declaration of the content. (string) - `type`: type of content reflected in `content-type` header. (string) diff --git a/src/modules/auth/openid.rs b/src/modules/auth/openid.rs index 7253ba8..1e5a063 100644 --- a/src/modules/auth/openid.rs +++ b/src/modules/auth/openid.rs @@ -43,6 +43,7 @@ pub struct OpenIDAuth { authorize_endpoint: String, token_endpoint: String, scope: String, + #[allow(unused)] next: DynNode, } diff --git a/src/modules/paths.rs b/src/modules/paths.rs index b994d48..8c6d0c5 100644 --- a/src/modules/paths.rs +++ b/src/modules/paths.rs @@ -1,7 +1,8 @@ use super::{Node, NodeContext, NodeKind, NodeRequest, NodeResponse}; use crate::{config::DynNode, error::ServiceError}; use futures::Future; -use regex::RegexSet; +use http::Uri; +use regex::{Regex, RegexSet}; use serde_yaml::Value; use std::{collections::BTreeMap, pin::Pin, sync::Arc}; @@ -9,6 +10,7 @@ pub struct PathsKind; struct Paths { matcher: RegexSet, + extractors: Vec<Regex>, handlers: Vec<DynNode>, } @@ -23,20 +25,27 @@ impl NodeKind for PathsKind { let mut handlers = Vec::new(); let mut patterns = Vec::new(); + let mut extractors = Vec::new(); for (k, v) in routes { + let pattern = format!("^{k}$"); handlers.push(v); - patterns.push(format!("^{}$", k)); + extractors.push(Regex::new(&pattern)?); + patterns.push(pattern); } let matcher = RegexSet::new(&patterns)?; - Ok(Arc::new(Paths { handlers, matcher })) + Ok(Arc::new(Paths { + handlers, + matcher, + extractors, + })) } } impl Node for Paths { fn handle<'a>( &'a self, context: &'a mut NodeContext, - request: NodeRequest, + mut request: NodeRequest, ) -> Pin<Box<dyn Future<Output = Result<NodeResponse, ServiceError>> + Send + Sync + 'a>> { Box::pin(async move { let path = request.uri().path(); @@ -48,6 +57,19 @@ impl Node for Paths { .next() .ok_or(ServiceError::UnknownPath)?; + let caps = self.extractors[index] + .captures(path) + .ok_or(ServiceError::Other)?; + + if let Some(rest) = caps.get(1) { + let mut parts = http::uri::Parts::default(); + parts.scheme = request.uri().scheme().cloned(); + parts.authority = request.uri().authority().cloned(); + parts.path_and_query = + Some(rest.as_str().parse().map_err(|_| ServiceError::Other)?); + *request.uri_mut() = Uri::from_parts(parts).map_err(|_| ServiceError::InvalidUri)? + } + self.handlers[index].handle(context, request).await }) } |