web: Use gloo_net instead of manually using web_sys
This relieves some headaches with on connect callback and spawning of the sending async task, since some SWFs like to send data before the connect event is called.
This commit is contained in:
parent
a067e9a5e8
commit
d3765027f0
|
@ -2031,6 +2031,36 @@ dependencies = [
|
|||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gloo-net"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ac9e8288ae2c632fa9f8657ac70bfe38a1530f345282d7ba66a1f70b72b7dc4"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"gloo-utils",
|
||||
"http",
|
||||
"js-sys",
|
||||
"pin-project",
|
||||
"thiserror",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gloo-utils"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glow"
|
||||
version = "0.12.3"
|
||||
|
@ -4217,8 +4247,10 @@ dependencies = [
|
|||
"base64",
|
||||
"chrono",
|
||||
"console_error_panic_hook",
|
||||
"futures-util",
|
||||
"generational-arena",
|
||||
"getrandom",
|
||||
"gloo-net",
|
||||
"js-sys",
|
||||
"ruffle_core",
|
||||
"ruffle_render",
|
||||
|
|
|
@ -50,6 +50,8 @@ serde = { version = "1.0.188", features = ["derive"] }
|
|||
thiserror = "1.0"
|
||||
base64 = "0.21.4"
|
||||
async-channel = "1.9.0"
|
||||
futures-util = { version = "0.3.28", features = ["sink"] }
|
||||
gloo-net = { version = "0.4.0", default-features = false, features = ["websocket"] }
|
||||
|
||||
[dependencies.ruffle_core]
|
||||
path = "../core"
|
||||
|
@ -64,5 +66,5 @@ features = [
|
|||
"ChannelMergerNode", "ChannelSplitterNode", "ClipboardEvent", "DataTransfer", "Element", "Event",
|
||||
"EventTarget", "GainNode", "Headers", "HtmlCanvasElement", "HtmlDocument", "HtmlElement", "HtmlFormElement",
|
||||
"HtmlInputElement", "HtmlTextAreaElement", "KeyboardEvent", "Location", "PointerEvent",
|
||||
"Request", "RequestInit", "Response", "Storage", "WheelEvent", "Window", "ProgressEvent", "WebSocket", "MessageEvent", "BinaryType", "ErrorEvent", "CloseEvent",
|
||||
"Request", "RequestInit", "Response", "Storage", "WheelEvent", "Window",
|
||||
]
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
//! Navigator backend for web
|
||||
use crate::WebSocketProxy;
|
||||
use async_channel::Receiver;
|
||||
use futures_util::{SinkExt, StreamExt};
|
||||
use gloo_net::websocket::{futures::WebSocket, Message};
|
||||
use js_sys::{Array, ArrayBuffer, Uint8Array};
|
||||
use ruffle_core::backend::navigator::{
|
||||
async_return, create_fetch_error, create_specific_fetch_error, ErrorResponse, NavigationMethod,
|
||||
|
@ -17,12 +19,11 @@ use tracing_subscriber::layer::Layered;
|
|||
use tracing_subscriber::Registry;
|
||||
use tracing_wasm::WASMLayer;
|
||||
use url::{ParseError, Url};
|
||||
use wasm_bindgen::prelude::Closure;
|
||||
use wasm_bindgen::JsCast;
|
||||
use wasm_bindgen_futures::{spawn_local, JsFuture};
|
||||
use web_sys::{
|
||||
window, Blob, BlobPropertyBag, CloseEvent, ErrorEvent, HtmlFormElement, HtmlInputElement,
|
||||
MessageEvent, Request as WebRequest, RequestInit, Response as WebResponse, WebSocket,
|
||||
window, Blob, BlobPropertyBag, HtmlFormElement, HtmlInputElement, Request as WebRequest,
|
||||
RequestInit, Response as WebResponse,
|
||||
};
|
||||
|
||||
pub struct WebNavigatorBackend {
|
||||
|
@ -368,6 +369,7 @@ impl NavigatorBackend for WebNavigatorBackend {
|
|||
fn connect_socket(
|
||||
&mut self,
|
||||
host: String,
|
||||
|
||||
port: u16,
|
||||
// NOTE: WebSocket does not allow specifying a timeout, so this goes unused.
|
||||
_timeout: Duration,
|
||||
|
@ -389,7 +391,7 @@ impl NavigatorBackend for WebNavigatorBackend {
|
|||
|
||||
tracing::info!("Connecting to {}", proxy.proxy_url);
|
||||
|
||||
let ws = match WebSocket::new(&proxy.proxy_url) {
|
||||
let ws = match WebSocket::open(&proxy.proxy_url) {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to create WebSocket, reason {:?}", e);
|
||||
|
@ -400,70 +402,43 @@ impl NavigatorBackend for WebNavigatorBackend {
|
|||
}
|
||||
};
|
||||
|
||||
ws.set_binary_type(web_sys::BinaryType::Arraybuffer);
|
||||
|
||||
let sender_onopen = sender.clone();
|
||||
let onopen_callback = Closure::<dyn FnMut()>::new(move || {
|
||||
sender_onopen
|
||||
let (mut sink, mut stream) = ws.split();
|
||||
sender
|
||||
.send(SocketAction::Connect(handle, ConnectionState::Connected))
|
||||
.expect("Working channel send");
|
||||
});
|
||||
.expect("working channel send");
|
||||
|
||||
let sender_onmessage = sender.clone();
|
||||
let onmessage_callback = Closure::<dyn FnMut(_)>::new(move |e: MessageEvent| {
|
||||
if let Ok(buf) = e.data().dyn_into::<js_sys::ArrayBuffer>() {
|
||||
// Handle array buffer.
|
||||
let array = js_sys::Uint8Array::new(&buf);
|
||||
sender_onmessage
|
||||
.send(SocketAction::Data(handle, array.to_vec()))
|
||||
.expect("Working channel send");
|
||||
} else if let Ok(_) = e.data().dyn_into::<web_sys::Blob>() {
|
||||
todo!("Unsupported blob payload");
|
||||
} else {
|
||||
todo!("Unsupported WebSocket payload");
|
||||
// Spawn future to handle incoming messages.
|
||||
let stream_sender = sender.clone();
|
||||
self.spawn_future(Box::pin(async move {
|
||||
while let Some(msg) = stream.next().await {
|
||||
match msg {
|
||||
Ok(Message::Bytes(buf)) => stream_sender
|
||||
.send(SocketAction::Data(handle, buf))
|
||||
.expect("working channel send"),
|
||||
Ok(_) => tracing::warn!("Server sent unexpected text message"),
|
||||
Err(_) => {
|
||||
stream_sender
|
||||
.send(SocketAction::Close(handle))
|
||||
.expect("working channel send");
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let sender_onclose = sender.clone();
|
||||
let onclose_callback = Closure::<dyn FnMut(_)>::new(move |_e: CloseEvent| {
|
||||
sender_onclose
|
||||
.send(SocketAction::Close(handle))
|
||||
.expect("working channel send");
|
||||
});
|
||||
|
||||
let sender_onerror = sender.clone();
|
||||
let onerror_callback = Closure::<dyn FnMut(_)>::new(move |_e: ErrorEvent| {
|
||||
sender_onerror
|
||||
.send(SocketAction::Close(handle))
|
||||
.expect("working channel send");
|
||||
});
|
||||
|
||||
ws.set_onopen(Some(onopen_callback.as_ref().unchecked_ref()));
|
||||
ws.set_onmessage(Some(onmessage_callback.as_ref().unchecked_ref()));
|
||||
ws.set_onclose(Some(onclose_callback.as_ref().unchecked_ref()));
|
||||
ws.set_onerror(Some(onerror_callback.as_ref().unchecked_ref()));
|
||||
|
||||
onopen_callback.forget();
|
||||
onmessage_callback.forget();
|
||||
onclose_callback.forget();
|
||||
onerror_callback.forget();
|
||||
Ok(())
|
||||
}));
|
||||
|
||||
// Spawn future to handle outgoing messages.
|
||||
self.spawn_future(Box::pin(async move {
|
||||
while let Ok(msg) = receiver.recv().await {
|
||||
if let Err(e) = ws.send_with_u8_array(&msg) {
|
||||
tracing::error!("Failed to send message to WebSocket {:?}", e);
|
||||
if let Err(e) = sink.send(Message::Bytes(msg)).await {
|
||||
tracing::warn!("Failed to send message to WebSocket {}", e);
|
||||
sender
|
||||
.send(SocketAction::Close(handle))
|
||||
.expect("working channel send");
|
||||
}
|
||||
}
|
||||
|
||||
// Close WebSocket as the sender was dropped.
|
||||
if let Err(e) = ws.close() {
|
||||
tracing::error!("Failed to close WebSocket connection {:?}", e);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue