Try websockets
This commit is contained in:
parent
0ca02591bd
commit
03d7ac54d1
9 changed files with 579 additions and 59 deletions
323
Cargo.lock
generated
323
Cargo.lock
generated
|
@ -2,12 +2,48 @@
|
|||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
|
@ -30,6 +66,106 @@ dependencies = [
|
|||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
|
||||
dependencies = [
|
||||
"matches",
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httparse"
|
||||
version = "1.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
|
||||
dependencies = [
|
||||
"matches",
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.57"
|
||||
|
@ -60,12 +196,30 @@ dependencies = [
|
|||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matches"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
|
||||
|
||||
[[package]]
|
||||
name = "memory_units"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3"
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.39"
|
||||
|
@ -84,12 +238,59 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scoped-tls"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.137"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
|
||||
|
||||
[[package]]
|
||||
name = "sha-1"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.96"
|
||||
|
@ -101,21 +302,109 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec_macros"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
||||
|
||||
[[package]]
|
||||
name = "tungstenite"
|
||||
version = "0.17.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d96a2dea40e7570482f28eb57afbe42d97551905da6a9400acc5c328d24004f5"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"http",
|
||||
"httparse",
|
||||
"log",
|
||||
"rand",
|
||||
"sha-1",
|
||||
"thiserror",
|
||||
"url",
|
||||
"utf-8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9"
|
||||
dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
"matches",
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "usdpl"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"js-sys",
|
||||
"usdpl-core",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-test",
|
||||
"wasm-rs-shared-channel",
|
||||
"web-sys",
|
||||
"wee_alloc",
|
||||
]
|
||||
|
@ -124,6 +413,7 @@ dependencies = [
|
|||
name = "usdpl-back"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"tungstenite",
|
||||
"usdpl-core",
|
||||
]
|
||||
|
||||
|
@ -135,6 +425,24 @@ version = "0.1.0"
|
|||
name = "usdpl-rs"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "utf-8"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.2+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.80"
|
||||
|
@ -225,6 +533,19 @@ dependencies = [
|
|||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-rs-shared-channel"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c555c35b5c51f5f134fb470b103435880fd7a9def4e830dc0081d8af5b09378b"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"js-sys",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.57"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "usdpl-back"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
edition = "2021"
|
||||
license = "GPL-3.0-only"
|
||||
repository = "https://github.com/NGnius/usdpl-rs"
|
||||
|
@ -14,3 +14,4 @@ crankshaft = []
|
|||
|
||||
[dependencies]
|
||||
usdpl-core = { version = "0.1.0", path = "../usdpl-core" }
|
||||
tungstenite = { version = "0.17" }
|
||||
|
|
6
usdpl-back/src/errors.rs
Normal file
6
usdpl-back/src/errors.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
pub enum ServerError {
|
||||
Tungstenite(tungstenite::error::Error),
|
||||
Io(std::io::Error),
|
||||
}
|
||||
|
||||
pub type ServerResult = Result<(), ServerError>;
|
|
@ -1,6 +1,9 @@
|
|||
use std::net::{TcpListener, TcpStream};
|
||||
use std::net::{TcpListener, TcpStream, SocketAddr};
|
||||
use std::collections::HashMap;
|
||||
use std::io::{Read, Write};
|
||||
//use std::io::{Read, Write};
|
||||
|
||||
use tungstenite::accept;
|
||||
use tungstenite::protocol::{Message, WebSocket};
|
||||
|
||||
use usdpl_core::serdes::{Dumpable, Loadable, Primitive};
|
||||
use usdpl_core::{RemoteCallResponse, socket};
|
||||
|
@ -27,7 +30,7 @@ impl<'a> Instance<'a> {
|
|||
self
|
||||
}
|
||||
|
||||
fn handle_packet<const ERROR: bool>(&mut self, packet: socket::Packet, buffer: &mut [u8], incoming: &mut TcpStream) -> std::io::Result<()> {
|
||||
fn handle_packet<const ERROR: bool>(&mut self, packet: socket::Packet, buffer: &mut [u8], incoming: &mut WebSocket<TcpStream>, peer_addr: &SocketAddr) -> super::ServerResult {
|
||||
match packet {
|
||||
socket::Packet::Call(obj) => {
|
||||
if let Some(target_func) = self.calls.get_mut(&obj.function) {
|
||||
|
@ -39,18 +42,22 @@ impl<'a> Instance<'a> {
|
|||
});
|
||||
let (ok, len) = response.dump(buffer);
|
||||
if !ok && ERROR {
|
||||
return Err(std::io::Error::new(std::io::ErrorKind::Unsupported, format!("Cannot dump return value of function `{}`", &obj.function)));
|
||||
return Err(super::ServerError::Io(std::io::Error::new(std::io::ErrorKind::Unsupported, format!("Cannot dump return value of function `{}`", &obj.function))));
|
||||
}
|
||||
if ERROR {
|
||||
incoming.write(&buffer[..len])?;
|
||||
let mut vec = Vec::with_capacity(len);
|
||||
vec.extend_from_slice(&buffer[..len]);
|
||||
incoming.write_message(Message::Binary(vec)).map_err(super::ServerError::Tungstenite)?;
|
||||
} else {
|
||||
incoming.write(&buffer[..len]).unwrap_or_default();
|
||||
let mut vec = Vec::with_capacity(len);
|
||||
vec.extend_from_slice(&buffer[..len]);
|
||||
incoming.write_message(Message::Binary(vec)).unwrap_or_default();
|
||||
}
|
||||
} else {
|
||||
if ERROR {
|
||||
return Err(std::io::Error::new(std::io::ErrorKind::Unsupported, format!("Invalid remote call `{}` received from {}", obj.function, incoming.peer_addr()?)));
|
||||
return Err(super::ServerError::Io(std::io::Error::new(std::io::ErrorKind::Unsupported, format!("Invalid remote call `{}` received from {}", obj.function, peer_addr))));
|
||||
} else {
|
||||
eprintln!("Invalid remote call `{}` received from {}", obj.function, incoming.peer_addr()?);
|
||||
eprintln!("Invalid remote call `{}` received from {}", obj.function, peer_addr);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -60,54 +67,89 @@ impl<'a> Instance<'a> {
|
|||
if let socket::Packet::Many(_) = packet {
|
||||
// drop nested socket packets (prevents DoS and bad practices)
|
||||
if ERROR {
|
||||
return Err(std::io::Error::new(std::io::ErrorKind::Unsupported, format!("Invalid nested Many packet received from {}", incoming.peer_addr()?)));
|
||||
return Err(super::ServerError::Io(std::io::Error::new(std::io::ErrorKind::Unsupported, format!("Invalid nested Many packet received from {}", peer_addr))));
|
||||
} else {
|
||||
eprintln!("Invalid nested Many packet received from {}", incoming.peer_addr()?);
|
||||
eprintln!("Invalid nested Many packet received from {}", peer_addr);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
self.handle_packet::<ERROR>(packet, buffer, incoming)?;
|
||||
self.handle_packet::<ERROR>(packet, buffer, incoming, peer_addr)?;
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
let (ok, len) = socket::Packet::Unsupported.dump(buffer);
|
||||
if !ok && ERROR {
|
||||
return Err(std::io::Error::new(std::io::ErrorKind::Unsupported, format!("Cannot dump unsupported packet")));
|
||||
return Err(super::ServerError::Io(std::io::Error::new(std::io::ErrorKind::Unsupported, format!("Cannot dump unsupported packet"))));
|
||||
}
|
||||
if ERROR {
|
||||
incoming.write(&buffer[..len])?;
|
||||
let mut vec = Vec::with_capacity(len);
|
||||
vec.extend_from_slice(&buffer[..len]);
|
||||
incoming.write_message(Message::Binary(vec)).map_err(super::ServerError::Tungstenite)?;
|
||||
} else {
|
||||
incoming.write(&buffer[..len]).unwrap_or_default();
|
||||
let mut vec = Vec::with_capacity(len);
|
||||
vec.extend_from_slice(&buffer[..len]);
|
||||
incoming.write_message(Message::Binary(vec)).unwrap_or_default();
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn serve<const ERROR: bool>(&mut self) -> std::io::Result<()> {
|
||||
pub fn serve<const ERROR: bool>(&mut self) -> super::ServerResult {
|
||||
let result = self.serve_internal::<ERROR>();
|
||||
//println!("Stopping server due to serve_internal returning a result");
|
||||
result
|
||||
}
|
||||
|
||||
/// Receive and execute callbacks forever
|
||||
pub fn serve_internal<const ERROR: bool>(&mut self) -> std::io::Result<()> {
|
||||
let listener = TcpListener::bind(socket::socket_addr(self.port))?;
|
||||
pub fn serve_internal<const ERROR: bool>(&mut self) -> super::ServerResult {
|
||||
let listener = TcpListener::bind(socket::socket_addr(self.port)).map_err(super::ServerError::Io)?;
|
||||
let mut buffer = [0u8; socket::PACKET_BUFFER_SIZE];
|
||||
for incoming in listener.incoming() {
|
||||
let mut incoming = incoming?;
|
||||
let mut buffer = [0u8; socket::PACKET_BUFFER_SIZE];
|
||||
let len = incoming.read(&mut buffer)?;
|
||||
let (obj_maybe, _) = socket::Packet::load(&buffer[..len]);
|
||||
if let Some(packet) = obj_maybe {
|
||||
self.handle_packet::<ERROR>(packet, &mut buffer, &mut incoming)?;
|
||||
} else {
|
||||
if ERROR {
|
||||
return Err(std::io::Error::new(std::io::ErrorKind::Unsupported, format!("Invalid packet received from {}", incoming.peer_addr()?)));
|
||||
let incoming = incoming.map_err(super::ServerError::Io)?;
|
||||
let peer_addr = incoming.peer_addr().map_err(super::ServerError::Io)?;
|
||||
let mut incoming = match accept(incoming) {
|
||||
Ok(s) => s,
|
||||
Err(_) => continue,
|
||||
};
|
||||
match incoming.read_message() {
|
||||
Err(e) => if ERROR {
|
||||
return Err(super::ServerError::Io(std::io::Error::new(std::io::ErrorKind::Unsupported, format!("Invalid message received from {}: {}", peer_addr, e))));
|
||||
} else {
|
||||
eprintln!("Invalid packet received from {}", incoming.peer_addr()?);
|
||||
eprintln!("Invalid message received from {}: {}", peer_addr, e);
|
||||
},
|
||||
Ok(Message::Binary(bin)) => {
|
||||
let (obj_maybe, _) = socket::Packet::load(bin.as_slice());
|
||||
if let Some(packet) = obj_maybe {
|
||||
self.handle_packet::<ERROR>(packet, &mut buffer, &mut incoming, &peer_addr)?;
|
||||
} else {
|
||||
if ERROR {
|
||||
return Err(super::ServerError::Io(std::io::Error::new(std::io::ErrorKind::Unsupported, format!("Invalid packet received from {}", peer_addr))));
|
||||
} else {
|
||||
eprintln!("Invalid packet received from {}", peer_addr);
|
||||
}
|
||||
}
|
||||
},
|
||||
Ok(_) => {
|
||||
let (_, len) = socket::Packet::Unsupported.dump(&mut buffer);
|
||||
if ERROR {
|
||||
let mut vec = Vec::with_capacity(len);
|
||||
vec.extend_from_slice(&buffer[..len]);
|
||||
incoming.write_message(Message::Binary(vec)).map_err(super::ServerError::Tungstenite)?;
|
||||
} else {
|
||||
let mut vec = Vec::with_capacity(len);
|
||||
vec.extend_from_slice(&buffer[..len]);
|
||||
incoming.write_message(Message::Binary(vec)).unwrap_or_default();
|
||||
}
|
||||
}
|
||||
}
|
||||
incoming.close(None).map_err(super::ServerError::Tungstenite)?;
|
||||
'endless: loop {
|
||||
match incoming.read_message() {
|
||||
Err(tungstenite::error::Error::ConnectionClosed) => break 'endless,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
incoming.shutdown(std::net::Shutdown::Both)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -115,15 +157,15 @@ impl<'a> Instance<'a> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::net::TcpStream;
|
||||
use super::*;
|
||||
//use std::net::TcpStream;
|
||||
//use super::*;
|
||||
|
||||
const PORT: u16 = 31337;
|
||||
//const PORT: u16 = 31337;
|
||||
|
||||
#[test]
|
||||
/*#[test]
|
||||
fn serve_full_test() -> std::io::Result<()> {
|
||||
let _server = std::thread::spawn(|| {
|
||||
Instance::new(PORT, PORT + 80)
|
||||
Instance::new(PORT)
|
||||
.register("echo".to_string(), &mut |params| params)
|
||||
.register("hello".to_string(), &mut |params| {
|
||||
if let Some(Primitive::String(name)) = params.get(0) {
|
||||
|
@ -176,7 +218,7 @@ mod tests {
|
|||
front.write(&buffer[..len]).unwrap();
|
||||
let _ = front.read(&mut buffer).unwrap();
|
||||
});
|
||||
Instance::new(PORT+1, PORT+1+80)
|
||||
Instance::new(PORT+1)
|
||||
.register("echo".to_string(), &mut |params| params)
|
||||
.register("hello".to_string(), &mut |params| {
|
||||
if let Some(Primitive::String(name)) = params.get(0) {
|
||||
|
@ -193,7 +235,7 @@ mod tests {
|
|||
#[should_panic]
|
||||
fn serve_unsupported_test() {
|
||||
let _server = std::thread::spawn(|| {
|
||||
Instance::new(PORT+2, PORT+2+80)
|
||||
Instance::new(PORT+2)
|
||||
.register("echo".to_string(), &mut |params| params)
|
||||
.register("hello".to_string(), &mut |params| {
|
||||
if let Some(Primitive::String(name)) = params.get(0) {
|
||||
|
@ -220,5 +262,5 @@ mod tests {
|
|||
} else {
|
||||
panic!("Wrong response packet type");
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
|
|
@ -4,9 +4,11 @@
|
|||
//! This is a minimalist TCP server for handling events from the front-end.
|
||||
//!
|
||||
|
||||
mod errors;
|
||||
mod instance;
|
||||
|
||||
pub use instance::Instance;
|
||||
pub use errors::{ServerError, ServerResult};
|
||||
|
||||
pub mod core {
|
||||
pub use usdpl_core::*;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "usdpl"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
authors = ["NGnius (Graham) <ngniusness@gmail.com>"]
|
||||
edition = "2021"
|
||||
license = "GPL-3.0-only"
|
||||
|
@ -17,7 +17,7 @@ decky = []
|
|||
crankshaft = []
|
||||
|
||||
[dependencies]
|
||||
wasm-bindgen = "0.2.63"
|
||||
wasm-bindgen = "0.2"
|
||||
|
||||
# The `console_error_panic_hook` crate provides better debugging of panics by
|
||||
# logging them with `console.error`. This is great for development, but requires
|
||||
|
@ -32,8 +32,9 @@ console_error_panic_hook = { version = "0.1.6", optional = true }
|
|||
# Unfortunately, `wee_alloc` requires nightly Rust when targeting wasm for now.
|
||||
wee_alloc = { version = "0.4.5", optional = true }
|
||||
|
||||
web-sys = { version = "0.3", features = ["TcpSocket"] }
|
||||
web-sys = { version = "0.3", features = ["WebSocket", "MessageEvent", "ErrorEvent", "BinaryType"] }
|
||||
js-sys = { version = "0.3" }
|
||||
wasm-rs-shared-channel = { version = "0.1" }
|
||||
|
||||
usdpl-core = { version = "0.1.0", path = "../usdpl-core" }
|
||||
|
||||
|
|
|
@ -1,22 +1,49 @@
|
|||
use std::net::TcpStream;
|
||||
use std::io::{Read, Write};
|
||||
|
||||
use web_sys::TcpSocket;
|
||||
use js_sys::{ArrayBuffer, DataView};
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen::JsCast;
|
||||
|
||||
use web_sys::{WebSocket, MessageEvent, ErrorEvent};
|
||||
use js_sys::{ArrayBuffer, DataView, Uint8Array};
|
||||
use wasm_rs_shared_channel::{Expects, spsc::{Receiver, Sender}};
|
||||
|
||||
use usdpl_core::socket;
|
||||
use usdpl_core::serdes::{Dumpable, Loadable};
|
||||
|
||||
use super::imports;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn send(packet: socket::Packet, port: u16) -> bool {
|
||||
let socket = match TcpSocket::new(socket::HOST_STR, port) {
|
||||
/// Send packet over a Javascript socket
|
||||
pub(crate) fn send_js(packet: socket::Packet, port: u16) -> Option<socket::Packet> {
|
||||
let addr = format!("wss://{}:{}", socket::HOST_STR, port);
|
||||
let socket = match WebSocket::new(&addr) {
|
||||
Ok(s) => s,
|
||||
Err(_) => return false,
|
||||
Err(e) => {
|
||||
imports::console_error(
|
||||
&format!("USDPL error: TcpSocket::new(...) failed with error {}",
|
||||
js_sys::JSON::stringify(&e)
|
||||
.map(|x| x.as_string().unwrap_or("WTF".into()))
|
||||
.unwrap_or("unknown error".into())));
|
||||
return None;
|
||||
}
|
||||
};
|
||||
socket.set_binary_type(web_sys::BinaryType::Arraybuffer);
|
||||
let (tx, rx) : (Sender<_>, Receiver<_>) = wasm_rs_shared_channel::spsc::channel(socket::PACKET_BUFFER_SIZE as u32 + 4).split();
|
||||
|
||||
let onmessage_callback = Closure::wrap(Box::new(onmessage_factory(tx)) as Box<dyn FnMut(MessageEvent)>);
|
||||
socket.set_onmessage(Some(onmessage_callback.as_ref().unchecked_ref()));
|
||||
//onmessage_callback.forget();
|
||||
|
||||
let onerror_callback = Closure::wrap(Box::new(onerror_factory()) as Box<dyn FnMut(ErrorEvent)>);
|
||||
socket.set_onerror(Some(onerror_callback.as_ref().unchecked_ref()));
|
||||
//onerror_callback.forget();
|
||||
|
||||
let mut buffer = [0u8; socket::PACKET_BUFFER_SIZE];
|
||||
let (ok, len) = packet.dump(&mut buffer);
|
||||
if !ok {
|
||||
return false;
|
||||
imports::console_error("USDPL error: packet dump failed");
|
||||
return None;
|
||||
}
|
||||
// copy to JS buffer
|
||||
let array_buffer = ArrayBuffer::new(len as u32);
|
||||
|
@ -25,28 +52,136 @@ pub(crate) fn send(packet: socket::Packet, port: u16) -> bool {
|
|||
dataview.set_uint8(i, buffer[i]);
|
||||
}
|
||||
match socket.send_with_array_buffer(&array_buffer) {
|
||||
Ok(b) => b,
|
||||
Err(_) => false
|
||||
Ok(_) => {},
|
||||
Err(e) => {
|
||||
imports::console_error(&format!("USDPL error: socket send_with_array_buffer(...) failed -- {:?}", e));
|
||||
return None;
|
||||
}
|
||||
}
|
||||
let result = match rx.recv(Some(std::time::Duration::from_secs(60))) {
|
||||
Ok(Some(val)) => {
|
||||
socket::Packet::load(&val.1[..val.0 as _]).0
|
||||
},
|
||||
Ok(None) => {
|
||||
imports::console_error(&format!("USDPL error: SharedChannel recv timed out"));
|
||||
None
|
||||
},
|
||||
Err(e) => {
|
||||
imports::console_error(&format!("USDPL error: got SharedChannel recv error -- {:?}", e));
|
||||
None
|
||||
}
|
||||
};
|
||||
socket.close().unwrap_or(());
|
||||
result
|
||||
}
|
||||
|
||||
fn onmessage_factory(sender: Sender<Sendable<{socket::PACKET_BUFFER_SIZE}>>) -> impl FnMut(MessageEvent) {
|
||||
move |e: MessageEvent| {
|
||||
if let Ok(buf) = e.data().dyn_into::<js_sys::ArrayBuffer>() {
|
||||
let mut buffer = [0u8; socket::PACKET_BUFFER_SIZE];
|
||||
let dataview = DataView::new(&buf, 0, buf.byte_length() as _);
|
||||
for i in 0..buf.byte_length() as usize {
|
||||
if i < socket::PACKET_BUFFER_SIZE {
|
||||
buffer[i] = dataview.get_uint8(i);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if let Err(e) = sender.send(&Sendable(buf.byte_length(), buffer)) {
|
||||
imports::console_error(&format!("USDPL error: got SharedChannel send error {:?}", e));
|
||||
}
|
||||
} else {
|
||||
imports::console_warn(&format!("USDPL warning: Got non-data message from {}", e.origin()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn onerror_factory() -> impl FnMut(ErrorEvent) {
|
||||
move |e: ErrorEvent| {
|
||||
imports::console_error(&format!("USDPL error: got socket error {}", e.message()))
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
/// Send packet over a WASM-native TCP socket
|
||||
pub(crate) fn send_native(packet: socket::Packet, port: u16) -> Option<socket::Packet> {
|
||||
let mut socket = match TcpStream::connect(socket::socket_addr(port)) {
|
||||
Ok(s) => s,
|
||||
Err(_) => return None,
|
||||
Err(e) => {
|
||||
imports::console_error(&format!("USDPL error: TcpStream failed to connect with error {}", e));
|
||||
return None;
|
||||
},
|
||||
};
|
||||
let mut buffer = [0u8; socket::PACKET_BUFFER_SIZE];
|
||||
let (ok, len) = packet.dump(&mut buffer);
|
||||
if !ok {
|
||||
imports::console_error("USDPL error: packet dump failed");
|
||||
return None;
|
||||
}
|
||||
match socket.write(&buffer[..len]) {
|
||||
Ok(_) => {},
|
||||
Err(_) => return None
|
||||
Err(e) => {
|
||||
imports::console_error(&format!("USDPL error: socket write failed with error {}", e));
|
||||
return None;
|
||||
}
|
||||
}
|
||||
let len = match socket.read(&mut buffer) {
|
||||
Ok(len) => len,
|
||||
Err(_) => return None
|
||||
Err(e) => {
|
||||
imports::console_error(&format!("USDPL error: socket read failed with error {}", e));
|
||||
return None;
|
||||
}
|
||||
};
|
||||
socket::Packet::load(&buffer[..len]).0
|
||||
}
|
||||
|
||||
struct Sendable<const SIZE: usize>(u32, [u8; SIZE]);
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SendableError(String);
|
||||
|
||||
impl std::error::Error for SendableError {}
|
||||
|
||||
impl std::fmt::Display for SendableError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
(&self.0 as &dyn std::fmt::Display).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const SIZE: usize> wasm_rs_shared_channel::Shareable for Sendable<SIZE> {
|
||||
type Error = SendableError;
|
||||
|
||||
fn to_bytes(&self) -> Result<Uint8Array, Self::Error> {
|
||||
let array = Uint8Array::new_with_length(SIZE as u32 + 4);
|
||||
let mut cursor = 0;
|
||||
for byte in self.0.to_le_bytes() {
|
||||
array.set_index(cursor, byte);
|
||||
cursor += 1;
|
||||
}
|
||||
for byte in self.1 {
|
||||
array.set_index(cursor, byte);
|
||||
cursor += 1;
|
||||
}
|
||||
Ok(array)
|
||||
}
|
||||
|
||||
fn from(bytes: &Uint8Array) -> Result<Result<Self, Expects>, Self::Error> {
|
||||
if bytes.length() < 4 {
|
||||
return Err(SendableError("Too small for size int".into()));
|
||||
}
|
||||
let len = u32::from_le_bytes([
|
||||
bytes.get_index(0),
|
||||
bytes.get_index(1),
|
||||
bytes.get_index(2),
|
||||
bytes.get_index(3),
|
||||
]);
|
||||
if bytes.length() < len + 4 {
|
||||
return Err(SendableError("Too small for buffer".into()));
|
||||
}
|
||||
let mut buf = [0u8; SIZE];
|
||||
for i in 0..len {
|
||||
buf[i as usize] = bytes.get_index(4 + i);
|
||||
}
|
||||
Ok(Ok(Sendable(len, buf)))
|
||||
}
|
||||
}
|
||||
|
|
13
usdpl-front/src/imports.rs
Normal file
13
usdpl-front/src/imports.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern {
|
||||
#[wasm_bindgen(js_namespace = console, js_name = log)]
|
||||
pub fn console_log(s: &str);
|
||||
|
||||
#[wasm_bindgen(js_namespace = console, js_name = warn)]
|
||||
pub fn console_warn(s: &str);
|
||||
|
||||
#[wasm_bindgen(js_namespace = console, js_name = error)]
|
||||
pub fn console_error(s: &str);
|
||||
}
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
mod connection;
|
||||
mod convert;
|
||||
mod imports;
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
|
@ -18,11 +19,6 @@ const REMOTE_PORT: std::sync::atomic::AtomicU16 = std::sync::atomic::AtomicU16::
|
|||
#[global_allocator]
|
||||
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern {
|
||||
//fn alert(s: &str);
|
||||
}
|
||||
|
||||
/// Initialize the front-end library
|
||||
#[wasm_bindgen]
|
||||
pub fn init_usdpl(port: u16) -> bool {
|
||||
|
@ -52,13 +48,16 @@ pub fn call_backend(name: String, parameters: Vec<JsValue>) -> Option<Vec<JsValu
|
|||
for val in parameters {
|
||||
params.push(convert::js_to_primitive(val));
|
||||
}
|
||||
let results = match connection::send_native(Packet::Call(RemoteCall {
|
||||
let results = match connection::send_js(Packet::Call(RemoteCall {
|
||||
id: next_id,
|
||||
function: name,
|
||||
parameters: params,
|
||||
}), REMOTE_PORT.load(std::sync::atomic::Ordering::Relaxed)) {
|
||||
Some(Packet::CallResponse(resp)) => resp,
|
||||
_ => return None,
|
||||
_ => {
|
||||
imports::console_error("USDPL error: connection::send_native(...) returned None");
|
||||
return None
|
||||
},
|
||||
};
|
||||
let mut js_results = Vec::with_capacity(results.response.len());
|
||||
for val in results.response {
|
||||
|
|
Loading…
Reference in a new issue