2022-07-21 17:03:18 +00:00
|
|
|
// @ts-ignore
|
2023-02-01 12:50:45 +00:00
|
|
|
import Hyperswarm from "@lumeweb/hyperswarm-web";
|
2023-07-01 08:21:09 +00:00
|
|
|
import type { ActiveQuery } from "@lumeweb/libkernel/module";
|
|
|
|
import {
|
|
|
|
addHandler,
|
|
|
|
getKey,
|
|
|
|
handleMessage,
|
|
|
|
handlePresentKey as handlePresentKeyModule,
|
2023-07-04 04:59:01 +00:00
|
|
|
logErr,
|
2023-07-01 08:21:09 +00:00
|
|
|
} from "@lumeweb/libkernel/module";
|
2023-04-07 23:48:23 +00:00
|
|
|
import { Buffer } from "buffer";
|
2023-07-01 08:21:09 +00:00
|
|
|
import { ed25519 } from "@noble/curves/ed25519";
|
2023-02-01 12:50:45 +00:00
|
|
|
import b4a from "b4a";
|
2023-02-06 17:31:07 +00:00
|
|
|
import { pubKeyToIpv6 } from "./addr.js";
|
2023-03-19 19:16:51 +00:00
|
|
|
import { EventEmitter2 as EventEmitter } from "eventemitter2";
|
2023-04-05 06:41:57 +00:00
|
|
|
// @ts-ignore
|
|
|
|
import Protomux from "protomux";
|
2023-04-07 23:48:23 +00:00
|
|
|
import defer, { DeferredPromise } from "p-defer";
|
2023-07-05 00:59:01 +00:00
|
|
|
import { concatBytes } from "@lumeweb/libkernel";
|
2022-07-21 17:03:18 +00:00
|
|
|
|
2023-03-19 11:19:19 +00:00
|
|
|
const MAX_PEER_LISTENERS = 20;
|
|
|
|
|
2023-02-06 17:31:07 +00:00
|
|
|
interface SwarmConnection {
|
2023-02-01 12:50:45 +00:00
|
|
|
swarm: number;
|
2022-08-03 16:01:11 +00:00
|
|
|
conn: any;
|
2023-04-07 23:48:23 +00:00
|
|
|
channels: Map<number, Protomux>;
|
2022-08-03 16:01:11 +00:00
|
|
|
}
|
|
|
|
|
2023-03-19 19:16:51 +00:00
|
|
|
interface SwarmEvents {
|
|
|
|
swarm: number;
|
|
|
|
events: EventEmitter;
|
|
|
|
}
|
|
|
|
|
2023-02-06 17:31:07 +00:00
|
|
|
const connections = new Map<number, SwarmConnection>();
|
2023-02-01 12:50:45 +00:00
|
|
|
const swarmInstances = new Map<number, Hyperswarm>();
|
2023-03-19 19:16:51 +00:00
|
|
|
const swarmEvents = new Map<number, SwarmEvents>();
|
2022-07-21 17:03:18 +00:00
|
|
|
|
2023-02-01 12:50:45 +00:00
|
|
|
let defaultSwarm: Hyperswarm;
|
2022-08-31 19:35:24 +00:00
|
|
|
|
|
|
|
let moduleReadyResolve: Function;
|
|
|
|
let moduleReady: Promise<void> = new Promise((resolve) => {
|
|
|
|
moduleReadyResolve = resolve;
|
|
|
|
});
|
2022-07-21 17:03:18 +00:00
|
|
|
|
|
|
|
onmessage = handleMessage;
|
2023-04-06 20:32:17 +00:00
|
|
|
|
2023-02-01 12:50:45 +00:00
|
|
|
function idFactory(start = 1) {
|
2022-08-31 19:35:24 +00:00
|
|
|
let id = start;
|
|
|
|
|
|
|
|
return function nextId() {
|
2023-07-01 09:21:30 +00:00
|
|
|
const _nextId = id;
|
2023-02-01 12:50:45 +00:00
|
|
|
id += 1;
|
2023-07-01 09:21:30 +00:00
|
|
|
return _nextId;
|
2022-08-31 19:35:24 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-02-01 12:50:45 +00:00
|
|
|
const getSwarmId = idFactory();
|
|
|
|
const getSocketId = idFactory();
|
2023-04-07 23:48:23 +00:00
|
|
|
const getChannelId = idFactory();
|
2023-04-09 06:27:56 +00:00
|
|
|
const getMessageId = idFactory();
|
2022-07-21 17:03:18 +00:00
|
|
|
|
2023-07-01 08:21:09 +00:00
|
|
|
addHandler("presentKey", handlePresentKey);
|
2023-02-06 17:31:07 +00:00
|
|
|
addHandler("join", handleJoin);
|
2023-02-01 12:50:45 +00:00
|
|
|
addHandler("getPeerByPubkey", handleGetPeerByPubkey);
|
2023-02-06 17:31:07 +00:00
|
|
|
|
2022-07-21 17:03:18 +00:00
|
|
|
addHandler("addRelay", handleAddRelay);
|
|
|
|
addHandler("removeRelay", handleRemoveRelay);
|
|
|
|
addHandler("clearRelays", handleClearRelays);
|
2022-08-14 10:49:24 +00:00
|
|
|
addHandler("getRelays", handleGetRelays);
|
2023-02-01 17:07:46 +00:00
|
|
|
addHandler("init", handleInit);
|
2022-07-21 17:03:18 +00:00
|
|
|
addHandler("ready", handleReady);
|
2023-02-06 17:31:07 +00:00
|
|
|
addHandler("listenConnections", handleListenConnections, {
|
|
|
|
receiveUpdates: true,
|
|
|
|
});
|
|
|
|
addHandler("socketGetInfo", handleGetSocketInfo);
|
2022-07-21 17:03:18 +00:00
|
|
|
|
2023-02-06 17:31:07 +00:00
|
|
|
addHandler("socketExists", handleSocketExists);
|
|
|
|
addHandler("socketListenEvent", handleSocketListenEvent, {
|
|
|
|
receiveUpdates: true,
|
|
|
|
});
|
|
|
|
addHandler("socketWrite", handleWriteSocketEvent);
|
|
|
|
addHandler("socketClose", handleCloseSocketEvent);
|
2023-04-15 07:58:53 +00:00
|
|
|
addHandler("createProtomuxChannel", handleCreateProtomuxChannel, {
|
2023-04-07 23:48:23 +00:00
|
|
|
receiveUpdates: true,
|
|
|
|
});
|
2023-04-15 07:58:53 +00:00
|
|
|
addHandler("createProtomuxMessage", handleCreateProtomuxMessage, {
|
2023-04-05 06:41:57 +00:00
|
|
|
receiveUpdates: true,
|
|
|
|
});
|
2023-04-15 07:57:34 +00:00
|
|
|
addHandler("createSwarm", handleCreateSwarm);
|
2023-04-06 20:32:17 +00:00
|
|
|
|
2023-07-01 08:21:09 +00:00
|
|
|
async function handlePresentKey(aq: ActiveQuery) {
|
|
|
|
handlePresentKeyModule({
|
2023-02-01 12:50:45 +00:00
|
|
|
callerInput: {
|
2023-07-04 04:59:01 +00:00
|
|
|
key: aq.callerInput.rootKey,
|
2023-02-01 12:50:45 +00:00
|
|
|
},
|
|
|
|
} as ActiveQuery);
|
|
|
|
|
|
|
|
if (!defaultSwarm) {
|
|
|
|
defaultSwarm = swarmInstances.get(await createSwarm()) as Hyperswarm;
|
2022-08-03 16:01:11 +00:00
|
|
|
}
|
2022-08-31 19:35:24 +00:00
|
|
|
moduleReadyResolve();
|
2022-08-03 16:01:11 +00:00
|
|
|
}
|
|
|
|
|
2023-02-01 12:50:45 +00:00
|
|
|
async function createSwarm(): Promise<number> {
|
2023-07-01 08:21:09 +00:00
|
|
|
const privateKey = await getKey();
|
|
|
|
const swarmInstance = new Hyperswarm({
|
|
|
|
keyPair: {
|
|
|
|
publicKey: ed25519.getPublicKey(privateKey),
|
2023-07-05 00:59:01 +00:00
|
|
|
secretKey: concatBytes(privateKey, ed25519.getPublicKey(privateKey)),
|
2023-07-01 08:21:09 +00:00
|
|
|
},
|
|
|
|
});
|
2023-02-01 12:50:45 +00:00
|
|
|
const id = getSwarmId();
|
|
|
|
swarmInstances.set(id, swarmInstance);
|
2022-08-03 16:01:11 +00:00
|
|
|
|
2023-02-18 03:37:05 +00:00
|
|
|
swarmInstance.onSelf("init", () => {
|
2023-03-19 19:16:51 +00:00
|
|
|
const swarmInstanceEvents = new EventEmitter();
|
|
|
|
swarmInstanceEvents.setMaxListeners(MAX_PEER_LISTENERS);
|
|
|
|
swarmEvents.set(id, { swarm: id, events: swarmInstanceEvents });
|
2023-02-18 03:37:05 +00:00
|
|
|
swarmInstance.on("connection", (peer: any) => {
|
|
|
|
const socketId = getSocketId();
|
2023-03-19 19:16:51 +00:00
|
|
|
connections.set(socketId, {
|
|
|
|
swarm: id,
|
|
|
|
conn: peer,
|
2023-04-07 23:48:23 +00:00
|
|
|
channels: new Map<number, Protomux>(),
|
2023-03-19 19:16:51 +00:00
|
|
|
});
|
2023-02-18 03:37:05 +00:00
|
|
|
|
2023-03-19 19:16:51 +00:00
|
|
|
peer.once("close", () => {
|
2023-02-18 03:37:05 +00:00
|
|
|
connections.delete(socketId);
|
|
|
|
});
|
2023-03-19 19:16:51 +00:00
|
|
|
|
|
|
|
swarmInstanceEvents.emit("connection", peer);
|
2022-08-03 16:01:11 +00:00
|
|
|
});
|
2022-07-21 17:03:18 +00:00
|
|
|
});
|
|
|
|
|
2023-03-19 19:16:51 +00:00
|
|
|
swarmInstance.onSelf("close", (...args) => {
|
|
|
|
swarmEvents.get(id)?.events.emit("close", ...args);
|
|
|
|
swarmEvents.get(id)?.events.removeAllListeners();
|
|
|
|
swarmEvents.delete(id);
|
|
|
|
});
|
|
|
|
|
2023-02-01 12:50:45 +00:00
|
|
|
return id;
|
2022-07-21 17:03:18 +00:00
|
|
|
}
|
|
|
|
|
2023-02-06 17:31:07 +00:00
|
|
|
function handleSocketListenEvent(aq: ActiveQuery) {
|
2022-07-21 17:03:18 +00:00
|
|
|
const { event = null } = aq.callerInput;
|
|
|
|
|
2022-08-03 16:01:11 +00:00
|
|
|
const socket = validateConnection(aq);
|
|
|
|
|
|
|
|
if (!socket) {
|
2022-07-21 17:03:18 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!event) {
|
|
|
|
aq.reject("Invalid event");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-09-19 12:11:54 +00:00
|
|
|
let responded = false;
|
|
|
|
const respond = () => {
|
|
|
|
if (responded) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
responded = true;
|
|
|
|
aq.respond();
|
|
|
|
};
|
|
|
|
|
2023-07-22 01:35:47 +00:00
|
|
|
const cb = (data: Buffer) => {
|
2023-04-06 22:07:51 +00:00
|
|
|
if (responded) {
|
|
|
|
return;
|
|
|
|
}
|
2022-07-21 17:03:18 +00:00
|
|
|
aq.sendUpdate(data);
|
|
|
|
};
|
|
|
|
|
|
|
|
socket.on(event, cb);
|
2023-03-18 18:41:20 +00:00
|
|
|
socket.once("close", () => {
|
2022-09-19 12:11:54 +00:00
|
|
|
socket.off(event, cb);
|
|
|
|
respond();
|
2022-07-21 17:03:18 +00:00
|
|
|
});
|
|
|
|
|
2023-02-06 17:31:07 +00:00
|
|
|
aq.setReceiveUpdate?.(() => {
|
|
|
|
socket.off(event, cb);
|
|
|
|
respond();
|
2022-07-21 17:03:18 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-08-13 19:12:49 +00:00
|
|
|
async function handleSocketExists(aq: ActiveQuery) {
|
|
|
|
const { id = null } = aq.callerInput;
|
|
|
|
|
2023-02-01 12:50:45 +00:00
|
|
|
aq.respond(connections.has(Number(id)));
|
2022-08-13 19:12:49 +00:00
|
|
|
}
|
|
|
|
|
2022-07-21 17:03:18 +00:00
|
|
|
function handleCloseSocketEvent(aq: ActiveQuery) {
|
2022-08-03 16:01:11 +00:00
|
|
|
const socket = validateConnection(aq);
|
2022-07-21 17:03:18 +00:00
|
|
|
|
2022-08-03 16:01:11 +00:00
|
|
|
if (!socket) {
|
2022-07-21 17:03:18 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-08-03 16:01:11 +00:00
|
|
|
socket.end();
|
2022-07-21 17:03:18 +00:00
|
|
|
|
|
|
|
aq.respond();
|
|
|
|
}
|
|
|
|
|
2023-04-06 22:07:51 +00:00
|
|
|
async function handleWriteSocketEvent(aq: ActiveQuery) {
|
2022-08-03 16:01:11 +00:00
|
|
|
const socket = validateConnection(aq);
|
2022-07-21 17:03:18 +00:00
|
|
|
|
2022-08-03 16:01:11 +00:00
|
|
|
if (!socket) {
|
2022-07-21 17:03:18 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
const { message = null } = aq.callerInput;
|
|
|
|
|
|
|
|
if (!message) {
|
|
|
|
aq.reject("empty message");
|
2023-07-01 08:21:09 +00:00
|
|
|
return;
|
2022-07-21 17:03:18 +00:00
|
|
|
}
|
|
|
|
|
2023-04-06 22:07:51 +00:00
|
|
|
await socket.mutex?.waitForUnlock();
|
|
|
|
|
2022-08-03 16:01:11 +00:00
|
|
|
socket.write(message);
|
2022-07-21 17:03:18 +00:00
|
|
|
|
|
|
|
aq.respond();
|
|
|
|
}
|
|
|
|
|
2022-08-03 16:01:11 +00:00
|
|
|
function validateConnection(aq: ActiveQuery): any | boolean {
|
2022-07-21 17:03:18 +00:00
|
|
|
const { id = null } = aq.callerInput;
|
|
|
|
|
2023-02-01 12:50:45 +00:00
|
|
|
if (!id || !connections.has(id)) {
|
2022-07-21 17:03:18 +00:00
|
|
|
aq.reject("Invalid connection id");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-02-01 12:50:45 +00:00
|
|
|
return connections.get(id)?.conn;
|
2022-08-03 16:01:11 +00:00
|
|
|
}
|
|
|
|
|
2023-02-01 12:50:45 +00:00
|
|
|
async function getSwarm(aq: ActiveQuery): Promise<Hyperswarm> {
|
2022-08-31 19:35:24 +00:00
|
|
|
await moduleReady;
|
2023-02-01 12:50:45 +00:00
|
|
|
let swarm;
|
2022-08-31 19:35:24 +00:00
|
|
|
if ("callerInput" in aq && aq.callerInput) {
|
2023-02-01 12:50:45 +00:00
|
|
|
swarm = aq.callerInput.swarm ?? null;
|
2022-08-03 16:01:11 +00:00
|
|
|
|
2023-02-01 12:50:45 +00:00
|
|
|
if (swarm && !swarmInstances.has(swarm)) {
|
|
|
|
const error = "Invalid swarm id";
|
2022-08-31 19:35:24 +00:00
|
|
|
aq.reject(error);
|
|
|
|
throw new Error(error);
|
|
|
|
}
|
2022-08-03 16:01:11 +00:00
|
|
|
}
|
|
|
|
|
2023-02-01 12:50:45 +00:00
|
|
|
if (!swarm) {
|
2023-04-06 17:16:46 +00:00
|
|
|
if (defaultSwarm.activeRelay && defaultSwarm.ready) {
|
|
|
|
await defaultSwarm.activeRelay.dht._protocol.opened;
|
|
|
|
}
|
2023-02-01 12:50:45 +00:00
|
|
|
return defaultSwarm;
|
2022-08-03 16:01:11 +00:00
|
|
|
}
|
|
|
|
|
2023-04-06 17:16:46 +00:00
|
|
|
if (swarm.activeRelay && swarm.ready) {
|
|
|
|
await swarm.activeRelay.dht._protocol.opened;
|
|
|
|
}
|
|
|
|
|
2023-02-01 12:50:45 +00:00
|
|
|
return swarmInstances.get(swarm) as Hyperswarm;
|
2022-07-21 17:03:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
async function handleAddRelay(aq: ActiveQuery) {
|
|
|
|
const { pubkey = null } = aq.callerInput;
|
|
|
|
|
|
|
|
if (!pubkey) {
|
|
|
|
aq.reject("invalid pubkey");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-02-01 13:18:31 +00:00
|
|
|
const swarm = await getSwarm(aq);
|
2022-08-31 19:35:24 +00:00
|
|
|
|
2023-02-01 13:18:31 +00:00
|
|
|
aq.respond(await swarm.addRelay(pubkey));
|
2022-07-21 17:03:18 +00:00
|
|
|
}
|
|
|
|
|
2022-08-31 19:35:24 +00:00
|
|
|
async function handleRemoveRelay(aq: ActiveQuery) {
|
2022-07-21 17:03:18 +00:00
|
|
|
const { pubkey = null } = aq.callerInput;
|
|
|
|
|
|
|
|
if (!pubkey) {
|
|
|
|
aq.reject("invalid pubkey");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-02-01 13:18:31 +00:00
|
|
|
const swarm = await getSwarm(aq);
|
2022-08-31 19:35:24 +00:00
|
|
|
|
2023-02-01 13:18:31 +00:00
|
|
|
aq.respond(swarm.removeRelay(pubkey));
|
2022-07-21 17:03:18 +00:00
|
|
|
}
|
|
|
|
|
2022-08-31 19:35:24 +00:00
|
|
|
async function handleClearRelays(aq: ActiveQuery) {
|
2023-02-01 13:18:31 +00:00
|
|
|
const swarm = await getSwarm(aq);
|
2022-08-03 16:01:11 +00:00
|
|
|
|
2023-02-01 13:18:31 +00:00
|
|
|
swarm.clearRelays();
|
2022-07-21 17:03:18 +00:00
|
|
|
|
|
|
|
aq.respond();
|
|
|
|
}
|
|
|
|
|
2022-08-31 19:35:24 +00:00
|
|
|
async function handleGetRelays(aq: ActiveQuery) {
|
2023-02-01 12:50:45 +00:00
|
|
|
aq.respond(await (await getSwarm(aq)).relays);
|
2022-08-14 10:49:24 +00:00
|
|
|
}
|
|
|
|
|
2023-02-06 17:31:07 +00:00
|
|
|
async function handleJoin(aq: ActiveQuery) {
|
2023-02-01 12:50:45 +00:00
|
|
|
const { topic = null } = aq.callerInput;
|
|
|
|
|
|
|
|
const swarm = await getSwarm(aq);
|
|
|
|
|
|
|
|
if (!topic) {
|
|
|
|
aq.reject("invalid topic");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!b4a.isBuffer(topic)) {
|
|
|
|
aq.reject("topic must be a buffer");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// @ts-ignore
|
2023-02-06 17:31:07 +00:00
|
|
|
swarm.join(topic, { server: false });
|
2022-07-21 17:03:18 +00:00
|
|
|
aq.respond();
|
|
|
|
}
|
2023-04-06 20:32:17 +00:00
|
|
|
|
2023-02-01 12:50:45 +00:00
|
|
|
async function handleGetPeerByPubkey(aq: ActiveQuery) {
|
|
|
|
const { pubkey = null } = aq.callerInput;
|
2022-08-03 16:01:11 +00:00
|
|
|
|
2023-02-01 12:50:45 +00:00
|
|
|
const swarm = await getSwarm(aq);
|
2022-08-03 16:01:11 +00:00
|
|
|
|
2023-02-01 12:50:45 +00:00
|
|
|
if (!pubkey) {
|
|
|
|
aq.reject("invalid topic");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!b4a.isBuffer(pubkey)) {
|
|
|
|
aq.reject("pubkey must be a buffer");
|
|
|
|
return;
|
|
|
|
}
|
2022-08-03 16:01:11 +00:00
|
|
|
|
2023-02-01 12:50:45 +00:00
|
|
|
// @ts-ignore
|
|
|
|
if (!swarm._allConnections.has(pubkey)) {
|
|
|
|
aq.reject("peer does not exist");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// @ts-ignore
|
|
|
|
const peer = swarm._allConnections.get(pubkey);
|
|
|
|
|
2023-02-06 17:31:07 +00:00
|
|
|
aq.respond(getSwarmToSocketConnectionId(peer));
|
2022-08-03 16:01:11 +00:00
|
|
|
}
|
|
|
|
|
2023-02-01 17:07:46 +00:00
|
|
|
async function handleInit(aq: ActiveQuery) {
|
2023-02-01 12:50:45 +00:00
|
|
|
const swarm = await getSwarm(aq);
|
2023-02-01 17:07:46 +00:00
|
|
|
try {
|
|
|
|
await swarm.init();
|
|
|
|
} catch (e) {
|
|
|
|
aq.reject((e as Error).message);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-02-01 12:50:45 +00:00
|
|
|
aq.respond();
|
2022-08-03 16:01:11 +00:00
|
|
|
}
|
2023-04-06 20:32:17 +00:00
|
|
|
|
2023-02-01 17:07:46 +00:00
|
|
|
async function handleReady(aq: ActiveQuery) {
|
|
|
|
const swarm = await getSwarm(aq);
|
|
|
|
|
2023-02-17 13:10:14 +00:00
|
|
|
if (swarm.activeRelay && swarm.ready) {
|
2023-02-01 17:07:46 +00:00
|
|
|
aq.respond();
|
2023-04-06 17:16:46 +00:00
|
|
|
await swarm.activeRelay.dht._protocol.opened;
|
2023-02-01 17:07:46 +00:00
|
|
|
return;
|
|
|
|
}
|
2023-04-06 17:16:46 +00:00
|
|
|
swarm.once("ready", async () => {
|
|
|
|
await swarm.activeRelay.dht._protocol.opened;
|
2023-02-01 17:07:46 +00:00
|
|
|
aq.respond();
|
|
|
|
});
|
|
|
|
}
|
2023-04-06 20:32:17 +00:00
|
|
|
|
2023-02-06 17:31:07 +00:00
|
|
|
async function handleListenConnections(aq: ActiveQuery) {
|
|
|
|
const swarm = await getSwarm(aq);
|
2023-04-04 12:07:02 +00:00
|
|
|
const swarmId = getSwarmToSwarmId(swarm);
|
2023-02-06 17:31:07 +00:00
|
|
|
|
|
|
|
const listener = (peer: any) => {
|
|
|
|
aq.sendUpdate(getSwarmToSocketConnectionId(peer));
|
|
|
|
};
|
|
|
|
|
2023-04-04 12:07:02 +00:00
|
|
|
const swarmEvent = swarmEvents.get(swarmId as number)?.events;
|
2023-03-19 19:16:51 +00:00
|
|
|
|
|
|
|
if (!swarmEvent) {
|
|
|
|
logErr("swarm event object is missing");
|
|
|
|
}
|
|
|
|
|
|
|
|
swarmEvent?.on("connection", listener);
|
2023-02-06 17:31:07 +00:00
|
|
|
|
|
|
|
aq.setReceiveUpdate?.(() => {
|
2023-03-19 19:16:51 +00:00
|
|
|
swarmEvent?.off("connection", listener);
|
2023-02-06 17:31:07 +00:00
|
|
|
aq.respond();
|
|
|
|
});
|
2023-02-17 02:40:40 +00:00
|
|
|
|
2023-04-04 15:22:37 +00:00
|
|
|
for (const conn of connections) {
|
|
|
|
if (conn[1].swarm === swarmId) {
|
|
|
|
listener(conn[1].conn);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-18 00:16:22 +00:00
|
|
|
const closeCb = () => {
|
2023-03-19 19:16:51 +00:00
|
|
|
swarmEvent?.off("connection", listener);
|
|
|
|
swarmEvent?.emit("close");
|
2023-02-17 02:40:40 +00:00
|
|
|
aq.respond();
|
2023-02-18 00:16:22 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const hookClose = () => {
|
2023-03-29 18:48:15 +00:00
|
|
|
swarmEvent?.once("close", closeCb);
|
2023-02-18 00:16:22 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
if (swarm.activeRelay) {
|
|
|
|
hookClose();
|
|
|
|
return;
|
|
|
|
}
|
2023-03-19 19:16:51 +00:00
|
|
|
swarm.onceSelf("ready", hookClose);
|
2023-02-06 17:31:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
async function handleGetSocketInfo(aq: ActiveQuery) {
|
|
|
|
const socket = validateConnection(aq);
|
|
|
|
|
|
|
|
if (!socket) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
aq.respond({
|
|
|
|
remotePublicKey: socket.remotePublicKey,
|
|
|
|
publicKey: socket.publicKey,
|
|
|
|
rawStream: {
|
|
|
|
remoteHost: pubKeyToIpv6(socket.remotePublicKey),
|
|
|
|
remotePort: 0,
|
|
|
|
remoteFamily: "IPv6",
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-04-15 07:58:53 +00:00
|
|
|
async function handleCreateProtomuxChannel(aq: ActiveQuery) {
|
2023-04-05 06:41:57 +00:00
|
|
|
const socket = validateConnection(aq);
|
|
|
|
|
|
|
|
if (!socket) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-04-07 23:48:23 +00:00
|
|
|
if (!("data" in aq.callerInput)) {
|
|
|
|
aq.reject("data required");
|
2023-04-06 20:32:17 +00:00
|
|
|
return;
|
|
|
|
}
|
2023-04-06 21:02:54 +00:00
|
|
|
|
2023-04-06 20:32:17 +00:00
|
|
|
const mux = Protomux.from(socket);
|
2023-04-07 23:48:23 +00:00
|
|
|
const data = aq.callerInput.data;
|
|
|
|
|
|
|
|
const handleCallback = (name: string, enabled: boolean) => {
|
|
|
|
if (!enabled && name !== "destroy") {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
return (...args: any) => {
|
|
|
|
args = args.filter(
|
2023-07-01 08:21:09 +00:00
|
|
|
(item: any) => item?.constructor.name.toLowerCase() !== "channel",
|
2023-04-07 23:48:23 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
if (name === "destroy") {
|
|
|
|
connections.get(aq.callerInput.id)?.channels.delete(channelId);
|
|
|
|
aq.respond();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!enabled) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
aq.sendUpdate({
|
|
|
|
action: name,
|
|
|
|
args,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2023-04-08 18:47:12 +00:00
|
|
|
let channel = mux.createChannel({
|
2023-04-07 23:48:23 +00:00
|
|
|
protocol: data?.protocol,
|
|
|
|
id: data?.id,
|
|
|
|
handshake: data?.handshake,
|
|
|
|
onopen: handleCallback("onopen", data?.onopen ?? undefined),
|
|
|
|
onclose: handleCallback("onclose", data?.onclose ?? undefined),
|
|
|
|
ondestroy: handleCallback("ondestroy", data?.ondestroy ?? undefined),
|
|
|
|
});
|
|
|
|
|
|
|
|
if (channel === null) {
|
|
|
|
aq.reject("duplicate channel");
|
|
|
|
return;
|
2023-04-06 20:32:17 +00:00
|
|
|
}
|
2023-04-07 23:48:23 +00:00
|
|
|
|
2023-04-08 18:47:12 +00:00
|
|
|
channel.open();
|
|
|
|
|
2023-04-07 23:48:23 +00:00
|
|
|
const channelId = getChannelId();
|
|
|
|
|
|
|
|
connections.get(aq.callerInput.id)?.channels.set(channelId, channel);
|
|
|
|
|
|
|
|
aq.sendUpdate(channelId);
|
|
|
|
}
|
|
|
|
|
2023-04-15 07:58:53 +00:00
|
|
|
async function handleCreateProtomuxMessage(aq: ActiveQuery) {
|
2023-04-07 23:48:23 +00:00
|
|
|
const socket = validateConnection(aq);
|
|
|
|
|
|
|
|
if (!socket) {
|
|
|
|
return;
|
2023-04-06 17:16:46 +00:00
|
|
|
}
|
2023-04-05 06:41:57 +00:00
|
|
|
|
2023-04-07 23:48:23 +00:00
|
|
|
if (!("data" in aq.callerInput)) {
|
|
|
|
aq.reject("action required");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!("channelId" in aq.callerInput)) {
|
|
|
|
aq.reject("channel id required");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const channel = connections
|
|
|
|
.get(aq.callerInput.id)
|
|
|
|
?.channels.get(aq.callerInput.channelId);
|
|
|
|
|
|
|
|
if (!channel) {
|
|
|
|
aq.reject("invalid channel");
|
|
|
|
}
|
2023-04-05 06:41:57 +00:00
|
|
|
|
2023-04-06 20:32:17 +00:00
|
|
|
const data = aq.callerInput.data;
|
2023-04-05 06:41:57 +00:00
|
|
|
|
2023-04-09 06:27:56 +00:00
|
|
|
const defers = new Map<number, DeferredPromise<any>>();
|
2023-04-05 06:41:57 +00:00
|
|
|
|
2023-04-07 23:48:23 +00:00
|
|
|
const handleEncoding = (enabled: boolean) => {
|
|
|
|
if (!enabled) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
const update = async (action: string, args: any) => {
|
2023-04-09 06:27:56 +00:00
|
|
|
const messageId = getMessageId();
|
|
|
|
const d = defer();
|
|
|
|
defers.set(messageId, d);
|
2023-04-07 23:48:23 +00:00
|
|
|
aq.sendUpdate({
|
2023-04-09 06:27:56 +00:00
|
|
|
id: messageId,
|
2023-04-07 23:48:23 +00:00
|
|
|
action,
|
|
|
|
args,
|
|
|
|
});
|
|
|
|
|
2023-04-09 06:27:56 +00:00
|
|
|
const ret = (await d.promise) as any;
|
2023-04-07 23:48:23 +00:00
|
|
|
if (ret[1]) {
|
2023-04-09 00:07:10 +00:00
|
|
|
if (ret[1].buffer) {
|
|
|
|
args[0].buffer = b4a.from(ret[1].buffer);
|
|
|
|
}
|
|
|
|
|
2023-04-07 23:48:23 +00:00
|
|
|
args[0].start = ret[1].start;
|
|
|
|
args[0].end = ret[1].end;
|
2023-04-06 20:32:17 +00:00
|
|
|
}
|
2023-04-05 06:41:57 +00:00
|
|
|
|
2023-04-07 23:48:23 +00:00
|
|
|
return ret[0];
|
|
|
|
};
|
2023-04-06 21:02:54 +00:00
|
|
|
|
2023-04-07 23:48:23 +00:00
|
|
|
return {
|
|
|
|
async preencode(...args: any) {
|
|
|
|
return update("preencode", args);
|
|
|
|
},
|
|
|
|
async encode(...args: any) {
|
|
|
|
return update("encode", args);
|
|
|
|
},
|
|
|
|
async decode(...args: any) {
|
2023-04-09 00:07:10 +00:00
|
|
|
return update("decode", args);
|
2023-04-07 23:48:23 +00:00
|
|
|
},
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2023-07-01 08:21:09 +00:00
|
|
|
aq.setReceiveUpdate?.((data: any) => {
|
2023-04-09 02:55:05 +00:00
|
|
|
if (data.action === "send") {
|
|
|
|
message.send(...data.args);
|
2023-04-09 00:07:10 +00:00
|
|
|
}
|
|
|
|
|
2023-04-09 06:27:56 +00:00
|
|
|
defers.get(data.id)?.resolve(data.args);
|
|
|
|
defers.delete(data.id);
|
2023-04-07 23:48:23 +00:00
|
|
|
});
|
|
|
|
|
2023-04-09 00:07:10 +00:00
|
|
|
if (data.onmessage) {
|
2023-07-05 03:03:49 +00:00
|
|
|
data.onmessage = async (...args: any) => {
|
|
|
|
for (let i = 0; i < args.length; i++) {
|
|
|
|
if (isPromise(args[i])) {
|
|
|
|
args[i] = await args[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-09 00:07:10 +00:00
|
|
|
args = args.filter(
|
2023-07-01 08:21:09 +00:00
|
|
|
(item: any) => item?.constructor.name.toLowerCase() !== "channel",
|
2023-04-09 00:07:10 +00:00
|
|
|
);
|
2023-07-05 03:03:49 +00:00
|
|
|
|
2023-04-09 00:07:10 +00:00
|
|
|
aq.sendUpdate({
|
|
|
|
action: "onmessage",
|
|
|
|
args,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-04-07 23:48:23 +00:00
|
|
|
const message = channel.addMessage({
|
|
|
|
encoding: handleEncoding(data.encoding ?? false),
|
2023-04-09 00:07:10 +00:00
|
|
|
onmessage: data.onmessage ?? noop,
|
2023-04-07 23:48:23 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
aq.sendUpdate({
|
|
|
|
action: "created",
|
|
|
|
});
|
2023-04-05 06:41:57 +00:00
|
|
|
}
|
|
|
|
|
2023-04-15 07:57:34 +00:00
|
|
|
async function handleCreateSwarm(aq: ActiveQuery) {
|
|
|
|
aq.respond(await createSwarm());
|
|
|
|
}
|
|
|
|
|
2023-02-06 17:31:07 +00:00
|
|
|
function getSwarmToSocketConnectionId(socket: any) {
|
|
|
|
for (const conn of connections) {
|
|
|
|
if (conn[1].conn === socket) {
|
|
|
|
return conn[0];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2023-04-06 20:32:17 +00:00
|
|
|
|
2023-03-19 19:16:51 +00:00
|
|
|
function getSwarmToSwarmId(swarm: any) {
|
|
|
|
for (const swarmInstance of swarmInstances) {
|
|
|
|
if (swarmInstance[1] === swarm) {
|
|
|
|
return swarmInstance[0];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2023-04-09 00:07:10 +00:00
|
|
|
|
|
|
|
function noop() {}
|
2023-07-05 03:03:49 +00:00
|
|
|
|
|
|
|
function isPromise(obj: Promise<any>) {
|
|
|
|
return (
|
|
|
|
!!obj &&
|
|
|
|
(typeof obj === "object" || typeof obj === "function") &&
|
|
|
|
typeof obj.then === "function"
|
|
|
|
);
|
|
|
|
}
|