joecalsend/src/http_server.rs

151 lines
5.3 KiB
Rust
Raw Normal View History

2025-08-08 16:51:26 +00:00
use std::{net::SocketAddr, path::Path, sync::Arc};
2025-07-04 22:15:52 +00:00
2025-07-04 00:00:11 +00:00
use axum::{
Json, Router,
2025-08-08 16:51:26 +00:00
extract::{ConnectInfo, DefaultBodyLimit, Request},
2025-07-04 00:00:11 +00:00
routing::{get, post},
};
2025-08-08 16:51:26 +00:00
use axum_server::tls_rustls::RustlsConfig;
use hyper::body::Incoming;
use hyper_util::rt::{TokioExecutor, TokioIo};
use tokio::{
net::TcpListener,
sync::{Mutex, mpsc},
};
use tokio_rustls::{
TlsAcceptor,
rustls::{
ServerConfig,
pki_types::{CertificateDer, PrivateKeyDer, pem::PemObject},
},
};
use tower::MakeService;
2025-07-04 00:00:11 +00:00
use tower_http::limit::RequestBodyLimitLayer;
2025-08-08 16:51:26 +00:00
use tower_service::Service;
2025-07-04 00:00:11 +00:00
use crate::{
2025-08-06 21:09:37 +00:00
JocalService,
discovery::register_device,
transfer::{prepare_upload, receive_upload},
2025-07-04 00:00:11 +00:00
};
2025-08-06 21:09:37 +00:00
impl JocalService {
pub async fn start_http_server(&self, stop_rx: mpsc::Receiver<()>) -> crate::error::Result<()> {
let app = self.create_router();
2025-07-08 22:46:31 +00:00
// TODO: make addr config
let addr = SocketAddr::from(([0, 0, 0, 0], self.config.device.port));
2025-08-08 16:51:26 +00:00
let (key, cert) = self.config.ssl();
2025-07-04 00:00:11 +00:00
2025-08-08 16:51:26 +00:00
let ssl_config = rustls_server_config(key, cert);
// let stop = Arc::new(Mutex::new(stop_rx));
/*
loop {
let tower_service = app.clone();
let tls_acceptor = acceptor.clone();
let stop = stop.clone();
let mut stop = stop.lock().await;
tokio::select! {
// Wait for new tcp connection
Ok((cnx, addr)) = listener.accept() => {
tokio::spawn(async move {
// Wait for tls handshake to happen
let Ok(stream) = tls_acceptor.accept(cnx).await else {
log::error!("error during tls handshake connection from {}", addr);
return;
};
// Hyper has its own `AsyncRead` and `AsyncWrite` traits and doesn't use tokio.
// `TokioIo` converts between them.
let stream = TokioIo::new(stream);
// Hyper also has its own `Service` trait and doesn't use tower. We can use
// `hyper::service::service_fn` to create a hyper `Service` that calls our app
// through `tower::Service::call`.
let hyper_service =
hyper::service::service_fn(move |request: Request<Incoming>| {
// We have to clone `tower_service` because hyper's `Service` uses `&self`
// whereas tower's `Service` requires `&mut self`.
//
// We don't need to call `poll_ready` since `Router` is always ready.
tower_service.clone().call(request)
});
let ret = hyper_util::server::conn::auto::Builder::new(TokioExecutor::new())
.serve_connection_with_upgrades(stream, hyper_service)
.await;
if let Err(err) = ret {
log::error!("error serving connection from {}: {}", addr, err);
}
});
}
_ = stop.recv() => {
log::info!("stopping http server");
break;
}
}
}
*/
axum_server::bind_rustls(addr, ssl_config)
.serve(app.into_make_service_with_connect_info::<SocketAddr>())
.await?;
// axum::serve(
// listener,
// app.into_make_service_with_connect_info::<SocketAddr>(),
// )
// .with_graceful_shutdown(shutdown(stop_rx))
// .await?;
2025-07-04 00:00:11 +00:00
Ok(())
}
fn create_router(&self) -> Router {
2025-07-04 00:00:11 +00:00
let device = self.device.clone();
Router::new()
.route("/api/localsend/v2/register", post(register_device))
.route(
"/api/localsend/v2/info",
2025-07-08 16:59:03 +00:00
get(move || async move { Json(device) }),
2025-07-04 00:00:11 +00:00
)
.route("/api/localsend/v2/prepare-upload", post(prepare_upload))
.route("/api/localsend/v2/upload", post(receive_upload))
2025-07-04 00:00:11 +00:00
.layer(DefaultBodyLimit::disable())
.layer(RequestBodyLimitLayer::new(1024 * 1024 * 1024))
.with_state(self.clone())
2025-07-04 00:00:11 +00:00
}
}
2025-07-06 23:02:11 +00:00
async fn shutdown(mut rx: mpsc::Receiver<()>) {
rx.recv().await.unwrap_or_default()
}
2025-08-08 16:51:26 +00:00
fn rustls_server_config(key: impl AsRef<Path>, cert: impl AsRef<Path>) -> RustlsConfig {
let key = match PrivateKeyDer::from_pem_file(&key) {
Ok(k) => k,
Err(e) => {
let path = key.as_ref().display();
log::error!("could not open {path} for reading; got {e:?}");
panic!()
}
};
let certs = CertificateDer::pem_file_iter(cert)
.unwrap()
.map(|cert| cert.unwrap())
.collect();
let mut config = ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(certs, key)
.expect("bad certificate/key");
config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
RustlsConfig::from_config(config.into())
}