Skip to content

Commit

Permalink
feat: add --compress option (#319)
Browse files Browse the repository at this point in the history
  • Loading branch information
sigoden authored Dec 14, 2023
1 parent cd84dff commit 3873f47
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 10 deletions.
49 changes: 49 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ percent-encoding = "2.3"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
futures = "0.3"
async_zip = { version = "0.0.15", default-features = false, features = ["deflate", "chrono", "tokio"] }
async_zip = { version = "0.0.15", default-features = false, features = ["deflate", "bzip2", "xz", "chrono", "tokio"] }
headers = "0.3"
mime_guess = "2.0"
if-addrs = "0.10.1"
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ Options:
--render-spa Serve SPA(Single Page Application)
--assets <path> Set the path to the assets directory for overriding the built-in assets
--log-format <format> Customize http log format
--compress <level> Set zip compress level [default: low] [possible values: none, low, medium, high]
--completions <shell> Print shell completion script for <shell> [possible values: bash, elvish, fish, powershell, zsh]
--tls-cert <path> Path to an SSL/TLS certificate to serve with HTTPS
--tls-key <path> Path to the SSL/TLS certificate's private key
Expand Down Expand Up @@ -326,6 +327,7 @@ All options can be set using environment variables prefixed with `DUFS_`.
--render-spa DUFS_RENDER_SPA=true
--assets <path> DUFS_ASSETS=/assets
--log-format <format> DUFS_LOG_FORMAT=""
--compress <compress> DUFS_COMPRESS="low"
--tls-cert <path> DUFS_TLS_CERT=cert.pem
--tls-key <path> DUFS_TLS_KEY=key.pem
```
Expand Down Expand Up @@ -361,6 +363,7 @@ render-try-index: true
render-spa: true
assets: ./assets/
log-format: '$remote_addr "$request" $status $http_user_agent'
compress: low
tls-cert: tests/data/cert.pem
tls-key: tests/data/key_pkcs1.pem
```
Expand Down
69 changes: 63 additions & 6 deletions src/args.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use anyhow::{bail, Context, Result};
use clap::builder::PossibleValuesParser;
use clap::{value_parser, Arg, ArgAction, ArgMatches, Command};
use async_zip::Compression;
use clap::builder::{PossibleValue, PossibleValuesParser};
use clap::{value_parser, Arg, ArgAction, ArgMatches, Command, ValueEnum};
use clap_complete::{generate, Generator, Shell};
use serde::{Deserialize, Deserializer};
use smart_default::SmartDefault;
Expand Down Expand Up @@ -196,6 +197,15 @@ pub fn build_cli() -> Command {
.value_name("format")
.help("Customize http log format"),
)
.arg(
Arg::new("compress")
.env("DUFS_COMPRESS")
.hide_env(true)
.value_parser(clap::builder::EnumValueParser::<Compress>::new())
.long("compress")
.value_name("level")
.help("Set zip compress level [default: low]")
)
.arg(
Arg::new("completions")
.long("completions")
Expand Down Expand Up @@ -270,6 +280,7 @@ pub struct Args {
#[serde(deserialize_with = "deserialize_log_http")]
#[serde(rename = "log-format")]
pub http_logger: HttpLogger,
pub compress: Compress,
pub tls_cert: Option<PathBuf>,
pub tls_key: Option<PathBuf>,
}
Expand Down Expand Up @@ -369,10 +380,6 @@ impl Args {
args.render_spa = matches.get_flag("render-spa");
}

if let Some(log_format) = matches.get_one::<String>("log-format") {
args.http_logger = log_format.parse()?;
}

if let Some(assets_path) = matches.get_one::<PathBuf>("assets") {
args.assets = Some(assets_path.clone());
}
Expand All @@ -381,6 +388,14 @@ impl Args {
args.assets = Some(Args::sanitize_assets_path(assets_path)?);
}

if let Some(log_format) = matches.get_one::<String>("log-format") {
args.http_logger = log_format.parse()?;
}

if let Some(compress) = matches.get_one::<Compress>("compress") {
args.compress = *compress;
}

#[cfg(feature = "tls")]
{
if let Some(tls_cert) = matches.get_one::<PathBuf>("tls-cert") {
Expand All @@ -403,6 +418,7 @@ impl Args {
args.tls_cert = None;
args.tls_key = None;
}
println!("{args:?}");

Ok(args)
}
Expand Down Expand Up @@ -461,6 +477,47 @@ impl BindAddr {
}
}

#[derive(Debug, Clone, Copy, PartialEq, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Compress {
None,
Low,
Medium,
High,
}

impl Default for Compress {
fn default() -> Self {
Self::Low
}
}

impl ValueEnum for Compress {
fn value_variants<'a>() -> &'a [Self] {
&[Self::None, Self::Low, Self::Medium, Self::High]
}

fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
Some(match self {
Compress::None => PossibleValue::new("none"),
Compress::Low => PossibleValue::new("low"),
Compress::Medium => PossibleValue::new("medium"),
Compress::High => PossibleValue::new("high"),
})
}
}

impl Compress {
pub fn to_compression(self) -> Compression {
match self {
Compress::None => Compression::Stored,
Compress::Low => Compression::Deflate,
Compress::Medium => Compression::Bz,
Compress::High => Compression::Xz,
}
}
}

fn deserialize_bind_addrs<'de, D>(deserializer: D) -> Result<Vec<BindAddr>, D::Error>
where
D: Deserializer<'de>,
Expand Down
15 changes: 13 additions & 2 deletions src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -581,8 +581,18 @@ impl Server {
let path = path.to_owned();
let hidden = self.args.hidden.clone();
let running = self.running.clone();
let compression = self.args.compress.to_compression();
tokio::spawn(async move {
if let Err(e) = zip_dir(&mut writer, &path, access_paths, &hidden, running).await {
if let Err(e) = zip_dir(
&mut writer,
&path,
access_paths,
&hidden,
compression,
running,
)
.await
{
error!("Failed to zip {}, {}", path.display(), e);
}
});
Expand Down Expand Up @@ -1422,6 +1432,7 @@ async fn zip_dir<W: AsyncWrite + Unpin>(
dir: &Path,
access_paths: AccessPaths,
hidden: &[String],
compression: Compression,
running: Arc<AtomicBool>,
) -> Result<()> {
let mut writer = ZipFileWriter::with_tokio(writer);
Expand Down Expand Up @@ -1475,7 +1486,7 @@ async fn zip_dir<W: AsyncWrite + Unpin>(
None => continue,
};
let (datetime, mode) = get_file_mtime_and_mode(&zip_path).await?;
let builder = ZipEntryBuilder::new(filename.into(), Compression::Deflate)
let builder = ZipEntryBuilder::new(filename.into(), compression)
.unix_permissions(mode)
.last_modification_date(ZipDateTime::from_chrono(&datetime));
let mut file = File::open(&zip_path).await?;
Expand Down
7 changes: 6 additions & 1 deletion tests/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@ fn head_dir_404(server: TestServer) -> Result<(), Error> {
}

#[rstest]
fn get_dir_zip(#[with(&["-A"])] server: TestServer) -> Result<(), Error> {
#[case(server(&["--allow-archive"] as &[&str]))]
#[case(server(&["--allow-archive", "--compress", "none"]))]
#[case(server(&["--allow-archive", "--compress", "low"]))]
#[case(server(&["--allow-archive", "--compress", "medium"]))]
#[case(server(&["--allow-archive", "--compress", "high"]))]
fn get_dir_zip(#[case] server: TestServer) -> Result<(), Error> {
let resp = reqwest::blocking::get(format!("{}?zip", server.url()))?;
assert_eq!(resp.status(), 200);
assert_eq!(
Expand Down

0 comments on commit 3873f47

Please sign in to comment.