From bc3ce6d2c21c95f0916a16aed307bd9f559f6813 Mon Sep 17 00:00:00 2001
From: Clar Fon <15850505+clarfonthey@users.noreply.github.com>
Date: Sun, 7 Jan 2024 15:14:21 -0500
Subject: [PATCH] Allow IPv6 serve, default `base_url` to listen interface
 instead of 127.0.0.1 (#2395)

* Parse interface as IpAddr, allow IPv6.

* Default base_url to socket address, instead of 127.0.0.1
---
 src/cli.rs       |  7 ++++---
 src/cmd/serve.rs | 41 ++++++++++++++++++++++-------------------
 src/main.rs      |  4 ++--
 3 files changed, 28 insertions(+), 24 deletions(-)

diff --git a/src/cli.rs b/src/cli.rs
index b55dc18a5e..1329edff4d 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -1,3 +1,4 @@
+use std::net::IpAddr;
 use std::path::PathBuf;
 
 use clap::{Parser, Subcommand};
@@ -54,7 +55,7 @@ pub enum Command {
     Serve {
         /// Interface to bind on
         #[clap(short = 'i', long, default_value = "127.0.0.1")]
-        interface: String,
+        interface: IpAddr,
 
         /// Which port to use
         #[clap(short = 'p', long, default_value_t = 1111)]
@@ -70,8 +71,8 @@ pub enum Command {
         force: bool,
 
         /// Changes the base_url
-        #[clap(short = 'u', long, default_value = "127.0.0.1")]
-        base_url: String,
+        #[clap(short = 'u', long)]
+        base_url: Option<String>,
 
         /// Include drafts when loading the site
         #[clap(long)]
diff --git a/src/cmd/serve.rs b/src/cmd/serve.rs
index dce008e1fa..d13b945942 100644
--- a/src/cmd/serve.rs
+++ b/src/cmd/serve.rs
@@ -24,7 +24,7 @@
 use std::cell::Cell;
 use std::fs::read_dir;
 use std::future::IntoFuture;
-use std::net::{SocketAddrV4, TcpListener};
+use std::net::{IpAddr, SocketAddr, TcpListener};
 use std::path::{Path, PathBuf, MAIN_SEPARATOR};
 use std::sync::mpsc::channel;
 use std::sync::Mutex;
@@ -321,20 +321,29 @@ fn rebuild_done_handling(broadcaster: &Sender, res: Result<()>, reload_path: &st
 #[allow(clippy::too_many_arguments)]
 fn create_new_site(
     root_dir: &Path,
-    interface: &str,
+    interface: IpAddr,
     interface_port: u16,
     output_dir: Option<&Path>,
     force: bool,
-    base_url: &str,
+    base_url: Option<&str>,
     config_file: &Path,
     include_drafts: bool,
-    no_port_append: bool,
+    mut no_port_append: bool,
     ws_port: Option<u16>,
-) -> Result<(Site, String)> {
+) -> Result<(Site, SocketAddr)> {
     SITE_CONTENT.write().unwrap().clear();
 
     let mut site = Site::new(root_dir, config_file)?;
-    let address = format!("{}:{}", interface, interface_port);
+    let address = SocketAddr::new(interface, interface_port);
+
+    // if no base URL provided, use socket address
+    let base_url = base_url.map_or_else(
+        || {
+            no_port_append = true;
+            address.to_string()
+        },
+        |u| u.to_string(),
+    );
 
     let base_url = if base_url == "/" {
         String::from("/")
@@ -381,11 +390,11 @@ fn create_new_site(
 #[allow(clippy::too_many_arguments)]
 pub fn serve(
     root_dir: &Path,
-    interface: &str,
+    interface: IpAddr,
     interface_port: u16,
     output_dir: Option<&Path>,
     force: bool,
-    base_url: &str,
+    base_url: Option<&str>,
     config_file: &Path,
     open: bool,
     include_drafts: bool,
@@ -394,7 +403,7 @@ pub fn serve(
     utc_offset: UtcOffset,
 ) -> Result<()> {
     let start = Instant::now();
-    let (mut site, address) = create_new_site(
+    let (mut site, bind_address) = create_new_site(
         root_dir,
         interface,
         interface_port,
@@ -409,12 +418,8 @@ pub fn serve(
     messages::report_elapsed_time(start);
 
     // Stop right there if we can't bind to the address
-    let bind_address: SocketAddrV4 = match address.parse() {
-        Ok(a) => a,
-        Err(_) => return Err(anyhow!("Invalid address: {}.", address)),
-    };
     if (TcpListener::bind(bind_address)).is_err() {
-        return Err(anyhow!("Cannot start server on address {}.", address));
+        return Err(anyhow!("Cannot start server on address {}.", bind_address));
     }
 
     let config_path = PathBuf::from(config_file);
@@ -466,8 +471,6 @@ pub fn serve(
 
     let broadcaster = {
         thread::spawn(move || {
-            let addr = address.parse().unwrap();
-
             let rt = tokio::runtime::Builder::new_current_thread()
                 .enable_all()
                 .build()
@@ -484,11 +487,11 @@ pub fn serve(
                     }
                 });
 
-                let server = Server::bind(&addr).serve(make_service);
+                let server = Server::bind(&bind_address).serve(make_service);
 
-                println!("Web server is available at http://{}\n", &address);
+                println!("Web server is available at http://{}\n", bind_address);
                 if open {
-                    if let Err(err) = open::that(format!("http://{}", &address)) {
+                    if let Err(err) = open::that(format!("http://{}", bind_address)) {
                         eprintln!("Failed to open URL in your browser: {}", err);
                     }
                 }
diff --git a/src/main.rs b/src/main.rs
index 59bc80df5c..48e95b9693 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -102,11 +102,11 @@ fn main() {
             console::info("Building site...");
             if let Err(e) = cmd::serve(
                 &root_dir,
-                &interface,
+                interface,
                 port,
                 output_dir.as_deref(),
                 force,
-                &base_url,
+                base_url.as_deref(),
                 &config_file,
                 open,
                 drafts,