From 4290e21c76e2904a489be14fa08878aeafac141b Mon Sep 17 00:00:00 2001 From: Jose Quintana Date: Thu, 29 Apr 2021 23:00:49 +0200 Subject: [PATCH] refactor: http1 with static files part of #37 --- Cargo.lock | 432 +++++++++++++++++++++++++++++------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Cargo.toml | 16 ++++++++-------- README.md | 2 +- src/cache.rs | 38 -------------------------------------- src/compression.rs | 19 ------------------- src/controller.rs | 38 ++++++++++++++++++++++++++++++++++++++ src/cors.rs | 36 ------------------------------------ src/error_page.rs | 4 ++++ src/fs.rs | 382 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 8 +++----- src/rejection.rs | 46 ---------------------------------------------- src/server.rs | 161 +++++++++++++++++++++++++++++++++++++++-------------------------------------------------------------------------------------------------------------------------- src/signals.rs | 51 --------------------------------------------------- 13 files changed, 504 insertions(+), 729 deletions(-) delete mode 100644 src/cache.rs delete mode 100644 src/compression.rs create mode 100644 src/controller.rs delete mode 100644 src/cors.rs create mode 100644 src/error_page.rs create mode 100644 src/fs.rs delete mode 100644 src/rejection.rs delete mode 100644 src/signals.rs diff --git a/Cargo.lock b/Cargo.lock index a4d5eb2..1d8ce00 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,27 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "alloc-no-stdlib" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5192ec435945d87bc2f70992b4d818154b5feede43c09fb7592146374eac90a6" - -[[package]] -name = "alloc-stdlib" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "697ed7edc0f1711de49ce108c541623a0af97c6c60b2f6e2b65229847ac843c2" -dependencies = [ - "alloc-no-stdlib", -] - -[[package]] name = "ansi_term" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -37,20 +16,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b" [[package]] -name = "async-compression" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443ccbb270374a2b1055fc72da40e1f237809cd6bb0e97e66d264cd138473a6" -dependencies = [ - "brotli", - "flate2", - "futures-core", - "memchr", - "pin-project-lite", - "tokio", -] - -[[package]] name = "autocfg" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -78,33 +43,6 @@ dependencies = [ ] [[package]] -name = "brotli" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f29919120f08613aadcd4383764e00526fc9f18b6c0895814faeed0dd78613e" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", - "brotli-decompressor", -] - -[[package]] -name = "brotli-decompressor" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1052e1c3b8d4d80eb84a8b94f0a1498797b5fb96314c001156a1c761940ef4ec" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", -] - -[[package]] -name = "bumpalo" -version = "3.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe" - -[[package]] name = "byteorder" version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -124,12 +62,6 @@ checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" [[package]] name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - -[[package]] -name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" @@ -164,15 +96,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" [[package]] -name = "crc32fast" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" -dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] name = "digest" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -182,40 +105,12 @@ dependencies = [ ] [[package]] -name = "either" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" - -[[package]] -name = "flate2" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0" -dependencies = [ - "cfg-if 1.0.0", - "crc32fast", - "libc", - "miniz_oxide", -] - -[[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] -name = "form_urlencoded" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" -dependencies = [ - "matches", - "percent-encoding", -] - -[[package]] name = "fs_extra" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -293,41 +188,16 @@ dependencies = [ ] [[package]] -name = "h2" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc018e188373e2777d0ef2467ebff62a08e66c3f5857b23c8fbec3018210dc00" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" - -[[package]] name = "headers" version = "0.3.4" -source = "git+https://github.com/joseluisq/hyper-headers.git?branch=headers_encoding#ca704fcb605adf33f327d0f5a41d5072606058a1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0b7591fb62902706ae8e7aaff416b1b0fa2c0fd0878b46dc13baa3712d8a855" dependencies = [ "base64", "bitflags", "bytes", "headers-core", "http", - "itertools", "mime", "sha-1", "time", @@ -336,7 +206,8 @@ dependencies = [ [[package]] name = "headers-core" version = "0.2.0" -source = "git+https://github.com/joseluisq/hyper-headers.git?branch=headers_encoding#ca704fcb605adf33f327d0f5a41d5072606058a1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" dependencies = [ "http", ] @@ -403,7 +274,6 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", "http", "http-body", "httparse", @@ -418,25 +288,6 @@ dependencies = [ ] [[package]] -name = "indexmap" -version = "1.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" -dependencies = [ - "autocfg", - "hashbrown", -] - -[[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.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -464,15 +315,6 @@ dependencies = [ ] [[package]] -name = "js-sys" -version = "0.3.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d99f9e3e84b8f67f846ef5b4cbbc3b1c29f6c759fcbce6f01aa0e73d932a24c" -dependencies = [ - "wasm-bindgen", -] - -[[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -490,7 +332,7 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -503,12 +345,6 @@ dependencies = [ ] [[package]] -name = "matches" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" - -[[package]] name = "memchr" version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -531,16 +367,6 @@ dependencies = [ ] [[package]] -name = "miniz_oxide" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" -dependencies = [ - "adler", - "autocfg", -] - -[[package]] name = "mio" version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -563,19 +389,6 @@ dependencies = [ ] [[package]] -name = "nix" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce" -dependencies = [ - "bitflags", - "cc", - "cfg-if 0.1.10", - "libc", - "void", -] - -[[package]] name = "ntapi" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -731,56 +544,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548" [[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin", - "untrusted", - "web-sys", - "winapi", -] - -[[package]] -name = "rustls" -version = "0.19.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" -dependencies = [ - "base64", - "log", - "ring", - "sct", - "webpki", -] - -[[package]] name = "ryu" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" [[package]] -name = "scoped-tls" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" - -[[package]] -name = "sct" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] name = "serde" version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -798,25 +567,13 @@ dependencies = [ ] [[package]] -name = "serde_urlencoded" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] name = "sha-1" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfebf75d25bd900fd1e7d11501efab59bc846dbc76196839663e6637bba9f25f" dependencies = [ "block-buffer", - "cfg-if 1.0.0", + "cfg-if", "cpuid-bool", "digest", "opaque-debug", @@ -832,22 +589,15 @@ dependencies = [ ] [[package]] -name = "signal" -version = "0.7.0" +name = "signal-hook-registry" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f6ce83b159ab6984d2419f495134972b48754d13ff2e3f8c998339942b56ed9" +checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6" dependencies = [ "libc", - "nix", ] [[package]] -name = "slab" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527" - -[[package]] name = "smallvec" version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -864,26 +614,24 @@ dependencies = [ ] [[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] name = "static-web-server" version = "2.0.0-beta.3" dependencies = [ "anyhow", + "bytes", + "futures", + "headers", + "hyper", "jemallocator", - "nix", + "mime_guess", "num_cpus", "once_cell", - "signal", + "percent-encoding", "structopt", "tokio", + "tokio-util", "tracing", "tracing-subscriber", - "warp", ] [[package]] @@ -912,9 +660,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.70" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9505f307c872bab8eb46f77ae357c8eba1fdacead58ee5a850116b1d7f82883" +checksum = "ad184cc9470f9117b2ac6817bfe297307418819ba40552f9b3846f05c33d5373" dependencies = [ "proc-macro2", "quote", @@ -962,29 +710,22 @@ dependencies = [ "memchr", "mio", "num_cpus", + "once_cell", "pin-project-lite", + "signal-hook-registry", + "tokio-macros", + "winapi", ] [[package]] -name = "tokio-rustls" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" -dependencies = [ - "rustls", - "tokio", - "webpki", -] - -[[package]] -name = "tokio-stream" -version = "0.1.5" +name = "tokio-macros" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e177a5d8c3bf36de9ebe6d58537d8879e964332f93fb3339e43f618c81361af0" +checksum = "caf7b11a536f46a809a8a9f0bb4237020f70ecbf115b842360afb127ea2fda57" dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -1013,8 +754,7 @@ version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01ebdc2bb4498ab1ab5f5b73c5803825e60199229ccba0698170e3be0e7f959f" dependencies = [ - "cfg-if 1.0.0", - "log", + "cfg-if", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -1123,24 +863,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" [[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - -[[package]] name = "version_check" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" [[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" - -[[package]] name = "want" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1151,114 +879,12 @@ dependencies = [ ] [[package]] -name = "warp" -version = "0.3.1" -source = "git+https://github.com/joseluisq/warp.git?branch=0.3.x#f638f8958addb953501a08d427aae64a4c4f5a21" -dependencies = [ - "async-compression", - "bytes", - "futures", - "headers", - "http", - "hyper", - "log", - "mime", - "mime_guess", - "percent-encoding", - "pin-project", - "scoped-tls", - "serde", - "serde_json", - "serde_urlencoded", - "tokio", - "tokio-rustls", - "tokio-stream", - "tokio-util", - "tower-service", - "tracing", -] - -[[package]] name = "wasi" version = "0.10.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" [[package]] -name = "wasm-bindgen" -version = "0.2.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83240549659d187488f91f33c0f8547cbfef0b2088bc470c116d1d260ef623d9" -dependencies = [ - "cfg-if 1.0.0", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae70622411ca953215ca6d06d3ebeb1e915f0f6613e3b495122878d7ebec7dae" -dependencies = [ - "bumpalo", - "lazy_static", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e734d91443f177bfdb41969de821e15c516931c3c3db3d318fa1b68975d0f6f" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53739ff08c8a68b0fdbcd54c372b8ab800b1449ab3c9d706503bc7dd1621b2c" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9a543ae66aa233d14bb765ed9af4a33e81b8b58d1584cf1b47ff8cd0b9e4489" - -[[package]] -name = "web-sys" -version = "0.3.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a905d57e488fec8861446d3393670fb50d27a262344013181c2cdf9fff5481be" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki" -version = "0.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/Cargo.toml b/Cargo.toml index 44fc6c2..3e393ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,21 +25,21 @@ name = "static-web-server" path = "src/bin/server.rs" [dependencies] -tokio = { version = "1", features = ["rt-multi-thread"], default-features = false } -warp = { git = "https://github.com/joseluisq/warp.git", branch = "0.3.x", features = ["tls", "compression"], default-features = false } +hyper = { version = "0.14", features = ["stream", "http1", "tcp", "server"] } +tokio = { version = "1", features = ["rt-multi-thread", "macros", "signal", "fs", "io-util"], default-features = false } +futures = { version = "0.3", default-features = false } +headers = "0.3" +tokio-util = { version = "0.6", features = ["io"] } anyhow = "1.0" tracing = "0.1" tracing-subscriber = "0.2" +mime_guess = "2.0" +bytes = "1.0" +percent-encoding = "2.1" structopt = { version = "0.3", default-features = false } num_cpus = { version = "1.13" } once_cell = "1.7" -[target.'cfg(not(windows))'.dependencies.nix] -version = "0.14" - -[target.'cfg(not(windows))'.dependencies.signal] -version = "0.7" - [target.'cfg(all(target_env = "musl", target_pointer_width = "64"))'.dependencies.jemallocator] version = "0.3" diff --git a/README.md b/README.md index 73985e6..69a6a2e 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ - Built with [Rust](https://rust-lang.org) which is focused on [safety, speed, and concurrency](https://kornel.ski/rust-c-speed). - Memory safety and very reduced CPU and RAM overhead. -- Blazing fast static files-serving and asynchronous powered by [Warp](https://github.com/seanmonstar/warp/) `v0.3` ([Hyper](https://github.com/hyperium/hyper/) `v0.14`), [Tokio](https://github.com/tokio-rs/tokio) `v1` and a set of [awesome crates](./Cargo.toml). +- Blazing fast static files-serving and asynchronous powered by [Hyper](https://github.com/hyperium/hyper/) `v0.14`, [Tokio](https://github.com/tokio-rs/tokio) `v1` and a set of [awesome crates](./Cargo.toml). - Suitable for lightweight [GNU/Linux Docker containers](https://hub.docker.com/r/joseluisq/static-web-server/tags). It's a fully __5MB__ static binary thanks to [Rust and Musl libc](https://doc.rust-lang.org/edition-guide/rust-2018/platform-and-target-support/musl-support-for-fully-static-binaries.html). - GZip, Deflate or Brotli compression for text-based web files only. - Compression on demand via [Accept-Encoding](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding) header. diff --git a/src/cache.rs b/src/cache.rs deleted file mode 100644 index c32e1c1..0000000 --- a/src/cache.rs +++ /dev/null @@ -1,38 +0,0 @@ -const CACHE_EXT_ONE_HOUR: [&str; 4] = ["atom", "json", "rss", "xml"]; -const CACHE_EXT_ONE_YEAR: [&str; 30] = [ - "bmp", "bz2", "css", "doc", "gif", "gz", "htc", "ico", "jpeg", "jpg", "js", "map", "mjs", - "mp3", "mp4", "ogg", "ogv", "pdf", "png", "rar", "rtf", "tar", "tgz", "wav", "weba", "webm", - "webp", "woff", "woff2", "zip", -]; - -/// It applies the corresponding Cache-Control headers based on a set of file types. -pub fn control_headers(res: warp::fs::File) -> warp::reply::WithHeader { - // Default max-age value in seconds (one day) - let mut max_age = 60 * 60 * 24_u64; - - if let Some(ext) = res.path().extension() { - if let Some(ext) = ext.to_str() { - if CACHE_EXT_ONE_HOUR.iter().any(|x| *x == ext) { - max_age = 60 * 60; - } else if CACHE_EXT_ONE_YEAR.iter().any(|x| *x == ext) { - max_age = 60 * 60 * 24 * 365; - } - } - } - - // HTML file types and others - warp::reply::with_header( - res, - "cache-control", - [ - "public, max-age=".to_string(), - duration(max_age).to_string(), - ] - .concat(), - ) -} - -/// It caps a duration value at ~136 years. -fn duration(n: u64) -> u32 { - std::cmp::min(n, u32::MAX as u64) as u32 -} diff --git a/src/compression.rs b/src/compression.rs deleted file mode 100644 index 7dd180d..0000000 --- a/src/compression.rs +++ /dev/null @@ -1,19 +0,0 @@ -/// Contains a common fixed list of text-based MIME types in order to apply compression. -pub const TEXT_MIME_TYPES: [&str; 16] = [ - "text/html", - "text/css", - "text/javascript", - "text/xml", - "text/plain", - "text/x-component", - "application/javascript", - "application/x-javascript", - "application/json", - "application/xml", - "application/rss+xml", - "application/atom+xml", - "font/truetype", - "font/opentype", - "application/vnd.ms-fontobject", - "image/svg+xml", -]; diff --git a/src/controller.rs b/src/controller.rs new file mode 100644 index 0000000..bb44d2f --- /dev/null +++ b/src/controller.rs @@ -0,0 +1,38 @@ +use headers::{AcceptRanges, ContentLength, ContentType, HeaderMapExt}; +use hyper::{Body, Request, Response}; +use std::path::Path; + +use crate::error::Result; +use crate::fs; + +/// Main server request entry point. +pub async fn handle(base: &Path, req: Request) -> Result> { + let path = req.uri().path(); + let resp = fs::handle_request(base, req.headers(), path).await; + match resp { + Ok(resp) => Ok(resp), + Err(status) => { + let method = req.method(); + tracing::warn!(method = ?method, status = status.as_u16(), error = ?status.to_string()); + + let mut body = Body::empty(); + let mut len = 0_u64; + if method == hyper::Method::GET { + let content = format!( + "{}

