use std::{net::SocketAddr, path::Path, sync::Arc}; use axum::{ Json, Router, extract::{ConnectInfo, DefaultBodyLimit, Request}, routing::{get, post}, }; 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; use tower_http::limit::RequestBodyLimitLayer; use tower_service::Service; use crate::{ JocalService, discovery::register_device, transfer::{prepare_upload, receive_upload}, }; impl JocalService { pub async fn start_http_server(&self, stop_rx: mpsc::Receiver<()>) -> crate::error::Result<()> { let app = self.create_router(); // TODO: make addr config let addr = SocketAddr::from(([0, 0, 0, 0], self.config.device.port)); let (key, cert) = self.config.ssl(); 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| { // 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::()) .await?; // axum::serve( // listener, // app.into_make_service_with_connect_info::(), // ) // .with_graceful_shutdown(shutdown(stop_rx)) // .await?; Ok(()) } fn create_router(&self) -> Router { let device = self.device.clone(); Router::new() .route("/api/localsend/v2/register", post(register_device)) .route( "/api/localsend/v2/info", get(move || async move { Json(device) }), ) .route("/api/localsend/v2/prepare-upload", post(prepare_upload)) .route("/api/localsend/v2/upload", post(receive_upload)) .layer(DefaultBodyLimit::disable()) .layer(RequestBodyLimitLayer::new(1024 * 1024 * 1024)) .with_state(self.clone()) } } async fn shutdown(mut rx: mpsc::Receiver<()>) { rx.recv().await.unwrap_or_default() } fn rustls_server_config(key: impl AsRef, cert: impl AsRef) -> 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()) }