diff --git a/Cargo.lock b/Cargo.lock index 0b8968d..1ed71f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,321 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "actix" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4af87564ff659dee8f9981540cac9418c45e910c8072fdedd643a262a38fcaf" +dependencies = [ + "actix-http", + "actix-rt", + "actix_derive", + "bitflags", + "bytes", + "crossbeam-channel", + "derive_more", + "futures 0.3.5", + "lazy_static", + "log", + "parking_lot", + "pin-project", + "smallvec", + "tokio", + "tokio-util 0.2.0", + "trust-dns-proto", + "trust-dns-resolver", +] + +[[package]] +name = "actix-codec" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09e55f0a5c2ca15795035d90c46bd0e73a5123b72f68f12596d6ba5282051380" +dependencies = [ + "bitflags", + "bytes", + "futures-core", + "futures-sink", + "log", + "tokio", + "tokio-util 0.2.0", +] + +[[package]] +name = "actix-connect" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c95cc9569221e9802bf4c377f6c18b90ef10227d787611decf79fd47d2a8e76c" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "derive_more", + "either", + "futures 0.3.5", + "http", + "log", + "rustls", + "tokio-rustls", + "trust-dns-proto", + "trust-dns-resolver", + "webpki", +] + +[[package]] +name = "actix-cors" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6206917d5c0fdd79d81cec9ef02d3e802df4abf276d96241e1f595d971e002" +dependencies = [ + "actix-service", + "actix-web", + "derive_more", + "futures 0.3.5", +] + +[[package]] +name = "actix-http" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c16664cc4fdea8030837ad5a845eb231fb93fc3c5c171edfefb52fad92ce9019" +dependencies = [ + "actix-codec", + "actix-connect", + "actix-rt", + "actix-service", + "actix-threadpool", + "actix-tls", + "actix-utils", + "base64 0.11.0", + "bitflags", + "brotli2", + "bytes", + "chrono", + "copyless", + "derive_more", + "either", + "encoding_rs", + "failure", + "flate2", + "futures-channel", + "futures-core", + "futures-util", + "fxhash", + "h2", + "http", + "httparse", + "indexmap", + "language-tags", + "lazy_static", + "log", + "mime", + "percent-encoding 2.1.0", + "pin-project", + "rand 0.7.3", + "regex", + "serde", + "serde_json", + "serde_urlencoded", + "sha1", + "slab", + "time", +] + +[[package]] +name = "actix-macros" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a60f9ba7c4e6df97f3aacb14bb5c0cd7d98a49dcbaed0d7f292912ad9a6a3ed2" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "actix-router" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d7a10ca4d94e8c8e7a87c5173aba1b97ba9a6563ca02b0e1cd23531093d3ec8" +dependencies = [ + "bytestring", + "http", + "log", + "regex", + "serde", +] + +[[package]] +name = "actix-rt" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "143fcc2912e0d1de2bcf4e2f720d2a60c28652ab4179685a1ee159e0fb3db227" +dependencies = [ + "actix-macros", + "actix-threadpool", + "copyless", + "futures-channel", + "futures-util", + "smallvec", + "tokio", +] + +[[package]] +name = "actix-server" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d74b464215a473c973a2d7d03a69cc10f4ce1f4b38a7659c5193dc5c675630" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "futures-channel", + "futures-util", + "log", + "mio", + "mio-uds", + "num_cpus", + "slab", + "socket2", +] + +[[package]] +name = "actix-service" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e4fc95dfa7e24171b2d0bb46b85f8ab0e8499e4e3caec691fc4ea65c287564" +dependencies = [ + "futures-util", + "pin-project", +] + +[[package]] +name = "actix-testing" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47239ca38799ab74ee6a8a94d1ce857014b2ac36f242f70f3f75a66f691e791c" +dependencies = [ + "actix-macros", + "actix-rt", + "actix-server", + "actix-service", + "log", + "socket2", +] + +[[package]] +name = "actix-threadpool" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91164716d956745c79dcea5e66d2aa04506549958accefcede5368c70f2fd4ff" +dependencies = [ + "derive_more", + "futures-channel", + "lazy_static", + "log", + "num_cpus", + "parking_lot", + "threadpool", +] + +[[package]] +name = "actix-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e5b4faaf105e9a6d389c606c298dcdb033061b00d532af9df56ff3a54995a8" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "derive_more", + "either", + "futures 0.3.5", + "log", + "rustls", + "tokio-rustls", + "webpki", + "webpki-roots", +] + +[[package]] +name = "actix-utils" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcf8f5631bf01adec2267808f00e228b761c60c0584cc9fa0b5364f41d147f4e" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "bitflags", + "bytes", + "either", + "futures 0.3.5", + "log", + "pin-project", + "slab", +] + +[[package]] +name = "actix-web" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3158e822461040822f0dbf1735b9c2ce1f95f93b651d7a7aded00b1efbb1f635" +dependencies = [ + "actix-codec", + "actix-http", + "actix-macros", + "actix-router", + "actix-rt", + "actix-server", + "actix-service", + "actix-testing", + "actix-threadpool", + "actix-tls", + "actix-utils", + "actix-web-codegen", + "awc", + "bytes", + "derive_more", + "encoding_rs", + "futures 0.3.5", + "fxhash", + "log", + "mime", + "net2", + "pin-project", + "regex", + "rustls", + "serde", + "serde_json", + "serde_urlencoded", + "time", + "url", +] + +[[package]] +name = "actix-web-codegen" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a71bf475cbe07281d0b3696abb48212db118e7e23219f13596ce865235ff5766" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "actix_derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b95aceadaf327f18f0df5962fedc1bde2f870566a0b9f65c89508a3b1f79334c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "addr2line" version = "0.12.0" @@ -80,6 +396,17 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "async-trait" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26c4f3195085c36ea8d24d32b2f828d23296a9370a28aa39d111f6f16bef9f3b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "atomicwrites" version = "0.2.5" @@ -114,6 +441,30 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" +[[package]] +name = "awc" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7601d4d1d7ef2335d6597a41b5fe069f6ab799b85f53565ab390e7b7065aac5" +dependencies = [ + "actix-codec", + "actix-http", + "actix-rt", + "actix-service", + "base64 0.11.0", + "bytes", + "derive_more", + "futures-core", + "log", + "mime", + "percent-encoding 2.1.0", + "rand 0.7.3", + "rustls", + "serde", + "serde_json", + "serde_urlencoded", +] + [[package]] name = "backtrace" version = "0.3.48" @@ -127,6 +478,15 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" +dependencies = [ + "byteorder", +] + [[package]] name = "base64" version = "0.11.0" @@ -209,22 +569,22 @@ dependencies = [ name = "bayard-rest" version = "0.8.1" dependencies = [ + "actix", + "actix-cors", + "actix-rt", + "actix-server", + "actix-web", "bayard-client", "bayard-common", "clap", "crossbeam-channel", "ctrlc", - "futures-util", - "hyper", - "lazy_static", "log", - "regex", + "num_cpus", "rustls", "serde", "serde_json", "serde_qs", - "tokio", - "tokio-rustls", ] [[package]] @@ -276,6 +636,26 @@ dependencies = [ "crunchy", ] +[[package]] +name = "brotli-sys" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4445dea95f4c2b41cde57cc9fee236ae4dbae88d8fcbdb4750fc1bb5d86aaecd" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "brotli2" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cb036c3eade309815c15ddbacec5b22c4d1f3983a774ab2eac2e3e9ea85568e" +dependencies = [ + "brotli-sys", + "libc", +] + [[package]] name = "bumpalo" version = "3.3.0" @@ -294,6 +674,15 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" +[[package]] +name = "bytestring" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7c05fa5172da78a62d9949d662d2ac89d4cc7355d7b49adee5163f1fb3f363" +dependencies = [ + "bytes", +] + [[package]] name = "cang-jie" version = "0.8.0" @@ -386,6 +775,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "copyless" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff9c56c9fb2a49c05ef0e431485a22400af20d33226dc0764d891d09e724127" + [[package]] name = "core-foundation" version = "0.7.0" @@ -504,6 +899,17 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72aa14c04dfae8dd7d8a2b1cb7ca2152618cd01336dbfe704b8dcbf8d41dbd69" +[[package]] +name = "derive_more" +version = "0.99.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2127768764f1556535c01b5326ef94bd60ff08dcfbdc544d53e69ed155610f5d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "downcast-rs" version = "1.1.1" @@ -595,6 +1001,18 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "enum-as-inner" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc4bfcfacb61d231109d1d55202c1f33263319668b168843e02ad4652725ec9c" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "env_logger" version = "0.7.1" @@ -937,7 +1355,7 @@ dependencies = [ "log", "slab", "tokio", - "tokio-util", + "tokio-util 0.3.1", ] [[package]] @@ -950,6 +1368,15 @@ dependencies = [ "autocfg 1.0.0", ] +[[package]] +name = "heck" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "hermit-abi" version = "0.1.13" @@ -959,6 +1386,17 @@ dependencies = [ "libc", ] +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi 0.3.8", +] + [[package]] name = "htmlescape" version = "0.3.1" @@ -1087,6 +1525,18 @@ dependencies = [ "libc", ] +[[package]] +name = "ipconfig" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7e2f18aece9709094573a9f24f483c4f65caa4298e2f7ae1b71cc65d853fad7" +dependencies = [ + "socket2", + "widestring", + "winapi 0.3.8", + "winreg 0.6.2", +] + [[package]] name = "itertools" version = "0.8.2" @@ -1144,6 +1594,12 @@ dependencies = [ "log", ] +[[package]] +name = "language-tags" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" + [[package]] name = "lazy_static" version = "1.4.0" @@ -1264,6 +1720,21 @@ dependencies = [ "tantivy", ] +[[package]] +name = "linked-hash-map" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" + +[[package]] +name = "lock_api" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.8" @@ -1273,6 +1744,21 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "lru-cache" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + [[package]] name = "matches" version = "0.1.8" @@ -1585,6 +2071,30 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "parking_lot" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" +dependencies = [ + "cfg-if", + "cloudabi", + "libc", + "redox_syscall", + "smallvec", + "winapi 0.3.8", +] + [[package]] name = "percent-encoding" version = "1.0.1" @@ -2034,7 +2544,17 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "winreg", + "winreg 0.7.0", +] + +[[package]] +name = "resolv-conf" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11834e137f3b14e309437a8276714eed3a80d1ef894869e510f2c0c0b98b9f4a" +dependencies = [ + "hostname", + "quick-error", ] [[package]] @@ -2070,11 +2590,11 @@ checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" [[package]] name = "rustls" -version = "0.17.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0d4a31f5d68413404705d6982529b0e11a9aacd4839d1d6222ee3b8cb4015e1" +checksum = "b25a18b1bf7387f0145e7f8324e700805aade3842dd3db2e74e4cdeb4677c09e" dependencies = [ - "base64 0.11.0", + "base64 0.10.1", "log", "ring", "sct", @@ -2200,6 +2720,12 @@ dependencies = [ "url", ] +[[package]] +name = "sha1" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" + [[package]] name = "signal-hook-registry" version = "1.2.0" @@ -2444,6 +2970,15 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + [[package]] name = "time" version = "0.1.43" @@ -2491,9 +3026,9 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.13.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15cb62a0d2770787abc96e99c1cd98fcf17f94959f3af63ca85bdfb203f051b4" +checksum = "3068d891551949b37681724d6b73666787cc63fa8e255c812a41d2513aff9775" dependencies = [ "futures-core", "rustls", @@ -2511,6 +3046,20 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-util" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "571da51182ec208780505a32528fc5512a8fe1443ab960b3f2f3ef093cd16930" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + [[package]] name = "tokio-util" version = "0.3.1" @@ -2531,6 +3080,45 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860" +[[package]] +name = "trust-dns-proto" +version = "0.18.0-alpha.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a7f3a2ab8a919f5eca52a468866a67ed7d3efa265d48a652a9a3452272b413f" +dependencies = [ + "async-trait", + "enum-as-inner", + "failure", + "futures 0.3.5", + "idna", + "lazy_static", + "log", + "rand 0.7.3", + "smallvec", + "socket2", + "tokio", + "url", +] + +[[package]] +name = "trust-dns-resolver" +version = "0.18.0-alpha.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f90b1502b226f8b2514c6d5b37bafa8c200d7ca4102d57dc36ee0f3b7a04a2f" +dependencies = [ + "cfg-if", + "failure", + "futures 0.3.5", + "ipconfig", + "lazy_static", + "log", + "lru-cache", + "resolv-conf", + "smallvec", + "tokio", + "trust-dns-proto", +] + [[package]] name = "try-lock" version = "0.2.2" @@ -2564,6 +3152,12 @@ dependencies = [ "smallvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" + [[package]] name = "unicode-width" version = "0.1.7" @@ -2748,6 +3342,21 @@ dependencies = [ "untrusted", ] +[[package]] +name = "webpki-roots" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a262ae37dd9d60f60dd473d1158f9fbebf110ba7b6a5051c8160460f6043718b" +dependencies = [ + "webpki", +] + +[[package]] +name = "widestring" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "effc0e4ff8085673ea7b9b2e3c73f6bd4d118810c9009ed8f1e16bd96c331db6" + [[package]] name = "winapi" version = "0.2.8" @@ -2791,6 +3400,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "winreg" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9" +dependencies = [ + "winapi 0.3.8", +] + [[package]] name = "winreg" version = "0.7.0" diff --git a/bayard-rest/Cargo.toml b/bayard-rest/Cargo.toml index 5648a67..b828d08 100644 --- a/bayard-rest/Cargo.toml +++ b/bayard-rest/Cargo.toml @@ -17,20 +17,20 @@ name = "bayard-rest" path = "src/main.rs" [dependencies] +actix = "0.9.0" +actix-cors = "0.2.0" +actix-rt = "1.1.1" +actix-server = "1.0.2" +actix-web = { version = "2.0.0", features = ["rustls"] } clap = "2.33.0" crossbeam-channel = "0.4.2" ctrlc = { version = "3.1.4", features = ["termination"] } -futures-util = "0.3.5" -hyper = "0.13.5" -lazy_static = "1.4.0" log = "0.4.8" -regex = "1.3.7" -rustls = "0.17.0" +num_cpus = "1.13.0" +rustls = "0.16.0" serde = { version = "1.0.106", features = ["derive"] } serde_json = "1.0.51" serde_qs = "0.5.2" -tokio = { version = "0.2.21", features = ["full"] } -tokio-rustls = "0.13.0" bayard-client = { version = "0.8.1", path = "../bayard-client" } bayard-common = { version = "0.8.1", path = "../bayard-common" } diff --git a/bayard-rest/src/main.rs b/bayard-rest/src/main.rs index e569b2a..4108ea7 100644 --- a/bayard-rest/src/main.rs +++ b/bayard-rest/src/main.rs @@ -2,13 +2,19 @@ extern crate clap; use clap::{App, AppSettings, Arg}; +use crossbeam_channel::select; +use log::*; use bayard_common::log::set_logger; -use bayard_rest::rest::server::{run, run_tls}; +use bayard_common::signal::sigterm_channel; +use bayard_rest::rest::server::RestServer; -fn main() -> Result<(), std::io::Error> { +#[actix_rt::main] +async fn main() -> std::io::Result<()> { set_logger(); + let threads = format!("{}", num_cpus::get().to_owned()); + let app = App::new(crate_name!()) .setting(AppSettings::DeriveDisplayOrder) .version(crate_version!()) @@ -44,38 +50,47 @@ fn main() -> Result<(), std::io::Error> { .default_value("0.0.0.0:5000") .takes_value(true), ) - // .arg( - // Arg::with_name("CORS_ORIGIN") - // .help("Add an origin that are allowed to make requests.") - // .short("o") - // .long("cors-origin") - // .value_name("ORIGIN") - // .takes_value(true), - // ) - // .arg( - // Arg::with_name("CORS_METHODS") - // .help("Set a list of methods which the allowed origins are allowed to access for requests.") - // .short("m") - // .long("cors-method") - // .value_name("METHODS") - // .takes_value(true) - // .multiple(true) - // .use_delimiter(true) - // .require_delimiter(true) - // .value_delimiter(","), - // ) - // .arg( - // Arg::with_name("CORS_HEADERS") - // .help("Set a list of header field names which can be used when this resource is accessed by allowed origins.") - // .short("l") - // .long("cors-headers") - // .value_name("HEADERS") - // .takes_value(true) - // .multiple(true) - // .use_delimiter(true) - // .require_delimiter(true) - // .value_delimiter(","), - // ) + .arg( + Arg::with_name("HTTP_WORKER_THREADS") + .help("Number of HTTP worker threads. By default http server uses number of available logical cpu as threads count.") + .short("w") + .long("worker-threads") + .value_name("THREADS") + .default_value(&threads) + .takes_value(true), + ) + .arg( + Arg::with_name("CORS_ORIGIN") + .help("Add an origin that are allowed to make requests.") + .short("o") + .long("cors-origin") + .value_name("ORIGIN") + .takes_value(true), + ) + .arg( + Arg::with_name("CORS_METHODS") + .help("Set a list of methods which the allowed origins are allowed to access for requests.") + .short("m") + .long("cors-method") + .value_name("METHODS") + .takes_value(true) + .multiple(true) + .use_delimiter(true) + .require_delimiter(true) + .value_delimiter(","), + ) + .arg( + Arg::with_name("CORS_HEADERS") + .help("Set a list of header field names which can be used when this resource is accessed by allowed origins.") + .short("l") + .long("cors-headers") + .value_name("HEADERS") + .takes_value(true) + .multiple(true) + .use_delimiter(true) + .require_delimiter(true) + .value_delimiter(","), + ) .arg( Arg::with_name("CERT_FILE") .help("Path to the TLS certificate file.") @@ -98,22 +113,27 @@ fn main() -> Result<(), std::io::Error> { let host = matches.value_of("HOST").unwrap(); let port = matches.value_of("PORT").unwrap().parse::().unwrap(); let index_address = matches.value_of("INDEX_ADDRESS").unwrap(); - // let mut cors_origin = "".to_string(); - // if let Some(_cors_origin) = matches.value_of("CORS_ORIGIN") { - // cors_origin = _cors_origin.to_string(); - // } - // let mut cors_methods = Vec::new(); - // if let Some(_cors_methods) = matches.values_of("CORS_METHODS") { - // _cors_methods - // .map(|s| cors_methods.push(s.to_string())) - // .count(); - // } - // let mut cors_headers = Vec::new(); - // if let Some(_cors_headers) = matches.values_of("CORS_HEADERS") { - // _cors_headers - // .map(|s| cors_headers.push(s.to_string())) - // .count(); - // } + let http_worker_threads = matches + .value_of("HTTP_WORKER_THREADS") + .unwrap() + .parse::() + .unwrap(); + let mut cors_origin = "".to_string(); + if let Some(_cors_origin) = matches.value_of("CORS_ORIGIN") { + cors_origin = _cors_origin.to_string(); + } + let mut cors_methods = Vec::new(); + if let Some(_cors_methods) = matches.values_of("CORS_METHODS") { + _cors_methods + .map(|s| cors_methods.push(s.to_string())) + .count(); + } + let mut cors_headers = Vec::new(); + if let Some(_cors_headers) = matches.values_of("CORS_HEADERS") { + _cors_headers + .map(|s| cors_headers.push(s.to_string())) + .count(); + } let mut cert_file = ""; if let Some(_cert_file) = matches.value_of("CERT_FILE") { cert_file = _cert_file; @@ -123,18 +143,66 @@ fn main() -> Result<(), std::io::Error> { key_file = _key_file; } - let rest_addr = format!("{}:{}", host, port); - let rest_address = rest_addr.as_ref(); + let rest_address = format!("{}:{}", host, port); + + let enable_cors = + !cors_origin.is_empty() && !cors_methods.is_empty() && !cors_headers.is_empty(); + let enable_tls = !cert_file.is_empty() && !key_file.is_empty(); - if cert_file != "" && key_file != "" { - if let Err(_e) = run_tls(rest_address, index_address, cert_file, key_file) { - std::process::exit(1); + let mut rest_server = match (enable_tls, enable_cors) { + (false, false) => { + RestServer::new(rest_address.as_str(), index_address, http_worker_threads) } - } else { - if let Err(_e) = run(rest_address, index_address) { - std::process::exit(1); + (false, true) => RestServer::new_cors( + rest_address.as_str(), + index_address, + http_worker_threads, + cors_origin, + cors_methods, + cors_headers, + ), + (true, false) => RestServer::new_tls( + rest_address.as_str(), + index_address, + http_worker_threads, + cert_file, + key_file, + ), + (true, true) => RestServer::new_cors_tls( + rest_address.as_str(), + index_address, + http_worker_threads, + cors_origin, + cors_methods, + cors_headers, + cert_file, + key_file, + ), + }; + info!("start rest service on {}", rest_address.as_str()); + + // Wait for signals for termination (SIGINT, SIGTERM). + let sigterm_receiver = sigterm_channel().unwrap(); + loop { + select! { + recv(sigterm_receiver) -> _ => { + info!("receive signal"); + break; + } } } - Ok(()) + match rest_server.shutdown().await { + Ok(_) => { + info!("stop rest service on {}:{}", host, port); + Ok(()) + } + Err(e) => { + error!( + "failed to stop rest service on {}:{}: error={}", + host, port, e + ); + Err(e) + } + } } diff --git a/bayard-rest/src/rest/handler.rs b/bayard-rest/src/rest/handler.rs index 8c99e20..15d3383 100644 --- a/bayard-rest/src/rest/handler.rs +++ b/bayard-rest/src/rest/handler.rs @@ -1,24 +1,148 @@ -use hyper::{header, Body, Method, Request, Response, StatusCode}; -use regex::Regex; +use actix_web::{delete, get, post, put, web, Error, HttpRequest, HttpResponse}; use serde::Deserialize; use serde_json::Value; -use bayard_client::index::client::IndexClient; -use lazy_static::lazy_static; +use crate::rest::server::AppState; -lazy_static! { - static ref RE_DOCUMENTS: Regex = Regex::new(r"^/v1/documents/(?P.+)$").unwrap(); - static ref RE_DOCUMENTS_BULK: Regex = Regex::new(r"^/v1/documents$").unwrap(); - static ref RE_COMMIT: Regex = Regex::new(r"^/v1/commit$").unwrap(); - static ref RE_ROLLBACK: Regex = Regex::new(r"^/v1/rollback$").unwrap(); - static ref RE_MERGE: Regex = Regex::new(r"^/v1/merge$").unwrap(); - static ref RE_SCHEMA: Regex = Regex::new(r"^/v1/schema$").unwrap(); - static ref RE_SEARCH: Regex = Regex::new(r"^/v1/search$").unwrap(); - static ref RE_STATUS: Regex = Regex::new(r"^/v1/status$").unwrap(); +#[get("/v1/documents/{id}")] +pub async fn get(state: AppState, id: web::Path) -> Result { + match state.index_client.lock().unwrap().get(id.into_inner()) { + Ok(s) => { + let res = HttpResponse::Ok().body(s); + Ok(res) + } + Err(e) => { + let res = HttpResponse::InternalServerError().body(format!("{}", e)); + Ok(res) + } + } +} + +#[put("/v1/documents/{id}")] +pub async fn set( + state: AppState, + body: web::Bytes, + id: web::Path, +) -> Result { + let json_str = String::from_utf8(body.to_vec()).unwrap(); + let mut value: Value = serde_json::from_str(json_str.as_str()).unwrap(); + value["_id"] = Value::String(id.into_inner()); + + let doc = serde_json::to_string(&value).unwrap(); + + match state.index_client.lock().unwrap().set(doc) { + Ok(_) => { + let res = HttpResponse::Ok().await.unwrap(); + Ok(res) + } + Err(e) => { + let res = HttpResponse::InternalServerError().body(format!("{}", e)); + Ok(res) + } + } +} + +#[delete("/v1/documents/{id}")] +pub async fn delete(state: AppState, id: web::Path) -> Result { + match state.index_client.lock().unwrap().delete(id.into_inner()) { + Ok(_) => { + let res = HttpResponse::Ok().await.unwrap(); + Ok(res) + } + Err(e) => { + let res = HttpResponse::InternalServerError().body(format!("{}", e)); + Ok(res) + } + } +} + +#[put("/v1/documents")] +pub async fn bulk_set(state: AppState, body: web::Bytes) -> Result { + let docs = String::from_utf8(body.to_vec()).unwrap(); + + match state.index_client.lock().unwrap().bulk_set(docs) { + Ok(_) => { + let res = HttpResponse::Ok().await.unwrap(); + Ok(res) + } + Err(e) => { + let res = HttpResponse::InternalServerError().body(format!("{}", e)); + Ok(res) + } + } +} + +#[delete("/v1/documents")] +pub async fn bulk_delete(state: AppState, body: web::Bytes) -> Result { + let docs = String::from_utf8(body.to_vec()).unwrap(); + + match state.index_client.lock().unwrap().bulk_delete(docs) { + Ok(_) => { + let res = HttpResponse::Ok().await.unwrap(); + Ok(res) + } + Err(e) => { + let res = HttpResponse::InternalServerError().body(format!("{}", e)); + Ok(res) + } + } +} + +#[get("/v1/commit")] +pub async fn commit(state: AppState) -> Result { + match state.index_client.lock().unwrap().commit() { + Ok(_) => { + let res = HttpResponse::Ok().await.unwrap(); + Ok(res) + } + Err(e) => { + let res = HttpResponse::InternalServerError().body(format!("{}", e)); + Ok(res) + } + } } -pub type GenericError = Box; -pub type Result = std::result::Result; +#[get("/v1/rollback")] +pub async fn rollback(state: AppState) -> Result { + match state.index_client.lock().unwrap().rollback() { + Ok(_) => { + let res = HttpResponse::Ok().await.unwrap(); + Ok(res) + } + Err(e) => { + let res = HttpResponse::InternalServerError().body(format!("{}", e)); + Ok(res) + } + } +} + +#[get("/v1/merge")] +pub async fn merge(state: AppState) -> Result { + match state.index_client.lock().unwrap().merge() { + Ok(_) => { + let res = HttpResponse::Ok().await.unwrap(); + Ok(res) + } + Err(e) => { + let res = HttpResponse::InternalServerError().body(format!("{}", e)); + Ok(res) + } + } +} + +#[get("/v1/schema")] +pub async fn schema(state: AppState) -> Result { + match state.index_client.lock().unwrap().schema() { + Ok(_) => { + let res = HttpResponse::Ok().await.unwrap(); + Ok(res) + } + Err(e) => { + let res = HttpResponse::InternalServerError().body(format!("{}", e)); + Ok(res) + } + } +} #[derive(Deserialize)] pub struct SearchQueryParams { @@ -30,256 +154,76 @@ pub struct SearchQueryParams { facet_prefix: Option>, } -pub async fn route(req: Request, mut index_client: IndexClient) -> Result> { - match req.uri().path() { - path => { - if RE_DOCUMENTS.is_match(path) { - let caps = RE_DOCUMENTS.captures(path).unwrap(); - let id: &str = &caps["id"]; - let id = String::from(id); - match req.method() { - &Method::GET => match index_client.get(id) { - Ok(s) => Ok(Response::builder() - .status(StatusCode::OK) - .header(header::CONTENT_TYPE, "application/json") - .body(Body::from(s)) - .unwrap()), - Err(e) => Ok(Response::builder() - .status(StatusCode::INTERNAL_SERVER_ERROR) - .body(Body::from(e.to_string())) - .unwrap()), - }, - &Method::PUT => { - let bytes = hyper::body::to_bytes(req.into_body()).await.unwrap(); - let json_str = String::from_utf8(bytes.to_vec()).unwrap(); - let mut value: Value = serde_json::from_str(json_str.as_str()).unwrap(); - value["_id"] = Value::String(id); - let doc = serde_json::to_string(&value).unwrap(); +#[post("/v1/search")] +pub async fn search( + state: AppState, + req: HttpRequest, + body: web::Bytes, +) -> Result { + let params = serde_qs::from_str::(&req.query_string()).unwrap(); - match index_client.set(doc) { - Ok(_) => Ok(Response::builder() - .status(StatusCode::OK) - .body(Body::empty()) - .unwrap()), - Err(e) => Ok(Response::builder() - .status(StatusCode::INTERNAL_SERVER_ERROR) - .body(Body::from(e.to_string())) - .unwrap()), - } - } - &Method::DELETE => match index_client.delete(id) { - Ok(_) => Ok(Response::builder() - .status(StatusCode::OK) - .body(Body::empty()) - .unwrap()), - Err(e) => Ok(Response::builder() - .status(StatusCode::INTERNAL_SERVER_ERROR) - .body(Body::from(e.to_string())) - .unwrap()), - }, - _ => Ok(Response::builder() - .status(StatusCode::METHOD_NOT_ALLOWED) - .body(Body::empty()) - .unwrap()), - } - } else if RE_DOCUMENTS_BULK.is_match(path) { - match req.method() { - &Method::PUT => { - let bytes = hyper::body::to_bytes(req.into_body()).await.unwrap(); - let json_str = String::from_utf8(bytes.to_vec()).unwrap(); - let value: Value = serde_json::from_str(json_str.as_str()).unwrap(); - let docs = serde_json::to_string(&value).unwrap(); - - match index_client.bulk_set(docs) { - Ok(_) => Ok(Response::builder() - .status(StatusCode::OK) - .body(Body::empty()) - .unwrap()), - Err(e) => Ok(Response::builder() - .status(StatusCode::INTERNAL_SERVER_ERROR) - .body(Body::from(e.to_string())) - .unwrap()), - } - } - &Method::DELETE => { - let bytes = hyper::body::to_bytes(req.into_body()).await.unwrap(); - let json_str = String::from_utf8(bytes.to_vec()).unwrap(); - let value: Value = serde_json::from_str(json_str.as_str()).unwrap(); - let docs = serde_json::to_string(&value).unwrap(); + let mut from = 0; + if let Some(_from) = params.from { + from = _from; + } - match index_client.bulk_delete(docs) { - Ok(_) => Ok(Response::builder() - .status(StatusCode::OK) - .body(Body::empty()) - .unwrap()), - Err(e) => Ok(Response::builder() - .status(StatusCode::INTERNAL_SERVER_ERROR) - .body(Body::from(e.to_string())) - .unwrap()), - } - } - _ => Ok(Response::builder() - .status(StatusCode::METHOD_NOT_ALLOWED) - .body(Body::empty()) - .unwrap()), - } - } else if RE_COMMIT.is_match(path) { - match req.method() { - &Method::GET => match index_client.commit() { - Ok(_) => Ok(Response::builder() - .status(StatusCode::OK) - .body(Body::empty()) - .unwrap()), - Err(e) => Ok(Response::builder() - .status(StatusCode::INTERNAL_SERVER_ERROR) - .body(Body::from(e.to_string())) - .unwrap()), - }, - _ => Ok(Response::builder() - .status(StatusCode::METHOD_NOT_ALLOWED) - .body(Body::empty()) - .unwrap()), - } - } else if RE_ROLLBACK.is_match(path) { - match req.method() { - &Method::GET => match index_client.rollback() { - Ok(_) => Ok(Response::builder() - .status(StatusCode::OK) - .body(Body::empty()) - .unwrap()), - Err(e) => Ok(Response::builder() - .status(StatusCode::INTERNAL_SERVER_ERROR) - .body(Body::from(e.to_string())) - .unwrap()), - }, - _ => Ok(Response::builder() - .status(StatusCode::METHOD_NOT_ALLOWED) - .body(Body::empty()) - .unwrap()), - } - } else if RE_MERGE.is_match(path) { - match req.method() { - &Method::GET => match index_client.merge() { - Ok(_) => Ok(Response::builder() - .status(StatusCode::OK) - .body(Body::empty()) - .unwrap()), - Err(e) => Ok(Response::builder() - .status(StatusCode::INTERNAL_SERVER_ERROR) - .body(Body::from(e.to_string())) - .unwrap()), - }, - _ => Ok(Response::builder() - .status(StatusCode::METHOD_NOT_ALLOWED) - .body(Body::empty()) - .unwrap()), - } - } else if RE_SCHEMA.is_match(path) { - match req.method() { - &Method::GET => match index_client.schema() { - Ok(s) => Ok(Response::builder() - .status(StatusCode::OK) - .header(header::CONTENT_TYPE, "application/json") - .body(Body::from(s)) - .unwrap()), - Err(e) => Ok(Response::builder() - .status(StatusCode::INTERNAL_SERVER_ERROR) - .body(Body::from(e.to_string())) - .unwrap()), - }, - _ => Ok(Response::builder() - .status(StatusCode::METHOD_NOT_ALLOWED) - .body(Body::empty()) - .unwrap()), - } - } else if RE_SEARCH.is_match(path) { - match req.method() { - &Method::POST => { - let params = - serde_qs::from_str::(req.uri().query().unwrap()) - .unwrap(); - let mut from = 0; - if let Some(_from) = params.from { - from = _from; - } + let mut limit = 10; + if let Some(_limit) = params.limit { + limit = _limit; + } - let mut limit = 10; - if let Some(_limit) = params.limit { - limit = _limit; - } + let mut exclude_count = false; + if let Some(_exclude_count) = params.exclude_count { + exclude_count = _exclude_count; + } - let mut exclude_count = false; - if let Some(_exclude_count) = params.exclude_count { - exclude_count = _exclude_count; - } + let mut exclude_docs = false; + if let Some(_exclude_docs) = params.exclude_docs { + exclude_docs = _exclude_docs; + } - let mut exclude_docs = false; - if let Some(_exclude_docs) = params.exclude_docs { - exclude_docs = _exclude_docs; - } + let mut facet_field = String::from(""); + if let Some(_facet_field) = params.facet_field { + facet_field = _facet_field; + } - let mut facet_field = String::from(""); - if let Some(_facet_field) = params.facet_field { - facet_field = _facet_field; - } + let mut facet_prefixes = Vec::new(); + if let Some(_facet_prefix) = params.facet_prefix { + facet_prefixes = _facet_prefix; + } - let mut facet_prefixes = Vec::new(); - if let Some(_facet_prefix) = params.facet_prefix { - facet_prefixes = _facet_prefix; - } + let query = String::from_utf8(body.to_vec()).unwrap(); - let bytes = hyper::body::to_bytes(req.into_body()).await.unwrap(); - let query = String::from_utf8(bytes.to_vec()).unwrap(); + match state.index_client.lock().unwrap().search( + query.as_str(), + from, + limit, + exclude_count, + exclude_docs, + facet_field.as_str(), + facet_prefixes, + ) { + Ok(s) => { + let res = HttpResponse::Ok().body(s); + Ok(res) + } + Err(e) => { + let res = HttpResponse::InternalServerError().body(format!("{}", e)); + Ok(res) + } + } +} - match index_client.search( - query.as_str(), - from, - limit, - exclude_count, - exclude_docs, - facet_field.as_str(), - facet_prefixes, - ) { - Ok(s) => Ok(Response::builder() - .status(StatusCode::OK) - .header(header::CONTENT_TYPE, "application/json") - .body(Body::from(s)) - .unwrap()), - Err(e) => Ok(Response::builder() - .status(StatusCode::INTERNAL_SERVER_ERROR) - .body(Body::from(e.to_string())) - .unwrap()), - } - } - _ => Ok(Response::builder() - .status(StatusCode::METHOD_NOT_ALLOWED) - .body(Body::empty()) - .unwrap()), - } - } else if RE_STATUS.is_match(path) { - match req.method() { - &Method::GET => match index_client.status() { - Ok(s) => Ok(Response::builder() - .status(StatusCode::OK) - .header(header::CONTENT_TYPE, "application/json") - .body(Body::from(s)) - .unwrap()), - Err(e) => Ok(Response::builder() - .status(StatusCode::INTERNAL_SERVER_ERROR) - .body(Body::from(e.to_string())) - .unwrap()), - }, - _ => Ok(Response::builder() - .status(StatusCode::METHOD_NOT_ALLOWED) - .body(Body::empty()) - .unwrap()), - } - } else { - Ok(Response::builder() - .status(StatusCode::NOT_FOUND) - .body(Body::empty()) - .unwrap()) - } +#[get("/v1/status")] +pub async fn status(state: AppState) -> Result { + match state.index_client.lock().unwrap().status() { + Ok(s) => { + let res = HttpResponse::Ok().body(s); + Ok(res) + } + Err(e) => { + let res = HttpResponse::InternalServerError().body(format!("{}", e)); + Ok(res) } } } diff --git a/bayard-rest/src/rest/server.rs b/bayard-rest/src/rest/server.rs index f59a758..4db7c1f 100644 --- a/bayard-rest/src/rest/server.rs +++ b/bayard-rest/src/rest/server.rs @@ -1,167 +1,213 @@ -use std::convert::TryFrom; -use std::io::{Error, ErrorKind}; -use std::net::SocketAddr; -use std::pin::Pin; -use std::task::{Context, Poll}; -use std::{fs, io, sync}; - -use futures_util::{ - future::{ready, TryFutureExt}, - stream::{Stream, StreamExt, TryStreamExt}, -}; -use hyper::service::{make_service_fn, service_fn}; -use hyper::Server; -use log::*; -use rustls::internal::pemfile; -use tokio::net::{TcpListener, TcpStream}; -use tokio_rustls::server::TlsStream; -use tokio_rustls::TlsAcceptor; +use std::fs::File; +use std::io; +use std::io::BufReader; +use std::iter::FromIterator; +use std::sync::Mutex; + +use actix_cors::Cors; +use actix_server::Server; +use actix_web::{middleware, web, App, HttpServer}; +use rustls::internal::pemfile::{certs, rsa_private_keys}; +use rustls::{NoClientAuth, ServerConfig}; use bayard_client::index::client::IndexClient; -use bayard_common::signal::shutdown_signal; - -use crate::rest::handler::{route, GenericError}; - -#[tokio::main] -pub async fn run( - rest_address: &str, - index_address: &str, -) -> Result<(), Box> { - let index_client = IndexClient::new(index_address); - let address: SocketAddr = rest_address.parse().unwrap(); - - let rest_server = Server::bind(&address).serve(make_service_fn(|_| { - let index_client = index_client.clone(); - async move { Ok::<_, GenericError>(service_fn(move |req| route(req, index_client.clone()))) } - })); - - let rest_server_graceful = rest_server.with_graceful_shutdown(shutdown_signal()); - info!("start rest service on {}", rest_address); - - match rest_server_graceful.await { - Ok(_) => { - info!("stop rest service on {}", rest_address); - Ok(()) - } - Err(e) => { - error!( - "failed to stop rest service on {}: error={}", - rest_address, e - ); - Err(Box::try_from(Error::new(ErrorKind::Other, e)).unwrap()) - } - } -} -#[tokio::main] -pub async fn run_tls( - rest_address: &str, - index_address: &str, - cert_file: &str, - key_file: &str, -) -> Result<(), Box> { - let index_client = IndexClient::new(index_address); - let address: SocketAddr = rest_address.parse().unwrap(); - - let tls_cfg = { - let certs = load_certs(cert_file)?; - let key = load_private_key(key_file)?; - let mut cfg = rustls::ServerConfig::new(rustls::NoClientAuth::new()); - cfg.set_single_cert(certs, key) - .map_err(|e| error(format!("{}", e)))?; - cfg.set_protocols(&[b"h2".to_vec(), b"http/1.1".to_vec()]); - sync::Arc::new(cfg) - }; - - let mut tcp_listener = TcpListener::bind(&address).await.unwrap(); - let tls_acceptor = TlsAcceptor::from(tls_cfg); - let incoming_tls_stream = tcp_listener - .incoming() - .map_err(|e| error(format!("Incoming failed: {:?}", e))) - .and_then(move |s| { - tls_acceptor.accept(s).map_err(|e| { - error!("TLS Error: {:?}", e); - error(format!("TLS Error: {:?}", e)) - }) - }) - .filter(|res| { - // Ignore failed accepts - ready(res.is_ok()) - }) - .boxed(); +use crate::rest::handler::{ + bulk_delete, bulk_set, commit, delete, get, merge, rollback, schema, search, set, status, +}; - let rest_server = - Server::builder(HyperAcceptor { - acceptor: incoming_tls_stream, - }) - .serve(make_service_fn(|_| { - let index_client = index_client.clone(); - async move { - Ok::<_, GenericError>(service_fn(move |req| route(req, index_client.clone()))) - } - })); - - let rest_server_graceful = rest_server.with_graceful_shutdown(shutdown_signal()); - info!("start rest service on {}", rest_address); - - match rest_server_graceful.await { - Ok(_) => { - info!("stop rest service on {}", rest_address); - Ok(()) - } - Err(e) => { - error!( - "failed to stop rest service on {}: error={}", - rest_address, e - ); - Err(Box::try_from(Error::new(ErrorKind::Other, e)).unwrap()) - } - } +pub struct AppData { + pub index_client: Mutex, } -// Load public certificate from file. -fn load_certs(filename: &str) -> io::Result> { - // Open certificate file. - let certfile = fs::File::open(filename) - .map_err(|e| error(format!("failed to open {}: {}", filename, e)))?; - let mut reader = io::BufReader::new(certfile); +pub type AppState = web::Data; - // Load and return certificate. - pemfile::certs(&mut reader).map_err(|_| error("failed to load certificate".into())) +pub struct RestServer { + server: Server, } -// Load private key from file. -fn load_private_key(filename: &str) -> io::Result { - // Open keyfile. - let keyfile = fs::File::open(filename) - .map_err(|e| error(format!("failed to open {}: {}", filename, e)))?; - let mut reader = io::BufReader::new(keyfile); - - // Load and return a single private key. - let keys = pemfile::rsa_private_keys(&mut reader) - .map_err(|_| error("failed to load private key".into()))?; - if keys.len() != 1 { - return Err(error("expected a single private key".into())); +impl RestServer { + pub fn new(address: &str, index_address: &str, worker_num: usize) -> RestServer { + let index_client = IndexClient::new(index_address); + let app_data = web::Data::new(AppData { + index_client: Mutex::new(index_client), + }); + + let server = HttpServer::new(move || { + App::new() + .app_data(app_data.clone()) + .wrap(middleware::DefaultHeaders::new().header("X-Version", "0.2")) + .wrap(middleware::Compress::default()) + .wrap(middleware::Logger::default()) + .service(get) + .service(set) + .service(delete) + .service(bulk_set) + .service(bulk_delete) + .service(commit) + .service(rollback) + .service(merge) + .service(schema) + .service(search) + .service(status) + }) + .bind(address) + .unwrap() + .workers(worker_num) + .run(); + + RestServer { server } } - Ok(keys[0].clone()) -} -fn error(err: String) -> Error { - Error::new(ErrorKind::Other, err) -} + pub fn new_cors( + address: &str, + index_address: &str, + worker_num: usize, + cors_origin: String, + cors_methods: Vec, + cors_headers: Vec, + ) -> RestServer { + let index_client = IndexClient::new(index_address); + let app_data = web::Data::new(AppData { + index_client: Mutex::new(index_client), + }); + + let server = HttpServer::new(move || { + App::new() + .app_data(app_data.clone()) + .wrap(middleware::DefaultHeaders::new().header("X-Version", "0.2")) + .wrap(middleware::Compress::default()) + .wrap(middleware::Logger::default()) + .wrap( + Cors::new() + .allowed_origin(cors_origin.as_ref()) + .allowed_methods(Vec::from_iter(cors_methods.iter().map(String::as_str))) + .allowed_headers(Vec::from_iter(cors_headers.iter().map(String::as_str))) + .finish(), + ) + .service(get) + .service(set) + .service(delete) + .service(bulk_set) + .service(bulk_delete) + .service(commit) + .service(rollback) + .service(merge) + .service(schema) + .service(search) + .service(status) + }) + .bind(address) + .unwrap() + .workers(worker_num) + .run(); -struct HyperAcceptor<'a> { - acceptor: Pin, io::Error>> + 'a>>, -} + RestServer { server } + } + + pub fn new_tls( + address: &str, + index_address: &str, + worker_num: usize, + cert_file: &str, + key_file: &str, + ) -> RestServer { + let index_client = IndexClient::new(index_address); + let app_data = web::Data::new(AppData { + index_client: Mutex::new(index_client), + }); + + // load ssl keys + let mut config = ServerConfig::new(NoClientAuth::new()); + let cert = &mut BufReader::new(File::open(cert_file).unwrap()); + let key = &mut BufReader::new(File::open(key_file).unwrap()); + let cert_chain = certs(cert).unwrap(); + let mut keys = rsa_private_keys(key).unwrap(); + config.set_single_cert(cert_chain, keys.remove(0)).unwrap(); + + let server = HttpServer::new(move || { + App::new() + .app_data(app_data.clone()) + .wrap(middleware::DefaultHeaders::new().header("X-Version", "0.2")) + .wrap(middleware::Compress::default()) + .wrap(middleware::Logger::default()) + .service(get) + .service(set) + .service(delete) + .service(bulk_set) + .service(bulk_delete) + .service(commit) + .service(rollback) + .service(merge) + .service(schema) + .service(search) + .service(status) + }) + .bind_rustls(address, config) + .unwrap() + .workers(worker_num) + .run(); -impl hyper::server::accept::Accept for HyperAcceptor<'_> { - type Conn = TlsStream; - type Error = io::Error; + RestServer { server } + } + + pub fn new_cors_tls( + address: &str, + index_address: &str, + worker_num: usize, + cors_origin: String, + cors_methods: Vec, + cors_headers: Vec, + cert_file: &str, + key_file: &str, + ) -> RestServer { + let index_client = IndexClient::new(index_address); + let app_data = web::Data::new(AppData { + index_client: Mutex::new(index_client), + }); + + // load ssl keys + let mut config = ServerConfig::new(NoClientAuth::new()); + let cert = &mut BufReader::new(File::open(cert_file).unwrap()); + let key = &mut BufReader::new(File::open(key_file).unwrap()); + let cert_chain = certs(cert).unwrap(); + let mut keys = rsa_private_keys(key).unwrap(); + config.set_single_cert(cert_chain, keys.remove(0)).unwrap(); + + let server = HttpServer::new(move || { + App::new() + .app_data(app_data.clone()) + .wrap(middleware::DefaultHeaders::new().header("X-Version", "0.2")) + .wrap(middleware::Compress::default()) + .wrap(middleware::Logger::default()) + .wrap( + Cors::new() + .allowed_origin(cors_origin.as_ref()) + .allowed_methods(Vec::from_iter(cors_methods.iter().map(String::as_str))) + .allowed_headers(Vec::from_iter(cors_headers.iter().map(String::as_str))) + .finish(), + ) + .service(get) + .service(set) + .service(delete) + .service(bulk_set) + .service(bulk_delete) + .service(commit) + .service(rollback) + .service(merge) + .service(schema) + .service(search) + .service(status) + }) + .bind_rustls(address, config) + .unwrap() + .workers(worker_num) + .run(); + + RestServer { server } + } - fn poll_accept( - mut self: Pin<&mut Self>, - cx: &mut Context, - ) -> Poll>> { - Pin::new(&mut self.acceptor).poll_next(cx) + pub async fn shutdown(&mut self) -> io::Result<()> { + Ok(self.server.stop(true).await) } }