Skip to content

Commit

Permalink
StaticDir support filter and Serve compressed static assets if availa…
Browse files Browse the repository at this point in the history
…ble (#510)

* StaticDir support filter and Serve compressed static assets if available

* Format Rust code using rustfmt

* fix ci

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
chrislearn and github-actions[bot] authored Nov 26, 2023
1 parent 2a4089b commit 04e0d20
Show file tree
Hide file tree
Showing 10 changed files with 236 additions and 56 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ indexmap = "2"
inventory = "0.3"
jsonwebtoken = "9.1"
mime = "0.3"
mime-infer = "2"
mime-infer = "3"
moka = "0.12"
multer = "2"
multimap = "0.9"
Expand Down
57 changes: 28 additions & 29 deletions crates/compression/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
//! Compression middleware for for Savlo web server framework.
//!
//! Read more: <https://salvo.rs>
use std::fmt::{self, Display};
use std::str::FromStr;

use indexmap::IndexMap;

use salvo_core::http::body::ResBody;
use salvo_core::http::header::{HeaderValue, ACCEPT_ENCODING, CONTENT_ENCODING, CONTENT_LENGTH, CONTENT_TYPE};
use salvo_core::http::{Mime, StatusCode};
use salvo_core::http::{self, Mime, StatusCode};
use salvo_core::{async_trait, Depot, FlowCtrl, Handler, Request, Response};

mod encoder;
Expand Down Expand Up @@ -53,7 +54,6 @@ pub enum CompressionAlgo {
#[cfg_attr(docsrs, doc(cfg(feature = "zstd")))]
Zstd,
}
impl CompressionAlgo {}

impl FromStr for CompressionAlgo {
type Err = String;
Expand All @@ -78,6 +78,22 @@ impl FromStr for CompressionAlgo {
}
}

impl Display for CompressionAlgo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
#[cfg(feature = "brotli")]
CompressionAlgo::Brotli => write!(f, "br"),
#[cfg(feature = "deflate")]
CompressionAlgo::Deflate => write!(f, "deflate"),
#[cfg(feature = "gzip")]
CompressionAlgo::Gzip => write!(f, "gzip"),
#[cfg(feature = "zstd")]
CompressionAlgo::Zstd => write!(f, "zstd"),
_ => write!(f, "unreached"),
}
}
}

impl From<CompressionAlgo> for HeaderValue {
#[inline]
fn from(algo: CompressionAlgo) -> Self {
Expand Down Expand Up @@ -267,7 +283,16 @@ impl Compression {
}
let header = req.headers().get(ACCEPT_ENCODING).and_then(|v| v.to_str().ok())?;

let accept_algos = parse_accept_encoding(header);
let accept_algos = http::parse_accept_encoding(header)
.into_iter()
.filter_map(|(algo, level)| {
if let Ok(algo) = algo.parse::<CompressionAlgo>() {
Some((algo, level))
} else {
None
}
})
.collect::<Vec<_>>();
if self.force_priority {
let accept_algos = accept_algos.into_iter().map(|(algo, _)| algo).collect::<Vec<_>>();
self.algos
Expand All @@ -282,32 +307,6 @@ impl Compression {
}
}

fn parse_accept_encoding(header: &str) -> Vec<(CompressionAlgo, u8)> {
let mut vec = header
.split(',')
.filter_map(|s| {
let mut iter = s.trim().split(';');
let (algo, q) = (iter.next()?, iter.next());
let algo = algo.trim().parse().ok()?;
let q = q
.and_then(|q| {
q.trim()
.strip_prefix("q=")
.and_then(|q| q.parse::<f32>().map(|f| (f * 100.0) as u8).ok())
})
.unwrap_or(100u8);
Some((algo, q))
})
.collect::<Vec<(CompressionAlgo, u8)>>();

vec.sort_by(|(_, a), (_, b)| match b.cmp(a) {
std::cmp::Ordering::Equal => std::cmp::Ordering::Greater,
other => other,
});

vec
}

#[async_trait]
impl Handler for Compression {
async fn handle(&self, req: &mut Request, depot: &mut Depot, res: &mut Response, ctrl: &mut FlowCtrl) {
Expand Down
30 changes: 29 additions & 1 deletion crates/core/src/http/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,36 @@ pub trait HttpConnection {
// }
// }

#[doc(hidden)]
pub fn parse_accept_encoding(header: &str) -> Vec<(String, u8)> {
let mut vec = header
.split(',')
.filter_map(|s| {
let mut iter = s.trim().split(';');
let (algo, q) = (iter.next()?, iter.next());
let algo = algo.trim();
let q = q
.and_then(|q| {
q.trim()
.strip_prefix("q=")
.and_then(|q| q.parse::<f32>().map(|f| (f * 100.0) as u8).ok())
})
.unwrap_or(100u8);
Some((algo.to_owned(), q))
})
.collect::<Vec<(String, u8)>>();

vec.sort_by(|(_, a), (_, b)| match b.cmp(a) {
std::cmp::Ordering::Equal => std::cmp::Ordering::Greater,
other => other,
});

vec
}

#[doc(hidden)]
#[inline]
pub(crate) fn guess_accept_mime(req: &Request, default_type: Option<Mime>) -> Mime {
pub fn guess_accept_mime(req: &Request, default_type: Option<Mime>) -> Mime {
let dmime: Mime = default_type.unwrap_or_else(|| "text/html".parse().unwrap());
let accept = req.accept();
accept.first().unwrap_or(&dmime).to_string().parse().unwrap_or(dmime)
Expand Down
Loading

0 comments on commit 04e0d20

Please sign in to comment.