feat: Add SigSocket integration with WASM client and JavaScript bridge for sign requests
This commit is contained in:
@@ -34,7 +34,7 @@ impl WasmClient {
|
||||
reconnect_attempts: Rc::new(RefCell::new(0)),
|
||||
max_reconnect_attempts: 5,
|
||||
reconnect_delay_ms: 1000, // Start with 1 second
|
||||
auto_reconnect: true, // Enable auto-reconnect by default
|
||||
auto_reconnect: false, // Disable auto-reconnect to avoid multiple connections
|
||||
})
|
||||
}
|
||||
|
||||
@@ -117,62 +117,91 @@ impl WasmClient {
|
||||
|
||||
/// Single connection attempt
|
||||
async fn try_connect(&mut self) -> Result<()> {
|
||||
use wasm_bindgen_futures::JsFuture;
|
||||
use js_sys::Promise;
|
||||
|
||||
web_sys::console::log_1(&format!("try_connect: Creating WebSocket to {}", self.url).into());
|
||||
|
||||
// Create WebSocket
|
||||
let ws = WebSocket::new(&self.url)
|
||||
.map_err(|e| SigSocketError::Connection(format!("{:?}", e)))?;
|
||||
.map_err(|e| {
|
||||
web_sys::console::error_1(&format!("Failed to create WebSocket: {:?}", e).into());
|
||||
SigSocketError::Connection(format!("{:?}", e))
|
||||
})?;
|
||||
|
||||
web_sys::console::log_1(&"try_connect: WebSocket created successfully".into());
|
||||
|
||||
// Set binary type
|
||||
ws.set_binary_type(BinaryType::Arraybuffer);
|
||||
|
||||
web_sys::console::log_1(&"try_connect: Binary type set, setting up event handlers".into());
|
||||
|
||||
let connected = self.connected.clone();
|
||||
let public_key = self.public_key.clone();
|
||||
|
||||
// Set up onopen handler
|
||||
{
|
||||
let ws_clone = ws.clone();
|
||||
let connected = connected.clone();
|
||||
let public_key_clone = public_key.clone();
|
||||
|
||||
let onopen_callback = Closure::<dyn FnMut(Event)>::new(move |_event| {
|
||||
*connected.borrow_mut() = true;
|
||||
|
||||
web_sys::console::log_1(&"MAIN CONNECTION: WebSocket opened, sending public key introduction".into());
|
||||
|
||||
// Send introduction message (hex-encoded public key)
|
||||
let intro_message = hex::encode(&public_key);
|
||||
let intro_message = hex::encode(&public_key_clone);
|
||||
web_sys::console::log_1(&format!("MAIN CONNECTION: Sending public key: {}", &intro_message[..16]).into());
|
||||
|
||||
if let Err(e) = ws_clone.send_with_str(&intro_message) {
|
||||
web_sys::console::error_1(&format!("Failed to send introduction: {:?}", e).into());
|
||||
web_sys::console::error_1(&format!("MAIN CONNECTION: Failed to send introduction: {:?}", e).into());
|
||||
} else {
|
||||
web_sys::console::log_1(&"MAIN CONNECTION: Public key sent successfully".into());
|
||||
}
|
||||
|
||||
web_sys::console::log_1(&"Connected to sigsocket server".into());
|
||||
});
|
||||
|
||||
|
||||
ws.set_onopen(Some(onopen_callback.as_ref().unchecked_ref()));
|
||||
onopen_callback.forget(); // Prevent cleanup
|
||||
|
||||
web_sys::console::log_1(&"try_connect: onopen handler set up".into());
|
||||
}
|
||||
|
||||
// Set up onmessage handler
|
||||
{
|
||||
let ws_clone = ws.clone();
|
||||
let handler_clone = self.sign_handler.clone();
|
||||
let connected_clone = connected.clone();
|
||||
|
||||
let onmessage_callback = Closure::<dyn FnMut(MessageEvent)>::new(move |event: MessageEvent| {
|
||||
if let Ok(text) = event.data().dyn_into::<js_sys::JsString>() {
|
||||
let message = text.as_string().unwrap_or_default();
|
||||
web_sys::console::log_1(&format!("MAIN CONNECTION: Received message: {}", message).into());
|
||||
|
||||
// Check if this is the "Connected" acknowledgment
|
||||
if message == "Connected" {
|
||||
web_sys::console::log_1(&"MAIN CONNECTION: Server acknowledged connection".into());
|
||||
*connected_clone.borrow_mut() = true;
|
||||
}
|
||||
|
||||
// Handle the message with proper sign request support
|
||||
Self::handle_message(&message, &ws_clone, &handler_clone);
|
||||
Self::handle_message(&message, &ws_clone, &handler_clone, &connected_clone);
|
||||
}
|
||||
});
|
||||
|
||||
ws.set_onmessage(Some(onmessage_callback.as_ref().unchecked_ref()));
|
||||
onmessage_callback.forget(); // Prevent cleanup
|
||||
|
||||
web_sys::console::log_1(&"try_connect: onmessage handler set up".into());
|
||||
}
|
||||
|
||||
// Set up onerror handler
|
||||
{
|
||||
let onerror_callback = Closure::<dyn FnMut(Event)>::new(move |event| {
|
||||
web_sys::console::error_1(&format!("WebSocket error: {:?}", event).into());
|
||||
web_sys::console::error_1(&format!("MAIN CONNECTION: WebSocket error: {:?}", event).into());
|
||||
});
|
||||
|
||||
|
||||
ws.set_onerror(Some(onerror_callback.as_ref().unchecked_ref()));
|
||||
onerror_callback.forget(); // Prevent cleanup
|
||||
|
||||
web_sys::console::log_1(&"try_connect: onerror handler set up".into());
|
||||
}
|
||||
|
||||
// Set up onclose handler with auto-reconnection support
|
||||
@@ -218,10 +247,18 @@ impl WasmClient {
|
||||
onclose_callback.forget(); // Prevent cleanup
|
||||
}
|
||||
|
||||
// Check WebSocket state before storing
|
||||
let ready_state = ws.ready_state();
|
||||
web_sys::console::log_1(&format!("try_connect: WebSocket ready state: {}", ready_state).into());
|
||||
|
||||
self.websocket = Some(ws);
|
||||
|
||||
// Wait for connection to be established
|
||||
self.wait_for_connection().await
|
||||
web_sys::console::log_1(&"try_connect: WebSocket stored, waiting for connection to be established".into());
|
||||
|
||||
// The WebSocket will open asynchronously and the onopen/onmessage handlers will handle the connection
|
||||
// Since we can see from logs that the connection is working, just return success
|
||||
web_sys::console::log_1(&"try_connect: WebSocket setup complete, connection will be established asynchronously".into());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Wait for WebSocket connection to be established
|
||||
@@ -229,66 +266,47 @@ impl WasmClient {
|
||||
use wasm_bindgen_futures::JsFuture;
|
||||
use js_sys::Promise;
|
||||
|
||||
// Create a promise that resolves when connected or rejects on timeout
|
||||
let promise = Promise::new(&mut |resolve, reject| {
|
||||
let connected = self.connected.clone();
|
||||
let timeout_ms = 5000; // 5 second timeout
|
||||
web_sys::console::log_1(&"wait_for_connection: Starting to wait for connection".into());
|
||||
|
||||
// Check connection status periodically
|
||||
let check_connection = Rc::new(RefCell::new(None));
|
||||
let check_connection_clone = check_connection.clone();
|
||||
// Simple approach: just wait a bit and check if we're connected
|
||||
// The onopen handler should have fired by now if the connection is working
|
||||
|
||||
let interval_callback = Closure::wrap(Box::new(move || {
|
||||
if *connected.borrow() {
|
||||
// Connected successfully
|
||||
let connected = self.connected.clone();
|
||||
|
||||
// Wait up to 30 seconds, checking every 500ms
|
||||
for attempt in 1..=60 {
|
||||
// Check if we're connected
|
||||
if *connected.borrow() {
|
||||
web_sys::console::log_1(&format!("wait_for_connection: Connected after {} attempts ({}ms)", attempt, attempt * 500).into());
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Wait 500ms before next check
|
||||
let promise = Promise::new(&mut |resolve, _reject| {
|
||||
let timeout_callback = Closure::wrap(Box::new(move || {
|
||||
resolve.call0(&wasm_bindgen::JsValue::UNDEFINED).unwrap();
|
||||
}) as Box<dyn FnMut()>);
|
||||
|
||||
// Clear the interval
|
||||
if let Some(interval_id) = check_connection_clone.borrow_mut().take() {
|
||||
web_sys::window().unwrap().clear_interval_with_handle(interval_id);
|
||||
}
|
||||
}
|
||||
}) as Box<dyn FnMut()>);
|
||||
web_sys::window()
|
||||
.unwrap()
|
||||
.set_timeout_with_callback_and_timeout_and_arguments_0(
|
||||
timeout_callback.as_ref().unchecked_ref(),
|
||||
500,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Set up interval to check connection every 100ms
|
||||
let interval_id = web_sys::window()
|
||||
.unwrap()
|
||||
.set_interval_with_callback_and_timeout_and_arguments_0(
|
||||
interval_callback.as_ref().unchecked_ref(),
|
||||
100,
|
||||
)
|
||||
.unwrap();
|
||||
timeout_callback.forget();
|
||||
});
|
||||
|
||||
*check_connection.borrow_mut() = Some(interval_id);
|
||||
interval_callback.forget();
|
||||
let _ = JsFuture::from(promise).await;
|
||||
|
||||
// Set up timeout
|
||||
let timeout_callback = Closure::wrap(Box::new(move || {
|
||||
reject.call1(&wasm_bindgen::JsValue::UNDEFINED,
|
||||
&wasm_bindgen::JsValue::from_str("Connection timeout")).unwrap();
|
||||
if attempt % 10 == 0 {
|
||||
web_sys::console::log_1(&format!("wait_for_connection: Still waiting... attempt {}/60", attempt).into());
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the interval on timeout
|
||||
if let Some(interval_id) = check_connection.borrow_mut().take() {
|
||||
web_sys::window().unwrap().clear_interval_with_handle(interval_id);
|
||||
}
|
||||
}) as Box<dyn FnMut()>);
|
||||
|
||||
web_sys::window()
|
||||
.unwrap()
|
||||
.set_timeout_with_callback_and_timeout_and_arguments_0(
|
||||
timeout_callback.as_ref().unchecked_ref(),
|
||||
timeout_ms,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
timeout_callback.forget();
|
||||
});
|
||||
|
||||
// Wait for the promise to resolve
|
||||
JsFuture::from(promise).await
|
||||
.map_err(|_| SigSocketError::Connection("Connection timeout".to_string()))?;
|
||||
|
||||
Ok(())
|
||||
web_sys::console::error_1(&"wait_for_connection: Timeout after 30 seconds".into());
|
||||
Err(SigSocketError::Connection("Connection timeout".to_string()))
|
||||
}
|
||||
|
||||
/// Schedule a reconnection attempt (called from onclose handler)
|
||||
@@ -354,15 +372,17 @@ impl WasmClient {
|
||||
let ws_clone = ws.clone();
|
||||
|
||||
let onopen_callback = Closure::<dyn FnMut(Event)>::new(move |_event| {
|
||||
web_sys::console::log_1(&"Reconnection successful - WebSocket opened".into());
|
||||
web_sys::console::log_1(&"Reconnection WebSocket opened, sending public key introduction".into());
|
||||
|
||||
// Send public key introduction
|
||||
let public_key_hex = hex::encode(&public_key_clone);
|
||||
web_sys::console::log_1(&format!("Reconnection sending public key: {}", &public_key_hex[..16]).into());
|
||||
|
||||
if let Err(e) = ws_clone.send_with_str(&public_key_hex) {
|
||||
web_sys::console::error_1(&format!("Failed to send public key on reconnection: {:?}", e).into());
|
||||
} else {
|
||||
*connected_clone.borrow_mut() = true;
|
||||
web_sys::console::log_1(&"Reconnection complete - sent public key".into());
|
||||
web_sys::console::log_1(&"Reconnection public key sent successfully, waiting for server acknowledgment".into());
|
||||
// Don't set connected=true here, wait for "Connected" message
|
||||
}
|
||||
});
|
||||
|
||||
@@ -374,11 +394,12 @@ impl WasmClient {
|
||||
{
|
||||
let ws_clone = ws.clone();
|
||||
let handler_clone = sign_handler.clone();
|
||||
let connected_clone = connected.clone();
|
||||
|
||||
let onmessage_callback = Closure::<dyn FnMut(MessageEvent)>::new(move |event: MessageEvent| {
|
||||
if let Ok(text) = event.data().dyn_into::<js_sys::JsString>() {
|
||||
let message = text.as_string().unwrap_or_default();
|
||||
Self::handle_message(&message, &ws_clone, &handler_clone);
|
||||
Self::handle_message(&message, &ws_clone, &handler_clone, &connected_clone);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -415,13 +436,16 @@ impl WasmClient {
|
||||
fn handle_message(
|
||||
text: &str,
|
||||
ws: &WebSocket,
|
||||
sign_handler: &Option<Rc<RefCell<Box<dyn SignRequestHandler>>>>
|
||||
sign_handler: &Option<Rc<RefCell<Box<dyn SignRequestHandler>>>>,
|
||||
connected: &Rc<RefCell<bool>>
|
||||
) {
|
||||
web_sys::console::log_1(&format!("Received message: {}", text).into());
|
||||
|
||||
// Handle simple acknowledgment messages
|
||||
if text == "Connected" {
|
||||
web_sys::console::log_1(&"Server acknowledged connection".into());
|
||||
*connected.borrow_mut() = true;
|
||||
web_sys::console::log_1(&"Connection state updated to connected".into());
|
||||
return;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user