use rama_core::{
Context, Layer, Service,
error::{ErrorContext, OpaqueError},
};
use rama_net::address::ProxyAddress;
use std::fmt;
#[derive(Debug, Clone, Default)]
pub struct HttpProxyAddressLayer {
address: Option<ProxyAddress>,
preserve: bool,
}
impl HttpProxyAddressLayer {
pub fn new(address: ProxyAddress) -> Self {
Self::maybe(Some(address))
}
pub fn maybe(address: Option<ProxyAddress>) -> Self {
Self {
address,
..Default::default()
}
}
pub fn try_from_env_default() -> Result<Self, OpaqueError> {
Self::try_from_env("HTTP_PROXY")
}
pub fn try_from_env(key: impl AsRef<str>) -> Result<Self, OpaqueError> {
let env_result = std::env::var(key.as_ref()).ok();
let env_result_mapped = env_result.as_ref().and_then(|v| {
let v = v.trim();
if v.is_empty() { None } else { Some(v) }
});
let proxy_address = match env_result_mapped {
Some(value) => Some(value.try_into().context("parse std env proxy info")?),
None => None,
};
Ok(Self::maybe(proxy_address))
}
pub fn preserve(mut self, preserve: bool) -> Self {
self.preserve = preserve;
self
}
pub fn set_preserve(&mut self, preserve: bool) -> &mut Self {
self.preserve = preserve;
self
}
}
impl<S> Layer<S> for HttpProxyAddressLayer {
type Service = HttpProxyAddressService<S>;
fn layer(&self, inner: S) -> Self::Service {
HttpProxyAddressService::maybe(inner, self.address.clone()).preserve(self.preserve)
}
fn into_layer(self, inner: S) -> Self::Service {
HttpProxyAddressService::maybe(inner, self.address).preserve(self.preserve)
}
}
pub struct HttpProxyAddressService<S> {
inner: S,
address: Option<ProxyAddress>,
preserve: bool,
}
impl<S: fmt::Debug> fmt::Debug for HttpProxyAddressService<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("HttpProxyAddressService")
.field("inner", &self.inner)
.field("address", &self.address)
.field("preserve", &self.preserve)
.finish()
}
}
impl<S: Clone> Clone for HttpProxyAddressService<S> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
address: self.address.clone(),
preserve: self.preserve,
}
}
}
impl<S> HttpProxyAddressService<S> {
pub const fn new(inner: S, address: ProxyAddress) -> Self {
Self::maybe(inner, Some(address))
}
pub const fn maybe(inner: S, address: Option<ProxyAddress>) -> Self {
Self {
inner,
address,
preserve: false,
}
}
pub fn try_from_env_default(inner: S) -> Result<Self, OpaqueError> {
Self::try_from_env(inner, "HTTP_PROXY")
}
pub fn try_from_env(inner: S, key: impl AsRef<str>) -> Result<Self, OpaqueError> {
let env_result = std::env::var(key.as_ref()).ok();
let env_result_mapped = env_result.as_ref().and_then(|v| {
let v = v.trim();
if v.is_empty() { None } else { Some(v) }
});
let proxy_address = match env_result_mapped {
Some(value) => Some(value.try_into().context("parse std env proxy info")?),
None => None,
};
Ok(Self::maybe(inner, proxy_address))
}
pub const fn preserve(mut self, preserve: bool) -> Self {
self.preserve = preserve;
self
}
pub fn set_preserve(&mut self, preserve: bool) -> &mut Self {
self.preserve = preserve;
self
}
}
impl<S, State, Request> Service<State, Request> for HttpProxyAddressService<S>
where
S: Service<State, Request>,
State: Clone + Send + Sync + 'static,
{
type Response = S::Response;
type Error = S::Error;
fn serve(
&self,
mut ctx: Context<State>,
req: Request,
) -> impl Future<Output = Result<Self::Response, Self::Error>> + Send + '_ {
if let Some(ref address) = self.address {
if !self.preserve || !ctx.contains::<ProxyAddress>() {
tracing::trace!(authority = %address.authority, "setting proxy address");
ctx.insert(address.clone());
}
}
self.inner.serve(ctx, req)
}
}