rama_http_backend/client/proxy/layer/
proxy_address.rs

1use rama_core::{
2    Context, Layer, Service,
3    error::{ErrorContext, OpaqueError},
4};
5use rama_net::address::ProxyAddress;
6use std::fmt;
7
8#[derive(Debug, Clone, Default)]
9/// A [`Layer`] which allows you to add a [`ProxyAddress`]
10/// to the [`Context`] in order to have your client connector
11/// make a connection via this proxy (e.g. by using [`HttpProxyConnectorLayer`]).
12///
13/// See [`HttpProxyAddressService`] for more information.
14///
15/// [`Context`]: rama_core::Context
16/// [`HttpProxyConnectorLayer`]: crate::client::proxy::layer::HttpProxyConnectorLayer
17pub struct HttpProxyAddressLayer {
18    address: Option<ProxyAddress>,
19    preserve: bool,
20}
21
22impl HttpProxyAddressLayer {
23    /// Create a new [`HttpProxyAddressLayer`] that will create
24    /// a service to set the given [`ProxyAddress`].
25    pub fn new(address: ProxyAddress) -> Self {
26        Self::maybe(Some(address))
27    }
28
29    /// Create a new [`HttpProxyAddressLayer`] which will create
30    /// a service that will set the given [`ProxyAddress`] if it is not
31    /// `None`.
32    pub fn maybe(address: Option<ProxyAddress>) -> Self {
33        Self {
34            address,
35            ..Default::default()
36        }
37    }
38
39    /// Try to create a new [`HttpProxyAddressLayer`] which will establish
40    /// a proxy connection over the environment variable `HTTP_PROXY`.
41    pub fn try_from_env_default() -> Result<Self, OpaqueError> {
42        Self::try_from_env("HTTP_PROXY")
43    }
44
45    /// Try to create a new [`HttpProxyAddressLayer`] which will establish
46    /// a proxy connection over the given environment variable.
47    pub fn try_from_env(key: impl AsRef<str>) -> Result<Self, OpaqueError> {
48        let env_result = std::env::var(key.as_ref()).ok();
49        let env_result_mapped = env_result.as_ref().and_then(|v| {
50            let v = v.trim();
51            if v.is_empty() { None } else { Some(v) }
52        });
53
54        let proxy_address = match env_result_mapped {
55            Some(value) => Some(value.try_into().context("parse std env proxy info")?),
56            None => None,
57        };
58
59        Ok(Self::maybe(proxy_address))
60    }
61
62    /// Preserve the existing [`ProxyAddress`] in the context if it already exists.
63    pub fn preserve(mut self, preserve: bool) -> Self {
64        self.preserve = preserve;
65        self
66    }
67
68    /// Preserve the existing [`ProxyAddress`] in the context if it already exists.
69    pub fn set_preserve(&mut self, preserve: bool) -> &mut Self {
70        self.preserve = preserve;
71        self
72    }
73}
74
75impl<S> Layer<S> for HttpProxyAddressLayer {
76    type Service = HttpProxyAddressService<S>;
77
78    fn layer(&self, inner: S) -> Self::Service {
79        HttpProxyAddressService::maybe(inner, self.address.clone()).preserve(self.preserve)
80    }
81
82    fn into_layer(self, inner: S) -> Self::Service {
83        HttpProxyAddressService::maybe(inner, self.address).preserve(self.preserve)
84    }
85}
86
87/// A [`Service`] which allows you to add a [`ProxyAddress`]
88/// to the [`Context`] in order to have your client connector
89/// make a connection via this proxy (e.g. by using [`HttpProxyConnectorLayer`]).
90///
91/// [`Context`]: rama_core::Context
92/// [`HttpProxyConnectorLayer`]: crate::client::proxy::layer::HttpProxyConnectorLayer
93pub struct HttpProxyAddressService<S> {
94    inner: S,
95    address: Option<ProxyAddress>,
96    preserve: bool,
97}
98
99impl<S: fmt::Debug> fmt::Debug for HttpProxyAddressService<S> {
100    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101        f.debug_struct("HttpProxyAddressService")
102            .field("inner", &self.inner)
103            .field("address", &self.address)
104            .field("preserve", &self.preserve)
105            .finish()
106    }
107}
108
109impl<S: Clone> Clone for HttpProxyAddressService<S> {
110    fn clone(&self) -> Self {
111        Self {
112            inner: self.inner.clone(),
113            address: self.address.clone(),
114            preserve: self.preserve,
115        }
116    }
117}
118
119impl<S> HttpProxyAddressService<S> {
120    /// Create a new [`HttpProxyAddressService`] that will create
121    /// a service to set the given [`ProxyAddress`].
122    pub const fn new(inner: S, address: ProxyAddress) -> Self {
123        Self::maybe(inner, Some(address))
124    }
125
126    /// Create a new [`HttpProxyAddressService`] which will create
127    /// a service that will set the given [`ProxyAddress`] if it is not
128    /// `None`.
129    pub const fn maybe(inner: S, address: Option<ProxyAddress>) -> Self {
130        Self {
131            inner,
132            address,
133            preserve: false,
134        }
135    }
136
137    /// Try to create a new [`HttpProxyAddressService`] which will establish
138    /// a proxy connection over the environment variable `HTTP_PROXY`.
139    pub fn try_from_env_default(inner: S) -> Result<Self, OpaqueError> {
140        Self::try_from_env(inner, "HTTP_PROXY")
141    }
142
143    /// Try to create a new [`HttpProxyAddressService`] which will establish
144    /// a proxy connection over the given environment variable.
145    pub fn try_from_env(inner: S, key: impl AsRef<str>) -> Result<Self, OpaqueError> {
146        let env_result = std::env::var(key.as_ref()).ok();
147        let env_result_mapped = env_result.as_ref().and_then(|v| {
148            let v = v.trim();
149            if v.is_empty() { None } else { Some(v) }
150        });
151
152        let proxy_address = match env_result_mapped {
153            Some(value) => Some(value.try_into().context("parse std env proxy info")?),
154            None => None,
155        };
156
157        Ok(Self::maybe(inner, proxy_address))
158    }
159
160    /// Preserve the existing [`ProxyAddress`] in the context if it already exists.
161    pub const fn preserve(mut self, preserve: bool) -> Self {
162        self.preserve = preserve;
163        self
164    }
165
166    /// Preserve the existing [`ProxyAddress`] in the context if it already exists.
167    pub fn set_preserve(&mut self, preserve: bool) -> &mut Self {
168        self.preserve = preserve;
169        self
170    }
171}
172
173impl<S, State, Request> Service<State, Request> for HttpProxyAddressService<S>
174where
175    S: Service<State, Request>,
176    State: Clone + Send + Sync + 'static,
177{
178    type Response = S::Response;
179    type Error = S::Error;
180
181    fn serve(
182        &self,
183        mut ctx: Context<State>,
184        req: Request,
185    ) -> impl Future<Output = Result<Self::Response, Self::Error>> + Send + '_ {
186        if let Some(ref address) = self.address {
187            if !self.preserve || !ctx.contains::<ProxyAddress>() {
188                tracing::trace!(authority = %address.authority, "setting proxy address");
189                ctx.insert(address.clone());
190            }
191        }
192        self.inner.serve(ctx, req)
193    }
194}