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",
|
"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]]
|
[[package]]
|
||||||
name = "glow"
|
name = "glow"
|
||||||
version = "0.12.3"
|
version = "0.12.3"
|
||||||
|
@ -4217,8 +4247,10 @@ dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
"chrono",
|
"chrono",
|
||||||
"console_error_panic_hook",
|
"console_error_panic_hook",
|
||||||
|
"futures-util",
|
||||||
"generational-arena",
|
"generational-arena",
|
||||||
"getrandom",
|
"getrandom",
|
||||||
|
"gloo-net",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"ruffle_core",
|
"ruffle_core",
|
||||||
"ruffle_render",
|
"ruffle_render",
|
||||||
|
|
|
@ -50,6 +50,8 @@ serde = { version = "1.0.188", features = ["derive"] }
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
base64 = "0.21.4"
|
base64 = "0.21.4"
|
||||||
async-channel = "1.9.0"
|
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]
|
[dependencies.ruffle_core]
|
||||||
path = "../core"
|
path = "../core"
|
||||||
|
@ -64,5 +66,5 @@ features = [
|
||||||
"ChannelMergerNode", "ChannelSplitterNode", "ClipboardEvent", "DataTransfer", "Element", "Event",
|
"ChannelMergerNode", "ChannelSplitterNode", "ClipboardEvent", "DataTransfer", "Element", "Event",
|
||||||
"EventTarget", "GainNode", "Headers", "HtmlCanvasElement", "HtmlDocument", "HtmlElement", "HtmlFormElement",
|
"EventTarget", "GainNode", "Headers", "HtmlCanvasElement", "HtmlDocument", "HtmlElement", "HtmlFormElement",
|
||||||
"HtmlInputElement", "HtmlTextAreaElement", "KeyboardEvent", "Location", "PointerEvent",
|
"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
|
//! Navigator backend for web
|
||||||
use crate::WebSocketProxy;
|
use crate::WebSocketProxy;
|
||||||
use async_channel::Receiver;
|
use async_channel::Receiver;
|
||||||
|
use futures_util::{SinkExt, StreamExt};
|
||||||
|
use gloo_net::websocket::{futures::WebSocket, Message};
|
||||||
use js_sys::{Array, ArrayBuffer, Uint8Array};
|
use js_sys::{Array, ArrayBuffer, Uint8Array};
|
||||||
use ruffle_core::backend::navigator::{
|
use ruffle_core::backend::navigator::{
|
||||||
async_return, create_fetch_error, create_specific_fetch_error, ErrorResponse, NavigationMethod,
|
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_subscriber::Registry;
|
||||||
use tracing_wasm::WASMLayer;
|
use tracing_wasm::WASMLayer;
|
||||||
use url::{ParseError, Url};
|
use url::{ParseError, Url};
|
||||||
use wasm_bindgen::prelude::Closure;
|
|
||||||
use wasm_bindgen::JsCast;
|
use wasm_bindgen::JsCast;
|
||||||
use wasm_bindgen_futures::{spawn_local, JsFuture};
|
use wasm_bindgen_futures::{spawn_local, JsFuture};
|
||||||
use web_sys::{
|
use web_sys::{
|
||||||
window, Blob, BlobPropertyBag, CloseEvent, ErrorEvent, HtmlFormElement, HtmlInputElement,
|
window, Blob, BlobPropertyBag, HtmlFormElement, HtmlInputElement, Request as WebRequest,
|
||||||
MessageEvent, Request as WebRequest, RequestInit, Response as WebResponse, WebSocket,
|
RequestInit, Response as WebResponse,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct WebNavigatorBackend {
|
pub struct WebNavigatorBackend {
|
||||||
|
@ -368,6 +369,7 @@ impl NavigatorBackend for WebNavigatorBackend {
|
||||||
fn connect_socket(
|
fn connect_socket(
|
||||||
&mut self,
|
&mut self,
|
||||||
host: String,
|
host: String,
|
||||||
|
|
||||||
port: u16,
|
port: u16,
|
||||||
// NOTE: WebSocket does not allow specifying a timeout, so this goes unused.
|
// NOTE: WebSocket does not allow specifying a timeout, so this goes unused.
|
||||||
_timeout: Duration,
|
_timeout: Duration,
|
||||||
|
@ -389,7 +391,7 @@ impl NavigatorBackend for WebNavigatorBackend {
|
||||||
|
|
||||||
tracing::info!("Connecting to {}", proxy.proxy_url);
|
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,
|
Ok(x) => x,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("Failed to create WebSocket, reason {:?}", 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 (mut sink, mut stream) = ws.split();
|
||||||
|
sender
|
||||||
let sender_onopen = sender.clone();
|
|
||||||
let onopen_callback = Closure::<dyn FnMut()>::new(move || {
|
|
||||||
sender_onopen
|
|
||||||
.send(SocketAction::Connect(handle, ConnectionState::Connected))
|
.send(SocketAction::Connect(handle, ConnectionState::Connected))
|
||||||
.expect("Working channel send");
|
.expect("working channel send");
|
||||||
});
|
|
||||||
|
|
||||||
let sender_onmessage = sender.clone();
|
// Spawn future to handle incoming messages.
|
||||||
let onmessage_callback = Closure::<dyn FnMut(_)>::new(move |e: MessageEvent| {
|
let stream_sender = sender.clone();
|
||||||
if let Ok(buf) = e.data().dyn_into::<js_sys::ArrayBuffer>() {
|
self.spawn_future(Box::pin(async move {
|
||||||
// Handle array buffer.
|
while let Some(msg) = stream.next().await {
|
||||||
let array = js_sys::Uint8Array::new(&buf);
|
match msg {
|
||||||
sender_onmessage
|
Ok(Message::Bytes(buf)) => stream_sender
|
||||||
.send(SocketAction::Data(handle, array.to_vec()))
|
.send(SocketAction::Data(handle, buf))
|
||||||
.expect("Working channel send");
|
.expect("working channel send"),
|
||||||
} else if let Ok(_) = e.data().dyn_into::<web_sys::Blob>() {
|
Ok(_) => tracing::warn!("Server sent unexpected text message"),
|
||||||
todo!("Unsupported blob payload");
|
Err(_) => {
|
||||||
} else {
|
stream_sender
|
||||||
todo!("Unsupported WebSocket payload");
|
.send(SocketAction::Close(handle))
|
||||||
|
.expect("working channel send");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
let sender_onclose = sender.clone();
|
Ok(())
|
||||||
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();
|
|
||||||
|
|
||||||
// Spawn future to handle outgoing messages.
|
// Spawn future to handle outgoing messages.
|
||||||
self.spawn_future(Box::pin(async move {
|
self.spawn_future(Box::pin(async move {
|
||||||
while let Ok(msg) = receiver.recv().await {
|
while let Ok(msg) = receiver.recv().await {
|
||||||
if let Err(e) = ws.send_with_u8_array(&msg) {
|
if let Err(e) = sink.send(Message::Bytes(msg)).await {
|
||||||
tracing::error!("Failed to send message to WebSocket {:?}", e);
|
tracing::warn!("Failed to send message to WebSocket {}", e);
|
||||||
sender
|
sender
|
||||||
.send(SocketAction::Close(handle))
|
.send(SocketAction::Close(handle))
|
||||||
.expect("working channel send");
|
.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(())
|
Ok(())
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue