@opensumi/ide-connection 3.1.2-next-1718957810.0 → 3.1.2

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.
@@ -1,5 +1,5 @@
1
1
  import { EventEmitter } from '@opensumi/events';
2
- import { DisposableStore, EventQueue, randomString } from '@opensumi/ide-core-common';
2
+ import { DisposableStore, EventQueue, StateTracer, randomString } from '@opensumi/ide-core-common';
3
3
 
4
4
  import { ChannelMessage, ErrorMessageCode } from './channel/types';
5
5
  import { IConnectionShape } from './connection/types';
@@ -15,82 +15,6 @@ export interface IWSChannelCreateOptions {
15
15
  logger?: ILogger;
16
16
 
17
17
  ensureServerReady?: boolean;
18
- deliveryTimeout?: number;
19
- }
20
-
21
- enum MessageDeliveryState {
22
- ReSend,
23
- Sended,
24
- Success,
25
- Failed,
26
- }
27
-
28
- class StateTracer {
29
- private map = new Map<string, MessageDeliveryState>();
30
-
31
- protected deliveryTimeout = 500;
32
- protected timerMap = new Map<string, NodeJS.Timeout>();
33
-
34
- setDeliveryTimeout(timeout: number) {
35
- this.deliveryTimeout = timeout;
36
- }
37
-
38
- protected set(traceId: string, state: MessageDeliveryState) {
39
- this.map.set(traceId, state);
40
- }
41
-
42
- get(traceId: string) {
43
- return this.map.get(traceId);
44
- }
45
-
46
- success(traceId: string) {
47
- this.map.set(traceId, MessageDeliveryState.Success);
48
- const timer = this.timerMap.get(traceId);
49
- if (timer) {
50
- clearTimeout(timer);
51
- }
52
- }
53
-
54
- dispose() {
55
- this.timerMap.forEach((timer) => {
56
- clearTimeout(timer);
57
- });
58
- }
59
-
60
- stop(traceId: string) {
61
- const timer = this.timerMap.get(traceId);
62
- if (timer) {
63
- clearTimeout(timer);
64
- }
65
- }
66
-
67
- send(
68
- traceId: string,
69
- options: {
70
- whenRetry: () => void;
71
- },
72
- ) {
73
- this.set(traceId, MessageDeliveryState.Sended);
74
- this.guard(traceId, options);
75
- }
76
-
77
- guard(
78
- traceId: string,
79
- options: {
80
- whenRetry: () => void;
81
- },
82
- ) {
83
- const timer = this.timerMap.get(traceId);
84
- if (timer) {
85
- clearTimeout(timer);
86
- }
87
-
88
- const newTimer = setTimeout(() => {
89
- this.set(traceId, MessageDeliveryState.ReSend);
90
- options.whenRetry();
91
- }, this.deliveryTimeout);
92
- this.timerMap.set(traceId, newTimer);
93
- }
94
18
  }
95
19
 
