Skip to content

Commit 3e5c078

Browse files
committed
fix(server) :: report the bounded port
Before the startup banner was built from the configuration not the one SQLPage was bound to. Setting `port` to `0`, which asks the operating system for a free port, announced `http://127.0.0.1:0`. Now it reports `HttpServer::addrs()`.
1 parent 2a0102e commit 3e5c078

2 files changed

Lines changed: 49 additions & 16 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
- OIDC now checks both normalized request paths and their resolved SQL files against protected prefixes, closing authentication bypasses through path and clean-URL aliases. Nonce verification also rejects provider-returned Argon2 parameters outside SQLPage's fixed low-cost profile before hashing.
77
- `cargo install sqlpage`, and any build from the crates.io tarball, no longer needs internet access. The browser libraries now come from npm and ship inside the published crate. Building from a git checkout needs `npm ci` first. Pre-built binaries and the Docker image are unaffected.
88
- The browser libraries are now part of the browser scripts. SQLPage no longer defines the `window.tabler` and `window.bootstrap` globals; custom scripts that reached for them should load their own copy of Bootstrap.
9+
- The startup message now reports the address the server actually bound instead of the one it was configured with.
910

1011
## v0.46.3
1112

‎src/webserver/http.rs‎

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ use chrono::{DateTime, Utc};
4040
use futures_util::StreamExt;
4141
use futures_util::stream::Stream;
4242
use std::borrow::Cow;
43+
use std::net::SocketAddr;
4344
use std::path::PathBuf;
4445
use std::pin::Pin;
4546
use std::sync::Arc;
@@ -680,7 +681,7 @@ pub async fn run_server(config: &AppConfig, state: AppState) -> anyhow::Result<(
680681
}
681682
}
682683

683-
log_welcome_message(config);
684+
log_welcome_message(config, &server.addrs());
684685
server
685686
.run()
686687
.await
@@ -691,25 +692,33 @@ pub async fn run_server(config: &AppConfig, state: AppState) -> anyhow::Result<(
691692
Ok(())
692693
}
693694

694-
fn log_welcome_message(config: &AppConfig) {
695+
fn website_url(bound_to: SocketAddr) -> String {
696+
let port = bound_to.port();
697+
let ip = bound_to.ip();
698+
if ip.is_unspecified() {
699+
format!(
700+
"http://localhost:{port}\n\
701+
(also accessible from other devices using your IP address)"
702+
)
703+
} else if ip.is_ipv6() {
704+
format!("http://[{ip}]:{port}")
705+
} else {
706+
format!("http://{ip}:{port}")
707+
}
708+
}
709+
710+
fn log_welcome_message(config: &AppConfig, bound_to: &[SocketAddr]) {
695711
let address_message = if let Some(unix_socket) = &config.unix_socket {
696712
format!("unix socket \"{}\"", unix_socket.display())
697713
} else if let Some(domain) = &config.https_domain {
698714
format!("https://{domain}")
699715
} else {
700-
let listen_on = config.listen_on();
701-
let port = listen_on.port();
702-
let ip = listen_on.ip();
703-
if ip.is_unspecified() {
704-
format!(
705-
"http://localhost:{port}\n\
706-
(also accessible from other devices using your IP address)"
707-
)
708-
} else if ip.is_ipv6() {
709-
format!("http://[{ip}]:{port}")
710-
} else {
711-
format!("http://{ip}:{port}")
712-
}
716+
bound_to
717+
.iter()
718+
.copied()
719+
.map(website_url)
720+
.collect::<Vec<String>>()
721+
.join("\n")
713722
};
714723

715724
let (sparkle, link, computer, rocket) = if cfg!(target_os = "windows") {
@@ -747,10 +756,33 @@ fn bind_unix_socket_err(e: std::io::Error, unix_socket: &std::path::Path) -> any
747756

748757
#[cfg(test)]
749758
mod tests {
750-
use super::{request_span_name, sql_execution_span_name};
759+
use super::{request_span_name, sql_execution_span_name, website_url};
751760
use actix_web::test::TestRequest;
752761
use std::path::Path;
753762

763+
#[test]
764+
fn website_url_reports_the_address_the_server_bound() {
765+
assert_eq!(
766+
website_url("127.0.0.1:34567".parse().unwrap()),
767+
"http://127.0.0.1:34567"
768+
);
769+
}
770+
771+
#[test]
772+
fn website_url_sends_an_unspecified_address_to_localhost() {
773+
assert!(
774+
website_url("0.0.0.0:8080".parse().unwrap()).starts_with("http://localhost:8080\n")
775+
);
776+
}
777+
778+
#[test]
779+
fn website_url_brackets_an_ipv6_address() {
780+
assert_eq!(
781+
website_url("[::1]:8080".parse().unwrap()),
782+
"http://[::1]:8080"
783+
);
784+
}
785+
754786
#[test]
755787
fn request_span_name_uses_request_path_when_no_matched_route_exists() {
756788
let request = TestRequest::with_uri("/todos/42?filter=open").to_srv_request();

0 commit comments

Comments
 (0)