aboutsummaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2023-02-24 13:23:58 +0100
committermetamuffin <metamuffin@disroot.org>2023-02-24 13:23:58 +0100
commit3ed98e04da0917e790063549676729c7051d67f7 (patch)
tree81fbb2c93172a7cbb284aaeed8c98d3f6a7c26b5 /src/main.rs
parentc0d504f9ae77f99e5484e92e2e9d3f68561129c5 (diff)
downloadgnix-3ed98e04da0917e790063549676729c7051d67f7.tar
gnix-3ed98e04da0917e790063549676729c7051d67f7.tar.bz2
gnix-3ed98e04da0917e790063549676729c7051d67f7.tar.zst
static file serving + bugs
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs128
1 files changed, 26 insertions, 102 deletions
diff --git a/src/main.rs b/src/main.rs
index 2edbe3d..c325e61 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,39 +1,35 @@
+#![feature(try_trait_v2)]
+
pub mod config;
+pub mod error;
+pub mod files;
+pub mod proxy;
-use crate::config::Config;
+use crate::{
+ config::{Config, HostConfig},
+ files::serve_files,
+ proxy::proxy_request,
+};
use anyhow::{anyhow, bail, Context, Result};
+use error::ServiceError;
use http_body_util::{combinators::BoxBody, BodyExt};
use hyper::{
body::Incoming,
- header::{HOST, UPGRADE},
- http::{
- uri::{PathAndQuery, Scheme},
- HeaderValue,
- },
+ header::{CONTENT_TYPE, HOST},
+ http::HeaderValue,
server::conn::http1,
service::service_fn,
- upgrade::OnUpgrade,
- Request, Response, StatusCode, Uri,
+ Request, Response, StatusCode,
};
-use log::{debug, error, info, warn};
+use log::{debug, info, warn};
use std::{fs::File, io::BufReader, net::SocketAddr, path::Path, sync::Arc};
use tokio::{
io::{AsyncRead, AsyncWrite},
- net::{TcpListener, TcpStream},
+ net::TcpListener,
signal::ctrl_c,
};
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");
@@ -60,7 +56,7 @@ async fn serve_http(config: Arc<Config>) -> Result<()> {
let (stream, addr) = listener.accept().await.context("accepting connection")?;
debug!("connection from {addr}");
let config = config.clone();
- tokio::spawn(async move { serve_stream(config, stream, addr) });
+ tokio::spawn(async move { serve_stream(config, stream, addr).await });
}
}
async fn serve_https(config: Arc<Config>) -> Result<()> {
@@ -114,10 +110,11 @@ pub async fn serve_stream<T: AsyncRead + AsyncWrite + Unpin + Send + 'static>(
Ok(r) => Ok(r),
Err(ServiceError::Hyper(e)) => Err(e),
Err(error) => Ok({
- let mut resp = Response::new(format!(
- "the reverse proxy encountered an issue: {error}"
- ));
+ let mut resp =
+ Response::new(format!("gnix encountered an issue: {error}"));
*resp.status_mut() = StatusCode::BAD_REQUEST;
+ resp.headers_mut()
+ .insert(CONTENT_TYPE, HeaderValue::from_static("text/plain"));
resp
}
.map(|b| b.map_err(|e| match e {}).boxed())),
@@ -148,34 +145,10 @@ fn load_private_key(path: &Path) -> anyhow::Result<rustls::PrivateKey> {
async fn service(
config: Arc<Config>,
- mut req: Request<Incoming>,
+ req: Request<Incoming>,
addr: SocketAddr,
-) -> Result<hyper::Response<BoxBody<bytes::Bytes, hyper::Error>>, ServiceError> {
- let scheme_secure = req.uri().scheme() == Some(&Scheme::HTTPS);
+) -> Result<hyper::Response<BoxBody<bytes::Bytes, ServiceError>>, ServiceError> {
debug!("{addr} ~> {:?} {}", req.headers().get(HOST), req.uri());
- *req.uri_mut() = Uri::builder()
- .path_and_query(
- req.uri()
- .clone()
- .path_and_query()
- .cloned()
- .unwrap_or(PathAndQuery::from_static("/")),
- )
- .build()
- .unwrap();
-
- req.headers_mut().insert(
- "x-forwarded-for",
- HeaderValue::from_str(&format!("{addr}")).unwrap(),
- );
- req.headers_mut().insert(
- "x-forwarded-proto",
- if scheme_secure {
- HeaderValue::from_static("https")
- } else {
- HeaderValue::from_static("http")
- },
- );
let route = config
.hosts
@@ -188,59 +161,10 @@ async fn service(
))
.ok_or(ServiceError::NoHost)?;
- let do_upgrade = req.headers().contains_key(UPGRADE);
- let on_upgrade_downstream = req.extensions_mut().remove::<OnUpgrade>();
-
- debug!("\tforwarding to {}", route.backend);
- let mut resp = {
- let client_stream = TcpStream::connect(&route.backend)
- .await
- .map_err(|_| ServiceError::CantConnect)?;
-
- let (mut sender, conn) = hyper::client::conn::http1::handshake(client_stream)
- .await
- .map_err(ServiceError::Hyper)?;
- tokio::task::spawn(async move {
- if let Err(err) = conn.await {
- warn!("connection failed: {:?}", err);
- }
- });
- sender
- .send_request(req)
- .await
- .map_err(ServiceError::Hyper)?
- };
-
- resp.headers_mut()
- .insert("server", HeaderValue::from_static("gnix"));
-
- if do_upgrade {
- let on_upgrade_upstream = resp.extensions_mut().remove::<OnUpgrade>();
- tokio::task::spawn(async move {
- debug!("about upgrading connection, sending empty response");
- match (
- on_upgrade_upstream.unwrap().await,
- on_upgrade_downstream.unwrap().await,
- ) {
- (Ok(mut upgraded_upstream), Ok(mut upgraded_downstream)) => {
- debug!("upgrade successful");
- match tokio::io::copy_bidirectional(
- &mut upgraded_downstream,
- &mut upgraded_upstream,
- )
- .await
- {
- Ok((from_client, from_server)) => {
- debug!("proxy socket terminated: {from_server} sent, {from_client} received")
- }
- Err(e) => warn!("proxy socket error: {e}"),
- }
- }
- (a, b) => eprintln!("upgrade error: upstream={a:?} downstream={b:?}"),
- }
- });
+ match route {
+ HostConfig::Backend { backend } => proxy_request(req, addr, backend).await,
+ HostConfig::Files { files } => serve_files(req, files).await,
}
- Ok(resp.map(|b| b.boxed()))
}
pub fn remove_port(s: &str) -> &str {