@opensumi/ide-connection 3.0.2-next-1715826860.0 → 3.0.2-next-1715852509.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/lib/browser/ws-channel-handler.d.ts +0 -6
- package/lib/browser/ws-channel-handler.d.ts.map +1 -1
- package/lib/browser/ws-channel-handler.js +9 -68
- package/lib/browser/ws-channel-handler.js.map +1 -1
- package/lib/common/connection/drivers/reconnecting-websocket.d.ts +1 -1
- package/lib/common/connection/drivers/reconnecting-websocket.d.ts.map +1 -1
- package/lib/common/connection/drivers/reconnecting-websocket.js +1 -1
- package/lib/common/connection/drivers/reconnecting-websocket.js.map +1 -1
- package/lib/common/fury-extends/one-of.d.ts +1 -2
- package/lib/common/fury-extends/one-of.d.ts.map +1 -1
- package/lib/common/fury-extends/one-of.js +5 -12
- package/lib/common/fury-extends/one-of.js.map +1 -1
- package/lib/common/rpc/connection.d.ts.map +1 -1
- package/lib/common/rpc/connection.js +6 -3
- package/lib/common/rpc/connection.js.map +1 -1
- package/lib/common/server-handler.d.ts +4 -4
- package/lib/common/server-handler.d.ts.map +1 -1
- package/lib/common/server-handler.js +12 -25
- package/lib/common/server-handler.js.map +1 -1
- package/lib/common/ws-channel.d.ts +23 -77
- package/lib/common/ws-channel.d.ts.map +1 -1
- package/lib/common/ws-channel.js +4 -77
- package/lib/common/ws-channel.js.map +1 -1
- package/lib/node/ws.d.ts.map +1 -1
- package/lib/node/ws.js +0 -1
- package/lib/node/ws.js.map +1 -1
- package/package.json +6 -6
- package/src/browser/ws-channel-handler.ts +10 -74
- package/src/common/connection/drivers/reconnecting-websocket.ts +3 -5
- package/src/common/fury-extends/one-of.ts +3 -12
- package/src/common/rpc/connection.ts +149 -148
- package/src/common/server-handler.ts +18 -41
- package/src/common/ws-channel.ts +35 -143
- package/src/node/ws.ts +0 -1
|
@@ -195,185 +195,186 @@ export class SumiConnection implements IDisposable {
|
|
|
195
195
|
listen() {
|
|
196
196
|
const { reader } = this.io;
|
|
197
197
|
|
|
198
|
-
this.
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
clearTimeout(this._timeoutHandles.get(requestId));
|
|
212
|
-
}
|
|
213
|
-
this._timeoutHandles.delete(requestId);
|
|
198
|
+
const toDispose = this.socket.onMessage((data) => {
|
|
199
|
+
reader.reset(data);
|
|
200
|
+
// skip version, currently only have version 1
|
|
201
|
+
reader.skip(1);
|
|
202
|
+
|
|
203
|
+
const opType = reader.uint8() as OperationType;
|
|
204
|
+
const requestId = reader.uint32();
|
|
205
|
+
|
|
206
|
+
if (this._timeoutHandles.has(requestId)) {
|
|
207
|
+
// Ignore some jest test scenarios where clearTimeout is not defined.
|
|
208
|
+
if (typeof clearTimeout === 'function') {
|
|
209
|
+
// @ts-ignore
|
|
210
|
+
clearTimeout(this._timeoutHandles.get(requestId));
|
|
214
211
|
}
|
|
212
|
+
this._timeoutHandles.delete(requestId);
|
|
213
|
+
}
|
|
215
214
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
215
|
+
switch (opType) {
|
|
216
|
+
case OperationType.Response: {
|
|
217
|
+
const method = reader.stringOfVarUInt32();
|
|
218
|
+
const status = reader.uint16();
|
|
220
219
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
220
|
+
const runCallback = (headers: IResponseHeaders, error?: any, result?: any) => {
|
|
221
|
+
const callback = this._callbacks.get(requestId);
|
|
222
|
+
if (!callback) {
|
|
223
|
+
this.logger.error(`Cannot find callback for request ${requestId}`);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
this._callbacks.delete(requestId);
|
|
227
228
|
|
|
228
|
-
|
|
229
|
+
callback(headers, error, result);
|
|
230
|
+
};
|
|
229
231
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
+
const headers = this.io.responseHeadersSerializer.read();
|
|
233
|
+
let err: any;
|
|
234
|
+
let result: any;
|
|
235
|
+
if (status === Status.Err) {
|
|
236
|
+
// todo: move to processor
|
|
237
|
+
const content = reader.stringOfVarUInt32();
|
|
238
|
+
err = parseError(content);
|
|
239
|
+
} else {
|
|
240
|
+
result = this.io.getProcessor(method).readResponse();
|
|
241
|
+
}
|
|
232
242
|
|
|
233
|
-
|
|
234
|
-
let
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
// todo: move to processor
|
|
238
|
-
const content = reader.stringOfVarUInt32();
|
|
239
|
-
err = parseError(content);
|
|
243
|
+
if (headers && headers.chunked) {
|
|
244
|
+
let activeReq: SumiReadableStream<any>;
|
|
245
|
+
if (this.activeRequestPool.has(requestId)) {
|
|
246
|
+
activeReq = this.activeRequestPool.get(requestId)!;
|
|
240
247
|
} else {
|
|
241
|
-
|
|
248
|
+
// new stream request
|
|
249
|
+
activeReq = new SumiReadableStream();
|
|
250
|
+
this.activeRequestPool.set(requestId, activeReq);
|
|
251
|
+
// resolve `activeReq` to caller
|
|
252
|
+
runCallback(headers, undefined, activeReq);
|
|
242
253
|
}
|
|
243
254
|
|
|
244
|
-
if (
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
activeReq = new SumiReadableStream();
|
|
251
|
-
this.activeRequestPool.set(requestId, activeReq);
|
|
252
|
-
// resolve `activeReq` to caller
|
|
253
|
-
runCallback(headers, undefined, activeReq);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
if (result === null) {
|
|
257
|
-
// when result is null, it means the stream is ended.
|
|
258
|
-
activeReq.end();
|
|
259
|
-
this.activeRequestPool.delete(requestId);
|
|
260
|
-
break;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
if (err) {
|
|
264
|
-
activeReq.emitError(err);
|
|
265
|
-
break;
|
|
266
|
-
}
|
|
255
|
+
if (result === null) {
|
|
256
|
+
// when result is null, it means the stream is ended.
|
|
257
|
+
activeReq.end();
|
|
258
|
+
this.activeRequestPool.delete(requestId);
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
267
261
|
|
|
268
|
-
|
|
262
|
+
if (err) {
|
|
263
|
+
activeReq.emitError(err);
|
|
269
264
|
break;
|
|
270
265
|
}
|
|
271
266
|
|
|
272
|
-
|
|
267
|
+
activeReq.emitData(result);
|
|
273
268
|
break;
|
|
274
269
|
}
|
|
275
|
-
case OperationType.Notification:
|
|
276
|
-
// fall through
|
|
277
|
-
case OperationType.Request: {
|
|
278
|
-
const method = reader.stringOfVarUInt32();
|
|
279
|
-
const headers = this.io.requestHeadersSerializer.read() as IRequestHeaders;
|
|
280
|
-
const args = this.io.getProcessor(method).readRequest();
|
|
281
|
-
|
|
282
|
-
if (headers.cancelable) {
|
|
283
|
-
const tokenSource = new CancellationTokenSource();
|
|
284
|
-
this._cancellationTokenSources.set(requestId, tokenSource);
|
|
285
|
-
args.push(tokenSource.token);
|
|
286
|
-
|
|
287
|
-
if (this._knownCanceledRequests.has(requestId)) {
|
|
288
|
-
tokenSource.cancel();
|
|
289
|
-
this._knownCanceledRequests.delete(requestId);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
270
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
271
|
+
runCallback(headers, err, result);
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
274
|
+
case OperationType.Notification:
|
|
275
|
+
// fall through
|
|
276
|
+
case OperationType.Request: {
|
|
277
|
+
const method = reader.stringOfVarUInt32();
|
|
278
|
+
const headers = this.io.requestHeadersSerializer.read() as IRequestHeaders;
|
|
279
|
+
const args = this.io.getProcessor(method).readRequest();
|
|
280
|
+
|
|
281
|
+
if (headers.cancelable) {
|
|
282
|
+
const tokenSource = new CancellationTokenSource();
|
|
283
|
+
this._cancellationTokenSources.set(requestId, tokenSource);
|
|
284
|
+
args.push(tokenSource.token);
|
|
285
|
+
|
|
286
|
+
if (this._knownCanceledRequests.has(requestId)) {
|
|
287
|
+
tokenSource.cancel();
|
|
288
|
+
this._knownCanceledRequests.delete(requestId);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
296
291
|
|
|
297
|
-
|
|
292
|
+
switch (opType) {
|
|
293
|
+
case OperationType.Request: {
|
|
294
|
+
this.capturer.captureOnRequest(requestId, method, args);
|
|
298
295
|
|
|
299
|
-
|
|
300
|
-
let result: any;
|
|
296
|
+
let promise: Promise<any>;
|
|
301
297
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
result = handler(...args);
|
|
305
|
-
} else if (this._starRequestHandler) {
|
|
306
|
-
result = this._starRequestHandler(method, args);
|
|
307
|
-
}
|
|
298
|
+
try {
|
|
299
|
+
let result: any;
|
|
308
300
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
301
|
+
const handler = this._requestHandlers.get(method);
|
|
302
|
+
if (handler) {
|
|
303
|
+
result = handler(...args);
|
|
304
|
+
} else if (this._starRequestHandler) {
|
|
305
|
+
result = this._starRequestHandler(method, args);
|
|
312
306
|
}
|
|
313
307
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
if (isReadableStream(result)) {
|
|
318
|
-
listenReadable(result, {
|
|
319
|
-
onData: (data) => {
|
|
320
|
-
this.socket.send(this.io.Response(requestId, method, chunkedResponseHeaders, data));
|
|
321
|
-
},
|
|
322
|
-
onEnd: () => {
|
|
323
|
-
this.socket.send(this.io.Response(requestId, method, chunkedResponseHeaders, null));
|
|
324
|
-
},
|
|
325
|
-
onError: (err) => {
|
|
326
|
-
this.socket.send(this.io.Error(requestId, method, chunkedResponseHeaders, err));
|
|
327
|
-
},
|
|
328
|
-
});
|
|
329
|
-
} else {
|
|
330
|
-
this.socket.send(this.io.Response(requestId, method, nullHeaders, result));
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
this._cancellationTokenSources.delete(requestId);
|
|
334
|
-
};
|
|
335
|
-
|
|
336
|
-
const onError = (err: Error) => {
|
|
337
|
-
this.traceRequestError(requestId, method, args, err);
|
|
338
|
-
|
|
339
|
-
this.socket.send(this.io.Error(requestId, method, nullHeaders, err));
|
|
340
|
-
this._cancellationTokenSources.delete(requestId);
|
|
341
|
-
};
|
|
342
|
-
|
|
343
|
-
promise.then(onSuccess).catch(onError);
|
|
344
|
-
break;
|
|
308
|
+
promise = Promise.resolve(result);
|
|
309
|
+
} catch (err) {
|
|
310
|
+
promise = Promise.reject(err);
|
|
345
311
|
}
|
|
346
|
-
case OperationType.Notification: {
|
|
347
|
-
this.capturer.captureOnNotification(requestId, method, args);
|
|
348
312
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
313
|
+
const onSuccess = (result: any) => {
|
|
314
|
+
this.capturer.captureOnRequestResult(requestId, method, result);
|
|
315
|
+
|
|
316
|
+
if (isReadableStream(result)) {
|
|
317
|
+
listenReadable(result, {
|
|
318
|
+
onData: (data) => {
|
|
319
|
+
this.socket.send(this.io.Response(requestId, method, chunkedResponseHeaders, data));
|
|
320
|
+
},
|
|
321
|
+
onEnd: () => {
|
|
322
|
+
this.socket.send(this.io.Response(requestId, method, chunkedResponseHeaders, null));
|
|
323
|
+
},
|
|
324
|
+
onError: (err) => {
|
|
325
|
+
this.socket.send(this.io.Error(requestId, method, chunkedResponseHeaders, err));
|
|
326
|
+
},
|
|
327
|
+
});
|
|
328
|
+
} else {
|
|
329
|
+
this.socket.send(this.io.Response(requestId, method, nullHeaders, result));
|
|
355
330
|
}
|
|
356
|
-
|
|
357
|
-
|
|
331
|
+
|
|
332
|
+
this._cancellationTokenSources.delete(requestId);
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
const onError = (err: Error) => {
|
|
336
|
+
this.traceRequestError(requestId, method, args, err);
|
|
337
|
+
|
|
338
|
+
this.socket.send(this.io.Error(requestId, method, nullHeaders, err));
|
|
339
|
+
this._cancellationTokenSources.delete(requestId);
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
promise.then(onSuccess).catch(onError);
|
|
343
|
+
break;
|
|
358
344
|
}
|
|
345
|
+
case OperationType.Notification: {
|
|
346
|
+
this.capturer.captureOnNotification(requestId, method, args);
|
|
359
347
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
348
|
+
const handler = this._notificationHandlers.get(method);
|
|
349
|
+
|
|
350
|
+
if (handler) {
|
|
351
|
+
handler(...args);
|
|
352
|
+
} else if (this._starNotificationHandler) {
|
|
353
|
+
this._starNotificationHandler(method, args);
|
|
354
|
+
}
|
|
355
|
+
break;
|
|
368
356
|
}
|
|
369
|
-
break;
|
|
370
357
|
}
|
|
371
|
-
|
|
372
|
-
|
|
358
|
+
|
|
359
|
+
break;
|
|
360
|
+
}
|
|
361
|
+
case OperationType.Cancel: {
|
|
362
|
+
const cancellationTokenSource = this._cancellationTokenSources.get(requestId);
|
|
363
|
+
if (cancellationTokenSource) {
|
|
364
|
+
cancellationTokenSource.cancel();
|
|
365
|
+
} else {
|
|
366
|
+
this._knownCanceledRequests.add(requestId);
|
|
373
367
|
}
|
|
368
|
+
break;
|
|
369
|
+
}
|
|
370
|
+
default: {
|
|
371
|
+
break;
|
|
374
372
|
}
|
|
375
|
-
}
|
|
376
|
-
);
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
if (toDispose) {
|
|
376
|
+
this.disposable.add(toDispose);
|
|
377
|
+
}
|
|
377
378
|
}
|
|
378
379
|
|
|
379
380
|
dispose(): void {
|
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
import { IConnectionShape } from './connection/types';
|
|
2
2
|
import { ILogger } from './types';
|
|
3
|
-
import {
|
|
4
|
-
ChannelMessage,
|
|
5
|
-
ErrorMessageCode,
|
|
6
|
-
WSChannel,
|
|
7
|
-
WSServerChannel,
|
|
8
|
-
parse,
|
|
9
|
-
pongMessage,
|
|
10
|
-
stringify,
|
|
11
|
-
} from './ws-channel';
|
|
3
|
+
import { ChannelMessage, WSChannel, WSServerChannel, parse, pongMessage } from './ws-channel';
|
|
12
4
|
|
|
13
5
|
export interface IPathHandler {
|
|
14
6
|
dispose: (channel: WSChannel, connectionId: string) => void;
|
|
@@ -76,7 +68,7 @@ export class CommonChannelPathHandler {
|
|
|
76
68
|
});
|
|
77
69
|
});
|
|
78
70
|
}
|
|
79
|
-
|
|
71
|
+
dispatchChannelOpen(path: string, channel: WSChannel, clientId: string) {
|
|
80
72
|
// 根据 path 拿到注册的 handler
|
|
81
73
|
let handlerArr = this.get(path);
|
|
82
74
|
let params: Record<string, string> | undefined;
|
|
@@ -106,38 +98,31 @@ export class CommonChannelPathHandler {
|
|
|
106
98
|
export const commonChannelPathHandler = new CommonChannelPathHandler();
|
|
107
99
|
|
|
108
100
|
export abstract class BaseCommonChannelHandler {
|
|
109
|
-
protected channelMap: Map<string,
|
|
101
|
+
protected channelMap: Map<string, WSChannel> = new Map();
|
|
110
102
|
|
|
111
|
-
|
|
103
|
+
heartbeatTimer: NodeJS.Timeout | null = null;
|
|
112
104
|
|
|
113
105
|
constructor(public handlerId: string, protected logger: ILogger = console) {}
|
|
114
106
|
|
|
115
107
|
abstract doHeartbeat(connection: any): void;
|
|
116
108
|
|
|
117
109
|
private heartbeat(connection: any) {
|
|
110
|
+
const timer = global.setTimeout(() => {
|
|
111
|
+
this.doHeartbeat(connection);
|
|
112
|
+
this.heartbeat(connection);
|
|
113
|
+
}, 5000);
|
|
114
|
+
|
|
118
115
|
if (this.heartbeatTimer) {
|
|
119
116
|
clearTimeout(this.heartbeatTimer);
|
|
120
117
|
}
|
|
121
118
|
|
|
122
|
-
this.heartbeatTimer =
|
|
123
|
-
this.doHeartbeat(connection);
|
|
124
|
-
this.heartbeat(connection);
|
|
125
|
-
}, 5000);
|
|
119
|
+
this.heartbeatTimer = timer;
|
|
126
120
|
}
|
|
127
121
|
|
|
128
122
|
receiveConnection(connection: IConnectionShape<Uint8Array>) {
|
|
129
123
|
let clientId: string;
|
|
130
124
|
this.heartbeat(connection);
|
|
131
125
|
|
|
132
|
-
const getOrCreateChannel = (id: string, clientId: string) => {
|
|
133
|
-
let channel = this.channelMap.get(id);
|
|
134
|
-
if (!channel) {
|
|
135
|
-
channel = new WSServerChannel(connection, { id, clientId, logger: this.logger });
|
|
136
|
-
this.channelMap.set(id, channel);
|
|
137
|
-
}
|
|
138
|
-
return channel;
|
|
139
|
-
};
|
|
140
|
-
|
|
141
126
|
connection.onMessage((data: Uint8Array) => {
|
|
142
127
|
let msg: ChannelMessage;
|
|
143
128
|
try {
|
|
@@ -148,30 +133,23 @@ export abstract class BaseCommonChannelHandler {
|
|
|
148
133
|
connection.send(pongMessage);
|
|
149
134
|
break;
|
|
150
135
|
case 'open': {
|
|
151
|
-
const { id, path
|
|
136
|
+
const { id, path } = msg;
|
|
152
137
|
clientId = msg.clientId;
|
|
153
138
|
this.logger.log(`open a new connection channel ${clientId} with path ${path}`);
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
139
|
+
|
|
140
|
+
const channel = new WSServerChannel(connection, { id, logger: this.logger });
|
|
141
|
+
this.channelMap.set(id, channel);
|
|
142
|
+
|
|
143
|
+
commonChannelPathHandler.dispatchChannelOpen(path, channel, clientId);
|
|
144
|
+
channel.serverReady();
|
|
157
145
|
break;
|
|
158
146
|
}
|
|
159
147
|
default: {
|
|
160
148
|
const { id } = msg;
|
|
161
|
-
|
|
162
149
|
const channel = this.channelMap.get(id);
|
|
163
150
|
if (channel) {
|
|
164
151
|
channel.dispatch(msg);
|
|
165
152
|
} else {
|
|
166
|
-
connection.send(
|
|
167
|
-
stringify({
|
|
168
|
-
kind: 'error',
|
|
169
|
-
id,
|
|
170
|
-
code: ErrorMessageCode.ChannelNotFound,
|
|
171
|
-
message: `channel ${id} not found`,
|
|
172
|
-
}),
|
|
173
|
-
);
|
|
174
|
-
|
|
175
153
|
this.logger.warn(`channel ${id} is not found`);
|
|
176
154
|
}
|
|
177
155
|
}
|
|
@@ -182,11 +160,10 @@ export abstract class BaseCommonChannelHandler {
|
|
|
182
160
|
});
|
|
183
161
|
|
|
184
162
|
connection.onceClose(() => {
|
|
185
|
-
this.logger.log(`connection ${clientId} is closed, dispose all channels`);
|
|
186
163
|
commonChannelPathHandler.disposeConnectionClientId(connection, clientId);
|
|
187
164
|
|
|
188
165
|
Array.from(this.channelMap.values())
|
|
189
|
-
.filter((channel) => channel.clientId
|
|
166
|
+
.filter((channel) => channel.id.toString().indexOf(clientId) !== -1)
|
|
190
167
|
.forEach((channel) => {
|
|
191
168
|
channel.close(1, 'close');
|
|
192
169
|
channel.dispose();
|