@opensumi/ide-connection 3.0.4-next-1716529130.0 → 3.0.4-next-1716962745.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.
Files changed (80) hide show
  1. package/lib/browser/ws-channel-handler.d.ts +8 -1
  2. package/lib/browser/ws-channel-handler.d.ts.map +1 -1
  3. package/lib/browser/ws-channel-handler.js +10 -21
  4. package/lib/browser/ws-channel-handler.js.map +1 -1
  5. package/lib/common/channel/index.d.ts +2 -0
  6. package/lib/common/channel/index.d.ts.map +1 -0
  7. package/lib/common/channel/index.js +5 -0
  8. package/lib/common/channel/index.js.map +1 -0
  9. package/lib/common/channel/types.d.ts +66 -0
  10. package/lib/common/channel/types.d.ts.map +1 -0
  11. package/lib/common/channel/types.js +8 -0
  12. package/lib/common/channel/types.js.map +1 -0
  13. package/lib/common/connection/drivers/simple.d.ts +7 -7
  14. package/lib/common/connection/drivers/simple.d.ts.map +1 -1
  15. package/lib/common/connection/drivers/simple.js.map +1 -1
  16. package/lib/common/index.d.ts +4 -0
  17. package/lib/common/index.d.ts.map +1 -1
  18. package/lib/common/index.js +4 -0
  19. package/lib/common/index.js.map +1 -1
  20. package/lib/common/rpc/connection.d.ts +4 -3
  21. package/lib/common/rpc/connection.d.ts.map +1 -1
  22. package/lib/common/rpc/connection.js +14 -30
  23. package/lib/common/rpc/connection.js.map +1 -1
  24. package/lib/common/rpc/message-io.d.ts +54 -11
  25. package/lib/common/rpc/message-io.d.ts.map +1 -1
  26. package/lib/common/rpc/message-io.js +63 -10
  27. package/lib/common/rpc/message-io.js.map +1 -1
  28. package/lib/common/rpc/multiplexer.d.ts +2 -0
  29. package/lib/common/rpc/multiplexer.d.ts.map +1 -1
  30. package/lib/common/rpc/multiplexer.js +1 -0
  31. package/lib/common/rpc/multiplexer.js.map +1 -1
  32. package/lib/common/rpc-service/index.d.ts +1 -0
  33. package/lib/common/rpc-service/index.d.ts.map +1 -1
  34. package/lib/common/rpc-service/index.js +1 -0
  35. package/lib/common/rpc-service/index.js.map +1 -1
  36. package/lib/common/rpc-service/registry.d.ts +2 -2
  37. package/lib/common/rpc-service/registry.d.ts.map +1 -1
  38. package/lib/common/rpc-service/registry.js +6 -0
  39. package/lib/common/rpc-service/registry.js.map +1 -1
  40. package/lib/common/serializer/fury.d.ts +122 -0
  41. package/lib/common/serializer/fury.d.ts.map +1 -0
  42. package/lib/common/serializer/fury.js +51 -0
  43. package/lib/common/serializer/fury.js.map +1 -0
  44. package/lib/common/serializer/index.d.ts +6 -0
  45. package/lib/common/serializer/index.d.ts.map +1 -0
  46. package/lib/common/serializer/index.js +21 -0
  47. package/lib/common/serializer/index.js.map +1 -0
  48. package/lib/common/serializer/raw.d.ts +4 -0
  49. package/lib/common/serializer/raw.d.ts.map +1 -0
  50. package/lib/common/serializer/raw.js +8 -0
  51. package/lib/common/serializer/raw.js.map +1 -0
  52. package/lib/common/serializer/types.d.ts +5 -0
  53. package/lib/common/serializer/types.d.ts.map +1 -0
  54. package/lib/common/serializer/types.js +3 -0
  55. package/lib/common/serializer/types.js.map +1 -0
  56. package/lib/common/server-handler.d.ts +7 -1
  57. package/lib/common/server-handler.d.ts.map +1 -1
  58. package/lib/common/server-handler.js +13 -11
  59. package/lib/common/server-handler.js.map +1 -1
  60. package/lib/common/ws-channel.d.ts +11 -201
  61. package/lib/common/ws-channel.d.ts.map +1 -1
  62. package/lib/common/ws-channel.js +49 -85
  63. package/lib/common/ws-channel.js.map +1 -1
  64. package/package.json +5 -5
  65. package/src/browser/ws-channel-handler.ts +26 -23
  66. package/src/common/channel/index.ts +1 -0
  67. package/src/common/channel/types.ts +82 -0
  68. package/src/common/connection/drivers/simple.ts +5 -5
  69. package/src/common/index.ts +4 -0
  70. package/src/common/rpc/connection.ts +19 -36
  71. package/src/common/rpc/message-io.ts +123 -10
  72. package/src/common/rpc/multiplexer.ts +3 -1
  73. package/src/common/rpc-service/index.ts +1 -0
  74. package/src/common/rpc-service/registry.ts +10 -2
  75. package/src/common/serializer/fury.ts +61 -0
  76. package/src/common/serializer/index.ts +23 -0
  77. package/src/common/serializer/raw.ts +8 -0
  78. package/src/common/serializer/types.ts +4 -0
  79. package/src/common/server-handler.ts +24 -26
  80. package/src/common/ws-channel.ts +72 -207
