@eclipse-glsp/protocol
Version:
The protocol definition for client-server communication in GLSP
116 lines (103 loc) • 4.6 kB
text/typescript
/********************************************************************************
* Copyright (c) 2023-2024 EclipseSource and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
import { Logger, MessageConnection } from 'vscode-jsonrpc';
import { MaybePromise } from '../../utils/type-util';
import { createWebSocketConnection, wrap } from './websocket-connection';
export interface GLSPWebSocketOptions {
/**
* Allow automatic reconnect of WebSocket connections
* @default true
*/
reconnecting?: boolean;
/**
* Max attempts of reconnects
* @default Infinity
*/
reconnectAttempts?: number;
/**
* The time delay in milliseconds between reconnect attempts
* @default 1000
*/
reconnectDelay?: number;
}
export const GLSPConnectionHandler = Symbol('GLSPConnectionHandler');
export interface GLSPConnectionHandler {
onConnection?(connection: MessageConnection): MaybePromise<void>;
onReconnect?(connection: MessageConnection): MaybePromise<void>;
logger?: Logger;
}
export class GLSPWebSocketProvider {
protected webSocket: WebSocket;
protected reconnectTimer: NodeJS.Timeout;
protected reconnectAttempts = 0;
protected options: GLSPWebSocketOptions = {
// default values
reconnecting: true,
reconnectAttempts: Infinity,
reconnectDelay: 1000
};
constructor(
protected url: string,
options?: GLSPWebSocketOptions
) {
this.options = Object.assign(this.options, options);
}
protected createWebSocket(url: string): WebSocket {
return new WebSocket(url);
}
listen(handler: GLSPConnectionHandler, isReconnecting = false): Promise<MessageConnection> {
this.webSocket = this.createWebSocket(this.url);
this.webSocket.onerror = (): void => {
handler.logger?.error('GLSPWebSocketProvider Connection to server errored. Please make sure that the server is running!');
clearInterval(this.reconnectTimer);
this.webSocket.close();
};
return new Promise(resolve => {
this.webSocket.onopen = (): void => {
clearInterval(this.reconnectTimer);
const wrappedSocket = wrap(this.webSocket);
const wsConnection = createWebSocketConnection(wrappedSocket, handler.logger);
this.webSocket.onclose = (): void => {
const { reconnecting, reconnectAttempts, reconnectDelay } = this.options;
if (reconnecting) {
if (this.reconnectAttempts >= reconnectAttempts!) {
handler.logger?.error(
'GLSPWebSocketProvider WebSocket reconnect failed - maximum number reconnect attempts ' +
`(${reconnectAttempts}) was exceeded!`
);
} else {
this.reconnectTimer = setInterval(() => {
handler.logger?.warn('GLSPWebSocketProvider reconnecting...');
this.listen(handler, true);
this.reconnectAttempts++;
}, reconnectDelay!);
}
} else {
handler.logger?.error('GLSPWebSocketProvider WebSocket will not reconnect - closing the connection now!');
}
};
if (isReconnecting) {
handler.logger?.warn('GLSPWebSocketProvider Reconnecting!');
handler.onReconnect?.(wsConnection);
} else {
handler.logger?.warn('GLSPWebSocketProvider Initializing!');
handler.onConnection?.(wsConnection);
}
resolve(wsConnection);
};
});
}
}