eccd6e16e2
* js error message * Ability to explicitly disconnect mixfetch * removed unused import * added disconnect method directly to sdk package * simplifying error throw Co-authored-by: Mark Sinclair <14054343+mmsinclair@users.noreply.github.com> * added onunload event listener handler * Using global instance of mixfetch to disconnect Co-authored-by: Mark Sinclair <14054343+mmsinclair@users.noreply.github.com> * Bump RC versions * MixFetch, move unload handler to creation and check for undefined `window` * Bump RC version * Force TLS on mixFetch demo * Add info about working around mixed content errors for mixFetch --------- Co-authored-by: Mark Sinclair <14054343+mmsinclair@users.noreply.github.com> Co-authored-by: Mark Sinclair <mmsinclair@users.noreply.github.com>
151 lines
5.1 KiB
Rust
151 lines
5.1 KiB
Rust
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
use crate::error::MixFetchError;
|
|
use crate::go_bridge::{goWasmCloseRemoteSocket, goWasmInjectConnError, goWasmInjectServerData};
|
|
use crate::RequestId;
|
|
use nym_ordered_buffer::OrderedMessageBuffer;
|
|
use nym_socks5_requests::SocketData;
|
|
use rand::{thread_rng, RngCore};
|
|
use std::collections::HashMap;
|
|
use std::sync::Arc;
|
|
use tokio::sync::Mutex;
|
|
use wasm_utils::{console_error, console_log};
|
|
|
|
#[derive(Clone, Default)]
|
|
pub struct ActiveRequests {
|
|
// TODO: think whether we need sync or async mutex here
|
|
inner: Arc<Mutex<HashMap<RequestId, ActiveRequest>>>,
|
|
}
|
|
|
|
impl ActiveRequests {
|
|
pub async fn start_new(&self) -> RequestId {
|
|
let mut guard = self.inner.lock().await;
|
|
let req = ActiveRequest::new();
|
|
let mut rng = thread_rng();
|
|
let request_id = loop {
|
|
let candidate = rng.next_u64();
|
|
if !guard.contains_key(&candidate) {
|
|
break candidate;
|
|
}
|
|
};
|
|
|
|
console_log!("starting new request {request_id}");
|
|
|
|
// it's impossible to insert a duplicate entry here since we're holding the lock for the map
|
|
// and we've generated an id that must have been unique
|
|
guard.insert(request_id, req);
|
|
|
|
request_id
|
|
}
|
|
|
|
pub async fn invalidate_all(&self) {
|
|
let mut guard = self.inner.lock().await;
|
|
for (id, _req) in guard.drain() {
|
|
let err = MixFetchError::AbortedRequest { request_id: id };
|
|
goWasmInjectConnError(id.to_string(), err.to_string())
|
|
}
|
|
}
|
|
|
|
pub async fn get_sending_sequence(&self, id: RequestId) -> Option<u64> {
|
|
let mut guard = self.inner.lock().await;
|
|
if let Some(req) = guard.get_mut(&id) {
|
|
Some(req.next_sending_seq())
|
|
} else {
|
|
console_error!("attempted to get sending sequence of request {id}, however it no longer exists. Has it been aborted?");
|
|
None
|
|
}
|
|
}
|
|
|
|
pub async fn finish(&self, id: RequestId) {
|
|
let mut guard = self.inner.lock().await;
|
|
let old = guard.remove(&id);
|
|
if old.is_none() {
|
|
console_error!("attempted to finish request {id}, but it seems to have never existed?")
|
|
}
|
|
}
|
|
|
|
pub async fn reject(&self, id: RequestId, err: MixFetchError) {
|
|
let mut guard = self.inner.lock().await;
|
|
let old = guard.remove(&id);
|
|
if old.is_none() {
|
|
console_error!("attempted to reject request {id}, but it seems to have never existed?")
|
|
}
|
|
|
|
goWasmInjectConnError(id.to_string(), err.to_string())
|
|
}
|
|
|
|
pub async fn try_send_data_to_go(&self, data: SocketData) {
|
|
let id = data.header.connection_id;
|
|
let mut guard = self.inner.lock().await;
|
|
let Some(req) = guard.get_mut(&id) else {
|
|
// if there's no data and the socket is closed, we're all good because our local must have already closed
|
|
if !data.data.is_empty() || !data.header.local_socket_closed {
|
|
console_error!("attempted to send data for request {id}, however it no longer exists. Has it been aborted?");
|
|
}
|
|
|
|
// TODO: if it doesn't exist here, make sure to clear Go's memory too
|
|
return;
|
|
};
|
|
|
|
if let Err(err) = req.insert_data(data) {
|
|
console_error!("failed to insert request data: {err}");
|
|
// this unwrap cannot possibly fail as we're holding an exclusive lock for the data
|
|
// and we have just borrowed the content
|
|
guard.remove(&id).unwrap();
|
|
return goWasmCloseRemoteSocket(id.to_string());
|
|
}
|
|
|
|
// TODO: clean this one up
|
|
if let Some(contiguous_data) = req.received_data.read() {
|
|
// TODO: deal with closing socket, etc.
|
|
if !contiguous_data.data.is_empty() {
|
|
goWasmInjectServerData(id.to_string(), contiguous_data.data);
|
|
}
|
|
// TODO: that's very crappy way of doing it.
|
|
if Some(contiguous_data.last_sequence) == req.finished_at {
|
|
goWasmCloseRemoteSocket(id.to_string());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct ActiveRequest {
|
|
received_data: OrderedMessageBuffer,
|
|
finished_at: Option<u64>,
|
|
|
|
sending_seq: u64,
|
|
}
|
|
|
|
impl ActiveRequest {
|
|
fn new() -> Self {
|
|
ActiveRequest {
|
|
received_data: Default::default(),
|
|
finished_at: None,
|
|
sending_seq: 0,
|
|
}
|
|
}
|
|
|
|
fn next_sending_seq(&mut self) -> u64 {
|
|
let next = self.sending_seq;
|
|
self.sending_seq += 1;
|
|
next
|
|
}
|
|
|
|
fn insert_data(&mut self, data: SocketData) -> Result<(), MixFetchError> {
|
|
if data.header.local_socket_closed {
|
|
if let Some(already_finished) = self.finished_at {
|
|
return Err(MixFetchError::DuplicateSocketClosure {
|
|
request: data.header.connection_id,
|
|
first: already_finished,
|
|
other: data.header.seq,
|
|
});
|
|
}
|
|
self.finished_at = Some(data.header.seq)
|
|
}
|
|
|
|
self.received_data.write(data.header.seq, data.data)?;
|
|
Ok(())
|
|
}
|
|
}
|