Skip to content

Commit 83d4308

Browse files
tortmayrtsmaeder
andauthored
Integrate new message-rpc prototype into core messaging API (extensions) (eclipse-theia#11011) (eclipse-theia#11228)
Refactors and improves the prototype of a faster JSON-RPC protocol initially contributed by @tsmaeder (See also eclipse-theia#10781). The encoding approach used in the initial POC has some performance drawbacks when encoding plain JSON objects. We refactored the protocol to improve the performance for JSON objects whilst maintaining the excellent performance for encoding objects that contain binary data. Integrates the new message-rpc prototype into the core messaging API (replacing vscode-ws-jsonrpc). This has major impacts on the Messaging API as we no longer expose a `Connection` object (which was provided by vscode-ws-jsonrpc) and directly rely on a generic transport `Channel` implementation instead. - Introduce `Channel` as the main transport concept for messages (instead of the dedicated `Connection` from vscode-jsonrpc) - Replace usage of `vscode-ws-jsonrpc` with a custom binary RPC protocol. - Remove 'vscode-ws-jsonrpc' depdency from "@theia/core/shared". - Refactor all connection providers to use the new binary protocol. - Ensure that the `RemoteFileSystemProvider` API uses `Uint8Arrays` over plain number arrays. This enables direct serialization as buffers and reduces the overhead of unnecessarily converting from and to `Uint8Arrays`. - Refactor terminal widget and terminal backend contribution so that the widgets communicates with the underlying terminal process using the new rpc protocol. - Rework the IPC bootstrap protocol so that it uses a binary pipe for message transmission instead of the `ipc` pipe which only supports string encoding. - Extend the `JsonRpcProxyFactory` with an optional `RpcConnectionFactory` that enables adopter to creates proxies with a that use a custom `RpcProtocol`/`RpcConnection`. The plugin API still uses its own RPC protocol implementation. This will be addressed in a follow-up PR. (See eclipse-theia#11093) Fix critical bugs - eclipse-theiaGH-11196 Remove dev/debug logoutput from IPCChannel - eclipse-theiaGH-11199 Refactor connection provider and channel multiplexer to properly handle a reconnecting backend - Remove console log in websocket-channel if the socket is not connected. Otherwise we end up in an endless loop. - Ensure that channels & RpcProtocol instances proplery dispose all resources if the backend disconnects. - Ensure that all previously open channels and RpcProtocol instances are properly restored once the backend reconnects. - eclipse-theia#11203 Ensure that debugging is handled gracefully (implicitly fixed with the fix for eclipse-theia#11199) - Remove dependency to `reconnecting-websocket` which is no longer needed since the swap to socket.io Fixes eclipse-theia#11196 Fixes eclipse-theia#11199 Contributed on behalf of STMicroelectronics. Co-authored-by: Thomas Mäder <[email protected]>
1 parent 53a272e commit 83d4308

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+2281
-642
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@
1717
- Added the following method to the interface `DebugService`: `fetchDynamicDebugConfiguration` as well as the property `onDidChangedDebugConfigurationProviders`.
1818
- Removed method `DebugPrefixConfiguration#runDynamicConfiguration`
1919
- [core] The interface `SelectComponentProps` was updated to rename a property from `value` to `defaultValue`
20+
- [core] Refactored the core messaging API. Replaced `vscode-ws-jsonrpc` with a custom RPC protocol that is better suited for handling binary data and enables message tunneling.
21+
This impacts all main concepts of the messaging API. The API no longer exposes a `Connection` object and uses a generic `Channel` implementation instead.
22+
- Replaces usage of `vscode-json-rpc`'s `Connection` with the new generic `Channel`. Affects `AbstractConnectionProvider`, `MessagingService`, `IPCConnectionProvider`, `ElectronMessagingService`
23+
- `MessagingService`: No longer offers the `listen` and `forward` method. Use `wsChannel` instead.
24+
- `RemoteFileSystemServer`: Use `UInt8Array` instead of plain number arrays for all arguments and return type that store binary data
25+
- `DebugAdapter`: Replaced the debug-service internal `Channel` implementation with the newly introduced generic `Channel`.
26+
[#11228](https://github.com/eclipse-theia/theia/pull/11228) - Contributed on behalf of STMicroelectronics.
2027

2128
## v1.26.0 - 5/26/2022
2229

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
"**/@types/node": "12"
1111
},
1212
"devDependencies": {
13+
"@types/chai": "4.3.0",
14+
"@types/chai-spies": "1.0.3",
1315
"@types/chai-string": "^1.4.0",
1416
"@types/jsdom": "^11.0.4",
1517
"@types/node": "12",
@@ -20,6 +22,8 @@
2022
"@typescript-eslint/eslint-plugin": "^4.8.1",
2123
"@typescript-eslint/eslint-plugin-tslint": "^4.8.1",
2224
"@typescript-eslint/parser": "^4.8.1",
25+
"chai": "4.3.4",
26+
"chai-spies": "1.0.0",
2327
"chai-string": "^1.4.0",
2428
"chalk": "4.0.0",
2529
"concurrently": "^3.5.0",

packages/core/README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,6 @@ export class SomeClass {
9999
- `react-virtualized` (from [`react-virtualized@^9.20.0`](https://www.npmjs.com/package/react-virtualized))
100100
- `vscode-languageserver-protocol` (from [`vscode-languageserver-protocol@~3.15.3`](https://www.npmjs.com/package/vscode-languageserver-protocol))
101101
- `vscode-uri` (from [`vscode-uri@^2.1.1`](https://www.npmjs.com/package/vscode-uri))
102-
- `vscode-ws-jsonrpc` (from [`vscode-ws-jsonrpc@^0.2.0`](https://www.npmjs.com/package/vscode-ws-jsonrpc))
103102
- `dompurify` (from [`dompurify@^2.2.9`](https://www.npmjs.com/package/dompurify))
104103
- `express` (from [`express@^4.16.3`](https://www.npmjs.com/package/express))
105104
- `lodash.debounce` (from [`lodash.debounce@^4.0.8`](https://www.npmjs.com/package/lodash.debounce))

packages/core/package.json

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@
6161
"react-dom": "^16.8.0",
6262
"react-tooltip": "^4.2.21",
6363
"react-virtualized": "^9.20.0",
64-
"reconnecting-websocket": "^4.2.0",
6564
"reflect-metadata": "^0.1.10",
6665
"route-parser": "^0.0.5",
6766
"safer-buffer": "^2.1.2",
@@ -70,7 +69,6 @@
7069
"uuid": "^8.3.2",
7170
"vscode-languageserver-protocol": "~3.15.3",
7271
"vscode-uri": "^2.1.1",
73-
"vscode-ws-jsonrpc": "^0.2.0",
7472
"ws": "^7.1.2",
7573
"yargs": "^15.3.1"
7674
},
@@ -113,8 +111,7 @@
113111
"react-dom",
114112
"react-virtualized",
115113
"vscode-languageserver-protocol",
116-
"vscode-uri",
117-
"vscode-ws-jsonrpc"
114+
"vscode-uri"
118115
],
119116
"export =": [
120117
"dompurify as DOMPurify",

packages/core/shared/vscode-ws-jsonrpc/index.d.ts

Lines changed: 0 additions & 1 deletion
This file was deleted.

packages/core/shared/vscode-ws-jsonrpc/index.js

Lines changed: 0 additions & 1 deletion
This file was deleted.

packages/core/src/browser/messaging/ws-connection-provider.ts

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@
1515
// *****************************************************************************
1616

1717
import { injectable, interfaces, decorate, unmanaged } from 'inversify';
18-
import { JsonRpcProxyFactory, JsonRpcProxy, Emitter, Event } from '../../common';
19-
import { WebSocketChannel } from '../../common/messaging/web-socket-channel';
18+
import { JsonRpcProxyFactory, JsonRpcProxy, Emitter, Event, Channel } from '../../common';
2019
import { Endpoint } from '../endpoint';
2120
import { AbstractConnectionProvider } from '../../common/messaging/abstract-connection-provider';
2221
import { io, Socket } from 'socket.io-client';
22+
import { IWebSocket, WebSocketChannel } from '../../common/messaging/web-socket-channel';
2323

2424
decorate(injectable(), JsonRpcProxyFactory);
2525
decorate(unmanaged(), JsonRpcProxyFactory, 0);
@@ -53,26 +53,42 @@ export class WebSocketConnectionProvider extends AbstractConnectionProvider<WebS
5353
constructor() {
5454
super();
5555
const url = this.createWebSocketUrl(WebSocketChannel.wsPath);
56-
const socket = this.createWebSocket(url);
57-
socket.on('connect', () => {
58-
this.fireSocketDidOpen();
59-
});
60-
socket.on('disconnect', reason => {
61-
for (const channel of [...this.channels.values()]) {
62-
channel.close(undefined, reason);
56+
this.socket = this.createWebSocket(url);
57+
this.socket.on('connect', () => {
58+
this.initializeMultiplexer();
59+
if (this.reconnectChannelOpeners.length > 0) {
60+
this.reconnectChannelOpeners.forEach(opener => opener());
61+
this.reconnectChannelOpeners = [];
6362
}
64-
this.fireSocketDidClose();
65-
});
66-
socket.on('message', data => {
67-
this.handleIncomingRawMessage(data);
63+
this.socket.on('disconnect', () => this.fireSocketDidClose());
64+
this.socket.on('message', () => this.onIncomingMessageActivityEmitter.fire(undefined));
65+
this.fireSocketDidOpen();
6866
});
69-
socket.connect();
70-
this.socket = socket;
67+
this.socket.connect();
7168
}
7269

73-
override openChannel(path: string, handler: (channel: WebSocketChannel) => void, options?: WebSocketOptions): void {
70+
protected createMainChannel(): Channel {
71+
return new WebSocketChannel(this.toIWebSocket(this.socket));
72+
}
73+
74+
protected toIWebSocket(socket: Socket): IWebSocket {
75+
return {
76+
close: () => {
77+
socket.removeAllListeners('disconnect');
78+
socket.removeAllListeners('error');
79+
socket.removeAllListeners('message');
80+
},
81+
isConnected: () => socket.connected,
82+
onClose: cb => socket.on('disconnect', reason => cb(reason)),
83+
onError: cb => socket.on('error', reason => cb(reason)),
84+
onMessage: cb => socket.on('message', data => cb(data)),
85+
send: message => socket.emit('message', message)
86+
};
87+
}
88+
89+
override async openChannel(path: string, handler: (channel: Channel) => void, options?: WebSocketOptions): Promise<void> {
7490
if (this.socket.connected) {
75-
super.openChannel(path, handler, options);
91+
return super.openChannel(path, handler, options);
7692
} else {
7793
const openChannel = () => {
7894
this.socket.off('connect', openChannel);
@@ -82,14 +98,6 @@ export class WebSocketConnectionProvider extends AbstractConnectionProvider<WebS
8298
}
8399
}
84100

85-
protected createChannel(id: number): WebSocketChannel {
86-
return new WebSocketChannel(id, content => {
87-
if (this.socket.connected) {
88-
this.socket.send(content);
89-
}
90-
});
91-
}
92-
93101
/**
94102
* @param path The handler to reach in the backend.
95103
*/
@@ -143,3 +151,4 @@ export class WebSocketConnectionProvider extends AbstractConnectionProvider<WebS
143151
this.onSocketDidCloseEmitter.fire(undefined);
144152
}
145153
}
154+

packages/core/src/browser/progress-status-bar-item.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
// *****************************************************************************
1616

1717
import { injectable, inject } from 'inversify';
18-
import { CancellationToken } from 'vscode-ws-jsonrpc';
18+
import { CancellationToken } from '../../shared/vscode-languageserver-protocol';
1919
import { ProgressClient, ProgressMessage, ProgressUpdate } from '../common';
2020
import { StatusBar, StatusBarAlignment } from './status-bar';
2121
import { Deferred } from '../common/promise-util';

packages/core/src/common/cancellation.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,14 @@ export namespace CancellationToken {
4949
isCancellationRequested: true,
5050
onCancellationRequested: shortcutEvent
5151
});
52+
53+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
54+
export function is(value: any): value is CancellationToken {
55+
const candidate = value as CancellationToken;
56+
return candidate && (candidate === CancellationToken.None
57+
|| candidate === CancellationToken.Cancelled
58+
|| (typeof candidate.isCancellationRequested === 'boolean' && !!candidate.onCancellationRequested));
59+
}
5260
}
5361

5462
export class CancellationError extends Error {

packages/core/src/common/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export * from './contribution-provider';
2929
export * from './path';
3030
export * from './logger';
3131
export * from './messaging';
32+
export * from './message-rpc';
3233
export * from './message-service';
3334
export * from './message-service-protocol';
3435
export * from './progress-service';

0 commit comments

Comments
 (0)