summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2024-11-11 14:54:15 +0100
committermetamuffin <metamuffin@disroot.org>2024-11-11 14:54:15 +0100
commitef1ed0beb17649e7c65eee7081c605282bae8111 (patch)
tree7ac8d4bfda1a77e2e0ea5bb69f60bf16d2bf7fec
parenta559aaf12f9b57ef1a1d440011e874e2c8d4f954 (diff)
downloadgnix-ef1ed0beb17649e7c65eee7081c605282bae8111.tar
gnix-ef1ed0beb17649e7c65eee7081c605282bae8111.tar.bz2
gnix-ef1ed0beb17649e7c65eee7081c605282bae8111.tar.zst
paths rest capture group
-rw-r--r--readme.md22
-rw-r--r--src/modules/auth/openid.rs1
-rw-r--r--src/modules/paths.rs30
3 files changed, 40 insertions, 13 deletions
diff --git a/readme.md b/readme.md
index 4817493..53f54f5 100644
--- a/readme.md
+++ b/readme.md
@@ -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
})
}