mirror of
https://github.com/lune-org/lune.git
synced 2025-05-04 10:43:57 +01:00
Rip the net out
This commit is contained in:
parent
1f43ff89f7
commit
d70e09a591
12 changed files with 31 additions and 1339 deletions
266
Cargo.lock
generated
266
Cargo.lock
generated
|
@ -715,12 +715,6 @@ dependencies = [
|
||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "data-encoding"
|
|
||||||
version = "2.9.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deflate64"
|
name = "deflate64"
|
||||||
version = "0.1.9"
|
version = "0.1.9"
|
||||||
|
@ -1058,7 +1052,6 @@ checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-macro",
|
"futures-macro",
|
||||||
"futures-sink",
|
|
||||||
"futures-task",
|
"futures-task",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"pin-utils",
|
"pin-utils",
|
||||||
|
@ -1147,26 +1140,7 @@ dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http 0.2.12",
|
"http",
|
||||||
"indexmap",
|
|
||||||
"slab",
|
|
||||||
"tokio",
|
|
||||||
"tokio-util",
|
|
||||||
"tracing",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "h2"
|
|
||||||
version = "0.4.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "75249d144030531f8dee69fe9cea04d3edf809a017ae445e2abdff6629e86633"
|
|
||||||
dependencies = [
|
|
||||||
"atomic-waker",
|
|
||||||
"bytes",
|
|
||||||
"fnv",
|
|
||||||
"futures-core",
|
|
||||||
"futures-sink",
|
|
||||||
"http 1.3.1",
|
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"slab",
|
"slab",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
@ -1221,17 +1195,6 @@ dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "http"
|
|
||||||
version = "1.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565"
|
|
||||||
dependencies = [
|
|
||||||
"bytes",
|
|
||||||
"fnv",
|
|
||||||
"itoa",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http-body"
|
name = "http-body"
|
||||||
version = "0.4.6"
|
version = "0.4.6"
|
||||||
|
@ -1239,30 +1202,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
|
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"http 0.2.12",
|
"http",
|
||||||
"pin-project-lite",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "http-body"
|
|
||||||
version = "1.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
|
|
||||||
dependencies = [
|
|
||||||
"bytes",
|
|
||||||
"http 1.3.1",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "http-body-util"
|
|
||||||
version = "0.1.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
|
|
||||||
dependencies = [
|
|
||||||
"bytes",
|
|
||||||
"futures-core",
|
|
||||||
"http 1.3.1",
|
|
||||||
"http-body 1.0.1",
|
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1288,9 +1228,9 @@ dependencies = [
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"h2 0.3.26",
|
"h2",
|
||||||
"http 0.2.12",
|
"http",
|
||||||
"http-body 0.4.6",
|
"http-body",
|
||||||
"httparse",
|
"httparse",
|
||||||
"httpdate",
|
"httpdate",
|
||||||
"itoa",
|
"itoa",
|
||||||
|
@ -1302,27 +1242,6 @@ dependencies = [
|
||||||
"want",
|
"want",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hyper"
|
|
||||||
version = "1.6.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80"
|
|
||||||
dependencies = [
|
|
||||||
"bytes",
|
|
||||||
"futures-channel",
|
|
||||||
"futures-util",
|
|
||||||
"h2 0.4.9",
|
|
||||||
"http 1.3.1",
|
|
||||||
"http-body 1.0.1",
|
|
||||||
"httparse",
|
|
||||||
"httpdate",
|
|
||||||
"itoa",
|
|
||||||
"pin-project-lite",
|
|
||||||
"smallvec",
|
|
||||||
"tokio",
|
|
||||||
"want",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper-rustls"
|
name = "hyper-rustls"
|
||||||
version = "0.24.2"
|
version = "0.24.2"
|
||||||
|
@ -1330,46 +1249,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590"
|
checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http 0.2.12",
|
"http",
|
||||||
"hyper 0.14.32",
|
"hyper",
|
||||||
"rustls 0.21.12",
|
"rustls",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-rustls 0.24.1",
|
"tokio-rustls",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hyper-tungstenite"
|
|
||||||
version = "0.13.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7a343d17fe7885302ed7252767dc7bb83609a874b6ff581142241ec4b73957ad"
|
|
||||||
dependencies = [
|
|
||||||
"http-body-util",
|
|
||||||
"hyper 1.6.0",
|
|
||||||
"hyper-util",
|
|
||||||
"pin-project-lite",
|
|
||||||
"tokio",
|
|
||||||
"tokio-tungstenite",
|
|
||||||
"tungstenite",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hyper-util"
|
|
||||||
version = "0.1.11"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2"
|
|
||||||
dependencies = [
|
|
||||||
"bytes",
|
|
||||||
"futures-channel",
|
|
||||||
"futures-util",
|
|
||||||
"http 1.3.1",
|
|
||||||
"http-body 1.0.1",
|
|
||||||
"hyper 1.6.0",
|
|
||||||
"libc",
|
|
||||||
"pin-project-lite",
|
|
||||||
"socket2",
|
|
||||||
"tokio",
|
|
||||||
"tower-service",
|
|
||||||
"tracing",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1790,21 +1674,15 @@ dependencies = [
|
||||||
name = "lune-std-net"
|
name = "lune-std-net"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"async-io",
|
||||||
|
"async-lock",
|
||||||
|
"blocking",
|
||||||
"bstr",
|
"bstr",
|
||||||
"futures-util",
|
"futures-lite",
|
||||||
"http 1.3.1",
|
|
||||||
"http-body-util",
|
|
||||||
"hyper 1.6.0",
|
|
||||||
"hyper-tungstenite",
|
|
||||||
"hyper-util",
|
|
||||||
"lune-std-serde",
|
"lune-std-serde",
|
||||||
"lune-utils",
|
"lune-utils",
|
||||||
"mlua",
|
"mlua",
|
||||||
"mlua-luau-scheduler",
|
"mlua-luau-scheduler",
|
||||||
"reqwest",
|
|
||||||
"tokio",
|
|
||||||
"tokio-tungstenite",
|
|
||||||
"urlencoding",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2634,10 +2512,10 @@ dependencies = [
|
||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"h2 0.3.26",
|
"h2",
|
||||||
"http 0.2.12",
|
"http",
|
||||||
"http-body 0.4.6",
|
"http-body",
|
||||||
"hyper 0.14.32",
|
"hyper",
|
||||||
"hyper-rustls",
|
"hyper-rustls",
|
||||||
"ipnet",
|
"ipnet",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
|
@ -2646,7 +2524,7 @@ dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"rustls 0.21.12",
|
"rustls",
|
||||||
"rustls-pemfile",
|
"rustls-pemfile",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
@ -2654,13 +2532,13 @@ dependencies = [
|
||||||
"sync_wrapper",
|
"sync_wrapper",
|
||||||
"system-configuration",
|
"system-configuration",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-rustls 0.24.1",
|
"tokio-rustls",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"url",
|
"url",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"wasm-bindgen-futures",
|
"wasm-bindgen-futures",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
"webpki-roots 0.25.4",
|
"webpki-roots",
|
||||||
"winreg 0.50.0",
|
"winreg 0.50.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -2793,24 +2671,10 @@ checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"ring",
|
"ring",
|
||||||
"rustls-webpki 0.101.7",
|
"rustls-webpki",
|
||||||
"sct",
|
"sct",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustls"
|
|
||||||
version = "0.22.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432"
|
|
||||||
dependencies = [
|
|
||||||
"log",
|
|
||||||
"ring",
|
|
||||||
"rustls-pki-types",
|
|
||||||
"rustls-webpki 0.102.8",
|
|
||||||
"subtle",
|
|
||||||
"zeroize",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-pemfile"
|
name = "rustls-pemfile"
|
||||||
version = "1.0.4"
|
version = "1.0.4"
|
||||||
|
@ -2820,12 +2684,6 @@ dependencies = [
|
||||||
"base64 0.21.7",
|
"base64 0.21.7",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustls-pki-types"
|
|
||||||
version = "1.11.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-webpki"
|
name = "rustls-webpki"
|
||||||
version = "0.101.7"
|
version = "0.101.7"
|
||||||
|
@ -2836,17 +2694,6 @@ dependencies = [
|
||||||
"untrusted",
|
"untrusted",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustls-webpki"
|
|
||||||
version = "0.102.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
|
|
||||||
dependencies = [
|
|
||||||
"ring",
|
|
||||||
"rustls-pki-types",
|
|
||||||
"untrusted",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustversion"
|
name = "rustversion"
|
||||||
version = "1.0.20"
|
version = "1.0.20"
|
||||||
|
@ -3437,37 +3284,10 @@ version = "0.24.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
|
checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rustls 0.21.12",
|
"rustls",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tokio-rustls"
|
|
||||||
version = "0.25.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f"
|
|
||||||
dependencies = [
|
|
||||||
"rustls 0.22.4",
|
|
||||||
"rustls-pki-types",
|
|
||||||
"tokio",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tokio-tungstenite"
|
|
||||||
version = "0.21.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38"
|
|
||||||
dependencies = [
|
|
||||||
"futures-util",
|
|
||||||
"log",
|
|
||||||
"rustls 0.22.4",
|
|
||||||
"rustls-pki-types",
|
|
||||||
"tokio",
|
|
||||||
"tokio-rustls 0.25.0",
|
|
||||||
"tungstenite",
|
|
||||||
"webpki-roots 0.26.8",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-util"
|
name = "tokio-util"
|
||||||
version = "0.7.15"
|
version = "0.7.15"
|
||||||
|
@ -3621,27 +3441,6 @@ version = "0.2.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tungstenite"
|
|
||||||
version = "0.21.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1"
|
|
||||||
dependencies = [
|
|
||||||
"byteorder 1.5.0",
|
|
||||||
"bytes",
|
|
||||||
"data-encoding",
|
|
||||||
"http 1.3.1",
|
|
||||||
"httparse",
|
|
||||||
"log",
|
|
||||||
"rand 0.8.5",
|
|
||||||
"rustls 0.22.4",
|
|
||||||
"rustls-pki-types",
|
|
||||||
"sha1 0.10.6",
|
|
||||||
"thiserror 1.0.69",
|
|
||||||
"url",
|
|
||||||
"utf-8",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typeid"
|
name = "typeid"
|
||||||
version = "1.0.3"
|
version = "1.0.3"
|
||||||
|
@ -3695,12 +3494,6 @@ dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "urlencoding"
|
|
||||||
version = "2.1.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ustr"
|
name = "ustr"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
@ -3714,12 +3507,6 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "utf-8"
|
|
||||||
version = "0.7.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf16_iter"
|
name = "utf16_iter"
|
||||||
version = "1.0.5"
|
version = "1.0.5"
|
||||||
|
@ -3877,15 +3664,6 @@ version = "0.25.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
|
checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "webpki-roots"
|
|
||||||
version = "0.26.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2210b291f7ea53617fbafcc4939f10914214ec15aace5ba62293a668f322c5c9"
|
|
||||||
dependencies = [
|
|
||||||
"rustls-pki-types",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
|
|
@ -16,24 +16,11 @@ workspace = true
|
||||||
mlua = { version = "0.10.3", features = ["luau"] }
|
mlua = { version = "0.10.3", features = ["luau"] }
|
||||||
mlua-luau-scheduler = { version = "0.1.0", path = "../mlua-luau-scheduler" }
|
mlua-luau-scheduler = { version = "0.1.0", path = "../mlua-luau-scheduler" }
|
||||||
|
|
||||||
|
async-io = "2.4"
|
||||||
|
async-lock = "3.4"
|
||||||
|
blocking = "1.6"
|
||||||
bstr = "1.9"
|
bstr = "1.9"
|
||||||
futures-util = "0.3"
|
futures-lite = "2.6"
|
||||||
hyper = { version = "1.1", features = ["full"] }
|
|
||||||
hyper-util = { version = "0.1", features = ["full"] }
|
|
||||||
http = "1.0"
|
|
||||||
http-body-util = { version = "0.1" }
|
|
||||||
hyper-tungstenite = { version = "0.13" }
|
|
||||||
reqwest = { version = "0.11", default-features = false, features = [
|
|
||||||
"rustls-tls",
|
|
||||||
] }
|
|
||||||
tokio-tungstenite = { version = "0.21", features = ["rustls-tls-webpki-roots"] }
|
|
||||||
urlencoding = "2.1"
|
|
||||||
|
|
||||||
tokio = { version = "1", default-features = false, features = [
|
|
||||||
"sync",
|
|
||||||
"net",
|
|
||||||
"macros",
|
|
||||||
] }
|
|
||||||
|
|
||||||
lune-utils = { version = "0.2.0", path = "../lune-utils" }
|
lune-utils = { version = "0.2.0", path = "../lune-utils" }
|
||||||
lune-std-serde = { version = "0.2.0", path = "../lune-std-serde" }
|
lune-std-serde = { version = "0.2.0", path = "../lune-std-serde" }
|
||||||
|
|
|
@ -1,163 +0,0 @@
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use mlua::prelude::*;
|
|
||||||
|
|
||||||
use reqwest::header::{HeaderMap, HeaderName, HeaderValue, CONTENT_ENCODING};
|
|
||||||
|
|
||||||
use lune_std_serde::{decompress, CompressDecompressFormat};
|
|
||||||
use lune_utils::TableBuilder;
|
|
||||||
|
|
||||||
use super::{config::RequestConfig, util::header_map_to_table};
|
|
||||||
|
|
||||||
const REGISTRY_KEY: &str = "NetClient";
|
|
||||||
|
|
||||||
pub struct NetClientBuilder {
|
|
||||||
builder: reqwest::ClientBuilder,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NetClientBuilder {
|
|
||||||
pub fn new() -> NetClientBuilder {
|
|
||||||
Self {
|
|
||||||
builder: reqwest::ClientBuilder::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn headers<K, V>(mut self, headers: &[(K, V)]) -> LuaResult<Self>
|
|
||||||
where
|
|
||||||
K: AsRef<str>,
|
|
||||||
V: AsRef<[u8]>,
|
|
||||||
{
|
|
||||||
let mut map = HeaderMap::new();
|
|
||||||
for (key, val) in headers {
|
|
||||||
let hkey = HeaderName::from_str(key.as_ref()).into_lua_err()?;
|
|
||||||
let hval = HeaderValue::from_bytes(val.as_ref()).into_lua_err()?;
|
|
||||||
map.insert(hkey, hval);
|
|
||||||
}
|
|
||||||
self.builder = self.builder.default_headers(map);
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build(self) -> LuaResult<NetClient> {
|
|
||||||
let client = self.builder.build().into_lua_err()?;
|
|
||||||
Ok(NetClient { inner: client })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct NetClient {
|
|
||||||
inner: reqwest::Client,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NetClient {
|
|
||||||
pub fn from_registry(lua: &Lua) -> Self {
|
|
||||||
lua.named_registry_value(REGISTRY_KEY)
|
|
||||||
.expect("Failed to get NetClient from lua registry")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_registry(self, lua: &Lua) {
|
|
||||||
lua.set_named_registry_value(REGISTRY_KEY, self)
|
|
||||||
.expect("Failed to store NetClient in lua registry");
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn request(&self, config: RequestConfig) -> LuaResult<NetClientResponse> {
|
|
||||||
// Create and send the request
|
|
||||||
let mut request = self.inner.request(config.method, config.url);
|
|
||||||
for (query, values) in config.query {
|
|
||||||
request = request.query(
|
|
||||||
&values
|
|
||||||
.iter()
|
|
||||||
.map(|v| (query.as_str(), v))
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
for (header, values) in config.headers {
|
|
||||||
for value in values {
|
|
||||||
request = request.header(header.as_str(), value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let res = request
|
|
||||||
.body(config.body.unwrap_or_default())
|
|
||||||
.send()
|
|
||||||
.await
|
|
||||||
.into_lua_err()?;
|
|
||||||
|
|
||||||
// Extract status, headers
|
|
||||||
let res_status = res.status().as_u16();
|
|
||||||
let res_status_text = res.status().canonical_reason();
|
|
||||||
let res_headers = res.headers().clone();
|
|
||||||
|
|
||||||
// Read response bytes
|
|
||||||
let mut res_bytes = res.bytes().await.into_lua_err()?.to_vec();
|
|
||||||
let mut res_decompressed = false;
|
|
||||||
|
|
||||||
// Check for extra options, decompression
|
|
||||||
if config.options.decompress {
|
|
||||||
let decompress_format = res_headers
|
|
||||||
.iter()
|
|
||||||
.find(|(name, _)| {
|
|
||||||
name.as_str()
|
|
||||||
.eq_ignore_ascii_case(CONTENT_ENCODING.as_str())
|
|
||||||
})
|
|
||||||
.and_then(|(_, value)| value.to_str().ok())
|
|
||||||
.and_then(CompressDecompressFormat::detect_from_header_str);
|
|
||||||
if let Some(format) = decompress_format {
|
|
||||||
res_bytes = decompress(res_bytes, format).await?;
|
|
||||||
res_decompressed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(NetClientResponse {
|
|
||||||
ok: (200..300).contains(&res_status),
|
|
||||||
status_code: res_status,
|
|
||||||
status_message: res_status_text.unwrap_or_default().to_string(),
|
|
||||||
headers: res_headers,
|
|
||||||
body: res_bytes,
|
|
||||||
body_decompressed: res_decompressed,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LuaUserData for NetClient {}
|
|
||||||
|
|
||||||
impl FromLua for NetClient {
|
|
||||||
fn from_lua(value: LuaValue, _: &Lua) -> LuaResult<Self> {
|
|
||||||
if let LuaValue::UserData(ud) = value {
|
|
||||||
if let Ok(ctx) = ud.borrow::<NetClient>() {
|
|
||||||
return Ok(ctx.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unreachable!("NetClient should only be used from registry")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&Lua> for NetClient {
|
|
||||||
fn from(value: &Lua) -> Self {
|
|
||||||
value
|
|
||||||
.named_registry_value(REGISTRY_KEY)
|
|
||||||
.expect("Missing require context in lua registry")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct NetClientResponse {
|
|
||||||
ok: bool,
|
|
||||||
status_code: u16,
|
|
||||||
status_message: String,
|
|
||||||
headers: HeaderMap,
|
|
||||||
body: Vec<u8>,
|
|
||||||
body_decompressed: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NetClientResponse {
|
|
||||||
pub fn into_lua_table(self, lua: &Lua) -> LuaResult<LuaTable> {
|
|
||||||
TableBuilder::new(lua.clone())?
|
|
||||||
.with_value("ok", self.ok)?
|
|
||||||
.with_value("statusCode", self.status_code)?
|
|
||||||
.with_value("statusMessage", self.status_message)?
|
|
||||||
.with_value(
|
|
||||||
"headers",
|
|
||||||
header_map_to_table(lua, self.headers, self.body_decompressed)?,
|
|
||||||
)?
|
|
||||||
.with_value("body", lua.create_string(&self.body)?)?
|
|
||||||
.build_readonly()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,231 +0,0 @@
|
||||||
use std::{
|
|
||||||
collections::HashMap,
|
|
||||||
net::{IpAddr, Ipv4Addr},
|
|
||||||
};
|
|
||||||
|
|
||||||
use bstr::{BString, ByteSlice};
|
|
||||||
use mlua::prelude::*;
|
|
||||||
|
|
||||||
use reqwest::Method;
|
|
||||||
|
|
||||||
use super::util::table_to_hash_map;
|
|
||||||
|
|
||||||
const DEFAULT_IP_ADDRESS: IpAddr = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
|
|
||||||
|
|
||||||
const WEB_SOCKET_UPDGRADE_REQUEST_HANDLER: &str = r#"
|
|
||||||
return {
|
|
||||||
status = 426,
|
|
||||||
body = "Upgrade Required",
|
|
||||||
headers = {
|
|
||||||
Upgrade = "websocket",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
"#;
|
|
||||||
|
|
||||||
// Net request config
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct RequestConfigOptions {
|
|
||||||
pub decompress: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for RequestConfigOptions {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self { decompress: true }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromLua for RequestConfigOptions {
|
|
||||||
fn from_lua(value: LuaValue, _: &Lua) -> LuaResult<Self> {
|
|
||||||
if let LuaValue::Nil = value {
|
|
||||||
// Nil means default options
|
|
||||||
Ok(Self::default())
|
|
||||||
} else if let LuaValue::Table(tab) = value {
|
|
||||||
// Table means custom options
|
|
||||||
let decompress = match tab.get::<Option<bool>>("decompress") {
|
|
||||||
Ok(decomp) => Ok(decomp.unwrap_or(true)),
|
|
||||||
Err(_) => Err(LuaError::RuntimeError(
|
|
||||||
"Invalid option value for 'decompress' in request config options".to_string(),
|
|
||||||
)),
|
|
||||||
}?;
|
|
||||||
Ok(Self { decompress })
|
|
||||||
} else {
|
|
||||||
// Anything else is invalid
|
|
||||||
Err(LuaError::FromLuaConversionError {
|
|
||||||
from: value.type_name(),
|
|
||||||
to: "RequestConfigOptions".to_string(),
|
|
||||||
message: Some(format!(
|
|
||||||
"Invalid request config options - expected table or nil, got {}",
|
|
||||||
value.type_name()
|
|
||||||
)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct RequestConfig {
|
|
||||||
pub url: String,
|
|
||||||
pub method: Method,
|
|
||||||
pub query: HashMap<String, Vec<String>>,
|
|
||||||
pub headers: HashMap<String, Vec<String>>,
|
|
||||||
pub body: Option<Vec<u8>>,
|
|
||||||
pub options: RequestConfigOptions,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromLua for RequestConfig {
|
|
||||||
fn from_lua(value: LuaValue, lua: &Lua) -> LuaResult<Self> {
|
|
||||||
// If we just got a string we assume its a GET request to a given url
|
|
||||||
if let LuaValue::String(s) = value {
|
|
||||||
Ok(Self {
|
|
||||||
url: s.to_string_lossy().to_string(),
|
|
||||||
method: Method::GET,
|
|
||||||
query: HashMap::new(),
|
|
||||||
headers: HashMap::new(),
|
|
||||||
body: None,
|
|
||||||
options: RequestConfigOptions::default(),
|
|
||||||
})
|
|
||||||
} else if let LuaValue::Table(tab) = value {
|
|
||||||
// If we got a table we are able to configure the entire request
|
|
||||||
// Extract url
|
|
||||||
let url = match tab.get::<LuaString>("url") {
|
|
||||||
Ok(config_url) => Ok(config_url.to_string_lossy().to_string()),
|
|
||||||
Err(_) => Err(LuaError::runtime("Missing 'url' in request config")),
|
|
||||||
}?;
|
|
||||||
// Extract method
|
|
||||||
let method = match tab.get::<LuaString>("method") {
|
|
||||||
Ok(config_method) => config_method.to_string_lossy().trim().to_ascii_uppercase(),
|
|
||||||
Err(_) => "GET".to_string(),
|
|
||||||
};
|
|
||||||
// Extract query
|
|
||||||
let query = match tab.get::<LuaTable>("query") {
|
|
||||||
Ok(tab) => table_to_hash_map(tab, "query")?,
|
|
||||||
Err(_) => HashMap::new(),
|
|
||||||
};
|
|
||||||
// Extract headers
|
|
||||||
let headers = match tab.get::<LuaTable>("headers") {
|
|
||||||
Ok(tab) => table_to_hash_map(tab, "headers")?,
|
|
||||||
Err(_) => HashMap::new(),
|
|
||||||
};
|
|
||||||
// Extract body
|
|
||||||
let body = match tab.get::<BString>("body") {
|
|
||||||
Ok(config_body) => Some(config_body.as_bytes().to_owned()),
|
|
||||||
Err(_) => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Convert method string into proper enum
|
|
||||||
let method = method.trim().to_ascii_uppercase();
|
|
||||||
let method = match method.as_ref() {
|
|
||||||
"GET" => Ok(Method::GET),
|
|
||||||
"POST" => Ok(Method::POST),
|
|
||||||
"PUT" => Ok(Method::PUT),
|
|
||||||
"DELETE" => Ok(Method::DELETE),
|
|
||||||
"HEAD" => Ok(Method::HEAD),
|
|
||||||
"OPTIONS" => Ok(Method::OPTIONS),
|
|
||||||
"PATCH" => Ok(Method::PATCH),
|
|
||||||
_ => Err(LuaError::RuntimeError(format!(
|
|
||||||
"Invalid request config method '{}'",
|
|
||||||
&method
|
|
||||||
))),
|
|
||||||
}?;
|
|
||||||
// Parse any extra options given
|
|
||||||
let options = match tab.get::<LuaValue>("options") {
|
|
||||||
Ok(opts) => RequestConfigOptions::from_lua(opts, lua)?,
|
|
||||||
Err(_) => RequestConfigOptions::default(),
|
|
||||||
};
|
|
||||||
// All good, validated and we got what we need
|
|
||||||
Ok(Self {
|
|
||||||
url,
|
|
||||||
method,
|
|
||||||
query,
|
|
||||||
headers,
|
|
||||||
body,
|
|
||||||
options,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
// Anything else is invalid
|
|
||||||
Err(LuaError::FromLuaConversionError {
|
|
||||||
from: value.type_name(),
|
|
||||||
to: "RequestConfig".to_string(),
|
|
||||||
message: Some(format!(
|
|
||||||
"Invalid request config - expected string or table, got {}",
|
|
||||||
value.type_name()
|
|
||||||
)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Net serve config
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ServeConfig {
|
|
||||||
pub address: IpAddr,
|
|
||||||
pub handle_request: LuaFunction,
|
|
||||||
pub handle_web_socket: Option<LuaFunction>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromLua for ServeConfig {
|
|
||||||
fn from_lua(value: LuaValue, lua: &Lua) -> LuaResult<Self> {
|
|
||||||
if let LuaValue::Function(f) = &value {
|
|
||||||
// Single function = request handler, rest is default
|
|
||||||
Ok(ServeConfig {
|
|
||||||
handle_request: f.clone(),
|
|
||||||
handle_web_socket: None,
|
|
||||||
address: DEFAULT_IP_ADDRESS,
|
|
||||||
})
|
|
||||||
} else if let LuaValue::Table(t) = &value {
|
|
||||||
// Table means custom options
|
|
||||||
let address: Option<LuaString> = t.get("address")?;
|
|
||||||
let handle_request: Option<LuaFunction> = t.get("handleRequest")?;
|
|
||||||
let handle_web_socket: Option<LuaFunction> = t.get("handleWebSocket")?;
|
|
||||||
if handle_request.is_some() || handle_web_socket.is_some() {
|
|
||||||
let address: IpAddr = match &address {
|
|
||||||
Some(addr) => {
|
|
||||||
let addr_str = addr.to_str()?;
|
|
||||||
|
|
||||||
addr_str
|
|
||||||
.trim_start_matches("http://")
|
|
||||||
.trim_start_matches("https://")
|
|
||||||
.parse()
|
|
||||||
.map_err(|_e| LuaError::FromLuaConversionError {
|
|
||||||
from: value.type_name(),
|
|
||||||
to: "ServeConfig".to_string(),
|
|
||||||
message: Some(format!(
|
|
||||||
"IP address format is incorrect - \
|
|
||||||
expected an IP in the form 'http://0.0.0.0' or '0.0.0.0', \
|
|
||||||
got '{addr_str}'"
|
|
||||||
)),
|
|
||||||
})?
|
|
||||||
}
|
|
||||||
None => DEFAULT_IP_ADDRESS,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
address,
|
|
||||||
handle_request: handle_request.unwrap_or_else(|| {
|
|
||||||
lua.load(WEB_SOCKET_UPDGRADE_REQUEST_HANDLER)
|
|
||||||
.into_function()
|
|
||||||
.expect("Failed to create default http responder function")
|
|
||||||
}),
|
|
||||||
handle_web_socket,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Err(LuaError::FromLuaConversionError {
|
|
||||||
from: value.type_name(),
|
|
||||||
to: "ServeConfig".to_string(),
|
|
||||||
message: Some(String::from(
|
|
||||||
"Invalid serve config - expected table with 'handleRequest' or 'handleWebSocket' function",
|
|
||||||
)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Anything else is invalid
|
|
||||||
Err(LuaError::FromLuaConversionError {
|
|
||||||
from: value.type_name(),
|
|
||||||
to: "ServeConfig".to_string(),
|
|
||||||
message: None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +1,9 @@
|
||||||
#![allow(clippy::cargo_common_metadata)]
|
#![allow(clippy::cargo_common_metadata)]
|
||||||
|
|
||||||
use mlua::prelude::*;
|
use mlua::prelude::*;
|
||||||
use mlua_luau_scheduler::LuaSpawnExt;
|
|
||||||
|
|
||||||
mod client;
|
|
||||||
mod config;
|
|
||||||
mod server;
|
|
||||||
mod util;
|
|
||||||
mod websocket;
|
|
||||||
|
|
||||||
use lune_utils::TableBuilder;
|
use lune_utils::TableBuilder;
|
||||||
|
|
||||||
use self::{
|
|
||||||
client::{NetClient, NetClientBuilder},
|
|
||||||
config::{RequestConfig, ServeConfig},
|
|
||||||
server::serve,
|
|
||||||
util::create_user_agent_header,
|
|
||||||
websocket::NetWebSocket,
|
|
||||||
};
|
|
||||||
|
|
||||||
const TYPEDEFS: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/types.d.luau"));
|
const TYPEDEFS: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/types.d.luau"));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -37,55 +22,11 @@ pub fn typedefs() -> String {
|
||||||
Errors when out of memory.
|
Errors when out of memory.
|
||||||
*/
|
*/
|
||||||
pub fn module(lua: Lua) -> LuaResult<LuaTable> {
|
pub fn module(lua: Lua) -> LuaResult<LuaTable> {
|
||||||
NetClientBuilder::new()
|
|
||||||
.headers(&[("User-Agent", create_user_agent_header(&lua)?)])?
|
|
||||||
.build()?
|
|
||||||
.into_registry(&lua);
|
|
||||||
TableBuilder::new(lua)?
|
TableBuilder::new(lua)?
|
||||||
.with_async_function("request", net_request)?
|
// .with_async_function("request", net_request)?
|
||||||
.with_async_function("socket", net_socket)?
|
// .with_async_function("socket", net_socket)?
|
||||||
.with_async_function("serve", net_serve)?
|
// .with_async_function("serve", net_serve)?
|
||||||
.with_function("urlEncode", net_url_encode)?
|
// .with_function("urlEncode", net_url_encode)?
|
||||||
.with_function("urlDecode", net_url_decode)?
|
// .with_function("urlDecode", net_url_decode)?
|
||||||
.build_readonly()
|
.build_readonly()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn net_request(lua: Lua, config: RequestConfig) -> LuaResult<LuaTable> {
|
|
||||||
let client = NetClient::from_registry(&lua);
|
|
||||||
// NOTE: We spawn the request as a background task to free up resources in lua
|
|
||||||
let res = lua.spawn(async move { client.request(config).await });
|
|
||||||
res.await?.into_lua_table(&lua)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn net_socket(lua: Lua, url: String) -> LuaResult<LuaValue> {
|
|
||||||
let (ws, _) = tokio_tungstenite::connect_async(url).await.into_lua_err()?;
|
|
||||||
NetWebSocket::new(ws).into_lua(&lua)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn net_serve(lua: Lua, (port, config): (u16, ServeConfig)) -> LuaResult<LuaTable> {
|
|
||||||
serve(lua, port, config).await
|
|
||||||
}
|
|
||||||
|
|
||||||
fn net_url_encode(
|
|
||||||
lua: &Lua,
|
|
||||||
(lua_string, as_binary): (LuaString, Option<bool>),
|
|
||||||
) -> LuaResult<LuaValue> {
|
|
||||||
if matches!(as_binary, Some(true)) {
|
|
||||||
urlencoding::encode_binary(&lua_string.as_bytes()).into_lua(lua)
|
|
||||||
} else {
|
|
||||||
urlencoding::encode(&lua_string.to_str()?).into_lua(lua)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn net_url_decode(
|
|
||||||
lua: &Lua,
|
|
||||||
(lua_string, as_binary): (LuaString, Option<bool>),
|
|
||||||
) -> LuaResult<LuaValue> {
|
|
||||||
if matches!(as_binary, Some(true)) {
|
|
||||||
urlencoding::decode_binary(&lua_string.as_bytes()).into_lua(lua)
|
|
||||||
} else {
|
|
||||||
urlencoding::decode(&lua_string.to_str()?)
|
|
||||||
.map_err(|e| LuaError::RuntimeError(format!("Encountered invalid encoding - {e}")))?
|
|
||||||
.into_lua(lua)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,58 +0,0 @@
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
||||||
|
|
||||||
use mlua::prelude::*;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub(super) struct SvcKeys {
|
|
||||||
key_request: &'static str,
|
|
||||||
key_websocket: Option<&'static str>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SvcKeys {
|
|
||||||
pub(super) fn new(
|
|
||||||
lua: Lua,
|
|
||||||
handle_request: LuaFunction,
|
|
||||||
handle_websocket: Option<LuaFunction>,
|
|
||||||
) -> LuaResult<Self> {
|
|
||||||
static SERVE_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
|
||||||
let count = SERVE_COUNTER.fetch_add(1, Ordering::Relaxed);
|
|
||||||
|
|
||||||
// NOTE: We leak strings here, but this is an acceptable tradeoff since programs
|
|
||||||
// generally only start one or a couple of servers and they are usually never dropped.
|
|
||||||
// Leaking here lets us keep this struct Copy and access the request handler callbacks
|
|
||||||
// very performantly, significantly reducing the per-request overhead of the server.
|
|
||||||
let key_request: &'static str =
|
|
||||||
Box::leak(format!("__net_serve_request_{count}").into_boxed_str());
|
|
||||||
let key_websocket: Option<&'static str> = if handle_websocket.is_some() {
|
|
||||||
Some(Box::leak(
|
|
||||||
format!("__net_serve_websocket_{count}").into_boxed_str(),
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
lua.set_named_registry_value(key_request, handle_request)?;
|
|
||||||
if let Some(key) = key_websocket {
|
|
||||||
lua.set_named_registry_value(key, handle_websocket.unwrap())?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
key_request,
|
|
||||||
key_websocket,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn has_websocket_handler(&self) -> bool {
|
|
||||||
self.key_websocket.is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn request_handler(&self, lua: &Lua) -> LuaResult<LuaFunction> {
|
|
||||||
lua.named_registry_value(self.key_request)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn websocket_handler(&self, lua: &Lua) -> LuaResult<Option<LuaFunction>> {
|
|
||||||
self.key_websocket
|
|
||||||
.map(|key| lua.named_registry_value(key))
|
|
||||||
.transpose()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,92 +0,0 @@
|
||||||
use std::net::SocketAddr;
|
|
||||||
|
|
||||||
use hyper::server::conn::http1;
|
|
||||||
use hyper_util::rt::TokioIo;
|
|
||||||
use tokio::{net::TcpListener, pin};
|
|
||||||
|
|
||||||
use mlua::prelude::*;
|
|
||||||
use mlua_luau_scheduler::LuaSpawnExt;
|
|
||||||
|
|
||||||
use lune_utils::TableBuilder;
|
|
||||||
|
|
||||||
use super::config::ServeConfig;
|
|
||||||
|
|
||||||
mod keys;
|
|
||||||
mod request;
|
|
||||||
mod response;
|
|
||||||
mod service;
|
|
||||||
|
|
||||||
use keys::SvcKeys;
|
|
||||||
use service::Svc;
|
|
||||||
|
|
||||||
pub async fn serve(lua: Lua, port: u16, config: ServeConfig) -> LuaResult<LuaTable> {
|
|
||||||
let addr: SocketAddr = (config.address, port).into();
|
|
||||||
let listener = TcpListener::bind(addr).await?;
|
|
||||||
|
|
||||||
let lua_svc = lua.clone();
|
|
||||||
let lua_inner = lua.clone();
|
|
||||||
|
|
||||||
let keys = SvcKeys::new(lua.clone(), config.handle_request, config.handle_web_socket)?;
|
|
||||||
let svc = Svc {
|
|
||||||
lua: lua_svc,
|
|
||||||
addr,
|
|
||||||
keys,
|
|
||||||
};
|
|
||||||
|
|
||||||
let (shutdown_tx, shutdown_rx) = tokio::sync::watch::channel(false);
|
|
||||||
lua.spawn_local(async move {
|
|
||||||
let mut shutdown_rx_outer = shutdown_rx.clone();
|
|
||||||
loop {
|
|
||||||
// Create futures for accepting new connections and shutting down
|
|
||||||
let fut_shutdown = shutdown_rx_outer.changed();
|
|
||||||
let fut_accept = async {
|
|
||||||
let stream = match listener.accept().await {
|
|
||||||
Err(_) => return,
|
|
||||||
Ok((s, _)) => s,
|
|
||||||
};
|
|
||||||
|
|
||||||
let io = TokioIo::new(stream);
|
|
||||||
let svc = svc.clone();
|
|
||||||
let mut shutdown_rx_inner = shutdown_rx.clone();
|
|
||||||
|
|
||||||
lua_inner.spawn_local(async move {
|
|
||||||
let conn = http1::Builder::new()
|
|
||||||
.keep_alive(true) // Web sockets need this
|
|
||||||
.serve_connection(io, svc)
|
|
||||||
.with_upgrades();
|
|
||||||
// NOTE: Because we need to use keep_alive for websockets, we need to
|
|
||||||
// also manually poll this future and handle the shutdown signal here
|
|
||||||
pin!(conn);
|
|
||||||
tokio::select! {
|
|
||||||
_ = conn.as_mut() => {}
|
|
||||||
_ = shutdown_rx_inner.changed() => {
|
|
||||||
conn.as_mut().graceful_shutdown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Wait for either a new connection or a shutdown signal
|
|
||||||
tokio::select! {
|
|
||||||
() = fut_accept => {}
|
|
||||||
res = fut_shutdown => {
|
|
||||||
// NOTE: We will only get a RecvError here if the serve handle is dropped,
|
|
||||||
// this means lua has garbage collected it and the user does not want
|
|
||||||
// to manually stop the server using the serve handle. Run forever.
|
|
||||||
if res.is_ok() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
TableBuilder::new(lua)?
|
|
||||||
.with_value("ip", addr.ip().to_string())?
|
|
||||||
.with_value("port", addr.port())?
|
|
||||||
.with_function("stop", move |_, (): ()| match shutdown_tx.send(true) {
|
|
||||||
Ok(()) => Ok(()),
|
|
||||||
Err(_) => Err(LuaError::runtime("Server already stopped")),
|
|
||||||
})?
|
|
||||||
.build_readonly()
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
use std::{collections::HashMap, net::SocketAddr};
|
|
||||||
|
|
||||||
use http::request::Parts;
|
|
||||||
|
|
||||||
use mlua::prelude::*;
|
|
||||||
|
|
||||||
use lune_utils::TableBuilder;
|
|
||||||
|
|
||||||
pub(super) struct LuaRequest {
|
|
||||||
pub(super) _remote_addr: SocketAddr,
|
|
||||||
pub(super) head: Parts,
|
|
||||||
pub(super) body: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LuaRequest {
|
|
||||||
pub fn into_lua_table(self, lua: &Lua) -> LuaResult<LuaTable> {
|
|
||||||
let method = self.head.method.as_str().to_string();
|
|
||||||
let path = self.head.uri.path().to_string();
|
|
||||||
let body = lua.create_string(&self.body)?;
|
|
||||||
|
|
||||||
#[allow(clippy::mutable_key_type)]
|
|
||||||
let query: HashMap<LuaString, LuaString> = self
|
|
||||||
.head
|
|
||||||
.uri
|
|
||||||
.query()
|
|
||||||
.unwrap_or_default()
|
|
||||||
.split('&')
|
|
||||||
.filter_map(|q| q.split_once('='))
|
|
||||||
.map(|(k, v)| {
|
|
||||||
let k = lua.create_string(k)?;
|
|
||||||
let v = lua.create_string(v)?;
|
|
||||||
Ok((k, v))
|
|
||||||
})
|
|
||||||
.collect::<LuaResult<_>>()?;
|
|
||||||
|
|
||||||
#[allow(clippy::mutable_key_type)]
|
|
||||||
let headers: HashMap<LuaString, LuaString> = self
|
|
||||||
.head
|
|
||||||
.headers
|
|
||||||
.iter()
|
|
||||||
.map(|(k, v)| {
|
|
||||||
let k = lua.create_string(k.as_str())?;
|
|
||||||
let v = lua.create_string(v.as_bytes())?;
|
|
||||||
Ok((k, v))
|
|
||||||
})
|
|
||||||
.collect::<LuaResult<_>>()?;
|
|
||||||
|
|
||||||
TableBuilder::new(lua.clone())?
|
|
||||||
.with_value("method", method)?
|
|
||||||
.with_value("path", path)?
|
|
||||||
.with_value("query", query)?
|
|
||||||
.with_value("headers", headers)?
|
|
||||||
.with_value("body", body)?
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,89 +0,0 @@
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use bstr::{BString, ByteSlice};
|
|
||||||
use http_body_util::Full;
|
|
||||||
use hyper::{
|
|
||||||
body::Bytes,
|
|
||||||
header::{HeaderName, HeaderValue},
|
|
||||||
HeaderMap, Response,
|
|
||||||
};
|
|
||||||
|
|
||||||
use mlua::prelude::*;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub(super) enum LuaResponseKind {
|
|
||||||
PlainText,
|
|
||||||
Table,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) struct LuaResponse {
|
|
||||||
pub(super) kind: LuaResponseKind,
|
|
||||||
pub(super) status: u16,
|
|
||||||
pub(super) headers: HeaderMap,
|
|
||||||
pub(super) body: Option<Vec<u8>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LuaResponse {
|
|
||||||
pub(super) fn into_response(self) -> LuaResult<Response<Full<Bytes>>> {
|
|
||||||
Ok(match self.kind {
|
|
||||||
LuaResponseKind::PlainText => Response::builder()
|
|
||||||
.status(200)
|
|
||||||
.header("Content-Type", "text/plain")
|
|
||||||
.body(Full::new(Bytes::from(self.body.unwrap())))
|
|
||||||
.into_lua_err()?,
|
|
||||||
LuaResponseKind::Table => {
|
|
||||||
let mut response = Response::builder()
|
|
||||||
.status(self.status)
|
|
||||||
.body(Full::new(Bytes::from(self.body.unwrap_or_default())))
|
|
||||||
.into_lua_err()?;
|
|
||||||
response.headers_mut().extend(self.headers);
|
|
||||||
response
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromLua for LuaResponse {
|
|
||||||
fn from_lua(value: LuaValue, _: &Lua) -> LuaResult<Self> {
|
|
||||||
match value {
|
|
||||||
// Plain strings from the handler are plaintext responses
|
|
||||||
LuaValue::String(s) => Ok(Self {
|
|
||||||
kind: LuaResponseKind::PlainText,
|
|
||||||
status: 200,
|
|
||||||
headers: HeaderMap::new(),
|
|
||||||
body: Some(s.as_bytes().to_vec()),
|
|
||||||
}),
|
|
||||||
// Tables are more detailed responses with potential status, headers, body
|
|
||||||
LuaValue::Table(t) => {
|
|
||||||
let status: Option<u16> = t.get("status")?;
|
|
||||||
let headers: Option<LuaTable> = t.get("headers")?;
|
|
||||||
let body: Option<BString> = t.get("body")?;
|
|
||||||
|
|
||||||
let mut headers_map = HeaderMap::new();
|
|
||||||
if let Some(headers) = headers {
|
|
||||||
for pair in headers.pairs::<String, LuaString>() {
|
|
||||||
let (h, v) = pair?;
|
|
||||||
let name = HeaderName::from_str(&h).into_lua_err()?;
|
|
||||||
let value = HeaderValue::from_bytes(&v.as_bytes()).into_lua_err()?;
|
|
||||||
headers_map.insert(name, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let body_bytes = body.map(|s| s.as_bytes().to_vec());
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
kind: LuaResponseKind::Table,
|
|
||||||
status: status.unwrap_or(200),
|
|
||||||
headers: headers_map,
|
|
||||||
body: body_bytes,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// Anything else is an error
|
|
||||||
value => Err(LuaError::FromLuaConversionError {
|
|
||||||
from: value.type_name(),
|
|
||||||
to: "NetServeResponse".to_string(),
|
|
||||||
message: None,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,82 +0,0 @@
|
||||||
use std::{future::Future, net::SocketAddr, pin::Pin};
|
|
||||||
|
|
||||||
use http_body_util::{BodyExt, Full};
|
|
||||||
use hyper::{
|
|
||||||
body::{Bytes, Incoming},
|
|
||||||
service::Service,
|
|
||||||
Request, Response,
|
|
||||||
};
|
|
||||||
use hyper_tungstenite::{is_upgrade_request, upgrade};
|
|
||||||
|
|
||||||
use mlua::prelude::*;
|
|
||||||
use mlua_luau_scheduler::{LuaSchedulerExt, LuaSpawnExt};
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
super::websocket::NetWebSocket, keys::SvcKeys, request::LuaRequest, response::LuaResponse,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub(super) struct Svc {
|
|
||||||
pub(super) lua: Lua,
|
|
||||||
pub(super) addr: SocketAddr,
|
|
||||||
pub(super) keys: SvcKeys,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Service<Request<Incoming>> for Svc {
|
|
||||||
type Response = Response<Full<Bytes>>;
|
|
||||||
type Error = LuaError;
|
|
||||||
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
|
|
||||||
|
|
||||||
fn call(&self, req: Request<Incoming>) -> Self::Future {
|
|
||||||
let lua = self.lua.clone();
|
|
||||||
let addr = self.addr;
|
|
||||||
let keys = self.keys;
|
|
||||||
|
|
||||||
if keys.has_websocket_handler() && is_upgrade_request(&req) {
|
|
||||||
Box::pin(async move {
|
|
||||||
let (res, sock) = upgrade(req, None).into_lua_err()?;
|
|
||||||
|
|
||||||
let lua_inner = lua.clone();
|
|
||||||
lua.spawn_local(async move {
|
|
||||||
let sock = sock.await.unwrap();
|
|
||||||
let lua_sock = NetWebSocket::new(sock);
|
|
||||||
let lua_val = lua_sock.into_lua(&lua_inner).unwrap();
|
|
||||||
|
|
||||||
let handler_websocket: LuaFunction =
|
|
||||||
keys.websocket_handler(&lua_inner).unwrap().unwrap();
|
|
||||||
|
|
||||||
lua_inner
|
|
||||||
.push_thread_back(handler_websocket, lua_val)
|
|
||||||
.unwrap();
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(res)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
let (head, body) = req.into_parts();
|
|
||||||
|
|
||||||
Box::pin(async move {
|
|
||||||
let handler_request: LuaFunction = keys.request_handler(&lua).unwrap();
|
|
||||||
|
|
||||||
let body = body.collect().await.into_lua_err()?;
|
|
||||||
let body = body.to_bytes().to_vec();
|
|
||||||
|
|
||||||
let lua_req = LuaRequest {
|
|
||||||
_remote_addr: addr,
|
|
||||||
head,
|
|
||||||
body,
|
|
||||||
};
|
|
||||||
let lua_req_table = lua_req.into_lua_table(&lua)?;
|
|
||||||
|
|
||||||
let thread_id = lua.push_thread_back(handler_request, lua_req_table)?;
|
|
||||||
lua.track_thread(thread_id);
|
|
||||||
lua.wait_for_thread(thread_id).await;
|
|
||||||
let thread_res = lua
|
|
||||||
.get_thread_result(thread_id)
|
|
||||||
.expect("Missing handler thread result")?;
|
|
||||||
|
|
||||||
LuaResponse::from_lua_multi(thread_res, &lua)?.into_response()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,94 +0,0 @@
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use hyper::header::{CONTENT_ENCODING, CONTENT_LENGTH};
|
|
||||||
use reqwest::header::HeaderMap;
|
|
||||||
|
|
||||||
use mlua::prelude::*;
|
|
||||||
|
|
||||||
use lune_utils::TableBuilder;
|
|
||||||
|
|
||||||
pub fn create_user_agent_header(lua: &Lua) -> LuaResult<String> {
|
|
||||||
let version_global = lua
|
|
||||||
.globals()
|
|
||||||
.get::<LuaString>("_VERSION")
|
|
||||||
.expect("Missing _VERSION global");
|
|
||||||
|
|
||||||
let version_global_str = version_global
|
|
||||||
.to_str()
|
|
||||||
.context("Invalid utf8 found in _VERSION global")?;
|
|
||||||
|
|
||||||
let (package_name, full_version) = version_global_str.split_once(' ').unwrap();
|
|
||||||
|
|
||||||
Ok(format!("{}/{}", package_name.to_lowercase(), full_version))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn header_map_to_table(
|
|
||||||
lua: &Lua,
|
|
||||||
headers: HeaderMap,
|
|
||||||
remove_content_headers: bool,
|
|
||||||
) -> LuaResult<LuaTable> {
|
|
||||||
let mut res_headers: HashMap<String, Vec<String>> = HashMap::new();
|
|
||||||
for (name, value) in &headers {
|
|
||||||
let name = name.as_str();
|
|
||||||
let value = value.to_str().unwrap().to_owned();
|
|
||||||
if let Some(existing) = res_headers.get_mut(name) {
|
|
||||||
existing.push(value);
|
|
||||||
} else {
|
|
||||||
res_headers.insert(name.to_owned(), vec![value]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if remove_content_headers {
|
|
||||||
let content_encoding_header_str = CONTENT_ENCODING.as_str();
|
|
||||||
let content_length_header_str = CONTENT_LENGTH.as_str();
|
|
||||||
res_headers.retain(|name, _| {
|
|
||||||
name != content_encoding_header_str && name != content_length_header_str
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut builder = TableBuilder::new(lua.clone())?;
|
|
||||||
for (name, mut values) in res_headers {
|
|
||||||
if values.len() == 1 {
|
|
||||||
let value = values.pop().unwrap().into_lua(lua)?;
|
|
||||||
builder = builder.with_value(name, value)?;
|
|
||||||
} else {
|
|
||||||
let values = TableBuilder::new(lua.clone())?
|
|
||||||
.with_sequential_values(values)?
|
|
||||||
.build_readonly()?
|
|
||||||
.into_lua(lua)?;
|
|
||||||
builder = builder.with_value(name, values)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.build_readonly()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn table_to_hash_map(
|
|
||||||
tab: LuaTable,
|
|
||||||
tab_origin_key: &'static str,
|
|
||||||
) -> LuaResult<HashMap<String, Vec<String>>> {
|
|
||||||
let mut map = HashMap::new();
|
|
||||||
|
|
||||||
for pair in tab.pairs::<String, LuaValue>() {
|
|
||||||
let (key, value) = pair?;
|
|
||||||
match value {
|
|
||||||
LuaValue::String(s) => {
|
|
||||||
map.insert(key, vec![s.to_str()?.to_owned()]);
|
|
||||||
}
|
|
||||||
LuaValue::Table(t) => {
|
|
||||||
let mut values = Vec::new();
|
|
||||||
for value in t.sequence_values::<LuaString>() {
|
|
||||||
values.push(value?.to_str()?.to_owned());
|
|
||||||
}
|
|
||||||
map.insert(key, values);
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return Err(LuaError::runtime(format!(
|
|
||||||
"Value for '{tab_origin_key}' must be a string or array of strings",
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(map)
|
|
||||||
}
|
|
|
@ -1,149 +0,0 @@
|
||||||
use std::sync::{
|
|
||||||
atomic::{AtomicBool, AtomicU16, Ordering},
|
|
||||||
Arc,
|
|
||||||
};
|
|
||||||
|
|
||||||
use bstr::{BString, ByteSlice};
|
|
||||||
use mlua::prelude::*;
|
|
||||||
|
|
||||||
use futures_util::{
|
|
||||||
stream::{SplitSink, SplitStream},
|
|
||||||
SinkExt, StreamExt,
|
|
||||||
};
|
|
||||||
use tokio::{
|
|
||||||
io::{AsyncRead, AsyncWrite},
|
|
||||||
sync::Mutex as AsyncMutex,
|
|
||||||
};
|
|
||||||
|
|
||||||
use hyper_tungstenite::{
|
|
||||||
tungstenite::{
|
|
||||||
protocol::{frame::coding::CloseCode as WsCloseCode, CloseFrame as WsCloseFrame},
|
|
||||||
Message as WsMessage,
|
|
||||||
},
|
|
||||||
WebSocketStream,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct NetWebSocket<T> {
|
|
||||||
close_code_exists: Arc<AtomicBool>,
|
|
||||||
close_code_value: Arc<AtomicU16>,
|
|
||||||
read_stream: Arc<AsyncMutex<SplitStream<WebSocketStream<T>>>>,
|
|
||||||
write_stream: Arc<AsyncMutex<SplitSink<WebSocketStream<T>, WsMessage>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Clone for NetWebSocket<T> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self {
|
|
||||||
close_code_exists: Arc::clone(&self.close_code_exists),
|
|
||||||
close_code_value: Arc::clone(&self.close_code_value),
|
|
||||||
read_stream: Arc::clone(&self.read_stream),
|
|
||||||
write_stream: Arc::clone(&self.write_stream),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> NetWebSocket<T>
|
|
||||||
where
|
|
||||||
T: AsyncRead + AsyncWrite + Unpin + 'static,
|
|
||||||
{
|
|
||||||
pub fn new(value: WebSocketStream<T>) -> Self {
|
|
||||||
let (write, read) = value.split();
|
|
||||||
|
|
||||||
Self {
|
|
||||||
close_code_exists: Arc::new(AtomicBool::new(false)),
|
|
||||||
close_code_value: Arc::new(AtomicU16::new(0)),
|
|
||||||
read_stream: Arc::new(AsyncMutex::new(read)),
|
|
||||||
write_stream: Arc::new(AsyncMutex::new(write)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_close_code(&self) -> Option<u16> {
|
|
||||||
if self.close_code_exists.load(Ordering::Relaxed) {
|
|
||||||
Some(self.close_code_value.load(Ordering::Relaxed))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_close_code(&self, code: u16) {
|
|
||||||
self.close_code_exists.store(true, Ordering::Relaxed);
|
|
||||||
self.close_code_value.store(code, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn send(&self, msg: WsMessage) -> LuaResult<()> {
|
|
||||||
let mut ws = self.write_stream.lock().await;
|
|
||||||
ws.send(msg).await.into_lua_err()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn next(&self) -> LuaResult<Option<WsMessage>> {
|
|
||||||
let mut ws = self.read_stream.lock().await;
|
|
||||||
ws.next().await.transpose().into_lua_err()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn close(&self, code: Option<u16>) -> LuaResult<()> {
|
|
||||||
if self.close_code_exists.load(Ordering::Relaxed) {
|
|
||||||
return Err(LuaError::runtime("Socket has already been closed"));
|
|
||||||
}
|
|
||||||
|
|
||||||
self.send(WsMessage::Close(Some(WsCloseFrame {
|
|
||||||
code: match code {
|
|
||||||
Some(code) if (1000..=4999).contains(&code) => WsCloseCode::from(code),
|
|
||||||
Some(code) => {
|
|
||||||
return Err(LuaError::runtime(format!(
|
|
||||||
"Close code must be between 1000 and 4999, got {code}"
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
None => WsCloseCode::Normal,
|
|
||||||
},
|
|
||||||
reason: "".into(),
|
|
||||||
})))
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let mut ws = self.write_stream.lock().await;
|
|
||||||
ws.close().await.into_lua_err()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> LuaUserData for NetWebSocket<T>
|
|
||||||
where
|
|
||||||
T: AsyncRead + AsyncWrite + Unpin + 'static,
|
|
||||||
{
|
|
||||||
fn add_fields<F: LuaUserDataFields<Self>>(fields: &mut F) {
|
|
||||||
fields.add_field_method_get("closeCode", |_, this| Ok(this.get_close_code()));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_methods<M: LuaUserDataMethods<Self>>(methods: &mut M) {
|
|
||||||
methods.add_async_method("close", |_, this, code: Option<u16>| async move {
|
|
||||||
this.close(code).await
|
|
||||||
});
|
|
||||||
|
|
||||||
methods.add_async_method(
|
|
||||||
"send",
|
|
||||||
|_, this, (string, as_binary): (BString, Option<bool>)| async move {
|
|
||||||
this.send(if as_binary.unwrap_or_default() {
|
|
||||||
WsMessage::Binary(string.as_bytes().to_vec())
|
|
||||||
} else {
|
|
||||||
let s = string.to_str().into_lua_err()?;
|
|
||||||
WsMessage::Text(s.to_string())
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
methods.add_async_method("next", |lua, this, (): ()| async move {
|
|
||||||
let msg = this.next().await?;
|
|
||||||
|
|
||||||
if let Some(WsMessage::Close(Some(frame))) = msg.as_ref() {
|
|
||||||
this.set_close_code(frame.code.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(match msg {
|
|
||||||
Some(WsMessage::Binary(bin)) => LuaValue::String(lua.create_string(bin)?),
|
|
||||||
Some(WsMessage::Text(txt)) => LuaValue::String(lua.create_string(txt)?),
|
|
||||||
Some(WsMessage::Close(_)) | None => LuaValue::Nil,
|
|
||||||
// Ignore ping/pong/frame messages, they are handled by tungstenite
|
|
||||||
msg => unreachable!("Unhandled message: {:?}", msg),
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Add table
Reference in a new issue