96
20
  export class WSChannel {
@@ -111,8 +35,6 @@ export class WSChannel {
111
35
  protected _isServerReady = false;
112
36
  protected _ensureServerReady: boolean | undefined;
113
37
 
114
- protected stateTracer = new StateTracer();
115
-
116
38
  public id: string;
117
39
 
118
40
  public channelPath: string;
@@ -131,9 +53,6 @@ export class WSChannel {
131
53
  }
132
54
 
133
55
  this._ensureServerReady = Boolean(ensureServerReady);
134
- if (options.deliveryTimeout) {
135
- this.stateTracer.setDeliveryTimeout(options.deliveryTimeout);
136
- }
137
56
 
138
57
  this._disposables.add(this.emitter.on('binary', (data) => this.onBinaryQueue.push(data)));
139
58
  }
@@ -149,30 +68,6 @@ export class WSChannel {
149
68
  this.connection.send(data);
150
69
  }
151
70
 
152
- /**
153
- * @param traceId 一个 connection token 用于在全链路中追踪一个消息的生命周期,防止消息未发送或者重复发送
154
- */
155
- protected ensureMessageDeliveried(data: ChannelMessage, traceId = randomString(16)) {
156
- const state = this.stateTracer.get(traceId);
157
- if (state && state >= MessageDeliveryState.Sended) {
158
- this.logger.error(`message already send already success or in progress, traceId: ${traceId}, state: ${state}`);
159
- return;
160
- }
161
-
162
- data.traceId = traceId;
163
- this.connection.send(data);
164
-
165
- this.stateTracer.send(traceId, {
166
- whenRetry: () => {
167
- if (this._isServerReady) {
168
- this.stateTracer.stop(traceId);
169
- return;
170
- }
171
- this.ensureMessageDeliveried(data, traceId);
172
- },
173
- });
174
- }
175
-
176
71
  onMessage(cb: (data: string) => any) {
177
72
  return this.emitter.on('message', cb);
178
73
  }
@@ -211,11 +106,11 @@ export class WSChannel {
211
106
  dispatch(msg: ChannelMessage) {
212
107
  switch (msg.kind) {
213
108
  case 'server-ready':
214
- if (msg.traceId) {
215
- this.stateTracer.success(msg.traceId);
216
- }
217
-
109
+ this.stateTracer.fulfill(msg.token);
218
110
  this.resume();
111
+ if (this.timer) {
112
+ clearTimeout(this.timer);
113
+ }
219
114
  this.emitter.emit('open', msg.id);
220
115
  break;
221
116
  case 'data':
@@ -241,24 +136,56 @@ export class WSChannel {
241
136
  }
242
137
  }
243
138
 
244
- open(path: string, clientId: string) {
139
+ stateTracer = this._disposables.add(new StateTracer());
140
+
141
+ /**
142
+ * @param connectionToken 一个 connection token 用于在全链路中追踪一个 channel 的生命周期,防止 channel 被重复打开
143
+ */
144
+ open(path: string, clientId: string, connectionToken = randomString(16)) {
245
145
  this.channelPath = path;
246
146
  this.clientId = clientId;
247
147
 
248
148
  this.LOG_TAG = `[WSChannel id=${this.id} path=${path}]`;
249
149
 
250
- const msg = {
150
+ if (this.stateTracer.has(connectionToken)) {
151
+ this.logger.warn(
152
+ `channel already opened or in progress, path: ${path}, clientId: ${clientId}, connectionToken: ${connectionToken}`,
153
+ );
154
+ return;
155
+ }
156
+
157
+ this.stateTracer.record(connectionToken);
158
+
159
+ this.connection.send({
251
160
  kind: 'open',
252
161
  id: this.id,
253
162
  path,
254
163
  clientId,
255
- } as ChannelMessage;
164
+ connectionToken,
165
+ });
256
166
 
257
167
  if (this._ensureServerReady) {
258
- this.ensureMessageDeliveried(msg);
259
- } else {
260
- this.connection.send(msg);
168
+ this.ensureOpenSend(path, clientId, connectionToken);
261
169
  }
170
+
171
+ return connectionToken;
172
+ }
173
+
174
+ protected timer: NodeJS.Timeout;
175
+ /**
176
+ * 启动定时器,确保 server-ready 消息在一定时间内到达
177
+ */
178
+ protected ensureOpenSend(path: string, clientId: string, connectionToken: string) {
179
+ if (this.timer) {
180
+ clearTimeout(this.timer);
181
+ }
182
+ this.timer = setTimeout(() => {
183
+ if (this._isServerReady) {
184
+ return;
185
+ }
186
+ this.stateTracer.delete(connectionToken);
187
+ this.open(path, clientId, connectionToken);
188
+ }, 500);
262
189
  }
263
190
 
264
191
  send(content: string) {
@@ -308,7 +235,9 @@ export class WSChannel {
308
235
  }
309
236
 
310
237
  dispose() {
311
- this.stateTracer.dispose();
238
+ if (this.timer) {
239
+ clearTimeout(this.timer);
240
+ }
312
241
  this.sendQueue = [];
313
242
  this._disposables.dispose();
314
243
  }
@@ -337,11 +266,11 @@ export class WSServerChannel extends WSChannel {
337
266
  this.clientId = options.clientId;
338
267
  }
339
268
 
340
- serverReady(traceId: string) {
269
+ serverReady(token: string) {
341
270
  this.connection.send({
342
271
  kind: 'server-ready',
343
272
  id: this.id,
344
- traceId,
273
+ token,
345
274
  });
346
275
  }
347
276