Repository: static-web-server/static-web-server (language: rust)

Issue:
Remote address and file request logging
### Discussed in https://github.com/joseluisq/static-web-server/discussions/77

<div type='discussions-op-text'>

<sup>Originally posted by **nicheath** January 11, 2022</sup>
I have been looking through the documentation and have been trying to find a way to log what IP requests what file. It doesn't seem possible yet but I wanted to make sure.</div>


Discussion / hints:

Fix patch:
diff --git a/docs/content/configuration/config-file.md b/docs/content/configuration/config-file.md
index 8346e084..9d827e7a 100644
--- a/docs/content/configuration/config-file.md
+++ b/docs/content/configuration/config-file.md
@@ -60,6 +60,16 @@ grace-period = 0
 #### Page fallback for 404s
 # page-fallback = "some_page.html"
 
+#### Log request Remote Address if available
+log-remote-address = false
+
+
+### Windows Only
+
+#### Run the web server as a Windows Service
+# windows-service = false
+
+
 
 [advanced]
 
diff --git a/src/handler.rs b/src/handler.rs
index a9ab4f74..dbb7905e 100644
--- a/src/handler.rs
+++ b/src/handler.rs
@@ -1,5 +1,5 @@
 use hyper::{header::WWW_AUTHENTICATE, Body, Method, Request, Response, StatusCode};
