egos-transfer 0.2.2 → 0.2.4
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/CLAUDE.md +79 -0
- package/dist/handler.js +2 -1
- package/dist/index.d.ts +34 -8
- package/dist/index.js +314 -199
- package/dist/router.js +9 -9
- package/package.json +1 -1
- package/src/handler.ts +1 -1
- package/src/index.ts +369 -207
- package/src/router.ts +9 -9
package/dist/index.js
CHANGED
|
@@ -33,22 +33,32 @@ const entity_1 = require("./entity");
|
|
|
33
33
|
const router_1 = require("./router");
|
|
34
34
|
const axios_1 = __importDefault(require("axios"));
|
|
35
35
|
const delay_1 = __importDefault(require("delay"));
|
|
36
|
+
/** 重连退避相关常量 */
|
|
37
|
+
const MAX_RECONNECT_BACKOFF = 30000; // 最大退避 30s
|
|
38
|
+
const BASE_RECONNECT_BACKOFF = 1000; // 基础退避 1s
|
|
36
39
|
class PeerTransfer {
|
|
37
|
-
constructor(deviceId, config,
|
|
38
|
-
this.
|
|
40
|
+
constructor(deviceId, config, isRandomPeerId = false) {
|
|
41
|
+
this.checkerId = null;
|
|
42
|
+
/** 重连尝试次数,成功连接后归零 */
|
|
43
|
+
this.reconnectAttempt = 0;
|
|
44
|
+
/** 标记实例已销毁,防止异步操作继续 */
|
|
45
|
+
this.destroyed = false;
|
|
46
|
+
/** 按 requestId 追踪进行中的请求 */
|
|
39
47
|
this.inRequest = new Map();
|
|
40
48
|
this.connectPool = new Map();
|
|
41
49
|
this.peerConfig = Object.assign(Object.assign({}, config), { key: String(Date.now()) });
|
|
42
50
|
this.requests = new Map();
|
|
51
|
+
this.requestTimers = new Map();
|
|
43
52
|
this.timeout = entity_1.REQUEST_TIMEOUT;
|
|
44
53
|
this.router = new router_1.PeerRouter();
|
|
45
54
|
this.isPaused = false;
|
|
46
|
-
this.peerId =
|
|
55
|
+
this.peerId = isRandomPeerId ? deviceId + '_' + Date.now() : deviceId;
|
|
47
56
|
this.deviceId = deviceId;
|
|
48
57
|
}
|
|
49
58
|
createPeer() {
|
|
50
59
|
return __awaiter(this, arguments, void 0, function* (force = false) {
|
|
51
60
|
var _a;
|
|
61
|
+
console.log('createPeer', this.isPaused, force);
|
|
52
62
|
if (this.isPaused && !force) {
|
|
53
63
|
return;
|
|
54
64
|
}
|
|
@@ -65,82 +75,90 @@ class PeerTransfer {
|
|
|
65
75
|
this.peer = peer;
|
|
66
76
|
yield this.initialize(this.peer);
|
|
67
77
|
this.checkConnection();
|
|
78
|
+
this.destroyed = false;
|
|
68
79
|
return this.peer;
|
|
69
80
|
});
|
|
70
81
|
}
|
|
71
82
|
initialize(peer) {
|
|
72
83
|
return __awaiter(this, void 0, void 0, function* () {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
});
|
|
93
|
-
peer.on('disconnected', () => {
|
|
94
|
-
if (!this.isPaused) {
|
|
95
|
-
setTimeout(() => {
|
|
96
|
-
if (!this.isPaused) {
|
|
97
|
-
this.reconnect();
|
|
98
|
-
}
|
|
99
|
-
}, constant_1.CONNECTION_RETRY);
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
// duplicated with close event
|
|
103
|
-
peer.on('close', (...args) => { });
|
|
104
|
-
peer.on('error', (err) => __awaiter(this, void 0, void 0, function* () {
|
|
105
|
-
if (err.message === constant_1.CONNECTION_PEER_CONFLICT) {
|
|
106
|
-
this.stop();
|
|
107
|
-
done(null);
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
if (err.message === constant_1.CONNECTION_LIMIT_EXCEED) {
|
|
111
|
-
this.stop();
|
|
112
|
-
this.onConnectLimit();
|
|
113
|
-
return done(null);
|
|
114
|
-
}
|
|
115
|
-
if (err.message == constant_1.UNAUTHORIZED) {
|
|
116
|
-
done(null);
|
|
117
|
-
return this.requireAuth();
|
|
118
|
-
}
|
|
119
|
-
if (err.type === 'network') {
|
|
120
|
-
done(null);
|
|
121
|
-
return this.reconnect();
|
|
122
|
-
}
|
|
123
|
-
if (err.type === 'server-error') {
|
|
124
|
-
done(null);
|
|
125
|
-
return this.reconnect();
|
|
126
|
-
}
|
|
127
|
-
done(null);
|
|
128
|
-
return this.createPeer(true);
|
|
129
|
-
}));
|
|
130
|
-
});
|
|
131
|
-
peer.on('connection', (conn) => {
|
|
132
|
-
conn.label = 'incoming';
|
|
133
|
-
this.onConnect(conn);
|
|
84
|
+
if (this.checkerId) {
|
|
85
|
+
clearInterval(this.checkerId);
|
|
86
|
+
this.checkerId = null;
|
|
87
|
+
}
|
|
88
|
+
if (this.booted) {
|
|
89
|
+
return this.booted;
|
|
90
|
+
}
|
|
91
|
+
this.booted = new Promise((resolve) => {
|
|
92
|
+
const timer = setTimeout(() => {
|
|
93
|
+
resolve(null);
|
|
94
|
+
}, constant_1.PEER_OPEN_TIMEOUT);
|
|
95
|
+
const done = (p) => {
|
|
96
|
+
clearTimeout(timer);
|
|
97
|
+
resolve(p);
|
|
98
|
+
};
|
|
99
|
+
peer.on('open', (id) => {
|
|
100
|
+
console.log('peer@open');
|
|
101
|
+
this.reconnectAttempt = 0; // 连接成功,重置退避计数
|
|
102
|
+
done(peer);
|
|
134
103
|
});
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
104
|
+
// peer.on('disconnected', () => {
|
|
105
|
+
// if (!this.isPaused) {
|
|
106
|
+
// this.reconnect();
|
|
107
|
+
// }
|
|
108
|
+
// });
|
|
109
|
+
peer.on('close', () => {
|
|
110
|
+
setTimeout(() => {
|
|
111
|
+
if (!this.isPaused && !this.destroyed) {
|
|
112
|
+
this.reconnect();
|
|
113
|
+
}
|
|
114
|
+
}, constant_1.CONNECTION_TIMEOUT);
|
|
138
115
|
});
|
|
139
|
-
|
|
116
|
+
peer.on('error', (err) => __awaiter(this, void 0, void 0, function* () {
|
|
117
|
+
if (err.message === constant_1.CONNECTION_PEER_CONFLICT) {
|
|
118
|
+
this.stop();
|
|
119
|
+
done(null);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (err.message === constant_1.CONNECTION_LIMIT_EXCEED) {
|
|
123
|
+
this.onConnectLimit();
|
|
124
|
+
return done(null);
|
|
125
|
+
}
|
|
126
|
+
if (err.message === constant_1.UNAUTHORIZED) {
|
|
127
|
+
done(null);
|
|
128
|
+
return this.requireAuth();
|
|
129
|
+
}
|
|
130
|
+
if (err.message === constant_1.CONNECTION_DUPLICATED) {
|
|
131
|
+
// 连接级重复错误,不应销毁重建 peer
|
|
132
|
+
// createConnection 重试循环会处理
|
|
133
|
+
console.warn('peer@error: connection duplicated, will retry');
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (err.type === 'network') {
|
|
137
|
+
done(null);
|
|
138
|
+
return this.reconnect();
|
|
139
|
+
}
|
|
140
|
+
if (err.type === 'server-error') {
|
|
141
|
+
done(null);
|
|
142
|
+
return this.reconnect();
|
|
143
|
+
}
|
|
144
|
+
done(null);
|
|
145
|
+
return this.createPeer(true);
|
|
146
|
+
}));
|
|
147
|
+
});
|
|
148
|
+
peer.on('connection', (conn) => {
|
|
149
|
+
conn.label = 'incoming';
|
|
150
|
+
this.onConnect(conn);
|
|
151
|
+
});
|
|
152
|
+
const socket = peer.socket;
|
|
153
|
+
socket === null || socket === void 0 ? void 0 : socket.on('message', (message) => {
|
|
154
|
+
this.handleSocketMessage(message);
|
|
155
|
+
});
|
|
156
|
+
this.onHeartbeat({ type: 'HEARTBEAT' });
|
|
157
|
+
try {
|
|
140
158
|
yield this.booted;
|
|
141
159
|
}
|
|
142
|
-
catch (
|
|
143
|
-
|
|
160
|
+
catch (_a) {
|
|
161
|
+
// booted rejected via timer timeout
|
|
144
162
|
}
|
|
145
163
|
finally {
|
|
146
164
|
this.booted = undefined;
|
|
@@ -158,10 +176,7 @@ class PeerTransfer {
|
|
|
158
176
|
this.onHeartbeat(message);
|
|
159
177
|
}
|
|
160
178
|
onHeartbeat(message) {
|
|
161
|
-
if (message.type !== 'HEARTBEAT') {
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
if (this.isPaused) {
|
|
179
|
+
if (message.type !== 'HEARTBEAT' || this.isPaused) {
|
|
165
180
|
return;
|
|
166
181
|
}
|
|
167
182
|
}
|
|
@@ -180,7 +195,6 @@ class PeerTransfer {
|
|
|
180
195
|
message: 'peer conflict',
|
|
181
196
|
},
|
|
182
197
|
});
|
|
183
|
-
return;
|
|
184
198
|
}
|
|
185
199
|
}
|
|
186
200
|
onOpen(message) {
|
|
@@ -193,32 +207,24 @@ class PeerTransfer {
|
|
|
193
207
|
if (message.type !== 'ONLINE') {
|
|
194
208
|
return;
|
|
195
209
|
}
|
|
196
|
-
|
|
197
|
-
this.online(deviceId);
|
|
210
|
+
this.online(message.payload.deviceId);
|
|
198
211
|
});
|
|
199
212
|
}
|
|
200
213
|
onDeviceOffline(message) {
|
|
201
214
|
if (message.type !== 'OFFLINE') {
|
|
202
215
|
return;
|
|
203
216
|
}
|
|
204
|
-
|
|
205
|
-
this.ping(deviceId);
|
|
217
|
+
this.ping(message.payload.deviceId);
|
|
206
218
|
}
|
|
207
219
|
removeConnection(peerId, connectionId) {
|
|
208
220
|
var _a, _b, _c;
|
|
209
|
-
// console.trace('removeConnection@', peerId, connectionId);
|
|
210
221
|
const conn = this.connectPool.get(peerId);
|
|
211
222
|
if (conn) {
|
|
212
223
|
if (connectionId && conn.connectionId !== connectionId) {
|
|
213
224
|
return;
|
|
214
225
|
}
|
|
215
|
-
const sending = this.inRequest.get(conn.connectionId);
|
|
216
|
-
if (sending) {
|
|
217
|
-
// 提前结束request,避免关闭连接时触发反馈
|
|
218
|
-
sending.feedback();
|
|
219
|
-
this.inRequest.delete(conn.connectionId);
|
|
220
|
-
}
|
|
221
226
|
this.connectPool.delete(peerId);
|
|
227
|
+
this.handleConnectionClose(peerId, conn);
|
|
222
228
|
conn.removeAllListeners();
|
|
223
229
|
conn.close();
|
|
224
230
|
(_a = this.peer) === null || _a === void 0 ? void 0 : _a._removeConnection(conn);
|
|
@@ -227,16 +233,52 @@ class PeerTransfer {
|
|
|
227
233
|
const connections = ((_b = this.peer) === null || _b === void 0 ? void 0 : _b.connections) || {};
|
|
228
234
|
const conns = connections[peerId] || [];
|
|
229
235
|
if (conns.length > 0) {
|
|
230
|
-
for (const
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
(_c = this.peer) === null || _c === void 0 ? void 0 : _c._removeConnection(
|
|
236
|
+
for (const c of conns) {
|
|
237
|
+
c.removeAllListeners();
|
|
238
|
+
c.close();
|
|
239
|
+
(_c = this.peer) === null || _c === void 0 ? void 0 : _c._removeConnection(c);
|
|
234
240
|
}
|
|
235
241
|
}
|
|
236
242
|
}
|
|
237
243
|
}
|
|
244
|
+
/**
|
|
245
|
+
* 连接关闭时尝试快速重连(5s 超时):
|
|
246
|
+
* - 重连成功 → 保留 inRequest 条目,更新 conn 引用
|
|
247
|
+
* - 重连失败 → 触发 feedback 并清理 inRequest
|
|
248
|
+
*/
|
|
249
|
+
handleConnectionClose(peerId, conn) {
|
|
250
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
251
|
+
const matchedEntries = [];
|
|
252
|
+
for (const [reqId, entry] of this.inRequest) {
|
|
253
|
+
if (entry.conn.connectionId === conn.connectionId) {
|
|
254
|
+
matchedEntries.push([reqId, entry]);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (matchedEntries.length === 0) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
// 快速重连,超时 5s
|
|
261
|
+
const newConn = yield Promise.race([
|
|
262
|
+
this.connectToPeer(peerId),
|
|
263
|
+
(0, delay_1.default)(5000).then(() => undefined),
|
|
264
|
+
]);
|
|
265
|
+
if (!this.isValidConnection(newConn)) {
|
|
266
|
+
// 重连失败,触发 feedback 让请求走重试逻辑
|
|
267
|
+
for (const [reqId, entry] of matchedEntries) {
|
|
268
|
+
entry.feedback();
|
|
269
|
+
this.inRequest.delete(reqId);
|
|
270
|
+
}
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
// 重连成功,更新 inRequest 中的 conn 引用
|
|
274
|
+
for (const [reqId, entry] of matchedEntries) {
|
|
275
|
+
if (this.inRequest.has(reqId)) {
|
|
276
|
+
this.inRequest.set(reqId, Object.assign(Object.assign({}, entry), { conn: newConn }));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
}
|
|
238
281
|
addConnection(conn) {
|
|
239
|
-
// console.log('addConnection@', conn.label);
|
|
240
282
|
this.connectPool.set(conn.peer, conn);
|
|
241
283
|
const connecting = PeerTransfer.connecting.get(conn.peer);
|
|
242
284
|
if (connecting) {
|
|
@@ -254,21 +296,24 @@ class PeerTransfer {
|
|
|
254
296
|
}
|
|
255
297
|
// turn credential expires
|
|
256
298
|
if (this.peerConfig.turnExpiredAt && this.peerConfig.turnExpiredAt - 30000 < Date.now()) {
|
|
257
|
-
// this.connectPool.clear();
|
|
258
299
|
yield this.reloadConfig();
|
|
259
300
|
yield this.createPeer(true);
|
|
260
301
|
return;
|
|
261
302
|
}
|
|
262
|
-
const deviceIds = this.connectPool.keys();
|
|
263
|
-
|
|
303
|
+
const deviceIds = Array.from(this.connectPool.keys());
|
|
304
|
+
const pingTasks = deviceIds.map((deviceId) => __awaiter(this, void 0, void 0, function* () {
|
|
264
305
|
const conn = this.connectPool.get(deviceId);
|
|
265
306
|
if (!this.isValidConnection(conn)) {
|
|
266
307
|
this.removeConnection(conn.peer, conn.connectionId);
|
|
308
|
+
return;
|
|
267
309
|
}
|
|
268
|
-
|
|
269
|
-
|
|
310
|
+
const ok = yield this.ping(conn.peer);
|
|
311
|
+
if (!ok) {
|
|
312
|
+
this.removeConnection(conn.peer, conn.connectionId);
|
|
270
313
|
}
|
|
271
|
-
}
|
|
314
|
+
}));
|
|
315
|
+
// 并行执行,不阻塞下个心跳周期
|
|
316
|
+
yield Promise.allSettled(pingTasks);
|
|
272
317
|
}), constant_1.HEARTBEAT_INTERVAL);
|
|
273
318
|
}
|
|
274
319
|
getApiHost() {
|
|
@@ -284,8 +329,7 @@ class PeerTransfer {
|
|
|
284
329
|
const apiHost = this.getApiHost();
|
|
285
330
|
const res = yield axios_1.default.get(`${apiHost}/api/v1/devices/${peerId}/status`);
|
|
286
331
|
if (res.status < 400) {
|
|
287
|
-
|
|
288
|
-
return data.data;
|
|
332
|
+
return res.data.data;
|
|
289
333
|
}
|
|
290
334
|
}
|
|
291
335
|
catch (err) {
|
|
@@ -293,12 +337,11 @@ class PeerTransfer {
|
|
|
293
337
|
}
|
|
294
338
|
});
|
|
295
339
|
}
|
|
296
|
-
connectToPeer(
|
|
340
|
+
connectToPeer(peerId) {
|
|
297
341
|
return __awaiter(this, void 0, void 0, function* () {
|
|
298
|
-
if (
|
|
342
|
+
if (peerId === this.deviceId) {
|
|
299
343
|
return;
|
|
300
344
|
}
|
|
301
|
-
const peerId = deviceId;
|
|
302
345
|
const existingConn = this.connectPool.get(peerId);
|
|
303
346
|
console.log('connectToPeer', this.isPaused, this.connectPool.size);
|
|
304
347
|
if (existingConn) {
|
|
@@ -321,17 +364,22 @@ class PeerTransfer {
|
|
|
321
364
|
}
|
|
322
365
|
createConnection(peerId) {
|
|
323
366
|
return __awaiter(this, void 0, void 0, function* () {
|
|
324
|
-
let resolve;
|
|
325
|
-
let reject;
|
|
326
367
|
const q = PeerTransfer.connecting.get(peerId);
|
|
327
368
|
if (q) {
|
|
328
369
|
return q.promise;
|
|
329
370
|
}
|
|
371
|
+
let resolve;
|
|
372
|
+
let reject;
|
|
330
373
|
const connectionPromise = new Promise((res, rej) => {
|
|
331
374
|
resolve = res;
|
|
332
375
|
reject = rej;
|
|
333
376
|
});
|
|
334
377
|
PeerTransfer.connecting.set(peerId, { promise: connectionPromise, resolve, reject });
|
|
378
|
+
// 兜底超时,防止 connecting 条目永远不清理
|
|
379
|
+
const guardTimer = setTimeout(() => {
|
|
380
|
+
PeerTransfer.connecting.delete(peerId);
|
|
381
|
+
resolve(undefined);
|
|
382
|
+
}, constant_1.CONNECTION_TIMEOUT * 3);
|
|
335
383
|
const doConnect = () => __awaiter(this, void 0, void 0, function* () {
|
|
336
384
|
var _a, _b;
|
|
337
385
|
try {
|
|
@@ -344,6 +392,7 @@ class PeerTransfer {
|
|
|
344
392
|
}
|
|
345
393
|
const cur = this.connectPool.get(peerId);
|
|
346
394
|
if (this.isValidConnection(cur)) {
|
|
395
|
+
clearTimeout(guardTimer);
|
|
347
396
|
return resolve(cur);
|
|
348
397
|
}
|
|
349
398
|
const conn = (_b = this.peer) === null || _b === void 0 ? void 0 : _b.connect(peerId, {
|
|
@@ -355,9 +404,11 @@ class PeerTransfer {
|
|
|
355
404
|
}
|
|
356
405
|
const connection = yield this.onConnect(conn).catch(() => { });
|
|
357
406
|
if (connection) {
|
|
407
|
+
clearTimeout(guardTimer);
|
|
358
408
|
return resolve(connection);
|
|
359
409
|
}
|
|
360
410
|
}
|
|
411
|
+
clearTimeout(guardTimer);
|
|
361
412
|
resolve(undefined);
|
|
362
413
|
return undefined;
|
|
363
414
|
}
|
|
@@ -373,6 +424,9 @@ class PeerTransfer {
|
|
|
373
424
|
return conn;
|
|
374
425
|
});
|
|
375
426
|
}
|
|
427
|
+
/**
|
|
428
|
+
* 心跳检测 - 直接 send,不走 request 管线,避免重试放大
|
|
429
|
+
*/
|
|
376
430
|
ping(peerId) {
|
|
377
431
|
return __awaiter(this, void 0, void 0, function* () {
|
|
378
432
|
if (this.deviceId === peerId) {
|
|
@@ -380,20 +434,37 @@ class PeerTransfer {
|
|
|
380
434
|
}
|
|
381
435
|
const conn = this.connectPool.get(peerId);
|
|
382
436
|
if (!this.isValidConnection(conn)) {
|
|
383
|
-
this.removeConnection(
|
|
437
|
+
this.removeConnection(peerId, conn === null || conn === void 0 ? void 0 : conn.connectionId);
|
|
384
438
|
return;
|
|
385
439
|
}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
440
|
+
try {
|
|
441
|
+
const requestId = yield this.genRequestId();
|
|
442
|
+
return new Promise((resolve) => {
|
|
443
|
+
const timer = setTimeout(() => {
|
|
444
|
+
this.requests.delete(requestId);
|
|
445
|
+
resolve(false);
|
|
446
|
+
}, constant_1.PING_TIMEOUT);
|
|
447
|
+
this.requests.set(requestId, () => {
|
|
448
|
+
clearTimeout(timer);
|
|
449
|
+
resolve(true);
|
|
450
|
+
});
|
|
451
|
+
conn.send({
|
|
452
|
+
route: entity_1.PeerRoutes.HEARTBEAT,
|
|
453
|
+
method: 'get',
|
|
454
|
+
body: {},
|
|
455
|
+
requestId,
|
|
456
|
+
scope: entity_1.PeerScope.REQUEST,
|
|
457
|
+
action: entity_1.PeerAction.SEND,
|
|
458
|
+
src: this.peerId,
|
|
459
|
+
dest: peerId,
|
|
460
|
+
headers: {},
|
|
461
|
+
createdAt: Date.now(),
|
|
462
|
+
});
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
catch (_a) {
|
|
394
466
|
return false;
|
|
395
467
|
}
|
|
396
|
-
return true;
|
|
397
468
|
});
|
|
398
469
|
}
|
|
399
470
|
onConnect(conn) {
|
|
@@ -402,9 +473,9 @@ class PeerTransfer {
|
|
|
402
473
|
return;
|
|
403
474
|
}
|
|
404
475
|
const exists = this.connectPool.get(conn.peer);
|
|
405
|
-
// console.log('onConnect', conn.label, exists);
|
|
406
476
|
if (exists) {
|
|
407
|
-
|
|
477
|
+
// 双方同时建连时,保留 peerId 字典序大的那条(确定性,不依赖时间戳)
|
|
478
|
+
if (exists.connectionId > conn.connectionId) {
|
|
408
479
|
this.removeConnection(conn.peer, conn.connectionId);
|
|
409
480
|
if (this.isValidConnection(exists)) {
|
|
410
481
|
return exists;
|
|
@@ -414,19 +485,24 @@ class PeerTransfer {
|
|
|
414
485
|
this.removeConnection(exists.peer, exists.connectionId);
|
|
415
486
|
}
|
|
416
487
|
}
|
|
417
|
-
|
|
488
|
+
if (conn.open) {
|
|
489
|
+
this.addConnection(conn);
|
|
490
|
+
this.bindConnectionData(conn);
|
|
491
|
+
return conn;
|
|
492
|
+
}
|
|
418
493
|
return new Promise((resolve) => {
|
|
494
|
+
let timer = null;
|
|
419
495
|
const done = (connection) => {
|
|
420
|
-
if (connection) {
|
|
421
|
-
this.addConnection(connection);
|
|
422
|
-
}
|
|
423
496
|
if (timer) {
|
|
424
497
|
clearTimeout(timer);
|
|
425
498
|
timer = null;
|
|
426
499
|
}
|
|
500
|
+
if (connection) {
|
|
501
|
+
this.addConnection(connection);
|
|
502
|
+
}
|
|
427
503
|
resolve(connection);
|
|
428
504
|
};
|
|
429
|
-
timer = setTimeout(() =>
|
|
505
|
+
timer = setTimeout(() => {
|
|
430
506
|
if (this.isValidConnection(conn)) {
|
|
431
507
|
this.addConnection(conn);
|
|
432
508
|
done(conn);
|
|
@@ -435,85 +511,114 @@ class PeerTransfer {
|
|
|
435
511
|
this.removeConnection(conn.peer, conn.connectionId);
|
|
436
512
|
done(undefined);
|
|
437
513
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
conn.on('
|
|
441
|
-
switch (message.scope) {
|
|
442
|
-
case entity_1.PeerScope.REQUEST:
|
|
443
|
-
return this.onRequest(conn, message);
|
|
444
|
-
case entity_1.PeerScope.RESPONSE:
|
|
445
|
-
return this.onResponse(conn, message);
|
|
446
|
-
default:
|
|
447
|
-
return this.responseFail(conn, message, 'unknown scope');
|
|
448
|
-
}
|
|
449
|
-
});
|
|
450
|
-
conn.on('error', (err) => __awaiter(this, void 0, void 0, function* () {
|
|
514
|
+
}, constant_1.CONNECTION_TIMEOUT);
|
|
515
|
+
this.bindConnectionData(conn);
|
|
516
|
+
conn.on('error', () => {
|
|
451
517
|
this.removeConnection(conn.peer, conn.connectionId);
|
|
452
|
-
// if (!this.peer?.open) {
|
|
453
|
-
// await this.reconnect();
|
|
454
|
-
// }
|
|
455
518
|
done(null);
|
|
456
|
-
})
|
|
457
|
-
conn.on('close', () =>
|
|
519
|
+
});
|
|
520
|
+
conn.on('close', () => {
|
|
458
521
|
console.log('close------------>', conn.label);
|
|
459
|
-
clearTimeout(timer);
|
|
460
522
|
this.removeConnection(conn.peer, conn.connectionId);
|
|
461
|
-
// if (!this.peer?.open) {
|
|
462
|
-
// await this.reconnect();
|
|
463
|
-
// }
|
|
464
523
|
done(null);
|
|
465
|
-
})
|
|
466
|
-
conn.on('open', () =>
|
|
467
|
-
clearTimeout(timer);
|
|
468
|
-
done(conn);
|
|
469
|
-
}));
|
|
470
|
-
if (this.isValidConnection(conn)) {
|
|
471
|
-
if (timer) {
|
|
472
|
-
clearTimeout(timer);
|
|
473
|
-
}
|
|
474
|
-
timer = null;
|
|
524
|
+
});
|
|
525
|
+
conn.on('open', () => {
|
|
475
526
|
done(conn);
|
|
476
|
-
}
|
|
527
|
+
});
|
|
477
528
|
});
|
|
478
529
|
});
|
|
479
530
|
}
|
|
531
|
+
/** 绑定 DataConnection 的 data 事件 */
|
|
532
|
+
bindConnectionData(conn) {
|
|
533
|
+
conn.on('data', (message) => {
|
|
534
|
+
switch (message.scope) {
|
|
535
|
+
case entity_1.PeerScope.REQUEST:
|
|
536
|
+
return this.onRequest(conn, message);
|
|
537
|
+
case entity_1.PeerScope.RESPONSE:
|
|
538
|
+
return this.onResponse(conn, message);
|
|
539
|
+
default:
|
|
540
|
+
return this.responseFail(conn, message, 'unknown scope');
|
|
541
|
+
}
|
|
542
|
+
});
|
|
543
|
+
}
|
|
480
544
|
request(peerId, payload) {
|
|
481
545
|
return __awaiter(this, void 0, void 0, function* () {
|
|
546
|
+
var _a;
|
|
547
|
+
const requestId = payload.requestId || (yield this.genRequestId());
|
|
548
|
+
const maxRetry = 2;
|
|
549
|
+
let res;
|
|
550
|
+
for (let attempt = 0; attempt <= maxRetry; attempt++) {
|
|
551
|
+
res = yield this.doRequestOnce(peerId, payload, requestId);
|
|
552
|
+
console.log('request once', res);
|
|
553
|
+
const code = (_a = res.error) === null || _a === void 0 ? void 0 : _a.code;
|
|
554
|
+
if (!res.error || (code !== 'CONN_CLOSED' && code !== 'CONN_FAILED')) {
|
|
555
|
+
return res;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
return res;
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* 执行一次请求,返回带错误码的结果供外层判断是否重试
|
|
563
|
+
* @param peerId - 目标 peerId
|
|
564
|
+
* @param payload - 请求负载
|
|
565
|
+
* @param requestId - 请求ID(重试时复用)
|
|
566
|
+
* @returns error.code: CONN_CLOSED/CONN_FAILED(可重试)、TIMEOUT(不重试)
|
|
567
|
+
*/
|
|
568
|
+
doRequestOnce(peerId, payload, requestId) {
|
|
569
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
570
|
+
console.log('doRequestOnce@@', peerId, this.destroyed, payload.route);
|
|
571
|
+
if (this.destroyed) {
|
|
572
|
+
return Object.assign(Object.assign({}, payload), { body: {}, error: { message: 'destroyed', code: 'CONN_CLOSED' } });
|
|
573
|
+
}
|
|
482
574
|
const conn = yield this.connectToPeer(peerId);
|
|
483
575
|
if (!this.isValidConnection(conn)) {
|
|
484
|
-
return Object.assign(Object.assign({}, payload), { body: {}, error: { message: 'build connection failed', code:
|
|
576
|
+
return Object.assign(Object.assign({}, payload), { body: {}, error: { message: 'build connection failed', code: 'CONN_FAILED' } });
|
|
485
577
|
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
const
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
}
|
|
578
|
+
return new Promise((resolve) => {
|
|
579
|
+
let timeout = null;
|
|
580
|
+
const cleanup = () => {
|
|
581
|
+
if (timeout) {
|
|
582
|
+
clearTimeout(timeout);
|
|
583
|
+
timeout = null;
|
|
584
|
+
}
|
|
585
|
+
this.inRequest.delete(requestId);
|
|
586
|
+
this.requests.delete(requestId);
|
|
587
|
+
// 清理 addRequest 可能创建的定时器
|
|
588
|
+
const oldTimer = this.requestTimers.get(requestId);
|
|
589
|
+
if (oldTimer) {
|
|
590
|
+
clearTimeout(oldTimer);
|
|
591
|
+
this.requestTimers.delete(requestId);
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
timeout = setTimeout(() => {
|
|
595
|
+
// 先清理,避免 removeConnection 触发 feedback 二次 resolve
|
|
596
|
+
cleanup();
|
|
493
597
|
this.removeConnection(conn.peer, conn.connectionId);
|
|
494
|
-
resolve(
|
|
598
|
+
resolve(Object.assign(Object.assign({}, payload), { body: {}, error: { message: 'request timeout', code: 'TIMEOUT' } }));
|
|
495
599
|
}, payload.timeout || entity_1.REQUEST_TIMEOUT);
|
|
496
|
-
this.inRequest.set(
|
|
600
|
+
this.inRequest.set(requestId, {
|
|
497
601
|
conn,
|
|
498
602
|
message: payload,
|
|
499
|
-
feedback: () =>
|
|
500
|
-
|
|
501
|
-
error: { message: 'send failed' }
|
|
502
|
-
}
|
|
603
|
+
feedback: () => {
|
|
604
|
+
cleanup();
|
|
605
|
+
resolve(Object.assign(Object.assign({}, payload), { body: {}, error: { message: 'send failed', code: 'CONN_CLOSED' } }));
|
|
606
|
+
},
|
|
503
607
|
});
|
|
504
|
-
|
|
505
|
-
|
|
608
|
+
// 直接 set,避免 addRequest 内部 setTimeout 在重试时误删新回调
|
|
609
|
+
this.requests.set(requestId, (message) => {
|
|
610
|
+
cleanup();
|
|
506
611
|
const now = Date.now();
|
|
507
612
|
if (now - message.createdAt > 3000) {
|
|
508
613
|
console.warn('[egos] slow peer request:', message.route, now - message.createdAt);
|
|
509
614
|
}
|
|
510
|
-
this.inRequest.delete(conn.connectionId);
|
|
511
615
|
resolve(message);
|
|
512
616
|
});
|
|
617
|
+
this.send(conn, Object.assign(Object.assign({}, payload), { requestId, headers: Object.assign({}, payload.headers) })).catch(() => {
|
|
618
|
+
cleanup();
|
|
619
|
+
resolve(Object.assign(Object.assign({}, payload), { body: {}, error: { message: 'send failed', code: 'CONN_CLOSED' } }));
|
|
620
|
+
});
|
|
513
621
|
});
|
|
514
|
-
yield this.send(conn, Object.assign(Object.assign({}, payload), { requestId, headers: Object.assign({}, payload.headers) }));
|
|
515
|
-
const res = yield q;
|
|
516
|
-
return res;
|
|
517
622
|
});
|
|
518
623
|
}
|
|
519
624
|
reply(conn, oldMessage, message) {
|
|
@@ -530,7 +635,7 @@ class PeerTransfer {
|
|
|
530
635
|
return __awaiter(this, void 0, void 0, function* () {
|
|
531
636
|
const internalApi = yield this.getApi(conn.peer);
|
|
532
637
|
let connection = conn;
|
|
533
|
-
if (!this.isValidConnection) {
|
|
638
|
+
if (!this.isValidConnection(conn)) {
|
|
534
639
|
const con = yield this.connectToPeer(conn.peer);
|
|
535
640
|
if (!con) {
|
|
536
641
|
return this.responseFail(conn, message, 'send with invalid connection');
|
|
@@ -538,8 +643,7 @@ class PeerTransfer {
|
|
|
538
643
|
connection = con;
|
|
539
644
|
}
|
|
540
645
|
const requestId = message.requestId || (yield this.genRequestId());
|
|
541
|
-
|
|
542
|
-
return sent;
|
|
646
|
+
return connection.send(Object.assign(Object.assign({}, message), { requestId, headers: Object.assign(Object.assign({}, message.headers), { ['x-device-token']: internalApi === null || internalApi === void 0 ? void 0 : internalApi.token }), scope: entity_1.PeerScope.REQUEST, action: entity_1.PeerAction.SEND, src: this.peerId, dest: conn.peer, createdAt: Date.now() }), false);
|
|
543
647
|
});
|
|
544
648
|
}
|
|
545
649
|
inheritMessage(receiveMsg) {
|
|
@@ -554,6 +658,7 @@ class PeerTransfer {
|
|
|
554
658
|
}
|
|
555
659
|
reconnect() {
|
|
556
660
|
return __awaiter(this, void 0, void 0, function* () {
|
|
661
|
+
console.log('reconnect', this.isPaused);
|
|
557
662
|
if (this.isPaused) {
|
|
558
663
|
return;
|
|
559
664
|
}
|
|
@@ -582,20 +687,21 @@ class PeerTransfer {
|
|
|
582
687
|
if (this.isPaused) {
|
|
583
688
|
return;
|
|
584
689
|
}
|
|
690
|
+
// 已连接且 token 未变,无需重连
|
|
585
691
|
if (((_a = this.peer) === null || _a === void 0 ? void 0 : _a.open) && this.peerConfig.token === ((_b = this.peer) === null || _b === void 0 ? void 0 : _b.options.token)) {
|
|
586
692
|
return this.peer;
|
|
587
693
|
}
|
|
694
|
+
// 指数退避
|
|
695
|
+
this.reconnectAttempt++;
|
|
696
|
+
const backoff = Math.min(BASE_RECONNECT_BACKOFF * Math.pow(2, this.reconnectAttempt - 1), MAX_RECONNECT_BACKOFF);
|
|
697
|
+
yield (0, delay_1.default)(backoff);
|
|
588
698
|
yield this.reloadConfig();
|
|
589
|
-
|
|
590
|
-
// return this.createPeer();
|
|
699
|
+
// 优先尝试 peer.reconnect() 轻量重连
|
|
591
700
|
if (((_c = this.peer) === null || _c === void 0 ? void 0 : _c.disconnected) && ((_d = this.peer) === null || _d === void 0 ? void 0 : _d.options.token) === this.peerConfig.token) {
|
|
592
701
|
this.peer.reconnect();
|
|
593
702
|
return this.peer;
|
|
594
703
|
}
|
|
595
|
-
|
|
596
|
-
yield this.reloadConfig();
|
|
597
|
-
return this.createPeer();
|
|
598
|
-
}
|
|
704
|
+
return this.createPeer();
|
|
599
705
|
}
|
|
600
706
|
catch (err) {
|
|
601
707
|
return this.createPeer();
|
|
@@ -604,11 +710,23 @@ class PeerTransfer {
|
|
|
604
710
|
}
|
|
605
711
|
clearInstance() {
|
|
606
712
|
var _a, _b;
|
|
713
|
+
console.info('clearInstance');
|
|
714
|
+
this.destroyed = true;
|
|
607
715
|
PeerTransfer.connecting.clear();
|
|
608
|
-
|
|
716
|
+
// 先触发所有 inRequest 的 feedback,让它们 resolve
|
|
717
|
+
for (const [reqId, entry] of this.inRequest) {
|
|
718
|
+
entry.feedback();
|
|
719
|
+
}
|
|
720
|
+
this.inRequest.clear();
|
|
721
|
+
const peerIds = Array.from(this.connectPool.keys());
|
|
609
722
|
for (const pId of peerIds) {
|
|
610
723
|
this.removeConnection(pId);
|
|
611
724
|
}
|
|
725
|
+
this.requests.clear();
|
|
726
|
+
for (const timer of this.requestTimers.values()) {
|
|
727
|
+
clearTimeout(timer);
|
|
728
|
+
}
|
|
729
|
+
this.requestTimers.clear();
|
|
612
730
|
(_a = this.peer) === null || _a === void 0 ? void 0 : _a.destroy();
|
|
613
731
|
(_b = this.peer) === null || _b === void 0 ? void 0 : _b.removeAllListeners();
|
|
614
732
|
this.booted = undefined;
|
|
@@ -625,9 +743,7 @@ class PeerTransfer {
|
|
|
625
743
|
onRequest(conn, message) {
|
|
626
744
|
return __awaiter(this, void 0, void 0, function* () {
|
|
627
745
|
try {
|
|
628
|
-
|
|
629
|
-
// console.log('onRequest-->', message.route);
|
|
630
|
-
// @todo default folder
|
|
746
|
+
console.log('onRequest@@', conn.peer, message.route);
|
|
631
747
|
yield this.router.run(conn, message);
|
|
632
748
|
}
|
|
633
749
|
catch (err) {
|
|
@@ -637,37 +753,35 @@ class PeerTransfer {
|
|
|
637
753
|
}
|
|
638
754
|
onResponse(conn, message) {
|
|
639
755
|
return __awaiter(this, void 0, void 0, function* () {
|
|
640
|
-
// @todo default folder
|
|
641
756
|
const cb = this.requests.get(message.requestId);
|
|
642
757
|
if (cb) {
|
|
643
758
|
cb.apply(this, [message]);
|
|
644
759
|
this.requests.delete(message.requestId);
|
|
645
760
|
}
|
|
646
|
-
// @todo throw err;
|
|
647
761
|
});
|
|
648
762
|
}
|
|
649
763
|
responseOk(conn, message) {
|
|
650
764
|
return __awaiter(this, void 0, void 0, function* () {
|
|
651
|
-
|
|
652
|
-
body: { ok: true },
|
|
653
|
-
};
|
|
654
|
-
return this.reply(conn, message, msg);
|
|
765
|
+
return this.reply(conn, message, { body: { ok: true } });
|
|
655
766
|
});
|
|
656
767
|
}
|
|
657
768
|
responseFail(conn, message, err) {
|
|
658
769
|
return __awaiter(this, void 0, void 0, function* () {
|
|
659
|
-
|
|
660
|
-
body: {},
|
|
661
|
-
error: err || 'service error',
|
|
662
|
-
};
|
|
663
|
-
return this.reply(conn, message, msg);
|
|
770
|
+
return this.reply(conn, message, { body: {}, error: err || 'service error' });
|
|
664
771
|
});
|
|
665
772
|
}
|
|
666
773
|
addRequest(id, cb) {
|
|
774
|
+
// 清除已有定时器,避免重复注册时旧定时器误删新回调
|
|
775
|
+
const oldTimer = this.requestTimers.get(id);
|
|
776
|
+
if (oldTimer) {
|
|
777
|
+
clearTimeout(oldTimer);
|
|
778
|
+
}
|
|
667
779
|
this.requests.set(id, cb);
|
|
668
|
-
setTimeout(() => {
|
|
780
|
+
const timer = setTimeout(() => {
|
|
669
781
|
this.requests.delete(id);
|
|
782
|
+
this.requestTimers.delete(id);
|
|
670
783
|
}, this.timeout);
|
|
784
|
+
this.requestTimers.set(id, timer);
|
|
671
785
|
}
|
|
672
786
|
use(handler, prefix = '') {
|
|
673
787
|
this.router.use(handler, prefix);
|
|
@@ -681,6 +795,7 @@ class PeerTransfer {
|
|
|
681
795
|
}
|
|
682
796
|
resume() {
|
|
683
797
|
this.isPaused = false;
|
|
798
|
+
this.destroyed = false;
|
|
684
799
|
return this.createPeer(true);
|
|
685
800
|
}
|
|
686
801
|
}
|