dzura/server/src/main.rs
2024-08-11 22:41:25 -07:00

201 lines
7.1 KiB
Rust

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<protocol::QRPacket>,
cache:WebCache,
}
async fn service_http(mut request:hyper::Request<hyper::body::Incoming>, args:HttpServiceArgs) -> Result<hyper::Response<http_body_util::Full<Bytes>>, 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<protocol::QRPacket> = 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();
}