{}

", + status, status + ); + len = content.len() as u64; + body = Body::from(content) + } + + let mut resp = Response::new(body); + *resp.status_mut() = status; + resp.headers_mut().typed_insert(ContentLength(len)); + resp.headers_mut().typed_insert(ContentType::html()); + resp.headers_mut().typed_insert(AcceptRanges::bytes()); + + Ok(resp) + } + } +} diff --git a/src/cors.rs b/src/cors.rs deleted file mode 100644 index 9482ba9..0000000 --- a/src/cors.rs +++ /dev/null @@ -1,36 +0,0 @@ -use std::collections::HashSet; -use warp::filters::cors::Builder; - -/// Warp filter which provides an optional CORS if its supported. -pub fn get_opt_cors_filter(origins: &str) -> (Option, String) { - let mut cors_allowed_hosts = String::new(); - let cors_filter = if origins.is_empty() { - None - } else if origins == "*" { - cors_allowed_hosts = origins.into(); - Some( - warp::cors() - .allow_any_origin() - .allow_methods(vec!["GET", "HEAD", "OPTIONS"]), - ) - } else { - cors_allowed_hosts = origins.into(); - let hosts = cors_allowed_hosts - .split(',') - .map(|s| s.trim().as_ref()) - .collect::>(); - - if hosts.is_empty() { - cors_allowed_hosts = hosts.into_iter().collect::>().join(", "); - None - } else { - Some( - warp::cors() - .allow_origins(hosts) - .allow_methods(vec!["GET", "HEAD", "OPTIONS"]), - ) - } - }; - - (cors_filter, cors_allowed_hosts) -} diff --git a/src/error_page.rs b/src/error_page.rs new file mode 100644 index 0000000..db455f2 --- /dev/null +++ b/src/error_page.rs @@ -0,0 +1,4 @@ +use once_cell::sync::OnceCell; + +pub static PAGE_404: OnceCell = OnceCell::new(); +pub static PAGE_50X: OnceCell = OnceCell::new(); diff --git a/src/fs.rs b/src/fs.rs new file mode 100644 index 0000000..4f5ff62 --- /dev/null +++ b/src/fs.rs @@ -0,0 +1,382 @@ +// Most of the this file is borrowed from https://github.com/seanmonstar/warp/blob/master/src/filters/fs.rs + +use bytes::{Bytes, BytesMut}; +use futures::future::Either; +use futures::{future, ready, stream, FutureExt, Stream, StreamExt, TryFutureExt}; +use headers::{ + AcceptRanges, ContentLength, ContentRange, ContentType, HeaderMap, HeaderMapExt, HeaderValue, + IfModifiedSince, IfRange, IfUnmodifiedSince, LastModified, Range, +}; +use hyper::{Body, Response, StatusCode}; +use percent_encoding::percent_decode_str; +use std::fs::Metadata; +use std::future::Future; +use std::io; +use std::ops::Bound; +use std::path::PathBuf; +use std::pin::Pin; +use std::sync::Arc; +use std::task::Poll; +use std::{cmp, path::Path}; +use tokio::fs::File as TkFile; +use tokio::io::AsyncSeekExt; +use tokio_util::io::poll_read_buf; + +/// A small Arch `PathBuf` wrapper since Arc doesn't implement AsRef. +#[derive(Clone, Debug)] +pub struct ArcPath(pub Arc); + +impl AsRef for ArcPath { + fn as_ref(&self) -> &Path { + (*self.0).as_ref() + } +} + +/// Entry point to handle web server requests which map to specific files +/// on file system and return a file response. +pub async fn handle_request( + base: &Path, + headers: &HeaderMap, + path: &str, +) -> Result, StatusCode> { + let base = Arc::new(base.into()); + let path = path_from_tail(base, path).await?; + file_reply(headers, path).await +} + +/// Reply with a file content. +fn file_reply( + headers: &HeaderMap, + path: ArcPath, +) -> impl Future, StatusCode>> + Send { + let conditionals = get_conditional_headers(headers); + TkFile::open(path.clone()).then(move |res| match res { + Ok(f) => Either::Left(file_conditional(f, path, conditionals)), + Err(err) => { + let rej = match err.kind() { + io::ErrorKind::NotFound => { + tracing::debug!("file not found: {:?}", path.as_ref().display()); + StatusCode::NOT_FOUND + } + io::ErrorKind::PermissionDenied => { + tracing::warn!("file permission denied: {:?}", path.as_ref().display()); + StatusCode::FORBIDDEN + } + _ => { + tracing::error!( + "file open error (path={:?}): {} ", + path.as_ref().display(), + err + ); + StatusCode::INTERNAL_SERVER_ERROR + } + }; + Either::Right(future::err(rej)) + } + }) +} + +fn get_conditional_headers(header_list: &HeaderMap) -> Conditionals { + let if_modified_since = header_list.typed_get::(); + let if_unmodified_since = header_list.typed_get::(); + let if_range = header_list.typed_get::(); + let range = header_list.typed_get::(); + + Conditionals { + if_modified_since, + if_unmodified_since, + if_range, + range, + } +} + +fn path_from_tail( + base: Arc, + tail: &str, +) -> impl Future> + Send { + future::ready(sanitize_path(base.as_ref(), tail)).and_then(|mut buf| async { + let is_dir = tokio::fs::metadata(buf.clone()) + .await + .map(|m| m.is_dir()) + .unwrap_or(false); + + if is_dir { + tracing::debug!("dir: appending index.html to directory path"); + buf.push("index.html"); + } + tracing::trace!("dir: {:?}", buf); + Ok(ArcPath(Arc::new(buf))) + }) +} + +fn sanitize_path(base: impl AsRef, tail: &str) -> Result { + let mut buf = PathBuf::from(base.as_ref()); + let p = match percent_decode_str(tail).decode_utf8() { + Ok(p) => p, + Err(err) => { + tracing::debug!("dir: failed to decode route={:?}: {:?}", tail, err); + return Err(StatusCode::UNSUPPORTED_MEDIA_TYPE); + } + }; + tracing::trace!("dir? base={:?}, route={:?}", base.as_ref(), p); + for seg in p.split('/') { + if seg.starts_with("..") { + tracing::warn!("dir: rejecting segment starting with '..'"); + return Err(StatusCode::NOT_FOUND); + } else if seg.contains('\\') { + tracing::warn!("dir: rejecting segment containing with backslash (\\)"); + return Err(StatusCode::NOT_FOUND); + } else { + buf.push(seg); + } + } + Ok(buf) +} + +#[derive(Debug)] +struct Conditionals { + if_modified_since: Option, + if_unmodified_since: Option, + if_range: Option, + range: Option, +} + +enum Cond { + NoBody(Response), + WithBody(Option), +} + +impl Conditionals { + fn check(self, last_modified: Option) -> Cond { + if let Some(since) = self.if_unmodified_since { + let precondition = last_modified + .map(|time| since.precondition_passes(time.into())) + .unwrap_or(false); + + tracing::trace!( + "if-unmodified-since? {:?} vs {:?} = {}", + since, + last_modified, + precondition + ); + if !precondition { + let mut res = Response::new(Body::empty()); + *res.status_mut() = StatusCode::PRECONDITION_FAILED; + return Cond::NoBody(res); + } + } + + if let Some(since) = self.if_modified_since { + tracing::trace!( + "if-modified-since? header = {:?}, file = {:?}", + since, + last_modified + ); + let unmodified = last_modified + .map(|time| !since.is_modified(time.into())) + // no last_modified means its always modified + .unwrap_or(false); + if unmodified { + let mut res = Response::new(Body::empty()); + *res.status_mut() = StatusCode::NOT_MODIFIED; + return Cond::NoBody(res); + } + } + + if let Some(if_range) = self.if_range { + tracing::trace!("if-range? {:?} vs {:?}", if_range, last_modified); + let can_range = !if_range.is_modified(None, last_modified.as_ref()); + if !can_range { + return Cond::WithBody(None); + } + } + + Cond::WithBody(self.range) + } +} + +fn file_conditional( + f: TkFile, + path: ArcPath, + conditionals: Conditionals, +) -> impl Future, StatusCode>> + Send { + file_metadata(f).map_ok(move |(file, meta)| { + let mut len = meta.len(); + let modified = meta.modified().ok().map(LastModified::from); + + match conditionals.check(modified) { + Cond::NoBody(resp) => resp, + Cond::WithBody(range) => { + bytes_range(range, len) + .map(|(start, end)| { + let sub_len = end - start; + let buf_size = optimal_buf_size(&meta); + let stream = file_stream(file, buf_size, (start, end)); + let body = Body::wrap_stream(stream); + + let mut resp = Response::new(body); + + if sub_len != len { + *resp.status_mut() = StatusCode::PARTIAL_CONTENT; + resp.headers_mut().typed_insert( + ContentRange::bytes(start..end, len).expect("valid ContentRange"), + ); + + len = sub_len; + } + + let mime = mime_guess::from_path(path.as_ref()).first_or_octet_stream(); + + resp.headers_mut().typed_insert(ContentLength(len)); + resp.headers_mut().typed_insert(ContentType::from(mime)); + resp.headers_mut().typed_insert(AcceptRanges::bytes()); + + if let Some(last_modified) = modified { + resp.headers_mut().typed_insert(last_modified); + } + + resp + }) + .unwrap_or_else(|BadRange| { + // bad byte range + let mut resp = Response::new(Body::empty()); + *resp.status_mut() = StatusCode::RANGE_NOT_SATISFIABLE; + resp.headers_mut() + .typed_insert(ContentRange::unsatisfied_bytes(len)); + resp + }) + } + } + }) +} + +async fn file_metadata(f: TkFile) -> Result<(TkFile, Metadata), StatusCode> { + match f.metadata().await { + Ok(meta) => Ok((f, meta)), + Err(err) => { + tracing::debug!("file metadata error: {}", err); + Err(StatusCode::INTERNAL_SERVER_ERROR) + } + } +} + +struct BadRange; + +fn bytes_range(range: Option, max_len: u64) -> Result<(u64, u64), BadRange> { + let range = if let Some(range) = range { + range + } else { + return Ok((0, max_len)); + }; + + let ret = range + .iter() + .map(|(start, end)| { + let start = match start { + Bound::Unbounded => 0, + Bound::Included(s) => s, + Bound::Excluded(s) => s + 1, + }; + + let end = match end { + Bound::Unbounded => max_len, + Bound::Included(s) => s + 1, + Bound::Excluded(s) => s, + }; + + if start < end && end <= max_len { + Ok((start, end)) + } else { + tracing::trace!("unsatisfiable byte range: {}-{}/{}", start, end, max_len); + Err(BadRange) + } + }) + .next() + .unwrap_or(Ok((0, max_len))); + ret +} + +fn file_stream( + mut file: TkFile, + buf_size: usize, + (start, end): (u64, u64), +) -> impl Stream> + Send { + let seek = async move { + if start != 0 { + file.seek(io::SeekFrom::Start(start)).await?; + } + Ok(file) + }; + + seek.into_stream() + .map(move |result| { + let mut buf = BytesMut::new(); + let mut len = end - start; + let mut f = match result { + Ok(f) => f, + Err(f) => return Either::Left(stream::once(future::err(f))), + }; + + Either::Right(stream::poll_fn(move |cx| { + if len == 0 { + return Poll::Ready(None); + } + reserve_at_least(&mut buf, buf_size); + + let n = match ready!(poll_read_buf(Pin::new(&mut f), cx, &mut buf)) { + Ok(n) => n as u64, + Err(err) => { + tracing::debug!("file read error: {}", err); + return Poll::Ready(Some(Err(err))); + } + }; + + if n == 0 { + tracing::debug!("file read found EOF before expected length"); + return Poll::Ready(None); + } + + let mut chunk = buf.split().freeze(); + if n > len { + chunk = chunk.split_to(len as usize); + len = 0; + } else { + len -= n; + } + + Poll::Ready(Some(Ok(chunk))) + })) + }) + .flatten() +} + +fn reserve_at_least(buf: &mut BytesMut, cap: usize) { + if buf.capacity() - buf.len() < cap { + buf.reserve(cap); + } +} + +const DEFAULT_READ_BUF_SIZE: usize = 8_192; + +fn optimal_buf_size(metadata: &Metadata) -> usize { + let block_size = get_block_size(metadata); + + // If file length is smaller than block size, don't waste space + // reserving a bigger-than-needed buffer. + cmp::min(block_size as u64, metadata.len()) as usize +} + +#[cfg(unix)] +fn get_block_size(metadata: &Metadata) -> usize { + use std::os::unix::fs::MetadataExt; + //TODO: blksize() returns u64, should handle bad cast... + //(really, a block size bigger than 4gb?) + + // Use device blocksize unless it's really small. + cmp::max(metadata.blksize() as usize, DEFAULT_READ_BUF_SIZE) +} + +#[cfg(not(unix))] +fn get_block_size(_metadata: &Metadata) -> usize { + DEFAULT_READ_BUF_SIZE +} diff --git a/src/lib.rs b/src/lib.rs index 57c9027..b2bc1a3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,15 +3,13 @@ #[macro_use] extern crate anyhow; -pub mod cache; -pub mod compression; pub mod config; -pub mod cors; +pub mod controller; +pub mod error_page; +pub mod fs; pub mod helpers; pub mod logger; -pub mod rejection; pub mod server; -pub mod signals; #[macro_use] pub mod error; diff --git a/src/rejection.rs b/src/rejection.rs deleted file mode 100644 index 75a825a..0000000 --- a/src/rejection.rs +++ /dev/null @@ -1,46 +0,0 @@ -use anyhow::Result; -use once_cell::sync::OnceCell; -use std::convert::Infallible; -use warp::http::StatusCode; -use warp::{Rejection, Reply}; - -pub static PAGE_404: OnceCell = OnceCell::new(); -pub static PAGE_50X: OnceCell = OnceCell::new(); - -/// It receives a `Rejection` and tries to return the corresponding HTML error reply. -pub async fn handle_rejection(err: Rejection) -> Result { - let mut content = String::new(); - let code = if err.is_not_found() { - content = PAGE_404 - .get() - .expect("page 404 is not initialized") - .to_string(); - StatusCode::NOT_FOUND - } else if err - .find::() - .is_some() - { - StatusCode::BAD_REQUEST - } else if err.find::().is_some() { - StatusCode::METHOD_NOT_ALLOWED - } else if err.find::().is_some() { - StatusCode::FORBIDDEN - } else if err.find::().is_some() { - StatusCode::UNSUPPORTED_MEDIA_TYPE - } else { - content = PAGE_50X - .get() - .expect("page 50x is not initialized") - .to_string(); - StatusCode::INTERNAL_SERVER_ERROR - }; - - if content.is_empty() { - content = format!( - "{}

