use crate::service::proxy::hyper::ProxyConfig;
use crate::service::{Layer, Service};
use crate::{builder, Builder};
use conjure_error::Error;
use http::header::PROXY_AUTHORIZATION;
use http::uri::Scheme;
use http::Request;
use std::future::Future;
pub struct ProxyLayer {
config: ProxyConfig,
}
impl ProxyLayer {
pub fn new(builder: &Builder<builder::Complete>) -> Result<ProxyLayer, Error> {
let config = ProxyConfig::from_config(builder.get_proxy())?;
Ok(ProxyLayer {
config: config.clone(),
})
}
}
impl<S> Layer<S> for ProxyLayer {
type Service = ProxyService<S>;
fn layer(self, inner: S) -> Self::Service {
ProxyService {
inner,
config: self.config,
}
}
}
pub struct ProxyService<S> {
inner: S,
config: ProxyConfig,
}
impl<S, B> Service<Request<B>> for ProxyService<S>
where
S: Service<Request<B>>,
{
type Response = S::Response;
type Error = S::Error;
fn call(&self, mut req: Request<B>) -> impl Future<Output = Result<S::Response, S::Error>> {
match &self.config {
ProxyConfig::Http(config) => {
if req.uri().scheme() == Some(&Scheme::HTTP) {
if let Some(credentials) = &config.credentials {
req.headers_mut()
.insert(PROXY_AUTHORIZATION, credentials.clone());
}
}
}
ProxyConfig::Direct => {}
}
self.inner.call(req)
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::config::{self, BasicCredentials, HostAndPort, HttpProxyConfig};
use crate::service;
use http::{HeaderMap, HeaderValue};
#[tokio::test]
async fn http_proxied_http() {
let config = ProxyConfig::from_config(&config::ProxyConfig::Http(
HttpProxyConfig::builder()
.host_and_port(HostAndPort::new("proxy", 1234))
.credentials(Some(BasicCredentials::new("admin", "hunter2")))
.build(),
))
.unwrap();
let service =
ProxyLayer { config }.layer(service::service_fn(|req| async { Ok::<_, ()>(req) }));
let req = Request::builder()
.uri("http://foobar.com/fizz/buzz")
.body(())
.unwrap();
let out = service.call(req).await.unwrap();
assert_eq!(out.uri(), "http://foobar.com/fizz/buzz");
let mut headers = HeaderMap::new();
headers.insert(
PROXY_AUTHORIZATION,
HeaderValue::from_static("Basic YWRtaW46aHVudGVyMg=="),
);
assert_eq!(*out.headers(), headers);
}
#[tokio::test]
async fn http_proxied_https() {
let config = ProxyConfig::from_config(&config::ProxyConfig::Http(
HttpProxyConfig::builder()
.host_and_port(HostAndPort::new("proxy", 1234))
.credentials(Some(BasicCredentials::new("admin", "hunter2")))
.build(),
))
.unwrap();
let service =
ProxyLayer { config }.layer(service::service_fn(|req| async { Ok::<_, ()>(req) }));
let req = Request::builder()
.uri("https://foobar.com/fizz/buzz")
.body(())
.unwrap();
let out = service.call(req).await.unwrap();
assert_eq!(out.uri(), "https://foobar.com/fizz/buzz");
let headers = HeaderMap::new();
assert_eq!(*out.headers(), headers);
}
#[tokio::test]
async fn unproxied() {
let config = ProxyConfig::from_config(&config::ProxyConfig::Direct).unwrap();
let service =
ProxyLayer { config }.layer(service::service_fn(|req| async { Ok::<_, ()>(req) }));
let req = Request::builder()
.uri("https://foobar.com/fizz/buzz")
.body(())
.unwrap();
let out = service.call(req).await.unwrap();
assert_eq!(out.uri(), "https://foobar.com/fizz/buzz");
let headers = HeaderMap::new();
assert_eq!(*out.headers(), headers);
}
}