@opensumi/ide-connection 3.0.4 → 3.0.5-next-1717466130.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 (88) hide show
  1. package/lib/browser/ws-channel-handler.d.ts +10 -1
  2. package/lib/browser/ws-channel-handler.d.ts.map +1 -1
  3. package/lib/browser/ws-channel-handler.js +11 -22
  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 +5 -4
  21. package/lib/common/rpc/connection.d.ts.map +1 -1
  22. package/lib/common/rpc/connection.js +21 -37
  23. package/lib/common/rpc/connection.js.map +1 -1
  24. package/lib/common/rpc/message-io.d.ts +62 -11
  25. package/lib/common/rpc/message-io.d.ts.map +1 -1
  26. package/lib/common/rpc/message-io.js +111 -10
  27. package/lib/common/rpc/message-io.js.map +1 -1
  28. package/lib/common/rpc/multiplexer.d.ts +11 -11
  29. package/lib/common/rpc/multiplexer.d.ts.map +1 -1
  30. package/lib/common/rpc/multiplexer.js +18 -17
  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/proxy/json.d.ts.map +1 -1
  37. package/lib/common/rpc-service/proxy/json.js +2 -2
  38. package/lib/common/rpc-service/proxy/json.js.map +1 -1
  39. package/lib/common/rpc-service/proxy/sumi.d.ts.map +1 -1
  40. package/lib/common/rpc-service/proxy/sumi.js +2 -2
  41. package/lib/common/rpc-service/proxy/sumi.js.map +1 -1
  42. package/lib/common/rpc-service/registry.d.ts +2 -2
  43. package/lib/common/rpc-service/registry.d.ts.map +1 -1
  44. package/lib/common/rpc-service/registry.js +6 -0
  45. package/lib/common/rpc-service/registry.js.map +1 -1
  46. package/lib/common/serializer/fury.d.ts +122 -0
  47. package/lib/common/serializer/fury.d.ts.map +1 -0
  48. package/lib/common/serializer/fury.js +51 -0
  49. package/lib/common/serializer/fury.js.map +1 -0
  50. package/lib/common/serializer/index.d.ts +7 -0
  51. package/lib/common/serializer/index.d.ts.map +1 -0
  52. package/lib/common/serializer/index.js +22 -0
  53. package/lib/common/serializer/index.js.map +1 -0
  54. package/lib/common/serializer/raw.d.ts +4 -0
  55. package/lib/common/serializer/raw.d.ts.map +1 -0
  56. package/lib/common/serializer/raw.js +8 -0
  57. package/lib/common/serializer/raw.js.map +1 -0
  58. package/lib/common/serializer/types.d.ts +5 -0
  59. package/lib/common/serializer/types.d.ts.map +1 -0
  60. package/lib/common/serializer/types.js +3 -0
  61. package/lib/common/serializer/types.js.map +1 -0
  62. package/lib/common/server-handler.d.ts +7 -1
  63. package/lib/common/server-handler.d.ts.map +1 -1
  64. package/lib/common/server-handler.js +13 -11
  65. package/lib/common/server-handler.js.map +1 -1
  66. package/lib/common/ws-channel.d.ts +10 -202
  67. package/lib/common/ws-channel.d.ts.map +1 -1
  68. package/lib/common/ws-channel.js +40 -92
  69. package/lib/common/ws-channel.js.map +1 -1
  70. package/package.json +5 -5
  71. package/src/browser/ws-channel-handler.ts +29 -26
  72. package/src/common/channel/index.ts +1 -0
  73. package/src/common/channel/types.ts +82 -0
  74. package/src/common/connection/drivers/simple.ts +5 -5
  75. package/src/common/index.ts +4 -0
  76. package/src/common/rpc/connection.ts +28 -44
  77. package/src/common/rpc/message-io.ts +172 -10
  78. package/src/common/rpc/multiplexer.ts +27 -29
  79. package/src/common/rpc-service/index.ts +1 -0
  80. package/src/common/rpc-service/proxy/json.ts +13 -11
  81. package/src/common/rpc-service/proxy/sumi.ts +5 -3
  82. package/src/common/rpc-service/registry.ts +10 -2
  83. package/src/common/serializer/fury.ts +61 -0
  84. package/src/common/serializer/index.ts +24 -0
  85. package/src/common/serializer/raw.ts +8 -0
  86. package/src/common/serializer/types.ts +4 -0
  87. package/src/common/server-handler.ts +24 -26
  88. package/src/common/ws-channel.ts +63 -225
@@ -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 { IMessageIO, 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?: IMessageIO;
41
41
  }
