egos-transfer 0.2.3 → 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 -198
- package/dist/router.js +9 -9
- package/package.json +1 -1
- package/src/handler.ts +1 -1
- package/src/index.ts +369 -206
- 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,81 +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.onConnectLimit();
|
|
112
|
-
return done(null);
|
|
113
|
-
}
|
|
114
|
-
if (err.message == constant_1.UNAUTHORIZED) {
|
|
115
|
-
done(null);
|
|
116
|
-
return this.requireAuth();
|
|
117
|
-
}
|
|
118
|
-
if (err.type === 'network') {
|
|
119
|
-
done(null);
|
|
120
|
-
return this.reconnect();
|
|
121
|
-
}
|
|
122
|
-
if (err.type === 'server-error') {
|
|
123
|
-
done(null);
|
|
124
|
-
return this.reconnect();
|
|
125
|
-
}
|
|
126
|
-
done(null);
|
|
127
|
-
return this.createPeer(true);
|
|
128
|
-
}));
|
|
129
|
-
});
|
|
130
|
-
peer.on('connection', (conn) => {
|
|
131
|
-
conn.label = 'incoming';
|
|
132
|
-
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);
|
|
133
103
|
});
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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);
|
|
137
115
|
});
|
|
138
|
-
|
|
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 {
|
|
139
158
|
yield this.booted;
|
|
140
159
|
}
|
|
141
|
-
catch (
|
|
142
|
-
|
|
160
|
+
catch (_a) {
|
|
161
|
+
// booted rejected via timer timeout
|
|
143
162
|
}
|
|
144
163
|
finally {
|
|
145
164
|
this.booted = undefined;
|
|
@@ -157,10 +176,7 @@ class PeerTransfer {
|
|
|
157
176
|
this.onHeartbeat(message);
|
|
158
177
|
}
|
|
159
178
|
onHeartbeat(message) {
|
|
160
|
-
if (message.type !== 'HEARTBEAT') {
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
if (this.isPaused) {
|
|
179
|
+
if (message.type !== 'HEARTBEAT' || this.isPaused) {
|
|
164
180
|
return;
|
|
165
181
|
}
|
|
166
182
|
}
|
|
@@ -179,7 +195,6 @@ class PeerTransfer {
|
|
|
179
195
|
message: 'peer conflict',
|
|
180
196
|
},
|
|
181
197
|
});
|
|
182
|
-
return;
|
|
183
198
|
}
|
|
184
199
|
}
|
|
185
200
|
onOpen(message) {
|
|
@@ -192,32 +207,24 @@ class PeerTransfer {
|
|
|
192
207
|
if (message.type !== 'ONLINE') {
|
|
193
208
|
return;
|
|
194
209
|
}
|
|
195
|
-
|
|
196
|
-
this.online(deviceId);
|
|
210
|
+
this.online(message.payload.deviceId);
|
|
197
211
|
});
|
|
198
212
|
}
|
|
199
213
|
onDeviceOffline(message) {
|
|
200
214
|
if (message.type !== 'OFFLINE') {
|
|
201
215
|
return;
|
|
202
216
|
}
|
|
203
|
-
|
|
204
|
-
this.ping(deviceId);
|
|
217
|
+
this.ping(message.payload.deviceId);
|
|
205
218
|
}
|
|
206
219
|
removeConnection(peerId, connectionId) {
|
|
207
220
|
var _a, _b, _c;
|
|
208
|
-
// console.trace('removeConnection@', peerId, connectionId);
|
|
209
221
|
const conn = this.connectPool.get(peerId);
|
|
210
222
|
if (conn) {
|
|
211
223
|
if (connectionId && conn.connectionId !== connectionId) {
|
|
212
224
|
return;
|
|
213
225
|
}
|
|
214
|
-
const sending = this.inRequest.get(conn.connectionId);
|
|
215
|
-
if (sending) {
|
|
216
|
-
// 提前结束request,避免关闭连接时触发反馈
|
|
217
|
-
sending.feedback();
|
|
218
|
-
this.inRequest.delete(conn.connectionId);
|
|
219
|
-
}
|
|
220
226
|
this.connectPool.delete(peerId);
|
|
227
|
+
this.handleConnectionClose(peerId, conn);
|
|
221
228
|
conn.removeAllListeners();
|
|
222
229
|
conn.close();
|
|
223
230
|
(_a = this.peer) === null || _a === void 0 ? void 0 : _a._removeConnection(conn);
|
|
@@ -226,16 +233,52 @@ class PeerTransfer {
|
|
|
226
233
|
const connections = ((_b = this.peer) === null || _b === void 0 ? void 0 : _b.connections) || {};
|
|
227
234
|
const conns = connections[peerId] || [];
|
|
228
235
|
if (conns.length > 0) {
|
|
229
|
-
for (const
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
(_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);
|
|
233
240
|
}
|
|
234
241
|
}
|
|
235
242
|
}
|
|
236
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
|
+
}
|
|
237
281
|
addConnection(conn) {
|
|
238
|
-
// console.log('addConnection@', conn.label);
|
|
239
282
|
this.connectPool.set(conn.peer, conn);
|
|
240
283
|
const connecting = PeerTransfer.connecting.get(conn.peer);
|
|
241
284
|
if (connecting) {
|
|
@@ -253,21 +296,24 @@ class PeerTransfer {
|
|
|
253
296
|
}
|
|
254
297
|
// turn credential expires
|
|
255
298
|
if (this.peerConfig.turnExpiredAt && this.peerConfig.turnExpiredAt - 30000 < Date.now()) {
|
|
256
|
-
// this.connectPool.clear();
|
|
257
299
|
yield this.reloadConfig();
|
|
258
300
|
yield this.createPeer(true);
|
|
259
301
|
return;
|
|
260
302
|
}
|
|
261
|
-
const deviceIds = this.connectPool.keys();
|
|
262
|
-
|
|
303
|
+
const deviceIds = Array.from(this.connectPool.keys());
|
|
304
|
+
const pingTasks = deviceIds.map((deviceId) => __awaiter(this, void 0, void 0, function* () {
|
|
263
305
|
const conn = this.connectPool.get(deviceId);
|
|
264
306
|
if (!this.isValidConnection(conn)) {
|
|
265
307
|
this.removeConnection(conn.peer, conn.connectionId);
|
|
308
|
+
return;
|
|
266
309
|
}
|
|
267
|
-
|
|
268
|
-
|
|
310
|
+
const ok = yield this.ping(conn.peer);
|
|
311
|
+
if (!ok) {
|
|
312
|
+
this.removeConnection(conn.peer, conn.connectionId);
|
|
269
313
|
}
|
|
270
|
-
}
|
|
314
|
+
}));
|
|
315
|
+
// 并行执行,不阻塞下个心跳周期
|
|
316
|
+
yield Promise.allSettled(pingTasks);
|
|
271
317
|
}), constant_1.HEARTBEAT_INTERVAL);
|
|
272
318
|
}
|
|
273
319
|
getApiHost() {
|
|
@@ -283,8 +329,7 @@ class PeerTransfer {
|
|
|
283
329
|
const apiHost = this.getApiHost();
|
|
284
330
|
const res = yield axios_1.default.get(`${apiHost}/api/v1/devices/${peerId}/status`);
|
|
285
331
|
if (res.status < 400) {
|
|
286
|
-
|
|
287
|
-
return data.data;
|
|
332
|
+
return res.data.data;
|
|
288
333
|
}
|
|
289
334
|
}
|
|
290
335
|
catch (err) {
|
|
@@ -292,12 +337,11 @@ class PeerTransfer {
|
|
|
292
337
|
}
|
|
293
338
|
});
|
|
294
339
|
}
|
|
295
|
-
connectToPeer(
|
|
340
|
+
connectToPeer(peerId) {
|
|
296
341
|
return __awaiter(this, void 0, void 0, function* () {
|
|
297
|
-
if (
|
|
342
|
+
if (peerId === this.deviceId) {
|
|
298
343
|
return;
|
|
299
344
|
}
|
|
300
|
-
const peerId = deviceId;
|
|
301
345
|
const existingConn = this.connectPool.get(peerId);
|
|
302
346
|
console.log('connectToPeer', this.isPaused, this.connectPool.size);
|
|
303
347
|
if (existingConn) {
|
|
@@ -320,17 +364,22 @@ class PeerTransfer {
|
|
|
320
364
|
}
|
|
321
365
|
createConnection(peerId) {
|
|
322
366
|
return __awaiter(this, void 0, void 0, function* () {
|
|
323
|
-
let resolve;
|
|
324
|
-
let reject;
|
|
325
367
|
const q = PeerTransfer.connecting.get(peerId);
|
|
326
368
|
if (q) {
|
|
327
369
|
return q.promise;
|
|
328
370
|
}
|
|
371
|
+
let resolve;
|
|
372
|
+
let reject;
|
|
329
373
|
const connectionPromise = new Promise((res, rej) => {
|
|
330
374
|
resolve = res;
|
|
331
375
|
reject = rej;
|
|
332
376
|
});
|
|
333
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);
|
|
334
383
|
const doConnect = () => __awaiter(this, void 0, void 0, function* () {
|
|
335
384
|
var _a, _b;
|
|
336
385
|
try {
|
|
@@ -343,6 +392,7 @@ class PeerTransfer {
|
|
|
343
392
|
}
|
|
344
393
|
const cur = this.connectPool.get(peerId);
|
|
345
394
|
if (this.isValidConnection(cur)) {
|
|
395
|
+
clearTimeout(guardTimer);
|
|
346
396
|
return resolve(cur);
|
|
347
397
|
}
|
|
348
398
|
const conn = (_b = this.peer) === null || _b === void 0 ? void 0 : _b.connect(peerId, {
|
|
@@ -354,9 +404,11 @@ class PeerTransfer {
|
|
|
354
404
|
}
|
|
355
405
|
const connection = yield this.onConnect(conn).catch(() => { });
|
|
356
406
|
if (connection) {
|
|
407
|
+
clearTimeout(guardTimer);
|
|
357
408
|
return resolve(connection);
|
|
358
409
|
}
|
|
359
410
|
}
|
|
411
|
+
clearTimeout(guardTimer);
|
|
360
412
|
resolve(undefined);
|
|
361
413
|
return undefined;
|
|
362
414
|
}
|
|
@@ -372,6 +424,9 @@ class PeerTransfer {
|
|
|
372
424
|
return conn;
|
|
373
425
|
});
|
|
374
426
|
}
|
|
427
|
+
/**
|
|
428
|
+
* 心跳检测 - 直接 send,不走 request 管线,避免重试放大
|
|
429
|
+
*/
|
|
375
430
|
ping(peerId) {
|
|
376
431
|
return __awaiter(this, void 0, void 0, function* () {
|
|
377
432
|
if (this.deviceId === peerId) {
|
|
@@ -379,20 +434,37 @@ class PeerTransfer {
|
|
|
379
434
|
}
|
|
380
435
|
const conn = this.connectPool.get(peerId);
|
|
381
436
|
if (!this.isValidConnection(conn)) {
|
|
382
|
-
this.removeConnection(
|
|
437
|
+
this.removeConnection(peerId, conn === null || conn === void 0 ? void 0 : conn.connectionId);
|
|
383
438
|
return;
|
|
384
439
|
}
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
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) {
|
|
393
466
|
return false;
|
|
394
467
|
}
|
|
395
|
-
return true;
|
|
396
468
|
});
|
|
397
469
|
}
|
|
398
470
|
onConnect(conn) {
|
|
@@ -401,9 +473,9 @@ class PeerTransfer {
|
|
|
401
473
|
return;
|
|
402
474
|
}
|
|
403
475
|
const exists = this.connectPool.get(conn.peer);
|
|
404
|
-
// console.log('onConnect', conn.label, exists);
|
|
405
476
|
if (exists) {
|
|
406
|
-
|
|
477
|
+
// 双方同时建连时,保留 peerId 字典序大的那条(确定性,不依赖时间戳)
|
|
478
|
+
if (exists.connectionId > conn.connectionId) {
|
|
407
479
|
this.removeConnection(conn.peer, conn.connectionId);
|
|
408
480
|
if (this.isValidConnection(exists)) {
|
|
409
481
|
return exists;
|
|
@@ -413,19 +485,24 @@ class PeerTransfer {
|
|
|
413
485
|
this.removeConnection(exists.peer, exists.connectionId);
|
|
414
486
|
}
|
|
415
487
|
}
|
|
416
|
-
|
|
488
|
+
if (conn.open) {
|
|
489
|
+
this.addConnection(conn);
|
|
490
|
+
this.bindConnectionData(conn);
|
|
491
|
+
return conn;
|
|
492
|
+
}
|
|
417
493
|
return new Promise((resolve) => {
|
|
494
|
+
let timer = null;
|
|
418
495
|
const done = (connection) => {
|
|
419
|
-
if (connection) {
|
|
420
|
-
this.addConnection(connection);
|
|
421
|
-
}
|
|
422
496
|
if (timer) {
|
|
423
497
|
clearTimeout(timer);
|
|
424
498
|
timer = null;
|
|
425
499
|
}
|
|
500
|
+
if (connection) {
|
|
501
|
+
this.addConnection(connection);
|
|
502
|
+
}
|
|
426
503
|
resolve(connection);
|
|
427
504
|
};
|
|
428
|
-
timer = setTimeout(() =>
|
|
505
|
+
timer = setTimeout(() => {
|
|
429
506
|
if (this.isValidConnection(conn)) {
|
|
430
507
|
this.addConnection(conn);
|
|
431
508
|
done(conn);
|
|
@@ -434,85 +511,114 @@ class PeerTransfer {
|
|
|
434
511
|
this.removeConnection(conn.peer, conn.connectionId);
|
|
435
512
|
done(undefined);
|
|
436
513
|
}
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
conn.on('
|
|
440
|
-
switch (message.scope) {
|
|
441
|
-
case entity_1.PeerScope.REQUEST:
|
|
442
|
-
return this.onRequest(conn, message);
|
|
443
|
-
case entity_1.PeerScope.RESPONSE:
|
|
444
|
-
return this.onResponse(conn, message);
|
|
445
|
-
default:
|
|
446
|
-
return this.responseFail(conn, message, 'unknown scope');
|
|
447
|
-
}
|
|
448
|
-
});
|
|
449
|
-
conn.on('error', (err) => __awaiter(this, void 0, void 0, function* () {
|
|
514
|
+
}, constant_1.CONNECTION_TIMEOUT);
|
|
515
|
+
this.bindConnectionData(conn);
|
|
516
|
+
conn.on('error', () => {
|
|
450
517
|
this.removeConnection(conn.peer, conn.connectionId);
|
|
451
|
-
// if (!this.peer?.open) {
|
|
452
|
-
// await this.reconnect();
|
|
453
|
-
// }
|
|
454
518
|
done(null);
|
|
455
|
-
})
|
|
456
|
-
conn.on('close', () =>
|
|
519
|
+
});
|
|
520
|
+
conn.on('close', () => {
|
|
457
521
|
console.log('close------------>', conn.label);
|
|
458
|
-
clearTimeout(timer);
|
|
459
522
|
this.removeConnection(conn.peer, conn.connectionId);
|
|
460
|
-
// if (!this.peer?.open) {
|
|
461
|
-
// await this.reconnect();
|
|
462
|
-
// }
|
|
463
523
|
done(null);
|
|
464
|
-
})
|
|
465
|
-
conn.on('open', () =>
|
|
466
|
-
clearTimeout(timer);
|
|
467
|
-
done(conn);
|
|
468
|
-
}));
|
|
469
|
-
if (this.isValidConnection(conn)) {
|
|
470
|
-
if (timer) {
|
|
471
|
-
clearTimeout(timer);
|
|
472
|
-
}
|
|
473
|
-
timer = null;
|
|
524
|
+
});
|
|
525
|
+
conn.on('open', () => {
|
|
474
526
|
done(conn);
|
|
475
|
-
}
|
|
527
|
+
});
|
|
476
528
|
});
|
|
477
529
|
});
|
|
478
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
|
+
}
|
|
479
544
|
request(peerId, payload) {
|
|
480
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
|
+
}
|
|
481
574
|
const conn = yield this.connectToPeer(peerId);
|
|
482
575
|
if (!this.isValidConnection(conn)) {
|
|
483
|
-
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' } });
|
|
484
577
|
}
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
const
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
}
|
|
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();
|
|
492
597
|
this.removeConnection(conn.peer, conn.connectionId);
|
|
493
|
-
resolve(
|
|
598
|
+
resolve(Object.assign(Object.assign({}, payload), { body: {}, error: { message: 'request timeout', code: 'TIMEOUT' } }));
|
|
494
599
|
}, payload.timeout || entity_1.REQUEST_TIMEOUT);
|
|
495
|
-
this.inRequest.set(
|
|
600
|
+
this.inRequest.set(requestId, {
|
|
496
601
|
conn,
|
|
497
602
|
message: payload,
|
|
498
|
-
feedback: () =>
|
|
499
|
-
|
|
500
|
-
error: { message: 'send failed' }
|
|
501
|
-
}
|
|
603
|
+
feedback: () => {
|
|
604
|
+
cleanup();
|
|
605
|
+
resolve(Object.assign(Object.assign({}, payload), { body: {}, error: { message: 'send failed', code: 'CONN_CLOSED' } }));
|
|
606
|
+
},
|
|
502
607
|
});
|
|
503
|
-
|
|
504
|
-
|
|
608
|
+
// 直接 set,避免 addRequest 内部 setTimeout 在重试时误删新回调
|
|
609
|
+
this.requests.set(requestId, (message) => {
|
|
610
|
+
cleanup();
|
|
505
611
|
const now = Date.now();
|
|
506
612
|
if (now - message.createdAt > 3000) {
|
|
507
613
|
console.warn('[egos] slow peer request:', message.route, now - message.createdAt);
|
|
508
614
|
}
|
|
509
|
-
this.inRequest.delete(conn.connectionId);
|
|
510
615
|
resolve(message);
|
|
511
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
|
+
});
|
|
512
621
|
});
|
|
513
|
-
yield this.send(conn, Object.assign(Object.assign({}, payload), { requestId, headers: Object.assign({}, payload.headers) }));
|
|
514
|
-
const res = yield q;
|
|
515
|
-
return res;
|
|
516
622
|
});
|
|
517
623
|
}
|
|
518
624
|
reply(conn, oldMessage, message) {
|
|
@@ -529,7 +635,7 @@ class PeerTransfer {
|
|
|
529
635
|
return __awaiter(this, void 0, void 0, function* () {
|
|
530
636
|
const internalApi = yield this.getApi(conn.peer);
|
|
531
637
|
let connection = conn;
|
|
532
|
-
if (!this.isValidConnection) {
|
|
638
|
+
if (!this.isValidConnection(conn)) {
|
|
533
639
|
const con = yield this.connectToPeer(conn.peer);
|
|
534
640
|
if (!con) {
|
|
535
641
|
return this.responseFail(conn, message, 'send with invalid connection');
|
|
@@ -537,8 +643,7 @@ class PeerTransfer {
|
|
|
537
643
|
connection = con;
|
|
538
644
|
}
|
|
539
645
|
const requestId = message.requestId || (yield this.genRequestId());
|
|
540
|
-
|
|
541
|
-
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);
|
|
542
647
|
});
|
|
543
648
|
}
|
|
544
649
|
inheritMessage(receiveMsg) {
|
|
@@ -553,6 +658,7 @@ class PeerTransfer {
|
|
|
553
658
|
}
|
|
554
659
|
reconnect() {
|
|
555
660
|
return __awaiter(this, void 0, void 0, function* () {
|
|
661
|
+
console.log('reconnect', this.isPaused);
|
|
556
662
|
if (this.isPaused) {
|
|
557
663
|
return;
|
|
558
664
|
}
|
|
@@ -581,20 +687,21 @@ class PeerTransfer {
|
|
|
581
687
|
if (this.isPaused) {
|
|
582
688
|
return;
|
|
583
689
|
}
|
|
690
|
+
// 已连接且 token 未变,无需重连
|
|
584
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)) {
|
|
585
692
|
return this.peer;
|
|
586
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);
|
|
587
698
|
yield this.reloadConfig();
|
|
588
|
-
|
|
589
|
-
// return this.createPeer();
|
|
699
|
+
// 优先尝试 peer.reconnect() 轻量重连
|
|
590
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) {
|
|
591
701
|
this.peer.reconnect();
|
|
592
702
|
return this.peer;
|
|
593
703
|
}
|
|
594
|
-
|
|
595
|
-
yield this.reloadConfig();
|
|
596
|
-
return this.createPeer();
|
|
597
|
-
}
|
|
704
|
+
return this.createPeer();
|
|
598
705
|
}
|
|
599
706
|
catch (err) {
|
|
600
707
|
return this.createPeer();
|
|
@@ -603,11 +710,23 @@ class PeerTransfer {
|
|
|
603
710
|
}
|
|
604
711
|
clearInstance() {
|
|
605
712
|
var _a, _b;
|
|
713
|
+
console.info('clearInstance');
|
|
714
|
+
this.destroyed = true;
|
|
606
715
|
PeerTransfer.connecting.clear();
|
|
607
|
-
|
|
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());
|
|
608
722
|
for (const pId of peerIds) {
|
|
609
723
|
this.removeConnection(pId);
|
|
610
724
|
}
|
|
725
|
+
this.requests.clear();
|
|
726
|
+
for (const timer of this.requestTimers.values()) {
|
|
727
|
+
clearTimeout(timer);
|
|
728
|
+
}
|
|
729
|
+
this.requestTimers.clear();
|
|
611
730
|
(_a = this.peer) === null || _a === void 0 ? void 0 : _a.destroy();
|
|
612
731
|
(_b = this.peer) === null || _b === void 0 ? void 0 : _b.removeAllListeners();
|
|
613
732
|
this.booted = undefined;
|
|
@@ -624,9 +743,7 @@ class PeerTransfer {
|
|
|
624
743
|
onRequest(conn, message) {
|
|
625
744
|
return __awaiter(this, void 0, void 0, function* () {
|
|
626
745
|
try {
|
|
627
|
-
|
|
628
|
-
// console.log('onRequest-->', message.route);
|
|
629
|
-
// @todo default folder
|
|
746
|
+
console.log('onRequest@@', conn.peer, message.route);
|
|
630
747
|
yield this.router.run(conn, message);
|
|
631
748
|
}
|
|
632
749
|
catch (err) {
|
|
@@ -636,37 +753,35 @@ class PeerTransfer {
|
|
|
636
753
|
}
|
|
637
754
|
onResponse(conn, message) {
|
|
638
755
|
return __awaiter(this, void 0, void 0, function* () {
|
|
639
|
-
// @todo default folder
|
|
640
756
|
const cb = this.requests.get(message.requestId);
|
|
641
757
|
if (cb) {
|
|
642
758
|
cb.apply(this, [message]);
|
|
643
759
|
this.requests.delete(message.requestId);
|
|
644
760
|
}
|
|
645
|
-
// @todo throw err;
|
|
646
761
|
});
|
|
647
762
|
}
|
|
648
763
|
responseOk(conn, message) {
|
|
649
764
|
return __awaiter(this, void 0, void 0, function* () {
|
|
650
|
-
|
|
651
|
-
body: { ok: true },
|
|
652
|
-
};
|
|
653
|
-
return this.reply(conn, message, msg);
|
|
765
|
+
return this.reply(conn, message, { body: { ok: true } });
|
|
654
766
|
});
|
|
655
767
|
}
|
|
656
768
|
responseFail(conn, message, err) {
|
|
657
769
|
return __awaiter(this, void 0, void 0, function* () {
|
|
658
|
-
|
|
659
|
-
body: {},
|
|
660
|
-
error: err || 'service error',
|
|
661
|
-
};
|
|
662
|
-
return this.reply(conn, message, msg);
|
|
770
|
+
return this.reply(conn, message, { body: {}, error: err || 'service error' });
|
|
663
771
|
});
|
|
664
772
|
}
|
|
665
773
|
addRequest(id, cb) {
|
|
774
|
+
// 清除已有定时器,避免重复注册时旧定时器误删新回调
|
|
775
|
+
const oldTimer = this.requestTimers.get(id);
|
|
776
|
+
if (oldTimer) {
|
|
777
|
+
clearTimeout(oldTimer);
|
|
778
|
+
}
|
|
666
779
|
this.requests.set(id, cb);
|
|
667
|
-
setTimeout(() => {
|
|
780
|
+
const timer = setTimeout(() => {
|
|
668
781
|
this.requests.delete(id);
|
|
782
|
+
this.requestTimers.delete(id);
|
|
669
783
|
}, this.timeout);
|
|
784
|
+
this.requestTimers.set(id, timer);
|
|
670
785
|
}
|
|
671
786
|
use(handler, prefix = '') {
|
|
672
787
|
this.router.use(handler, prefix);
|
|
@@ -680,6 +795,7 @@ class PeerTransfer {
|
|
|
680
795
|
}
|
|
681
796
|
resume() {
|
|
682
797
|
this.isPaused = false;
|
|
798
|
+
this.destroyed = false;
|
|
683
799
|
return this.createPeer(true);
|
|
684
800
|
}
|
|
685
801
|
}
|