diff options
author | metamuffin <metamuffin@disroot.org> | 2024-09-20 21:36:22 +0200 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2024-09-20 21:36:36 +0200 |
commit | a294ea6772b1bf1bccc6f43dad026e5574de15af (patch) | |
tree | 8a47e08c81d6e5ebe0968a9a9c53952b4b368232 /server/registry/src/main.rs | |
parent | 44e90c75d10815633edaf979847c89b5d62242a3 (diff) | |
download | hurrycurry-a294ea6772b1bf1bccc6f43dad026e5574de15af.tar hurrycurry-a294ea6772b1bf1bccc6f43dad026e5574de15af.tar.bz2 hurrycurry-a294ea6772b1bf1bccc6f43dad026e5574de15af.tar.zst |
html listing
Diffstat (limited to 'server/registry/src/main.rs')
-rw-r--r-- | server/registry/src/main.rs | 140 |
1 files changed, 37 insertions, 103 deletions
diff --git a/server/registry/src/main.rs b/server/registry/src/main.rs index f40ff043..e979289f 100644 --- a/server/registry/src/main.rs +++ b/server/registry/src/main.rs @@ -1,24 +1,20 @@ -use log::{debug, error, info}; -use rocket::{ - get, - http::hyper::Uri, - post, - response::content::RawJson, - routes, - serde::{json::Json, Serialize}, - State, -}; -use serde::Deserialize; +pub mod list; +pub mod register; + +use list::{generate_html_list, generate_json_list, r_list}; +use log::{error, info}; +use register::r_register; +use rocket::{get, routes, serde::Serialize, Config}; use std::{ cmp::Reverse, collections::HashMap, env::var, - net::IpAddr, + net::{IpAddr, Ipv4Addr}, str::FromStr, sync::Arc, time::{Duration, Instant}, }; -use tokio::{net::lookup_host, sync::RwLock, time::interval}; +use tokio::{sync::RwLock, time::interval}; fn main() { env_logger::init_from_env("LOG"); @@ -30,6 +26,13 @@ fn main() { .block_on(async move { tokio::task::spawn(Registry::update_loop(registry.clone())); rocket::build() + .configure(Config { + address: var("BIND_ADDR") + .map(|a| IpAddr::from_str(&a).unwrap()) + .unwrap_or(IpAddr::V4(Ipv4Addr::LOCALHOST)), + port: var("PORT").map(|p| p.parse().unwrap()).unwrap_or(8000), + ..Default::default() + }) .manage(registry) .mount("/", routes![r_index, r_list, r_register]) .ignite() @@ -43,7 +46,8 @@ fn main() { #[derive(Default)] struct Registry { - response: Arc<str>, + json_response: Arc<str>, + html_response: Arc<str>, servers: HashMap<u128, Entry>, } @@ -56,18 +60,14 @@ impl Registry { )); loop { interval.tick().await; - info!("updating list"); if let Err(e) = r.write().await.update() { error!("update failed: {e}") } } } pub fn update(&mut self) -> anyhow::Result<()> { - self.servers.retain(|_, e| { - e.address - .retain(|_, updated| updated.elapsed() < Duration::from_secs(120)); - e.address.len() > 0 - }); + info!("updating list"); + self.remove_dead(); let mut list = self .servers @@ -77,14 +77,25 @@ impl Registry { address: e.address.keys().cloned().collect(), last_game: e.last_game, players_online: e.players_online, + version: e.version, }) .collect::<Vec<_>>(); list.sort_by_key(|e| Reverse(e.players_online)); - self.response = serde_json::to_string(&list)?.into(); + self.json_response = generate_json_list(&list)?; + self.html_response = generate_html_list(&list)?; + + info!("done. {} servers registered", self.servers.len()); Ok(()) } + pub fn remove_dead(&mut self) { + self.servers.retain(|_, e| { + e.address + .retain(|_, updated| updated.elapsed() < Duration::from_secs(120)); + e.address.len() > 0 + }); + } } #[derive(Debug)] @@ -93,14 +104,16 @@ struct Entry { address: HashMap<String, Instant>, players_online: usize, last_game: i64, + version: (usize, usize), } #[derive(Debug, Serialize)] -struct PublicEntry { +pub struct PublicEntry { name: String, address: Vec<String>, players_online: usize, last_game: i64, + version: (usize, usize), } impl Default for Entry { @@ -110,91 +123,12 @@ impl Default for Entry { last_game: 0, name: String::new(), players_online: 0, + version: (0, 0), } } } #[get("/")] fn r_index() -> &'static str { - "Hurry Curry! registry service" -} - -#[get("/v1/list")] -async fn r_list(registry: &State<Arc<RwLock<Registry>>>) -> RawJson<Arc<str>> { - RawJson(registry.read().await.response.clone()) -} - -#[derive(Debug, Deserialize)] -struct Submission { - secret: u128, - name: String, - players: usize, - last_game: i64, - - uri: String, -} - -#[post("/v1/register", data = "<submission>")] -async fn r_register<'a>( - client_addr: IpAddr, - registry: &State<Arc<RwLock<Registry>>>, - submission: Json<Submission>, -) -> Result<&'static str, &'static str> { - debug!("submission {submission:?}"); - let uri = Uri::from_str(&submission.uri).map_err(|_| "invalid uri")?; - - let scheme = uri.scheme().ok_or("no scheme")?.as_str(); - let secure = match scheme { - "ws" => false, - "wss" => true, - _ => return Err("invalid scheme"), - }; - let host = uri.host().ok_or("no host")?; - let port = uri.port_u16().unwrap_or(if secure { 443 } else { 27032 }); - - let uri_q = match IpAddr::from_str(host) { - Ok(mut addr) => { - if addr.is_unspecified() { - addr = client_addr; - } - if addr.is_loopback() { - return Err("loopback address"); - } - if addr.is_multicast() { - return Err("multicast address"); - } - if client_addr == addr { - format!("{scheme}://{addr}:{port}",) - } else { - return Err("source address does not match uri"); - } - } - Err(_) => { - if lookup_host(format!("{host}:0")) - .await - .map_err(|_| "dns lookup failed")? - .find(|a| a.ip() == client_addr) - .is_some() - { - format!("{scheme}://{host}:{port}") - } else { - return Err("host verification failed"); - } - } - }; - - let mut g = registry.write().await; - - if g.servers.len() > 1000 { - return Err("too many registered servers"); - } - - info!("submission approved for {uri_q:?}"); - let entry = g.servers.entry(submission.secret).or_default(); - entry.name = submission.name.clone(); - entry.players_online = submission.players; - entry.last_game = submission.last_game; - entry.address.insert(uri_q, Instant::now()); - - Ok("ok") + "Hurry Curry! Server Registry Service" } |