@@ -1,9 +1,16 @@
1
1
  import { EventEmitter } from '@opensumi/events';
2
2
  import { Barrier, Deferred, DisposableStore, IReporterService, MultiMap, REPORT_NAME } from '@opensumi/ide-core-common';
3
3
 
4
+ import { ChannelMessage } from '../common/channel/types';
4
5
  import { IRuntimeSocketConnection } from '../common/connection';
6
+ import { IConnectionShape } from '../common/connection/types';
7
+ import { ISerializer, furySerializer, wrapSerializer } from '../common/serializer';
5
8
  import { ConnectionInfo, WSCloseInfo } from '../common/types';
6
- import { ErrorMessageCode, WSChannel, parse, pingMessage } from '../common/ws-channel';
9
+ import { WSChannel } from '../common/ws-channel';
10
+
11
+ export interface WSChannelHandlerOptions {
12
+ serializer?: ISerializer<ChannelMessage, any>;
13
+ }
7
14
 
8
15
  /**
9
16
  * Channel Handler in browser
@@ -12,6 +19,8 @@ export class WSChannelHandler {
12
19
  private _disposables = new DisposableStore();
13
20
 
14
21
  private _onChannelCreatedEmitter = this._disposables.add(new EventEmitter<Record<string, [WSChannel]>>());
22
+
23
+ wrappedConnection: IConnectionShape<ChannelMessage>;
15
24
  public onChannelCreated(path: string, listener: (channel: WSChannel) => void) {
16
25
  return this._onChannelCreatedEmitter.on(path, listener);
17
26
  }
@@ -30,10 +39,17 @@ export class WSChannelHandler {
30
39
 
31
40
  LOG_TAG: string;
32
41
 
33
- constructor(public connection: IRuntimeSocketConnection<Uint8Array>, logger: any, clientId: string) {
42
+ constructor(
43
+ public connection: IRuntimeSocketConnection<Uint8Array>,
44
+ logger: any,
45
+ clientId: string,
46
+ options: WSChannelHandlerOptions = {},
47
+ ) {
34
48
  this.logger = logger || this.logger;
35
49
  this.clientId = clientId;
36
50
  this.LOG_TAG = `[WSChannelHandler] [client-id:${this.clientId}]`;
51
+ const serializer = options.serializer || furySerializer;
52
+ this.wrappedConnection = wrapSerializer(this.connection, serializer);
37
53
  }
38
54
  // 为解决建立连接之后,替换成可落盘的 logger
39
55
  replaceLogger(logger: any) {
@@ -49,37 +65,24 @@ export class WSChannelHandler {
49
65
  clearTimeout(this.heartbeatMessageTimer);
50
66
  }
51
67
  this.heartbeatMessageTimer = global.setTimeout(() => {
52
- this.connection.send(pingMessage);
68
+ this.channelMap.forEach((channel) => {
69
+ channel.ping();
70
+ });
71
+
53
72
  this.heartbeatMessage();
54
73
  }, 10 * 1000);
55
74
  }
56
75
 
57
76
  public async initHandler() {
58
- this.connection.onMessage((message) => {
77
+ this.wrappedConnection.onMessage((msg) => {
59
78
  // 一个心跳周期内如果有收到消息,则不需要再发送心跳
60
79
  this.heartbeatMessage();
61
80
 
62
- const msg = parse(message);
63
-
64
81
  switch (msg.kind) {
65
82
  case 'pong':
66
- // pong 没有 msg.id, 且不需要分发, 不处理
67
- break;
68
- case 'error':
69
- this.logger.error(this.LOG_TAG, `receive error: id: ${msg.id}, code: ${msg.code}, error: ${msg.message}`);
70
- switch (msg.code) {
71
- case ErrorMessageCode.ChannelNotFound:
72
- if (this.channelMap.has(msg.id)) {
73
- // 如果远程报错 channel 不存在但是本机存在,则重新打开
74
- const channel = this.channelMap.get(msg.id)!;
75
- if (channel.channelPath) {
76
- channel.pause();
77
- channel.open(channel.channelPath, this.clientId);
78
- }
79
- }
80
- break;
81
- }
83
+ // pong 不需要分发, 不处理
82
84
  break;
85
+
83
86
  default: {
84
87
  const channel = this.channelMap.get(msg.id);
85
88
  if (channel) {
@@ -138,7 +141,7 @@ export class WSChannelHandler {
138
141
  this.logger.log(this.LOG_TAG, `channel ${key} already exists, dispose it`);
139
142
  }
140
143
 
141
- const channel = new WSChannel(this.connection, {
144
+ const channel = new WSChannel(this.wrappedConnection, {
142
145
  id: key,
143
146
  logger: this.logger,
144
147
  ensureServerReady: true,
@@ -0,0 +1 @@
1
+ export * from './types';
@@ -0,0 +1,82 @@
1
+ export type ChannelMessage =
2
+ | PingMessage
3
+ | PongMessage
4
+ | OpenMessage
5
+ | ServerReadyMessage
6
+ | DataMessage
7
+ | BinaryMessage
8
+ | CloseMessage
9
+ | ErrorMessage;
10
+
11
+ /**
12
+ * `ping` and `pong` are used to detect whether the connection is alive.
13
+ */
14
+ export interface PingMessage {
15
+ kind: 'ping';
16
+ id: string;
17
+ }
18
+
19
+ /**
20
+ * when server receive a `ping` message, it should reply a `pong` message, vice versa.
21
+ */
22
+ export interface PongMessage {
23
+ kind: 'pong';
24
+ id: string;
25
+ }
26
+
27
+ /**
28
+ * `data` message indicate that the channel has received some data.
29
+ * the `content` field is the data, it should be a string.
30
+ */
31
+ export interface DataMessage {
32
+ kind: 'data';
33
+ id: string;
34
+ content: string;
35
+ }
36
+
37
+ export interface BinaryMessage {
38
+ kind: 'binary';
39
+ id: string;
40
+ binary: Uint8Array;
41
+ }
42
+
43
+ export interface CloseMessage {
44
+ kind: 'close';
45
+ id: string;
46
+ code: number;
47
+ reason: string;
48
+ }
49
+
50
+ /**
51
+ * `open` message is used to open a new channel.
52
+ * `path` is used to identify which handler should be used to handle the channel.
53
+ * `clientId` is used to identify the client.
54
+ */
55
+ export interface OpenMessage {
56
+ kind: 'open';
57
+ id: string;
58
+ path: string;
59
+ clientId: string;
60
+ connectionToken: string;
61
+ }
62
+
63
+ export enum ErrorMessageCode {
64
+ ChannelNotFound = 1,
65
+ }
66
+
67
+ export interface ErrorMessage {
68
+ kind: 'error';
69
+ id: string;
70
+ code: ErrorMessageCode;
71
+ message: string;
72
+ }
73
+
74
+ /**
75
+ * when server receive a `open` message, it should reply a `server-ready` message.
76
+ * this is indicate that the channel is ready to use.
77
+ */
78
+ export interface ServerReadyMessage {
79
+ kind: 'server-ready';
80
+ id: string;
81
+ token: string;
82
+ }
@@ -2,19 +2,19 @@ import { IDisposable } from '@opensumi/ide-core-common';
2
2
 