42
42
 
43
43
  const chunkedResponseHeaders: IResponseHeaders = {
@@ -55,13 +55,13 @@ export class SumiConnection implements IDisposable {
55
55
  private _requestId = 0;
56
56
  private _callbacks = new Map<number, TRequestCallback>();
57
57
 
58
- private readonly _timeoutHandles = new Map<number, NodeJS.Timeout | number>();
58
+ private readonly _reqTimeoutHandles = new Map<number, NodeJS.Timeout | number>();
59
59
  private readonly _cancellationTokenSources = new Map<number, CancellationTokenSource>();
60
60
  private readonly _knownCanceledRequests = new Set<number>();
61
61
 
62
62
  protected activeRequestPool = new Map<number, SumiReadableStream<any>>();
63
63
 
64
- public io = new MessageIO();
64
+ public io: IMessageIO;
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
  }
@@ -113,7 +115,7 @@ export class SumiConnection implements IDisposable {
113
115
  const timeoutHandle = setTimeout(() => {
114
116
  this._handleTimeout(method, requestId);
115
117
  }, this.options.timeout);
116
- this._timeoutHandles.set(requestId, timeoutHandle);
118
+ this._reqTimeoutHandles.set(requestId, timeoutHandle);
117
119
  }
118
120
 
119
121
  const cancellationToken: CancellationToken | undefined =
@@ -146,13 +148,13 @@ export class SumiConnection implements IDisposable {
146
148
  }
147
149
 
148
150
  private _handleTimeout(method: string, requestId: number) {
149
- if (!this._callbacks.has(requestId) || !this._timeoutHandles.has(requestId)) {
151
+ if (!this._callbacks.has(requestId) || !this._reqTimeoutHandles.has(requestId)) {
150
152
  return;
151
153
  }
152
154
 
153
155
  const callback = this._callbacks.get(requestId)!;
154
156
  this._callbacks.delete(requestId);
155
- this._timeoutHandles.delete(requestId);
157
+ this._reqTimeoutHandles.delete(requestId);
156
158
  callback(nullHeaders, new MethodTimeoutError(method));
157
159
  }
158
160
 
@@ -193,35 +195,29 @@ 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();
206
-
207
- 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
- }
213
- this._timeoutHandles.delete(requestId);
214
- }
200
+ const message = this.io.parse(data);
201
+
202
+ const opType = message.kind;
203
+ const requestId = message.requestId;
215
204
 
