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(-)
@@ -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) {
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);
}
}
@@ -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};
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(
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 {}",
@@ -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>(
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);