diff --git a/Cargo.lock b/Cargo.lock index 2a00fb6..69c8bee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -244,15 +244,6 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" -[[package]] -name = "basic-toml" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "823388e228f614e9558c6804262db37960ec8821856535f5c3f59913140558f8" -dependencies = [ - "serde", -] - [[package]] name = "bitflags" version = "2.6.0" @@ -309,7 +300,6 @@ dependencies = [ "anyhow", "axum", "axum-extra", - "basic-toml", "blah", "clap", "ed25519-dalek", @@ -327,6 +317,7 @@ dependencies = [ "serde_urlencoded", "tokio", "tokio-stream", + "toml", "tower-http", "tracing", "tracing-subscriber", @@ -1749,6 +1740,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -2111,6 +2111,40 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +dependencies = [ + "indexmap 2.4.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tower" version = "0.4.13" @@ -2554,6 +2588,15 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +dependencies = [ + "memchr", +] + [[package]] name = "zerocopy" version = "0.7.35" diff --git a/blahd/Cargo.toml b/blahd/Cargo.toml index 6de844e..a9bb7fa 100644 --- a/blahd/Cargo.toml +++ b/blahd/Cargo.toml @@ -7,7 +7,6 @@ edition = "2021" anyhow = "1" axum = { version = "0.7", features = ["ws"] } axum-extra = "0.9" -basic-toml = "0.1.9" clap = { version = "4", features = ["derive"] } ed25519-dalek = "2" futures-util = "0.3" @@ -22,6 +21,7 @@ serde_json = "1" serde_urlencoded = "0.7.1" tokio = { version = "1", features = ["macros", "rt-multi-thread", "sync", "time"] } tokio-stream = { version = "0.1", features = ["sync"] } +toml = "0.8" tower-http = { version = "0.5", features = ["cors", "limit"] } tracing = "0.1" tracing-subscriber = "0.3" diff --git a/blahd/config.example.toml b/blahd/config.example.toml index 594cfe6..78ef411 100644 --- a/blahd/config.example.toml +++ b/blahd/config.example.toml @@ -16,12 +16,12 @@ path = "/var/lib/blahd/db.sqlite" # Note that parent directory will never be created and must already exist. create = true -[server] - +[listen] # (Required) -# The socket address to listen on. -listen = "localhost:8080" +# The local address to listen on. +address = "localhost:8080" +[server] # (Required) # The global absolute URL prefix where this service is hosted. # It is for link generation and must not have trailing slash. diff --git a/blahd/src/bin/blahd.rs b/blahd/src/bin/blahd.rs index 2731e80..46a9bb0 100644 --- a/blahd/src/bin/blahd.rs +++ b/blahd/src/bin/blahd.rs @@ -1,7 +1,8 @@ use std::path::PathBuf; +use std::sync::Arc; use anyhow::{Context, Result}; -use blahd::config::Config; +use blahd::config::{Config, ListenConfig}; use blahd::{AppState, Database}; /// Blah Chat Server @@ -29,7 +30,7 @@ fn main() -> Result<()> { fn parse_config(path: &std::path::Path) -> Result { let src = std::fs::read_to_string(path)?; - let config = basic_toml::from_str::(&src)?; + let config = toml::from_str::(&src)?; config.validate()?; Ok(config) } @@ -38,12 +39,11 @@ fn main() -> Result<()> { Cli::Serve { config } => { let config = parse_config(&config)?; let db = Database::open(&config.database).context("failed to open database")?; - let st = AppState::new(db, config.server); tokio::runtime::Builder::new_multi_thread() .enable_all() .build() .context("failed to initialize tokio runtime")? - .block_on(st.serve()) + .block_on(main_serve(db, config)) } Cli::Validate { config } => { parse_config(&config)?; @@ -51,3 +51,21 @@ fn main() -> Result<()> { } } } + +async fn main_serve(db: Database, config: Config) -> Result<()> { + let listener = match &config.listen { + ListenConfig::Address(addr) => tokio::net::TcpListener::bind(addr) + .await + .context("failed to listen on socket")?, + }; + let st = AppState::new(db, config.server); + + tracing::info!("listening on {:?}", config.listen); + let router = blahd::router(Arc::new(st)); + let _ = sd_notify::notify(true, &[sd_notify::NotifyState::Ready]); + + axum::serve(listener, router) + .await + .context("failed to serve")?; + Ok(()) +} diff --git a/blahd/src/config.rs b/blahd/src/config.rs index 088f530..ebda26c 100644 --- a/blahd/src/config.rs +++ b/blahd/src/config.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; use std::time::Duration; use anyhow::{ensure, Result}; -use serde::{Deserialize, Deserializer}; +use serde::{Deserialize, Deserializer, Serialize}; use serde_inline_default::serde_inline_default; use url::Url; @@ -11,6 +11,7 @@ use url::Url; #[serde(deny_unknown_fields)] pub struct Config { pub database: DatabaseConfig, + pub listen: ListenConfig, pub server: ServerConfig, } @@ -25,11 +26,17 @@ pub struct DatabaseConfig { pub create: bool, } +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(rename_all = "snake_case")] +pub enum ListenConfig { + Address(String), + // TODO: Unix socket. +} + #[serde_inline_default] #[derive(Debug, Clone, Deserialize)] #[serde(deny_unknown_fields)] pub struct ServerConfig { - pub listen: String, pub base_url: Url, #[serde_inline_default(1024.try_into().expect("not zero"))] @@ -71,7 +78,7 @@ mod tests { #[test] fn example_config() { let src = std::fs::read_to_string("config.example.toml").unwrap(); - let config = basic_toml::from_str::(&src).unwrap(); + let config = toml::from_str::(&src).unwrap(); config.validate().unwrap(); } } diff --git a/blahd/src/lib.rs b/blahd/src/lib.rs index a6fccf5..b281d1a 100644 --- a/blahd/src/lib.rs +++ b/blahd/src/lib.rs @@ -2,7 +2,7 @@ use std::num::NonZeroUsize; use std::sync::Arc; use std::time::{Duration, SystemTime}; -use anyhow::{Context, Result}; +use anyhow::Result; use axum::extract::ws; use axum::extract::{Path, Query, State, WebSocketUpgrade}; use axum::http::{header, StatusCode}; @@ -57,10 +57,6 @@ impl AppState { } } - pub async fn serve(self) -> Result<()> { - serve(Arc::new(self)).await - } - fn verify_signed_data(&self, data: &WithSig) -> Result<(), ApiError> { let Ok(()) = data.verify() else { return Err(error_response!( @@ -94,20 +90,6 @@ impl AppState { type ArcState = State>; -async fn serve(st: Arc) -> Result<()> { - let listener = tokio::net::TcpListener::bind(&st.config.listen) - .await - .context("failed to listen on socket")?; - tracing::info!("listening on {}", st.config.listen); - let router = router(st.clone()); - let _ = sd_notify::notify(true, &[sd_notify::NotifyState::Ready]); - - axum::serve(listener, router) - .await - .context("failed to serve")?; - Ok(()) -} - pub fn router(st: Arc) -> Router { Router::new() .route("/ws", get(handle_ws)) diff --git a/blahd/tests/basic.rs b/blahd/tests/basic.rs index f6017e1..5bae4a6 100644 --- a/blahd/tests/basic.rs +++ b/blahd/tests/basic.rs @@ -37,13 +37,7 @@ fn server() -> Server { let listener = TcpListener::from_std(listener).unwrap(); // TODO: Testing config is hard to build because it does have a `Default` impl. - let config = basic_toml::from_str(&format!( - r#" -listen = "" # TODO: unused -base_url = "http://{LOCALHOST}:{port}" - "# - )) - .unwrap(); + let config = toml::from_str(&format!(r#"base_url="http://{LOCALHOST}:{port}""#)).unwrap(); let st = AppState::new(db, config); let router = blahd::router(Arc::new(st));