diff --git a/Cargo-minimal.lock b/Cargo-minimal.lock index 22fabf0a3..852908e2d 100644 --- a/Cargo-minimal.lock +++ b/Cargo-minimal.lock @@ -1144,7 +1144,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -1167,6 +1167,15 @@ dependencies = [ "const-random", ] +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + [[package]] name = "electrum-client" version = "0.18.0" @@ -2045,6 +2054,12 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + [[package]] name = "lock_api" version = "0.4.12" @@ -2165,6 +2180,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", +] + [[package]] name = "nix" version = "0.30.1" @@ -2194,7 +2220,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -2403,7 +2429,7 @@ dependencies = [ "http-body-util", "hyper", "hyper-util", - "nix", + "nix 0.30.1", "payjoin", "payjoin-test-utils", "r2d2", @@ -2440,6 +2466,7 @@ dependencies = [ "serde", "tempfile", "tokio", + "tokio-listener", "tokio-rustls", "tokio-rustls-acme", "tokio-stream", @@ -2554,6 +2581,26 @@ dependencies = [ "sha2 0.10.8", ] +[[package]] +name = "pin-project" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "pin-project-lite" version = "0.2.14" @@ -3723,6 +3770,24 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "tokio-listener" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8a49c7295dbf705691c238002e4be940d5e0605f204683b1c8e55490032c79d" +dependencies = [ + "clap", + "document-features", + "futures-core", + "futures-util", + "nix 0.26.4", + "pin-project", + "socket2 0.5.10", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "tokio-macros" version = "2.6.0" diff --git a/Cargo-recent.lock b/Cargo-recent.lock index 22fabf0a3..852908e2d 100644 --- a/Cargo-recent.lock +++ b/Cargo-recent.lock @@ -1144,7 +1144,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -1167,6 +1167,15 @@ dependencies = [ "const-random", ] +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + [[package]] name = "electrum-client" version = "0.18.0" @@ -2045,6 +2054,12 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + [[package]] name = "lock_api" version = "0.4.12" @@ -2165,6 +2180,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", +] + [[package]] name = "nix" version = "0.30.1" @@ -2194,7 +2220,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -2403,7 +2429,7 @@ dependencies = [ "http-body-util", "hyper", "hyper-util", - "nix", + "nix 0.30.1", "payjoin", "payjoin-test-utils", "r2d2", @@ -2440,6 +2466,7 @@ dependencies = [ "serde", "tempfile", "tokio", + "tokio-listener", "tokio-rustls", "tokio-rustls-acme", "tokio-stream", @@ -2554,6 +2581,26 @@ dependencies = [ "sha2 0.10.8", ] +[[package]] +name = "pin-project" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "pin-project-lite" version = "0.2.14" @@ -3723,6 +3770,24 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "tokio-listener" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8a49c7295dbf705691c238002e4be940d5e0605f204683b1c8e55490032c79d" +dependencies = [ + "clap", + "document-features", + "futures-core", + "futures-util", + "nix 0.26.4", + "pin-project", + "socket2 0.5.10", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "tokio-macros" version = "2.6.0" diff --git a/payjoin-directory/Cargo.toml b/payjoin-directory/Cargo.toml index 35c8704fc..b755c0090 100644 --- a/payjoin-directory/Cargo.toml +++ b/payjoin-directory/Cargo.toml @@ -36,6 +36,7 @@ prometheus = "0.13.4" rand = "0.8" serde = { version = "1.0.219", features = ["derive"] } tokio = { version = "1.47.1", features = ["full"] } +tokio-listener = { version = "0.5", features = ["clap"] } tokio-rustls = { version = "0.26.2", features = [ "ring", ], default-features = false, optional = true } diff --git a/payjoin-directory/docker-compose.yml b/payjoin-directory/docker-compose.yml index e62704261..034208fd4 100644 --- a/payjoin-directory/docker-compose.yml +++ b/payjoin-directory/docker-compose.yml @@ -28,7 +28,7 @@ services: environment: RUST_LOG: "trace" PJ_DB_HOST: "redis:6379" - PJ_DIR_PORT: "8080" + PJ_LISTEN_ADDR: "[::]:8080" depends_on: - redis networks: diff --git a/payjoin-directory/src/cli.rs b/payjoin-directory/src/cli.rs index 548f7e7f0..d05a2bd47 100644 --- a/payjoin-directory/src/cli.rs +++ b/payjoin-directory/src/cli.rs @@ -2,6 +2,7 @@ use std::env; use std::path::PathBuf; use clap::Parser; +use tokio_listener::ListenerAddressLFlag; #[derive(Debug, Parser)] #[command( @@ -10,11 +11,11 @@ use clap::Parser; long_about = None, )] pub struct Cli { - #[arg(long, short = 'p', env = "PJ_DIR_PORT", help = "The port to bind [default: 8080]")] - pub port: Option, // TODO tokio_listener::ListenerAddressLFlag + #[clap(flatten)] + pub listen: ListenerAddressLFlag, - #[arg(long, env = "PJ_METRIC_PORT", help = "The port to bind for prometheus metrics export")] - pub metrics_port: Option, // TODO tokio_listener::ListenerAddressLFlag + #[arg(long = "metrics-listen-addr")] + pub metrics_listen_addr: Option, #[cfg(feature = "acme")] #[clap(flatten)] diff --git a/payjoin-directory/src/config.rs b/payjoin-directory/src/config.rs index c16c34542..53c4e31b8 100644 --- a/payjoin-directory/src/config.rs +++ b/payjoin-directory/src/config.rs @@ -5,15 +5,16 @@ use anyhow::Result; use config::builder::DefaultState; use config::{ConfigError, File, FileFormat}; use serde::Deserialize; +use tokio_listener::ListenerAddress; type Builder = config::builder::ConfigBuilder; use crate::cli::Cli; -#[derive(Debug, Clone, Deserialize)] +#[derive(Debug, Clone)] pub struct Config { - pub listen_addr: String, // TODO tokio_listener::ListenerAddressLFlag - pub metrics_listen_addr: Option, // TODO tokio_listener::ListenerAddressLFlag + pub listen_addr: ListenerAddress, + pub metrics_listen_addr: Option, pub timeout: Duration, pub storage_dir: PathBuf, pub ohttp_keys: PathBuf, // TODO OhttpConfig struct with rotation params, etc @@ -51,8 +52,17 @@ impl Config { let built_config = config.build()?; Ok(Config { - listen_addr: built_config.get("listen_addr")?, - metrics_listen_addr: built_config.get("metrics_listen_addr").ok(), + listen_addr: cli + .listen + .listen_address + .clone() + .ok_or_else(|| ConfigError::Message("listen_addr is required".to_string()))?, + metrics_listen_addr: match &cli.metrics_listen_addr { + Some(s) => Some(s.parse().map_err(|e| { + ConfigError::Message(format!("invalid metrics listen addr: {e}")) + })?), + None => None, + }, timeout: Duration::from_secs(built_config.get("timeout")?), storage_dir: built_config.get("storage_dir")?, ohttp_keys: built_config.get("ohttp_keys")?, @@ -73,13 +83,6 @@ impl Config { fn add_defaults(config: Builder, cli: &Cli) -> Result { let config = config - .set_default("listen_addr", "[::]:8080")? - .set_override_option("listen_addr", cli.port.map(|port| format!("[::]:{}", port)))? - .set_default("metrics_listen_addr", Option::::None)? - .set_override_option( - "metrics_listen_addr", - cli.metrics_port.map(|port| format!("localhost:{}", port)), - )? .set_default("timeout", Some(30))? .set_override_option("timeout", cli.timeout)? .set_default("ohttp_keys", "ohttp_keys")? diff --git a/payjoin-directory/src/lib.rs b/payjoin-directory/src/lib.rs index d571e6746..ac16ca11d 100644 --- a/payjoin-directory/src/lib.rs +++ b/payjoin-directory/src/lib.rs @@ -13,6 +13,7 @@ use hyper::{Method, Request, Response, StatusCode, Uri}; use hyper_util::rt::TokioIo; use payjoin::directory::{ShortId, ShortIdError, ENCAPSULATED_MESSAGE_BYTES}; use tokio::net::TcpListener; +use tokio_listener::Listener; #[cfg(feature = "acme")] use tokio_rustls_acme::AcmeConfig; use tokio_stream::wrappers::TcpListenerStream; @@ -131,10 +132,7 @@ impl Service { self.serve_connections(tls_incoming).await; } - pub async fn serve_tcp(self, listener: TcpListener) { - let tcp_incoming = TcpListenerStream::new(listener); - self.serve_connections(tcp_incoming).await; - } + pub async fn serve_tcp(self, listener: Listener) { self.serve_connections(listener).await; } async fn serve_connections(self, mut incoming_connections: S) where @@ -414,24 +412,30 @@ impl Service { } } - pub async fn serve_metrics_tcp( - &self, - listener: tokio::net::TcpListener, - ) -> Result<(), BoxError> { - while let Ok((stream, _)) = listener.accept().await { - let io = TokioIo::new(stream); - let service = self.clone(); - tokio::spawn(async move { - let service = hyper::service::service_fn(move |req| { - let this = service.clone(); - async move { this.serve_metrics_request(req).await } - }); - if let Err(err) = - http1::Builder::new().serve_connection(io, service).with_upgrades().await - { - error!("Error serving connection: {:?}", err); + pub async fn serve_metrics_tcp(&self, mut listener: Listener) -> Result<(), BoxError> { + while let Some(conn_result) = listener.next().await { + match conn_result { + Ok(stream) => { + let io = TokioIo::new(stream); + let service = self.clone(); + tokio::spawn(async move { + let service = hyper::service::service_fn(move |req| { + let this = service.clone(); + async move { this.serve_metrics_request(req).await } + }); + if let Err(err) = http1::Builder::new() + .serve_connection(io, service) + .with_upgrades() + .await + { + error!("Error serving connection: {:?}", err); + } + }); } - }); + Err(e) => { + error!("Error accepting connection: {:?}", e); + } + } } Ok(()) diff --git a/payjoin-directory/src/main.rs b/payjoin-directory/src/main.rs index 6f00f6598..5bcf07271 100644 --- a/payjoin-directory/src/main.rs +++ b/payjoin-directory/src/main.rs @@ -1,7 +1,7 @@ use clap::Parser; use payjoin_directory::metrics::Metrics; use payjoin_directory::*; -use tokio::net::TcpListener; +use tokio_listener::{SystemOptions, UserOptions}; use tracing_subscriber::filter::LevelFilter; use tracing_subscriber::EnvFilter; @@ -33,19 +33,29 @@ async fn main() -> Result<(), BoxError> { let service = Service::new(db, ohttp.into(), metrics); // Start metrics server in the background - if let Some(addr) = config.metrics_listen_addr { - let metrics_listener = TcpListener::bind(addr.clone()).await?; + if let Some(metrics_addr) = config.metrics_listen_addr.clone() { + let metrics_listener = tokio_listener::Listener::bind( + &metrics_addr, + &SystemOptions::default(), + &UserOptions::default(), + ) + .await?; let service_clone = service.clone(); tokio::spawn(async move { if let Err(e) = service_clone.serve_metrics_tcp(metrics_listener).await { eprintln!("Metrics server error: {e}"); } - println!("Metrics server started on {}", addr); + println!("Metrics server started on {}", metrics_addr); }); } - let listener = TcpListener::bind(config.listen_addr).await?; + let listener = tokio_listener::Listener::bind( + &config.listen_addr, + &SystemOptions::default(), + &UserOptions::default(), + ) + .await?; #[cfg(feature = "acme")] if let Some(acme_config) = config.acme {