sx-peerjs-http-util 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -201
- package/dist/MessageHandler.d.ts +8 -0
- package/dist/MessageHandler.d.ts.map +1 -1
- package/dist/PeerJsWrapper.d.ts +89 -3
- package/dist/PeerJsWrapper.d.ts.map +1 -1
- package/dist/Router.d.ts +186 -0
- package/dist/Router.d.ts.map +1 -0
- package/dist/RoutingDB.d.ts +75 -0
- package/dist/RoutingDB.d.ts.map +1 -0
- package/dist/constants.d.ts +23 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +918 -199
- package/dist/index.esm.js.map +3 -3
- package/dist/index.umd.js +918 -199
- package/dist/index.umd.js.map +3 -3
- package/dist/types.d.ts +48 -7
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/Routing.d.ts +0 -88
- package/dist/Routing.d.ts.map +0 -1
package/dist/index.umd.js
CHANGED
|
@@ -647,7 +647,16 @@ var PeerJsHttpUtil = (() => {
|
|
|
647
647
|
var index_exports = {};
|
|
648
648
|
__export(index_exports, {
|
|
649
649
|
PeerJsWrapper: () => PeerJsWrapper,
|
|
650
|
-
VERSION: () => VERSION
|
|
650
|
+
VERSION: () => VERSION,
|
|
651
|
+
clearAllRoutingData: () => clearAllRoutingData,
|
|
652
|
+
deleteRouteEntry: () => deleteRouteEntry,
|
|
653
|
+
initRoutingDB: () => initRoutingDB,
|
|
654
|
+
loadDirectNodes: () => loadDirectNodes,
|
|
655
|
+
loadRoutingTable: () => loadRoutingTable,
|
|
656
|
+
saveDirectNode: () => saveDirectNode,
|
|
657
|
+
saveDirectNodes: () => saveDirectNodes,
|
|
658
|
+
saveRouteEntries: () => saveRouteEntries,
|
|
659
|
+
saveRouteEntry: () => saveRouteEntry
|
|
651
660
|
});
|
|
652
661
|
|
|
653
662
|
// node_modules/peerjs-js-binarypack/dist/binarypack.mjs
|
|
@@ -4798,16 +4807,146 @@ var PeerJsHttpUtil = (() => {
|
|
|
4798
4807
|
}
|
|
4799
4808
|
};
|
|
4800
4809
|
|
|
4801
|
-
// src/
|
|
4802
|
-
var
|
|
4810
|
+
// src/constants.ts
|
|
4811
|
+
var CONNECTION_TIMEOUT_MS = 3e4;
|
|
4812
|
+
var SEND_TIMEOUT_MS = 1e4;
|
|
4813
|
+
var RECONNECT_DELAY_MS = 1e3;
|
|
4814
|
+
var ROUTE_EXPIRE_AGE_MS = 5 * 60 * 1e3;
|
|
4815
|
+
var MAX_DIRECT_NODES = 5;
|
|
4816
|
+
var ROUTE_CLEANUP_INTERVAL_MS = 60 * 1e3;
|
|
4817
|
+
var ROUTE_BROADCAST_INTERVAL_MS = 30 * 1e3;
|
|
4818
|
+
var DEFAULT_TTL = 128;
|
|
4819
|
+
|
|
4820
|
+
// src/RoutingDB.ts
|
|
4821
|
+
var DB_NAME = "peerjs-routing-db";
|
|
4822
|
+
var DB_VERSION = 1;
|
|
4823
|
+
var ROUTING_TABLE_STORE = "routing-table";
|
|
4824
|
+
var DIRECT_NODES_STORE = "direct-nodes";
|
|
4825
|
+
var db = null;
|
|
4826
|
+
function withStore(storeName, mode, operation) {
|
|
4827
|
+
return openDB().then((database) => {
|
|
4828
|
+
return new Promise((resolve, reject) => {
|
|
4829
|
+
const tx = database.transaction(storeName, mode);
|
|
4830
|
+
const store = tx.objectStore(storeName);
|
|
4831
|
+
const request = operation(store);
|
|
4832
|
+
if (request) {
|
|
4833
|
+
request.onerror = () => reject(request.error);
|
|
4834
|
+
request.onsuccess = () => resolve(request.result);
|
|
4835
|
+
} else {
|
|
4836
|
+
tx.oncomplete = () => resolve(void 0);
|
|
4837
|
+
tx.onerror = () => reject(tx.error);
|
|
4838
|
+
}
|
|
4839
|
+
});
|
|
4840
|
+
});
|
|
4841
|
+
}
|
|
4842
|
+
function openDB() {
|
|
4843
|
+
return new Promise((resolve, reject) => {
|
|
4844
|
+
if (db) {
|
|
4845
|
+
resolve(db);
|
|
4846
|
+
return;
|
|
4847
|
+
}
|
|
4848
|
+
const request = indexedDB.open(DB_NAME, DB_VERSION);
|
|
4849
|
+
request.onerror = () => reject(request.error);
|
|
4850
|
+
request.onsuccess = () => {
|
|
4851
|
+
db = request.result;
|
|
4852
|
+
resolve(db);
|
|
4853
|
+
};
|
|
4854
|
+
request.onupgradeneeded = (e) => {
|
|
4855
|
+
const req = e.target;
|
|
4856
|
+
const database = req.result;
|
|
4857
|
+
if (!database.objectStoreNames.contains(ROUTING_TABLE_STORE)) {
|
|
4858
|
+
const routingStore = database.createObjectStore(ROUTING_TABLE_STORE, {
|
|
4859
|
+
keyPath: "target"
|
|
4860
|
+
});
|
|
4861
|
+
routingStore.createIndex("timestamp", "timestamp", { unique: false });
|
|
4862
|
+
}
|
|
4863
|
+
if (!database.objectStoreNames.contains(DIRECT_NODES_STORE)) {
|
|
4864
|
+
const nodesStore = database.createObjectStore(DIRECT_NODES_STORE, {
|
|
4865
|
+
keyPath: "nodeId"
|
|
4866
|
+
});
|
|
4867
|
+
nodesStore.createIndex("timestamp", "timestamp", { unique: false });
|
|
4868
|
+
}
|
|
4869
|
+
};
|
|
4870
|
+
});
|
|
4871
|
+
}
|
|
4872
|
+
async function initRoutingDB() {
|
|
4873
|
+
await openDB();
|
|
4874
|
+
}
|
|
4875
|
+
async function saveRouteEntry(entry) {
|
|
4876
|
+
await withStore(ROUTING_TABLE_STORE, "readwrite", (store) => store.put(entry));
|
|
4877
|
+
}
|
|
4878
|
+
async function saveRouteEntries(entries) {
|
|
4879
|
+
await openDB().then((database) => {
|
|
4880
|
+
return new Promise((resolve, reject) => {
|
|
4881
|
+
const tx = database.transaction(ROUTING_TABLE_STORE, "readwrite");
|
|
4882
|
+
const store = tx.objectStore(ROUTING_TABLE_STORE);
|
|
4883
|
+
for (const entry of entries) {
|
|
4884
|
+
store.put(entry);
|
|
4885
|
+
}
|
|
4886
|
+
tx.oncomplete = () => resolve();
|
|
4887
|
+
tx.onerror = () => reject(tx.error);
|
|
4888
|
+
});
|
|
4889
|
+
});
|
|
4890
|
+
}
|
|
4891
|
+
async function deleteRouteEntry(target) {
|
|
4892
|
+
await withStore(ROUTING_TABLE_STORE, "readwrite", (store) => store.delete(target));
|
|
4893
|
+
}
|
|
4894
|
+
async function loadRoutingTable() {
|
|
4895
|
+
return withStore(ROUTING_TABLE_STORE, "readonly", (store) => store.getAll());
|
|
4896
|
+
}
|
|
4897
|
+
async function saveDirectNode(node) {
|
|
4898
|
+
await withStore(DIRECT_NODES_STORE, "readwrite", (store) => store.put(node));
|
|
4899
|
+
}
|
|
4900
|
+
async function saveDirectNodes(nodes) {
|
|
4901
|
+
await openDB().then((database) => {
|
|
4902
|
+
return new Promise((resolve, reject) => {
|
|
4903
|
+
const tx = database.transaction(DIRECT_NODES_STORE, "readwrite");
|
|
4904
|
+
const store = tx.objectStore(DIRECT_NODES_STORE);
|
|
4905
|
+
for (const node of nodes) {
|
|
4906
|
+
store.put(node);
|
|
4907
|
+
}
|
|
4908
|
+
tx.oncomplete = () => resolve();
|
|
4909
|
+
tx.onerror = () => reject(tx.error);
|
|
4910
|
+
});
|
|
4911
|
+
});
|
|
4912
|
+
}
|
|
4913
|
+
async function loadDirectNodes() {
|
|
4914
|
+
return withStore(DIRECT_NODES_STORE, "readonly", (store) => store.getAll());
|
|
4915
|
+
}
|
|
4916
|
+
async function clearAllRoutingData() {
|
|
4917
|
+
const database = await openDB();
|
|
4918
|
+
await new Promise((resolve, reject) => {
|
|
4919
|
+
const tx = database.transaction(ROUTING_TABLE_STORE, "readwrite");
|
|
4920
|
+
const store = tx.objectStore(ROUTING_TABLE_STORE);
|
|
4921
|
+
store.clear();
|
|
4922
|
+
tx.oncomplete = () => resolve();
|
|
4923
|
+
tx.onerror = () => reject(tx.error);
|
|
4924
|
+
});
|
|
4925
|
+
await new Promise((resolve, reject) => {
|
|
4926
|
+
const tx = database.transaction(DIRECT_NODES_STORE, "readwrite");
|
|
4927
|
+
const store = tx.objectStore(DIRECT_NODES_STORE);
|
|
4928
|
+
store.clear();
|
|
4929
|
+
tx.oncomplete = () => resolve();
|
|
4930
|
+
tx.onerror = () => reject(tx.error);
|
|
4931
|
+
});
|
|
4932
|
+
}
|
|
4933
|
+
|
|
4934
|
+
// src/Router.ts
|
|
4935
|
+
var Router = class {
|
|
4803
4936
|
/** 路由表:target -> RouteEntry */
|
|
4804
4937
|
routingTable = /* @__PURE__ */ new Map();
|
|
4805
|
-
/**
|
|
4806
|
-
|
|
4938
|
+
/** 直连节点及延迟列表 */
|
|
4939
|
+
directNodes = [];
|
|
4807
4940
|
/** 中继配置 */
|
|
4808
4941
|
relayConfig;
|
|
4809
4942
|
/** 回调函数集合 */
|
|
4810
4943
|
callbacks;
|
|
4944
|
+
/** 等待路由发现响应的 pending 队列 */
|
|
4945
|
+
pendingRouteQueries = /* @__PURE__ */ new Map();
|
|
4946
|
+
/** 定时清理定时器 */
|
|
4947
|
+
cleanupTimer = null;
|
|
4948
|
+
/** 周期广播定时器 */
|
|
4949
|
+
broadcastTimer = null;
|
|
4811
4950
|
/**
|
|
4812
4951
|
* 创建路由管理器
|
|
4813
4952
|
* @param callbacks 回调函数集合
|
|
@@ -4818,76 +4957,243 @@ var PeerJsHttpUtil = (() => {
|
|
|
4818
4957
|
this.relayConfig = relayConfig ?? {};
|
|
4819
4958
|
}
|
|
4820
4959
|
/**
|
|
4821
|
-
*
|
|
4822
|
-
|
|
4960
|
+
* 初始化路由管理器(从 IndexedDB 加载数据并启动定时任务)
|
|
4961
|
+
*/
|
|
4962
|
+
async init() {
|
|
4963
|
+
await initRoutingDB();
|
|
4964
|
+
await this.loadFromDB();
|
|
4965
|
+
this.startMaintenanceTasks();
|
|
4966
|
+
}
|
|
4967
|
+
/**
|
|
4968
|
+
* 从 IndexedDB 加载路由数据
|
|
4969
|
+
*/
|
|
4970
|
+
async loadFromDB() {
|
|
4971
|
+
try {
|
|
4972
|
+
const routes = await loadRoutingTable();
|
|
4973
|
+
for (const entry of routes) {
|
|
4974
|
+
this.routingTable.set(entry.target, entry);
|
|
4975
|
+
}
|
|
4976
|
+
this.callbacks.debugLog("Routing", "loadedRoutes", routes.length);
|
|
4977
|
+
} catch (e) {
|
|
4978
|
+
this.callbacks.debugLog("Routing", "loadError", e);
|
|
4979
|
+
}
|
|
4980
|
+
try {
|
|
4981
|
+
const nodes = await loadDirectNodes();
|
|
4982
|
+
this.directNodes = nodes;
|
|
4983
|
+
this.callbacks.debugLog("Routing", "loadedNodes", nodes.length);
|
|
4984
|
+
} catch (e) {
|
|
4985
|
+
this.callbacks.debugLog("Routing", "loadNodesError", e);
|
|
4986
|
+
}
|
|
4987
|
+
}
|
|
4988
|
+
/**
|
|
4989
|
+
* 启动定时维护任务
|
|
4990
|
+
*/
|
|
4991
|
+
startMaintenanceTasks() {
|
|
4992
|
+
this.cleanupTimer = setInterval(() => {
|
|
4993
|
+
this.cleanupExpiredEntries();
|
|
4994
|
+
}, ROUTE_CLEANUP_INTERVAL_MS);
|
|
4995
|
+
this.broadcastTimer = setInterval(() => {
|
|
4996
|
+
this.broadcastRouteUpdate();
|
|
4997
|
+
}, ROUTE_BROADCAST_INTERVAL_MS);
|
|
4998
|
+
}
|
|
4999
|
+
/**
|
|
5000
|
+
* 清理过期路由条目
|
|
5001
|
+
*/
|
|
5002
|
+
async cleanupExpiredEntries() {
|
|
5003
|
+
const now = Date.now();
|
|
5004
|
+
for (const [target, entry] of this.routingTable) {
|
|
5005
|
+
if (now - entry.timestamp > ROUTE_EXPIRE_AGE_MS) {
|
|
5006
|
+
this.routingTable.delete(target);
|
|
5007
|
+
deleteRouteEntry(target).catch(() => {
|
|
5008
|
+
});
|
|
5009
|
+
}
|
|
5010
|
+
}
|
|
5011
|
+
this.directNodes = this.directNodes.filter((node) => {
|
|
5012
|
+
const isExpired = now - node.timestamp > ROUTE_EXPIRE_AGE_MS;
|
|
5013
|
+
if (isExpired) {
|
|
5014
|
+
return false;
|
|
5015
|
+
}
|
|
5016
|
+
return true;
|
|
5017
|
+
});
|
|
5018
|
+
this.callbacks.debugLog("Routing", "cleanup", {
|
|
5019
|
+
routes: this.routingTable.size,
|
|
5020
|
+
nodes: this.directNodes.length
|
|
5021
|
+
});
|
|
5022
|
+
}
|
|
5023
|
+
/**
|
|
5024
|
+
* 持久化路由表到 IndexedDB
|
|
5025
|
+
*/
|
|
5026
|
+
async persist() {
|
|
5027
|
+
try {
|
|
5028
|
+
const entries = Array.from(this.routingTable.values());
|
|
5029
|
+
await saveRouteEntries(entries);
|
|
5030
|
+
} catch (e) {
|
|
5031
|
+
this.callbacks.debugLog("Routing", "persistError", e);
|
|
5032
|
+
}
|
|
5033
|
+
try {
|
|
5034
|
+
await saveDirectNodes(this.directNodes);
|
|
5035
|
+
} catch (e) {
|
|
5036
|
+
this.callbacks.debugLog("Routing", "persistNodesError", e);
|
|
5037
|
+
}
|
|
5038
|
+
}
|
|
5039
|
+
/**
|
|
5040
|
+
* 销毁路由管理器(清理定时器)
|
|
5041
|
+
*/
|
|
5042
|
+
destroy() {
|
|
5043
|
+
if (this.cleanupTimer) {
|
|
5044
|
+
clearInterval(this.cleanupTimer);
|
|
5045
|
+
this.cleanupTimer = null;
|
|
5046
|
+
}
|
|
5047
|
+
if (this.broadcastTimer) {
|
|
5048
|
+
clearInterval(this.broadcastTimer);
|
|
5049
|
+
this.broadcastTimer = null;
|
|
5050
|
+
}
|
|
5051
|
+
this.pendingRouteQueries.forEach((pending) => {
|
|
5052
|
+
clearTimeout(pending.timer);
|
|
5053
|
+
});
|
|
5054
|
+
this.pendingRouteQueries.clear();
|
|
5055
|
+
}
|
|
5056
|
+
/**
|
|
5057
|
+
* 记录成功的直连通信
|
|
5058
|
+
* @param nodeId 节点 ID
|
|
5059
|
+
* @param latency 延迟(毫秒)
|
|
5060
|
+
*/
|
|
5061
|
+
recordDirectNode(nodeId, latency) {
|
|
5062
|
+
const myPeerId = this.callbacks.getMyPeerId();
|
|
5063
|
+
if (nodeId === myPeerId) return;
|
|
5064
|
+
const existing = this.directNodes.find((n) => n.nodeId === nodeId);
|
|
5065
|
+
const timestamp = Date.now();
|
|
5066
|
+
if (existing) {
|
|
5067
|
+
existing.latency = latency;
|
|
5068
|
+
existing.timestamp = timestamp;
|
|
5069
|
+
} else {
|
|
5070
|
+
const maxRelayNodes = this.relayConfig.maxRelayNodes ?? MAX_DIRECT_NODES;
|
|
5071
|
+
this.directNodes.push({ nodeId, latency, timestamp });
|
|
5072
|
+
if (this.directNodes.length > maxRelayNodes) {
|
|
5073
|
+
this.directNodes.sort((a, b) => a.latency - b.latency);
|
|
5074
|
+
this.directNodes.shift();
|
|
5075
|
+
}
|
|
5076
|
+
}
|
|
5077
|
+
this.callbacks.debugLog("Routing", "directNode", { nodeId, latency });
|
|
5078
|
+
}
|
|
5079
|
+
/**
|
|
5080
|
+
* 获取直连节点列表(按延迟升序)
|
|
5081
|
+
* @returns 直连节点列表
|
|
5082
|
+
*/
|
|
5083
|
+
getDirectNodes() {
|
|
5084
|
+
return [...this.directNodes].sort((a, b) => a.latency - b.latency);
|
|
5085
|
+
}
|
|
5086
|
+
/**
|
|
5087
|
+
* 检查是否可以直连目标节点
|
|
5088
|
+
* @param targetId 目标节点 ID
|
|
5089
|
+
* @returns 是否可以直连
|
|
5090
|
+
*/
|
|
5091
|
+
canReachDirectly(targetId) {
|
|
5092
|
+
return this.directNodes.some((n) => n.nodeId === targetId);
|
|
5093
|
+
}
|
|
5094
|
+
/**
|
|
5095
|
+
* 获取到直连节点的延迟
|
|
5096
|
+
* @param nodeId 节点 ID
|
|
5097
|
+
* @returns 延迟(毫秒),如果不存在返回 null
|
|
5098
|
+
*/
|
|
5099
|
+
getDirectLatency(nodeId) {
|
|
5100
|
+
const node = this.directNodes.find((n) => n.nodeId === nodeId);
|
|
5101
|
+
return node ? node.latency : null;
|
|
5102
|
+
}
|
|
5103
|
+
/**
|
|
5104
|
+
* 移除失效的路由(通信失败时调用)
|
|
5105
|
+
* @param nodeId 失效的节点 ID
|
|
5106
|
+
*/
|
|
5107
|
+
removeRoute(nodeId) {
|
|
5108
|
+
let removed = false;
|
|
5109
|
+
for (const [target, entry] of this.routingTable) {
|
|
5110
|
+
const originalLength = entry.nextHops.length;
|
|
5111
|
+
entry.nextHops = entry.nextHops.filter((h) => h.nodeId !== nodeId);
|
|
5112
|
+
if (entry.nextHops.length === 0) {
|
|
5113
|
+
this.routingTable.delete(target);
|
|
5114
|
+
deleteRouteEntry(target).catch(() => {
|
|
5115
|
+
});
|
|
5116
|
+
removed = true;
|
|
5117
|
+
} else if (entry.nextHops.length < originalLength) {
|
|
5118
|
+
entry.timestamp = Date.now();
|
|
5119
|
+
saveRouteEntry(entry).catch(() => {
|
|
5120
|
+
});
|
|
5121
|
+
removed = true;
|
|
5122
|
+
}
|
|
5123
|
+
}
|
|
5124
|
+
const nodeIndex = this.directNodes.findIndex((n) => n.nodeId === nodeId);
|
|
5125
|
+
if (nodeIndex !== -1) {
|
|
5126
|
+
this.directNodes.splice(nodeIndex, 1);
|
|
5127
|
+
removed = true;
|
|
5128
|
+
}
|
|
5129
|
+
if (removed) {
|
|
5130
|
+
this.callbacks.debugLog("Routing", "routeRemoved", nodeId);
|
|
5131
|
+
}
|
|
5132
|
+
}
|
|
5133
|
+
/**
|
|
5134
|
+
* 检查路由表是否为空
|
|
5135
|
+
* @returns 是否为空
|
|
5136
|
+
*/
|
|
5137
|
+
isRoutingTableEmpty() {
|
|
5138
|
+
return this.routingTable.size === 0;
|
|
5139
|
+
}
|
|
5140
|
+
/**
|
|
5141
|
+
* 记录成功通信的节点(兼容旧接口)
|
|
4823
5142
|
* @param nodeId 节点 ID
|
|
4824
5143
|
*/
|
|
4825
5144
|
recordSuccessfulNode(nodeId) {
|
|
4826
5145
|
const myPeerId = this.callbacks.getMyPeerId();
|
|
4827
5146
|
if (nodeId === myPeerId) return;
|
|
4828
|
-
|
|
5147
|
+
const existing = this.directNodes.find((n) => n.nodeId === nodeId);
|
|
5148
|
+
if (!existing) {
|
|
4829
5149
|
const maxRelayNodes = this.relayConfig.maxRelayNodes ?? 5;
|
|
4830
|
-
this.
|
|
4831
|
-
if (this.
|
|
4832
|
-
this.
|
|
5150
|
+
this.directNodes.push({ nodeId, latency: 100, timestamp: Date.now() });
|
|
5151
|
+
if (this.directNodes.length > maxRelayNodes) {
|
|
5152
|
+
this.directNodes.sort((a, b) => a.latency - b.latency);
|
|
5153
|
+
this.directNodes.shift();
|
|
4833
5154
|
}
|
|
4834
|
-
this.callbacks.debugLog("Routing", "newNode", nodeId);
|
|
4835
5155
|
}
|
|
4836
5156
|
}
|
|
4837
5157
|
/**
|
|
4838
5158
|
* 广播路由更新
|
|
4839
|
-
*
|
|
5159
|
+
* 向所有直连节点发送路由更新消息,告知它们本节点可达的节点列表
|
|
4840
5160
|
*/
|
|
4841
5161
|
async broadcastRouteUpdate() {
|
|
4842
5162
|
const myPeerId = this.callbacks.getMyPeerId();
|
|
4843
|
-
const reachableNodes =
|
|
4844
|
-
for (const
|
|
5163
|
+
const reachableNodes = this.getReachableNodes();
|
|
5164
|
+
for (const node of this.directNodes) {
|
|
4845
5165
|
try {
|
|
4846
|
-
await this.sendRouteUpdate(nodeId, reachableNodes);
|
|
5166
|
+
await this.sendRouteUpdate(node.nodeId, reachableNodes);
|
|
4847
5167
|
} catch {
|
|
4848
5168
|
}
|
|
4849
5169
|
}
|
|
4850
5170
|
}
|
|
5171
|
+
/**
|
|
5172
|
+
* 获取本节点可达的节点列表
|
|
5173
|
+
* @returns 可达节点数组(直连节点 + 自己)
|
|
5174
|
+
*/
|
|
5175
|
+
getReachableNodes() {
|
|
5176
|
+
const myPeerId = this.callbacks.getMyPeerId();
|
|
5177
|
+
const directNodeIds = this.directNodes.map((n) => n.nodeId);
|
|
5178
|
+
return [.../* @__PURE__ */ new Set([...directNodeIds, myPeerId])];
|
|
5179
|
+
}
|
|
4851
5180
|
/**
|
|
4852
5181
|
* 发送路由更新到指定节点
|
|
4853
5182
|
* @param targetId 目标节点 ID
|
|
4854
5183
|
* @param reachableNodes 可达的节点列表
|
|
4855
5184
|
*/
|
|
4856
5185
|
async sendRouteUpdate(targetId, reachableNodes) {
|
|
4857
|
-
const peerInstance = this.callbacks.getPeerInstance();
|
|
4858
5186
|
const myPeerId = this.callbacks.getMyPeerId();
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
const message = {
|
|
4870
|
-
type: "route-update",
|
|
4871
|
-
id: `${myPeerId}-route-${Date.now()}`,
|
|
4872
|
-
originalTarget: targetId,
|
|
4873
|
-
relayPath: [],
|
|
4874
|
-
forwardPath: [],
|
|
4875
|
-
routeUpdate: { reachableNodes }
|
|
4876
|
-
};
|
|
4877
|
-
conn.send(message);
|
|
4878
|
-
clearTimeout(timeout);
|
|
4879
|
-
conn.close();
|
|
4880
|
-
resolve();
|
|
4881
|
-
});
|
|
4882
|
-
conn.on("error", () => {
|
|
4883
|
-
clearTimeout(timeout);
|
|
4884
|
-
reject(new Error("Route update failed"));
|
|
4885
|
-
});
|
|
4886
|
-
conn.on("close", () => {
|
|
4887
|
-
clearTimeout(timeout);
|
|
4888
|
-
resolve();
|
|
4889
|
-
});
|
|
4890
|
-
});
|
|
5187
|
+
const message = {
|
|
5188
|
+
type: "route-update",
|
|
5189
|
+
id: `${myPeerId}-route-${Date.now()}`,
|
|
5190
|
+
originalTarget: targetId,
|
|
5191
|
+
relayPath: [],
|
|
5192
|
+
forwardPath: [],
|
|
5193
|
+
ttl: DEFAULT_TTL,
|
|
5194
|
+
routeUpdate: { reachableNodes }
|
|
5195
|
+
};
|
|
5196
|
+
await this.callbacks.sendRelayMessage(targetId, message);
|
|
4891
5197
|
}
|
|
4892
5198
|
/**
|
|
4893
5199
|
* 处理收到的路由更新
|
|
@@ -4900,39 +5206,217 @@ var PeerJsHttpUtil = (() => {
|
|
|
4900
5206
|
const myPeerId = this.callbacks.getMyPeerId();
|
|
4901
5207
|
const { reachableNodes } = message.routeUpdate;
|
|
4902
5208
|
const timestamp = Date.now();
|
|
5209
|
+
const viaLatency = this.getDirectLatency(fromPeerId) ?? 100;
|
|
4903
5210
|
for (const target of reachableNodes) {
|
|
4904
5211
|
if (target === myPeerId) continue;
|
|
4905
|
-
|
|
4906
|
-
const
|
|
4907
|
-
if (!
|
|
4908
|
-
|
|
5212
|
+
let entry = this.routingTable.get(target);
|
|
5213
|
+
const totalLatency = viaLatency + 100;
|
|
5214
|
+
if (!entry) {
|
|
5215
|
+
entry = {
|
|
4909
5216
|
target,
|
|
4910
|
-
|
|
4911
|
-
hops,
|
|
4912
|
-
via: fromPeerId,
|
|
5217
|
+
nextHops: [],
|
|
5218
|
+
hops: 1,
|
|
4913
5219
|
timestamp
|
|
5220
|
+
};
|
|
5221
|
+
this.routingTable.set(target, entry);
|
|
5222
|
+
}
|
|
5223
|
+
const existingHop = entry.nextHops.find((h) => h.nodeId === fromPeerId);
|
|
5224
|
+
if (existingHop) {
|
|
5225
|
+
existingHop.latency = totalLatency;
|
|
5226
|
+
} else {
|
|
5227
|
+
entry.nextHops.push({ nodeId: fromPeerId, latency: totalLatency });
|
|
5228
|
+
}
|
|
5229
|
+
entry.nextHops.sort((a, b) => a.latency - b.latency);
|
|
5230
|
+
entry.hops = Math.min(entry.hops, 1);
|
|
5231
|
+
entry.timestamp = timestamp;
|
|
5232
|
+
this.callbacks.debugLog("Routing", "update", { target, nextHop: fromPeerId, latency: totalLatency });
|
|
5233
|
+
}
|
|
5234
|
+
}
|
|
5235
|
+
/**
|
|
5236
|
+
* 执行路由发现广播
|
|
5237
|
+
* 当直连和路由表都失败时,向所有直连节点广播询问谁能连通目标
|
|
5238
|
+
* @param targetId 目标节点 ID
|
|
5239
|
+
* @returns 路由条目(如果发现)
|
|
5240
|
+
*/
|
|
5241
|
+
async discoverRoute(targetId) {
|
|
5242
|
+
const myPeerId = this.callbacks.getMyPeerId();
|
|
5243
|
+
const directNodes = this.getDirectNodes();
|
|
5244
|
+
if (directNodes.length === 0) {
|
|
5245
|
+
this.callbacks.debugLog("Routing", "discoverRoute", "no direct nodes");
|
|
5246
|
+
return null;
|
|
5247
|
+
}
|
|
5248
|
+
this.callbacks.debugLog("Routing", "discoverRoute", { targetId, directNodes: directNodes.length });
|
|
5249
|
+
const queryId = `${myPeerId}-query-${Date.now()}`;
|
|
5250
|
+
const routeEntry = await new Promise((resolve, reject) => {
|
|
5251
|
+
const timer = setTimeout(() => {
|
|
5252
|
+
this.pendingRouteQueries.delete(queryId);
|
|
5253
|
+
resolve(null);
|
|
5254
|
+
}, CONNECTION_TIMEOUT_MS);
|
|
5255
|
+
this.pendingRouteQueries.set(queryId, { resolve, reject, timer });
|
|
5256
|
+
const message = {
|
|
5257
|
+
type: "route-query",
|
|
5258
|
+
id: queryId,
|
|
5259
|
+
originalTarget: targetId,
|
|
5260
|
+
relayPath: [myPeerId],
|
|
5261
|
+
forwardPath: [],
|
|
5262
|
+
ttl: DEFAULT_TTL,
|
|
5263
|
+
routeQuery: {
|
|
5264
|
+
queryOrigin: myPeerId,
|
|
5265
|
+
targetNode: targetId,
|
|
5266
|
+
queryPath: [myPeerId]
|
|
5267
|
+
}
|
|
5268
|
+
};
|
|
5269
|
+
for (const node of directNodes) {
|
|
5270
|
+
this.callbacks.sendRelayMessage(node.nodeId, message).catch(() => {
|
|
5271
|
+
});
|
|
5272
|
+
}
|
|
5273
|
+
});
|
|
5274
|
+
return routeEntry;
|
|
5275
|
+
}
|
|
5276
|
+
/**
|
|
5277
|
+
* 处理路由查询消息
|
|
5278
|
+
* @param fromPeerId 发送查询的节点
|
|
5279
|
+
* @param message 路由查询消息
|
|
5280
|
+
*/
|
|
5281
|
+
handleRouteQuery(fromPeerId, message) {
|
|
5282
|
+
if (!message.routeQuery) return;
|
|
5283
|
+
const myPeerId = this.callbacks.getMyPeerId();
|
|
5284
|
+
const { queryOrigin, targetNode, queryPath } = message.routeQuery;
|
|
5285
|
+
if (targetNode === myPeerId) {
|
|
5286
|
+
const latency = this.getDirectLatency(fromPeerId) ?? 100;
|
|
5287
|
+
const response = {
|
|
5288
|
+
type: "route-response",
|
|
5289
|
+
id: `${myPeerId}-resp-${Date.now()}`,
|
|
5290
|
+
originalTarget: queryOrigin,
|
|
5291
|
+
relayPath: [],
|
|
5292
|
+
forwardPath: [],
|
|
5293
|
+
ttl: DEFAULT_TTL,
|
|
5294
|
+
routeResponse: {
|
|
5295
|
+
queryOrigin,
|
|
5296
|
+
responder: myPeerId,
|
|
5297
|
+
targetNode,
|
|
5298
|
+
latency
|
|
5299
|
+
}
|
|
5300
|
+
};
|
|
5301
|
+
this.callbacks.sendRelayMessage(fromPeerId, response);
|
|
5302
|
+
return;
|
|
5303
|
+
}
|
|
5304
|
+
if (queryPath.includes(myPeerId)) {
|
|
5305
|
+
return;
|
|
5306
|
+
}
|
|
5307
|
+
const currentTTL = message.ttl ?? DEFAULT_TTL;
|
|
5308
|
+
if (currentTTL <= 0) {
|
|
5309
|
+
this.callbacks.debugLog("Routing", "ttlExpired", { type: "route-query", id: message.id });
|
|
5310
|
+
return;
|
|
5311
|
+
}
|
|
5312
|
+
const nextHop = this.findNextHopToTarget(targetNode);
|
|
5313
|
+
if (nextHop) {
|
|
5314
|
+
const latency = (this.getDirectLatency(fromPeerId) ?? 100) + nextHop.latency;
|
|
5315
|
+
const response = {
|
|
5316
|
+
type: "route-response",
|
|
5317
|
+
id: `${myPeerId}-resp-${Date.now()}`,
|
|
5318
|
+
originalTarget: queryOrigin,
|
|
5319
|
+
relayPath: [],
|
|
5320
|
+
forwardPath: [],
|
|
5321
|
+
ttl: DEFAULT_TTL,
|
|
5322
|
+
routeResponse: {
|
|
5323
|
+
queryOrigin,
|
|
5324
|
+
responder: myPeerId,
|
|
5325
|
+
targetNode,
|
|
5326
|
+
latency
|
|
5327
|
+
}
|
|
5328
|
+
};
|
|
5329
|
+
this.callbacks.sendRelayMessage(fromPeerId, response);
|
|
5330
|
+
return;
|
|
5331
|
+
}
|
|
5332
|
+
const newPath = [...queryPath, myPeerId];
|
|
5333
|
+
const forwardMessage = {
|
|
5334
|
+
...message,
|
|
5335
|
+
relayPath: newPath,
|
|
5336
|
+
ttl: currentTTL - 1,
|
|
5337
|
+
routeQuery: {
|
|
5338
|
+
...message.routeQuery,
|
|
5339
|
+
queryPath: newPath
|
|
5340
|
+
}
|
|
5341
|
+
};
|
|
5342
|
+
for (const node of this.directNodes) {
|
|
5343
|
+
if (node.nodeId !== fromPeerId) {
|
|
5344
|
+
this.callbacks.sendRelayMessage(node.nodeId, forwardMessage).catch(() => {
|
|
4914
5345
|
});
|
|
4915
|
-
this.callbacks.debugLog("Routing", "update", { target, nextHop: fromPeerId, hops });
|
|
4916
5346
|
}
|
|
4917
5347
|
}
|
|
4918
5348
|
}
|
|
4919
5349
|
/**
|
|
4920
|
-
*
|
|
5350
|
+
* 处理路由查询响应
|
|
5351
|
+
* @param fromPeerId 响应者节点
|
|
5352
|
+
* @param message 路由响应消息
|
|
5353
|
+
*/
|
|
5354
|
+
handleRouteResponse(fromPeerId, message) {
|
|
5355
|
+
if (!message.routeResponse) return;
|
|
5356
|
+
const { queryOrigin, targetNode, latency } = message.routeResponse;
|
|
5357
|
+
const myPeerId = this.callbacks.getMyPeerId();
|
|
5358
|
+
if (queryOrigin !== myPeerId) return;
|
|
5359
|
+
const pending = Array.from(this.pendingRouteQueries.values())[0];
|
|
5360
|
+
if (!pending) return;
|
|
5361
|
+
let entry = this.routingTable.get(targetNode);
|
|
5362
|
+
const timestamp = Date.now();
|
|
5363
|
+
if (!entry) {
|
|
5364
|
+
entry = {
|
|
5365
|
+
target: targetNode,
|
|
5366
|
+
nextHops: [],
|
|
5367
|
+
hops: 1,
|
|
5368
|
+
timestamp
|
|
5369
|
+
};
|
|
5370
|
+
this.routingTable.set(targetNode, entry);
|
|
5371
|
+
}
|
|
5372
|
+
const existingHop = entry.nextHops.find((h) => h.nodeId === fromPeerId);
|
|
5373
|
+
if (existingHop) {
|
|
5374
|
+
existingHop.latency = latency;
|
|
5375
|
+
} else {
|
|
5376
|
+
entry.nextHops.push({ nodeId: fromPeerId, latency });
|
|
5377
|
+
}
|
|
5378
|
+
entry.nextHops.sort((a, b) => a.latency - b.latency);
|
|
5379
|
+
entry.timestamp = timestamp;
|
|
5380
|
+
this.callbacks.debugLog("Routing", "discovered", { targetNode, nextHop: fromPeerId, latency });
|
|
5381
|
+
clearTimeout(pending.timer);
|
|
5382
|
+
pending.resolve(entry);
|
|
5383
|
+
}
|
|
5384
|
+
/**
|
|
5385
|
+
* 查找到目标节点的下一跳
|
|
5386
|
+
* @param targetId 目标节点 ID
|
|
5387
|
+
* @returns 下一跳信息,如果没有则返回 null
|
|
5388
|
+
*/
|
|
5389
|
+
findNextHopToTarget(targetId) {
|
|
5390
|
+
const entry = this.routingTable.get(targetId);
|
|
5391
|
+
if (!entry || entry.nextHops.length === 0) return null;
|
|
5392
|
+
return entry.nextHops[0];
|
|
5393
|
+
}
|
|
5394
|
+
/**
|
|
5395
|
+
* 获取到目标节点的所有下一跳(按延迟升序)
|
|
5396
|
+
* @param targetId 目标节点 ID
|
|
5397
|
+
* @returns 下一跳列表
|
|
5398
|
+
*/
|
|
5399
|
+
getNextHopsToTarget(targetId) {
|
|
5400
|
+
const entry = this.routingTable.get(targetId);
|
|
5401
|
+
return entry ? [...entry.nextHops] : [];
|
|
5402
|
+
}
|
|
5403
|
+
/**
|
|
5404
|
+
* 获取路由表
|
|
4921
5405
|
* @returns 路由表对象
|
|
4922
5406
|
*/
|
|
4923
5407
|
getRoutingTable() {
|
|
4924
5408
|
const result = {};
|
|
4925
5409
|
this.routingTable.forEach((entry, target) => {
|
|
4926
|
-
result[target] = entry;
|
|
5410
|
+
result[target] = { ...entry, nextHops: [...entry.nextHops] };
|
|
4927
5411
|
});
|
|
4928
5412
|
return result;
|
|
4929
5413
|
}
|
|
4930
5414
|
/**
|
|
4931
|
-
*
|
|
4932
|
-
* @returns
|
|
5415
|
+
* 获取已知节点列表(兼容旧接口)
|
|
5416
|
+
* @returns 节点 ID 数组
|
|
4933
5417
|
*/
|
|
4934
5418
|
getKnownNodes() {
|
|
4935
|
-
return
|
|
5419
|
+
return this.directNodes.map((n) => n.nodeId);
|
|
4936
5420
|
}
|
|
4937
5421
|
};
|
|
4938
5422
|
|
|
@@ -4991,6 +5475,14 @@ var PeerJsHttpUtil = (() => {
|
|
|
4991
5475
|
}
|
|
4992
5476
|
return { status: 200, data: result };
|
|
4993
5477
|
}
|
|
5478
|
+
const currentTTL = relayMessage.ttl ?? DEFAULT_TTL;
|
|
5479
|
+
if (currentTTL <= 0) {
|
|
5480
|
+
this.callbacks.debugLog("MessageHandler", "ttlExpired", { type: "relay-request", id: relayMessage.id });
|
|
5481
|
+
return {
|
|
5482
|
+
status: 502,
|
|
5483
|
+
data: { error: "TTL expired - message dropped to prevent routing loop" }
|
|
5484
|
+
};
|
|
5485
|
+
}
|
|
4994
5486
|
if (forwardPath.length > 0) {
|
|
4995
5487
|
const nextHop = forwardPath[0];
|
|
4996
5488
|
const remainingPath = forwardPath.slice(1);
|
|
@@ -5001,6 +5493,7 @@ var PeerJsHttpUtil = (() => {
|
|
|
5001
5493
|
originalTarget,
|
|
5002
5494
|
relayPath: [...relayPath, myPeerId],
|
|
5003
5495
|
forwardPath: remainingPath,
|
|
5496
|
+
ttl: currentTTL - 1,
|
|
5004
5497
|
request
|
|
5005
5498
|
});
|
|
5006
5499
|
return response;
|
|
@@ -5043,54 +5536,70 @@ var PeerJsHttpUtil = (() => {
|
|
|
5043
5536
|
return typeof result === "object" && result !== null && "status" in result && "data" in result;
|
|
5044
5537
|
}
|
|
5045
5538
|
/**
|
|
5046
|
-
*
|
|
5047
|
-
* @param
|
|
5048
|
-
* @param message
|
|
5049
|
-
* @
|
|
5539
|
+
* 创建连接并发送中继消息的通用方法
|
|
5540
|
+
* @param targetId 目标节点 ID
|
|
5541
|
+
* @param message 要发送的消息
|
|
5542
|
+
* @param extractResponse 从响应消息中提取数据的函数
|
|
5543
|
+
* @returns Promise<Response>
|
|
5050
5544
|
*/
|
|
5051
|
-
|
|
5545
|
+
createConnectionAndSend(targetId, message, extractResponse) {
|
|
5052
5546
|
return new Promise((resolve, reject) => {
|
|
5053
|
-
this.callbacks.debugLog("MessageHandler", "
|
|
5547
|
+
this.callbacks.debugLog("MessageHandler", "createConnectionAndSend", { targetId });
|
|
5054
5548
|
this.callbacks.waitForReady().then(() => {
|
|
5055
5549
|
const peerInstance = this.callbacks.getPeerInstance();
|
|
5056
5550
|
if (!peerInstance) {
|
|
5057
5551
|
reject(new Error("Peer instance not available"));
|
|
5058
5552
|
return;
|
|
5059
5553
|
}
|
|
5060
|
-
const conn = peerInstance.connect(
|
|
5554
|
+
const conn = peerInstance.connect(targetId, { reliable: true });
|
|
5061
5555
|
const timeout = setTimeout(() => {
|
|
5062
5556
|
conn.close();
|
|
5063
|
-
reject(new Error(`
|
|
5064
|
-
},
|
|
5557
|
+
reject(new Error(`Connection timeout: ${targetId}`));
|
|
5558
|
+
}, CONNECTION_TIMEOUT_MS);
|
|
5065
5559
|
conn.on("open", () => {
|
|
5066
|
-
this.callbacks.debugLog("Conn", "open",
|
|
5560
|
+
this.callbacks.debugLog("Conn", "open", targetId);
|
|
5067
5561
|
conn.send(message);
|
|
5068
5562
|
});
|
|
5069
5563
|
conn.on("data", (responseData) => {
|
|
5070
5564
|
const response = responseData;
|
|
5071
|
-
|
|
5565
|
+
const extracted = extractResponse(response);
|
|
5566
|
+
if (extracted) {
|
|
5072
5567
|
clearTimeout(timeout);
|
|
5073
5568
|
conn.close();
|
|
5074
|
-
|
|
5075
|
-
resolve(response.response);
|
|
5076
|
-
} else {
|
|
5077
|
-
reject(new Error("Invalid relay response"));
|
|
5078
|
-
}
|
|
5569
|
+
resolve(extracted);
|
|
5079
5570
|
}
|
|
5080
5571
|
});
|
|
5081
5572
|
conn.on("error", (err) => {
|
|
5082
|
-
this.callbacks.debugLog("Conn", "error", { peer:
|
|
5573
|
+
this.callbacks.debugLog("Conn", "error", { peer: targetId, error: err });
|
|
5083
5574
|
clearTimeout(timeout);
|
|
5084
5575
|
reject(err);
|
|
5085
5576
|
});
|
|
5086
5577
|
conn.on("close", () => {
|
|
5087
|
-
this.callbacks.debugLog("Conn", "close",
|
|
5578
|
+
this.callbacks.debugLog("Conn", "close", targetId);
|
|
5088
5579
|
clearTimeout(timeout);
|
|
5089
|
-
reject(new Error("
|
|
5580
|
+
reject(new Error("Connection closed"));
|
|
5090
5581
|
});
|
|
5091
5582
|
}).catch(reject);
|
|
5092
5583
|
});
|
|
5093
5584
|
}
|
|
5585
|
+
/**
|
|
5586
|
+
* 转发中继请求到下一个节点
|
|
5587
|
+
* @param nextHop 下一跳节点 ID
|
|
5588
|
+
* @param message 要转发的消息
|
|
5589
|
+
* @returns 响应数据
|
|
5590
|
+
*/
|
|
5591
|
+
async forwardRelay(nextHop, message) {
|
|
5592
|
+
return this.createConnectionAndSend(
|
|
5593
|
+
nextHop,
|
|
5594
|
+
message,
|
|
5595
|
+
(response) => {
|
|
5596
|
+
if (response.type === "relay-response" && response.response) {
|
|
5597
|
+
return response.response;
|
|
5598
|
+
}
|
|
5599
|
+
return null;
|
|
5600
|
+
}
|
|
5601
|
+
);
|
|
5602
|
+
}
|
|
5094
5603
|
/**
|
|
5095
5604
|
* 转发到最终目标节点(当没有更多中继节点时使用)
|
|
5096
5605
|
* @param targetId 目标节点 ID
|
|
@@ -5100,65 +5609,37 @@ var PeerJsHttpUtil = (() => {
|
|
|
5100
5609
|
*/
|
|
5101
5610
|
async forwardToTarget(targetId, request, originalMessage) {
|
|
5102
5611
|
const myPeerId = this.callbacks.getMyPeerId();
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5612
|
+
const currentTTL = originalMessage.ttl ?? DEFAULT_TTL;
|
|
5613
|
+
const message = {
|
|
5614
|
+
type: "relay-request",
|
|
5615
|
+
id: originalMessage.id,
|
|
5616
|
+
originalTarget: originalMessage.originalTarget,
|
|
5617
|
+
relayPath: [...originalMessage.relayPath, myPeerId],
|
|
5618
|
+
forwardPath: [],
|
|
5619
|
+
ttl: currentTTL - 1,
|
|
5620
|
+
request
|
|
5621
|
+
};
|
|
5622
|
+
const response = await this.createConnectionAndSend(
|
|
5623
|
+
targetId,
|
|
5624
|
+
message,
|
|
5625
|
+
(resp) => {
|
|
5626
|
+
if (resp.type === "relay-response" && resp.response) {
|
|
5627
|
+
return resp.response;
|
|
5110
5628
|
}
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
}, 3e4);
|
|
5116
|
-
conn.on("open", () => {
|
|
5117
|
-
this.callbacks.debugLog("Conn", "open", targetId);
|
|
5118
|
-
const message = {
|
|
5119
|
-
type: "relay-request",
|
|
5120
|
-
id: originalMessage.id,
|
|
5121
|
-
originalTarget: originalMessage.originalTarget,
|
|
5122
|
-
relayPath: [...originalMessage.relayPath, myPeerId],
|
|
5123
|
-
forwardPath: [],
|
|
5124
|
-
request
|
|
5125
|
-
};
|
|
5126
|
-
conn.send(message);
|
|
5127
|
-
});
|
|
5128
|
-
conn.on("data", (responseData) => {
|
|
5129
|
-
const response = responseData;
|
|
5130
|
-
if (response.type === "relay-response") {
|
|
5131
|
-
clearTimeout(timeout);
|
|
5132
|
-
conn.close();
|
|
5133
|
-
if (response.response) {
|
|
5134
|
-
resolve(response.response.data);
|
|
5135
|
-
} else {
|
|
5136
|
-
reject(new Error("Invalid relay response"));
|
|
5137
|
-
}
|
|
5138
|
-
}
|
|
5139
|
-
});
|
|
5140
|
-
conn.on("error", (err) => {
|
|
5141
|
-
this.callbacks.debugLog("Conn", "error", { peer: targetId, error: err });
|
|
5142
|
-
clearTimeout(timeout);
|
|
5143
|
-
reject(err);
|
|
5144
|
-
});
|
|
5145
|
-
conn.on("close", () => {
|
|
5146
|
-
this.callbacks.debugLog("Conn", "close", targetId);
|
|
5147
|
-
clearTimeout(timeout);
|
|
5148
|
-
reject(new Error("Forward connection closed"));
|
|
5149
|
-
});
|
|
5150
|
-
}).catch(reject);
|
|
5151
|
-
});
|
|
5629
|
+
return null;
|
|
5630
|
+
}
|
|
5631
|
+
);
|
|
5632
|
+
return response.data;
|
|
5152
5633
|
}
|
|
5153
5634
|
};
|
|
5154
5635
|
|
|
5155
5636
|
// src/PeerJsWrapper.ts
|
|
5156
|
-
var VERSION = "1.
|
|
5637
|
+
var VERSION = "1.2.0";
|
|
5157
5638
|
console.log(`[sx-peerjs-http-util] v${VERSION}`);
|
|
5158
5639
|
function generateUUID() {
|
|
5159
5640
|
return crypto.randomUUID();
|
|
5160
5641
|
}
|
|
5161
|
-
var PeerJsWrapper = class {
|
|
5642
|
+
var PeerJsWrapper = class _PeerJsWrapper {
|
|
5162
5643
|
/** 本地 Peer ID */
|
|
5163
5644
|
myPeerId;
|
|
5164
5645
|
/** PeerJS 实例 */
|
|
@@ -5182,7 +5663,7 @@ var PeerJsHttpUtil = (() => {
|
|
|
5182
5663
|
/** 来电监听器集合 */
|
|
5183
5664
|
incomingCallListeners = /* @__PURE__ */ new Set();
|
|
5184
5665
|
/** 路由管理器 */
|
|
5185
|
-
|
|
5666
|
+
router;
|
|
5186
5667
|
/** 消息处理器 */
|
|
5187
5668
|
messageHandler;
|
|
5188
5669
|
/**
|
|
@@ -5199,17 +5680,32 @@ var PeerJsHttpUtil = (() => {
|
|
|
5199
5680
|
const callbacks = {
|
|
5200
5681
|
getMyPeerId: () => this.myPeerId,
|
|
5201
5682
|
getPeerInstance: () => this.peerInstance,
|
|
5202
|
-
debugLog: this.debugLog.bind(this)
|
|
5683
|
+
debugLog: this.debugLog.bind(this),
|
|
5684
|
+
sendRelayMessage: (targetId, message) => this.sendRelayMessage(targetId, message)
|
|
5203
5685
|
};
|
|
5204
|
-
this.
|
|
5686
|
+
this.router = new Router(callbacks, relayConfig);
|
|
5687
|
+
this.router.init();
|
|
5205
5688
|
this.messageHandler = new MessageHandler({
|
|
5206
5689
|
...callbacks,
|
|
5207
5690
|
waitForReady: () => this.waitForReady(),
|
|
5208
5691
|
getSimpleHandlers: () => this.simpleHandlers,
|
|
5209
|
-
onRouteUpdate: (fromPeerId, message) => this.
|
|
5692
|
+
onRouteUpdate: (fromPeerId, message) => this.router.handleRouteUpdate(fromPeerId, message)
|
|
5210
5693
|
});
|
|
5211
5694
|
this.connect();
|
|
5212
5695
|
}
|
|
5696
|
+
/**
|
|
5697
|
+
* 创建实例并等待就绪(语法糖)
|
|
5698
|
+
* @param peerId 可选的 Peer ID
|
|
5699
|
+
* @param isDebug 是否开启调试模式
|
|
5700
|
+
* @param server 可选的信令服务器配置
|
|
5701
|
+
* @param relayConfig 可选的中继配置
|
|
5702
|
+
* @returns Promise<PeerJsWrapper>
|
|
5703
|
+
*/
|
|
5704
|
+
static async create(peerId, isDebug, server, relayConfig) {
|
|
5705
|
+
const wrapper = new _PeerJsWrapper(peerId, isDebug, server, relayConfig);
|
|
5706
|
+
await wrapper.whenReady();
|
|
5707
|
+
return wrapper;
|
|
5708
|
+
}
|
|
5213
5709
|
debugLog(obj, event, data) {
|
|
5214
5710
|
if (this.isDebug) {
|
|
5215
5711
|
console.log(obj, event, data);
|
|
@@ -5253,7 +5749,7 @@ var PeerJsHttpUtil = (() => {
|
|
|
5253
5749
|
this.reconnectTimer = setTimeout(() => {
|
|
5254
5750
|
this.reconnectTimer = null;
|
|
5255
5751
|
this.reconnect();
|
|
5256
|
-
},
|
|
5752
|
+
}, RECONNECT_DELAY_MS);
|
|
5257
5753
|
}
|
|
5258
5754
|
reconnect() {
|
|
5259
5755
|
if (this.isDestroyed) return;
|
|
@@ -5297,32 +5793,149 @@ var PeerJsHttpUtil = (() => {
|
|
|
5297
5793
|
this.peerInstance.on("error", onError);
|
|
5298
5794
|
});
|
|
5299
5795
|
}
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5796
|
+
getRoutingTable() {
|
|
5797
|
+
return this.router.getRoutingTable();
|
|
5798
|
+
}
|
|
5799
|
+
getKnownNodes() {
|
|
5800
|
+
return this.router.getKnownNodes();
|
|
5801
|
+
}
|
|
5802
|
+
/**
|
|
5803
|
+
* 发送中继消息的辅助方法
|
|
5804
|
+
* @param targetId 目标节点 ID
|
|
5805
|
+
* @param message 中继消息
|
|
5806
|
+
*/
|
|
5807
|
+
sendRelayMessage(targetId, message) {
|
|
5305
5808
|
return new Promise((resolve, reject) => {
|
|
5306
|
-
this.
|
|
5809
|
+
if (!this.peerInstance) {
|
|
5810
|
+
reject(new Error("Peer instance not available"));
|
|
5811
|
+
return;
|
|
5812
|
+
}
|
|
5813
|
+
const conn = this.peerInstance.connect(targetId, { reliable: true });
|
|
5814
|
+
const timeout = setTimeout(() => {
|
|
5815
|
+
conn.close();
|
|
5816
|
+
reject(new Error(`Send to ${targetId} timeout`));
|
|
5817
|
+
}, SEND_TIMEOUT_MS);
|
|
5818
|
+
conn.on("open", () => {
|
|
5819
|
+
conn.send(message);
|
|
5820
|
+
clearTimeout(timeout);
|
|
5821
|
+
conn.close();
|
|
5822
|
+
resolve();
|
|
5823
|
+
});
|
|
5824
|
+
conn.on("error", () => {
|
|
5825
|
+
clearTimeout(timeout);
|
|
5826
|
+
reject(new Error(`Send to ${targetId} failed`));
|
|
5827
|
+
});
|
|
5828
|
+
conn.on("close", () => {
|
|
5829
|
+
clearTimeout(timeout);
|
|
5830
|
+
resolve();
|
|
5831
|
+
});
|
|
5832
|
+
});
|
|
5833
|
+
}
|
|
5834
|
+
/**
|
|
5835
|
+
* 尝试直连目标节点
|
|
5836
|
+
* @param targetId 目标节点 ID
|
|
5837
|
+
* @param path 请求路径
|
|
5838
|
+
* @param data 请求数据
|
|
5839
|
+
* @param requestId 请求 ID
|
|
5840
|
+
* @returns Promise<unknown> - 响应数据
|
|
5841
|
+
*/
|
|
5842
|
+
tryDirectConnect(targetId, path, data, requestId) {
|
|
5843
|
+
return new Promise((resolve, reject) => {
|
|
5844
|
+
if (!this.peerInstance) {
|
|
5845
|
+
reject(new Error("Peer instance not available"));
|
|
5846
|
+
return;
|
|
5847
|
+
}
|
|
5848
|
+
const startTime = Date.now();
|
|
5849
|
+
const conn = this.peerInstance.connect(targetId, { reliable: true });
|
|
5850
|
+
let resolved = false;
|
|
5851
|
+
const timeout = setTimeout(() => {
|
|
5852
|
+
if (!resolved) {
|
|
5853
|
+
resolved = true;
|
|
5854
|
+
conn.close();
|
|
5855
|
+
reject(new Error(`Request timeout: ${targetId}${path}`));
|
|
5856
|
+
}
|
|
5857
|
+
}, CONNECTION_TIMEOUT_MS);
|
|
5858
|
+
conn.on("open", () => {
|
|
5859
|
+
const latency = Date.now() - startTime;
|
|
5860
|
+
this.debugLog("Conn", "open", { peer: targetId, latency });
|
|
5861
|
+
const request = { path, data };
|
|
5862
|
+
const message = {
|
|
5863
|
+
type: "request",
|
|
5864
|
+
id: requestId,
|
|
5865
|
+
request
|
|
5866
|
+
};
|
|
5867
|
+
conn.send(message);
|
|
5868
|
+
});
|
|
5869
|
+
conn.on("data", (responseData) => {
|
|
5870
|
+
this.debugLog("Conn", "data", { peer: targetId, data: responseData });
|
|
5871
|
+
const message = responseData;
|
|
5872
|
+
if (message.type === "response" && message.id === requestId) {
|
|
5873
|
+
if (!resolved) {
|
|
5874
|
+
resolved = true;
|
|
5875
|
+
clearTimeout(timeout);
|
|
5876
|
+
const response = message.response;
|
|
5877
|
+
if (response.status < 200 || response.status >= 300) {
|
|
5878
|
+
conn.close();
|
|
5879
|
+
reject(new Error(`Request failed: ${response.status} ${JSON.stringify(response.data)}`));
|
|
5880
|
+
} else {
|
|
5881
|
+
const latency = Date.now() - startTime;
|
|
5882
|
+
this.router.recordDirectNode(targetId, latency);
|
|
5883
|
+
this.router.broadcastRouteUpdate();
|
|
5884
|
+
resolve(response.data);
|
|
5885
|
+
}
|
|
5886
|
+
}
|
|
5887
|
+
}
|
|
5888
|
+
});
|
|
5889
|
+
conn.on("error", (err) => {
|
|
5890
|
+
if (!resolved) {
|
|
5891
|
+
resolved = true;
|
|
5892
|
+
clearTimeout(timeout);
|
|
5893
|
+
this.debugLog("Conn", "error", { peer: targetId, error: err });
|
|
5894
|
+
reject(err);
|
|
5895
|
+
}
|
|
5896
|
+
});
|
|
5897
|
+
conn.on("close", () => {
|
|
5898
|
+
if (!resolved) {
|
|
5899
|
+
resolved = true;
|
|
5900
|
+
clearTimeout(timeout);
|
|
5901
|
+
this.debugLog("Conn", "close", targetId);
|
|
5902
|
+
reject(new Error("Connection closed"));
|
|
5903
|
+
}
|
|
5904
|
+
});
|
|
5905
|
+
});
|
|
5906
|
+
}
|
|
5907
|
+
/**
|
|
5908
|
+
* 通过中继节点转发请求
|
|
5909
|
+
* @param nextHopId 下一跳节点 ID
|
|
5910
|
+
* @param targetId 原始目标节点 ID
|
|
5911
|
+
* @param path 请求路径
|
|
5912
|
+
* @param data 请求数据
|
|
5913
|
+
* @returns Promise<unknown> - 响应数据
|
|
5914
|
+
*/
|
|
5915
|
+
relayVia(nextHopId, targetId, path, data) {
|
|
5916
|
+
return new Promise((resolve, reject) => {
|
|
5917
|
+
this.debugLog("PeerJsWrapper", "relayVia", { targetId, nextHop: nextHopId });
|
|
5307
5918
|
this.waitForReady().then(() => {
|
|
5308
5919
|
if (!this.peerInstance) {
|
|
5309
5920
|
reject(new Error("Peer instance not available"));
|
|
5310
5921
|
return;
|
|
5311
5922
|
}
|
|
5312
|
-
const conn = this.peerInstance.connect(
|
|
5923
|
+
const conn = this.peerInstance.connect(nextHopId, { reliable: true });
|
|
5924
|
+
const startTime = Date.now();
|
|
5313
5925
|
const timeout = setTimeout(() => {
|
|
5314
5926
|
conn.close();
|
|
5315
|
-
reject(new Error(`Relay timeout: ${
|
|
5316
|
-
},
|
|
5927
|
+
reject(new Error(`Relay timeout: ${nextHopId}${path}`));
|
|
5928
|
+
}, CONNECTION_TIMEOUT_MS);
|
|
5317
5929
|
conn.on("open", () => {
|
|
5318
|
-
this.debugLog("Conn", "open",
|
|
5930
|
+
this.debugLog("Conn", "open", nextHopId);
|
|
5319
5931
|
const request = { path, data };
|
|
5320
5932
|
const message = {
|
|
5321
5933
|
type: "relay-request",
|
|
5322
5934
|
id: `${this.myPeerId}-${Date.now()}-${Math.random()}`,
|
|
5323
5935
|
originalTarget: targetId,
|
|
5324
|
-
relayPath: [],
|
|
5325
|
-
forwardPath:
|
|
5936
|
+
relayPath: [this.myPeerId],
|
|
5937
|
+
forwardPath: [],
|
|
5938
|
+
ttl: DEFAULT_TTL,
|
|
5326
5939
|
request
|
|
5327
5940
|
};
|
|
5328
5941
|
conn.send(message);
|
|
@@ -5337,102 +5950,204 @@ var PeerJsHttpUtil = (() => {
|
|
|
5337
5950
|
if (response.status < 200 || response.status >= 300) {
|
|
5338
5951
|
reject(new Error(`Relay failed: ${response.status} ${JSON.stringify(response.data)}`));
|
|
5339
5952
|
} else {
|
|
5340
|
-
|
|
5341
|
-
this.
|
|
5953
|
+
const latency = Date.now() - startTime;
|
|
5954
|
+
this.router.recordDirectNode(nextHopId, latency);
|
|
5955
|
+
this.router.broadcastRouteUpdate();
|
|
5342
5956
|
resolve(response.data);
|
|
5343
5957
|
}
|
|
5344
5958
|
}
|
|
5345
5959
|
} else if (message.type === "route-update") {
|
|
5346
|
-
this.
|
|
5960
|
+
this.router.handleRouteUpdate(nextHopId, message);
|
|
5961
|
+
} else if (message.type === "route-query") {
|
|
5962
|
+
this.router.handleRouteQuery(nextHopId, message);
|
|
5963
|
+
} else if (message.type === "route-response") {
|
|
5964
|
+
this.router.handleRouteResponse(nextHopId, message);
|
|
5347
5965
|
}
|
|
5348
5966
|
});
|
|
5349
5967
|
conn.on("error", (err) => {
|
|
5350
|
-
this.debugLog("Conn", "error", { peer:
|
|
5968
|
+
this.debugLog("Conn", "error", { peer: nextHopId, error: err });
|
|
5351
5969
|
clearTimeout(timeout);
|
|
5352
5970
|
reject(err);
|
|
5353
5971
|
});
|
|
5354
5972
|
conn.on("close", () => {
|
|
5355
|
-
this.debugLog("Conn", "close",
|
|
5973
|
+
this.debugLog("Conn", "close", nextHopId);
|
|
5356
5974
|
clearTimeout(timeout);
|
|
5357
5975
|
reject(new Error("Relay connection closed"));
|
|
5358
5976
|
});
|
|
5359
5977
|
}).catch(reject);
|
|
5360
5978
|
});
|
|
5361
5979
|
}
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
5980
|
+
/**
|
|
5981
|
+
* 自动路由发送
|
|
5982
|
+
*
|
|
5983
|
+
* 1. 查路由表 → 有路由 → 尝试中继 → 全部失败 → 降级直连 → 失败 → 结束
|
|
5984
|
+
* 2. 路由表无目标 → 直连 → 失败 → 结束
|
|
5985
|
+
* @param peerId 目标节点 ID
|
|
5986
|
+
* @param path 请求路径
|
|
5987
|
+
* @param data 请求数据
|
|
5988
|
+
* @returns Promise<unknown> - 响应数据
|
|
5989
|
+
*/
|
|
5368
5990
|
send(peerId, path, data) {
|
|
5991
|
+
const requestId = `${this.myPeerId}-${Date.now()}-${Math.random()}`;
|
|
5369
5992
|
return new Promise((resolve, reject) => {
|
|
5370
5993
|
this.debugLog("PeerJsWrapper", "send", { peerId, path, data });
|
|
5994
|
+
const nextHops = this.router.getNextHopsToTarget(peerId);
|
|
5995
|
+
if (nextHops.length > 0) {
|
|
5996
|
+
this.tryRelayChain(peerId, path, data, nextHops, 0).then(resolve).catch((relayErr) => {
|
|
5997
|
+
this.debugLog("PeerJsWrapper", "relayFailed", { peerId, error: relayErr.message });
|
|
5998
|
+
this.fallbackToDirect(peerId, path, data, requestId).then(resolve).catch(reject);
|
|
5999
|
+
});
|
|
6000
|
+
} else {
|
|
6001
|
+
this.tryDirectConnect(peerId, path, data, requestId).then(resolve).catch((directErr) => {
|
|
6002
|
+
this.debugLog("PeerJsWrapper", "directFailed", { peerId, error: directErr.message });
|
|
6003
|
+
this.handleSendError(peerId, directErr).then(resolve).catch(reject);
|
|
6004
|
+
});
|
|
6005
|
+
}
|
|
6006
|
+
});
|
|
6007
|
+
}
|
|
6008
|
+
/**
|
|
6009
|
+
* 处理发送错误
|
|
6010
|
+
* @param peerId 目标节点 ID
|
|
6011
|
+
* @param error 错误对象
|
|
6012
|
+
* @returns Promise
|
|
6013
|
+
*/
|
|
6014
|
+
async handleSendError(peerId, error) {
|
|
6015
|
+
const errorMsg = error.message;
|
|
6016
|
+
const isHttpError = errorMsg.includes("Request failed:") || errorMsg.includes("404") || errorMsg.includes("500");
|
|
6017
|
+
const isConnectionError = errorMsg.includes("timeout") || errorMsg.includes("Connection closed") || errorMsg.includes("Peer instance not available");
|
|
6018
|
+
if (isHttpError) {
|
|
6019
|
+
throw error;
|
|
6020
|
+
}
|
|
6021
|
+
if (!isConnectionError) {
|
|
6022
|
+
throw error;
|
|
6023
|
+
}
|
|
6024
|
+
this.router.removeRoute(peerId);
|
|
6025
|
+
this.debugLog("PeerJsWrapper", "routeRemoved", peerId);
|
|
6026
|
+
if (!this.router.isRoutingTableEmpty()) {
|
|
6027
|
+
return this.performRouteDiscovery(peerId, "", void 0, "");
|
|
6028
|
+
}
|
|
6029
|
+
throw new Error(`Cannot reach ${peerId}: no route found and routing table is empty`);
|
|
6030
|
+
}
|
|
6031
|
+
/**
|
|
6032
|
+
* 降级到直连尝试
|
|
6033
|
+
* @param peerId 目标节点 ID
|
|
6034
|
+
* @param path 请求路径
|
|
6035
|
+
* @param data 请求数据
|
|
6036
|
+
* @param requestId 请求 ID
|
|
6037
|
+
* @returns Promise
|
|
6038
|
+
*/
|
|
6039
|
+
async fallbackToDirect(peerId, path, data, requestId) {
|
|
6040
|
+
this.debugLog("PeerJsWrapper", "fallbackToDirect", peerId);
|
|
6041
|
+
return this.tryDirectConnect(peerId, path, data, requestId).catch((directErr) => {
|
|
6042
|
+
this.debugLog("PeerJsWrapper", "directFailed", { peerId, error: directErr.message });
|
|
6043
|
+
this.handleSendError(peerId, directErr);
|
|
6044
|
+
});
|
|
6045
|
+
}
|
|
6046
|
+
/**
|
|
6047
|
+
* 尝试通过中继链转发
|
|
6048
|
+
* @param targetId 目标节点 ID
|
|
6049
|
+
* @param path 请求路径
|
|
6050
|
+
* @param data 请求数据
|
|
6051
|
+
* @param nextHops 下一跳列表
|
|
6052
|
+
* @param index 当前尝试的下一跳索引
|
|
6053
|
+
* @returns Promise<unknown>
|
|
6054
|
+
*/
|
|
6055
|
+
tryRelayChain(targetId, path, data, nextHops, index) {
|
|
6056
|
+
if (index >= nextHops.length) {
|
|
6057
|
+
return Promise.reject(new Error("All relay nodes failed"));
|
|
6058
|
+
}
|
|
6059
|
+
const nextHop = nextHops[index];
|
|
6060
|
+
this.debugLog("PeerJsWrapper", "tryRelay", { targetId, nextHop: nextHop.nodeId, latency: nextHop.latency });
|
|
6061
|
+
return this.relayVia(nextHop.nodeId, targetId, path, data).catch(() => {
|
|
6062
|
+
return this.tryRelayChain(targetId, path, data, nextHops, index + 1);
|
|
6063
|
+
});
|
|
6064
|
+
}
|
|
6065
|
+
/**
|
|
6066
|
+
* 执行路由发现
|
|
6067
|
+
* @param targetId 目标节点 ID
|
|
6068
|
+
* @param path 请求路径
|
|
6069
|
+
* @param data 请求数据
|
|
6070
|
+
* @param requestId 请求 ID
|
|
6071
|
+
* @returns Promise<unknown>
|
|
6072
|
+
*/
|
|
6073
|
+
async performRouteDiscovery(targetId, path, data, requestId) {
|
|
6074
|
+
this.debugLog("PeerJsWrapper", "routeDiscovery", { targetId });
|
|
6075
|
+
const routeEntry = await this.router.discoverRoute(targetId);
|
|
6076
|
+
if (!routeEntry || routeEntry.nextHops.length === 0) {
|
|
6077
|
+
throw new Error(`Cannot reach ${targetId}: no route found`);
|
|
6078
|
+
}
|
|
6079
|
+
this.debugLog("PeerJsWrapper", "routeFound", { targetId, nextHops: routeEntry.nextHops });
|
|
6080
|
+
return this.tryRelayChain(targetId, path, data, routeEntry.nextHops, 0);
|
|
6081
|
+
}
|
|
6082
|
+
/**
|
|
6083
|
+
* 中继发送(内部方法,不对外暴露)
|
|
6084
|
+
* @param targetId 目标节点 ID
|
|
6085
|
+
* @param path 请求路径
|
|
6086
|
+
* @param data 请求数据
|
|
6087
|
+
* @param relayNodes 手动指定的中继节点(可选,不指定则自动路由)
|
|
6088
|
+
* @returns Promise<unknown>
|
|
6089
|
+
*/
|
|
6090
|
+
relaySend(targetId, path, data, relayNodes) {
|
|
6091
|
+
if (!relayNodes || relayNodes.length === 0) {
|
|
6092
|
+
return this.send(targetId, path, data);
|
|
6093
|
+
}
|
|
6094
|
+
const [firstRelay, ...remainingRelays] = relayNodes;
|
|
6095
|
+
return new Promise((resolve, reject) => {
|
|
6096
|
+
this.debugLog("PeerJsWrapper", "relaySend", { targetId, firstRelay, remainingRelays });
|
|
5371
6097
|
this.waitForReady().then(() => {
|
|
5372
6098
|
if (!this.peerInstance) {
|
|
5373
6099
|
reject(new Error("Peer instance not available"));
|
|
5374
6100
|
return;
|
|
5375
6101
|
}
|
|
5376
|
-
const conn = this.peerInstance.connect(
|
|
6102
|
+
const conn = this.peerInstance.connect(firstRelay, { reliable: true });
|
|
5377
6103
|
const timeout = setTimeout(() => {
|
|
5378
6104
|
conn.close();
|
|
5379
|
-
reject(new Error(`
|
|
5380
|
-
},
|
|
5381
|
-
const requestId = `${this.myPeerId}-${Date.now()}-${Math.random()}`;
|
|
5382
|
-
this.pendingRequests.set(requestId, { resolve, reject, timeout });
|
|
6105
|
+
reject(new Error(`Relay timeout: ${firstRelay}${path}`));
|
|
6106
|
+
}, CONNECTION_TIMEOUT_MS);
|
|
5383
6107
|
conn.on("open", () => {
|
|
5384
|
-
this.debugLog("Conn", "open",
|
|
6108
|
+
this.debugLog("Conn", "open", firstRelay);
|
|
5385
6109
|
const request = { path, data };
|
|
5386
6110
|
const message = {
|
|
5387
|
-
type: "request",
|
|
5388
|
-
id:
|
|
6111
|
+
type: "relay-request",
|
|
6112
|
+
id: `${this.myPeerId}-${Date.now()}-${Math.random()}`,
|
|
6113
|
+
originalTarget: targetId,
|
|
6114
|
+
relayPath: [],
|
|
6115
|
+
forwardPath: remainingRelays,
|
|
6116
|
+
ttl: DEFAULT_TTL,
|
|
5389
6117
|
request
|
|
5390
6118
|
};
|
|
5391
6119
|
conn.send(message);
|
|
5392
6120
|
});
|
|
5393
6121
|
conn.on("data", (responseData) => {
|
|
5394
|
-
this.debugLog("Conn", "data", { peer: peerId, data: responseData });
|
|
5395
6122
|
const message = responseData;
|
|
5396
|
-
if (message.type === "response"
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
const response = message.response;
|
|
6123
|
+
if (message.type === "relay-response") {
|
|
6124
|
+
clearTimeout(timeout);
|
|
6125
|
+
conn.close();
|
|
6126
|
+
const response = message.response;
|
|
6127
|
+
if (response) {
|
|
5402
6128
|
if (response.status < 200 || response.status >= 300) {
|
|
5403
|
-
|
|
5404
|
-
new Error(`Request failed: ${response.status} ${JSON.stringify(response.data)}`)
|
|
5405
|
-
);
|
|
6129
|
+
reject(new Error(`Relay failed: ${response.status} ${JSON.stringify(response.data)}`));
|
|
5406
6130
|
} else {
|
|
5407
|
-
this.
|
|
5408
|
-
this.
|
|
5409
|
-
|
|
6131
|
+
this.router.recordSuccessfulNode(firstRelay);
|
|
6132
|
+
this.router.broadcastRouteUpdate();
|
|
6133
|
+
resolve(response.data);
|
|
5410
6134
|
}
|
|
5411
6135
|
}
|
|
5412
|
-
|
|
6136
|
+
} else if (message.type === "route-update") {
|
|
6137
|
+
this.router.handleRouteUpdate(firstRelay, message);
|
|
5413
6138
|
}
|
|
5414
6139
|
});
|
|
5415
6140
|
conn.on("error", (err) => {
|
|
5416
|
-
this.debugLog("Conn", "error", { peer:
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
clearTimeout(pending.timeout);
|
|
5420
|
-
this.pendingRequests.delete(requestId);
|
|
5421
|
-
pending.reject(err);
|
|
5422
|
-
}
|
|
6141
|
+
this.debugLog("Conn", "error", { peer: firstRelay, error: err });
|
|
6142
|
+
clearTimeout(timeout);
|
|
6143
|
+
reject(err);
|
|
5423
6144
|
});
|
|
5424
6145
|
conn.on("close", () => {
|
|
5425
|
-
this.debugLog("Conn", "close",
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
clearTimeout(pending.timeout);
|
|
5429
|
-
this.pendingRequests.delete(requestId);
|
|
5430
|
-
pending.reject(new Error("Connection closed"));
|
|
5431
|
-
}
|
|
6146
|
+
this.debugLog("Conn", "close", firstRelay);
|
|
6147
|
+
clearTimeout(timeout);
|
|
6148
|
+
reject(new Error("Relay connection closed"));
|
|
5432
6149
|
});
|
|
5433
|
-
}).catch(
|
|
5434
|
-
reject(err);
|
|
5435
|
-
});
|
|
6150
|
+
}).catch(reject);
|
|
5436
6151
|
});
|
|
5437
6152
|
}
|
|
5438
6153
|
setupIncomingConnectionHandler() {
|
|
@@ -5499,7 +6214,7 @@ var PeerJsHttpUtil = (() => {
|
|
|
5499
6214
|
}
|
|
5500
6215
|
}
|
|
5501
6216
|
} else if (message.type === "route-update") {
|
|
5502
|
-
this.
|
|
6217
|
+
this.router.handleRouteUpdate(conn.peer, message);
|
|
5503
6218
|
}
|
|
5504
6219
|
});
|
|
5505
6220
|
conn.on("close", () => {
|
|
@@ -5687,6 +6402,10 @@ var PeerJsHttpUtil = (() => {
|
|
|
5687
6402
|
this.peerInstance.destroy();
|
|
5688
6403
|
this.peerInstance = null;
|
|
5689
6404
|
}
|
|
6405
|
+
if (this.router) {
|
|
6406
|
+
this.router.persist();
|
|
6407
|
+
this.router.destroy();
|
|
6408
|
+
}
|
|
5690
6409
|
}
|
|
5691
6410
|
};
|
|
5692
6411
|
return __toCommonJS(index_exports);
|