diff --git a/Cargo.lock b/Cargo.lock index cfd9fc6..fdc4da4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,139 +1,5 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -[[package]] -name = "actix" -version = "0.7.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c616db5fa4b0c40702fb75201c2af7f8aa8f3a2e2c1dda3b0655772aa949666" -dependencies = [ - "actix_derive", - "bitflags", - "bytes", - "crossbeam-channel", - "failure", - "fnv", - "futures", - "libc", - "log", - "parking_lot 0.7.1", - "smallvec 0.6.13", - "tokio", - "tokio-codec", - "tokio-executor", - "tokio-io", - "tokio-reactor", - "tokio-signal", - "tokio-tcp", - "tokio-timer", - "trust-dns-proto 0.5.0", - "trust-dns-resolver", - "uuid", -] - -[[package]] -name = "actix-net" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bebfbe6629e0131730746718c9e032b58f02c6ce06ed7c982b9fef6c8545acd" -dependencies = [ - "actix", - "bytes", - "futures", - "log", - "mio", - "net2", - "num_cpus", - "slab", - "tokio", - "tokio-codec", - "tokio-current-thread", - "tokio-io", - "tokio-reactor", - "tokio-tcp", - "tokio-timer", - "tower-service", - "trust-dns-resolver", -] - -[[package]] -name = "actix-web" -version = "0.7.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0ac60f86c65a50b140139f499f4f7c6e49e4b5d88fbfba08e4e3975991f7bf4" -dependencies = [ - "actix", - "actix-net", - "base64 0.10.1", - "bitflags", - "brotli2", - "byteorder", - "bytes", - "cookie", - "encoding", - "failure", - "flate2", - "futures", - "futures-cpupool", - "h2", - "http", - "httparse", - "language-tags", - "lazy_static", - "lazycell", - "log", - "mime", - "mime_guess", - "mio", - "net2", - "num_cpus", - "parking_lot 0.7.1", - "percent-encoding", - "rand 0.6.5", - "regex", - "serde", - "serde_json", - "serde_urlencoded", - "sha1", - "slab", - "smallvec 0.6.13", - "time", - "tokio", - "tokio-current-thread", - "tokio-io", - "tokio-reactor", - "tokio-tcp", - "tokio-timer", - "url", - "v_htmlescape", - "version_check 0.1.5", -] - -[[package]] -name = "actix_derive" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4300e9431455322ae393d43a2ba1ef96b8080573c0fc23b196219efedfb6ba69" -dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "syn 0.15.44", -] - -[[package]] -name = "adler32" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" - -[[package]] -name = "aho-corasick" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada" -dependencies = [ - "memchr", -] - [[package]] name = "ansi_term" version = "0.11.0" @@ -160,58 +26,17 @@ dependencies = [ "winapi 0.3.8", ] -[[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.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" -[[package]] -name = "backtrace" -version = "0.3.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e692897359247cc6bb902933361652380af0f1b7651ae5c5013407f30e109e" -dependencies = [ - "backtrace-sys", - "cfg-if", - "libc", - "rustc-demangle", -] - -[[package]] -name = "backtrace-sys" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fbebbe1c9d1f383a9cc7e8ccdb471b91c8d024ee9c2ca5b5346121fe8b4399" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "base64" -version = "0.9.3" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" -dependencies = [ - "byteorder", - "safemem", -] - -[[package]] -name = "base64" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" -dependencies = [ - "byteorder", -] +checksum = "7d5ca2cd0adc3f48f9e9ea5a6bbdf9ccc0bfade884847e484d452414c7ccffb3" [[package]] name = "binascii" @@ -236,25 +61,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] -name = "brotli-sys" -version = "0.3.2" +name = "block-buffer" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4445dea95f4c2b41cde57cc9fee236ae4dbae88d8fcbdb4750fc1bb5d86aaecd" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" dependencies = [ - "cc", - "libc", + "block-padding", + "byte-tools", + "byteorder", + "generic-array", ] [[package]] -name = "brotli2" -version = "0.3.2" +name = "block-padding" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cb036c3eade309815c15ddbacec5b22c4d1f3983a774ab2eac2e3e9ea85568e" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" dependencies = [ - "brotli-sys", - "libc", + "byte-tools", ] +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + [[package]] name = "byteorder" version = "1.3.4" @@ -263,39 +95,9 @@ checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" [[package]] name = "bytes" -version = "0.4.12" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" -dependencies = [ - "byteorder", - "iovec", -] - -[[package]] -name = "bzip2" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42b7c3cbf0fa9c1b82308d57191728ca0256cb821220f4e2fd410a72ade26e3b" -dependencies = [ - "bzip2-sys", - "libc", -] - -[[package]] -name = "bzip2-sys" -version = "0.1.8+1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05305b41c5034ff0e93937ac64133d109b5a2660114ec45e9760bc6816d83038" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "cc" -version = "1.0.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d87b23d6a92cd03af510a5ade527033f6aa6fa92161e2d5863a907d4c5e31d" +checksum = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" [[package]] name = "cfg-if" @@ -319,99 +121,12 @@ dependencies = [ ] [[package]] -name = "cloudabi" -version = "0.0.3" +name = "digest" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" dependencies = [ - "bitflags", -] - -[[package]] -name = "cookie" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9fac5e7bdefb6160fb181ee0eaa6f96704b625c70e6d61c465cb35750a4ea12" -dependencies = [ - "base64 0.9.3", - "ring", - "time", - "url", -] - -[[package]] -name = "crc32fast" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-channel" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8ec7fcd21571dc78f96cc96243cab8d8f035247c3efd16c687be154c3fa9efa" -dependencies = [ - "crossbeam-utils 0.6.6", -] - -[[package]] -name = "crossbeam-deque" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils 0.7.2", - "maybe-uninit", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" -dependencies = [ - "autocfg 1.0.0", - "cfg-if", - "crossbeam-utils 0.7.2", - "lazy_static", - "maybe-uninit", - "memoffset", - "scopeguard 1.1.0", -] - -[[package]] -name = "crossbeam-queue" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db" -dependencies = [ - "cfg-if", - "crossbeam-utils 0.7.2", -] - -[[package]] -name = "crossbeam-utils" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" -dependencies = [ - "cfg-if", - "lazy_static", -] - -[[package]] -name = "crossbeam-utils" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" -dependencies = [ - "autocfg 1.0.0", - "cfg-if", - "lazy_static", + "generic-array", ] [[package]] @@ -421,99 +136,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3" [[package]] -name = "encoding" -version = "0.2.33" +name = "fake-simd" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec" -dependencies = [ - "encoding-index-japanese", - "encoding-index-korean", - "encoding-index-simpchinese", - "encoding-index-singlebyte", - "encoding-index-tradchinese", -] - -[[package]] -name = "encoding-index-japanese" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding-index-korean" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding-index-simpchinese" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d87a7194909b9118fc707194baa434a4e3b0fb6a5a757c73c3adb07aa25031f7" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding-index-singlebyte" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3351d5acffb224af9ca265f435b859c7c01537c0849754d3db3fdf2bfe2ae84a" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding-index-tradchinese" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd0e20d5688ce3cab59eb3ef3a2083a5c77bf496cb798dc6fcdb75f323890c18" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding_index_tests" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" - -[[package]] -name = "error-chain" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6930e04918388a9a2e41d518c25cf679ccafe26733fb4127dbf21993f2575d46" -dependencies = [ - "backtrace", -] - -[[package]] -name = "failure" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8529c2421efa3066a5cbd8063d2244603824daccb6936b079010bb2aa89464b" -dependencies = [ - "backtrace", - "failure_derive", -] - -[[package]] -name = "failure_derive" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "030a733c8287d6213886dd487564ff5c8f6aae10278b3588ed177f9d18f8d231" -dependencies = [ - "proc-macro2 1.0.10", - "quote 1.0.4", - "syn 1.0.18", - "synstructure", -] +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" [[package]] name = "fern" @@ -524,31 +150,12 @@ dependencies = [ "log", ] -[[package]] -name = "flate2" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cfff41391129e0a856d6d822600b8d71179d46879e310417eb9c762eb178b42" -dependencies = [ - "cfg-if", - "crc32fast", - "libc", - "miniz-sys", - "miniz_oxide", -] - [[package]] name = "fnv" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" -[[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" @@ -567,36 +174,115 @@ checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" [[package]] name = "futures" -version = "0.1.29" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" +checksum = "5c329ae8753502fb44ae4fc2b622fa2a94652c41e795143765ba0927f92ab780" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] [[package]] -name = "futures-cpupool" -version = "0.1.8" +name = "futures-channel" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" +checksum = "f0c77d04ce8edd9cb903932b608268b3fffec4163dc053b3b402bf47eac1f1a8" dependencies = [ - "futures", - "num_cpus", + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a" + +[[package]] +name = "futures-io" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a638959aa96152c7a4cddf50fcb1e3fede0583b27157c26e67d6f99904090dc6" + +[[package]] +name = "futures-sink" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3466821b4bc114d95b087b850a724c6f83115e929bc88f1fa98a3304a944c8a6" + +[[package]] +name = "futures-task" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b0a34e53cf6cdcd0178aa573aed466b646eb3db769570841fda0c7ede375a27" + +[[package]] +name = "futures-util" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5" +dependencies = [ + "futures-core", + "futures-sink", + "futures-task", + "pin-utils", +] + +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +dependencies = [ + "typenum", ] [[package]] name = "h2" -version = "0.1.26" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5b34c246847f938a410a03c5458c7fee2274436675e76d8b903c08efc29c462" +checksum = "377038bf3c89d18d6ca1431e7a5027194fbd724ca10592b9487ede5e8e144f42" dependencies = [ - "byteorder", "bytes", "fnv", - "futures", + "futures-core", + "futures-sink", + "futures-util", "http", "indexmap", "log", "slab", - "string", - "tokio-io", + "tokio", + "tokio-util", +] + +[[package]] +name = "headers" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed18eb2459bf1a09ad2d6b1547840c3e5e62882fa09b9a6a20b1de8e3228848f" +dependencies = [ + "base64", + "bitflags", + "bytes", + "headers-core", + "http", + "mime", + "sha-1", + "time", +] + +[[package]] +name = "headers-core" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +dependencies = [ + "http", ] [[package]] @@ -608,28 +294,27 @@ dependencies = [ "libc", ] -[[package]] -name = "hostname" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" -dependencies = [ - "libc", - "match_cfg", - "winapi 0.3.8", -] - [[package]] name = "http" -version = "0.1.21" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6ccf5ede3a895d8856620237b2f02972c1bbc78d2965ad7fe8838d4a0ed41f0" +checksum = "28d569972648b2c512421b5f2a405ad6ac9666547189d0c5477a3f200f3e02f9" dependencies = [ "bytes", "fnv", "itoa", ] +[[package]] +name = "http-body" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" +dependencies = [ + "bytes", + "http", +] + [[package]] name = "httparse" version = "1.3.4" @@ -637,10 +322,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" [[package]] -name = "idna" -version = "0.1.5" +name = "hyper" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" +checksum = "96816e1d921eca64d208a85aab4f7798455a8e34229ee5a88c935bdee1b78b14" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "log", + "net2", + "pin-project", + "time", + "tokio", + "tower-service", + "want", +] + +[[package]] +name = "idna" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" dependencies = [ "matches", "unicode-bidi", @@ -653,7 +362,7 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292" dependencies = [ - "autocfg 1.0.0", + "autocfg", ] [[package]] @@ -665,19 +374,6 @@ dependencies = [ "libc", ] -[[package]] -name = "ipconfig" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f7eadeaf4b52700de180d147c4805f199854600b36faa963d91114827b2ffc" -dependencies = [ - "error-chain", - "socket2", - "widestring", - "winapi 0.3.8", - "winreg", -] - [[package]] name = "itoa" version = "0.4.5" @@ -694,55 +390,18 @@ dependencies = [ "winapi-build", ] -[[package]] -name = "language-tags" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" - [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -[[package]] -name = "lazycell" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" - [[package]] name = "libc" version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" -[[package]] -name = "linked-hash-map" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" - -[[package]] -name = "lock_api" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" -dependencies = [ - "owning_ref", - "scopeguard 0.3.3", -] - -[[package]] -name = "lock_api" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" -dependencies = [ - "scopeguard 1.1.0", -] - [[package]] name = "log" version = "0.4.8" @@ -752,48 +411,18 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "lru-cache" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" -dependencies = [ - "linked-hash-map", -] - -[[package]] -name = "match_cfg" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" - [[package]] name = "matches" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" -[[package]] -name = "maybe-uninit" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" - [[package]] name = "memchr" version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" -[[package]] -name = "memoffset" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4fc2c02a7e374099d4ee95a193111f72d2110197fe200272371758f6c3643d8" -dependencies = [ - "autocfg 1.0.0", -] - [[package]] name = "mime" version = "0.3.16" @@ -810,30 +439,11 @@ dependencies = [ "unicase", ] -[[package]] -name = "miniz-sys" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9e3ae51cea1576ceba0dde3d484d30e6e5b86dee0b2d412fe3a16a15c98202" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "miniz_oxide" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa679ff6578b1cddee93d7e82e263b94a575e0bfced07284eb0c037c1d2416a5" -dependencies = [ - "adler32", -] - [[package]] name = "mio" -version = "0.6.21" +version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f" +checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" dependencies = [ "cfg-if", "fuchsia-zircon", @@ -850,9 +460,9 @@ dependencies = [ [[package]] name = "mio-uds" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" +checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" dependencies = [ "iovec", "libc", @@ -873,25 +483,15 @@ dependencies = [ [[package]] name = "net2" -version = "0.2.33" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" +checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7" dependencies = [ "cfg-if", "libc", "winapi 0.3.8", ] -[[package]] -name = "nom" -version = "4.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" -dependencies = [ - "memchr", - "version_check 0.1.5", -] - [[package]] name = "num_cpus" version = "1.13.0" @@ -903,100 +503,56 @@ dependencies = [ ] [[package]] -name = "owning_ref" -version = "0.4.1" +name = "opaque-debug" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff55baddef9e4ad00f88b6c743a2a8062d4c6ade126c2a528644b8e444d52ce" -dependencies = [ - "stable_deref_trait", -] - -[[package]] -name = "parking_lot" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337" -dependencies = [ - "lock_api 0.1.5", - "parking_lot_core 0.4.0", -] - -[[package]] -name = "parking_lot" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" -dependencies = [ - "lock_api 0.3.4", - "parking_lot_core 0.6.2", - "rustc_version", -] - -[[package]] -name = "parking_lot_core" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" -dependencies = [ - "libc", - "rand 0.6.5", - "rustc_version", - "smallvec 0.6.13", - "winapi 0.3.8", -] - -[[package]] -name = "parking_lot_core" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" -dependencies = [ - "cfg-if", - "cloudabi", - "libc", - "redox_syscall", - "rustc_version", - "smallvec 0.6.13", - "winapi 0.3.8", -] +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" [[package]] name = "percent-encoding" -version = "1.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] -name = "proc-macro2" -version = "0.4.30" +name = "pin-project" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +checksum = "36e3dcd42688c05a66f841d22c5d8390d9a5d4c9aaf57b9285eae4900a080063" dependencies = [ - "unicode-xid 0.1.0", + "pin-project-internal", ] [[package]] -name = "proc-macro2" -version = "1.0.10" +name = "pin-project-internal" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" +checksum = "f4d7346ac577ff1296e06a418e7618e22655bae834d4970cb6e39d6da8119969" dependencies = [ - "unicode-xid 0.2.0", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "quick-error" -version = "1.2.3" +name = "pin-project-lite" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +checksum = "237844750cfbb86f67afe27eee600dfbbcb6188d734139b534cbfbf4f96792ae" [[package]] -name = "quote" -version = "0.6.13" +name = "pin-utils" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "proc-macro2" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8872cf6f48eee44265156c111456a700ab3483686b3f96df4cf5481c89157319" dependencies = [ - "proc-macro2 0.4.30", + "unicode-xid", ] [[package]] @@ -1005,196 +561,7 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c1f4b0efa5fc5e8ceb705136bfee52cfdb6a4e3509f770b478cd6ed434232a7" dependencies = [ - "proc-macro2 1.0.10", -] - -[[package]] -name = "rand" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" -dependencies = [ - "cloudabi", - "fuchsia-cprng", - "libc", - "rand_core 0.3.1", - "winapi 0.3.8", -] - -[[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", - "rand_core 0.4.2", - "rand_hc", - "rand_isaac", - "rand_jitter", - "rand_os", - "rand_pcg", - "rand_xorshift", - "winapi 0.3.8", -] - -[[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_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_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_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.8", -] - -[[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.8", -] - -[[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_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.1.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" - -[[package]] -name = "regex" -version = "1.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6020f034922e3194c711b82a627453881bc4682166cabb07134a10c26ba7692" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", - "thread_local", -] - -[[package]] -name = "regex-syntax" -version = "0.6.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" - -[[package]] -name = "resolv-conf" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11834e137f3b14e309437a8276714eed3a80d1ef894869e510f2c0c0b98b9f4a" -dependencies = [ - "hostname", - "quick-error", -] - -[[package]] -name = "ring" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c4db68a2e35f3497146b7e4563df7d4773a2433230c5e4b448328e31740458a" -dependencies = [ - "cc", - "lazy_static", - "libc", - "untrusted", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" - -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver", + "proc-macro2", ] [[package]] @@ -1204,37 +571,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1" [[package]] -name = "safemem" -version = "0.3.3" +name = "scoped-tls" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" - -[[package]] -name = "scopeguard" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" [[package]] name = "serde" @@ -1251,9 +591,9 @@ version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" dependencies = [ - "proc-macro2 1.0.10", - "quote 1.0.4", - "syn 1.0.18", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -1269,9 +609,9 @@ dependencies = [ [[package]] name = "serde_urlencoded" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "642dd69105886af2efd227f75a520ec9b44a820d65bc133a9131f7d229fd165a" +checksum = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97" dependencies = [ "dtoa", "itoa", @@ -1280,10 +620,16 @@ dependencies = [ ] [[package]] -name = "sha1" -version = "0.6.0" +name = "sha-1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" +checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" +dependencies = [ + "block-buffer", + "digest", + "fake-simd", + "opaque-debug", +] [[package]] name = "signal-hook-registry" @@ -1301,86 +647,27 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" -[[package]] -name = "smallvec" -version = "0.6.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" -dependencies = [ - "maybe-uninit", -] - [[package]] name = "smallvec" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4" -[[package]] -name = "socket2" -version = "0.3.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "winapi 0.3.8", -] - -[[package]] -name = "stable_deref_trait" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" - -[[package]] -name = "string" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d" -dependencies = [ - "bytes", -] - [[package]] name = "strsim" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" -[[package]] -name = "syn" -version = "0.15.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" -dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "unicode-xid 0.1.0", -] - [[package]] name = "syn" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "410a7488c0a728c7ceb4ad59b9567eb4053d02e8cc7f5c0e0eeeb39518369213" dependencies = [ - "proc-macro2 1.0.10", - "quote 1.0.4", - "unicode-xid 0.2.0", -] - -[[package]] -name = "synstructure" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" -dependencies = [ - "proc-macro2 1.0.10", - "quote 1.0.4", - "syn 1.0.18", - "unicode-xid 0.2.0", + "proc-macro2", + "quote", + "unicode-xid", ] [[package]] @@ -1392,15 +679,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "thread_local" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" -dependencies = [ - "lazy_static", -] - [[package]] name = "time" version = "0.1.43" @@ -1413,201 +691,50 @@ dependencies = [ [[package]] name = "tokio" -version = "0.1.22" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" +checksum = "05c1d570eb1a36f0345a5ce9c6c6e665b70b73d11236912c0b477616aeec47b1" dependencies = [ "bytes", - "futures", - "mio", - "num_cpus", - "tokio-codec", - "tokio-current-thread", - "tokio-executor", - "tokio-fs", - "tokio-io", - "tokio-reactor", - "tokio-sync", - "tokio-tcp", - "tokio-threadpool", - "tokio-timer", - "tokio-udp", - "tokio-uds", -] - -[[package]] -name = "tokio-codec" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25b2998660ba0e70d18684de5d06b70b70a3a747469af9dea7618cc59e75976b" -dependencies = [ - "bytes", - "futures", - "tokio-io", -] - -[[package]] -name = "tokio-current-thread" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e" -dependencies = [ - "futures", - "tokio-executor", -] - -[[package]] -name = "tokio-executor" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" -dependencies = [ - "crossbeam-utils 0.7.2", - "futures", -] - -[[package]] -name = "tokio-fs" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "297a1206e0ca6302a0eed35b700d292b275256f596e2f3fea7729d5e629b6ff4" -dependencies = [ - "futures", - "tokio-io", - "tokio-threadpool", -] - -[[package]] -name = "tokio-io" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" -dependencies = [ - "bytes", - "futures", - "log", -] - -[[package]] -name = "tokio-reactor" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351" -dependencies = [ - "crossbeam-utils 0.7.2", - "futures", + "fnv", + "futures-core", + "iovec", "lazy_static", - "log", - "mio", - "num_cpus", - "parking_lot 0.9.0", - "slab", - "tokio-executor", - "tokio-io", - "tokio-sync", -] - -[[package]] -name = "tokio-signal" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c34c6e548f101053321cba3da7cbb87a610b85555884c41b07da2eb91aff12" -dependencies = [ - "futures", "libc", + "memchr", "mio", "mio-uds", + "num_cpus", + "pin-project-lite", "signal-hook-registry", - "tokio-executor", - "tokio-io", - "tokio-reactor", + "slab", + "tokio-macros", "winapi 0.3.8", ] [[package]] -name = "tokio-sync" -version = "0.1.8" +name = "tokio-macros" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee" +checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389" dependencies = [ - "fnv", - "futures", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "tokio-tcp" -version = "0.1.4" +name = "tokio-util" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72" +checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" dependencies = [ "bytes", - "futures", - "iovec", - "mio", - "tokio-io", - "tokio-reactor", -] - -[[package]] -name = "tokio-threadpool" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df720b6581784c118f0eb4310796b12b1d242a7eb95f716a8367855325c25f89" -dependencies = [ - "crossbeam-deque", - "crossbeam-queue", - "crossbeam-utils 0.7.2", - "futures", - "lazy_static", + "futures-core", + "futures-sink", "log", - "num_cpus", - "slab", - "tokio-executor", -] - -[[package]] -name = "tokio-timer" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296" -dependencies = [ - "crossbeam-utils 0.7.2", - "futures", - "slab", - "tokio-executor", -] - -[[package]] -name = "tokio-udp" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2a0b10e610b39c38b031a2fcab08e4b82f16ece36504988dcbd81dbba650d82" -dependencies = [ - "bytes", - "futures", - "log", - "mio", - "tokio-codec", - "tokio-io", - "tokio-reactor", -] - -[[package]] -name = "tokio-uds" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5076db410d6fdc6523df7595447629099a1fdc47b3d9f896220780fa48faf798" -dependencies = [ - "bytes", - "futures", - "iovec", - "libc", - "log", - "mio", - "mio-uds", - "tokio-codec", - "tokio-io", - "tokio-reactor", + "pin-project-lite", + "tokio", ] [[package]] @@ -1621,98 +748,36 @@ dependencies = [ [[package]] name = "tower-service" -version = "0.1.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b32f72af77f1bfe3d3d4da8516a238ebe7039b51dd8637a09841ac7f16d2c987" -dependencies = [ - "futures", -] +checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860" [[package]] -name = "trust-dns-proto" -version = "0.5.0" +name = "try-lock" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0838272e89f1c693b4df38dc353412e389cf548ceed6f9fd1af5a8d6e0e7cf74" -dependencies = [ - "byteorder", - "failure", - "futures", - "idna", - "lazy_static", - "log", - "rand 0.5.6", - "smallvec 0.6.13", - "socket2", - "tokio-executor", - "tokio-io", - "tokio-reactor", - "tokio-tcp", - "tokio-timer", - "tokio-udp", - "url", -] +checksum = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" [[package]] -name = "trust-dns-proto" -version = "0.6.3" +name = "typenum" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09144f0992b0870fa8d2972cc069cbf1e3c0fda64d1f3d45c4d68d0e0b52ad4e" -dependencies = [ - "byteorder", - "failure", - "futures", - "idna", - "lazy_static", - "log", - "rand 0.5.6", - "smallvec 0.6.13", - "socket2", - "tokio-executor", - "tokio-io", - "tokio-reactor", - "tokio-tcp", - "tokio-timer", - "tokio-udp", - "url", -] - -[[package]] -name = "trust-dns-resolver" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9f877f7a1ad821ab350505e1f1b146a4960402991787191d6d8cab2ce2de2c" -dependencies = [ - "cfg-if", - "failure", - "futures", - "ipconfig", - "lazy_static", - "log", - "lru-cache", - "resolv-conf", - "smallvec 0.6.13", - "tokio", - "trust-dns-proto 0.6.3", -] +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" [[package]] name = "udpt-rs" -version = "3.0.1" +version = "3.0.0-alpha" dependencies = [ - "actix-net", - "actix-web", "binascii", "bincode", - "bzip2", "clap", "fern", - "futures", - "lazy_static", "log", - "num_cpus", "serde", "serde_json", + "tokio", "toml", + "warp", ] [[package]] @@ -1721,7 +786,7 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" dependencies = [ - "version_check 0.9.1", + "version_check", ] [[package]] @@ -1739,7 +804,7 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" dependencies = [ - "smallvec 1.4.0", + "smallvec", ] [[package]] @@ -1748,75 +813,28 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" -[[package]] -name = "unicode-xid" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" - [[package]] name = "unicode-xid" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" -[[package]] -name = "untrusted" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cd1f4b4e96b46aeb8d4855db4a7a9bd96eeeb5c6a1ab54593328761642ce2f" - [[package]] name = "url" -version = "1.7.2" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" +checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" dependencies = [ - "encoding", "idna", "matches", "percent-encoding", ] [[package]] -name = "uuid" -version = "0.7.4" +name = "urlencoding" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90dbc611eb48397705a6b0f6e917da23ae517e4d127123d2cf7674206627d32a" -dependencies = [ - "rand 0.6.5", -] - -[[package]] -name = "v_escape" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "660b101c07b5d0863deb9e7fb3138777e858d6d2a79f9e6049a27d1cc77c6da6" -dependencies = [ - "v_escape_derive", -] - -[[package]] -name = "v_escape_derive" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ca2a14bc3fc5b64d188b087a7d3a927df87b152e941ccfbc66672e20c467ae" -dependencies = [ - "nom", - "proc-macro2 1.0.10", - "quote 1.0.4", - "syn 1.0.18", -] - -[[package]] -name = "v_htmlescape" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33e939c0d8cf047514fb6ba7d5aac78bc56677a6938b2ee67000b91f2e97e41" -dependencies = [ - "cfg-if", - "v_escape", -] +checksum = "3df3561629a8bb4c57e5a2e4c43348d9e29c7c29d9b1c4c1f47166deca8f37ed" [[package]] name = "vec_map" @@ -1824,12 +842,6 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" -[[package]] -name = "version_check" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" - [[package]] name = "version_check" version = "0.9.1" @@ -1837,10 +849,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" [[package]] -name = "widestring" +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "warp" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7157704c2e12e3d2189c507b7482c52820a16dfa4465ba91add92f266667cadb" +checksum = "54cd1e2b3eb3539284d88b76a9afcf5e20f2ef2fab74db5b21a1c30d7d945e82" +dependencies = [ + "bytes", + "futures", + "headers", + "http", + "hyper", + "log", + "mime", + "mime_guess", + "pin-project", + "scoped-tls", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tower-service", + "urlencoding", +] [[package]] name = "winapi" @@ -1876,15 +916,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "winreg" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a27a759395c1195c4cc5cda607ef6f8f6498f64e78f7900f5de0a127a424704a" -dependencies = [ - "winapi 0.3.8", -] - [[package]] name = "ws2_32-sys" version = "0.2.1" diff --git a/Cargo.toml b/Cargo.toml index c324455..3d0106f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,15 +11,11 @@ lto = "fat" [dependencies] serde = {version = "1.0", features = ["derive"]} bincode = "1.2" -actix-web = "0.7" -actix-net = "0.2" +warp = {version = "0.2", default-features = false} +tokio = {version = "0.2", features = ["macros", "net", "rt-threaded", "fs", "sync", "blocking", "signal"]} binascii = "0.1" toml = "0.5" clap = "2.33" log = "0.4" fern = "0.6" -num_cpus = "1.13" serde_json = "1.0" -bzip2 = "0.3" -futures = "0.1" -lazy_static = "1.4" diff --git a/src/config.rs b/src/config.rs index 19f273e..2f1f3e7 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,119 +1,121 @@ -use std; -use std::collections::HashMap; -use toml; -pub use crate::tracker::TrackerMode; -use serde::Deserialize; - -#[derive(Deserialize)] -pub struct UDPConfig { - bind_address: String, - announce_interval: u32, -} - -impl UDPConfig { - pub fn get_address(&self) -> &str { - self.bind_address.as_str() - } - - pub fn get_announce_interval(&self) -> u32 { - self.announce_interval - } -} - -#[derive(Deserialize)] -pub struct HTTPConfig { - bind_address: String, - access_tokens: HashMap, -} - -impl HTTPConfig { - pub fn get_address(&self) -> &str { - self.bind_address.as_str() - } - - pub fn get_access_tokens(&self) -> &HashMap { - &self.access_tokens - } -} - -#[derive(Deserialize)] -pub struct Configuration { - mode: TrackerMode, - udp: UDPConfig, - http: Option, - log_level: Option, - db_path: Option, - cleanup_interval: Option, -} - -#[derive(Debug)] -pub enum ConfigError { - IOError(std::io::Error), - ParseError(toml::de::Error), -} - -impl std::fmt::Display for ConfigError { - fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - ConfigError::IOError(e) => e.fmt(formatter), - ConfigError::ParseError(e) => e.fmt(formatter), - } - } -} -impl std::error::Error for ConfigError {} - -impl Configuration { - pub fn load(data: &[u8]) -> Result { - toml::from_slice(data) - } - - pub fn load_file(path: &str) -> Result { - match std::fs::read(path) { - Err(e) => Err(ConfigError::IOError(e)), - Ok(data) => match Self::load(data.as_slice()) { - Ok(cfg) => Ok(cfg), - Err(e) => Err(ConfigError::ParseError(e)), - }, - } - } - - pub fn get_mode(&self) -> &TrackerMode { - &self.mode - } - - pub fn get_udp_config(&self) -> &UDPConfig { - &self.udp - } - - pub fn get_log_level(&self) -> &Option { - &self.log_level - } - - pub fn get_http_config(&self) -> &Option { - &self.http - } - - pub fn get_db_path(&self) -> &Option { - &self.db_path - } - - pub fn get_cleanup_interval(&self) -> &Option { - &self.cleanup_interval - } -} - -impl Default for Configuration { - fn default() -> Configuration { - Configuration { - log_level: None, - mode: TrackerMode::DynamicMode, - udp: UDPConfig { - announce_interval: 120, - bind_address: String::from("0.0.0.0:6969"), - }, - http: None, - db_path: None, - cleanup_interval: None, - } - } -} +pub use crate::tracker::TrackerMode; +use serde::Deserialize; +use std; +use std::collections::HashMap; +use toml; + +#[derive(Deserialize)] +pub struct UDPConfig { + bind_address: String, + announce_interval: u32, +} + +impl UDPConfig { + pub fn get_address(&self) -> &str { + self.bind_address.as_str() + } + + pub fn get_announce_interval(&self) -> u32 { + self.announce_interval + } +} + +#[derive(Deserialize)] +pub struct HTTPConfig { + bind_address: String, + access_tokens: HashMap, +} + +impl HTTPConfig { + pub fn get_address(&self) -> &str { + self.bind_address.as_str() + } + + pub fn get_access_tokens(&self) -> &HashMap { + &self.access_tokens + } +} + +#[derive(Deserialize)] +pub struct Configuration { + mode: TrackerMode, + udp: UDPConfig, + http: Option, + log_level: Option, + db_path: Option, + cleanup_interval: Option, +} + +#[derive(Debug)] +pub enum ConfigError { + IOError(std::io::Error), + ParseError(toml::de::Error), +} + +impl std::fmt::Display for ConfigError { + fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + ConfigError::IOError(e) => e.fmt(formatter), + ConfigError::ParseError(e) => e.fmt(formatter), + } + } +} +impl std::error::Error for ConfigError {} + +impl Configuration { + pub fn load(data: &[u8]) -> Result { + toml::from_slice(data) + } + + pub fn load_file(path: &str) -> Result { + match std::fs::read(path) { + Err(e) => Err(ConfigError::IOError(e)), + Ok(data) => { + match Self::load(data.as_slice()) { + Ok(cfg) => Ok(cfg), + Err(e) => Err(ConfigError::ParseError(e)), + } + } + } + } + + pub fn get_mode(&self) -> &TrackerMode { + &self.mode + } + + pub fn get_udp_config(&self) -> &UDPConfig { + &self.udp + } + + pub fn get_log_level(&self) -> &Option { + &self.log_level + } + + pub fn get_http_config(&self) -> Option<&HTTPConfig> { + self.http.as_ref() + } + + pub fn get_db_path(&self) -> &Option { + &self.db_path + } + + pub fn get_cleanup_interval(&self) -> Option { + self.cleanup_interval + } +} + +impl Default for Configuration { + fn default() -> Configuration { + Configuration { + log_level: None, + mode: TrackerMode::DynamicMode, + udp: UDPConfig { + announce_interval: 120, + bind_address: String::from("0.0.0.0:6969"), + }, + http: None, + db_path: None, + cleanup_interval: None, + } + } +} diff --git a/src/main.rs b/src/main.rs index 1cd41f3..3b84fc1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,8 @@ #![forbid(unsafe_code)] use clap; -use log::{trace, warn, info, debug, error}; use fern; -use num_cpus; -use lazy_static::lazy_static; +use log::{error, info, trace, warn}; mod config; mod server; @@ -15,25 +13,23 @@ mod webserver; use config::Configuration; use std::process::exit; -lazy_static!{ - static ref term_mutex: std::sync::Arc = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)); -} - fn setup_logging(cfg: &Configuration) { let log_level = match cfg.get_log_level() { None => log::LevelFilter::Info, - Some(level) => match level.as_str() { - "off" => log::LevelFilter::Off, - "trace" => log::LevelFilter::Trace, - "debug" => log::LevelFilter::Debug, - "info" => log::LevelFilter::Info, - "warn" => log::LevelFilter::Warn, - "error" => log::LevelFilter::Error, - _ => { - eprintln!("udpt: unknown log level encountered '{}'", level.as_str()); - exit(-1); + Some(level) => { + match level.as_str() { + "off" => log::LevelFilter::Off, + "trace" => log::LevelFilter::Trace, + "debug" => log::LevelFilter::Debug, + "info" => log::LevelFilter::Info, + "warn" => log::LevelFilter::Warn, + "error" => log::LevelFilter::Error, + _ => { + eprintln!("udpt: unknown log level encountered '{}'", level.as_str()); + exit(-1); + } } - }, + } }; if let Err(err) = fern::Dispatch::new() @@ -59,11 +55,8 @@ fn setup_logging(cfg: &Configuration) { info!("logging initialized."); } -fn signal_termination() { - term_mutex.store(true, std::sync::atomic::Ordering::Relaxed); -} - -fn main() { +#[tokio::main] +async fn main() { let parser = clap::App::new(env!("CARGO_PKG_NAME")) .about(env!("CARGO_PKG_DESCRIPTION")) .author(env!("CARGO_PKG_AUTHORS")) @@ -95,17 +88,19 @@ fn main() { if !file_path.exists() { warn!("database file \"{}\" doesn't exist.", path); tracker::TorrentTracker::new(cfg.get_mode().clone()) - } - else { - let mut input_file = match std::fs::File::open(file_path) { + } else { + let mut input_file = match tokio::fs::File::open(file_path).await { Ok(v) => v, Err(err) => { error!("failed to open \"{}\". error: {}", path.as_str(), err); panic!("error opening file. check logs."); } }; - match tracker::TorrentTracker::load_database(cfg.get_mode().clone(), &mut input_file) { - Ok(v) => v, + match tracker::TorrentTracker::load_database(cfg.get_mode().clone(), &mut input_file).await { + Ok(v) => { + info!("database loaded."); + v + } Err(err) => { error!("failed to load database. error: {}", err); panic!("failed to load database. check logs."); @@ -116,111 +111,67 @@ fn main() { None => tracker::TorrentTracker::new(cfg.get_mode().clone()), }; - let mut threads = Vec::new(); - let tracker = std::sync::Arc::new(tracker_obj); - let http_server = if cfg.get_http_config().is_some() { - let http_tracker_ref = tracker.clone(); - let cfg_ref = cfg.clone(); + if cfg.get_http_config().is_some() { + let https_tracker = tracker.clone(); + let http_cfg = cfg.clone(); - Some(webserver::WebServer::new(http_tracker_ref, cfg_ref)) - } else { - None - }; + info!("Starting http server"); + tokio::spawn(async move { + let http_cfg = http_cfg.get_http_config().unwrap(); + let bind_addr = http_cfg.get_address(); + let tokens = http_cfg.get_access_tokens(); - let udp_server = std::sync::Arc::new(server::UDPTracker::new(cfg.clone(), tracker.clone()).unwrap()); + let server = webserver::build_server(https_tracker, tokens.clone()); + server.bind(bind_addr.parse::().unwrap()).await; + }); + } + + let mut udp_server = server::UDPTracker::new(cfg.clone(), tracker.clone()) + .await + .expect("failed to bind udp socket"); trace!("Waiting for UDP packets"); - let logical_cpus = num_cpus::get(); - for i in 0..logical_cpus { - debug!("starting thread {}/{}", i + 1, logical_cpus); - let server_handle = udp_server.clone(); - let thread_term_ref = term_mutex.clone(); - threads.push(std::thread::spawn(move || loop { - match server_handle.accept_packet() { - Err(e) => { - if thread_term_ref.load(std::sync::atomic::Ordering::Relaxed) == true { - debug!("Thread terminating..."); - break; - } - match e.kind() { - std::io::ErrorKind::TimedOut => {}, - std::io::ErrorKind::WouldBlock => {}, - _ => { - error!("Failed to process packet. {}", e); - } - } - } - Ok(_) => {} + let udp_server = tokio::spawn(async move { + loop { + if let Err(err) = udp_server.accept_packet().await { + eprintln!("error: {}", err); } - })); - } - - match cfg.get_db_path() { - Some(db_path) => { - let db_p = db_path.clone(); - let tracker_clone = tracker.clone(); - let cleanup_interval = match *cfg.get_cleanup_interval() { - Some(v) => v, - None => 10 * 60, - }; - - let thread_term_mutex = term_mutex.clone(); - threads.push(std::thread::spawn(move || { - let timeout = std::time::Duration::new(cleanup_interval, 0); - - let timeout_start = std::time::Instant::now(); - let mut timeout_remaining = timeout; - loop { - std::thread::park_timeout(std::time::Duration::new(cleanup_interval, 0)); - - if thread_term_mutex.load(std::sync::atomic::Ordering::Relaxed) { - debug!("Maintenance thread terminating."); - break; - } - - let elapsed = std::time::Instant::now() - timeout_start; - if elapsed < timeout_remaining { - timeout_remaining = timeout - elapsed; - continue; - } - else { - timeout_remaining = timeout; - } - - debug!("periodically saving database."); - tracker_clone.periodic_task(db_p.as_str()); - debug!("database saved."); - } - })); - }, - None => {} - } - - loop { - if term_mutex.load(std::sync::atomic::Ordering::Relaxed) { - // termination signaled. start cleanup. - break; } - std::thread::sleep(std::time::Duration::from_secs(1)); - } - - match http_server { - Some(v) => v.shutdown(), - None => {}, - }; - - while !threads.is_empty() { - if let Some(thread) = threads.pop() { - thread.thread().unpark(); - let _ = thread.join(); - } - } + }); + let weak_tracker = std::sync::Arc::downgrade(&tracker); if let Some(db_path) = cfg.get_db_path() { - info!("running final cleanup & saving database..."); - tracker.periodic_task(db_path.as_str()); + let db_path = db_path.clone(); + let interval = cfg.get_cleanup_interval().unwrap_or(600); + + tokio::spawn(async move { + let interval = std::time::Duration::from_secs(interval); + let mut interval = tokio::time::interval(interval); + interval.tick().await; // first tick is immediate... + loop { + interval.tick().await; + if let Some(tracker) = weak_tracker.upgrade() { + tracker.periodic_task(&db_path).await; + } else { + break; + } + } + }); } + + let ctrl_c = tokio::signal::ctrl_c(); + + tokio::select! { + _ = udp_server => { warn!("udp server exited.") }, + _ = ctrl_c => { info!("CTRL-C, exiting...") }, + } + + if let Some(path) = cfg.get_db_path() { + info!("saving database..."); + tracker.periodic_task(path).await; + } + info!("goodbye."); } diff --git a/src/server.rs b/src/server.rs index edeb162..e505d3a 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,8 +1,9 @@ +use log::{debug, error, trace}; use std; use std::io::Write; -use std::net::{SocketAddr, UdpSocket}; +use std::net::SocketAddr; use std::sync::Arc; -use log::{error, trace, debug}; +use tokio::net::UdpSocket; use bincode; use serde::{Deserialize, Serialize}; @@ -107,31 +108,18 @@ struct UDPScrapeResponseEntry { } pub struct UDPTracker { - server: std::net::UdpSocket, + server: UdpSocket, tracker: std::sync::Arc, config: Arc, } impl UDPTracker { - pub fn new( - config: Arc, - tracker: std::sync::Arc + pub async fn new( + config: Arc, tracker: std::sync::Arc, ) -> Result { let cfg = config.clone(); - let server = match UdpSocket::bind(cfg.get_udp_config().get_address()) { - Ok(s) => s, - Err(e) => { - return Err(e); - } - }; - - match server.set_read_timeout(Some(std::time::Duration::from_secs(1))) { - Ok(_) => {}, - Err(err) => { - error!("Failed to set read timeout on socket; will try to continue anyway. err: {}", err); - } - } + let server = UdpSocket::bind(cfg.get_udp_config().get_address()).await?; Ok(UDPTracker { server, @@ -140,7 +128,8 @@ impl UDPTracker { }) } - fn handle_packet(&self, remote_address: &SocketAddr, payload: &[u8]) { + // TODO: remove `mut` once https://github.com/tokio-rs/tokio/issues/1624 is resolved + async fn handle_packet(&mut self, remote_address: &SocketAddr, payload: &[u8]) { let header: UDPRequestHeader = match unpack(payload) { Some(val) => val, None => { @@ -150,9 +139,9 @@ impl UDPTracker { }; match header.action { - Actions::Connect => self.handle_connect(remote_address, &header, payload), - Actions::Announce => self.handle_announce(remote_address, &header, payload), - Actions::Scrape => self.handle_scrape(remote_address, &header, payload), + Actions::Connect => self.handle_connect(remote_address, &header, payload).await, + Actions::Announce => self.handle_announce(remote_address, &header, payload).await, + Actions::Scrape => self.handle_scrape(remote_address, &header, payload).await, _ => { trace!("invalid action from {}", remote_address); // someone is playing around... ignore request. @@ -161,7 +150,8 @@ impl UDPTracker { } } - fn handle_connect(&self, remote_addr: &SocketAddr, header: &UDPRequestHeader, _payload: &[u8]) { + // TODO: remove `mut` once https://github.com/tokio-rs/tokio/issues/1624 is resolved + async fn handle_connect(&mut self, remote_addr: &SocketAddr, header: &UDPRequestHeader, _payload: &[u8]) { if header.connection_id != PROTOCOL_ID { trace!("Bad protocol magic from {}", remote_addr); return; @@ -178,15 +168,16 @@ impl UDPTracker { connection_id: conn_id, }; - let mut payload_buffer = [0u8; MAX_PACKET_SIZE]; - let mut payload = StackVec::from(&mut payload_buffer); + let mut payload_buffer = vec![0u8; MAX_PACKET_SIZE]; + let mut payload = StackVec::from(payload_buffer.as_mut_slice()); if let Ok(_) = pack_into(&mut payload, &response) { - let _ = self.send_packet(remote_addr, payload.as_slice()); + let _ = self.send_packet(remote_addr, payload.as_slice()).await; } } - fn handle_announce(&self, remote_addr: &SocketAddr, header: &UDPRequestHeader, payload: &[u8]) { + // TODO: remove `mut` once https://github.com/tokio-rs/tokio/issues/1624 is resolved + async fn handle_announce(&mut self, remote_addr: &SocketAddr, header: &UDPRequestHeader, payload: &[u8]) { if header.connection_id != self.get_connection_id(remote_addr) { return; } @@ -205,63 +196,57 @@ impl UDPTracker { let bep41_payload = &payload[plen..]; // TODO: process BEP0041 payload. - trace!( - "BEP0041 payload of {} bytes from {}", - bep41_payload.len(), - remote_addr - ); + trace!("BEP0041 payload of {} bytes from {}", bep41_payload.len(), remote_addr); } } if packet.ip_address != 0 { // TODO: allow configurability of ip address // for now, ignore request. - trace!( - "announce request for other IP ignored. (from {})", - remote_addr - ); + trace!("announce request for other IP ignored. (from {})", remote_addr); return; } let client_addr = SocketAddr::new(remote_addr.ip(), packet.port); let info_hash = packet.info_hash.into(); - match self.tracker.update_torrent_and_get_stats( - &info_hash, - &packet.peer_id, - &client_addr, - packet.uploaded, - packet.downloaded, - packet.left, - packet.event, - ) { + match self + .tracker + .update_torrent_and_get_stats( + &info_hash, + &packet.peer_id, + &client_addr, + packet.uploaded, + packet.downloaded, + packet.left, + packet.event, + ) + .await + { tracker::TorrentStats::Stats { leechers, complete: _, seeders, } => { - let peers = match self.tracker.get_torrent_peers(&info_hash, &client_addr) { + let peers = match self.tracker.get_torrent_peers(&info_hash, &client_addr).await { Some(v) => v, None => { return; } }; - let mut payload_buffer = [0u8; MAX_PACKET_SIZE]; + let mut payload_buffer = vec![0u8; MAX_PACKET_SIZE]; let mut payload = StackVec::from(&mut payload_buffer); - match pack_into( - &mut payload, - &UDPAnnounceResponse { - header: UDPResponseHeader { - action: Actions::Announce, - transaction_id: packet.header.transaction_id, - }, - seeders, - interval: self.config.get_udp_config().get_announce_interval(), - leechers, + match pack_into(&mut payload, &UDPAnnounceResponse { + header: UDPResponseHeader { + action: Actions::Announce, + transaction_id: packet.header.transaction_id, }, - ) { + seeders, + interval: self.config.get_udp_config().get_announce_interval(), + leechers, + }) { Ok(_) => {} Err(_) => { return; @@ -279,24 +264,24 @@ impl UDPTracker { }; let port_hton = client_addr.port().to_be(); - let _ = - payload.write(&[(port_hton & 0xff) as u8, ((port_hton >> 8) & 0xff) as u8]); + let _ = payload.write(&[(port_hton & 0xff) as u8, ((port_hton >> 8) & 0xff) as u8]); } - let _ = self.send_packet(&client_addr, payload.as_slice()); + let _ = self.send_packet(&client_addr, payload.as_slice()).await; } tracker::TorrentStats::TorrentFlagged => { - self.send_error(&client_addr, &packet.header, "torrent flagged."); + self.send_error(&client_addr, &packet.header, "torrent flagged.").await; return; } tracker::TorrentStats::TorrentNotRegistered => { - self.send_error(&client_addr, &packet.header, "torrent not registered."); + self.send_error(&client_addr, &packet.header, "torrent not registered.").await; return; } } } - fn handle_scrape(&self, remote_addr: &SocketAddr, header: &UDPRequestHeader, payload: &[u8]) { + // TODO: remove `mut` once https://github.com/tokio-rs/tokio/issues/1624 is resolved + async fn handle_scrape(&mut self, remote_addr: &SocketAddr, header: &UDPRequestHeader, payload: &[u8]) { if header.connection_id != self.get_connection_id(remote_addr) { return; } @@ -306,16 +291,17 @@ impl UDPTracker { let mut response_buffer = [0u8; 8 + MAX_SCRAPE * 12]; let mut response = StackVec::from(&mut response_buffer); - if pack_into(&mut response, &UDPResponseHeader{ + if pack_into(&mut response, &UDPResponseHeader { action: Actions::Scrape, transaction_id: header.transaction_id, - }).is_err() { + }) + .is_err() + { // not much we can do... error!("failed to encode udp scrape response header."); return; } - // skip first 16 bytes for header... let info_hash_array = &payload[16..]; @@ -323,45 +309,47 @@ impl UDPTracker { trace!("received weird length for scrape info_hash array (!mod20)."); } - let db = self.tracker.get_database(); + { + let db = self.tracker.get_database().await; - for torrent_index in 0..MAX_SCRAPE { - let info_hash_start = torrent_index * 20; - let info_hash_end = (torrent_index + 1) * 20; + for torrent_index in 0..MAX_SCRAPE { + let info_hash_start = torrent_index * 20; + let info_hash_end = (torrent_index + 1) * 20; - if info_hash_end > info_hash_array.len() { - break; - } - - let info_hash = &info_hash_array[info_hash_start..info_hash_end]; - let ih = tracker::InfoHash::from(info_hash); - let result = match db.get(&ih) { - Some(torrent_info) => { - let (seeders, completed, leechers) = torrent_info.get_stats(); - - UDPScrapeResponseEntry{ - seeders, - completed, - leechers, - } - }, - None => { - UDPScrapeResponseEntry{ - seeders: 0, - completed: 0, - leechers: 0, - } + if info_hash_end > info_hash_array.len() { + break; } - }; - if pack_into(&mut response, &result).is_err() { - debug!("failed to encode scrape entry."); - return; + let info_hash = &info_hash_array[info_hash_start..info_hash_end]; + let ih = tracker::InfoHash::from(info_hash); + let result = match db.get(&ih) { + Some(torrent_info) => { + let (seeders, completed, leechers) = torrent_info.get_stats(); + + UDPScrapeResponseEntry { + seeders, + completed, + leechers, + } + } + None => { + UDPScrapeResponseEntry { + seeders: 0, + completed: 0, + leechers: 0, + } + } + }; + + if pack_into(&mut response, &result).is_err() { + debug!("failed to encode scrape entry."); + return; + } } } // if sending fails, not much we can do... - let _ = self.send_packet(&remote_addr, &response.as_slice()); + let _ = self.send_packet(&remote_addr, &response.as_slice()).await; } fn get_connection_id(&self, remote_address: &SocketAddr) -> u64 { @@ -371,43 +359,37 @@ impl UDPTracker { } } - fn send_packet( - &self, - remote_addr: &SocketAddr, - payload: &[u8], - ) -> Result { - self.server.send_to(payload, remote_addr) + // TODO: remove `mut` once https://github.com/tokio-rs/tokio/issues/1624 is resolved + async fn send_packet(&mut self, remote_addr: &SocketAddr, payload: &[u8]) -> Result { + self.server.send_to(payload, remote_addr).await } - fn send_error(&self, remote_addr: &SocketAddr, header: &UDPRequestHeader, error_msg: &str) { - let mut payload_buffer = [0u8; MAX_PACKET_SIZE]; + // TODO: remove `mut` once https://github.com/tokio-rs/tokio/issues/1624 is resolved + async fn send_error(&mut self, remote_addr: &SocketAddr, header: &UDPRequestHeader, error_msg: &str) { + let mut payload_buffer = vec![0u8; MAX_PACKET_SIZE]; let mut payload = StackVec::from(&mut payload_buffer); - if let Ok(_) = pack_into( - &mut payload, - &UDPResponseHeader { - transaction_id: header.transaction_id, - action: Actions::Error, - }, - ) { + if let Ok(_) = pack_into(&mut payload, &UDPResponseHeader { + transaction_id: header.transaction_id, + action: Actions::Error, + }) { let msg_bytes = Vec::from(error_msg.as_bytes()); payload.extend(msg_bytes); - let _ = self.send_packet(remote_addr, payload.as_slice()); + let _ = self.send_packet(remote_addr, payload.as_slice()).await; } } - pub fn accept_packet(&self) -> Result<(), std::io::Error> { - let mut packet = [0u8; MAX_PACKET_SIZE]; - match self.server.recv_from(&mut packet) { - Ok((size, remote_address)) => { - debug!("Received {} bytes from {}", size, remote_address); - self.handle_packet(&remote_address, &packet[..size]); + // TODO: remove `mut` for `accept_packet`, and spawn once https://github.com/tokio-rs/tokio/issues/1624 is resolved + pub async fn accept_packet(&mut self) -> Result<(), std::io::Error> { + let mut packet = vec![0u8; MAX_PACKET_SIZE]; + let (size, remote_address) = self.server.recv_from(packet.as_mut_slice()).await?; - Ok(()) - } - Err(e) => Err(e), - } + // tokio::spawn(async { + debug!("Received {} bytes from {}", size, remote_address); + self.handle_packet(&remote_address, &packet[..size]).await; + // }); + Ok(()) } } @@ -426,10 +408,7 @@ mod tests { assert!(pack_into(&mut payload, &mystruct).is_ok()); assert_eq!(payload.len(), 16); - assert_eq!( - payload.as_slice(), - &[0, 0, 0, 0, 0, 0, 0, 200u8, 0, 0, 0, 0, 0, 1, 47, 203] - ); + assert_eq!(payload.as_slice(), &[0, 0, 0, 0, 0, 0, 0, 200u8, 0, 0, 0, 0, 0, 1, 47, 203]); } #[test] diff --git a/src/stackvec.rs b/src/stackvec.rs index 9021c53..a011273 100644 --- a/src/stackvec.rs +++ b/src/stackvec.rs @@ -10,10 +10,6 @@ impl<'a, T> StackVec<'a, T> { StackVec { data, length: 0 } } - pub fn len(&self) -> usize { - self.length - } - pub fn as_slice(&self) -> &[T] { &self.data[0..self.length] } diff --git a/src/tracker.rs b/src/tracker.rs index fc5e481..5ad33ca 100644 --- a/src/tracker.rs +++ b/src/tracker.rs @@ -1,7 +1,11 @@ - use crate::server::Events; -use serde::{Serialize, Deserialize}; use log::{error, trace}; +use serde::{Deserialize, Serialize}; +use std::borrow::Cow; +use std::collections::BTreeMap; +use tokio::io::{AsyncBufReadExt, AsyncWriteExt}; +use tokio::stream::StreamExt; +use tokio::sync::RwLock; #[derive(Deserialize, Clone, PartialEq)] pub enum TrackerMode { @@ -18,6 +22,7 @@ pub enum TrackerMode { PrivateMode, } +#[derive(Clone)] struct TorrentPeer { ip: std::net::SocketAddr, uploaded: u64, @@ -32,6 +37,27 @@ pub struct InfoHash { info_hash: [u8; 20], } +impl std::fmt::Display for InfoHash { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + let mut chars = [0u8; 40]; + binascii::bin2hex(&self.info_hash, &mut chars).expect("failed to hexlify"); + write!(f, "{}", std::str::from_utf8(&chars).unwrap()) + } +} + +impl std::str::FromStr for InfoHash { + type Err = binascii::ConvertError; + + fn from_str(s: &str) -> Result { + let mut i = Self { info_hash: [0u8; 20] }; + if s.len() != 40 { + return Err(binascii::ConvertError::InvalidInputLength); + } + binascii::hex2bin(s.as_bytes(), &mut i.info_hash)?; + Ok(i) + } +} + impl std::cmp::PartialOrd for InfoHash { fn partial_cmp(&self, other: &InfoHash) -> Option { self.info_hash.partial_cmp(&other.info_hash) @@ -41,9 +67,7 @@ impl std::cmp::PartialOrd for InfoHash { impl std::convert::From<&[u8]> for InfoHash { fn from(data: &[u8]) -> InfoHash { assert_eq!(data.len(), 20); - let mut ret = InfoHash{ - info_hash: [0u8; 20], - }; + let mut ret = InfoHash { info_hash: [0u8; 20] }; ret.info_hash.clone_from_slice(data); return ret; } @@ -58,9 +82,7 @@ impl std::convert::Into for [u8; 20] { impl serde::ser::Serialize for InfoHash { fn serialize(&self, serializer: S) -> Result { let mut buffer = [0u8; 40]; - let bytes_out = binascii::bin2hex(&self.info_hash, &mut buffer) - .ok() - .unwrap(); + let bytes_out = binascii::bin2hex(&self.info_hash, &mut buffer).ok().unwrap(); let str_out = std::str::from_utf8(bytes_out).unwrap(); serializer.serialize_str(str_out) @@ -84,9 +106,7 @@ impl<'v> serde::de::Visitor<'v> for InfoHashVisitor { )); } - let mut res = InfoHash { - info_hash: [0u8; 20], - }; + let mut res = InfoHash { info_hash: [0u8; 20] }; if let Err(_) = binascii::hex2bin(v.as_bytes(), &mut res.info_hash) { return Err(serde::de::Error::invalid_value( @@ -107,7 +127,7 @@ impl<'de> serde::de::Deserialize<'de> for InfoHash { pub type PeerId = [u8; 20]; -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Clone)] pub struct TorrentEntry { is_flagged: bool, @@ -135,28 +155,20 @@ impl TorrentEntry { } pub fn update_peer( - &mut self, - peer_id: &PeerId, - remote_address: &std::net::SocketAddr, - uploaded: u64, - downloaded: u64, - left: u64, + &mut self, peer_id: &PeerId, remote_address: &std::net::SocketAddr, uploaded: u64, downloaded: u64, left: u64, event: Events, ) { let is_seeder = left == 0 && uploaded > 0; let mut was_seeder = false; let mut is_completed = left == 0 && (event as u32) == (Events::Complete as u32); - if let Some(prev) = self.peers.insert( - *peer_id, - TorrentPeer { - updated: std::time::SystemTime::now(), - left, - downloaded, - uploaded, - ip: *remote_address, - event, - }, - ) { + if let Some(prev) = self.peers.insert(*peer_id, TorrentPeer { + updated: std::time::SystemTime::now(), + left, + downloaded, + uploaded, + ip: *remote_address, + event, + }) { was_seeder = prev.left == 0 && prev.uploaded > 0; if is_completed && (prev.event as u32) == (Events::Complete as u32) { @@ -200,13 +212,13 @@ impl TorrentEntry { } struct TorrentDatabase { - torrent_peers: std::sync::RwLock>, + torrent_peers: tokio::sync::RwLock>, } impl Default for TorrentDatabase { fn default() -> Self { TorrentDatabase { - torrent_peers: std::sync::RwLock::new(std::collections::BTreeMap::new()), + torrent_peers: tokio::sync::RwLock::new(std::collections::BTreeMap::new()), } } } @@ -216,14 +228,16 @@ pub struct TorrentTracker { database: TorrentDatabase, } +#[derive(Serialize, Deserialize)] +struct DatabaseRow<'a> { + info_hash: InfoHash, + entry: Cow<'a, TorrentEntry>, +} + pub enum TorrentStats { TorrentFlagged, TorrentNotRegistered, - Stats { - seeders: u32, - leechers: u32, - complete: u32, - }, + Stats { seeders: u32, leechers: u32, complete: u32 }, } impl TorrentTracker { @@ -231,32 +245,52 @@ impl TorrentTracker { TorrentTracker { mode, database: TorrentDatabase { - torrent_peers: std::sync::RwLock::new(std::collections::BTreeMap::new()), + torrent_peers: RwLock::new(std::collections::BTreeMap::new()), }, } } - pub fn load_database( - mode: TrackerMode, - reader: &mut R, - ) -> serde_json::Result { - let decomp_reader = bzip2::read::BzDecoder::new(reader); - let result: serde_json::Result> = - serde_json::from_reader(decomp_reader); - match result { - Ok(v) => Ok(TorrentTracker { - mode, - database: TorrentDatabase { - torrent_peers: std::sync::RwLock::new(v), - }, - }), - Err(e) => Err(e), + pub async fn load_database( + mode: TrackerMode, reader: &mut R, + ) -> Result { + let reader = tokio::io::BufReader::new(reader); + let mut tmp: Vec = Vec::with_capacity(4096); + tmp.resize(tmp.capacity(), 0); + + let res = TorrentTracker::new(mode); + let mut db = res.database.torrent_peers.write().await; + + let mut records = reader + .lines() + .filter_map(|res| { + if let Err(ref err) = res { + error!("failed to read lines! {}", err); + } + res.ok() + }) + .map(|line| serde_json::from_str::(&line)) + .filter_map(|jsr| { + if let Err(ref err) = jsr { + error!("failed to parse json: {}", err); + } + jsr.ok() + }); + + while let Some(entry) = records.next().await { + let x = || (entry.entry, entry.info_hash); + let (a, b) = x(); + let a = a.into_owned(); + db.insert(b, a); } + + drop(db); + + Ok(res) } /// Adding torrents is not relevant to dynamic trackers. - pub fn add_torrent(&self, info_hash: &InfoHash) -> Result<(), ()> { - let mut write_lock = self.database.torrent_peers.write().unwrap(); + pub async fn add_torrent(&self, info_hash: &InfoHash) -> Result<(), ()> { + let mut write_lock = self.database.torrent_peers.write().await; match write_lock.entry(info_hash.clone()) { std::collections::btree_map::Entry::Vacant(ve) => { ve.insert(TorrentEntry::new()); @@ -269,9 +303,9 @@ impl TorrentTracker { } /// If the torrent is flagged, it will not be removed unless force is set to true. - pub fn remove_torrent(&self, info_hash: &InfoHash, force: bool) -> Result<(), ()> { + pub async fn remove_torrent(&self, info_hash: &InfoHash, force: bool) -> Result<(), ()> { use std::collections::btree_map::Entry; - let mut entry_lock = self.database.torrent_peers.write().unwrap(); + let mut entry_lock = self.database.torrent_peers.write().await; let torrent_entry = entry_lock.entry(info_hash.clone()); match torrent_entry { Entry::Vacant(_) => { @@ -289,28 +323,23 @@ impl TorrentTracker { } /// flagged torrents will result in a tracking error. This is to allow enforcement against piracy. - pub fn set_torrent_flag(&self, info_hash: &InfoHash, is_flagged: bool) { - if let Some(entry) = self - .database - .torrent_peers - .write() - .unwrap() - .get_mut(info_hash) - { + pub async fn set_torrent_flag(&self, info_hash: &InfoHash, is_flagged: bool) -> bool { + if let Some(entry) = self.database.torrent_peers.write().await.get_mut(info_hash) { if is_flagged && !entry.is_flagged { // empty peer list. entry.peers.clear(); } entry.is_flagged = is_flagged; + true + } else { + false } } - pub fn get_torrent_peers( - &self, - info_hash: &InfoHash, - remote_addr: &std::net::SocketAddr, + pub async fn get_torrent_peers( + &self, info_hash: &InfoHash, remote_addr: &std::net::SocketAddr, ) -> Option> { - let read_lock = self.database.torrent_peers.read().unwrap(); + let read_lock = self.database.torrent_peers.read().await; match read_lock.get(info_hash) { None => { return None; @@ -321,25 +350,21 @@ impl TorrentTracker { }; } - pub fn update_torrent_and_get_stats( - &self, - info_hash: &InfoHash, - peer_id: &PeerId, - remote_address: &std::net::SocketAddr, - uploaded: u64, - downloaded: u64, - left: u64, - event: Events, + pub async fn update_torrent_and_get_stats( + &self, info_hash: &InfoHash, peer_id: &PeerId, remote_address: &std::net::SocketAddr, uploaded: u64, + downloaded: u64, left: u64, event: Events, ) -> TorrentStats { use std::collections::btree_map::Entry; - let mut torrent_peers = self.database.torrent_peers.write().unwrap(); + let mut torrent_peers = self.database.torrent_peers.write().await; let torrent_entry = match torrent_peers.entry(info_hash.clone()) { - Entry::Vacant(vacant) => match self.mode { - TrackerMode::DynamicMode => vacant.insert(TorrentEntry::new()), - _ => { - return TorrentStats::TorrentNotRegistered; + Entry::Vacant(vacant) => { + match self.mode { + TrackerMode::DynamicMode => vacant.insert(TorrentEntry::new()), + _ => { + return TorrentStats::TorrentNotRegistered; + } } - }, + } Entry::Occupied(entry) => { if entry.get().is_flagged() { return TorrentStats::TorrentFlagged; @@ -359,70 +384,75 @@ impl TorrentTracker { }; } - pub(crate) fn get_database( - &self, - ) -> std::sync::RwLockReadGuard> { - self.database.torrent_peers.read().unwrap() + pub(crate) async fn get_database<'a>(&'a self) -> tokio::sync::RwLockReadGuard<'a, BTreeMap> { + self.database.torrent_peers.read().await } - pub fn save_database(&self, writer: &mut W) -> serde_json::Result<()> { - let compressor = bzip2::write::BzEncoder::new(writer, bzip2::Compression::Best); + pub async fn save_database(&self, mut writer: W) -> Result<(), std::io::Error> { + // TODO: find async friendly compressor - let db_lock = self.database.torrent_peers.read().unwrap(); + let db_lock = self.database.torrent_peers.read().await; - let db = &*db_lock; + let db: &BTreeMap = &*db_lock; + let mut tmp = Vec::with_capacity(4096); - serde_json::to_writer(compressor, &db) + for row in db { + let entry = DatabaseRow { + info_hash: row.0.clone(), + entry: Cow::Borrowed(row.1), + }; + tmp.clear(); + if let Err(err) = serde_json::to_writer(&mut tmp, &entry) { + error!("failed to serialize: {}", err); + continue; + }; + tmp.push(b'\n'); + writer.write_all(&tmp).await?; + } + Ok(()) } - fn cleanup(&self) { + async fn cleanup(&self) { use std::ops::Add; - let now = std::time::SystemTime::now(); - match self.database.torrent_peers.write() { - Err(err) => { - error!("failed to obtain write lock on database. err: {}", err); - return; - } - Ok(mut db) => { - let mut torrents_to_remove = Vec::new(); + let mut lock = self.database.torrent_peers.write().await; + let db: &mut BTreeMap = &mut *lock; + let mut torrents_to_remove = Vec::new(); - for (k, v) in db.iter_mut() { - // timed-out peers.. - { - let mut peers_to_remove = Vec::new(); - let torrent_peers = &mut v.peers; + for (k, v) in db.iter_mut() { + // timed-out peers.. + { + let mut peers_to_remove = Vec::new(); + let torrent_peers = &mut v.peers; - for (peer_id, state) in torrent_peers.iter() { - if state.updated.add(std::time::Duration::new(3600 * 2, 0)) < now { - // over 2 hours past since last update... - peers_to_remove.push(*peer_id); - } - } - - for peer_id in peers_to_remove.iter() { - torrent_peers.remove(peer_id); - } - } - - if self.mode == TrackerMode::DynamicMode { - // peer-less torrents.. - if v.peers.len() == 0 { - torrents_to_remove.push(k.clone()); - } + for (peer_id, state) in torrent_peers.iter() { + if state.updated.add(std::time::Duration::new(3600 * 2, 0)) < now { + // over 2 hours past since last update... + peers_to_remove.push(*peer_id); } } - for info_hash in torrents_to_remove { - db.remove(&info_hash); + for peer_id in peers_to_remove.iter() { + torrent_peers.remove(peer_id); + } + } + + if self.mode == TrackerMode::DynamicMode { + // peer-less torrents.. + if v.peers.len() == 0 { + torrents_to_remove.push(k.clone()); } } } + + for info_hash in torrents_to_remove { + db.remove(&info_hash); + } } - pub fn periodic_task(&self, db_path: &str) { + pub async fn periodic_task(&self, db_path: &str) { // cleanup db - self.cleanup(); + self.cleanup().await; // save journal db. let mut journal_path = std::path::PathBuf::from(db_path); @@ -435,7 +465,7 @@ impl TorrentTracker { // scope to make sure backup file is dropped/closed. { - let mut file = match std::fs::File::create(jp_str) { + let mut file = match tokio::fs::File::create(jp_str).await { Err(err) => { error!("failed to open file '{}': {}", db_path, err); return; @@ -443,7 +473,7 @@ impl TorrentTracker { Ok(v) => v, }; trace!("writing database to {}", jp_str); - if let Err(err) = self.save_database(&mut file) { + if let Err(err) = self.save_database(&mut file).await { error!("failed saving database. {}", err); return; } @@ -451,7 +481,7 @@ impl TorrentTracker { // overwrite previous db trace!("renaming '{}' to '{}'", jp_str, db_path); - if let Err(err) = std::fs::rename(jp_str, db_path) { + if let Err(err) = tokio::fs::rename(jp_str, db_path).await { error!("failed to move db backup. {}", err); } } @@ -474,14 +504,14 @@ mod tests { is_sync::(); } - #[test] - fn test_save_db() { + #[tokio::test] + async fn test_save_db() { let tracker = TorrentTracker::new(TrackerMode::DynamicMode); tracker.add_torrent(&[0u8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0].into()); let mut out = Vec::new(); - tracker.save_database(&mut out).unwrap(); + tracker.save_database(&mut out).await.expect("db save failed"); assert!(out.len() > 0); } diff --git a/src/webserver.rs b/src/webserver.rs index 266d721..d0fa0c8 100644 --- a/src/webserver.rs +++ b/src/webserver.rs @@ -1,419 +1,193 @@ -use std::collections::HashMap; -use std::sync::Arc; - -use actix_web; -use actix_net; -use binascii; - -use crate::config; -use crate::tracker::TorrentTracker; -use log::{error, warn, info, debug}; - -const SERVER: &str = concat!("udpt/", env!("CARGO_PKG_VERSION")); - -pub struct WebServer { - thread: std::thread::JoinHandle<()>, - addr: Option>, -} - -mod http_responses { - use serde::Serialize; - use crate::tracker::InfoHash; - - #[derive(Serialize)] - pub struct TorrentInfo { - pub is_flagged: bool, - pub leecher_count: u32, - pub seeder_count: u32, - pub completed: u32, - } - - #[derive(Serialize)] - pub struct TorrentList { - pub offset: u32, - pub length: u32, - pub total: u32, - pub torrents: Vec, - } - - #[derive(Serialize)] - #[serde(rename_all = "snake_case")] - pub enum APIResponse { - Error(String), - TorrentList(TorrentList), - TorrentInfo(TorrentInfo), - } -} - -struct UdptState { - // k=token, v=username. - access_tokens: HashMap, - tracker: Arc, -} - -impl UdptState { - fn new(tracker: Arc, tokens: HashMap) -> UdptState { - UdptState { - tracker, - access_tokens: tokens, - } - } -} - -#[derive(Debug)] -struct UdptRequestState { - current_user: Option, -} - -impl Default for UdptRequestState { - fn default() -> Self { - UdptRequestState { - current_user: Option::None, - } - } -} - -impl UdptRequestState { - fn get_user(req: &actix_web::HttpRequest) -> Option { - let exts = req.extensions(); - let req_state: Option<&UdptRequestState> = exts.get(); - match req_state { - None => None, - Option::Some(state) => match state.current_user { - Option::Some(ref v) => Option::Some(v.clone()), - None => { - error!( - "Invalid API token from {} @ {}", - req.peer_addr().unwrap(), - req.path() - ); - return None; - } - }, - } - } -} - -struct UdptMiddleware; - -impl actix_web::middleware::Middleware for UdptMiddleware { - fn start( - &self, - req: &actix_web::HttpRequest, - ) -> actix_web::Result { - let mut req_state = UdptRequestState::default(); - if let Option::Some(token) = req.query().get("token") { - let app_state: &UdptState = req.state(); - if let Option::Some(v) = app_state.access_tokens.get(token) { - req_state.current_user = Option::Some(v.clone()); - } - } - req.extensions_mut().insert(req_state); - Ok(actix_web::middleware::Started::Done) - } - - fn response( - &self, - _req: &actix_web::HttpRequest, - mut resp: actix_web::HttpResponse, - ) -> actix_web::Result { - resp.headers_mut().insert( - actix_web::http::header::SERVER, - actix_web::http::header::HeaderValue::from_static(SERVER), - ); - - Ok(actix_web::middleware::Response::Done(resp)) - } -} - -impl WebServer { - fn get_access_tokens(cfg: &config::HTTPConfig, tokens: &mut HashMap) { - for (user, token) in cfg.get_access_tokens().iter() { - tokens.insert(token.clone(), user.clone()); - } - if tokens.len() == 0 { - warn!("No access tokens provided. HTTP API will not be useful."); - } - } - - pub fn shutdown(self) { - match self.addr { - Some(v) => { - use futures::future::Future; - - v.send(actix_web::actix::signal::Signal(actix_web::actix::signal::SignalType::Term)).wait().unwrap(); - }, - None => {}, - }; - - self.thread.thread().unpark(); - let _ = self.thread.join(); - } - - pub fn new( - tracker: Arc, - cfg: Arc, - ) -> WebServer { - let cfg_cp = cfg.clone(); - - let (tx_addr, rx_addr) = std::sync::mpsc::channel(); - - let thread = std::thread::spawn(move || { - let server = actix_web::server::HttpServer::new(move || { - let mut access_tokens = HashMap::new(); - - if let Some(http_cfg) = cfg_cp.get_http_config() { - Self::get_access_tokens(http_cfg, &mut access_tokens); - } - - let state = UdptState::new(tracker.clone(), access_tokens); - - actix_web::App::::with_state(state) - .middleware(UdptMiddleware) - .resource("/t", |r| r.f(Self::view_torrent_list)) - .scope(r"/t/{info_hash:[\dA-Fa-f]{40,40}}", |scope| { - scope.resource("", |r| { - r.method(actix_web::http::Method::GET) - .f(Self::view_torrent_stats); - r.method(actix_web::http::Method::POST) - .f(Self::torrent_action); - }) - }) - .resource("/", |r| { - r.method(actix_web::http::Method::GET).f(Self::view_root) - }) - }); - - if let Some(http_cfg) = cfg.get_http_config() { - let bind_addr = http_cfg.get_address(); - match server.bind(bind_addr) { - Ok(v) => { - let sys = actix_web::actix::System::new("http-server"); - let addr = v.start(); - let _ = tx_addr.send(addr); - sys.run(); - } - Err(err) => { - error!("Failed to bind http server. {}", err); - } - } - } else { - unreachable!(); - } - }); - - let addr = match rx_addr.recv() { - Ok(v) => Some(v), - Err(_) => None - }; - - WebServer { - thread, - addr, - } - } - - fn view_root(_req: &actix_web::HttpRequest) -> actix_web::HttpResponse { - actix_web::HttpResponse::build(actix_web::http::StatusCode::OK) - .content_type("text/html") - .body(r#"Powered by https://github.com/naim94a/udpt"#) - } - - fn view_torrent_list(req: &actix_web::HttpRequest) -> impl actix_web::Responder { - use std::str::FromStr; - - if UdptRequestState::get_user(req).is_none() { - return actix_web::Json(http_responses::APIResponse::Error(String::from( - "access_denied", - ))); - } - - let req_offset = match req.query().get("offset") { - None => 0, - Some(v) => match u32::from_str(v.as_str()) { - Ok(v) => v, - Err(_) => 0, - }, - }; - - let mut req_limit = match req.query().get("limit") { - None => 0, - Some(v) => match u32::from_str(v.as_str()) { - Ok(v) => v, - Err(_) => 0, - }, - }; - - if req_limit > 4096 { - req_limit = 4096; - } else if req_limit == 0 { - req_limit = 1000; - } - - let app_state: &UdptState = req.state(); - let app_db = app_state.tracker.get_database(); - - let total = app_db.len() as u32; - - let mut torrents = Vec::with_capacity(req_limit as usize); - - for (info_hash, _) in app_db - .iter() - .skip(req_offset as usize) - .take(req_limit as usize) - { - torrents.push(info_hash.clone()); - } - - actix_web::Json(http_responses::APIResponse::TorrentList( - http_responses::TorrentList { - total, - length: torrents.len() as u32, - offset: req_offset, - torrents, - }, - )) - } - - fn view_torrent_stats(req: &actix_web::HttpRequest) -> actix_web::HttpResponse { - use actix_web::FromRequest; - - if UdptRequestState::get_user(req).is_none() { - return actix_web::HttpResponse::build(actix_web::http::StatusCode::UNAUTHORIZED).json( - http_responses::APIResponse::Error(String::from("access_denied")), - ); - } - - let path: actix_web::Path = match actix_web::Path::extract(req) { - Ok(v) => v, - Err(_) => { - return actix_web::HttpResponse::build( - actix_web::http::StatusCode::INTERNAL_SERVER_ERROR, - ) - .json(http_responses::APIResponse::Error(String::from( - "internal_error", - ))); - } - }; - - let mut info_hash = [0u8; 20]; - if let Err(_) = binascii::hex2bin((*path).as_bytes(), &mut info_hash) { - return actix_web::HttpResponse::build(actix_web::http::StatusCode::BAD_REQUEST).json( - http_responses::APIResponse::Error(String::from("invalid_info_hash")), - ); - } - - let app_state: &UdptState = req.state(); - - let db = app_state.tracker.get_database(); - let entry = match db.get(&info_hash.into()) { - Some(v) => v, - None => { - return actix_web::HttpResponse::build(actix_web::http::StatusCode::NOT_FOUND).json( - http_responses::APIResponse::Error(String::from("not_found")), - ); - } - }; - - let is_flagged = entry.is_flagged(); - let (seeders, completed, leechers) = entry.get_stats(); - - return actix_web::HttpResponse::build(actix_web::http::StatusCode::OK).json( - http_responses::APIResponse::TorrentInfo(http_responses::TorrentInfo { - is_flagged, - seeder_count: seeders, - leecher_count: leechers, - completed, - }), - ); - } - - fn torrent_action(req: &actix_web::HttpRequest) -> actix_web::HttpResponse { - use actix_web::FromRequest; - - if UdptRequestState::get_user(req).is_none() { - return actix_web::HttpResponse::build(actix_web::http::StatusCode::UNAUTHORIZED).json( - http_responses::APIResponse::Error(String::from("access_denied")), - ); - } - - let query = req.query(); - let action_opt = query.get("action"); - let action = match action_opt { - Some(v) => v, - None => { - return actix_web::HttpResponse::build(actix_web::http::StatusCode::BAD_REQUEST) - .json(http_responses::APIResponse::Error(String::from( - "action_required", - ))); - } - }; - - let app_state: &UdptState = req.state(); - - let path: actix_web::Path = match actix_web::Path::extract(req) { - Ok(v) => v, - Err(_err) => { - return actix_web::HttpResponse::build( - actix_web::http::StatusCode::INTERNAL_SERVER_ERROR, - ) - .json(http_responses::APIResponse::Error(String::from( - "internal_error", - ))); - } - }; - - let info_hash_str = &(*path); - let mut info_hash = [0u8; 20]; - if let Err(_) = binascii::hex2bin(info_hash_str.as_bytes(), &mut info_hash) { - return actix_web::HttpResponse::build(actix_web::http::StatusCode::BAD_REQUEST).json( - http_responses::APIResponse::Error(String::from("invalid_info_hash")), - ); - } - - match action.as_str() { - "flag" => { - app_state.tracker.set_torrent_flag(&info_hash.into(), true); - info!("Flagged {}", info_hash_str.as_str()); - return actix_web::HttpResponse::build(actix_web::http::StatusCode::OK).body(""); - } - "unflag" => { - app_state.tracker.set_torrent_flag(&info_hash.into(), false); - info!("Unflagged {}", info_hash_str.as_str()); - return actix_web::HttpResponse::build(actix_web::http::StatusCode::OK).body(""); - } - "add" => { - let success = app_state.tracker.add_torrent(&info_hash.into()).is_ok(); - info!("Added {}, success={}", info_hash_str.as_str(), success); - let code = if success { - actix_web::http::StatusCode::OK - } else { - actix_web::http::StatusCode::INTERNAL_SERVER_ERROR - }; - - return actix_web::HttpResponse::build(code).body(""); - } - "remove" => { - let success = app_state - .tracker - .remove_torrent(&info_hash.into(), true) - .is_ok(); - info!("Removed {}, success={}", info_hash_str.as_str(), success); - let code = if success { - actix_web::http::StatusCode::OK - } else { - actix_web::http::StatusCode::INTERNAL_SERVER_ERROR - }; - - return actix_web::HttpResponse::build(code).body(""); - } - _ => { - debug!("Invalid action {}", action.as_str()); - return actix_web::HttpResponse::build(actix_web::http::StatusCode::BAD_REQUEST) - .json(http_responses::APIResponse::Error(String::from( - "invalid_action", - ))); - } - } - } -} +use crate::tracker::{InfoHash, TorrentTracker}; +use serde::{Deserialize, Serialize}; +use std::cmp::min; +use std::collections::{HashMap, HashSet}; +use std::sync::Arc; +use warp::{filters, reply, reply::Reply, serve, Filter, Server}; + +fn view_root() -> impl Reply { + reply::html(concat!( + r#" + + udpt/"#, + env!("CARGO_PKG_VERSION"), + r#" + + + This is your udpt torrent tracker. + + "# + )) +} + +#[derive(Deserialize, Debug)] +struct TorrentInfoQuery { + offset: Option, + limit: Option, +} + +#[derive(Serialize)] +struct TorrentEntry<'a> { + info_hash: &'a InfoHash, + #[serde(flatten)] + data: &'a crate::tracker::TorrentEntry, +} + +#[derive(Serialize, Deserialize)] +struct TorrentFlag { + is_flagged: bool, +} + +#[derive(Serialize, Debug)] +#[serde(tag = "status", rename_all = "snake_case")] +enum ActionStatus<'a> { + Ok, + Err { reason: std::borrow::Cow<'a, str> }, +} + +impl warp::reject::Reject for ActionStatus<'static> {} + +fn authenticate(tokens: HashMap) -> impl Filter + Clone { + #[derive(Deserialize)] + struct AuthToken { + token: Option, + } + + let tokens: HashSet = tokens.into_iter().map(|(_, v)| v).collect(); + + let tokens = Arc::new(tokens); + warp::filters::any::any() + .map(move || tokens.clone()) + .and(filters::query::query::()) + .and_then(|tokens: Arc>, token: AuthToken| { + async move { + if let Some(token) = token.token { + if tokens.contains(&token) { + return Ok(()); + } + } + Err(warp::reject::custom(ActionStatus::Err { + reason: "Access Denied".into(), + })) + } + }) + .untuple_one() +} + +pub fn build_server( + tracker: Arc, tokens: HashMap, +) -> Server + Clone + Send + Sync + 'static> { + let root = filters::path::end().map(|| view_root()); + + let t1 = tracker.clone(); + // view_torrent_list -> GET /t/?offset=:u32&limit=:u32 HTTP/1.1 + let view_torrent_list = filters::path::end() + .and(filters::method::get()) + .and(filters::query::query()) + .map(move |limits| { + let tracker = t1.clone(); + (limits, tracker) + }) + .and_then(|(limits, tracker): (TorrentInfoQuery, Arc)| { + async move { + let offset = limits.offset.unwrap_or(0); + let limit = min(limits.limit.unwrap_or(1000), 4000); + + let db = tracker.get_database().await; + let results: Vec<_> = db + .iter() + .map(|(k, v)| TorrentEntry { info_hash: k, data: v }) + .skip(offset as usize) + .take(limit as usize) + .collect(); + + Result::<_, warp::reject::Rejection>::Ok(reply::json(&results)) + } + }); + + let t2 = tracker.clone(); + // view_torrent_info -> GET /t/:infohash HTTP/* + let view_torrent_info = filters::method::get() + .and(filters::path::param()) + .map(move |info_hash: InfoHash| { + let tracker = t2.clone(); + (info_hash, tracker) + }) + .and_then(|(info_hash, tracker): (InfoHash, Arc)| { + async move { + let db = tracker.get_database().await; + let info = match db.get(&info_hash) { + Some(v) => v, + None => return Err(warp::reject::reject()), + }; + + Ok(reply::json(&TorrentEntry { + info_hash: &info_hash, + data: info, + })) + } + }); + + // DELETE /t/:info_hash + let t3 = tracker.clone(); + let delete_torrent = filters::method::post() + .and(filters::path::param()) + .map(move |info_hash: InfoHash| { + let tracker = t3.clone(); + (info_hash, tracker) + }) + .and_then(|(info_hash, tracker): (InfoHash, Arc)| { + async move { + let resp = match tracker.remove_torrent(&info_hash, true).await.is_ok() { + true => ActionStatus::Ok, + false => { + ActionStatus::Err { + reason: "failed to delete torrent".into(), + } + } + }; + + Result::<_, warp::Rejection>::Ok(reply::json(&resp)) + } + }); + + let t4 = tracker.clone(); + // add_torrent/alter: POST /t/:info_hash + // (optional) BODY: json: {"is_flagged": boolean} + let change_torrent = filters::method::post() + .and(filters::path::param()) + .and(filters::body::content_length_limit(4096)) + .and(filters::body::json()) + .map(move |info_hash: InfoHash, body: Option| { + let tracker = t4.clone(); + (info_hash, tracker, body) + }) + .and_then( + |(info_hash, tracker, body): (InfoHash, Arc, Option)| { + async move { + let is_flagged = body.map(|e| e.is_flagged).unwrap_or(false); + if !tracker.set_torrent_flag(&info_hash, is_flagged).await { + // torrent doesn't exist, add it... + + if is_flagged { + if tracker.add_torrent(&info_hash).await.is_ok() { + tracker.set_torrent_flag(&info_hash, is_flagged).await; + } else { + return Err(warp::reject::custom(ActionStatus::Err { + reason: "failed to flag torrent".into(), + })); + } + } + } + + Result::<_, warp::Rejection>::Ok(reply::json(&ActionStatus::Ok)) + } + }, + ); + let torrent_mgmt = + filters::path::path("t").and(view_torrent_list.or(delete_torrent).or(view_torrent_info).or(change_torrent)); + + let server = root.or(authenticate(tokens).and(torrent_mgmt)); + + serve(server) +}