aboutsummaryrefslogtreecommitdiff
path: root/server/registry/src/main.rs
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2024-09-20 21:36:22 +0200
committermetamuffin <metamuffin@disroot.org>2024-09-20 21:36:36 +0200
commita294ea6772b1bf1bccc6f43dad026e5574de15af (patch)
tree8a47e08c81d6e5ebe0968a9a9c53952b4b368232 /server/registry/src/main.rs
parent44e90c75d10815633edaf979847c89b5d62242a3 (diff)
downloadhurrycurry-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.rs140
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"
}