boxy/src/server.rs
2025-08-07 20:08:42 +02:00

143 lines
4.5 KiB
Rust

use std::{any::type_name_of_val, collections::HashMap, error::Error, sync::Arc};
use http_body_util::{Either, Full};
use hyper::{
Request, Response, StatusCode,
body::{Body, Bytes, Incoming},
rt::{Read, Write},
server::conn::http1,
service::{HttpService, Service},
};
use hyper_util::rt::TokioIo;
use json::JsonValue;
use log::{error, info};
use rustls::server::Acceptor;
use tokio::net::{TcpListener, TcpStream};
use tokio_rustls::{LazyConfigAcceptor, StartHandshake};
use crate::tls::TlsOption;
pub type GeneralResponse = Response<GeneralBody>;
pub type GeneralBody = Either<Incoming, Full<Bytes>>;
pub fn to_general_response(res: Response<Incoming>) -> GeneralResponse {
let (parts, body) = res.into_parts();
Response::from_parts(parts, GeneralBody::Left(body))
}
pub struct Server<S> {
listener: TcpListener,
service: S,
tls: TlsOption,
}
pub trait TcpIntercept {
fn stream(&mut self, stream: &TcpStream);
}
pub async fn default_response() -> GeneralResponse {
Response::builder()
.status(404)
.body(GeneralBody::Right(Full::from(Bytes::from(
"That route doesn't exist.",
))))
.unwrap()
}
pub async fn custom_resp(e: StatusCode, m: String) -> GeneralResponse {
Response::builder()
.status(e)
.body(GeneralBody::Right(Full::from(Bytes::from(m))))
.unwrap()
}
pub async fn json_to_vec(v: JsonValue) -> Option<Vec<String>> {
if let JsonValue::Array(arr) = v {
Some(
arr.into_iter()
.map(|val| val.as_str().unwrap().to_string())
.collect(),
)
} else {
None
}
}
impl<S> Server<S>
where
S: TcpIntercept + Sync,
S: Service<Request<Incoming>> + Clone + Send + 'static,
S: HttpService<Incoming> + Clone + Send,
<S::ResBody as Body>::Error: Into<Box<dyn Error + Send + Sync>>,
<S::ResBody as Body>::Data: Send,
S::ResBody: Send,
<S as HttpService<Incoming>>::Future: Send,
{
pub async fn handle(&self) {
info!(
"Server started at http://{} for service: {}",
self.listener.local_addr().unwrap(),
type_name_of_val(&self.service)
);
loop {
let (tcp_stream, _) = self.listener.accept().await.unwrap();
let mut svc_clone = self.service.clone();
let tls = self.tls.clone();
tokio::task::spawn(async move {
svc_clone.stream(&tcp_stream);
match tls {
TlsOption::NoTls => {
if let Err(err) = http1::Builder::new()
.writev(false)
.serve_connection(TokioIo::new(tcp_stream), svc_clone)
.await
{
error!("Error while trying to serve connection: {err}")
};
}
TlsOption::Tls(x) => {
let acceptor = LazyConfigAcceptor::new(Acceptor::default(), tcp_stream);
match acceptor.await {
Ok(y) => {
let hello = y.client_hello();
let hostname = hello.server_name().clone().unwrap();
let config = Arc::new(x.matcher(hostname).unwrap());
let stream = y
.into_stream(config)
.await
.unwrap();
if let Err(err) = http1::Builder::new()
.writev(false)
.serve_connection(TokioIo::new(stream), svc_clone)
.await
{
error!("Error while trying to serve connection: {err}")
}
}
Err(e) => {
error!("Error while initiating handshake: {e}");
return;
}
}
}
};
});
}
}
pub async fn new(service: S, a: (String, u16), tls: TlsOption) -> Result<Self, Box<dyn Error>> {
Ok(Self {
listener: TcpListener::bind(&a).await?,
service,
tls,
})
}
}
/*
*/