3
3
  import { BaseConnection } from './base';
4
4
 
5
- export class SimpleConnection extends BaseConnection<Uint8Array> {
5
+ export class SimpleConnection<T = Uint8Array> extends BaseConnection<T> {
6
6
  constructor(
7
7
  public options: {
8
- send?: (data: Uint8Array) => void;
9
- onMessage?: (cb: (data: Uint8Array) => void) => IDisposable;
8
+ send?: (data: T) => void;
9
+ onMessage?: (cb: (data: T) => void) => IDisposable;
10
10
  } = {},
11
11
  ) {
12
12
  super();
13
13
  }
14
- send(data: Uint8Array): void {
14
+ send(data: T): void {
15
15
  this.options.send?.(data);
16
16
  }
17
- onMessage(cb: (data: Uint8Array) => void): IDisposable {
17
+ onMessage(cb: (data: T) => void): IDisposable {
18
18
  if (this.options.onMessage) {
19
19
  return this.options.onMessage(cb);
20
20
  }
@@ -1,7 +1,11 @@
1
1
  export * from './rpc-service/proxy';
2
+ export * from './rpc-service';
2
3
  export * from './rpc/multiplexer';
3
4
  export * from './rpcProtocol';
4
5
  export * from './capturer';
5
6
  export * from './ws-channel';
6
7
  export * from './connect';
7
8
  export * from './types';
9
+ export * from './connection';
10
+ export * from './serializer';
11
+ export * from './channel';
@@ -5,7 +5,6 @@ import {
5
5
  DisposableStore,
6
6
  IDisposable,
7
7
  canceled,
8
- parseError,
9
8
  } from '@opensumi/ide-utils';
10
9
  import { SumiReadableStream, isReadableStream, listenReadable } from '@opensumi/ide-utils/lib/stream';
11
10
 
@@ -15,9 +14,8 @@ import { METHOD_NOT_REGISTERED } from '../constants';
15
14
  import { ILogger } from '../types';
16
15
 
17
16
  import { MethodTimeoutError } from './errors';
18
- import { MessageIO, OperationType, Status } from './message-io';
17
+ import { BaseMessageIO, MessageIO, OperationType, RPCErrorMessage, RPCResponseMessage } from './message-io';
19
18
  import {
20
- IRequestHeaders,
21
19
  IResponseHeaders,
22
20
  TGenericNotificationHandler,
23
21
  TGenericRequestHandler,
@@ -38,6 +36,8 @@ export interface ISumiConnectionOptions {
38
36
  * The name of the connection, used for debugging(and can see in opensumi-devtools).
39
37
  */
40
38
  name?: string;
39
+
40
+ io?: BaseMessageIO;
41
41
  }
42
42
 
43
43
  const chunkedResponseHeaders: IResponseHeaders = {
@@ -61,7 +61,7 @@ export class SumiConnection implements IDisposable {
61
61
 
62
62
  protected activeRequestPool = new Map<number, SumiReadableStream<any>>();
63
63
 
64
- public io = new MessageIO();
64
+ public io: BaseMessageIO;
65
65
  protected logger: ILogger;
66
66
 
67
67
  protected capturer: Capturer;
@@ -73,6 +73,8 @@ export class SumiConnection implements IDisposable {
73
73
  this.logger = getDebugLogger();
74
74
  }
75
75
 
76
+ this.io = options.io || new MessageIO();
77
+
76
78
  this.capturer = new Capturer(options.name || 'sumi');
77
79
  this.disposable.add(this.capturer);
78
80
  }
@@ -193,35 +195,28 @@ export class SumiConnection implements IDisposable {
193
195
  }
194
196
 
195
197
  listen() {
196
- const { reader } = this.io;
197
-
198
198
  this.disposable.add(
199
199
  this.socket.onMessage((data) => {
200
- reader.reset(data);
201
- // skip version, currently only have version 1
202
- reader.skip(1);
203
-
204
- const opType = reader.uint8() as OperationType;
205
- const requestId = reader.uint32();
200
+ const message = this.io.readMessage(data);
201
+ const opType = message.kind;
202
+ const requestId = message.requestId;
206
203
 
207
204
  if (this._timeoutHandles.has(requestId)) {
208
- // Ignore some jest test scenarios where clearTimeout is not defined.
209
- if (typeof clearTimeout === 'function') {
210
- // @ts-ignore
211
- clearTimeout(this._timeoutHandles.get(requestId));
212
- }
205
+ clearTimeout(this._timeoutHandles.get(requestId));
213
206
  this._timeoutHandles.delete(requestId);
214
207
  }
215
208
 
216
209
  switch (opType) {
210
+ case OperationType.Error:
217
211
  case OperationType.Response: {
218
- const method = reader.stringOfVarUInt32();
219
- const status = reader.uint16();
212
+ const { headers, method } = message;
213
+ const err = (message as RPCErrorMessage).error;
214
+ const result = (message as RPCResponseMessage).result;
220
215
 
221
216
  const runCallback = (headers: IResponseHeaders, error?: any, result?: any) => {
222
217
  const callback = this._callbacks.get(requestId);
223
218
  if (!callback) {
224
- this.logger.error(`Cannot find callback for request ${requestId}: ${method}, status: ${status}`);
219
+ this.logger.error(`Cannot find callback for request ${requestId}: ${method}`);
225
220
  return;
226
221
  }
227
222
 
@@ -230,17 +225,6 @@ export class SumiConnection implements IDisposable {
230
225
  callback(headers, error, result);
231
226
  };
232
227
 
233
- const headers = this.io.responseHeadersSerializer.read();
234
- let err: any;
235
- let result: any;
236
- if (status === Status.Err) {
237
- // todo: move to processor
238
- const content = reader.stringOfVarUInt32();
239
- err = parseError(content);
240
- } else {
241
- result = this.io.getProcessor(method).readResponse();
242
- }
243
-
244
228
  if (headers && headers.chunked) {
245
229
  let activeReq: SumiReadableStream<any>;
246
230
  if (this.activeRequestPool.has(requestId)) {
@@ -275,9 +259,7 @@ export class SumiConnection implements IDisposable {
275
259
  case OperationType.Notification:
276
260
  // fall through
277
261
  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();
262
+ const { method, headers, args } = message;
281
263
 
282
264
  if (headers.cancelable) {
283
265
  const tokenSource = new CancellationTokenSource();
@@ -321,16 +303,17 @@ export class SumiConnection implements IDisposable {
321
303
  },
322
304
  onEnd: () => {
323
305
  this.socket.send(this.io.Response(requestId, method, chunkedResponseHeaders, null));
306
+ this._cancellationTokenSources.delete(requestId);
324
307
  },
325
308
  onError: (err) => {
326
309
  this.socket.send(this.io.Error(requestId, method, chunkedResponseHeaders, err));
310
+ this._cancellationTokenSources.delete(requestId);
327
311
  },
328
312
  });
329
313
  } else {
330
314
  this.socket.send(this.io.Response(requestId, method, nullHeaders, result));
315
+ this._cancellationTokenSources.delete(requestId);
331
316
  }
332
-
333
- this._cancellationTokenSources.delete(requestId);
334
317
  };
335
318
 
336
319
  const onError = (err: Error) => {
@@ -1,8 +1,9 @@
1
1
  import Fury, { Serializer, Type, TypeDescription } from '@furyjs/fury';
2
2
  import { generateSerializer } from '@furyjs/fury/dist/lib/gen';
3
+ import { PlatformBuffer } from '@furyjs/fury/dist/lib/platformBuffer';
3
4
  import { BinaryReader, BinaryWriter } from '@furyjs/fury/dist/lib/type';
4
5
 
5
- import { stringifyError } from '@opensumi/ide-core-common/lib/utils';
6
+ import { parseError, stringifyError } from '@opensumi/ide-core-common/lib/utils';
6
7
 
7
8
  import { AnySerializer, IObjectTransfer } from '../fury-extends/any';
8
9
  import { furyFactory } from '../fury-extends/shared';
@@ -24,11 +25,7 @@ export enum OperationType {
24
25
  Notification,
25
26
  Response,
26
27
  Cancel,
27
- }
28
-
29
- export enum Status {
30
- OK,
31
- Err,
28
+ Error,
32
29
  }
33
30
 
34
31
  export const HeadersProto = {
@@ -45,6 +42,7 @@ const PacketPrefix = {
45
42
  Notification: (OperationType.Notification << 8) | ProtoVersionV1,
46
43
  Response: (OperationType.Response << 8) | ProtoVersionV1,
47
44
  Cancel: (OperationType.Cancel << 8) | ProtoVersionV1,
45
+ Error: (OperationType.Error << 8) | ProtoVersionV1,
48
46
  } as const;
49
47
 
50
48
  class SumiProtocolSerializer implements IProtocolSerializer {
@@ -105,7 +103,66 @@ export class AnyProtocolSerializer implements IProtocolSerializer {
105
103
  }
106
104
  }
107
105
 
108
- export class MessageIO {
106
+ export interface RPCRequestMessage {
107
+ kind: OperationType.Request;
108
+ requestId: number;
109
+ method: string;
110
+ headers: IRequestHeaders;
111
+ args: any[];
112
+ }
113
+
114
+ export interface RPCNotificationMessage {
115
+ kind: OperationType.Notification;
116
+ requestId: number;
117
+ method: string;
118
+ headers: IRequestHeaders;
119
+ args: any[];
120
+ }
121
+
122
+ export interface RPCResponseMessage {
123
+ kind: OperationType.Response;
124
+ requestId: number;
125
+ method: string;
126
+ headers: Record<string, any>;
127
+ result: any;
128
+ }
129
+
130
+ export interface RPCErrorMessage {
131
+ kind: OperationType.Error;
132
+ requestId: number;
133
+ method: string;
134
+ headers: Record<string, any>;
135
+ error: any;
136
+ }
137
+
138
+ export interface RPCCancelMessage {
139
+ kind: OperationType.Cancel;
140
+ requestId: number;
141
+ }
142
+
143
+ export type RPCMessage =
144
+ | RPCRequestMessage
145
+ | RPCNotificationMessage
146
+ | RPCResponseMessage
147
+ | RPCErrorMessage
148
+ | RPCCancelMessage;
149
+
150
+ export abstract class BaseMessageIO<T = any> {
151
+ abstract loadProtocolMethod?(
152
+ methodProtocol: TSumiProtocolMethod,
153
+ options?: { nameConverter?: (str: string) => string },
154
+ ): void;
155
+
156
+ abstract Request(requestId: number, method: string, headers: IRequestHeaders, args: any[]): T;
157
+ abstract Notification(requestId: number, method: string, headers: IRequestHeaders, args: any[]): T;
158
+ abstract Cancel(requestId: number): T;
159
+ abstract Response(requestId: number, method: string, headers: Record<string, any>, result: any): T;
160
+ abstract Error(requestId: number, method: string, headers: Record<string, any>, error: any): T;
161
+
162
+ abstract readMessage(data: T): RPCMessage;
163
+ }
164
+
165
+ export class MessageIO extends BaseMessageIO<PlatformBuffer> {
109
166
  fury: Fury;
110
167
  reader: BinaryReader;
111
168
  writer: BinaryWriter;
@@ -118,6 +175,7 @@ export class MessageIO {
118
175
  responseHeadersSerializer: Serializer<IResponseHeaders, IRequestHeaders>;
119
176
 
120
177
  constructor() {
178
+ super();
121
179
  const fury = furyFactory();
122
180
  this.fury = fury.fury;
123
181
  this.reader = fury.reader;
@@ -216,7 +274,6 @@ export class MessageIO {
216
274
  writer.uint16(PacketPrefix.Response);
217
275
  writer.uint32(requestId);
218
276
  writer.stringOfVarUInt32(method);
219
- writer.uint16(Status.OK);
220
277
  this.responseHeadersSerializer.write(headers);
221
278
  this.getProcessor(method).writeResponse(result);
222
279
 
@@ -227,13 +284,69 @@ export class MessageIO {
227
284
  const { writer } = this;
228
285
  writer.reset();
229
286
 
230
- writer.uint16(PacketPrefix.Response);
287
+ writer.uint16(PacketPrefix.Error);
231
288
  writer.uint32(requestId);
232
289
  writer.stringOfVarUInt32(method);
233
- writer.uint16(Status.Err);
234
290
  this.responseHeadersSerializer.write(headers);
235
291
  writer.stringOfVarUInt32(stringifyError(error));
236
292
 
237
293
  return writer.dump();
238
294
  }
295
+
296
+ readMessage(data: PlatformBuffer): RPCMessage {
297
+ const { reader } = this;
298
+ reader.reset(data);
299
+
300
+ // skip version, currently only have version 1
301
+ reader.skip(1);
302
+ const opType = reader.uint8() as OperationType;
303
+ const requestId = reader.uint32();
304
+
305
+ switch (opType) {
306
+ case OperationType.Request:
307
+ case OperationType.Notification: {
308
+ const method = reader.stringOfVarUInt32();
309
+ const headers = this.requestHeadersSerializer.read() as IRequestHeaders;
310
+ const args = this.getProcessor(method).readRequest();
311
+ return {
312
+ kind: opType,
313
+ requestId,
314
+ method,
315
+ headers,
316
+ args,
317
+ };
318
+ }
319
+ case OperationType.Error: {
320
+ const method = reader.stringOfVarUInt32();
321
+ const headers = this.responseHeadersSerializer.read() as IResponseHeaders;
322
+ const error = parseError(reader.stringOfVarUInt32());
323
+ return {
324
+ kind: OperationType.Error,
325
+ requestId,
326
+ method,
327
+ headers,
328
+ error,
329
+ };
330
+ }
331
+ case OperationType.Response: {
332
+ const method = reader.stringOfVarUInt32();
333
+ const headers = this.responseHeadersSerializer.read() as IResponseHeaders;
334
+ const result = this.getProcessor(method).readResponse();
335
+ return {
336
+ kind: OperationType.Response,
337
+ requestId,
338
+ method,
339
+ headers,
340
+ result,
341
+ };
342
+ }
343
+ case OperationType.Cancel:
344
+ return {
345
+ kind: OperationType.Cancel,
346
+ requestId,
347
+ };
348
+ default:
349
+ throw new Error(`Unknown message type: ${opType}`);
350
+ }
351
+ }
239
352
  }
@@ -2,7 +2,7 @@ import { BaseConnection } from '../connection';
2
2
  import { ExtObjectTransfer } from '../fury-extends/any';
3
3
 
4
4
  import { ISumiConnectionOptions, SumiConnection } from './connection';
5
- import { AnyProtocolSerializer } from './message-io';
5
+ import { AnyProtocolSerializer, MessageIO } from './message-io';
6
6
  import { TSumiProtocol } from './types';
7
7
 
8
8
  export class ProxyIdentifier<T = any> {
@@ -57,12 +57,14 @@ export class SumiConnectionMultiplexer extends SumiConnection implements IRPCPro
57
57
  protected readonly _locals: Map<string, any>;
58
58
  protected readonly _proxies: Map<string, any>;
59
59
  protected _knownProtocols: Record<string, TSumiProtocol>;
60
+ io: MessageIO;
60
61
 
61
62
  constructor(protected socket: BaseConnection<Uint8Array>, protected options: ISumiMultiplexerConnectionOptions = {}) {
62
63
  super(socket, options);
63
64
  this._locals = new Map();
64
65
  this._proxies = new Map();
65
66
  this._knownProtocols = options.knownProtocols || {};
67
+ this.io = new MessageIO();
66
68
  this.io.setAnySerializer(new AnyProtocolSerializer(this.io.writer, this.io.reader, ExtObjectTransfer));
67
69
 
68
70
  this.onRequestNotFound((rpcName: string, args: any[]) => this.invoke(rpcName, args));
@@ -1,5 +1,6 @@
1
1
  export * from './stub';
2
2
  export * from './center';
3
+ export * from './registry';
3
4
 
4
5
  export abstract class RPCService<T = any> {
5
6
  rpcClient?: T[];
@@ -1,6 +1,6 @@
1
1
  import { DisposableStore, Emitter, IDisposable } from '@opensumi/ide-core-common';
2
2
 
3
- import { MessageIO, TSumiProtocol, TSumiProtocolMethod } from '../rpc';
3
+ import { BaseMessageIO, TSumiProtocol, TSumiProtocolMethod } from '../rpc';
4
4
  import { RPCServiceMethod } from '../types';
5
5
 
6
6
  const skipMethods = new Set(['constructor']);
@@ -119,13 +119,21 @@ export class ProtocolRegistry {
119
119
  this.emitter.fire(serviceNames);
120
120
  }
121
121
 
122
- applyTo(io: MessageIO) {
122
+ applyTo(io: BaseMessageIO) {
123
+ if (!io.loadProtocolMethod) {
124
+ return;
125
+ }
126
+
123
127
  for (const protocol of this.protocolMap.values()) {
124
128
  io.loadProtocolMethod(protocol);
125
129
  }
126
130
 
127
131
  this._disposables.add(
128
132
  this.onProtocolUpdate((methods) => {
133
+ if (!io.loadProtocolMethod) {
134
+ return;
135
+ }
136
+
129
137
  for (const method of methods) {
130
138
  const protocol = this.protocolMap.get(method);
131
139
  if (protocol) {
@@ -0,0 +1,61 @@
1
+ import { Type } from '@furyjs/fury';
2
+
3
+ import { ChannelMessage } from '../channel/types';
4
+ import { oneOf } from '../fury-extends/one-of';
5
+
6
+ import { ISerializer } from './types';
7
+
8
+ export const PingProtocol = Type.object('ping', {
9
+ id: Type.string(),
10
+ });
11
+
12
+ export const PongProtocol = Type.object('pong', {
13
+ id: Type.string(),
14
+ });
15
+
16
+ export const OpenProtocol = Type.object('open', {
17
+ clientId: Type.string(),
18
+ id: Type.string(),
19
+ path: Type.string(),
20
+ connectionToken: Type.string(),
21
+ });
22
+
23
+ export const ServerReadyProtocol = Type.object('server-ready', {
24
+ id: Type.string(),
25
+ token: Type.string(),
26
+ });
27
+
28
+ export const ErrorProtocol = Type.object('error', {
29
+ id: Type.string(),
30
+ code: Type.uint16(),
31
+ message: Type.string(),
32
+ });
33
+
34
+ export const DataProtocol = Type.object('data', {
35
+ id: Type.string(),
36
+ content: Type.string(),
37
+ });
38
+
39
+ export const BinaryProtocol = Type.object('binary', {
40
+ id: Type.string(),
41
+ binary: Type.binary(),
42
+ });
43
+
44
+ export const CloseProtocol = Type.object('close', {
45
+ id: Type.string(),
46
+ code: Type.uint32(),
47
+ reason: Type.string(),
48
+ });
49
+
50
+ const serializer = oneOf([
51
+ PingProtocol,
52
+ PongProtocol,
53
+ OpenProtocol,
54
+ ServerReadyProtocol,
55
+ DataProtocol,
56
+ BinaryProtocol,
57
+ CloseProtocol,
58
+ ErrorProtocol,
59
+ ]);
60
+
61
+ export const furySerializer: ISerializer<ChannelMessage, Uint8Array> = serializer;
@@ -0,0 +1,23 @@
1
+ import { IConnectionShape } from '../connection/types';
2
+
3
+ import { ISerializer } from './types';
4
+
5
+ export * from './fury';
6
+ export * from './types';
7
+
8
+ export const wrapSerializer = <FROM, TO>(
9
+ connection: IConnectionShape<TO>,
10
+ serializer: ISerializer<FROM, TO>,
11
+ ): IConnectionShape<FROM> => ({
12
+ onceClose(cb) {
13
+ return connection.onceClose(cb);
14
+ },
15
+ onMessage(cb) {
16
+ return connection.onMessage((data) => {
17
+ cb(serializer.deserialize(data));
18
+ });
19
+ },
20
+ send(data) {
21
+ connection.send(serializer.serialize(data));
22
+ },
23
+ });