use crate::security::headers::strip_hop_by_hop;
use bytes::Bytes;
use http::{Request, Response, StatusCode};
use http_body_util::{BodyExt, Full};
use hyper_util::client::legacy::Client;
use hyper_util::client::legacy::connect::HttpConnector;
pub async fn proxy_request(
upstream_addr: &str,
req: Request<hyper::body::Incoming>,
) -> Result<Response<Full<Bytes>>, StatusCode> {
let (parts, body) = req.into_parts();
let body_bytes = body
.collect()
.await
.map_err(|_| StatusCode::BAD_REQUEST)?
.to_bytes();
forward_to_upstream(upstream_addr, parts, body_bytes).await
}
pub async fn proxy_request_bytes(
upstream_addr: &str,
req: Request<Full<Bytes>>,
) -> Result<Response<Full<Bytes>>, StatusCode> {
let (parts, body) = req.into_parts();
let body_bytes = body
.collect()
.await
.map_err(|_| StatusCode::BAD_REQUEST)?
.to_bytes();
forward_to_upstream(upstream_addr, parts, body_bytes).await
}
async fn forward_to_upstream(
upstream_addr: &str,
mut parts: http::request::Parts,
body_bytes: Bytes,
) -> Result<Response<Full<Bytes>>, StatusCode> {
let upstream_uri = format!(
"http://{}{}",
upstream_addr,
parts
.uri
.path_and_query()
.map(|pq| pq.as_str())
.unwrap_or("/")
);
let upstream_uri: http::Uri = upstream_uri
.parse()
.map_err(|_| StatusCode::BAD_GATEWAY)?;
strip_hop_by_hop(&mut parts.headers);
parts.headers.remove(http::header::HOST);
let mut builder = Request::builder()
.method(parts.method)
.uri(upstream_uri)
.version(http::Version::HTTP_11);
let headers = builder.headers_mut().unwrap();
for (name, value) in &parts.headers {
headers.insert(name, value.clone());
}
let upstream_req = builder
.body(Full::new(body_bytes))
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
let client: Client<HttpConnector, Full<Bytes>> =
Client::builder(hyper_util::rt::TokioExecutor::new()).build_http();
let resp = client
.request(upstream_req)
.await
.map_err(|_| StatusCode::BAD_GATEWAY)?;
let (resp_parts, resp_body) = resp.into_parts();
let upstream_has_cl = resp_parts.headers.get(http::header::CONTENT_LENGTH).is_some();
let upstream_has_te = resp_parts.headers.get(http::header::TRANSFER_ENCODING).is_some();
if upstream_has_cl && upstream_has_te {
return Err(StatusCode::BAD_GATEWAY);
}
let resp_body_bytes = resp_body
.collect()
.await
.map_err(|_| StatusCode::BAD_GATEWAY)?
.to_bytes();
let mut resp_builder = Response::builder().status(resp_parts.status);
let resp_headers = resp_builder.headers_mut().unwrap();
for (name, value) in resp_parts.headers {
if let Some(name) = name {
resp_headers.insert(name, value);
}
}
resp_builder
.body(Full::new(resp_body_bytes))
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
}