{}

", - code, code - ); - } - - Ok(warp::reply::with_status(warp::reply::html(content), code)) -} diff --git a/src/server.rs b/src/server.rs index bc887fe..c18aaba 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,13 +1,16 @@ +use hyper::server::Server as HyperServer; +use hyper::service::{make_service_fn, service_fn}; use std::net::{IpAddr, SocketAddr}; -use std::path::PathBuf; +use std::sync::Arc; use structopt::StructOpt; -use warp::Filter; +use tokio::signal; -use crate::{cache, cors, helpers, logger, rejection, Result}; use crate::{ - compression::TEXT_MIME_TYPES, config::{Config, CONFIG}, + error_page, }; +use crate::{controller::handle, fs::ArcPath}; +use crate::{error, helpers, logger, Result}; /// Define a multi-thread HTTP or HTTP/2 web server. pub struct Server { @@ -25,10 +28,11 @@ impl Server { 0 | 1 => 1, _ => num_cpus::get() * opts.threads_multiplier, }; + Self { threads } } - /// Build and run the multi-thread `Server` spawning a new Tokio asynchronous task for it. + /// Build and run the multi-thread `Server`. pub fn run(self) -> Result { tokio::runtime::Builder::new_multi_thread() .enable_all() @@ -36,7 +40,7 @@ impl Server { .worker_threads(self.threads) .build()? .block_on(async { - let r = self.run_server_with_config().await; + let r = self.start_server().await; if r.is_err() { panic!("Server error during start up: {:?}", r.unwrap_err()) } @@ -45,8 +49,8 @@ impl Server { Ok(()) } - /// Create and run the `Warp` server spawning a new Tokio asynchronous task with the given configuration. - async fn run_server_with_config(self) -> Result { + /// Run the inner Hyper `HyperServer` forever on the current thread with the given configuration. + async fn start_server(self) -> Result { let opts = Config::global(); logger::init(&opts.log_level)?; @@ -61,34 +65,41 @@ impl Server { let root_dir = helpers::get_valid_dirpath(&opts.root)?; // Custom error pages content - rejection::PAGE_404 + error_page::PAGE_404 .set(helpers::read_file_content(opts.page404.as_ref())) .expect("page 404 is not initialized"); - rejection::PAGE_50X + error_page::PAGE_50X .set(helpers::read_file_content(opts.page50x.as_ref())) .expect("page 50x is not initialized"); - // CORS support - let (cors_filter_opt, cors_allowed_origins) = - cors::get_opt_cors_filter(opts.cors_allow_origins.as_ref()); + // TODO: CORS support - // HTTP/2 + TLS - let http2 = opts.http2; - let http2_tls_cert_path = &opts.http2_tls_cert; - let http2_tls_key_path = &opts.http2_tls_key; + // TODO: HTTP/2 + TLS // Spawn a new Tokio asynchronous server task determined by the given options - tokio::task::spawn(run_server_with_options( - addr, - root_dir, - http2, - http2_tls_cert_path, - http2_tls_key_path, - cors_filter_opt, - cors_allowed_origins, - )); - - handle_signals(); + + let span = tracing::info_span!("Server::run", ?addr, threads = ?self.threads); + tracing::info!(parent: &span, "listening on http://{}", addr); + + let root_dir = ArcPath(Arc::new(root_dir)); + let create_service = make_service_fn(move |_| { + let root_dir = root_dir.clone(); + async move { + Ok::<_, error::Error>(service_fn(move |req| { + let root_dir = root_dir.clone(); + async move { handle(root_dir.as_ref(), req).await } + })) + } + }); + HyperServer::bind(&addr) + .serve(create_service) + .with_graceful_shutdown(async { + signal::ctrl_c() + .await + .expect("failed to install CTRL+C signal handler"); + tracing::warn!(parent: &span, "CTRL+C signal caught and execution exited"); + }) + .await?; Ok(()) } @@ -99,97 +110,3 @@ impl Default for Server { Self::new() } } - -/// It creates and starts a Warp HTTP or HTTP/2 server with its options. -pub async fn run_server_with_options( - addr: SocketAddr, - root_dir: PathBuf, - http2: bool, - http2_tls_cert_path: &'static str, - http2_tls_key_path: &'static str, - cors_filter_opt: Option, - cors_allowed_origins: String, -) { - // Base fs directory filter - let base_fs_dir_filter = warp::fs::dir(root_dir.clone()) - .map(cache::control_headers) - .with(warp::trace::request()) - .recover(rejection::handle_rejection); - - // Public HEAD endpoint - let public_head = warp::head().and(base_fs_dir_filter.clone()); - - // Public GET endpoint (default) - let public_get_default = warp::get().and(base_fs_dir_filter); - - // Current fs directory filter - let fs_dir_filter = warp::fs::dir(root_dir) - .map(cache::control_headers) - .with(warp::compression::auto(|headers| { - // Skip compression for non-text-based MIME types - if let Some(content_type) = headers.get("content-type") { - !TEXT_MIME_TYPES.iter().any(|h| h == content_type) - } else { - false - } - })) - .with(warp::trace::request()) - .recover(rejection::handle_rejection); - - // Determine CORS filter - if let Some(cors_filter) = cors_filter_opt { - tracing::info!( - cors_enabled = ?true, - allowed_origins = ?cors_allowed_origins - ); - - let public_head = public_head.with(cors_filter.clone()); - let public_get_default = public_get_default.with(cors_filter.clone()); - let public_get = warp::get().and(fs_dir_filter).with(cors_filter.clone()); - - let server = warp::serve(public_head.or(public_get).or(public_get_default)); - - if http2 { - server - .tls() - .cert_path(http2_tls_cert_path) - .key_path(http2_tls_key_path) - .run(addr) - .await - } else { - server.run(addr).await - } - } else { - let public_get = warp::get().and(fs_dir_filter); - - let server = warp::serve(public_head.or(public_get).or(public_get_default)); - - if http2 { - server - .tls() - .cert_path(http2_tls_cert_path) - .key_path(http2_tls_key_path) - .run(addr) - .await - } else { - server.run(addr).await - } - } -} - -#[cfg(not(windows))] -/// Handle incoming signals for Unix-like OS's only -fn handle_signals() { - use crate::signals; - - signals::wait(|sig: signals::Signal| { - let code = signals::as_int(sig); - tracing::warn!("Signal {} caught. Server execution exited.", code); - std::process::exit(code) - }); -} - -#[cfg(windows)] -fn handle_signals() { - // TODO: Windows signals... -} diff --git a/src/signals.rs b/src/signals.rs deleted file mode 100644 index 6947b11..0000000 --- a/src/signals.rs +++ /dev/null @@ -1,51 +0,0 @@ -use nix::errno::Errno; -use nix::libc::c_int; -use nix::sys::signal::{SIGCHLD, SIGINT, SIGTERM}; -use nix::sys::wait::WaitStatus::{Exited, Signaled, StillAlive}; -use nix::sys::wait::{waitpid, WaitPidFlag}; -use nix::Error; - -pub use signal::Signal; - -/// It waits for an incoming Termination Signal like Ctrl+C (SIGINT), SIGTERM, etc -pub fn wait(func: F) -where - F: Fn(signal::Signal), -{ - let sig_trap = signal::trap::Trap::trap(&[SIGTERM, SIGINT, SIGCHLD]); - for sig in sig_trap { - match sig { - SIGCHLD => { - // Current std::process::Command ip does not have a way to find - // process id, so we just wait until we have no children - loop { - match waitpid(None, Some(WaitPidFlag::WNOHANG)) { - Ok(Exited(pid, status)) => { - println!("{} exited with status {}", pid, status); - continue; - } - Ok(Signaled(pid, sig, _)) => { - println!("{} killed by {}", pid, sig as c_int); - continue; - } - Ok(StillAlive) => break, - Ok(status) => { - println!("Temporary status {:?}", status); - continue; - } - Err(Error::Sys(Errno::ECHILD)) => return, - Err(e) => { - panic!("Error {:?}", e); - } - } - } - } - sig => func(sig), - } - } -} - -/// It casts a given `signal::Signal` to `i32`. -pub fn as_int(sig: signal::Signal) -> i32 { - sig as c_int -} -- libgit2 1.7.2