@theia/core 1.26.0 → 1.27.0-next.10
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/README.md +6 -7
- package/lib/browser/messaging/ws-connection-provider.d.ts +5 -4
- package/lib/browser/messaging/ws-connection-provider.d.ts.map +1 -1
- package/lib/browser/messaging/ws-connection-provider.js +30 -23
- package/lib/browser/messaging/ws-connection-provider.js.map +1 -1
- package/lib/browser/progress-status-bar-item.d.ts +1 -1
- package/lib/browser/progress-status-bar-item.d.ts.map +1 -1
- package/lib/browser/tree/tree-compression/compressed-tree-widget.js +2 -2
- package/lib/browser/tree/tree-compression/compressed-tree-widget.js.map +1 -1
- package/lib/browser/widgets/select-component.d.ts +4 -1
- package/lib/browser/widgets/select-component.d.ts.map +1 -1
- package/lib/browser/widgets/select-component.js +30 -16
- package/lib/browser/widgets/select-component.js.map +1 -1
- package/lib/common/cancellation.d.ts +1 -0
- package/lib/common/cancellation.d.ts.map +1 -1
- package/lib/common/cancellation.js +8 -0
- package/lib/common/cancellation.js.map +1 -1
- package/lib/common/index.d.ts +2 -0
- package/lib/common/index.d.ts.map +1 -1
- package/lib/common/index.js +2 -0
- package/lib/common/index.js.map +1 -1
- package/lib/common/message-rpc/channel.d.ts +106 -0
- package/lib/common/message-rpc/channel.d.ts.map +1 -0
- package/lib/common/message-rpc/channel.js +195 -0
- package/lib/common/message-rpc/channel.js.map +1 -0
- package/lib/common/message-rpc/channel.spec.d.ts +9 -0
- package/lib/common/message-rpc/channel.spec.d.ts.map +1 -0
- package/lib/common/message-rpc/channel.spec.js +80 -0
- package/lib/common/message-rpc/channel.spec.js.map +1 -0
- package/lib/common/message-rpc/index.d.ts +4 -0
- package/lib/common/message-rpc/index.d.ts.map +1 -0
- package/lib/{node/messaging/logger.js → common/message-rpc/index.js} +6 -19
- package/lib/common/message-rpc/index.js.map +1 -0
- package/lib/common/message-rpc/message-buffer.d.ts +50 -0
- package/lib/common/message-rpc/message-buffer.d.ts.map +1 -0
- package/lib/common/message-rpc/message-buffer.js +56 -0
- package/lib/common/message-rpc/message-buffer.js.map +1 -0
- package/lib/common/message-rpc/rpc-message-encoder.d.ts +159 -0
- package/lib/common/message-rpc/rpc-message-encoder.d.ts.map +1 -0
- package/lib/common/message-rpc/rpc-message-encoder.js +362 -0
- package/lib/common/message-rpc/rpc-message-encoder.js.map +1 -0
- package/lib/common/message-rpc/rpc-message-encoder.spec.d.ts +2 -0
- package/lib/common/message-rpc/rpc-message-encoder.spec.d.ts.map +1 -0
- package/lib/common/message-rpc/rpc-message-encoder.spec.js +37 -0
- package/lib/common/message-rpc/rpc-message-encoder.spec.js.map +1 -0
- package/lib/common/message-rpc/rpc-protocol.d.ts +61 -0
- package/lib/common/message-rpc/rpc-protocol.d.ts.map +1 -0
- package/lib/common/message-rpc/rpc-protocol.js +183 -0
- package/lib/common/message-rpc/rpc-protocol.js.map +1 -0
- package/lib/common/message-rpc/uint8-array-message-buffer.d.ts +52 -0
- package/lib/common/message-rpc/uint8-array-message-buffer.d.ts.map +1 -0
- package/lib/common/message-rpc/uint8-array-message-buffer.js +169 -0
- package/lib/common/message-rpc/uint8-array-message-buffer.js.map +1 -0
- package/lib/common/message-rpc/uint8-array-message-buffer.spec.d.ts +2 -0
- package/lib/common/message-rpc/uint8-array-message-buffer.spec.d.ts.map +1 -0
- package/lib/common/message-rpc/uint8-array-message-buffer.spec.js +39 -0
- package/lib/common/message-rpc/uint8-array-message-buffer.spec.js.map +1 -0
- package/lib/common/messaging/abstract-connection-provider.d.ts +9 -8
- package/lib/common/messaging/abstract-connection-provider.d.ts.map +1 -1
- package/lib/common/messaging/abstract-connection-provider.js +20 -35
- package/lib/common/messaging/abstract-connection-provider.js.map +1 -1
- package/lib/common/messaging/connection-error-handler.d.ts +1 -2
- package/lib/common/messaging/connection-error-handler.d.ts.map +1 -1
- package/lib/common/messaging/connection-error-handler.js +1 -1
- package/lib/common/messaging/connection-error-handler.js.map +1 -1
- package/lib/common/messaging/handler.d.ts +2 -2
- package/lib/common/messaging/handler.d.ts.map +1 -1
- package/lib/common/messaging/proxy-factory.d.ts +13 -7
- package/lib/common/messaging/proxy-factory.d.ts.map +1 -1
- package/lib/common/messaging/proxy-factory.js +18 -13
- package/lib/common/messaging/proxy-factory.js.map +1 -1
- package/lib/common/messaging/proxy-factory.spec.js +4 -15
- package/lib/common/messaging/proxy-factory.spec.js.map +1 -1
- package/lib/common/messaging/web-socket-channel.d.ts +54 -48
- package/lib/common/messaging/web-socket-channel.d.ts.map +1 -1
- package/lib/common/messaging/web-socket-channel.js +41 -105
- package/lib/common/messaging/web-socket-channel.js.map +1 -1
- package/lib/electron-browser/messaging/electron-ipc-connection-provider.d.ts +2 -2
- package/lib/electron-browser/messaging/electron-ipc-connection-provider.d.ts.map +1 -1
- package/lib/electron-browser/messaging/electron-ipc-connection-provider.js +18 -7
- package/lib/electron-browser/messaging/electron-ipc-connection-provider.js.map +1 -1
- package/lib/electron-browser/messaging/electron-ws-connection-provider.d.ts +2 -2
- package/lib/electron-browser/messaging/electron-ws-connection-provider.d.ts.map +1 -1
- package/lib/electron-browser/messaging/electron-ws-connection-provider.js +5 -7
- package/lib/electron-browser/messaging/electron-ws-connection-provider.js.map +1 -1
- package/lib/electron-main/messaging/electron-messaging-contribution.d.ts +37 -9
- package/lib/electron-main/messaging/electron-messaging-contribution.d.ts.map +1 -1
- package/lib/electron-main/messaging/electron-messaging-contribution.js +83 -68
- package/lib/electron-main/messaging/electron-messaging-contribution.js.map +1 -1
- package/lib/electron-main/messaging/electron-messaging-service.d.ts +2 -8
- package/lib/electron-main/messaging/electron-messaging-service.d.ts.map +1 -1
- package/lib/electron-main/messaging/electron-messaging-service.js.map +1 -1
- package/lib/electron-main/theia-electron-window.d.ts.map +1 -1
- package/lib/electron-main/theia-electron-window.js +11 -8
- package/lib/electron-main/theia-electron-window.js.map +1 -1
- package/lib/node/messaging/binary-message-pipe.d.ts +45 -0
- package/lib/node/messaging/binary-message-pipe.d.ts.map +1 -0
- package/lib/node/messaging/binary-message-pipe.js +152 -0
- package/lib/node/messaging/binary-message-pipe.js.map +1 -0
- package/lib/node/messaging/ipc-bootstrap.js +2 -11
- package/lib/node/messaging/ipc-bootstrap.js.map +1 -1
- package/lib/node/messaging/ipc-channel.d.ts +26 -0
- package/lib/node/messaging/ipc-channel.d.ts.map +1 -0
- package/lib/node/messaging/ipc-channel.js +86 -0
- package/lib/node/messaging/ipc-channel.js.map +1 -0
- package/lib/node/messaging/ipc-connection-provider.d.ts +3 -5
- package/lib/node/messaging/ipc-connection-provider.d.ts.map +1 -1
- package/lib/node/messaging/ipc-connection-provider.js +14 -31
- package/lib/node/messaging/ipc-connection-provider.js.map +1 -1
- package/lib/node/messaging/ipc-protocol.d.ts +2 -2
- package/lib/node/messaging/ipc-protocol.d.ts.map +1 -1
- package/lib/node/messaging/messaging-contribution.d.ts +6 -9
- package/lib/node/messaging/messaging-contribution.d.ts.map +1 -1
- package/lib/node/messaging/messaging-contribution.js +23 -68
- package/lib/node/messaging/messaging-contribution.js.map +1 -1
- package/lib/node/messaging/messaging-service.d.ts +4 -23
- package/lib/node/messaging/messaging-service.d.ts.map +1 -1
- package/lib/node/messaging/messaging-service.js +1 -15
- package/lib/node/messaging/messaging-service.js.map +1 -1
- package/lib/node/messaging/test/test-web-socket-channel.d.ts +4 -2
- package/lib/node/messaging/test/test-web-socket-channel.d.ts.map +1 -1
- package/lib/node/messaging/test/test-web-socket-channel.js +25 -12
- package/lib/node/messaging/test/test-web-socket-channel.js.map +1 -1
- package/package.json +5 -8
- package/src/browser/messaging/ws-connection-provider.ts +34 -25
- package/src/browser/progress-status-bar-item.ts +1 -1
- package/src/browser/style/menus.css +1 -0
- package/src/browser/tree/tree-compression/compressed-tree-widget.tsx +2 -2
- package/src/browser/widgets/select-component.tsx +37 -17
- package/src/common/cancellation.ts +8 -0
- package/src/common/index.ts +2 -0
- package/src/common/message-rpc/channel.spec.ts +88 -0
- package/src/common/message-rpc/channel.ts +260 -0
- package/src/{node/messaging/logger.ts → common/message-rpc/index.ts} +4 -23
- package/src/common/message-rpc/message-buffer.ts +99 -0
- package/src/common/message-rpc/rpc-message-encoder.spec.ts +42 -0
- package/src/common/message-rpc/rpc-message-encoder.ts +497 -0
- package/src/common/message-rpc/rpc-protocol.ts +217 -0
- package/src/common/message-rpc/uint8-array-message-buffer.spec.ts +41 -0
- package/src/common/message-rpc/uint8-array-message-buffer.ts +206 -0
- package/src/common/messaging/abstract-connection-provider.ts +28 -37
- package/src/common/messaging/connection-error-handler.ts +1 -2
- package/src/common/messaging/handler.ts +2 -2
- package/src/common/messaging/proxy-factory.spec.ts +4 -17
- package/src/common/messaging/proxy-factory.ts +27 -16
- package/src/common/messaging/web-socket-channel.ts +79 -135
- package/src/electron-browser/messaging/electron-ipc-connection-provider.ts +21 -7
- package/src/electron-browser/messaging/electron-ws-connection-provider.ts +5 -8
- package/src/electron-main/messaging/electron-messaging-contribution.ts +87 -65
- package/src/electron-main/messaging/electron-messaging-service.ts +2 -8
- package/src/electron-main/theia-electron-window.ts +12 -9
- package/src/node/messaging/binary-message-pipe.ts +168 -0
- package/src/node/messaging/ipc-bootstrap.ts +3 -11
- package/src/node/messaging/ipc-channel.ts +97 -0
- package/src/node/messaging/ipc-connection-provider.ts +18 -35
- package/src/node/messaging/ipc-protocol.ts +2 -2
- package/src/node/messaging/messaging-contribution.ts +29 -74
- package/src/node/messaging/messaging-service.ts +4 -31
- package/src/node/messaging/test/test-web-socket-channel.ts +26 -17
- package/lib/node/messaging/logger.d.ts +0 -8
- package/lib/node/messaging/logger.d.ts.map +0 -1
- package/lib/node/messaging/logger.js.map +0 -1
- package/shared/vscode-ws-jsonrpc/index.d.ts +0 -1
- package/shared/vscode-ws-jsonrpc/index.js +0 -1
|
@@ -16,10 +16,12 @@
|
|
|
16
16
|
|
|
17
17
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
18
18
|
|
|
19
|
-
import {
|
|
19
|
+
import { ResponseError } from '../message-rpc/rpc-message-encoder';
|
|
20
20
|
import { ApplicationError } from '../application-error';
|
|
21
|
-
import { Event, Emitter } from '../event';
|
|
22
21
|
import { Disposable } from '../disposable';
|
|
22
|
+
import { Emitter, Event } from '../event';
|
|
23
|
+
import { Channel } from '../message-rpc/channel';
|
|
24
|
+
import { RequestHandler, RpcProtocol } from '../message-rpc/rpc-protocol';
|
|
23
25
|
import { ConnectionHandler } from './handler';
|
|
24
26
|
|
|
25
27
|
export type JsonRpcServer<Client> = Disposable & {
|
|
@@ -45,13 +47,19 @@ export class JsonRpcConnectionHandler<T extends object> implements ConnectionHan
|
|
|
45
47
|
readonly factoryConstructor: new () => JsonRpcProxyFactory<T> = JsonRpcProxyFactory
|
|
46
48
|
) { }
|
|
47
49
|
|
|
48
|
-
onConnection(connection:
|
|
50
|
+
onConnection(connection: Channel): void {
|
|
49
51
|
const factory = new this.factoryConstructor();
|
|
50
52
|
const proxy = factory.createProxy();
|
|
51
53
|
factory.target = this.targetFactory(proxy);
|
|
52
54
|
factory.listen(connection);
|
|
53
55
|
}
|
|
54
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Factory for creating a new {@link RpcConnection} for a given chanel and {@link RequestHandler}.
|
|
59
|
+
*/
|
|
60
|
+
export type RpcConnectionFactory = (channel: Channel, requestHandler: RequestHandler) => RpcProtocol;
|
|
61
|
+
|
|
62
|
+
const defaultRPCConnectionFactory: RpcConnectionFactory = (channel, requestHandler) => new RpcProtocol(channel, requestHandler);
|
|
55
63
|
|
|
56
64
|
/**
|
|
57
65
|
* Factory for JSON-RPC proxy objects.
|
|
@@ -95,13 +103,14 @@ export class JsonRpcConnectionHandler<T extends object> implements ConnectionHan
|
|
|
95
103
|
*
|
|
96
104
|
* @param <T> - The type of the object to expose to JSON-RPC.
|
|
97
105
|
*/
|
|
106
|
+
|
|
98
107
|
export class JsonRpcProxyFactory<T extends object> implements ProxyHandler<T> {
|
|
99
108
|
|
|
100
109
|
protected readonly onDidOpenConnectionEmitter = new Emitter<void>();
|
|
101
110
|
protected readonly onDidCloseConnectionEmitter = new Emitter<void>();
|
|
102
111
|
|
|
103
|
-
protected connectionPromiseResolve: (connection:
|
|
104
|
-
protected connectionPromise: Promise<
|
|
112
|
+
protected connectionPromiseResolve: (connection: RpcProtocol) => void;
|
|
113
|
+
protected connectionPromise: Promise<RpcProtocol>;
|
|
105
114
|
|
|
106
115
|
/**
|
|
107
116
|
* Build a new JsonRpcProxyFactory.
|
|
@@ -109,7 +118,7 @@ export class JsonRpcProxyFactory<T extends object> implements ProxyHandler<T> {
|
|
|
109
118
|
* @param target - The object to expose to JSON-RPC methods calls. If this
|
|
110
119
|
* is omitted, the proxy won't be able to handle requests, only send them.
|
|
111
120
|
*/
|
|
112
|
-
constructor(public target?: any) {
|
|
121
|
+
constructor(public target?: any, protected rpcConnectionFactory = defaultRPCConnectionFactory) {
|
|
113
122
|
this.waitForConnection();
|
|
114
123
|
}
|
|
115
124
|
|
|
@@ -118,9 +127,11 @@ export class JsonRpcProxyFactory<T extends object> implements ProxyHandler<T> {
|
|
|
118
127
|
this.connectionPromiseResolve = resolve
|
|
119
128
|
);
|
|
120
129
|
this.connectionPromise.then(connection => {
|
|
121
|
-
connection.onClose(() =>
|
|
122
|
-
this.onDidCloseConnectionEmitter.fire(undefined)
|
|
123
|
-
|
|
130
|
+
connection.channel.onClose(() => {
|
|
131
|
+
this.onDidCloseConnectionEmitter.fire(undefined);
|
|
132
|
+
// Wait for connection in case the backend reconnects
|
|
133
|
+
this.waitForConnection();
|
|
134
|
+
});
|
|
124
135
|
this.onDidOpenConnectionEmitter.fire(undefined);
|
|
125
136
|
});
|
|
126
137
|
}
|
|
@@ -131,11 +142,10 @@ export class JsonRpcProxyFactory<T extends object> implements ProxyHandler<T> {
|
|
|
131
142
|
* This connection will be used to send/receive JSON-RPC requests and
|
|
132
143
|
* response.
|
|
133
144
|
*/
|
|
134
|
-
listen(
|
|
135
|
-
connection.
|
|
136
|
-
connection.onNotification(
|
|
137
|
-
|
|
138
|
-
connection.listen();
|
|
145
|
+
listen(channel: Channel): void {
|
|
146
|
+
const connection = this.rpcConnectionFactory(channel, (meth, args) => this.onRequest(meth, ...args));
|
|
147
|
+
connection.onNotification(event => this.onNotification(event.method, ...event.args));
|
|
148
|
+
|
|
139
149
|
this.connectionPromiseResolve(connection);
|
|
140
150
|
}
|
|
141
151
|
|
|
@@ -239,10 +249,10 @@ export class JsonRpcProxyFactory<T extends object> implements ProxyHandler<T> {
|
|
|
239
249
|
new Promise<void>((resolve, reject) => {
|
|
240
250
|
try {
|
|
241
251
|
if (isNotify) {
|
|
242
|
-
connection.sendNotification(method,
|
|
252
|
+
connection.sendNotification(method, args);
|
|
243
253
|
resolve(undefined);
|
|
244
254
|
} else {
|
|
245
|
-
const resultPromise = connection.sendRequest(method,
|
|
255
|
+
const resultPromise = connection.sendRequest(method, args) as Promise<any>;
|
|
246
256
|
resultPromise
|
|
247
257
|
.catch((err: any) => reject(this.deserializeError(capturedError, err)))
|
|
248
258
|
.then((result: any) => resolve(result));
|
|
@@ -293,3 +303,4 @@ export class JsonRpcProxyFactory<T extends object> implements ProxyHandler<T> {
|
|
|
293
303
|
}
|
|
294
304
|
|
|
295
305
|
}
|
|
306
|
+
|
|
@@ -16,157 +16,101 @@
|
|
|
16
16
|
|
|
17
17
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
18
18
|
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
import { Emitter, Event } from '../event';
|
|
20
|
+
import { WriteBuffer } from '../message-rpc';
|
|
21
|
+
import { Uint8ArrayReadBuffer, Uint8ArrayWriteBuffer } from '../message-rpc/uint8-array-message-buffer';
|
|
22
|
+
import { Channel, MessageProvider, ChannelCloseEvent } from '../message-rpc/channel';
|
|
23
|
+
import { DisposableCollection } from '../disposable';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* A channel that manages the main websocket connection between frontend and backend. All service channels
|
|
27
|
+
* are reusing this main channel. (multiplexing). An {@link IWebSocket} abstraction is used to keep the implementation
|
|
28
|
+
* independent of the actual websocket implementation and its execution context (backend vs. frontend).
|
|
29
|
+
*/
|
|
30
|
+
export class WebSocketChannel implements Channel {
|
|
25
31
|
static wsPath = '/services';
|
|
26
32
|
|
|
27
|
-
protected readonly
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
constructor(
|
|
31
|
-
readonly id: number,
|
|
32
|
-
protected readonly doSend: (content: string) => void
|
|
33
|
-
) { }
|
|
34
|
-
|
|
35
|
-
dispose(): void {
|
|
36
|
-
this.toDispose.dispose();
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
protected checkNotDisposed(): void {
|
|
40
|
-
if (this.toDispose.disposed) {
|
|
41
|
-
throw new Error('The channel has been disposed.');
|
|
42
|
-
}
|
|
33
|
+
protected readonly onCloseEmitter: Emitter<ChannelCloseEvent> = new Emitter();
|
|
34
|
+
get onClose(): Event<ChannelCloseEvent> {
|
|
35
|
+
return this.onCloseEmitter.event;
|
|
43
36
|
}
|
|
44
37
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
} else if (message.kind === 'data') {
|
|
49
|
-
this.fireMessage(message.content);
|
|
50
|
-
} else if (message.kind === 'close') {
|
|
51
|
-
this.fireClose(message.code, message.reason);
|
|
52
|
-
}
|
|
38
|
+
protected readonly onMessageEmitter: Emitter<MessageProvider> = new Emitter();
|
|
39
|
+
get onMessage(): Event<MessageProvider> {
|
|
40
|
+
return this.onMessageEmitter.event;
|
|
53
41
|
}
|
|
54
42
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
this.
|
|
58
|
-
kind: 'open',
|
|
59
|
-
id: this.id,
|
|
60
|
-
path
|
|
61
|
-
}));
|
|
43
|
+
protected readonly onErrorEmitter: Emitter<unknown> = new Emitter();
|
|
44
|
+
get onError(): Event<unknown> {
|
|
45
|
+
return this.onErrorEmitter.event;
|
|
62
46
|
}
|
|
63
47
|
|
|
64
|
-
|
|
65
|
-
this.checkNotDisposed();
|
|
66
|
-
this.doSend(JSON.stringify(<WebSocketChannel.ReadyMessage>{
|
|
67
|
-
kind: 'ready',
|
|
68
|
-
id: this.id
|
|
69
|
-
}));
|
|
70
|
-
}
|
|
48
|
+
protected toDispose = new DisposableCollection();
|
|
71
49
|
|
|
72
|
-
|
|
73
|
-
this.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
50
|
+
constructor(protected readonly socket: IWebSocket) {
|
|
51
|
+
this.toDispose.pushAll([this.onCloseEmitter, this.onMessageEmitter, this.onErrorEmitter]);
|
|
52
|
+
socket.onClose((reason, code) => this.onCloseEmitter.fire({ reason, code }));
|
|
53
|
+
socket.onClose(() => this.close());
|
|
54
|
+
socket.onError(error => this.onErrorEmitter.fire(error));
|
|
55
|
+
// eslint-disable-next-line arrow-body-style
|
|
56
|
+
socket.onMessage(data => this.onMessageEmitter.fire(() => {
|
|
57
|
+
// In the browser context socketIO receives binary messages as ArrayBuffers.
|
|
58
|
+
// So we have to convert them to a Uint8Array before delegating the message to the read buffer.
|
|
59
|
+
const buffer = data instanceof ArrayBuffer ? new Uint8Array(data) : data;
|
|
60
|
+
return new Uint8ArrayReadBuffer(buffer);
|
|
78
61
|
}));
|
|
79
62
|
}
|
|
80
63
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
// Do not try to close the channel if it is already closing.
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
this.checkNotDisposed();
|
|
87
|
-
this.doSend(JSON.stringify(<WebSocketChannel.CloseMessage>{
|
|
88
|
-
kind: 'close',
|
|
89
|
-
id: this.id,
|
|
90
|
-
code,
|
|
91
|
-
reason
|
|
92
|
-
}));
|
|
93
|
-
this.fireClose(code, reason);
|
|
94
|
-
}
|
|
64
|
+
getWriteBuffer(): WriteBuffer {
|
|
65
|
+
const result = new Uint8ArrayWriteBuffer();
|
|
95
66
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
this.doSend(JSON.stringify(<WebSocketChannel.CloseMessage>{
|
|
102
|
-
kind: 'close',
|
|
103
|
-
id: this.id,
|
|
104
|
-
code,
|
|
105
|
-
reason
|
|
106
|
-
}));
|
|
107
|
-
this.fireClose(code, reason);
|
|
108
|
-
}
|
|
67
|
+
result.onCommit(buffer => {
|
|
68
|
+
if (this.socket.isConnected()) {
|
|
69
|
+
this.socket.send(buffer);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
109
72
|
|
|
110
|
-
|
|
111
|
-
onOpen(cb: () => void): void {
|
|
112
|
-
this.checkNotDisposed();
|
|
113
|
-
this.fireOpen = cb;
|
|
114
|
-
this.toDispose.push(Disposable.create(() => this.fireOpen = () => { }));
|
|
73
|
+
return result;
|
|
115
74
|
}
|
|
116
75
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
this.
|
|
120
|
-
this.fireMessage = cb;
|
|
121
|
-
this.toDispose.push(Disposable.create(() => this.fireMessage = () => { }));
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
fireError: (reason: any) => void = () => { };
|
|
125
|
-
onError(cb: (reason: any) => void): void {
|
|
126
|
-
this.checkNotDisposed();
|
|
127
|
-
this.fireError = cb;
|
|
128
|
-
this.toDispose.push(Disposable.create(() => this.fireError = () => { }));
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
protected closing = false;
|
|
132
|
-
protected fireClose(code: number, reason: string): void {
|
|
133
|
-
if (this.closing) {
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
this.closing = true;
|
|
137
|
-
try {
|
|
138
|
-
this.closeEmitter.fire([code, reason]);
|
|
139
|
-
} finally {
|
|
140
|
-
this.closing = false;
|
|
141
|
-
}
|
|
142
|
-
this.dispose();
|
|
143
|
-
}
|
|
144
|
-
onClose(cb: (code: number, reason: string) => void): Disposable {
|
|
145
|
-
this.checkNotDisposed();
|
|
146
|
-
return this.closeEmitter.event(([code, reason]) => cb(code, reason));
|
|
76
|
+
close(): void {
|
|
77
|
+
this.toDispose.dispose();
|
|
78
|
+
this.socket.close();
|
|
147
79
|
}
|
|
148
|
-
|
|
149
80
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* An abstraction that enables reuse of the `{@link WebSocketChannel} class in the frontend and backend
|
|
84
|
+
* independent of the actual underlying socket implementation.
|
|
85
|
+
*/
|
|
86
|
+
export interface IWebSocket {
|
|
87
|
+
/**
|
|
88
|
+
* Sends the given message over the web socket in binary format.
|
|
89
|
+
* @param message The binary message.
|
|
90
|
+
*/
|
|
91
|
+
send(message: Uint8Array): void;
|
|
92
|
+
/**
|
|
93
|
+
* Closes the websocket from the local side.
|
|
94
|
+
*/
|
|
95
|
+
close(): void;
|
|
96
|
+
/**
|
|
97
|
+
* The connection state of the web socket.
|
|
98
|
+
*/
|
|
99
|
+
isConnected(): boolean;
|
|
100
|
+
/**
|
|
101
|
+
* Listener callback to handle incoming messages.
|
|
102
|
+
* @param cb The callback.
|
|
103
|
+
*/
|
|
104
|
+
onMessage(cb: (message: Uint8Array) => void): void;
|
|
105
|
+
/**
|
|
106
|
+
* Listener callback to handle socket errors.
|
|
107
|
+
* @param cb The callback.
|
|
108
|
+
*/
|
|
109
|
+
onError(cb: (reason: any) => void): void;
|
|
110
|
+
/**
|
|
111
|
+
* Listener callback to handle close events (Remote side).
|
|
112
|
+
* @param cb The callback.
|
|
113
|
+
*/
|
|
114
|
+
onClose(cb: (reason: string, code?: number) => void): void;
|
|
172
115
|
}
|
|
116
|
+
|
|
@@ -17,9 +17,11 @@
|
|
|
17
17
|
import { Event as ElectronEvent, ipcRenderer } from '@theia/electron/shared/electron';
|
|
18
18
|
import { injectable, interfaces } from 'inversify';
|
|
19
19
|
import { JsonRpcProxy } from '../../common/messaging';
|
|
20
|
-
import { WebSocketChannel } from '../../common/messaging/web-socket-channel';
|
|
21
20
|
import { AbstractConnectionProvider } from '../../common/messaging/abstract-connection-provider';
|
|
22
21
|
import { THEIA_ELECTRON_IPC_CHANNEL_NAME } from '../../electron-common/messaging/electron-connection-handler';
|
|
22
|
+
import { Emitter, Event } from '../../common';
|
|
23
|
+
import { Uint8ArrayReadBuffer, Uint8ArrayWriteBuffer } from '../../common/message-rpc/uint8-array-message-buffer';
|
|
24
|
+
import { Channel, MessageProvider } from '../../common/message-rpc/channel';
|
|
23
25
|
|
|
24
26
|
export interface ElectronIpcOptions {
|
|
25
27
|
}
|
|
@@ -36,15 +38,27 @@ export class ElectronIpcConnectionProvider extends AbstractConnectionProvider<El
|
|
|
36
38
|
|
|
37
39
|
constructor() {
|
|
38
40
|
super();
|
|
39
|
-
|
|
40
|
-
this.handleIncomingRawMessage(data);
|
|
41
|
-
});
|
|
41
|
+
this.initializeMultiplexer();
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
protected
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
protected createMainChannel(): Channel {
|
|
45
|
+
const onMessageEmitter = new Emitter<MessageProvider>();
|
|
46
|
+
ipcRenderer.on(THEIA_ELECTRON_IPC_CHANNEL_NAME, (_event: ElectronEvent, data: Uint8Array) => {
|
|
47
|
+
onMessageEmitter.fire(() => new Uint8ArrayReadBuffer(data));
|
|
47
48
|
});
|
|
49
|
+
return {
|
|
50
|
+
close: () => Event.None,
|
|
51
|
+
getWriteBuffer: () => {
|
|
52
|
+
const writer = new Uint8ArrayWriteBuffer();
|
|
53
|
+
writer.onCommit(buffer =>
|
|
54
|
+
ipcRenderer.send(THEIA_ELECTRON_IPC_CHANNEL_NAME, buffer)
|
|
55
|
+
);
|
|
56
|
+
return writer;
|
|
57
|
+
},
|
|
58
|
+
onClose: Event.None,
|
|
59
|
+
onError: Event.None,
|
|
60
|
+
onMessage: onMessageEmitter.event
|
|
61
|
+
};
|
|
48
62
|
}
|
|
49
63
|
|
|
50
64
|
}
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
// *****************************************************************************
|
|
16
16
|
|
|
17
17
|
import { injectable } from 'inversify';
|
|
18
|
-
import { WebSocketChannel } from '../../common/messaging/web-socket-channel';
|
|
19
18
|
import { WebSocketConnectionProvider, WebSocketOptions } from '../../browser/messaging/ws-connection-provider';
|
|
20
19
|
import { FrontendApplicationContribution } from '../../browser/frontend-application';
|
|
20
|
+
import { Channel } from '../../common';
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Customized connection provider between the frontend and the backend in electron environment.
|
|
@@ -34,16 +34,13 @@ export class ElectronWebSocketConnectionProvider extends WebSocketConnectionProv
|
|
|
34
34
|
|
|
35
35
|
onStop(): void {
|
|
36
36
|
this.stopping = true;
|
|
37
|
-
//
|
|
37
|
+
// Manually close the websocket connections `onStop`. Otherwise, the channels will be closed with 30 sec (`MessagingContribution#checkAliveTimeout`) delay.
|
|
38
38
|
// https://github.com/eclipse-theia/theia/issues/6499
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
// But we cannot use `1001`: https://github.com/TypeFox/vscode-ws-jsonrpc/issues/15
|
|
42
|
-
channel.close(1000, 'The frontend is "going away"...');
|
|
43
|
-
}
|
|
39
|
+
// `1001` indicates that an endpoint is "going away", such as a server going down or a browser having navigated away from a page.
|
|
40
|
+
this.channelMultiPlexer?.onUnderlyingChannelClose({ reason: 'The frontend is "going away"', code: 1001 });
|
|
44
41
|
}
|
|
45
42
|
|
|
46
|
-
override openChannel(path: string, handler: (channel:
|
|
43
|
+
override async openChannel(path: string, handler: (channel: Channel) => void, options?: WebSocketOptions): Promise<void> {
|
|
47
44
|
if (!this.stopping) {
|
|
48
45
|
super.openChannel(path, handler, options);
|
|
49
46
|
}
|
|
@@ -16,24 +16,23 @@
|
|
|
16
16
|
|
|
17
17
|
import { IpcMainEvent, ipcMain, WebContents } from '@theia/electron/shared/electron';
|
|
18
18
|
import { inject, injectable, named, postConstruct } from 'inversify';
|
|
19
|
-
import { MessageConnection } from 'vscode-ws-jsonrpc';
|
|
20
|
-
import { createWebSocketConnection } from 'vscode-ws-jsonrpc/lib/socket/connection';
|
|
21
19
|
import { ContributionProvider } from '../../common/contribution-provider';
|
|
22
|
-
import { WebSocketChannel } from '../../common/messaging/web-socket-channel';
|
|
23
20
|
import { MessagingContribution } from '../../node/messaging/messaging-contribution';
|
|
24
|
-
import { ConsoleLogger } from '../../node/messaging/logger';
|
|
25
21
|
import { ElectronConnectionHandler, THEIA_ELECTRON_IPC_CHANNEL_NAME } from '../../electron-common/messaging/electron-connection-handler';
|
|
26
22
|
import { ElectronMainApplicationContribution } from '../electron-main-application';
|
|
27
23
|
import { ElectronMessagingService } from './electron-messaging-service';
|
|
24
|
+
import { Channel, ChannelCloseEvent, ChannelMultiplexer, MessageProvider } from '../../common/message-rpc/channel';
|
|
25
|
+
import { Emitter, Event, WriteBuffer } from '../../common';
|
|
26
|
+
import { Uint8ArrayReadBuffer, Uint8ArrayWriteBuffer } from '../../common/message-rpc/uint8-array-message-buffer';
|
|
28
27
|
|
|
29
28
|
/**
|
|
30
29
|
* This component replicates the role filled by `MessagingContribution` but for Electron.
|
|
31
30
|
* Unlike the WebSocket based implementation, we do not expect to receive
|
|
32
31
|
* connection events. Instead, we'll create channels based on incoming `open`
|
|
33
32
|
* events on the `ipcMain` channel.
|
|
34
|
-
*
|
|
35
33
|
* This component allows communication between renderer process (frontend) and electron main process.
|
|
36
34
|
*/
|
|
35
|
+
|
|
37
36
|
@injectable()
|
|
38
37
|
export class ElectronMessagingContribution implements ElectronMainApplicationContribution, ElectronMessagingService {
|
|
39
38
|
|
|
@@ -43,89 +42,112 @@ export class ElectronMessagingContribution implements ElectronMainApplicationCon
|
|
|
43
42
|
@inject(ContributionProvider) @named(ElectronConnectionHandler)
|
|
44
43
|
protected readonly connectionHandlers: ContributionProvider<ElectronConnectionHandler>;
|
|
45
44
|
|
|
46
|
-
protected readonly channelHandlers = new MessagingContribution.ConnectionHandlers<
|
|
47
|
-
|
|
45
|
+
protected readonly channelHandlers = new MessagingContribution.ConnectionHandlers<Channel>();
|
|
46
|
+
/**
|
|
47
|
+
* Each electron window has a main chanel and its own multiplexer to route multiple client messages the same IPC connection.
|
|
48
|
+
*/
|
|
49
|
+
protected readonly windowChannelMultiplexer = new Map<number, { channel: ElectronWebContentChannel, multiPlexer: ChannelMultiplexer }>();
|
|
48
50
|
|
|
49
51
|
@postConstruct()
|
|
50
52
|
protected init(): void {
|
|
51
|
-
ipcMain.on(THEIA_ELECTRON_IPC_CHANNEL_NAME, (event: IpcMainEvent, data:
|
|
52
|
-
this.
|
|
53
|
+
ipcMain.on(THEIA_ELECTRON_IPC_CHANNEL_NAME, (event: IpcMainEvent, data: Uint8Array) => {
|
|
54
|
+
this.handleIpcEvent(event, data);
|
|
53
55
|
});
|
|
54
56
|
}
|
|
55
57
|
|
|
58
|
+
protected handleIpcEvent(event: IpcMainEvent, data: Uint8Array): void {
|
|
59
|
+
const sender = event.sender;
|
|
60
|
+
// Get the multiplexer for a given window id
|
|
61
|
+
try {
|
|
62
|
+
const windowChannelData = this.windowChannelMultiplexer.get(sender.id) ?? this.createWindowChannelData(sender);
|
|
63
|
+
windowChannelData!.channel.onMessageEmitter.fire(() => new Uint8ArrayReadBuffer(data));
|
|
64
|
+
} catch (error) {
|
|
65
|
+
console.error('IPC: Failed to handle message', { error, data });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Creates a new multiplexer for a given sender/window
|
|
70
|
+
protected createWindowChannelData(sender: Electron.WebContents): { channel: ElectronWebContentChannel, multiPlexer: ChannelMultiplexer } {
|
|
71
|
+
const mainChannel = this.createWindowMainChannel(sender);
|
|
72
|
+
const multiPlexer = new ChannelMultiplexer(mainChannel);
|
|
73
|
+
multiPlexer.onDidOpenChannel(openEvent => {
|
|
74
|
+
const { channel, id } = openEvent;
|
|
75
|
+
if (this.channelHandlers.route(id, channel)) {
|
|
76
|
+
console.debug(`Opening channel for service path '${id}'.`);
|
|
77
|
+
channel.onClose(() => console.debug(`Closing channel on service path '${id}'.`));
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
sender.once('did-navigate', () => multiPlexer.onUnderlyingChannelClose({ reason: 'Window was refreshed' })); // When refreshing the browser window.
|
|
82
|
+
sender.once('destroyed', () => multiPlexer.onUnderlyingChannelClose({ reason: 'Window was closed' })); // When closing the browser window.
|
|
83
|
+
const data = { channel: mainChannel, multiPlexer };
|
|
84
|
+
this.windowChannelMultiplexer.set(sender.id, data);
|
|
85
|
+
return data;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Creates the main channel to a window.
|
|
90
|
+
* @param sender The window that the channel should be established to.
|
|
91
|
+
*/
|
|
92
|
+
protected createWindowMainChannel(sender: WebContents): ElectronWebContentChannel {
|
|
93
|
+
return new ElectronWebContentChannel(sender);
|
|
94
|
+
}
|
|
95
|
+
|
|
56
96
|
onStart(): void {
|
|
57
97
|
for (const contribution of this.messagingContributions.getContributions()) {
|
|
58
98
|
contribution.configure(this);
|
|
59
99
|
}
|
|
60
100
|
for (const connectionHandler of this.connectionHandlers.getContributions()) {
|
|
61
101
|
this.channelHandlers.push(connectionHandler.path, (params, channel) => {
|
|
62
|
-
|
|
63
|
-
connectionHandler.onConnection(connection);
|
|
102
|
+
connectionHandler.onConnection(channel);
|
|
64
103
|
});
|
|
65
104
|
}
|
|
66
105
|
}
|
|
67
106
|
|
|
68
|
-
listen(spec: string, callback: (params: ElectronMessagingService.PathParams, connection: MessageConnection) => void): void {
|
|
69
|
-
this.ipcChannel(spec, (params, channel) => {
|
|
70
|
-
const connection = createWebSocketConnection(channel, new ConsoleLogger());
|
|
71
|
-
callback(params, connection);
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
|
|
75
107
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
76
|
-
ipcChannel(spec: string, callback: (params: any, channel:
|
|
108
|
+
ipcChannel(spec: string, callback: (params: any, channel: Channel) => void): void {
|
|
77
109
|
this.channelHandlers.push(spec, callback);
|
|
78
110
|
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Used to establish a connection between the ipcMain and the Electron frontend (window).
|
|
115
|
+
* Messages a transferred via electron IPC.
|
|
116
|
+
*/
|
|
117
|
+
export class ElectronWebContentChannel implements Channel {
|
|
118
|
+
protected readonly onCloseEmitter: Emitter<ChannelCloseEvent> = new Emitter();
|
|
119
|
+
get onClose(): Event<ChannelCloseEvent> {
|
|
120
|
+
return this.onCloseEmitter.event;
|
|
121
|
+
}
|
|
79
122
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const { id, path } = message;
|
|
93
|
-
const channel = this.createChannel(id, sender);
|
|
94
|
-
if (this.channelHandlers.route(path, channel)) {
|
|
95
|
-
channel.ready();
|
|
96
|
-
channels.set(id, channel);
|
|
97
|
-
channel.onClose(() => channels.delete(id));
|
|
98
|
-
} else {
|
|
99
|
-
console.error('Cannot find a service for the path: ' + path);
|
|
100
|
-
}
|
|
101
|
-
} else {
|
|
102
|
-
const { id } = message;
|
|
103
|
-
const channel = channels.get(id);
|
|
104
|
-
if (channel) {
|
|
105
|
-
channel.handleMessage(message);
|
|
106
|
-
} else {
|
|
107
|
-
console.error('The ipc channel does not exist', id);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
const close = () => {
|
|
111
|
-
for (const channel of Array.from(channels.values())) {
|
|
112
|
-
channel.close(undefined, 'webContent destroyed');
|
|
113
|
-
}
|
|
114
|
-
channels.clear();
|
|
115
|
-
};
|
|
116
|
-
sender.once('did-navigate', close); // When refreshing the browser window.
|
|
117
|
-
sender.once('destroyed', close); // When closing the browser window.
|
|
118
|
-
} catch (error) {
|
|
119
|
-
console.error('IPC: Failed to handle message', { error, data });
|
|
120
|
-
}
|
|
123
|
+
// Make the message emitter public so that we can easily forward messages received from the ipcMain.
|
|
124
|
+
readonly onMessageEmitter: Emitter<MessageProvider> = new Emitter();
|
|
125
|
+
get onMessage(): Event<MessageProvider> {
|
|
126
|
+
return this.onMessageEmitter.event;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
protected readonly onErrorEmitter: Emitter<unknown> = new Emitter();
|
|
130
|
+
get onError(): Event<unknown> {
|
|
131
|
+
return this.onErrorEmitter.event;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
constructor(protected readonly sender: Electron.WebContents) {
|
|
121
135
|
}
|
|
122
136
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
137
|
+
getWriteBuffer(): WriteBuffer {
|
|
138
|
+
const writer = new Uint8ArrayWriteBuffer();
|
|
139
|
+
|
|
140
|
+
writer.onCommit(buffer => {
|
|
141
|
+
if (!this.sender.isDestroyed()) {
|
|
142
|
+
this.sender.send(THEIA_ELECTRON_IPC_CHANNEL_NAME, buffer);
|
|
127
143
|
}
|
|
128
144
|
});
|
|
129
|
-
}
|
|
130
145
|
|
|
146
|
+
return writer;
|
|
147
|
+
}
|
|
148
|
+
close(): void {
|
|
149
|
+
this.onCloseEmitter.dispose();
|
|
150
|
+
this.onMessageEmitter.dispose();
|
|
151
|
+
this.onErrorEmitter.dispose();
|
|
152
|
+
}
|
|
131
153
|
}
|
|
@@ -14,20 +14,14 @@
|
|
|
14
14
|
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
|
|
15
15
|
// *****************************************************************************
|
|
16
16
|
|
|
17
|
-
import
|
|
18
|
-
import type { WebSocketChannel } from '../../common/messaging/web-socket-channel';
|
|
17
|
+
import { Channel } from '../../common/message-rpc/channel';
|
|
19
18
|
|
|
20
19
|
export interface ElectronMessagingService {
|
|
21
|
-
/**
|
|
22
|
-
* Accept a JSON-RPC connection on the given path.
|
|
23
|
-
* A path supports the route syntax: https://github.com/rcs/route-parser#what-can-i-use-in-my-routes.
|
|
24
|
-
*/
|
|
25
|
-
listen(path: string, callback: (params: ElectronMessagingService.PathParams, connection: MessageConnection) => void): void;
|
|
26
20
|
/**
|
|
27
21
|
* Accept an ipc channel on the given path.
|
|
28
22
|
* A path supports the route syntax: https://github.com/rcs/route-parser#what-can-i-use-in-my-routes.
|
|
29
23
|
*/
|
|
30
|
-
ipcChannel(path: string, callback: (params: ElectronMessagingService.PathParams, socket:
|
|
24
|
+
ipcChannel(path: string, callback: (params: ElectronMessagingService.PathParams, socket: Channel) => void): void;
|
|
31
25
|
}
|
|
32
26
|
export namespace ElectronMessagingService {
|
|
33
27
|
export interface PathParams {
|