From 9857f26bdd4e9e87b13a9755b1e00569e9459238 Mon Sep 17 00:00:00 2001 From: boasting-squirrel Date: Fri, 8 Mar 2019 19:59:59 +0100 Subject: Download folder as a tar working --- Cargo.lock | 85 ++++++++++++++++++++++++++++++++++++++++++++++++--------- Cargo.toml | 4 +++ src/archive.rs | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/listing.rs | 73 ++++++++++++++++++++++++++++++++++++------------- src/main.rs | 2 ++ src/renderer.rs | 35 ++++++++++++++++++++++++ 6 files changed, 247 insertions(+), 32 deletions(-) create mode 100644 src/archive.rs diff --git a/Cargo.lock b/Cargo.lock index 36da52f..0a3cd81 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7,7 +7,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "actix_derive 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -35,7 +35,7 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "actix 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", @@ -64,7 +64,7 @@ dependencies = [ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "brotli2 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "cookie 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -243,7 +243,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "bytes" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -477,6 +477,16 @@ dependencies = [ "synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "filetime" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.49 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "flate2" version = "1.0.6" @@ -532,7 +542,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "http 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", @@ -570,7 +580,7 @@ name = "http" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -752,10 +762,12 @@ dependencies = [ "actix-web 0.7.18 (registry+https://github.com/rust-lang/crates.io-index)", "alphanumeric-sort 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "bytesize 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "chrono-humanize 0.0.11 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "maud 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", "nanoid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -763,6 +775,8 @@ dependencies = [ "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "simplelog 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", + "tar 0.4.21 (registry+https://github.com/rust-lang/crates.io-index)", + "tempfile 3.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "yansi 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1137,6 +1151,14 @@ dependencies = [ "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "remove_dir_all" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "resolv-conf" version = "0.6.2" @@ -1343,6 +1365,30 @@ dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "tar" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "filetime 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.49 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)", + "xattr 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tempfile" +version = "3.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.49 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)", + "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "term" version = "0.5.1" @@ -1393,7 +1439,7 @@ name = "tokio" version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1416,7 +1462,7 @@ name = "tokio-codec" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1454,7 +1500,7 @@ name = "tokio-io" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1505,7 +1551,7 @@ name = "tokio-tcp" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1546,7 +1592,7 @@ name = "tokio-udp" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1560,7 +1606,7 @@ name = "tokio-uds" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.49 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1824,6 +1870,14 @@ dependencies = [ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "xattr" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.49 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "yansi" version = "0.5.0" @@ -1851,7 +1905,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum brotli2 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0cb036c3eade309815c15ddbacec5b22c4d1f3983a774ab2eac2e3e9ea85568e" "checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" "checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" -"checksum bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "40ade3d27603c2cb345eb0912aec461a6dec7e06a4ae48589904e808335c7afa" +"checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" "checksum bytesize 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "716960a18f978640f25101b5cbf1c6f6b0d3192fab36a2d98ca96f0ecbe41010" "checksum cc 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)" = "4390a3b5f4f6bce9c1d0c00128379df433e53777fdd30e92f16a529332baec4e" "checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4" @@ -1878,6 +1932,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum error-chain 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6930e04918388a9a2e41d518c25cf679ccafe26733fb4127dbf21993f2575d46" "checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" "checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" +"checksum filetime 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a2df5c1a8c4be27e7707789dc42ae65976e60b394afd293d1419ab915833e646" "checksum flate2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2291c165c8e703ee54ef3055ad6188e3d51108e2ded18e9f2476e774fc5ad3d4" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" @@ -1955,6 +2010,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" "checksum regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53ee8cfdddb2e0291adfb9f13d31d3bbe0a03c9a402c01b1e24188d86c35b24f" "checksum regex-syntax 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8c2f35eedad5295fdf00a63d7d4b238135723f92b434ec06774dad15c7ab0861" +"checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" "checksum resolv-conf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b263b4aa1b5de9ffc0054a2386f96992058bb6870aab516f8cdeb8a667d56dcb" "checksum ring 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2c4db68a2e35f3497146b7e4563df7d4773a2433230c5e4b448328e31740458a" "checksum rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "adacaae16d02b6ec37fdc7acfcddf365978de76d1983d3ee22afc260e1ca9619" @@ -1982,6 +2038,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum structopt-derive 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "ef98172b1a00b0bec738508d3726540edcbd186d50dfd326f2b1febbb3559f04" "checksum syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)" = "f92e629aa1d9c827b2bb8297046c1ccffc57c99b947a680d3ccff1f136a3bee9" "checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015" +"checksum tar 0.4.21 (registry+https://github.com/rust-lang/crates.io-index)" = "904b43da53c99b929c4484fa281e5535f2eb86b3040de3e3e5b69708e2a8bd65" +"checksum tempfile 3.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b86c784c88d98c801132806dadd3819ed29d8600836c4088e855cdf3e178ed8a" "checksum term 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5e6b677dd1e8214ea1ef4297f85dbcbed8e8cdddb561040cc998ca2551c37561" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" "checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6" @@ -2031,4 +2089,5 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum winreg 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a27a759395c1195c4cc5cda607ef6f8f6498f64e78f7900f5de0a127a424704a" "checksum winutil 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7daf138b6b14196e3830a588acf1e86966c694d3e8fb026fb105b8b5dca07e6e" "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +"checksum xattr 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" "checksum yansi 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71" diff --git a/Cargo.toml b/Cargo.toml index f98e802..d008bd1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,3 +37,7 @@ chrono = "0.4.6" chrono-humanize = "0.0.11" maud = { version = "0.20.0", features = ["actix-web"] } serde = { version = "1.0.89", features = ["derive"] } +tar = "0.4" +tempfile = "3.0.7" +bytes = "0.4.12" +futures = "0.1.25" \ No newline at end of file diff --git a/src/archive.rs b/src/archive.rs new file mode 100644 index 0000000..cc39207 --- /dev/null +++ b/src/archive.rs @@ -0,0 +1,80 @@ +use bytes::Bytes; +use serde::Deserialize; +use std::fs::{File, OpenOptions}; +use std::io::Read; +use std::path::PathBuf; +use tar::Builder; +use tempfile::tempdir; + +#[derive(Debug)] +pub enum CompressionError { + IOError(std::io::Error), + NoneError(std::option::NoneError), +} + +impl From for CompressionError { + fn from(err: std::option::NoneError) -> CompressionError { + CompressionError::NoneError(err) + } +} + +impl From for CompressionError { + fn from(err: std::io::Error) -> CompressionError { + CompressionError::IOError(err) + } +} + +/// Available compression methods +#[derive(Debug, Deserialize, Clone)] +pub enum CompressionMethod { + /// ZIP + #[serde(alias = "targz")] + TarGz, +} + +impl CompressionMethod { + pub fn to_string(&self) -> String { + match &self { + CompressionMethod::TarGz => "targz", + } + .to_string() + } + + pub fn extension(&self) -> String { + match &self { + CompressionMethod::TarGz => "tar.gz", + } + .to_string() + } +} + +pub fn create_archive_file( + method: &CompressionMethod, + dir: &PathBuf, +) -> Result<(String, usize, Bytes), CompressionError> { + match method { + CompressionMethod::TarGz => tgz_compress(&dir), + } +} + +fn tgz_compress(dir: &PathBuf) -> Result<(String, usize, Bytes), CompressionError> { + let src_dir = dir.display().to_string(); + let inner_folder = dir.file_name()?.to_str()?; + let dst_filename = format!("{}.tar", inner_folder); + let tmp_dir = tempdir()?; + + let dst_filepath = tmp_dir.path().join(dst_filename.clone()); + let tar_file = File::create(&dst_filepath)?; + let mut tar_builder = Builder::new(&tar_file); + tar_builder.append_dir_all(inner_folder, src_dir)?; + tar_builder.finish()?; + + let mut tar_file = OpenOptions::new().read(true).open(&dst_filepath)?; + let mut contents = Vec::new(); + let content_length = tar_file.read_to_end(&mut contents).unwrap(); + + let mut data = Bytes::new(); + data.extend_from_slice(&contents); + + Ok((dst_filename, content_length, data)) +} diff --git a/src/listing.rs b/src/listing.rs index 57bef17..565b5bf 100644 --- a/src/listing.rs +++ b/src/listing.rs @@ -1,12 +1,15 @@ -use actix_web::{fs, FromRequest, HttpRequest, HttpResponse, Query, Result}; +use actix_web::{fs, http, Body, FromRequest, HttpRequest, HttpResponse, Query, Result}; use bytesize::ByteSize; +use futures::stream::once; use htmlescape::encode_minimal as escape_html_entity; use percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET}; use serde::Deserialize; use std::io; use std::path::Path; use std::time::SystemTime; +use yansi::Paint; +use crate::archive; use crate::renderer; /// Query parameters @@ -14,6 +17,7 @@ use crate::renderer; struct QueryParameters { sort: Option, order: Option, + download: Option, } /// Available sorting methods @@ -134,11 +138,16 @@ pub fn directory_listing( let is_root = base.parent().is_none() || req.path() == random_route; let page_parent = base.parent().map(|p| p.display().to_string()); - let (sort_method, sort_order) = if let Ok(query) = Query::::extract(req) { - (query.sort.clone(), query.order.clone()) - } else { - (None, None) - }; + let (sort_method, sort_order, download) = + if let Ok(query) = Query::::extract(req) { + ( + query.sort.clone(), + query.order.clone(), + query.download.clone(), + ) + } else { + (None, None, None) + }; let mut entries: Vec = Vec::new(); @@ -218,17 +227,43 @@ pub fn directory_listing( } } - Ok(HttpResponse::Ok() - .content_type("text/html; charset=utf-8") - .body( - renderer::page( - &title, - entries, - is_root, - page_parent, - sort_method, - sort_order, - ) - .into_string(), - )) + if let Some(compression_method) = &download { + match archive::create_archive_file(&compression_method, &dir.path) { + Ok((filename, content_length, content)) => Ok(HttpResponse::Ok() + .content_type("application/tar") + .content_length(content_length as u64) + .header("Content-Transfer-Encoding", "binary") + .header( + "Content-Disposition", + format!("attachment; filename={:?}", filename), + ) + .chunked() + .body(Body::Streaming(Box::new(once(Ok(content)))))), + Err(err) => { + println!( + "{error} an error occured while compressing {folder}: {err:?}", + error = Paint::red("error:").bold(), + folder = dir.path.display(), + err = err + ); + Ok(HttpResponse::Ok() + .status(http::StatusCode::INTERNAL_SERVER_ERROR) + .body("")) + } + } + } else { + Ok(HttpResponse::Ok() + .content_type("text/html; charset=utf-8") + .body( + renderer::page( + &title, + entries, + is_root, + page_parent, + sort_method, + sort_order, + ) + .into_string(), + )) + } } diff --git a/src/main.rs b/src/main.rs index b15088c..9f2cacf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ #![feature(proc_macro_hygiene)] +#![feature(try_trait)] use actix_web::{fs, middleware, server, App}; use clap::crate_version; @@ -9,6 +10,7 @@ use std::thread; use std::time::Duration; use yansi::{Color, Paint}; +mod archive; mod args; mod auth; mod listing; diff --git a/src/renderer.rs b/src/renderer.rs index 89a9248..b83a67c 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -3,6 +3,7 @@ use chrono_humanize::{Accuracy, HumanTime, Tense}; use maud::{html, Markup, PreEscaped, DOCTYPE}; use std::time::SystemTime; +use crate::archive; use crate::listing; /// Renders the file listing @@ -19,6 +20,9 @@ pub fn page( body { span #top { } h1 { (page_title) } + div.download { + (archive_button(archive::CompressionMethod::TarGz)) + } table { thead { th { (build_link("name", "Name", &sort_method, &sort_order)) } @@ -50,6 +54,18 @@ pub fn page( } } +/// Partial: archive button +fn archive_button(compress_method: archive::CompressionMethod) -> Markup { + let link = format!("?download={}", compress_method.to_string()); + let text = format!("Download .{}", compress_method.extension()); + + html! { + a href=(link) { + (text) + } + } +} + /// Partial: table header link fn build_link( name: &str, @@ -259,6 +275,25 @@ fn css() -> Markup { color: #3498db; text-decoration: none; } + .download { + display: flex; + justify-content: flex-end; + padding: 0.125rem; + } + .download a, .download a:visited { + color: #3498db; + } + .download a { + background: #efefef; + padding: 0.5rem; + border-radius: 0.2rem; + } + .download a:hover { + background: #deeef7a6; + } + .download a:not(:last-of-type) { + margin-right: 1rem; + } @media (max-width: 600px) { h1 { font-size: 1.375em; -- cgit v1.2.3 From e7c269b12ec8168671e61787227d0fecc2756590 Mon Sep 17 00:00:00 2001 From: boasting-squirrel Date: Fri, 8 Mar 2019 20:25:02 +0100 Subject: Working example of tar.gz --- Cargo.lock | 12 ++++++++++++ Cargo.toml | 3 ++- src/archive.rs | 22 +++++++++++++++++----- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0a3cd81..7bfbd62 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -660,6 +660,16 @@ name = "libc" version = "0.2.49" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "libflate" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crc32fast 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "linked-hash-map" version = "0.4.2" @@ -769,6 +779,7 @@ dependencies = [ "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libflate 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", "maud 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", "nanoid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1956,6 +1967,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" "checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" "checksum libc 0.2.49 (registry+https://github.com/rust-lang/crates.io-index)" = "413f3dfc802c5dc91dc570b05125b6cda9855edfaa9825c9849807876376e70e" +"checksum libflate 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "54d1ddf9c52870243c5689d7638d888331c1116aa5b398f3ba1acfa7d8758ca1" "checksum linked-hash-map 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7860ec297f7008ff7a1e3382d7f7e1dcd69efc94751a2284bafc3d013c2aa939" "checksum literalext 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2f42dd699527975a1e0d722e0707998671188a0125f2051d2d192fc201184a81" "checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" diff --git a/Cargo.toml b/Cargo.toml index d008bd1..5f2fbcd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,4 +40,5 @@ serde = { version = "1.0.89", features = ["derive"] } tar = "0.4" tempfile = "3.0.7" bytes = "0.4.12" -futures = "0.1.25" \ No newline at end of file +futures = "0.1.25" +libflate = "0.1.20" \ No newline at end of file diff --git a/src/archive.rs b/src/archive.rs index cc39207..47af660 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -1,7 +1,8 @@ use bytes::Bytes; +use libflate::gzip::Encoder; use serde::Deserialize; use std::fs::{File, OpenOptions}; -use std::io::Read; +use std::io::{self, Read}; use std::path::PathBuf; use tar::Builder; use tempfile::tempdir; @@ -61,6 +62,7 @@ fn tgz_compress(dir: &PathBuf) -> Result<(String, usize, Bytes), CompressionErro let src_dir = dir.display().to_string(); let inner_folder = dir.file_name()?.to_str()?; let dst_filename = format!("{}.tar", inner_folder); + let dst_tgz_filename = format!("{}.gz", dst_filename); let tmp_dir = tempdir()?; let dst_filepath = tmp_dir.path().join(dst_filename.clone()); @@ -70,11 +72,21 @@ fn tgz_compress(dir: &PathBuf) -> Result<(String, usize, Bytes), CompressionErro tar_builder.finish()?; let mut tar_file = OpenOptions::new().read(true).open(&dst_filepath)?; - let mut contents = Vec::new(); - let content_length = tar_file.read_to_end(&mut contents).unwrap(); + let mut tar_content = Vec::new(); + let content_length = tar_file.read_to_end(&mut tar_content).unwrap(); + + let gz_data = gzip(&mut tar_content)?; let mut data = Bytes::new(); - data.extend_from_slice(&contents); + data.extend_from_slice(&gz_data); + + Ok((dst_tgz_filename, content_length, data)) +} + +fn gzip(mut data: &[u8]) -> Result, CompressionError> { + let mut encoder = Encoder::new(Vec::new())?; + io::copy(&mut data, &mut encoder)?; + let data = encoder.finish().into_result()?; - Ok((dst_filename, content_length, data)) + Ok(data) } -- cgit v1.2.3 From eec5f353ffe584fcdcd7e43670fbd30d9e9f04e3 Mon Sep 17 00:00:00 2001 From: boasting-squirrel Date: Fri, 8 Mar 2019 22:23:33 +0100 Subject: Refactored some code --- src/archive.rs | 74 +++++++++++++++++++++++++++++++++++----------------------- src/listing.rs | 4 ++-- 2 files changed, 47 insertions(+), 31 deletions(-) diff --git a/src/archive.rs b/src/archive.rs index 47af660..d9ca6e9 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -7,24 +7,6 @@ use std::path::PathBuf; use tar::Builder; use tempfile::tempdir; -#[derive(Debug)] -pub enum CompressionError { - IOError(std::io::Error), - NoneError(std::option::NoneError), -} - -impl From for CompressionError { - fn from(err: std::option::NoneError) -> CompressionError { - CompressionError::NoneError(err) - } -} - -impl From for CompressionError { - fn from(err: std::io::Error) -> CompressionError { - CompressionError::IOError(err) - } -} - /// Available compression methods #[derive(Debug, Deserialize, Clone)] pub enum CompressionMethod { @@ -49,40 +31,74 @@ impl CompressionMethod { } } +/// Possible errors +#[derive(Debug)] +pub enum CompressionError { + IOError(std::io::Error), + NoneError(std::option::NoneError), +} + +impl From for CompressionError { + fn from(err: std::option::NoneError) -> CompressionError { + CompressionError::NoneError(err) + } +} + +impl From for CompressionError { + fn from(err: std::io::Error) -> CompressionError { + CompressionError::IOError(err) + } +} + pub fn create_archive_file( method: &CompressionMethod, dir: &PathBuf, -) -> Result<(String, usize, Bytes), CompressionError> { +) -> Result<(String, Bytes), CompressionError> { match method { CompressionMethod::TarGz => tgz_compress(&dir), } } -fn tgz_compress(dir: &PathBuf) -> Result<(String, usize, Bytes), CompressionError> { +/// Compresses a given folder in .tar.gz format +fn tgz_compress(dir: &PathBuf) -> Result<(String, Bytes), CompressionError> { let src_dir = dir.display().to_string(); let inner_folder = dir.file_name()?.to_str()?; let dst_filename = format!("{}.tar", inner_folder); let dst_tgz_filename = format!("{}.gz", dst_filename); - let tmp_dir = tempdir()?; + let tar_content = tar(src_dir, dst_filename, inner_folder.to_string())?; + let gz_data = gzip(&tar_content)?; + + let mut data = Bytes::new(); + data.extend_from_slice(&gz_data); + + Ok((dst_tgz_filename, data)) +} + +/// Creates a temporary tar file of a given directory, reads it and returns its content as bytes +fn tar( + src_dir: String, + dst_filename: String, + inner_folder: String, +) -> Result, CompressionError> { + let tmp_dir = tempdir()?; let dst_filepath = tmp_dir.path().join(dst_filename.clone()); let tar_file = File::create(&dst_filepath)?; + + // Create a TAR file of src_dir let mut tar_builder = Builder::new(&tar_file); tar_builder.append_dir_all(inner_folder, src_dir)?; - tar_builder.finish()?; + tar_builder.into_inner()?; + // Read the content of the TAR file and store it as bytes let mut tar_file = OpenOptions::new().read(true).open(&dst_filepath)?; let mut tar_content = Vec::new(); - let content_length = tar_file.read_to_end(&mut tar_content).unwrap(); - - let gz_data = gzip(&mut tar_content)?; - - let mut data = Bytes::new(); - data.extend_from_slice(&gz_data); + tar_file.read_to_end(&mut tar_content)?; - Ok((dst_tgz_filename, content_length, data)) + Ok(tar_content) } +/// Compresses a stream of bytes using the GZIP algorithm fn gzip(mut data: &[u8]) -> Result, CompressionError> { let mut encoder = Encoder::new(Vec::new())?; io::copy(&mut data, &mut encoder)?; diff --git a/src/listing.rs b/src/listing.rs index 565b5bf..c9542b8 100644 --- a/src/listing.rs +++ b/src/listing.rs @@ -229,9 +229,9 @@ pub fn directory_listing( if let Some(compression_method) = &download { match archive::create_archive_file(&compression_method, &dir.path) { - Ok((filename, content_length, content)) => Ok(HttpResponse::Ok() + Ok((filename, content)) => Ok(HttpResponse::Ok() .content_type("application/tar") - .content_length(content_length as u64) + .content_length(content.len() as u64) .header("Content-Transfer-Encoding", "binary") .header( "Content-Disposition", -- cgit v1.2.3 From e04fc9675433f695b37104e57b28bd33d37ad5ab Mon Sep 17 00:00:00 2001 From: boasting-squirrel Date: Fri, 8 Mar 2019 23:25:01 +0100 Subject: Corrected comment --- src/archive.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/archive.rs b/src/archive.rs index d9ca6e9..0f09005 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -10,7 +10,7 @@ use tempfile::tempdir; /// Available compression methods #[derive(Debug, Deserialize, Clone)] pub enum CompressionMethod { - /// ZIP + /// TAR GZ #[serde(alias = "targz")] TarGz, } -- cgit v1.2.3 From 46932e7d5664c97ad65aefc3f670f7e64e6f8e0d Mon Sep 17 00:00:00 2001 From: boasting-squirrel Date: Fri, 8 Mar 2019 23:57:05 +0100 Subject: Improved HTTP headers --- src/archive.rs | 14 ++++++++++++++ src/listing.rs | 3 ++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/archive.rs b/src/archive.rs index 0f09005..1b31d08 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -1,3 +1,4 @@ +use actix_web::http::ContentEncoding; use bytes::Bytes; use libflate::gzip::Encoder; use serde::Deserialize; @@ -29,6 +30,19 @@ impl CompressionMethod { } .to_string() } + + pub fn content_type(&self) -> String { + match &self { + CompressionMethod::TarGz => "application/gzip", + } + .to_string() + } + + pub fn content_encoding(&self) -> ContentEncoding { + match &self { + CompressionMethod::TarGz => ContentEncoding::Gzip, + } + } } /// Possible errors diff --git a/src/listing.rs b/src/listing.rs index c9542b8..f7198a5 100644 --- a/src/listing.rs +++ b/src/listing.rs @@ -230,8 +230,9 @@ pub fn directory_listing( if let Some(compression_method) = &download { match archive::create_archive_file(&compression_method, &dir.path) { Ok((filename, content)) => Ok(HttpResponse::Ok() - .content_type("application/tar") + .content_type(compression_method.content_type()) .content_length(content.len() as u64) + .content_encoding(compression_method.content_encoding()) .header("Content-Transfer-Encoding", "binary") .header( "Content-Disposition", -- cgit v1.2.3 From b077424d68659923e6ac13f364434314085a0376 Mon Sep 17 00:00:00 2001 From: boasting-squirrel Date: Sat, 9 Mar 2019 11:42:45 +0100 Subject: Temporary workaround for symlinks issue --- src/archive.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/archive.rs b/src/archive.rs index 1b31d08..bc3a1c8 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -101,6 +101,11 @@ fn tar( // Create a TAR file of src_dir let mut tar_builder = Builder::new(&tar_file); + + // Temporary workaround for known issue: + // https://github.com/alexcrichton/tar-rs/issues/147 + // https://github.com/alexcrichton/tar-rs/issues/174 + tar_builder.follow_symlinks(false); tar_builder.append_dir_all(inner_folder, src_dir)?; tar_builder.into_inner()?; -- cgit v1.2.3 From 5cde963bb25c44695a8e72432c739416cbaa1014 Mon Sep 17 00:00:00 2001 From: boasting-squirrel Date: Sat, 9 Mar 2019 11:45:34 +0100 Subject: Removed debug message --- src/listing.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/listing.rs b/src/listing.rs index f7198a5..88dab40 100644 --- a/src/listing.rs +++ b/src/listing.rs @@ -240,12 +240,11 @@ pub fn directory_listing( ) .chunked() .body(Body::Streaming(Box::new(once(Ok(content)))))), - Err(err) => { + Err(_) => { println!( - "{error} an error occured while compressing {folder}: {err:?}", + "{error} an error occured while compressing {folder}", error = Paint::red("error:").bold(), folder = dir.path.display(), - err = err ); Ok(HttpResponse::Ok() .status(http::StatusCode::INTERNAL_SERVER_ERROR) -- cgit v1.2.3 From bfb7e9f345e4033a895f28183a4ff2af4ba8f501 Mon Sep 17 00:00:00 2001 From: boasting-squirrel Date: Sat, 9 Mar 2019 18:03:29 +0100 Subject: Updated README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 73dba75..cc8d30f 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,7 @@ Sometimes this is just a more practical and quick way than doing things properly - Single binary drop-in with no extra dependencies required - Authentication support with username and password - Mega fast and highly parallel (thanks to [Rust](https://www.rust-lang.org/) and [Actix](https://actix.rs/)) +- Folder download (compressed in .tar.gz) ## How to install -- cgit v1.2.3 From d56804a37f0c44160f27195853d578d8f85ddba1 Mon Sep 17 00:00:00 2001 From: boasting-squirrel Date: Tue, 12 Mar 2019 00:25:02 +0100 Subject: Changed Info: to info: to be consistent --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 9f2cacf..1bddd6d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -121,7 +121,7 @@ fn main() { version = crate_version!() ); if !miniserve_config.path_explicitly_chosen { - println!("{info} miniserve has been invoked without an explicit path so it will serve the current directory.", info=Color::Blue.paint("Info:").bold()); + println!("{info} miniserve has been invoked without an explicit path so it will serve the current directory.", info=Color::Blue.paint("info:").bold()); println!( " Invoke with -h|--help to see options or invoke as `miniserve .` to hide this advice." ); -- cgit v1.2.3 From aeb51dcf43665741a3438360151a4424e9b243e0 Mon Sep 17 00:00:00 2001 From: boasting-squirrel Date: Tue, 12 Mar 2019 00:25:56 +0100 Subject: Started to add helpful messages for errors which could occur during archiving --- Cargo.lock | 1 + Cargo.toml | 3 +- src/archive.rs | 82 ++++++++++++++++++++++++++++++---------------------- src/errors.rs | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/listing.rs | 44 ++++++++++++++++++---------- src/main.rs | 1 + 6 files changed, 171 insertions(+), 50 deletions(-) create mode 100644 src/errors.rs diff --git a/Cargo.lock b/Cargo.lock index 7bfbd62..6016cf5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -777,6 +777,7 @@ dependencies = [ "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "chrono-humanize 0.0.11 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "libflate 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 5f2fbcd..ad5a144 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,4 +41,5 @@ tar = "0.4" tempfile = "3.0.7" bytes = "0.4.12" futures = "0.1.25" -libflate = "0.1.20" \ No newline at end of file +libflate = "0.1.20" +failure = "0.1.5" \ No newline at end of file diff --git a/src/archive.rs b/src/archive.rs index bc3a1c8..afbcc6b 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -1,5 +1,6 @@ use actix_web::http::ContentEncoding; use bytes::Bytes; +use failure::ResultExt; use libflate::gzip::Encoder; use serde::Deserialize; use std::fs::{File, OpenOptions}; @@ -7,6 +8,9 @@ use std::io::{self, Read}; use std::path::PathBuf; use tar::Builder; use tempfile::tempdir; +use yansi::Color; + +use crate::errors; /// Available compression methods #[derive(Debug, Deserialize, Clone)] @@ -45,43 +49,25 @@ impl CompressionMethod { } } -/// Possible errors -#[derive(Debug)] -pub enum CompressionError { - IOError(std::io::Error), - NoneError(std::option::NoneError), -} - -impl From for CompressionError { - fn from(err: std::option::NoneError) -> CompressionError { - CompressionError::NoneError(err) - } -} - -impl From for CompressionError { - fn from(err: std::io::Error) -> CompressionError { - CompressionError::IOError(err) - } -} - pub fn create_archive_file( method: &CompressionMethod, dir: &PathBuf, -) -> Result<(String, Bytes), CompressionError> { +) -> Result<(String, Bytes), errors::CompressionError> { match method { CompressionMethod::TarGz => tgz_compress(&dir), } } /// Compresses a given folder in .tar.gz format -fn tgz_compress(dir: &PathBuf) -> Result<(String, Bytes), CompressionError> { +fn tgz_compress(dir: &PathBuf) -> Result<(String, Bytes), errors::CompressionError> { let src_dir = dir.display().to_string(); let inner_folder = dir.file_name()?.to_str()?; let dst_filename = format!("{}.tar", inner_folder); let dst_tgz_filename = format!("{}.gz", dst_filename); - let tar_content = tar(src_dir, dst_filename, inner_folder.to_string())?; - let gz_data = gzip(&tar_content)?; + let tar_content = tar(src_dir, dst_filename, inner_folder.to_string()) + .context(errors::CompressionErrorKind::TarContentError)?; + let gz_data = gzip(&tar_content).context(errors::CompressionErrorKind::GZipContentError)?; let mut data = Bytes::new(); data.extend_from_slice(&gz_data); @@ -94,10 +80,13 @@ fn tar( src_dir: String, dst_filename: String, inner_folder: String, -) -> Result, CompressionError> { - let tmp_dir = tempdir()?; +) -> Result, errors::CompressionError> { + let tmp_dir = tempdir().context(errors::CompressionErrorKind::CreateTemporaryFileError)?; let dst_filepath = tmp_dir.path().join(dst_filename.clone()); - let tar_file = File::create(&dst_filepath)?; + let tar_file = + File::create(&dst_filepath).context(errors::CompressionErrorKind::CreateFileError { + path: color_path(&dst_filepath.display().to_string()), + })?; // Create a TAR file of src_dir let mut tar_builder = Builder::new(&tar_file); @@ -106,22 +95,47 @@ fn tar( // https://github.com/alexcrichton/tar-rs/issues/147 // https://github.com/alexcrichton/tar-rs/issues/174 tar_builder.follow_symlinks(false); - tar_builder.append_dir_all(inner_folder, src_dir)?; - tar_builder.into_inner()?; + tar_builder.append_dir_all(inner_folder, &src_dir).context( + errors::CompressionErrorKind::TarBuildingError { + message: format!( + "failed to append the content of {} in the TAR archive", + color_path(&src_dir) + ), + }, + )?; + tar_builder + .into_inner() + .context(errors::CompressionErrorKind::TarBuildingError { + message: "failed to finish writing the TAR archive".to_string(), + })?; // Read the content of the TAR file and store it as bytes - let mut tar_file = OpenOptions::new().read(true).open(&dst_filepath)?; + let mut tar_file = OpenOptions::new().read(true).open(&dst_filepath).context( + errors::CompressionErrorKind::OpenFileError { + path: color_path(&dst_filepath.display().to_string()), + }, + )?; let mut tar_content = Vec::new(); - tar_file.read_to_end(&mut tar_content)?; + tar_file + .read_to_end(&mut tar_content) + .context(errors::CompressionErrorKind::TarContentError)?; Ok(tar_content) } /// Compresses a stream of bytes using the GZIP algorithm -fn gzip(mut data: &[u8]) -> Result, CompressionError> { - let mut encoder = Encoder::new(Vec::new())?; - io::copy(&mut data, &mut encoder)?; - let data = encoder.finish().into_result()?; +fn gzip(mut data: &[u8]) -> Result, errors::CompressionError> { + let mut encoder = + Encoder::new(Vec::new()).context(errors::CompressionErrorKind::GZipBuildingError)?; + io::copy(&mut data, &mut encoder).context(errors::CompressionErrorKind::GZipBuildingError)?; + let data = encoder + .finish() + .into_result() + .context(errors::CompressionErrorKind::GZipBuildingError)?; Ok(data) } + +fn color_path(path: &str) -> String { + Color::White.paint(path).bold().to_string() +} diff --git a/src/errors.rs b/src/errors.rs new file mode 100644 index 0000000..c85d123 --- /dev/null +++ b/src/errors.rs @@ -0,0 +1,90 @@ +use failure::{Backtrace, Context, Fail}; +use std::fmt::{self, Debug, Display}; +use yansi::Color; + +/// Kinds of error which might happen during folder archive generation +#[derive(Clone, Debug, PartialEq, Eq, Fail)] +pub enum CompressionErrorKind { + #[fail(display = "Could not open file {}", path)] + OpenFileError { path: String }, + #[fail(display = "Could not create temporary file")] + CreateTemporaryFileError, + #[fail(display = "Could not create file {}", path)] + CreateFileError { path: String }, + #[fail(display = "Could not retrieve entity name from the given path. + This can either mean that the entity has non UTF-8 characters in its name, + or that its name ends with \"..\"")] + InvalidDirectoryName, + #[fail(display = "Failed to create the TAR archive: {}", message)] + TarBuildingError { message: String }, + #[fail(display = "Failed to create the GZIP archive")] + GZipBuildingError, + #[fail(display = "Failed to retrieve TAR content")] + TarContentError, + #[fail(display = "Failed to retrieve GZIP content")] + GZipContentError, +} + +pub fn print_chain(err: CompressionError) { + for cause in Fail::iter_causes(&err) { + println!( + "{} {}", + Color::Magenta.paint("Caused by:").to_string(), + cause + ); + } +} + +pub struct CompressionError { + inner: Context, +} + +impl CompressionError { + fn new(kind: CompressionErrorKind) -> CompressionError { + CompressionError { + inner: Context::new(kind), + } + } +} + +impl Fail for CompressionError { + fn cause(&self) -> Option<&Fail> { + self.inner.cause() + } + + fn backtrace(&self) -> Option<&Backtrace> { + self.inner.backtrace() + } +} + +impl Display for CompressionError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.inner, f) + } +} + +impl Debug for CompressionError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Debug::fmt(&self.inner, f) + } +} + +impl From> for CompressionError { + fn from(inner: Context) -> CompressionError { + CompressionError { inner } + } +} + +impl From for CompressionError { + fn from(kind: CompressionErrorKind) -> CompressionError { + CompressionError { + inner: Context::new(kind), + } + } +} + +impl From for CompressionError { + fn from(_: std::option::NoneError) -> CompressionError { + CompressionError::new(CompressionErrorKind::InvalidDirectoryName) + } +} diff --git a/src/listing.rs b/src/listing.rs index 88dab40..ab3b28b 100644 --- a/src/listing.rs +++ b/src/listing.rs @@ -7,9 +7,10 @@ use serde::Deserialize; use std::io; use std::path::Path; use std::time::SystemTime; -use yansi::Paint; +use yansi::{Color, Paint}; use crate::archive; +use crate::errors; use crate::renderer; /// Query parameters @@ -228,24 +229,37 @@ pub fn directory_listing( } if let Some(compression_method) = &download { + println!( + "{info} Creating an archive ({extension}) of {path}...", + info = Color::Blue.paint("info:").bold(), + extension = Color::White.paint(compression_method.extension()).bold(), + path = Color::White.paint(&dir.path.display().to_string()).bold() + ); match archive::create_archive_file(&compression_method, &dir.path) { - Ok((filename, content)) => Ok(HttpResponse::Ok() - .content_type(compression_method.content_type()) - .content_length(content.len() as u64) - .content_encoding(compression_method.content_encoding()) - .header("Content-Transfer-Encoding", "binary") - .header( - "Content-Disposition", - format!("attachment; filename={:?}", filename), - ) - .chunked() - .body(Body::Streaming(Box::new(once(Ok(content)))))), - Err(_) => { + Ok((filename, content)) => { + println!( + "{success} Archive successfully created !", + success = Color::Green.paint("success:").bold() + ); + Ok(HttpResponse::Ok() + .content_type(compression_method.content_type()) + .content_length(content.len() as u64) + .content_encoding(compression_method.content_encoding()) + .header("Content-Transfer-Encoding", "binary") + .header( + "Content-Disposition", + format!("attachment; filename={:?}", filename), + ) + .chunked() + .body(Body::Streaming(Box::new(once(Ok(content)))))) + } + Err(err) => { println!( - "{error} an error occured while compressing {folder}", + "{error} {err}", error = Paint::red("error:").bold(), - folder = dir.path.display(), + err = Paint::white(&err).bold() ); + errors::print_chain(err); Ok(HttpResponse::Ok() .status(http::StatusCode::INTERNAL_SERVER_ERROR) .body("")) diff --git a/src/main.rs b/src/main.rs index 1bddd6d..59389b1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,6 +15,7 @@ mod args; mod auth; mod listing; mod renderer; +mod errors; #[derive(Clone, Debug)] /// Configuration of the Miniserve application -- cgit v1.2.3 From 67d771fc3a954ea439e77afac092c84c5489d074 Mon Sep 17 00:00:00 2001 From: boasting-squirrel Date: Tue, 12 Mar 2019 19:23:22 +0100 Subject: Added some error messages + reworked the print_error_chain method --- src/archive.rs | 17 +++++++++++------ src/errors.rs | 28 ++++++++++++++++++++++------ src/listing.rs | 14 +++++--------- 3 files changed, 38 insertions(+), 21 deletions(-) diff --git a/src/archive.rs b/src/archive.rs index afbcc6b..556fd69 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -126,12 +126,17 @@ fn tar( /// Compresses a stream of bytes using the GZIP algorithm fn gzip(mut data: &[u8]) -> Result, errors::CompressionError> { let mut encoder = - Encoder::new(Vec::new()).context(errors::CompressionErrorKind::GZipBuildingError)?; - io::copy(&mut data, &mut encoder).context(errors::CompressionErrorKind::GZipBuildingError)?; - let data = encoder - .finish() - .into_result() - .context(errors::CompressionErrorKind::GZipBuildingError)?; + Encoder::new(Vec::new()).context(errors::CompressionErrorKind::GZipBuildingError { + message: "failed to create GZIP encoder".to_string(), + })?; + io::copy(&mut data, &mut encoder).context(errors::CompressionErrorKind::GZipBuildingError { + message: "failed to write GZIP data".to_string(), + })?; + let data = encoder.finish().into_result().context( + errors::CompressionErrorKind::GZipBuildingError { + message: "failed to write GZIP trailer".to_string(), + }, + )?; Ok(data) } diff --git a/src/errors.rs b/src/errors.rs index c85d123..191d382 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,9 +1,9 @@ use failure::{Backtrace, Context, Fail}; use std::fmt::{self, Debug, Display}; -use yansi::Color; +use yansi::{Color, Paint}; /// Kinds of error which might happen during folder archive generation -#[derive(Clone, Debug, PartialEq, Eq, Fail)] +#[derive(Debug, Fail)] pub enum CompressionErrorKind { #[fail(display = "Could not open file {}", path)] OpenFileError { path: String }, @@ -17,21 +17,37 @@ pub enum CompressionErrorKind { InvalidDirectoryName, #[fail(display = "Failed to create the TAR archive: {}", message)] TarBuildingError { message: String }, - #[fail(display = "Failed to create the GZIP archive")] - GZipBuildingError, + #[fail(display = "Failed to create the GZIP archive: {}", message)] + GZipBuildingError { message: String }, #[fail(display = "Failed to retrieve TAR content")] TarContentError, #[fail(display = "Failed to retrieve GZIP content")] GZipContentError, } -pub fn print_chain(err: CompressionError) { +pub fn print_error_chain(err: CompressionError) { + println!( + "{error} {err}", + error = Paint::red("error:").bold(), + err = Paint::white(&err).bold() + ); + print_backtrace(&err); for cause in Fail::iter_causes(&err) { println!( "{} {}", - Color::Magenta.paint("Caused by:").to_string(), + Color::RGB(255, 192, 0).paint("caused by:").to_string(), cause ); + print_backtrace(cause); + } +} + +fn print_backtrace(err: &dyn Fail) { + if let Some(backtrace) = err.backtrace() { + let backtrace = backtrace.to_string(); + if backtrace != "" { + println!("{}", backtrace); + } } } diff --git a/src/listing.rs b/src/listing.rs index ab3b28b..a9f0f19 100644 --- a/src/listing.rs +++ b/src/listing.rs @@ -7,7 +7,7 @@ use serde::Deserialize; use std::io; use std::path::Path; use std::time::SystemTime; -use yansi::{Color, Paint}; +use yansi::Color; use crate::archive; use crate::errors; @@ -238,8 +238,9 @@ pub fn directory_listing( match archive::create_archive_file(&compression_method, &dir.path) { Ok((filename, content)) => { println!( - "{success} Archive successfully created !", - success = Color::Green.paint("success:").bold() + "{success} {file} successfully created !", + success = Color::Green.paint("success:").bold(), + file = Color::White.paint(&filename).bold(), ); Ok(HttpResponse::Ok() .content_type(compression_method.content_type()) @@ -254,12 +255,7 @@ pub fn directory_listing( .body(Body::Streaming(Box::new(once(Ok(content)))))) } Err(err) => { - println!( - "{error} {err}", - error = Paint::red("error:").bold(), - err = Paint::white(&err).bold() - ); - errors::print_chain(err); + errors::print_error_chain(err); Ok(HttpResponse::Ok() .status(http::StatusCode::INTERNAL_SERVER_ERROR) .body("")) -- cgit v1.2.3 From 122a949ec49f84a49e7a5bec657a93a65faadce1 Mon Sep 17 00:00:00 2001 From: boasting-squirrel Date: Tue, 12 Mar 2019 20:16:45 +0100 Subject: Better error messages for invalid path --- src/archive.rs | 16 +++++++++++++++- src/errors.rs | 14 ++++---------- src/main.rs | 3 +-- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/archive.rs b/src/archive.rs index 556fd69..9df1e5e 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -61,7 +61,21 @@ pub fn create_archive_file( /// Compresses a given folder in .tar.gz format fn tgz_compress(dir: &PathBuf) -> Result<(String, Bytes), errors::CompressionError> { let src_dir = dir.display().to_string(); - let inner_folder = dir.file_name()?.to_str()?; + let inner_folder = match dir.file_name() { + Some(directory_name) => match directory_name.to_str() { + Some(directory) => directory, + None => { + return Err(errors::CompressionError::new( + errors::CompressionErrorKind::InvalidUTF8DirectoryName, + )) + } + }, + None => { + return Err(errors::CompressionError::new( + errors::CompressionErrorKind::InvalidDirectoryName, + )) + } + }; let dst_filename = format!("{}.tar", inner_folder); let dst_tgz_filename = format!("{}.gz", dst_filename); diff --git a/src/errors.rs b/src/errors.rs index 191d382..6781bc6 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -11,10 +11,10 @@ pub enum CompressionErrorKind { CreateTemporaryFileError, #[fail(display = "Could not create file {}", path)] CreateFileError { path: String }, - #[fail(display = "Could not retrieve entity name from the given path. - This can either mean that the entity has non UTF-8 characters in its name, - or that its name ends with \"..\"")] + #[fail(display = "Invalid path: directory name cannot end with \"..\"")] InvalidDirectoryName, + #[fail(display = "Directory name contains invalid UTF-8 characters")] + InvalidUTF8DirectoryName, #[fail(display = "Failed to create the TAR archive: {}", message)] TarBuildingError { message: String }, #[fail(display = "Failed to create the GZIP archive: {}", message)] @@ -56,7 +56,7 @@ pub struct CompressionError { } impl CompressionError { - fn new(kind: CompressionErrorKind) -> CompressionError { + pub fn new(kind: CompressionErrorKind) -> CompressionError { CompressionError { inner: Context::new(kind), } @@ -98,9 +98,3 @@ impl From for CompressionError { } } } - -impl From for CompressionError { - fn from(_: std::option::NoneError) -> CompressionError { - CompressionError::new(CompressionErrorKind::InvalidDirectoryName) - } -} diff --git a/src/main.rs b/src/main.rs index 59389b1..260551c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,4 @@ #![feature(proc_macro_hygiene)] -#![feature(try_trait)] use actix_web::{fs, middleware, server, App}; use clap::crate_version; @@ -13,9 +12,9 @@ use yansi::{Color, Paint}; mod archive; mod args; mod auth; +mod errors; mod listing; mod renderer; -mod errors; #[derive(Clone, Debug)] /// Configuration of the Miniserve application -- cgit v1.2.3 From ab41998aa56c4e05be9e5f00ade6be6726cc9d68 Mon Sep 17 00:00:00 2001 From: boasting-squirrel Date: Tue, 12 Mar 2019 21:36:47 +0100 Subject: updated Cargo.lock --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 907169b..3c23259 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -786,9 +786,9 @@ dependencies = [ "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "simplelog 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "tar 0.4.21 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "structopt 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "yansi 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -- cgit v1.2.3 From e516fd628216d5ddf3bd3a7b836b3d9b14e57ee3 Mon Sep 17 00:00:00 2001 From: boasting-squirrel Date: Wed, 13 Mar 2019 00:31:55 +0100 Subject: Improved error message --- src/errors.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/errors.rs b/src/errors.rs index 6781bc6..d78216f 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -11,9 +11,9 @@ pub enum CompressionErrorKind { CreateTemporaryFileError, #[fail(display = "Could not create file {}", path)] CreateFileError { path: String }, - #[fail(display = "Invalid path: directory name cannot end with \"..\"")] + #[fail(display = "Invalid path: directory name terminates in \"..\"")] InvalidDirectoryName, - #[fail(display = "Directory name contains invalid UTF-8 characters")] + #[fail(display = "Invalid path: directory name contains invalid UTF-8 characters")] InvalidUTF8DirectoryName, #[fail(display = "Failed to create the TAR archive: {}", message)] TarBuildingError { message: String }, -- cgit v1.2.3 From ae9fe2829a85501334c28b5e3980529ed55198ad Mon Sep 17 00:00:00 2001 From: boasting-squirrel Date: Wed, 13 Mar 2019 00:44:30 +0100 Subject: Added some docs comments to errors.rs --- src/errors.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/errors.rs b/src/errors.rs index d78216f..3fcda8f 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -2,7 +2,7 @@ use failure::{Backtrace, Context, Fail}; use std::fmt::{self, Debug, Display}; use yansi::{Color, Paint}; -/// Kinds of error which might happen during folder archive generation +/// Kinds of errors which might happen during the generation of an archive #[derive(Debug, Fail)] pub enum CompressionErrorKind { #[fail(display = "Could not open file {}", path)] @@ -25,6 +25,8 @@ pub enum CompressionErrorKind { GZipContentError, } +/// Prints the full chain of error, up to the root cause. +/// If RUST_BACKTRACE is set to 1, also prints the backtrace for each error pub fn print_error_chain(err: CompressionError) { println!( "{error} {err}", @@ -42,6 +44,8 @@ pub fn print_error_chain(err: CompressionError) { } } +/// Prints the backtrace of an error +/// RUST_BACKTRACE needs to be set to 1 to display the backtrace fn print_backtrace(err: &dyn Fail) { if let Some(backtrace) = err.backtrace() { let backtrace = backtrace.to_string(); @@ -51,6 +55,7 @@ fn print_backtrace(err: &dyn Fail) { } } +/// Based on https://boats.gitlab.io/failure/error-errorkind.html pub struct CompressionError { inner: Context, } -- cgit v1.2.3 From 2723babb9b8ddef120dfeb9b671e18f1a46dfb96 Mon Sep 17 00:00:00 2001 From: boasting-squirrel Date: Wed, 13 Mar 2019 18:08:49 +0100 Subject: Build tar in buffer instead of in tempfile --- src/archive.rs | 42 ++++++++++-------------------------------- src/errors.rs | 6 ------ 2 files changed, 10 insertions(+), 38 deletions(-) diff --git a/src/archive.rs b/src/archive.rs index 9df1e5e..c535c50 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -3,11 +3,9 @@ use bytes::Bytes; use failure::ResultExt; use libflate::gzip::Encoder; use serde::Deserialize; -use std::fs::{File, OpenOptions}; -use std::io::{self, Read}; +use std::io; use std::path::PathBuf; use tar::Builder; -use tempfile::tempdir; use yansi::Color; use crate::errors; @@ -79,7 +77,7 @@ fn tgz_compress(dir: &PathBuf) -> Result<(String, Bytes), errors::CompressionErr let dst_filename = format!("{}.tar", inner_folder); let dst_tgz_filename = format!("{}.gz", dst_filename); - let tar_content = tar(src_dir, dst_filename, inner_folder.to_string()) + let tar_content = tar(src_dir, inner_folder.to_string()) .context(errors::CompressionErrorKind::TarContentError)?; let gz_data = gzip(&tar_content).context(errors::CompressionErrorKind::GZipContentError)?; @@ -90,20 +88,9 @@ fn tgz_compress(dir: &PathBuf) -> Result<(String, Bytes), errors::CompressionErr } /// Creates a temporary tar file of a given directory, reads it and returns its content as bytes -fn tar( - src_dir: String, - dst_filename: String, - inner_folder: String, -) -> Result, errors::CompressionError> { - let tmp_dir = tempdir().context(errors::CompressionErrorKind::CreateTemporaryFileError)?; - let dst_filepath = tmp_dir.path().join(dst_filename.clone()); - let tar_file = - File::create(&dst_filepath).context(errors::CompressionErrorKind::CreateFileError { - path: color_path(&dst_filepath.display().to_string()), - })?; - +fn tar(src_dir: String, inner_folder: String) -> Result, errors::CompressionError> { // Create a TAR file of src_dir - let mut tar_builder = Builder::new(&tar_file); + let mut tar_builder = Builder::new(Vec::new()); // Temporary workaround for known issue: // https://github.com/alexcrichton/tar-rs/issues/147 @@ -117,22 +104,13 @@ fn tar( ), }, )?; - tar_builder - .into_inner() - .context(errors::CompressionErrorKind::TarBuildingError { - message: "failed to finish writing the TAR archive".to_string(), - })?; - // Read the content of the TAR file and store it as bytes - let mut tar_file = OpenOptions::new().read(true).open(&dst_filepath).context( - errors::CompressionErrorKind::OpenFileError { - path: color_path(&dst_filepath.display().to_string()), - }, - )?; - let mut tar_content = Vec::new(); - tar_file - .read_to_end(&mut tar_content) - .context(errors::CompressionErrorKind::TarContentError)?; + let tar_content = + tar_builder + .into_inner() + .context(errors::CompressionErrorKind::TarBuildingError { + message: "failed to finish writing the TAR archive".to_string(), + })?; Ok(tar_content) } diff --git a/src/errors.rs b/src/errors.rs index 3fcda8f..8cfedff 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -5,12 +5,6 @@ use yansi::{Color, Paint}; /// Kinds of errors which might happen during the generation of an archive #[derive(Debug, Fail)] pub enum CompressionErrorKind { - #[fail(display = "Could not open file {}", path)] - OpenFileError { path: String }, - #[fail(display = "Could not create temporary file")] - CreateTemporaryFileError, - #[fail(display = "Could not create file {}", path)] - CreateFileError { path: String }, #[fail(display = "Invalid path: directory name terminates in \"..\"")] InvalidDirectoryName, #[fail(display = "Invalid path: directory name contains invalid UTF-8 characters")] -- cgit v1.2.3 From 15915c428181ed739c7f349a5322127c1aa9be4d Mon Sep 17 00:00:00 2001 From: boasting-squirrel Date: Wed, 13 Mar 2019 18:12:58 +0100 Subject: Removed tempfile from dependencies --- Cargo.lock | 24 ------------------------ Cargo.toml | 1 - 2 files changed, 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3c23259..1e0b693 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -788,7 +788,6 @@ dependencies = [ "simplelog 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "tar 0.4.21 (registry+https://github.com/rust-lang/crates.io-index)", - "tempfile 3.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "yansi 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1163,14 +1162,6 @@ dependencies = [ "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "remove_dir_all" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "resolv-conf" version = "0.6.2" @@ -1388,19 +1379,6 @@ dependencies = [ "xattr 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "tempfile" -version = "3.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.49 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)", - "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "term" version = "0.5.1" @@ -2023,7 +2001,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" "checksum regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53ee8cfdddb2e0291adfb9f13d31d3bbe0a03c9a402c01b1e24188d86c35b24f" "checksum regex-syntax 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8c2f35eedad5295fdf00a63d7d4b238135723f92b434ec06774dad15c7ab0861" -"checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" "checksum resolv-conf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b263b4aa1b5de9ffc0054a2386f96992058bb6870aab516f8cdeb8a667d56dcb" "checksum ring 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2c4db68a2e35f3497146b7e4563df7d4773a2433230c5e4b448328e31740458a" "checksum rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "adacaae16d02b6ec37fdc7acfcddf365978de76d1983d3ee22afc260e1ca9619" @@ -2052,7 +2029,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)" = "f92e629aa1d9c827b2bb8297046c1ccffc57c99b947a680d3ccff1f136a3bee9" "checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015" "checksum tar 0.4.21 (registry+https://github.com/rust-lang/crates.io-index)" = "904b43da53c99b929c4484fa281e5535f2eb86b3040de3e3e5b69708e2a8bd65" -"checksum tempfile 3.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b86c784c88d98c801132806dadd3819ed29d8600836c4088e855cdf3e178ed8a" "checksum term 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5e6b677dd1e8214ea1ef4297f85dbcbed8e8cdddb561040cc998ca2551c37561" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" "checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6" diff --git a/Cargo.toml b/Cargo.toml index 3851aee..3ec73f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,6 @@ chrono-humanize = "0.0.11" maud = { version = "0.20.0", features = ["actix-web"] } serde = { version = "1.0.89", features = ["derive"] } tar = "0.4" -tempfile = "3.0.7" bytes = "0.4.12" futures = "0.1.25" libflate = "0.1.20" -- cgit v1.2.3 From 3c3a4ebf62846a819d4114a6b79b3ea225bde6ec Mon Sep 17 00:00:00 2001 From: boasting-squirrel Date: Wed, 13 Mar 2019 18:18:30 +0100 Subject: upgraded to libflate 0.1.21 --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1e0b693..410cb9b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -662,7 +662,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libflate" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -780,7 +780,7 @@ dependencies = [ "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libflate 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", + "libflate 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "maud 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", "nanoid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1946,7 +1946,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" "checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" "checksum libc 0.2.49 (registry+https://github.com/rust-lang/crates.io-index)" = "413f3dfc802c5dc91dc570b05125b6cda9855edfaa9825c9849807876376e70e" -"checksum libflate 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "54d1ddf9c52870243c5689d7638d888331c1116aa5b398f3ba1acfa7d8758ca1" +"checksum libflate 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "7346a83e8a2c3958d44d24225d905385dc31fc16e89dffb356c457b278914d20" "checksum linked-hash-map 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7860ec297f7008ff7a1e3382d7f7e1dcd69efc94751a2284bafc3d013c2aa939" "checksum literalext 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2f42dd699527975a1e0d722e0707998671188a0125f2051d2d192fc201184a81" "checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" diff --git a/Cargo.toml b/Cargo.toml index 3ec73f9..922e1ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,5 +40,5 @@ serde = { version = "1.0.89", features = ["derive"] } tar = "0.4" bytes = "0.4.12" futures = "0.1.25" -libflate = "0.1.20" +libflate = "0.1.21" failure = "0.1.5" \ No newline at end of file -- cgit v1.2.3 From 17ef61a126e81c9ecfd1ebdd89537e854a06cae6 Mon Sep 17 00:00:00 2001 From: boasting-squirrel Date: Wed, 13 Mar 2019 19:30:54 +0100 Subject: Switched to standard Rust logging system --- Cargo.lock | 1 + Cargo.toml | 3 ++- src/archive.rs | 7 +------ src/errors.rs | 15 +++------------ src/listing.rs | 16 +++++----------- src/main.rs | 19 ++++++++++--------- 6 files changed, 22 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 410cb9b..ff7dbc3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -781,6 +781,7 @@ dependencies = [ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "libflate 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "maud 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", "nanoid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 922e1ca..e9cce56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,4 +41,5 @@ tar = "0.4" bytes = "0.4.12" futures = "0.1.25" libflate = "0.1.21" -failure = "0.1.5" \ No newline at end of file +failure = "0.1.5" +log = "0.4.6" \ No newline at end of file diff --git a/src/archive.rs b/src/archive.rs index c535c50..e1460b9 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -6,7 +6,6 @@ use serde::Deserialize; use std::io; use std::path::PathBuf; use tar::Builder; -use yansi::Color; use crate::errors; @@ -100,7 +99,7 @@ fn tar(src_dir: String, inner_folder: String) -> Result, errors::Compres errors::CompressionErrorKind::TarBuildingError { message: format!( "failed to append the content of {} in the TAR archive", - color_path(&src_dir) + &src_dir ), }, )?; @@ -132,7 +131,3 @@ fn gzip(mut data: &[u8]) -> Result, errors::CompressionError> { Ok(data) } - -fn color_path(path: &str) -> String { - Color::White.paint(path).bold().to_string() -} diff --git a/src/errors.rs b/src/errors.rs index 8cfedff..a9b6c74 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,6 +1,5 @@ use failure::{Backtrace, Context, Fail}; use std::fmt::{self, Debug, Display}; -use yansi::{Color, Paint}; /// Kinds of errors which might happen during the generation of an archive #[derive(Debug, Fail)] @@ -22,18 +21,10 @@ pub enum CompressionErrorKind { /// Prints the full chain of error, up to the root cause. /// If RUST_BACKTRACE is set to 1, also prints the backtrace for each error pub fn print_error_chain(err: CompressionError) { - println!( - "{error} {err}", - error = Paint::red("error:").bold(), - err = Paint::white(&err).bold() - ); + log::error!("{}", &err); print_backtrace(&err); for cause in Fail::iter_causes(&err) { - println!( - "{} {}", - Color::RGB(255, 192, 0).paint("caused by:").to_string(), - cause - ); + log::error!("caused by: {}", cause); print_backtrace(cause); } } @@ -44,7 +35,7 @@ fn print_backtrace(err: &dyn Fail) { if let Some(backtrace) = err.backtrace() { let backtrace = backtrace.to_string(); if backtrace != "" { - println!("{}", backtrace); + log::error!("{}", backtrace); } } } diff --git a/src/listing.rs b/src/listing.rs index a9f0f19..b820aa4 100644 --- a/src/listing.rs +++ b/src/listing.rs @@ -7,7 +7,6 @@ use serde::Deserialize; use std::io; use std::path::Path; use std::time::SystemTime; -use yansi::Color; use crate::archive; use crate::errors; @@ -229,19 +228,14 @@ pub fn directory_listing( } if let Some(compression_method) = &download { - println!( - "{info} Creating an archive ({extension}) of {path}...", - info = Color::Blue.paint("info:").bold(), - extension = Color::White.paint(compression_method.extension()).bold(), - path = Color::White.paint(&dir.path.display().to_string()).bold() + log::info!( + "Creating an archive ({extension}) of {path}...", + extension = compression_method.extension(), + path = &dir.path.display().to_string() ); match archive::create_archive_file(&compression_method, &dir.path) { Ok((filename, content)) => { - println!( - "{success} {file} successfully created !", - success = Color::Green.paint("success:").bold(), - file = Color::White.paint(&filename).bold(), - ); + log::info!("{file} successfully created !", file = &filename); Ok(HttpResponse::Ok() .content_type(compression_method.content_type()) .content_length(content.len() as u64) diff --git a/src/main.rs b/src/main.rs index 260551c..f662a73 100644 --- a/src/main.rs +++ b/src/main.rs @@ -50,6 +50,13 @@ fn main() { } let miniserve_config = args::parse_args(); + + let _ = if miniserve_config.verbose { + TermLogger::init(LevelFilter::Info, Config::default()) + } else { + TermLogger::init(LevelFilter::Error, Config::default()) + }; + if miniserve_config.no_symlinks && miniserve_config .path @@ -58,16 +65,10 @@ fn main() { .file_type() .is_symlink() { - println!( - "{error} The no-symlinks option cannot be used with a symlink path", - error = Paint::red("error:").bold(), - ); + log::error!("The no-symlinks option cannot be used with a symlink path"); return; } - if miniserve_config.verbose { - let _ = TermLogger::init(LevelFilter::Info, Config::default()); - } let sys = actix::System::new("miniserve"); let inside_config = miniserve_config.clone(); @@ -121,7 +122,7 @@ fn main() { version = crate_version!() ); if !miniserve_config.path_explicitly_chosen { - println!("{info} miniserve has been invoked without an explicit path so it will serve the current directory.", info=Color::Blue.paint("info:").bold()); + println!("{warning} miniserve has been invoked without an explicit path so it will serve the current directory.", warning=Color::RGB(255, 192, 0).paint("Notice:").bold()); println!( " Invoke with -h|--help to see options or invoke as `miniserve .` to hide this advice." ); @@ -166,7 +167,7 @@ fn main() { path = Color::Yellow.paint(path_string).bold(), addresses = addresses, ); - println!("Quit by pressing CTRL-C"); + println!("\nQuit by pressing CTRL-C"); let _ = sys.run(); } -- cgit v1.2.3 From e7fadb7d631824841fa61cb2373c7e260e3b7b6a Mon Sep 17 00:00:00 2001 From: boasting-squirrel Date: Thu, 14 Mar 2019 00:08:19 +0100 Subject: Improved grammar of TarBuildingError error message --- src/archive.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/archive.rs b/src/archive.rs index e1460b9..b71d50f 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -98,7 +98,7 @@ fn tar(src_dir: String, inner_folder: String) -> Result, errors::Compres tar_builder.append_dir_all(inner_folder, &src_dir).context( errors::CompressionErrorKind::TarBuildingError { message: format!( - "failed to append the content of {} in the TAR archive", + "failed to append the content of {} to the TAR archive", &src_dir ), }, -- cgit v1.2.3 From 20283096380f86595c959de8d5f045437e84c964 Mon Sep 17 00:00:00 2001 From: boasting-squirrel Date: Thu, 14 Mar 2019 20:30:06 +0100 Subject: Switched to tar-rs 0.4.22 and propagate no-symlink argument to tar generation --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- src/archive.rs | 14 ++++++-------- src/listing.rs | 2 +- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ff7dbc3..26f6848 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -788,7 +788,7 @@ dependencies = [ "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "simplelog 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", - "tar 0.4.21 (registry+https://github.com/rust-lang/crates.io-index)", + "tar 0.4.22 (registry+https://github.com/rust-lang/crates.io-index)", "yansi 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1371,7 +1371,7 @@ dependencies = [ [[package]] name = "tar" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "filetime 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2029,7 +2029,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum structopt-derive 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "528aeb7351d042e6ffbc2a6fb76a86f9b622fdf7c25932798e7a82cb03bc94c6" "checksum syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)" = "f92e629aa1d9c827b2bb8297046c1ccffc57c99b947a680d3ccff1f136a3bee9" "checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015" -"checksum tar 0.4.21 (registry+https://github.com/rust-lang/crates.io-index)" = "904b43da53c99b929c4484fa281e5535f2eb86b3040de3e3e5b69708e2a8bd65" +"checksum tar 0.4.22 (registry+https://github.com/rust-lang/crates.io-index)" = "c2167ff53da2a661702b3299f71a91b61b1dffef36b4b2884b1f9c67254c0133" "checksum term 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5e6b677dd1e8214ea1ef4297f85dbcbed8e8cdddb561040cc998ca2551c37561" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" "checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6" diff --git a/Cargo.toml b/Cargo.toml index e9cce56..6f4aa16 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,7 @@ chrono = "0.4.6" chrono-humanize = "0.0.11" maud = { version = "0.20.0", features = ["actix-web"] } serde = { version = "1.0.89", features = ["derive"] } -tar = "0.4" +tar = "0.4.22" bytes = "0.4.12" futures = "0.1.25" libflate = "0.1.21" diff --git a/src/archive.rs b/src/archive.rs index b71d50f..b351fb9 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -49,14 +49,15 @@ impl CompressionMethod { pub fn create_archive_file( method: &CompressionMethod, dir: &PathBuf, + skip_symlinks: bool ) -> Result<(String, Bytes), errors::CompressionError> { match method { - CompressionMethod::TarGz => tgz_compress(&dir), + CompressionMethod::TarGz => tgz_compress(&dir, skip_symlinks), } } /// Compresses a given folder in .tar.gz format -fn tgz_compress(dir: &PathBuf) -> Result<(String, Bytes), errors::CompressionError> { +fn tgz_compress(dir: &PathBuf, skip_symlinks: bool) -> Result<(String, Bytes), errors::CompressionError> { let src_dir = dir.display().to_string(); let inner_folder = match dir.file_name() { Some(directory_name) => match directory_name.to_str() { @@ -76,7 +77,7 @@ fn tgz_compress(dir: &PathBuf) -> Result<(String, Bytes), errors::CompressionErr let dst_filename = format!("{}.tar", inner_folder); let dst_tgz_filename = format!("{}.gz", dst_filename); - let tar_content = tar(src_dir, inner_folder.to_string()) + let tar_content = tar(src_dir, inner_folder.to_string(), skip_symlinks) .context(errors::CompressionErrorKind::TarContentError)?; let gz_data = gzip(&tar_content).context(errors::CompressionErrorKind::GZipContentError)?; @@ -87,14 +88,11 @@ fn tgz_compress(dir: &PathBuf) -> Result<(String, Bytes), errors::CompressionErr } /// Creates a temporary tar file of a given directory, reads it and returns its content as bytes -fn tar(src_dir: String, inner_folder: String) -> Result, errors::CompressionError> { +fn tar(src_dir: String, inner_folder: String, skip_symlinks: bool) -> Result, errors::CompressionError> { // Create a TAR file of src_dir let mut tar_builder = Builder::new(Vec::new()); - // Temporary workaround for known issue: - // https://github.com/alexcrichton/tar-rs/issues/147 - // https://github.com/alexcrichton/tar-rs/issues/174 - tar_builder.follow_symlinks(false); + tar_builder.follow_symlinks(!skip_symlinks); tar_builder.append_dir_all(inner_folder, &src_dir).context( errors::CompressionErrorKind::TarBuildingError { message: format!( diff --git a/src/listing.rs b/src/listing.rs index b820aa4..a243c22 100644 --- a/src/listing.rs +++ b/src/listing.rs @@ -233,7 +233,7 @@ pub fn directory_listing( extension = compression_method.extension(), path = &dir.path.display().to_string() ); - match archive::create_archive_file(&compression_method, &dir.path) { + match archive::create_archive_file(&compression_method, &dir.path, skip_symlinks) { Ok((filename, content)) => { log::info!("{file} successfully created !", file = &filename); Ok(HttpResponse::Ok() -- cgit v1.2.3 From ed705b967a85990b0e0d36187c9d64bba96f8046 Mon Sep 17 00:00:00 2001 From: boasting-squirrel Date: Thu, 14 Mar 2019 20:32:04 +0100 Subject: Cargo fmt --- src/archive.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/archive.rs b/src/archive.rs index b351fb9..c536c2d 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -49,7 +49,7 @@ impl CompressionMethod { pub fn create_archive_file( method: &CompressionMethod, dir: &PathBuf, - skip_symlinks: bool + skip_symlinks: bool, ) -> Result<(String, Bytes), errors::CompressionError> { match method { CompressionMethod::TarGz => tgz_compress(&dir, skip_symlinks), @@ -57,7 +57,10 @@ pub fn create_archive_file( } /// Compresses a given folder in .tar.gz format -fn tgz_compress(dir: &PathBuf, skip_symlinks: bool) -> Result<(String, Bytes), errors::CompressionError> { +fn tgz_compress( + dir: &PathBuf, + skip_symlinks: bool, +) -> Result<(String, Bytes), errors::CompressionError> { let src_dir = dir.display().to_string(); let inner_folder = match dir.file_name() { Some(directory_name) => match directory_name.to_str() { @@ -88,7 +91,11 @@ fn tgz_compress(dir: &PathBuf, skip_symlinks: bool) -> Result<(String, Bytes), e } /// Creates a temporary tar file of a given directory, reads it and returns its content as bytes -fn tar(src_dir: String, inner_folder: String, skip_symlinks: bool) -> Result, errors::CompressionError> { +fn tar( + src_dir: String, + inner_folder: String, + skip_symlinks: bool, +) -> Result, errors::CompressionError> { // Create a TAR file of src_dir let mut tar_builder = Builder::new(Vec::new()); -- cgit v1.2.3 From 312a644244d0ce9e299183a4985adf356598785e Mon Sep 17 00:00:00 2001 From: boasting-squirrel Date: Fri, 15 Mar 2019 17:50:30 +0100 Subject: Improved design --- src/renderer.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/renderer.rs b/src/renderer.rs index b83a67c..38a3802 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -19,7 +19,7 @@ pub fn page( (page_header(page_title)) body { span #top { } - h1 { (page_title) } + h1.title { (page_title) } div.download { (archive_button(archive::CompressionMethod::TarGz)) } @@ -182,6 +182,9 @@ fn css() -> Markup { margin: 0; padding: 0; } + h1 { + font-size: 1.5rem; + } table { margin-top: 2rem; width: 100%; @@ -277,8 +280,8 @@ fn css() -> Markup { } .download { display: flex; - justify-content: flex-end; - padding: 0.125rem; + flex-wrap: wrap; + margin-top: .5rem; } .download a, .download a:visited { color: #3498db; @@ -287,6 +290,7 @@ fn css() -> Markup { background: #efefef; padding: 0.5rem; border-radius: 0.2rem; + margin-top: 1rem; } .download a:hover { background: #deeef7a6; -- cgit v1.2.3 From bc293fa54a612ff7e2d6088ba5d91890cad75b81 Mon Sep 17 00:00:00 2001 From: boasting-squirrel Date: Fri, 15 Mar 2019 17:50:59 +0100 Subject: Renamed create_archive_file function and added documentation --- src/archive.rs | 10 ++++++---- src/listing.rs | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/archive.rs b/src/archive.rs index c536c2d..bc8ea3b 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -46,7 +46,9 @@ impl CompressionMethod { } } -pub fn create_archive_file( +/// Creates an archive of a folder, using the algorithm the user chose from the web interface +/// This method returns the archive as a stream of bytes +pub fn create_archive( method: &CompressionMethod, dir: &PathBuf, skip_symlinks: bool, @@ -56,7 +58,7 @@ pub fn create_archive_file( } } -/// Compresses a given folder in .tar.gz format +/// Compresses a given folder in .tar.gz format, and returns the result as a stream of bytes fn tgz_compress( dir: &PathBuf, skip_symlinks: bool, @@ -90,7 +92,7 @@ fn tgz_compress( Ok((dst_tgz_filename, data)) } -/// Creates a temporary tar file of a given directory, reads it and returns its content as bytes +/// Creates a TAR archive of a folder, and returns it as a stream of bytes fn tar( src_dir: String, inner_folder: String, @@ -119,7 +121,7 @@ fn tar( Ok(tar_content) } -/// Compresses a stream of bytes using the GZIP algorithm +/// Compresses a stream of bytes using the GZIP algorithm, and returns the resulting stream fn gzip(mut data: &[u8]) -> Result, errors::CompressionError> { let mut encoder = Encoder::new(Vec::new()).context(errors::CompressionErrorKind::GZipBuildingError { diff --git a/src/listing.rs b/src/listing.rs index a243c22..ed3b63d 100644 --- a/src/listing.rs +++ b/src/listing.rs @@ -233,7 +233,7 @@ pub fn directory_listing( extension = compression_method.extension(), path = &dir.path.display().to_string() ); - match archive::create_archive_file(&compression_method, &dir.path, skip_symlinks) { + match archive::create_archive(&compression_method, &dir.path, skip_symlinks) { Ok((filename, content)) => { log::info!("{file} successfully created !", file = &filename); Ok(HttpResponse::Ok() -- cgit v1.2.3 From cc80d54d5274ad54a916617d4f1d7686880192ab Mon Sep 17 00:00:00 2001 From: boasting-squirrel Date: Fri, 15 Mar 2019 17:55:17 +0100 Subject: Added missing padding on download div --- src/renderer.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/renderer.rs b/src/renderer.rs index 38a3802..66fc714 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -282,6 +282,7 @@ fn css() -> Markup { display: flex; flex-wrap: wrap; margin-top: .5rem; + padding: 0.125rem; } .download a, .download a:visited { color: #3498db; -- cgit v1.2.3 From 6a3db431ef0eb0d235bf5ae319076b2ce229c583 Mon Sep 17 00:00:00 2001 From: boasting-squirrel Date: Tue, 19 Mar 2019 20:08:26 +0100 Subject: Removed Content-Length --- src/listing.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/listing.rs b/src/listing.rs index ed3b63d..c4daf88 100644 --- a/src/listing.rs +++ b/src/listing.rs @@ -238,7 +238,6 @@ pub fn directory_listing( log::info!("{file} successfully created !", file = &filename); Ok(HttpResponse::Ok() .content_type(compression_method.content_type()) - .content_length(content.len() as u64) .content_encoding(compression_method.content_encoding()) .header("Content-Transfer-Encoding", "binary") .header( -- cgit v1.2.3 From 5b5f599055fb6221936c0985f656d0c4b7b2cb23 Mon Sep 17 00:00:00 2001 From: boasting-squirrel Date: Tue, 19 Mar 2019 20:08:57 +0100 Subject: Added documentation for errors and removed useless errors --- src/archive.rs | 11 ++++++++--- src/errors.rs | 14 ++++++++++---- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/archive.rs b/src/archive.rs index bc8ea3b..fcf39fd 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -82,9 +82,14 @@ fn tgz_compress( let dst_filename = format!("{}.tar", inner_folder); let dst_tgz_filename = format!("{}.gz", dst_filename); - let tar_content = tar(src_dir, inner_folder.to_string(), skip_symlinks) - .context(errors::CompressionErrorKind::TarContentError)?; - let gz_data = gzip(&tar_content).context(errors::CompressionErrorKind::GZipContentError)?; + let tar_content = tar(src_dir, inner_folder.to_string(), skip_symlinks).context( + errors::CompressionErrorKind::TarBuildingError { + message: "an error occured while writing the TAR archive".to_string(), + }, + )?; + let gz_data = gzip(&tar_content).context(errors::CompressionErrorKind::GZipBuildingError { + message: "an error occured while writing the GZIP archive".to_string(), + })?; let mut data = Bytes::new(); data.extend_from_slice(&gz_data); diff --git a/src/errors.rs b/src/errors.rs index a9b6c74..2aa5f58 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -4,18 +4,24 @@ use std::fmt::{self, Debug, Display}; /// Kinds of errors which might happen during the generation of an archive #[derive(Debug, Fail)] pub enum CompressionErrorKind { + /// This error will occur if the directory name could not be retrieved from the path + /// See https://doc.rust-lang.org/std/path/struct.Path.html#method.file_name #[fail(display = "Invalid path: directory name terminates in \"..\"")] InvalidDirectoryName, + /// This error will occur when trying to convert an OSString into a String, if the path + /// contains invalid UTF-8 characters + /// See https://doc.rust-lang.org/std/ffi/struct.OsStr.html#method.to_str #[fail(display = "Invalid path: directory name contains invalid UTF-8 characters")] InvalidUTF8DirectoryName, + /// This error might occur while building a TAR archive, or while writing the termination sections + /// See https://docs.rs/tar/0.4.22/tar/struct.Builder.html#method.append_dir_all + /// and https://docs.rs/tar/0.4.22/tar/struct.Builder.html#method.into_inner #[fail(display = "Failed to create the TAR archive: {}", message)] TarBuildingError { message: String }, + /// This error might occur while building a GZIP archive, or while writing the GZIP trailer + /// See https://docs.rs/libflate/0.1.21/libflate/gzip/struct.Encoder.html#method.finish #[fail(display = "Failed to create the GZIP archive: {}", message)] GZipBuildingError { message: String }, - #[fail(display = "Failed to retrieve TAR content")] - TarContentError, - #[fail(display = "Failed to retrieve GZIP content")] - GZipContentError, } /// Prints the full chain of error, up to the root cause. -- cgit v1.2.3 From b9ee5574f389134ccfbdd970eb9a4a355c4e091b Mon Sep 17 00:00:00 2001 From: boasting-squirrel Date: Tue, 19 Mar 2019 20:39:43 +0100 Subject: Fixed comments --- src/archive.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/archive.rs b/src/archive.rs index fcf39fd..206d252 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -103,10 +103,10 @@ fn tar( inner_folder: String, skip_symlinks: bool, ) -> Result, errors::CompressionError> { - // Create a TAR file of src_dir let mut tar_builder = Builder::new(Vec::new()); tar_builder.follow_symlinks(!skip_symlinks); + // Recursively adds the content of src_dir into the archive stream tar_builder.append_dir_all(inner_folder, &src_dir).context( errors::CompressionErrorKind::TarBuildingError { message: format!( -- cgit v1.2.3 From 20b7c82599ae23638bb9a79e3b7aafa1f7a42232 Mon Sep 17 00:00:00 2001 From: boasting-squirrel Date: Tue, 19 Mar 2019 21:00:14 +0100 Subject: Updated README with known limitations --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index c2a85e4..2b8e8b3 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,10 @@ Sometimes this is just a more practical and quick way than doing things properly - Mega fast and highly parallel (thanks to [Rust](https://www.rust-lang.org/) and [Actix](https://actix.rs/)) - Folder download (compressed in .tar.gz) +## Known limitations + +- **For now**, the tar.gz compression is not async-ready, which means that the whole archive needs to be created (in memory) before the download starts. While it should not be a problem for small folders, the download feature can really get resource-heavy for large folders. + ## How to install **On Linux**: Download `miniserve-linux` from [the releases page](https://github.com/svenstaro/miniserve/releases) and run -- cgit v1.2.3