aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAli MJ Al-Nasrawy <alimjalnasrawy@gmail.com>2021-04-18 03:47:23 +0000
committerGitHub <noreply@github.com>2021-04-18 03:47:23 +0000
commitc368a113957d42666f080303783174e4adaf02b4 (patch)
tree96349d7f01be73cf6f80962bf34a9af543c2971c
parentDon't use different syle for symlink symbol (diff)
parentUpdate README for separate .tar and .tar.gz flags (diff)
downloadminiserve-c368a113957d42666f080303783174e4adaf02b4.tar.gz
miniserve-c368a113957d42666f080303783174e4adaf02b4.zip
Merge branch 'master' into rfc-resolve-symlinks
-rw-r--r--CHANGELOG.md17
-rw-r--r--Cargo.lock415
-rw-r--r--Cargo.toml39
-rw-r--r--README.md27
-rw-r--r--data/style.scss7
-rw-r--r--src/archive.rs31
-rw-r--r--src/args.rs121
-rw-r--r--src/errors.rs52
-rw-r--r--src/file_upload.rs4
-rw-r--r--src/listing.rs30
-rw-r--r--src/main.rs132
-rw-r--r--src/renderer.rs13
-rw-r--r--tests/archive.rs41
-rw-r--r--tests/cli.rs28
-rw-r--r--tests/fixtures/mod.rs3
-rw-r--r--tests/serve_request.rs11
16 files changed, 511 insertions, 460 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a126f26..488ba6d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,20 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
<!-- next-header -->
## [Unreleased] - ReleaseDate
+- Fix breadcrumbs for right-to-left languages [#489](https://github.com/svenstaro/miniserve/pull/489) (thanks @aliemjay)
+- Fix URL percent encoding for special characters [#485](https://github.com/svenstaro/miniserve/pull/485) (thanks @aliemjay)
+- Wrap breadcrumbs at any char [#496](https://github.com/svenstaro/miniserve/pull/496) (thanks @aliemjay)
+- Add separate flags for compressed and uncompressed tar archives [#492](https://github.com/svenstaro/miniserve/pull/492) (thanks @deantvv)
+
+## [0.13.0] - 2021-03-28
+- Change default log level to `Warn`
+- Change some messages a bit to be more clear
+- Add `--print-completions` to print shell completions for various supported shells [#482](https://github.com/svenstaro/miniserve/pull/482) (thanks @rouge8)
+- Don't print some messages if not attached to an interactive terminal
+- Refuse to start if not attached to interactive terminal and no explicit path is provided
+
+ This is a security consideration as you wouldn't want to run miniserve without an explicit path
+ as a service. You could end up serving `/` or `/root` in case those working directories are set.
## [0.12.1] - 2021-03-27
- Fix QR code not showing when using both `--random-route` and `--qrcode` [#480](https://github.com/svenstaro/miniserve/pull/480) (thanks @rouge8)
@@ -73,7 +87,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Some theme related bug fixes (thanks @boastful-squirrel)
<!-- next-url -->
-[Unreleased]: https://github.com/svenstaro/miniserve/compare/v0.12.1...HEAD
+[Unreleased]: https://github.com/svenstaro/miniserve/compare/v0.13.0...HEAD
+[0.13.0]: https://github.com/svenstaro/miniserve/compare/v0.12.1...v0.13.0
[0.12.1]: https://github.com/svenstaro/miniserve/compare/v0.12.0...v0.12.1
[0.12.0]: https://github.com/svenstaro/miniserve/compare/v0.11.0...v0.12.0
[0.11.0]: https://github.com/svenstaro/miniserve/compare/v0.10.4...v0.11.0
diff --git a/Cargo.lock b/Cargo.lock
index df4bb1d..9629fc3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -13,7 +13,7 @@ dependencies = [
"futures-core",
"futures-sink",
"log",
- "pin-project 0.4.27",
+ "pin-project 0.4.28",
"tokio 0.2.25",
"tokio-util 0.3.1",
]
@@ -93,7 +93,7 @@ dependencies = [
"log",
"mime",
"percent-encoding",
- "pin-project 1.0.6",
+ "pin-project 1.0.7",
"rand 0.7.3",
"regex",
"serde",
@@ -187,7 +187,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0052435d581b5be835d11f4eb3bce417c8af18d87ddf8ace99f8e67e595882bb"
dependencies = [
"futures-util",
- "pin-project 0.4.27",
+ "pin-project 0.4.28",
]
[[package]]
@@ -247,7 +247,7 @@ dependencies = [
"futures-sink",
"futures-util",
"log",
- "pin-project 0.4.27",
+ "pin-project 0.4.28",
"slab",
]
@@ -279,7 +279,7 @@ dependencies = [
"fxhash",
"log",
"mime",
- "pin-project 1.0.6",
+ "pin-project 1.0.7",
"regex",
"serde",
"serde_json",
@@ -313,6 +313,12 @@ dependencies = [
]
[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
name = "adler32"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -387,9 +393,9 @@ dependencies = [
[[package]]
name = "async-trait"
-version = "0.1.48"
+version = "0.1.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36ea56748e10732c49404c153638a15ec3d6211ec5ff35d9bb20e13b93576adf"
+checksum = "0b98e84bbb4cbcdd97da190ba0c58a1bb0de2c1fdf67d159e192ed766aeca722"
dependencies = [
"proc-macro2",
"quote",
@@ -409,12 +415,6 @@ dependencies = [
[[package]]
name = "autocfg"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
-
-[[package]]
-name = "autocfg"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
@@ -618,7 +618,7 @@ dependencies = [
"libc",
"num-integer",
"num-traits",
- "time 0.1.44",
+ "time 0.1.43",
"winapi 0.3.9",
]
@@ -647,15 +647,6 @@ dependencies = [
]
[[package]]
-name = "cloudabi"
-version = "0.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
-dependencies = [
- "bitflags",
-]
-
-[[package]]
name = "codemap"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -663,9 +654,9 @@ checksum = "b9e769b5c8c8283982a987c6e948e540254f1058d5a74b8794914d4ef5fc2a24"
[[package]]
name = "const_fn"
-version = "0.4.5"
+version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6"
+checksum = "402da840495de3f976eaefc3485b7f5eb5b0bf9761f9a47be27fe975b3b8c2ec"
[[package]]
name = "convert_case"
@@ -711,7 +702,7 @@ version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49"
dependencies = [
- "autocfg 1.0.1",
+ "autocfg",
"cfg-if 1.0.0",
"lazy_static",
]
@@ -812,11 +803,11 @@ dependencies = [
[[package]]
name = "flate2"
-version = "1.0.14"
+version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2cfff41391129e0a856d6d822600b8d71179d46879e310417eb9c762eb178b42"
+checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0"
dependencies = [
- "cfg-if 0.1.10",
+ "cfg-if 1.0.0",
"crc32fast",
"libc",
"miniz_oxide",
@@ -848,12 +839,6 @@ dependencies = [
]
[[package]]
-name = "fuchsia-cprng"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
-
-[[package]]
name = "fuchsia-zircon"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -881,9 +866,9 @@ dependencies = [
[[package]]
name = "futures"
-version = "0.3.13"
+version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f55667319111d593ba876406af7c409c0ebb44dc4be6132a783ccf163ea14c1"
+checksum = "a9d5813545e459ad3ca1bff9915e9ad7f1a47dc6a91b627ce321d5863b7dd253"
dependencies = [
"futures-channel",
"futures-core",
@@ -896,9 +881,9 @@ dependencies = [
[[package]]
name = "futures-channel"
-version = "0.3.13"
+version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c2dd2df839b57db9ab69c2c9d8f3e8c81984781937fe2807dc6dcf3b2ad2939"
+checksum = "ce79c6a52a299137a6013061e0cf0e688fce5d7f1bc60125f520912fdb29ec25"
dependencies = [
"futures-core",
"futures-sink",
@@ -906,15 +891,15 @@ dependencies = [
[[package]]
name = "futures-core"
-version = "0.3.13"
+version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "15496a72fabf0e62bdc3df11a59a3787429221dd0710ba8ef163d6f7a9112c94"
+checksum = "098cd1c6dda6ca01650f1a37a794245eb73181d0d4d4e955e2f3c37db7af1815"
[[package]]
name = "futures-executor"
-version = "0.3.13"
+version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "891a4b7b96d84d5940084b2a37632dd65deeae662c114ceaa2c879629c9c0ad1"
+checksum = "10f6cb7042eda00f0049b1d2080aa4b93442997ee507eb3828e8bd7577f94c9d"
dependencies = [
"futures-core",
"futures-task",
@@ -923,15 +908,15 @@ dependencies = [
[[package]]
name = "futures-io"
-version = "0.3.13"
+version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d71c2c65c57704c32f5241c1223167c2c3294fd34ac020c807ddbe6db287ba59"
+checksum = "365a1a1fb30ea1c03a830fdb2158f5236833ac81fa0ad12fe35b29cddc35cb04"
[[package]]
name = "futures-macro"
-version = "0.3.13"
+version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea405816a5139fb39af82c2beb921d52143f556038378d6db21183a5c37fbfb7"
+checksum = "668c6733a182cd7deb4f1de7ba3bf2120823835b3bcfbeacf7d2c4a773c1bb8b"
dependencies = [
"proc-macro-hack",
"proc-macro2",
@@ -941,21 +926,21 @@ dependencies = [
[[package]]
name = "futures-sink"
-version = "0.3.13"
+version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85754d98985841b7d4f5e8e6fbfa4a4ac847916893ec511a2917ccd8525b8bb3"
+checksum = "5c5629433c555de3d82861a7a4e3794a4c40040390907cfbfd7143a92a426c23"
[[package]]
name = "futures-task"
-version = "0.3.13"
+version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa189ef211c15ee602667a6fcfe1c1fd9e07d42250d2156382820fba33c9df80"
+checksum = "ba7aa51095076f3ba6d9a1f702f74bd05ec65f555d70d2033d55ba8d69f581bc"
[[package]]
name = "futures-util"
-version = "0.3.13"
+version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1812c7ab8aedf8d6f2701a43e1243acdbcc2b36ab26e2ad421eb99ac963d96d1"
+checksum = "3c144ad54d60f23927f0a6b6d816e4271278b64f005ad65e4e35291d2de9c025"
dependencies = [
"futures-channel",
"futures-core",
@@ -1009,7 +994,7 @@ checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
dependencies = [
"cfg-if 1.0.0",
"libc",
- "wasi 0.10.0+wasi-snapshot-preview1",
+ "wasi 0.10.2+wasi-snapshot-preview1",
]
[[package]]
@@ -1090,8 +1075,8 @@ dependencies = [
"http",
"indexmap",
"slab",
- "tokio 1.4.0",
- "tokio-util 0.6.5",
+ "tokio 1.5.0",
+ "tokio-util 0.6.6",
"tracing",
]
@@ -1102,7 +1087,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e91b62f79061a0bc2e046024cb7ba44b08419ed238ecbd9adbd787434b9e8c25"
dependencies = [
"ahash",
- "autocfg 1.0.1",
+ "autocfg",
]
[[package]]
@@ -1162,9 +1147,9 @@ dependencies = [
[[package]]
name = "http"
-version = "0.2.3"
+version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7245cd7449cc792608c3c8a9eaf69bd4eabbabf802713748fd739c98b82f0747"
+checksum = "527e8c9ac747e28542699a951517aa9a6945af506cd1f2e1b53a576c17b6cc11"
dependencies = [
"bytes 1.0.1",
"fnv",
@@ -1184,9 +1169,9 @@ dependencies = [
[[package]]
name = "httparse"
-version = "1.3.5"
+version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "615caabe2c3160b313d52ccc905335f4ed5f10881dd63dc5699d47e90be85691"
+checksum = "4a1ce40d6fc9764887c2fdc7305c3dcc429ba11ff981c1509416afd5697e4437"
[[package]]
name = "httpdate"
@@ -1210,9 +1195,9 @@ dependencies = [
"httparse",
"httpdate",
"itoa",
- "pin-project 1.0.6",
+ "pin-project 1.0.7",
"socket2 0.4.0",
- "tokio 1.4.0",
+ "tokio 1.5.0",
"tower-service",
"tracing",
"want",
@@ -1228,16 +1213,16 @@ dependencies = [
"hyper",
"log",
"rustls",
- "tokio 1.4.0",
+ "tokio 1.5.0",
"tokio-rustls",
"webpki",
]
[[package]]
name = "idna"
-version = "0.2.2"
+version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89829a5d69c23d348314a7ac337fe39173b61149a9864deabd260983aed48c21"
+checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
dependencies = [
"matches",
"unicode-bidi",
@@ -1268,7 +1253,7 @@ version = "1.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3"
dependencies = [
- "autocfg 1.0.1",
+ "autocfg",
"hashbrown 0.9.1",
]
@@ -1316,9 +1301,9 @@ checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
[[package]]
name = "js-sys"
-version = "0.3.49"
+version = "0.3.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc15e39392125075f60c95ba416f5381ff6c3a948ff02ab12464715adf56c821"
+checksum = "2d99f9e3e84b8f67f846ef5b4cbbc3b1c29f6c759fcbce6f01aa0e73d932a24c"
dependencies = [
"wasm-bindgen",
]
@@ -1356,27 +1341,29 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
-version = "0.2.91"
+version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7"
+checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
[[package]]
name = "libflate"
-version = "1.0.4"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "158ae2ca09a761eaf6050894f5a6f013f2773dafe24f67bfa73a7504580e2916"
+checksum = "6d87eae36b3f680f7f01645121b782798b56ef33c53f83d1c66ba3a22b60bfe3"
dependencies = [
"adler32",
"crc32fast",
"libflate_lz77",
- "rle-decode-fast",
]
[[package]]
name = "libflate_lz77"
-version = "1.0.0"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3286f09f7d4926fc486334f28d8d2e6ebe4f7f9994494b6dab27ddfad2c9b11b"
+checksum = "39a734c0493409afcd49deee13c006a04e3586b9761a03543c6272c9c51f2f5a"
+dependencies = [
+ "rle-decode-fast",
+]
[[package]]
name = "linked-hash-map"
@@ -1386,9 +1373,9 @@ checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
[[package]]
name = "lock_api"
-version = "0.4.2"
+version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312"
+checksum = "5a3c91c24eae6777794bb1997ad98bbb87daf92890acab859f7eaa4320333176"
dependencies = [
"scopeguard",
]
@@ -1513,7 +1500,7 @@ dependencies = [
[[package]]
name = "miniserve"
-version = "0.12.2-alpha.0"
+version = "0.13.1-alpha.0"
dependencies = [
"actix-files",
"actix-multipart",
@@ -1522,6 +1509,7 @@ dependencies = [
"alphanumeric-sort",
"assert_cmd",
"assert_fs",
+ "atty",
"bytes 1.0.1",
"bytesize",
"chrono",
@@ -1559,11 +1547,12 @@ dependencies = [
[[package]]
name = "miniz_oxide"
-version = "0.3.7"
+version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435"
+checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
dependencies = [
- "adler32",
+ "adler",
+ "autocfg",
]
[[package]]
@@ -1632,11 +1621,11 @@ dependencies = [
[[package]]
name = "nanoid"
-version = "0.3.0"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a6226bc4e142124cb44e309a37a04cd9bb10e740d8642855441d3b14808f635e"
+checksum = "3ffa00dec017b5b1a8b7cf5e2c008bfda1aa7e0697ac1508b491fdf2622fb4d8"
dependencies = [
- "rand 0.6.5",
+ "rand 0.8.3",
]
[[package]]
@@ -1687,7 +1676,7 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d0a3d5e207573f948a9e5376662aa743a2ea13f7c50a554d7af443a73fbfeba"
dependencies = [
- "autocfg 1.0.1",
+ "autocfg",
"num-integer",
"num-traits",
]
@@ -1698,7 +1687,7 @@ version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
dependencies = [
- "autocfg 1.0.1",
+ "autocfg",
"num-traits",
]
@@ -1708,7 +1697,7 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
dependencies = [
- "autocfg 1.0.1",
+ "autocfg",
"num-bigint",
"num-integer",
"num-traits",
@@ -1720,7 +1709,7 @@ version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
dependencies = [
- "autocfg 1.0.1",
+ "autocfg",
]
[[package]]
@@ -1859,27 +1848,27 @@ dependencies = [
[[package]]
name = "pin-project"
-version = "0.4.27"
+version = "0.4.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2ffbc8e94b38ea3d2d8ba92aea2983b503cd75d0888d75b86bb37970b5698e15"
+checksum = "918192b5c59119d51e0cd221f4d49dde9112824ba717369e903c97d076083d0f"
dependencies = [
- "pin-project-internal 0.4.27",
+ "pin-project-internal 0.4.28",
]
[[package]]
name = "pin-project"
-version = "1.0.6"
+version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc174859768806e91ae575187ada95c91a29e96a98dc5d2cd9a1fed039501ba6"
+checksum = "c7509cc106041c40a4518d2af7a61530e1eed0e6285296a3d8c5472806ccc4a4"
dependencies = [
- "pin-project-internal 1.0.6",
+ "pin-project-internal 1.0.7",
]
[[package]]
name = "pin-project-internal"
-version = "0.4.27"
+version = "0.4.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "65ad2ae56b6abe3a1ee25f15ee605bacadb9a764edaba9c2bf4103800d4a1895"
+checksum = "3be26700300be6d9d23264c73211d8190e755b6b5ca7a1b28230025511b52a5e"
dependencies = [
"proc-macro2",
"quote",
@@ -1888,9 +1877,9 @@ dependencies = [
[[package]]
name = "pin-project-internal"
-version = "1.0.6"
+version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a490329918e856ed1b083f244e3bfe2d8c4f336407e4ea9e1a9f479ff09049e5"
+checksum = "48c950132583b500556b1efd71d45b319029f2b71518d979fcc208e16b42426f"
dependencies = [
"proc-macro2",
"quote",
@@ -1970,9 +1959,9 @@ dependencies = [
[[package]]
name = "pretty_assertions"
-version = "0.7.1"
+version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f297542c27a7df8d45de2b0e620308ab883ad232d06c14b76ac3e144bda50184"
+checksum = "1cab0e7c02cf376875e9335e0ba1da535775beb5450d21e1dffca068818ed98b"
dependencies = [
"ansi_term 0.12.1",
"ctor",
@@ -2018,9 +2007,9 @@ checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086"
[[package]]
name = "proc-macro2"
-version = "1.0.24"
+version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
+checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec"
dependencies = [
"unicode-xid",
]
@@ -2048,25 +2037,6 @@ dependencies = [
[[package]]
name = "rand"
-version = "0.6.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
-dependencies = [
- "autocfg 0.1.7",
- "libc",
- "rand_chacha 0.1.1",
- "rand_core 0.4.2",
- "rand_hc 0.1.0",
- "rand_isaac",
- "rand_jitter",
- "rand_os",
- "rand_pcg 0.1.2",
- "rand_xorshift",
- "winapi 0.3.9",
-]
-
-[[package]]
-name = "rand"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
@@ -2076,7 +2046,7 @@ dependencies = [
"rand_chacha 0.2.2",
"rand_core 0.5.1",
"rand_hc 0.2.0",
- "rand_pcg 0.2.1",
+ "rand_pcg",
]
[[package]]
@@ -2093,16 +2063,6 @@ dependencies = [
[[package]]
name = "rand_chacha"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
-dependencies = [
- "autocfg 0.1.7",
- "rand_core 0.3.1",
-]
-
-[[package]]
-name = "rand_chacha"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
@@ -2123,21 +2083,6 @@ dependencies = [
[[package]]
name = "rand_core"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
-dependencies = [
- "rand_core 0.4.2",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
-
-[[package]]
-name = "rand_core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
@@ -2156,15 +2101,6 @@ dependencies = [
[[package]]
name = "rand_hc"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
-dependencies = [
- "rand_core 0.3.1",
-]
-
-[[package]]
-name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
@@ -2182,50 +2118,6 @@ dependencies = [
]
[[package]]
-name = "rand_isaac"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
-dependencies = [
- "rand_core 0.3.1",
-]
-
-[[package]]
-name = "rand_jitter"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b"
-dependencies = [
- "libc",
- "rand_core 0.4.2",
- "winapi 0.3.9",
-]
-
-[[package]]
-name = "rand_os"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
-dependencies = [
- "cloudabi",
- "fuchsia-cprng",
- "libc",
- "rand_core 0.4.2",
- "rdrand",
- "winapi 0.3.9",
-]
-
-[[package]]
-name = "rand_pcg"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44"
-dependencies = [
- "autocfg 0.1.7",
- "rand_core 0.4.2",
-]
-
-[[package]]
name = "rand_pcg"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2235,28 +2127,10 @@ dependencies = [
]
[[package]]
-name = "rand_xorshift"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
-dependencies = [
- "rand_core 0.3.1",
-]
-
-[[package]]
-name = "rdrand"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
-dependencies = [
- "rand_core 0.3.1",
-]
-
-[[package]]
name = "redox_syscall"
-version = "0.2.5"
+version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9"
+checksum = "8270314b5ccceb518e7e578952f0b72b88222d02e8f77f5ecf7abbb673539041"
dependencies = [
"bitflags",
]
@@ -2298,9 +2172,9 @@ dependencies = [
[[package]]
name = "reqwest"
-version = "0.11.2"
+version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf12057f289428dbf5c591c74bf10392e4a8003f993405a902f20117019022d4"
+checksum = "2296f2fac53979e8ccbc4a1136b25dcefd37be9ed7e4a1f6b05a6029c84ff124"
dependencies = [
"base64",
"bytes 1.0.1",
@@ -2322,7 +2196,7 @@ dependencies = [
"rustls",
"serde",
"serde_urlencoded",
- "tokio 1.4.0",
+ "tokio 1.5.0",
"tokio-rustls",
"url",
"wasm-bindgen",
@@ -2396,9 +2270,9 @@ dependencies = [
[[package]]
name = "rustls"
-version = "0.19.0"
+version = "0.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "064fd21ff87c6e87ed4506e68beb42459caa4a0e2eb144932e6776768556980b"
+checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7"
dependencies = [
"base64",
"log",
@@ -2430,9 +2304,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "sct"
-version = "0.6.0"
+version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c"
+checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce"
dependencies = [
"ring",
"untrusted",
@@ -2568,9 +2442,9 @@ dependencies = [
[[package]]
name = "simplelog"
-version = "0.9.0"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4bc0ffd69814a9b251d43afcabf96dad1b29f5028378056257be9e3fecc9f720"
+checksum = "59d0fe306a0ced1c88a58042dc22fc2ddd000982c26d75f6aa09a394547c41e0"
dependencies = [
"chrono",
"log",
@@ -2755,9 +2629,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "1.0.65"
+version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3a1d708c221c5a612956ef9f75b37e454e88d1f7b899fbd3a18d4252012d663"
+checksum = "48fe99c6bd8b1cc636890bcc071842de909d902c81ac7dab53ba33c421ab8ffb"
dependencies = [
"proc-macro2",
"quote",
@@ -2858,12 +2732,11 @@ dependencies = [
[[package]]
name = "time"
-version = "0.1.44"
+version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
+checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
dependencies = [
"libc",
- "wasi 0.10.0+wasi-snapshot-preview1",
"winapi 0.3.9",
]
@@ -2907,9 +2780,9 @@ dependencies = [
[[package]]
name = "tinyvec"
-version = "1.1.1"
+version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "317cca572a0e89c3ce0ca1f1bdc9369547fe318a683418e42ac8f59d14701023"
+checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342"
dependencies = [
"tinyvec_macros",
]
@@ -2942,11 +2815,11 @@ dependencies = [
[[package]]
name = "tokio"
-version = "1.4.0"
+version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "134af885d758d645f0f0505c9a8b3f9bf8a348fd822e112ab5248138348f1722"
+checksum = "83f0c8e7c0addab50b663055baf787d0af7f413a46e6e7fb9559a4e4db7137a5"
dependencies = [
- "autocfg 1.0.1",
+ "autocfg",
"bytes 1.0.1",
"libc",
"memchr",
@@ -2962,7 +2835,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6"
dependencies = [
"rustls",
- "tokio 1.4.0",
+ "tokio 1.5.0",
"webpki",
]
@@ -2982,16 +2855,16 @@ dependencies = [
[[package]]
name = "tokio-util"
-version = "0.6.5"
+version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5143d049e85af7fbc36f5454d990e62c2df705b3589f123b71f441b6b59f443f"
+checksum = "940a12c99365c31ea8dd9ba04ec1be183ffe4920102bb7122c2f515437601e8e"
dependencies = [
"bytes 1.0.1",
"futures-core",
"futures-sink",
"log",
"pin-project-lite 0.2.6",
- "tokio 1.4.0",
+ "tokio 1.5.0",
]
[[package]]
@@ -3027,7 +2900,7 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2"
dependencies = [
- "pin-project 1.0.6",
+ "pin-project 1.0.7",
"tracing",
]
@@ -3121,9 +2994,9 @@ dependencies = [
[[package]]
name = "unicode-bidi"
-version = "0.3.4"
+version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
+checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0"
dependencies = [
"matches",
]
@@ -3175,9 +3048,9 @@ dependencies = [
[[package]]
name = "utf-8"
-version = "0.7.5"
+version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7"
+checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]]
name = "v_escape"
@@ -3267,15 +3140,15 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "wasi"
-version = "0.10.0+wasi-snapshot-preview1"
+version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
+checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]]
name = "wasm-bindgen"
-version = "0.2.72"
+version = "0.2.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8fe8f61dba8e5d645a4d8132dc7a0a66861ed5e1045d2c0ed940fab33bac0fbe"
+checksum = "83240549659d187488f91f33c0f8547cbfef0b2088bc470c116d1d260ef623d9"
dependencies = [
"cfg-if 1.0.0",
"serde",
@@ -3285,9 +3158,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
-version = "0.2.72"
+version = "0.2.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "046ceba58ff062da072c7cb4ba5b22a37f00a302483f7e2a6cdc18fedbdc1fd3"
+checksum = "ae70622411ca953215ca6d06d3ebeb1e915f0f6613e3b495122878d7ebec7dae"
dependencies = [
"bumpalo",
"lazy_static",
@@ -3300,9 +3173,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-futures"
-version = "0.4.22"
+version = "0.4.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "73157efb9af26fb564bb59a009afd1c7c334a44db171d280690d0c3faaec3468"
+checksum = "81b8b767af23de6ac18bf2168b690bed2902743ddf0fb39252e36f9e2bfc63ea"
dependencies = [
"cfg-if 1.0.0",
"js-sys",
@@ -3312,9 +3185,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
-version = "0.2.72"
+version = "0.2.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ef9aa01d36cda046f797c57959ff5f3c615c9cc63997a8d545831ec7976819b"
+checksum = "3e734d91443f177bfdb41969de821e15c516931c3c3db3d318fa1b68975d0f6f"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -3322,9 +3195,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
-version = "0.2.72"
+version = "0.2.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96eb45c1b2ee33545a813a92dbb53856418bf7eb54ab34f7f7ff1448a5b3735d"
+checksum = "d53739ff08c8a68b0fdbcd54c372b8ab800b1449ab3c9d706503bc7dd1621b2c"
dependencies = [
"proc-macro2",
"quote",
@@ -3335,15 +3208,15 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
-version = "0.2.72"
+version = "0.2.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b7148f4696fb4960a346eaa60bbfb42a1ac4ebba21f750f75fc1375b098d5ffa"
+checksum = "d9a543ae66aa233d14bb765ed9af4a33e81b8b58d1584cf1b47ff8cd0b9e4489"
[[package]]
name = "web-sys"
-version = "0.3.49"
+version = "0.3.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59fe19d70f5dacc03f6e46777213facae5ac3801575d56ca6cbd4c93dcd12310"
+checksum = "a905d57e488fec8861446d3393670fb50d27a262344013181c2cdf9fff5481be"
dependencies = [
"js-sys",
"wasm-bindgen",
@@ -3361,9 +3234,9 @@ dependencies = [
[[package]]
name = "webpki-roots"
-version = "0.21.0"
+version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "82015b7e0b8bad8185994674a13a93306bea76cf5a16c5a181382fd3a5ec2376"
+checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940"
dependencies = [
"webpki",
]
@@ -3463,7 +3336,7 @@ dependencies = [
"log",
"mac",
"markup5ever",
- "time 0.1.44",
+ "time 0.1.43",
]
[[package]]
@@ -3474,14 +3347,14 @@ checksum = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71"
[[package]]
name = "zip"
-version = "0.5.11"
+version = "0.5.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8264fcea9b7a036a4a5103d7153e988dbc2ebbafb34f68a3c2d404b6b82d74b6"
+checksum = "9c83dc9b784d252127720168abd71ea82bf8c3d96b17dc565b5e2a02854f2b27"
dependencies = [
"byteorder",
"bzip2",
"crc32fast",
"flate2",
"thiserror",
- "time 0.1.44",
+ "time 0.1.43",
]
diff --git a/Cargo.toml b/Cargo.toml
index d02617e..cc264ca 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "miniserve"
-version = "0.12.2-alpha.0"
+version = "0.13.1-alpha.0"
description = "For when you really just want to serve some files over HTTP right now!"
authors = ["Sven-Hendrik Haase <svenstaro@gmail.com>", "Boastful Squirrel <boastful.squirrel@gmail.com>"]
repository = "https://github.com/svenstaro/miniserve"
@@ -17,37 +17,38 @@ codegen-units = 1
panic = 'abort'
[dependencies]
-yansi = "0.5"
actix-web = "3"
-simplelog = "0.9"
+actix-files = "0.5"
+actix-multipart = "0.3"
+actix-web-httpauth = "0.5"
+maud = { version = "0.22", features = ["actix-web"] }
+yansi = "0.5"
+simplelog = "0.10"
percent-encoding = "2"
port_check = "0.1"
-bytesize = "1.0.0"
-nanoid = "0.3"
+bytesize = "1"
+nanoid = "0.4"
alphanumeric-sort = "1"
structopt = "0.3"
-chrono = "0.4.19"
-chrono-humanize = "0.1.2"
-maud = { version = "0.22.2", features = ["actix-web"] }
+chrono = "0.4"
+chrono-humanize = "0.1"
serde = { version = "1", features = ["derive"] }
-tar = "0.4.33"
-futures = "0.3.13"
+tar = "0.4"
+futures = "0.3"
libflate = "1"
thiserror = "1"
-log = "0.4.14"
-strum = "0.20.0"
-strum_macros = "0.20.1"
+log = "0.4"
+strum = "0.20"
+strum_macros = "0.20"
sha2 = "0.9"
-hex = "0.4.3"
+hex = "0.4"
zip = "0.5.11"
qrcodegen = "1"
-actix-files = "0.5"
-actix-multipart = "0.3.0"
-actix-web-httpauth = "0.5.1"
mime = "0.3"
httparse = "1"
-http = "0.2.3"
+http = "0.2"
bytes = "1"
+atty = "0.2"
[dev-dependencies]
assert_cmd = "1"
@@ -60,4 +61,4 @@ pretty_assertions = "0.7"
url = "2"
[build-dependencies]
-grass = "0.10.4"
+grass = "0.10"
diff --git a/README.md b/README.md
index 9e1eb7e..6aa1782 100644
--- a/README.md
+++ b/README.md
@@ -67,12 +67,14 @@ 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 on the fly as `.tar.gz` or `.zip`)
- File uploading
-- Pretty themes
+- Pretty themes (with light and dark theme support)
- Scan QR code for quick access
+- Shell completions
+- Sane and secure defaults
## Usage
- miniserve 0.12.1
+ miniserve 0.13.0
Sven-Hendrik Haase <svenstaro@gmail.com>, Boastful Squirrel <boastful.squirrel@gmail.com>
For when you really just want to serve some files over HTTP right now!
@@ -84,7 +86,10 @@ Sometimes this is just a more practical and quick way than doing things properly
List directories first
-r, --enable-tar
- Enable tar archive generation
+ Enable uncompressed tar archive generation
+
+ -g, --enable-tar-gz
+ Enable gz-compressed tar archive generation
-z, --enable-zip
Enable zip archive generation
@@ -140,6 +145,9 @@ Sometimes this is just a more practical and quick way than doing things properly
-p, --port <port>
Port to use [default: 8080]
+ --print-completions <shell>
+ Generate completion file for a shell [possible values: zsh, bash, fish,
+ powershell, elvish]
-t, --title <title>
Shown instead of host in page title and heading
@@ -184,6 +192,19 @@ Alternatively install with [Homebrew](https://brew.sh/).
docker run -v /tmp:/tmp -p 8080:8080 --rm -it svenstaro/miniserve /tmp
+## Shell completions
+
+If you'd like to make use of the built-in shell completion support, you need to run `miniserve
+--print-completions <your-shell>` and put the completions in the correct place for your shell. A
+few examples with common paths are provided below:
+
+ # For bash
+ miniserve --print-completions bash > ~/.local/share/bash-completion/miniserve
+ # For zsh
+ miniserve --print-completions zsh > /usr/local/share/zsh/site-functions/_miniserve
+ # For fish
+ miniserve --print-completions fish > ~/.config/fish/completions/miniserve.fish
+
## Binding behavior
For convenience reasons, miniserve will try to bind on all interfaces by default (if no `-i` is provided).
diff --git a/data/style.scss b/data/style.scss
index e94436a..0b87265 100644
--- a/data/style.scss
+++ b/data/style.scss
@@ -39,7 +39,12 @@ body {
}
.title {
- word-break: break-word;
+ word-break: break-all;
+}
+
+.title a {
+ font-weight: bold;
+ color: var(--directory_link_color);
}
.footer {
diff --git a/src/archive.rs b/src/archive.rs
index 894ee3f..e53aea8 100644
--- a/src/archive.rs
+++ b/src/archive.rs
@@ -53,9 +53,9 @@ impl CompressionMethod {
}
}
- pub fn is_enabled(self, tar_enabled: bool, zip_enabled: bool) -> bool {
+ pub fn is_enabled(self, tar_enabled: bool, tar_gz_enabled: bool, zip_enabled: bool) -> bool {
match self {
- CompressionMethod::TarGz => tar_enabled,
+ CompressionMethod::TarGz => tar_gz_enabled,
CompressionMethod::Tar => tar_enabled,
CompressionMethod::Zip => zip_enabled,
}
@@ -220,15 +220,18 @@ where
let mut buffer = Vec::new();
while !paths_queue.is_empty() {
let next = paths_queue.pop().ok_or_else(|| {
- ContextualError::CustomError("Could not get path from queue".to_string())
+ ContextualError::ArchiveCreationDetailError("Could not get path from queue".to_string())
})?;
let current_dir = next.as_path();
let directory_entry_iterator = std::fs::read_dir(current_dir)
.map_err(|e| ContextualError::IoError("Could not read directory".to_string(), e))?;
- let zip_directory =
- Path::new(zip_root_folder_name).join(current_dir.strip_prefix(directory).map_err(
- |_| ContextualError::CustomError("Could not append base directory".to_string()),
- )?);
+ let zip_directory = Path::new(zip_root_folder_name).join(
+ current_dir.strip_prefix(directory).map_err(|_| {
+ ContextualError::ArchiveCreationDetailError(
+ "Could not append base directory".to_string(),
+ )
+ })?,
+ );
for entry in directory_entry_iterator {
let entry_path = entry
@@ -259,10 +262,14 @@ where
zip_writer
.start_file(relative_path.to_string_lossy(), options)
.map_err(|_| {
- ContextualError::CustomError("Could not add file path to ZIP".to_string())
+ ContextualError::ArchiveCreationDetailError(
+ "Could not add file path to ZIP".to_string(),
+ )
})?;
zip_writer.write(buffer.as_ref()).map_err(|_| {
- ContextualError::CustomError("Could not write file to ZIP".to_string())
+ ContextualError::ArchiveCreationDetailError(
+ "Could not write file to ZIP".to_string(),
+ )
})?;
buffer.clear();
} else if entry_metadata.is_dir() {
@@ -270,7 +277,7 @@ where
zip_writer
.add_directory(relative_path.to_string_lossy(), options)
.map_err(|_| {
- ContextualError::CustomError(
+ ContextualError::ArchiveCreationDetailError(
"Could not add directory path to ZIP".to_string(),
)
})?;
@@ -280,7 +287,9 @@ where
}
zip_writer.finish().map_err(|_| {
- ContextualError::CustomError("Could not finish writing ZIP archive".to_string())
+ ContextualError::ArchiveCreationDetailError(
+ "Could not finish writing ZIP archive".to_string(),
+ )
})?;
Ok(())
}
diff --git a/src/args.rs b/src/args.rs
index 909a88f..819618f 100644
--- a/src/args.rs
+++ b/src/args.rs
@@ -1,7 +1,6 @@
use bytes::Bytes;
use http::header::{HeaderMap, HeaderName, HeaderValue};
-use port_check::free_local_port;
-use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
+use std::net::IpAddr;
use std::path::PathBuf;
use structopt::StructOpt;
@@ -9,11 +8,6 @@ use crate::auth;
use crate::errors::ContextualError;
use crate::renderer;
-/// Possible characters for random routes
-const ROUTE_ALPHABET: [char; 16] = [
- '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'a', 'b', 'c', 'd', 'e', 'f',
-];
-
#[derive(StructOpt)]
#[structopt(
name = "miniserve",
@@ -21,25 +15,25 @@ const ROUTE_ALPHABET: [char; 16] = [
about,
global_settings = &[structopt::clap::AppSettings::ColoredHelp],
)]
-struct CliArgs {
+pub struct CliArgs {
/// Be verbose, includes emitting access logs
#[structopt(short = "v", long = "verbose")]
- verbose: bool,
+ pub verbose: bool,
/// Which path to serve
#[structopt(name = "PATH", parse(from_os_str))]
- path: Option<PathBuf>,
+ pub path: Option<PathBuf>,
/// The name of a directory index file to serve, like "index.html"
///
/// Normally, when miniserve serves a directory, it creates a listing for that directory.
/// However, if a directory contains this file, miniserve will serve that file instead.
#[structopt(long, parse(from_os_str), name = "index_file")]
- index: Option<PathBuf>,
+ pub index: Option<PathBuf>,
/// Port to use
#[structopt(short = "p", long = "port", default_value = "8080")]
- port: u16,
+ pub port: u16,
/// Interface to listen on
#[structopt(
@@ -48,7 +42,7 @@ struct CliArgs {
parse(try_from_str = parse_interface),
number_of_values = 1,
)]
- interfaces: Vec<IpAddr>,
+ pub interfaces: Vec<IpAddr>,
/// Set authentication. Currently supported formats:
/// username:password, username:sha256:hash, username:sha512:hash
@@ -59,19 +53,19 @@ struct CliArgs {
parse(try_from_str = parse_auth),
number_of_values = 1,
)]
- auth: Vec<auth::RequiredAuth>,
+ pub auth: Vec<auth::RequiredAuth>,
/// Generate a random 6-hexdigit route
#[structopt(long = "random-route")]
- random_route: bool,
+ pub random_route: bool,
/// Do not follow symbolic links
#[structopt(short = "P", long = "no-symlinks")]
- no_symlinks: bool,
+ pub no_symlinks: bool,
/// Show hidden files
#[structopt(short = "H", long = "hidden")]
- hidden: bool,
+ pub hidden: bool,
/// Default color scheme
#[structopt(
@@ -81,7 +75,7 @@ struct CliArgs {
possible_values = &renderer::THEME_SLUGS,
case_insensitive = true,
)]
- color_scheme: String,
+ pub color_scheme: String,
/// Default color scheme
#[structopt(
@@ -91,46 +85,54 @@ struct CliArgs {
possible_values = &renderer::THEME_SLUGS,
case_insensitive = true,
)]
- color_scheme_dark: String,
+ pub color_scheme_dark: String,
/// Enable QR code display
#[structopt(short = "q", long = "qrcode")]
- qrcode: bool,
+ pub qrcode: bool,
/// Enable file uploading
#[structopt(short = "u", long = "upload-files")]
- file_upload: bool,
+ pub file_upload: bool,
/// Enable overriding existing files during file upload
#[structopt(short = "o", long = "overwrite-files")]
- overwrite_files: bool,
+ pub overwrite_files: bool,
- /// Enable tar archive generation
+ /// Enable uncompressed tar archive generation
#[structopt(short = "r", long = "enable-tar")]
- enable_tar: bool,
+ pub enable_tar: bool,
+
+ /// Enable gz-compressed tar archive generation
+ #[structopt(short = "g", long = "enable-tar-gz")]
+ pub enable_tar_gz: bool,
/// Enable zip archive generation
///
/// WARNING: Zipping large directories can result in out-of-memory exception
/// because zip generation is done in memory and cannot be sent on the fly
#[structopt(short = "z", long = "enable-zip")]
- enable_zip: bool,
+ pub enable_zip: bool,
/// List directories first
#[structopt(short = "D", long = "dirs-first")]
- dirs_first: bool,
+ pub dirs_first: bool,
/// Shown instead of host in page title and heading
#[structopt(short = "t", long = "title")]
- title: Option<String>,
+ pub title: Option<String>,
/// Set custom header for responses
#[structopt(long = "header", parse(try_from_str = parse_header), number_of_values = 1)]
- header: Vec<HeaderMap>,
+ pub header: Vec<HeaderMap>,
/// Hide version footer
#[structopt(short = "F", long = "hide-version-footer")]
- hide_version_footer: bool,
+ pub hide_version_footer: bool,
+
+ /// Generate completion file for a shell
+ #[structopt(long = "print-completions", value_name = "shell", possible_values = &structopt::clap::Shell::variants())]
+ pub print_completions: Option<structopt::clap::Shell>,
}
/// Checks wether an interface is valid, i.e. it can be parsed into an IP address
@@ -204,67 +206,6 @@ pub fn parse_header(src: &str) -> Result<HeaderMap, httparse::Error> {
Ok(header_map)
}
-/// Parses the command line arguments
-pub fn parse_args() -> crate::MiniserveConfig {
- let args = CliArgs::from_args();
-
- let interfaces = if !args.interfaces.is_empty() {
- args.interfaces
- } else {
- vec![
- IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)),
- IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
- ]
- };
-
- let random_route = if args.random_route {
- Some(nanoid::nanoid!(6, &ROUTE_ALPHABET))
- } else {
- None
- };
-
- // Generate some random routes for the favicon and css so that they are very unlikely to conflict with
- // real files.
- let favicon_route = nanoid::nanoid!(10, &ROUTE_ALPHABET);
- let css_route = nanoid::nanoid!(10, &ROUTE_ALPHABET);
-
- let default_color_scheme = args.color_scheme;
- let default_color_scheme_dark = args.color_scheme_dark;
-
- let path_explicitly_chosen = args.path.is_some() || args.index.is_some();
-
- let port = match args.port {
- 0 => free_local_port().expect("no free ports available"),
- _ => args.port,
- };
-
- crate::MiniserveConfig {
- verbose: args.verbose,
- path: args.path.unwrap_or_else(|| PathBuf::from(".")),
- port,
- interfaces,
- auth: args.auth,
- path_explicitly_chosen,
- no_symlinks: args.no_symlinks,
- show_hidden: args.hidden,
- random_route,
- favicon_route,
- css_route,
- default_color_scheme,
- default_color_scheme_dark,
- index: args.index,
- overwrite_files: args.overwrite_files,
- show_qrcode: args.qrcode,
- file_upload: args.file_upload,
- tar_enabled: args.enable_tar,
- zip_enabled: args.enable_zip,
- dirs_first: args.dirs_first,
- title: args.title,
- header: args.header,
- hide_version_footer: args.hide_version_footer,
- }
-}
-
#[rustfmt::skip]
#[cfg(test)]
mod tests {
diff --git a/src/errors.rs b/src/errors.rs
index 3287fc3..f079657 100644
--- a/src/errors.rs
+++ b/src/errors.rs
@@ -2,65 +2,78 @@ use thiserror::Error;
#[derive(Debug, Error)]
pub enum ContextualError {
- /// Fully customized errors, not inheriting from any error
- #[error("{0}")]
- CustomError(String),
-
/// Any kind of IO errors
#[error("{0}\ncaused by: {1}")]
IoError(String, std::io::Error),
- /// MultipartError, which might occur during file upload, when processing the multipart request fails
+ /// Might occur during file upload, when processing the multipart request fails
#[error("Failed to process multipart request\ncaused by: {0}")]
MultipartError(actix_multipart::MultipartError),
+ /// Might occur during file upload
+ #[error("File already exists, and the overwrite_files option has not been set")]
+ DuplicateFileError,
+
/// Any error related to an invalid path (failed to retrieve entry name, unexpected entry type, etc)
#[error("Invalid path\ncaused by: {0}")]
InvalidPathError(String),
- /// This error might occur if the HTTP credential string does not respect the expected format
+ /// Might occur if the HTTP credential string does not respect the expected format
#[error("Invalid format for credentials string. Expected username:password, username:sha256:hash or username:sha512:hash")]
InvalidAuthFormat,
- /// This error might occure if the hash method is neither sha256 nor sha512
+ /// Might occure if the hash method is neither sha256 nor sha512
#[error("{0} is not a valid hashing method. Expected sha256 or sha512")]
InvalidHashMethod(String),
- /// This error might occur if the HTTP auth hash password is not a valid hex code
+ /// Might occur if the HTTP auth hash password is not a valid hex code
#[error("Invalid format for password hash. Expected hex code")]
InvalidPasswordHash,
- /// This error might occur if the HTTP auth password exceeds 255 characters
+ /// Might occur if the HTTP auth password exceeds 255 characters
#[error("HTTP password length exceeds 255 characters")]
PasswordTooLongError,
- /// This error might occur if the user has unsufficient permissions to create an entry in a given directory
+ /// Might occur if the user has unsufficient permissions to create an entry in a given directory
#[error("Insufficient permissions to create file in {0}")]
InsufficientPermissionsError(String),
- /// Any error related to parsing.
+ /// Any error related to parsing
#[error("Failed to parse {0}\ncaused by: {1}")]
ParseError(String, String),
- /// This error might occur when the creation of an archive fails
+ /// Might occur when the creation of an archive fails
#[error("An error occured while creating the {0}\ncaused by: {1}")]
ArchiveCreationError(String, Box<ContextualError>),
- /// This error might occur when the HTTP authentication fails
+ /// More specific archive creation failure reason
+ #[error("{0}")]
+ ArchiveCreationDetailError(String),
+
+ /// Might occur when the HTTP authentication fails
#[error("An error occured during HTTP authentication\ncaused by: {0}")]
HttpAuthenticationError(Box<ContextualError>),
- /// This error might occur when the HTTP credentials are not correct
+ /// Might occur when the HTTP credentials are not correct
#[error("Invalid credentials for HTTP authentication")]
InvalidHttpCredentials,
- /// This error might occur when an HTTP request is invalid
+ /// Might occur when an HTTP request is invalid
#[error("Invalid HTTP request\ncaused by: {0}")]
InvalidHttpRequestError(String),
- /// This error might occur when trying to access a page that does not exist
+ /// Might occur when trying to access a page that does not exist
#[error("Route {0} could not be found")]
RouteNotFoundError(String),
+
+ /// In case miniserve was invoked without an interactive terminal and without an explicit path
+ #[error("Refusing to start as no explicit serve path was set and no interactive terminal was attached
+Please set an explicit serve path like: `miniserve /my/path`")]
+ NoExplicitPathAndNoTerminal,
+
+ /// In case miniserve was invoked with --no-symlinks but the serve path is a symlink
+ #[error("The -P|--no-symlinks option was provided but the serve path '{0}' is a symlink")]
+ NoSymlinksOptionWithSymlinkServePath(String),
}
pub fn log_error_chain(description: String) {
@@ -68,10 +81,3 @@ pub fn log_error_chain(description: String) {
log::error!("{}", cause);
}
}
-
-/// This makes creating CustomErrors easier
-impl From<String> for ContextualError {
- fn from(msg: String) -> ContextualError {
- ContextualError::CustomError(msg)
- }
-}
diff --git a/src/file_upload.rs b/src/file_upload.rs
index 785d72f..93b7109 100644
--- a/src/file_upload.rs
+++ b/src/file_upload.rs
@@ -20,9 +20,7 @@ fn save_file(
overwrite_files: bool,
) -> Pin<Box<dyn Future<Output = Result<i64, ContextualError>>>> {
if !overwrite_files && file_path.exists() {
- return Box::pin(future::err(ContextualError::CustomError(
- "File already exists, and the overwrite_files option has not been set".to_string(),
- )));
+ return Box::pin(future::err(ContextualError::DuplicateFileError));
}
let mut file = match std::fs::File::create(&file_path) {
diff --git a/src/listing.rs b/src/listing.rs
index 9d74906..79a4172 100644
--- a/src/listing.rs
+++ b/src/listing.rs
@@ -4,7 +4,7 @@ use actix_web::http::StatusCode;
use actix_web::web::Query;
use actix_web::{HttpRequest, HttpResponse, Result};
use bytesize::ByteSize;
-use percent_encoding::{percent_decode_str, utf8_percent_encode, AsciiSet, CONTROLS};
+use percent_encoding::{percent_decode_str, utf8_percent_encode};
use qrcodegen::{QrCode, QrCodeEcc};
use serde::Deserialize;
use std::io;
@@ -15,8 +15,17 @@ use strum_macros::{Display, EnumString};
use crate::archive::CompressionMethod;
use crate::errors::{self, ContextualError};
use crate::renderer;
-
-const FRAGMENT: &AsciiSet = &CONTROLS.add(b' ').add(b'"').add(b'<').add(b'>').add(b'`');
+use percent_encode_sets::PATH_SEGMENT;
+
+/// "percent-encode sets" as defined by WHATWG specs:
+/// https://url.spec.whatwg.org/#percent-encoded-bytes
+mod percent_encode_sets {
+ use percent_encoding::{AsciiSet, CONTROLS};
+ const BASE: &AsciiSet = &CONTROLS.add(b'%');
+ pub const QUERY: &AsciiSet = &BASE.add(b' ').add(b'"').add(b'#').add(b'<').add(b'>');
+ pub const PATH: &AsciiSet = &QUERY.add(b'?').add(b'`').add(b'{').add(b'}');
+ pub const PATH_SEGMENT: &AsciiSet = &PATH.add(b'/');
+}
/// Query parameters
#[derive(Deserialize)]
@@ -155,6 +164,7 @@ pub fn directory_listing(
show_qrcode: bool,
upload_route: String,
tar_enabled: bool,
+ tar_gz_enabled: bool,
zip_enabled: bool,
dirs_first: bool,
hide_version_footer: bool,
@@ -210,7 +220,7 @@ pub fn directory_listing(
Component::Normal(s) => {
name = s.to_string_lossy().to_string();
link_accumulator
- .push_str(&(utf8_percent_encode(&name, FRAGMENT).to_string() + "/"));
+ .push_str(&(utf8_percent_encode(&name, PATH_SEGMENT).to_string() + "/"));
}
_ => name = "".to_string(),
};
@@ -248,12 +258,7 @@ pub fn directory_listing(
for entry in dir.path.read_dir()? {
if dir.is_visible(&entry) || show_hidden {
let entry = entry?;
- let p = match entry.path().strip_prefix(&dir.path) {
- Ok(p) => base.join(p),
- Err(_) => continue,
- };
// show file url as relative to static path
- let file_url = utf8_percent_encode(&p.to_string_lossy(), FRAGMENT).to_string();
let file_name = entry.file_name().to_string_lossy().to_string();
let (is_symlink, metadata) = match entry.metadata() {
Ok(metadata) if metadata.file_type().is_symlink() => {
@@ -262,6 +267,10 @@ pub fn directory_listing(
}
res => (false, res),
};
+ let file_url = base
+ .join(&utf8_percent_encode(&file_name, PATH_SEGMENT).to_string())
+ .to_string_lossy()
+ .to_string();
// if file is a directory, add '/' to the end of the name
if let Ok(metadata) = metadata {
@@ -328,7 +337,7 @@ pub fn directory_listing(
}
if let Some(compression_method) = query_params.download {
- if !compression_method.is_enabled(tar_enabled, zip_enabled) {
+ if !compression_method.is_enabled(tar_enabled, tar_gz_enabled, zip_enabled) {
return Ok(ServiceResponse::new(
req.clone(),
HttpResponse::Forbidden()
@@ -411,6 +420,7 @@ pub fn directory_listing(
&encoded_dir,
breadcrumbs,
tar_enabled,
+ tar_gz_enabled,
zip_enabled,
hide_version_footer,
)
diff --git a/src/main.rs b/src/main.rs
index 17ab204..f174d57 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,3 +1,9 @@
+use std::io;
+use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
+use std::thread;
+use std::time::Duration;
+use std::{io::Write, path::PathBuf};
+
use actix_web::web;
use actix_web::{
http::{header::ContentType, StatusCode},
@@ -6,11 +12,9 @@ use actix_web::{
use actix_web::{middleware, App, HttpRequest, HttpResponse};
use actix_web_httpauth::middleware::HttpAuthentication;
use http::header::HeaderMap;
-use std::io::{self, Write};
-use std::net::{IpAddr, Ipv4Addr, SocketAddr};
-use std::thread;
-use std::time::Duration;
+use log::{error, warn};
use structopt::clap::crate_version;
+use structopt::StructOpt;
use yansi::{Color, Paint};
mod archive;
@@ -24,6 +28,11 @@ mod renderer;
use crate::errors::ContextualError;
+/// Possible characters for random routes
+const ROUTE_ALPHABET: [char; 16] = [
+ '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'a', 'b', 'c', 'd', 'e', 'f',
+];
+
#[derive(Clone)]
/// Configuration of the Miniserve application
pub struct MiniserveConfig {
@@ -81,9 +90,12 @@ pub struct MiniserveConfig {
/// Enable upload to override existing files
pub overwrite_files: bool,
- /// If false, creation of tar archives is disabled
+ /// If false, creation of uncompressed tar archives is disabled
pub tar_enabled: bool,
+ /// If false, creation of gz-compressed tar archives is disabled
+ pub tar_gz_enabled: bool,
+
/// If false, creation of zip archives is disabled
pub zip_enabled: bool,
@@ -100,31 +112,101 @@ pub struct MiniserveConfig {
pub hide_version_footer: bool,
}
+impl MiniserveConfig {
+ /// Parses the command line arguments
+ fn from_args(args: args::CliArgs) -> Self {
+ let interfaces = if !args.interfaces.is_empty() {
+ args.interfaces
+ } else {
+ vec![
+ IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)),
+ IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
+ ]
+ };
+
+ let random_route = if args.random_route {
+ Some(nanoid::nanoid!(6, &ROUTE_ALPHABET))
+ } else {
+ None
+ };
+
+ // Generate some random routes for the favicon and css so that they are very unlikely to conflict with
+ // real files.
+ let favicon_route = nanoid::nanoid!(10, &ROUTE_ALPHABET);
+ let css_route = nanoid::nanoid!(10, &ROUTE_ALPHABET);
+
+ let default_color_scheme = args.color_scheme;
+ let default_color_scheme_dark = args.color_scheme_dark;
+
+ let path_explicitly_chosen = args.path.is_some() || args.index.is_some();
+
+ let port = match args.port {
+ 0 => port_check::free_local_port().expect("no free ports available"),
+ _ => args.port,
+ };
+
+ crate::MiniserveConfig {
+ verbose: args.verbose,
+ path: args.path.unwrap_or_else(|| PathBuf::from(".")),
+ port,
+ interfaces,
+ auth: args.auth,
+ path_explicitly_chosen,
+ no_symlinks: args.no_symlinks,
+ show_hidden: args.hidden,
+ random_route,
+ favicon_route,
+ css_route,
+ default_color_scheme,
+ default_color_scheme_dark,
+ index: args.index,
+ overwrite_files: args.overwrite_files,
+ show_qrcode: args.qrcode,
+ file_upload: args.file_upload,
+ tar_enabled: args.enable_tar,
+ tar_gz_enabled: args.enable_tar_gz,
+ zip_enabled: args.enable_zip,
+ dirs_first: args.dirs_first,
+ title: args.title,
+ header: args.header,
+ hide_version_footer: args.hide_version_footer,
+ }
+ }
+}
+
fn main() {
- match run() {
+ let args = args::CliArgs::from_args();
+
+ if let Some(shell) = args.print_completions {
+ args::CliArgs::clap().gen_completions_to("miniserve", shell, &mut std::io::stdout());
+ return;
+ }
+
+ let miniserve_config = MiniserveConfig::from_args(args);
+
+ match run(miniserve_config) {
Ok(()) => (),
Err(e) => errors::log_error_chain(e.to_string()),
}
}
#[actix_web::main(miniserve)]
-async fn run() -> Result<(), ContextualError> {
+async fn run(miniserve_config: MiniserveConfig) -> Result<(), ContextualError> {
if cfg!(windows) && !Paint::enable_windows_ascii() {
Paint::disable();
}
- let miniserve_config = args::parse_args();
-
let log_level = if miniserve_config.verbose {
simplelog::LevelFilter::Info
} else {
- simplelog::LevelFilter::Error
+ simplelog::LevelFilter::Warn
};
if simplelog::TermLogger::init(
log_level,
simplelog::Config::default(),
simplelog::TerminalMode::Mixed,
+ simplelog::ColorChoice::Auto,
)
.is_err()
{
@@ -143,8 +225,8 @@ async fn run() -> Result<(), ContextualError> {
.is_symlink();
if is_symlink {
- return Err(ContextualError::from(
- "The no-symlinks option cannot be used with a symlink path".to_string(),
+ return Err(ContextualError::NoSymlinksOptionWithSymlinkServePath(
+ miniserve_config.path.to_string_lossy().to_string(),
));
}
}
@@ -175,9 +257,9 @@ async fn run() -> Result<(), ContextualError> {
if let Some(index_path) = &miniserve_config.index {
let has_index: std::path::PathBuf = [&canon_path, index_path].iter().collect();
if !has_index.exists() {
- println!(
- "{warning} The provided index file could not be found.",
- warning = Color::RGB(255, 192, 0).paint("Notice:").bold()
+ error!(
+ "The file '{}' provided for option --index could not be found.",
+ index_path.to_string_lossy()
);
}
}
@@ -189,9 +271,17 @@ async fn run() -> Result<(), ContextualError> {
version = crate_version!()
);
if !miniserve_config.path_explicitly_chosen {
- 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."
+ // If the path to serve has NOT been explicitly chosen and if this is NOT an interactive
+ // terminal, we should refuse to start for security reasons. This would be the case when
+ // running miniserve as a service but forgetting to set the path. This could be pretty
+ // dangerous if given with an undesired context path (for instance /root or /).
+ if !atty::is(atty::Stream::Stdout) {
+ return Err(ContextualError::NoExplicitPathAndNoTerminal);
+ }
+
+ warn!("miniserve has been invoked without an explicit path so it will serve the current directory after a short delay.");
+ warn!(
+ "Invoke with -h|--help to see options or invoke as `miniserve .` to hide this advice."
);
print!("Starting server in ");
io::stdout()
@@ -284,7 +374,9 @@ async fn run() -> Result<(), ContextualError> {
addresses = addresses,
);
- println!("\nQuit by pressing CTRL-C");
+ if atty::is(atty::Stream::Stdout) {
+ println!("\nQuit by pressing CTRL-C");
+ }
srv.await
.map_err(|e| ContextualError::IoError("".to_owned(), e))
@@ -323,6 +415,7 @@ fn configure_app(app: &mut web::ServiceConfig, conf: &MiniserveConfig) {
let show_qrcode = conf.show_qrcode;
let file_upload = conf.file_upload;
let tar_enabled = conf.tar_enabled;
+ let tar_gz_enabled = conf.tar_gz_enabled;
let zip_enabled = conf.zip_enabled;
let dirs_first = conf.dirs_first;
let hide_version_footer = conf.hide_version_footer;
@@ -365,6 +458,7 @@ fn configure_app(app: &mut web::ServiceConfig, conf: &MiniserveConfig) {
show_qrcode,
u_r.clone(),
tar_enabled,
+ tar_gz_enabled,
zip_enabled,
dirs_first,
hide_version_footer,
diff --git a/src/renderer.rs b/src/renderer.rs
index c51f364..1f57164 100644
--- a/src/renderer.rs
+++ b/src/renderer.rs
@@ -26,6 +26,7 @@ pub fn page(
encoded_dir: &str,
breadcrumbs: Vec<Breadcrumb>,
tar_enabled: bool,
+ tar_gz_enabled: bool,
zip_enabled: bool,
hide_version_footer: bool,
) -> Markup {
@@ -80,24 +81,24 @@ pub fn page(
(color_scheme_selector(show_qrcode))
div.container {
span#top { }
- h1.title {
+ h1.title dir="ltr" {
@for el in breadcrumbs {
@if el.link == "." {
// wrapped in span so the text doesn't shift slightly when it turns into a link
- span { (el.name) }
+ span { bdi { (el.name) } }
} @else {
- a.directory href=(parametrized_link(&el.link, sort_method, sort_order)) {
- (el.name)
+ a href=(parametrized_link(&el.link, sort_method, sort_order)) {
+ bdi { (el.name) }
}
}
"/"
}
}
div.toolbar {
- @if tar_enabled || zip_enabled {
+ @if tar_enabled || tar_gz_enabled || zip_enabled {
div.download {
@for compression_method in CompressionMethod::iter() {
- @if compression_method.is_enabled(tar_enabled, zip_enabled) {
+ @if compression_method.is_enabled(tar_enabled, tar_gz_enabled, zip_enabled) {
(archive_button(compression_method, sort_method, sort_order))
}
}
diff --git a/tests/archive.rs b/tests/archive.rs
index c170bc3..6a7f8bf 100644
--- a/tests/archive.rs
+++ b/tests/archive.rs
@@ -51,3 +51,44 @@ fn archives_are_disabled(tmpdir: TempDir, port: u16) -> Result<(), Error> {
Ok(())
}
+
+#[rstest]
+fn test_tar_archives(tmpdir: TempDir, port: u16) -> Result<(), Error> {
+ let mut child = Command::cargo_bin("miniserve")?
+ .arg(tmpdir.path())
+ .arg("-p")
+ .arg(port.to_string())
+ .arg("-g")
+ .stdout(Stdio::null())
+ .spawn()?;
+
+ sleep(Duration::from_secs(1));
+
+ // Ensure the links to the tar archive exists and tar not exists
+ let body = reqwest::blocking::get(format!("http://localhost:{}", port).as_str())?
+ .error_for_status()?;
+ let parsed = Document::from_read(body)?;
+ assert!(parsed.find(Text).any(|x| x.text() == "Download .tar.gz"));
+ assert!(parsed.find(Text).all(|x| x.text() != "Download .tar"));
+
+ // Try to download, only tar_gz should works
+ assert_eq!(
+ reqwest::blocking::get(format!("http://localhost:{}/?download=tar_gz", port).as_str())?
+ .status(),
+ StatusCode::OK
+ );
+ assert_eq!(
+ reqwest::blocking::get(format!("http://localhost:{}/?download=tar", port).as_str())?
+ .status(),
+ StatusCode::FORBIDDEN
+ );
+ assert_eq!(
+ reqwest::blocking::get(format!("http://localhost:{}/?download=zip", port).as_str())?
+ .status(),
+ StatusCode::FORBIDDEN
+ );
+
+ child.kill()?;
+
+ Ok(())
+}
diff --git a/tests/cli.rs b/tests/cli.rs
index e09473d..f88b284 100644
--- a/tests/cli.rs
+++ b/tests/cli.rs
@@ -3,7 +3,7 @@ mod fixtures;
use assert_cmd::prelude::*;
use fixtures::Error;
use std::process::Command;
-use structopt::clap::{crate_name, crate_version};
+use structopt::clap::{crate_name, crate_version, Shell};
#[test]
/// Show help and exit.
@@ -27,3 +27,29 @@ fn version_shows() -> Result<(), Error> {
Ok(())
}
+
+#[test]
+/// Print completions and exit.
+fn print_completions() -> Result<(), Error> {
+ for shell in &Shell::variants() {
+ Command::cargo_bin("miniserve")?
+ .arg("--print-completions")
+ .arg(&shell)
+ .assert()
+ .success();
+ }
+
+ Ok(())
+}
+
+#[test]
+/// Print completions rejects invalid shells.
+fn print_completions_invalid_shell() -> Result<(), Error> {
+ Command::cargo_bin("miniserve")?
+ .arg("--print-completions")
+ .arg("fakeshell")
+ .assert()
+ .failure();
+
+ Ok(())
+}
diff --git a/tests/fixtures/mod.rs b/tests/fixtures/mod.rs
index 7efbe5e..1cf6c59 100644
--- a/tests/fixtures/mod.rs
+++ b/tests/fixtures/mod.rs
@@ -16,6 +16,9 @@ pub static FILES: &[&str] = &[
"test \" \' & < >.csv",
"😀.data",
"⎙.mp4",
+ "#[]{}()@!$&'`+,;= %20.test",
+ #[cfg(unix)]
+ ":?#[]{}<>()@!$&'`|*+,;= %20.test",
];
/// Hidden files for testing purpose
diff --git a/tests/serve_request.rs b/tests/serve_request.rs
index 1e92339..e259b9e 100644
--- a/tests/serve_request.rs
+++ b/tests/serve_request.rs
@@ -55,6 +55,12 @@ fn serves_requests_with_non_default_port(tmpdir: TempDir, port: u16) -> Result<(
for &file in FILES {
let f = parsed.find(|x: &Node| x.text() == file).next().unwrap();
+ reqwest::blocking::get(format!(
+ "http://localhost:{}/{}",
+ port,
+ f.attr("href").unwrap()
+ ))?
+ .error_for_status()?;
assert_eq!(
format!("/{}", file),
percent_encoding::percent_decode_str(f.attr("href").unwrap()).decode_utf8_lossy(),
@@ -259,17 +265,18 @@ fn serves_requests_custom_index_notice(tmpdir: TempDir, port: u16) -> Result<(),
.arg(port.to_string())
.arg(tmpdir.path())
.stdout(Stdio::piped())
+ .stderr(Stdio::piped())
.spawn()?;
sleep(Duration::from_secs(1));
child.kill()?;
let output = child.wait_with_output().expect("Failed to read stdout");
- let all_text = String::from_utf8(output.stdout);
+ let all_text = String::from_utf8(output.stderr);
assert!(all_text
.unwrap()
- .contains("The provided index file could not be found"));
+ .contains("The file 'not.html' provided for option --index could not be found."));
Ok(())
}