@opensumi/ide-connection 2.27.3-next-1706520813.0 → 2.27.3-rc-1706687185.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 (158) hide show
  1. package/lib/browser/ws-channel-handler.d.ts +9 -7
  2. package/lib/browser/ws-channel-handler.d.ts.map +1 -1
  3. package/lib/browser/ws-channel-handler.js +65 -64
  4. package/lib/browser/ws-channel-handler.js.map +1 -1
  5. package/lib/common/connect.d.ts +6 -57
  6. package/lib/common/connect.d.ts.map +1 -1
  7. package/lib/common/connect.js +9 -168
  8. package/lib/common/connect.js.map +1 -1
  9. package/lib/common/connection/buffers.d.ts +55 -0
  10. package/lib/common/connection/buffers.d.ts.map +1 -0
  11. package/lib/common/connection/buffers.js +244 -0
  12. package/lib/common/connection/buffers.js.map +1 -0
  13. package/lib/common/connection/drivers/base.d.ts +9 -0
  14. package/lib/common/connection/drivers/base.d.ts.map +1 -0
  15. package/lib/common/connection/drivers/base.js +11 -0
  16. package/lib/common/connection/drivers/base.js.map +1 -0
  17. package/lib/common/connection/drivers/empty.d.ts +8 -0
  18. package/lib/common/connection/drivers/empty.d.ts.map +1 -0
  19. package/lib/common/connection/drivers/empty.js +21 -0
  20. package/lib/common/connection/drivers/empty.js.map +1 -0
  21. package/lib/common/connection/drivers/index.d.ts +6 -0
  22. package/lib/common/connection/drivers/index.d.ts.map +1 -0
  23. package/lib/common/connection/drivers/index.js +9 -0
  24. package/lib/common/connection/drivers/index.js.map +1 -0
  25. package/lib/common/connection/drivers/node-message-port.d.ts +12 -0
  26. package/lib/common/connection/drivers/node-message-port.d.ts.map +1 -0
  27. package/lib/common/connection/drivers/node-message-port.js +31 -0
  28. package/lib/common/connection/drivers/node-message-port.js.map +1 -0
  29. package/lib/common/connection/drivers/reconnecting-websocket.d.ts +15 -0
  30. package/lib/common/connection/drivers/reconnecting-websocket.d.ts.map +1 -0
  31. package/lib/common/connection/drivers/reconnecting-websocket.js +83 -0
  32. package/lib/common/connection/drivers/reconnecting-websocket.js.map +1 -0
  33. package/lib/common/connection/drivers/socket.d.ts +17 -0
  34. package/lib/common/connection/drivers/socket.d.ts.map +1 -0
  35. package/lib/common/connection/drivers/socket.js +56 -0
  36. package/lib/common/connection/drivers/socket.js.map +1 -0
  37. package/lib/common/connection/drivers/stream-decoder.d.ts +67 -0
  38. package/lib/common/connection/drivers/stream-decoder.d.ts.map +1 -0
  39. package/lib/common/connection/drivers/stream-decoder.js +171 -0
  40. package/lib/common/connection/drivers/stream-decoder.js.map +1 -0
  41. package/lib/common/connection/drivers/utils.d.ts +12 -0
  42. package/lib/common/connection/drivers/utils.d.ts.map +1 -0
  43. package/lib/common/connection/drivers/utils.js +49 -0
  44. package/lib/common/connection/drivers/utils.js.map +1 -0
  45. package/lib/common/connection/drivers/ws-websocket.d.ts +11 -0
  46. package/lib/common/connection/drivers/ws-websocket.d.ts.map +1 -0
  47. package/lib/common/connection/drivers/ws-websocket.js +31 -0
  48. package/lib/common/connection/drivers/ws-websocket.js.map +1 -0
  49. package/lib/common/connection/index.d.ts +2 -0
  50. package/lib/common/connection/index.d.ts.map +1 -0
  51. package/lib/common/connection/index.js +5 -0
  52. package/lib/common/connection/index.js.map +1 -0
  53. package/lib/common/connection/types.d.ts +7 -0
  54. package/lib/common/connection/types.d.ts.map +1 -0
  55. package/lib/common/connection/types.js +3 -0
  56. package/lib/common/connection/types.js.map +1 -0
  57. package/lib/common/constants.d.ts +2 -0
  58. package/lib/common/constants.d.ts.map +1 -0
  59. package/lib/common/constants.js +5 -0
  60. package/lib/common/constants.js.map +1 -0
  61. package/lib/common/{rpcProtocol.d.ts → ext-rpc-protocol.d.ts} +10 -11
  62. package/lib/common/ext-rpc-protocol.d.ts.map +1 -0
  63. package/lib/common/{rpcProtocol.js → ext-rpc-protocol.js} +22 -19
  64. package/lib/common/ext-rpc-protocol.js.map +1 -0
  65. package/lib/common/index.d.ts +2 -1
  66. package/lib/common/index.d.ts.map +1 -1
  67. package/lib/common/index.js +2 -1
  68. package/lib/common/index.js.map +1 -1
  69. package/lib/common/proxy/base.d.ts +22 -0
  70. package/lib/common/proxy/base.d.ts.map +1 -0
  71. package/lib/common/proxy/base.js +47 -0
  72. package/lib/common/proxy/base.js.map +1 -0
  73. package/lib/common/proxy/index.d.ts +8 -0
  74. package/lib/common/proxy/index.d.ts.map +1 -0
  75. package/lib/common/proxy/index.js +12 -0
  76. package/lib/common/proxy/index.js.map +1 -0
  77. package/lib/common/proxy/legacy.d.ts +23 -0
  78. package/lib/common/proxy/legacy.d.ts.map +1 -0
  79. package/lib/common/proxy/legacy.js +183 -0
  80. package/lib/common/proxy/legacy.js.map +1 -0
  81. package/lib/common/rpc-service/center.d.ts +20 -0
  82. package/lib/common/rpc-service/center.d.ts.map +1 -0
  83. package/lib/common/rpc-service/center.js +100 -0
  84. package/lib/common/rpc-service/center.js.map +1 -0
  85. package/lib/common/rpc-service/index.d.ts +3 -0
  86. package/lib/common/rpc-service/index.d.ts.map +1 -0
  87. package/lib/common/rpc-service/index.js +6 -0
  88. package/lib/common/rpc-service/index.js.map +1 -0
  89. package/lib/common/rpc-service/stub.d.ts +15 -0
  90. package/lib/common/rpc-service/stub.d.ts.map +1 -0
  91. package/lib/common/rpc-service/stub.js +48 -0
  92. package/lib/common/rpc-service/stub.js.map +1 -0
  93. package/lib/common/types.d.ts +15 -0
  94. package/lib/common/types.d.ts.map +1 -0
  95. package/lib/common/types.js +9 -0
  96. package/lib/common/types.js.map +1 -0
  97. package/lib/common/utils.d.ts +4 -2
  98. package/lib/common/utils.d.ts.map +1 -1
  99. package/lib/common/utils.js +32 -9
  100. package/lib/common/utils.js.map +1 -1
  101. package/lib/common/ws-channel.d.ts +104 -27
  102. package/lib/common/ws-channel.d.ts.map +1 -1
  103. package/lib/common/ws-channel.js +96 -27
  104. package/lib/common/ws-channel.js.map +1 -1
  105. package/lib/node/common-channel-handler.d.ts +18 -16
  106. package/lib/node/common-channel-handler.d.ts.map +1 -1
  107. package/lib/node/common-channel-handler.js +42 -59
  108. package/lib/node/common-channel-handler.js.map +1 -1
  109. package/lib/node/index.d.ts +0 -3
  110. package/lib/node/index.d.ts.map +1 -1
  111. package/lib/node/index.js +0 -5
  112. package/lib/node/index.js.map +1 -1
  113. package/lib/node/ws.d.ts +1 -1
  114. package/lib/node/ws.d.ts.map +1 -1
  115. package/lib/node/ws.js +2 -0
  116. package/lib/node/ws.js.map +1 -1
  117. package/package.json +9 -7
  118. package/src/browser/ws-channel-handler.ts +82 -70
  119. package/src/common/connect.ts +7 -193
  120. package/src/common/connection/buffers.ts +284 -0
  121. package/src/common/connection/drivers/base.ts +15 -0
  122. package/src/common/connection/drivers/empty.ts +19 -0
  123. package/src/common/connection/drivers/index.ts +5 -0
  124. package/src/common/connection/drivers/node-message-port.ts +33 -0
  125. package/src/common/connection/drivers/reconnecting-websocket.ts +86 -0
  126. package/src/common/connection/drivers/socket.ts +62 -0
  127. package/src/common/connection/drivers/stream-decoder.ts +196 -0
  128. package/src/common/connection/drivers/utils.ts +52 -0
  129. package/src/common/connection/drivers/ws-websocket.ts +31 -0
  130. package/src/common/connection/index.ts +1 -0
  131. package/src/common/connection/types.ts +7 -0
  132. package/src/common/constants.ts +1 -0
  133. package/src/common/{rpcProtocol.ts → ext-rpc-protocol.ts} +43 -31
  134. package/src/common/index.ts +2 -1
  135. package/src/common/proxy/base.ts +67 -0
  136. package/src/common/proxy/index.ts +10 -0
  137. package/src/common/proxy/legacy.ts +200 -0
  138. package/src/common/rpc-service/center.ts +124 -0
  139. package/src/common/rpc-service/index.ts +2 -0
  140. package/src/common/rpc-service/stub.ts +49 -0
  141. package/src/common/types.ts +17 -0
  142. package/src/common/utils.ts +31 -8
  143. package/src/common/ws-channel.ts +175 -48
  144. package/src/node/common-channel-handler.ts +68 -80
  145. package/src/node/index.ts +0 -5
  146. package/src/node/ws.ts +3 -1
  147. package/lib/common/proxy.d.ts +0 -47
  148. package/lib/common/proxy.d.ts.map +0 -1
  149. package/lib/common/proxy.js +0 -272
  150. package/lib/common/proxy.js.map +0 -1
  151. package/lib/common/rpcProtocol.d.ts.map +0 -1
  152. package/lib/common/rpcProtocol.js.map +0 -1
  153. package/lib/node/connect.d.ts +0 -4
  154. package/lib/node/connect.d.ts.map +0 -1
  155. package/lib/node/connect.js +0 -9
  156. package/lib/node/connect.js.map +0 -1
  157. package/src/common/proxy.ts +0 -303
  158. package/src/node/connect.ts +0 -11
