Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
[workspace]
resolver = "2"
members = ["response", "reverse_proxy"]
[package]
name = "reverse_proxy"
version = "0.2.1"
edition = "2024"

[workspace.dependencies]
[dependencies]
bytes = "1.11"
http-body-util = "0.1"
hyper = { version = "1.9", features = ["full"] }
Expand Down
15 changes: 0 additions & 15 deletions response/Cargo.toml

This file was deleted.

16 changes: 0 additions & 16 deletions reverse_proxy/Cargo.toml

This file was deleted.

19 changes: 11 additions & 8 deletions reverse_proxy/src/addresses.rs → src/addresses.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use crate::config::Config;
use crate::errors::Error;
use crate::response::{AddressMap, AddressParams};
use hyper::Uri;
use response::{AddressMap, AddressParams};

// Three map errors

pub fn create_address_map(config: &Config) -> Result<AddressMap, String> {
pub fn create_address_map(config: &Config) -> Result<AddressMap, Error> {
let mut hashmap = AddressMap::new();
if let Err(e) = add_addresses_to_map(&mut hashmap, &config.addresses, false) {
return Err(e);
Expand All @@ -23,21 +22,25 @@ fn add_addresses_to_map(
url_map: &mut AddressMap,
addresses: &Vec<(String, String)>,
is_dangerous: bool,
) -> Result<(), String> {
) -> Result<(), Error> {
for (source_str, target_str) in addresses {
let source_uri = match Uri::try_from(source_str) {
Ok(uri) => uri,
Err(e) => return Err(e.to_string()),
Err(e) => return Err(Error::Uri(e)),
};

let source_host = match source_uri.host() {
Some(h) => h,
_ => return Err("could not parse host from source uri".to_string()),
_ => {
return Err(Error::Custom(
"could not parse host from source uri".to_string(),
));
}
};

let uri = match Uri::try_from(target_str) {
Ok(uri) => uri,
Err(e) => return Err(e.to_string()),
Err(e) => return Err(Error::Uri(e)),
};

url_map.insert(source_host.to_string(), AddressParams { uri, is_dangerous });
Expand Down
16 changes: 11 additions & 5 deletions reverse_proxy/src/config.rs → src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use std::path;
use std::path::PathBuf;
use tokio::fs;

use crate::errors::Error;

#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct Config {
pub host_and_port: String,
Expand All @@ -15,25 +17,29 @@ pub struct Config {

// Serde path fs errors in one function

pub async fn from_filepath(filepath: &PathBuf) -> Result<Config, String> {
pub async fn from_filepath(filepath: &PathBuf) -> Result<Config, Error> {
let config_path = match path::absolute(filepath) {
Ok(pb) => pb,
Err(e) => return Err(e.to_string()),
Err(e) => return Err(Error::Io(e)),
};

let json_as_str = match fs::read_to_string(&config_path).await {
Ok(r) => r,
Err(e) => return Err(e.to_string()),
Err(e) => return Err(Error::Io(e)),
};

let parent_dir = match config_path.parent() {
Some(p) => p.to_path_buf(),
_ => return Err("parent directory of config not found".to_string()),
_ => {
return Err(Error::Custom(
"parent directory of config not found".to_string(),
));
}
};

let mut config: Config = match serde_json::from_str(&json_as_str) {
Ok(j) => j,
Err(e) => return Err(e.to_string()),
Err(e) => return Err(Error::SerdeJson(e)),
};

config.key_filepath = parent_dir.join(&config.key_filepath);
Expand Down
26 changes: 26 additions & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use hyper::http::uri::InvalidUri;
use native_tls::Error as NativeTlsError;
use serde_json::Error as SerdeJsonError;
use std::fmt;
use std::io::Error as IoError;

#[derive(Debug)]
pub enum Error {
Io(IoError),
SerdeJson(SerdeJsonError),
NativeTls(NativeTlsError),
Uri(InvalidUri),
Custom(String),
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::Io(err) => write!(f, "{}", err),
Error::NativeTls(err) => write!(f, "{}", err),
Error::SerdeJson(err) => write!(f, "{}", err),
Error::Uri(err) => write!(f, "{}", err),
Error::Custom(err) => write!(f, "{}", err),
}
}
}
32 changes: 21 additions & 11 deletions reverse_proxy/src/main.rs → src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,25 @@ use tokio::net::TcpListener;

mod addresses;
mod config;
mod errors;
mod requests;
mod response;
mod service;

// Needs to be errors
// create key errors
use crate::errors::Error;

#[tokio::main]
async fn main() -> Result<(), String> {
async fn main() -> Result<(), Error> {
// create config
let args = match env::args().nth(1) {
Some(a) => path::PathBuf::from(a),
None => return Err("argument error: argv[0] config path not provided".to_string()),
None => {
return Err(Error::Custom(
"argument error: argv[0] config path not provided".to_string(),
));
}
};

let config = match config::from_filepath(&args).await {
Ok(c) => c,
Err(e) => return Err(e),
Expand All @@ -34,35 +41,35 @@ async fn main() -> Result<(), String> {
// tls cert and keys
let cert = match fs::read(&config.cert_filepath).await {
Ok(f) => f,
Err(e) => return Err(e.to_string()),
Err(e) => return Err(Error::Io(e)),
};
let key = match fs::read(&config.key_filepath).await {
Ok(f) => f,
Err(e) => return Err(e.to_string()),
Err(e) => return Err(Error::Io(e)),
};
let identity = match Identity::from_pkcs8(&cert, &key) {
Ok(pk) => pk,
Err(e) => return Err(e.to_string()),
Err(e) => return Err(Error::NativeTls(e)),
};

// create tls acceptor
let tls_acceptor = match native_tls::TlsAcceptor::new(identity) {
Ok(acceptor) => tokio_native_tls::TlsAcceptor::from(acceptor),
Err(e) => return Err(e.to_string()),
Err(e) => return Err(Error::NativeTls(e)),
};

// bind tcp listeners
let listener = match TcpListener::bind(&config.host_and_port).await {
Ok(l) => l,
Err(e) => return Err(e.to_string()),
Err(e) => return Err(Error::Io(e)),
};

println!("Reverse Proxy: {}", &config.host_and_port);

loop {
let (socket, _remote_addr) = match listener.accept().await {
Ok(s) => s,
Err(e) => return Err(e.to_string()),
Err(_e) => continue,
};

let acceptor = tls_acceptor.clone();
Expand All @@ -74,7 +81,10 @@ async fn main() -> Result<(), String> {
tokio::task::spawn(async move {
let io = match acceptor.accept(socket).await {
Ok(s) => TokioIo::new(s),
Err(_e) => return,
Err(e) => {
println!("{}", e);
return;
}
};

if let Err(e) = Builder::new(TokioExecutor::new())
Expand Down
13 changes: 7 additions & 6 deletions response/src/requests.rs → src/requests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use http_body_util::combinators::BoxBody;
use http_body_util::{BodyExt, Full};
use hyper::body::Incoming;
use hyper::client::conn::{http1, http2};
use hyper::{header, Request, Response, StatusCode, Uri};
use hyper::{Request, Response, StatusCode, Uri, header};
use hyper_util::rt::{TokioExecutor, TokioIo};
use native_tls::TlsConnector;
use tokio::net::TcpStream;
Expand All @@ -16,7 +16,8 @@ const FAILED_TO_PROCESS_REQUEST_ERROR: &str = "failed to process request";
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 URI from upstream URI"),
_ => 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() {
Expand Down Expand Up @@ -90,7 +91,7 @@ pub async fn send_http1_request(
return create_fallback_response(
&StatusCode::SERVICE_UNAVAILABLE,
&UPSTREAM_HANDSHAKE_ERROR,
)
);
}
};

Expand Down Expand Up @@ -125,7 +126,7 @@ pub async fn send_http1_tls_request(
return create_fallback_response(
&StatusCode::SERVICE_UNAVAILABLE,
&UPSTREAM_HANDSHAKE_ERROR,
)
);
}
};

Expand Down Expand Up @@ -159,7 +160,7 @@ pub async fn send_http2_request(
return create_fallback_response(
&StatusCode::SERVICE_UNAVAILABLE,
&UPSTREAM_HANDSHAKE_ERROR,
)
);
}
};

Expand Down Expand Up @@ -194,7 +195,7 @@ pub async fn send_http2_tls_request(
return create_fallback_response(
&StatusCode::SERVICE_UNAVAILABLE,
&UPSTREAM_HANDSHAKE_ERROR,
)
);
}
};

Expand Down
8 changes: 4 additions & 4 deletions response/src/lib.rs → src/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ use bytes::Bytes;
use http_body_util::combinators::BoxBody;
use hyper::body::Incoming;
use hyper::http::uri::InvalidUriParts;
use hyper::{header, Request, Response, StatusCode, Uri};
use hyper::{Request, Response, StatusCode, Uri, header};
use std::collections::HashMap;
use std::sync::Arc;

mod requests;
use crate::requests;

pub type BoxedResponse = Response<BoxBody<Bytes, hyper::Error>>;

Expand All @@ -31,7 +31,7 @@ pub async fn build_response(
return requests::create_fallback_response(
&StatusCode::BAD_REQUEST,
&URI_FROM_REQUEST_ERROR,
)
);
}
};

Expand All @@ -42,7 +42,7 @@ pub async fn build_response(
return requests::create_fallback_response(
&StatusCode::BAD_GATEWAY,
&URI_FROM_REQUEST_ERROR,
)
);
}
};

Expand Down
9 changes: 5 additions & 4 deletions reverse_proxy/src/service.rs → src/service.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use crate::response::{AddressMap, BoxedResponse, build_response};
use hyper::Request;
use hyper::body::Incoming;
use hyper::http;
use hyper::service::Service;
use hyper::Request;
use response::{build_response, AddressMap};
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
Expand All @@ -11,8 +12,8 @@ pub struct Svc {
}

impl Service<Request<Incoming>> for Svc {
type Response = response::BoxedResponse;
type Error = hyper::http::Error;
type Response = BoxedResponse;
type Error = http::Error;
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;

fn call(&self, req: Request<Incoming>) -> Self::Future {
Expand Down
Loading