From: Adrián Arroyo Calle Date: Tue, 5 Sep 2023 18:40:58 +0000 (+0200) Subject: Replace Hyper with Warp for HTTP server X-Git-Tag: remove~76^2 X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=660860bccf1b000a8ba707a37579956deb1c208f;p=scryer-prolog.git Replace Hyper with Warp for HTTP server - Use Warp - Optimize clones - HTTPS server - Content-Length limit - HTTP Basic Auth - Stop server with Ctrl-C --- diff --git a/Cargo.lock b/Cargo.lock index c383d82e..e054b9db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -81,9 +81,15 @@ checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" [[package]] name = "base64" -version = "0.21.3" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "414dcefbc63d77c526a76b3afcf6fbb9b5e2791c19c3aa2297733208750c6e53" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" [[package]] name = "bit-set" @@ -188,9 +194,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "byte-tools" @@ -206,9 +212,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cc" @@ -227,17 +233,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.26" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", - "time", "wasm-bindgen", - "winapi", + "windows-targets", ] [[package]] @@ -365,19 +370,19 @@ dependencies = [ [[package]] name = "ctrlc" -version = "3.4.0" +version = "3.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a011bbe2c35ce9c1f143b7af6f94f29a167beb4cd1d29e6740ce836f723120e" +checksum = "82e95fbd621905b854affdc67943b043a0fbb6ed7385fd5a25650d19a8a6cfdf" dependencies = [ - "nix", + "nix 0.27.1", "windows-sys", ] [[package]] name = "dashmap" -version = "5.5.1" +version = "5.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edd72493923899c6f10c641bdbdeddc7183d6396641d99c1a0d1597f37f92e28" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", "hashbrown 0.14.0", @@ -551,9 +556,9 @@ checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" [[package]] name = "errno" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" +checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" dependencies = [ "errno-dragonfly", "libc", @@ -699,7 +704,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.37", ] [[package]] @@ -769,7 +774,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "wasm-bindgen", ] @@ -832,6 +837,30 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +[[package]] +name = "headers" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" +dependencies = [ + "base64 0.21.4", + "bytes", + "headers-core", + "http", + "httpdate", + "mime", + "sha1", +] + +[[package]] +name = "headers-core" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +dependencies = [ + "http", +] + [[package]] name = "heck" version = "0.3.3" @@ -843,9 +872,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" [[package]] name = "home" @@ -903,29 +932,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "http-body" -version = "1.0.0-rc.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "951dfc2e32ac02d67c90c0d65bd27009a635dc9b381a2cc7d284ab01e3a0150d" -dependencies = [ - "bytes", - "http", -] - -[[package]] -name = "http-body-util" -version = "0.1.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08ef12f041acdd397010e5fb6433270c147d3b8b2d0a840cd7fff8e531dca5c8" -dependencies = [ - "bytes", - "futures-util", - "http", - "http-body 1.0.0-rc.2", - "pin-project-lite", -] - [[package]] name = "httparse" version = "1.8.0" @@ -950,7 +956,7 @@ dependencies = [ "futures-util", "h2", "http", - "http-body 0.4.5", + "http-body", "httparse", "httpdate", "itoa", @@ -962,28 +968,6 @@ dependencies = [ "want", ] -[[package]] -name = "hyper" -version = "1.0.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b75264b2003a3913f118d35c586e535293b3e22e41f074930762929d071e092" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body 1.0.0-rc.2", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "tokio", - "tracing", - "want", -] - [[package]] name = "hyper-tls" version = "0.5.0" @@ -991,7 +975,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes", - "hyper 0.14.27", + "hyper", "native-tls", "tokio", "tokio-native-tls", @@ -1119,9 +1103,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.147" +version = "0.2.148" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" [[package]] name = "libffi" @@ -1154,9 +1138,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" +checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" [[package]] name = "lock_api" @@ -1214,9 +1198,9 @@ checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" [[package]] name = "memchr" -version = "2.5.0" +version = "2.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" [[package]] name = "mime" @@ -1224,6 +1208,16 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "miniz_oxide" version = "0.7.1" @@ -1253,7 +1247,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "windows-sys", ] @@ -1285,6 +1279,24 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "multer" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01acbdc23469fd8fe07ab135923371d5f5a422fbf9c522158677c8eb15bc51c2" +dependencies = [ + "bytes", + "encoding_rs", + "futures-util", + "http", + "httparse", + "log", + "memchr", + "mime", + "spin 0.9.8", + "version_check", +] + [[package]] name = "native-tls" version = "0.2.11" @@ -1320,9 +1332,20 @@ dependencies = [ [[package]] name = "nix" -version = "0.26.3" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abbbc55ad7b13aac85f9401c796dcda1b864e07fcad40ad47792eaa8932ea502" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", +] + +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ "bitflags 2.4.0", "cfg-if", @@ -1374,9 +1397,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" dependencies = [ "memchr", ] @@ -1416,7 +1439,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.37", ] [[package]] @@ -1427,9 +1450,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.92" +version = "0.9.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db7e971c2c2bba161b2d2fdf37080177eff520b3bc044787c7f1f5f9e78d869b" +checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" dependencies = [ "cc", "libc", @@ -1582,6 +1605,26 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pin-project" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", +] + [[package]] name = "pin-project-lite" version = "0.2.13" @@ -1647,9 +1690,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" dependencies = [ "unicode-ident", ] @@ -1756,15 +1799,15 @@ version = "0.11.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" dependencies = [ - "base64 0.21.3", + "base64 0.21.4", "bytes", "encoding_rs", "futures-core", "futures-util", "h2", "http", - "http-body 0.4.5", - "hyper 0.14.27", + "http-body", + "hyper", "hyper-tls", "ipnet", "js-sys", @@ -1796,7 +1839,7 @@ dependencies = [ "cc", "libc", "once_cell", - "spin", + "spin 0.5.2", "untrusted", "web-sys", "winapi", @@ -1812,7 +1855,7 @@ dependencies = [ "getrandom", "libc", "once_cell", - "spin", + "spin 0.5.2", "untrusted", "web-sys", "winapi", @@ -1846,9 +1889,9 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" -version = "0.38.9" +version = "0.38.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bfe0f2582b4931a45d1fa608f8a8722e8b3c7ac54dd6d5f3b3212791fedef49" +checksum = "747c788e9ce8e92b12cd485c49ddf90723550b654b32508f979b71a7b1ecda4f" dependencies = [ "bitflags 2.4.0", "errno", @@ -1857,6 +1900,27 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "rustls" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" +dependencies = [ + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" +dependencies = [ + "base64 0.21.4", +] + [[package]] name = "rustversion" version = "1.0.14" @@ -1877,7 +1941,7 @@ dependencies = [ "libc", "log", "memchr", - "nix", + "nix 0.26.4", "radix_trie", "scopeguard", "unicode-segmentation", @@ -1910,6 +1974,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "scopeguard" version = "1.2.0" @@ -1942,8 +2012,6 @@ dependencies = [ "getrandom", "git-version", "hostname", - "http-body-util", - "hyper 1.0.0-rc.3", "indexmap", "lazy_static", "lexical", @@ -1975,16 +2043,27 @@ dependencies = [ "static_assertions", "strum", "strum_macros", - "syn 2.0.32", + "syn 2.0.37", "to-syn-value", "to-syn-value_derive", "tokio", "walkdir", + "warp", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", ] +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "security-framework" version = "2.9.2" @@ -2047,14 +2126,14 @@ checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.37", ] [[package]] name = "serde_json" -version = "1.0.105" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ "itoa", "ryu", @@ -2095,7 +2174,18 @@ checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.37", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", ] [[package]] @@ -2179,9 +2269,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" [[package]] name = "socket2" @@ -2195,9 +2285,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" +checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" dependencies = [ "libc", "windows-sys", @@ -2209,6 +2299,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "static_assertions" version = "1.1.0" @@ -2291,9 +2387,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.32" +version = "2.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2" +checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" dependencies = [ "proc-macro2", "quote", @@ -2338,33 +2434,22 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "thiserror" -version = "1.0.47" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f" +checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.47" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b" +checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", -] - -[[package]] -name = "time" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", + "syn 2.0.37", ] [[package]] @@ -2388,7 +2473,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfcc684f2ceaec3b4e8689657c9e0944b07bf5e34563e0bd758c4d42c05c82ed" dependencies = [ - "syn 2.0.32", + "syn 2.0.37", "to-syn-value_derive", ] @@ -2400,7 +2485,7 @@ checksum = "3dfffda778de8443144ff3b042ddf14e8bc5445f0fd9fe937c3d252535dc9212" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.37", ] [[package]] @@ -2417,7 +2502,7 @@ dependencies = [ "parking_lot 0.12.1", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.3", + "socket2 0.5.4", "tokio-macros", "windows-sys", ] @@ -2430,7 +2515,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.37", ] [[package]] @@ -2443,11 +2528,45 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +dependencies = [ + "rustls", + "tokio", + "webpki", +] + +[[package]] +name = "tokio-stream" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54319c93411147bced34cb5609a80e0a8e44c5999c93903a81cd866630ec0bfd" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite", +] + [[package]] name = "tokio-util" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d" dependencies = [ "bytes", "futures-core", @@ -2470,6 +2589,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", + "log", "pin-project-lite", "tracing-core", ] @@ -2489,11 +2609,39 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +[[package]] +name = "tungstenite" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ee6ab729cd4cf0fd55218530c4522ed30b7b6081752839b68fcec8d0960788" +dependencies = [ + "base64 0.13.1", + "byteorder", + "bytes", + "http", + "httparse", + "log", + "rand", + "sha1", + "thiserror", + "url", + "utf-8", +] + [[package]] name = "typenum" -version = "1.16.0" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicase" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] [[package]] name = "unicode-bidi" @@ -2503,9 +2651,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" @@ -2524,9 +2672,9 @@ checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" [[package]] name = "unicode-width" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" [[package]] name = "untrusted" @@ -2536,9 +2684,9 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" dependencies = [ "form_urlencoded", "idna", @@ -2580,9 +2728,9 @@ dependencies = [ [[package]] name = "walkdir" -version = "2.3.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" dependencies = [ "same-file", "winapi-util", @@ -2598,10 +2746,36 @@ dependencies = [ ] [[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" +name = "warp" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" +checksum = "ba431ef570df1287f7f8b07e376491ad54f84d26ac473489427231e1718e1f69" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "headers", + "http", + "hyper", + "log", + "mime", + "mime_guess", + "multer", + "percent-encoding", + "pin-project", + "rustls-pemfile", + "scoped-tls", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-rustls", + "tokio-stream", + "tokio-tungstenite", + "tokio-util", + "tower-service", + "tracing", +] [[package]] name = "wasi" @@ -2630,7 +2804,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.37", "wasm-bindgen-shared", ] @@ -2664,7 +2838,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.37", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2685,6 +2859,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0e74f82d49d545ad128049b7e88f6576df2da6b02e9ce565c6f533be576957e" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "winapi" version = "0.3.9" @@ -2703,9 +2887,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi", ] diff --git a/Cargo.toml b/Cargo.toml index 92b35ded..dbf7cfc0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ ffi = ["dep:libffi"] repl = ["dep:crossterm", "dep:ctrlc", "dep:rustyline"] hostname = ["dep:hostname"] tls = ["dep:native-tls"] -http = ["dep:hyper", "dep:reqwest"] +http = ["dep:warp", "dep:reqwest"] rust_beta_channel = [] crypto-full = [] @@ -66,7 +66,6 @@ ryu = "1.0.9" futures = "0.3" libloading = "0.7" derive_deref = "1.1.1" -http-body-util = "0.1.0-rc.2" bytes = "1" dashu = "0.4.0" num-order = { version = "1.2.0" } @@ -79,7 +78,7 @@ crossterm = { version = "0.20.0", optional = true } ctrlc = { version = "3.2.2", optional = true } rustyline = { version = "12.0.0", optional = true } native-tls = { version = "0.2.4", optional = true } -hyper = { version = "=1.0.0-rc.3", features = ["full"], optional = true } +warp = { version = "=0.3.5", features = ["tls"], optional = true } reqwest = { version = "0.11.18", features = ["blocking"], optional = true } tokio = { version = "1.28.2", features = ["full"] } diff --git a/README.md b/README.md index 4ba1f9d6..81236b65 100644 --- a/README.md +++ b/README.md @@ -624,7 +624,7 @@ The modules that ship with Scryer Prolog are also called Probabilistic predicates and random number generators. * [`http/http_open`](src/lib/http/http_open.pl) Open a stream to read answers from web servers. HTTPS is also supported. -* [`http/http_server`](src/lib/http/http_server.pl) Runs a HTTP/1.1 and HTTP/2.0 web server. Uses [Hyper](https://hyper.rs) as a backend. Supports some query and form handling. +* [`http/http_server`](src/lib/http/http_server.pl) Runs a HTTP/1.1 and HTTP/2.0 web server. Uses [Warp](https://github.com/seanmonstar/warp) as a backend. Supports some query and form handling. * [`sgml`](src/lib/sgml.pl) `load_html/3` and `load_xml/3` represent HTML and XML documents as Prolog terms for convenient and efficient reasoning. Use diff --git a/build/instructions_template.rs b/build/instructions_template.rs index f0a5a49f..894254c7 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -558,7 +558,7 @@ enum SystemClauseType { DeterministicLengthRundown, #[strum_discriminants(strum(props(Arity = "7", Name = "$http_open")))] HttpOpen, - #[strum_discriminants(strum(props(Arity = "2", Name = "$http_listen")))] + #[strum_discriminants(strum(props(Arity = "5", Name = "$http_listen")))] HttpListen, #[strum_discriminants(strum(props(Arity = "7", Name = "$http_accept")))] HttpAccept, diff --git a/src/http.rs b/src/http.rs index 13d6ff31..46888f12 100644 --- a/src/http.rs +++ b/src/http.rs @@ -1,55 +1,23 @@ -use bytes::Bytes; -use http_body_util::Full; -use hyper::service::Service; -use hyper::{body::Incoming as IncomingBody, Request, Response}; -use std::future::Future; -use std::pin::Pin; -use std::sync::{Arc, Condvar, Mutex}; +use std::sync::{Arc, Mutex, Condvar}; +use std::io::BufRead; + +use warp::http; pub struct HttpListener { pub incoming: std::sync::mpsc::Receiver, } -#[derive(Debug)] pub struct HttpRequest { - pub request: Request, + pub request_data: HttpRequestData, pub response: HttpResponse, } -pub type HttpResponse = Arc<(Mutex, Mutex>>>, Condvar)>; - -pub struct HttpService { - pub tx: std::sync::mpsc::SyncSender, -} - -impl Service> for HttpService { - type Response = Response>; - type Error = hyper::Error; - type Future = Pin> + Send>>; - - fn call(&mut self, req: Request) -> Self::Future { - // new connection! - // we send the Request info to Prolog - let response = Arc::new((Mutex::new(false), Mutex::new(None), Condvar::new())); - let http_request = HttpRequest { - request: req, - response: Arc::clone(&response), - }; - self.tx.send(http_request).unwrap(); +pub type HttpResponse = Arc<(Mutex, Mutex>, Condvar)>; - // we wait for the Response info from Prolog - { - let (ready, _response, cvar) = &*response; - let mut ready = ready.lock().unwrap(); - while !*ready { - ready = cvar.wait(ready).unwrap(); - } - } - { - let (_, response, _) = &*response; - let response = response.lock().unwrap().take(); - let res = response.expect("Data race error in HTTP Server"); - Box::pin(async move { Ok(res) }) - } - } +pub struct HttpRequestData { + pub method: http::Method, + pub headers: http::HeaderMap, + pub path: String, + pub query: String, + pub body: Box, } diff --git a/src/lib/http/http_server.pl b/src/lib/http/http_server.pl index 381d5607..faa76ce5 100644 --- a/src/lib/http/http_server.pl +++ b/src/lib/http/http_server.pl @@ -6,8 +6,8 @@ */ /** This library provides an starting point to build HTTP server based applications. -It is based on [Hyper](https://hyper.rs/), which allows for HTTP/1.0, HTTP/1.1 and HTTP/2. However, -some advanced features that Hyper provides are still not accesible. +It is based on [Warp](https://github.com/seanmonstar/warp), which allows for HTTP/1.0, HTTP/1.1 and HTTP/2. However, +some advanced features that Warp provides are still not accesible. ## Usage @@ -46,7 +46,6 @@ recommeded to use the helper predicates, which are easier to understand and clea Some things that are still missing: - Read forms in multipart format - - HTTP Basic Auth - Session handling via cookies - HTML Templating (but you can use [Teruel](https://github.com/aarroyoc/teruel/), [Marquete](https://github.com/aarroyoc/marquete/) or [Djota](https://github.com/aarroyoc/djota) for that) */ @@ -54,14 +53,19 @@ Some things that are still missing: :- module(http_server, [ http_listen/2, + http_listen/3, http_headers/2, http_status_code/2, http_body/2, http_redirect/2, - http_query/3 + http_query/3, + http_basic_auth/4 ]). :- meta_predicate http_listen(?, :). +:- meta_predicate http_listen(?, :, ?). + +:- meta_predicate http_basic_auth(:, :, ?, ?). :- use_module(library(charsio)). :- use_module(library(crypto)). @@ -74,25 +78,58 @@ Some things that are still missing: %% http_listen(+Port, +Handlers). % +% Equivalent to `http_listen(Port, Handlers, [])`. +http_listen(Port, Module:Handlers0) :- + must_be(integer, Port), + must_be(list, Handlers0), + maplist(module_qualification(Module), Handlers0, Handlers), + http_listen_(Port, Handlers, []). + +%% http_listen(+Port, +Handlers, +Options). +% % Listens for HTTP connections on port Port. Each handler on the list Handlers should be of the form: `HttpVerb(PathUnification, Predicate)`. % For example: `get(user/User, get_info(User))` will match an HTTP request that is a GET, the path unifies with /user/User (where User is a variable) -% and it will call `get_info` with three arguments: an `http_request` term, an `http_response` term and User. -http_listen(Port, Module:Handlers0) :- +% and it will call `get_info` with three arguments: an `http_request` term, an `http_response` term and User. +% +% The following options are supported: +% +% - `tls_key(+Key)` - a TLS key for HTTPS (string) +% - `tls_cert(+Cert)` - a TLS cert for HTTPS (string) +% - `content_length_limit(+Limit)` - maximum length (in bytes) for the incoming bodies. By default, 32KB. +% +% In order to have a HTTPS server (instead of plain HTTP), both `tls_key` and `tls_cert` options must be provided. +http_listen(Port, Module:Handlers0, Options) :- must_be(integer, Port), must_be(list, Handlers0), + must_be(list, Options), maplist(module_qualification(Module), Handlers0, Handlers), - http_listen_(Port, Handlers). + http_listen_(Port, Handlers, Options). module_qualification(M, H0, H) :- H0 =.. [Method, Path, Goal], H =.. [Method, Path, M:Goal]. -http_listen_(Port, Handlers) :- +http_listen_(Port, Handlers, Options) :- + parse_options(Options, TLSKey, TLSCert, ContentLengthLimit), phrase(format_("0.0.0.0:~d", [Port]), Addr), - '$http_listen'(Addr, HttpListener),!, + '$http_listen'(Addr, HttpListener, TLSKey, TLSCert, ContentLengthLimit),!, format("Listening at ~s\n", [Addr]), http_loop(HttpListener, Handlers). +parse_options(Options, TLSKey, TLSCert, ContentLengthLimit) :- + member_option_default(tls_key, Options, "", TLSKey), + member_option_default(tls_cert, Options, "", TLSCert), + member_option_default(content_length_limit, Options, 32768, ContentLengthLimit), + must_be(integer, ContentLengthLimit). + +member_option_default(Key, List, _Default, Value) :- + X =.. [Key, Value], + member(X, List). +member_option_default(Key, List, Default, Default) :- + X =.. [Key, _], + \+ member(X, List). + + http_loop(HttpListener, Handlers) :- '$http_accept'(HttpListener, RequestMethod, RequestPath, RequestHeaders, RequestQuery, RequestStream, ResponseHandle), current_time(Time), @@ -114,7 +151,7 @@ http_loop(HttpListener, Handlers) :- ) ; ( '$http_answer'(ResponseHandle, 404, [], ResponseStream), - call_cleanup(format(ResponseStream, "Not Found"), close(ResponseStream))) + call_cleanup(format(ResponseStream, "Not Found", []), close(ResponseStream))) ), http_loop(HttpListener, Handlers). @@ -352,3 +389,49 @@ url_decode([Char|Chars]) --> url_decode(Chars). url_decode([]) --> []. + +%% http_basic_auth(+LoginPredicate, +Handler, +Request, -Response) +% +% Metapredicate that wraps an existing Handler with an HTTP Basic Auth flow. +% Checks if a given user + password is authorized to execute that handler, returning 401 +% if it's not satisfied. +% +% `LoginPredicate` must be a predicate of arity 2 that takes a User and a Password. +% `Handler` will have, in addition to the Request and Response arguments, a User argument +% containing the User given in the authentication. +% +% Example: +% +% ``` +% main :- +% http_listen(8800,[get('/', http_basic_auth(login, inside_handler("data")))]). +% +% login(User, Pass) :- +% User = "aarroyoc", +% Pass = "123456". +% +% inside_handler(Data, User, Request, Response) :- +% http_body(Response, text(User)). +% ``` +http_basic_auth(LoginPredicate, Handler, Request, Response) :- + http_headers(Request, Headers), + member("authorization"-AuthorizationStr, Headers), + append("Basic ", Coded, AuthorizationStr), + chars_base64(UserPass, Coded, []), + append(User, [':'|Password], UserPass), + ( + call(LoginPredicate, User, Password) -> + call(Handler, User, Request, Response) + ; http_basic_auth_unauthorized_response(Response) + ). + +http_basic_auth(_LoginPredicate, _Handler, Request, Response) :- + http_headers(Request, Headers), + \+ member("authorization"-_, Headers), + http_basic_auth_unauthorized_response(Response). + +http_basic_auth_unauthorized_response(Response) :- + http_status_code(Response, 401), + http_headers(Response, ["www-authenticate"-"Basic realm=\"Scryer Prolog\", charset=\"UTF-8\""]), + http_body(Response, text("Unauthorized")). + diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index 89174091..1ac43189 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -5499,6 +5499,17 @@ impl Machine { if interruption { self.machine_st.throw_interrupt_exception(); self.machine_st.backtrack(); + + #[cfg(not(target_arch = "wasm32"))] + let runtime = tokio::runtime::Runtime::new().unwrap(); + #[cfg(target_arch = "wasm32")] + let runtime = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap(); + + let old_runtime = std::mem::replace(&mut self.runtime, runtime); + old_runtime.shutdown_background(); } } Err(_) => unreachable!(), diff --git a/src/machine/streams.rs b/src/machine/streams.rs index b5632417..1dfbfaa9 100644 --- a/src/machine/streams.rs +++ b/src/machine/streams.rs @@ -30,6 +30,9 @@ use std::ptr; #[cfg(feature = "tls")] use native_tls::TlsStream; +#[cfg(feature = "http")] +use warp::hyper; + #[derive(Debug, BitfieldSpecifier, Clone, Copy, PartialEq, Eq, Hash)] #[bits = 1] pub enum StreamType { @@ -290,9 +293,9 @@ impl Read for HttpReadStream { #[cfg(feature = "http")] pub struct HttpWriteStream { status_code: u16, - headers: hyper::HeaderMap, + headers: mem::ManuallyDrop, response: TypedArenaPtr, - buffer: Vec, + buffer: mem::ManuallyDrop>, } #[cfg(feature = "http")] @@ -312,24 +315,33 @@ impl Write for HttpWriteStream { #[inline] fn flush(&mut self) -> std::io::Result<()> { - let (ready, response, cvar) = &**self.response; - - let mut ready = ready.lock().unwrap(); - { - let mut response = response.lock().unwrap(); - - let bytes = bytes::Bytes::copy_from_slice(&self.buffer); - let mut response_ = hyper::Response::builder().status(self.status_code); - *response_.headers_mut().unwrap() = self.headers.clone(); - *response = Some(response_.body(http_body_util::Full::new(bytes)).unwrap()); - } - *ready = true; - cvar.notify_one(); + Ok(()) + } +} - Ok(()) +#[cfg(feature = "http")] +impl HttpWriteStream { + fn drop(&mut self) { + let headers = unsafe { mem::ManuallyDrop::take(&mut self.headers) }; + let buffer = unsafe { mem::ManuallyDrop::take(&mut self.buffer) }; + + let (ready, response, cvar) = &**self.response; + + let mut ready = ready.lock().unwrap(); + { + let mut response = response.lock().unwrap(); + + let mut response_ = warp::http::Response::builder() + .status(self.status_code); + *response_.headers_mut().unwrap() = headers; + *response = Some(response_.body(warp::hyper::Body::from(buffer)).unwrap()); + } + *ready = true; + cvar.notify_one(); } } + #[derive(Debug)] pub struct StandardOutputStream {} @@ -1243,15 +1255,15 @@ impl Stream { headers: hyper::HeaderMap, arena: &mut Arena, ) -> Self { - Stream::HttpWrite(arena_alloc!( - StreamLayout::new(CharReader::new(HttpWriteStream { - response, - status_code, - headers, - buffer: Vec::new(), - })), - arena - )) + Stream::HttpWrite(arena_alloc!( + StreamLayout::new(CharReader::new(HttpWriteStream { + response, + status_code, + headers: mem::ManuallyDrop::new(headers), + buffer: mem::ManuallyDrop::new(Vec::new()), + })), + arena + )) } #[inline] @@ -1299,7 +1311,8 @@ impl Stream { Ok(()) } #[cfg(feature = "http")] - Stream::HttpWrite(ref mut http_stream) => { + Stream::HttpWrite(ref mut http_stream) => { + http_stream.inner_mut().drop(); unsafe { http_stream.set_tag(ArenaHeaderTag::Dropped); std::ptr::drop_in_place(&mut http_stream.inner_mut().buffer as *mut _); diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 34b26808..897dab67 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -14,7 +14,7 @@ use crate::forms::*; use crate::heap_iter::*; use crate::heap_print::*; #[cfg(feature = "http")] -use crate::http::{HttpListener, HttpResponse, HttpService}; +use crate::http::{HttpRequestData, HttpListener, HttpResponse, HttpRequest}; use crate::instructions::*; use crate::machine; use crate::machine::code_walker::*; @@ -44,14 +44,14 @@ pub(crate) use ref_thread_local::RefThreadLocal; use std::cell::Cell; use std::cmp::Ordering; -use std::collections::BTreeSet; +use std::collections::{BTreeSet}; use std::convert::TryFrom; use std::env; #[cfg(feature = "ffi")] use std::ffi::CString; use std::fs; use std::hash::{BuildHasher, BuildHasherDefault}; -use std::io::{ErrorKind, Read, Write}; +use std::io::{ErrorKind, Read, BufRead, Write}; use std::iter::{once, FromIterator}; use std::mem; use std::net::{SocketAddr, TcpListener, TcpStream, ToSocketAddrs}; @@ -59,6 +59,7 @@ use std::num::NonZeroU32; use std::ops::Sub; use std::process; use std::str::FromStr; +use std::sync::{Mutex, Arc, Condvar}; use chrono::{offset::Local, DateTime}; #[cfg(not(target_arch = "wasm32"))] @@ -91,16 +92,15 @@ use base64; use roxmltree; use select; -use bytes::Buf; -use http_body_util::BodyExt; #[cfg(feature = "http")] -use hyper::header::{HeaderName, HeaderValue}; +use warp::hyper::header::{HeaderValue, HeaderName}; #[cfg(feature = "http")] -use hyper::server::conn::http1; +use warp::hyper::{HeaderMap, Method}; #[cfg(feature = "http")] -use hyper::{HeaderMap, Method}; +use warp::{Buf, Filter}; #[cfg(feature = "http")] use reqwest::Url; +use futures::future; #[cfg(feature = "repl")] pub(crate) fn get_key() -> KeyEvent { @@ -4412,54 +4412,119 @@ impl Machine { #[inline(always)] pub(crate) fn http_listen(&mut self) -> CallResult { let address_sink = self.deref_register(1); - if let Some(address_str) = self.machine_st.value_to_str_like(address_sink) { - let address_string = address_str.as_str(); - let addr: SocketAddr = match address_string - .to_socket_addrs() - .ok() - .and_then(|mut s| s.next()) - { - Some(addr) => addr, + let tls_key = self.deref_register(3); + let tls_cert = self.deref_register(4); + let content_length_limit = self.deref_register(5); + const CONTENT_LENGTH_LIMIT_DEFAULT: u64 = 32768; + let content_length_limit = match Number::try_from(content_length_limit) { + Ok(Number::Fixnum(n)) => if n.get_num() >= 0 { + n.get_num() as u64 + } else { + CONTENT_LENGTH_LIMIT_DEFAULT + }, + Ok(Number::Integer(n)) => { + let n: Result = (&*n).try_into(); + match n { + Ok(u) => u, + Err(_) => CONTENT_LENGTH_LIMIT_DEFAULT, + } + } + _ => CONTENT_LENGTH_LIMIT_DEFAULT, + }; + + let ssl_server: Option<(String,String)> = { + match self.machine_st.value_to_str_like(tls_key) { + Some(key) => { + match self.machine_st.value_to_str_like(tls_cert) { + Some(cert) => { + let key_str = key.as_str(); + let cert_str = cert.as_str(); + + if key_str.is_empty() || cert_str.is_empty() { + None + } else { + Some((key_str.to_string(), cert_str.to_string())) + } + } + None => None + } + } + None => None + } + }; + + if let Some(address_str) = self.machine_st.value_to_str_like(address_sink) { + let address_string = address_str.as_str(); + let addr: SocketAddr = match address_string.to_socket_addrs().ok().and_then(|mut s| s.next()) { + Some(addr) => addr, _ => { self.machine_st.fail = true; return Ok(()); } }; - let (tx, rx) = std::sync::mpsc::sync_channel(1024); - - let _guard = self.runtime.enter(); - let listener = match self - .runtime - .block_on(async { tokio::net::TcpListener::bind(addr).await }) - { - Ok(listener) => listener, - Err(_) => { - return Err(self.machine_st.open_permission_error( - address_sink, - atom!("http_listen"), - 2, - )); - } - }; - - self.runtime.spawn(async move { - loop { - let tx = tx.clone(); - let (stream, _) = listener.accept().await.unwrap(); - - tokio::task::spawn(async move { - if let Err(err) = http1::Builder::new() - .serve_connection(stream, HttpService { tx }) - .await - { - eprintln!("Error serving connection: {:?}", err); - } - }); - } - }); - let http_listener = HttpListener { incoming: rx }; - let http_listener = arena_alloc!(http_listener, &mut self.machine_st.arena); + let (tx, rx) = std::sync::mpsc::sync_channel(1024); + + fn get_reader(body: impl Buf + Send + 'static) -> Box { + Box::new(body.reader()) + } + + let serve = warp::body::aggregate() + .and(warp::header::optional::(warp::http::header::CONTENT_LENGTH.as_str())) + .and(warp::method()) + .and(warp::header::headers_cloned()) + .and(warp::path::full()) + .and(warp::query::raw().or_else(|_| future::ready(Ok::<(String,), warp::Rejection>(("".to_string(),))))) + .map(move |body, content_length, method, headers: warp::http::HeaderMap, path: warp::filters::path::FullPath, query| { + if let Some(content_length) = content_length { + if content_length > content_length_limit { + return warp::http::Response::builder() + .status(413) + .body(warp::hyper::Body::empty()) + .unwrap(); + } + } + + let http_request_data = HttpRequestData { + method, + headers, + path: path.as_str().to_string(), + query, + body: get_reader(body), + }; + let response = Arc::new((Mutex::new(false), Mutex::new(None), Condvar::new())); + let http_request = HttpRequest { request_data: http_request_data, response: Arc::clone(&response) }; + // we send the request to http_accept + tx.send(http_request).unwrap(); + + // we wait for the Response info from Prolog + { + let (ready, _response, cvar) = &*response; + let mut ready = ready.lock().unwrap(); + while !*ready { + ready = cvar.wait(ready).unwrap(); + } + } + { + let (_, response, _) = &*response; + let response = response.lock().unwrap().take(); + response.expect("Data race error in HTTP server") + } + }); + + self.runtime.spawn(async move { + match ssl_server { + Some((key, cert)) => { + warp::serve(serve).tls().key(key).cert(cert).run(addr).await + } + None => { + warp::serve(serve).run(addr).await + } + } + }); + + let http_listener = HttpListener { incoming: rx }; + let http_listener = arena_alloc!(http_listener, &mut self.machine_st.arena); let addr = self.deref_register(2); self.machine_st.bind( addr.as_var().unwrap(), @@ -4472,75 +4537,95 @@ impl Machine { #[cfg(feature = "http")] #[inline(always)] pub(crate) fn http_accept(&mut self) -> CallResult { - let culprit = self.deref_register(1); - let method = self.deref_register(2); - let path = self.deref_register(3); - let query = self.deref_register(5); - let stream_addr = self.deref_register(6); - let handle_addr = self.deref_register(7); - read_heap_cell!(culprit, - (HeapCellValueTag::Cons, cons_ptr) => { - match_untyped_arena_ptr!(cons_ptr, - (ArenaHeaderTag::HttpListener, http_listener) => { - match http_listener.incoming.recv() { - Ok(request) => { - let method_atom = match *request.request.method() { - Method::GET => atom!("get"), - Method::POST => atom!("post"), - Method::PUT => atom!("put"), - Method::DELETE => atom!("delete"), - Method::PATCH => atom!("patch"), - Method::HEAD => atom!("head"), - _ => unreachable!(), - }; - let path_atom = AtomTable::build_with(&self.machine_st.atom_tbl, request.request.uri().path()); - let path_cell = atom_as_cstr_cell!(path_atom); - let headers: Vec = request.request.headers().iter().map(|(header_name, header_value)| { - let h = self.machine_st.heap.len(); - - let header_term = functor!( - AtomTable::build_with(&self.machine_st.atom_tbl, header_name.as_str()), - [cell(string_as_cstr_cell!(AtomTable::build_with(&self.machine_st.atom_tbl, header_value.to_str().unwrap())))] - ); - - self.machine_st.heap.extend(header_term.into_iter()); - str_loc_as_cell!(h) - }).collect(); - - let headers_list = iter_to_heap_list(&mut self.machine_st.heap, headers.into_iter()); - - let query_str = request.request.uri().query().unwrap_or(""); - let query_atom = AtomTable::build_with(&self.machine_st.atom_tbl, query_str); - let query_cell = string_as_cstr_cell!(query_atom); - - let hyper_req = request.request; - let buf = self.runtime.block_on(async {hyper_req.collect().await.unwrap().aggregate()}); - let reader = buf.reader(); - - let mut stream = Stream::from_http_stream( - path_atom, - Box::new(reader), - &mut self.machine_st.arena - ); - *stream.options_mut() = StreamOptions::default(); - stream.options_mut().set_stream_type(StreamType::Binary); - self.indices.streams.insert(stream); - let stream = stream_as_cell!(stream); - - let handle = arena_alloc!(request.response, &mut self.machine_st.arena); - - self.machine_st.bind(method.as_var().unwrap(), atom_as_cell!(method_atom)); - self.machine_st.bind(path.as_var().unwrap(), path_cell); - unify!(self.machine_st, heap_loc_as_cell!(headers_list), self.machine_st.registers[4]); - self.machine_st.bind(query.as_var().unwrap(), query_cell); - self.machine_st.bind(stream_addr.as_var().unwrap(), stream); - self.machine_st.bind(handle_addr.as_var().unwrap(), typed_arena_ptr_as_cell!(handle)); - } - Err(_) => { - self.machine_st.fail = true; + let culprit = self.deref_register(1); + let method = self.deref_register(2); + let path = self.deref_register(3); + let query = self.deref_register(5); + let stream_addr = self.deref_register(6); + let handle_addr = self.deref_register(7); + read_heap_cell!(culprit, + (HeapCellValueTag::Cons, cons_ptr) => { + match_untyped_arena_ptr!(cons_ptr, + (ArenaHeaderTag::HttpListener, http_listener) => { + loop { + match http_listener.incoming.recv_timeout(std::time::Duration::from_millis(200)) { + Ok(request) => { + let method_atom = match request.request_data.method { + Method::GET => atom!("get"), + Method::POST => atom!("post"), + Method::PUT => atom!("put"), + Method::DELETE => atom!("delete"), + Method::PATCH => atom!("patch"), + Method::HEAD => atom!("head"), + Method::OPTIONS => atom!("options"), + Method::TRACE => atom!("trace"), + Method::CONNECT => atom!("connect"), + _ => atom!("unsupported_extension"), + }; + let path_atom = AtomTable::build_with(&self.machine_st.atom_tbl, &request.request_data.path); + let path_cell = atom_as_cstr_cell!(path_atom); + let headers: Vec = request.request_data.headers.iter().map(|(header_name, header_value)| { + let h = self.machine_st.heap.len(); + let header_term = functor!(AtomTable::build_with(&self.machine_st.atom_tbl, header_name.as_str()), [cell(string_as_cstr_cell!(AtomTable::build_with(&self.machine_st.atom_tbl, header_value.to_str().unwrap())))]); + + self.machine_st.heap.extend(header_term.into_iter()); + str_loc_as_cell!(h) + }).collect(); + + let headers_list = iter_to_heap_list(&mut self.machine_st.heap, headers.into_iter()); + + let query_str = request.request_data.query; + let query_atom = AtomTable::build_with(&self.machine_st.atom_tbl, &query_str); + let query_cell = string_as_cstr_cell!(query_atom); + + let mut stream = Stream::from_http_stream( + path_atom, + request.request_data.body, + &mut self.machine_st.arena + ); + *stream.options_mut() = StreamOptions::default(); + stream.options_mut().set_stream_type(StreamType::Binary); + self.indices.streams.insert(stream); + let stream = stream_as_cell!(stream); + + let handle = arena_alloc!(request.response, &mut self.machine_st.arena); + + self.machine_st.bind(method.as_var().unwrap(), atom_as_cell!(method_atom)); + self.machine_st.bind(path.as_var().unwrap(), path_cell); + unify!(self.machine_st, heap_loc_as_cell!(headers_list), self.machine_st.registers[4]); + self.machine_st.bind(query.as_var().unwrap(), query_cell); + self.machine_st.bind(stream_addr.as_var().unwrap(), stream); + self.machine_st.bind(handle_addr.as_var().unwrap(), typed_arena_ptr_as_cell!(handle)); + break + } + Err(std::sync::mpsc::RecvTimeoutError::Timeout) => { + let interrupted = machine::INTERRUPT.load(std::sync::atomic::Ordering::Relaxed); + + match machine::INTERRUPT.compare_exchange( + interrupted, + false, + std::sync::atomic::Ordering::Relaxed, + std::sync::atomic::Ordering::Relaxed, + ) { + Ok(interruption) => { + if interruption { + self.machine_st.throw_interrupt_exception(); + self.machine_st.backtrack(); + let old_runtime = std::mem::replace(&mut self.runtime, tokio::runtime::Runtime::new().unwrap()); + old_runtime.shutdown_background(); + break + } + } + Err(_) => unreachable!(), + } + + } + Err(_) => { + self.machine_st.fail = true; + } + } } } - } _ => { unreachable!(); }