-use std::{future::Future, path::PathBuf, sync::Arc};
+use std::{future::Future, net::SocketAddr, path::PathBuf, sync::Arc};
 
 use crate::{
     basic_auth, compression, control_headers, cors, custom_headers, error_page, fallback_page,
@@ -20,6 +20,7 @@ pub struct RequestHandlerOpts {
     pub page50x: Vec<u8>,
     pub page_fallback: Vec<u8>,
     pub basic_auth: String,
+    pub log_remote_address: bool,
 
     // Advanced options
     pub advanced_opts: Option<Advanced>,
@@ -35,6 +36,7 @@ impl RequestHandler {
     pub fn handle<'a>(
         &'a self,
         req: &'a mut Request<Body>,
+        remote_addr: Option<SocketAddr>,
     ) -> impl Future<Output = Result<Response<Body>, Error>> + Send + 'a {
         let method = req.method();
         let headers = req.headers();
@@ -45,9 +47,23 @@ impl RequestHandler {
         let uri_query = uri.query();
         let dir_listing = self.opts.dir_listing;
         let dir_listing_order = self.opts.dir_listing_order;
+        let log_remote_addr = self.opts.log_remote_address;
 
         let mut cors_headers: Option<http::HeaderMap> = None;
 
+        // Log request information with its remote address if available
+        let mut remote_addr_str = String::new();
+        if log_remote_addr {
+            remote_addr_str.push_str(" remote_addr=");
+            remote_addr_str.push_str(&remote_addr.map_or("".to_owned(), |v| v.to_string()));
+        }
+        tracing::info!(
+            "incoming request: method={} uri={}{}",
+            method,
+            uri,
+            remote_addr_str,
+        );
+
         async move {
             // Check for disallowed HTTP methods and reject request accordently
             if !(method == Method::GET || method == Method::HEAD || method == Method::OPTIONS) {
diff --git a/src/server.rs b/src/server.rs
index 77f469b4..4b4d6507 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -162,6 +162,10 @@ impl Server {
             !general.basic_auth.is_empty()
         );
 
+        // Log remote address option
+        let log_remote_address = general.log_remote_address;
+        tracing::info!("log remote address: enabled={}", log_remote_address);
+
         // Grace period option
         let grace_period = general.grace_period;
         tracing::info!("grace period before graceful shutdown: {}s", grace_period);
@@ -180,6 +184,7 @@ impl Server {
                 page50x,
                 page_fallback,
                 basic_auth,
+                log_remote_address,
                 advanced_opts,
             }),
         });
diff --git a/src/service.rs b/src/service.rs
index 7f5aa8b4..315fd161 100644
--- a/src/service.rs
+++ b/src/service.rs
@@ -1,12 +1,12 @@
 use hyper::{service::Service, Body, Request, Response};
 use std::convert::Infallible;
 use std::future::{ready, Future, Ready};
+use std::net::SocketAddr;
 use std::pin::Pin;
 use std::sync::Arc;
 use std::task::{Context, Poll};
 
-use crate::handler::RequestHandler;
-use crate::Error;
+use crate::{handler::RequestHandler, transport::Transport, Error};
 
 /// It defines the router service which is the main entry point for Hyper Server.
 pub struct RouterService {
@@ -21,7 +21,7 @@ impl RouterService {
     }
 }
 
-impl<T> Service<T> for RouterService {
+impl<T: Transport + Send + 'static> Service<&T> for RouterService {
     type Response = RequestService;
     type Error = Infallible;
     type Future = Ready<Result<Self::Response, Self::Error>>;
@@ -30,14 +30,15 @@ impl<T> Service<T> for RouterService {
         Poll::Ready(Ok(()))
     }
 
-    fn call(&mut self, _: T) -> Self::Future {
-        ready(Ok(self.builder.build()))
+    fn call(&mut self, conn: &T) -> Self::Future {
+        ready(Ok(self.builder.build(conn.remote_addr())))
     }
 }
 
 /// It defines a Hyper service request which delegates a request handler.
 pub struct RequestService {
     handler: Arc<RequestHandler>,
+    remote_addr: Option<SocketAddr>,
 }
 
 impl Service<Request<Body>> for RequestService {
@@ -51,7 +52,8 @@ impl Service<Request<Body>> for RequestService {
 
     fn call(&mut self, mut req: Request<Body>) -> Self::Future {
         let handler = self.handler.clone();
-        Box::pin(async move { handler.handle(&mut req).await })
+        let remote_addr = self.remote_addr;
+        Box::pin(async move { handler.handle(&mut req, remote_addr).await })
     }
 }
 
@@ -67,9 +69,10 @@ impl RequestServiceBuilder {
         }
     }
 
-    pub fn build(&self) -> RequestService {
+    pub fn build(&self, remote_addr: Option<SocketAddr>) -> RequestService {
         RequestService {
             handler: self.handler.clone(),
+            remote_addr,
         }
     }
 }
diff --git a/src/settings/cli.rs b/src/settings/cli.rs
index 9f2a033d..f062165a 100644
--- a/src/settings/cli.rs
+++ b/src/settings/cli.rs
@@ -170,6 +170,15 @@ pub struct General {
     /// Server TOML configuration file path.
     pub config_file: Option<PathBuf>,
 
+    #[structopt(
+        long,
+        parse(try_from_str),
+        default_value = "false",
+        env = "SERVER_LOG_REMOTE_ADDRESS"
+    )]
+    /// Log incoming requests information along with its remote address if available using the `info` log level.
+    pub log_remote_address: bool,
+
     //
     // Windows specific arguments and commands
     //
diff --git a/src/settings/file.rs b/src/settings/file.rs
index d0b565cc..22be141b 100644
--- a/src/settings/file.rs
+++ b/src/settings/file.rs
@@ -96,6 +96,8 @@ pub struct General {
     pub grace_period: Option<u8>,
 
     pub page_fallback: Option<PathBuf>,
+
+    pub log_remote_address: Option<bool>,
 }
 
 /// Full server configuration
diff --git a/src/settings/mod.rs b/src/settings/mod.rs
index dcb37073..4b793ded 100644
--- a/src/settings/mod.rs
+++ b/src/settings/mod.rs
@@ -61,6 +61,7 @@ impl Settings {
         let mut threads_multiplier = opts.threads_multiplier;
         let mut grace_period = opts.grace_period;
         let mut page_fallback = opts.page_fallback;
+        let mut log_remote_address = opts.log_remote_address;
 
         // Define the advanced file options
         let mut settings_advanced: Option<Advanced> = None;
@@ -145,6 +146,9 @@ impl Settings {
                     if let Some(v) = general.page_fallback {
                         page_fallback = Some(v)
                     }
+                    if let Some(v) = general.log_remote_address {
+                        log_remote_address = v
+                    }
                 }
 
                 // Prepare the "advanced" options
@@ -206,6 +210,7 @@ impl Settings {
                 threads_multiplier,
                 grace_period,
                 page_fallback,
+                log_remote_address,
 
                 // NOTE:
                 // Windows-only options and commands


Test patch:
diff --git a/tests/toml/config.toml b/tests/toml/config.toml
index 51de796c..47ff0905 100644
--- a/tests/toml/config.toml
+++ b/tests/toml/config.toml
@@ -45,6 +45,19 @@ grace-period = 0
 #### Page fallback for 404s
 page-fallback = ""
 
+#### Page fallback for 404s
+page-fallback = ""
+
+#### Log request Remote Address if available
+log-remote-address = false
+
+
+### Windows Only
+
+#### Run the web server as a Windows Service
+# windows-service = false
+
+
 [advanced]
 
 #### HTTP Headers customization
@@ -56,7 +69,7 @@ headers = { Access-Control-Allow-Origin = "*", X-XSS-PROTECTION = "1; mode=block
 
 # #### b. Multiline version
 [[advanced.headers]]
-source = "index.html"
+source = "/index.html"
 [advanced.headers.headers]
 Cache-Control = "public, max-age=36000"
 Content-Security-Policy = "frame-ancestors 'self'"