diff --git a/Cargo.lock b/Cargo.lock index da3b89d..cd2772a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,62 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "assert_cmd" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93ae1ddd39efd67689deb1979d80bad3bf7f2b09c6e6117c8d1f2443b5e2f83e" +dependencies = [ + "bstr", + "doc-comment", + "predicates", + "predicates-core", + "predicates-tree", + "wait-timeout", +] + +[[package]] +name = "assert_fs" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf09bb72e00da477c2596865e8873227e2196d263cca35414048875dbbeea1be" +dependencies = [ + "doc-comment", + "globwalk", + "predicates", + "predicates-core", + "predicates-tree", + "tempfile", +] + +[[package]] +name = "async-attributes" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" +dependencies = [ + "quote", + "syn", +] + [[package]] name = "async-channel" version = "1.6.1" @@ -36,6 +92,20 @@ dependencies = [ "zstd-safe", ] +[[package]] +name = "async-executor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "once_cell", + "slab", +] + [[package]] name = "async-fs" version = "1.5.0" @@ -47,6 +117,40 @@ dependencies = [ "futures-lite", ] +[[package]] +name = "async-global-executor" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd8b508d585e01084059b60f06ade4cb7415cd2e4084b71dd1cb44e7d3fb9880" +dependencies = [ + "async-channel", + "async-executor", + "async-io", + "async-lock", + "blocking", + "futures-lite", + "once_cell", +] + +[[package]] +name = "async-io" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5e18f61464ae81cde0a23e713ae8fd299580c54d697a35820cfd0625b8b0e07" +dependencies = [ + "concurrent-queue", + "futures-lite", + "libc", + "log", + "once_cell", + "parking", + "polling", + "slab", + "socket2", + "waker-fn", + "winapi 0.3.9", +] + [[package]] name = "async-lock" version = "2.5.0" @@ -56,12 +160,51 @@ dependencies = [ "event-listener", ] +[[package]] +name = "async-std" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52580991739c5cdb36cde8b2a516371c0a3b70dda36d916cc08b82372916808c" +dependencies = [ + "async-attributes", + "async-channel", + "async-global-executor", + "async-io", + "async-lock", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "num_cpus", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + [[package]] name = "async-task" version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30696a84d817107fc028e049980e09d5e140e8da8f1caeb17e8e950658a3cea9" +[[package]] +name = "async-trait" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "async-walkdir" version = "0.2.0" @@ -113,12 +256,36 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[package]] +name = "bit-set" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + [[package]] name = "block-buffer" version = "0.10.2" @@ -142,6 +309,17 @@ dependencies = [ "once_cell", ] +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata", +] + [[package]] name = "bumpalo" version = "3.10.0" @@ -246,6 +424,22 @@ dependencies = [ "cache-padded", ] +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + [[package]] name = "cpufeatures" version = "0.2.2" @@ -264,6 +458,16 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-utils" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" +dependencies = [ + "cfg-if", + "lazy_static", +] + [[package]] name = "crypto-common" version = "0.1.3" @@ -274,25 +478,89 @@ dependencies = [ "typenum", ] +[[package]] +name = "ctor" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "diff" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" + +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + [[package]] name = "digest" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" dependencies = [ - "block-buffer", + "block-buffer 0.10.2", "crypto-common", ] +[[package]] +name = "digest_auth" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa30657988b2ced88f68fe490889e739bf98d342916c33ed3100af1d6f1cbc9c" +dependencies = [ + "digest 0.9.0", + "hex", + "md-5", + "rand 0.8.5", + "sha2", +] + +[[package]] +name = "diqwest" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3cebabb6a61cb79191ab4cfbe1ebed9d2357f20bfd224855ec5d02bd1076c52" +dependencies = [ + "async-trait", + "digest_auth", + "reqwest", +] + +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + [[package]] name = "duf" version = "0.15.1" dependencies = [ + "assert_cmd", + "assert_fs", "async-walkdir", "async_zip", "base64", "chrono", "clap", + "diqwest", "env_logger", "futures", "get_if_addrs", @@ -303,19 +571,42 @@ dependencies = [ "md5", "mime_guess", "percent-encoding", + "port_check", + "predicates", + "pretty_assertions", + "regex", + "reqwest", + "rstest", "rustls", - "rustls-pemfile", + "rustls-pemfile 1.0.0", + "select", "serde", "serde_json", "tokio", "tokio-rustls", "tokio-stream", "tokio-util", + "url", "urlencoding", "uuid", "xml-rs", ] +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "encoding_rs" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +dependencies = [ + "cfg-if", +] + [[package]] name = "env_logger" version = "0.9.0" @@ -351,12 +642,56 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +dependencies = [ + "num-traits", +] + [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "futf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + [[package]] name = "futures" version = "0.3.21" @@ -443,6 +778,12 @@ version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" +[[package]] +name = "futures-timer" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" + [[package]] name = "futures-util" version = "0.3.21" @@ -499,6 +840,17 @@ dependencies = [ "libc", ] +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + [[package]] name = "getrandom" version = "0.2.6" @@ -510,6 +862,61 @@ dependencies = [ "wasi 0.10.0+wasi-snapshot-preview1", ] +[[package]] +name = "globset" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a" +dependencies = [ + "aho-corasick", + "bstr", + "fnv", + "log", + "regex", +] + +[[package]] +name = "globwalk" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" +dependencies = [ + "bitflags", + "ignore", + "walkdir", +] + +[[package]] +name = "gloo-timers" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fb7d06c1c8cc2a29bee7ec961009a0b2caa0793ee4900c2ffb348734ba1c8f9" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "h2" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.11.2" @@ -550,6 +957,26 @@ dependencies = [ "libc", ] +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "html5ever" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5c13fb08e5d4dfc151ee5e88bae63f7773d61852f3bdc73c9f4b9e1bde03148" +dependencies = [ + "log", + "mac", + "markup5ever", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "http" version = "0.2.8" @@ -600,6 +1027,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", + "h2", "http", "http-body", "httparse", @@ -613,6 +1041,61 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac" +dependencies = [ + "http", + "hyper", + "rustls", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "ignore" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d" +dependencies = [ + "crossbeam-utils", + "globset", + "lazy_static", + "log", + "memchr", + "regex", + "same-file", + "thread_local", + "walkdir", + "winapi-util", +] + [[package]] name = "indexmap" version = "1.8.2" @@ -632,6 +1115,21 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "ipnet" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" + +[[package]] +name = "itertools" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.2" @@ -656,6 +1154,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -668,6 +1175,16 @@ version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +[[package]] +name = "lock_api" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.17" @@ -675,6 +1192,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if", + "value-bag", ] [[package]] @@ -688,6 +1206,55 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" + +[[package]] +name = "markup5ever" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a24f40fb03852d1cdd84330cddcaf98e9ec08a7b7768e952fad3b4cf048ec8fd" +dependencies = [ + "log", + "phf", + "phf_codegen", + "string_cache", + "string_cache_codegen", + "tendril", +] + +[[package]] +name = "markup5ever_rcdom" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f015da43bcd8d4f144559a3423f4591d69b8ce0652c905374da7205df336ae2b" +dependencies = [ + "html5ever", + "markup5ever", + "tendril", + "xml5ever", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "md-5" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15" +dependencies = [ + "block-buffer 0.9.0", + "digest 0.9.0", + "opaque-debug", +] + [[package]] name = "md5" version = "0.7.0" @@ -737,6 +1304,36 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "native-tls" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" + +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + [[package]] name = "num-integer" version = "0.1.45" @@ -772,24 +1369,164 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "openssl" +version = "0.10.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb81a6430ac911acb25fe5ac8f1d2af1b4ea8a4fdfda0f1ee4292af2e2d8eb0e" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835363342df5fba8354c5b453325b110ffd54044e588c539cf2f20a8014e4cb1" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "os_str_bytes" version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa" +[[package]] +name = "output_vt100" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "parking" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + [[package]] name = "percent-encoding" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +[[package]] +name = "phf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" +dependencies = [ + "phf_shared 0.8.0", +] + +[[package]] +name = "phf_codegen" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", +] + +[[package]] +name = "phf_generator" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +dependencies = [ + "phf_shared 0.8.0", + "rand 0.7.3", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand 0.8.5", +] + +[[package]] +name = "phf_shared" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +dependencies = [ + "siphasher", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project-lite" version = "0.2.9" @@ -808,12 +1545,79 @@ version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" +[[package]] +name = "polling" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259" +dependencies = [ + "cfg-if", + "libc", + "log", + "wepoll-ffi", + "winapi 0.3.9", +] + +[[package]] +name = "port_check" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6519412c9e0d4be579b9f0618364d19cb434b324fc6ddb1b27b1e682c7105ed" + [[package]] name = "ppv-lite86" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "predicates" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5aab5be6e4732b473071984b3164dbbfb7a3674d30ea5ff44410b6bcd960c3c" +dependencies = [ + "difflib", + "float-cmp", + "itertools", + "normalize-line-endings", + "predicates-core", + "regex", +] + +[[package]] +name = "predicates-core" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da1c2388b1513e1b605fcec39a95e0a9e8ef088f71443ef37099fa9ae6673fcb" + +[[package]] +name = "predicates-tree" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d86de6de25020a36c6d3643a86d9a6a9f552107c0559c60ea03551b5e16c032" +dependencies = [ + "predicates-core", + "termtree", +] + +[[package]] +name = "pretty_assertions" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89f989ac94207d048d92db058e4f6ec7342b0971fc58d1271ca148b799b3563" +dependencies = [ + "ansi_term", + "ctor", + "diff", + "output_vt100", +] + [[package]] name = "proc-macro2" version = "1.0.39" @@ -832,6 +1636,20 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", + "rand_pcg", +] + [[package]] name = "rand" version = "0.8.5" @@ -839,8 +1657,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.3", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", ] [[package]] @@ -850,7 +1678,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.3", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", ] [[package]] @@ -859,7 +1696,108 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" dependencies = [ - "getrandom", + "getrandom 0.2.6", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "redox_syscall" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" + +[[package]] +name = "regex-syntax" +version = "0.6.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "reqwest" +version = "0.11.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a1f7aa4f35e5e8b4160449f51afc758f0ce6454315a9fa7d0d113e958c41eb" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-rustls", + "hyper-tls", + "ipnet", + "js-sys", + "lazy_static", + "log", + "mime", + "mime_guess", + "native-tls", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-pemfile 0.3.0", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tokio-rustls", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", + "winreg", ] [[package]] @@ -877,6 +1815,41 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "rstest" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b939295f93cb1d12bc1a83cf9ee963199b133fb8a79832dd51b68bb9f59a04dc" +dependencies = [ + "async-std", + "futures", + "futures-timer", + "rstest_macros", + "rustc_version", +] + +[[package]] +name = "rstest_macros" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78aba848123782ba59340928ec7d876ebe745aa0365d6af8a630f19a5c16116" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "rustc_version", + "syn", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "rustls" version = "0.20.6" @@ -889,6 +1862,15 @@ dependencies = [ "webpki", ] +[[package]] +name = "rustls-pemfile" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee86d63972a7c661d1536fefe8c3c8407321c3df668891286de28abcd087360" +dependencies = [ + "base64", +] + [[package]] name = "rustls-pemfile" version = "1.0.0" @@ -904,6 +1886,31 @@ version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" +dependencies = [ + "lazy_static", + "windows-sys", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + [[package]] name = "sct" version = "0.7.0" @@ -914,6 +1921,46 @@ dependencies = [ "untrusted", ] +[[package]] +name = "security-framework" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "select" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee061f90afcc8678bef7a78d0d121683f0ba753f740ff7005f833ec445876b7" +dependencies = [ + "bit-set", + "html5ever", + "markup5ever_rcdom", +] + +[[package]] +name = "semver" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a41d061efea015927ac527063765e73601444cdc344ba855bc7bd44578b25e1c" + [[package]] name = "serde" version = "1.0.137" @@ -945,6 +1992,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "sha-1" version = "0.10.0" @@ -953,7 +2012,20 @@ checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.10.3", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", ] [[package]] @@ -965,12 +2037,24 @@ dependencies = [ "libc", ] +[[package]] +name = "siphasher" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" + [[package]] name = "slab" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" +[[package]] +name = "smallvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" + [[package]] name = "socket2" version = "0.4.4" @@ -987,6 +2071,32 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "string_cache" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213494b7a2b503146286049378ce02b482200519accc31872ee8be91fa820a08" +dependencies = [ + "new_debug_unreachable", + "once_cell", + "parking_lot", + "phf_shared 0.10.0", + "precomputed-hash", + "serde", +] + +[[package]] +name = "string_cache_codegen" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro2", + "quote", +] + [[package]] name = "syn" version = "1.0.96" @@ -998,6 +2108,37 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi 0.3.9", +] + +[[package]] +name = "tendril" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +dependencies = [ + "futf", + "mac", + "utf-8", +] + +[[package]] +name = "termtree" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b" + [[package]] name = "textwrap" version = "0.15.0" @@ -1024,6 +2165,15 @@ dependencies = [ "syn", ] +[[package]] +name = "thread_local" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +dependencies = [ + "once_cell", +] + [[package]] name = "time" version = "0.1.44" @@ -1035,6 +2185,21 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + [[package]] name = "tokio" version = "1.19.2" @@ -1065,6 +2230,16 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.23.4" @@ -1148,46 +2323,115 @@ dependencies = [ "version_check", ] +[[package]] +name = "unicode-bidi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" + [[package]] name = "unicode-ident" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" +[[package]] +name = "unicode-normalization" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +dependencies = [ + "tinyvec", +] + [[package]] name = "untrusted" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + [[package]] name = "urlencoding" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68b90931029ab9b034b300b797048cf23723400aa757e8a2bfb9d748102f9821" +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "uuid" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6d5d669b51467dcf7b2f1a796ce0f955f05f01cafda6c19d6e95f730df29238" dependencies = [ - "getrandom", - "rand", + "getrandom 0.2.6", + "rand 0.8.5", ] +[[package]] +name = "value-bag" +version = "1.0.0-alpha.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2209b78d1249f7e6f3293657c9779fe31ced465df091bbd433a1cf88e916ec55" +dependencies = [ + "ctor", + "version_check", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + [[package]] name = "waker-fn" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi 0.3.9", + "winapi-util", +] + [[package]] name = "want" version = "0.3.0" @@ -1198,6 +2442,12 @@ dependencies = [ "try-lock", ] +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + [[package]] name = "wasi" version = "0.10.0+wasi-snapshot-preview1" @@ -1235,6 +2485,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f741de44b75e14c35df886aff5f1eb73aa114fa5d4d00dcd37b5e01259bf3b2" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.80" @@ -1284,6 +2546,24 @@ dependencies = [ "untrusted", ] +[[package]] +name = "webpki-roots" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d8de8415c823c8abd270ad483c6feeac771fad964890779f9a8cb24fbbc1bf" +dependencies = [ + "webpki", +] + +[[package]] +name = "wepoll-ffi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" +dependencies = [ + "cc", +] + [[package]] name = "winapi" version = "0.2.8" @@ -1306,6 +2586,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -1355,12 +2644,33 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "xml-rs" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" +[[package]] +name = "xml5ever" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9234163818fd8e2418fcde330655e757900d4236acd8cc70fef345ef91f6d865" +dependencies = [ + "log", + "mac", + "markup5ever", + "time", +] + [[package]] name = "xz2" version = "0.1.7" diff --git a/Cargo.toml b/Cargo.toml index cd7c08e..ada6344 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,6 @@ description = "Duf is a simple file server." license = "MIT OR Apache-2.0" homepage = "https://github.com/sigoden/duf" repository = "https://github.com/sigoden/duf" -autotests = false categories = ["command-line-utilities", "web-programming::http-server"] keywords = ["static", "file", "server", "webdav", "cli"] @@ -39,6 +38,19 @@ xml-rs = "0.8" env_logger = { version = "0.9", default-features = false, features = ["humantime"] } log = "0.4" +[dev-dependencies] +assert_cmd = "2" +reqwest = { version = "0.11", features = ["blocking", "multipart", "rustls-tls"], default-features = false } +assert_fs = "1" +select = "0.5" +port_check = "0.1" +rstest = "0.13" +regex = "1" +pretty_assertions = "1.2" +url = "2" +diqwest = { version = "1", features = ["blocking"] } +predicates = "2" + [profile.release] lto = true strip = true diff --git a/src/args.rs b/src/args.rs index 4a2f24a..42d1941 100644 --- a/src/args.rs +++ b/src/args.rs @@ -206,8 +206,8 @@ fn to_addr(ip: &str, port: u16) -> BoxResult { // Load public certificate from file. fn load_certs(filename: &str) -> BoxResult> { // Open certificate file. - let certfile = - fs::File::open(&filename).map_err(|e| format!("Failed to open {}: {}", &filename, e))?; + let certfile = fs::File::open(&filename) + .map_err(|e| format!("Failed to access `{}`, {}", &filename, e))?; let mut reader = io::BufReader::new(certfile); // Load and return certificate. @@ -221,8 +221,8 @@ fn load_certs(filename: &str) -> BoxResult> { // Load private key from file. fn load_private_key(filename: &str) -> BoxResult { // Open keyfile. - let keyfile = - fs::File::open(&filename).map_err(|e| format!("Failed to open {}: {}", &filename, e))?; + let keyfile = fs::File::open(&filename) + .map_err(|e| format!("Failed to access `{}`, {}", &filename, e))?; let mut reader = io::BufReader::new(keyfile); // Load and return a single private key. diff --git a/src/server.rs b/src/server.rs index 61b5e12..b89f7c7 100644 --- a/src/server.rs +++ b/src/server.rs @@ -149,6 +149,13 @@ impl InnerService { } let req_path = req.uri().path(); + let headers = req.headers(); + let method = req.method().clone(); + + if req_path == "/favicon.ico" && method == Method::GET { + self.handle_send_favicon(req.headers(), &mut res).await?; + return Ok(res); + } let path = match self.extract_path(req_path) { Some(v) => v, @@ -175,15 +182,6 @@ impl InnerService { status!(res, StatusCode::NOT_FOUND); return Ok(res); } - if is_miss && path.ends_with("favicon.ico") { - *res.body_mut() = Body::from(FAVICON_ICO); - res.headers_mut() - .insert("content-type", "image/x-icon".parse().unwrap()); - return Ok(res); - } - - let headers = req.headers(); - let method = req.method().clone(); match method { Method::GET | Method::HEAD => { @@ -247,12 +245,30 @@ impl InnerService { status!(res, StatusCode::NOT_FOUND); } } - "MKCOL" if allow_upload && is_miss => self.handle_mkcol(path, &mut res).await?, - "COPY" if allow_upload && !is_miss => { - self.handle_copy(path, headers, &mut res).await? + "MKCOL" => { + if !allow_upload || !is_miss { + status!(res, StatusCode::FORBIDDEN); + } else { + self.handle_mkcol(path, &mut res).await?; + } } - "MOVE" if allow_upload && allow_delete && !is_miss => { - self.handle_move(path, headers, &mut res).await? + "COPY" => { + if !allow_upload { + status!(res, StatusCode::FORBIDDEN); + } else if is_miss { + status!(res, StatusCode::NOT_FOUND); + } else { + self.handle_copy(path, headers, &mut res).await? + } + } + "MOVE" => { + if !allow_upload || !allow_delete { + status!(res, StatusCode::FORBIDDEN); + } else if is_miss { + status!(res, StatusCode::NOT_FOUND); + } else { + self.handle_move(path, headers, &mut res).await? + } } "LOCK" => { // Fake lock @@ -286,7 +302,13 @@ impl InnerService { ) -> BoxResult<()> { ensure_path_parent(path).await?; - let mut file = fs::File::create(&path).await?; + let mut file = match fs::File::create(&path).await { + Ok(v) => v, + Err(_) => { + status!(res, StatusCode::FORBIDDEN); + return Ok(()); + } + }; let body_with_io_error = req .body_mut() @@ -436,6 +458,25 @@ impl InnerService { Ok(()) } + async fn handle_send_favicon( + &self, + headers: &HeaderMap, + res: &mut Response, + ) -> BoxResult<()> { + let path = self.args.path.join("favicon.ico"); + let meta = fs::metadata(&path).await.ok(); + let is_file = meta.map(|v| v.is_file()).unwrap_or_default(); + if is_file { + self.handle_send_file(path.as_path(), headers, false, res) + .await?; + } else { + *res.body_mut() = Body::from(FAVICON_ICO); + res.headers_mut() + .insert("content-type", "image/x-icon".parse().unwrap()); + } + Ok(()) + } + async fn handle_send_file( &self, path: &Path, @@ -534,10 +575,10 @@ impl InnerService { return Ok(()); } }, - None => 0, + None => 1, }; let mut paths = vec![self.to_pathitem(path, &self.args.path).await?.unwrap()]; - if depth > 0 { + if depth != 0 { match self.list_dir(path, &self.args.path).await { Ok(child) => paths.extend(child), Err(_) => { @@ -588,7 +629,7 @@ impl InnerService { let meta = fs::symlink_metadata(path).await?; if meta.is_dir() { - status!(res, StatusCode::BAD_REQUEST); + status!(res, StatusCode::FORBIDDEN); return Ok(()); } @@ -690,7 +731,10 @@ impl InnerService { r#" Files in {}/ - Duf - + "#, rel_path.display(), INDEX_CSS, @@ -811,15 +855,9 @@ impl InnerService { PathType::Dir | PathType::SymlinkDir => None, PathType::File | PathType::SymlinkFile => Some(meta.len()), }; - let base_name = rel_path - .file_name() - .and_then(|v| v.to_str()) - .unwrap_or("/") - .to_owned(); let name = normalize_path(rel_path); Ok(Some(PathItem { path_type, - base_name, name, mtime, size, @@ -839,7 +877,6 @@ struct IndexData { #[derive(Debug, Serialize, Eq, PartialEq, Ord, PartialOrd)] struct PathItem { path_type: PathType, - base_name: String, name: String, mtime: u64, size: Option, @@ -849,7 +886,7 @@ impl PathItem { pub fn to_dav_xml(&self, prefix: &str) -> String { let mtime = Utc.timestamp_millis(self.mtime as i64).to_rfc2822(); let href = encode_uri(&format!("{}{}", prefix, &self.name)); - let displayname = escape_str_pcdata(&self.base_name); + let displayname = escape_str_pcdata(self.base_name()); match self.path_type { PathType::Dir | PathType::SymlinkDir => format!( r#" @@ -885,6 +922,12 @@ impl PathItem { ), } } + fn base_name(&self) -> &str { + Path::new(&self.name) + .file_name() + .and_then(|v| v.to_str()) + .unwrap_or_default() + } } #[derive(Debug, Serialize, Eq, PartialEq, Ord, PartialOrd)] @@ -1016,13 +1059,14 @@ fn print_listening(addr: &SocketAddr, prefix: &str, tls: bool) { let addrs = retrieve_listening_addrs(addr); let protocol = if tls { "https" } else { "http" }; if addrs.len() == 1 { - eprintln!("Listening on {}://{}{}", protocol, addr, prefix); + println!("Listening on {}://{}{}", protocol, addr, prefix); } else { - eprintln!("Listening on:"); - for addr in addrs { - eprintln!(" {}://{}{}", protocol, addr, prefix); - } - eprintln!(); + let message = addrs + .iter() + .map(|addr| format!(" {}://{}{}", protocol, addr, prefix)) + .collect::>() + .join("\n"); + println!("Listening on:\n{}\n", message); } } diff --git a/tests/allow.rs b/tests/allow.rs new file mode 100644 index 0000000..8675605 --- /dev/null +++ b/tests/allow.rs @@ -0,0 +1,61 @@ +mod fixtures; +mod utils; + +use fixtures::{server, Error, TestServer}; +use rstest::rstest; + +#[rstest] +fn default_not_allow_upload(server: TestServer) -> Result<(), Error> { + let url = format!("{}file1", server.url()); + let resp = fetch!(b"PUT", &url).body(b"abc".to_vec()).send()?; + assert_eq!(resp.status(), 403); + Ok(()) +} + +#[rstest] +fn default_not_allow_delete(server: TestServer) -> Result<(), Error> { + let url = format!("{}test.html", server.url()); + let resp = fetch!(b"DELETE", &url).send()?; + assert_eq!(resp.status(), 403); + Ok(()) +} + +#[rstest] +fn default_not_exist_dir(server: TestServer) -> Result<(), Error> { + let resp = reqwest::blocking::get(format!("{}404/", server.url()))?; + assert_eq!(resp.status(), 404); + Ok(()) +} + +#[rstest] +fn allow_upload_not_exist_dir( + #[with(&["--allow-upload"])] server: TestServer, +) -> Result<(), Error> { + let resp = reqwest::blocking::get(format!("{}404/", server.url()))?; + assert_eq!(resp.status(), 200); + Ok(()) +} + +#[rstest] +fn allow_upload_no_override(#[with(&["--allow-upload"])] server: TestServer) -> Result<(), Error> { + let url = format!("{}index.html", server.url()); + let resp = fetch!(b"PUT", &url).body(b"abc".to_vec()).send()?; + assert_eq!(resp.status(), 403); + Ok(()) +} + +#[rstest] +fn allow_delete_no_override(#[with(&["--allow-delete"])] server: TestServer) -> Result<(), Error> { + let url = format!("{}index.html", server.url()); + let resp = fetch!(b"PUT", &url).body(b"abc".to_vec()).send()?; + assert_eq!(resp.status(), 403); + Ok(()) +} + +#[rstest] +fn allow_upload_delete_can_override(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { + let url = format!("{}index.html", server.url()); + let resp = fetch!(b"PUT", &url).body(b"abc".to_vec()).send()?; + assert_eq!(resp.status(), 201); + Ok(()) +} diff --git a/tests/auth.rs b/tests/auth.rs new file mode 100644 index 0000000..48174b2 --- /dev/null +++ b/tests/auth.rs @@ -0,0 +1,38 @@ +mod fixtures; +mod utils; + +use diqwest::blocking::WithDigestAuth; +use fixtures::{server, Error, TestServer}; +use rstest::rstest; + +#[rstest] +fn no_auth(#[with(&["--auth", "user:pass", "-A"])] server: TestServer) -> Result<(), Error> { + let resp = reqwest::blocking::get(server.url())?; + assert_eq!(resp.status(), 401); + assert!(resp.headers().contains_key("www-authenticate")); + let url = format!("{}file1", server.url()); + let resp = fetch!(b"PUT", &url).body(b"abc".to_vec()).send()?; + assert_eq!(resp.status(), 401); + Ok(()) +} + +#[rstest] +fn auth(#[with(&["--auth", "user:pass", "-A"])] server: TestServer) -> Result<(), Error> { + let url = format!("{}file1", server.url()); + let resp = fetch!(b"PUT", &url).body(b"abc".to_vec()).send()?; + assert_eq!(resp.status(), 401); + let resp = fetch!(b"PUT", &url) + .body(b"abc".to_vec()) + .send_with_digest_auth("user", "pass")?; + assert_eq!(resp.status(), 201); + Ok(()) +} + +#[rstest] +fn auth_skip_access( + #[with(&["--auth", "user:pass", "--no-auth-access"])] server: TestServer, +) -> Result<(), Error> { + let resp = reqwest::blocking::get(server.url())?; + assert_eq!(resp.status(), 200); + Ok(()) +} diff --git a/tests/bind.rs b/tests/bind.rs new file mode 100644 index 0000000..4850d90 --- /dev/null +++ b/tests/bind.rs @@ -0,0 +1,80 @@ +mod fixtures; + +use fixtures::{port, server, tmpdir, Error, TestServer}; + +use assert_cmd::prelude::*; +use assert_fs::fixture::TempDir; +use regex::Regex; +use rstest::rstest; +use std::io::{BufRead, BufReader}; +use std::process::{Command, Stdio}; + +#[rstest] +#[case(&["-b", "20.205.243.166"])] +fn bind_fails(tmpdir: TempDir, port: u16, #[case] args: &[&str]) -> Result<(), Error> { + Command::cargo_bin("duf")? + .env("RUST_LOG", "false") + .arg(tmpdir.path()) + .arg("-p") + .arg(port.to_string()) + .args(args) + .assert() + .stderr(predicates::str::contains("creating server listener")) + .failure(); + + Ok(()) +} + +#[rstest] +fn bind_ipv4(server: TestServer) -> Result<(), Error> { + assert!(reqwest::blocking::get(format!("http://127.0.0.1:{}", server.port()).as_str()).is_ok()); + Ok(()) +} + +#[rstest] +fn bind_ipv6(#[with(&["-b", "::"])] server: TestServer) -> Result<(), Error> { + assert_eq!( + reqwest::blocking::get(format!("http://127.0.0.1:{}", server.port()).as_str()).is_ok(), + !cfg!(windows) + ); + assert!(reqwest::blocking::get(format!("http://[::1]:{}", server.port()).as_str()).is_ok()); + Ok(()) +} + +#[rstest] +#[case(&[] as &[&str])] +#[case(&["--path-prefix", "/prefix"])] +fn validate_printed_urls(tmpdir: TempDir, port: u16, #[case] args: &[&str]) -> Result<(), Error> { + let mut child = Command::cargo_bin("duf")? + .env("RUST_LOG", "false") + .arg(tmpdir.path()) + .arg("-p") + .arg(port.to_string()) + .args(args) + .stdout(Stdio::piped()) + .spawn()?; + + // WARN assumes urls list is terminated by an empty line + let url_lines = BufReader::new(child.stdout.take().unwrap()) + .lines() + .map(|line| line.expect("Error reading stdout")) + .take_while(|line| !line.is_empty()) /* non-empty lines */ + .collect::>(); + let url_lines = url_lines.join("\n"); + + let urls = Regex::new(r"http://[a-zA-Z0-9\.\[\]:/]+") + .unwrap() + .captures_iter(url_lines.as_str()) + .map(|caps| caps.get(0).unwrap().as_str()) + .collect::>(); + + assert!(!urls.is_empty()); + + for url in urls { + reqwest::blocking::get(url)?.error_for_status()?; + } + + child.kill()?; + + Ok(()) +} diff --git a/tests/cors.rs b/tests/cors.rs new file mode 100644 index 0000000..7c107bc --- /dev/null +++ b/tests/cors.rs @@ -0,0 +1,37 @@ +mod fixtures; +mod utils; + +use fixtures::{server, Error, TestServer}; +use rstest::rstest; + +#[rstest] +fn cors(#[with(&["--cors"])] server: TestServer) -> Result<(), Error> { + let resp = reqwest::blocking::get(server.url())?; + + assert_eq!( + resp.headers().get("access-control-allow-origin").unwrap(), + "*" + ); + assert_eq!( + resp.headers().get("access-control-allow-headers").unwrap(), + "range, content-type, accept, origin, www-authenticate" + ); + + Ok(()) +} + +#[rstest] +fn cors_options(#[with(&["--cors"])] server: TestServer) -> Result<(), Error> { + let resp = fetch!(b"OPTIONS", server.url()).send()?; + + assert_eq!( + resp.headers().get("access-control-allow-origin").unwrap(), + "*" + ); + assert_eq!( + resp.headers().get("access-control-allow-headers").unwrap(), + "range, content-type, accept, origin, www-authenticate" + ); + + Ok(()) +} diff --git a/tests/data/cert.pem b/tests/data/cert.pem new file mode 100644 index 0000000..dd122ed --- /dev/null +++ b/tests/data/cert.pem @@ -0,0 +1,29 @@ +-----BEGIN CERTIFICATE----- +MIIFCTCCAvGgAwIBAgIUcegjikATvwNSIbN43QybKWIcKSMwDQYJKoZIhvcNAQEL +BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTIyMDYxMTA4NTQyMloXDTMyMDYw +ODA4NTQyMlowFDESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEAo2wdMbFPkX7CAF/Y+hVj5bwm4dlxhwW2Z9Ic2RZFC5w2 +oK2XwyasDBEqDlgv/bN4xObAVlDZ/4/SuTVSDrNB8dtQl7GTWptpbFKJUdNocU88 +wqd4k/cLZg2aiQqnZKD88w/AxXnYw+F8yU0pFGj9GX0S5at3/V1hrBVxVO8Y99bb +gnJA8NMm0Pw2xYZS++ULuzoECk0xbNdtbtPrIuweI5mMvsJvtiw67EIdl3N9Lj5p +L4a7X1C0Xk5H4mOcwM0qq3m31HsCW91PMCjU6suo764rx5Jqv0n9HCNxdiSEadCw +f+GrmKtFOw3DcGPETg5AJR8H3rG1agKKjI+vRtL/tZ7coFOhZKXdjGvvUFcWcqO+ +GppHh16pzJDXi2qeD9Cu5b2ayM2uBnfV7Q3FjOeDqD+BCJ0ClaqNmAD9TF2htzdu +Inl+G3OJb4cqaYjaF5YmiZISfrimK5eR2I3et5cqnbuDHMKvDfUd9Jgj/2IqPOHJ +EguuXSO7WNKfQmlTv7EN/xrD6jiB/M8ADaSxjCqTbtKNyCbJlu2Wy9WlDXwPkNW8 +g70T4Br4U4Iy3N/0w2lAAhiizdC2jkehSKmWE2nmixGSXxkSOMgXQXDJ9RBtDQfd +8ym/ADfyVndUSnHvf9jCH1NPHlFbB7RVSvUHX22Qq63NUvhV32ct+/IyD/qPpl0C +AwEAAaNTMFEwHQYDVR0OBBYEFKwSSbPXBIkmzja3/cNJyqhWy96WMB8GA1UdIwQY +MBaAFKwSSbPXBIkmzja3/cNJyqhWy96WMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI +hvcNAQELBQADggIBAHcrdu1nGDN5YvcHXzbBx73AC921fmn5xxzeFRO7af157g5h +4zornLMk4Obp+UGkMbWK4K0NAQXKKm5WjcmoOHNRg7TgTE7b1gcVuS4phdwlIqA6 +eZGg+NWZyeaIJNjdHgWgGoe+S+5Ne1I7sDKiEXrOzITJrDcQgBKFF08kqT6UNY2W +q90m+olPtrewAMgWllpxJ90u4qifPcwP+neDZJim9MhVYtHHeFsmyzlS185iasj8 +sxvp5HDTopmz0tDuiLHvOMKmyf7vapsnbqEGngQi2qV9rBmldyRLnWSe8u/FN31f +zhSk1ikSm1cQ/iyL898XexSmTafyaF8ELswdIMHkGZkVQurWeKn3/CEDXokXkpMI +4dlCSgM7SU+XtcjtXbR8/pHpcW2ZnBR0la/qIv81aNKkJeUkTcPC8BUv4jI/oT6z +LRrvRjMnHJjnADACuutlNRU4/e7h1XuvlXgFHsp63k7GJXouoIwdHjfkErZXsoEX +WeS+pPatkT7wbhfgYVwglMRIpgCu++htSRCV/lbSuYzCG6mKtxJyy4eslSjpHNPG +wELDKgzsgLtuTyNfP458O9i8x6wf9J6eVaHe3nqgqkOnnmQxEYnsPaFUMWG1/DYi +U+mA/VdQrPe3J4Z082sCe4MVmTzWlWCDpNFFQpv51NbWzc/kuIZuJCAwoZD0 +-----END CERTIFICATE----- diff --git a/tests/data/generate_tls_certs.sh b/tests/data/generate_tls_certs.sh new file mode 100755 index 0000000..ed23639 --- /dev/null +++ b/tests/data/generate_tls_certs.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +openssl req -subj '/CN=localhost' -x509 -newkey rsa:4096 -keyout key_pkcs8.pem -out cert.pem -nodes -days 3650 +openssl rsa -in key_pkcs8.pem -out key_pkcs1.pem diff --git a/tests/data/key_pkcs1.pem b/tests/data/key_pkcs1.pem new file mode 100644 index 0000000..88fd6f0 --- /dev/null +++ b/tests/data/key_pkcs1.pem @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/tests/data/key_pkcs8.pem b/tests/data/key_pkcs8.pem new file mode 100644 index 0000000..8810d7a --- /dev/null +++ b/tests/data/key_pkcs8.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +afHJpnY0yZEqN1PeDaKVOCQAHbD8qQ== +-----END PRIVATE KEY----- diff --git a/tests/favicon.rs b/tests/favicon.rs new file mode 100644 index 0000000..12feb47 --- /dev/null +++ b/tests/favicon.rs @@ -0,0 +1,25 @@ +mod fixtures; +mod utils; + +use fixtures::{server, Error, TestServer}; +use rstest::rstest; + +#[rstest] +fn default_favicon(server: TestServer) -> Result<(), Error> { + let resp = reqwest::blocking::get(format!("{}favicon.ico", server.url()))?; + assert_eq!(resp.status(), 200); + assert_eq!(resp.headers().get("content-type").unwrap(), "image/x-icon"); + Ok(()) +} + +#[rstest] +fn exist_favicon(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { + let url = format!("{}favicon.ico", server.url()); + let data = b"abc"; + let resp = fetch!(b"PUT", &url).body(data.to_vec()).send()?; + assert_eq!(resp.status(), 201); + let resp = reqwest::blocking::get(url)?; + assert_eq!(resp.status(), 200); + assert_eq!(resp.bytes()?, data.to_vec()); + Ok(()) +} diff --git a/tests/fixtures.rs b/tests/fixtures.rs new file mode 100644 index 0000000..0ff9970 --- /dev/null +++ b/tests/fixtures.rs @@ -0,0 +1,191 @@ +use assert_cmd::prelude::*; +use assert_fs::fixture::TempDir; +use assert_fs::prelude::*; +use port_check::free_local_port; +use reqwest::Url; +use rstest::fixture; +use std::process::{Child, Command, Stdio}; +use std::thread::sleep; +use std::time::{Duration, Instant}; + +#[allow(dead_code)] +pub type Error = Box; + +/// File names for testing purpose +#[allow(dead_code)] +pub static FILES: &[&str] = &[ + "test.txt", + "test.html", + "index.html", + "test.mkv", + #[cfg(not(windows))] + "test \" \' & < >.csv", + "😀.data", + "⎙.mp4", + "#[]{}()@!$&'`+,;= %20.test", + #[cfg(unix)] + ":?#[]{}<>()@!$&'`|*+,;= %20.test", + #[cfg(not(windows))] + "foo\\bar.test", +]; + +/// Directory names for testing purpose +#[allow(dead_code)] +pub static DIR_NO_INDEX: &str = "dir-no-index/"; + +/// Directory names for testing purpose +#[allow(dead_code)] +pub static DIRECTORIES: &[&str] = &["dira/", "dirb/", "dirc/", DIR_NO_INDEX]; + +/// Name of a deeply nested file +#[allow(dead_code)] +pub static DEEPLY_NESTED_FILE: &str = "very/deeply/nested/test.rs"; + +/// Test fixture which creates a temporary directory with a few files and directories inside. +/// The directories also contain files. +#[fixture] +#[allow(dead_code)] +pub fn tmpdir() -> TempDir { + let tmpdir = assert_fs::TempDir::new().expect("Couldn't create a temp dir for tests"); + for file in FILES { + tmpdir + .child(file) + .write_str(&format!("This is {}", file)) + .expect("Couldn't write to file"); + } + for directory in DIRECTORIES { + for file in FILES { + if *directory == DIR_NO_INDEX { + continue; + } + tmpdir + .child(format!("{}{}", directory, file)) + .write_str(&format!("This is {}{}", directory, file)) + .expect("Couldn't write to file"); + } + } + + tmpdir + .child(&DEEPLY_NESTED_FILE) + .write_str("File in a deeply nested directory.") + .expect("Couldn't write to file"); + tmpdir +} + +/// Get a free port. +#[fixture] +#[allow(dead_code)] +pub fn port() -> u16 { + free_local_port().expect("Couldn't find a free local port") +} + +/// Run miniserve as a server; Start with a temporary directory, a free port and some +/// optional arguments then wait for a while for the server setup to complete. +#[fixture] +#[allow(dead_code)] +pub fn server(#[default(&[] as &[&str])] args: I) -> TestServer +where + I: IntoIterator + Clone, + I::Item: AsRef, +{ + let port = port(); + let tmpdir = tmpdir(); + let child = Command::cargo_bin("duf") + .expect("Couldn't find test binary") + .env("RUST_LOG", "false") + .arg(tmpdir.path()) + .arg("-p") + .arg(port.to_string()) + .args(args.clone()) + .stdout(Stdio::null()) + .spawn() + .expect("Couldn't run test binary"); + let is_tls = args + .into_iter() + .any(|x| x.as_ref().to_str().unwrap().contains("tls")); + + wait_for_port(port); + TestServer::new(port, tmpdir, child, is_tls) +} + +/// Same as `server()` but ignore stderr +#[fixture] +#[allow(dead_code)] +pub fn server_no_stderr(#[default(&[] as &[&str])] args: I) -> TestServer +where + I: IntoIterator + Clone, + I::Item: AsRef, +{ + let port = port(); + let tmpdir = tmpdir(); + let child = Command::cargo_bin("duf") + .expect("Couldn't find test binary") + .env("RUST_LOG", "false") + .arg(tmpdir.path()) + .arg("-p") + .arg(port.to_string()) + .args(args.clone()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .spawn() + .expect("Couldn't run test binary"); + let is_tls = args + .into_iter() + .any(|x| x.as_ref().to_str().unwrap().contains("tls")); + + wait_for_port(port); + TestServer::new(port, tmpdir, child, is_tls) +} + +/// Wait a max of 1s for the port to become available. +fn wait_for_port(port: u16) { + let start_wait = Instant::now(); + + while !port_check::is_port_reachable(format!("localhost:{}", port)) { + sleep(Duration::from_millis(100)); + + if start_wait.elapsed().as_secs() > 1 { + panic!("timeout waiting for port {}", port); + } + } +} + +#[allow(dead_code)] +pub struct TestServer { + port: u16, + tmpdir: TempDir, + child: Child, + is_tls: bool, +} + +#[allow(dead_code)] +impl TestServer { + pub fn new(port: u16, tmpdir: TempDir, child: Child, is_tls: bool) -> Self { + Self { + port, + tmpdir, + child, + is_tls, + } + } + + pub fn url(&self) -> Url { + let protocol = if self.is_tls { "https" } else { "http" }; + Url::parse(&format!("{}://localhost:{}", protocol, self.port)).unwrap() + } + + pub fn path(&self) -> &std::path::Path { + self.tmpdir.path() + } + + pub fn port(&self) -> u16 { + self.port + } +} + +impl Drop for TestServer { + fn drop(&mut self) { + self.child.kill().expect("Couldn't kill test server"); + self.child.wait().unwrap(); + } +} diff --git a/tests/http.rs b/tests/http.rs new file mode 100644 index 0000000..d8a8847 --- /dev/null +++ b/tests/http.rs @@ -0,0 +1,184 @@ +mod fixtures; +mod utils; + +use fixtures::{server, Error, TestServer}; +use rstest::rstest; + +#[rstest] +fn get_dir(server: TestServer) -> Result<(), Error> { + let resp = reqwest::blocking::get(server.url())?; + assert_index_resp!(resp); + Ok(()) +} + +#[rstest] +fn head_dir(server: TestServer) -> Result<(), Error> { + let resp = fetch!(b"HEAD", server.url()).send()?; + assert_eq!(resp.status(), 200); + assert_eq!( + resp.headers().get("content-type").unwrap(), + "text/html; charset=utf-8" + ); + assert_eq!(resp.text()?, ""); + Ok(()) +} + +#[rstest] +fn get_dir_404(server: TestServer) -> Result<(), Error> { + let resp = reqwest::blocking::get(format!("{}404/", server.url()))?; + assert_eq!(resp.status(), 404); + Ok(()) +} + +#[rstest] +fn head_dir_404(server: TestServer) -> Result<(), Error> { + let resp = fetch!(b"HEAD", format!("{}404/", server.url())).send()?; + assert_eq!(resp.status(), 404); + Ok(()) +} + +#[rstest] +fn get_dir_zip(server: TestServer) -> Result<(), Error> { + let resp = reqwest::blocking::get(format!("{}?zip", server.url()))?; + assert_eq!(resp.status(), 200); + assert_eq!( + resp.headers().get("content-type").unwrap(), + "application/zip" + ); + assert!(resp.headers().contains_key("content-disposition")); + Ok(()) +} + +#[rstest] +fn head_dir_zip(server: TestServer) -> Result<(), Error> { + let resp = fetch!(b"HEAD", format!("{}?zip", server.url())).send()?; + assert_eq!(resp.status(), 200); + assert_eq!( + resp.headers().get("content-type").unwrap(), + "application/zip" + ); + assert!(resp.headers().contains_key("content-disposition")); + assert_eq!(resp.text()?, ""); + Ok(()) +} + +#[rstest] +fn get_dir_search(server: TestServer) -> Result<(), Error> { + let resp = reqwest::blocking::get(format!("{}?q={}", server.url(), "test.html"))?; + assert_eq!(resp.status(), 200); + let paths = utils::retrive_index_paths(&resp.text()?); + assert!(!paths.is_empty()); + for p in paths { + assert!(p.contains(&"test.html")); + } + Ok(()) +} + +#[rstest] +fn head_dir_search(server: TestServer) -> Result<(), Error> { + let resp = fetch!(b"HEAD", format!("{}?q={}", server.url(), "test.html")).send()?; + assert_eq!(resp.status(), 200); + assert_eq!( + resp.headers().get("content-type").unwrap(), + "text/html; charset=utf-8" + ); + assert_eq!(resp.text()?, ""); + Ok(()) +} + +#[rstest] +fn get_file(server: TestServer) -> Result<(), Error> { + let resp = reqwest::blocking::get(format!("{}index.html", server.url()))?; + assert_eq!(resp.status(), 200); + assert_eq!(resp.headers().get("content-type").unwrap(), "text/html"); + assert_eq!(resp.headers().get("accept-ranges").unwrap(), "bytes"); + assert!(resp.headers().contains_key("etag")); + assert!(resp.headers().contains_key("last-modified")); + assert!(resp.headers().contains_key("content-length")); + assert_eq!(resp.text()?, "This is index.html"); + Ok(()) +} + +#[rstest] +fn head_file(server: TestServer) -> Result<(), Error> { + let resp = fetch!(b"HEAD", format!("{}index.html", server.url())).send()?; + assert_eq!(resp.status(), 200); + assert_eq!(resp.headers().get("content-type").unwrap(), "text/html"); + assert_eq!(resp.headers().get("accept-ranges").unwrap(), "bytes"); + assert!(resp.headers().contains_key("etag")); + assert!(resp.headers().contains_key("last-modified")); + assert!(resp.headers().contains_key("content-length")); + assert_eq!(resp.text()?, ""); + Ok(()) +} + +#[rstest] +fn get_file_404(server: TestServer) -> Result<(), Error> { + let resp = reqwest::blocking::get(format!("{}404", server.url()))?; + assert_eq!(resp.status(), 404); + Ok(()) +} + +#[rstest] +fn head_file_404(server: TestServer) -> Result<(), Error> { + let resp = fetch!(b"HEAD", format!("{}404", server.url())).send()?; + assert_eq!(resp.status(), 404); + Ok(()) +} + +#[rstest] +fn options_dir(server: TestServer) -> Result<(), Error> { + let resp = fetch!(b"OPTIONS", format!("{}index.html", server.url())).send()?; + assert_eq!(resp.status(), 204); + assert_eq!( + resp.headers().get("allow").unwrap(), + "GET,HEAD,PUT,OPTIONS,DELETE,PROPFIND,COPY,MOVE" + ); + assert_eq!(resp.headers().get("dav").unwrap(), "1"); + Ok(()) +} + +#[rstest] +fn put_file(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { + let url = format!("{}file1", server.url()); + let resp = fetch!(b"PUT", &url).body(b"abc".to_vec()).send()?; + assert_eq!(resp.status(), 201); + let resp = reqwest::blocking::get(url)?; + assert_eq!(resp.status(), 200); + Ok(()) +} + +#[rstest] +fn put_file_create_dir(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { + let url = format!("{}xyz/file1", server.url()); + let resp = fetch!(b"PUT", &url).body(b"abc".to_vec()).send()?; + assert_eq!(resp.status(), 201); + let resp = reqwest::blocking::get(url)?; + assert_eq!(resp.status(), 200); + Ok(()) +} + +#[rstest] +fn put_file_conflict_dir(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { + let url = format!("{}dira", server.url()); + let resp = fetch!(b"PUT", &url).body(b"abc".to_vec()).send()?; + assert_eq!(resp.status(), 403); + Ok(()) +} + +#[rstest] +fn delete_file(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { + let url = format!("{}test.html", server.url()); + let resp = fetch!(b"DELETE", &url).send()?; + assert_eq!(resp.status(), 204); + let resp = reqwest::blocking::get(url)?; + assert_eq!(resp.status(), 404); + Ok(()) +} + +#[rstest] +fn delete_file_404(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { + let resp = fetch!(b"DELETE", format!("{}file1", server.url())).send()?; + assert_eq!(resp.status(), 404); + Ok(()) +} diff --git a/tests/path_prefix.rs b/tests/path_prefix.rs new file mode 100644 index 0000000..dd34acf --- /dev/null +++ b/tests/path_prefix.rs @@ -0,0 +1,30 @@ +mod fixtures; +mod utils; + +use fixtures::{server, Error, TestServer}; +use rstest::rstest; + +#[rstest] +fn path_prefix_index(#[with(&["--path-prefix", "xyz"])] server: TestServer) -> Result<(), Error> { + let resp = reqwest::blocking::get(format!("{}{}", server.url(), "xyz"))?; + assert_index_resp!(resp); + Ok(()) +} + +#[rstest] +fn path_prefix_file(#[with(&["--path-prefix", "xyz"])] server: TestServer) -> Result<(), Error> { + let resp = reqwest::blocking::get(format!("{}{}/index.html", server.url(), "xyz"))?; + assert_eq!(resp.status(), 200); + assert_eq!(resp.text()?, "This is index.html"); + Ok(()) +} + +#[rstest] +fn path_prefix_propfind( + #[with(&["--path-prefix", "xyz"])] server: TestServer, +) -> Result<(), Error> { + let resp = fetch!(b"PROPFIND", format!("{}{}", server.url(), "xyz")).send()?; + let text = resp.text()?; + assert!(text.contains("/xyz/")); + Ok(()) +} diff --git a/tests/render.rs b/tests/render.rs new file mode 100644 index 0000000..e666265 --- /dev/null +++ b/tests/render.rs @@ -0,0 +1,35 @@ +mod fixtures; + +use fixtures::{server, Error, TestServer, DIR_NO_INDEX}; +use rstest::rstest; + +#[rstest] +fn render_index(#[with(&["--render-index"])] server: TestServer) -> Result<(), Error> { + let resp = reqwest::blocking::get(server.url())?; + let text = resp.text()?; + assert_eq!(text, "This is index.html"); + Ok(()) +} + +#[rstest] +fn render_index_404(#[with(&["--render-index"])] server: TestServer) -> Result<(), Error> { + let resp = reqwest::blocking::get(format!("{}/{}", server.url(), DIR_NO_INDEX))?; + assert_eq!(resp.status(), 404); + Ok(()) +} + +#[rstest] +fn render_spa(#[with(&["--render-spa"])] server: TestServer) -> Result<(), Error> { + let resp = reqwest::blocking::get(server.url())?; + let text = resp.text()?; + assert_eq!(text, "This is index.html"); + Ok(()) +} + +#[rstest] +fn render_spa_no_404(#[with(&["--render-spa"])] server: TestServer) -> Result<(), Error> { + let resp = reqwest::blocking::get(format!("{}/{}", server.url(), DIR_NO_INDEX))?; + let text = resp.text()?; + assert_eq!(text, "This is index.html"); + Ok(()) +} diff --git a/tests/symlink.rs b/tests/symlink.rs new file mode 100644 index 0000000..85a7261 --- /dev/null +++ b/tests/symlink.rs @@ -0,0 +1,46 @@ +mod fixtures; +mod utils; + +use assert_fs::fixture::TempDir; +use fixtures::{server, tmpdir, Error, TestServer}; +use rstest::rstest; + +#[cfg(unix)] +use std::os::unix::fs::symlink as symlink_dir; +#[cfg(windows)] +use std::os::windows::fs::symlink_dir; + +#[rstest] +fn default_not_allow_symlink(server: TestServer, tmpdir: TempDir) -> Result<(), Error> { + // Create symlink directory "foo" to point outside the root + let dir = "foo"; + symlink_dir(tmpdir.path(), server.path().join(dir)).expect("Couldn't create symlink"); + let resp = reqwest::blocking::get(format!("{}{}", server.url(), dir))?; + assert_eq!(resp.status(), 404); + let resp = reqwest::blocking::get(format!("{}{}/index.html", server.url(), dir))?; + assert_eq!(resp.status(), 404); + let resp = reqwest::blocking::get(server.url())?; + let paths = utils::retrive_index_paths(&resp.text()?); + assert!(!paths.is_empty()); + assert!(!paths.contains(&format!("{}/", dir))); + Ok(()) +} + +#[rstest] +fn allow_symlink( + #[with(&["--allow-symlink"])] server: TestServer, + tmpdir: TempDir, +) -> Result<(), Error> { + // Create symlink directory "foo" to point outside the root + let dir = "foo"; + symlink_dir(tmpdir.path(), server.path().join(dir)).expect("Couldn't create symlink"); + let resp = reqwest::blocking::get(format!("{}{}", server.url(), dir))?; + assert_eq!(resp.status(), 200); + let resp = reqwest::blocking::get(format!("{}{}/index.html", server.url(), dir))?; + assert_eq!(resp.status(), 200); + let resp = reqwest::blocking::get(server.url())?; + let paths = utils::retrive_index_paths(&resp.text()?); + assert!(!paths.is_empty()); + assert!(paths.contains(&format!("{}/", dir))); + Ok(()) +} diff --git a/tests/tls.rs b/tests/tls.rs new file mode 100644 index 0000000..6b9e6bd --- /dev/null +++ b/tests/tls.rs @@ -0,0 +1,51 @@ +mod fixtures; +mod utils; + +use assert_cmd::Command; +use fixtures::{server, Error, TestServer}; +use predicates::str::contains; +use reqwest::blocking::ClientBuilder; +use rstest::rstest; + +/// Can start the server with TLS and receive encrypted responses. +#[rstest] +#[case(server(&[ + "--tls-cert", "tests/data/cert.pem", + "--tls-key", "tests/data/key_pkcs8.pem", +]))] +#[case(server(&[ + "--tls-cert", "tests/data/cert.pem", + "--tls-key", "tests/data/key_pkcs1.pem", +]))] +fn tls_works(#[case] server: TestServer) -> Result<(), Error> { + let client = ClientBuilder::new() + .danger_accept_invalid_certs(true) + .build()?; + let resp = client.get(server.url()).send()?.error_for_status()?; + assert_index_resp!(resp); + Ok(()) +} + +/// Wrong path for cert throws error. +#[rstest] +fn wrong_path_cert() -> Result<(), Error> { + Command::cargo_bin("duf")? + .args(&["--tls-cert", "wrong", "--tls-key", "tests/data/key.pem"]) + .assert() + .failure() + .stderr(contains("error: Failed to access `wrong`")); + + Ok(()) +} + +/// Wrong paths for key throws errors. +#[rstest] +fn wrong_path_key() -> Result<(), Error> { + Command::cargo_bin("duf")? + .args(&["--tls-cert", "tests/data/cert.pem", "--tls-key", "wrong"]) + .assert() + .failure() + .stderr(contains("error: Failed to access `wrong`")); + + Ok(()) +} diff --git a/tests/utils.rs b/tests/utils.rs new file mode 100644 index 0000000..a0f7eda --- /dev/null +++ b/tests/utils.rs @@ -0,0 +1,61 @@ +use serde_json::Value; +use std::collections::HashSet; + +#[macro_export] +macro_rules! assert_index_resp { + ($resp:ident) => { + assert_index_resp!($resp, self::fixtures::FILES) + }; + ($resp:ident, $files:expr) => { + assert_eq!($resp.status(), 200); + let body = $resp.text()?; + let paths = self::utils::retrive_index_paths(&body); + assert!(!paths.is_empty()); + for file in $files { + assert!(paths.contains(&file.to_string())); + } + }; +} + +#[macro_export] +macro_rules! fetch { + ($method:literal, $url:expr) => { + reqwest::blocking::Client::new().request(hyper::Method::from_bytes($method)?, $url) + }; +} + +#[allow(dead_code)] +pub fn retrive_index_paths(index: &str) -> HashSet { + retrive_index_paths_impl(index).unwrap_or_default() +} + +#[allow(dead_code)] +pub fn encode_uri(v: &str) -> String { + let parts: Vec<_> = v.split('/').map(urlencoding::encode).collect(); + parts.join("/") +} + +fn retrive_index_paths_impl(index: &str) -> Option> { + let lines: Vec<&str> = index.lines().collect(); + let (i, _) = lines + .iter() + .enumerate() + .find(|(_, v)| v.contains("const DATA"))?; + let line = lines.get(i + 1)?; + let value: Value = line.parse().ok()?; + let paths = value + .get("paths")? + .as_array()? + .iter() + .flat_map(|v| { + let name = v.get("name")?.as_str()?; + let path_type = v.get("path_type")?.as_str()?; + if path_type.ends_with("Dir") { + Some(format!("{}/", name)) + } else { + Some(name.to_owned()) + } + }) + .collect(); + Some(paths) +} diff --git a/tests/webdav.rs b/tests/webdav.rs new file mode 100644 index 0000000..fee0ce8 --- /dev/null +++ b/tests/webdav.rs @@ -0,0 +1,203 @@ +mod fixtures; +mod utils; + +use fixtures::{server, Error, TestServer, FILES}; +use rstest::rstest; +use xml::escape::escape_str_pcdata; + +#[rstest] +fn propfind_dir(server: TestServer) -> Result<(), Error> { + let resp = fetch!(b"PROPFIND", format!("{}dira", server.url())).send()?; + assert_eq!(resp.status(), 207); + let body = resp.text()?; + assert!(body.contains("/dira")); + assert!(body.contains("dira")); + for f in FILES { + assert!(body.contains(&format!("/dira/{}", utils::encode_uri(f)))); + assert!(body.contains(&format!( + "{}", + escape_str_pcdata(f) + ))); + } + Ok(()) +} + +#[rstest] +fn propfind_dir_depth0(server: TestServer) -> Result<(), Error> { + let resp = fetch!(b"PROPFIND", format!("{}dira", server.url())) + .header("depth", "0") + .send()?; + assert_eq!(resp.status(), 207); + let body = resp.text()?; + assert!(body.contains("/dira")); + assert!(body.contains("dira")); + assert_eq!( + body.lines() + .filter(|v| *v == "HTTP/1.1 200 OK") + .count(), + 1 + ); + Ok(()) +} + +#[rstest] +fn propfind_404(server: TestServer) -> Result<(), Error> { + let resp = fetch!(b"PROPFIND", format!("{}404", server.url())).send()?; + assert_eq!(resp.status(), 404); + Ok(()) +} + +#[rstest] +fn propfind_file(server: TestServer) -> Result<(), Error> { + let resp = fetch!(b"PROPFIND", format!("{}test.html", server.url())).send()?; + assert_eq!(resp.status(), 207); + let body = resp.text()?; + assert!(body.contains("/test.html")); + assert!(body.contains("test.html")); + assert_eq!( + body.lines() + .filter(|v| *v == "HTTP/1.1 200 OK") + .count(), + 1 + ); + Ok(()) +} + +#[rstest] +fn proppatch_file(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { + let resp = fetch!(b"PROPPATCH", format!("{}test.html", server.url())).send()?; + assert_eq!(resp.status(), 207); + let body = resp.text()?; + assert!(body.contains("/test.html")); + Ok(()) +} + +#[rstest] +fn proppatch_404(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { + let resp = fetch!(b"PROPPATCH", format!("{}404", server.url())).send()?; + assert_eq!(resp.status(), 404); + Ok(()) +} + +#[rstest] +fn mkcol_dir(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { + let resp = fetch!(b"MKCOL", format!("{}newdir", server.url())).send()?; + assert_eq!(resp.status(), 201); + Ok(()) +} + +#[rstest] +fn mkcol_not_allow_upload(server: TestServer) -> Result<(), Error> { + let resp = fetch!(b"MKCOL", format!("{}newdir", server.url())).send()?; + assert_eq!(resp.status(), 403); + Ok(()) +} + +#[rstest] +fn copy_file(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { + let new_url = format!("{}test2.html", server.url()); + let resp = fetch!(b"COPY", format!("{}test.html", server.url())) + .header("Destination", &new_url) + .send()?; + assert_eq!(resp.status(), 204); + let resp = reqwest::blocking::get(new_url)?; + assert_eq!(resp.status(), 200); + Ok(()) +} + +#[rstest] +fn copy_not_allow_upload(server: TestServer) -> Result<(), Error> { + let new_url = format!("{}test2.html", server.url()); + let resp = fetch!(b"COPY", format!("{}test.html", server.url())) + .header("Destination", &new_url) + .send()?; + assert_eq!(resp.status(), 403); + Ok(()) +} + +#[rstest] +fn copy_file_404(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { + let new_url = format!("{}test2.html", server.url()); + let resp = fetch!(b"COPY", format!("{}404", server.url())) + .header("Destination", &new_url) + .send()?; + assert_eq!(resp.status(), 404); + Ok(()) +} + +#[rstest] +fn move_file(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { + let origin_url = format!("{}test.html", server.url()); + let new_url = format!("{}test2.html", server.url()); + let resp = fetch!(b"MOVE", &origin_url) + .header("Destination", &new_url) + .send()?; + assert_eq!(resp.status(), 204); + let resp = reqwest::blocking::get(new_url)?; + assert_eq!(resp.status(), 200); + let resp = reqwest::blocking::get(origin_url)?; + assert_eq!(resp.status(), 404); + Ok(()) +} + +#[rstest] +fn move_not_allow_upload(#[with(&["--allow-delete"])] server: TestServer) -> Result<(), Error> { + let origin_url = format!("{}test.html", server.url()); + let new_url = format!("{}test2.html", server.url()); + let resp = fetch!(b"MOVE", &origin_url) + .header("Destination", &new_url) + .send()?; + assert_eq!(resp.status(), 403); + Ok(()) +} + +#[rstest] +fn move_not_allow_delete(#[with(&["--allow-upload"])] server: TestServer) -> Result<(), Error> { + let origin_url = format!("{}test.html", server.url()); + let new_url = format!("{}test2.html", server.url()); + let resp = fetch!(b"MOVE", &origin_url) + .header("Destination", &new_url) + .send()?; + assert_eq!(resp.status(), 403); + Ok(()) +} + +#[rstest] +fn move_file_404(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { + let new_url = format!("{}test2.html", server.url()); + let resp = fetch!(b"MOVE", format!("{}404", server.url())) + .header("Destination", &new_url) + .send()?; + assert_eq!(resp.status(), 404); + Ok(()) +} + +#[rstest] +fn lock_file(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { + let resp = fetch!(b"LOCK", format!("{}test.html", server.url())).send()?; + assert_eq!(resp.status(), 200); + let body = resp.text()?; + assert!(body.contains("/test.html")); + Ok(()) +} + +#[rstest] +fn lock_file_404(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { + let resp = fetch!(b"LOCK", format!("{}404", server.url())).send()?; + assert_eq!(resp.status(), 404); + Ok(()) +} + +#[rstest] +fn unlock_file(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { + let resp = fetch!(b"LOCK", format!("{}test.html", server.url())).send()?; + assert_eq!(resp.status(), 200); + Ok(()) +} + +#[rstest] +fn unlock_file_404(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { + let resp = fetch!(b"LOCK", format!("{}404", server.url())).send()?; + assert_eq!(resp.status(), 404); + Ok(()) +}