-
-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
useful for http services to turn request headers into typed extension config values, e.g. proxy config, tls config, http config, emulation config, etc...
- Loading branch information
glendc
committed
Nov 7, 2023
1 parent
2245492
commit 8a19834
Showing
9 changed files
with
193 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
rama/src/net/http/headers.rs → rama/src/net/http/header_value.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
use http::{ | ||
use crate::net::http::{ | ||
header::{AsHeaderName, GetAll}, | ||
HeaderValue, Request, Response, | ||
}; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,7 @@ | ||
mod headers; | ||
pub use headers::HeaderValueGetter; | ||
mod header_value; | ||
pub use header_value::HeaderValueGetter; | ||
|
||
pub use http::{ | ||
header, request, response, HeaderMap, HeaderName, HeaderValue, Method, Request, Response, | ||
StatusCode, | ||
}; |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
use std::marker::PhantomData; | ||
|
||
use serde::de::DeserializeOwned; | ||
|
||
use crate::{ | ||
net::http::{HeaderValueGetter, Request}, | ||
service::{BoxError, Layer, Service}, | ||
}; | ||
|
||
#[derive(Debug)] | ||
pub struct HeaderConfigService<T, S> { | ||
inner: S, | ||
key: String, | ||
_marker: PhantomData<T>, | ||
} | ||
|
||
impl<T, S> HeaderConfigService<T, S> { | ||
pub fn new(inner: S, key: String) -> Self { | ||
Self { | ||
inner, | ||
key, | ||
_marker: PhantomData, | ||
} | ||
} | ||
} | ||
|
||
impl<T, S> Clone for HeaderConfigService<T, S> | ||
where | ||
S: Clone, | ||
{ | ||
fn clone(&self) -> Self { | ||
Self { | ||
inner: self.inner.clone(), | ||
key: self.key.clone(), | ||
_marker: PhantomData, | ||
} | ||
} | ||
} | ||
|
||
impl<T, S, Body, E> Service<Request<Body>> for HeaderConfigService<T, S> | ||
where | ||
S: Service<Request<Body>, Error = E>, | ||
T: DeserializeOwned + Send + Sync + 'static, | ||
E: Into<BoxError>, | ||
{ | ||
type Response = S::Response; | ||
type Error = BoxError; | ||
|
||
async fn call(&mut self, mut request: Request<Body>) -> Result<Self::Response, Self::Error> { | ||
let value = request | ||
.header_value(&self.key) | ||
.ok_or(HeaderMissingErr(self.key.clone()))? | ||
.to_str()?; | ||
let config = serde_urlencoded::from_str::<T>(value)?; | ||
request.extensions_mut().insert(config); | ||
self.inner.call(request).await.map_err(Into::into) | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
struct HeaderMissingErr(String); | ||
|
||
impl std::fmt::Display for HeaderMissingErr { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
write!(f, "`{}` header is missing", self.0) | ||
} | ||
} | ||
|
||
impl std::error::Error for HeaderMissingErr {} | ||
|
||
pub struct HeaderConfigLayer<T> { | ||
key: String, | ||
_marker: PhantomData<T>, | ||
} | ||
|
||
impl<T> HeaderConfigLayer<T> { | ||
pub fn new(key: String) -> Self { | ||
Self { | ||
key, | ||
_marker: PhantomData, | ||
} | ||
} | ||
} | ||
|
||
impl<T, S> Layer<S> for HeaderConfigLayer<T> | ||
where | ||
S: Service<Request<()>>, | ||
{ | ||
type Service = HeaderConfigService<T, S>; | ||
|
||
fn layer(&self, inner: S) -> Self::Service { | ||
HeaderConfigService { | ||
inner, | ||
key: self.key.clone(), | ||
_marker: PhantomData, | ||
} | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod test { | ||
use serde::Deserialize; | ||
|
||
use crate::net::http::Method; | ||
|
||
use super::*; | ||
|
||
#[tokio::test] | ||
async fn test_header_config_happy_path() { | ||
let request = Request::builder() | ||
.method(Method::GET) | ||
.uri("https://www.example.com") | ||
.header("x-proxy-config", "s=E%26G&n=1&b=true") | ||
.body(()) | ||
.unwrap(); | ||
|
||
let inner_service = crate::service::service_fn(|req: Request<()>| async move { | ||
let cfg: &Config = req.extensions().get().unwrap(); | ||
assert_eq!(cfg.s, "E&G"); | ||
assert_eq!(cfg.n, 1); | ||
assert!(cfg.m.is_none()); | ||
assert!(cfg.b); | ||
|
||
Ok::<_, std::convert::Infallible>(()) | ||
}); | ||
|
||
let mut service = | ||
HeaderConfigService::<Config, _>::new(inner_service, "x-proxy-config".to_string()); | ||
|
||
service.call(request).await.unwrap(); | ||
} | ||
|
||
#[tokio::test] | ||
async fn test_header_config_missing_header() { | ||
let request = Request::builder() | ||
.method(Method::GET) | ||
.uri("https://www.example.com") | ||
.body(()) | ||
.unwrap(); | ||
|
||
let inner_service = crate::service::service_fn(|_: Request<()>| async move { | ||
Ok::<_, std::convert::Infallible>(()) | ||
}); | ||
|
||
let mut service = | ||
HeaderConfigService::<Config, _>::new(inner_service, "x-proxy-config".to_string()); | ||
|
||
let result = service.call(request).await; | ||
assert!(result.is_err()); | ||
} | ||
|
||
#[tokio::test] | ||
async fn test_header_config_invalid_config() { | ||
let request = Request::builder() | ||
.method(Method::GET) | ||
.uri("https://www.example.com") | ||
.header("x-proxy-config", "s=bar&n=1&b=invalid") | ||
.body(()) | ||
.unwrap(); | ||
|
||
let inner_service = crate::service::service_fn(|_: Request<()>| async move { | ||
Ok::<_, std::convert::Infallible>(()) | ||
}); | ||
|
||
let mut service = | ||
HeaderConfigService::<Config, _>::new(inner_service, "x-proxy-config".to_string()); | ||
|
||
let result = service.call(request).await; | ||
assert!(result.is_err()); | ||
} | ||
|
||
#[derive(Debug, Deserialize)] | ||
struct Config { | ||
s: String, | ||
n: i32, | ||
m: Option<i32>, | ||
b: bool, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
|
||
mod header_config; | ||
pub use header_config::{HeaderConfigLayer, HeaderConfigService}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,3 @@ | ||
pub mod header; | ||
pub mod layer; | ||
pub mod service; | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters