From af7a760f24ca4d5a72d5b5d9e2dbf95465e6a867 Mon Sep 17 00:00:00 2001 From: Ali MJ Al-Nasrawy Date: Sun, 18 Apr 2021 18:55:35 +0300 Subject: Fail if any address fails to bind --- src/main.rs | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index c1d17b4..4ce2bd1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -240,25 +240,20 @@ async fn run(miniserve_config: MiniserveConfig) -> Result<(), ContextualError> { .default_service(web::get().to(error_404)) }); - #[cfg(feature = "tls")] - let srv = if let Some(tls_config) = miniserve_config.tls_rustls_config { - srv.bind_rustls(socket_addresses.as_slice(), tls_config) - .map_err(|e| ContextualError::IoError("Failed to bind server".to_string(), e))? - .shutdown_timeout(0) - .run() - } else { - srv.bind(socket_addresses.as_slice()) - .map_err(|e| ContextualError::IoError("Failed to bind server".to_string(), e))? - .shutdown_timeout(0) - .run() - }; + let srv = socket_addresses.iter().try_fold(srv, |srv, addr| { + #[cfg(feature = "tls")] + let srv = match &miniserve_config.tls_rustls_config { + Some(tls_config) => srv.bind_rustls(addr, tls_config.clone()), + None => srv.bind(addr), + }; + + #[cfg(not(feature = "tls"))] + let srv = srv.bind(addr); + + srv.map_err(|e| ContextualError::IoError(format!("Failed to bind server to {}", addr), e)) + })?; - #[cfg(not(feature = "tls"))] - let srv = srv - .bind(socket_addresses.as_slice()) - .map_err(|e| ContextualError::IoError("Failed to bind server".to_string(), e))? - .shutdown_timeout(0) - .run(); + let srv = srv.shutdown_timeout(0).run(); println!( "Serving path {path} at {addresses}", -- cgit v1.2.3 From 0ca6e36011df820a8848b77b0e126ace81df86b1 Mon Sep 17 00:00:00 2001 From: Ali MJ Al-Nasrawy Date: Sun, 18 Apr 2021 18:57:35 +0300 Subject: Fix -i 0.0.0.0 Don't use `interfaces`, use `miniserve_config.interfaces` instead! Otherwise, "0.0.0.0" is converted to "localhost"! --- src/main.rs | 27 ++++----------------------- 1 file changed, 4 insertions(+), 23 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 4ce2bd1..19bd16b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -196,30 +196,11 @@ async fn run(miniserve_config: MiniserveConfig) -> Result<(), ContextualError> { } } - let socket_addresses = interfaces + let socket_addresses = miniserve_config + .interfaces .iter() - .map(|interface| { - format!( - "{interface}:{port}", - interface = &interface, - port = miniserve_config.port, - ) - .parse::() - }) - .collect::, _>>(); - - let socket_addresses = match socket_addresses { - Ok(addresses) => addresses, - Err(e) => { - // Note that this should never fail, since CLI parsing succeeded - // This means the format of each IP address is valid, and so is the port - // Valid IpAddr + valid port == valid SocketAddr - return Err(ContextualError::ParseError( - "string as socket address".to_string(), - e.to_string(), - )); - } - }; + .map(|&interface| SocketAddr::new(interface, miniserve_config.port)) + .collect::>(); let srv = actix_web::HttpServer::new(move || { App::new() -- cgit v1.2.3 From 28da05437de8f15df379041a653ee72cf136204b Mon Sep 17 00:00:00 2001 From: Ali MJ Al-Nasrawy Date: Fri, 7 May 2021 03:05:09 +0300 Subject: Fix default binding behaviour On some platforms, binding to to both "::" and "0.0.0.0" at the same time is not allowed because "::" may already accepts ipv4 connections. For other platforms, binding to both is necessary to support ipv4 and ipv6. This platform-specific behaviour is due to the variation in the default value for the socket option "IPV6_ONLY". Fix this by always setting the "IPv6_ONLY" sockopt to true! --- src/main.rs | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 19bd16b..d34f0aa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ use std::io; use std::io::Write; -use std::net::{IpAddr, Ipv4Addr, SocketAddr}; +use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpListener}; use std::thread; use std::time::Duration; @@ -200,7 +200,7 @@ async fn run(miniserve_config: MiniserveConfig) -> Result<(), ContextualError> { .interfaces .iter() .map(|&interface| SocketAddr::new(interface, miniserve_config.port)) - .collect::>(); + .collect::>(); let srv = actix_web::HttpServer::new(move || { App::new() @@ -222,14 +222,18 @@ async fn run(miniserve_config: MiniserveConfig) -> Result<(), ContextualError> { }); let srv = socket_addresses.iter().try_fold(srv, |srv, addr| { + let listener = create_tcp_listener(*addr).map_err(|e| { + ContextualError::IoError(format!("Failed to bind server to {}", addr), e) + })?; + #[cfg(feature = "tls")] let srv = match &miniserve_config.tls_rustls_config { - Some(tls_config) => srv.bind_rustls(addr, tls_config.clone()), - None => srv.bind(addr), + Some(tls_config) => srv.listen_rustls(listener, tls_config.clone()), + None => srv.listen(listener), }; #[cfg(not(feature = "tls"))] - let srv = srv.bind(addr); + let srv = srv.listen(listener); srv.map_err(|e| ContextualError::IoError(format!("Failed to bind server to {}", addr), e)) })?; @@ -250,6 +254,19 @@ async fn run(miniserve_config: MiniserveConfig) -> Result<(), ContextualError> { .map_err(|e| ContextualError::IoError("".to_owned(), e)) } +/// Allows us to set low-level socket options +fn create_tcp_listener(addr: SocketAddr) -> io::Result { + use socket2::{Domain, Protocol, Socket, Type}; + let socket = Socket::new(Domain::for_address(addr), Type::STREAM, Some(Protocol::TCP))?; + if addr.is_ipv6() { + socket.set_only_v6(true)?; + } + socket.set_reuse_address(true)?; + socket.bind(&addr.into())?; + socket.listen(1024 /* Default backlog */)?; + Ok(TcpListener::from(socket)) +} + fn configure_header(conf: &MiniserveConfig) -> middleware::DefaultHeaders { let headers = conf.clone().header; -- cgit v1.2.3 From 5692a9740c447674aeba2743315bab52e44ccf36 Mon Sep 17 00:00:00 2001 From: Ali MJ Al-Nasrawy Date: Fri, 7 May 2021 18:29:10 +0300 Subject: Show IP addresses of all local interfaces .. when binding to wildcard addresses (:: or 0.0.0.0) * Remove local variable `interfaces` because it is no longer used multiple times. --- src/main.rs | 91 +++++++++++++++++++++++++++++-------------------------------- 1 file changed, 43 insertions(+), 48 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index d34f0aa..9ea784b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ use std::io; use std::io::Write; -use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpListener}; +use std::net::{IpAddr, SocketAddr, TcpListener}; use std::thread; use std::time::Duration; @@ -102,23 +102,6 @@ async fn run(miniserve_config: MiniserveConfig) -> Result<(), ContextualError> { let inside_config = miniserve_config.clone(); - let interfaces = miniserve_config - .interfaces - .iter() - .map(|&interface| { - if interface == IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)) { - // If the interface is 0.0.0.0, we'll change it to 127.0.0.1 so that clicking the link will - // also work on Windows. Why can't Windows interpret 0.0.0.0? - "127.0.0.1".to_string() - } else if interface.is_ipv6() { - // If the interface is IPv6 then we'll print it with brackets so that it is clickable. - format!("[{}]", interface) - } else { - format!("{}", interface) - } - }) - .collect::>(); - let canon_path = miniserve_config.path.canonicalize().map_err(|e| { ContextualError::IoError("Failed to resolve path to be served".to_string(), e) })?; @@ -164,37 +147,49 @@ async fn run(miniserve_config: MiniserveConfig) -> Result<(), ContextualError> { thread::sleep(Duration::from_millis(500)); } } - let mut addresses = String::new(); - for interface in &interfaces { - if !addresses.is_empty() { - addresses.push_str(", "); - } - let protocol = if miniserve_config.tls_rustls_config.is_some() { - "https" - } else { - "http" - }; - addresses.push_str(&format!( - "{}", - Color::Green - .paint(format!( - "{protocol}://{interface}:{port}", - protocol = protocol, - interface = &interface, - port = miniserve_config.port - )) - .bold() - )); - - if let Some(random_route) = miniserve_config.clone().random_route { - addresses.push_str(&format!( - "{}", - Color::Green - .paint(format!("/{random_route}", random_route = random_route,)) - .bold() - )); + + let addresses = { + let (mut ifaces, wildcard): (Vec<_>, Vec<_>) = miniserve_config + .interfaces + .clone() + .into_iter() + .partition(|addr| !addr.is_unspecified()); + + // Replace wildcard addresses with local interface addresses + if !wildcard.is_empty() { + let all_ipv4 = wildcard.iter().any(|addr| addr.is_ipv4()); + let all_ipv6 = wildcard.iter().any(|addr| addr.is_ipv6()); + ifaces = get_if_addrs::get_if_addrs() + .unwrap_or_else(|e| { + error!("Failed to get local interface addresses: {}", e); + Default::default() + }) + .into_iter() + .map(|iface| iface.ip()) + .filter(|ip| (all_ipv4 && ip.is_ipv4()) || (all_ipv6 && ip.is_ipv6())) + .collect(); + ifaces.sort(); } - } + + let urls = ifaces + .into_iter() + .map(|addr| match addr { + IpAddr::V4(_) => format!("{}:{}", addr, miniserve_config.port), + IpAddr::V6(_) => format!("[{}]:{}", addr, miniserve_config.port), + }) + .map(|addr| match miniserve_config.tls_rustls_config { + Some(_) => format!("https://{}", addr), + None => format!("http://{}", addr), + }) + .map(|url| match miniserve_config.random_route { + Some(ref random_route) => format!("{}/{}", url, random_route), + None => url, + }) + .map(|url| Color::Green.paint(url).bold().to_string()) + .collect::>(); + + urls.join(", ") + }; let socket_addresses = miniserve_config .interfaces -- cgit v1.2.3 From 1d421e4c6a546c149d2729b1cb6d810693851a33 Mon Sep 17 00:00:00 2001 From: Ali MJ Al-Nasrawy Date: Fri, 7 May 2021 19:15:46 +0300 Subject: Use exit codes for failure --- src/main.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 9ea784b..6a340f0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -52,10 +52,11 @@ fn main() -> Result<()> { let miniserve_config = MiniserveConfig::try_from_args(args)?; - match run(miniserve_config) { - Ok(()) => (), - Err(e) => errors::log_error_chain(e.to_string()), - } + run(miniserve_config).map_err(|e| { + errors::log_error_chain(e.to_string()); + e + })?; + Ok(()) } -- cgit v1.2.3 From c872e896d5518437bde5cf3affdf9b9f4e59e244 Mon Sep 17 00:00:00 2001 From: Ali MJ Al-Nasrawy Date: Sun, 29 Aug 2021 05:52:01 +0300 Subject: add comment to create_tcp_listener --- src/main.rs | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 6a340f0..bb080e6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -251,6 +251,10 @@ async fn run(miniserve_config: MiniserveConfig) -> Result<(), ContextualError> { } /// Allows us to set low-level socket options +/// +/// This mainly used to set `set_only_v6` socket option +/// to get a consistent behavior across platforms. +/// see: https://github.com/svenstaro/miniserve/pull/500 fn create_tcp_listener(addr: SocketAddr) -> io::Result { use socket2::{Domain, Protocol, Socket, Type}; let socket = Socket::new(Domain::for_address(addr), Type::STREAM, Some(Protocol::TCP))?; -- cgit v1.2.3 From 7dd8d84a1fb583b116dc59c4157f91dabe1e967e Mon Sep 17 00:00:00 2001 From: Ali MJ Al-Nasrawy Date: Sun, 29 Aug 2021 06:05:43 +0300 Subject: order interface vertically --- src/main.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index bb080e6..a537bf9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -189,7 +189,7 @@ async fn run(miniserve_config: MiniserveConfig) -> Result<(), ContextualError> { .map(|url| Color::Green.paint(url).bold().to_string()) .collect::>(); - urls.join(", ") + urls.join("\n\t") }; let socket_addresses = miniserve_config @@ -237,13 +237,13 @@ async fn run(miniserve_config: MiniserveConfig) -> Result<(), ContextualError> { let srv = srv.shutdown_timeout(0).run(); println!( - "Serving path {path} at {addresses}", + "Serving path {path} at:\n\t{addresses}\n", path = Color::Yellow.paint(path_string).bold(), addresses = addresses, ); if atty::is(atty::Stream::Stdout) { - println!("\nQuit by pressing CTRL-C"); + println!("Quit by pressing CTRL-C"); } srv.await -- cgit v1.2.3 From 55a07105a5c6b5f9ba0224c3f54c1076b63888ab Mon Sep 17 00:00:00 2001 From: Ali MJ Al-Nasrawy Date: Mon, 30 Aug 2021 06:58:36 +0300 Subject: address review comment --- src/main.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index a537bf9..882fd08 100644 --- a/src/main.rs +++ b/src/main.rs @@ -149,7 +149,7 @@ async fn run(miniserve_config: MiniserveConfig) -> Result<(), ContextualError> { } } - let addresses = { + let display_urls = { let (mut ifaces, wildcard): (Vec<_>, Vec<_>) = miniserve_config .interfaces .clone() @@ -172,7 +172,7 @@ async fn run(miniserve_config: MiniserveConfig) -> Result<(), ContextualError> { ifaces.sort(); } - let urls = ifaces + ifaces .into_iter() .map(|addr| match addr { IpAddr::V4(_) => format!("{}:{}", addr, miniserve_config.port), @@ -187,9 +187,7 @@ async fn run(miniserve_config: MiniserveConfig) -> Result<(), ContextualError> { None => url, }) .map(|url| Color::Green.paint(url).bold().to_string()) - .collect::>(); - - urls.join("\n\t") + .collect::>() }; let socket_addresses = miniserve_config @@ -198,6 +196,11 @@ async fn run(miniserve_config: MiniserveConfig) -> Result<(), ContextualError> { .map(|&interface| SocketAddr::new(interface, miniserve_config.port)) .collect::>(); + let display_sockets = socket_addresses + .iter() + .map(|sock| Color::Green.paint(sock.to_string()).bold().to_string()) + .collect::>(); + let srv = actix_web::HttpServer::new(move || { App::new() .wrap(configure_header(&inside_config.clone())) @@ -236,10 +239,13 @@ async fn run(miniserve_config: MiniserveConfig) -> Result<(), ContextualError> { let srv = srv.shutdown_timeout(0).run(); + println!("Bound to {}", display_sockets.join(", ")); + + println!("Serving path {}", Color::Yellow.paint(path_string).bold()); + println!( - "Serving path {path} at:\n\t{addresses}\n", - path = Color::Yellow.paint(path_string).bold(), - addresses = addresses, + "Availabe at (non-exhaustive list):\n {}\n", + display_urls.join("\n "), ); if atty::is(atty::Stream::Stdout) { -- cgit v1.2.3