Improve error handling

This commit is contained in:
Riley Apeldoorn 2022-07-21 15:34:55 +02:00
parent 06bf4d9afb
commit 060d47cbcc
1 changed files with 46 additions and 22 deletions

View File

@ -1,5 +1,5 @@
use std::net::SocketAddr; use std::net::SocketAddr;
use hyper::{service::{make_service_fn, service_fn}, Error, Server, Response, StatusCode, Body, Request, Uri, client::HttpConnector}; use hyper::{service::{make_service_fn, service_fn}, Server, Response, StatusCode, Body, Request, Uri, client::HttpConnector, http};
pub mod parse; pub mod parse;
@ -60,8 +60,7 @@ async fn main() {
// circuit). // circuit).
let res = Response::builder() let res = Response::builder()
.status(StatusCode::BAD_GATEWAY) .status(StatusCode::BAD_GATEWAY)
.body(Body::empty()) .body(Body::empty())?;
.expect("Failed to construct response");
Ok (res) Ok (res)
@ -118,6 +117,33 @@ pub fn parse (data: String) -> Config {
Config (rules) Config (rules)
} }
/// Runtime errors.
#[derive(Debug)]
pub enum Error {
Hyper (hyper::Error),
Http (http::Error),
}
impl std::error::Error for Error {}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::Hyper (e) => e.fmt(f),
Error::Http (e) => e.fmt(f),
}
}
}
impl From<hyper::Error> for Error {
fn from (v: hyper::Error) -> Self { Self::Hyper(v) }
}
impl From<http::Error> for Error {
fn from (v: http::Error) -> Self { Self::Http(v) }
}
/// A rule consists of a [`Pattern`] and an [`Effect`]. If the pattern matches, /// A rule consists of a [`Pattern`] and an [`Effect`]. If the pattern matches,
/// the effect is performed. /// the effect is performed.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -204,39 +230,37 @@ pub enum Effect {
impl Effect { impl Effect {
/// Perform the effect. /// Perform the effect.
pub async fn perform (&self, client: Client, mut req: Request<Body>) -> hyper::Result<Response<Body>> { pub async fn perform (&self, client: Client, mut req: Request<Body>) -> Result<Response<Body>, Error> {
match self { let res = match self {
Effect::Proxy { port, .. } => { Effect::Proxy { port, .. } => {
let host = "0.0.0.0"; // Support for custom hosts added later let host = "0.0.0.0"; // Support for custom hosts added later
let path = req let path = req
.uri() .uri()
.path_and_query() .path_and_query()
.and_then(|x| { .map(|x| x.as_str())
// Reject all requests where the path doesn't start with a `/`,
// and strip the first `/` off all paths so we can ensure that
// the path is actually separated from the host and port.
x.as_str().strip_prefix('/')
})
.unwrap_or(""); .unwrap_or("");
let target = format!("http://{host}:{port}/{path}"); let uri = Uri::builder()
.authority(format!("{host}:{port}"))
.scheme("http")
.path_and_query(path)
.build()?;
println!("Proxying to {uri}");
let uri = target.parse().unwrap();
*req.uri_mut() = uri; *req.uri_mut() = uri;
client.request(req).await?
println!("Proxying to {target}");
client.request(req).await
}, },
Effect::Redirect (uri) => Ok ({ Effect::Redirect (uri) => {
println!("Redirecting to {uri}"); println!("Redirecting to {uri}");
Response::builder() Response::builder()
.status(StatusCode::PERMANENT_REDIRECT) .status(StatusCode::PERMANENT_REDIRECT)
.header("Location", uri) .header("Location", uri)
.body(Body::empty()) .body(Body::empty())?
.unwrap() },
}), };
}
Ok (res)
} }
} }