use std::net::SocketAddr; use bus::Bus; mod util; mod app; mod system; mod protocol; mod manager; use app::App; use hyper::body::Bytes; use hyper_util::rt::TokioIo; use system::{cache::WebCache, net::Stream}; #[derive(Clone)] struct HttpServiceArgs { bus:Bus, cache:WebCache, } async fn service_http(mut request:hyper::Request, args:HttpServiceArgs) -> Result>, std::convert::Infallible> // Serve cached files and upgrade websocket connections. // { use hyper::{Response, body::Bytes, header::{CONTENT_TYPE, CACHE_CONTROL}}; use http_body_util::Full; println!("Serving: {}", request.uri().path()); match request.uri().path() { "/.css" => Ok(Response::builder() .header(CONTENT_TYPE, "text/css") .header(CACHE_CONTROL, "no-cache") .body(Full::new(Bytes::from(args.cache.css()))).unwrap()), "/.js" => Ok(Response::builder() .header(CONTENT_TYPE, "text/javascript") .header(CACHE_CONTROL, "no-cache") .body(Full::new(Bytes::from(args.cache.js()))).unwrap()), "/favicon.png" => Ok(Response::builder() .header(CONTENT_TYPE, "image/png") .body(Full::new(Bytes::from(args.cache.favicon()))).unwrap()), // Assets "/asset/omen_dawn.svg" => Ok(Response::builder() .header(CONTENT_TYPE, "image/svg") .body(Full::new(Bytes::from(args.cache.asset(0, 6)))).unwrap()), "/asset/dragon_dawn.svg" => Ok(Response::builder() .header(CONTENT_TYPE, "image/svg") .body(Full::new(Bytes::from(args.cache.asset(0, 5)))).unwrap()), "/asset/castle_dawn.svg" => Ok(Response::builder() .header(CONTENT_TYPE, "image/svg") .body(Full::new(Bytes::from(args.cache.asset(0, 4)))).unwrap()), "/asset/tower_dawn.svg" => Ok(Response::builder() .header(CONTENT_TYPE, "image/svg") .body(Full::new(Bytes::from(args.cache.asset(0, 3)))).unwrap()), "/asset/lance_dawn.svg" => Ok(Response::builder() .header(CONTENT_TYPE, "image/svg") .body(Full::new(Bytes::from(args.cache.asset(0, 2)))).unwrap()), "/asset/knight_dawn.svg" => Ok(Response::builder() .header(CONTENT_TYPE, "image/svg") .body(Full::new(Bytes::from(args.cache.asset(0, 1)))).unwrap()), "/asset/militia_dawn.svg" => Ok(Response::builder() .header(CONTENT_TYPE, "image/svg") .body(Full::new(Bytes::from(args.cache.asset(0, 0)))).unwrap()), "/asset/omen_dusk.svg" => Ok(Response::builder() .header(CONTENT_TYPE, "image/svg") .body(Full::new(Bytes::from(args.cache.asset(1, 6)))).unwrap()), "/asset/dragon_dusk.svg" => Ok(Response::builder() .header(CONTENT_TYPE, "image/svg") .body(Full::new(Bytes::from(args.cache.asset(1, 5)))).unwrap()), "/asset/castle_dusk.svg" => Ok(Response::builder() .header(CONTENT_TYPE, "image/svg") .body(Full::new(Bytes::from(args.cache.asset(1, 4)))).unwrap()), "/asset/tower_dusk.svg" => Ok(Response::builder() .header(CONTENT_TYPE, "image/svg") .body(Full::new(Bytes::from(args.cache.asset(1, 3)))).unwrap()), "/asset/lance_dusk.svg" => Ok(Response::builder() .header(CONTENT_TYPE, "image/svg") .body(Full::new(Bytes::from(args.cache.asset(1, 2)))).unwrap()), "/asset/knight_dusk.svg" => Ok(Response::builder() .header(CONTENT_TYPE, "image/svg") .body(Full::new(Bytes::from(args.cache.asset(1, 1)))).unwrap()), "/asset/militia_dusk.svg" => Ok(Response::builder() .header(CONTENT_TYPE, "image/svg") .body(Full::new(Bytes::from(args.cache.asset(1, 0)))).unwrap()), _ => { if hyper_tungstenite::is_upgrade_request(&request) { if let Ok((response, websocket)) = hyper_tungstenite::upgrade(&mut request, None) { tokio::task::spawn(async move { match websocket.await { Ok(websocket) => manager::handle_ws(websocket, args).await, Err(_) => Err(()), }.ok() }); Ok(response) } else { Ok(Response::builder() .status(401) .body(Full::new(Bytes::new())) .unwrap()) } } else { Ok(Response::builder() .header(CONTENT_TYPE, "text/html") .header(CACHE_CONTROL, "no-cache") .body(Full::new(Bytes::from(args.cache.html()))).unwrap()) } } } } async fn handle_http(stream:system::net::tls::TlsStream, addr:SocketAddr, args:HttpServiceArgs) -> Result<(),()> // Hand off socket connection to Hyper server. // { use hyper::server::conn::http1; use hyper::service::service_fn; println!("Connection from {}", addr.to_string()); let io = TokioIo::new(stream.to_stream()); let conn = http1::Builder::new() .serve_connection(io, service_fn(move |req| { service_http(req, args.clone()) })); conn.with_upgrades().await.ok(); Ok(()) } #[tokio::main] async fn main() { use system::net::{ Server, tls::*, }; // Initialize application data. let app; if let Ok(result) = App::init() { app = result; } else { println!("fatal: failed to initialize server."); return; } // Initialize central bus and data serivce. let b_main :Bus = Bus::new_as(bus::Mode::Transmitter); match b_main.connect() { Ok(bus) => { tokio::spawn(async move { manager::thread_system(app, bus).await; }); } Err(_) => { println!("fatal: failed to initialize bus."); return; } } // Initialize HTTPS service. match b_main.connect() { Ok(bus) => { let cache = WebCache::init(); let mut server = TlsServer::new(); if server.add_cert("omen.kirisame.com", "cert/fullchain.pem", "cert/privkey.pem").await.is_ok() { println!("Loaded cert file."); } match server.bind("0.0.0.0:38612").await { Ok(_) => { println!("Listener bind successful."); tokio::spawn(async move { while server.accept(handle_http, HttpServiceArgs { bus:bus.connect().unwrap(), cache:cache.clone(), }).await.is_ok() { } }); } Err(_) => { println!("error: failed to bind port 38612."); } } } Err(_) => { println!("error: failed to initialize HTTPS service."); } } std::thread::park(); }