@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.
- package/lib/browser/ws-channel-handler.d.ts +8 -1
- package/lib/browser/ws-channel-handler.d.ts.map +1 -1
- package/lib/browser/ws-channel-handler.js +10 -21
- package/lib/browser/ws-channel-handler.js.map +1 -1
- package/lib/common/channel/index.d.ts +2 -0
- package/lib/common/channel/index.d.ts.map +1 -0
- package/lib/common/channel/index.js +5 -0
- package/lib/common/channel/index.js.map +1 -0
- package/lib/common/channel/types.d.ts +66 -0
- package/lib/common/channel/types.d.ts.map +1 -0
- package/lib/common/channel/types.js +8 -0
- package/lib/common/channel/types.js.map +1 -0
- package/lib/common/connection/drivers/simple.d.ts +7 -7
- package/lib/common/connection/drivers/simple.d.ts.map +1 -1
- package/lib/common/connection/drivers/simple.js.map +1 -1
- package/lib/common/index.d.ts +4 -0
- package/lib/common/index.d.ts.map +1 -1
- package/lib/common/index.js +4 -0
- package/lib/common/index.js.map +1 -1
- package/lib/common/rpc/connection.d.ts +4 -3
- package/lib/common/rpc/connection.d.ts.map +1 -1
- package/lib/common/rpc/connection.js +14 -30
- package/lib/common/rpc/connection.js.map +1 -1
- package/lib/common/rpc/message-io.d.ts +54 -11
- package/lib/common/rpc/message-io.d.ts.map +1 -1
- package/lib/common/rpc/message-io.js +63 -10
- package/lib/common/rpc/message-io.js.map +1 -1
- package/lib/common/rpc/multiplexer.d.ts +2 -0
- package/lib/common/rpc/multiplexer.d.ts.map +1 -1
- package/lib/common/rpc/multiplexer.js +1 -0
- package/lib/common/rpc/multiplexer.js.map +1 -1
- package/lib/common/rpc-service/index.d.ts +1 -0
- package/lib/common/rpc-service/index.d.ts.map +1 -1
- package/lib/common/rpc-service/index.js +1 -0
- package/lib/common/rpc-service/index.js.map +1 -1
- package/lib/common/rpc-service/registry.d.ts +2 -2
- package/lib/common/rpc-service/registry.d.ts.map +1 -1
- package/lib/common/rpc-service/registry.js +6 -0
- package/lib/common/rpc-service/registry.js.map +1 -1
- package/lib/common/serializer/fury.d.ts +122 -0
- package/lib/common/serializer/fury.d.ts.map +1 -0
- package/lib/common/serializer/fury.js +51 -0
- package/lib/common/serializer/fury.js.map +1 -0
- package/lib/common/serializer/index.d.ts +6 -0
- package/lib/common/serializer/index.d.ts.map +1 -0
- package/lib/common/serializer/index.js +21 -0
- package/lib/common/serializer/index.js.map +1 -0
- package/lib/common/serializer/raw.d.ts +4 -0
- package/lib/common/serializer/raw.d.ts.map +1 -0
- package/lib/common/serializer/raw.js +8 -0
- package/lib/common/serializer/raw.js.map +1 -0
- package/lib/common/serializer/types.d.ts +5 -0
- package/lib/common/serializer/types.d.ts.map +1 -0
- package/lib/common/serializer/types.js +3 -0
- package/lib/common/serializer/types.js.map +1 -0
- package/lib/common/server-handler.d.ts +7 -1
- package/lib/common/server-handler.d.ts.map +1 -1
- package/lib/common/server-handler.js +13 -11
- package/lib/common/server-handler.js.map +1 -1
- package/lib/common/ws-channel.d.ts +11 -201
- package/lib/common/ws-channel.d.ts.map +1 -1
- package/lib/common/ws-channel.js +49 -85
- package/lib/common/ws-channel.js.map +1 -1
- package/package.json +5 -5
- package/src/browser/ws-channel-handler.ts +26 -23
- package/src/common/channel/index.ts +1 -0
- package/src/common/channel/types.ts +82 -0
- package/src/common/connection/drivers/simple.ts +5 -5
- package/src/common/index.ts +4 -0
- package/src/common/rpc/connection.ts +19 -36
- package/src/common/rpc/message-io.ts +123 -10
- package/src/common/rpc/multiplexer.ts +3 -1
- package/src/common/rpc-service/index.ts +1 -0
- package/src/common/rpc-service/registry.ts +10 -2
- package/src/common/serializer/fury.ts +61 -0
- package/src/common/serializer/index.ts +23 -0
- package/src/common/serializer/raw.ts +8 -0
- package/src/common/serializer/types.ts +4 -0
- package/src/common/server-handler.ts +24 -26
- 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 {
|
|
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(
|
|
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.
|
|
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.
|
|
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
|
|
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.
|
|
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<
|
|
5
|
+
export class SimpleConnection<T = Uint8Array> extends BaseConnection<T> {
|
|
6
6
|
constructor(
|
|
7
7
|
public options: {
|
|
8
|
-
send?: (data:
|
|
9
|
-
onMessage?: (cb: (data:
|
|
8
|
+
send?: (data: T) => void;
|
|
9
|
+
onMessage?: (cb: (data: T) => void) => IDisposable;
|
|
10
10
|
} = {},
|
|
11
11
|
) {
|
|
12
12
|
super();
|
|
13
13
|
}
|
|
14
|
-
send(data:
|
|
14
|
+
send(data: T): void {
|
|
15
15
|
this.options.send?.(data);
|
|
16
16
|
}
|
|
17
|
-
onMessage(cb: (data:
|
|
17
|
+
onMessage(cb: (data: T) => void): IDisposable {
|
|
18
18
|
if (this.options.onMessage) {
|
|
19
19
|
return this.options.onMessage(cb);
|
|
20
20
|
}
|
package/src/common/index.ts
CHANGED
|
@@ -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,
|
|
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
|
|
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
|
-
|
|
201
|
-
|
|
202
|
-
|
|
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
|
-
|
|
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 =
|
|
219
|
-
const
|
|
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}
|
|
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 =
|
|
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
|
|
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.
|
|
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,6 +1,6 @@
|
|
|
1
1
|
import { DisposableStore, Emitter, IDisposable } from '@opensumi/ide-core-common';
|
|
2
2
|
|
|
3
|
-
import {
|
|
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:
|
|
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
|
+
});
|