Add deno-only bootstrap peer implementation. Uses libdatachannel for the RTCDatachannel implementation.
Fix Typescript sourcemap serving PeerManager: more robust when RTCPeerConnection fails or is not present Separate source maps so the main files arent bloated
This commit is contained in:
@@ -33,6 +33,9 @@ export class PeerManager {
|
||||
let peername = `${adjective}_${snake}`;
|
||||
return peername;
|
||||
}
|
||||
isBootstrapPeer(peerID) {
|
||||
return this.bootstrapPeerIDs?.has(peerID);
|
||||
}
|
||||
websocketSend(message) {
|
||||
if (!this.websocket) {
|
||||
throw new Error();
|
||||
@@ -60,7 +63,7 @@ export class PeerManager {
|
||||
}
|
||||
this.messageSuperlog && console.log.apply(null, log("->signaler:", message));
|
||||
if (message.type === "hello2") {
|
||||
if (!this.isBootstrapPeer && Array.isArray(message?.bootstrapPeers)) {
|
||||
if (!this._isBootstrapPeer && Array.isArray(message?.bootstrapPeers)) {
|
||||
this.bootstrapPeerIDs = new Set(message.bootstrapPeers);
|
||||
}
|
||||
this.onHello2Received(this.bootstrapPeerIDs);
|
||||
@@ -79,7 +82,7 @@ export class PeerManager {
|
||||
if (!peerConnection) {
|
||||
let remotePeerID = message.from;
|
||||
let newPeer = new PeerConnection(this, remotePeerID, this.websocketSendPeerMessage.bind(this));
|
||||
if (this.isBootstrapPeer) {
|
||||
if (this._isBootstrapPeer) {
|
||||
newPeer.setPolite(false);
|
||||
}
|
||||
peerConnection = newPeer;
|
||||
@@ -105,7 +108,7 @@ export class PeerManager {
|
||||
return newPeer;
|
||||
}
|
||||
async onHello2Received(bootstrapPeerIDs) {
|
||||
if (this.isBootstrapPeer) {
|
||||
if (this._isBootstrapPeer) {
|
||||
this.connectPromiseCallbacks?.resolve();
|
||||
return;
|
||||
}
|
||||
@@ -139,7 +142,7 @@ export class PeerManager {
|
||||
peer_id: this.peerID,
|
||||
session_id: this.sessionID,
|
||||
// peer_name: app.peername,
|
||||
is_bootstrap_peer: this.isBootstrapPeer,
|
||||
is_bootstrap_peer: this._isBootstrapPeer,
|
||||
// peer_description: this.rtcPeerDescription
|
||||
});
|
||||
}
|
||||
@@ -164,7 +167,7 @@ export class PeerManager {
|
||||
this.searchQueryFunctions = new Map();
|
||||
this.RPC_remote = new Map();
|
||||
this.rpc = {};
|
||||
this.isBootstrapPeer = false;
|
||||
this._isBootstrapPeer = false;
|
||||
this.bootstrapPeerConnections = null;
|
||||
this.sessionID = generateID();
|
||||
this.websocket = null;
|
||||
@@ -188,7 +191,7 @@ export class PeerManager {
|
||||
this.animals = ['shrew', 'jerboa', 'lemur', 'weasel', 'possum', 'possum', 'marmoset', 'planigale', 'mole', 'narwhal'];
|
||||
this.adjectives = ['snazzy', 'whimsical', 'jazzy', 'bonkers', 'wobbly', 'spiffy', 'chirpy', 'zesty', 'bubbly', 'perky', 'sassy'];
|
||||
this.snakes = ['mamba', 'cobra', 'python', 'viper', 'krait', 'sidewinder', 'constrictor', 'boa', 'asp', 'anaconda', 'krait'];
|
||||
this.isBootstrapPeer = isBootstrapPeer;
|
||||
this._isBootstrapPeer = isBootstrapPeer;
|
||||
this.peers = new Map();
|
||||
this.routingTable = new Map();
|
||||
this.userID = userID;
|
||||
@@ -244,7 +247,7 @@ export class PeerManager {
|
||||
}
|
||||
numActive++;
|
||||
}
|
||||
if (!this.isBootstrapPeer && numActive === 0) {
|
||||
if (!this._isBootstrapPeer && numActive === 0) {
|
||||
console.log.apply(null, log(`No peers connected, will attempt to reconnect in ${this.reconnectPeriod} seconds...`));
|
||||
// Websocket reconnect
|
||||
if (this.websocket?.readyState === WebSocket.OPEN) {
|
||||
@@ -257,6 +260,11 @@ export class PeerManager {
|
||||
let output = `Current status:` + "\n" + `[${logID(this.peerID)}]${this.getPeername(this.peerID)}[local]` + "\n";
|
||||
for (let [peerID, peer] of this.peers) {
|
||||
output += `[${logID(peerID)}]${peer.rtcPeer?.connectionState}:${this.getPeername(peerID)}${this.bootstrapPeerIDs?.has(peerID) ? "[Bootstrap]" : ""}` + "\n";
|
||||
if (peer.rpcSuperlog) {
|
||||
for (let transactionID of peer.pendingRPCs.keys()) {
|
||||
output += `[${logID(transactionID)}]`;
|
||||
}
|
||||
}
|
||||
}
|
||||
output += `numActivePeers: ${numActive}` + "\n";
|
||||
console.log.apply(null, log(output));
|
||||
@@ -421,7 +429,7 @@ class PeerConnection {
|
||||
this.send({ type: "hello datachannel", from: this.peerManager.peerID, to: this.remotePeerID });
|
||||
// this.dataChannel?.send(`{typeHello datachannel from: ${this.peerManager.peerID}`);
|
||||
// console.log.apply(null, log([...this.peerManager.peers.keys()]));
|
||||
if (this.peerManager.isBootstrapPeer) {
|
||||
if (this.peerManager._isBootstrapPeer) {
|
||||
this.send({ type: 'initial_peers', from: this.peerManager.peerID, peers: [...this.peerManager.peers.keys()].filter(entry => entry !== this.remotePeerID) });
|
||||
// this.dataChannel.send(JSON.stringify());
|
||||
}
|
||||
@@ -442,6 +450,9 @@ class PeerConnection {
|
||||
}
|
||||
async connect() {
|
||||
let connectionPromise = new Promise((resolve, reject) => { this.connectionPromise = { resolve, reject }; });
|
||||
if (!(typeof RTCPeerConnection === "function")) {
|
||||
throw new Error("RTCPeerConnection is not a function, exiting.");
|
||||
}
|
||||
this.rtcPeer = new RTCPeerConnection(PeerConnection.config);
|
||||
this.rtcPeer.onconnectionstatechange = async (e) => {
|
||||
this.webRTCSuperlog && console.log.apply(null, log(`rtcPeer: onconnectionstatechange: ${this.rtcPeer?.connectionState}: ${this.remotePeerID}`));
|
||||
@@ -519,14 +530,19 @@ class PeerConnection {
|
||||
}
|
||||
async onWebsocketMessage(message) {
|
||||
if (message.type == "rtc_connect") {
|
||||
this.rtcPeer?.setRemoteDescription(message.description);
|
||||
try {
|
||||
this.rtcPeer?.setRemoteDescription(message.description);
|
||||
}
|
||||
catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
// /*
|
||||
// let ignoreOffer = false;
|
||||
// let isSettingRemoteAnswerPending = false;
|
||||
// signaler.onmessage = async ({ data: { description, candidate } }) => {
|
||||
if (!this.rtcPeer) {
|
||||
throw new Error();
|
||||
throw new Error("Unable to instantiate RTCPeerConnection, exiting.");
|
||||
}
|
||||
let description = null;
|
||||
if (message.type == "rtc_description") {
|
||||
@@ -547,7 +563,12 @@ class PeerConnection {
|
||||
return;
|
||||
}
|
||||
this.isSettingRemoteAnswerPending = description.type == "answer";
|
||||
await this.rtcPeer.setRemoteDescription(description);
|
||||
try {
|
||||
await this.rtcPeer.setRemoteDescription(description);
|
||||
}
|
||||
catch (e) {
|
||||
console.log("PeerConnection:setRemoteDescription:failed:", e, description);
|
||||
}
|
||||
this.isSettingRemoteAnswerPending = false;
|
||||
if (description.type === "offer") {
|
||||
await this.rtcPeer.setLocalDescription();
|
||||
@@ -558,9 +579,10 @@ class PeerConnection {
|
||||
try {
|
||||
await this.rtcPeer.addIceCandidate(candidate);
|
||||
}
|
||||
catch (err) {
|
||||
catch (e) {
|
||||
if (!this.ignoreOffer) {
|
||||
throw err;
|
||||
console.log("PeerConnection:addIceCandidate:failed:", e, candidate);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -632,7 +654,7 @@ class PeerConnection {
|
||||
// Think about a timeout here to auto reject it after a while.
|
||||
let promise = new Promise((resolve, reject) => {
|
||||
this.pendingRPCs.set(transactionID, { resolve, reject, functionName });
|
||||
// setTimeout(() => reject("bad"), 1000);
|
||||
setTimeout(() => reject(`function:${functionName}[${transactionID}] failed to resolve after 10 seconds.`), 10000);
|
||||
});
|
||||
let message = {
|
||||
type: "rpc_call",
|
||||
@@ -661,14 +683,12 @@ class PeerConnection {
|
||||
throw new Error();
|
||||
}
|
||||
pendingRPC.resolve(message.response);
|
||||
this.pendingRPCs.delete(message.transaction_id);
|
||||
}
|
||||
if (type === "rpc_call") {
|
||||
this.rpcSuperlog && console.log.apply(null, log(`[${logID(this.remotePeerID)}]->[rpc][${logID(this.peerManager.peerID)}] call: `, message.function_name, message.transaction_id, JSON.stringify(message.args, null, 2)));
|
||||
let response = await this.peerManager.callFromRemote(message.function_name, message.args);
|
||||
this.rpcSuperlog && console.log.apply(null, log(`[rpc] call: response:`, response));
|
||||
if (response === undefined) {
|
||||
return;
|
||||
}
|
||||
let responseMessage = { type: 'rpc_response', function_name: message.function_name, transaction_id: message.transaction_id, response: response };
|
||||
this.send(responseMessage);
|
||||
}
|
||||
@@ -715,3 +735,4 @@ PeerConnection.config = {
|
||||
{ urls: "stun:stun4.l.google.com" },
|
||||
],
|
||||
};
|
||||
//# sourceMappingURL=PeerManager.js.map
|
||||
Reference in New Issue
Block a user