diff --git a/Cargo.toml b/Cargo.toml index 805ba61..01773fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "reverse_proxy" -version = "0.2.1" +version = "0.3.1" edition = "2024" [dependencies] diff --git a/LICENSE b/LICENSE index 539791b..6d4f5e2 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ BSD 3-Clause License -Copyright (c) 2022, W-lfpup +Copyright (c) 2026, Here By There LLC All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/README.md b/README.md index b3f1b9d..adb5bdc 100644 --- a/README.md +++ b/README.md @@ -31,12 +31,14 @@ All filepaths can be absolute or relative to the config filepath. "host_and_port": "", "key_filepath": "", "cert_filepath": "", - "addresses": [ - ["", ""] - ], - "dangerous_self_signed_addresses": [ - ["", ""] - ] + "addresses": [{ + "incoming": "origin_address", + "outgoing": "destination_address", + }], + "dangerous_self_signed_addresses": [{ + "incoming": "origin_address", + "outgoing": "destination_address", + }] } ``` diff --git a/demo/index.html b/demo/index.html deleted file mode 100644 index d0119df..0000000 --- a/demo/index.html +++ /dev/null @@ -1 +0,0 @@ -

hello!

diff --git a/reverse_proxy_config.example.json b/reverse_proxy.config.example.json similarity index 61% rename from reverse_proxy_config.example.json rename to reverse_proxy.config.example.json index 892c61d..64e06fe 100644 --- a/reverse_proxy_config.example.json +++ b/reverse_proxy.config.example.json @@ -3,9 +3,9 @@ "key_filepath": "./demo/self-signed-key.key", "cert_filepath": "./demo/self-signed-cert.crt", "addresses": [ - [ - "https://127.0.0.1", - "http://127.0.0.1:3000" - ] + { + "incoming": "https://example.com", + "outgoing": "http://127.0.0.1:3000" + } ] -} \ No newline at end of file +} diff --git a/src/addresses.rs b/src/addresses.rs index 0c7b1a3..6086b3f 100644 --- a/src/addresses.rs +++ b/src/addresses.rs @@ -1,4 +1,4 @@ -use crate::config::Config; +use crate::config::{Address, Config}; use crate::errors::Error; use crate::response::{AddressMap, AddressParams}; use hyper::Uri; @@ -20,16 +20,17 @@ pub fn create_address_map(config: &Config) -> Result { fn add_addresses_to_map( url_map: &mut AddressMap, - addresses: &Vec<(String, String)>, + addresses: &Vec
, is_dangerous: bool, ) -> Result<(), Error> { - for (source_str, target_str) in addresses { - let source_uri = match Uri::try_from(source_str) { + for address in addresses { + let Address { incoming, outgoing } = address; + let incoming_uri = match Uri::try_from(incoming) { Ok(uri) => uri, Err(e) => return Err(Error::Uri(e)), }; - let source_host = match source_uri.host() { + let incoming_host = match incoming_uri.host() { Some(h) => h, _ => { return Err(Error::Custom( @@ -38,12 +39,15 @@ fn add_addresses_to_map( } }; - let uri = match Uri::try_from(target_str) { + let uri = match Uri::try_from(outgoing) { Ok(uri) => uri, Err(e) => return Err(Error::Uri(e)), }; - url_map.insert(source_host.to_string(), AddressParams { uri, is_dangerous }); + url_map.insert( + incoming_host.to_string(), + AddressParams { uri, is_dangerous }, + ); } Ok(()) diff --git a/src/config.rs b/src/config.rs index 07cbaf0..cdd4c4c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,22 +1,25 @@ +use crate::errors::Error; use serde::{Deserialize, Serialize}; use serde_json; use std::path; use std::path::PathBuf; use tokio::fs; -use crate::errors::Error; +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct Address { + pub incoming: String, + pub outgoing: String, +} #[derive(Clone, Serialize, Deserialize, Debug)] pub struct Config { pub host_and_port: String, pub key_filepath: PathBuf, pub cert_filepath: PathBuf, - pub addresses: Vec<(String, String)>, - pub dangerous_self_signed_addresses: Option>, + pub addresses: Vec
, + pub dangerous_self_signed_addresses: Option>, } -// Serde path fs errors in one function - pub async fn from_filepath(filepath: &PathBuf) -> Result { let config_path = match path::absolute(filepath) { Ok(pb) => pb, diff --git a/src/main.rs b/src/main.rs index e31736d..efbcfed 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +use crate::errors::Error; use hyper_util::rt::{TokioExecutor, TokioIo}; use hyper_util::server::conn::auto::Builder; use native_tls::Identity; @@ -13,8 +14,6 @@ mod requests; mod response; mod service; -use crate::errors::Error; - #[tokio::main] async fn main() -> Result<(), Error> { // create config @@ -64,7 +63,7 @@ async fn main() -> Result<(), Error> { Err(e) => return Err(Error::Io(e)), }; - println!("Reverse Proxy: {}", &config.host_and_port); + println!("Reverse proxy @ {}", &config.host_and_port); loop { let (socket, _remote_addr) = match listener.accept().await { diff --git a/src/requests.rs b/src/requests.rs index 2234e0d..656ac5b 100644 --- a/src/requests.rs +++ b/src/requests.rs @@ -17,7 +17,6 @@ fn get_host_and_authority<'a>(uri: &Uri) -> Result<(String, String), &'a str> { let host = match uri.host() { Some(h) => h.to_string(), _ => return Err("failed to retrieve downstream URI from upstream URI"), - // _ => return Err(Error::Custom("failed to retrieve downstream URI from upstream URI".to_string())), }; let port = match uri.port() { diff --git a/src/response.rs b/src/response.rs index a725d04..d744201 100644 --- a/src/response.rs +++ b/src/response.rs @@ -1,17 +1,15 @@ +use crate::requests; use bytes::Bytes; use http_body_util::combinators::BoxBody; use hyper::body::Incoming; use hyper::http::uri::InvalidUriParts; -use hyper::{Request, Response, StatusCode, Uri, header}; +use hyper::{Request, Response, StatusCode, Uri}; use std::collections::HashMap; -use std::sync::Arc; - -use crate::requests; pub type BoxedResponse = Response>; -const URI_FROM_REQUEST_ERROR: &str = "unable to parse upstream URI from request"; -const UPSTREAM_URI_ERROR: &str = "falied to update request with upstream URI"; +pub const URI_FROM_REQUEST_ERROR: &str = "unable to parse upstream URI from request"; +pub const UPSTREAM_URI_ERROR: &str = "falied to update request with upstream URI"; #[derive(Clone, Debug)] pub struct AddressParams { @@ -21,74 +19,10 @@ pub struct AddressParams { pub type AddressMap = HashMap; -pub async fn build_response( - mut req: Request, - addresses: Arc>, -) -> Result { - let host = match get_host(&req) { - Some(uri) => uri, - _ => { - return requests::create_fallback_response( - &StatusCode::BAD_REQUEST, - &URI_FROM_REQUEST_ERROR, - ); - } - }; - - // get target uri - let address_params = match addresses.get(&host) { - Some(params) => params, - _ => { - return requests::create_fallback_response( - &StatusCode::BAD_GATEWAY, - &URI_FROM_REQUEST_ERROR, - ); - } - }; - - if let Err(_) = update_request_with_dest_uri(&mut req, &address_params.uri) { - return requests::create_fallback_response( - &StatusCode::INTERNAL_SERVER_ERROR, - &UPSTREAM_URI_ERROR, - ); - }; - - get_response(req, address_params.is_dangerous).await -} - -fn get_host(req: &Request) -> Option { - // http2 - if let Some(host) = req.uri().host() { - return Some(host.to_string()); - }; - - // http 1 - let host_header = match req.headers().get(header::HOST) { - Some(h) => h, - _ => return None, - }; - - let host_str = match host_header.to_str() { - Ok(hs) => hs, - _ => return None, - }; - - let host_as_uri = match Uri::try_from(host_str) { - Ok(hau) => hau, - _ => return None, - }; - - if let Some(host) = host_as_uri.host() { - return Some(host.to_string()); - } - - None -} - fn update_request_with_dest_uri( - req: &mut Request, + mut req: Request, target_uri: &Uri, -) -> Result<(), InvalidUriParts> { +) -> Result, InvalidUriParts> { let mut dest_parts = target_uri.clone().into_parts(); dest_parts.path_and_query = None; @@ -101,14 +35,25 @@ fn update_request_with_dest_uri( Err(e) => return Err(e), }; - Ok(()) + Ok(req) } -async fn get_response( +pub async fn build_response( req: Request, - is_dangerous: bool, + address_params: AddressParams, ) -> Result { let version = req.version(); + + let req = match update_request_with_dest_uri(req, &address_params.uri) { + Ok(r) => r, + _ => { + return requests::create_fallback_response( + &StatusCode::INTERNAL_SERVER_ERROR, + &UPSTREAM_URI_ERROR, + ); + } + }; + let scheme = match req.uri().scheme() { Some(a) => a.as_str(), _ => "http", @@ -116,10 +61,10 @@ async fn get_response( match (version, scheme) { (hyper::Version::HTTP_2, "https") => { - requests::send_http2_tls_request(req, is_dangerous).await + requests::send_http2_tls_request(req, address_params.is_dangerous).await } (hyper::Version::HTTP_2, _) => requests::send_http2_request(req).await, - (_, "https") => requests::send_http1_tls_request(req, is_dangerous).await, + (_, "https") => requests::send_http1_tls_request(req, address_params.is_dangerous).await, _ => requests::send_http1_request(req).await, } } diff --git a/src/service.rs b/src/service.rs index 02d92f7..c94353d 100644 --- a/src/service.rs +++ b/src/service.rs @@ -1,8 +1,10 @@ +use crate::requests; +use crate::response; use crate::response::{AddressMap, BoxedResponse, build_response}; -use hyper::Request; use hyper::body::Incoming; use hyper::http; use hyper::service::Service; +use hyper::{Request, StatusCode, Uri, header}; use std::future::Future; use std::pin::Pin; use std::sync::Arc; @@ -18,6 +20,61 @@ impl Service> for Svc { fn call(&self, req: Request) -> Self::Future { let addresses = self.addresses.clone(); - Box::pin(async move { build_response(req, addresses).await }) + + let host = match get_host(&req) { + Some(uri) => uri, + _ => { + return Box::pin(async move { + requests::create_fallback_response( + &StatusCode::BAD_REQUEST, + &response::URI_FROM_REQUEST_ERROR, + ) + }); + } + }; + + // get target uri + let address_params = match addresses.get(&host) { + Some(params) => params.clone(), + _ => { + return Box::pin(async move { + requests::create_fallback_response( + &StatusCode::BAD_GATEWAY, + &response::URI_FROM_REQUEST_ERROR, + ) + }); + } + }; + + Box::pin(async move { build_response(req, address_params).await }) } } + +pub fn get_host(req: &Request) -> Option { + // http2 + if let Some(host) = req.uri().host() { + return Some(host.to_string()); + }; + + // http 1 + let host_header = match req.headers().get(header::HOST) { + Some(h) => h, + _ => return None, + }; + + let host_str = match host_header.to_str() { + Ok(hs) => hs, + _ => return None, + }; + + let host_as_uri = match Uri::try_from(host_str) { + Ok(hau) => hau, + _ => return None, + }; + + if let Some(host) = host_as_uri.host() { + return Some(host.to_string()); + } + + None +}