diff --git a/Cargo.lock b/Cargo.lock index 8a731a3..adc7141 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,274 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "actix-codec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" -dependencies = [ - "bitflags 2.4.2", - "bytes", - "futures-core", - "futures-sink", - "memchr", - "pin-project-lite", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "actix-files" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf0bdd6ff79de7c9a021f5d9ea79ce23e108d8bfc9b49b5b4a2cf6fad5a35212" -dependencies = [ - "actix-http", - "actix-service", - "actix-utils", - "actix-web", - "bitflags 2.4.2", - "bytes", - "derive_more", - "futures-core", - "http-range", - "log", - "mime", - "mime_guess", - "percent-encoding", - "pin-project-lite", - "v_htmlescape", -] - -[[package]] -name = "actix-http" -version = "3.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d223b13fd481fc0d1f83bb12659ae774d9e3601814c68a0bc539731698cca743" -dependencies = [ - "actix-codec", - "actix-rt", - "actix-service", - "actix-tls", - "actix-utils", - "ahash", - "base64", - "bitflags 2.4.2", - "bytes", - "bytestring", - "derive_more", - "encoding_rs", - "futures-core", - "h2", - "http 0.2.11", - "httparse", - "httpdate", - "itoa", - "language-tags", - "local-channel", - "mime", - "percent-encoding", - "pin-project-lite", - "rand", - "sha1", - "smallvec", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "actix-macros" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" -dependencies = [ - "quote", - "syn 2.0.50", -] - -[[package]] -name = "actix-router" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d22475596539443685426b6bdadb926ad0ecaefdfc5fb05e5e3441f15463c511" -dependencies = [ - "bytestring", - "http 0.2.11", - "regex", - "serde", - "tracing", -] - -[[package]] -name = "actix-rt" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28f32d40287d3f402ae0028a9d54bef51af15c8769492826a69d28f81893151d" -dependencies = [ - "futures-core", - "tokio", -] - -[[package]] -name = "actix-server" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eb13e7eef0423ea6eab0e59f6c72e7cb46d33691ad56a726b3cd07ddec2c2d4" -dependencies = [ - "actix-rt", - "actix-service", - "actix-utils", - "futures-core", - "futures-util", - "mio", - "socket2", - "tokio", - "tracing", -] - -[[package]] -name = "actix-service" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a" -dependencies = [ - "futures-core", - "paste", - "pin-project-lite", -] - -[[package]] -name = "actix-tls" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4cce60a2f2b477bc72e5cde0af1812a6e82d8fd85b5570a5dcf2a5bf2c5be5f" -dependencies = [ - "actix-rt", - "actix-service", - "actix-utils", - "futures-core", - "impl-more", - "openssl", - "pin-project-lite", - "tokio", - "tokio-openssl", - "tokio-util", - "tracing", -] - -[[package]] -name = "actix-utils" -version = "3.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8" -dependencies = [ - "local-waker", - "pin-project-lite", -] - -[[package]] -name = "actix-web" -version = "4.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a6556ddebb638c2358714d853257ed226ece6023ef9364f23f0c70737ea984" -dependencies = [ - "actix-codec", - "actix-http", - "actix-macros", - "actix-router", - "actix-rt", - "actix-server", - "actix-service", - "actix-tls", - "actix-utils", - "actix-web-codegen", - "ahash", - "bytes", - "bytestring", - "cfg-if", - "derive_more", - "encoding_rs", - "futures-core", - "futures-util", - "itoa", - "language-tags", - "log", - "mime", - "once_cell", - "pin-project-lite", - "regex", - "serde", - "serde_json", - "serde_urlencoded", - "smallvec", - "socket2", - "time", - "url", -] - -[[package]] -name = "actix-web-codegen" -version = "4.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1f50ebbb30eca122b188319a4398b3f7bb4a8cdf50ecfb73bfc6a3c3ce54f5" -dependencies = [ - "actix-router", - "proc-macro2", - "quote", - "syn 2.0.50", -] - -[[package]] -name = "actix-web-lab" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7675c1a84eec1b179c844cdea8488e3e409d8e4984026e92fa96c87dd86f33c6" -dependencies = [ - "actix-http", - "actix-router", - "actix-service", - "actix-utils", - "actix-web", - "actix-web-lab-derive", - "ahash", - "arc-swap", - "async-trait", - "bytes", - "bytestring", - "csv", - "derive_more", - "futures-core", - "futures-util", - "http 0.2.11", - "impl-more", - "itertools", - "local-channel", - "mediatype", - "mime", - "once_cell", - "pin-project-lite", - "regex", - "serde", - "serde_html_form", - "serde_json", - "tokio", - "tokio-stream", - "tracing", -] - -[[package]] -name = "actix-web-lab-derive" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aa0b287c8de4a76b691f29dbb5451e8dd5b79d777eaf87350c9b0cbfdb5e968" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.50", -] - [[package]] name = "addr2line" version = "0.21.0" @@ -285,19 +17,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "ahash" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d713b3834d76b85304d4d525563c1276e2e30dc97cc67bfb4585a4a29fc2c89f" -dependencies = [ - "cfg-if", - "getrandom", - "once_cell", - "version_check", - "zerocopy", -] - [[package]] name = "aho-corasick" version = "1.1.2" @@ -438,6 +157,56 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "axum" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1236b4b292f6c4d6dc34604bb5120d85c3fe1d1aa596bd5cc52ca054d13e7b9e" +dependencies = [ + "async-trait", + "axum-core", + "bytes", + "futures-util", + "http 1.0.0", + "http-body 1.0.0", + "http-body-util", + "hyper 1.2.0", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 1.0.0", + "http-body 1.0.0", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper", + "tower-layer", + "tower-service", +] + [[package]] name = "backtrace" version = "0.3.69" @@ -480,15 +249,6 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - [[package]] name = "built" version = "0.7.1" @@ -524,15 +284,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" -[[package]] -name = "bytestring" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d80203ea6b29df88012294f62733de21cfeab47f17b41af3a38bc30a03ee72" -dependencies = [ - "bytes", -] - [[package]] name = "cairo-rs" version = "0.18.5" @@ -697,12 +448,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - [[package]] name = "core-foundation" version = "0.9.4" @@ -801,69 +546,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "csv" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" -dependencies = [ - "csv-core", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "csv-core" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" -dependencies = [ - "memchr", -] - -[[package]] -name = "deranged" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" -dependencies = [ - "powerfmt", -] - -[[package]] -name = "derive_more" -version = "0.99.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" -dependencies = [ - "convert_case", - "proc-macro2", - "quote", - "rustc_version", - "syn 1.0.109", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] - [[package]] name = "dirs-next" version = "2.0.0" @@ -1244,16 +926,6 @@ dependencies = [ "x11", ] -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - [[package]] name = "getrandom" version = "0.2.12" @@ -1445,6 +1117,25 @@ dependencies = [ "tracing", ] +[[package]] +name = "h2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31d030e59af851932b72ceebadf4a2b5986dba4c3b99dd2493f8273a0f151943" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 1.0.0", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.14.3" @@ -1455,11 +1146,9 @@ checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" name = "hath-rust" version = "1.1.2" dependencies = [ - "actix-files", - "actix-tls", - "actix-web", - "actix-web-lab", + "arc-swap", "async-stream", + "axum", "built", "bytes", "chrono", @@ -1469,6 +1158,7 @@ dependencies = [ "filetime", "futures", "hex", + "http-body 1.0.0", "http-body-util", "hyper 1.2.0", "hyper-util", @@ -1489,7 +1179,10 @@ dependencies = [ "tempfile", "tikv-jemallocator", "tokio", + "tokio-openssl", "tokio-stream", + "tower", + "tower-http", "tray-icon", "windows 0.53.0", ] @@ -1569,10 +1262,10 @@ dependencies = [ ] [[package]] -name = "http-range" -version = "0.1.5" +name = "http-range-header" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" +checksum = "3ce4ef31cda248bbdb6e6820603b82dfcd9e833db65a43e997a0ccec777d11fe" [[package]] name = "httparse" @@ -1596,7 +1289,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", + "h2 0.3.24", "http 0.2.11", "http-body 0.4.6", "httparse", @@ -1619,9 +1312,11 @@ dependencies = [ "bytes", "futures-channel", "futures-util", + "h2 0.4.2", "http 1.0.0", "http-body 1.0.0", "httparse", + "httpdate", "itoa", "pin-project-lite", "smallvec", @@ -1704,12 +1399,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "impl-more" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "206ca75c9c03ba3d4ace2460e57b189f39f43de612c2f85836e65c929701bb2d" - [[package]] name = "indexmap" version = "2.2.3" @@ -1751,15 +1440,6 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.10" @@ -1817,12 +1497,6 @@ dependencies = [ "unicode-segmentation", ] -[[package]] -name = "language-tags" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" - [[package]] name = "lazy_static" version = "1.4.0" @@ -1910,23 +1584,6 @@ version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" -[[package]] -name = "local-channel" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6cbc85e69b8df4b8bb8b89ec634e7189099cea8927a276b7384ce5488e53ec8" -dependencies = [ - "futures-core", - "futures-sink", - "local-waker", -] - -[[package]] -name = "local-waker" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487" - [[package]] name = "lock_api" version = "0.4.11" @@ -1953,10 +1610,10 @@ dependencies = [ ] [[package]] -name = "mediatype" -version = "0.19.18" +name = "matchit" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8878cd8d1b3c8c8ae4b2ba0a36652b7cf192f618a599a7fbdfa25cffd4ea72dd" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" [[package]] name = "memchr" @@ -2066,12 +1723,6 @@ dependencies = [ "unicode-segmentation", ] -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - [[package]] name = "num-traits" version = "0.2.18" @@ -2235,12 +1886,6 @@ dependencies = [ "windows-targets 0.48.5", ] -[[package]] -name = "paste" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" - [[package]] name = "percent-encoding" version = "2.3.1" @@ -2257,6 +1902,26 @@ dependencies = [ "indexmap", ] +[[package]] +name = "pin-project" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0302c4a0442c456bd56f841aee5c3bfd17967563f6fadc9ceb9f9c23cf3807e0" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.50", +] + [[package]] name = "pin-project-lite" version = "0.2.13" @@ -2288,12 +1953,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - [[package]] name = "ppv-lite86" version = "0.2.17" @@ -2464,7 +2123,7 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "h2", + "h2 0.3.24", "http 0.2.11", "http-body 0.4.6", "hyper 0.14.28", @@ -2571,6 +2230,12 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + [[package]] name = "ryu" version = "1.0.17" @@ -2628,19 +2293,6 @@ dependencies = [ "syn 2.0.50", ] -[[package]] -name = "serde_html_form" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20e1066e1cfa6692a722cf40386a2caec36da5ddc4a2c16df592f0f609677e8c" -dependencies = [ - "form_urlencoded", - "indexmap", - "itoa", - "ryu", - "serde", -] - [[package]] name = "serde_json" version = "1.0.114" @@ -2673,17 +2325,6 @@ dependencies = [ "serde", ] -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - [[package]] name = "signal-hook" version = "0.3.17" @@ -2948,37 +2589,6 @@ dependencies = [ "tikv-jemalloc-sys", ] -[[package]] -name = "time" -version = "0.3.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" - -[[package]] -name = "time-macros" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" -dependencies = [ - "num-conv", - "time-core", -] - [[package]] name = "tinyvec" version = "1.6.0" @@ -3027,8 +2637,7 @@ dependencies = [ [[package]] name = "tokio-openssl" version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ffab79df67727f6acf57f1ff743091873c24c579b1e2ce4d8f53e47ded4d63d" +source = "git+https://github.com/james58899/tokio-openssl?rev=c0b49c9#c0b49c9052450a13d0c85538e8f36095106bd0f4" dependencies = [ "futures-util", "openssl", @@ -3128,6 +2737,53 @@ dependencies = [ "winnow", ] +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0da193277a4e2c33e59e09b5861580c33dd0a637c3883d0fa74ba40c0374af2e" +dependencies = [ + "bitflags 2.4.2", + "bytes", + "futures-util", + "http 1.0.0", + "http-body 1.0.0", + "http-body-util", + "http-range-header", + "httpdate", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + [[package]] name = "tower-service" version = "0.3.2" @@ -3142,21 +2798,9 @@ checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "log", "pin-project-lite", - "tracing-attributes", "tracing-core", ] -[[package]] -name = "tracing-attributes" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.50", -] - [[package]] name = "tracing-core" version = "0.1.32" @@ -3191,12 +2835,6 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "typenum" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" - [[package]] name = "unicase" version = "2.7.0" @@ -3262,12 +2900,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" -[[package]] -name = "v_htmlescape" -version = "0.15.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e8257fbc510f0a46eb602c10215901938b5c2a7d5e70fc11483b1d3c9b5b18c" - [[package]] name = "vcpkg" version = "0.2.15" @@ -3755,23 +3387,3 @@ dependencies = [ "once_cell", "pkg-config", ] - -[[package]] -name = "zerocopy" -version = "0.7.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.50", -] diff --git a/Cargo.toml b/Cargo.toml index ea849aa..e0ba4bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,11 +5,9 @@ edition = "2021" build = "build.rs" [dependencies] -actix-files = "0.6" -actix-tls = { version = "*", default-features = false, features = ["accept"] } -actix-web = { version = "4.5", default-features = false, features = ["macros", "openssl"] } -actix-web-lab = "0.20" +arc-swap = "1.6" async-stream = "0.3" +axum = { version = "0.7", default-features = false, features = ["http1", "matched-path", "tokio"] } bytes = "1.5" chrono = "0.4" clap = { version = "4.5", features = ["derive", "wrap_help"] } @@ -18,6 +16,7 @@ filesize = "0.2" filetime = "0.2" futures = "0.3" hex = "0.4" +http-body = "1.0" http-body-util = "0.1" hyper = { version = "1.2", features = ["client", "http1"] } hyper-util = { version = "0.1", features = ["tokio"] } @@ -28,14 +27,17 @@ once_cell = "1.19" openssl = { version = "*", features = ["vendored"] } parking_lot = { version = "0.12", features = ["hardware-lock-elision", "deadlock_detection"] } pin-project-lite = "0.2" -rand = { version = "0.8", default-features = false, features = ["small_rng"] } +rand = { version = "0.8", features = ["small_rng"] } regex = "1.10" reqwest = { version = "0.11", default-features = false, features = ["rustls-tls", "stream", "socks"] } scopeguard = "1.2" socket2 = "0.5" tempfile = "3.10" tokio = { version = "1", features = ["full", "parking_lot"] } +tokio-openssl = "0.6" tokio-stream = { version = "0.1", default-features = false, features = ["fs"] } +tower = { version = "0.4", features = ["util", "timeout"] } +tower-http = { version = "0.5", features = ["fs"] } [target.'cfg(not(any(target_env = "msvc", target_os = "macos")))'.dependencies] tikv-jemallocator = { version = "0.5", features = ["background_threads", "unprefixed_malloc_on_supported_platforms"] } @@ -52,6 +54,9 @@ tao = "0.26" built = { version = "0.7", features = ["git2", "chrono", "semver"] } openssl-src = { version = "300", features = ["weak-crypto"] } +[patch.crates-io] +tokio-openssl = { git = "https://github.com/james58899/tokio-openssl", rev = "c0b49c9" } + [profile.release] lto = true strip = true diff --git a/src/main.rs b/src/main.rs index a29254b..6357d03 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,23 +1,6 @@ #![windows_subsystem = "windows"] -use std::{ - collections::HashMap, - error::Error, - net::{Ipv4Addr, SocketAddrV4}, - ops::RangeInclusive, - path::Path, - sync::Arc, - time::Duration, -}; +use std::{collections::HashMap, error::Error, ops::RangeInclusive, path::Path, sync::Arc, time::Duration}; -use actix_tls::accept::openssl::TlsStream; -use actix_web::{ - dev::{Server, Service}, - http::{header, ConnectionType}, - middleware::DefaultHeaders, - rt::net::TcpStream, - web::{to, Data}, - App, HttpServer, -}; use clap::Parser; use futures::TryFutureExt; use inquire::{ @@ -26,11 +9,7 @@ use inquire::{ }; use log::{error, info, warn}; use once_cell::sync::Lazy; -use openssl::{ - pkcs12::ParsedPkcs12_2, - ssl::{ClientHelloResponse, SslAcceptor, SslAcceptorBuilder, SslMethod, SslOptions}, -}; -use parking_lot::{Mutex, RwLock}; +use parking_lot::Mutex; use regex::Regex; use reqwest::Proxy; use tempfile::TempPath; @@ -51,6 +30,7 @@ use crate::{ gallery_downloader::GalleryDownloader, logger::Logger, rpc::RPCClient, + server::Server, util::{create_dirs, create_http_client}, }; @@ -62,6 +42,7 @@ mod middleware; mod route; mod rpc; mod rpc_http_client; +mod server; mod util; #[cfg(not(target_env = "msvc"))] @@ -81,7 +62,7 @@ static VERSION: Lazy = Lazy::new(|| { built_info::BUILT_TIME_UTC ) }); -static CLIENT_VERSION: &str = "1.6.2"; +pub static CLIENT_VERSION: &str = "1.6.2"; static MAX_KEY_TIME_DRIFT: RangeInclusive = -300..=300; mod built_info { @@ -139,7 +120,7 @@ struct Args { type DownloadState = Mutex>>, Arc>)>>; -struct AppState { +pub struct AppState { runtime: Handle, reqwest: reqwest::Client, rpc: Arc, @@ -230,7 +211,9 @@ async fn main() -> Result<(), Box> { let has_proxy = proxy.is_some(); // Command channel let (tx, mut rx) = mpsc::channel::(1); - let (server, cert_changer) = create_server( + + info!("Starting HTTP server..."); + let server = Server::new( args.port.unwrap_or_else(|| init_settings.client_port()), client.get_cert().await.unwrap(), AppState { @@ -245,10 +228,6 @@ async fn main() -> Result<(), Box> { ); let server_handle = server.handle(); - // Http server loop - info!("Starting HTTP server..."); - tokio::spawn(server); - info!("Notifying the server that we have finished starting up the client..."); if client.connect_check(init_settings).await.is_none() { error!("Startup notification failed."); @@ -278,10 +257,7 @@ async fn main() -> Result<(), Box> { match command { Command::ReloadCert => { match client2.get_cert().await { - Some(cert) => match cert_changer.send(cert) { - Ok(_) => continue, // Cert update send - Err(_) => error!("Update SSL cert fail"), - }, + Some(cert) => server_handle.update_cert(cert), None => error!("Fetch SSL cert fail"), } @@ -361,8 +337,8 @@ async fn main() -> Result<(), Box> { job.abort(); } info!("Shutdown in progress - please wait"); - sleep(Duration::from_secs(20)).await; - server_handle.stop(true).await; + sleep(Duration::from_secs(15)).await; + server.shutdown().await; logger.shutdown().await; Ok(()) } @@ -435,120 +411,6 @@ After registering, enter your ID and Key below to start your client. Ok((id, key)) } -fn create_server(port: u16, cert: ParsedPkcs12_2, data: AppState) -> (Server, watch::Sender) { - let logger = middleware::Logger::default(); - let connection_counter = middleware::ConnectionCounter::new(data.rpc.settings(), data.command_channel.clone()); - let app_data = Data::new(data); - - // Cert changer - let (cert_sender, mut cert_receiver) = watch::channel(cert); - let ssl_context = Arc::new(RwLock::new(create_ssl_acceptor(&cert_receiver.borrow_and_update()).build())); - let ssl_context_write = ssl_context.clone(); - let mut ssl_acceptor = create_ssl_acceptor(&cert_receiver.clone().borrow_and_update()); - ssl_acceptor.set_client_hello_callback(move |ssl, _alert| { - ssl.set_ssl_context(ssl_context.read().context())?; - Ok(ClientHelloResponse::SUCCESS) - }); - tokio::spawn(async move { - while cert_receiver.changed().await.is_ok() { - *ssl_context_write.write() = create_ssl_acceptor(&cert_receiver.borrow()).build(); - } - }); - - let server = HttpServer::new(move || { - App::new() - .app_data(app_data.clone()) - .wrap(middleware::Timeout::new(Duration::from_secs(181))) - .wrap(logger.clone()) - .wrap(connection_counter.clone()) - .wrap(DefaultHeaders::new().add(( - header::SERVER, - format!("Genetic Lifeform and Distributed Open Server {CLIENT_VERSION}"), - ))) - .wrap_fn(|req, next| { - next.call(req).map_ok(|mut res| { - let head = res.response_mut().head_mut(); - head.set_connection_type(ConnectionType::Close); - head.set_camel_case_headers(true); - res - }) - }) - .default_service(to(route::default)) - .configure(route::configure) - }) - .disable_signals() - .shutdown_timeout(10) - .client_request_timeout(Duration::from_secs(15)) - .on_connect(|conn, _ext| { - if let Some(tcp) = conn.downcast_ref::>() { - tcp.get_ref().set_nodelay(true).unwrap(); - } - }) - .bind_openssl(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), port), ssl_acceptor) - .unwrap() - .run(); - - (server, cert_sender) -} - -fn create_ssl_acceptor(cert: &ParsedPkcs12_2) -> SslAcceptorBuilder { - let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls_server()).unwrap(); - builder.clear_options(SslOptions::NO_TLSV1_3); - builder.set_options(SslOptions::NO_RENEGOTIATION | SslOptions::ENABLE_MIDDLEBOX_COMPAT); - - // From https://wiki.mozilla.org/Security/Server_Side_TLS#Old_backward_compatibility - cpufeatures::new!(cpuid_aes, "aes"); - if !cpuid_aes::get() { - // Not have AES hardware acceleration, prefer ChaCha20. - builder - .set_cipher_list( - "@SECLEVEL=0:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:\ - ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:\ - ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:\ - DHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:\ - ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:\ - ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:\ - ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:\ - ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:\ - DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:\ - AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:\ - DES-CBC3-SHA", - ) - .unwrap(); - builder - .set_ciphersuites("TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384") - .unwrap(); - } else { - // Prioritize ChaCha ciphers when preferred by clients. - builder.set_options(SslOptions::PRIORITIZE_CHACHA); - - builder - .set_cipher_list( - "@SECLEVEL=0:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:\ - ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:\ - ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:\ - DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:\ - ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:\ - ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:\ - ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:\ - ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:\ - DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:\ - AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:\ - DES-CBC3-SHA", - ) - .unwrap(); - builder - .set_ciphersuites("TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256") - .unwrap(); - } - builder.set_private_key(cert.pkey.as_ref().unwrap()).unwrap(); - builder.set_certificate(cert.cert.as_ref().unwrap()).unwrap(); - if let Some(i) = &cert.ca { - i.iter().for_each(|j| builder.add_extra_chain_cert(j.to_owned()).unwrap()); - } - builder -} - #[cfg(unix)] async fn wait_shutdown_signal(mut shutdown_channel: UnboundedReceiver<()>) { use tokio::signal::unix::{signal, SignalKind}; diff --git a/src/middleware/connection_counter.rs b/src/middleware/connection_counter.rs index 60f30c5..bb27e8a 100644 --- a/src/middleware/connection_counter.rs +++ b/src/middleware/connection_counter.rs @@ -1,5 +1,4 @@ use std::{ - future::{ready, Ready}, pin::Pin, sync::{ atomic::{AtomicU64, Ordering}, @@ -8,20 +7,21 @@ use std::{ task::{Context, Poll}, }; -use actix_web::{ - body::{BodySize, MessageBody}, - dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform}, - web::Bytes, - Error, +use axum::{ + body::{Body, HttpBody}, + extract::Request, + response::Response, }; -use futures::future::LocalBoxFuture; +use futures::future::BoxFuture; +use http_body::{Frame, SizeHint}; use pin_project_lite::pin_project; use tokio::sync::mpsc::Sender; +use tower::{Layer, Service}; use crate::{rpc::Settings, Command}; #[derive(Clone)] -pub struct ConnectionCounter { +pub(super) struct ConnectionCounter { data: ConnectionCounterState, } @@ -44,44 +44,39 @@ impl ConnectionCounter { } } -impl Transform for ConnectionCounter -where - S: Service, Error = Error>, - S::Future: 'static, - B: MessageBody, -{ - type Response = ServiceResponse>; - type Error = Error; - type InitError = (); - type Transform = ConnectionCounterMiddleware; - type Future = Ready>; - - fn new_transform(&self, service: S) -> Self::Future { - ready(Ok(ConnectionCounterMiddleware { +impl Layer for ConnectionCounter { + type Service = ConnectionCounterMiddleware; + + fn layer(&self, service: S) -> Self::Service { + ConnectionCounterMiddleware { data: self.data.clone(), service, - })) + } } } -pub struct ConnectionCounterMiddleware { +#[derive(Clone)] +pub(super) struct ConnectionCounterMiddleware { data: ConnectionCounterState, service: S, } -impl Service for ConnectionCounterMiddleware +impl Service for ConnectionCounterMiddleware where - S: Service, Error = Error>, - S::Future: 'static, - B: MessageBody, + S: Service + Send + 'static, + S::Future: Send + 'static, { - type Response = ServiceResponse>; - type Error = Error; - type Future = LocalBoxFuture<'static, Result>; + type Response = Response; + + type Error = S::Error; - forward_ready!(service); + type Future = BoxFuture<'static, Result>; - fn call(&self, req: ServiceRequest) -> Self::Future { + fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { + self.service.poll_ready(ctx) + } + + fn call(&mut self, req: Request) -> Self::Future { let counter = self.data.counter.clone(); if counter.fetch_add(1, Ordering::Relaxed) > (self.data.settings.max_connection() as f64 * 0.8).ceil() as u64 { let _ = self.data.command_channel.try_send(Command::Overload); @@ -91,7 +86,7 @@ where Box::pin(async move { match fut.await { - Ok(res) => Ok(res.map_body(|_, body| ConnectionCounterFinalizer { body, counter })), + Ok(res) => Ok(res.map(|body| ConnectionCounterFinalizer { body, counter })), Err(err) => { counter.fetch_sub(1, Ordering::Relaxed); Err(err) @@ -102,29 +97,34 @@ where } pin_project! { - pub struct ConnectionCounterFinalizer { + pub(super) struct ConnectionCounterFinalizer { #[pin] - body: B, + body: Body, counter: Arc, } - impl PinnedDrop for ConnectionCounterFinalizer { + impl PinnedDrop for ConnectionCounterFinalizer { fn drop(this: Pin<&mut Self>) { this.counter.fetch_sub(1, Ordering::Relaxed); } } } -impl MessageBody for ConnectionCounterFinalizer { - type Error = B::Error; +impl HttpBody for ConnectionCounterFinalizer { + type Data = ::Data; - #[inline] - fn size(&self) -> BodySize { - self.body.size() - } + type Error = ::Error; - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>> { + fn poll_frame(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll, Self::Error>>> { let this = self.project(); - this.body.poll_next(cx) + this.body.poll_frame(cx) + } + + fn is_end_stream(&self) -> bool { + self.body.is_end_stream() + } + + fn size_hint(&self) -> SizeHint { + self.body.size_hint() } } diff --git a/src/middleware/logger.rs b/src/middleware/logger.rs index d7d94e8..1086f30 100644 --- a/src/middleware/logger.rs +++ b/src/middleware/logger.rs @@ -1,6 +1,6 @@ use std::{ cmp::max, - future::{ready, Ready}, + net::SocketAddr, pin::Pin, sync::{ atomic::{AtomicU64, Ordering}, @@ -10,22 +10,20 @@ use std::{ time::Instant, }; -use actix_web::{ - body::{ - BodySize::{self, Sized}, - MessageBody, - }, - dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform}, - http::Method, - web::Bytes, - Error, +use axum::{ + body::{Body, HttpBody}, + extract::{ConnectInfo, Request}, + http::{header::CONTENT_LENGTH, HeaderValue, Method}, + response::Response, }; -use futures::future::LocalBoxFuture; +use futures::future::BoxFuture; +use http_body::{Frame, SizeHint}; use log::info; use pin_project_lite::pin_project; +use tower::{Layer, Service}; #[derive(Clone)] -pub struct Logger { +pub(super) struct Logger { counter: Arc, } @@ -37,66 +35,69 @@ impl Default for Logger { } } -impl Transform for Logger -where - S: Service, Error = Error>, - S::Future: 'static, - B: MessageBody, -{ - type Response = ServiceResponse>; - type Error = Error; - type Transform = LoggerMiddleware; - type InitError = (); - type Future = Ready>; - - fn new_transform(&self, service: S) -> Self::Future { - ready(Ok(LoggerMiddleware { +impl Layer for Logger { + type Service = LoggerMiddleware; + + fn layer(&self, inner: S) -> Self::Service { + LoggerMiddleware { counter: self.counter.clone(), - service, - })) + service: inner, + } } } -pub struct LoggerMiddleware { +#[derive(Clone)] +pub(super) struct LoggerMiddleware { counter: Arc, service: S, } -impl Service for LoggerMiddleware +impl Service for LoggerMiddleware where - S: Service, Error = Error>, - S::Future: 'static, - B: MessageBody, + S: Service + Send + 'static, + S::Future: Send + 'static, { - type Response = ServiceResponse>; - type Error = Error; - type Future = LocalBoxFuture<'static, Result>; + type Response = Response; + + type Error = S::Error; - fn call(&self, req: ServiceRequest) -> Self::Future { + type Future = BoxFuture<'static, Result>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.service.poll_ready(cx) + } + + fn call(&mut self, req: Request) -> Self::Future { let start = Instant::now(); let count = self.counter.fetch_add(1, Ordering::Relaxed) + 1; - let ip = req.connection_info().peer_addr().unwrap_or("-").to_string(); + let ip = req + .extensions() + .get::>() + .map(|i| i.ip().to_string()) + .unwrap_or_else(|| "-".into()); let is_head = req.method() == Method::HEAD; - let request = if req.query_string().is_empty() { - format!("{} {} {:?}", req.method(), req.path(), req.version()) + let uri = req.uri(); + let request = if uri.query().is_none() { + format!("{} {} {:?}", req.method(), uri.path(), req.version()) } else { - format!("{} {}?{} {:?}", req.method(), req.path(), req.query_string(), req.version()) + format!("{} {}?{} {:?}", req.method(), uri.path(), uri.query().unwrap(), req.version()) }; + let fut = self.service.call(req); Box::pin(async move { let res = fut.await?; - let code = res.response().status().as_u16(); + let code = res.status().as_u16(); let mut size = 0; if !is_head { - if let Sized(i) = res.response().body().size() { - size = i; + if let Some(Ok(i)) = res.headers().get(CONTENT_LENGTH).map(HeaderValue::to_str) { + size = i.parse::().unwrap_or_default(); } } info!("{{{}/{:16} Code={} Byte={:<8} {}", count, ip.clone() + "}", code, size, request); - Ok(res.map_body(|_, body| LoggerFinalizer { + Ok(res.map(|body| LoggerFinalizer { body, count, ip, @@ -107,14 +108,12 @@ where })) }) } - - forward_ready!(service); } pin_project! { - pub struct LoggerFinalizer { + pub(super) struct LoggerFinalizer { #[pin] - body: B, + body: Body, count: u64, ip: String, code: u16, @@ -123,7 +122,7 @@ pin_project! { body_start: std::time::Instant, } - impl PinnedDrop for LoggerFinalizer { + impl PinnedDrop for LoggerFinalizer { fn drop(this: Pin<&mut Self>) { info!("{{{}/{:16} Code={} Byte={:<8} Finished processing request in {}ms ({:.2} KB/s)", this.count, @@ -137,16 +136,21 @@ pin_project! { } } -impl MessageBody for LoggerFinalizer { - type Error = B::Error; +impl HttpBody for LoggerFinalizer { + type Data = ::Data; - #[inline] - fn size(&self) -> BodySize { - self.body.size() - } + type Error = ::Error; - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>> { + fn poll_frame(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll, Self::Error>>> { let this = self.project(); - this.body.poll_next(cx) + this.body.poll_frame(cx) + } + + fn is_end_stream(&self) -> bool { + self.body.is_end_stream() + } + + fn size_hint(&self) -> SizeHint { + self.body.size_hint() } } diff --git a/src/middleware/mod.rs b/src/middleware/mod.rs index 5c54b9d..41f1c33 100644 --- a/src/middleware/mod.rs +++ b/src/middleware/mod.rs @@ -1,7 +1,44 @@ mod connection_counter; mod logger; -mod timeout; -pub use self::connection_counter::ConnectionCounter; -pub use self::logger::Logger; -pub use self::timeout::Timeout; +use std::{sync::Arc, time::Duration}; + +use axum::{ + error_handling::HandleErrorLayer, + http::{ + header::{CONNECTION, SERVER}, + HeaderValue, StatusCode, + }, + middleware, + response::Response, + Router, +}; +use once_cell::sync::Lazy; +use tower::{timeout::TimeoutLayer, ServiceBuilder}; + +use crate::{AppState, CLIENT_VERSION}; + +use self::connection_counter::ConnectionCounter; +use self::logger::Logger; + +static SERVER_HEADER: Lazy = + Lazy::new(|| HeaderValue::from_maybe_shared(format!("Genetic Lifeform and Distributed Open Server {CLIENT_VERSION}")).unwrap()); + +pub fn register_layer(router: Router>, data: &AppState) -> Router> { + router + .layer( + ServiceBuilder::new() + .layer(HandleErrorLayer::new(|_| async { StatusCode::SERVICE_UNAVAILABLE })) + .layer(TimeoutLayer::new(Duration::from_secs(181))), + ) + .layer(Logger::default()) + .layer(ConnectionCounter::new(data.rpc.settings(), data.command_channel.clone())) + .layer(middleware::map_response(default_headers)) +} + +async fn default_headers(mut response: Response) -> Response { + let headers = response.headers_mut(); + headers.insert(SERVER, SERVER_HEADER.clone()); + headers.insert(CONNECTION, "close".try_into().unwrap()); + response +} diff --git a/src/middleware/timeout.rs b/src/middleware/timeout.rs deleted file mode 100644 index e44fc00..0000000 --- a/src/middleware/timeout.rs +++ /dev/null @@ -1,64 +0,0 @@ -use std::time::Duration; - -use actix_web::{ - body::MessageBody, - dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform}, - error::ErrorServiceUnavailable, - Error, -}; -use futures::future::{ready, LocalBoxFuture, Ready}; -use tokio::time::timeout; - -pub struct Timeout { - timeout: Duration, -} - -impl Timeout { - pub fn new(timeout: Duration) -> Self { - Self { timeout } - } -} - -impl Transform for Timeout -where - S: Service, Error = Error>, - S::Future: 'static, - B: MessageBody, -{ - type Response = ServiceResponse; - type Error = Error; - type Transform = TimeoutMiddleware; - type InitError = (); - type Future = Ready>; - - fn new_transform(&self, service: S) -> Self::Future { - ready(Ok(TimeoutMiddleware { - service, - timeout: self.timeout, - })) - } -} - -pub struct TimeoutMiddleware { - service: S, - timeout: Duration, -} - -impl Service for TimeoutMiddleware -where - S: Service, Error = Error>, - S::Future: 'static, - B: MessageBody, -{ - type Response = ServiceResponse; - type Error = Error; - type Future = LocalBoxFuture<'static, Result>; - - forward_ready!(service); - - fn call(&self, req: ServiceRequest) -> Self::Future { - let duration = self.timeout; - let fut = self.service.call(req); - Box::pin(async move { timeout(duration, fut).await.map_err(ErrorServiceUnavailable)? }) - } -} diff --git a/src/route/cache.rs b/src/route/cache.rs index e9a44e3..a648337 100644 --- a/src/route/cache.rs +++ b/src/route/cache.rs @@ -1,16 +1,17 @@ use std::{io::SeekFrom, ops::RangeInclusive, sync::Arc, time::Duration}; -use actix_files::NamedFile; -use actix_web::{ - body::SizedStream, - http::header::{ContentDisposition, ContentType, DispositionParam, DispositionType::Inline, HeaderName, HeaderValue, CACHE_CONTROL}, - route, - web::{BytesMut, Data}, - HttpRequest, HttpResponse, Responder, -}; -use actix_web_lab::extract::Path; use async_stream::stream; -use futures::{FutureExt, StreamExt}; +use axum::{ + body::Body, + extract::{Path, Request, State}, + http::{ + header::{CACHE_CONTROL, CONTENT_DISPOSITION, CONTENT_LENGTH, CONTENT_TYPE}, + HeaderName, HeaderValue, + }, + response::{IntoResponse, Response}, +}; +use bytes::BytesMut; +use futures::StreamExt; use log::error; use openssl::sha::Sha1; use tokio::{ @@ -19,10 +20,12 @@ use tokio::{ sync::watch, time::timeout, }; +use tower::util::ServiceExt; +use tower_http::services::ServeFile; use crate::{ cache_manager::CacheFileInfo, - route::{forbidden, parse_additional}, + route::{forbidden, not_found, parse_additional}, util::{create_http_client, string_to_hash}, AppState, }; @@ -31,20 +34,19 @@ const TTL: RangeInclusive = -900..=900; // Token TTL 15 minutes #[allow(clippy::declare_interior_mutable_const)] const CACHE_HEADER: (HeaderName, HeaderValue) = (CACHE_CONTROL, HeaderValue::from_static("public, max-age=31536000")); -#[route("/h/{fileid}/{additional}/{filename:.*}", method = "GET", method = "HEAD")] -async fn hath( - req: HttpRequest, +pub(super) async fn hath( Path((file_id, additional, file_name)): Path<(String, String, String)>, - data: Data, -) -> impl Responder { + data: State>, + req: Request, +) -> impl IntoResponse { let additional = parse_additional(&additional); let mut keystamp = additional.get("keystamp").unwrap_or(&"").split('-'); let file_index = additional.get("fileindex").unwrap_or(&""); let xres = additional.get("xres").unwrap_or(&""); - let content_disposition = ContentDisposition { - disposition: Inline, - parameters: vec![DispositionParam::Filename(file_name)], - }; + let content_disposition = ( + CONTENT_DISPOSITION, + HeaderValue::from_maybe_shared(format!("inline; filename=\"{file_name}\"")).unwrap(), + ); // keystamp check let time = keystamp.next().unwrap_or_default(); @@ -58,21 +60,14 @@ async fn hath( // Check cache hit let info = match CacheFileInfo::from_file_id(&file_id) { Some(info) => info, - None => return HttpResponse::NotFound().body("An error has occurred. (404)"), + None => return not_found(), }; - if let Some(file) = data - .cache_manager - .get_file(&info) - .then(|f| async move { tokio::task::spawn_blocking(|| NamedFile::open(f?).ok()).await.ok().flatten() }) - .await - { - let mut res = file - .use_etag(false) - .set_content_disposition(content_disposition) - .set_content_type(info.mime_type()) - .into_response(&req); - res.headers_mut().insert(CACHE_HEADER.0, CACHE_HEADER.1); - return res; + if let Some(path) = data.cache_manager.get_file(&info).await { + let mut res = ServeFile::new_with_mime(path, &info.mime_type()).oneshot(req).await.unwrap(); + let header = res.headers_mut(); + header.insert(CACHE_HEADER.0, CACHE_HEADER.1); + header.insert(content_disposition.0, content_disposition.1); + return res.map(Body::new); } // Cache miss, proxy request @@ -96,7 +91,7 @@ async fn hath( if let Err(err) = tempfile { error!("Waiting tempfile create error: {}", err); data.download_state.lock().remove(&info.hash()); - return HttpResponse::NotFound().body("An error has occurred. (404)"); + return not_found(); } (tempfile.unwrap().as_ref().unwrap().clone(), progress.subscribe()) } else { @@ -111,9 +106,7 @@ async fn hath( let sources = match data.rpc.sr_fetch(file_index, xres, &file_id).await { Some(v) => v, - None => { - return HttpResponse::NotFound().body("An error has occurred. (404)"); - } + None => return not_found(), }; // Download worker @@ -209,16 +202,15 @@ async fn hath( // Wait download start or 404 if *rx.borrow() == 0 && rx.changed().await.is_err() { - return HttpResponse::NotFound().body("An error has occurred. (404)"); + return not_found(); } - let mut builder = HttpResponse::Ok(); - builder.insert_header(ContentType(info.mime_type())); - builder.insert_header(CACHE_HEADER); - builder.insert_header(content_disposition); - builder.body(SizedStream::new( - file_size, - stream! { + Response::builder() + .header(CONTENT_LENGTH, file_size) + .header(CONTENT_TYPE, HeaderValue::from_maybe_shared(info.mime_type().to_string()).unwrap()) + .header(CACHE_HEADER.0, CACHE_HEADER.1) + .header(content_disposition.0, content_disposition.1) + .body(Body::from_stream(stream! { let mut file = File::open(temp_path.as_ref()).await.unwrap(); let mut read_off = 0; let mut write_off = *rx.borrow(); @@ -242,6 +234,6 @@ async fn hath( } } } - }, - )) + })) + .unwrap() } diff --git a/src/route/mod.rs b/src/route/mod.rs index e42699d..f0a895a 100644 --- a/src/route/mod.rs +++ b/src/route/mod.rs @@ -1,46 +1,50 @@ -use std::collections::HashMap; +use std::{collections::HashMap, sync::Arc}; -use actix_web::{http::header::ContentType, route, web::ServiceConfig, HttpResponse, Responder}; -use reqwest::header::LOCATION; +use axum::{ + body::Body, + http::{header::LOCATION, Response, StatusCode}, + response::{Html, IntoResponse}, + routing::get, + Router, +}; -use self::{cache::hath, server_command::servercmd, speed_test::speedtest}; +use crate::{ + route::{cache::hath, server_command::servercmd, speed_test::speedtest}, + AppState, +}; mod cache; mod server_command; mod speed_test; -pub fn configure(cfg: &mut ServiceConfig) { - cfg.service(favicon) - .service(robots) - .service(servercmd) - .service(hath) - .service(speedtest); +pub fn register_route(router: Router>) -> Router> { + router + .route("/favicon.ico", get(favicon).head(favicon)) + .route("/robots.txt", get(robots).head(robots)) + .route("/servercmd/:command/:additional/:time/:key", get(servercmd).head(servercmd)) + .route("/t/:size/:time/:hash/:random", get(speedtest).head(speedtest)) + .route("/h/:fileid/:additional/*filename", get(hath).head(hath)) + .fallback(get(default).head(default)) } -pub async fn default() -> impl Responder { - HttpResponse::NotFound() - .content_type(ContentType::html()) - .body("An error has occurred. (404)") +pub async fn default() -> impl IntoResponse { + (StatusCode::NOT_FOUND, Html("An error has occurred. (404)")) } -#[route("/favicon.ico", method = "GET", method = "HEAD")] -async fn favicon() -> impl Responder { - HttpResponse::MovedPermanently() - .insert_header((LOCATION, "https://e-hentai.org/favicon.ico")) - .finish() +async fn favicon() -> impl IntoResponse { + (StatusCode::MOVED_PERMANENTLY, [(LOCATION, "https://e-hentai.org/favicon.ico")]) } -#[route("/robots.txt", method = "GET", method = "HEAD")] -async fn robots() -> impl Responder { - HttpResponse::Ok() - .content_type(ContentType::plaintext()) - .body("User-agent: *\nDisallow: /") +async fn robots() -> impl IntoResponse { + "User-agent: *\nDisallow: /" } -fn forbidden() -> HttpResponse { - HttpResponse::Forbidden() - .content_type(ContentType::html()) - .body("An error has occurred. (403)") +fn forbidden() -> Response { + (StatusCode::FORBIDDEN, Html("An error has occurred. (403)")).into_response() +} + +fn not_found() -> Response { + (StatusCode::NOT_FOUND, "An error has occurred. (404)").into_response() } fn parse_additional(additional: &str) -> HashMap<&str, &str> { diff --git a/src/route/server_command.rs b/src/route/server_command.rs index 487150a..5ed555b 100644 --- a/src/route/server_command.rs +++ b/src/route/server_command.rs @@ -1,10 +1,15 @@ use std::{ cmp::max, + net::SocketAddr, + sync::Arc, time::{Duration, Instant}, }; -use actix_web::{route, web::Data, HttpRequest, HttpResponse, Responder}; -use actix_web_lab::extract::Path; +use axum::{ + extract::{ConnectInfo, Path, State}, + http::StatusCode, + response::{IntoResponse, Response}, +}; use futures::TryStreamExt; use log::debug; use rand::{prelude::SmallRng, Rng, SeedableRng}; @@ -19,19 +24,13 @@ use crate::{ AppState, Command, MAX_KEY_TIME_DRIFT, }; -#[route("/servercmd/{command}/{additional:[^/]*}/{time}/{key}", method = "GET", method = "HEAD")] -async fn servercmd( - req: HttpRequest, +pub(super) async fn servercmd( + ConnectInfo(addr): ConnectInfo, Path((command, additional, time, hash)): Path<(String, String, i64, String)>, - data: Data, -) -> impl Responder { + data: State>, +) -> impl IntoResponse { // Server IP check - if !req - .connection_info() - .peer_addr() - .map(|ip| data.rpc.is_vaild_rpc_server(ip)) - .unwrap_or(false) - { + if !data.rpc.is_vaild_rpc_server(&addr.ip().to_string()) { debug!("Got a servercmd from an unauthorized IP address"); return forbidden(); } @@ -46,7 +45,7 @@ async fn servercmd( } match command.to_lowercase().as_str() { - "still_alive" => HttpResponse::Ok().body("I feel FANTASTIC and I'm still alive"), + "still_alive" => "I feel FANTASTIC and I'm still alive".into_response(), "threaded_proxy_test" => { let additional = parse_additional(&additional); @@ -64,7 +63,7 @@ async fn servercmd( ); if host.is_empty() || port == 0 || size == 0 || count == 0 || timestamp == 0 || token.is_empty() { - return HttpResponse::BadRequest().finish(); + return StatusCode::BAD_REQUEST.into_response(); } // Switch to MT tokio runtime @@ -134,7 +133,7 @@ async fn servercmd( let ms = total_time.as_millis(); let speed = (size * success) as f64 / ms.checked_div(success as u128).unwrap_or(1) as f64; debug!("Speedtest result: success {}/{}, speed {:.2} KB/s", success, count, speed); - HttpResponse::Ok().body(format!("OK:{}-{}", success, ms)) + format!("OK:{}-{}", success, ms).into_response() } "speed_test" => random_response( parse_additional(&additional) @@ -144,16 +143,16 @@ async fn servercmd( ), "refresh_settings" => { let _ = data.command_channel.send(Command::RefreshSettings).await; // Ignore error - HttpResponse::Ok().finish() + Response::default() } "start_downloader" => { let _ = data.command_channel.send(Command::StartDownloader).await; // Ignore error - HttpResponse::Ok().finish() + Response::default() } "refresh_certs" => { let _ = data.command_channel.send(Command::ReloadCert).await; // Ignore error - HttpResponse::Ok().finish() + Response::default() } - _ => HttpResponse::Ok().body("INVALID_COMMAND"), + _ => "INVALID_COMMAND".into_response(), } } diff --git a/src/route/speed_test.rs b/src/route/speed_test.rs index ad9336c..ff5ffa1 100644 --- a/src/route/speed_test.rs +++ b/src/route/speed_test.rs @@ -1,20 +1,19 @@ -use std::{cmp, convert::Infallible}; +use std::{cmp, convert::Infallible, sync::Arc}; -use actix_web::{ - body::SizedStream, - route, - web::{Bytes, Data}, - HttpResponse, Responder, -}; -use actix_web_lab::extract::Path; use async_stream::stream; +use axum::{ + body::Body, + extract::{Path, State}, + http::header::CONTENT_LENGTH, + response::Response, +}; +use bytes::Bytes; use rand::{prelude::SmallRng, RngCore, SeedableRng}; use crate::{route::forbidden, util::string_to_hash, AppState, MAX_KEY_TIME_DRIFT}; // example: /t/5242880/1645930666/bce541b2a97788319e53a754b47e1801204ae7bf/43432228 -#[route("/t/{size}/{time}/{hash}/{random}", method = "GET", method = "HEAD")] -async fn speedtest(Path((size, time, hash)): Path<(u64, i64, String)>, data: Data) -> impl Responder { +pub(super) async fn speedtest(Path((size, time, hash)): Path<(u64, i64, String)>, data: State>) -> Response { // Check time & hash let hash_string = format!("hentai@home-speedtest-{}-{}-{}-{}", size, time, data.rpc.id(), data.rpc.key()); if !MAX_KEY_TIME_DRIFT.contains(&(data.rpc.get_timestemp() - time)) || string_to_hash(hash_string) != hash { @@ -24,10 +23,10 @@ async fn speedtest(Path((size, time, hash)): Path<(u64, i64, String)>, data: Dat random_response(size) } -pub(super) fn random_response(size: u64) -> HttpResponse { - HttpResponse::Ok().body(SizedStream::new( - size, - stream! { +pub(super) fn random_response(size: u64) -> Response { + Response::builder() + .header(CONTENT_LENGTH, size) + .body(Body::from_stream(stream! { let mut buffer = [0; 8192]; SmallRng::from_entropy().fill_bytes(&mut buffer); let buffer = Bytes::copy_from_slice(&buffer); @@ -38,6 +37,6 @@ pub(super) fn random_response(size: u64) -> HttpResponse { yield Result::Ok::(buffer.slice(0..size)); filled += size as u64; } - }, - )) + })) + .unwrap() } diff --git a/src/server/mod.rs b/src/server/mod.rs new file mode 100644 index 0000000..de9c71f --- /dev/null +++ b/src/server/mod.rs @@ -0,0 +1,185 @@ +use std::{ + net::SocketAddr, + pin::Pin, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, + time::Duration, +}; + +use arc_swap::ArcSwap; +use axum::Router; +use futures::pin_mut; +use hyper::server::conn::http1; +use hyper_util::rt::{TokioIo, TokioTimer}; +use log::info; +use openssl::{ + pkcs12::ParsedPkcs12_2, + ssl::{Ssl, SslAcceptor}, +}; +use tokio::{ + io::AsyncWriteExt, + net::{TcpListener, TcpSocket, TcpStream}, + sync::{watch, Notify}, + task::JoinHandle, + time::timeout, +}; +use tokio_openssl::SslStream; +use tower::Service; + +use crate::{middleware, route, AppState}; + +use self::ssl::create_ssl_acceptor; + +pub mod ssl; + +pub struct Server { + handle: Arc, + task: JoinHandle<()>, +} + +pub struct ServerHandle { + shutdown: AtomicBool, + shutdown_notify: Notify, + ssl_acceptor: ArcSwap, +} + +impl Server { + pub fn new(port: u16, cert: ParsedPkcs12_2, data: AppState) -> Self { + let handle = Arc::new(ServerHandle::new(create_ssl_acceptor(&cert))); + let mut listener = bind(SocketAddr::from(([0, 0, 0, 0], port))); + + let mut builder = http1::Builder::new(); + builder + .header_read_timeout(Duration::from_secs(15)) + .timer(TokioTimer::new()) + .keep_alive(false) + .title_case_headers(true) + .writev(true) + .max_buf_size(8192); + + let mut router = Router::new(); + router = route::register_route(router); + router = middleware::register_layer(router, &data); + + let service = router + .with_state(Arc::new(data)) + .into_make_service_with_connect_info::(); + + let handle2 = handle.clone(); + // Accept loop + let task = tokio::spawn(async move { + let (shutdown_tx, _) = watch::channel(()); + loop { + if handle.shutdown.load(Ordering::Relaxed) { + info!("Accept loop stopped!"); + break; + } + + // Wait for new tcp connection + let (stream, addr) = tokio::select! { + biased; + result = accept(&mut listener) => result, + _ = handle.shutdown_notify.notified() => break, // Shutdown + }; + + // Process connection + let handle = handle.clone(); + let builder = builder.clone(); + let app = service.clone().call(addr).await.unwrap(); + let mut shutdown_rx = shutdown_tx.subscribe(); + tokio::spawn(async move { + // TLS handshake + let mut ssl_stream = SslStream::new(Ssl::new(handle.ssl_acceptor.load().context()).unwrap(), stream).unwrap(); + match timeout(Duration::from_secs(10), SslStream::accept(Pin::new(&mut ssl_stream))).await { + Ok(Ok(_)) => (), + _ => return, // Handshake timeout or error + } + + // First byte timeout + if (timeout(Duration::from_secs(10), SslStream::peek(Pin::new(&mut ssl_stream), &mut [0])).await).is_err() { + let _ = ssl_stream.shutdown().await; + return; + } + + // Process request + let stream = TokioIo::new(ssl_stream); // Tokio to hyper trait + let service = hyper::service::service_fn(move |r| app.clone().call(r)); // Tower to hyper trait + let fut = timeout(Duration::from_secs(300), builder.serve_connection(stream, service)); + pin_mut!(fut); + tokio::select! { + biased; + _ = &mut fut => (), + _ = shutdown_rx.changed() => { // Graceful shutdown + let _ = timeout(Duration::from_secs(10), fut).await; + }, + }; + }); + } + + // Close listener + drop(listener); + + // Wait connection complated or timeout + shutdown_tx.send_replace(()); // Notify all connection + let _ = timeout(Duration::from_secs(15), shutdown_tx.closed()).await; + }); + + Self { handle: handle2, task } + } + + pub fn handle(&self) -> Arc { + self.handle.clone() + } + + /// Graceful shutdown and wait server loop stop. + pub async fn shutdown(self) { + self.handle.shutdown(); + let _ = self.task.await; + } +} + +impl ServerHandle { + fn new(ssl: SslAcceptor) -> Self { + Self { + shutdown: AtomicBool::new(false), + shutdown_notify: Notify::new(), + ssl_acceptor: ArcSwap::new(Arc::new(ssl)), + } + } + + pub fn update_cert(&self, cert: ParsedPkcs12_2) { + let acceptor = create_ssl_acceptor(&cert); + self.ssl_acceptor.store(Arc::new(acceptor)); + } + + fn shutdown(&self) { + self.shutdown.store(true, Ordering::Relaxed); + self.shutdown_notify.notify_waiters(); + } +} + +fn bind(addr: SocketAddr) -> TcpListener { + let socket = match addr { + SocketAddr::V4(_) => TcpSocket::new_v4(), + SocketAddr::V6(_) => TcpSocket::new_v6(), + } + .expect("Server listener socket create error."); + + let _ = socket.set_reuseaddr(true); // Without this need wait all connection close before restart + let _ = socket.set_keepalive(true); + let _ = socket.set_nodelay(true); + + socket.bind(addr).expect("Server listener socket bind error."); + socket.listen(512).expect("Server listener socket listen error.") +} + +async fn accept(listener: &mut TcpListener) -> (TcpStream, SocketAddr) { + loop { + match listener.accept().await { + Ok(value) => return value, + Err(_) => tokio::time::sleep(Duration::from_millis(50)).await, // Too many open files, retry. + } + } +} diff --git a/src/server/ssl.rs b/src/server/ssl.rs new file mode 100644 index 0000000..61eb5c1 --- /dev/null +++ b/src/server/ssl.rs @@ -0,0 +1,65 @@ +use openssl::{ + pkcs12::ParsedPkcs12_2, + ssl::{SslAcceptor, SslMethod, SslMode, SslOptions, SslSessionCacheMode}, +}; + +pub fn create_ssl_acceptor(cert: &ParsedPkcs12_2) -> SslAcceptor { + // TODO error handle + let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls_server()).unwrap(); + builder.clear_options(SslOptions::NO_TLSV1_3); + builder.set_options(SslOptions::NO_RENEGOTIATION | SslOptions::ENABLE_MIDDLEBOX_COMPAT); + builder.set_mode(SslMode::RELEASE_BUFFERS); + builder.set_session_cache_mode(SslSessionCacheMode::OFF); // Disable session ID resumption + + // From https://wiki.mozilla.org/Security/Server_Side_TLS#Old_backward_compatibility + cpufeatures::new!(cpuid_aes, "aes"); + if !cpuid_aes::get() { + // Not have AES hardware acceleration, prefer ChaCha20. + builder + .set_cipher_list( + "@SECLEVEL=0:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:\ + ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:\ + ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:\ + DHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:\ + ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:\ + ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:\ + ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:\ + ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:\ + DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:\ + AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:\ + DES-CBC3-SHA", + ) + .unwrap(); + builder + .set_ciphersuites("TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384") + .unwrap(); + } else { + // Prioritize ChaCha ciphers when preferred by clients. + builder.set_options(SslOptions::PRIORITIZE_CHACHA); + + builder + .set_cipher_list( + "@SECLEVEL=0:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:\ + ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:\ + ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:\ + DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:\ + ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:\ + ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:\ + ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:\ + ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:\ + DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:\ + AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:\ + DES-CBC3-SHA", + ) + .unwrap(); + builder + .set_ciphersuites("TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256") + .unwrap(); + } + builder.set_private_key(cert.pkey.as_ref().unwrap()).unwrap(); + builder.set_certificate(cert.cert.as_ref().unwrap()).unwrap(); + if let Some(i) = &cert.ca { + i.iter().for_each(|j| builder.add_extra_chain_cert(j.to_owned()).unwrap()); + } + builder.build() +}