diff options
author | Ali MJ Al-Nasrawy <alimjalnasrawy@gmail.com> | 2021-04-18 03:47:23 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-04-18 03:47:23 +0000 |
commit | c368a113957d42666f080303783174e4adaf02b4 (patch) | |
tree | 96349d7f01be73cf6f80962bf34a9af543c2971c | |
parent | Don't use different syle for symlink symbol (diff) | |
parent | Update README for separate .tar and .tar.gz flags (diff) | |
download | miniserve-c368a113957d42666f080303783174e4adaf02b4.tar.gz miniserve-c368a113957d42666f080303783174e4adaf02b4.zip |
Merge branch 'master' into rfc-resolve-symlinks
-rw-r--r-- | CHANGELOG.md | 17 | ||||
-rw-r--r-- | Cargo.lock | 415 | ||||
-rw-r--r-- | Cargo.toml | 39 | ||||
-rw-r--r-- | README.md | 27 | ||||
-rw-r--r-- | data/style.scss | 7 | ||||
-rw-r--r-- | src/archive.rs | 31 | ||||
-rw-r--r-- | src/args.rs | 121 | ||||
-rw-r--r-- | src/errors.rs | 52 | ||||
-rw-r--r-- | src/file_upload.rs | 4 | ||||
-rw-r--r-- | src/listing.rs | 30 | ||||
-rw-r--r-- | src/main.rs | 132 | ||||
-rw-r--r-- | src/renderer.rs | 13 | ||||
-rw-r--r-- | tests/archive.rs | 41 | ||||
-rw-r--r-- | tests/cli.rs | 28 | ||||
-rw-r--r-- | tests/fixtures/mod.rs | 3 | ||||
-rw-r--r-- | tests/serve_request.rs | 11 |
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 @@ -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", ] @@ -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" @@ -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(()) } |