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

Issue:
Minor optimizations / potential improvements
There's a few things that popped up while looking at the code that could be improved:
* The [`Mutex` in `FileStream`](https://github.com/static-web-server/static-web-server/blob/b2cff1b1661b071617a021a21ddc2b6c9481c5dc/src/static_files.rs#L370) is unnecessary. This was probably added as a way to get around the borrow checker but futures don't get polled concurrently so there's no need use one here.
* There's a couple of instances where `PathBuf`s are being cloned (e.g. [this one](https://github.com/static-web-server/static-web-server/blob/b2cff1b1661b071617a021a21ddc2b6c9481c5dc/src/static_files.rs#L155)) that could be avoided.
* The mime type within the content-type header is being [cloned unnecessarily](https://github.com/static-web-server/static-web-server/blob/b2cff1b1661b071617a021a21ddc2b6c9481c5dc/src/compression.rs#L73). The content-type header has a `Mime` inside which can be turned into a str ref so you can directly look it up in the slice of mime types without turning it into a `String`.

None of these are very important/should be noticeable but I think fixing them will also make the code a bit cleaner.


Discussion / hints:

Fix patch:
diff --git a/src/compression.rs b/src/compression.rs
index a85b6ff0..349f567c 100644
--- a/src/compression.rs
+++ b/src/compression.rs
@@ -9,6 +9,7 @@ use hyper::{
     header::{HeaderValue, CONTENT_ENCODING, CONTENT_LENGTH},
     Body, Method, Response,
 };
+use mime_guess::Mime;
 use pin_project::pin_project;
 use std::pin::Pin;
 use std::task::{Context, Poll};
@@ -70,8 +71,8 @@ pub fn auto(
     if let Some(encoding) = get_prefered_encoding(headers) {
         // Skip compression for non-text-based MIME types
         if let Some(content_type) = resp.headers().typed_get::<ContentType>() {
-            let content_type = &content_type.to_string();
-            if !TEXT_MIME_TYPES.iter().any(|h| *h == content_type) {
+            let mime = Mime::from(content_type);
+            if !TEXT_MIME_TYPES.iter().any(|h| *h == mime) {
                 return Ok(resp);
             }
         }
diff --git a/src/compression_static.rs b/src/compression_static.rs
index a5693404..f7762499 100644
--- a/src/compression_static.rs
+++ b/src/compression_static.rs
@@ -1,13 +1,17 @@
 use headers::{ContentCoding, HeaderMap, HeaderValue};
-use std::{fs::Metadata, path::PathBuf};
+use std::{
+    ffi::OsStr,
+    fs::Metadata,
+    path::{Path, PathBuf},
+};
 
 use crate::{compression, static_files::file_metadata};
 
 /// Search for the pre-compressed variant of the given file path.
-pub async fn precompressed_variant(
-    file_path: PathBuf,
-    headers: &HeaderMap<HeaderValue>,
-) -> Option<(PathBuf, Metadata, &str)> {
+pub async fn precompressed_variant<'a>(
+    file_path: &Path,
+    headers: &'a HeaderMap<HeaderValue>,
+) -> Option<(PathBuf, Metadata, &'a str)> {
     let mut precompressed = None;
 
     tracing::trace!(
@@ -30,11 +34,10 @@ pub async fn precompressed_variant(
 
     // Try to find the pre-compressed metadata variant for the given file path
     if let Some(ext) = precomp_ext {
-        let mut filepath_precomp = file_path;
-        if let Some(filename) = filepath_precomp.file_name() {
-            let filename = filename.to_str().unwrap();
+        let filename = file_path.file_name().and_then(OsStr::to_str);
+        if let Some(filename) = filename {
             let precomp_file_name = [filename, ".", ext].concat();
-            filepath_precomp.set_file_name(precomp_file_name);
+            let filepath_precomp = file_path.with_file_name(precomp_file_name);
 
             tracing::trace!(
                 "getting metadata for pre-compressed file variant {}",
diff --git a/src/static_files.rs b/src/static_files.rs
index 19f6f898..d8fbed4c 100644
--- a/src/static_files.rs
+++ b/src/static_files.rs
@@ -18,7 +18,6 @@ use std::io::{self, BufReader, Read, Seek, SeekFrom};
 use std::ops::Bound;
 use std::path::{Component, Path, PathBuf};
 use std::pin::Pin;
-use std::sync::Mutex;
 use std::task::{Context, Poll};
 
 use crate::directory_listing::DirListFmt;
@@ -152,7 +151,7 @@ async fn composed_file_metadata<'a>(
     if compression_static {
         tried_precompressed = true;
         if let Some((path, meta, ext)) =
-            compression_static::precompressed_variant(file_path.clone(), headers).await
+            compression_static::precompressed_variant(file_path, headers).await
         {
             return Ok((file_path, meta, false, Some((path, ext))));
         }
@@ -180,7 +179,7 @@ async fn composed_file_metadata<'a>(
             // Second pre-compressed variant check for the given file path
             if compression_static && !tried_precompressed {
                 if let Some((path, meta, ext)) =
-                    compression_static::precompressed_variant(file_path.clone(), headers).await
+                    compression_static::precompressed_variant(file_path, headers).await
                 {
                     return Ok((file_path, meta, false, Some((path, ext))));
                 }
@@ -220,7 +219,7 @@ fn file_reply<'a>(
 ) -> impl Future<Output = Result<Response<Body>, StatusCode>> + Send + 'a {
     let conditionals = get_conditional_headers(headers);
 
-    let file_path = path_precompressed.unwrap_or_else(|| path.clone());
+    let file_path = path_precompressed.as_ref().unwrap_or(path);
 
     match File::open(file_path) {
         Ok(file) => Either::Left(response_body(file, path, meta, conditionals)),
@@ -367,23 +366,15 @@ const READ_BUF_SIZE: usize = 8_192;
 
 #[derive(Debug)]
 pub struct FileStream<T> {
-    reader: Mutex<T>,
+    reader: T,
 }
 
-impl<T: Read> Stream for FileStream<T> {
+impl<T: Read + Unpin> Stream for FileStream<T> {
     type Item = Result<Bytes>;
 
     fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Option<Self::Item>> {
-        let mut reader = match self.reader.lock() {
-            Ok(reader) => reader,
-            Err(err) => {
-                tracing::error!("file stream error: {:?}", err);
-                return Poll::Ready(Some(Err(anyhow::anyhow!("failed to read stream file"))));
-            }
-        };
-
         let mut buf = BytesMut::zeroed(READ_BUF_SIZE);
-        match reader.read(&mut buf[..]) {
+        match Pin::into_inner(self).reader.read(&mut buf[..]) {
             Ok(n) => {
                 if n == 0 {
                     Poll::Ready(None)
@@ -420,7 +411,7 @@ async fn response_body(
                     };
 
                     let sub_len = end - start;
-                    let reader = Mutex::new(BufReader::new(file).take(sub_len));
+                    let reader = BufReader::new(file).take(sub_len);
                     let stream = FileStream { reader };
 
                     let body = Body::wrap_stream(stream);


Test patch: