index : static-web-server.git

ascending towards madness

author Matias Fontanini <matias.fontanini@gmail.com> 2023-02-26 22:59:32.0 +00:00:00
committer GitHub <noreply@github.com> 2023-02-26 22:59:32.0 +00:00:00
commit
9796d35a8b50e15cbe3abecd8888065a1bdc888d [patch]
tree
8d7e60fd42db5121de40b37ef66eb92a7886a9e3
parent
b2cff1b1661b071617a021a21ddc2b6c9481c5dc
download
9796d35a8b50e15cbe3abecd8888065a1bdc888d.tar.gz

refactor: minor optimizations and small improvements (#177)

* Avoid content-type header's contents cloning
* Don't wrap the file being read in a Mutex
* Avoid cloning paths when possible
* Simplify compressed file path handling logic

Diff

 src/compression.rs        |  5 +++--
 src/compression_static.rs | 21 ++++++++++++---------
 src/static_files.rs       | 23 +++++++----------------
 3 files changed, 22 insertions(+), 27 deletions(-)

diff --git a/src/compression.rs b/src/compression.rs
index a85b6ff..349f567 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 a569340..f776249 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 19f6f89..d8fbed4 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);