@@ -1,105 +1,188 @@
1
- import { stringify } from './utils';
1
+ import type net from 'net';
2
+
3
+ import Fury, { Type } from '@furyjs/fury';
4
+ import type WebSocket from 'ws';
5
+
6
+ import { EventEmitter } from '@opensumi/events';
7
+ import { DisposableCollection } from '@opensumi/ide-core-common';
8
+
9
+ import { NetSocketConnection, WSWebSocketConnection } from './connection';
10
+ import { IConnectionShape } from './connection/types';
11
+ import { createWebSocketConnection } from './message';
12
+ import { ILogger } from './types';
2
13
 
3
14
  export interface IWebSocket {
4
15
  send(content: string): void;
5
- close(...args): void;
16
+ close(...args: any[]): void;
6
17
  onMessage(cb: (data: any) => void): void;
7
18
  onError(cb: (reason: any) => void): void;
8
19
  onClose(cb: (code: number, reason: string) => void): void;
9
20
  }
10
21
 
11
- export interface ClientMessage {
12
- kind: 'client';
22
+ /**
23
+ * `ping` and `pong` are used to detect whether the connection is alive.
24
+ */
25
+ export interface PingMessage {
26
+ kind: 'ping';
27
+ id: string;
13
28
  clientId: string;
14
29
  }
15
- export interface HeartbeatMessage {
16
- kind: 'heartbeat';
30
+
31
+ /**
32
+ * when server receive a `ping` message, it should reply a `pong` message, vice versa.
33
+ */
34
+ export interface PongMessage {
35
+ kind: 'pong';
36
+ id: string;
17
37
  clientId: string;
18
38
  }
39
+
40
+ /**
41
+ * `open` message is used to open a new channel.
42
+ * `path` is used to identify which handler should be used to handle the channel.
43
+ * `clientId` is used to identify the client.
44
+ */
19
45
  export interface OpenMessage {
20
46
  kind: 'open';
21
- id: number;
47
+ id: string;
22
48
  path: string;
49
+ clientId: string;
23
50
  }
24
- export interface ReadyMessage {
25
- kind: 'ready';
26
- id: number;
51
+
52
+ /**
53
+ * when server receive a `open` message, it should reply a `server-ready` message.
54
+ * this is indicate that the channel is ready to use.
55
+ */
56
+ export interface ServerReadyMessage {
57
+ kind: 'server-ready';
58
+ id: string;
27
59
  }
60
+
61
+ /**
62
+ * `data` message indicate that the channel has received some data.
63
+ * the `content` field is the data, it should be a string.
64
+ */
28
65
  export interface DataMessage {
29
66
  kind: 'data';
30
- id: number;
67
+ id: string;
31
68
  content: string;
32
69
  }
70
+
33
71
  export interface CloseMessage {
34
72
  kind: 'close';
35
- id: number;
73
+ id: string;
36
74
  code: number;
37
75
  reason: string;
38
76
  }
39
- export type ChannelMessage = HeartbeatMessage | ClientMessage | OpenMessage | ReadyMessage | DataMessage | CloseMessage;
77
+
78
+ export type ChannelMessage = PingMessage | PongMessage | OpenMessage | ServerReadyMessage | DataMessage | CloseMessage;
79
+
80
+ export interface IWSChannelCreateOptions {
81
+ /**
82
+ * every channel's unique id, it only used in client to server architecture.
83
+ * server will store this id and use it to identify which channel should be used.
84
+ */
85
+ id: string;
86
+ logger?: ILogger;
87
+ }
40
88
 
41
89
  export class WSChannel implements IWebSocket {
42
- public id: number | string;
90
+ protected emitter = new EventEmitter<{
91
+ message: [data: string];
92
+ open: [id: string];
93
+ reopen: [];
94
+ close: [code?: number, reason?: string];
95
+ }>();
96
+
97
+ public id: string;
98
+ public LOG_TAG = '[WSChannel]';
99
+
43
100
  public channelPath: string;
44
101
 
45
- private connectionSend: (content: string) => void;
46
- private fireMessage: (data: any) => void;
47
- private fireOpen: (id: number) => void;
48
- public fireReOpen: () => void;
49
- private fireClose: (code: number, reason: string) => void;
102
+ logger: ILogger = console;
50
103
 
51
- public messageConnection: any;
104
+ static forClient(connection: IConnectionShape<Uint8Array>, options: IWSChannelCreateOptions) {
105
+ const disposable = new DisposableCollection();
106
+ const channel = new WSChannel(connection, options);
52
107
 
53
- constructor(connectionSend: (content: string) => void, id?: number | string) {
54
- this.connectionSend = connectionSend;
55
- if (id) {
56
- this.id = id;
57
- }
108
+ disposable.push(
109
+ connection.onMessage((data) => {
110
+ channel.handleMessage(parse(data));
111
+ }),
112
+ );
113
+ disposable.push(channel);
114
+
115
+ disposable.push(
116
+ connection.onceClose(() => {
117
+ disposable.dispose();
118
+ }),
119
+ );
120
+
121
+ return channel;
58
122
  }
59
123
 
60
- public setConnectionSend(connectionSend: (content: string) => void) {
61
- this.connectionSend = connectionSend;
124
+ static forWebSocket(socket: WebSocket, options: IWSChannelCreateOptions) {
125
+ const wsConnection = new WSWebSocketConnection(socket);
126
+ return WSChannel.forClient(wsConnection, options);
127
+ }
128
+
129
+ static forNetSocket(socket: net.Socket, options: IWSChannelCreateOptions) {
130
+ const wsConnection = new NetSocketConnection(socket);
131
+ return WSChannel.forClient(wsConnection, options);
132
+ }
133
+
134
+ constructor(public connection: IConnectionShape<Uint8Array>, options: IWSChannelCreateOptions) {
135
+ const { id, logger } = options;
136
+ this.id = id;
137
+
138
+ if (logger) {
139
+ this.logger = logger;
140
+ }
141
+
142
+ this.LOG_TAG = `[WSChannel] [id:${id}]`;
62
143
  }
63
144
 
64
145
  // server
65
- onMessage(cb: (data: any) => any) {
66
- this.fireMessage = cb;
146
+ onMessage(cb: (data: string) => any) {
147
+ return this.emitter.on('message', cb);
67
148
  }
68
- onOpen(cb: (id: number) => void) {
69
- this.fireOpen = cb;
149
+ onOpen(cb: (id: string) => void) {
150
+ return this.emitter.on('open', cb);
70
151
  }
71
- onReOpen(cb: () => void) {
72
- this.fireReOpen = cb;
152
+ onReopen(cb: () => void) {
153
+ return this.emitter.on('reopen', cb);
73
154
  }
74
- ready() {
75
- this.connectionSend(
155
+ serverReady() {
156
+ this.connection.send(
76
157
  stringify({
77
- kind: 'ready',
158
+ kind: 'server-ready',
78
159
  id: this.id,
79
160
  }),
80
161
  );
81
162
  }
163
+
82
164
  handleMessage(msg: ChannelMessage) {
83
- if (msg.kind === 'ready' && this.fireOpen) {
84
- this.fireOpen(msg.id);
85
- } else if (msg.kind === 'data' && this.fireMessage) {
86
- this.fireMessage(msg.content);
165
+ if (msg.kind === 'server-ready') {
166
+ this.emitter.emit('open', msg.id);
167
+ } else if (msg.kind === 'data') {
168
+ this.emitter.emit('message', msg.content);
87
169
  }
88
170
  }
89
171
 
90
172
  // client
91
- open(path: string) {
173
+ open(path: string, clientId: string) {
92
174
  this.channelPath = path;
93
- this.connectionSend(
175
+ this.connection.send(
94
176
  stringify({
95
177
  kind: 'open',
96
178
  id: this.id,
97
179
  path,
180
+ clientId,
98
181
  }),
99
182
  );
100
183
  }
101
184
  send(content: string) {
102
- this.connectionSend(
185
+ this.connection.send(
103
186
  stringify({
104
187
  kind: 'data',
105
188
  id: this.id,
@@ -107,14 +190,36 @@ export class WSChannel implements IWebSocket {
107
190
  }),
108
191
  );
109
192
  }
193
+ hasMessageListener() {
194
+ return this.emitter.hasListener('message');
195
+ }
110
196
  onError() {}
111
- close(code: number, reason: string) {
112
- if (this.fireClose) {
113
- this.fireClose(code, reason);
114
- }
197
+ close(code?: number, reason?: string) {
198
+ this.emitter.emit('close', code, reason);
199
+ }
200
+ fireReopen() {
201
+ this.emitter.emit('reopen');
115
202
  }
116
203
  onClose(cb: (code: number, reason: string) => void) {
117
- this.fireClose = cb;
204
+ return this.emitter.on('close', cb);
205
+ }
206
+ createMessageConnection() {
207
+ return createWebSocketConnection(this);
208
+ }
209
+ dispose() {
210
+ this.emitter.dispose();
211
+ }
212
+
213
+ listen(channel: WSChannel) {
214
+ channel.onMessage((data) => {
215
+ this.send(data);
216
+ });
217
+ channel.onClose((code, reason) => {
218
+ this.close(code, reason);
219
+ });
220
+ channel.onReopen(() => {
221
+ this.fireReopen();
222
+ });
118
223
  }
119
224
  }
120
225
 
@@ -142,3 +247,25 @@ export class ChildConnectPath {
142
247
  };
143
248
  }
144
249
  }
250
+
251
+ const fury = new Fury({});
252
+
253
+ export const wsChannelProtocol = Type.object('ws-channel-protocol', {
254
+ kind: Type.string(),
255
+ clientId: Type.string(),
256
+ id: Type.string(),
257
+ path: Type.string(),
258
+ content: Type.string(),
259
+ code: Type.uint32(),
260
+ reason: Type.string(),
261
+ });
262
+
263
+ const wsChannelProtocolSerializer = fury.registerSerializer(wsChannelProtocol);
264
+
265
+ export function stringify(obj: ChannelMessage): Uint8Array {
266
+ return wsChannelProtocolSerializer.serialize(obj);
267
+ }
268
+
269
+ export function parse(input: Uint8Array): ChannelMessage {
270
+ return wsChannelProtocolSerializer.deserialize(input) as any;
271
+ }
@@ -1,23 +1,24 @@
1
- import pathMatch from 'path-match';
2
- import ws from 'ws';
1
+ import { MatchFunction, match } from 'path-to-regexp';
2
+ import WebSocket from 'ws';
3
3
 
4
- import { stringify, parse } from '../common/utils';
5
- import { WSChannel, ChannelMessage } from '../common/ws-channel';
4
+ import { ILogger } from '../common';
5
+ import { WSWebSocketConnection } from '../common/connection';
6
+ import { WSChannel, ChannelMessage, stringify, parse } from '../common/ws-channel';
6
7
 
7
8
  import { WebSocketHandler, CommonChannelHandlerOptions } from './ws';
8
9
 
9
- export interface IPathHander {
10
+ export interface IPathHandler {
10
11
  dispose: (connection: any, connectionId: string) => void;
11
- handler: (connection: any, connectionId: string, params?: any) => void;
12
+ handler: (connection: any, connectionId: string, params?: Record<string, string>) => void;
12
13
  reconnect?: (connection: any, connectionId: string) => void;
13
14
  connection?: any;
14
15
  }
15
16
 
16
17
  export class CommonChannelPathHandler {
17
- private handlerMap: Map<string, IPathHander[]> = new Map();
18
+ private handlerMap: Map<string, IPathHandler[]> = new Map();
18
19
  private paramsKey: Map<string, string> = new Map();
19
20
 
20
- register(channelPath: string, handler: IPathHander) {
21
+ register(channelPath: string, handler: IPathHandler) {
21
22
  const paramsIndex = channelPath.indexOf('/:');
22
23
  const hasParams = paramsIndex >= 0;
23
24
  let channelToken = channelPath;
@@ -28,18 +29,18 @@ export class CommonChannelPathHandler {
28
29
  if (!this.handlerMap.has(channelToken)) {
29
30
  this.handlerMap.set(channelToken, []);
30
31
  }
31
- const handlerArr = this.handlerMap.get(channelToken) as IPathHander[];
32
+ const handlerArr = this.handlerMap.get(channelToken) as IPathHandler[];
32
33
  const handlerFn = handler.handler.bind(handler);
33
- const setHandler = (connection, clientId, params) => {
34
- handler.connection = connection;
35
- handlerFn(connection, clientId, params);
34
+ const setHandler = (channel: WSChannel, clientId: string, params: any) => {
35
+ handler.connection = channel;
36
+ handlerFn(channel, clientId, params);
36
37
  };
37
38
  handler.handler = setHandler;
38
39
  handlerArr.push(handler);
39
40
  this.handlerMap.set(channelToken, handlerArr);
40
41
  }
41
- getParams(channelPath: string, value: string) {
42
- const params = {};
42
+ getParams(channelPath: string, value: string): Record<string, string> {
43
+ const params = {} as Record<string, string>;
43
44
  if (this.paramsKey.has(channelPath)) {
44
45
  const key = this.paramsKey.get(channelPath);
45
46
  if (key) {
@@ -48,7 +49,7 @@ export class CommonChannelPathHandler {
48
49
  }
49
50
  return params;
50
51
  }
51
- removeHandler(channelPath: string, handler: IPathHander) {
52
+ removeHandler(channelPath: string, handler: IPathHandler) {
52
53
  const paramsIndex = channelPath.indexOf(':');
53
54
  const hasParams = paramsIndex >= 0;
54
55
  let channelToken = channelPath;
@@ -65,9 +66,9 @@ export class CommonChannelPathHandler {
65
66
  get(channelPath: string) {
66
67
  return this.handlerMap.get(channelPath);
67
68
  }
68
- disposeConnectionClientId(connection: ws, clientId: string) {
69
- this.handlerMap.forEach((handlerArr: IPathHander[]) => {
70
- handlerArr.forEach((handler: IPathHander) => {
69
+ disposeConnectionClientId(connection: WebSocket, clientId: string) {
70
+ this.handlerMap.forEach((handlerArr: IPathHandler[]) => {
71
+ handlerArr.forEach((handler: IPathHandler) => {
71
72
  handler.dispose(connection, clientId);
72
73
  });
73
74
  });
@@ -79,28 +80,26 @@ export class CommonChannelPathHandler {
79
80
 
80
81
  export const commonChannelPathHandler = new CommonChannelPathHandler();
81
82
 
82
- // 后台 Web 链接处理类
83
+ /**
84
+ * Channel Handler for nodejs
85
+ */
83
86
  export class CommonChannelHandler extends WebSocketHandler {
84
- static channelId = 0;
85
-
86
87
  public handlerId = 'common-channel';
87
- private wsServer: ws.Server;
88
- protected handlerRoute: (wsPathname: string) => any;
89
- private channelMap: Map<string | number, WSChannel> = new Map();
90
- private connectionMap: Map<string, ws> = new Map();
88
+ private wsServer: WebSocket.Server;
89
+ protected handlerRoute: MatchFunction;
90
+ private channelMap: Map<string, WSChannel> = new Map();
91
91
  private heartbeatMap: Map<string, NodeJS.Timeout> = new Map();
92
92
 
93
- constructor(routePath: string, private logger: any = console, private options: CommonChannelHandlerOptions = {}) {
93
+ constructor(routePath: string, private logger: ILogger = console, private options: CommonChannelHandlerOptions = {}) {
94
94
  super();
95
- const route = pathMatch(options.pathMatchOptions);
96
- this.handlerRoute = route(`${routePath}`);
95
+ this.handlerRoute = match(routePath, options.pathMatchOptions);
97
96
  this.initWSServer();
98
97
  }
99
- private hearbeat(connectionId: string, connection: ws) {
98
+
99
+ private heartbeat(connectionId: string, connection: WebSocket) {
100
100
  const timer = global.setTimeout(() => {
101
101
  connection.ping();
102
- // console.log(`connectionId ${connectionId} ping`);
103
- this.hearbeat(connectionId, connection);
102
+ this.heartbeat(connectionId, connection);
104
103
  }, 5000);
105
104
 
106
105
  this.heartbeatMap.set(connectionId, timer);
@@ -108,40 +107,38 @@ export class CommonChannelHandler extends WebSocketHandler {
108
107
 
109
108
  private initWSServer() {
110
109
  this.logger.log('init Common Channel Handler');
111
- this.wsServer = new ws.Server({
110
+ this.wsServer = new WebSocket.Server({
112
111
  noServer: true,
113
112
  ...this.options.wsServerOptions,
114
113
  });
115
- this.wsServer.on('connection', (connection: ws) => {
116
- let connectionId;
117
- connection.on('message', (msg: string) => {
114
+ this.wsServer.on('connection', (connection: WebSocket) => {
115
+ let clientId: string;
116
+
117
+ connection.on('message', (msg: Uint8Array) => {
118
118
  let msgObj: ChannelMessage;
119
119
  try {
120
120
  msgObj = parse(msg);
121
121
 
122
- // 心跳消息
123
- if (msgObj.kind === 'heartbeat') {
124
- connection.send(stringify(`heartbeat ${msgObj.clientId}`));
125
- } else if (msgObj.kind === 'client') {
126
- const clientId = msgObj.clientId;
127
- this.logger.log(`New connection with id ${clientId}`);
128
- connectionId = clientId;
129
- this.connectionMap.set(clientId, connection);
130
- this.hearbeat(connectionId, connection);
131
- // channel 消息处理
122
+ if (msgObj.kind === 'ping') {
123
+ connection.send(
124
+ stringify({
125
+ kind: 'pong',
126
+ id: msgObj.id,
127
+ clientId,
128
+ }),
129
+ );
132
130
  } else if (msgObj.kind === 'open') {
133
- const channelId = msgObj.id; // CommonChannelHandler.channelId ++;
134
- const { path } = msgObj;
135
- this.logger.log(`Open a new connection channel ${channelId} with path ${path}`);
131
+ const { id, path } = msgObj;
132
+ clientId = msgObj.clientId;
133
+ this.logger.log(`open a new connection channel ${clientId} with path ${path}`);
134
+ this.heartbeat(id, connection);
136
135
 
137
- // 生成 channel 对象
138
- const connectionSend = this.channelConnectionSend(connection);
139
- const channel = new WSChannel(connectionSend, channelId);
140
- this.channelMap.set(channelId, channel);
136
+ const channel = new WSChannel(new WSWebSocketConnection(connection), { id });
137
+ this.channelMap.set(id, channel);
141
138
 
142
139
  // 根据 path 拿到注册的 handler
143
140
  let handlerArr = commonChannelPathHandler.get(path);
144
- let params;
141
+ let params: Record<string, string> | undefined;
145
142
  // 尝试通过父路径查找处理函数,如server/:id方式注册的handler
146
143
  if (!handlerArr) {
147
144
  const slashIndex = path.indexOf('/');
@@ -155,11 +152,11 @@ export class CommonChannelHandler extends WebSocketHandler {
155
152
  if (handlerArr) {
156
153
  for (let i = 0, len = handlerArr.length; i < len; i++) {
157
154
  const handler = handlerArr[i];
158
- handler.handler(channel, connectionId, params);
155
+ handler.handler(channel, clientId, params);
159
156
  }
160
157
  }
161
158
 
162
- channel.ready();
159
+ channel.serverReady();
163
160
  } else {
164
161
  const { id } = msgObj;
165
162
  const channel = this.channelMap.get(id);
@@ -170,51 +167,42 @@ export class CommonChannelHandler extends WebSocketHandler {
170
167
  }
171
168
  }
172
169
  } catch (e) {
173
- this.logger.warn(e);
170
+ this.logger.error('handle connection message error', e);
174
171
  }
175
172
  });
176
173
 
177
- connection.on('close', () => {
178
- commonChannelPathHandler.disposeConnectionClientId(connection, connectionId as string);
174
+ connection.once('close', () => {
175
+ commonChannelPathHandler.disposeConnectionClientId(connection, clientId);
179
176
 
180
- if (this.heartbeatMap.has(connectionId)) {
181
- clearTimeout(this.heartbeatMap.get(connectionId) as NodeJS.Timeout);
182
- this.heartbeatMap.delete(connectionId);
177
+ if (this.heartbeatMap.has(clientId)) {
178
+ clearTimeout(this.heartbeatMap.get(clientId) as NodeJS.Timeout);
179
+ this.heartbeatMap.delete(clientId);
183
180
 
184
- this.logger.verbose(`Clear heartbeat from channel ${connectionId}`);
181
+ this.logger.log(`Clear heartbeat from channel ${clientId}`);
185
182
  }
186
183
 
187
184
  Array.from(this.channelMap.values())
188
- .filter((channel) => channel.id.toString().indexOf(connectionId) !== -1)
185
+ .filter((channel) => channel.id.toString().indexOf(clientId) !== -1)
189
186
  .forEach((channel) => {
190
187
  channel.close(1, 'close');
188
+ channel.dispose();
191
189
  this.channelMap.delete(channel.id);
192
- this.logger.verbose(`Remove connection channel ${channel.id}`);
190
+ this.logger.log(`Remove connection channel ${channel.id}`);
193
191
  });
194
192
  });
195
193
  });
196
194
  }
197
195
 
198
- private channelConnectionSend = (connection: ws) => (content: string) => {
199
- if (connection.readyState === connection.OPEN) {
200
- connection.send(content, (err: any) => {
201
- if (err) {
202
- this.logger.log(err);
203
- }
204
- });
205
- }
206
- };
207
- public handleUpgrade(wsPathname: string, request: any, socket: any, head: any): boolean {
208
- const routeResult = this.handlerRoute(wsPathname);
196
+ public handleUpgrade(pathname: string, request: any, socket: any, head: any): boolean {
197
+ const routeResult = this.handlerRoute(pathname);
209
198
 
210
199
  if (routeResult) {
211
- const wsServer = this.wsServer;
212
- wsServer.handleUpgrade(request, socket, head, (connection: any) => {
213
- connection.routeParam = {
214
- pathname: wsPathname,
200
+ this.wsServer.handleUpgrade(request, socket, head, (connection) => {
201
+ (connection as any).routeParam = {
202
+ pathname,
215
203
  };
216
204
 
217
- wsServer.emit('connection', connection);
205
+ this.wsServer.emit('connection', connection);
218
206
  });
219
207
  return true;
220
208
  }
package/src/node/index.ts CHANGED
@@ -1,7 +1,2 @@
1
- import { SocketMessageReader, SocketMessageWriter } from '@opensumi/vscode-jsonrpc/lib/node/main';
2
-
3
1
  export * from './ws';
4
2
  export * from './common-channel-handler';
5
- export * from './connect';
6
-
7
- export { SocketMessageReader, SocketMessageWriter };
package/src/node/ws.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import assert from 'assert';
1
2
  import http from 'http';
2
3
  import url from 'url';
3
4
 
@@ -5,7 +6,7 @@ import ws from 'ws';
5
6
 
6
7
  export abstract class WebSocketHandler {
7
8
  abstract handlerId: string;
8
- abstract handleUpgrade(wsPathname: string, request: any, socket: any, head: any): boolean;
9
+ abstract handleUpgrade(pathname: string, request: any, socket: any, head: any): boolean;
9
10
  init?(): void;
10
11
  }
11
12
 
@@ -88,6 +89,7 @@ export class WebSocketServerRoute {
88
89
  const wsServerHandlerArr = this.wsServerHandlerArr;
89
90
 
90
91
  server.on('upgrade', (request, socket, head) => {
92
+ assert(request.url, 'cannot parse url from http request');
91
93
  const wsPathname: string = url.parse(request.url).pathname as string;
92
94
 
93
95
  let wsHandlerIndex = 0;
@@ -1,47 +0,0 @@
1
- import type { MessageConnection } from '@opensumi/vscode-jsonrpc/lib/common/connection';
2
- export interface ILogger {
3
- warn(...args: any[]): void;
4
- }
5
- export declare abstract class RPCService<T = any> {
6
- rpcClient?: T[];
7
- rpcRegistered?: boolean;
8
- register?(): () => Promise<T>;
9
- get client(): T | undefined;
10
- }
11
- export declare const NOTREGISTERMETHOD = "$$NOTREGISTERMETHOD";
12
- export declare class ProxyClient {
13
- proxy: any;
14
- reservedWords: string[];
15
- constructor(proxy: any, reservedWords?: string[]);
16
- getClient(): {};
17
- }
18
- export declare class RPCProxy {
19
- target?: RPCService<any> | undefined;
20
- private connectionPromise;
21
- private connectionPromiseResolve;
22
- private connection;
23
- private proxyService;
24
- private logger;
25
- private capture;
26
- constructor(target?: RPCService<any> | undefined, logger?: ILogger);
27
- listenService(service: any): void;
28
- listen(connection: MessageConnection): void;
29
- createProxy(): any;
30
- get(target: any, p: PropertyKey): (...args: any[]) => Promise<unknown>;
31
- private getServiceMethod;
32
- private bindOnRequest;
33
- private waitForConnection;
34
- /**
35
- * 对于纯数组参数的情况,收到请求/通知后做展开操作
36
- * 因为在通信层会为每个 rpc 调用添加一个 CancellationToken 参数
37
- * 如果参数本身是数组, 在方法中如果使用 spread 运算符获取参数(...args),则会出现 [...args, MutableToken] 这种情况
38
- * 所以发送请求时将这类参数统一再用数组包了一层,形如 [[...args]], 参考 {@link RPCProxy.get get} 方法
39
- * 此时接收到的数组类参数固定长度为 2,且最后一项一定是 MutableToken
40
- * @param args
41
- * @returns args
42
- */
43
- private serializeArguments;
44
- private onRequest;
45
- private onNotification;
46
- }
47
- //# sourceMappingURL=proxy.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/common/proxy.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gDAAgD,CAAC;AAIxF,MAAM,WAAW,OAAO;IACtB,IAAI,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;CAC5B;AAED,8BAAsB,UAAU,CAAC,CAAC,GAAG,GAAG;IACtC,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC;IAChB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,IAAI,MAAM,OAAO,CAAC,CAAC,CAAC;IAC7B,IAAI,MAAM,kBAET;CACF;AAED,eAAO,MAAM,iBAAiB,wBAAwB,CAAC;AAEvD,qBAAa,WAAW;IACf,KAAK,EAAE,GAAG,CAAC;IACX,aAAa,EAAE,MAAM,EAAE,CAAC;gBAEnB,KAAK,EAAE,GAAG,EAAE,aAAa,WAAW;IAIzC,SAAS;CAcjB;AAMD,qBAAa,QAAQ;IAcA,MAAM,CAAC;IAb1B,OAAO,CAAC,iBAAiB,CAA6B;IACtD,OAAO,CAAC,wBAAwB,CAA0C;IAC1E,OAAO,CAAC,UAAU,CAAoB;IACtC,OAAO,CAAC,YAAY,CAAW;IAC/B,OAAO,CAAC,MAAM,CAAU;IAExB,OAAO,CAAC,OAAO;gBAOI,MAAM,CAAC,6BAAY,EAAE,MAAM,CAAC,EAAE,OAAO;IAIjD,aAAa,CAAC,OAAO,KAAA;IAerB,MAAM,CAAC,UAAU,EAAE,iBAAiB;IAUpC,WAAW,IAAI,GAAG;IAMlB,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,WAAW,aAGnB,GAAG,EAAE;IAmExB,OAAO,CAAC,gBAAgB;IAmBxB,OAAO,CAAC,aAAa;IAkErB,OAAO,CAAC,iBAAiB;IAMzB;;;;;;;;OAQG;IACH,OAAO,CAAC,kBAAkB;YASZ,SAAS;IAmBvB,OAAO,CAAC,cAAc;CAOvB"}