summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2023-02-12 10:15:03 +0100
committermetamuffin <metamuffin@disroot.org>2023-02-12 10:15:03 +0100
commit1f6ade88e4f7b8fdb48ad040b469e33292faaf74 (patch)
treef5fa84348dfa84b38753dbe92e60ef2ebcbe5cf5
parentf7d989c8785bb83bfd02a0fa7287b8bf4e383ac6 (diff)
downloadgnix-1f6ade88e4f7b8fdb48ad040b469e33292faaf74.tar
gnix-1f6ade88e4f7b8fdb48ad040b469e33292faaf74.tar.bz2
gnix-1f6ade88e4f7b8fdb48ad040b469e33292faaf74.tar.zst
doc + error handling
-rw-r--r--Cargo.lock21
-rw-r--r--Cargo.toml1
-rw-r--r--readme.md7
-rw-r--r--src/main.rs47
4 files changed, 56 insertions, 20 deletions
diff --git a/Cargo.lock b/Cargo.lock
index b03202d..357ae03 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -151,6 +151,7 @@ dependencies = [
"rustls",
"rustls-pemfile",
"serde",
+ "thiserror",
"tokio",
"tokio-rustls",
"toml",
@@ -629,6 +630,26 @@ dependencies = [
]
[[package]]
+name = "thiserror"
+version = "1.0.38"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.38"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "tokio"
version = "1.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index d69cc35..f3bfafb 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -14,6 +14,7 @@ tokio-rustls = "0.23.4"
bytes = "1.4.0"
anyhow = "1.0.69"
+thiserror = "1.0.38"
env_logger = "0.10.0"
log = "0.4.17"
diff --git a/readme.md b/readme.md
index 304a1df..67150c8 100644
--- a/readme.md
+++ b/readme.md
@@ -2,6 +2,13 @@
a simple stupid reverse proxy
+## Features
+
+- Simple to configure (see below)
+- Handles connection upgrades correctly by default (websocket, etc.)
+- TLS support
+- _TODO: h2; match on uris; connection pools_
+
## Usage
Run the binary with the a path to the configuration as the first argument. The
diff --git a/src/main.rs b/src/main.rs
index f507ed5..7f6ec88 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -5,12 +5,12 @@ use anyhow::{anyhow, bail, Context, Result};
use http_body_util::{combinators::BoxBody, BodyExt};
use hyper::{
body::Incoming,
- header::UPGRADE,
+ header::{HOST, UPGRADE},
http::{uri::PathAndQuery, HeaderValue},
server::conn::http1,
service::service_fn,
upgrade::OnUpgrade,
- Request, Response, Uri,
+ Request, Response, StatusCode, Uri,
};
use log::{debug, error, info, warn};
use std::{fs::File, io::BufReader, path::Path, sync::Arc};
@@ -20,6 +20,16 @@ use tokio::{
};
use tokio_rustls::TlsAcceptor;
+#[derive(Debug, thiserror::Error)]
+enum ServiceError {
+ #[error("hyper error")]
+ Hyper(hyper::Error),
+ #[error("unknown host")]
+ NoHost,
+ #[error("can't connect to the backend")]
+ CantConnect,
+}
+
#[tokio::main]
async fn main() -> anyhow::Result<()> {
env_logger::init_from_env("LOG");
@@ -61,10 +71,7 @@ async fn serve_https(config: Arc<Config>) -> Result<()> {
.with_safe_defaults()
.with_no_client_auth()
.with_single_cert(certs, key)?;
- cfg.alpn_protocols = vec![
- // b"h2".to_vec(),
- b"http/1.1".to_vec(),
- ];
+ cfg.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
Arc::new(cfg)
};
let listener = TcpListener::bind(https_config.bind).await?;
@@ -74,8 +81,10 @@ async fn serve_https(config: Arc<Config>) -> Result<()> {
loop {
let (stream, addr) = listener.accept().await.context("accepting connection")?;
debug!("connection from {addr}");
- let stream = tls_acceptor.accept(stream).await.context("accepting tls")?;
- serve_stream(config.clone(), stream)
+ match tls_acceptor.accept(stream).await {
+ Ok(stream) => serve_stream(config.clone(), stream),
+ Err(e) => warn!("error accepting tls: {e}"),
+ };
}
}
@@ -93,9 +102,13 @@ pub fn serve_stream<T: AsyncRead + AsyncWrite + Unpin + Send + 'static>(
match service(config, req).await {
Ok(r) => Ok(r),
Err(ServiceError::Hyper(e)) => Err(e),
- Err(other) => Ok(Response::new(format!(
- "the reverse proxy encountered an error: gnix-{other:?}"
- ))
+ Err(error) => Ok({
+ let mut resp = Response::new(format!(
+ "the reverse proxy encountered an issue: {error}"
+ ));
+ *resp.status_mut() = StatusCode::BAD_REQUEST;
+ resp
+ }
.map(|b| b.map_err(|e| match e {}).boxed())),
}
}
@@ -103,7 +116,7 @@ pub fn serve_stream<T: AsyncRead + AsyncWrite + Unpin + Send + 'static>(
)
.with_upgrades();
if let Err(err) = conn.await {
- error!("error: {:?}", err);
+ warn!("error: {:?}", err);
}
});
}
@@ -123,17 +136,11 @@ fn load_private_key(path: &Path) -> anyhow::Result<rustls::PrivateKey> {
Ok(rustls::PrivateKey(keys[0].clone()))
}
-#[derive(Debug)]
-enum ServiceError {
- Hyper(hyper::Error),
- NoHost,
- CantConnect,
-}
-
async fn service(
config: Arc<Config>,
mut req: Request<Incoming>,
) -> Result<hyper::Response<BoxBody<bytes::Bytes, hyper::Error>>, ServiceError> {
+ debug!("<- {:?} {}", req.headers().get(HOST), req.uri());
*req.uri_mut() = Uri::builder()
.scheme("http")
.authority("backend")
@@ -151,7 +158,7 @@ async fn service(
.hosts
.get(remove_port(
&req.headers()
- .get("host")
+ .get(HOST)
.and_then(|e| e.to_str().ok())
.map(String::from)
.unwrap_or(String::from("")),