diff --git a/Cargo.lock b/Cargo.lock index d5eec15a88..cb28e02171 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,14 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "addr2line" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a49806b9dadc843c61e7c97e72490ad7f7220ae249012fbda9ad0609457c0543" +dependencies = [ + "gimli", +] + [[package]] name = "adler32" version = "1.0.4" @@ -17,9 +26,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.3.2" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0989268a37e128d4d7a8028f1c60099430113fdbc70419010601ce51a228e4fe" +checksum = "2f3e0bf23f51883cce372d5d5892211236856e4bb37fb942e1eb135ee0f146e3" dependencies = [ "const-random", ] @@ -64,10 +73,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "000444226fcff248f2bc4c7625be32c63caccfecc2723a2b9f78a7487a49c407" [[package]] -name = "anyhow" -version = "1.0.27" +name = "ansi_term" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "013a6e0a2cbe3d20f9c60b65458f7a7f7a5e636c5d0f45a5a6aee5d4b1f01785" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi 0.3.8", +] + +[[package]] +name = "anyhow" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f" [[package]] name = "anymap" @@ -107,8 +125,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0609c78bd572f4edc74310dfb63a01f5609d53fa8b4dd7c4d98aef3b3e8d72d1" dependencies = [ "proc-macro-hack", - "quote 1.0.3", - "syn 1.0.16", + "quote 1.0.6", + "syn 1.0.27", ] [[package]] @@ -242,26 +260,17 @@ checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" [[package]] name = "backtrace" -version = "0.3.45" +version = "0.3.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad235dabf00f36301792cfe82499880ba54c6486be094d1047b02bacb67c14e8" +checksum = "0df2f85c8a2abbe3b7d7e748052fdd9b76a0458fdeb16ad4223f5eca78c7c130" dependencies = [ - "backtrace-sys", + "addr2line", "cfg-if", "libc", + "object", "rustc-demangle", ] -[[package]] -name = "backtrace-sys" -version = "0.1.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca797db0057bae1a7aa2eef3283a874695455cecf08a43bfb8507ee0ebc1ed69" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "base-x" version = "0.2.6" @@ -305,9 +314,9 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.53.2" +version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb26d6a69a335b8cb0e7c7e9775cd5666611dc50a37177c3f2cedcfc040e8c8" +checksum = "c72a978d268b1d70b0e963217e60fdabd9523a941457a6c42a7315d15c7e89e5" dependencies = [ "bitflags", "cexpr", @@ -316,8 +325,8 @@ dependencies = [ "lazy_static", "lazycell", "peeking_take_while", - "proc-macro2 1.0.9", - "quote 1.0.3", + "proc-macro2 1.0.17", + "quote 1.0.6", "regex", "rustc-hash", "shlex", @@ -366,7 +375,7 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "parking_lot 0.10.0", + "parking_lot 0.10.2", "slab", ] @@ -392,9 +401,9 @@ dependencies = [ [[package]] name = "bstr" -version = "0.2.11" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "502ae1441a0a5adb8fbd38a5955a6416b9493e92b465de5e4a9bde6a539c2c48" +checksum = "31accafdb70df7871592c058eca3985b71104e15ac32f64706022c58867da931" dependencies = [ "lazy_static", "memchr", @@ -414,9 +423,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.2.1" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12ae9db68ad7fac5fe51304d20f016c911539251075a214f8e663babefa35187" +checksum = "5356f1d23ee24a1f785a56d1d1a5f0fd5b0f6a0c0fb2412ce11da71649ab78f6" [[package]] name = "byteorder" @@ -488,9 +497,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.50" +version = "1.0.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" +checksum = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311" dependencies = [ "jobserver", ] @@ -549,9 +558,9 @@ checksum = "498d20a7aaf62625b9bf26e637cf7736417cde1d0c99f1d04d1170229a85cf87" [[package]] name = "clang-sys" -version = "0.29.2" +version = "0.29.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f92986241798376849e1a007827041fed9bb36195822c2049d18e174420e0534" +checksum = "fe6837df1d5cba2397b835c8530f51723267e16abbf83892e9e5af4f0e5dd10a" dependencies = [ "glob", "libc", @@ -560,9 +569,9 @@ dependencies = [ [[package]] name = "clap" -version = "2.33.0" +version = "2.33.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" +checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129" dependencies = [ "bitflags", "textwrap", @@ -686,9 +695,9 @@ dependencies = [ [[package]] name = "const-tweaker" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7081900ff8f4b89046f8898eb8af6ed26be5a47299c56147d5a7dac74298b0" +checksum = "7fbe3e1d2fccd896d451adb486910a0bfc233fd6dcafdb4e13bac7de72f8f250" dependencies = [ "anyhow", "async-std", @@ -707,9 +716,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a43d28ffebd3bb949c8c274de94fb84826134a023c5e6dac528c38a0f1cf1ba" dependencies = [ "darling", - "proc-macro2 1.0.9", - "quote 1.0.3", - "syn 1.0.16", + "proc-macro2 1.0.17", + "quote 1.0.6", + "syn 1.0.27", ] [[package]] @@ -834,16 +843,16 @@ dependencies = [ [[package]] name = "criterion" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc755679c12bda8e5523a71e4d654b6bf2e14bd838dfc48cde6559a05caf7d1" +checksum = "63f696897c88b57f4ffe3c69d8e1a0613c7d0e6c4833363c8560fbde9c47b966" dependencies = [ "atty", "cast", "clap", "criterion-plot", "csv", - "itertools", + "itertools 0.9.0", "lazy_static", "num-traits 0.2.11", "oorandom", @@ -859,12 +868,12 @@ dependencies = [ [[package]] name = "criterion-plot" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01e15e0ea58e8234f96146b1f91fa9d0e4dd7a38da93ff7a75d42c0b9d3a545" +checksum = "ddeaf7989f00f2e1d871a26a110f3ed713632feac17f65f03ca938c542618b60" dependencies = [ "cast", - "itertools", + "itertools 0.9.0", ] [[package]] @@ -1038,10 +1047,10 @@ checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.9", - "quote 1.0.3", + "proc-macro2 1.0.17", + "quote 1.0.6", "strsim", - "syn 1.0.16", + "syn 1.0.27", ] [[package]] @@ -1051,26 +1060,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" dependencies = [ "darling_core", - "quote 1.0.3", - "syn 1.0.16", + "quote 1.0.6", + "syn 1.0.27", ] [[package]] name = "dashmap" -version = "3.7.0" +version = "3.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "010ef3f25ed5bb93505a3238d19957622190268640526aab07174c66ccf5d611" +checksum = "d8b384aed866a28e92a6943f4f5d869f0a623776b550751cb87e711148803c18" dependencies = [ - "ahash 0.3.2", + "ahash 0.3.5", "cfg-if", "num_cpus", ] [[package]] name = "data-encoding" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11c0346158a19b3627234e15596f5e465c360fcdb97d817bcb255e0510f5a788" +checksum = "72aa14c04dfae8dd7d8a2b1cb7ca2152618cd01336dbfe704b8dcbf8d41dbd69" [[package]] name = "deflate" @@ -1096,9 +1105,9 @@ dependencies = [ [[package]] name = "deunicode" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "307dde1a517939465bc4042b47377284a56cee6160f8066f1f5035eb7b25a3fc" +checksum = "80115a2dfde04491e181c2440a39e4be26e52d9ca4e92bed213f65b94e0b8db1" [[package]] name = "diesel" @@ -1117,9 +1126,9 @@ version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45f5098f628d02a7a0f68ddba586fb61e80edec3bdc1be3b921f4ceec60858d3" dependencies = [ - "proc-macro2 1.0.9", - "quote 1.0.3", - "syn 1.0.16", + "proc-macro2 1.0.17", + "quote 1.0.6", + "syn 1.0.27", ] [[package]] @@ -1233,9 +1242,9 @@ checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" [[package]] name = "encoding_rs" -version = "0.8.22" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd8d03faa7fe0c1431609dfad7bbe827af30f82e1e2ae6f7ee4fca6bd764bc28" +checksum = "e8ac63f94732332f44fe654443c46f6375d1939684c17b0afb6cb56b0456e171" dependencies = [ "cfg-if", ] @@ -1260,7 +1269,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d371106cc88ffdfb1eabd7111e432da544f16f3e2d7bf1dfe8bf575f1df045cd" dependencies = [ "backtrace", - "version_check 0.9.1", + "version_check 0.9.2", ] [[package]] @@ -1295,9 +1304,9 @@ dependencies = [ [[package]] name = "failure" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8529c2421efa3066a5cbd8063d2244603824daccb6936b079010bb2aa89464b" +checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" dependencies = [ "backtrace", "failure_derive", @@ -1305,13 +1314,13 @@ dependencies = [ [[package]] name = "failure_derive" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "030a733c8287d6213886dd487564ff5c8f6aae10278b3588ed177f9d18f8d231" +checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" dependencies = [ - "proc-macro2 1.0.9", - "quote 1.0.3", - "syn 1.0.16", + "proc-macro2 1.0.17", + "quote 1.0.6", + "syn 1.0.27", "synstructure", ] @@ -1328,9 +1337,9 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.8" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ff6d4dab0aa0c8e6346d46052e93b13a16cf847b54ed357087c35011048cc7d" +checksum = "affc17579b132fc2461adf7c575cc6e8b134ebca52c51f5411388965227dc695" dependencies = [ "cfg-if", "libc", @@ -1352,9 +1361,9 @@ checksum = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33" [[package]] name = "fnv" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foreign-types" @@ -1420,9 +1429,9 @@ checksum = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" [[package]] name = "futures" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c329ae8753502fb44ae4fc2b622fa2a94652c41e795143765ba0927f92ab780" +checksum = "1e05b85ec287aac0dc34db7d4a569323df697f9c55b99b15d6b4ef8cde49f613" dependencies = [ "futures-channel", "futures-core", @@ -1435,9 +1444,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c77d04ce8edd9cb903932b608268b3fffec4163dc053b3b402bf47eac1f1a8" +checksum = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5" dependencies = [ "futures-core", "futures-sink", @@ -1445,9 +1454,9 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a" +checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399" [[package]] name = "futures-cpupool" @@ -1461,44 +1470,48 @@ dependencies = [ [[package]] name = "futures-executor" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f674f3e1bcb15b37284a90cedf55afdba482ab061c407a9c0ebbd0f3109741ba" +checksum = "10d6bb888be1153d3abeb9006b11b02cf5e9b209fda28693c31ae1e4e012e314" dependencies = [ "futures-core", "futures-task", "futures-util", + "num_cpus", ] [[package]] name = "futures-io" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a638959aa96152c7a4cddf50fcb1e3fede0583b27157c26e67d6f99904090dc6" +checksum = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789" [[package]] name = "futures-macro" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a5081aa3de1f7542a794a397cde100ed903b0630152d0973479018fd85423a7" +checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39" dependencies = [ "proc-macro-hack", - "proc-macro2 1.0.9", - "quote 1.0.3", - "syn 1.0.16", + "proc-macro2 1.0.17", + "quote 1.0.6", + "syn 1.0.27", ] [[package]] name = "futures-sink" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3466821b4bc114d95b087b850a724c6f83115e929bc88f1fa98a3304a944c8a6" +checksum = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc" [[package]] name = "futures-task" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0a34e53cf6cdcd0178aa573aed466b646eb3db769570841fda0c7ede375a27" +checksum = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626" +dependencies = [ + "once_cell", +] [[package]] name = "futures-timer" @@ -1508,9 +1521,9 @@ checksum = "a1de7508b218029b0f01662ed8f61b1c964b3ae99d6f25462d0f55a595109df6" [[package]] name = "futures-util" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5" +checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6" dependencies = [ "futures 0.1.29", "futures-channel", @@ -1520,6 +1533,7 @@ dependencies = [ "futures-sink", "futures-task", "memchr", + "pin-project", "pin-utils", "proc-macro-hack", "proc-macro-nested", @@ -1692,9 +1706,9 @@ dependencies = [ [[package]] name = "gilrs-core" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdd4ea2d919ecb594362fa26b0f172729b9ee9b95e407fbad95e0a49cadc143" +checksum = "43c758daf46af26d6872fe55507e3b2339779a160a06ad7a9b2a082f221209cd" dependencies = [ "core-foundation 0.6.4", "io-kit-sys", @@ -1709,6 +1723,12 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "gimli" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc8e0c9bce37868955864dbecd2b1ab2bdf967e6f28066d65aaac620444b65c" + [[package]] name = "gio" version = "0.4.1" @@ -1988,20 +2008,20 @@ dependencies = [ [[package]] name = "h2" -version = "0.2.2" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5c295d1c0c68e4e42003d75f908f5e16a1edd1cbe0b0d02e4dc2006a384f47" +checksum = "79b7246d7e4b979c03fa093da39cfb3617a96bbeee6310af63991668d7e843ff" dependencies = [ "bytes 0.5.4", "fnv", "futures-core", "futures-sink", "futures-util", - "http 0.2.0", + "http 0.2.1", "indexmap", "log 0.4.8", "slab", - "tokio 0.2.13", + "tokio 0.2.21", "tokio-util", ] @@ -2019,9 +2039,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.8" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1010591b26bbfe835e9faeabeb11866061cc7dcebffd56ad7d0942d0e61aefd8" +checksum = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71" dependencies = [ "libc", ] @@ -2067,9 +2087,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b708cc7f06493459026f53b9a61a7a121a5d1ec6238dee58ea4941132b30156b" +checksum = "28d569972648b2c512421b5f2a405ad6ac9666547189d0c5477a3f200f3e02f9" dependencies = [ "bytes 0.5.4", "fnv", @@ -2095,7 +2115,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" dependencies = [ "bytes 0.5.4", - "http 0.2.0", + "http 0.2.1", ] [[package]] @@ -2106,7 +2126,7 @@ checksum = "9625f605ddfaf894bf78a544a7b8e31f562dc843654723a49892d9c7e75ac708" dependencies = [ "async-std", "bytes 0.4.12", - "futures 0.3.4", + "futures 0.3.5", "http 0.1.21", "pin-project-lite", ] @@ -2117,7 +2137,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e33d5dae94e0fdb82f9524ea2f2b98458b3d8448526d8cc8beccb3d3fded8aff" dependencies = [ - "futures 0.3.4", + "futures 0.3.5", "http 0.1.21", "http-service", "hyper 0.12.35", @@ -2170,16 +2190,16 @@ dependencies = [ [[package]] name = "hyper" -version = "0.13.3" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7b15203263d1faa615f9337d79c1d37959439dc46c2b4faab33286fadc2a1c5" +checksum = "96816e1d921eca64d208a85aab4f7798455a8e34229ee5a88c935bdee1b78b14" dependencies = [ "bytes 0.5.4", "futures-channel", "futures-core", "futures-util", - "h2 0.2.2", - "http 0.2.0", + "h2 0.2.5", + "http 0.2.1", "http-body 0.3.1", "httparse", "itoa", @@ -2187,7 +2207,7 @@ dependencies = [ "net2", "pin-project", "time", - "tokio 0.2.13", + "tokio 0.2.21", "tower-service", "want 0.3.0", ] @@ -2201,11 +2221,11 @@ dependencies = [ "bytes 0.5.4", "ct-logs", "futures-util", - "hyper 0.13.3", + "hyper 0.13.5", "log 0.4.8", "rustls", "rustls-native-certs", - "tokio 0.2.13", + "tokio 0.2.21", "tokio-rustls", "webpki", ] @@ -2295,9 +2315,9 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c346c299e3fe8ef94dc10c2c0253d858a69aac1245157a3bf4125915d528caf" +checksum = "7777a24a1ce5de49fcdde84ec46efa487c3af49d5b6e6e0a50367cc5c1096182" [[package]] name = "io-kit-sys" @@ -2327,6 +2347,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "0.4.5" @@ -2344,9 +2373,9 @@ dependencies = [ [[package]] name = "jpeg-decoder" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0256f0aec7352539102a9efbcb75543227b7ab1117e0f95450023af730128451" +checksum = "5b47b4c4e017b01abdc5bcc126d2d1002e5a75bbe3ce73f9f4f311a916363704" dependencies = [ "byteorder 1.3.4", "rayon", @@ -2354,9 +2383,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.36" +version = "0.3.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cb931d43e71f560c81badb0191596562bafad2be06a3f9025b845c847c60df5" +checksum = "fa5a448de267e7358beaf4a5d849518fe9a0c13fce7afd44b06e68550e5562a7" dependencies = [ "wasm-bindgen", ] @@ -2379,9 +2408,9 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" [[package]] name = "kv-log-macro" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c54d9f465d530a752e6ebdc217e081a7a614b48cb200f6f0aee21ba6bc9aabb" +checksum = "4ff57d6d215f7ca7eb35a9a64d656ba4d9d2bef114d741dc08048e75e2f5d418" dependencies = [ "log 0.4.8", ] @@ -2411,9 +2440,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.67" +version = "0.2.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018" +checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" [[package]] name = "libgit2-sys" @@ -2452,9 +2481,9 @@ dependencies = [ [[package]] name = "libssh2-sys" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bb70f29dc7c31d32c97577f13f41221af981b31248083e347b7f2c39225a6bc" +checksum = "d45f516b9b19ea6c940b9f36d36734062a153a2b4cc9ef31d82c54bb9780f525" dependencies = [ "cc", "libc", @@ -2497,15 +2526,15 @@ dependencies = [ [[package]] name = "linked-hash-map" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" +checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" [[package]] name = "lock_api" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b" +checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" dependencies = [ "scopeguard", ] @@ -2562,6 +2591,15 @@ dependencies = [ "libc", ] +[[package]] +name = "matchers" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" +dependencies = [ + "regex-automata", +] + [[package]] name = "matches" version = "0.1.8" @@ -2592,11 +2630,11 @@ dependencies = [ [[package]] name = "memoffset" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9" +checksum = "b4fc2c02a7e374099d4ee95a193111f72d2110197fe200272371758f6c3643d8" dependencies = [ - "rustc_version", + "autocfg 1.0.0", ] [[package]] @@ -2615,9 +2653,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9753f12909fd8d923f75ae5c3258cae1ed3c8ec052e1b38c93c21a6d157f789c" dependencies = [ "migrations_internals", - "proc-macro2 1.0.9", - "quote 1.0.3", - "syn 1.0.16", + "proc-macro2 1.0.17", + "quote 1.0.6", + "syn 1.0.27", ] [[package]] @@ -2674,9 +2712,9 @@ dependencies = [ [[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", @@ -2705,9 +2743,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", @@ -2763,9 +2801,9 @@ 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", @@ -2830,7 +2868,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b471253da97532da4b61552249c521e01e736071f71c1a4f7ebbfbf0a06aad6" dependencies = [ "memchr", - "version_check 0.9.1", + "version_check 0.9.2", ] [[package]] @@ -2934,9 +2972,9 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da4dc79f9e6c81bef96148c8f6b8e72ad4541caa4a24373e900a36da07de03a3" +checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" dependencies = [ "autocfg 1.0.0", "num-bigint", @@ -2964,9 +3002,9 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" dependencies = [ "hermit-abi", "libc", @@ -3001,6 +3039,12 @@ dependencies = [ "objc", ] +[[package]] +name = "object" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cbca9424c482ee628fa549d9c812e2cd22f1180b9222c9200fdfa6eb31aecb2" + [[package]] name = "ogg" version = "0.7.0" @@ -3012,15 +3056,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b" +checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" [[package]] name = "oorandom" -version = "11.1.0" +version = "11.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebcec7c9c2a95cacc7cd0ecb89d8a8454eca13906f6deb55258ffff0adeb9405" +checksum = "94af325bc33c7f60191be4e2c984d48aaa21e2854f473b85398344b60c9b6358" [[package]] name = "openssl-probe" @@ -3030,9 +3074,9 @@ checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" [[package]] name = "openssl-sys" -version = "0.9.54" +version = "0.9.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1024c0a59774200a555087a6da3f253a9095a5f344e353b212ac4c8b8e450986" +checksum = "7410fef80af8ac071d4f63755c0ab89ac3df0fd1ea91f1d1f37cf5cec4395990" dependencies = [ "autocfg 1.0.0", "cc", @@ -3137,12 +3181,12 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.10.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e98c49ab0b7ce5b222f2cc9193fc4efe11c6d0bd4f648e374684a6857b1cfc" +checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e" dependencies = [ "lock_api", - "parking_lot_core 0.7.0", + "parking_lot_core 0.7.2", ] [[package]] @@ -3174,15 +3218,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1" +checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" dependencies = [ "cfg-if", "cloudabi", "libc", "redox_syscall", - "smallvec 1.2.0", + "smallvec 1.4.0", "winapi 0.3.8", ] @@ -3254,35 +3298,35 @@ dependencies = [ [[package]] name = "pin-project" -version = "0.4.8" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7804a463a8d9572f13453c516a5faea534a2403d7ced2f0c7e100eeff072772c" +checksum = "edc93aeee735e60ecb40cf740eb319ff23eab1c5748abfdb5c180e4ce49f7791" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "0.4.8" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385322a45f2ecf3410c68d2a549a4a2685e8051d0f278e39743ff4e451cb9b3f" +checksum = "e58db2081ba5b4c93bd6be09c40fd36cb9193a8336c384f3b40012e531aa7e40" dependencies = [ - "proc-macro2 1.0.9", - "quote 1.0.3", - "syn 1.0.16", + "proc-macro2 1.0.17", + "quote 1.0.6", + "syn 1.0.27", ] [[package]] name = "pin-project-lite" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "237844750cfbb86f67afe27eee600dfbbcb6188d734139b534cbfbf4f96792ae" +checksum = "f7505eeebd78492e0f6108f7171c4948dbb120ee8119d9d77d0afa5469bef67f" [[package]] name = "pin-utils" -version = "0.1.0-alpha.4" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "piston-float" @@ -3319,9 +3363,9 @@ checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" [[package]] name = "plotters" -version = "0.2.12" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e3bb8da247d27ae212529352020f3e5ee16e83c0c258061d27b08ab92675eeb" +checksum = "0d1685fbe7beba33de0330629da9d955ac75bd54f33d7b79f9a895590124f6bb" dependencies = [ "js-sys", "num-traits 0.2.11", @@ -3351,9 +3395,9 @@ dependencies = [ [[package]] name = "ppv-lite86" -version = "0.2.6" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" +checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" [[package]] name = "pretty_env_logger" @@ -3368,20 +3412,15 @@ dependencies = [ [[package]] name = "proc-macro-hack" -version = "0.5.11" +version = "0.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" -dependencies = [ - "proc-macro2 1.0.9", - "quote 1.0.3", - "syn 1.0.16", -] +checksum = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4" [[package]] name = "proc-macro-nested" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e" +checksum = "8e946095f9d3ed29ec38de908c22f95d9ac008e424c7bcae54c75a79c527c694" [[package]] name = "proc-macro2" @@ -3403,9 +3442,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.9" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435" +checksum = "1502d12e458c49a4c9cbff560d0fe0060c252bc29799ed94ca2ed4bb665a0101" dependencies = [ "unicode-xid 0.2.0", ] @@ -3438,9 +3477,9 @@ dependencies = [ [[package]] name = "protobuf" -version = "2.10.2" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a5325d019a4d837d3abde0a836920f959e33d350f77b5f1e289e061e774942" +checksum = "8e86d370532557ae7573551a1ec8235a0f8d6cb276c7c9e6aa490b511c447485" [[package]] name = "quick-error" @@ -3468,11 +3507,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.3" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" +checksum = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea" dependencies = [ - "proc-macro2 1.0.9", + "proc-macro2 1.0.17", ] [[package]] @@ -3725,9 +3764,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.3.4" +version = "1.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322cf97724bea3ee221b78fe25ac9c46114ebb51747ad5babd51a2fc6a8235a8" +checksum = "a6020f034922e3194c711b82a627453881bc4682166cabb07134a10c26ba7692" dependencies = [ "aho-corasick", "memchr", @@ -3742,13 +3781,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" dependencies = [ "byteorder 1.3.4", + "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.6.16" +version = "0.6.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1132f845907680735a84409c3bebc64d1364a5683ffbce899550cd09d5eaefc1" +checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" [[package]] name = "remove_dir_all" @@ -3770,9 +3810,9 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "http 0.2.0", + "http 0.2.1", "http-body 0.3.1", - "hyper 0.13.3", + "hyper 0.13.5", "hyper-rustls", "js-sys", "lazy_static", @@ -3785,7 +3825,7 @@ dependencies = [ "serde", "serde_urlencoded", "time", - "tokio 0.2.13", + "tokio 0.2.21", "tokio-rustls", "url 2.1.1", "wasm-bindgen", @@ -3797,13 +3837,13 @@ dependencies = [ [[package]] name = "ring" -version = "0.16.11" +version = "0.16.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "741ba1704ae21999c00942f9f5944f801e977f54302af346b596287599ad1862" +checksum = "703516ae74571f24b465b4a1431e81e2ad51336cb0ded733a55a1aa3eccac196" dependencies = [ "cc", - "lazy_static", "libc", + "once_cell", "spin", "untrusted", "web-sys", @@ -3946,17 +3986,16 @@ version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "310942406a39981bed7e12b09182a221a29e0990f3e7e0c971f131922ed135d5" dependencies = [ - "rusttype 0.8.2", + "rusttype 0.8.3", ] [[package]] name = "rusttype" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14a911032fb5791ccbeec9f28fdcb9bf0983b81f227bafdfd227c658d0731c8a" +checksum = "9f61411055101f7b60ecf1041d87fb74205fb20b0c7a723f07ef39174cf6b4c0" dependencies = [ "approx 0.3.2", - "arrayvec 0.5.1", "crossbeam-deque", "crossbeam-utils 0.7.2", "linked-hash-map", @@ -3979,9 +4018,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" +checksum = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1" [[package]] name = "safemem" @@ -4009,9 +4048,9 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.17" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "507a9e6e8ffe0a4e0ebb9a10293e62fdf7657c06f1b8bb07a8fcf697d2abf295" +checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" dependencies = [ "lazy_static", "winapi 0.3.8", @@ -4065,21 +4104,22 @@ dependencies = [ [[package]] name = "security-framework" -version = "0.4.1" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97bbedbe81904398b6ebb054b3e912f99d55807125790f3198ac990d98def5b0" +checksum = "64808902d7d99f78eaddd2b4e2509713babc3dc3c85ad6f4c447680f3c01e535" dependencies = [ "bitflags", "core-foundation 0.7.0", "core-foundation-sys 0.7.0", + "libc", "security-framework-sys", ] [[package]] name = "security-framework-sys" -version = "0.4.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06fd2f23e31ef68dd2328cc383bd493142e46107a3a0e24f7d734e3f3b80fe4c" +checksum = "17bf11d99252f512695eb468de5516e5cf75455521e69dfe343f3b74e4748405" dependencies = [ "core-foundation-sys 0.7.0", "libc", @@ -4102,29 +4142,29 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.105" +version = "1.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e707fbbf255b8fc8c3b99abb91e7257a622caeb20a9818cbadbeeede4e0932ff" +checksum = "99e7b308464d16b56eba9964e4972a3eee817760ab60d88c3f86e1fecb08204c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.105" +version = "1.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac5d00fc561ba2724df6758a17de23df5914f20e41cb00f94d5b7ae42fffaff8" +checksum = "818fbf6bfa9a42d3bfcaca148547aa00c7b915bec71d1757aa2d44ca68771984" dependencies = [ - "proc-macro2 1.0.9", - "quote 1.0.3", - "syn 1.0.16", + "proc-macro2 1.0.17", + "quote 1.0.6", + "syn 1.0.27", ] [[package]] name = "serde_json" -version = "1.0.48" +version = "1.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9371ade75d4c2d6cb154141b9752cf3781ec9c05e0e5cf35060e1e70ee7b9c25" +checksum = "993948e75b189211a9b31a7528f950c6adc21f9720b6438ff80a7fa2f864cea2" dependencies = [ "itoa", "ryu", @@ -4161,6 +4201,15 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" +[[package]] +name = "sharded-slab" +version = "0.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06d5a3f5166fb5b42a5439f2eee8b9de149e235961e3eb21c5808fc3ea17ff3e" +dependencies = [ + "lazy_static", +] + [[package]] name = "shared_library" version = "0.1.9" @@ -4197,9 +4246,9 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1f37080f2751fbf091dbdebaa95bd6cf9dbf74ad1d50396b1908518a1747fdf" dependencies = [ - "proc-macro2 1.0.9", - "quote 1.0.3", - "syn 1.0.16", + "proc-macro2 1.0.17", + "quote 1.0.6", + "syn 1.0.27", ] [[package]] @@ -4231,9 +4280,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.2.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc" +checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4" [[package]] name = "smithay-client-toolkit" @@ -4365,11 +4414,11 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" dependencies = [ - "proc-macro2 1.0.9", - "quote 1.0.3", + "proc-macro2 1.0.17", + "quote 1.0.6", "serde", "serde_derive", - "syn 1.0.16", + "syn 1.0.27", ] [[package]] @@ -4379,13 +4428,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" dependencies = [ "base-x", - "proc-macro2 1.0.9", - "quote 1.0.3", + "proc-macro2 1.0.17", + "quote 1.0.6", "serde", "serde_derive", "serde_json", "sha1", - "syn 1.0.16", + "syn 1.0.27", ] [[package]] @@ -4445,12 +4494,12 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.16" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859" +checksum = "ef781e621ee763a2a40721a8861ec519cb76966aee03bb5d00adb6a31dc1c1de" dependencies = [ - "proc-macro2 1.0.9", - "quote 1.0.3", + "proc-macro2 1.0.17", + "quote 1.0.6", "unicode-xid 0.2.0", ] @@ -4460,9 +4509,9 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" dependencies = [ - "proc-macro2 1.0.9", - "quote 1.0.3", - "syn 1.0.16", + "proc-macro2 1.0.17", + "quote 1.0.6", + "syn 1.0.27", "unicode-xid 0.2.0", ] @@ -4516,9 +4565,9 @@ dependencies = [ [[package]] name = "threadpool" -version = "1.7.1" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2f0c90a5f3459330ac8bc0d2f879c693bb7a2f59689c1083fc4ef83834da865" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" dependencies = [ "num_cpus", ] @@ -4531,7 +4580,7 @@ checksum = "e619c99048ae107912703d0efeec4ff4fbff704f064e51d3eee614b28ea7b739" dependencies = [ "async-std", "cookie", - "futures 0.3.4", + "futures 0.3.5", "http 0.1.21", "http-service", "http-service-hyper", @@ -4558,12 +4607,11 @@ dependencies = [ [[package]] name = "time" -version = "0.1.42" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" +checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" dependencies = [ "libc", - "redox_syscall", "winapi 0.3.8", ] @@ -4582,9 +4630,9 @@ dependencies = [ [[package]] name = "tinytemplate" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a3c6667d3e65eb1bc3aed6fd14011c6cbc3a0665218ab7f5daf040b9ec371a" +checksum = "45e4bc5ac99433e0dcb8b9f309dd271a165ae37dde129b9e0ce1bfdd8bfe4891" dependencies = [ "serde", "serde_json", @@ -4610,12 +4658,13 @@ dependencies = [ [[package]] name = "tokio" -version = "0.2.13" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa5e81d6bc4e67fe889d5783bd2a128ab2e0cfa487e0be16b6a8d177b101616" +checksum = "d099fa27b9702bed751524694adbe393e18b36b204da91eb1cbbbbb4a5ee2d58" dependencies = [ "bytes 0.5.4", "fnv", + "futures-core", "iovec", "lazy_static", "memchr", @@ -4688,13 +4737,13 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4adb8b3e5f86b707f1b54e7c15b6de52617a823608ccda98a15d3a24222f265a" +checksum = "15cb62a0d2770787abc96e99c1cd98fcf17f94959f3af63ca85bdfb203f051b4" dependencies = [ "futures-core", "rustls", - "tokio 0.2.13", + "tokio 0.2.21", "webpki", ] @@ -4753,16 +4802,16 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "571da51182ec208780505a32528fc5512a8fe1443ab960b3f2f3ef093cd16930" +checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" dependencies = [ "bytes 0.5.4", "futures-core", "futures-sink", "log 0.4.8", "pin-project-lite", - "tokio 0.2.13", + "tokio 0.2.21", ] [[package]] @@ -4780,12 +4829,58 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860" +[[package]] +name = "tracing" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7c6b59d116d218cb2d990eb06b77b64043e0268ef7323aae63d8b30ae462923" +dependencies = [ + "cfg-if", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aa83a9a47081cd522c09c81b31aec2c9273424976f922ad61c053b58350b715" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "tracing-futures" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab7bb6f14721aa00656086e9335d363c5c8747bae02ebe32ea2c7dece5689b4c" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "tracing-subscriber" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d53c40489aa69c9aed21ff483f26886ca8403df33bdc2d2f87c60c1617826d2" +dependencies = [ + "ansi_term", + "chrono", + "lazy_static", + "matchers", + "regex", + "sharded-slab", + "smallvec 1.4.0", + "tracing-core", +] + [[package]] name = "treeculler" version = "0.1.0" -source = "git+https://gitlab.com/yusdacra/treeculler.git#6c0fdf1c1cbf00be22e37410985d6a3973cd9bed" +source = "git+https://gitlab.com/yusdacra/treeculler.git#efcf5283cf386117a7e654abdaa45ef664a08e42" dependencies = [ "num-traits 0.2.11", + "vek 0.11.0", ] [[package]] @@ -4824,7 +4919,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 0.9.2", ] [[package]] @@ -4842,7 +4937,7 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" dependencies = [ - "smallvec 1.2.0", + "smallvec 1.4.0", ] [[package]] @@ -4865,9 +4960,9 @@ checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" [[package]] name = "untrusted" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60369ef7a31de49bcb3f6ca728d4ba7300d9a1658f94c727d4cab8c8d9f4aece" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" @@ -4926,9 +5021,9 @@ checksum = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" [[package]] name = "vec_map" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "vek" @@ -4945,9 +5040,23 @@ dependencies = [ [[package]] name = "vek" -version = "0.10.0" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c98f7e1c1400d5b1704baee82cbc56a3fde406769555ead0f2306e43ebab967" +checksum = "4e44defd4e0c629bdc842e5d180dda428b3abd2c6b0c7e1fced8c718f65d5f77" +dependencies = [ + "approx 0.3.2", + "num-integer", + "num-traits 0.2.11", + "rustc_version", + "serde", + "static_assertions 1.1.0", +] + +[[package]] +name = "vek" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5c74ee8a1ab829cab2b921f45c6e3d9a02b99f973ae68775c05547debf20bdf" dependencies = [ "approx 0.3.2", "num-integer", @@ -4979,7 +5088,7 @@ dependencies = [ "num_cpus", "specs", "uvth", - "vek 0.10.0", + "vek 0.10.4", "veloren-common", ] @@ -5013,7 +5122,7 @@ dependencies = [ "specs", "specs-idvs", "sum_type", - "vek 0.10.0", + "vek 0.10.4", ] [[package]] @@ -5043,7 +5152,7 @@ dependencies = [ "specs", "specs-idvs", "uvth", - "vek 0.10.0", + "vek 0.10.4", "veloren-common", "veloren-world", ] @@ -5101,7 +5210,7 @@ dependencies = [ "specs-idvs", "treeculler", "uvth", - "vek 0.10.0", + "vek 0.10.4", "veloren-client", "veloren-common", "veloren-server", @@ -5120,7 +5229,7 @@ dependencies = [ "fxhash", "hashbrown", "image", - "itertools", + "itertools 0.8.2", "lazy_static", "log 0.4.8", "minifb", @@ -5136,10 +5245,27 @@ dependencies = [ "roots", "serde", "serde_derive", - "vek 0.10.0", + "vek 0.10.4", "veloren-common", ] +[[package]] +name = "veloren_network" +version = "0.1.0" +dependencies = [ + "async-std", + "bincode", + "futures 0.3.5", + "lazy_static", + "prometheus", + "rand 0.7.3", + "serde", + "tracing", + "tracing-futures", + "tracing-subscriber", + "uvth", +] + [[package]] name = "version_check" version = "0.1.5" @@ -5148,9 +5274,9 @@ checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" [[package]] name = "version_check" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" [[package]] name = "void" @@ -5198,9 +5324,9 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] name = "wasm-bindgen" -version = "0.2.59" +version = "0.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3557c397ab5a8e347d434782bcd31fc1483d927a6826804cec05cc792ee2519d" +checksum = "e3c7d40d09cdbf0f4895ae58cf57d92e1e57a9dd8ed2e8390514b54a47cc5551" dependencies = [ "cfg-if", "serde", @@ -5210,24 +5336,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.59" +version = "0.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0da9c9a19850d3af6df1cb9574970b566d617ecfaf36eb0b706b6f3ef9bd2f8" +checksum = "c3972e137ebf830900db522d6c8fd74d1900dcfc733462e9a12e942b00b4ac94" dependencies = [ "bumpalo", "lazy_static", "log 0.4.8", - "proc-macro2 1.0.9", - "quote 1.0.3", - "syn 1.0.16", + "proc-macro2 1.0.17", + "quote 1.0.6", + "syn 1.0.27", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.9" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "457414a91863c0ec00090dba537f88ab955d93ca6555862c29b6d860990b8a8a" +checksum = "8a369c5e1dfb7569e14d62af4da642a3cbc2f9a3652fe586e26ac22222aa4b04" dependencies = [ "cfg-if", "js-sys", @@ -5237,32 +5363,32 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.59" +version = "0.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f6fde1d36e75a714b5fe0cffbb78978f222ea6baebb726af13c78869fdb4205" +checksum = "2cd85aa2c579e8892442954685f0d801f9129de24fa2136b2c6a539c76b65776" dependencies = [ - "quote 1.0.3", + "quote 1.0.6", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.59" +version = "0.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bda4168030a6412ea8a047e27238cadf56f0e53516e1e83fec0a8b7c786f6d" +checksum = "8eb197bd3a47553334907ffd2f16507b4f4f01bbec3ac921a7719e0decdfe72a" dependencies = [ - "proc-macro2 1.0.9", - "quote 1.0.3", - "syn 1.0.16", + "proc-macro2 1.0.17", + "quote 1.0.6", + "syn 1.0.27", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.59" +version = "0.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc9f36ad51f25b0219a3d4d13b90eb44cd075dff8b6280cca015775d7acaddd8" +checksum = "a91c2916119c17a8e316507afaaa2dd94b47646048014bbdf6bef098c1bb58ad" [[package]] name = "wayland-client" @@ -5383,9 +5509,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.36" +version = "0.3.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721c6263e2c66fd44501cc5efbfa2b7dfa775d13e4ea38c46299646ed1f9c70a" +checksum = "8bc359e5dd3b46cb9687a051d50a2fdd228e4ba7cf6fcf861a5365c3d671a642" dependencies = [ "js-sys", "wasm-bindgen", @@ -5440,9 +5566,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ccfbf554c6ad11084fb7517daca16cfdcaccbdadba4fc336f032a8b12c2ad80" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ "winapi 0.3.8", ] @@ -5546,6 +5672,6 @@ checksum = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" [[package]] name = "xml-rs" -version = "0.8.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "541b12c998c5b56aa2b4e6f18f03664eef9a4fd0a246a55594efae6cc2d964b5" +checksum = "b07db065a5cf61a7e4ba64f29e67db906fb1787316516c4e6e5ff0fea1efcd8a" diff --git a/Cargo.toml b/Cargo.toml index 98990c57d5..a5fa2b3da3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "server-cli", "voxygen", "world", + "network", ] # default profile for devs, fast to compile, okay enough to run, no debug information @@ -24,6 +25,8 @@ incremental = true # All dependencies (but not this crate itself) [profile.dev.package."*"] opt-level = 3 +[profile.dev.package."veloren_network"] +opt-level = 2 [profile.dev.package."veloren-common"] opt-level = 2 [profile.dev.package."veloren-client"] diff --git a/network/Cargo.toml b/network/Cargo.toml new file mode 100644 index 0000000000..3c791e32b7 --- /dev/null +++ b/network/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "veloren_network" +version = "0.1.0" +authors = ["Marcel Märtens "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +#serialisation +bincode = "1.2" +serde = { version = "1.0" } +#sending +async-std = { version = "~1.5", features = ["std"] } +#tracing and metrics +tracing = { version = "0.1", default-features = false } +tracing-futures = "0.2" +prometheus = { version = "0.7", default-features = false } +#async +futures = { version = "0.3", features = ["thread-pool"] } +#mpsc channel registry +lazy_static = { version = "1.4", default-features = false } +rand = { version = "0.7" } + +[dev-dependencies] +tracing-subscriber = { version = "0.2.3", default-features = false, features = ["env-filter", "fmt", "chrono", "ansi", "smallvec"] } +uvth = { version = "3.1", default-features = false } \ No newline at end of file diff --git a/network/examples/chat/Cargo.lock b/network/examples/chat/Cargo.lock new file mode 100644 index 0000000000..8839dcce09 --- /dev/null +++ b/network/examples/chat/Cargo.lock @@ -0,0 +1,855 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi 0.3.8", +] + +[[package]] +name = "async-std" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "538ecb01eb64eecd772087e5b6f7540cbc917f047727339a472dafed2185b267" +dependencies = [ + "async-task", + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "futures-core", + "futures-io", + "futures-timer", + "kv-log-macro", + "log", + "memchr", + "mio", + "mio-uds", + "num_cpus", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "async-task" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ac2c016b079e771204030951c366db398864f5026f84a44dafb0ff20f02085d" +dependencies = [ + "libc", + "winapi 0.3.8", +] + +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" + +[[package]] +name = "bincode" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5753e2a71534719bf3f4e57006c3a4f0d2c672a4b676eec84161f763eca87dbf" +dependencies = [ + "byteorder", + "serde", +] + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "chrono" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2" +dependencies = [ + "num-integer", + "num-traits", + "time", +] + +[[package]] +name = "clap" +version = "2.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" +dependencies = [ + "bitflags", + "textwrap", + "unicode-width", +] + +[[package]] +name = "crossbeam-channel" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cced8691919c02aac3cb0a1bc2e9b73d89e832bf9a06fc579d4e71b68a2da061" +dependencies = [ + "crossbeam-utils", + "maybe-uninit", +] + +[[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", + "maybe-uninit", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "lazy_static", + "maybe-uninit", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +dependencies = [ + "autocfg", + "cfg-if", + "lazy_static", +] + +[[package]] +name = "fnv" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +dependencies = [ + "bitflags", + "fuchsia-zircon-sys", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" + +[[package]] +name = "futures" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c329ae8753502fb44ae4fc2b622fa2a94652c41e795143765ba0927f92ab780" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c77d04ce8edd9cb903932b608268b3fffec4163dc053b3b402bf47eac1f1a8" +dependencies = [ + "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-executor" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f674f3e1bcb15b37284a90cedf55afdba482ab061c407a9c0ebbd0f3109741ba" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", + "num_cpus", +] + +[[package]] +name = "futures-io" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a638959aa96152c7a4cddf50fcb1e3fede0583b27157c26e67d6f99904090dc6" + +[[package]] +name = "futures-macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a5081aa3de1f7542a794a397cde100ed903b0630152d0973479018fd85423a7" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[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-timer" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1de7508b218029b0f01662ed8f61b1c964b3ae99d6f25462d0f55a595109df6" + +[[package]] +name = "futures-util" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hermit-abi" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "725cf19794cf90aa94e65050cb4191ff5d8fa87a498383774c47b332e3af952e" +dependencies = [ + "libc", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "kv-log-macro" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c54d9f465d530a752e6ebdc217e081a7a614b48cb200f6f0aee21ba6bc9aabb" +dependencies = [ + "log", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" + +[[package]] +name = "log" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matchers" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" +dependencies = [ + "regex-automata", +] + +[[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", +] + +[[package]] +name = "mio" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f" +dependencies = [ + "cfg-if", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", + "libc", + "log", + "miow", + "net2", + "slab", + "winapi 0.2.8", +] + +[[package]] +name = "mio-uds" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" +dependencies = [ + "iovec", + "libc", + "mio", +] + +[[package]] +name = "miow" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +dependencies = [ + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", +] + +[[package]] +name = "net2" +version = "0.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" +dependencies = [ + "cfg-if", + "libc", + "winapi 0.3.8", +] + +[[package]] +name = "network-speed" +version = "0.1.0" +dependencies = [ + "async-std", + "bincode", + "clap", + "futures", + "serde", + "tracing", + "tracing-subscriber", + "veloren_network", +] + +[[package]] +name = "num-integer" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b" + +[[package]] +name = "pin-project" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f6a7f5eee6292c559c793430c55c00aea9d3b3d1905e855806ca4d7253426a2" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8988430ce790d8682672117bc06dda364c0be32d3abd738234f19f3240bad99a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "237844750cfbb86f67afe27eee600dfbbcb6188d734139b534cbfbf4f96792ae" + +[[package]] +name = "pin-utils" +version = "0.1.0-alpha.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" + +[[package]] +name = "ppv-lite86" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" + +[[package]] +name = "proc-macro-hack" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63" + +[[package]] +name = "proc-macro-nested" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e946095f9d3ed29ec38de908c22f95d9ac008e424c7bcae54c75a79c527c694" + +[[package]] +name = "proc-macro2" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "prometheus" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5567486d5778e2c6455b1b90ff1c558f29e751fc018130fa182e15828e728af1" +dependencies = [ + "cfg-if", + "fnv", + "lazy_static", + "quick-error", + "spin", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom", + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core", +] + +[[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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6946991529684867e47d86474e3a6d0c0ab9b82d5821e314b1ede31fa3a4b3" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", +] + +[[package]] +name = "regex-automata" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" +dependencies = [ + "byteorder", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "serde" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sharded-slab" +version = "0.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae75d0445b5d3778c9da3d1f840faa16d0627c8607f78a74daf69e5b988c39a1" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" + +[[package]] +name = "smallvec" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05720e22615919e4734f6a99ceae50d00226c3c5aca406e102ebc33298214e0a" + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "syn" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +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.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" +dependencies = [ + "libc", + "redox_syscall", + "winapi 0.3.8", +] + +[[package]] +name = "tracing" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1721cc8cf7d770cc4257872507180f35a4797272f5962f24c806af9e7faf52ab" +dependencies = [ + "cfg-if", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aa83a9a47081cd522c09c81b31aec2c9273424976f922ad61c053b58350b715" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "tracing-futures" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58b0b7fd92dc7b71f29623cc6836dd7200f32161a2313dd78be233a8405694f6" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "tracing-subscriber" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfc50df245be6f0adf35c399cb16dea60e2c7d6cc83ff5dc22d727df06dd6f0c" +dependencies = [ + "ansi_term", + "chrono", + "lazy_static", + "matchers", + "regex", + "sharded-slab", + "smallvec", + "tracing-core", +] + +[[package]] +name = "unicode-width" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" + +[[package]] +name = "veloren_network" +version = "0.1.0" +dependencies = [ + "async-std", + "bincode", + "futures", + "lazy_static", + "prometheus", + "rand", + "serde", + "tracing", + "tracing-futures", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] diff --git a/network/examples/chat/Cargo.toml b/network/examples/chat/Cargo.toml new file mode 100644 index 0000000000..a5291966cf --- /dev/null +++ b/network/examples/chat/Cargo.toml @@ -0,0 +1,19 @@ +[workspace] + +[package] +name = "network-speed" +version = "0.1.0" +authors = ["Marcel Märtens "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +network = { package = "veloren_network", path = "../../../network" } +clap = { version = "2.33", default-features = false } +async-std = { version = "1.5", default-features = false } +futures = { version = "0.3", default-features = false } +tracing = { version = "0.1", default-features = false } +tracing-subscriber = { version = "0.2.3", default-features = false, features = ["env-filter", "fmt", "chrono", "ansi", "smallvec"] } +bincode = "1.2" +serde = { version = "1.0", features = ["derive"] } \ No newline at end of file diff --git a/network/examples/chat/src/main.rs b/network/examples/chat/src/main.rs new file mode 100644 index 0000000000..a3a4fabcab --- /dev/null +++ b/network/examples/chat/src/main.rs @@ -0,0 +1,189 @@ +//!run with +//! ```bash +//! (cd network/examples/chat && RUST_BACKTRACE=1 cargo run --release -- --trace=info --port 15006) +//! (cd network/examples/chat && RUST_BACKTRACE=1 cargo run --release -- --trace=info --port 15006 --mode=client) +//! ``` +use async_std::io; +use clap::{App, Arg}; +use futures::executor::{block_on, ThreadPool}; +use network::{Address, Network, Participant, Pid, PROMISES_CONSISTENCY, PROMISES_ORDERED}; +use std::{sync::Arc, thread, time::Duration}; +use tracing::*; +use tracing_subscriber::EnvFilter; + +///This example contains a simple chatserver, that allows to send messages +/// between participants, it's neither pretty nor perfect, but it should show how to integrate network +fn main() { + let matches = App::new("Chat example") + .version("0.1.0") + .author("Marcel Märtens ") + .about("example chat implemented with veloren-network") + .arg( + Arg::with_name("mode") + .short("m") + .long("mode") + .takes_value(true) + .possible_values(&["server", "client", "both"]) + .default_value("both") + .help( + "choose whether you want to start the server or client or both needed for \ + this program", + ), + ) + .arg( + Arg::with_name("port") + .short("p") + .long("port") + .takes_value(true) + .default_value("52000") + .help("port to listen on"), + ) + .arg( + Arg::with_name("ip") + .long("ip") + .takes_value(true) + .default_value("127.0.0.1") + .help("ip to listen and connect to"), + ) + .arg( + Arg::with_name("protocol") + .long("protocol") + .takes_value(true) + .default_value("tcp") + .possible_values(&["tcp", "upd", "mpsc"]) + .help( + "underlying protocol used for this test, mpsc can only combined with mode=both", + ), + ) + .arg( + Arg::with_name("trace") + .short("t") + .long("trace") + .takes_value(true) + .default_value("warn") + .possible_values(&["trace", "debug", "info", "warn", "error"]) + .help("set trace level, not this has a performance impact!"), + ) + .get_matches(); + + let trace = matches.value_of("trace").unwrap(); + let filter = EnvFilter::from_default_env().add_directive(trace.parse().unwrap()); + tracing_subscriber::FmtSubscriber::builder() + .with_max_level(Level::TRACE) + .with_env_filter(filter) + .init(); + + let port: u16 = matches.value_of("port").unwrap().parse().unwrap(); + let ip: &str = matches.value_of("ip").unwrap(); + let address = match matches.value_of("protocol") { + Some("tcp") => Address::Tcp(format!("{}:{}", ip, port).parse().unwrap()), + Some("udp") => Address::Udp(format!("{}:{}", ip, port).parse().unwrap()), + _ => panic!("invalid mode, run --help!"), + }; + + let mut background = None; + match matches.value_of("mode") { + Some("server") => server(address), + Some("client") => client(address), + Some("both") => { + let address1 = address.clone(); + background = Some(thread::spawn(|| server(address1))); + thread::sleep(Duration::from_millis(200)); //start client after server + client(address) + }, + _ => panic!("invalid mode, run --help!"), + }; + if let Some(background) = background { + background.join().unwrap(); + } +} + +fn server(address: Address) { + let (server, f) = Network::new(Pid::new(), None); + let server = Arc::new(server); + std::thread::spawn(f); + let pool = ThreadPool::new().unwrap(); + block_on(async { + server.listen(address).await.unwrap(); + loop { + let p1 = server.connected().await.unwrap(); + let server1 = server.clone(); + pool.spawn_ok(client_connection(server1, p1)); + } + }); +} + +async fn client_connection(network: Arc, participant: Arc) { + let mut s1 = participant.opened().await.unwrap(); + let username = s1.recv::().await.unwrap(); + println!("[{}] connected", username); + loop { + match s1.recv::().await { + Err(_) => { + break; + }, + Ok(msg) => { + println!("[{}]: {}", username, msg); + let mut parts = network.participants().await; + for (_, p) in parts.drain() { + match p + .open(32, PROMISES_ORDERED | PROMISES_CONSISTENCY) + .await { + Err(_) => { + //probably disconnected, remove it + network.disconnect(p).await.unwrap(); + }, + Ok(mut s) => s.send((username.clone(), msg.clone())).unwrap(), + }; + } + }, + } + } + println!("[{}] disconnected", username); +} + +fn client(address: Address) { + let (client, f) = Network::new(Pid::new(), None); + std::thread::spawn(f); + let pool = ThreadPool::new().unwrap(); + + block_on(async { + let p1 = client.connect(address.clone()).await.unwrap(); //remote representation of p1 + let mut s1 = p1 + .open(16, PROMISES_ORDERED | PROMISES_CONSISTENCY) + .await + .unwrap(); //remote representation of s1 + println!("Enter your username:"); + let mut username = String::new(); + io::stdin().read_line(&mut username).await.unwrap(); + username = username.split_whitespace().collect(); + println!("Your username is: {}", username); + println!("write /quit to close"); + pool.spawn_ok(read_messages(p1)); + s1.send(username).unwrap(); + loop { + let mut line = String::new(); + io::stdin().read_line(&mut line).await.unwrap(); + line = line.split_whitespace().collect(); + if line.as_str() == "/quit" { + println!("goodbye"); + break; + } else { + s1.send(line).unwrap(); + } + } + }); + thread::sleep(Duration::from_millis(30)); // TODO: still needed for correct shutdown +} + +// I am quite lazy, the sending is done in a single stream above, but for +// receiving i open and close a stream per message. this can be done easier but +// this allows me to be quite lazy on the server side and just get a list of +// all participants and send to them... +async fn read_messages(participant: Arc) { + while let Ok(mut s) = participant.opened().await { + let (username, message) = s.recv::<(String, String)>().await.unwrap(); + println!("[{}]: {}", username, message); + } + println!("gracefully shut down"); +} diff --git a/network/examples/fileshare/Cargo.lock b/network/examples/fileshare/Cargo.lock new file mode 100644 index 0000000000..de5da54e7e --- /dev/null +++ b/network/examples/fileshare/Cargo.lock @@ -0,0 +1,946 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi 0.3.8", +] + +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "arrayvec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" + +[[package]] +name = "async-std" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "538ecb01eb64eecd772087e5b6f7540cbc917f047727339a472dafed2185b267" +dependencies = [ + "async-task", + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "futures-core", + "futures-io", + "futures-timer", + "kv-log-macro", + "log", + "memchr", + "mio", + "mio-uds", + "num_cpus", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "async-task" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ac2c016b079e771204030951c366db398864f5026f84a44dafb0ff20f02085d" +dependencies = [ + "libc", + "winapi 0.3.8", +] + +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" + +[[package]] +name = "base64" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" + +[[package]] +name = "bincode" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5753e2a71534719bf3f4e57006c3a4f0d2c672a4b676eec84161f763eca87dbf" +dependencies = [ + "byteorder", + "serde", +] + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "blake2b_simd" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq", +] + +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "chrono" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2" +dependencies = [ + "num-integer", + "num-traits", + "time", +] + +[[package]] +name = "clap" +version = "2.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" +dependencies = [ + "bitflags", + "textwrap", + "unicode-width", +] + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "crossbeam-channel" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cced8691919c02aac3cb0a1bc2e9b73d89e832bf9a06fc579d4e71b68a2da061" +dependencies = [ + "crossbeam-utils", + "maybe-uninit", +] + +[[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", + "maybe-uninit", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "lazy_static", + "maybe-uninit", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +dependencies = [ + "autocfg", + "cfg-if", + "lazy_static", +] + +[[package]] +name = "dirs" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" +dependencies = [ + "cfg-if", + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b" +dependencies = [ + "cfg-if", + "libc", + "redox_users", + "winapi 0.3.8", +] + +[[package]] +name = "fileshare" +version = "0.1.0" +dependencies = [ + "async-std", + "bincode", + "clap", + "futures", + "rand", + "serde", + "shellexpand", + "tracing", + "tracing-subscriber", + "veloren_network", +] + +[[package]] +name = "fnv" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +dependencies = [ + "bitflags", + "fuchsia-zircon-sys", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" + +[[package]] +name = "futures" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c329ae8753502fb44ae4fc2b622fa2a94652c41e795143765ba0927f92ab780" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c77d04ce8edd9cb903932b608268b3fffec4163dc053b3b402bf47eac1f1a8" +dependencies = [ + "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-executor" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f674f3e1bcb15b37284a90cedf55afdba482ab061c407a9c0ebbd0f3109741ba" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", + "num_cpus", +] + +[[package]] +name = "futures-io" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a638959aa96152c7a4cddf50fcb1e3fede0583b27157c26e67d6f99904090dc6" + +[[package]] +name = "futures-macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a5081aa3de1f7542a794a397cde100ed903b0630152d0973479018fd85423a7" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[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-timer" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1de7508b218029b0f01662ed8f61b1c964b3ae99d6f25462d0f55a595109df6" + +[[package]] +name = "futures-util" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hermit-abi" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "725cf19794cf90aa94e65050cb4191ff5d8fa87a498383774c47b332e3af952e" +dependencies = [ + "libc", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "kv-log-macro" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c54d9f465d530a752e6ebdc217e081a7a614b48cb200f6f0aee21ba6bc9aabb" +dependencies = [ + "log", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" + +[[package]] +name = "log" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matchers" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" +dependencies = [ + "regex-automata", +] + +[[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", +] + +[[package]] +name = "mio" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f" +dependencies = [ + "cfg-if", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", + "libc", + "log", + "miow", + "net2", + "slab", + "winapi 0.2.8", +] + +[[package]] +name = "mio-uds" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" +dependencies = [ + "iovec", + "libc", + "mio", +] + +[[package]] +name = "miow" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +dependencies = [ + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", +] + +[[package]] +name = "net2" +version = "0.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" +dependencies = [ + "cfg-if", + "libc", + "winapi 0.3.8", +] + +[[package]] +name = "num-integer" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b" + +[[package]] +name = "pin-project" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f6a7f5eee6292c559c793430c55c00aea9d3b3d1905e855806ca4d7253426a2" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8988430ce790d8682672117bc06dda364c0be32d3abd738234f19f3240bad99a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "237844750cfbb86f67afe27eee600dfbbcb6188d734139b534cbfbf4f96792ae" + +[[package]] +name = "pin-utils" +version = "0.1.0-alpha.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" + +[[package]] +name = "ppv-lite86" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" + +[[package]] +name = "proc-macro-hack" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63" + +[[package]] +name = "proc-macro-nested" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e946095f9d3ed29ec38de908c22f95d9ac008e424c7bcae54c75a79c527c694" + +[[package]] +name = "proc-macro2" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "prometheus" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5567486d5778e2c6455b1b90ff1c558f29e751fc018130fa182e15828e728af1" +dependencies = [ + "cfg-if", + "fnv", + "lazy_static", + "quick-error", + "spin", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom", + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" + +[[package]] +name = "redox_users" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431" +dependencies = [ + "getrandom", + "redox_syscall", + "rust-argon2", +] + +[[package]] +name = "regex" +version = "1.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6946991529684867e47d86474e3a6d0c0ab9b82d5821e314b1ede31fa3a4b3" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", +] + +[[package]] +name = "regex-automata" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" +dependencies = [ + "byteorder", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" + +[[package]] +name = "rust-argon2" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017" +dependencies = [ + "base64", + "blake2b_simd", + "constant_time_eq", + "crossbeam-utils", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "serde" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sharded-slab" +version = "0.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae75d0445b5d3778c9da3d1f840faa16d0627c8607f78a74daf69e5b988c39a1" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shellexpand" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2b22262a9aaf9464d356f656fea420634f78c881c5eebd5ef5e66d8b9bc603" +dependencies = [ + "dirs", +] + +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" + +[[package]] +name = "smallvec" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05720e22615919e4734f6a99ceae50d00226c3c5aca406e102ebc33298214e0a" + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "syn" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +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.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" +dependencies = [ + "libc", + "redox_syscall", + "winapi 0.3.8", +] + +[[package]] +name = "tracing" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1721cc8cf7d770cc4257872507180f35a4797272f5962f24c806af9e7faf52ab" +dependencies = [ + "cfg-if", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aa83a9a47081cd522c09c81b31aec2c9273424976f922ad61c053b58350b715" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "tracing-futures" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58b0b7fd92dc7b71f29623cc6836dd7200f32161a2313dd78be233a8405694f6" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "tracing-subscriber" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfc50df245be6f0adf35c399cb16dea60e2c7d6cc83ff5dc22d727df06dd6f0c" +dependencies = [ + "ansi_term", + "chrono", + "lazy_static", + "matchers", + "regex", + "sharded-slab", + "smallvec", + "tracing-core", +] + +[[package]] +name = "unicode-width" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" + +[[package]] +name = "veloren_network" +version = "0.1.0" +dependencies = [ + "async-std", + "bincode", + "futures", + "lazy_static", + "prometheus", + "rand", + "serde", + "tracing", + "tracing-futures", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] diff --git a/network/examples/fileshare/Cargo.toml b/network/examples/fileshare/Cargo.toml new file mode 100644 index 0000000000..492985e51a --- /dev/null +++ b/network/examples/fileshare/Cargo.toml @@ -0,0 +1,21 @@ +[workspace] + +[package] +name = "fileshare" +version = "0.1.0" +authors = ["Marcel Märtens "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +network = { package = "veloren_network", path = "../../../network" } +clap = { version = "2.33", default-features = false } +async-std = { version = "1.5", default-features = false } +futures = { version = "0.3", default-features = false } +tracing = { version = "0.1", default-features = false } +tracing-subscriber = { version = "0.2.3", default-features = false, features = ["env-filter", "fmt", "chrono", "ansi", "smallvec"] } +bincode = "1.2" +serde = { version = "1.0", features = ["derive"] } +rand = "0.7.3" +shellexpand = "2.0.0" \ No newline at end of file diff --git a/network/examples/fileshare/src/commands.rs b/network/examples/fileshare/src/commands.rs new file mode 100644 index 0000000000..99178ea018 --- /dev/null +++ b/network/examples/fileshare/src/commands.rs @@ -0,0 +1,87 @@ +use async_std::{ + fs, + path::{Path, PathBuf}, +}; +use network::{Address, Participant, Stream}; +use rand::Rng; +use serde::{Deserialize, Serialize}; + +use std::{collections::HashMap, sync::Arc}; + +#[derive(Debug)] +pub enum LocalCommand { + Shutdown, + Disconnect, + Connect(Address), + List, + Serve(FileInfo), + Get(u32, Option), +} + +#[derive(Serialize, Deserialize, Debug)] +pub enum Command { + List, + Get(u32), +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +pub struct FileInfo { + id: u32, + pub path: String, + pub size: u64, + pub hash: String, +} + +pub struct RemoteInfo { + infos: HashMap, + _participant: Arc, + pub cmd_out: Stream, + pub file_out: Stream, +} + +impl FileInfo { + pub async fn new(path: &Path) -> Option { + let mt = match fs::metadata(&path).await { + Err(e) => { + println!( + "cannot get metadata for file: {:?}, does it exist? Error: {:?}", + &path, &e + ); + return None; + }, + Ok(mt) => mt, + }; + let size = mt.len(); + Some(Self { + id: rand::thread_rng().gen(), + path: path.as_os_str().to_os_string().into_string().unwrap(), + size, + hash: "".to_owned(), + }) + } + + pub async fn load(&self) -> Result, std::io::Error> { fs::read(self.path()).await } + + pub fn id(&self) -> u32 { self.id } + + pub fn path(&self) -> PathBuf { self.path.parse().unwrap() } +} + +impl RemoteInfo { + pub fn new(cmd_out: Stream, file_out: Stream, participant: Arc) -> Self { + Self { + infos: HashMap::new(), + _participant: participant, + cmd_out, + file_out, + } + } + + pub fn get_info(&self, id: u32) -> Option { self.infos.get(&id).map(|fi| fi.clone()) } + + pub fn insert_infos(&mut self, mut fi: Vec) { + for fi in fi.drain(..) { + self.infos.insert(fi.id(), fi); + } + } +} diff --git a/network/examples/fileshare/src/main.rs b/network/examples/fileshare/src/main.rs new file mode 100644 index 0000000000..5647dfaf07 --- /dev/null +++ b/network/examples/fileshare/src/main.rs @@ -0,0 +1,204 @@ +#![feature(async_closure, exclusive_range_pattern)] +//!run with +//! (cd network/examples/fileshare && RUST_BACKTRACE=1 cargo run --profile=release -Z unstable-options -- --trace=info --port 15006) +//! (cd network/examples/fileshare && RUST_BACKTRACE=1 cargo run --profile=release -Z unstable-options -- --trace=info --port 15007) +//! ``` +use async_std::{io, path::PathBuf}; +use clap::{App, Arg, SubCommand}; +use futures::{ + channel::mpsc, + executor::{block_on, ThreadPool}, + sink::SinkExt, +}; +use network::Address; +use std::{thread, time::Duration}; +use tracing::*; +use tracing_subscriber::EnvFilter; +mod commands; +mod server; +use commands::{FileInfo, LocalCommand}; +use server::Server; + +fn main() { + let matches = App::new("File Server") + .version("0.1.0") + .author("Marcel Märtens ") + .about("example file server implemented with veloren-network") + .arg( + Arg::with_name("port") + .short("p") + .long("port") + .takes_value(true) + .default_value("15006") + .help("port to listen on"), + ) + .arg( + Arg::with_name("trace") + .short("t") + .long("trace") + .takes_value(true) + .default_value("warn") + .possible_values(&["trace", "debug", "info", "warn", "error"]) + .help("set trace level, not this has a performance impact!"), + ) + .get_matches(); + + let trace = matches.value_of("trace").unwrap(); + let filter = EnvFilter::from_default_env() + .add_directive(trace.parse().unwrap()) + .add_directive("fileshare::server=trace".parse().unwrap()) + .add_directive("fileshare::commands=trace".parse().unwrap()); + tracing_subscriber::FmtSubscriber::builder() + .with_max_level(Level::TRACE) + .with_env_filter(filter) + .init(); + + let port: u16 = matches.value_of("port").unwrap().parse().unwrap(); + let address = Address::Tcp(format!("{}:{}", "127.0.0.1", port).parse().unwrap()); + + let (server, cmd_sender) = Server::new(); + let pool = ThreadPool::new().unwrap(); + pool.spawn_ok(server.run(address)); + + thread::sleep(Duration::from_millis(50)); //just for trace + + block_on(client(cmd_sender)); +} + +fn file_exists(file: String) -> Result<(), String> { + let file: std::path::PathBuf = shellexpand::tilde(&file).parse().unwrap(); + if file.exists() { + Ok(()) + } else { + Err(format!("file does not exist")) + } +} + +fn get_options<'a, 'b>() -> App<'a, 'b> { + App::new("") + .setting(clap::AppSettings::NoBinaryName) + .setting(clap::AppSettings::SubcommandRequired) + .setting(clap::AppSettings::VersionlessSubcommands) + .setting(clap::AppSettings::SubcommandRequiredElseHelp) + .setting(clap::AppSettings::ColorAuto) + .subcommand(SubCommand::with_name("quit").about("closes program")) + .subcommand(SubCommand::with_name("disconnect").about("stop connections to all servers")) + .subcommand(SubCommand::with_name("t").about("quick test by connectiong to 127.0.0.1:1231")) + .subcommand( + SubCommand::with_name("connect") + .about("opens a connection to another instance of this fileserver network") + .setting(clap::AppSettings::NoBinaryName) + .arg( + Arg::with_name("ip:port") + .help("ip and port to connect to, example '127.0.0.1:1231'") + .required(true) + .validator(|ipport| match ipport.parse::() { + Ok(_) => Ok(()), + Err(e) => Err(format!("must be valid Ip:Port combination {:?}", e)), + }), + ), + ) + .subcommand(SubCommand::with_name("list").about("lists all available files on the network")) + .subcommand( + SubCommand::with_name("serve") + .about("make file available on the network") + .arg( + Arg::with_name("file") + .help("file to serve") + .required(true) + .validator(file_exists), + ), + ) + .subcommand( + SubCommand::with_name("get") + .about( + "downloads file with the id from the `list` command. Optionally provide a \ + storage path, if none is provided it will be saved in the current directory \ + with the remote filename", + ) + .arg( + Arg::with_name("id") + .help("id to download. get the id from the `list` command") + .required(true) + .validator(|id| match id.parse::() { + Ok(_) => Ok(()), + Err(e) => Err(format!("must be a number {:?}", e)), + }), + ) + .arg(Arg::with_name("file").help("local path to store the file to")), + ) +} + +async fn client(mut cmd_sender: mpsc::UnboundedSender) { + use std::io::Write; + + loop { + let mut line = String::new(); + print!("==> "); + std::io::stdout().flush().unwrap(); + io::stdin().read_line(&mut line).await.unwrap(); + let matches = match get_options().get_matches_from_safe(line.split_whitespace()) { + Err(e) => { + println!("{}", e.message); + continue; + }, + Ok(matches) => matches, + }; + + match matches.subcommand() { + ("quit", _) => { + cmd_sender.send(LocalCommand::Shutdown).await.unwrap(); + println!("goodbye"); + break; + }, + ("disconnect", _) => { + cmd_sender.send(LocalCommand::Disconnect).await.unwrap(); + }, + ("connect", Some(connect_matches)) => { + let socketaddr = connect_matches.value_of("ip:port").unwrap().parse().unwrap(); + cmd_sender + .send(LocalCommand::Connect(Address::Tcp(socketaddr))) + .await + .unwrap(); + }, + ("t", _) => { + cmd_sender + .send(LocalCommand::Connect(Address::Tcp( + "127.0.0.1:1231".parse().unwrap(), + ))) + .await + .unwrap(); + }, + ("serve", Some(serve_matches)) => { + let path = shellexpand::tilde(serve_matches.value_of("file").unwrap()); + let path: PathBuf = path.parse().unwrap(); + if let Some(fileinfo) = FileInfo::new(&path).await { + cmd_sender + .send(LocalCommand::Serve(fileinfo)) + .await + .unwrap(); + } + }, + ("list", _) => { + cmd_sender.send(LocalCommand::List).await.unwrap(); + }, + ("get", Some(get_matches)) => { + let id: u32 = get_matches.value_of("id").unwrap().parse().unwrap(); + let file = get_matches.value_of("file"); + cmd_sender + .send(LocalCommand::Get(id, file.map(|s| s.to_string()))) + .await + .unwrap(); + }, + + (_, _) => { + unreachable!("this subcommand isn't yet handled"); + }, + } + // this 100 ms is because i am super lazy, and i want to appear the logs before + // the next '==>' appears... + thread::sleep(Duration::from_millis(100)); + println!(""); + } + thread::sleep(Duration::from_millis(30)); // TODO: still needed for correct shutdown +} diff --git a/network/examples/fileshare/src/server.rs b/network/examples/fileshare/src/server.rs new file mode 100644 index 0000000000..f6312a58b1 --- /dev/null +++ b/network/examples/fileshare/src/server.rs @@ -0,0 +1,213 @@ +use crate::commands::{Command, FileInfo, LocalCommand, RemoteInfo}; +use async_std::{ + fs, + path::PathBuf, + sync::{Mutex, RwLock}, +}; +use futures::{channel::mpsc, future::FutureExt, stream::StreamExt}; +use network::{Address, Network, Participant, Pid, Stream, PROMISES_CONSISTENCY, PROMISES_ORDERED}; +use std::{collections::HashMap, sync::Arc}; +use tracing::*; + +#[derive(Debug)] +struct ControlChannels { + command_receiver: mpsc::UnboundedReceiver, +} + +pub struct Server { + run_channels: Option, + network: Network, + served: RwLock>, + remotes: RwLock>>>, + receiving_files: Mutex>>, +} + +impl Server { + pub fn new() -> (Self, mpsc::UnboundedSender) { + let (command_sender, command_receiver) = mpsc::unbounded(); + + let (network, f) = Network::new(Pid::new(), None); + std::thread::spawn(f); + + let run_channels = Some(ControlChannels { command_receiver }); + ( + Server { + run_channels, + network, + served: RwLock::new(vec![]), + remotes: RwLock::new(HashMap::new()), + receiving_files: Mutex::new(HashMap::new()), + }, + command_sender, + ) + } + + pub async fn run(mut self, address: Address) { + let run_channels = self.run_channels.take().unwrap(); + + self.network.listen(address).await.unwrap(); + + futures::join!( + self.command_manager(run_channels.command_receiver,), + self.connect_manager(), + ); + } + + async fn command_manager(&self, command_receiver: mpsc::UnboundedReceiver) { + trace!("start command_manager"); + command_receiver + .for_each_concurrent(None, async move |cmd| { + match cmd { + LocalCommand::Shutdown => { + println!("shutting down service"); + return; + }, + LocalCommand::Disconnect => { + self.remotes.write().await.clear(); + for (_, p) in self.network.participants().await.drain() { + self.network.disconnect(p).await.unwrap(); + } + println!("disconnecting all connections"); + return; + }, + LocalCommand::Connect(addr) => { + println!("trying to connect to: {:?}", &addr); + match self.network.connect(addr.clone()).await { + Ok(p) => self.loop_participant(p).await, + Err(e) => { + println!("failled to connect to {:?}, err: {:?}", &addr, e); + }, + } + }, + LocalCommand::Serve(fileinfo) => { + self.served.write().await.push(fileinfo.clone()); + println!("serving file: {:?}", fileinfo.path); + }, + LocalCommand::List => { + let mut total_file_infos = vec![]; + for ri in self.remotes.read().await.values() { + let mut ri = ri.lock().await; + ri.cmd_out.send(Command::List).unwrap(); + let mut file_infos = ri.cmd_out.recv::>().await.unwrap(); + ri.insert_infos(file_infos.clone()); + total_file_infos.append(&mut file_infos); + } + print_fileinfos(&total_file_infos); + }, + LocalCommand::Get(id, path) => { + // i dont know the owner, just broadcast, i am laaaazyyy + for ri in self.remotes.read().await.values() { + let mut ri = ri.lock().await; + if ri.get_info(id).is_some() { + //found provider, send request. + self.receiving_files.lock().await.insert(id, path.clone()); + ri.cmd_out.send(Command::Get(id)).unwrap(); + // the answer is handled via the other stream! + break; + } + } + }, + } + }) + .await; + trace!("stop command_manager"); + } + + async fn connect_manager(&self) { + trace!("start connect_manager"); + let iter = futures::stream::unfold((), |_| { + self.network.connected().map(|r| r.ok().map(|v| (v, ()))) + }); + + iter.for_each_concurrent(/* limit */ None, async move |participant| { + self.loop_participant(participant).await; + }) + .await; + trace!("stop connect_manager"); + } + + async fn loop_participant(&self, p: Arc) { + if let (Ok(cmd_out), Ok(file_out), Ok(cmd_in), Ok(file_in)) = ( + p.open(15, PROMISES_CONSISTENCY | PROMISES_ORDERED).await, + p.open(40, PROMISES_CONSISTENCY).await, + p.opened().await, + p.opened().await, + ) { + debug!(?p, "connection successfully initiated"); + let id = p.remote_pid(); + let ri = Arc::new(Mutex::new(RemoteInfo::new(cmd_out, file_out, p))); + self.remotes.write().await.insert(id, ri.clone()); + futures::join!( + self.handle_remote_cmd(cmd_in, ri.clone()), + self.handle_files(file_in, ri.clone()), + ); + } + } + + async fn handle_remote_cmd(&self, mut stream: Stream, remote_info: Arc>) { + while let Ok(msg) = stream.recv::().await { + println!("got message: {:?}", &msg); + match msg { + Command::List => { + info!("request to send my list"); + let served = self.served.read().await.clone(); + stream.send(served).unwrap(); + }, + Command::Get(id) => { + for file_info in self.served.read().await.iter() { + if file_info.id() == id { + info!("request to send file i got, sending it"); + if let Ok(data) = file_info.load().await { + match remote_info.lock().await.file_out.send((file_info, data)) { + Ok(_) => debug!("send file"), + Err(e) => error!(?e, "sending file failed"), + } + } else { + warn!("cannot send file as loading failed, oes it still exist?"); + } + } + } + }, + } + } + } + + async fn handle_files(&self, mut stream: Stream, _remote_info: Arc>) { + while let Ok((fi, data)) = stream.recv::<(FileInfo, Vec)>().await { + debug!(?fi, "got file"); + let path = self.receiving_files.lock().await.remove(&fi.id()).flatten(); + let path: PathBuf = match &path { + Some(path) => shellexpand::tilde(&path).parse().unwrap(), + None => { + let mut path = std::env::current_dir().unwrap(); + path.push(fi.path().file_name().unwrap()); + trace!("no path provided, saving down to {:?}", path); + PathBuf::from(path) + }, + }; + debug!("received file, going to save it under {:?}", path); + fs::write(path, data).await.unwrap(); + } + } +} + +fn print_fileinfos(infos: &Vec) { + let mut i = 0; + for info in infos { + let bytes = info.size; + match bytes { + 0..100_000 => println!("{}: {}bytes '{}'", info.id(), bytes, info.path), + 100_000..100_000_000 => { + println!("{}: {}bytes '{}'", info.id(), bytes / 1024, info.path) + }, + _ => println!( + "{}: {}bytes '{}'", + info.id(), + bytes / 1024 / 1024, + info.path + ), + } + i += 1; + } + println!("-- {} files available", i); +} diff --git a/network/examples/network-speed/Cargo.lock b/network/examples/network-speed/Cargo.lock new file mode 100644 index 0000000000..58b125e281 --- /dev/null +++ b/network/examples/network-speed/Cargo.lock @@ -0,0 +1,937 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi 0.3.8", +] + +[[package]] +name = "ascii" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbf56136a5198c7b01a49e3afcbef6cf84597273d298f54432926024107b0109" + +[[package]] +name = "async-std" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "538ecb01eb64eecd772087e5b6f7540cbc917f047727339a472dafed2185b267" +dependencies = [ + "async-task", + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "futures-core", + "futures-io", + "futures-timer", + "kv-log-macro", + "log", + "memchr", + "mio", + "mio-uds", + "num_cpus", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "async-task" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ac2c016b079e771204030951c366db398864f5026f84a44dafb0ff20f02085d" +dependencies = [ + "libc", + "winapi 0.3.8", +] + +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" + +[[package]] +name = "bincode" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5753e2a71534719bf3f4e57006c3a4f0d2c672a4b676eec84161f763eca87dbf" +dependencies = [ + "byteorder", + "serde", +] + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "chrono" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2" +dependencies = [ + "num-integer", + "num-traits", + "time", +] + +[[package]] +name = "chunked_transfer" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b89647f09b9f4c838cb622799b2843e4e13bff64661dab9a0362bb92985addd" + +[[package]] +name = "clap" +version = "2.33.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129" +dependencies = [ + "bitflags", + "textwrap", + "unicode-width", +] + +[[package]] +name = "crossbeam-channel" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cced8691919c02aac3cb0a1bc2e9b73d89e832bf9a06fc579d4e71b68a2da061" +dependencies = [ + "crossbeam-utils", + "maybe-uninit", +] + +[[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", + "maybe-uninit", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "lazy_static", + "maybe-uninit", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +dependencies = [ + "autocfg", + "cfg-if", + "lazy_static", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +dependencies = [ + "bitflags", + "fuchsia-zircon-sys", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" + +[[package]] +name = "futures" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e05b85ec287aac0dc34db7d4a569323df697f9c55b99b15d6b4ef8cde49f613" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399" + +[[package]] +name = "futures-executor" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d6bb888be1153d3abeb9006b11b02cf5e9b209fda28693c31ae1e4e012e314" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", + "num_cpus", +] + +[[package]] +name = "futures-io" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789" + +[[package]] +name = "futures-macro" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc" + +[[package]] +name = "futures-task" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626" +dependencies = [ + "once_cell", +] + +[[package]] +name = "futures-timer" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1de7508b218029b0f01662ed8f61b1c964b3ae99d6f25462d0f55a595109df6" + +[[package]] +name = "futures-util" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project", + "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hermit-abi" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71" +dependencies = [ + "libc", +] + +[[package]] +name = "idna" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "kv-log-macro" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ff57d6d215f7ca7eb35a9a64d656ba4d9d2bef114d741dc08048e75e2f5d418" +dependencies = [ + "log", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" + +[[package]] +name = "log" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matchers" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" +dependencies = [ + "regex-automata", +] + +[[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", +] + +[[package]] +name = "mio" +version = "0.6.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" +dependencies = [ + "cfg-if", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", + "libc", + "log", + "miow", + "net2", + "slab", + "winapi 0.2.8", +] + +[[package]] +name = "mio-uds" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" +dependencies = [ + "iovec", + "libc", + "mio", +] + +[[package]] +name = "miow" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +dependencies = [ + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", +] + +[[package]] +name = "net2" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7" +dependencies = [ + "cfg-if", + "libc", + "winapi 0.3.8", +] + +[[package]] +name = "network-speed" +version = "0.1.0" +dependencies = [ + "bincode", + "clap", + "futures", + "prometheus", + "serde", + "tiny_http", + "tracing", + "tracing-subscriber", + "veloren_network", +] + +[[package]] +name = "num-integer" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pin-project" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edc93aeee735e60ecb40cf740eb319ff23eab1c5748abfdb5c180e4ce49f7791" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e58db2081ba5b4c93bd6be09c40fd36cb9193a8336c384f3b40012e531aa7e40" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9df32da11d84f3a7d70205549562966279adb900e080fad3dccd8e64afccf0ad" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" + +[[package]] +name = "proc-macro-hack" +version = "0.5.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4" + +[[package]] +name = "proc-macro-nested" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e946095f9d3ed29ec38de908c22f95d9ac008e424c7bcae54c75a79c527c694" + +[[package]] +name = "proc-macro2" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "prometheus" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5567486d5778e2c6455b1b90ff1c558f29e751fc018130fa182e15828e728af1" +dependencies = [ + "cfg-if", + "fnv", + "lazy_static", + "protobuf", + "quick-error", + "spin", +] + +[[package]] +name = "protobuf" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e86d370532557ae7573551a1ec8235a0f8d6cb276c7c9e6aa490b511c447485" + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom", + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core", +] + +[[package]] +name = "regex" +version = "1.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", +] + +[[package]] +name = "regex-automata" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" +dependencies = [ + "byteorder", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "serde" +version = "1.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9124df5b40cbd380080b2cc6ab894c040a3070d995f5c9dc77e18c34a8ae37d" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f2c3ac8e6ca1e9c80b8be1023940162bf81ae3cffbb1809474152f2ce1eb250" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sharded-slab" +version = "0.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06d5a3f5166fb5b42a5439f2eee8b9de149e235961e3eb21c5808fc3ea17ff3e" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" + +[[package]] +name = "smallvec" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4" + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "syn" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93a56fabc59dce20fe48b6c832cc249c713e7ed88fa28b0ee0a3bfcaae5fe4e2" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +dependencies = [ + "libc", + "winapi 0.3.8", +] + +[[package]] +name = "tiny_http" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15ce4fc3c4cdea1a4399bb1819a539195fb69db4bbe0bde5b7c7f18fed412e02" +dependencies = [ + "ascii", + "chrono", + "chunked_transfer", + "log", + "url", +] + +[[package]] +name = "tracing" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7c6b59d116d218cb2d990eb06b77b64043e0268ef7323aae63d8b30ae462923" +dependencies = [ + "cfg-if", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aa83a9a47081cd522c09c81b31aec2c9273424976f922ad61c053b58350b715" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "tracing-futures" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab7bb6f14721aa00656086e9335d363c5c8747bae02ebe32ea2c7dece5689b4c" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "tracing-subscriber" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d53c40489aa69c9aed21ff483f26886ca8403df33bdc2d2f87c60c1617826d2" +dependencies = [ + "ansi_term", + "chrono", + "lazy_static", + "matchers", + "regex", + "sharded-slab", + "smallvec", + "tracing-core", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +dependencies = [ + "matches", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" +dependencies = [ + "smallvec", +] + +[[package]] +name = "unicode-width" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" + +[[package]] +name = "url" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" +dependencies = [ + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "veloren_network" +version = "0.1.0" +dependencies = [ + "async-std", + "bincode", + "futures", + "lazy_static", + "prometheus", + "rand", + "serde", + "tracing", + "tracing-futures", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] diff --git a/network/examples/network-speed/Cargo.toml b/network/examples/network-speed/Cargo.toml new file mode 100644 index 0000000000..10ec82e375 --- /dev/null +++ b/network/examples/network-speed/Cargo.toml @@ -0,0 +1,20 @@ +[workspace] + +[package] +name = "network-speed" +version = "0.1.0" +authors = ["Marcel Märtens "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +network = { package = "veloren_network", path = "../../../network" } +clap = { version = "2.33", default-features = false } +futures = { version = "0.3", default-features = false } +tracing = { version = "0.1", default-features = false } +tracing-subscriber = { version = "0.2.3", default-features = false, features = ["env-filter", "fmt", "chrono", "ansi", "smallvec"] } +bincode = "1.2" +prometheus = "0.7" +tiny_http = "0.7.0" +serde = { version = "1.0", features = ["derive"] } \ No newline at end of file diff --git a/network/examples/network-speed/src/main.rs b/network/examples/network-speed/src/main.rs new file mode 100644 index 0000000000..3e702ae2ce --- /dev/null +++ b/network/examples/network-speed/src/main.rs @@ -0,0 +1,189 @@ +///run with +/// ```bash +/// (cd network/examples/network-speed && RUST_BACKTRACE=1 cargo run --profile=debuginfo -Z unstable-options -- --trace=error --protocol=tcp --mode=server) +/// (cd network/examples/network-speed && RUST_BACKTRACE=1 cargo run --profile=debuginfo -Z unstable-options -- --trace=error --protocol=tcp --mode=client) +/// ``` +mod metrics; + +use clap::{App, Arg}; +use futures::executor::block_on; +use network::{Address, MessageBuffer, Network, Pid, PROMISES_CONSISTENCY, PROMISES_ORDERED}; +use serde::{Deserialize, Serialize}; +use std::{ + sync::Arc, + thread, + time::{Duration, Instant}, +}; +use tracing::*; +use tracing_subscriber::EnvFilter; + +#[derive(Serialize, Deserialize, Debug)] +enum Msg { + Ping { id: u64, data: Vec }, + Pong { id: u64, data: Vec }, +} + +/// This utility tests the speed of veloren network by creating a client that +/// opens a stream and pipes as many messages through it as possible. +fn main() { + let matches = App::new("Veloren Speed Test Utility") + .version("0.1.0") + .author("Marcel Märtens ") + .about("Runs speedtests regarding different parameter to benchmark veloren-network") + .arg( + Arg::with_name("mode") + .short("m") + .long("mode") + .takes_value(true) + .possible_values(&["server", "client", "both"]) + .default_value("both") + .help( + "choose whether you want to start the server or client or both needed for \ + this program", + ), + ) + .arg( + Arg::with_name("port") + .short("p") + .long("port") + .takes_value(true) + .default_value("52000") + .help("port to listen on"), + ) + .arg( + Arg::with_name("ip") + .long("ip") + .takes_value(true) + .default_value("127.0.0.1") + .help("ip to listen and connect to"), + ) + .arg( + Arg::with_name("protocol") + .long("protocol") + .takes_value(true) + .default_value("tcp") + .possible_values(&["tcp", "udp", "mpsc"]) + .help( + "underlying protocol used for this test, mpsc can only combined with mode=both", + ), + ) + .arg( + Arg::with_name("trace") + .short("t") + .long("trace") + .takes_value(true) + .default_value("warn") + .possible_values(&["trace", "debug", "info", "warn", "error"]) + .help("set trace level, not this has a performance impact!"), + ) + .get_matches(); + + let trace = matches.value_of("trace").unwrap(); + let filter = EnvFilter::from_default_env() + .add_directive(trace.parse().unwrap()) + .add_directive("network_speed=debug".parse().unwrap()) + .add_directive("veloren_network::participant=trace".parse().unwrap()) + .add_directive("veloren_network::protocol=trace".parse().unwrap()) + .add_directive("veloren_network::scheduler=trace".parse().unwrap()) + .add_directive("veloren_network::api=trace".parse().unwrap()) + /* + .add_directive("veloren_network::participant=debug".parse().unwrap()).add_directive("veloren_network::api=debug".parse().unwrap())*/; + tracing_subscriber::FmtSubscriber::builder() + .with_max_level(Level::ERROR) + .with_env_filter(filter) + .init(); + + let port: u16 = matches.value_of("port").unwrap().parse().unwrap(); + let ip: &str = matches.value_of("ip").unwrap(); + let address = match matches.value_of("protocol") { + Some("tcp") => Address::Tcp(format!("{}:{}", ip, port).parse().unwrap()), + Some("udp") => Address::Udp(format!("{}:{}", ip, port).parse().unwrap()), + _ => panic!("invalid mode, run --help!"), + }; + + let mut background = None; + match matches.value_of("mode") { + Some("server") => server(address), + Some("client") => client(address), + Some("both") => { + let address1 = address.clone(); + background = Some(thread::spawn(|| server(address1))); + thread::sleep(Duration::from_millis(200)); //start client after server + client(address); + }, + _ => panic!("invalid mode, run --help!"), + }; + if let Some(background) = background { + background.join().unwrap(); + } +} + +fn server(address: Address) { + let mut metrics = metrics::SimpleMetrics::new(); + let (server, f) = Network::new(Pid::new(), Some(metrics.registry())); + std::thread::spawn(f); + metrics.run("0.0.0.0:59112".parse().unwrap()).unwrap(); + block_on(server.listen(address)).unwrap(); + + loop { + info!("waiting for participant to connect"); + let p1 = block_on(server.connected()).unwrap(); //remote representation of p1 + let mut s1 = block_on(p1.opened()).unwrap(); //remote representation of s1 + block_on(async { + let mut last = Instant::now(); + let mut id = 0u64; + while let Ok(_msg) = s1.recv_raw().await { + id += 1; + if id.rem_euclid(1000000) == 0 { + let new = Instant::now(); + let diff = new.duration_since(last); + last = new; + println!("recv 1.000.000 took {}", diff.as_millis()); + } + } + info!("other stream was closed"); + }); + } +} + +fn client(address: Address) { + let mut metrics = metrics::SimpleMetrics::new(); + let (client, f) = Network::new(Pid::new(), Some(metrics.registry())); + std::thread::spawn(f); + metrics.run("0.0.0.0:59111".parse().unwrap()).unwrap(); + + let p1 = block_on(client.connect(address.clone())).unwrap(); //remote representation of p1 + let mut s1 = block_on(p1.open(16, PROMISES_ORDERED | PROMISES_CONSISTENCY)).unwrap(); //remote representation of s1 + let mut last = Instant::now(); + let mut id = 0u64; + let raw_msg = Arc::new(MessageBuffer { + data: bincode::serialize(&Msg::Ping { + id, + data: vec![0; 1000], + }) + .unwrap(), + }); + loop { + s1.send_raw(raw_msg.clone()).unwrap(); + id += 1; + if id.rem_euclid(1000000) == 0 { + let new = Instant::now(); + let diff = new.duration_since(last); + last = new; + println!("send 1.000.000 took {}", diff.as_millis()); + } + if id > 2000000 { + println!("stop"); + std::thread::sleep(std::time::Duration::from_millis(5000)); + break; + } + } + drop(s1); + std::thread::sleep(std::time::Duration::from_millis(5000)); + info!("closing participant"); + block_on(client.disconnect(p1)).unwrap(); + std::thread::sleep(std::time::Duration::from_millis(25000)); + info!("DROPPING! client"); + drop(client); + std::thread::sleep(std::time::Duration::from_millis(25000)); +} diff --git a/network/examples/network-speed/src/metrics.rs b/network/examples/network-speed/src/metrics.rs new file mode 100644 index 0000000000..978c686d58 --- /dev/null +++ b/network/examples/network-speed/src/metrics.rs @@ -0,0 +1,94 @@ +use prometheus::{Encoder, Registry, TextEncoder}; +use std::{ + error::Error, + net::SocketAddr, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, + thread, +}; +use tiny_http; +use tracing::*; + +pub struct SimpleMetrics { + running: Arc, + handle: Option>, + registry: Option, +} + +impl SimpleMetrics { + pub fn new() -> Self { + let running = Arc::new(AtomicBool::new(false)); + let registry = Some(Registry::new()); + + Self { + running, + handle: None, + registry, + } + } + + pub fn registry(&self) -> &Registry { + match self.registry { + Some(ref r) => r, + None => panic!("You cannot longer register new metrics after the server has started!"), + } + } + + pub fn run(&mut self, addr: SocketAddr) -> Result<(), Box> { + self.running.store(true, Ordering::Relaxed); + let running2 = self.running.clone(); + + let registry = self + .registry + .take() + .expect("ServerMetrics must be already started"); + + //TODO: make this a job + self.handle = Some(thread::spawn(move || { + let server = tiny_http::Server::http(addr).unwrap(); + const TIMEOUT: std::time::Duration = std::time::Duration::from_secs(1); + debug!("starting tiny_http server to serve metrics"); + while running2.load(Ordering::Relaxed) { + let request = match server.recv_timeout(TIMEOUT) { + Ok(Some(rq)) => rq, + Ok(None) => continue, + Err(e) => { + println!("error: {}", e); + break; + }, + }; + let mf = registry.gather(); + let encoder = TextEncoder::new(); + let mut buffer = vec![]; + encoder + .encode(&mf, &mut buffer) + .expect("Failed to encoder metrics text."); + let response = tiny_http::Response::from_string( + String::from_utf8(buffer).expect("Failed to parse bytes as a string."), + ); + match request.respond(response) { + Err(e) => error!( + ?e, + "The metrics HTTP server had encountered and error with answering" + ), + _ => (), + } + } + debug!("stopping tiny_http server to serve metrics"); + })); + Ok(()) + } +} + +impl Drop for SimpleMetrics { + fn drop(&mut self) { + self.running.store(false, Ordering::Relaxed); + let handle = self.handle.take(); + handle + .expect("ServerMetrics worker handle does not exist.") + .join() + .expect("Error shutting down prometheus metric exporter"); + } +} diff --git a/network/examples/tcp-loadtest/Cargo.lock b/network/examples/tcp-loadtest/Cargo.lock new file mode 100644 index 0000000000..189d054fd9 --- /dev/null +++ b/network/examples/tcp-loadtest/Cargo.lock @@ -0,0 +1,84 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "getrandom" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" + +[[package]] +name = "ppv-lite86" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom", + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core", +] + +[[package]] +name = "tcp-loadtest" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" diff --git a/network/examples/tcp-loadtest/Cargo.toml b/network/examples/tcp-loadtest/Cargo.toml new file mode 100644 index 0000000000..f6639da4c6 --- /dev/null +++ b/network/examples/tcp-loadtest/Cargo.toml @@ -0,0 +1,13 @@ +[workspace] + +[package] +name = "tcp-loadtest" +version = "0.1.0" +authors = ["Marcel Märtens "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +rand = "0.7" \ No newline at end of file diff --git a/network/examples/tcp-loadtest/src/main.rs b/network/examples/tcp-loadtest/src/main.rs new file mode 100644 index 0000000000..d1d2bbe9f4 --- /dev/null +++ b/network/examples/tcp-loadtest/src/main.rs @@ -0,0 +1,112 @@ +//!run with +//! ```bash +//! (cd network/examples/tcp-loadtest && RUST_BACKTRACE=1 cargo run 127.0.0.1 52000) +//! ``` +use std::{ + env, + io::Write, + net::{SocketAddr, TcpStream}, + sync::{ + atomic::{AtomicU64, Ordering}, + Arc, + }, + thread, + time::{Duration, Instant}, +}; +extern crate rand; + +fn setup() -> Result { + let args: Vec = env::args().collect(); + if args.len() < 3 { + println!("usage: tcp-loadtest "); + println!("example: tcp-loadtest 127.0.0.1 52000"); + return Err(1); + } + let a: SocketAddr = format!("{}:{}", args[1], args[2]).parse().unwrap(); + println!("You provided address: {}", &a); + return Ok(a); +} +/// This example file is not running veloren-network at all, +/// instead it's just trying to create 4 threads and pump as much bytes as +/// possible through a specific listener, the listener needs to be created +/// before this program is started. +fn main() -> Result<(), u32> { + let addr = Arc::new(setup()?); + let data: Arc = Arc::new( + (0..1000000) + .map(|_| (0x20u8 + (rand::random::() * 96.0) as u8) as char) + .collect(), + ); + + let total_bytes_send = Arc::new(AtomicU64::new(0)); + let total_send_count = Arc::new(AtomicU64::new(0)); + let total_finished_threads = Arc::new(AtomicU64::new(0)); + let start_time = Instant::now(); + + let mut threads = Vec::new(); + let thread_count = 4; + for i in 0..thread_count { + let addr = addr.clone(); + let total_bytes_send = total_bytes_send.clone(); + let total_send_count = total_send_count.clone(); + let total_finished_threads = total_finished_threads.clone(); + let data = data.clone(); + threads.push(thread::spawn(move || { + let mut stream = match TcpStream::connect(addr.as_ref()) { + Err(err) => { + total_finished_threads.fetch_add(1, Ordering::Relaxed); + panic!("could not open connection: {}", err); + }, + Ok(s) => s, + }; + let mut thread_bytes_send: u64 = 0; + let mut thread_last_sync = Instant::now(); + + loop { + let tosend: u64 = rand::random::() as u64 * 10 + 1000; + thread_bytes_send += tosend; + + let cur = Instant::now(); + if cur.duration_since(thread_last_sync) >= Duration::from_secs(1) { + thread_last_sync = cur; + println!("[{}]send: {}MiB/s", i, thread_bytes_send / (1024 * 1024)); + total_bytes_send.fetch_add(thread_bytes_send, Ordering::Relaxed); + thread_bytes_send = 0; + } + + total_send_count.fetch_add(1, Ordering::Relaxed); + let ret = stream.write_all(data[0..(tosend as usize)].as_bytes()); + if ret.is_err() { + println!("[{}] error: {}", i, ret.err().unwrap()); + total_finished_threads.fetch_add(1, Ordering::Relaxed); + return; + } + //stream.flush(); + } + })); + } + + while total_finished_threads.load(Ordering::Relaxed) < thread_count { + thread::sleep(Duration::from_millis(10)); + } + + let cur = Instant::now(); + let dur = cur.duration_since(start_time); + println!("================"); + println!("test endet"); + println!( + "total send: {}MiB", + total_bytes_send.load(Ordering::Relaxed) / (1024 * 1024) + ); + println!("total time: {}s", dur.as_secs()); + println!( + "average: {}KiB/s", + total_bytes_send.load(Ordering::Relaxed) * 1000 / dur.as_millis() as u64 / 1024 + ); + println!( + "send count: {}/s", + total_send_count.load(Ordering::Relaxed) * 1000 / dur.as_millis() as u64 + ); + + Ok(()) +} diff --git a/network/src/api.rs b/network/src/api.rs new file mode 100644 index 0000000000..05be3c3f3b --- /dev/null +++ b/network/src/api.rs @@ -0,0 +1,1031 @@ +//! +//! +//! +//! (cd network/examples/async_recv && RUST_BACKTRACE=1 cargo run) +use crate::{ + message::{self, partial_eq_bincode, IncomingMessage, MessageBuffer, OutgoingMessage}, + scheduler::Scheduler, + types::{Mid, Pid, Prio, Promises, Sid}, +}; +use async_std::{io, sync::RwLock, task}; +use futures::{ + channel::{mpsc, oneshot}, + sink::SinkExt, + stream::StreamExt, +}; +use prometheus::Registry; +use serde::{de::DeserializeOwned, Serialize}; +use std::{ + collections::HashMap, + net::SocketAddr, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, +}; +use tracing::*; +use tracing_futures::Instrument; + +/// Represents a Tcp or Udp or Mpsc address +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub enum Address { + Tcp(SocketAddr), + Udp(SocketAddr), + Mpsc(u64), +} + +/// `Participants` are generated by the [`Network`] and represent a connection +/// to a remote Participant. Look at the [`connect`] and [`connected`] method of +/// [`Networks`] on how to generate `Participants` +/// +/// [`Networks`]: crate::api::Network +/// [`connect`]: Network::connect +/// [`connected`]: Network::connected +pub struct Participant { + local_pid: Pid, + remote_pid: Pid, + a2b_steam_open_s: RwLock)>>, + b2a_stream_opened_r: RwLock>, + closed: AtomicBool, + a2s_disconnect_s: + Option>)>>, +} + +/// `Streams` represents a channel to send `n` messages with a certain priority +/// and [`Promises`]. messages need always to be send between 2 `Streams`. +/// +/// `Streams` are generated by the [`Participant`]. +/// Look at the [`open`] and [`opened`] method of [`Participant`] on how to +/// generate `Streams` +/// +/// Unlike [`Network`] and [`Participant`], `Streams` don't implement interior +/// mutability, as multiple threads don't need access to the same `Stream`. +/// [`Sync`] is not supported! In that case multiple `Streams` should be used +/// instead. However it's still possible to [`Send`] `Streams`. +/// +/// [`Networks`]: crate::api::Network +/// [`open`]: Participant::open +/// [`opened`]: Participant::opened +/// [`Send`]: std::marker::Send +/// [`Sync`]: std::marker::Sync +#[derive(Debug)] +pub struct Stream { + pid: Pid, + sid: Sid, + mid: Mid, + prio: Prio, + promises: Promises, + a2b_msg_s: std::sync::mpsc::Sender<(Prio, Sid, OutgoingMessage)>, + b2a_msg_recv_r: mpsc::UnboundedReceiver, + closed: Arc, + a2b_close_stream_s: Option>, +} + +/// Error type thrown by [`Networks`](Network) methods +#[derive(Debug)] +pub enum NetworkError { + NetworkClosed, + ListenFailed(std::io::Error), +} + +/// Error type thrown by [`Participants`](Participant) methods +#[derive(Debug, PartialEq)] +pub enum ParticipantError { + ParticipantClosed, +} + +/// Error type thrown by [`Streams`](Stream) methods +#[derive(Debug)] +pub enum StreamError { + StreamClosed, + DeserializeError(Box), +} + +/// Use the `Network` to create connections to other [`Participants`] +/// +/// The `Network` is the single source that handles all connections in your +/// Application. You can pass it around multiple threads in an +/// [`Arc`](std::sync::Arc) as all commands have internal mutability. +/// +/// The `Network` has methods to [`connect`] and [`disconnect`] to other +/// [`Participants`] via their [`Address`]. All [`Participants`] will be stored +/// in the Network until explicitly disconnected, which is the only way to close +/// the sockets. +/// +/// # Examples +/// ```rust +/// use veloren_network::{Network, Address, Pid}; +/// use futures::executor::block_on; +/// +/// # fn main() -> std::result::Result<(), Box> { +/// // Create a Network, listen on port `2999` to accept connections and connect to port `8080` to connect to a (pseudo) database Application +/// let (network, f) = Network::new(Pid::new(), None); +/// std::thread::spawn(f); +/// block_on(async{ +/// # //setup pseudo database! +/// # let (database, fd) = Network::new(Pid::new(), None); +/// # std::thread::spawn(fd); +/// # database.listen(Address::Tcp("127.0.0.1:8080".parse().unwrap())).await?; +/// network.listen(Address::Tcp("127.0.0.1:2999".parse().unwrap())).await?; +/// let database = network.connect(Address::Tcp("127.0.0.1:8080".parse().unwrap())).await?; +/// # Ok(()) +/// }) +/// # } +/// ``` +/// +/// [`Participants`]: crate::api::Participant +/// [`connect`]: Network::connect +/// [`disconnect`]: Network::disconnect +pub struct Network { + local_pid: Pid, + participants: RwLock>>, + listen_sender: + RwLock>)>>, + connect_sender: + RwLock>)>>, + connected_receiver: RwLock>, + shutdown_sender: Option>, +} + +impl Network { + /// Generates a new `Network` to handle all connections in an Application + /// + /// # Arguments + /// * `participant_id` - provide it by calling [`Pid::new()`], usually you + /// don't want to reuse a Pid for 2 `Networks` + /// * `registry` - Provide a Registy in order to collect Prometheus metrics + /// by this `Network`, `None` will deactivate Tracing. Tracing is done via + /// [`prometheus`] + /// + /// # Result + /// * `Self` - returns a `Network` which can be `Send` to multiple areas of + /// your code, including multiple threads. This is the base strct of this + /// crate. + /// * `FnOnce` - you need to run the returning FnOnce exactly once, probably + /// in it's own thread. this is NOT done internally, so that you are free + /// to choose the threadpool implementation of your choice. We recommend + /// using [`ThreadPool`] from [`uvth`] crate. This fn will runn the + /// Scheduler to handle all `Network` internals. Additional threads will + /// be allocated on an internal async-aware threadpool + /// + /// # Examples + /// ```rust + /// //Example with uvth + /// use uvth::ThreadPoolBuilder; + /// use veloren_network::{Address, Network, Pid}; + /// + /// let pool = ThreadPoolBuilder::new().build(); + /// let (network, f) = Network::new(Pid::new(), None); + /// pool.execute(f); + /// ``` + /// + /// ```rust + /// //Example with std::thread + /// use veloren_network::{Address, Network, Pid}; + /// + /// let (network, f) = Network::new(Pid::new(), None); + /// std::thread::spawn(f); + /// ``` + /// + /// Usually you only create a single `Network` for an appliregistrycation, + /// except when client and server are in the same application, then you + /// will want 2. However there are no technical limitations from + /// creating more. + /// + /// [`Pid::new()`]: crate::types::Pid::new + /// [`ThreadPool`]: https://docs.rs/uvth/newest/uvth/struct.ThreadPool.html + /// [`uvth`]: https://docs.rs/uvth + pub fn new( + participant_id: Pid, + registry: Option<&Registry>, + ) -> (Self, impl std::ops::FnOnce()) { + let p = participant_id; + debug!(?p, "starting Network"); + let (scheduler, listen_sender, connect_sender, connected_receiver, shutdown_sender) = + Scheduler::new(participant_id, registry); + ( + Self { + local_pid: participant_id, + participants: RwLock::new(HashMap::new()), + listen_sender: RwLock::new(listen_sender), + connect_sender: RwLock::new(connect_sender), + connected_receiver: RwLock::new(connected_receiver), + shutdown_sender: Some(shutdown_sender), + }, + move || { + trace!(?p, "starting sheduler in own thread"); + let _handle = task::block_on( + scheduler + .run() + .instrument(tracing::info_span!("scheduler", ?p)), + ); + trace!(?p, "stopping sheduler and his own thread"); + }, + ) + } + + /// starts listening on an [`Address`]. + /// When the method returns the `Network` is ready to listen for incoming + /// connections OR has returned a [`NetworkError`] (e.g. port already used). + /// You can call [`connected`] to asynchrony wait for a [`Participant`] to + /// connect. You can call `listen` on multiple addresses, e.g. to + /// support multiple Protocols or NICs. + /// + /// # Examples + /// ```rust + /// use futures::executor::block_on; + /// use veloren_network::{Address, Network, Pid}; + /// + /// # fn main() -> std::result::Result<(), Box> { + /// // Create a Network, listen on port `2000` TCP on all NICs and `2001` UDP locally + /// let (network, f) = Network::new(Pid::new(), None); + /// std::thread::spawn(f); + /// block_on(async { + /// network + /// .listen(Address::Tcp("0.0.0.0:2000".parse().unwrap())) + /// .await?; + /// network + /// .listen(Address::Udp("127.0.0.1:2001".parse().unwrap())) + /// .await?; + /// # Ok(()) + /// }) + /// # } + /// ``` + /// + /// [`connected`]: Network::connected + pub async fn listen(&self, address: Address) -> Result<(), NetworkError> { + let (s2a_result_s, s2a_result_r) = oneshot::channel::>(); + debug!(?address, "listening on address"); + self.listen_sender + .write() + .await + .send((address, s2a_result_s)) + .await?; + match s2a_result_r.await? { + //waiting guarantees that we either listened sucessfully or get an error like port in + // use + Ok(()) => Ok(()), + Err(e) => Err(NetworkError::ListenFailed(e)), + } + } + + /// starts connectiong to an [`Address`]. + /// When the method returns the Network either returns a [`Participant`] + /// ready to open [`Streams`] on OR has returned a [`NetworkError`] (e.g. + /// can't connect, or invalid Handshake) # Examples + /// ```rust + /// use futures::executor::block_on; + /// use veloren_network::{Address, Network, Pid}; + /// + /// # fn main() -> std::result::Result<(), Box> { + /// // Create a Network, connect on port `2010` TCP and `2011` UDP like listening above + /// let (network, f) = Network::new(Pid::new(), None); + /// std::thread::spawn(f); + /// # let (remote, fr) = Network::new(Pid::new(), None); + /// # std::thread::spawn(fr); + /// block_on(async { + /// # remote.listen(Address::Tcp("0.0.0.0:2010".parse().unwrap())).await?; + /// # remote.listen(Address::Udp("0.0.0.0:2011".parse().unwrap())).await?; + /// let p1 = network + /// .connect(Address::Tcp("127.0.0.1:2010".parse().unwrap())) + /// .await?; + /// # //this doesn't work yet, so skip the test + /// # //TODO fixme! + /// # return Ok(()); + /// let p2 = network + /// .connect(Address::Udp("127.0.0.1:2011".parse().unwrap())) + /// .await?; + /// assert!(std::sync::Arc::ptr_eq(&p1, &p2)); + /// # Ok(()) + /// }) + /// # } + /// ``` + /// Usually the `Network` guarantees that a operation on a [`Participant`] + /// succeeds, e.g. by automatic retrying unless it fails completely e.g. by + /// disconnecting from the remote. If 2 [`Addresses`] you `connect` to + /// belongs to the same [`Participant`], you get the same [`Participant`] as + /// a result. This is useful e.g. by connecting to the same + /// [`Participant`] via multiple Protocols. + /// + /// [`Streams`]: crate::api::Stream + /// [`Addresses`]: crate::api::Address + pub async fn connect(&self, address: Address) -> Result, NetworkError> { + let (pid_sender, pid_receiver) = oneshot::channel::>(); + debug!(?address, "connect to address"); + self.connect_sender + .write() + .await + .send((address, pid_sender)) + .await?; + let participant = pid_receiver.await??; + let pid = participant.remote_pid; + debug!( + ?pid, + "received Participant id from remote and return to user" + ); + let participant = Arc::new(participant); + self.participants + .write() + .await + .insert(participant.remote_pid, participant.clone()); + Ok(participant) + } + + /// returns a [`Participant`] created from a [`Address`] you called + /// [`listen`] on before. This function will either return a working + /// [`Participant`] ready to open [`Streams`] on OR has returned a + /// [`NetworkError`] (e.g. Network got closed) + /// + /// # Examples + /// ```rust + /// use futures::executor::block_on; + /// use veloren_network::{Address, Network, Pid}; + /// + /// # fn main() -> std::result::Result<(), Box> { + /// // Create a Network, listen on port `2020` TCP and opens returns their Pid + /// let (network, f) = Network::new(Pid::new(), None); + /// std::thread::spawn(f); + /// # let (remote, fr) = Network::new(Pid::new(), None); + /// # std::thread::spawn(fr); + /// block_on(async { + /// network + /// .listen(Address::Tcp("0.0.0.0:2020".parse().unwrap())) + /// .await?; + /// # remote.connect(Address::Tcp("0.0.0.0:2020".parse().unwrap())).await?; + /// while let Ok(participant) = network.connected().await { + /// println!("Participant connected: {}", participant.remote_pid()); + /// # //skip test here as it would be a endless loop + /// # break; + /// } + /// # Ok(()) + /// }) + /// # } + /// ``` + /// + /// [`Streams`]: crate::api::Stream + /// [`listen`]: crate::api::Network::listen + pub async fn connected(&self) -> Result, NetworkError> { + let participant = self.connected_receiver.write().await.next().await?; + let participant = Arc::new(participant); + self.participants + .write() + .await + .insert(participant.remote_pid, participant.clone()); + Ok(participant) + } + + /// disconnecting a [`Participant`] where you move the last existing + /// [`Arc`]. As the [`Network`] also holds [`Arc`] to the + /// [`Participant`], you need to provide the last [`Arc`] and + /// are not allowed to keep others. If you do so the [`Participant`] + /// can't be disconnected properly. If you no longer have the respective + /// [`Participant`], try using the [`participants`] method to get it. + /// + /// This function will wait for all [`Streams`] to properly close, including + /// all messages to be send before closing. If an error occurs with one + /// of the messages. + /// Except if the remote side already dropped the [`Participant`] + /// simultaneously, then messages won't be sended + /// + /// There is NO `disconnected` function in `Network`, if a [`Participant`] + /// is no longer reachable (e.g. as the network cable was unplugged) the + /// [`Participant`] will fail all action, but needs to be manually + /// disconected, using this function. + /// + /// # Examples + /// ```rust + /// use futures::executor::block_on; + /// use veloren_network::{Address, Network, Pid}; + /// + /// # fn main() -> std::result::Result<(), Box> { + /// // Create a Network, listen on port `2030` TCP and opens returns their Pid and close connection. + /// let (network, f) = Network::new(Pid::new(), None); + /// std::thread::spawn(f); + /// # let (remote, fr) = Network::new(Pid::new(), None); + /// # std::thread::spawn(fr); + /// block_on(async { + /// network + /// .listen(Address::Tcp("0.0.0.0:2030".parse().unwrap())) + /// .await?; + /// # remote.connect(Address::Tcp("0.0.0.0:2030".parse().unwrap())).await?; + /// while let Ok(participant) = network.connected().await { + /// println!("Participant connected: {}", participant.remote_pid()); + /// network.disconnect(participant).await?; + /// # //skip test here as it would be a endless loop + /// # break; + /// } + /// # Ok(()) + /// }) + /// # } + /// ``` + /// + /// [`Arc`]: crate::api::Participant + /// [`Streams`]: crate::api::Stream + /// [`participants`]: Network::participants + /// [`Arc`]: std::sync::Arc + pub async fn disconnect(&self, participant: Arc) -> Result<(), NetworkError> { + // Remove, Close and try_unwrap error when unwrap fails! + let pid = participant.remote_pid; + debug!(?pid, "removing participant from network"); + self.participants.write().await.remove(&pid)?; + participant.closed.store(true, Ordering::Relaxed); + + match Arc::try_unwrap(participant) { + Err(_) => { + warn!( + "you are disconnecting and still keeping a reference to this participant, \ + this is a bad idea. Participant will only be dropped when you drop your last \ + reference" + ); + }, + Ok(mut participant) => { + trace!("waiting now for participant to close"); + let (finished_sender, finished_receiver) = oneshot::channel(); + // we are deleting here asyncly before DROP is called. Because this is done + // nativly async, while drop needs an BLOCK! Drop will recognis + // that it has been delete here and don't try another double delete. + participant + .a2s_disconnect_s + .take() + .unwrap() + .send((pid, finished_sender)) + .await + .expect("something is wrong in internal scheduler coding"); + let res = finished_receiver.await.unwrap(); + trace!("participant is now closed"); + res?; + }, + }; + + Ok(()) + } + + /// returns a copy of all current connected [`Participants`], + /// including ones, which can't send data anymore as the underlying sockets + /// are closed already but haven't been [`disconnected`] yet. + /// + /// [`Participants`]: crate::api::Participant + /// [`disconnected`]: Network::disconnect + pub async fn participants(&self) -> HashMap> { + self.participants.read().await.clone() + } +} + +impl Participant { + pub(crate) fn new( + local_pid: Pid, + remote_pid: Pid, + a2b_steam_open_s: mpsc::UnboundedSender<(Prio, Promises, oneshot::Sender)>, + b2a_stream_opened_r: mpsc::UnboundedReceiver, + a2s_disconnect_s: mpsc::UnboundedSender<(Pid, oneshot::Sender>)>, + ) -> Self { + Self { + local_pid, + remote_pid, + a2b_steam_open_s: RwLock::new(a2b_steam_open_s), + b2a_stream_opened_r: RwLock::new(b2a_stream_opened_r), + closed: AtomicBool::new(false), + a2s_disconnect_s: Some(a2s_disconnect_s), + } + } + + /// Opens a [`Stream`] on this `Participant` with a certain Priority and + /// [`Promises`] + /// + /// # Arguments + /// * `prio` - valid between 0-63. The priority rates the throughput for + /// messages of the [`Stream`] e.g. prio 5 messages will get 1/2 the speed + /// prio0 messages have. Prio10 messages only 1/4 and Prio 15 only 1/8, + /// etc... + /// * `promises` - use a combination of you prefered [`Promises`], see the + /// link for further documentation. You can combine them, e.g. + /// `PROMISES_ORDERED | PROMISES_CONSISTENCY` The Stream will then + /// guarantee that those promisses are met. + /// + /// A [`ParticipantError`] might be thrown if the `Participant` is already + /// closed. [`Streams`] can be created without a answer from the remote + /// side, resulting in very fast creation and closing latency. + /// + /// # Examples + /// ```rust + /// use futures::executor::block_on; + /// use veloren_network::{Address, Network, Pid, PROMISES_CONSISTENCY, PROMISES_ORDERED}; + /// + /// # fn main() -> std::result::Result<(), Box> { + /// // Create a Network, connect on port 2100 and open a stream + /// let (network, f) = Network::new(Pid::new(), None); + /// std::thread::spawn(f); + /// # let (remote, fr) = Network::new(Pid::new(), None); + /// # std::thread::spawn(fr); + /// block_on(async { + /// # remote.listen(Address::Tcp("0.0.0.0:2100".parse().unwrap())).await?; + /// let p1 = network + /// .connect(Address::Tcp("127.0.0.1:2100".parse().unwrap())) + /// .await?; + /// let _s1 = p1.open(16, PROMISES_ORDERED | PROMISES_CONSISTENCY).await?; + /// # Ok(()) + /// }) + /// # } + /// ``` + /// + /// [`Streams`]: crate::api::Stream + pub async fn open(&self, prio: u8, promises: Promises) -> Result { + //use this lock for now to make sure that only one open at a time is made, + // TODO: not sure if we can paralise that, check in future + let mut a2b_steam_open_s = self.a2b_steam_open_s.write().await; + if self.closed.load(Ordering::Relaxed) { + warn!(?self.remote_pid, "participant is closed but another open is tried on it"); + return Err(ParticipantError::ParticipantClosed); + } + let (p2a_return_stream_s, p2a_return_stream_r) = oneshot::channel(); + if a2b_steam_open_s + .send((prio, promises, p2a_return_stream_s)) + .await + .is_err() + { + debug!(?self.remote_pid, "stream_open_sender failed, closing participant"); + self.closed.store(true, Ordering::Relaxed); + return Err(ParticipantError::ParticipantClosed); + } + match p2a_return_stream_r.await { + Ok(stream) => { + let sid = stream.sid; + debug!(?sid, ?self.remote_pid, "opened stream"); + Ok(stream) + }, + Err(_) => { + debug!(?self.remote_pid, "p2a_return_stream_r failed, closing participant"); + self.closed.store(true, Ordering::Relaxed); + Err(ParticipantError::ParticipantClosed) + }, + } + } + + /// Use this method to handle [`Streams`] opened from remote site, like the + /// [`connected`] method of [`Network`]. This is the associated method + /// to [`open`]. It's guaranteed that the order of [`open`] and `opened` + /// is equal. The `nth` [`Streams`] on one side will represent the `nth` on + /// the other side. A [`ParticipantError`] might be thrown if the + /// `Participant` is already closed. + /// + /// # Examples + /// ```rust + /// use veloren_network::{Network, Pid, Address, PROMISES_ORDERED, PROMISES_CONSISTENCY}; + /// use futures::executor::block_on; + /// + /// # fn main() -> std::result::Result<(), Box> { + /// // Create a Network, connect on port 2110 and wait for the other side to open a stream + /// // Note: It's quite unusal to activly connect, but then wait on a stream to be connected, usually the Appication taking initiative want's to also create the first Stream. + /// let (network, f) = Network::new(Pid::new(), None); + /// std::thread::spawn(f); + /// # let (remote, fr) = Network::new(Pid::new(), None); + /// # std::thread::spawn(fr); + /// block_on(async { + /// # remote.listen(Address::Tcp("0.0.0.0:2110".parse().unwrap())).await?; + /// let p1 = network.connect(Address::Tcp("127.0.0.1:2110".parse().unwrap())).await?; + /// # let p2 = remote.connected().await?; + /// # p2.open(16, PROMISES_ORDERED | PROMISES_CONSISTENCY).await?; + /// let _s1 = p1.opened().await?; + /// # Ok(()) + /// }) + /// # } + /// ``` + /// + /// [`Streams`]: crate::api::Stream + /// [`connected`]: Network::connected + /// [`open`]: Participant::open + pub async fn opened(&self) -> Result { + //use this lock for now to make sure that only one open at a time is made, + // TODO: not sure if we can paralise that, check in future + let mut stream_opened_receiver = self.b2a_stream_opened_r.write().await; + if self.closed.load(Ordering::Relaxed) { + warn!(?self.remote_pid, "participant is closed but another open is tried on it"); + return Err(ParticipantError::ParticipantClosed); + } + match stream_opened_receiver.next().await { + Some(stream) => { + let sid = stream.sid; + debug!(?sid, ?self.remote_pid, "receive opened stream"); + Ok(stream) + }, + None => { + debug!(?self.remote_pid, "stream_opened_receiver failed, closing participant"); + self.closed.store(true, Ordering::Relaxed); + Err(ParticipantError::ParticipantClosed) + }, + } + } + + /// Returns the remote [`Pid`] + pub fn remote_pid(&self) -> Pid { self.remote_pid } +} + +impl Stream { + #[allow(clippy::too_many_arguments)] + pub(crate) fn new( + pid: Pid, + sid: Sid, + prio: Prio, + promises: Promises, + a2b_msg_s: std::sync::mpsc::Sender<(Prio, Sid, OutgoingMessage)>, + b2a_msg_recv_r: mpsc::UnboundedReceiver, + closed: Arc, + a2b_close_stream_s: mpsc::UnboundedSender, + ) -> Self { + Self { + pid, + sid, + mid: 0, + prio, + promises, + a2b_msg_s, + b2a_msg_recv_r, + closed, + a2b_close_stream_s: Some(a2b_close_stream_s), + } + } + + /// use to send a arbitrary message to the remote side, by having the remote + /// side also opened a `Stream` linked to this. the message will be + /// [`Serialized`], which actually is quite slow compared to most other + /// calculations done. A faster method [`send_raw`] exists, when extra + /// speed is needed. The other side needs to use the respective [`recv`] + /// function and know the type send. + /// + /// `send` is an exception to the `async` messages, as it's probably called + /// quite often so it doesn't wait for execution. Which also means, that + /// no feedback is provided. It's to assume that the Message got `send` + /// correctly. If a error occurred, the next call will return an Error. + /// If the [`Participant`] disconnected it will also be unable to be used + /// any more. A [`StreamError`] will be returned in the error case, e.g. + /// when the `Stream` got closed already. + /// + /// Note when a `Stream` is dropped locally, it will still send all + /// messages, though the `drop` will return immediately, however, when a + /// [`Participant`] gets gracefully shut down, all remaining messages + /// will be send. If the `Stream` is dropped from remote side no further + /// messages are send, because the remote side has no way of listening + /// to them either way. If the last channel is destroyed (e.g. by losing + /// the internet connection or non-gracefull shutdown, pending messages + /// are also dropped. + /// + /// # Example + /// ``` + /// use veloren_network::{Network, Address, Pid}; + /// # use veloren_network::{PROMISES_ORDERED, PROMISES_CONSISTENCY}; + /// use futures::executor::block_on; + /// + /// # fn main() -> std::result::Result<(), Box> { + /// // Create a Network, listen on Port `2200` and wait for a Stream to be opened, then answer `Hello World` + /// let (network, f) = Network::new(Pid::new(), None); + /// std::thread::spawn(f); + /// # let (remote, fr) = Network::new(Pid::new(), None); + /// # std::thread::spawn(fr); + /// block_on(async { + /// network.listen(Address::Tcp("127.0.0.1:2200".parse().unwrap())).await?; + /// # let remote_p = remote.connect(Address::Tcp("127.0.0.1:2200".parse().unwrap())).await?; + /// # // keep it alive + /// # let _stream_p = remote_p.open(16, PROMISES_ORDERED | PROMISES_CONSISTENCY).await?; + /// let participant_a = network.connected().await?; + /// let mut stream_a = participant_a.opened().await?; + /// //Send Message + /// stream_a.send("Hello World")?; + /// # Ok(()) + /// }) + /// # } + /// ``` + /// + /// [`send_raw`]: Stream::send_raw + /// [`recv`]: Stream::recv + /// [`Serialized`]: Serialize + #[inline] + pub fn send(&mut self, msg: M) -> Result<(), StreamError> { + self.send_raw(Arc::new(message::serialize(&msg))) + } + + /// This methods give the option to skip multiple calls of [`bincode`], e.g. + /// in case the same Message needs to send on multiple `Streams` to multiple + /// [`Participants`]. Other then that, the same rules apply than for + /// [`send`] + /// + /// # Example + /// ```rust + /// use veloren_network::{Network, Address, Pid, MessageBuffer}; + /// # use veloren_network::{PROMISES_ORDERED, PROMISES_CONSISTENCY}; + /// use futures::executor::block_on; + /// use bincode; + /// use std::sync::Arc; + /// + /// # fn main() -> std::result::Result<(), Box> { + /// let (network, f) = Network::new(Pid::new(), None); + /// std::thread::spawn(f); + /// # let (remote1, fr1) = Network::new(Pid::new(), None); + /// # std::thread::spawn(fr1); + /// # let (remote2, fr2) = Network::new(Pid::new(), None); + /// # std::thread::spawn(fr2); + /// block_on(async { + /// network.listen(Address::Tcp("127.0.0.1:2210".parse().unwrap())).await?; + /// # let remote1_p = remote1.connect(Address::Tcp("127.0.0.1:2210".parse().unwrap())).await?; + /// # let remote2_p = remote2.connect(Address::Tcp("127.0.0.1:2210".parse().unwrap())).await?; + /// # assert_eq!(remote1_p.remote_pid(), remote2_p.remote_pid()); + /// # remote1_p.open(16, PROMISES_ORDERED | PROMISES_CONSISTENCY).await?; + /// # remote2_p.open(16, PROMISES_ORDERED | PROMISES_CONSISTENCY).await?; + /// let participant_a = network.connected().await?; + /// let participant_b = network.connected().await?; + /// let mut stream_a = participant_a.opened().await?; + /// let mut stream_b = participant_b.opened().await?; + /// + /// //Prepare Message and decode it + /// let msg = "Hello World"; + /// let raw_msg = Arc::new(MessageBuffer{ + /// data: bincode::serialize(&msg).unwrap(), + /// }); + /// //Send same Message to multiple Streams + /// stream_a.send_raw(raw_msg.clone()); + /// stream_b.send_raw(raw_msg.clone()); + /// # Ok(()) + /// }) + /// # } + /// ``` + /// + /// [`send`]: Stream::send + /// [`Participants`]: crate::api::Participant + pub fn send_raw(&mut self, messagebuffer: Arc) -> Result<(), StreamError> { + if self.closed.load(Ordering::Relaxed) { + return Err(StreamError::StreamClosed); + } + //debug!(?messagebuffer, "sending a message"); + self.a2b_msg_s.send((self.prio, self.sid, OutgoingMessage { + buffer: messagebuffer, + cursor: 0, + mid: self.mid, + sid: self.sid, + }))?; + self.mid += 1; + Ok(()) + } + + /// use `recv` to wait on a Message send from the remote side by their + /// `Stream`. The Message needs to implement [`DeserializeOwned`] and + /// thus, the resulting type must already be known by the receiving side. + /// If this is not know from the Application logic, one could use a `Enum` + /// and then handle the received message via a `match` state. + /// + /// A [`StreamError`] will be returned in the error case, e.g. when the + /// `Stream` got closed already. + /// + /// # Example + /// ``` + /// use veloren_network::{Network, Address, Pid}; + /// # use veloren_network::{PROMISES_ORDERED, PROMISES_CONSISTENCY}; + /// use futures::executor::block_on; + /// + /// # fn main() -> std::result::Result<(), Box> { + /// // Create a Network, listen on Port `2220` and wait for a Stream to be opened, then listen on it + /// let (network, f) = Network::new(Pid::new(), None); + /// std::thread::spawn(f); + /// # let (remote, fr) = Network::new(Pid::new(), None); + /// # std::thread::spawn(fr); + /// block_on(async { + /// network.listen(Address::Tcp("127.0.0.1:2220".parse().unwrap())).await?; + /// # let remote_p = remote.connect(Address::Tcp("127.0.0.1:2220".parse().unwrap())).await?; + /// # let mut stream_p = remote_p.open(16, PROMISES_ORDERED | PROMISES_CONSISTENCY).await?; + /// # stream_p.send("Hello World"); + /// let participant_a = network.connected().await?; + /// let mut stream_a = participant_a.opened().await?; + /// //Send Message + /// println!("{}", stream_a.recv::().await?); + /// # Ok(()) + /// }) + /// # } + /// ``` + #[inline] + pub async fn recv(&mut self) -> Result { + Ok(message::deserialize(self.recv_raw().await?)?) + } + + /// the equivalent like [`send_raw`] but for [`recv`], no [`bincode`] is + /// executed for performance reasons. + /// + /// [`send_raw`]: Stream::send_raw + /// [`recv`]: Stream::recv + pub async fn recv_raw(&mut self) -> Result { + //no need to access self.closed here, as when this stream is closed the Channel + // is closed which will trigger a None + let msg = self.b2a_msg_recv_r.next().await?; + //info!(?msg, "delivering a message"); + Ok(msg.buffer) + } +} + +impl Drop for Network { + fn drop(&mut self) { + let pid = self.local_pid; + debug!(?pid, "shutting down Network"); + debug!( + ?pid, + "shutting down Participants of Network, while we still have metrics" + ); + task::block_on(async { + // we need to carefully shut down here! as otherwise we might call + // Participant::Drop with a2s_disconnect_s here which would open + // another task::block, which would panic! also i can't `.write` on + // `self.participants` as the `disconnect` fn needs it. + let mut participant_clone = self.participants().await; + for (_, p) in participant_clone.drain() { + if let Err(e) = self.disconnect(p).await { + error!( + ?e, + "error while dropping network, the error occured when dropping a \ + participant but can't be notified to the user any more" + ); + } + } + self.participants.write().await.clear(); + }); + debug!(?pid, "shutting down Scheduler"); + self.shutdown_sender + .take() + .unwrap() + .send(()) + .expect("scheduler is closed, but nobody other should be able to close it"); + debug!(?pid, "participants have shut down!"); + } +} + +impl Drop for Participant { + fn drop(&mut self) { + // ignore closed, as we need to send it even though we disconnected the + // participant from network + let pid = self.remote_pid; + debug!(?pid, "shutting down Participant"); + match self.a2s_disconnect_s.take() { + None => debug!( + ?pid, + "Participant has been shutdown cleanly, no further waiting is requiered!" + ), + Some(mut a2s_disconnect_s) => { + debug!( + ?pid, + "unclean shutdown detected, active waiting for client to be disconnected" + ); + task::block_on(async { + let (finished_sender, finished_receiver) = oneshot::channel(); + a2s_disconnect_s + .send((self.remote_pid, finished_sender)) + .await + .expect("something is wrong in internal scheduler coding"); + match finished_receiver.await { + Ok(Err(e)) => error!( + ?pid, + ?e, + "Error while dropping the participant, couldn't send all outgoing \ + messages, dropping remaining" + ), + Err(e) => warn!( + ?e, + "//TODO i dont know why the finish doesnt work, i normally would \ + expect to have sended a return message from the participant... \ + ignoring to not caue a panic for now, please fix me" + ), + _ => (), + }; + }); + }, + } + debug!(?pid, "network dropped"); + } +} + +impl Drop for Stream { + fn drop(&mut self) { + // a send if closed is unecessary but doesnt hurt, we must not crash here + if !self.closed.load(Ordering::Relaxed) { + let sid = self.sid; + let pid = self.pid; + debug!(?pid, ?sid, "shutting down Stream"); + if task::block_on(self.a2b_close_stream_s.take().unwrap().send(self.sid)).is_err() { + warn!( + "Other side got already dropped, probably due to timing, other side will \ + handle this gracefully" + ); + }; + } else { + let sid = self.sid; + let pid = self.pid; + debug!(?pid, ?sid, "not needed"); + } + } +} + +impl std::fmt::Debug for Participant { + #[inline] + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let status = if self.closed.load(Ordering::Relaxed) { + "[CLOSED]" + } else { + "[OPEN]" + }; + write!( + f, + "Participant {{{} local_pid: {:?}, remote_pid: {:?} }}", + status, &self.local_pid, &self.remote_pid, + ) + } +} + +impl From> for StreamError { + fn from(_err: std::sync::mpsc::SendError) -> Self { StreamError::StreamClosed } +} + +impl From> for ParticipantError { + fn from(_err: std::sync::mpsc::SendError) -> Self { ParticipantError::ParticipantClosed } +} + +impl From> for NetworkError { + fn from(_err: std::sync::mpsc::SendError) -> Self { NetworkError::NetworkClosed } +} + +impl From for NetworkError { + fn from(err: async_std::io::Error) -> Self { NetworkError::ListenFailed(err) } +} + +impl From for StreamError { + fn from(_err: std::option::NoneError) -> Self { StreamError::StreamClosed } +} + +impl From for ParticipantError { + fn from(_err: std::option::NoneError) -> Self { ParticipantError::ParticipantClosed } +} + +impl From for NetworkError { + fn from(_err: std::option::NoneError) -> Self { NetworkError::NetworkClosed } +} + +impl From for ParticipantError { + fn from(_err: mpsc::SendError) -> Self { ParticipantError::ParticipantClosed } +} + +impl From for NetworkError { + fn from(_err: mpsc::SendError) -> Self { NetworkError::NetworkClosed } +} + +impl From for ParticipantError { + fn from(_err: oneshot::Canceled) -> Self { ParticipantError::ParticipantClosed } +} + +impl From for NetworkError { + fn from(_err: oneshot::Canceled) -> Self { NetworkError::NetworkClosed } +} + +impl From> for StreamError { + fn from(err: Box) -> Self { StreamError::DeserializeError(err) } +} + +impl core::fmt::Display for StreamError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + StreamError::StreamClosed => write!(f, "stream closed"), + StreamError::DeserializeError(err) => { + write!(f, "deserialize error on message: {}", err) + }, + } + } +} + +impl core::fmt::Display for ParticipantError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + ParticipantError::ParticipantClosed => write!(f, "participant closed"), + } + } +} + +impl core::fmt::Display for NetworkError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + NetworkError::NetworkClosed => write!(f, "network closed"), + NetworkError::ListenFailed(_) => write!(f, "listening failed"), + } + } +} + +/// implementing PartialEq as it's super convenient in tests +impl core::cmp::PartialEq for StreamError { + fn eq(&self, other: &Self) -> bool { + match self { + StreamError::StreamClosed => match other { + StreamError::StreamClosed => true, + StreamError::DeserializeError(_) => false, + }, + StreamError::DeserializeError(err) => match other { + StreamError::StreamClosed => false, + StreamError::DeserializeError(other_err) => partial_eq_bincode(err, other_err), + }, + } + } +} + +impl std::error::Error for StreamError {} +impl std::error::Error for ParticipantError {} +impl std::error::Error for NetworkError {} diff --git a/network/src/channel.rs b/network/src/channel.rs new file mode 100644 index 0000000000..b62f08938a --- /dev/null +++ b/network/src/channel.rs @@ -0,0 +1,341 @@ +use crate::{ + metrics::NetworkMetrics, + protocols::Protocols, + types::{ + Cid, Frame, Pid, Sid, STREAM_ID_OFFSET1, STREAM_ID_OFFSET2, VELOREN_MAGIC_NUMBER, + VELOREN_NETWORK_VERSION, + }, +}; +use futures::{ + channel::{mpsc, oneshot}, + join, + sink::SinkExt, + stream::StreamExt, + FutureExt, +}; +use std::sync::Arc; +use tracing::*; + +pub(crate) struct Channel { + cid: Cid, + c2w_frame_r: Option>, + read_stop_receiver: Option>, +} + +impl Channel { + pub fn new(cid: u64) -> (Self, mpsc::UnboundedSender, oneshot::Sender<()>) { + let (c2w_frame_s, c2w_frame_r) = mpsc::unbounded::(); + let (read_stop_sender, read_stop_receiver) = oneshot::channel(); + ( + Self { + cid, + c2w_frame_r: Some(c2w_frame_r), + read_stop_receiver: Some(read_stop_receiver), + }, + c2w_frame_s, + read_stop_sender, + ) + } + + pub async fn run( + mut self, + protocol: Protocols, + mut w2c_cid_frame_s: mpsc::UnboundedSender<(Cid, Frame)>, + mut leftover_cid_frame: Vec<(Cid, Frame)>, + ) { + let c2w_frame_r = self.c2w_frame_r.take().unwrap(); + let read_stop_receiver = self.read_stop_receiver.take().unwrap(); + + //reapply leftovers from handshake + let cnt = leftover_cid_frame.len(); + trace!(?self.cid, ?cnt, "reapplying leftovers"); + for cid_frame in leftover_cid_frame.drain(..) { + w2c_cid_frame_s.send(cid_frame).await.unwrap(); + } + trace!(?self.cid, ?cnt, "all leftovers reapplied"); + + trace!(?self.cid, "start up channel"); + match protocol { + Protocols::Tcp(tcp) => { + futures::join!( + tcp.read_from_wire(self.cid, &mut w2c_cid_frame_s, read_stop_receiver), + tcp.write_to_wire(self.cid, c2w_frame_r), + ); + }, + Protocols::Udp(udp) => { + futures::join!( + udp.read_from_wire(self.cid, &mut w2c_cid_frame_s, read_stop_receiver), + udp.write_to_wire(self.cid, c2w_frame_r), + ); + }, + } + + trace!(?self.cid, "shut down channel"); + } +} + +#[derive(Debug)] +pub(crate) struct Handshake { + cid: Cid, + local_pid: Pid, + secret: u128, + init_handshake: bool, + metrics: Arc, +} + +impl Handshake { + #[cfg(debug_assertions)] + const WRONG_NUMBER: &'static [u8] = "Handshake does not contain the magic number requiered by \ + veloren server.\nWe are not sure if you are a valid \ + veloren client.\nClosing the connection" + .as_bytes(); + #[cfg(debug_assertions)] + const WRONG_VERSION: &'static str = "Handshake does contain a correct magic number, but \ + invalid version.\nWe don't know how to communicate with \ + you.\nClosing the connection"; + + pub fn new( + cid: u64, + local_pid: Pid, + secret: u128, + metrics: Arc, + init_handshake: bool, + ) -> Self { + Self { + cid, + local_pid, + secret, + metrics, + init_handshake, + } + } + + pub async fn setup( + self, + protocol: &Protocols, + ) -> Result<(Pid, Sid, u128, Vec<(Cid, Frame)>), ()> { + let (c2w_frame_s, c2w_frame_r) = mpsc::unbounded::(); + let (mut w2c_cid_frame_s, mut w2c_cid_frame_r) = mpsc::unbounded::<(Cid, Frame)>(); + + let (read_stop_sender, read_stop_receiver) = oneshot::channel(); + let handler_future = + self.frame_handler(&mut w2c_cid_frame_r, c2w_frame_s, read_stop_sender); + let res = match protocol { + Protocols::Tcp(tcp) => { + (join! { + tcp.read_from_wire(self.cid, &mut w2c_cid_frame_s, read_stop_receiver), + tcp.write_to_wire(self.cid, c2w_frame_r).fuse(), + handler_future, + }) + .2 + }, + Protocols::Udp(udp) => { + (join! { + udp.read_from_wire(self.cid, &mut w2c_cid_frame_s, read_stop_receiver), + udp.write_to_wire(self.cid, c2w_frame_r), + handler_future, + }) + .2 + }, + }; + + #[allow(clippy::unit_arg)] + match res { + Ok(res) => { + let mut leftover_frames = vec![]; + while let Ok(Some(cid_frame)) = w2c_cid_frame_r.try_next() { + leftover_frames.push(cid_frame); + } + let cnt = leftover_frames.len(); + if cnt > 0 { + debug!(?self.cid, ?cnt, "Some additional frames got already transfered, piping them to the bparticipant as leftover_frames"); + } + Ok((res.0, res.1, res.2, leftover_frames)) + }, + Err(e) => Err(e), + } + } + + async fn frame_handler( + &self, + w2c_cid_frame_r: &mut mpsc::UnboundedReceiver<(Cid, Frame)>, + mut c2w_frame_s: mpsc::UnboundedSender, + _read_stop_sender: oneshot::Sender<()>, + ) -> Result<(Pid, Sid, u128), ()> { + const ERR_S: &str = "Got A Raw Message, these are usually Debug Messages indicating that \ + something went wrong on network layer and connection will be closed"; + let mut pid_string = "".to_string(); + let cid_string = self.cid.to_string(); + + if self.init_handshake { + self.send_handshake(&mut c2w_frame_s).await; + } + + match w2c_cid_frame_r.next().await { + Some(( + _, + Frame::Handshake { + magic_number, + version, + }, + )) => { + trace!(?magic_number, ?version, "recv handshake"); + self.metrics + .frames_in_total + .with_label_values(&["", &cid_string, "Handshake"]) + .inc(); + if magic_number != VELOREN_MAGIC_NUMBER { + error!(?magic_number, "connection with invalid magic_number"); + #[cfg(debug_assertions)] + { + self.metrics + .frames_out_total + .with_label_values(&["", &cid_string, "Raw"]) + .inc(); + debug!("sending client instructions before killing"); + c2w_frame_s + .send(Frame::Raw(Self::WRONG_NUMBER.to_vec())) + .await + .unwrap(); + c2w_frame_s.send(Frame::Shutdown).await.unwrap(); + } + return Err(()); + } + if version != VELOREN_NETWORK_VERSION { + error!(?version, "connection with wrong network version"); + #[cfg(debug_assertions)] + { + debug!("sending client instructions before killing"); + self.metrics + .frames_out_total + .with_label_values(&["", &cid_string, "Raw"]) + .inc(); + c2w_frame_s + .send(Frame::Raw( + format!( + "{} Our Version: {:?}\nYour Version: {:?}\nClosing the \ + connection", + Self::WRONG_VERSION, + VELOREN_NETWORK_VERSION, + version, + ) + .as_bytes() + .to_vec(), + )) + .await + .unwrap(); + c2w_frame_s.send(Frame::Shutdown {}).await.unwrap(); + } + return Err(()); + } + debug!("handshake completed"); + if self.init_handshake { + self.send_init(&mut c2w_frame_s, &pid_string).await; + } else { + self.send_handshake(&mut c2w_frame_s).await; + } + }, + Some((_, Frame::Shutdown)) => { + info!("shutdown signal received"); + self.metrics + .frames_in_total + .with_label_values(&[&pid_string, &cid_string, "Shutdown"]) + .inc(); + return Err(()); + }, + Some((_, Frame::Raw(bytes))) => { + self.metrics + .frames_in_total + .with_label_values(&[&pid_string, &cid_string, "Raw"]) + .inc(); + match std::str::from_utf8(bytes.as_slice()) { + Ok(string) => error!(?string, ERR_S), + _ => error!(?bytes, ERR_S), + } + return Err(()); + }, + Some((_, frame)) => { + self.metrics + .frames_in_total + .with_label_values(&[&pid_string, &cid_string, frame.get_string()]) + .inc(); + return Err(()); + }, + None => return Err(()), + }; + + match w2c_cid_frame_r.next().await { + Some((_, Frame::Init { pid, secret })) => { + debug!(?pid, "Participant send their ID"); + pid_string = pid.to_string(); + self.metrics + .frames_in_total + .with_label_values(&[&pid_string, &cid_string, "ParticipantId"]) + .inc(); + let stream_id_offset = if self.init_handshake { + STREAM_ID_OFFSET1 + } else { + self.send_init(&mut c2w_frame_s, &pid_string).await; + STREAM_ID_OFFSET2 + }; + info!(?pid, "this Handshake is now configured!"); + Ok((pid, stream_id_offset, secret)) + }, + Some((_, Frame::Shutdown)) => { + info!("shutdown signal received"); + self.metrics + .frames_in_total + .with_label_values(&[&pid_string, &cid_string, "Shutdown"]) + .inc(); + Err(()) + }, + Some((_, Frame::Raw(bytes))) => { + self.metrics + .frames_in_total + .with_label_values(&[&pid_string, &cid_string, "Raw"]) + .inc(); + match std::str::from_utf8(bytes.as_slice()) { + Ok(string) => error!(?string, ERR_S), + _ => error!(?bytes, ERR_S), + } + Err(()) + }, + Some((_, frame)) => { + self.metrics + .frames_in_total + .with_label_values(&[&pid_string, &cid_string, frame.get_string()]) + .inc(); + Err(()) + }, + None => Err(()), + } + } + + async fn send_handshake(&self, c2w_frame_s: &mut mpsc::UnboundedSender) { + self.metrics + .frames_out_total + .with_label_values(&["", &self.cid.to_string(), "Handshake"]) + .inc(); + c2w_frame_s + .send(Frame::Handshake { + magic_number: VELOREN_MAGIC_NUMBER, + version: VELOREN_NETWORK_VERSION, + }) + .await + .unwrap(); + } + + async fn send_init(&self, c2w_frame_s: &mut mpsc::UnboundedSender, pid_string: &str) { + self.metrics + .frames_out_total + .with_label_values(&[pid_string, &self.cid.to_string(), "ParticipantId"]) + .inc(); + c2w_frame_s + .send(Frame::Init { + pid: self.local_pid, + secret: self.secret, + }) + .await + .unwrap(); + } +} diff --git a/network/src/lib.rs b/network/src/lib.rs new file mode 100644 index 0000000000..36568f2dc7 --- /dev/null +++ b/network/src/lib.rs @@ -0,0 +1,118 @@ +#![deny(unsafe_code)] +#![cfg_attr(test, deny(rust_2018_idioms))] +#![cfg_attr(test, deny(warnings))] +#![feature(try_trait, const_if_match)] + +//! Crate to handle high level networking of messages with different +//! requirements and priorities over a number of protocols +//! +//! To start with the `veloren_network` crate you should focus on the 3 +//! elementar structs [`Network`], [`Participant`] and [`Stream`]. +//! +//! Say you have an application that wants to communicate with other application +//! over a Network or on the same computer. Now each application instances the +//! struct [`Network`] once with a new [`Pid`]. The Pid is necessary to identify +//! other [`Networks`] over the network protocols (e.g. TCP, UDP) +//! +//! To connect to another application, you must know it's [`Address`]. One side +//! will call [`connect`], the other [`connected`]. If successfull both +//! applications will now get a [`Arc`]. +//! +//! This [`Participant`] represents the connection between those 2 applications. +//! over the respective [`Address`] and with it the choosen network protocol. +//! However messages can't be send directly via [`Participants`], instead you +//! must open a [`Stream`] on it. Like above, one side has to call [`open`], the +//! other [`opened`]. [`Streams`] can have a different priority and +//! [`Promises`]. +//! +//! You can now use the [`Stream`] to [`send`] and [`recv`] in both directions. +//! You can send all kind of messages that implement [`serde`]. +//! As the receiving side needs to know the format, it sometimes is useful to +//! always send a specific Enum and then handling it with a big `match` +//! statement This create makes heavily use of `async`, except for [`send`] +//! which returns always directly. +//! +//! For best practices see the `examples` folder of this crate containing useful +//! code snippets, a simple client/server below. Of course due to the async +//! nature, no strict client server separation is necessary +//! +//! # Examples +//! ```rust +//! use async_std::task::sleep; +//! use futures::{executor::block_on, join}; +//! use veloren_network::{Address, Network, Pid, PROMISES_CONSISTENCY, PROMISES_ORDERED}; +//! +//! // Client +//! async fn client() -> std::result::Result<(), Box> { +//! sleep(std::time::Duration::from_secs(1)).await; // `connect` MUST be after `listen` +//! let (client_network, f) = Network::new(Pid::new(), None); +//! std::thread::spawn(f); +//! let server = client_network +//! .connect(Address::Tcp("127.0.0.1:12345".parse().unwrap())) +//! .await?; +//! let mut stream = server +//! .open(10, PROMISES_ORDERED | PROMISES_CONSISTENCY) +//! .await?; +//! stream.send("Hello World")?; +//! Ok(()) +//! } +//! +//! // Server +//! async fn server() -> std::result::Result<(), Box> { +//! let (server_network, f) = Network::new(Pid::new(), None); +//! std::thread::spawn(f); +//! server_network +//! .listen(Address::Tcp("127.0.0.1:12345".parse().unwrap())) +//! .await?; +//! let client = server_network.connected().await?; +//! let mut stream = client.opened().await?; +//! let msg: String = stream.recv().await?; +//! println!("got message: {}", msg); +//! assert_eq!(msg, "Hello World"); +//! Ok(()) +//! } +//! +//! fn main() -> std::result::Result<(), Box> { +//! block_on(async { +//! let (result_c, result_s) = join!(client(), server(),); +//! result_c?; +//! result_s?; +//! Ok(()) +//! }) +//! } +//! ``` +//! +//! [`Network`]: crate::api::Network +//! [`Networks`]: crate::api::Network +//! [`connect`]: crate::api::Network::connect +//! [`connected`]: crate::api::Network::connected +//! [`Arc`]: crate::api::Participant +//! [`Participant`]: crate::api::Participant +//! [`Participants`]: crate::api::Participant +//! [`open`]: crate::api::Participant::open +//! [`opened`]: crate::api::Participant::opened +//! [`Stream`]: crate::api::Stream +//! [`Streams`]: crate::api::Stream +//! [`send`]: crate::api::Stream::send +//! [`recv`]: crate::api::Stream::recv +//! [`Pid`]: crate::types::Pid +//! [`Address`]: crate::api::Address +//! [`Promises`]: crate::types::Promises + +mod api; +mod channel; +mod message; +mod metrics; +mod participant; +mod prios; +mod protocols; +mod scheduler; +#[macro_use] +mod types; + +pub use api::{Address, Network, NetworkError, Participant, ParticipantError, Stream, StreamError}; +pub use message::MessageBuffer; +pub use types::{ + Pid, Promises, PROMISES_COMPRESSED, PROMISES_CONSISTENCY, PROMISES_ENCRYPTED, + PROMISES_GUARANTEED_DELIVERY, PROMISES_NONE, PROMISES_ORDERED, +}; diff --git a/network/src/message.rs b/network/src/message.rs new file mode 100644 index 0000000000..e1460eaaab --- /dev/null +++ b/network/src/message.rs @@ -0,0 +1,146 @@ +use serde::{de::DeserializeOwned, Serialize}; +//use std::collections::VecDeque; +use crate::types::{Mid, Sid}; +use std::{io, sync::Arc}; + +//Todo: Evaluate switching to VecDeque for quickly adding and removing data +// from front, back. +// - It would prob requiere custom bincode code but thats possible. +/// Support struct used for optimising sending the same Message to multiple +/// [`Stream`] +/// +/// For an example usage see: [`send_raw`] +/// +/// [`Stream`]: crate::api::Stream +/// [`send_raw`]: crate::api::Stream::send_raw +pub struct MessageBuffer { + pub data: Vec, +} + +#[derive(Debug)] +pub(crate) struct OutgoingMessage { + pub buffer: Arc, + pub cursor: u64, + pub mid: Mid, + pub sid: Sid, +} + +#[derive(Debug)] +pub(crate) struct IncomingMessage { + pub buffer: MessageBuffer, + pub length: u64, + pub mid: Mid, + pub sid: Sid, +} + +pub(crate) fn serialize(message: &M) -> MessageBuffer { + //this will never fail: https://docs.rs/bincode/0.8.0/bincode/fn.serialize.html + let writer = bincode::serialize(message).unwrap(); + MessageBuffer { data: writer } +} + +//pub(crate) fn deserialize(buffer: MessageBuffer) -> +// std::Result> { +pub(crate) fn deserialize(buffer: MessageBuffer) -> bincode::Result { + let span = buffer.data; + //this might fail if you choose the wrong type for M. in that case probably X + // got transfered while you assume Y. probably this means your application + // logic is wrong. E.g. You expect a String, but just get a u8. + bincode::deserialize(span.as_slice()) +} + +///wouldn't trust this aaaassss much, fine for tests +pub(crate) fn partial_eq_io_error(first: &io::Error, second: &io::Error) -> bool { + if let Some(f) = first.raw_os_error() { + if let Some(s) = second.raw_os_error() { + f == s + } else { + false + } + } else { + let fk = first.kind(); + fk == second.kind() && fk != io::ErrorKind::Other + } +} + +pub(crate) fn partial_eq_bincode(first: &bincode::ErrorKind, second: &bincode::ErrorKind) -> bool { + match *first { + bincode::ErrorKind::Io(ref f) => match *second { + bincode::ErrorKind::Io(ref s) => partial_eq_io_error(f, s), + _ => false, + }, + bincode::ErrorKind::InvalidUtf8Encoding(f) => match *second { + bincode::ErrorKind::InvalidUtf8Encoding(s) => f == s, + _ => false, + }, + bincode::ErrorKind::InvalidBoolEncoding(f) => match *second { + bincode::ErrorKind::InvalidBoolEncoding(s) => f == s, + _ => false, + }, + bincode::ErrorKind::InvalidCharEncoding => match *second { + bincode::ErrorKind::InvalidCharEncoding => true, + _ => false, + }, + bincode::ErrorKind::InvalidTagEncoding(f) => match *second { + bincode::ErrorKind::InvalidTagEncoding(s) => f == s, + _ => false, + }, + bincode::ErrorKind::DeserializeAnyNotSupported => match *second { + bincode::ErrorKind::DeserializeAnyNotSupported => true, + _ => false, + }, + bincode::ErrorKind::SizeLimit => match *second { + bincode::ErrorKind::SizeLimit => true, + _ => false, + }, + bincode::ErrorKind::SequenceMustHaveLength => match *second { + bincode::ErrorKind::SequenceMustHaveLength => true, + _ => false, + }, + bincode::ErrorKind::Custom(ref f) => match *second { + bincode::ErrorKind::Custom(ref s) => f == s, + _ => false, + }, + } +} + +impl std::fmt::Debug for MessageBuffer { + #[inline] + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + //TODO: small messages! + let len = self.data.len(); + if len > 20 { + write!( + f, + "MessageBuffer(len: {}, {}, {}, {}, {:?}..{:?})", + len, + u32::from_le_bytes([self.data[0], self.data[1], self.data[2], self.data[3]]), + u32::from_le_bytes([self.data[4], self.data[5], self.data[6], self.data[7]]), + u32::from_le_bytes([self.data[8], self.data[9], self.data[10], self.data[11]]), + &self.data[13..16], + &self.data[len - 8..len] + ) + } else { + write!(f, "MessageBuffer(len: {}, {:?})", len, &self.data[..]) + } + } +} + +#[cfg(test)] +mod tests { + use crate::message::*; + + #[test] + fn serialize_test() { + let msg = "abc"; + let mb = serialize(&msg); + assert_eq!(mb.data.len(), 11); + assert_eq!(mb.data[0], 3); + assert_eq!(mb.data[1], 0); + assert_eq!(mb.data[7], 0); + assert_eq!(mb.data[8], 'a' as u8); + assert_eq!(mb.data[8], 97); + assert_eq!(mb.data[9], 'b' as u8); + assert_eq!(mb.data[10], 'c' as u8); + } +} diff --git a/network/src/metrics.rs b/network/src/metrics.rs new file mode 100644 index 0000000000..eb875040f7 --- /dev/null +++ b/network/src/metrics.rs @@ -0,0 +1,397 @@ +use crate::types::{Cid, Frame, Pid}; +use prometheus::{ + core::{AtomicI64, GenericCounter}, + IntCounter, IntCounterVec, IntGauge, IntGaugeVec, Opts, Registry, +}; +use std::error::Error; +use tracing::*; + +//TODO: switch over to Counter for frames_count, message_count, bytes_send, +// frames_message_count 1 NetworkMetrics per Network +#[allow(dead_code)] +pub struct NetworkMetrics { + pub listen_requests_total: IntCounterVec, + pub connect_requests_total: IntCounterVec, + pub participants_connected_total: IntCounter, + pub participants_disconnected_total: IntCounter, + // opened Channels, seperated by PARTICIPANT + pub channels_connected_total: IntCounterVec, + pub channels_disconnected_total: IntCounterVec, + // opened streams, seperated by PARTICIPANT + pub streams_opened_total: IntCounterVec, + pub streams_closed_total: IntCounterVec, + pub network_info: IntGauge, + // Frames counted a channel level, seperated by CHANNEL (and PARTICIPANT) AND FRAME TYPE, + pub frames_out_total: IntCounterVec, + pub frames_in_total: IntCounterVec, + // Frames counted at protocol level, seperated by CHANNEL (and PARTICIPANT) AND FRAME TYPE, + pub frames_wire_out_total: IntCounterVec, + pub frames_wire_in_total: IntCounterVec, + // throughput at protocol level, seperated by CHANNEL (and PARTICIPANT), + pub wire_out_throughput: IntCounterVec, + pub wire_in_throughput: IntCounterVec, + // send(prio) Messages count, seperated by STREAM AND PARTICIPANT, + pub message_out_total: IntCounterVec, + // send(prio) Messages throughput, seperated by STREAM AND PARTICIPANT, + pub message_out_throughput: IntCounterVec, + // TODO: queued Messages, seperated by STREAM (add PART, CHANNEL), + // queued Messages, seperated by PARTICIPANT + pub queued_count: IntGaugeVec, + // TODO: queued Messages bytes, seperated by STREAM (add PART, CHANNEL), + // queued Messages bytes, seperated by PARTICIPANT + pub queued_bytes: IntGaugeVec, + // ping calculated based on last msg seperated by PARTICIPANT + pub participants_ping: IntGaugeVec, +} + +impl NetworkMetrics { + #[allow(dead_code)] + pub fn new(local_pid: &Pid) -> Result> { + let listen_requests_total = IntCounterVec::new( + Opts::new( + "listen_requests_total", + "shows the number of listen requests to the scheduler", + ), + &["protocol"], + )?; + let connect_requests_total = IntCounterVec::new( + Opts::new( + "connect_requests_total", + "shows the number of connect requests to the scheduler", + ), + &["protocol"], + )?; + let participants_connected_total = IntCounter::with_opts(Opts::new( + "participants_connected_total", + "shows the number of participants connected to the network", + ))?; + let participants_disconnected_total = IntCounter::with_opts(Opts::new( + "participants_disconnected_total", + "shows the number of participants disconnected to the network", + ))?; + let channels_connected_total = IntCounterVec::new( + Opts::new( + "channels_connected_total", + "number of all channels currently connected on the network", + ), + &["participant"], + )?; + let channels_disconnected_total = IntCounterVec::new( + Opts::new( + "channels_disconnected_total", + "number of all channels currently disconnected on the network", + ), + &["participant"], + )?; + let streams_opened_total = IntCounterVec::new( + Opts::new( + "streams_opened_total", + "number of all streams currently open on the network", + ), + &["participant"], + )?; + let streams_closed_total = IntCounterVec::new( + Opts::new( + "streams_closed_total", + "number of all streams currently open on the network", + ), + &["participant"], + )?; + let opts = Opts::new("network_info", "Static Network information") + .const_label( + "version", + &format!( + "{}.{}.{}", + &crate::types::VELOREN_NETWORK_VERSION[0], + &crate::types::VELOREN_NETWORK_VERSION[1], + &crate::types::VELOREN_NETWORK_VERSION[2] + ), + ) + .const_label("local_pid", &format!("{}", &local_pid)); + let network_info = IntGauge::with_opts(opts)?; + let frames_out_total = IntCounterVec::new( + Opts::new( + "frames_out_total", + "number of all frames send per channel, at the channel level", + ), + &["participant", "channel", "frametype"], + )?; + let frames_in_total = IntCounterVec::new( + Opts::new( + "frames_in_total", + "number of all frames received per channel, at the channel level", + ), + &["participant", "channel", "frametype"], + )?; + let frames_wire_out_total = IntCounterVec::new( + Opts::new( + "frames_wire_out_total", + "number of all frames send per channel, at the protocol level", + ), + &["channel", "frametype"], + )?; + let frames_wire_in_total = IntCounterVec::new( + Opts::new( + "frames_wire_in_total", + "number of all frames received per channel, at the protocol level", + ), + &["channel", "frametype"], + )?; + let wire_out_throughput = IntCounterVec::new( + Opts::new( + "wire_out_throughput", + "throupgput of all data frames send per channel, at the protocol level", + ), + &["channel"], + )?; + let wire_in_throughput = IntCounterVec::new( + Opts::new( + "wire_in_throughput", + "throupgput of all data frames send per channel, at the protocol level", + ), + &["channel"], + )?; + //TODO IN + let message_out_total = IntCounterVec::new( + Opts::new( + "message_out_total", + "number of messages send by streams on the network", + ), + &["participant", "stream"], + )?; + //TODO IN + let message_out_throughput = IntCounterVec::new( + Opts::new( + "message_out_throughput", + "throughput of messages send by streams on the network", + ), + &["participant", "stream"], + )?; + let queued_count = IntGaugeVec::new( + Opts::new( + "queued_count", + "queued number of messages by participant on the network", + ), + &["channel"], + )?; + let queued_bytes = IntGaugeVec::new( + Opts::new( + "queued_bytes", + "queued bytes of messages by participant on the network", + ), + &["channel"], + )?; + let participants_ping = IntGaugeVec::new( + Opts::new( + "participants_ping", + "ping time to participants on the network", + ), + &["channel"], + )?; + + Ok(Self { + listen_requests_total, + connect_requests_total, + participants_connected_total, + participants_disconnected_total, + channels_connected_total, + channels_disconnected_total, + streams_opened_total, + streams_closed_total, + network_info, + frames_out_total, + frames_in_total, + frames_wire_out_total, + frames_wire_in_total, + wire_out_throughput, + wire_in_throughput, + message_out_total, + message_out_throughput, + queued_count, + queued_bytes, + participants_ping, + }) + } + + pub fn register(&self, registry: &Registry) -> Result<(), Box> { + registry.register(Box::new(self.listen_requests_total.clone()))?; + registry.register(Box::new(self.connect_requests_total.clone()))?; + registry.register(Box::new(self.participants_connected_total.clone()))?; + registry.register(Box::new(self.participants_disconnected_total.clone()))?; + registry.register(Box::new(self.channels_connected_total.clone()))?; + registry.register(Box::new(self.channels_disconnected_total.clone()))?; + registry.register(Box::new(self.streams_opened_total.clone()))?; + registry.register(Box::new(self.streams_closed_total.clone()))?; + registry.register(Box::new(self.network_info.clone()))?; + registry.register(Box::new(self.frames_out_total.clone()))?; + registry.register(Box::new(self.frames_in_total.clone()))?; + registry.register(Box::new(self.frames_wire_out_total.clone()))?; + registry.register(Box::new(self.frames_wire_in_total.clone()))?; + registry.register(Box::new(self.wire_out_throughput.clone()))?; + registry.register(Box::new(self.wire_in_throughput.clone()))?; + registry.register(Box::new(self.message_out_total.clone()))?; + registry.register(Box::new(self.message_out_throughput.clone()))?; + registry.register(Box::new(self.queued_count.clone()))?; + registry.register(Box::new(self.queued_bytes.clone()))?; + registry.register(Box::new(self.participants_ping.clone()))?; + Ok(()) + } + + //pub fn _is_100th_tick(&self) -> bool { + // self.tick.load(Ordering::Relaxed).rem_euclid(100) == 0 } +} + +impl std::fmt::Debug for NetworkMetrics { + #[inline] + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "NetworkMetrics()") + } +} + +/* +pub(crate) struct PidCidFrameCache { + metric: MetricVec, + pid: String, + cache: Vec<[T::M; 8]>, +} +*/ + +pub(crate) struct PidCidFrameCache { + metric: IntCounterVec, + pid: String, + cache: Vec<[GenericCounter; Frame::FRAMES_LEN as usize]>, +} + +impl PidCidFrameCache { + const CACHE_SIZE: usize = 16; + + pub fn new(metric: IntCounterVec, pid: Pid) -> Self { + Self { + metric, + pid: pid.to_string(), + cache: vec![], + } + } + + fn populate(&mut self, cid: Cid) { + let start_cid = self.cache.len(); + for i in start_cid..=cid as usize { + let cid = (i as Cid).to_string(); + let entry = [ + self.metric + .with_label_values(&[&self.pid, &cid, Frame::int_to_string(0)]), + self.metric + .with_label_values(&[&self.pid, &cid, Frame::int_to_string(1)]), + self.metric + .with_label_values(&[&self.pid, &cid, Frame::int_to_string(2)]), + self.metric + .with_label_values(&[&self.pid, &cid, Frame::int_to_string(3)]), + self.metric + .with_label_values(&[&self.pid, &cid, Frame::int_to_string(4)]), + self.metric + .with_label_values(&[&self.pid, &cid, Frame::int_to_string(5)]), + self.metric + .with_label_values(&[&self.pid, &cid, Frame::int_to_string(6)]), + self.metric + .with_label_values(&[&self.pid, &cid, Frame::int_to_string(7)]), + ]; + self.cache.push(entry); + } + } + + pub fn with_label_values(&mut self, cid: Cid, frame: &Frame) -> &GenericCounter { + if cid > (Self::CACHE_SIZE as Cid) { + warn!( + ?cid, + "cid, getting quite high, is this a attack on the cache?" + ); + } + self.populate(cid); + &self.cache[cid as usize][frame.get_int() as usize] + } +} + +pub(crate) struct CidFrameCache { + cache: [GenericCounter; Frame::FRAMES_LEN as usize], +} + +impl CidFrameCache { + pub fn new(metric: IntCounterVec, cid: Cid) -> Self { + let cid = cid.to_string(); + let cache = [ + metric.with_label_values(&[&cid, Frame::int_to_string(0)]), + metric.with_label_values(&[&cid, Frame::int_to_string(1)]), + metric.with_label_values(&[&cid, Frame::int_to_string(2)]), + metric.with_label_values(&[&cid, Frame::int_to_string(3)]), + metric.with_label_values(&[&cid, Frame::int_to_string(4)]), + metric.with_label_values(&[&cid, Frame::int_to_string(5)]), + metric.with_label_values(&[&cid, Frame::int_to_string(6)]), + metric.with_label_values(&[&cid, Frame::int_to_string(7)]), + ]; + Self { cache } + } + + pub fn with_label_values(&mut self, frame: &Frame) -> &GenericCounter { + &self.cache[frame.get_int() as usize] + } +} + +#[cfg(test)] +mod tests { + use crate::{ + metrics::*, + types::{Frame, Pid}, + }; + + #[test] + fn register_metrics() { + let registry = Registry::new(); + let metrics = NetworkMetrics::new(&Pid::fake(1)).unwrap(); + metrics.register(®istry).unwrap(); + } + + #[test] + fn pid_cid_frame_cache() { + let pid = Pid::fake(1); + let frame1 = Frame::Raw("Foo".as_bytes().to_vec()); + let frame2 = Frame::Raw("Bar".as_bytes().to_vec()); + let metrics = NetworkMetrics::new(&pid).unwrap(); + let mut cache = PidCidFrameCache::new(metrics.frames_in_total, pid); + let v1 = cache.with_label_values(1, &frame1); + v1.inc(); + assert_eq!(v1.get(), 1); + let v2 = cache.with_label_values(1, &frame1); + v2.inc(); + assert_eq!(v2.get(), 2); + let v3 = cache.with_label_values(1, &frame2); + v3.inc(); + assert_eq!(v3.get(), 3); + let v4 = cache.with_label_values(3, &frame1); + v4.inc(); + assert_eq!(v4.get(), 1); + let v5 = cache.with_label_values(3, &Frame::Shutdown); + v5.inc(); + assert_eq!(v5.get(), 1); + } + + #[test] + fn cid_frame_cache() { + let pid = Pid::fake(1); + let frame1 = Frame::Raw("Foo".as_bytes().to_vec()); + let frame2 = Frame::Raw("Bar".as_bytes().to_vec()); + let metrics = NetworkMetrics::new(&pid).unwrap(); + let mut cache = CidFrameCache::new(metrics.frames_wire_out_total, 1); + let v1 = cache.with_label_values(&frame1); + v1.inc(); + assert_eq!(v1.get(), 1); + let v2 = cache.with_label_values(&frame1); + v2.inc(); + assert_eq!(v2.get(), 2); + let v3 = cache.with_label_values(&frame2); + v3.inc(); + assert_eq!(v3.get(), 3); + let v4 = cache.with_label_values(&Frame::Shutdown); + v4.inc(); + assert_eq!(v4.get(), 1); + } +} diff --git a/network/src/mio_worker.rs b/network/src/mio_worker.rs new file mode 100644 index 0000000000..805adf82ee --- /dev/null +++ b/network/src/mio_worker.rs @@ -0,0 +1,308 @@ +use crate::{api::Promise, internal::Channel, message::OutGoingMessage, tcp_channel::TcpChannel}; +use enumset::EnumSet; +use mio::{self, net::TcpListener, Poll, PollOpt, Ready, Token}; +use mio_extras::channel::{channel, Receiver, Sender}; +use std::{ + collections::HashMap, + sync::{mpsc::TryRecvError, Arc, RwLock}, + time::Instant, +}; +use tlid; +use tracing::{debug, error, info, span, trace, warn, Level}; +use uvth::ThreadPool; + +#[derive(Debug)] +pub(crate) enum TokenObjects { + TcpListener(TcpListener), + TcpChannel(TcpChannel), +} + +pub(crate) struct MioTokens { + pool: tlid::Pool>, + pub tokens: HashMap, //TODO: move to Vec for faster lookup +} + +impl MioTokens { + pub fn new(pool: tlid::Pool>) -> Self { + MioTokens { + pool, + tokens: HashMap::new(), + } + } + + pub fn construct(&mut self) -> Token { Token(self.pool.next()) } + + pub fn insert(&mut self, tok: Token, obj: TokenObjects) { + trace!(?tok, ?obj, "added new token"); + self.tokens.insert(tok, obj); + } +} + +// MioStatistics should be copied in order to not hold locks for long +#[derive(Clone, Default)] +pub struct MioStatistics { + nano_wait: u128, + nano_busy: u128, +} + +pub(crate) enum CtrlMsg { + Shutdown, + Register(TokenObjects, Ready, PollOpt), + OpenStream { + pid: uuid::Uuid, + prio: u8, + promises: EnumSet, + }, + CloseStream { + pid: uuid::Uuid, + sid: u32, + }, + Send(OutGoingMessage), +} + +/* + The MioWorker runs in it's own thread, + it has a given set of Channels to work with. + It is monitored, and when it's thread is fully loaded it can be splitted up into 2 MioWorkers +*/ +pub struct MioWorker { + tag: u64, /* only relevant for logs */ + pid: uuid::Uuid, + poll: Arc, + mio_statistics: Arc>, + ctrl_tx: Sender, +} + +impl MioWorker { + pub const CTRL_TOK: Token = Token(0); + + pub fn new( + tag: u64, + pid: uuid::Uuid, + thread_pool: Arc, + mut token_pool: tlid::Pool>, + ) -> Self { + let poll = Arc::new(Poll::new().unwrap()); + let poll_clone = poll.clone(); + let mio_statistics = Arc::new(RwLock::new(MioStatistics::default())); + let mio_statistics_clone = mio_statistics.clone(); + + let (ctrl_tx, ctrl_rx) = channel(); + poll.register(&ctrl_rx, Self::CTRL_TOK, Ready::readable(), PollOpt::edge()) + .unwrap(); + // reserve 10 tokens in case they start with 0, //TODO: cleaner method + for _ in 0..10 { + token_pool.next(); + } + + let mw = MioWorker { + tag, + pid, + poll, + mio_statistics, + ctrl_tx, + }; + thread_pool.execute(move || { + mio_worker( + tag, + pid, + poll_clone, + mio_statistics_clone, + token_pool, + ctrl_rx, + ) + }); + mw + } + + pub fn get_load_ratio(&self) -> f32 { + let statistics = self.mio_statistics.read().unwrap(); + statistics.nano_busy as f32 / (statistics.nano_busy + statistics.nano_wait + 1) as f32 + } + + //TODO: split 4->5 MioWorkers and merge 5->4 MioWorkers + + pub(crate) fn get_tx(&self) -> Sender { self.ctrl_tx.clone() } +} + +impl Drop for MioWorker { + fn drop(&mut self) { let _ = self.ctrl_tx.send(CtrlMsg::Shutdown); } +} + +fn mio_worker( + tag: u64, + pid: uuid::Uuid, + poll: Arc, + mio_statistics: Arc>, + mut token_pool: tlid::Pool>, + ctrl_rx: Receiver, +) { + let mut mio_tokens = MioTokens::new(token_pool); + let mut events = mio::Events::with_capacity(1024); + let mut buf: [u8; 65000] = [0; 65000]; + let span = span!(Level::INFO, "mio worker", ?tag); + let _enter = span.enter(); + loop { + let time_before_poll = Instant::now(); + if let Err(err) = poll.poll(&mut events, None) { + error!("network poll error: {}", err); + return; + } + let time_after_poll = Instant::now(); + for event in &events { + match event.token() { + MioWorker::CTRL_TOK => { + if handle_ctl(&ctrl_rx, &mut mio_tokens, &poll, &mut buf, time_after_poll) { + return; + } + }, + _ => handle_tok( + pid, + event, + &mut mio_tokens, + &poll, + &mut buf, + time_after_poll, + ), + }; + } + handle_statistics(&mio_statistics, time_before_poll, time_after_poll); + } +} + +fn handle_ctl( + ctrl_rx: &Receiver, + mio_tokens: &mut MioTokens, + poll: &Arc, + buf: &mut [u8; 65000], + time_after_poll: Instant, +) -> bool { + match ctrl_rx.try_recv() { + Ok(CtrlMsg::Shutdown) => { + debug!("Shutting Down"); + return true; + }, + Ok(CtrlMsg::Register(handle, interest, opts)) => { + let tok = mio_tokens.construct(); + match &handle { + TokenObjects::TcpListener(h) => poll.register(h, tok, interest, opts).unwrap(), + TokenObjects::TcpChannel(channel) => poll + .register(&channel.tcpstream, tok, interest, opts) + .unwrap(), + } + debug!(?handle, ?tok, "Registered new handle"); + mio_tokens.insert(tok, handle); + }, + Ok(CtrlMsg::OpenStream { + pid, + prio, + promises, + }) => { + for (tok, obj) in mio_tokens.tokens.iter_mut() { + if let TokenObjects::TcpChannel(channel) = obj { + channel.open_stream(prio, promises); //TODO: check participant + channel.write(buf, time_after_poll); + } + } + //TODO: + }, + Ok(CtrlMsg::CloseStream { pid, sid }) => { + //TODO: + for to in mio_tokens.tokens.values_mut() { + if let TokenObjects::TcpChannel(channel) = to { + channel.close_stream(sid); //TODO: check participant + channel.write(buf, time_after_poll); + } + } + }, + Ok(_) => unimplemented!("dad"), + Err(TryRecvError::Empty) => {}, + Err(err) => { + //postbox_tx.send(Err(err.into()))?; + return true; + }, + } + false +} + +fn handle_tok( + pid: uuid::Uuid, + event: mio::Event, + mio_tokens: &mut MioTokens, + poll: &Arc, + buf: &mut [u8; 65000], + time_after_poll: Instant, +) { + match mio_tokens.tokens.get_mut(&event.token()) { + Some(e) => { + trace!(?event, "event"); + match e { + TokenObjects::TcpListener(listener) => match listener.accept() { + Ok((mut remote_stream, _)) => { + info!(?remote_stream, "remote connected"); + + let tok = mio_tokens.construct(); + poll.register( + &remote_stream, + tok, + Ready::readable() | Ready::writable(), + PollOpt::edge(), + ) + .unwrap(); + trace!(?remote_stream, ?tok, "registered"); + let mut channel = TcpChannel::new(remote_stream); + channel.handshake(); + channel.participant_id(pid); + + mio_tokens + .tokens + .insert(tok, TokenObjects::TcpChannel(channel)); + }, + Err(err) => { + error!(?err, "error during remote connected"); + }, + }, + TokenObjects::TcpChannel(channel) => { + if event.readiness().is_readable() { + trace!(?channel.tcpstream, "stream readable"); + channel.read(buf, time_after_poll); + } + if event.readiness().is_writable() { + trace!(?channel.tcpstream, "stream writeable"); + channel.write(buf, time_after_poll); + } + }, + } + }, + None => panic!("Unexpected event token '{:?}'", &event.token()), + }; +} + +fn handle_statistics( + mio_statistics: &Arc>, + time_before_poll: Instant, + time_after_poll: Instant, +) { + let time_after_work = Instant::now(); + match mio_statistics.try_write() { + Ok(mut mio_statistics) => { + const OLD_KEEP_FACTOR: f64 = 0.995; + //in order to weight new data stronger than older we fade them out with a + // factor < 1. for 0.995 under full load (500 ticks a 1ms) we keep 8% of the old + // value this means, that we start to see load comming up after + // 500ms, but not for small spikes - as reordering for smaller spikes would be + // to slow + mio_statistics.nano_wait = (mio_statistics.nano_wait as f64 * OLD_KEEP_FACTOR) as u128 + + time_after_poll.duration_since(time_before_poll).as_nanos(); + mio_statistics.nano_busy = (mio_statistics.nano_busy as f64 * OLD_KEEP_FACTOR) as u128 + + time_after_work.duration_since(time_after_poll).as_nanos(); + + trace!( + "current Load {}", + mio_statistics.nano_busy as f32 + / (mio_statistics.nano_busy + mio_statistics.nano_wait + 1) as f32 + ); + }, + Err(e) => warn!("statistics dropped because they are currently accecssed"), + } +} diff --git a/network/src/participant.rs b/network/src/participant.rs new file mode 100644 index 0000000000..0dec87fd70 --- /dev/null +++ b/network/src/participant.rs @@ -0,0 +1,625 @@ +use crate::{ + api::Stream, + channel::Channel, + message::{IncomingMessage, MessageBuffer, OutgoingMessage}, + metrics::{NetworkMetrics, PidCidFrameCache}, + prios::PrioManager, + protocols::Protocols, + types::{Cid, Frame, Pid, Prio, Promises, Sid}, +}; +use async_std::sync::RwLock; +use futures::{ + channel::{mpsc, oneshot}, + future::FutureExt, + select, + sink::SinkExt, + stream::StreamExt, +}; +use std::{ + collections::{HashMap, VecDeque}, + sync::{ + atomic::{AtomicBool, AtomicUsize, Ordering}, + Arc, + }, + time::{Duration, Instant}, +}; +use tracing::*; + +#[derive(Debug)] +struct ChannelInfo { + cid: Cid, + cid_string: String, //optimisationmetrics + b2w_frame_s: mpsc::UnboundedSender, + b2r_read_shutdown: oneshot::Sender<()>, +} + +#[derive(Debug)] +struct StreamInfo { + prio: Prio, + promises: Promises, + b2a_msg_recv_s: mpsc::UnboundedSender, + closed: Arc, +} + +#[derive(Debug)] +#[allow(clippy::type_complexity)] +struct ControlChannels { + a2b_steam_open_r: mpsc::UnboundedReceiver<(Prio, Promises, oneshot::Sender)>, + b2a_stream_opened_s: mpsc::UnboundedSender, + s2b_create_channel_r: + mpsc::UnboundedReceiver<(Cid, Sid, Protocols, Vec<(Cid, Frame)>, oneshot::Sender<()>)>, + a2b_close_stream_r: mpsc::UnboundedReceiver, + a2b_close_stream_s: mpsc::UnboundedSender, + s2b_shutdown_bparticipant_r: oneshot::Receiver>>, /* own */ +} + +#[derive(Debug)] +pub struct BParticipant { + remote_pid: Pid, + remote_pid_string: String, //optimisation + offset_sid: Sid, + channels: Arc>>, + streams: RwLock>, + running_mgr: AtomicUsize, + run_channels: Option, + metrics: Arc, +} + +impl BParticipant { + #[allow(clippy::type_complexity)] + pub(crate) fn new( + remote_pid: Pid, + offset_sid: Sid, + metrics: Arc, + ) -> ( + Self, + mpsc::UnboundedSender<(Prio, Promises, oneshot::Sender)>, + mpsc::UnboundedReceiver, + mpsc::UnboundedSender<(Cid, Sid, Protocols, Vec<(Cid, Frame)>, oneshot::Sender<()>)>, + oneshot::Sender>>, + ) { + let (a2b_steam_open_s, a2b_steam_open_r) = + mpsc::unbounded::<(Prio, Promises, oneshot::Sender)>(); + let (b2a_stream_opened_s, b2a_stream_opened_r) = mpsc::unbounded::(); + let (a2b_close_stream_s, a2b_close_stream_r) = mpsc::unbounded(); + let (s2b_shutdown_bparticipant_s, s2b_shutdown_bparticipant_r) = oneshot::channel(); + let (s2b_create_channel_s, s2b_create_channel_r) = mpsc::unbounded(); + + let run_channels = Some(ControlChannels { + a2b_steam_open_r, + b2a_stream_opened_s, + s2b_create_channel_r, + a2b_close_stream_r, + a2b_close_stream_s, + s2b_shutdown_bparticipant_r, + }); + + ( + Self { + remote_pid, + remote_pid_string: remote_pid.to_string(), + offset_sid, + channels: Arc::new(RwLock::new(vec![])), + streams: RwLock::new(HashMap::new()), + running_mgr: AtomicUsize::new(0), + run_channels, + metrics, + }, + a2b_steam_open_s, + b2a_stream_opened_r, + s2b_create_channel_s, + s2b_shutdown_bparticipant_s, + ) + } + + pub async fn run(mut self, b2s_prio_statistic_s: mpsc::UnboundedSender<(Pid, u64, u64)>) { + //those managers that listen on api::Participant need an additional oneshot for + // shutdown scenario, those handled by scheduler will be closed by it. + let (shutdown_send_mgr_sender, shutdown_send_mgr_receiver) = oneshot::channel(); + let (shutdown_stream_close_mgr_sender, shutdown_stream_close_mgr_receiver) = + oneshot::channel(); + let (shutdown_open_mgr_sender, shutdown_open_mgr_receiver) = oneshot::channel(); + let (b2b_prios_flushed_s, b2b_prios_flushed_r) = oneshot::channel(); + let (w2b_frames_s, w2b_frames_r) = mpsc::unbounded::<(Cid, Frame)>(); + let (prios, a2p_msg_s, b2p_notify_empty_stream_s) = + PrioManager::new(self.metrics.clone(), self.remote_pid_string.clone()); + + let run_channels = self.run_channels.take().unwrap(); + futures::join!( + self.open_mgr( + run_channels.a2b_steam_open_r, + run_channels.a2b_close_stream_s.clone(), + a2p_msg_s.clone(), + shutdown_open_mgr_receiver, + ), + self.handle_frames_mgr( + w2b_frames_r, + run_channels.b2a_stream_opened_s, + run_channels.a2b_close_stream_s, + a2p_msg_s.clone(), + ), + self.create_channel_mgr(run_channels.s2b_create_channel_r, w2b_frames_s), + self.send_mgr( + prios, + shutdown_send_mgr_receiver, + b2b_prios_flushed_s, + b2s_prio_statistic_s + ), + self.stream_close_mgr( + run_channels.a2b_close_stream_r, + shutdown_stream_close_mgr_receiver, + b2p_notify_empty_stream_s, + ), + self.participant_shutdown_mgr( + run_channels.s2b_shutdown_bparticipant_r, + b2b_prios_flushed_r, + vec!( + shutdown_send_mgr_sender, + shutdown_open_mgr_sender, + shutdown_stream_close_mgr_sender + ) + ), + ); + } + + async fn send_mgr( + &self, + mut prios: PrioManager, + mut shutdown_send_mgr_receiver: oneshot::Receiver<()>, + b2b_prios_flushed_s: oneshot::Sender<()>, + mut b2s_prio_statistic_s: mpsc::UnboundedSender<(Pid, u64, u64)>, + ) { + //This time equals the MINIMUM Latency in average, so keep it down and //Todo: + // make it configureable or switch to await E.g. Prio 0 = await, prio 50 + // wait for more messages + const TICK_TIME: Duration = Duration::from_millis(10); + const FRAMES_PER_TICK: usize = 10005; + self.running_mgr.fetch_add(1, Ordering::Relaxed); + let mut closing_up = false; + trace!("start send_mgr"); + let mut send_cache = + PidCidFrameCache::new(self.metrics.frames_out_total.clone(), self.remote_pid); + //while !self.closed.load(Ordering::Relaxed) { + loop { + let mut frames = VecDeque::new(); + prios.fill_frames(FRAMES_PER_TICK, &mut frames).await; + let len = frames.len(); + if len > 0 { + trace!("tick {}", len); + } + for (_, frame) in frames { + self.send_frame(frame, &mut send_cache).await; + } + b2s_prio_statistic_s + .send((self.remote_pid, len as u64, /* */ 0)) + .await + .unwrap(); + async_std::task::sleep(TICK_TIME).await; + //shutdown after all msg are send! + if closing_up && (len == 0) { + break; + } + //this IF below the break IF to give it another chance to close all streams + // closed + if !closing_up && shutdown_send_mgr_receiver.try_recv().unwrap().is_some() { + closing_up = true; + } + } + trace!("stop send_mgr"); + b2b_prios_flushed_s.send(()).unwrap(); + self.running_mgr.fetch_sub(1, Ordering::Relaxed); + } + + //retruns false if sending isn't possible. In that case we have to render the + // Participant `closed` + #[must_use = "You need to check if the send was successful and report to client!"] + async fn send_frame( + &self, + frame: Frame, + frames_out_total_cache: &mut PidCidFrameCache, + ) -> bool { + // find out ideal channel here + //TODO: just take first + let mut lock = self.channels.write().await; + if let Some(ci) = lock.get_mut(0) { + //note: this is technically wrong we should only increase when it suceeded, but + // this requiered me to clone `frame` which is a to big performance impact for + // error handling + frames_out_total_cache + .with_label_values(ci.cid, &frame) + .inc(); + if let Err(e) = ci.b2w_frame_s.send(frame).await { + warn!( + ?e, + "the channel got closed unexpectedly, cleaning it up now." + ); + let ci = lock.remove(0); + if let Err(e) = ci.b2r_read_shutdown.send(()) { + debug!( + ?e, + "error shutdowning channel, which is prob fine as we detected it to no \ + longer work in the first place" + ); + }; + //TODO + warn!( + "FIXME: the frame is actually drop. which is fine for now as the participant \ + will be closed, but not if we do channel-takeover" + ); + false + } else { + true + } + } else { + error!("participant has no channel to communicate on"); + false + } + } + + async fn handle_frames_mgr( + &self, + mut w2b_frames_r: mpsc::UnboundedReceiver<(Cid, Frame)>, + mut b2a_stream_opened_s: mpsc::UnboundedSender, + a2b_close_stream_s: mpsc::UnboundedSender, + a2p_msg_s: std::sync::mpsc::Sender<(Prio, Sid, OutgoingMessage)>, + ) { + self.running_mgr.fetch_add(1, Ordering::Relaxed); + trace!("start handle_frames_mgr"); + let mut messages = HashMap::new(); + let mut dropped_instant = Instant::now(); + let mut dropped_cnt = 0u64; + let mut dropped_sid = Sid::new(0); + + while let Some((cid, frame)) = w2b_frames_r.next().await { + let cid_string = cid.to_string(); + //trace!("handling frame"); + self.metrics + .frames_in_total + .with_label_values(&[&self.remote_pid_string, &cid_string, frame.get_string()]) + .inc(); + match frame { + Frame::OpenStream { + sid, + prio, + promises, + } => { + let a2p_msg_s = a2p_msg_s.clone(); + let stream = self + .create_stream(sid, prio, promises, a2p_msg_s, &a2b_close_stream_s) + .await; + b2a_stream_opened_s.send(stream).await.unwrap(); + trace!("opened frame from remote"); + }, + Frame::CloseStream { sid } => { + // Closing is realised by setting a AtomicBool to true, however we also have a + // guarantee that send or recv fails if the other one is destroyed + // However Stream.send() is not async and their receiver isn't dropped if Steam + // is dropped, so i need a way to notify the Stream that it's send messages will + // be dropped... from remote, notify local + trace!( + ?sid, + "got remote request to close a stream, without flushing it, local \ + messages are dropped" + ); + // no wait for flush here, as the remote wouldn't care anyway. + if let Some(si) = self.streams.write().await.remove(&sid) { + self.metrics + .streams_closed_total + .with_label_values(&[&self.remote_pid_string]) + .inc(); + si.closed.store(true, Ordering::Relaxed); + trace!(?sid, "closed stream from remote"); + } else { + warn!( + ?sid, + "couldn't find stream to close, either this is a duplicate message, \ + or the local copy of the Stream got closed simultaniously" + ); + } + }, + Frame::DataHeader { mid, sid, length } => { + let imsg = IncomingMessage { + buffer: MessageBuffer { data: Vec::new() }, + length, + mid, + sid, + }; + messages.insert(mid, imsg); + }, + Frame::Data { + mid, + start: _, + mut data, + } => { + let finished = if let Some(imsg) = messages.get_mut(&mid) { + imsg.buffer.data.append(&mut data); + imsg.buffer.data.len() as u64 == imsg.length + } else { + false + }; + if finished { + //debug!(?mid, "finished receiving message"); + let imsg = messages.remove(&mid).unwrap(); + if let Some(si) = self.streams.write().await.get_mut(&imsg.sid) { + if let Err(e) = si.b2a_msg_recv_s.send(imsg).await { + warn!( + ?e, + ?mid, + "dropping message, as streams seem to be in act of beeing \ + dropped right now" + ); + } + } else { + //aggregate errors + let n = Instant::now(); + if dropped_sid != imsg.sid + || n.duration_since(dropped_instant) > Duration::from_secs(1) + { + warn!( + ?dropped_cnt, + "dropping multiple messages as stream no longer seems to \ + exist because it was dropped probably." + ); + dropped_cnt = 0; + dropped_instant = n; + dropped_sid = imsg.sid; + } else { + dropped_cnt += 1; + } + } + } + }, + _ => unreachable!("never reaches frame!"), + } + } + if dropped_cnt > 0 { + warn!( + ?dropped_cnt, + "dropping multiple messages as stream no longer seems to exist because it was \ + dropped probably." + ); + } + trace!("stop handle_frames_mgr"); + self.running_mgr.fetch_sub(1, Ordering::Relaxed); + } + + #[allow(clippy::type_complexity)] + async fn create_channel_mgr( + &self, + s2b_create_channel_r: mpsc::UnboundedReceiver<( + Cid, + Sid, + Protocols, + Vec<(Cid, Frame)>, + oneshot::Sender<()>, + )>, + w2b_frames_s: mpsc::UnboundedSender<(Cid, Frame)>, + ) { + self.running_mgr.fetch_add(1, Ordering::Relaxed); + trace!("start create_channel_mgr"); + s2b_create_channel_r + .for_each_concurrent( + None, + |(cid, _, protocol, leftover_cid_frame, b2s_create_channel_done_s)| { + // This channel is now configured, and we are running it in scope of the + // participant. + let w2b_frames_s = w2b_frames_s.clone(); + let channels = self.channels.clone(); + async move { + let (channel, b2w_frame_s, b2r_read_shutdown) = Channel::new(cid); + channels.write().await.push(ChannelInfo { + cid, + cid_string: cid.to_string(), + b2w_frame_s, + b2r_read_shutdown, + }); + b2s_create_channel_done_s.send(()).unwrap(); + self.metrics + .channels_connected_total + .with_label_values(&[&self.remote_pid_string]) + .inc(); + trace!(?cid, "running channel in participant"); + channel + .run(protocol, w2b_frames_s, leftover_cid_frame) + .await; + self.metrics + .channels_disconnected_total + .with_label_values(&[&self.remote_pid_string]) + .inc(); + trace!(?cid, "channel got closed"); + } + }, + ) + .await; + trace!("stop create_channel_mgr"); + self.running_mgr.fetch_sub(1, Ordering::Relaxed); + } + + async fn open_mgr( + &self, + mut a2b_steam_open_r: mpsc::UnboundedReceiver<(Prio, Promises, oneshot::Sender)>, + a2b_close_stream_s: mpsc::UnboundedSender, + a2p_msg_s: std::sync::mpsc::Sender<(Prio, Sid, OutgoingMessage)>, + shutdown_open_mgr_receiver: oneshot::Receiver<()>, + ) { + self.running_mgr.fetch_add(1, Ordering::Relaxed); + trace!("start open_mgr"); + let mut stream_ids = self.offset_sid; + let mut send_cache = + PidCidFrameCache::new(self.metrics.frames_out_total.clone(), self.remote_pid); + let mut shutdown_open_mgr_receiver = shutdown_open_mgr_receiver.fuse(); + //from api or shutdown signal + while let Some((prio, promises, p2a_return_stream)) = select! { + next = a2b_steam_open_r.next().fuse() => next, + _ = shutdown_open_mgr_receiver => None, + } { + debug!(?prio, ?promises, "got request to open a new steam"); + let a2p_msg_s = a2p_msg_s.clone(); + let sid = stream_ids; + let stream = self + .create_stream(sid, prio, promises, a2p_msg_s, &a2b_close_stream_s) + .await; + if self + .send_frame( + Frame::OpenStream { + sid, + prio, + promises, + }, + &mut send_cache, + ) + .await + { + //On error, we drop this, so it gets closed and client will handle this as an + // Err any way (: + p2a_return_stream.send(stream).unwrap(); + stream_ids += Sid::from(1); + } + } + trace!("stop open_mgr"); + self.running_mgr.fetch_sub(1, Ordering::Relaxed); + } + + /// when activated this function will drop the participant completly and + /// wait for everything to go right! Then return 1. Shutting down + /// Streams for API and End user! 2. Wait for all "prio queued" Messages + /// to be send. 3. Send Stream + async fn participant_shutdown_mgr( + &self, + s2b_shutdown_bparticipant_r: oneshot::Receiver>>, + b2b_prios_flushed_r: oneshot::Receiver<()>, + mut to_shutdown: Vec>, + ) { + self.running_mgr.fetch_add(1, Ordering::Relaxed); + trace!("start participant_shutdown_mgr"); + let sender = s2b_shutdown_bparticipant_r.await.unwrap(); + debug!("closing all managers"); + for sender in to_shutdown.drain(..) { + if let Err(e) = sender.send(()) { + warn!(?e, "manager seems to be closed already, weird, maybe a bug"); + }; + } + debug!("closing all streams"); + for (sid, si) in self.streams.write().await.drain() { + trace!(?sid, "shutting down Stream"); + si.closed.store(true, Ordering::Relaxed); + } + debug!("waiting for prios to be flushed"); + b2b_prios_flushed_r.await.unwrap(); + debug!("closing all channels"); + for ci in self.channels.write().await.drain(..) { + if let Err(e) = ci.b2r_read_shutdown.send(()) { + debug!(?e, ?ci.cid, "seems like this read protocol got already dropped by closing the Stream itself, just ignoring the fact"); + }; + } + //Wait for other bparticipants mgr to close via AtomicUsize + const SLEEP_TIME: Duration = Duration::from_millis(5); + async_std::task::sleep(SLEEP_TIME).await; + let mut i: u32 = 1; + while self.running_mgr.load(Ordering::Relaxed) > 1 { + i += 1; + if i.rem_euclid(10) == 1 { + trace!( + "waiting for bparticipant mgr to shut down, remaining {}", + self.running_mgr.load(Ordering::Relaxed) - 1 + ); + } + async_std::task::sleep(SLEEP_TIME * i).await; + } + trace!("all bparticipant mgr (except me) are shut down now"); + self.metrics.participants_disconnected_total.inc(); + sender.send(Ok(())).unwrap(); + trace!("stop participant_shutdown_mgr"); + self.running_mgr.fetch_sub(1, Ordering::Relaxed); + } + + async fn stream_close_mgr( + &self, + mut a2b_close_stream_r: mpsc::UnboundedReceiver, + shutdown_stream_close_mgr_receiver: oneshot::Receiver<()>, + b2p_notify_empty_stream_s: std::sync::mpsc::Sender<(Sid, oneshot::Sender<()>)>, + ) { + self.running_mgr.fetch_add(1, Ordering::Relaxed); + trace!("start stream_close_mgr"); + let mut send_cache = + PidCidFrameCache::new(self.metrics.frames_out_total.clone(), self.remote_pid); + let mut shutdown_stream_close_mgr_receiver = shutdown_stream_close_mgr_receiver.fuse(); + + //from api or shutdown signal + while let Some(sid) = select! { + next = a2b_close_stream_r.next().fuse() => next, + _ = shutdown_stream_close_mgr_receiver => None, + } { + //TODO: make this concurrent! + //TODO: Performance, closing is slow! + trace!(?sid, "got request from api to close steam"); + //This needs to first stop clients from sending any more. + //Then it will wait for all pending messages (in prio) to be send to the + // protocol After this happened the stream is closed + //Only after all messages are send to the prococol, we can send the CloseStream + // frame! If we would send it before, all followup messages couldn't + // be handled at the remote side. + + trace!(?sid, "stopping api to use this stream"); + match self.streams.read().await.get(&sid) { + Some(si) => { + si.closed.store(true, Ordering::Relaxed); + }, + None => warn!("couldn't find the stream, might be simulanious close from remote"), + } + + //TODO: what happens if RIGHT NOW the remote sends a StreamClose and this + // streams get closed and removed? RACE CONDITION + trace!(?sid, "wait for stream to be flushed"); + let (s2b_stream_finished_closed_s, s2b_stream_finished_closed_r) = oneshot::channel(); + b2p_notify_empty_stream_s + .send((sid, s2b_stream_finished_closed_s)) + .unwrap(); + s2b_stream_finished_closed_r.await.unwrap(); + + trace!(?sid, "stream was successfully flushed"); + self.metrics + .streams_closed_total + .with_label_values(&[&self.remote_pid_string]) + .inc(); + //only now remove the Stream, that means we can still recv on it. + self.streams.write().await.remove(&sid); + self.send_frame(Frame::CloseStream { sid }, &mut send_cache) + .await; + } + trace!("stop stream_close_mgr"); + self.running_mgr.fetch_sub(1, Ordering::Relaxed); + } + + async fn create_stream( + &self, + sid: Sid, + prio: Prio, + promises: Promises, + a2p_msg_s: std::sync::mpsc::Sender<(Prio, Sid, OutgoingMessage)>, + a2b_close_stream_s: &mpsc::UnboundedSender, + ) -> Stream { + let (b2a_msg_recv_s, b2a_msg_recv_r) = mpsc::unbounded::(); + let closed = Arc::new(AtomicBool::new(false)); + self.streams.write().await.insert(sid, StreamInfo { + prio, + promises, + b2a_msg_recv_s, + closed: closed.clone(), + }); + self.metrics + .streams_opened_total + .with_label_values(&[&self.remote_pid_string]) + .inc(); + Stream::new( + self.remote_pid, + sid, + prio, + promises, + a2p_msg_s, + b2a_msg_recv_r, + closed.clone(), + a2b_close_stream_s.clone(), + ) + } +} diff --git a/network/src/prios.rs b/network/src/prios.rs new file mode 100644 index 0000000000..dac46270ee --- /dev/null +++ b/network/src/prios.rs @@ -0,0 +1,603 @@ +//!Priorities are handled the following way. +//!Prios from 0-63 are allowed. +//!all 5 numbers the throughput is halved. +//!E.g. in the same time 100 prio0 messages are send, only 50 prio5, 25 prio10, +//! 12 prio15 or 6 prio20 messages are send. Note: TODO: prio0 will be send +//! immeadiatly when found! + +use crate::{ + message::OutgoingMessage, + metrics::NetworkMetrics, + types::{Frame, Prio, Sid}, +}; +use futures::channel::oneshot; +use std::{ + collections::{HashMap, HashSet, VecDeque}, + sync::{ + mpsc::{channel, Receiver, Sender}, + Arc, + }, +}; + +use tracing::*; + +const PRIO_MAX: usize = 64; + +struct PidSidInfo { + len: u64, + empty_notify: Option>, +} + +pub(crate) struct PrioManager { + points: [u32; PRIO_MAX], + messages: [VecDeque<(Sid, OutgoingMessage)>; PRIO_MAX], + messages_rx: Receiver<(Prio, Sid, OutgoingMessage)>, + sid_owned: HashMap, + //you can register to be notified if a pid_sid combination is flushed completly here + sid_flushed_rx: Receiver<(Sid, oneshot::Sender<()>)>, + queued: HashSet, + metrics: Arc, + pid: String, +} + +impl PrioManager { + const FRAME_DATA_SIZE: u64 = 1400; + const PRIOS: [u32; PRIO_MAX] = [ + 100, 115, 132, 152, 174, 200, 230, 264, 303, 348, 400, 459, 528, 606, 696, 800, 919, 1056, + 1213, 1393, 1600, 1838, 2111, 2425, 2786, 3200, 3676, 4222, 4850, 5572, 6400, 7352, 8445, + 9701, 11143, 12800, 14703, 16890, 19401, 22286, 25600, 29407, 33779, 38802, 44572, 51200, + 58813, 67559, 77605, 89144, 102400, 117627, 135118, 155209, 178289, 204800, 235253, 270235, + 310419, 356578, 409600, 470507, 540470, 620838, + ]; + + #[allow(clippy::type_complexity)] + pub fn new( + metrics: Arc, + pid: String, + ) -> ( + Self, + Sender<(Prio, Sid, OutgoingMessage)>, + Sender<(Sid, oneshot::Sender<()>)>, + ) { + // (a2p_msg_s, a2p_msg_r) + let (messages_tx, messages_rx) = channel(); + let (sid_flushed_tx, sid_flushed_rx) = channel(); + ( + Self { + points: [0; PRIO_MAX], + messages: [ + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + VecDeque::new(), + ], + messages_rx, + queued: HashSet::new(), //TODO: optimize with u64 and 64 bits + sid_flushed_rx, + sid_owned: HashMap::new(), + metrics, + pid, + }, + messages_tx, + sid_flushed_tx, + ) + } + + async fn tick(&mut self) { + // Check Range + let mut messages = 0; + let mut closed = 0; + for (prio, sid, msg) in self.messages_rx.try_iter() { + debug_assert!(prio as usize <= PRIO_MAX); + messages += 1; + self.metrics + .message_out_total + .with_label_values(&[&self.pid, &sid.to_string()]) + .inc(); + self.metrics + .message_out_throughput + .with_label_values(&[&self.pid, &sid.to_string()]) + .inc_by(msg.buffer.data.len() as i64); + //trace!(?prio, ?sid, "tick"); + self.queued.insert(prio); + self.messages[prio as usize].push_back((sid, msg)); + if let Some(cnt) = self.sid_owned.get_mut(&sid) { + cnt.len += 1; + } else { + self.sid_owned.insert(sid, PidSidInfo { + len: 1, + empty_notify: None, + }); + } + } + //this must be AFTER messages + for (sid, return_sender) in self.sid_flushed_rx.try_iter() { + closed += 1; + if let Some(cnt) = self.sid_owned.get_mut(&sid) { + // register sender + cnt.empty_notify = Some(return_sender); + } else { + // return immediately + return_sender.send(()).unwrap(); + } + } + if messages > 0 || closed > 0 { + trace!(?messages, ?closed, "tick"); + } + } + + //if None returned, we are empty! + fn calc_next_prio(&self) -> Option { + // compare all queued prios, max 64 operations + let mut lowest = std::u32::MAX; + let mut lowest_id = None; + for &n in &self.queued { + let n_points = self.points[n as usize]; + if n_points < lowest { + lowest = n_points; + lowest_id = Some(n) + } else if n_points == lowest && lowest_id.is_some() && n < lowest_id.unwrap() { + //on equial points lowest first! + lowest_id = Some(n) + } + } + lowest_id + } + + /// returns if msg is empty + fn tick_msg>( + msg: &mut OutgoingMessage, + msg_sid: Sid, + frames: &mut E, + ) -> bool { + let to_send = std::cmp::min( + msg.buffer.data.len() as u64 - msg.cursor, + Self::FRAME_DATA_SIZE, + ); + if to_send > 0 { + if msg.cursor == 0 { + frames.extend(std::iter::once((msg_sid, Frame::DataHeader { + mid: msg.mid, + sid: msg.sid, + length: msg.buffer.data.len() as u64, + }))); + } + frames.extend(std::iter::once((msg_sid, Frame::Data { + mid: msg.mid, + start: msg.cursor, + data: msg.buffer.data[msg.cursor as usize..(msg.cursor + to_send) as usize] + .to_vec(), + }))); + }; + msg.cursor += to_send; + msg.cursor >= msg.buffer.data.len() as u64 + } + + /// no_of_frames = frames.len() + /// Your goal is to try to find a realistic no_of_frames! + /// no_of_frames should be choosen so, that all Frames can be send out till + /// the next tick! + /// - if no_of_frames is too high you will fill either the Socket buffer, + /// or your internal buffer. In that case you will increase latency for + /// high prio messages! + /// - if no_of_frames is too low you wont saturate your Socket fully, thus + /// have a lower bandwidth as possible + pub async fn fill_frames>( + &mut self, + no_of_frames: usize, + frames: &mut E, + ) { + for v in self.messages.iter_mut() { + v.reserve_exact(no_of_frames) + } + self.tick().await; + for _ in 0..no_of_frames { + match self.calc_next_prio() { + Some(prio) => { + //let prio2 = self.calc_next_prio().unwrap(); + //trace!(?prio, "handle next prio"); + self.points[prio as usize] += Self::PRIOS[prio as usize]; + //pop message from front of VecDeque, handle it and push it back, so that all + // => messages with same prio get a fair chance :) + //TODO: evalaute not poping every time + match self.messages[prio as usize].pop_front() { + Some((sid, mut msg)) => { + if Self::tick_msg(&mut msg, sid, frames) { + //debug!(?m.mid, "finish message"); + //check if prio is empty + if self.messages[prio as usize].is_empty() { + self.queued.remove(&prio); + } + //decrease pid_sid counter by 1 again + let cnt = self.sid_owned.get_mut(&sid).expect( + "the pid_sid_owned counter works wrong, more pid,sid removed \ + than inserted", + ); + cnt.len -= 1; + if cnt.len == 0 { + let cnt = self.sid_owned.remove(&sid).unwrap(); + if let Some(empty_notify) = cnt.empty_notify { + empty_notify.send(()).unwrap(); + } + } + } else { + error!(?msg.mid, "repush message"); + self.messages[prio as usize].push_back((sid, msg)); + } + }, + None => unreachable!("msg not in VecDeque, but queued"), + } + }, + None => { + //QUEUE is empty, we are clearing the POINTS to not build up huge pipes of + // POINTS on a prio from the past + self.points = [0; PRIO_MAX]; + break; + }, + } + } + } +} + +impl std::fmt::Debug for PrioManager { + #[inline] + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut cnt = 0; + for m in self.messages.iter() { + cnt += m.len(); + } + write!(f, "PrioManager(len: {}, queued: {:?})", cnt, &self.queued,) + } +} + +#[cfg(test)] +mod tests { + use crate::{ + message::{MessageBuffer, OutgoingMessage}, + metrics::NetworkMetrics, + prios::*, + types::{Frame, Pid, Prio, Sid}, + }; + use futures::{channel::oneshot, executor::block_on}; + use std::{ + collections::VecDeque, + sync::{mpsc::Sender, Arc}, + }; + + const SIZE: u64 = PrioManager::FRAME_DATA_SIZE; + const USIZE: usize = PrioManager::FRAME_DATA_SIZE as usize; + + fn mock_new() -> ( + PrioManager, + Sender<(Prio, Sid, OutgoingMessage)>, + Sender<(Sid, oneshot::Sender<()>)>, + ) { + let pid = Pid::fake(1); + PrioManager::new( + Arc::new(NetworkMetrics::new(&pid).unwrap()), + pid.to_string(), + ) + } + + fn mock_out(prio: Prio, sid: u64) -> (Prio, Sid, OutgoingMessage) { + let sid = Sid::new(sid); + (prio, sid, OutgoingMessage { + buffer: Arc::new(MessageBuffer { + data: vec![48, 49, 50], + }), + cursor: 0, + mid: 1, + sid, + }) + } + + fn mock_out_large(prio: Prio, sid: u64) -> (Prio, Sid, OutgoingMessage) { + let sid = Sid::new(sid); + let mut data = vec![48; USIZE]; + data.append(&mut vec![49; USIZE]); + data.append(&mut vec![50; 20]); + (prio, sid, OutgoingMessage { + buffer: Arc::new(MessageBuffer { data }), + cursor: 0, + mid: 1, + sid, + }) + } + + fn assert_header(frames: &mut VecDeque<(Sid, Frame)>, f_sid: u64, f_length: u64) { + let frame = frames + .pop_front() + .expect("frames vecdeque doesn't contain enough frames!") + .1; + if let Frame::DataHeader { mid, sid, length } = frame { + assert_eq!(mid, 1); + assert_eq!(sid, Sid::new(f_sid)); + assert_eq!(length, f_length); + } else { + panic!("wrong frame type!, expected DataHeader"); + } + } + + fn assert_data(frames: &mut VecDeque<(Sid, Frame)>, f_start: u64, f_data: Vec) { + let frame = frames + .pop_front() + .expect("frames vecdeque doesn't contain enough frames!") + .1; + if let Frame::Data { mid, start, data } = frame { + assert_eq!(mid, 1); + assert_eq!(start, f_start); + assert_eq!(data, f_data); + } else { + panic!("wrong frame type!, expected Data"); + } + } + + #[test] + fn single_p16() { + let (mut mgr, msg_tx, _flush_tx) = mock_new(); + msg_tx.send(mock_out(16, 1337)).unwrap(); + let mut frames = VecDeque::new(); + block_on(mgr.fill_frames(100, &mut frames)); + + assert_header(&mut frames, 1337, 3); + assert_data(&mut frames, 0, vec![48, 49, 50]); + assert!(frames.is_empty()); + } + + #[test] + fn single_p16_p20() { + let (mut mgr, msg_tx, _flush_tx) = mock_new(); + msg_tx.send(mock_out(16, 1337)).unwrap(); + msg_tx.send(mock_out(20, 42)).unwrap(); + let mut frames = VecDeque::new(); + + block_on(mgr.fill_frames(100, &mut frames)); + assert_header(&mut frames, 1337, 3); + assert_data(&mut frames, 0, vec![48, 49, 50]); + assert_header(&mut frames, 42, 3); + assert_data(&mut frames, 0, vec![48, 49, 50]); + assert!(frames.is_empty()); + } + + #[test] + fn single_p20_p16() { + let (mut mgr, msg_tx, _flush_tx) = mock_new(); + msg_tx.send(mock_out(20, 42)).unwrap(); + msg_tx.send(mock_out(16, 1337)).unwrap(); + let mut frames = VecDeque::new(); + block_on(mgr.fill_frames(100, &mut frames)); + + assert_header(&mut frames, 1337, 3); + assert_data(&mut frames, 0, vec![48, 49, 50]); + assert_header(&mut frames, 42, 3); + assert_data(&mut frames, 0, vec![48, 49, 50]); + assert!(frames.is_empty()); + } + + #[test] + fn multiple_p16_p20() { + let (mut mgr, msg_tx, _flush_tx) = mock_new(); + msg_tx.send(mock_out(20, 2)).unwrap(); + msg_tx.send(mock_out(16, 1)).unwrap(); + msg_tx.send(mock_out(16, 3)).unwrap(); + msg_tx.send(mock_out(16, 5)).unwrap(); + msg_tx.send(mock_out(20, 4)).unwrap(); + msg_tx.send(mock_out(20, 7)).unwrap(); + msg_tx.send(mock_out(16, 6)).unwrap(); + msg_tx.send(mock_out(20, 10)).unwrap(); + msg_tx.send(mock_out(16, 8)).unwrap(); + msg_tx.send(mock_out(20, 12)).unwrap(); + msg_tx.send(mock_out(16, 9)).unwrap(); + msg_tx.send(mock_out(16, 11)).unwrap(); + msg_tx.send(mock_out(20, 13)).unwrap(); + let mut frames = VecDeque::new(); + block_on(mgr.fill_frames(100, &mut frames)); + + for i in 1..14 { + assert_header(&mut frames, i, 3); + assert_data(&mut frames, 0, vec![48, 49, 50]); + } + assert!(frames.is_empty()); + } + + #[test] + fn multiple_fill_frames_p16_p20() { + let (mut mgr, msg_tx, _flush_tx) = mock_new(); + msg_tx.send(mock_out(20, 2)).unwrap(); + msg_tx.send(mock_out(16, 1)).unwrap(); + msg_tx.send(mock_out(16, 3)).unwrap(); + msg_tx.send(mock_out(16, 5)).unwrap(); + msg_tx.send(mock_out(20, 4)).unwrap(); + msg_tx.send(mock_out(20, 7)).unwrap(); + msg_tx.send(mock_out(16, 6)).unwrap(); + msg_tx.send(mock_out(20, 10)).unwrap(); + msg_tx.send(mock_out(16, 8)).unwrap(); + msg_tx.send(mock_out(20, 12)).unwrap(); + msg_tx.send(mock_out(16, 9)).unwrap(); + msg_tx.send(mock_out(16, 11)).unwrap(); + msg_tx.send(mock_out(20, 13)).unwrap(); + + let mut frames = VecDeque::new(); + block_on(mgr.fill_frames(3, &mut frames)); + for i in 1..4 { + assert_header(&mut frames, i, 3); + assert_data(&mut frames, 0, vec![48, 49, 50]); + } + assert!(frames.is_empty()); + block_on(mgr.fill_frames(11, &mut frames)); + for i in 4..14 { + assert_header(&mut frames, i, 3); + assert_data(&mut frames, 0, vec![48, 49, 50]); + } + assert!(frames.is_empty()); + } + + #[test] + fn single_large_p16() { + let (mut mgr, msg_tx, _flush_tx) = mock_new(); + msg_tx.send(mock_out_large(16, 1)).unwrap(); + let mut frames = VecDeque::new(); + block_on(mgr.fill_frames(100, &mut frames)); + + assert_header(&mut frames, 1, SIZE * 2 + 20); + assert_data(&mut frames, 0, vec![48; USIZE]); + assert_data(&mut frames, SIZE, vec![49; USIZE]); + assert_data(&mut frames, SIZE * 2, vec![50; 20]); + assert!(frames.is_empty()); + } + + #[test] + fn multiple_large_p16() { + let (mut mgr, msg_tx, _flush_tx) = mock_new(); + msg_tx.send(mock_out_large(16, 1)).unwrap(); + msg_tx.send(mock_out_large(16, 2)).unwrap(); + let mut frames = VecDeque::new(); + block_on(mgr.fill_frames(100, &mut frames)); + + assert_header(&mut frames, 1, SIZE * 2 + 20); + assert_data(&mut frames, 0, vec![48; USIZE]); + assert_header(&mut frames, 2, SIZE * 2 + 20); + assert_data(&mut frames, 0, vec![48; USIZE]); + assert_data(&mut frames, SIZE, vec![49; USIZE]); + assert_data(&mut frames, SIZE, vec![49; USIZE]); + assert_data(&mut frames, SIZE * 2, vec![50; 20]); + assert_data(&mut frames, SIZE * 2, vec![50; 20]); + assert!(frames.is_empty()); + } + + #[test] + fn multiple_large_p16_sudden_p0() { + let (mut mgr, msg_tx, _flush_tx) = mock_new(); + msg_tx.send(mock_out_large(16, 1)).unwrap(); + msg_tx.send(mock_out_large(16, 2)).unwrap(); + let mut frames = VecDeque::new(); + block_on(mgr.fill_frames(3, &mut frames)); + + assert_header(&mut frames, 1, SIZE * 2 + 20); + assert_data(&mut frames, 0, vec![48; USIZE]); + assert_header(&mut frames, 2, SIZE * 2 + 20); + assert_data(&mut frames, 0, vec![48; USIZE]); + assert_data(&mut frames, SIZE, vec![49; USIZE]); + + msg_tx.send(mock_out(0, 3)).unwrap(); + block_on(mgr.fill_frames(100, &mut frames)); + + assert_header(&mut frames, 3, 3); + assert_data(&mut frames, 0, vec![48, 49, 50]); + + assert_data(&mut frames, SIZE, vec![49; USIZE]); + assert_data(&mut frames, SIZE * 2, vec![50; 20]); + assert_data(&mut frames, SIZE * 2, vec![50; 20]); + assert!(frames.is_empty()); + } + + #[test] + fn single_p20_thousand_p16_at_once() { + let (mut mgr, msg_tx, _flush_tx) = mock_new(); + for _ in 0..998 { + msg_tx.send(mock_out(16, 2)).unwrap(); + } + msg_tx.send(mock_out(20, 1)).unwrap(); + msg_tx.send(mock_out(16, 2)).unwrap(); + msg_tx.send(mock_out(16, 2)).unwrap(); + let mut frames = VecDeque::new(); + block_on(mgr.fill_frames(2000, &mut frames)); + + assert_header(&mut frames, 2, 3); + assert_data(&mut frames, 0, vec![48, 49, 50]); + assert_header(&mut frames, 1, 3); + assert_data(&mut frames, 0, vec![48, 49, 50]); + assert_header(&mut frames, 2, 3); + assert_data(&mut frames, 0, vec![48, 49, 50]); + assert_header(&mut frames, 2, 3); + //unimportant + } + + #[test] + fn single_p20_thousand_p16_later() { + let (mut mgr, msg_tx, _flush_tx) = mock_new(); + for _ in 0..998 { + msg_tx.send(mock_out(16, 2)).unwrap(); + } + let mut frames = VecDeque::new(); + block_on(mgr.fill_frames(2000, &mut frames)); + //^unimportant frames, gonna be dropped + msg_tx.send(mock_out(20, 1)).unwrap(); + msg_tx.send(mock_out(16, 2)).unwrap(); + msg_tx.send(mock_out(16, 2)).unwrap(); + let mut frames = VecDeque::new(); + block_on(mgr.fill_frames(2000, &mut frames)); + + //important in that test is, that after the first frames got cleared i reset + // the Points even though 998 prio 16 messages have been send at this + // point and 0 prio20 messages the next mesasge is a prio16 message + // again, and only then prio20! we dont want to build dept over a idling + // connection + assert_header(&mut frames, 2, 3); + assert_data(&mut frames, 0, vec![48, 49, 50]); + assert_header(&mut frames, 1, 3); + assert_data(&mut frames, 0, vec![48, 49, 50]); + assert_header(&mut frames, 2, 3); + //unimportant + } +} diff --git a/network/src/protocols.rs b/network/src/protocols.rs new file mode 100644 index 0000000000..9cd0db19cb --- /dev/null +++ b/network/src/protocols.rs @@ -0,0 +1,723 @@ +use crate::{ + metrics::{CidFrameCache, NetworkMetrics}, + types::{Cid, Frame, Mid, Pid, Sid}, +}; +use async_std::{ + net::{TcpStream, UdpSocket}, + prelude::*, +}; +use futures::{ + channel::{mpsc, oneshot}, + future::FutureExt, + lock::Mutex, + select, + sink::SinkExt, + stream::StreamExt, +}; +use std::{net::SocketAddr, sync::Arc}; +use tracing::*; + +// Reserving bytes 0, 10, 13 as i have enough space and want to make it easy to +// detect a invalid client, e.g. sending an empty line would make 10 first char +// const FRAME_RESERVED_1: u8 = 0; +const FRAME_HANDSHAKE: u8 = 1; +const FRAME_INIT: u8 = 2; +const FRAME_SHUTDOWN: u8 = 3; +const FRAME_OPEN_STREAM: u8 = 4; +const FRAME_CLOSE_STREAM: u8 = 5; +const FRAME_DATA_HEADER: u8 = 6; +const FRAME_DATA: u8 = 7; +const FRAME_RAW: u8 = 8; +//const FRAME_RESERVED_2: u8 = 10; +//const FRAME_RESERVED_3: u8 = 13; + +#[derive(Debug)] +pub(crate) enum Protocols { + Tcp(TcpProtocol), + Udp(UdpProtocol), + //Mpsc(MpscChannel), +} + +#[derive(Debug)] +pub(crate) struct TcpProtocol { + stream: TcpStream, + metrics: Arc, +} + +#[derive(Debug)] +pub(crate) struct UdpProtocol { + socket: Arc, + remote_addr: SocketAddr, + metrics: Arc, + data_in: Mutex>>, +} + +//TODO: PERFORMACE: Use BufWriter and BufReader from std::io! +impl TcpProtocol { + pub(crate) fn new(stream: TcpStream, metrics: Arc) -> Self { + Self { stream, metrics } + } + + /// read_except and if it fails, close the protocol + async fn read_except_or_close( + cid: Cid, + mut stream: &TcpStream, + mut bytes: &mut [u8], + w2c_cid_frame_s: &mut mpsc::UnboundedSender<(Cid, Frame)>, + ) { + if let Err(e) = stream.read_exact(&mut bytes).await { + warn!( + ?e, + "closing tcp protocol due to read error, sending close frame to gracefully \ + shutdown" + ); + w2c_cid_frame_s.send((cid, Frame::Shutdown)).await.unwrap(); + } + } + + pub async fn read_from_wire( + &self, + cid: Cid, + w2c_cid_frame_s: &mut mpsc::UnboundedSender<(Cid, Frame)>, + end_receiver: oneshot::Receiver<()>, + ) { + trace!("starting up tcp read()"); + let mut metrics_cache = CidFrameCache::new(self.metrics.frames_wire_in_total.clone(), cid); + let throughput_cache = self + .metrics + .wire_in_throughput + .with_label_values(&[&cid.to_string()]); + let mut stream = self.stream.clone(); + let mut end_receiver = end_receiver.fuse(); + + loop { + let mut bytes = [0u8; 1]; + let r = select! { + r = stream.read_exact(&mut bytes).fuse() => r, + _ = end_receiver => break, + }; + if r.is_err() { + info!("tcp stream closed, shutting down read"); + break; + } + let frame_no = bytes[0]; + let frame = match frame_no { + FRAME_HANDSHAKE => { + let mut bytes = [0u8; 19]; + Self::read_except_or_close(cid, &stream, &mut bytes, w2c_cid_frame_s).await; + let magic_number = [ + bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], + ]; + Frame::Handshake { + magic_number, + version: [ + u32::from_le_bytes([bytes[7], bytes[8], bytes[9], bytes[10]]), + u32::from_le_bytes([bytes[11], bytes[12], bytes[13], bytes[14]]), + u32::from_le_bytes([bytes[15], bytes[16], bytes[17], bytes[18]]), + ], + } + }, + FRAME_INIT => { + let mut bytes = [0u8; 16]; + Self::read_except_or_close(cid, &stream, &mut bytes, w2c_cid_frame_s).await; + let pid = Pid::from_le_bytes(bytes); + stream.read_exact(&mut bytes).await.unwrap(); + let secret = u128::from_le_bytes(bytes); + Frame::Init { pid, secret } + }, + FRAME_SHUTDOWN => Frame::Shutdown, + FRAME_OPEN_STREAM => { + let mut bytes = [0u8; 10]; + Self::read_except_or_close(cid, &stream, &mut bytes, w2c_cid_frame_s).await; + let sid = Sid::from_le_bytes([ + bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], + bytes[7], + ]); + let prio = bytes[8]; + let promises = bytes[9]; + Frame::OpenStream { + sid, + prio, + promises, + } + }, + FRAME_CLOSE_STREAM => { + let mut bytes = [0u8; 8]; + Self::read_except_or_close(cid, &stream, &mut bytes, w2c_cid_frame_s).await; + let sid = Sid::from_le_bytes([ + bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], + bytes[7], + ]); + Frame::CloseStream { sid } + }, + FRAME_DATA_HEADER => { + let mut bytes = [0u8; 24]; + Self::read_except_or_close(cid, &stream, &mut bytes, w2c_cid_frame_s).await; + let mid = Mid::from_le_bytes([ + bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], + bytes[7], + ]); + let sid = Sid::from_le_bytes([ + bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], + bytes[15], + ]); + let length = u64::from_le_bytes([ + bytes[16], bytes[17], bytes[18], bytes[19], bytes[20], bytes[21], + bytes[22], bytes[23], + ]); + Frame::DataHeader { mid, sid, length } + }, + FRAME_DATA => { + let mut bytes = [0u8; 18]; + Self::read_except_or_close(cid, &stream, &mut bytes, w2c_cid_frame_s).await; + let mid = Mid::from_le_bytes([ + bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], + bytes[7], + ]); + let start = u64::from_le_bytes([ + bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], + bytes[15], + ]); + let length = u16::from_le_bytes([bytes[16], bytes[17]]); + let mut data = vec![0; length as usize]; + throughput_cache.inc_by(length as i64); + Self::read_except_or_close(cid, &stream, &mut data, w2c_cid_frame_s).await; + Frame::Data { mid, start, data } + }, + FRAME_RAW => { + let mut bytes = [0u8; 2]; + Self::read_except_or_close(cid, &stream, &mut bytes, w2c_cid_frame_s).await; + let length = u16::from_le_bytes([bytes[0], bytes[1]]); + let mut data = vec![0; length as usize]; + Self::read_except_or_close(cid, &stream, &mut data, w2c_cid_frame_s).await; + Frame::Raw(data) + }, + _ => { + // report a RAW frame, but cannot rely on the next 2 bytes to be a size. + // guessing 256 bytes, which might help to sort down issues + let mut data = vec![0; 256]; + Self::read_except_or_close(cid, &stream, &mut data, w2c_cid_frame_s).await; + Frame::Raw(data) + }, + }; + metrics_cache.with_label_values(&frame).inc(); + w2c_cid_frame_s.send((cid, frame)).await.unwrap(); + } + trace!("shutting down tcp read()"); + } + + /// read_except and if it fails, close the protocol + async fn write_or_close( + stream: &mut TcpStream, + bytes: &[u8], + to_wire_receiver: &mut mpsc::UnboundedReceiver, + ) -> bool { + match stream.write_all(&bytes).await { + Err(e) => { + warn!( + ?e, + "got an error writing to tcp, going to close this channel" + ); + to_wire_receiver.close(); + true + }, + _ => false, + } + } + + //dezerialize here as this is executed in a seperate thread PER channel. + // Limites Throughput per single Receiver but stays in same thread (maybe as its + // in a threadpool) for TCP, UDP and MPSC + pub async fn write_to_wire(&self, cid: Cid, mut c2w_frame_r: mpsc::UnboundedReceiver) { + trace!("starting up tcp write()"); + let mut stream = self.stream.clone(); + let mut metrics_cache = CidFrameCache::new(self.metrics.frames_wire_out_total.clone(), cid); + let throughput_cache = self + .metrics + .wire_out_throughput + .with_label_values(&[&cid.to_string()]); + while let Some(frame) = c2w_frame_r.next().await { + metrics_cache.with_label_values(&frame).inc(); + if match frame { + Frame::Handshake { + magic_number, + version, + } => { + Self::write_or_close( + &mut stream, + &FRAME_HANDSHAKE.to_be_bytes(), + &mut c2w_frame_r, + ) + .await + || Self::write_or_close(&mut stream, &magic_number, &mut c2w_frame_r).await + || Self::write_or_close( + &mut stream, + &version[0].to_le_bytes(), + &mut c2w_frame_r, + ) + .await + || Self::write_or_close( + &mut stream, + &version[1].to_le_bytes(), + &mut c2w_frame_r, + ) + .await + || Self::write_or_close( + &mut stream, + &version[2].to_le_bytes(), + &mut c2w_frame_r, + ) + .await + }, + Frame::Init { pid, secret } => { + Self::write_or_close(&mut stream, &FRAME_INIT.to_be_bytes(), &mut c2w_frame_r) + .await + || Self::write_or_close(&mut stream, &pid.to_le_bytes(), &mut c2w_frame_r) + .await + || Self::write_or_close( + &mut stream, + &secret.to_le_bytes(), + &mut c2w_frame_r, + ) + .await + }, + Frame::Shutdown => { + Self::write_or_close( + &mut stream, + &FRAME_SHUTDOWN.to_be_bytes(), + &mut c2w_frame_r, + ) + .await + }, + Frame::OpenStream { + sid, + prio, + promises, + } => { + Self::write_or_close( + &mut stream, + &FRAME_OPEN_STREAM.to_be_bytes(), + &mut c2w_frame_r, + ) + .await + || Self::write_or_close(&mut stream, &sid.to_le_bytes(), &mut c2w_frame_r) + .await + || Self::write_or_close(&mut stream, &prio.to_le_bytes(), &mut c2w_frame_r) + .await + || Self::write_or_close( + &mut stream, + &promises.to_le_bytes(), + &mut c2w_frame_r, + ) + .await + }, + Frame::CloseStream { sid } => { + Self::write_or_close( + &mut stream, + &FRAME_CLOSE_STREAM.to_be_bytes(), + &mut c2w_frame_r, + ) + .await + || Self::write_or_close(&mut stream, &sid.to_le_bytes(), &mut c2w_frame_r) + .await + }, + Frame::DataHeader { mid, sid, length } => { + Self::write_or_close( + &mut stream, + &FRAME_DATA_HEADER.to_be_bytes(), + &mut c2w_frame_r, + ) + .await + || Self::write_or_close(&mut stream, &mid.to_le_bytes(), &mut c2w_frame_r) + .await + || Self::write_or_close(&mut stream, &sid.to_le_bytes(), &mut c2w_frame_r) + .await + || Self::write_or_close( + &mut stream, + &length.to_le_bytes(), + &mut c2w_frame_r, + ) + .await + }, + Frame::Data { mid, start, data } => { + throughput_cache.inc_by(data.len() as i64); + Self::write_or_close(&mut stream, &FRAME_DATA.to_be_bytes(), &mut c2w_frame_r) + .await + || Self::write_or_close(&mut stream, &mid.to_le_bytes(), &mut c2w_frame_r) + .await + || Self::write_or_close(&mut stream, &start.to_le_bytes(), &mut c2w_frame_r) + .await + || Self::write_or_close( + &mut stream, + &(data.len() as u16).to_le_bytes(), + &mut c2w_frame_r, + ) + .await + || Self::write_or_close(&mut stream, &data, &mut c2w_frame_r).await + }, + Frame::Raw(data) => { + Self::write_or_close(&mut stream, &FRAME_RAW.to_be_bytes(), &mut c2w_frame_r) + .await + || Self::write_or_close( + &mut stream, + &(data.len() as u16).to_le_bytes(), + &mut c2w_frame_r, + ) + .await + || Self::write_or_close(&mut stream, &data, &mut c2w_frame_r).await + }, + } { + //failure + return; + } + } + trace!("shutting down tcp write()"); + } +} + +impl UdpProtocol { + pub(crate) fn new( + socket: Arc, + remote_addr: SocketAddr, + metrics: Arc, + data_in: mpsc::UnboundedReceiver>, + ) -> Self { + Self { + socket, + remote_addr, + metrics, + data_in: Mutex::new(data_in), + } + } + + pub async fn read_from_wire( + &self, + cid: Cid, + w2c_cid_frame_s: &mut mpsc::UnboundedSender<(Cid, Frame)>, + end_receiver: oneshot::Receiver<()>, + ) { + trace!("starting up udp read()"); + let mut metrics_cache = CidFrameCache::new(self.metrics.frames_wire_in_total.clone(), cid); + let throughput_cache = self + .metrics + .wire_in_throughput + .with_label_values(&[&cid.to_string()]); + let mut data_in = self.data_in.lock().await; + let mut end_receiver = end_receiver.fuse(); + while let Some(bytes) = select! { + r = data_in.next().fuse() => r, + _ = end_receiver => None, + } { + trace!("got raw UDP message with len: {}", bytes.len()); + let frame_no = bytes[0]; + let frame = match frame_no { + FRAME_HANDSHAKE => { + let bytes = &bytes[1..20]; + let magic_number = [ + bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], + ]; + Frame::Handshake { + magic_number, + version: [ + u32::from_le_bytes([bytes[7], bytes[8], bytes[9], bytes[10]]), + u32::from_le_bytes([bytes[11], bytes[12], bytes[13], bytes[14]]), + u32::from_le_bytes([bytes[15], bytes[16], bytes[17], bytes[18]]), + ], + } + }, + FRAME_INIT => { + let pid = Pid::from_le_bytes([ + bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], + bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], + bytes[15], bytes[16], + ]); + let secret = u128::from_le_bytes([ + bytes[17], bytes[18], bytes[19], bytes[20], bytes[21], bytes[22], + bytes[23], bytes[24], bytes[25], bytes[26], bytes[27], bytes[28], + bytes[29], bytes[30], bytes[31], bytes[32], + ]); + Frame::Init { pid, secret } + }, + FRAME_SHUTDOWN => Frame::Shutdown, + FRAME_OPEN_STREAM => { + let bytes = &bytes[1..11]; + let sid = Sid::from_le_bytes([ + bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], + bytes[7], + ]); + let prio = bytes[8]; + let promises = bytes[9]; + Frame::OpenStream { + sid, + prio, + promises, + } + }, + FRAME_CLOSE_STREAM => { + let bytes = &bytes[1..9]; + let sid = Sid::from_le_bytes([ + bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], + bytes[7], + ]); + Frame::CloseStream { sid } + }, + FRAME_DATA_HEADER => { + let bytes = &bytes[1..25]; + let mid = Mid::from_le_bytes([ + bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], + bytes[7], + ]); + let sid = Sid::from_le_bytes([ + bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], + bytes[15], + ]); + let length = u64::from_le_bytes([ + bytes[16], bytes[17], bytes[18], bytes[19], bytes[20], bytes[21], + bytes[22], bytes[23], + ]); + Frame::DataHeader { mid, sid, length } + }, + FRAME_DATA => { + let mid = Mid::from_le_bytes([ + bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], + bytes[8], + ]); + let start = u64::from_le_bytes([ + bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15], + bytes[16], + ]); + let length = u16::from_le_bytes([bytes[17], bytes[18]]); + let mut data = vec![0; length as usize]; + throughput_cache.inc_by(length as i64); + data.copy_from_slice(&bytes[19..]); + Frame::Data { mid, start, data } + }, + FRAME_RAW => { + let length = u16::from_le_bytes([bytes[1], bytes[2]]); + let mut data = vec![0; length as usize]; + data.copy_from_slice(&bytes[3..]); + Frame::Raw(data) + }, + _ => Frame::Raw(bytes), + }; + metrics_cache.with_label_values(&frame).inc(); + w2c_cid_frame_s.send((cid, frame)).await.unwrap(); + } + trace!("shutting down udp read()"); + } + + pub async fn write_to_wire(&self, cid: Cid, mut c2w_frame_r: mpsc::UnboundedReceiver) { + trace!("starting up udp write()"); + let mut buffer = [0u8; 2000]; + let mut metrics_cache = CidFrameCache::new(self.metrics.frames_wire_out_total.clone(), cid); + let throughput_cache = self + .metrics + .wire_out_throughput + .with_label_values(&[&cid.to_string()]); + while let Some(frame) = c2w_frame_r.next().await { + metrics_cache.with_label_values(&frame).inc(); + let len = match frame { + Frame::Handshake { + magic_number, + version, + } => { + let x = FRAME_HANDSHAKE.to_be_bytes(); + buffer[0] = x[0]; + buffer[1] = magic_number[0]; + buffer[2] = magic_number[1]; + buffer[3] = magic_number[2]; + buffer[4] = magic_number[3]; + buffer[5] = magic_number[4]; + buffer[6] = magic_number[5]; + buffer[7] = magic_number[6]; + let x = version[0].to_le_bytes(); + buffer[8] = x[0]; + buffer[9] = x[1]; + buffer[10] = x[2]; + buffer[11] = x[3]; + let x = version[1].to_le_bytes(); + buffer[12] = x[0]; + buffer[13] = x[1]; + buffer[14] = x[2]; + buffer[15] = x[3]; + let x = version[2].to_le_bytes(); + buffer[16] = x[0]; + buffer[17] = x[1]; + buffer[18] = x[2]; + buffer[19] = x[3]; + 20 + }, + Frame::Init { pid, secret } => { + let x = FRAME_INIT.to_be_bytes(); + buffer[0] = x[0]; + let x = pid.to_le_bytes(); + buffer[1] = x[0]; + buffer[2] = x[1]; + buffer[3] = x[2]; + buffer[4] = x[3]; + buffer[5] = x[4]; + buffer[6] = x[5]; + buffer[7] = x[6]; + buffer[8] = x[7]; + buffer[9] = x[8]; + buffer[10] = x[9]; + buffer[11] = x[10]; + buffer[12] = x[11]; + buffer[13] = x[12]; + buffer[14] = x[13]; + buffer[15] = x[14]; + buffer[16] = x[15]; + let x = secret.to_le_bytes(); + buffer[17] = x[0]; + buffer[18] = x[1]; + buffer[19] = x[2]; + buffer[20] = x[3]; + buffer[21] = x[4]; + buffer[22] = x[5]; + buffer[23] = x[6]; + buffer[24] = x[7]; + buffer[25] = x[8]; + buffer[26] = x[9]; + buffer[27] = x[10]; + buffer[28] = x[11]; + buffer[29] = x[12]; + buffer[30] = x[13]; + buffer[31] = x[14]; + buffer[32] = x[15]; + 33 + }, + Frame::Shutdown => { + let x = FRAME_SHUTDOWN.to_be_bytes(); + buffer[0] = x[0]; + 1 + }, + Frame::OpenStream { + sid, + prio, + promises, + } => { + let x = FRAME_OPEN_STREAM.to_be_bytes(); + buffer[0] = x[0]; + let x = sid.to_le_bytes(); + buffer[1] = x[0]; + buffer[2] = x[1]; + buffer[3] = x[2]; + buffer[4] = x[3]; + buffer[5] = x[4]; + buffer[6] = x[5]; + buffer[7] = x[6]; + buffer[8] = x[7]; + let x = prio.to_le_bytes(); + buffer[9] = x[0]; + let x = promises.to_le_bytes(); + buffer[10] = x[0]; + 11 + }, + Frame::CloseStream { sid } => { + let x = FRAME_CLOSE_STREAM.to_be_bytes(); + buffer[0] = x[0]; + let x = sid.to_le_bytes(); + buffer[1] = x[0]; + buffer[2] = x[1]; + buffer[3] = x[2]; + buffer[4] = x[3]; + buffer[5] = x[4]; + buffer[6] = x[5]; + buffer[7] = x[6]; + buffer[8] = x[7]; + 9 + }, + Frame::DataHeader { mid, sid, length } => { + let x = FRAME_DATA_HEADER.to_be_bytes(); + buffer[0] = x[0]; + let x = mid.to_le_bytes(); + buffer[1] = x[0]; + buffer[2] = x[1]; + buffer[3] = x[2]; + buffer[4] = x[3]; + buffer[5] = x[4]; + buffer[6] = x[5]; + buffer[7] = x[6]; + buffer[8] = x[7]; + let x = sid.to_le_bytes(); + buffer[9] = x[0]; + buffer[10] = x[1]; + buffer[11] = x[2]; + buffer[12] = x[3]; + buffer[13] = x[4]; + buffer[14] = x[5]; + buffer[15] = x[6]; + buffer[16] = x[7]; + let x = length.to_le_bytes(); + buffer[17] = x[0]; + buffer[18] = x[1]; + buffer[19] = x[2]; + buffer[20] = x[3]; + buffer[21] = x[4]; + buffer[22] = x[5]; + buffer[23] = x[6]; + buffer[24] = x[7]; + 25 + }, + Frame::Data { mid, start, data } => { + let x = FRAME_DATA.to_be_bytes(); + buffer[0] = x[0]; + let x = mid.to_le_bytes(); + buffer[1] = x[0]; + buffer[2] = x[1]; + buffer[3] = x[2]; + buffer[4] = x[3]; + buffer[5] = x[4]; + buffer[6] = x[5]; + buffer[7] = x[6]; + buffer[8] = x[7]; + let x = start.to_le_bytes(); + buffer[9] = x[0]; + buffer[10] = x[1]; + buffer[11] = x[2]; + buffer[12] = x[3]; + buffer[13] = x[4]; + buffer[14] = x[5]; + buffer[15] = x[6]; + buffer[16] = x[7]; + let x = (data.len() as u16).to_le_bytes(); + buffer[17] = x[0]; + buffer[18] = x[1]; + buffer[19..(data.len() + 19)].clone_from_slice(&data[..]); + throughput_cache.inc_by(data.len() as i64); + 19 + data.len() + }, + Frame::Raw(data) => { + let x = FRAME_RAW.to_be_bytes(); + buffer[0] = x[0]; + let x = (data.len() as u16).to_le_bytes(); + buffer[1] = x[0]; + buffer[2] = x[1]; + buffer[3..(data.len() + 3)].clone_from_slice(&data[..]); + 3 + data.len() + }, + }; + let mut start = 0; + while start < len { + trace!(?start, ?len, "splitting up udp frame in multiple packages"); + match self + .socket + .send_to(&buffer[start..len], self.remote_addr) + .await + { + Ok(n) => { + start += n; + if n != len { + error!( + "THIS DOESNT WORK, as RECEIVER CURRENLTY ONLY HANDLES 1 FRAME per \ + UDP message. splitting up will fail!" + ); + } + }, + Err(e) => error!(?e, "need to handle that error!"), + } + } + } + trace!("shutting down udp write()"); + } +} diff --git a/network/src/scheduler.rs b/network/src/scheduler.rs new file mode 100644 index 0000000000..7179a8491d --- /dev/null +++ b/network/src/scheduler.rs @@ -0,0 +1,594 @@ +use crate::{ + api::{Address, Participant}, + channel::Handshake, + metrics::NetworkMetrics, + participant::BParticipant, + protocols::{Protocols, TcpProtocol, UdpProtocol}, + types::{Cid, Frame, Pid, Sid}, +}; +use async_std::{ + io, net, + sync::{Mutex, RwLock}, +}; +use futures::{ + channel::{mpsc, oneshot}, + executor::ThreadPool, + future::FutureExt, + select, + sink::SinkExt, + stream::StreamExt, +}; +use prometheus::Registry; +use rand::Rng; +use std::{ + collections::HashMap, + sync::{ + atomic::{AtomicBool, AtomicU64, Ordering}, + Arc, + }, +}; +use tracing::*; +use tracing_futures::Instrument; + +#[derive(Debug)] +#[allow(clippy::type_complexity)] +struct ParticipantInfo { + secret: u128, + s2b_create_channel_s: + mpsc::UnboundedSender<(Cid, Sid, Protocols, Vec<(Cid, Frame)>, oneshot::Sender<()>)>, + s2b_shutdown_bparticipant_s: + Option>>>, +} + +/// Naming of Channels `x2x` +/// - a: api +/// - s: scheduler +/// - b: bparticipant +/// - p: prios +/// - r: protocol +/// - w: wire +/// - c: channel/handshake +#[derive(Debug)] +struct ControlChannels { + a2s_listen_r: mpsc::UnboundedReceiver<(Address, oneshot::Sender>)>, + a2s_connect_r: mpsc::UnboundedReceiver<(Address, oneshot::Sender>)>, + a2s_scheduler_shutdown_r: oneshot::Receiver<()>, + a2s_disconnect_r: mpsc::UnboundedReceiver<(Pid, oneshot::Sender>)>, + b2s_prio_statistic_r: mpsc::UnboundedReceiver<(Pid, u64, u64)>, +} + +#[derive(Debug, Clone)] +struct ParticipantChannels { + s2a_connected_s: mpsc::UnboundedSender, + a2s_disconnect_s: mpsc::UnboundedSender<(Pid, oneshot::Sender>)>, + b2s_prio_statistic_s: mpsc::UnboundedSender<(Pid, u64, u64)>, +} + +#[derive(Debug)] +pub struct Scheduler { + local_pid: Pid, + local_secret: u128, + closed: AtomicBool, + pool: Arc, + run_channels: Option, + participant_channels: Arc>>, + participants: Arc>>, + channel_ids: Arc, + channel_listener: RwLock>>, + metrics: Arc, +} + +impl Scheduler { + #[allow(clippy::type_complexity)] + pub fn new( + local_pid: Pid, + registry: Option<&Registry>, + ) -> ( + Self, + mpsc::UnboundedSender<(Address, oneshot::Sender>)>, + mpsc::UnboundedSender<(Address, oneshot::Sender>)>, + mpsc::UnboundedReceiver, + oneshot::Sender<()>, + ) { + let (a2s_listen_s, a2s_listen_r) = + mpsc::unbounded::<(Address, oneshot::Sender>)>(); + let (a2s_connect_s, a2s_connect_r) = + mpsc::unbounded::<(Address, oneshot::Sender>)>(); + let (s2a_connected_s, s2a_connected_r) = mpsc::unbounded::(); + let (a2s_scheduler_shutdown_s, a2s_scheduler_shutdown_r) = oneshot::channel::<()>(); + let (a2s_disconnect_s, a2s_disconnect_r) = + mpsc::unbounded::<(Pid, oneshot::Sender>)>(); + let (b2s_prio_statistic_s, b2s_prio_statistic_r) = mpsc::unbounded::<(Pid, u64, u64)>(); + + let run_channels = Some(ControlChannels { + a2s_listen_r, + a2s_connect_r, + a2s_scheduler_shutdown_r, + a2s_disconnect_r, + b2s_prio_statistic_r, + }); + + let participant_channels = ParticipantChannels { + s2a_connected_s, + a2s_disconnect_s, + b2s_prio_statistic_s, + }; + + let metrics = Arc::new(NetworkMetrics::new(&local_pid).unwrap()); + if let Some(registry) = registry { + metrics.register(registry).unwrap(); + } + + let mut rng = rand::thread_rng(); + let local_secret: u128 = rng.gen(); + + ( + Self { + local_pid, + local_secret, + closed: AtomicBool::new(false), + pool: Arc::new(ThreadPool::new().unwrap()), + run_channels, + participant_channels: Arc::new(Mutex::new(Some(participant_channels))), + participants: Arc::new(RwLock::new(HashMap::new())), + channel_ids: Arc::new(AtomicU64::new(0)), + channel_listener: RwLock::new(HashMap::new()), + metrics, + }, + a2s_listen_s, + a2s_connect_s, + s2a_connected_r, + a2s_scheduler_shutdown_s, + ) + } + + pub async fn run(mut self) { + let run_channels = self.run_channels.take().unwrap(); + + futures::join!( + self.listen_mgr(run_channels.a2s_listen_r), + self.connect_mgr(run_channels.a2s_connect_r), + self.disconnect_mgr(run_channels.a2s_disconnect_r), + self.prio_adj_mgr(run_channels.b2s_prio_statistic_r), + self.scheduler_shutdown_mgr(run_channels.a2s_scheduler_shutdown_r), + ); + } + + async fn listen_mgr( + &self, + a2s_listen_r: mpsc::UnboundedReceiver<(Address, oneshot::Sender>)>, + ) { + trace!("start listen_mgr"); + a2s_listen_r + .for_each_concurrent(None, |(address, s2a_listen_result_s)| { + let address = address; + + async move { + debug!(?address, "got request to open a channel_creator"); + self.metrics + .listen_requests_total + .with_label_values(&[match address { + Address::Tcp(_) => "tcp", + Address::Udp(_) => "udp", + Address::Mpsc(_) => "mpsc", + }]) + .inc(); + let (end_sender, end_receiver) = oneshot::channel::<()>(); + self.channel_listener + .write() + .await + .insert(address.clone(), end_sender); + self.channel_creator(address, end_receiver, s2a_listen_result_s) + .await; + } + }) + .await; + trace!("stop listen_mgr"); + } + + async fn connect_mgr( + &self, + mut a2s_connect_r: mpsc::UnboundedReceiver<( + Address, + oneshot::Sender>, + )>, + ) { + trace!("start connect_mgr"); + while let Some((addr, pid_sender)) = a2s_connect_r.next().await { + let (protocol, handshake) = match addr { + Address::Tcp(addr) => { + self.metrics + .connect_requests_total + .with_label_values(&["tcp"]) + .inc(); + let stream = match net::TcpStream::connect(addr).await { + Ok(stream) => stream, + Err(e) => { + pid_sender.send(Err(e)).unwrap(); + continue; + }, + }; + info!("Connecting Tcp to: {}", stream.peer_addr().unwrap()); + ( + Protocols::Tcp(TcpProtocol::new(stream, self.metrics.clone())), + false, + ) + }, + Address::Udp(addr) => { + self.metrics + .connect_requests_total + .with_label_values(&["udp"]) + .inc(); + let socket = match net::UdpSocket::bind("0.0.0.0:0").await { + Ok(socket) => Arc::new(socket), + Err(e) => { + pid_sender.send(Err(e)).unwrap(); + continue; + }, + }; + if let Err(e) = socket.connect(addr).await { + pid_sender.send(Err(e)).unwrap(); + continue; + }; + info!("Connecting Udp to: {}", addr); + let (udp_data_sender, udp_data_receiver) = mpsc::unbounded::>(); + let protocol = UdpProtocol::new( + socket.clone(), + addr, + self.metrics.clone(), + udp_data_receiver, + ); + self.pool.spawn_ok( + Self::udp_single_channel_connect(socket.clone(), udp_data_sender) + .instrument(tracing::info_span!("udp", ?addr)), + ); + (Protocols::Udp(protocol), true) + }, + _ => unimplemented!(), + }; + self.init_protocol(protocol, Some(pid_sender), handshake) + .await; + } + trace!("stop connect_mgr"); + } + + async fn disconnect_mgr( + &self, + mut a2s_disconnect_r: mpsc::UnboundedReceiver<( + Pid, + oneshot::Sender>, + )>, + ) { + trace!("start disconnect_mgr"); + while let Some((pid, return_once_successfull_shutdown)) = a2s_disconnect_r.next().await { + //Closing Participants is done the following way: + // 1. We drop our senders and receivers + // 2. we need to close BParticipant, this will drop its senderns and receivers + // 3. Participant will try to access the BParticipant senders and receivers with + // their next api action, it will fail and be closed then. + trace!(?pid, "got request to close participant"); + if let Some(mut pi) = self.participants.write().await.remove(&pid) { + let (finished_sender, finished_receiver) = oneshot::channel(); + pi.s2b_shutdown_bparticipant_s + .take() + .unwrap() + .send(finished_sender) + .unwrap(); + drop(pi); + let e = finished_receiver.await.unwrap(); + return_once_successfull_shutdown.send(e).unwrap(); + } else { + debug!(?pid, "looks like participant is already dropped"); + return_once_successfull_shutdown.send(Ok(())).unwrap(); + } + trace!(?pid, "closed participant"); + } + trace!("stop disconnect_mgr"); + } + + async fn prio_adj_mgr( + &self, + mut b2s_prio_statistic_r: mpsc::UnboundedReceiver<(Pid, u64, u64)>, + ) { + trace!("start prio_adj_mgr"); + while let Some((_pid, _frame_cnt, _unused)) = b2s_prio_statistic_r.next().await { + + //TODO adjust prios in participants here! + } + trace!("stop prio_adj_mgr"); + } + + async fn scheduler_shutdown_mgr(&self, a2s_scheduler_shutdown_r: oneshot::Receiver<()>) { + trace!("start scheduler_shutdown_mgr"); + a2s_scheduler_shutdown_r.await.unwrap(); + self.closed.store(true, Ordering::Relaxed); + debug!("shutting down all BParticipants gracefully"); + let mut participants = self.participants.write().await; + let mut waitings = vec![]; + for (pid, mut pi) in participants.drain() { + trace!(?pid, "shutting down BParticipants"); + let (finished_sender, finished_receiver) = oneshot::channel(); + waitings.push((pid, finished_receiver)); + pi.s2b_shutdown_bparticipant_s + .take() + .unwrap() + .send(finished_sender) + .unwrap(); + } + debug!("wait for partiticipants to be shut down"); + for (pid, recv) in waitings { + if let Err(e) = recv.await { + error!( + ?pid, + ?e, + "failed to finish sending all remainding messages to participant when \ + shutting down" + ); + }; + } + //removing the possibility to create new participants, needed to close down + // some mgr: + self.participant_channels.lock().await.take(); + + trace!("stop scheduler_shutdown_mgr"); + } + + async fn channel_creator( + &self, + addr: Address, + s2s_stop_listening_r: oneshot::Receiver<()>, + s2a_listen_result_s: oneshot::Sender>, + ) { + trace!(?addr, "start up channel creator"); + match addr { + Address::Tcp(addr) => { + let listener = match net::TcpListener::bind(addr).await { + Ok(listener) => { + s2a_listen_result_s.send(Ok(())).unwrap(); + listener + }, + Err(e) => { + info!( + ?addr, + ?e, + "listener couldn't be started due to error on tcp bind" + ); + s2a_listen_result_s.send(Err(e)).unwrap(); + return; + }, + }; + trace!(?addr, "listener bound"); + let mut incoming = listener.incoming(); + let mut end_receiver = s2s_stop_listening_r.fuse(); + while let Some(stream) = select! { + next = incoming.next().fuse() => next, + _ = end_receiver => None, + } { + let stream = stream.unwrap(); + info!("Accepting Tcp from: {}", stream.peer_addr().unwrap()); + let protocol = TcpProtocol::new(stream, self.metrics.clone()); + self.init_protocol(Protocols::Tcp(protocol), None, true) + .await; + } + }, + Address::Udp(addr) => { + let socket = match net::UdpSocket::bind(addr).await { + Ok(socket) => { + s2a_listen_result_s.send(Ok(())).unwrap(); + Arc::new(socket) + }, + Err(e) => { + info!( + ?addr, + ?e, + "listener couldn't be started due to error on udp bind" + ); + s2a_listen_result_s.send(Err(e)).unwrap(); + return; + }, + }; + trace!(?addr, "listener bound"); + // receiving is done from here and will be piped to protocol as UDP does not + // have any state + let mut listeners = HashMap::new(); + let mut end_receiver = s2s_stop_listening_r.fuse(); + let mut data = [0u8; 9216]; + while let Ok((size, remote_addr)) = select! { + next = socket.recv_from(&mut data).fuse() => next, + _ = end_receiver => Err(std::io::Error::new(std::io::ErrorKind::Other, "")), + } { + let mut datavec = Vec::with_capacity(size); + datavec.extend_from_slice(&data[0..size]); + //Due to the async nature i cannot make of .entry() as it would lead to a still + // borrowed in another branch situation + #[allow(clippy::map_entry)] + if !listeners.contains_key(&remote_addr) { + info!("Accepting Udp from: {}", &remote_addr); + let (udp_data_sender, udp_data_receiver) = mpsc::unbounded::>(); + listeners.insert(remote_addr, udp_data_sender); + let protocol = UdpProtocol::new( + socket.clone(), + remote_addr, + self.metrics.clone(), + udp_data_receiver, + ); + self.init_protocol(Protocols::Udp(protocol), None, false) + .await; + } + let udp_data_sender = listeners.get_mut(&remote_addr).unwrap(); + udp_data_sender.send(datavec).await.unwrap(); + } + }, + _ => unimplemented!(), + } + trace!(?addr, "ending channel creator"); + } + + async fn udp_single_channel_connect( + socket: Arc, + mut w2p_udp_package_s: mpsc::UnboundedSender>, + ) { + let addr = socket.local_addr(); + trace!(?addr, "start udp_single_channel_connect"); + //TODO: implement real closing + let (_end_sender, end_receiver) = oneshot::channel::<()>(); + + // receiving is done from here and will be piped to protocol as UDP does not + // have any state + let mut end_receiver = end_receiver.fuse(); + let mut data = [0u8; 9216]; + while let Ok(size) = select! { + next = socket.recv(&mut data).fuse() => next, + _ = end_receiver => Err(std::io::Error::new(std::io::ErrorKind::Other, "")), + } { + let mut datavec = Vec::with_capacity(size); + datavec.extend_from_slice(&data[0..size]); + w2p_udp_package_s.send(datavec).await.unwrap(); + } + trace!(?addr, "stop udp_single_channel_connect"); + } + + async fn init_protocol( + &self, + protocol: Protocols, + s2a_return_pid_s: Option>>, + send_handshake: bool, + ) { + //channels are unknown till PID is known! + /* When A connects to a NETWORK, we, the listener answers with a Handshake. + Pro: - Its easier to debug, as someone who opens a port gets a magic number back! + Contra: - DOS posibility because we answer fist + - Speed, because otherwise the message can be send with the creation + */ + let mut participant_channels = self.participant_channels.lock().await.clone().unwrap(); + // spawn is needed here, e.g. for TCP connect it would mean that only 1 + // participant can be in handshake phase ever! Someone could deadlock + // the whole server easily for new clients UDP doesnt work at all, as + // the UDP listening is done in another place. + let cid = self.channel_ids.fetch_add(1, Ordering::Relaxed); + let participants = self.participants.clone(); + let metrics = self.metrics.clone(); + let pool = self.pool.clone(); + let local_pid = self.local_pid; + let local_secret = self.local_secret; + // this is necessary for UDP to work at all and to remove code duplication + self.pool.spawn_ok( + async move { + trace!(?cid, "open channel and be ready for Handshake"); + let handshake = Handshake::new( + cid, + local_pid, + local_secret, + metrics.clone(), + send_handshake, + ); + match handshake.setup(&protocol).await { + Ok((pid, sid, secret, leftover_cid_frame)) => { + trace!( + ?cid, + ?pid, + "detected that my channel is ready!, activating it :)" + ); + let mut participants = participants.write().await; + if !participants.contains_key(&pid) { + debug!(?cid, "new participant connected via a channel"); + let ( + bparticipant, + a2b_steam_open_s, + b2a_stream_opened_r, + mut s2b_create_channel_s, + s2b_shutdown_bparticipant_s, + ) = BParticipant::new(pid, sid, metrics.clone()); + + let participant = Participant::new( + local_pid, + pid, + a2b_steam_open_s, + b2a_stream_opened_r, + participant_channels.a2s_disconnect_s, + ); + + metrics.participants_connected_total.inc(); + participants.insert(pid, ParticipantInfo { + secret, + s2b_create_channel_s: s2b_create_channel_s.clone(), + s2b_shutdown_bparticipant_s: Some(s2b_shutdown_bparticipant_s), + }); + pool.spawn_ok( + bparticipant + .run(participant_channels.b2s_prio_statistic_s) + .instrument(tracing::info_span!("participant", ?pid)), + ); + //create a new channel within BParticipant and wait for it to run + let (b2s_create_channel_done_s, b2s_create_channel_done_r) = + oneshot::channel(); + //From now on wire connects directly with bparticipant! + s2b_create_channel_s + .send(( + cid, + sid, + protocol, + leftover_cid_frame, + b2s_create_channel_done_s, + )) + .await + .unwrap(); + b2s_create_channel_done_r.await.unwrap(); + if let Some(pid_oneshot) = s2a_return_pid_s { + // someone is waiting with `connect`, so give them their PID + pid_oneshot.send(Ok(participant)).unwrap(); + } else { + // noone is waiting on this Participant, return in to Network + participant_channels + .s2a_connected_s + .send(participant) + .await + .unwrap(); + } + } else { + let pi = &participants[&pid]; + trace!("2nd+ channel of participant, going to compare security ids"); + if pi.secret != secret { + warn!( + ?pid, + ?secret, + "Detected incompatible Secret!, this is probably an attack!" + ); + error!("just dropping here, TODO handle this correctly!"); + //TODO + if let Some(pid_oneshot) = s2a_return_pid_s { + // someone is waiting with `connect`, so give them their Error + pid_oneshot + .send(Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "invalid secret, denying connection", + ))) + .unwrap(); + } + return; + } + error!( + "ufff i cant answer the pid_oneshot. as i need to create the SAME \ + participant. maybe switch to ARC" + ); + } + //From now on this CHANNEL can receiver other frames! + // move directly to participant! + }, + Err(()) => { + if let Some(pid_oneshot) = s2a_return_pid_s { + // someone is waiting with `connect`, so give them their Error + pid_oneshot + .send(Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "handshake failed, denying connection", + ))) + .unwrap(); + } + }, + } + } + .instrument(tracing::trace_span!("")), + ); /*WORKAROUND FOR SPAN NOT TO GET LOST*/ + } +} diff --git a/network/src/tcp_channel.rs b/network/src/tcp_channel.rs new file mode 100644 index 0000000000..b3c28a1563 --- /dev/null +++ b/network/src/tcp_channel.rs @@ -0,0 +1,190 @@ +use crate::{ + api::Promise, + internal::{Channel, Stream, TcpFrame, VELOREN_MAGIC_NUMBER, VELOREN_NETWORK_VERSION}, +}; +use bincode; +use enumset::EnumSet; +use mio::{self, net::TcpStream}; +use std::{ + collections::VecDeque, + io::{Read, Write}, + time::Instant, +}; +use tracing::*; + +#[derive(Debug)] +pub(crate) struct TcpChannel { + stream_id_pool: tlid::Pool>, //TODO: stream_id unique per participant + msg_id_pool: tlid::Pool>, //TODO: msg_id unique per participant + participant_id: Option, + pub tcpstream: TcpStream, + pub streams: Vec, + pub send_queue: VecDeque, + pub recv_queue: VecDeque, +} + +impl TcpChannel { + pub fn new(tcpstream: TcpStream) -> Self { + TcpChannel { + stream_id_pool: tlid::Pool::new_full(), + msg_id_pool: tlid::Pool::new_full(), + participant_id: None, + tcpstream, + streams: Vec::new(), + send_queue: VecDeque::new(), + recv_queue: VecDeque::new(), + } + } + + fn handle_frame(&mut self, frame: TcpFrame) { + match frame { + TcpFrame::Handshake { + magic_number, + version, + } => { + if magic_number != VELOREN_MAGIC_NUMBER { + error!("tcp connection with invalid handshake, closing connection"); + #[cfg(debug_assertions)] + { + debug!("sending client instructions before killing"); + let _ = self.tcpstream.write( + "Handshake does not contain the magic number requiered by veloren \ + server.\nWe are not sure if you are a valid veloren client.\nClosing \ + the connection" + .as_bytes(), + ); + } + } + if version != VELOREN_NETWORK_VERSION { + error!("tcp connection with wrong network version"); + #[cfg(debug_assertions)] + { + debug!("sending client instructions before killing"); + let _ = self.tcpstream.write( + format!( + "Handshake does not contain a correct magic number, but invalid \ + version.\nWe don't know how to communicate with you.\nOur \ + Version: {:?}\nYour Version: {:?}\nClosing the connection", + VELOREN_NETWORK_VERSION, version, + ) + .as_bytes(), + ); + } + } + info!(?self, "handshake completed"); + }, + TcpFrame::ParticipantId { pid } => { + self.participant_id = Some(pid); + info!("Participant: {} send their ID", pid); + }, + TcpFrame::OpenStream { + sid, + prio, + promises, + } => { + if let Some(pid) = self.participant_id { + let sid = self.stream_id_pool.next(); + let stream = Stream::new(sid, prio, promises.clone()); + self.streams.push(stream); + info!("Participant: {} opened a stream", pid); + } + }, + TcpFrame::CloseStream { sid } => { + if let Some(pid) = self.participant_id { + self.streams.retain(|stream| stream.sid() != sid); + info!("Participant: {} closed a stream", pid); + } + }, + TcpFrame::DataHeader { id, length } => { + info!("Data Header {}", id); + }, + TcpFrame::Data { id, frame_no, data } => { + info!("Data Package {}", id); + }, + } + } +} + +impl Channel for TcpChannel { + fn read(&mut self, uninitialized_dirty_speed_buffer: &mut [u8; 65000], aprox_time: Instant) { + match self.tcpstream.read(uninitialized_dirty_speed_buffer) { + Ok(n) => { + trace!("incomming message with len: {}", n); + let mut cur = std::io::Cursor::new(&uninitialized_dirty_speed_buffer[..n]); + while cur.position() < n as u64 { + let r: Result = bincode::deserialize_from(&mut cur); + match r { + Ok(frame) => self.handle_frame(frame), + Err(e) => { + error!( + ?self, + ?e, + "failure parsing a message with len: {}, starting with: {:?}", + n, + &uninitialized_dirty_speed_buffer[0..std::cmp::min(n, 10)] + ); + }, + } + } + }, + Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => { + debug!("would block"); + }, + Err(e) => { + panic!("{}", e); + }, + }; + } + + fn write(&mut self, uninitialized_dirty_speed_buffer: &mut [u8; 65000], aprox_time: Instant) { + while let Some(elem) = self.send_queue.pop_front() { + if let Ok(mut data) = bincode::serialize(&elem) { + let total = data.len(); + match self.tcpstream.write(&data) { + Ok(n) if n == total => {}, + Ok(n) => { + error!("could only send part"); + //let data = data.drain(n..).collect(); //TODO: + // validate n.. is correct + // to_send.push_front(data); + }, + Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => { + debug!("would block"); + }, + Err(e) => { + panic!("{}", e); + }, + }; + }; + } + } + + fn open_stream(&mut self, prio: u8, promises: EnumSet) -> u32 { + // validate promises + let sid = self.stream_id_pool.next(); + let stream = Stream::new(sid, prio, promises.clone()); + self.streams.push(stream); + self.send_queue.push_back(TcpFrame::OpenStream { + sid, + prio, + promises, + }); + sid + } + + fn close_stream(&mut self, sid: u32) { + self.streams.retain(|stream| stream.sid() != sid); + self.send_queue.push_back(TcpFrame::CloseStream { sid }); + } + + fn handshake(&mut self) { + self.send_queue.push_back(TcpFrame::Handshake { + magic_number: VELOREN_MAGIC_NUMBER.to_string(), + version: VELOREN_NETWORK_VERSION, + }); + } + + fn participant_id(&mut self, pid: uuid::Uuid) { + self.send_queue.push_back(TcpFrame::ParticipantId { pid }); + } +} diff --git a/network/src/types.rs b/network/src/types.rs new file mode 100644 index 0000000000..fc0fb8c698 --- /dev/null +++ b/network/src/types.rs @@ -0,0 +1,341 @@ +use rand::Rng; + +pub type Mid = u64; +pub type Cid = u64; +pub type Prio = u8; +/// use promises to modify the behavior of [`Streams`]. +/// available promises are: +/// * [`PROMISES_NONE`] +/// * [`PROMISES_ORDERED`] +/// * [`PROMISES_CONSISTENCY`] +/// * [`PROMISES_GUARANTEED_DELIVERY`] +/// * [`PROMISES_COMPRESSED`] +/// * [`PROMISES_ENCRYPTED`] +/// +/// [`Streams`]: crate::api::Stream +pub type Promises = u8; + +/// use for no special promises on this [`Stream`](crate::api::Stream). +pub const PROMISES_NONE: Promises = 0; +/// this will guarantee that the order of messages which are send on one side, +/// is the same when received on the other. +pub const PROMISES_ORDERED: Promises = 1; +/// this will guarantee that messages received haven't been altered by errors, +/// like bit flips, this is done with a checksum. +pub const PROMISES_CONSISTENCY: Promises = 2; +/// this will guarantee that the other side will receive every message exactly +/// once no messages are droped +pub const PROMISES_GUARANTEED_DELIVERY: Promises = 4; +/// this will enable the internal compression on this +/// [`Stream`](crate::api::Stream) +pub const PROMISES_COMPRESSED: Promises = 8; +/// this will enable the internal encryption on this +/// [`Stream`](crate::api::Stream) +pub const PROMISES_ENCRYPTED: Promises = 16; + +pub(crate) const VELOREN_MAGIC_NUMBER: [u8; 7] = [86, 69, 76, 79, 82, 69, 78]; //VELOREN +pub const VELOREN_NETWORK_VERSION: [u32; 3] = [0, 2, 0]; +pub(crate) const STREAM_ID_OFFSET1: Sid = Sid::new(0); +pub(crate) const STREAM_ID_OFFSET2: Sid = Sid::new(u64::MAX / 2); + +/// Support struct used for uniquely identifying [`Participant`] over the +/// [`Network`]. +/// +/// [`Participant`]: crate::api::Participant +/// [`Network`]: crate::api::Network +#[derive(PartialEq, Eq, Hash, Clone, Copy)] +pub struct Pid { + internal: u128, +} + +#[derive(PartialEq, Eq, Hash, Clone, Copy)] +pub(crate) struct Sid { + internal: u64, +} + +// Used for Communication between Channel <----(TCP/UDP)----> Channel +#[derive(Debug)] +pub(crate) enum Frame { + Handshake { + magic_number: [u8; 7], + version: [u32; 3], + }, + Init { + pid: Pid, + secret: u128, + }, + Shutdown, /* Shutsdown this channel gracefully, if all channels are shut down, Participant + * is deleted */ + OpenStream { + sid: Sid, + prio: Prio, + promises: Promises, + }, + CloseStream { + sid: Sid, + }, + DataHeader { + mid: Mid, + sid: Sid, + length: u64, + }, + Data { + mid: Mid, + start: u64, + data: Vec, + }, + /* WARNING: Sending RAW is only used for debug purposes in case someone write a new API + * against veloren Server! */ + Raw(Vec), +} + +impl Frame { + pub const FRAMES_LEN: u8 = 8; + + pub const fn int_to_string(i: u8) -> &'static str { + match i { + 0 => "Handshake", + 1 => "Init", + 2 => "Shutdown", + 3 => "OpenStream", + 4 => "CloseStream", + 5 => "DataHeader", + 6 => "Data", + 7 => "Raw", + _ => "", + } + } + + pub fn get_int(&self) -> u8 { + match self { + Frame::Handshake { + magic_number: _, + version: _, + } => 0, + Frame::Init { pid: _, secret: _ } => 1, + Frame::Shutdown => 2, + Frame::OpenStream { + sid: _, + prio: _, + promises: _, + } => 3, + Frame::CloseStream { sid: _ } => 4, + Frame::DataHeader { + mid: _, + sid: _, + length: _, + } => 5, + Frame::Data { + mid: _, + start: _, + data: _, + } => 6, + Frame::Raw(_) => 7, + } + } + + pub fn get_string(&self) -> &str { Self::int_to_string(self.get_int()) } +} + +impl Pid { + /// create a new Pid with a random interior value + /// + /// # Example + /// ```rust + /// use veloren_network::{Network, Pid}; + /// + /// let pid = Pid::new(); + /// let _ = Network::new(pid, None); + /// ``` + pub fn new() -> Self { + Self { + internal: rand::thread_rng().gen(), + } + } + + /// don't use fake! just for testing! + /// This will panic if pid i greater than 7, as I do not want you to use + /// this in production! + #[doc(hidden)] + pub fn fake(pid: u8) -> Self { + assert!(pid < 8); + Self { + internal: pid as u128, + } + } + + pub(crate) fn to_le_bytes(&self) -> [u8; 16] { self.internal.to_le_bytes() } + + pub(crate) fn from_le_bytes(bytes: [u8; 16]) -> Self { + Self { + internal: u128::from_le_bytes(bytes), + } + } +} + +impl Sid { + pub const fn new(internal: u64) -> Self { Self { internal } } + + pub(crate) fn to_le_bytes(&self) -> [u8; 8] { self.internal.to_le_bytes() } + + pub(crate) fn from_le_bytes(bytes: [u8; 8]) -> Self { + Self { + internal: u64::from_le_bytes(bytes), + } + } +} + +impl std::fmt::Debug for Pid { + #[inline] + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + const BITS_PER_SIXLET: usize = 6; + //only print last 6 chars of number as full u128 logs are unreadable + const CHAR_COUNT: usize = 6; + for i in 0..CHAR_COUNT { + write!( + f, + "{}", + sixlet_to_str((self.internal >> (i * BITS_PER_SIXLET)) & 0x3F) + )?; + } + Ok(()) + } +} + +impl Default for Pid { + fn default() -> Self { Pid::new() } +} + +impl std::fmt::Display for Pid { + #[inline] + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{:?}", self) } +} + +impl std::ops::AddAssign for Sid { + fn add_assign(&mut self, other: Self) { + *self = Self { + internal: self.internal + other.internal, + }; + } +} + +impl std::fmt::Debug for Sid { + #[inline] + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + //only print last 6 chars of number as full u128 logs are unreadable + write!(f, "{}", self.internal.rem_euclid(1000000)) + } +} + +impl From for Sid { + fn from(internal: u64) -> Self { Sid { internal } } +} + +impl std::fmt::Display for Sid { + #[inline] + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.internal) + } +} + +#[inline] +fn sixlet_to_str(sixlet: u128) -> char { + match sixlet { + 0 => 'A', + 1 => 'B', + 2 => 'C', + 3 => 'D', + 4 => 'E', + 5 => 'F', + 6 => 'G', + 7 => 'H', + 8 => 'I', + 9 => 'J', + 10 => 'K', + 11 => 'L', + 12 => 'M', + 13 => 'N', + 14 => 'O', + 15 => 'P', + 16 => 'Q', + 17 => 'R', + 18 => 'S', + 19 => 'T', + 20 => 'U', + 21 => 'V', + 22 => 'W', + 23 => 'X', + 24 => 'Y', + 25 => 'Z', + 26 => 'a', + 27 => 'b', + 28 => 'c', + 29 => 'd', + 30 => 'e', + 31 => 'f', + 32 => 'g', + 33 => 'h', + 34 => 'i', + 35 => 'j', + 36 => 'k', + 37 => 'l', + 38 => 'm', + 39 => 'n', + 40 => 'o', + 41 => 'p', + 42 => 'q', + 43 => 'r', + 44 => 's', + 45 => 't', + 46 => 'u', + 47 => 'v', + 48 => 'w', + 49 => 'x', + 50 => 'y', + 51 => 'z', + 52 => '0', + 53 => '1', + 54 => '2', + 55 => '3', + 56 => '4', + 57 => '5', + 58 => '6', + 59 => '7', + 60 => '8', + 61 => '9', + 62 => '+', + 63 => '/', + _ => '-', + } +} + +#[cfg(test)] +mod tests { + use crate::types::*; + + #[test] + fn frame_int2str() { + assert_eq!(Frame::int_to_string(3), "OpenStream"); + assert_eq!(Frame::int_to_string(7), "Raw"); + assert_eq!(Frame::int_to_string(8), ""); + } + + #[test] + fn frame_get_int() { + assert_eq!(Frame::get_int(&Frame::Raw("Foo".as_bytes().to_vec())), 7); + assert_eq!(Frame::get_int(&Frame::Shutdown), 2); + } + + #[test] + fn frame_creation() { + Pid::new(); + assert_eq!(format!("{}", Pid::fake(2)), "CAAAAA"); + } + + #[test] + fn test_sixlet_to_str() { + assert_eq!(sixlet_to_str(0), 'A'); + assert_eq!(sixlet_to_str(63), '/'); + assert_eq!(sixlet_to_str(64), '-'); + } +} diff --git a/network/tests/closing.rs b/network/tests/closing.rs new file mode 100644 index 0000000000..bed4d3de68 --- /dev/null +++ b/network/tests/closing.rs @@ -0,0 +1,166 @@ +use async_std::task; +use task::block_on; +use veloren_network::StreamError; +mod helper; +use helper::{network_participant_stream, tcp}; + +#[test] +fn close_network() { + let (_, _) = helper::setup(false, 0); + let (_, _p1_a, mut s1_a, _, _p1_b, mut s1_b) = block_on(network_participant_stream(tcp())); + + std::thread::sleep(std::time::Duration::from_millis(200)); + + assert_eq!(s1_a.send("Hello World"), Err(StreamError::StreamClosed)); + let msg1: Result = block_on(s1_b.recv()); + assert_eq!(msg1, Err(StreamError::StreamClosed)); +} + +#[test] +fn close_participant() { + let (_, _) = helper::setup(false, 0); + let (n_a, p1_a, mut s1_a, n_b, p1_b, mut s1_b) = block_on(network_participant_stream(tcp())); + + block_on(n_a.disconnect(p1_a)).unwrap(); + block_on(n_b.disconnect(p1_b)).unwrap(); + + assert_eq!(s1_a.send("Hello World"), Err(StreamError::StreamClosed)); + assert_eq!( + block_on(s1_b.recv::()), + Err(StreamError::StreamClosed) + ); +} + +#[test] +fn close_stream() { + let (_, _) = helper::setup(false, 0); + let (_n_a, _, mut s1_a, _n_b, _, _) = block_on(network_participant_stream(tcp())); + + // s1_b is dropped directly while s1_a isn't + std::thread::sleep(std::time::Duration::from_millis(200)); + + assert_eq!(s1_a.send("Hello World"), Err(StreamError::StreamClosed)); + assert_eq!( + block_on(s1_a.recv::()), + Err(StreamError::StreamClosed) + ); +} + +///THIS is actually a bug which currently luckily doesn't trigger, but with new +/// async-std WE must make sure, if a stream is `drop`ed inside a `block_on`, +/// that no panic is thrown. +#[test] +fn close_streams_in_block_on() { + let (_, _) = helper::setup(false, 0); + let (_n_a, _p_a, s1_a, _n_b, _p_b, s1_b) = block_on(network_participant_stream(tcp())); + block_on(async { + //make it locally so that they are dropped later + let mut s1_a = s1_a; + let mut s1_b = s1_b; + s1_a.send("ping").unwrap(); + assert_eq!(s1_b.recv().await, Ok("ping".to_string())); + drop(s1_a); + }); +} + +#[test] +fn stream_simple_3msg_then_close() { + let (_, _) = helper::setup(false, 0); + let (_n_a, _, mut s1_a, _n_b, _, mut s1_b) = block_on(network_participant_stream(tcp())); + + s1_a.send(1u8).unwrap(); + s1_a.send(42).unwrap(); + s1_a.send("3rdMessage").unwrap(); + assert_eq!(block_on(s1_b.recv()), Ok(1u8)); + assert_eq!(block_on(s1_b.recv()), Ok(42)); + assert_eq!(block_on(s1_b.recv()), Ok("3rdMessage".to_string())); + drop(s1_a); + std::thread::sleep(std::time::Duration::from_millis(200)); + assert_eq!(s1_b.send("Hello World"), Err(StreamError::StreamClosed)); +} + +#[test] +fn stream_send_first_then_receive() { + // recv should still be possible even if stream got closed if they are in queue + let (_, _) = helper::setup(false, 0); + let (_n_a, _, mut s1_a, _n_b, _, mut s1_b) = block_on(network_participant_stream(tcp())); + + s1_a.send(1u8).unwrap(); + s1_a.send(42).unwrap(); + s1_a.send("3rdMessage").unwrap(); + drop(s1_a); + std::thread::sleep(std::time::Duration::from_millis(500)); + assert_eq!(block_on(s1_b.recv()), Ok(1u8)); + assert_eq!(block_on(s1_b.recv()), Ok(42)); + assert_eq!(block_on(s1_b.recv()), Ok("3rdMessage".to_string())); + assert_eq!(s1_b.send("Hello World"), Err(StreamError::StreamClosed)); +} + +#[test] +fn stream_send_1_then_close_stream() { + let (_, _) = helper::setup(false, 0); + let (_n_a, _, mut s1_a, _n_b, _, mut s1_b) = block_on(network_participant_stream(tcp())); + s1_a.send("this message must be received, even if stream is closed already!") + .unwrap(); + drop(s1_a); + std::thread::sleep(std::time::Duration::from_millis(500)); + let exp = Ok("this message must be received, even if stream is closed already!".to_string()); + assert_eq!(block_on(s1_b.recv()), exp); + println!("all received and done"); +} + +#[test] +fn stream_send_100000_then_close_stream() { + let (_, _) = helper::setup(false, 0); + let (_n_a, _, mut s1_a, _n_b, _, mut s1_b) = block_on(network_participant_stream(tcp())); + for _ in 0..100000 { + s1_a.send("woop_PARTY_HARD_woop").unwrap(); + } + drop(s1_a); + let exp = Ok("woop_PARTY_HARD_woop".to_string()); + println!("start receiving"); + block_on(async { + for _ in 0..100000 { + assert_eq!(s1_b.recv().await, exp); + } + }); + println!("all received and done"); +} + +#[test] +fn stream_send_100000_then_close_stream_remote() { + let (_, _) = helper::setup(false, 0); + let (_n_a, _, mut s1_a, _n_b, _, _s1_b) = block_on(network_participant_stream(tcp())); + for _ in 0..100000 { + s1_a.send("woop_PARTY_HARD_woop").unwrap(); + } + drop(s1_a); + drop(_s1_b); + //no receiving +} + +#[test] +fn stream_send_100000_then_close_stream_remote2() { + let (_, _) = helper::setup(false, 0); + let (_n_a, _, mut s1_a, _n_b, _, _s1_b) = block_on(network_participant_stream(tcp())); + for _ in 0..100000 { + s1_a.send("woop_PARTY_HARD_woop").unwrap(); + } + drop(_s1_b); + std::thread::sleep(std::time::Duration::from_millis(200)); + drop(s1_a); + //no receiving +} + +#[test] +fn stream_send_100000_then_close_stream_remote3() { + let (_, _) = helper::setup(false, 0); + let (_n_a, _, mut s1_a, _n_b, _, _s1_b) = block_on(network_participant_stream(tcp())); + for _ in 0..100000 { + s1_a.send("woop_PARTY_HARD_woop").unwrap(); + } + drop(_s1_b); + std::thread::sleep(std::time::Duration::from_millis(200)); + drop(s1_a); + //no receiving +} diff --git a/network/tests/helper.rs b/network/tests/helper.rs new file mode 100644 index 0000000000..f043074e8e --- /dev/null +++ b/network/tests/helper.rs @@ -0,0 +1,93 @@ +use lazy_static::*; +use std::{ + net::SocketAddr, + sync::{ + atomic::{AtomicU16, Ordering}, + Arc, + }, + thread, + time::Duration, +}; +use tracing::*; +use tracing_subscriber::EnvFilter; +use veloren_network::{Address, Network, Participant, Pid, Stream, PROMISES_NONE}; + +#[allow(dead_code)] +pub fn setup(tracing: bool, mut sleep: u64) -> (u64, u64) { + if tracing { + sleep += 1000 + } + if sleep > 0 { + thread::sleep(Duration::from_millis(sleep)); + } + + let _subscriber = if tracing { + let filter = EnvFilter::from_default_env() + .add_directive("trace".parse().unwrap()) + .add_directive("async_std::task::block_on=warn".parse().unwrap()) + .add_directive("veloren_network::tests=trace".parse().unwrap()) + .add_directive("veloren_network::controller=trace".parse().unwrap()) + .add_directive("veloren_network::channel=trace".parse().unwrap()) + .add_directive("veloren_network::message=trace".parse().unwrap()) + .add_directive("veloren_network::metrics=trace".parse().unwrap()) + .add_directive("veloren_network::types=trace".parse().unwrap()); + + Some( + tracing_subscriber::FmtSubscriber::builder() + // all spans/events with a level higher than TRACE (e.g, info, warn, etc.) + // will be written to stdout. + .with_max_level(Level::TRACE) + .with_env_filter(filter) + // sets this to be the default, global subscriber for this application. + .try_init(), + ) + } else { + None + }; + + (0, 0) +} + +#[allow(dead_code)] +pub async fn network_participant_stream( + addr: Address, +) -> ( + Network, + Arc, + Stream, + Network, + Arc, + Stream, +) { + let (n_a, f_a) = Network::new(Pid::fake(1), None); + std::thread::spawn(f_a); + let (n_b, f_b) = Network::new(Pid::fake(2), None); + std::thread::spawn(f_b); + + n_a.listen(addr.clone()).await.unwrap(); + let p1_b = n_b.connect(addr).await.unwrap(); + let p1_a = n_a.connected().await.unwrap(); + + let s1_a = p1_a.open(10, PROMISES_NONE).await.unwrap(); + let s1_b = p1_b.opened().await.unwrap(); + + (n_a, p1_a, s1_a, n_b, p1_b, s1_b) +} + +#[allow(dead_code)] +pub fn tcp() -> veloren_network::Address { + lazy_static! { + static ref PORTS: AtomicU16 = AtomicU16::new(5000); + } + let port = PORTS.fetch_add(1, Ordering::Relaxed); + veloren_network::Address::Tcp(SocketAddr::from(([127, 0, 0, 1], port))) +} + +#[allow(dead_code)] +pub fn udp() -> veloren_network::Address { + lazy_static! { + static ref PORTS: AtomicU16 = AtomicU16::new(5000); + } + let port = PORTS.fetch_add(1, Ordering::Relaxed); + veloren_network::Address::Udp(SocketAddr::from(([127, 0, 0, 1], port))) +} diff --git a/network/tests/integration.rs b/network/tests/integration.rs new file mode 100644 index 0000000000..fe40810eb3 --- /dev/null +++ b/network/tests/integration.rs @@ -0,0 +1,184 @@ +use async_std::task; +use task::block_on; +use veloren_network::{NetworkError, StreamError}; +mod helper; +use helper::{network_participant_stream, tcp, udp}; +use std::io::ErrorKind; +use veloren_network::{Address, Network, Pid, PROMISES_CONSISTENCY, PROMISES_ORDERED}; + +#[test] +#[ignore] +fn network_20s() { + let (_, _) = helper::setup(false, 0); + let (_n_a, _, _, _n_b, _, _) = block_on(network_participant_stream(tcp())); + std::thread::sleep(std::time::Duration::from_secs(30)); +} + +#[test] +fn stream_simple() { + let (_, _) = helper::setup(false, 0); + let (_n_a, _, mut s1_a, _n_b, _, mut s1_b) = block_on(network_participant_stream(tcp())); + + s1_a.send("Hello World").unwrap(); + assert_eq!(block_on(s1_b.recv()), Ok("Hello World".to_string())); +} + +#[test] +fn stream_simple_3msg() { + let (_, _) = helper::setup(false, 0); + let (_n_a, _, mut s1_a, _n_b, _, mut s1_b) = block_on(network_participant_stream(tcp())); + + s1_a.send("Hello World").unwrap(); + s1_a.send(1337).unwrap(); + assert_eq!(block_on(s1_b.recv()), Ok("Hello World".to_string())); + assert_eq!(block_on(s1_b.recv()), Ok(1337)); + s1_a.send("3rdMessage").unwrap(); + assert_eq!(block_on(s1_b.recv()), Ok("3rdMessage".to_string())); +} + +#[test] +fn stream_simple_udp() { + let (_, _) = helper::setup(false, 0); + let (_n_a, _, mut s1_a, _n_b, _, mut s1_b) = block_on(network_participant_stream(udp())); + + s1_a.send("Hello World").unwrap(); + assert_eq!(block_on(s1_b.recv()), Ok("Hello World".to_string())); +} + +#[test] +fn stream_simple_udp_3msg() { + let (_, _) = helper::setup(false, 0); + let (_n_a, _, mut s1_a, _n_b, _, mut s1_b) = block_on(network_participant_stream(udp())); + + s1_a.send("Hello World").unwrap(); + s1_a.send(1337).unwrap(); + assert_eq!(block_on(s1_b.recv()), Ok("Hello World".to_string())); + assert_eq!(block_on(s1_b.recv()), Ok(1337)); + s1_a.send("3rdMessage").unwrap(); + assert_eq!(block_on(s1_b.recv()), Ok("3rdMessage".to_string())); +} + +#[test] +#[ignore] +fn tcp_and_udp_2_connections() -> std::result::Result<(), Box> { + let (_, _) = helper::setup(false, 0); + let (network, f) = Network::new(Pid::new(), None); + let (remote, fr) = Network::new(Pid::new(), None); + std::thread::spawn(f); + std::thread::spawn(fr); + block_on(async { + remote + .listen(Address::Tcp("0.0.0.0:2000".parse().unwrap())) + .await?; + remote + .listen(Address::Udp("0.0.0.0:2001".parse().unwrap())) + .await?; + let p1 = network + .connect(Address::Tcp("127.0.0.1:2000".parse().unwrap())) + .await?; + let p2 = network + .connect(Address::Udp("127.0.0.1:2001".parse().unwrap())) + .await?; + assert!(std::sync::Arc::ptr_eq(&p1, &p2)); + Ok(()) + }) +} + +#[test] +fn failed_listen_on_used_ports() -> std::result::Result<(), Box> { + let (_, _) = helper::setup(false, 0); + let (network, f) = Network::new(Pid::new(), None); + std::thread::spawn(f); + let udp1 = udp(); + let tcp1 = tcp(); + block_on(network.listen(udp1.clone()))?; + block_on(network.listen(tcp1.clone()))?; + std::thread::sleep(std::time::Duration::from_millis(200)); + + let (network2, f2) = Network::new(Pid::new(), None); + std::thread::spawn(f2); + let e1 = block_on(network2.listen(udp1)); + let e2 = block_on(network2.listen(tcp1)); + match e1 { + Err(NetworkError::ListenFailed(e)) if e.kind() == ErrorKind::AddrInUse => (), + _ => assert!(false), + }; + match e2 { + Err(NetworkError::ListenFailed(e)) if e.kind() == ErrorKind::AddrInUse => (), + _ => assert!(false), + }; + Ok(()) +} + +/// There is a bug an impris-desktop-1 which fails the DOC tests, +/// it fails exactly `api_stream_send_main` and `api_stream_recv_main` by +/// deadlocking at different times! +/// So i rather put the same test into a unit test, these are now duplicate to +/// the api, but are left here, just to be save! +#[test] +fn api_stream_send_main() -> std::result::Result<(), Box> { + let (_, _) = helper::setup(false, 0); + // Create a Network, listen on Port `1200` and wait for a Stream to be opened, + // then answer `Hello World` + let (network, f) = Network::new(Pid::new(), None); + let (remote, fr) = Network::new(Pid::new(), None); + std::thread::spawn(f); + std::thread::spawn(fr); + block_on(async { + network + .listen(Address::Tcp("127.0.0.1:1200".parse().unwrap())) + .await?; + let remote_p = remote + .connect(Address::Tcp("127.0.0.1:1200".parse().unwrap())) + .await?; + // keep it alive + let _stream_p = remote_p + .open(16, PROMISES_ORDERED | PROMISES_CONSISTENCY) + .await?; + let participant_a = network.connected().await?; + let mut stream_a = participant_a.opened().await?; + //Send Message + stream_a.send("Hello World")?; + Ok(()) + }) +} + +#[test] +fn api_stream_recv_main() -> std::result::Result<(), Box> { + let (_, _) = helper::setup(false, 0); + // Create a Network, listen on Port `1220` and wait for a Stream to be opened, + // then listen on it + let (network, f) = Network::new(Pid::new(), None); + let (remote, fr) = Network::new(Pid::new(), None); + std::thread::spawn(f); + std::thread::spawn(fr); + block_on(async { + network + .listen(Address::Tcp("127.0.0.1:1220".parse().unwrap())) + .await?; + let remote_p = remote + .connect(Address::Tcp("127.0.0.1:1220".parse().unwrap())) + .await?; + let mut stream_p = remote_p + .open(16, PROMISES_ORDERED | PROMISES_CONSISTENCY) + .await?; + stream_p.send("Hello World")?; + let participant_a = network.connected().await?; + let mut stream_a = participant_a.opened().await?; + //Send Message + assert_eq!("Hello World".to_string(), stream_a.recv::().await?); + Ok(()) + }) +} + +#[test] +fn wrong_parse() { + let (_, _) = helper::setup(false, 0); + let (_n_a, _, mut s1_a, _n_b, _, mut s1_b) = block_on(network_participant_stream(tcp())); + + s1_a.send(1337).unwrap(); + match block_on(s1_b.recv::()) { + Err(StreamError::DeserializeError(_)) => assert!(true), + _ => assert!(false, "this should fail, but it doesnt!"), + } +} diff --git a/server/src/lib.rs b/server/src/lib.rs index 6b8efeedf5..bbcd8ec84d 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -38,7 +38,7 @@ use common::{ vol::{ReadVol, RectVolSize}, }; use log::{debug, error}; -use metrics::ServerMetrics; +use metrics::{ServerMetrics, TickMetrics}; use specs::{join::Join, Builder, Entity as EcsEntity, RunNow, SystemData, WorldExt}; use std::{ i32, @@ -80,6 +80,7 @@ pub struct Server { server_info: ServerInfo, metrics: ServerMetrics, + tick_metrics: TickMetrics, server_settings: ServerSettings, } @@ -215,6 +216,14 @@ impl Server { state.ecs_mut().insert(DeletedEntities::default()); + let mut metrics = ServerMetrics::new(); + // register all metrics submodules here + let tick_metrics = TickMetrics::new(metrics.registry(), metrics.tick_clone()) + .expect("Failed to initialize server tick metrics submodule."); + metrics + .run(settings.metrics_address) + .expect("Failed to initialize server metrics submodule."); + let this = Self { state, world: Arc::new(world), @@ -233,8 +242,8 @@ impl Server { git_date: common::util::GIT_DATE.to_string(), auth_provider: settings.auth_server_address.clone(), }, - metrics: ServerMetrics::new(settings.metrics_address) - .expect("Failed to initialize server metrics submodule."), + metrics, + tick_metrics, server_settings: settings.clone(), }; @@ -401,90 +410,91 @@ impl Server { let total_sys_ran_in_dispatcher_nanos = terrain_nanos + waypoint_nanos; // Report timing info - self.metrics + self.tick_metrics .tick_time .with_label_values(&["new connections"]) .set((before_message_system - before_new_connections).as_nanos() as i64); - self.metrics + self.tick_metrics .tick_time .with_label_values(&["state tick"]) .set( (before_handle_events - before_state_tick).as_nanos() as i64 - total_sys_ran_in_dispatcher_nanos, ); - self.metrics + self.tick_metrics .tick_time .with_label_values(&["handle server events"]) .set((before_update_terrain_and_regions - before_handle_events).as_nanos() as i64); - self.metrics + self.tick_metrics .tick_time .with_label_values(&["update terrain and region map"]) .set((before_sync - before_update_terrain_and_regions).as_nanos() as i64); - self.metrics + self.tick_metrics .tick_time .with_label_values(&["world tick"]) .set((before_entity_cleanup - before_world_tick).as_nanos() as i64); - self.metrics + self.tick_metrics .tick_time .with_label_values(&["entity cleanup"]) .set((end_of_server_tick - before_entity_cleanup).as_nanos() as i64); - self.metrics + self.tick_metrics .tick_time .with_label_values(&["entity sync"]) .set(entity_sync_nanos); - self.metrics + self.tick_metrics .tick_time .with_label_values(&["message"]) .set(message_nanos); - self.metrics + self.tick_metrics .tick_time .with_label_values(&["sentinel"]) .set(sentinel_nanos); - self.metrics + self.tick_metrics .tick_time .with_label_values(&["subscription"]) .set(subscription_nanos); - self.metrics + self.tick_metrics .tick_time .with_label_values(&["terrain sync"]) .set(terrain_sync_nanos); - self.metrics + self.tick_metrics .tick_time .with_label_values(&["terrain"]) .set(terrain_nanos); - self.metrics + self.tick_metrics .tick_time .with_label_values(&["waypoint"]) .set(waypoint_nanos); - self.metrics + self.tick_metrics .tick_time .with_label_values(&["persistence:stats"]) .set(stats_persistence_nanos); // Report other info - self.metrics + self.tick_metrics .player_online .set(self.state.ecs().read_storage::().join().count() as i64); - self.metrics + self.tick_metrics .time_of_day .set(self.state.ecs().read_resource::().0); - if self.metrics.is_100th_tick() { + if self.tick_metrics.is_100th_tick() { let mut chonk_cnt = 0; let chunk_cnt = self.state.terrain().iter().fold(0, |a, (_, c)| { chonk_cnt += 1; a + c.sub_chunks_len() }); - self.metrics.chonks_count.set(chonk_cnt as i64); - self.metrics.chunks_count.set(chunk_cnt as i64); + self.tick_metrics.chonks_count.set(chonk_cnt as i64); + self.tick_metrics.chunks_count.set(chunk_cnt as i64); let entity_count = self.state.ecs().entities().join().count(); - self.metrics.entity_count.set(entity_count as i64); + self.tick_metrics.entity_count.set(entity_count as i64); } //self.metrics.entity_count.set(self.state.); - self.metrics + self.tick_metrics .tick_time .with_label_values(&["metrics"]) .set(end_of_server_tick.elapsed().as_nanos() as i64); + self.metrics.tick(); // 8) Finish the tick, pass control back to the frontend. @@ -588,7 +598,7 @@ impl Server { .is_some() } - pub fn number_of_players(&self) -> i64 { self.metrics.player_online.get() } + pub fn number_of_players(&self) -> i64 { self.tick_metrics.player_online.get() } } impl Drop for Server { diff --git a/server/src/metrics.rs b/server/src/metrics.rs index 23c9a70e51..0c13a98c8c 100644 --- a/server/src/metrics.rs +++ b/server/src/metrics.rs @@ -6,14 +6,14 @@ use std::{ error::Error, net::SocketAddr, sync::{ - atomic::{AtomicBool, Ordering}, + atomic::{AtomicBool, AtomicU64, Ordering}, Arc, }, thread, time::{Duration, SystemTime, UNIX_EPOCH}, }; -pub struct ServerMetrics { +pub struct TickMetrics { pub chonks_count: IntGauge, pub chunks_count: IntGauge, pub player_online: IntGauge, @@ -23,77 +23,116 @@ pub struct ServerMetrics { pub start_time: IntGauge, pub time_of_day: Gauge, pub light_count: IntGauge, - running: Arc, - pub handle: Option>, - pub every_100th: i8, + tick: Arc, } -impl ServerMetrics { - pub fn new(addr: SocketAddr) -> Result> { - let opts = Opts::new( +pub struct ServerMetrics { + running: Arc, + handle: Option>, + registry: Option, + tick: Arc, +} + +impl TickMetrics { + pub fn new(registry: &Registry, tick: Arc) -> Result> { + let player_online = IntGauge::with_opts(Opts::new( "player_online", "shows the number of clients connected to the server", - ); - let player_online = IntGauge::with_opts(opts)?; - let opts = Opts::new( + ))?; + let entity_count = IntGauge::with_opts(Opts::new( "entity_count", "number of all entities currently active on the server", - ); - let entity_count = IntGauge::with_opts(opts)?; + ))?; let opts = Opts::new("veloren_build_info", "Build information") .const_label("hash", &common::util::GIT_HASH) .const_label("version", ""); let build_info = IntGauge::with_opts(opts)?; - let opts = Opts::new( + let start_time = IntGauge::with_opts(Opts::new( "veloren_start_time", "start time of the server in seconds since EPOCH", - ); - let start_time = IntGauge::with_opts(opts)?; - let opts = Opts::new("time_of_day", "ingame time in ingame-seconds"); - let time_of_day = Gauge::with_opts(opts)?; - let opts = Opts::new( + ))?; + let time_of_day = + Gauge::with_opts(Opts::new("time_of_day", "ingame time in ingame-seconds"))?; + let light_count = IntGauge::with_opts(Opts::new( "light_count", "number of all lights currently active on the server", - ); - let light_count = IntGauge::with_opts(opts)?; - let opts = Opts::new( + ))?; + let chonks_count = IntGauge::with_opts(Opts::new( "chonks_count", "number of all chonks currently active on the server", - ); - let chonks_count = IntGauge::with_opts(opts)?; - let opts = Opts::new( + ))?; + let chunks_count = IntGauge::with_opts(Opts::new( "chunks_count", "number of all chunks currently active on the server", - ); - let chunks_count = IntGauge::with_opts(opts)?; - let vec = IntGaugeVec::new( + ))?; + let tick_time = IntGaugeVec::from(IntGaugeVec::new( Opts::new("tick_time", "time in ns requiered for a tick of the server"), &["period"], - )?; - let tick_time = IntGaugeVec::from(vec); + )?); let since_the_epoch = SystemTime::now() .duration_since(UNIX_EPOCH) .expect("Time went backwards"); start_time.set(since_the_epoch.as_secs().try_into()?); - let registry = Registry::new(); - //registry.register(Box::new(chonks_count.clone())).unwrap(); registry.register(Box::new(player_online.clone()))?; registry.register(Box::new(entity_count.clone()))?; registry.register(Box::new(build_info.clone()))?; registry.register(Box::new(start_time.clone()))?; registry.register(Box::new(time_of_day.clone()))?; - //registry.register(Box::new(light_count.clone())).unwrap(); registry.register(Box::new(chonks_count.clone()))?; registry.register(Box::new(chunks_count.clone()))?; registry.register(Box::new(tick_time.clone()))?; - let running = Arc::new(AtomicBool::new(true)); - let running2 = running.clone(); + Ok(Self { + chonks_count, + chunks_count, + player_online, + entity_count, + tick_time, + build_info, + start_time, + time_of_day, + light_count, + tick, + }) + } + + pub fn is_100th_tick(&self) -> bool { self.tick.load(Ordering::Relaxed).rem_euclid(100) == 0 } +} + +impl ServerMetrics { + pub fn new() -> Self { + let running = Arc::new(AtomicBool::new(false)); + let tick = Arc::new(AtomicU64::new(0)); + let registry = Some(Registry::new()); + + Self { + running, + handle: None, + registry, + tick, + } + } + + pub fn registry(&self) -> &Registry { + match self.registry { + Some(ref r) => r, + None => panic!("You cannot longer register new metrics after the server has started!"), + } + } + + pub fn run(&mut self, addr: SocketAddr) -> Result<(), Box> { + self.running.store(true, Ordering::Relaxed); + let running2 = self.running.clone(); + + let registry = self + .registry + .take() + .expect("ServerMetrics must be already started"); //TODO: make this a job - let handle = Some(thread::spawn(move || { + self.handle = Some(thread::spawn(move || { let server = Server::new(addr, move |request| { router!(request, (GET) (/metrics) => { @@ -106,7 +145,7 @@ impl ServerMetrics { _ => rouille::Response::empty_404() ) }) - .expect("Failed to start server"); + .expect("Failed to start server"); info!("Started server metrics: {}", addr); while running2.load(Ordering::Relaxed) { server.poll(); @@ -114,32 +153,12 @@ impl ServerMetrics { thread::sleep(Duration::from_millis(100)); } })); - - Ok(Self { - chonks_count, - chunks_count, - player_online, - entity_count, - tick_time, - build_info, - start_time, - time_of_day, - light_count, - running, - handle, - every_100th: 0, - }) + Ok(()) } - pub fn is_100th_tick(&mut self) -> bool { - self.every_100th += 1; - if self.every_100th == 100 { - self.every_100th = 0; - true - } else { - false - } - } + pub fn tick(&self) -> u64 { self.tick.fetch_add(1, Ordering::Relaxed) + 1 } + + pub fn tick_clone(&self) -> Arc { self.tick.clone() } } impl Drop for ServerMetrics {