216
205
  switch (opType) {
206
+ case OperationType.Error:
217
207
  case OperationType.Response: {
218
- const method = reader.stringOfVarUInt32();
219
- const status = reader.uint16();
208
+ const { headers, method } = message;
209
+ const err = (message as RPCErrorMessage).error;
210
+ const result = (message as RPCResponseMessage).result;
211
+
212
+ if (this._reqTimeoutHandles.has(requestId)) {
213
+ clearTimeout(this._reqTimeoutHandles.get(requestId));
214
+ this._reqTimeoutHandles.delete(requestId);
215
+ }
220
216
 
221
217
  const runCallback = (headers: IResponseHeaders, error?: any, result?: any) => {
222
218
  const callback = this._callbacks.get(requestId);
223
219
  if (!callback) {
224
- this.logger.error(`Cannot find callback for request ${requestId}: ${method}, status: ${status}`);
220
+ this.logger.error(`Cannot find callback for request ${requestId}: ${method}`);
225
221
  return;
226
222
  }
227
223
 
@@ -230,17 +226,6 @@ export class SumiConnection implements IDisposable {
230
226
  callback(headers, error, result);
231
227
  };
232
228
 
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
229
  if (headers && headers.chunked) {
245
230
  let activeReq: SumiReadableStream<any>;
246
231
  if (this.activeRequestPool.has(requestId)) {
@@ -275,9 +260,7 @@ export class SumiConnection implements IDisposable {
275
260
  case OperationType.Notification:
276
261
  // fall through
277
262
  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();
263
+ const { method, headers, args } = message;
281
264
 
282
265
  if (headers.cancelable) {
283
266
  const tokenSource = new CancellationTokenSource();
@@ -321,16 +304,17 @@ export class SumiConnection implements IDisposable {
321
304
  },
322
305
  onEnd: () => {
323
306
  this.socket.send(this.io.Response(requestId, method, chunkedResponseHeaders, null));
307
+ this._cancellationTokenSources.delete(requestId);
324
308
  },
325
309
  onError: (err) => {
326
310
  this.socket.send(this.io.Error(requestId, method, chunkedResponseHeaders, err));
311
+ this._cancellationTokenSources.delete(requestId);
327
312
  },
328
313
  });
329
314
  } else {
330
315
  this.socket.send(this.io.Response(requestId, method, nullHeaders, result));
316
+ this._cancellationTokenSources.delete(requestId);
331
317
  }
332
-
333
- this._cancellationTokenSources.delete(requestId);
334
318
  };
335
319
 
336
320
  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: IResponseHeaders;
127
+ result: any;
128
+ }
129
+
130
+ export interface RPCErrorMessage {
131
+ kind: OperationType.Error;
132
+ requestId: number;
133
+ method: string;
134
+ headers: IResponseHeaders;
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 IMessageIO<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 parse(data: T): RPCMessage;
163
+ }
164
+
165
+ export class MessageIO extends IMessageIO<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,118 @@ 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
+ parse(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
+ }
352
+ }
353
+
354
+ export class RawMessageIO implements IMessageIO<RPCMessage> {
355
+ Request(requestId: number, method: string, headers: IRequestHeaders, args: any[]): RPCRequestMessage {
356
+ return {
357
+ kind: OperationType.Request,
358
+ requestId,
359
+ method,
360
+ headers,
361
+ args,
362
+ };
363
+ }
364
+ Notification(requestId: number, method: string, headers: IRequestHeaders, args: any[]): RPCNotificationMessage {
365
+ return {
366
+ kind: OperationType.Notification,
367
+ requestId,
368
+ method,
369
+ headers,
370
+ args,
371
+ };
372
+ }
373
+ Cancel(requestId: number): RPCCancelMessage {
374
+ return {
375
+ kind: OperationType.Cancel,
376
+ requestId,
377
+ };
378
+ }
379
+ Response(requestId: number, method: string, headers: Record<string, any>, result: any): RPCResponseMessage {
380
+ return {
381
+ kind: OperationType.Response,
382
+ requestId,
383
+ headers,
384
+ method,
385
+ result,
386
+ };
387
+ }
388
+ Error(requestId: number, method: string, headers: Record<string, any>, error: any): RPCErrorMessage {
389
+ return {
390
+ kind: OperationType.Error,
391
+ requestId,
392
+ method,
393
+ headers,
394
+ error,
395
+ };
396
+ }
397
+
398
+ parse(data: any): RPCMessage {
399
+ return data;
400
+ }
239
401
  }
@@ -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> {
@@ -20,13 +20,6 @@ export class ProxyIdentifier<T = any> {
20
20
  }
21
21
  }
22
22
 
23
- export interface ISumiMultiplexerConnectionOptions extends ISumiConnectionOptions {
24
- /**
25
- * Known protocols that will be loaded automatically when a proxy is created.
26
- */
27
- knownProtocols?: Record<string, TSumiProtocol>;
28
- }
29
-
30
23
  export const IRPCProtocol = Symbol('IRPCProtocol');
31
24
  export interface IRPCProtocol {
32
25
  getProxy<T>(proxyId: ProxyIdentifier<T>): T;
@@ -34,6 +27,24 @@ export interface IRPCProtocol {
34
27
  get<T>(identifier: ProxyIdentifier<T>): T;
35
28
  }
36
29
 
30
+ /**
31
+ *
32
+ * @param protocols Known protocols that will be loaded automatically when a proxy is created.
33
+ * @returns
34
+ */
35
+ export function createExtMessageIO(protocols?: Map<ProxyIdentifier<any>, TSumiProtocol>) {
36
+ const io = new MessageIO();
37
+ io.setAnySerializer(new AnyProtocolSerializer(io.writer, io.reader, ExtObjectTransfer));
38
+
39
+ protocols?.forEach((protocol, proxyId) => {
40
+ io.loadProtocol(protocol, {
41
+ nameConverter: (str: string) => SumiConnectionMultiplexer.getRPCName(proxyId.serviceId, str),
42
+ });
43
+ });
44
+
45
+ return io;
46
+ }
47
+
37
48
  /**
38
49
  * A connection multiplexer that allows to register multiple local RPC services and to create proxies for them.
39
50
  */
@@ -41,7 +52,7 @@ export class SumiConnectionMultiplexer extends SumiConnection implements IRPCPro
41
52
  protected static SEP = '/';
42
53
  protected static SEP_LENGTH = SumiConnectionMultiplexer.SEP.length;
43
54
 
44
- protected static getRPCName(serviceId: string, methodName: string) {
55
+ static getRPCName(serviceId: string, methodName: string) {
45
56
  return `${serviceId}${SumiConnectionMultiplexer.SEP}${methodName}`;
46
57
  }
47
58
 
@@ -56,14 +67,16 @@ export class SumiConnectionMultiplexer extends SumiConnection implements IRPCPro
56
67
 
57
68
  protected readonly _locals: Map<string, any>;
58
69
  protected readonly _proxies: Map<string, any>;
59
- protected _knownProtocols: Record<string, TSumiProtocol>;
60
70
 
61
- constructor(protected socket: BaseConnection<Uint8Array>, protected options: ISumiMultiplexerConnectionOptions = {}) {
62
- super(socket, options);
71
+ io: MessageIO;
72
+
73
+ constructor(protected socket: BaseConnection<Uint8Array>, protected options: ISumiConnectionOptions = {}) {
74
+ super(socket, {
75
+ ...options,
76
+ io: options.io || createExtMessageIO(),
77
+ });
63
78
  this._locals = new Map();
64
79
  this._proxies = new Map();
65
- this._knownProtocols = options.knownProtocols || {};
66
- this.io.setAnySerializer(new AnyProtocolSerializer(this.io.writer, this.io.reader, ExtObjectTransfer));
67
80
 
68
81
  this.onRequestNotFound((rpcName: string, args: any[]) => this.invoke(rpcName, args));
69
82
 
@@ -75,10 +88,6 @@ export class SumiConnectionMultiplexer extends SumiConnection implements IRPCPro
75
88
  public set<T>(identifier: ProxyIdentifier<T>, instance: any) {
76
89
  const id = SumiConnectionMultiplexer.normalizeServiceId(identifier.serviceId);
77
90
  this._locals.set(id, instance);
78
- const protocol = this._knownProtocols[identifier.serviceId];
79
- if (protocol) {
80
- this.loadProtocol(id, protocol);
81
- }
82
91
 
83
92
  return instance;
84
93
  }
@@ -87,21 +96,10 @@ export class SumiConnectionMultiplexer extends SumiConnection implements IRPCPro
87
96
  return this._locals.get(SumiConnectionMultiplexer.normalizeServiceId(identifier.serviceId));
88
97
  }
89
98
 
90
- protected loadProtocol(rpcId: string, protocol: TSumiProtocol) {
91
- this.io.loadProtocol(protocol, {
92
- nameConverter: (str: string) => SumiConnectionMultiplexer.getRPCName(rpcId, str),
93
- });
94
- }
95
-
96
99
  public getProxy<T>(proxyId: ProxyIdentifier<T>) {
97
100
  const serviceId = SumiConnectionMultiplexer.normalizeServiceId(proxyId.serviceId);
98
101
 
99
102
  if (!this._proxies.has(serviceId)) {
100
- const protocol = this._knownProtocols[proxyId.serviceId];
101
- if (protocol) {
102
- this.loadProtocol(serviceId, protocol);
103
- }
104
-
105
103
  this._proxies.set(serviceId, this._createProxy(serviceId));
106
104
  }
107
105
 
@@ -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[];
@@ -127,16 +127,18 @@ export class ProxyJson extends ProxyBase<MessageConnection> {
127
127
  listen(connection: MessageConnection): void {
128
128
  super.listen(connection);
129
129
 
130
- connection.onRequest((method) => {
131
- if (!this.registry.has(method)) {
132
- const requestId = this.nextRequestId();
133
- this.capturer.captureOnRequest(requestId, method, []);
134
- const result = {
135
- data: METHOD_NOT_REGISTERED,
136
- };
137
- this.capturer.captureOnRequestFail(requestId, method, result.data);
138
- return result;
139
- }
140
- });
130
+ this._disposables.add(
131
+ connection.onRequest((method) => {
132
+ if (!this.registry.has(method)) {
133
+ const requestId = this.nextRequestId();
134
+ this.capturer.captureOnRequest(requestId, method, []);
135
+ const result = {
136
+ data: METHOD_NOT_REGISTERED,
137
+ };
138
+ this.capturer.captureOnRequestFail(requestId, method, result.data);
139
+ return result;
140
+ }
141
+ }),
142
+ );
141
143
  }
142
144
  }
@@ -35,8 +35,10 @@ export class ProxySumi extends ProxyBase<SumiConnection> {
35
35
 
36
36
  listen(connection: SumiConnection): void {
37
37
  super.listen(connection);
38
- connection.onRequestNotFound(() => {
39
- throw METHOD_NOT_REGISTERED;
40
- });
38
+ this._disposables.add(
39
+ connection.onRequestNotFound(() => {
40
+ throw METHOD_NOT_REGISTERED;
41
+ }),
42
+ );
41
43
  }
42
44
  }