Feature/mixfetch disconnect (#3890)
* 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>
This commit is contained in:
committed by
GitHub
parent
3ccec3857e
commit
eccd6e16e2
Generated
+6
-5
@@ -3020,7 +3020,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "extension-storage"
|
||||
version = "1.2.0-rc.5"
|
||||
version = "1.2.0-rc.7"
|
||||
dependencies = [
|
||||
"bip39",
|
||||
"console_error_panic_hook",
|
||||
@@ -5613,7 +5613,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mix-fetch-wasm"
|
||||
version = "1.2.0-rc.5"
|
||||
version = "1.2.0-rc.7"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"js-sys",
|
||||
@@ -6298,7 +6298,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-client-wasm"
|
||||
version = "1.2.0-rc.5"
|
||||
version = "1.2.0-rc.7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures",
|
||||
@@ -6917,7 +6917,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-node-tester-wasm"
|
||||
version = "1.2.0-rc.5"
|
||||
version = "1.2.0-rc.7"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"js-sys",
|
||||
@@ -7375,6 +7375,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"wasmtimer",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7510,7 +7511,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-wasm-sdk"
|
||||
version = "1.2.0-rc.5"
|
||||
version = "1.2.0-rc.7"
|
||||
dependencies = [
|
||||
"mix-fetch-wasm",
|
||||
"nym-client-wasm",
|
||||
|
||||
@@ -155,6 +155,7 @@ k256 = "0.13"
|
||||
lazy_static = "1.4.0"
|
||||
log = "0.4"
|
||||
once_cell = "1.7.2"
|
||||
parking_lot = "0.12.1"
|
||||
rand = "0.8.5"
|
||||
reqwest = "0.11.18"
|
||||
serde = "1.0.152"
|
||||
|
||||
@@ -13,17 +13,17 @@ pub enum GatewayClientError {
|
||||
#[error("Connection to the gateway is not established")]
|
||||
ConnectionNotEstablished,
|
||||
|
||||
#[error("Gateway returned an error response - {0}")]
|
||||
#[error("Gateway returned an error response: {0}")]
|
||||
GatewayError(String),
|
||||
|
||||
#[error("There was a network error - {0}")]
|
||||
#[error("There was a network error: {0}")]
|
||||
NetworkError(#[from] WsError),
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[error("There was a network error")]
|
||||
#[error("There was a network error: {0}")]
|
||||
NetworkErrorWasm(#[from] JsError),
|
||||
|
||||
#[error("Invalid URL - {0}")]
|
||||
#[error("Invalid URL: {0}")]
|
||||
InvalidURL(String),
|
||||
|
||||
#[error("No shared key was provided or obtained")]
|
||||
@@ -32,7 +32,7 @@ pub enum GatewayClientError {
|
||||
#[error("No bandwidth controller provided")]
|
||||
NoBandwidthControllerAvailable,
|
||||
|
||||
#[error("Bandwidth controller error - {0}")]
|
||||
#[error("Bandwidth controller error: {0}")]
|
||||
BandwidthControllerError(#[from] nym_bandwidth_controller::error::BandwidthControllerError),
|
||||
|
||||
#[error("Connection was abruptly closed")]
|
||||
@@ -62,7 +62,7 @@ pub enum GatewayClientError {
|
||||
#[error("Connection is in an invalid state - please send a bug report")]
|
||||
ConnectionInInvalidState,
|
||||
|
||||
#[error("Failed to finish registration handshake - {0}")]
|
||||
#[error("Failed to finish registration handshake: {0}")]
|
||||
RegistrationFailure(HandshakeError),
|
||||
|
||||
#[error("Authentication failure")]
|
||||
|
||||
@@ -14,14 +14,18 @@ thiserror = { workspace = true }
|
||||
tokio = { workspace = true, features = ["macros", "sync"] }
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio]
|
||||
version = "1.24.1"
|
||||
workspace = true
|
||||
features = ["signal", "time", "rt"]
|
||||
|
||||
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen-futures]
|
||||
version = "0.4"
|
||||
workspace = true
|
||||
|
||||
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen]
|
||||
version = "0.2.83"
|
||||
workspace = true
|
||||
|
||||
[target."cfg(target_arch = \"wasm32\")".dependencies.wasmtimer]
|
||||
workspace = true
|
||||
features = ["tokio"]
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal", "test-util", "macros"] }
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "net", "signal", "test-util", "macros"] }
|
||||
|
||||
+11
-17
@@ -6,14 +6,17 @@ use log::{log, Level};
|
||||
use std::future::Future;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::{error::Error, time::Duration};
|
||||
use tokio::{
|
||||
sync::{
|
||||
mpsc,
|
||||
watch::{self, error::SendError},
|
||||
},
|
||||
time::sleep,
|
||||
use tokio::sync::{
|
||||
mpsc,
|
||||
watch::{self, error::SendError},
|
||||
};
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio::time::{sleep, timeout};
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasmtimer::tokio::{sleep, timeout};
|
||||
|
||||
const DEFAULT_SHUTDOWN_TIMER_SECS: u64 = 5;
|
||||
|
||||
pub(crate) type SentError = Box<dyn Error + Send + Sync>;
|
||||
@@ -216,16 +219,10 @@ impl TaskManager {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let interrupt_future = tokio::signal::ctrl_c();
|
||||
|
||||
// in wasm we'll never get our shutdown anyway...
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let interrupt_future = futures::future::pending::<()>();
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let wait_future = tokio::time::sleep(Duration::from_secs(self.shutdown_timer_secs));
|
||||
|
||||
// TODO: we should be using a `Delay` here for wasm
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let wait_future = futures::future::pending::<()>();
|
||||
let wait_future = sleep(Duration::from_secs(self.shutdown_timer_secs));
|
||||
|
||||
tokio::select! {
|
||||
_ = self.notify_tx.closed() => {
|
||||
@@ -300,7 +297,6 @@ impl TaskClient {
|
||||
const MAX_NAME_LENGTH: usize = 128;
|
||||
const OVERFLOW_NAME: &'static str = "reached maximum TaskClient children name depth";
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
const SHUTDOWN_TIMEOUT_WAITING_FOR_SIGNAL_ON_EXIT: Duration = Duration::from_secs(5);
|
||||
|
||||
fn new(
|
||||
@@ -428,12 +424,10 @@ impl TaskClient {
|
||||
|
||||
pub async fn recv_timeout(&mut self) {
|
||||
if self.mode.is_dummy() {
|
||||
#[cfg_attr(target_arch = "wasm32", allow(clippy::needless_return))]
|
||||
return pending().await;
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
if let Err(timeout) = tokio::time::timeout(
|
||||
if let Err(timeout) = timeout(
|
||||
Self::SHUTDOWN_TIMEOUT_WAITING_FOR_SIGNAL_ON_EXIT,
|
||||
self.recv(),
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "extension-storage"
|
||||
version = "1.2.0-rc.5"
|
||||
version = "1.2.0-rc.7"
|
||||
edition = "2021"
|
||||
license = "Apache-2.0"
|
||||
repository = "https://github.com/nymtech/nym"
|
||||
|
||||
Generated
+1
@@ -4595,6 +4595,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"wasmtimer",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"license": "Apache 2.0",
|
||||
"workspaces": [
|
||||
"dist/wasm/**",
|
||||
"dist/node/**",
|
||||
"sdk/typescript/packages/**",
|
||||
"ts-packages/*",
|
||||
"nym-wallet",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nymproject/contract-clients",
|
||||
"version": "1.2.0-rc.5",
|
||||
"version": "1.2.0-rc.7",
|
||||
"description": "A client for all Nym smart contracts",
|
||||
"license": "Apache-2.0",
|
||||
"author": "Nym Technologies SA",
|
||||
|
||||
@@ -7,16 +7,18 @@ import Box from '@mui/material/Box';
|
||||
import { mixFetch } from '@nymproject/mix-fetch-full-fat';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Paper from '@mui/material/Paper';
|
||||
import type { SetupMixFetchOps } from '@nymproject/mix-fetch';
|
||||
|
||||
const defaultUrl = 'https://nymtech.net/favicon.svg';
|
||||
const args = { mode: 'unsafe-ignore-cors' };
|
||||
const mixFetchOptions = {
|
||||
const mixFetchOptions: SetupMixFetchOps = {
|
||||
preferredGateway: 'E3mvZTHQCdBvhfr178Swx9g4QG3kkRUun7YnToLMcMbM', // with WSS
|
||||
preferredNetworkRequester:
|
||||
'GiRjFWrMxt58pEMuusm4yT3RxoMD1MMPrR9M2N4VWRJP.3CNZBPq4vg7v7qozjGjdPMXcvDmkbWPCgbGCjQVw9n6Z@2xU4CBE6QiiYt6EyBXSALwxkNvM7gqJfjHXaMkjiFmYW',
|
||||
mixFetchOverride: {
|
||||
requestTimeoutMs: 60_000,
|
||||
},
|
||||
forceTls: true, // force WSS
|
||||
};
|
||||
|
||||
export const MixFetch = () => {
|
||||
|
||||
@@ -36,10 +36,10 @@
|
||||
"@mui/icons-material": "^5.14.9",
|
||||
"@mui/lab": "^5.0.0-alpha.145",
|
||||
"@mui/material": "^5.14.8",
|
||||
"@nymproject/contract-clients": "^1.2.0-rc.5",
|
||||
"@nymproject/mix-fetch": "^1.2.0-rc.5",
|
||||
"@nymproject/mix-fetch-full-fat": "^1.2.0-rc.5",
|
||||
"@nymproject/sdk-full-fat": "^1.2.0-rc.5",
|
||||
"@nymproject/contract-clients": "^1.2.0-rc.7",
|
||||
"@nymproject/mix-fetch": "^1.2.0-rc.7",
|
||||
"@nymproject/mix-fetch-full-fat": "^1.2.0-rc.7",
|
||||
"@nymproject/sdk-full-fat": "^1.2.0-rc.7",
|
||||
"chain-registry": "^1.19.0",
|
||||
"cosmjs-types": "^0.8.0",
|
||||
"next": "^13.4.19",
|
||||
|
||||
@@ -42,3 +42,24 @@ root CA certificate you need in our list. [Send us a PR](https://github.com/nymt
|
||||
3. If you are using `mixFetch` in a web app with HTTPS you will need to use a gateway that has Secure Websockets to
|
||||
avoid getting a [mixed content](https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content) error.
|
||||
|
||||
4. Workaround for Mixed Content Errors because you might be using `mixFetch` from web app served from HTTPS while
|
||||
connecting a gateway that only listens on a plain websocket, without HTTPS/TLS.
|
||||
|
||||
We are currently working on a feature that adds a Secure Websocket (WSS) listener with HTTPS (automatically generated with LetsEncrypt) to Nym's
|
||||
gateways.
|
||||
|
||||
While we are adding this feature, you can use a gateway that has Caddy providing HTTPS/WSS by adding this to the options when settings up `mixFetch`:
|
||||
|
||||
```ts
|
||||
import type { SetupMixFetchOps } from '@nymproject/mix-fetch';
|
||||
|
||||
const mixFetchOptions: SetupMixFetchOps = {
|
||||
preferredGateway: 'E3mvZTHQCdBvhfr178Swx9g4QG3kkRUun7YnToLMcMbM', // with WSS
|
||||
preferredNetworkRequester:
|
||||
'GiRjFWrMxt58pEMuusm4yT3RxoMD1MMPrR9M2N4VWRJP.3CNZBPq4vg7v7qozjGjdPMXcvDmkbWPCgbGCjQVw9n6Z@2xU4CBE6QiiYt6EyBXSALwxkNvM7gqJfjHXaMkjiFmYW',
|
||||
mixFetchOverride: {
|
||||
requestTimeoutMs: 60_000,
|
||||
},
|
||||
forceTls: true, // force WSS
|
||||
};
|
||||
```
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"source": "src/index.html",
|
||||
"dependencies": {
|
||||
"parcel": "^2.9.3",
|
||||
"@nymproject/mix-fetch": ">=1.2.0-rc.5 || ^1"
|
||||
"@nymproject/mix-fetch": ">=1.2.0-rc.7 || ^1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "parcel --no-cache",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"version": "1.0.0",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@nymproject/mix-fetch": ">=1.2.0-rc.2 || ^1"
|
||||
"@nymproject/mix-fetch": ">=1.2.0-rc.7 || ^1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.22.10",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"license": "Apache-2.0",
|
||||
"source": "../src/index.html",
|
||||
"dependencies": {
|
||||
"@nymproject/mix-fetch": ">=1.2.0-rc.2 || ^1"
|
||||
"@nymproject/mix-fetch": ">=1.2.0-rc.7 || ^1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "npx parcel --no-cache",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createMixFetch } from '@nymproject/mix-fetch';
|
||||
import { createMixFetch, disconnectMixFetch } from '@nymproject/mix-fetch';
|
||||
|
||||
function appendOutput(value: string) {
|
||||
const el = document.getElementById('output') as HTMLPreElement;
|
||||
@@ -54,6 +54,10 @@ async function main() {
|
||||
appendOutput(JSON.stringify(resp, null, 2));
|
||||
appendOutput(JSON.stringify({ text }, null, 2));
|
||||
|
||||
console.log('disconnecting');
|
||||
await disconnectMixFetch();
|
||||
console.log('disconnected! all further usages should fail');
|
||||
|
||||
// get an image
|
||||
url = 'https://nymtech.net/favicon.svg';
|
||||
resp = await mixFetch(url, args);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nymproject/mix-fetch",
|
||||
"version": "1.2.0-rc.5",
|
||||
"version": "1.2.0-rc.7",
|
||||
"description": "This package is a drop-in replacement for `fetch` to send HTTP requests over the Nym Mixnet.",
|
||||
"license": "Apache-2.0",
|
||||
"author": "Nym Technologies SA",
|
||||
@@ -33,7 +33,7 @@
|
||||
"tsc": "tsc --noEmit true"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nymproject/mix-fetch-wasm": ">=1.2.0-rc.0 || 1",
|
||||
"@nymproject/mix-fetch-wasm": ">=1.2.0-rc.7 || 1",
|
||||
"comlink": "^4.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -81,4 +81,4 @@
|
||||
"private": false,
|
||||
"type": "module",
|
||||
"types": "./dist/esm/index.d.ts"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ export const createMixFetch = async (): Promise<IMixFetch> => {
|
||||
const body = Object.values(workerResponse.body)[0]; // we are expecting only one value to be set in `.body`
|
||||
return new Response(body, { headers, status, statusText });
|
||||
},
|
||||
disconnectMixFetch: wrappedWorker.disconnectMixFetch,
|
||||
};
|
||||
|
||||
return mixFetchWebWorker;
|
||||
|
||||
@@ -22,10 +22,20 @@ declare global {
|
||||
* @param opts Optional settings
|
||||
*/
|
||||
export const createMixFetch = async (opts?: SetupMixFetchOps) => {
|
||||
if (!window) {
|
||||
throw new Error('`window` is not defined');
|
||||
}
|
||||
|
||||
if (!window.__mixFetchGlobal) {
|
||||
// load the worker and set up mixFetch with defaults
|
||||
window.__mixFetchGlobal = await createMixFetchInternal();
|
||||
await window.__mixFetchGlobal.setupMixFetch(opts);
|
||||
|
||||
window.onunload = async () => {
|
||||
if (window.__mixFetchGlobal) {
|
||||
await window.__mixFetchGlobal.disconnectMixFetch();
|
||||
}
|
||||
};
|
||||
}
|
||||
return window.__mixFetchGlobal;
|
||||
};
|
||||
@@ -49,3 +59,21 @@ export const mixFetch: IMixFetchFn = async (url, args, opts?: SetupMixFetchOps)
|
||||
// execute user request
|
||||
return instance.mixFetch(url, args);
|
||||
};
|
||||
|
||||
/**
|
||||
* Stops the usage of mixFetch and disconnect the client from the mixnet.
|
||||
*/
|
||||
export const disconnectMixFetch = async (): Promise<void> => {
|
||||
if (!window) {
|
||||
throw new Error('`window` is not defined');
|
||||
}
|
||||
|
||||
// JS: I'm ignoring this lint (no-else-return) because I want to explicitly state
|
||||
// that `__mixFetchGlobal` is definitely not null in the else branch.
|
||||
if (!window.__mixFetchGlobal) {
|
||||
throw new Error("mixFetch hasn't been setup");
|
||||
// eslint-disable-next-line no-else-return
|
||||
} else {
|
||||
return window.__mixFetchGlobal.disconnectMixFetch();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -12,11 +12,13 @@ export type SetupMixFetchOps = MixFetchOpts & {
|
||||
export interface IMixFetchWebWorker {
|
||||
mixFetch: IMixFetchWorkerFn;
|
||||
setupMixFetch: (opts?: SetupMixFetchOps) => Promise<void>;
|
||||
disconnectMixFetch: () => Promise<void>;
|
||||
}
|
||||
|
||||
export interface IMixFetch {
|
||||
mixFetch: IMixFetchFn;
|
||||
setupMixFetch: (opts?: SetupMixFetchOps) => Promise<void>;
|
||||
disconnectMixFetch: () => Promise<void>;
|
||||
}
|
||||
|
||||
export enum EventKinds {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable no-restricted-globals */
|
||||
import { setupMixFetch } from '@nymproject/mix-fetch-wasm';
|
||||
import { setupMixFetch, disconnectMixFetch } from '@nymproject/mix-fetch-wasm';
|
||||
import * as Comlink from 'comlink';
|
||||
import type { IMixFetchWebWorker, LoadedEvent } from '../types';
|
||||
import { EventKinds, ResponseBodyConfigMap, ResponseBodyConfigMapDefaults } from '../types';
|
||||
@@ -53,6 +53,11 @@ export async function run() {
|
||||
}
|
||||
await setupMixFetch(opts || {});
|
||||
},
|
||||
disconnectMixFetch: async () => {
|
||||
console.log('[Worker] --- disconnectMixFetch ---');
|
||||
|
||||
await disconnectMixFetch();
|
||||
},
|
||||
};
|
||||
|
||||
// start comlink listening for messages and handle them above
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nymproject/node-tester",
|
||||
"version": "1.2.0-rc.5",
|
||||
"version": "1.2.0-rc.7",
|
||||
"description": "This package provides a tester that can send test packets to mixnode that is part of the Nym Mixnet.",
|
||||
"license": "Apache-2.0",
|
||||
"author": "Nym Technologies SA",
|
||||
@@ -25,7 +25,7 @@
|
||||
"tsc": "tsc --noEmit true"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nymproject/nym-node-tester-wasm": ">=1.2.0-rc.0 || ^1",
|
||||
"@nymproject/nym-node-tester-wasm": ">=1.2.0-rc.7 || ^1",
|
||||
"comlink": "^4.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -71,4 +71,4 @@
|
||||
"private": false,
|
||||
"type": "module",
|
||||
"types": "./dist/esm/index.d.ts"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
"tsc": "tsc --noEmit true"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nymproject/nym-client-wasm-node": ">=1.2.0-rc.5 || ^1",
|
||||
"@nymproject/nym-client-wasm-node": ">=1.2.0-rc.7 || ^1",
|
||||
"comlink": "^4.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nymproject/sdk-react",
|
||||
"version": "1.2.0-rc.5",
|
||||
"version": "1.2.0-rc.7",
|
||||
"license": "Apache-2.0",
|
||||
"author": "Nym Technologies SA",
|
||||
"files": [
|
||||
@@ -20,7 +20,7 @@
|
||||
"tsc": "tsc --noEmit true"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nymproject/sdk": ">=1.2.0-rc.2 || ^1"
|
||||
"@nymproject/sdk": ">=1.2.0-rc.7 || ^1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.17.5",
|
||||
@@ -67,4 +67,4 @@
|
||||
"private": false,
|
||||
"type": "module",
|
||||
"types": "./dist/index.d.ts"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nymproject/sdk",
|
||||
"version": "1.2.0-rc.5",
|
||||
"version": "1.2.0-rc.7",
|
||||
"license": "Apache-2.0",
|
||||
"author": "Nym Technologies SA",
|
||||
"files": [
|
||||
@@ -30,7 +30,7 @@
|
||||
"tsc": "tsc --noEmit true"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nymproject/nym-client-wasm": ">=1.2.0-rc.0 || ^1",
|
||||
"@nymproject/nym-client-wasm": ">=1.2.0-rc.7 || ^1",
|
||||
"comlink": "^4.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -80,4 +80,4 @@
|
||||
"private": false,
|
||||
"type": "module",
|
||||
"types": "./dist/esm/index.d.ts"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nymproject/nym-validator-client",
|
||||
"version": "1.2.0-rc.5",
|
||||
"version": "1.2.0-rc.7",
|
||||
"description": "A TypeScript client for interacting with smart contracts in Nym validators",
|
||||
"license": "Apache-2.0",
|
||||
"author": "Nym Technologies SA (https://nymtech.net)",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "nym-client-wasm"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jedrzej Stuczynski <andrew@nymtech.net>"]
|
||||
version = "1.2.0-rc.5"
|
||||
version = "1.2.0-rc.7"
|
||||
edition = "2021"
|
||||
keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy", "client"]
|
||||
license = "Apache-2.0"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "nym-wasm-sdk"
|
||||
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
|
||||
version = "1.2.0-rc.5"
|
||||
version = "1.2.0-rc.7"
|
||||
edition = "2021"
|
||||
keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy"]
|
||||
license = "Apache-2.0"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "mix-fetch-wasm"
|
||||
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
|
||||
version = "1.2.0-rc.5"
|
||||
version = "1.2.0-rc.7"
|
||||
edition = "2021"
|
||||
keywords = ["nym", "fetch", "wasm", "webassembly", "privacy"]
|
||||
license = "Apache-2.0"
|
||||
|
||||
@@ -55,7 +55,7 @@ func setLoggingLevel(_ js.Value, args []js.Value) any {
|
||||
|
||||
func mixFetch(_ js.Value, args []js.Value) (any, error) {
|
||||
if !rust_bridge.RsIsInitialised() {
|
||||
return nil, errors.New("mix fetch hasn't been initialised")
|
||||
return nil, errors.New("mix fetch is not available")
|
||||
}
|
||||
|
||||
if len(args) == 0 {
|
||||
|
||||
@@ -33,6 +33,7 @@ const {
|
||||
send_client_data,
|
||||
start_new_mixnet_connection,
|
||||
setupMixFetch,
|
||||
disconnectMixFetch,
|
||||
setupMixFetchWithConfig,
|
||||
mix_fetch_initialised,
|
||||
finish_mixnet_connection} = wasm_bindgen;
|
||||
@@ -120,12 +121,12 @@ async function wasm_bindgenSetup() {
|
||||
}
|
||||
|
||||
async function nativeSetup() {
|
||||
const preferredGateway = "6qQYb4ArXANU6HJDxzH4PFCUqYb39Dae2Gem2KpxescM";
|
||||
const validator = 'https://qa-nym-api.qa.nymte.ch/api';
|
||||
// const preferredGateway = "6qQYb4ArXANU6HJDxzH4PFCUqYb39Dae2Gem2KpxescM";
|
||||
// const validator = 'https://qa-nym-api.qa.nymte.ch/api';
|
||||
|
||||
// local
|
||||
const mixFetchNetworkRequesterAddress= "2o47bhnXWna6VEyt4mXMGQQAbXfpKmX7BkjkxUz8uQVi.6uQGnCqSczpXwh86NdbsCoDDXuqZQM9Uwko8GE7uC9g8@6qQYb4ArXANU6HJDxzH4PFCUqYb39Dae2Gem2KpxescM";
|
||||
// const mixFetchNetworkRequesterAddress= "GqiGWmKRCbGQFSqH88BzLKijvZgipnqhmbNFsmkZw84t.4L8sXFuAUyUYyHZYgMdM3AtiusKnYUft6Pd8e41rrCHA@6qQYb4ArXANU6HJDxzH4PFCUqYb39Dae2Gem2KpxescM";
|
||||
// const preferredNetworkRequester= "2o47bhnXWna6VEyt4mXMGQQAbXfpKmX7BkjkxUz8uQVi.6uQGnCqSczpXwh86NdbsCoDDXuqZQM9Uwko8GE7uC9g8@6qQYb4ArXANU6HJDxzH4PFCUqYb39Dae2Gem2KpxescM";
|
||||
// const preferredNetworkRequester= "GqiGWmKRCbGQFSqH88BzLKijvZgipnqhmbNFsmkZw84t.4L8sXFuAUyUYyHZYgMdM3AtiusKnYUft6Pd8e41rrCHA@6qQYb4ArXANU6HJDxzH4PFCUqYb39Dae2Gem2KpxescM";
|
||||
|
||||
// those are just some examples, there are obviously more permutations;
|
||||
// note, the extra optional argument is of the following type:
|
||||
@@ -147,9 +148,9 @@ async function nativeSetup() {
|
||||
*/
|
||||
|
||||
// #1
|
||||
// await setupMixFetch(mixFetchNetworkRequesterAddress)
|
||||
// await setupMixFetch(preferredNetworkRequester)
|
||||
// #2
|
||||
// await setupMixFetch(mixFetchNetworkRequesterAddress, { nymApiUrl: validator })
|
||||
// await setupMixFetch(preferredNetworkRequester, { nymApiUrl: validator })
|
||||
// // #3
|
||||
const noCoverTrafficOverride = {
|
||||
traffic: { disableMainPoissonPacketDistribution: true },
|
||||
@@ -187,18 +188,37 @@ async function testMixFetch() {
|
||||
const {target} = event.data.args;
|
||||
const url = target;
|
||||
|
||||
const args = { mode: "cors", redirect: "manual" }
|
||||
// const args = { mode: "unsafe-ignore-cors" }
|
||||
// const args = { mode: "ors", redirect: "manual", signal }
|
||||
const args = { mode: "unsafe-ignore-cors" }
|
||||
|
||||
try {
|
||||
console.log('using mixFetch...');
|
||||
const mixFetchRes = await mixFetch(url, args)
|
||||
console.log(">>> MIX FETCH")
|
||||
await logFetchResult(mixFetchRes)
|
||||
|
||||
console.log('done')
|
||||
|
||||
} catch(e) {
|
||||
console.error("mix fetch request failure: ", e)
|
||||
}
|
||||
|
||||
console.log("will disconnect");
|
||||
await disconnectMixFetch()
|
||||
|
||||
try {
|
||||
console.log('using mixFetch...');
|
||||
const mixFetchRes = await mixFetch(url, args)
|
||||
console.log(">>> MIX FETCH")
|
||||
await logFetchResult(mixFetchRes)
|
||||
|
||||
console.log('done')
|
||||
|
||||
} catch(e) {
|
||||
console.error("mix fetch request failure: ", e)
|
||||
}
|
||||
|
||||
|
||||
// try {
|
||||
// console.log('using normal Fetch...');
|
||||
// const fetchRes = await fetch(url, args)
|
||||
|
||||
@@ -39,6 +39,14 @@ impl ActiveRequests {
|
||||
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) {
|
||||
|
||||
@@ -10,6 +10,8 @@ use crate::socks_helpers::{socks5_connect_request, socks5_data_request};
|
||||
use crate::{config, RequestId};
|
||||
use js_sys::Promise;
|
||||
use nym_socks5_requests::RemoteAddress;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use tokio::sync::Mutex;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen_futures::future_to_promise;
|
||||
use wasm_client_core::client::base_client::{BaseClientBuilder, ClientInput, ClientOutput};
|
||||
@@ -26,6 +28,8 @@ use wasm_utils::error::PromisableResult;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub struct MixFetchClient {
|
||||
invalidated: AtomicBool,
|
||||
|
||||
mix_fetch_config: config::MixFetch,
|
||||
|
||||
self_address: Recipient,
|
||||
@@ -34,9 +38,8 @@ pub struct MixFetchClient {
|
||||
|
||||
requests: ActiveRequests,
|
||||
|
||||
// even though we don't use graceful shutdowns, other components rely on existence of this struct
|
||||
// and if it's dropped, everything will start going offline
|
||||
_task_manager: TaskManager,
|
||||
// this has to be guarded by a mutex to be able to disconnect with an immutable reference
|
||||
_task_manager: Mutex<TaskManager>,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
@@ -126,12 +129,13 @@ impl MixFetchClientBuilder {
|
||||
Self::start_reconstructor(client_output, active_requests.clone());
|
||||
|
||||
Ok(MixFetchClient {
|
||||
invalidated: AtomicBool::new(false),
|
||||
mix_fetch_config: self.config.mix_fetch,
|
||||
self_address,
|
||||
client_input,
|
||||
requests: active_requests,
|
||||
// this cannot failed as we haven't passed an external task manager
|
||||
_task_manager: started_client.task_handle.try_into_task_manager().unwrap(),
|
||||
_task_manager: Mutex::new(started_client.task_handle.try_into_task_manager().unwrap()),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -164,6 +168,26 @@ impl MixFetchClient {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn active(&self) -> bool {
|
||||
!self.invalidated.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub async fn disconnect(&self) -> Result<(), MixFetchError> {
|
||||
self.invalidated.store(true, Ordering::Relaxed);
|
||||
|
||||
console_log!("sending shutdown signal");
|
||||
let mut shutdown_guard = self._task_manager.lock().await;
|
||||
shutdown_guard.signal_shutdown().ok();
|
||||
|
||||
console_log!("waiting for shutdown to complete");
|
||||
shutdown_guard.wait_for_shutdown().await;
|
||||
|
||||
self.requests.invalidate_all().await;
|
||||
|
||||
console_log!("done");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn self_address(&self) -> String {
|
||||
self.self_address.to_string()
|
||||
}
|
||||
|
||||
@@ -40,6 +40,9 @@ pub enum MixFetchError {
|
||||
#[error("mix fetch has already been initialised before")]
|
||||
AlreadyInitialised,
|
||||
|
||||
#[error("mix fetch client has already been disconnected")]
|
||||
Disconnected,
|
||||
|
||||
#[error("provided mix fetch url wasn't a string")]
|
||||
NotStringMixFetchUrl,
|
||||
|
||||
|
||||
@@ -126,6 +126,20 @@ pub fn setup_mix_fetch_with_config(config: MixFetchConfig, opts: MixFetchOptsSim
|
||||
})
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = disconnectMixFetch)]
|
||||
pub async fn disconnect_mix_fetch() -> Promise {
|
||||
future_to_promise(async move {
|
||||
disconnect_mix_fetch_async()
|
||||
.await
|
||||
.map(|_| JsValue::undefined())
|
||||
.map_promise_err()
|
||||
})
|
||||
}
|
||||
|
||||
async fn disconnect_mix_fetch_async() -> Result<(), MixFetchError> {
|
||||
mix_fetch_client()?.disconnect().await
|
||||
}
|
||||
|
||||
pub(super) fn set_mix_fetch_client(mix_fetch_client: MixFetchClient) -> Result<(), MixFetchError> {
|
||||
MIX_FETCH
|
||||
.set(mix_fetch_client)
|
||||
@@ -133,7 +147,12 @@ pub(super) fn set_mix_fetch_client(mix_fetch_client: MixFetchClient) -> Result<(
|
||||
}
|
||||
|
||||
pub(super) fn mix_fetch_client() -> Result<&'static MixFetchClient, MixFetchError> {
|
||||
MIX_FETCH.get().ok_or(MixFetchError::Uninitialised)
|
||||
let mix_fetch = MIX_FETCH.get().ok_or(MixFetchError::Uninitialised)?;
|
||||
if !mix_fetch.active() {
|
||||
return Err(MixFetchError::Disconnected);
|
||||
}
|
||||
|
||||
Ok(mix_fetch)
|
||||
}
|
||||
|
||||
async fn setup_mix_fetch_async(
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{mix_fetch_client, MIX_FETCH};
|
||||
use crate::error::MixFetchError;
|
||||
use crate::mix_fetch_client;
|
||||
use js_sys::Promise;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen::JsValue;
|
||||
@@ -26,7 +27,7 @@ pub fn send_client_data(stringified_request_id: String, data: Vec<u8>) -> Promis
|
||||
future_to_promise(async move {
|
||||
// this error should be impossible in normal use
|
||||
// (unless, of course, user is messing around, but then it's their fault for this panic)
|
||||
let mix_fetch = mix_fetch_client().expect("mix fetch hasn't been setup");
|
||||
let mix_fetch = mix_fetch_client().map_err(JsValue::from)?;
|
||||
mix_fetch.forward_request_content(request_id, data).await?;
|
||||
Ok(JsValue::undefined())
|
||||
})
|
||||
@@ -44,7 +45,7 @@ pub fn start_new_mixnet_connection(target: String) -> Promise {
|
||||
future_to_promise(async move {
|
||||
// this error should be impossible in normal use
|
||||
// (unless, of course, user is messing around, but then it's their fault for this panic)
|
||||
let mix_fetch = mix_fetch_client().expect("mix fetch hasn't been setup");
|
||||
let mix_fetch = mix_fetch_client().map_err(JsValue::from)?;
|
||||
mix_fetch
|
||||
.connect_to_mixnet(target)
|
||||
.await
|
||||
@@ -54,8 +55,9 @@ pub fn start_new_mixnet_connection(target: String) -> Promise {
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn mix_fetch_initialised() -> bool {
|
||||
MIX_FETCH.get().is_some()
|
||||
pub fn mix_fetch_initialised() -> Result<bool, MixFetchError> {
|
||||
mix_fetch_client()?;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
/// Called by go runtime whenever it's done with a connection
|
||||
@@ -76,7 +78,7 @@ pub fn finish_mixnet_connection(stringified_request_id: String) -> Promise {
|
||||
future_to_promise(async move {
|
||||
// this error should be impossible in normal use
|
||||
// (unless, of course, user is messing around, but then it's their fault for this panic)
|
||||
let mix_fetch = mix_fetch_client().expect("mix fetch hasn't been setup");
|
||||
let mix_fetch = mix_fetch_client().map_err(JsValue::from)?;
|
||||
mix_fetch.disconnect_from_mixnet(request_id).await?;
|
||||
Ok(JsValue::undefined())
|
||||
})
|
||||
|
||||
@@ -23,7 +23,7 @@ mod request_writer;
|
||||
mod socks_helpers;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub(crate) use fetch::{mix_fetch_client, RequestId, MIX_FETCH};
|
||||
pub(crate) use fetch::{mix_fetch_client, RequestId};
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "nym-node-tester-wasm"
|
||||
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
|
||||
version = "1.2.0-rc.5"
|
||||
version = "1.2.0-rc.7"
|
||||
edition = "2021"
|
||||
keywords = ["nym", "sphinx", "webassembly", "privacy", "tester"]
|
||||
license = "Apache-2.0"
|
||||
|
||||
@@ -2844,11 +2844,6 @@
|
||||
resolved "https://registry.yarnpkg.com/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-16.8.1.tgz#6ec1930aaf4d9dea19149d6b3d100b2c7e69d582"
|
||||
integrity sha512-yHZ5FAcx54rVc31R0yIpniepkHMPwaxG23l8E/ZYbL1iPwE/Wc1HeUzUvxUuSXtguRp7ihcRhaUEPkcSl2EAVw==
|
||||
|
||||
"@nymproject/nym-client-wasm-node@>=1.2.0-rc.5 || ^1":
|
||||
version "1.2.0-rc.5"
|
||||
resolved "https://registry.yarnpkg.com/@nymproject/nym-client-wasm-node/-/nym-client-wasm-node-1.2.0-rc.5.tgz#464cc905a029cedb3acbe5513f60d47c22a28137"
|
||||
integrity sha512-NF15b+DVfLgC3zx3wszZkxdyaDvQcxxxNF4sr6y5osQDMXbixRMU/rNDF8kEa/cSDNipxf8G5Z0ApJSGKjcehQ==
|
||||
|
||||
"@nymproject/nym-validator-client@^0.18.0":
|
||||
version "0.18.0"
|
||||
resolved "https://registry.yarnpkg.com/@nymproject/nym-validator-client/-/nym-validator-client-0.18.0.tgz#4dd72bafdf6c72b603242f32c0bb9a1f9e475b98"
|
||||
|
||||
Reference in New Issue
Block a user