From 2eb4319fb459f30ad897e8467878154c6790aaef Mon Sep 17 00:00:00 2001 From: Dave Thompson Date: Mon, 10 May 2021 15:36:43 +0100 Subject: [PATCH] Add ability to intercept websocket upgrades and raise events. Signed-off-by: dwt@outlook.com --- .../messaging/messaging-backend-module.ts | 3 ++ .../node/messaging/messaging-contribution.ts | 5 ++ .../src/node/messaging/messaging-listeners.ts | 52 +++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 packages/core/src/node/messaging/messaging-listeners.ts diff --git a/packages/core/src/node/messaging/messaging-backend-module.ts b/packages/core/src/node/messaging/messaging-backend-module.ts index 99ccf7d1fb245..67590050ea297 100644 --- a/packages/core/src/node/messaging/messaging-backend-module.ts +++ b/packages/core/src/node/messaging/messaging-backend-module.ts @@ -20,6 +20,7 @@ import { BackendApplicationContribution } from '../backend-application'; import { MessagingContribution, MessagingContainer } from './messaging-contribution'; import { ConnectionContainerModule } from './connection-container-module'; import { MessagingService } from './messaging-service'; +import { MessagingListener, MessagingListenerContribution } from './messaging-listeners'; export const messagingBackendModule = new ContainerModule(bind => { bindContributionProvider(bind, ConnectionContainerModule); @@ -31,4 +32,6 @@ export const messagingBackendModule = new ContainerModule(bind => { return child.get(MessagingService.Identifier); }).inSingletonScope(); bind(BackendApplicationContribution).toService(MessagingContribution); + bind(MessagingListener).toSelf().inSingletonScope(); + bindContributionProvider(bind, MessagingListenerContribution); }); diff --git a/packages/core/src/node/messaging/messaging-contribution.ts b/packages/core/src/node/messaging/messaging-contribution.ts index daa455c9487ab..5294aab726216 100644 --- a/packages/core/src/node/messaging/messaging-contribution.ts +++ b/packages/core/src/node/messaging/messaging-contribution.ts @@ -32,6 +32,7 @@ import { ConsoleLogger } from './logger'; import { ConnectionContainerModule } from './connection-container-module'; import Route = require('route-parser'); import { WsRequestValidator } from '../ws-request-validators'; +import { MessagingListener } from './messaging-listeners'; export const MessagingContainer = Symbol('MessagingContainer'); @@ -50,6 +51,9 @@ export class MessagingContribution implements BackendApplicationContribution, Me @inject(WsRequestValidator) protected readonly wsRequestValidator: WsRequestValidator; + @inject(MessagingListener) + protected readonly messagingListener: MessagingListener; + protected webSocketServer: ws.Server | undefined; protected readonly wsHandlers = new MessagingContribution.ConnectionHandlers(); protected readonly channelHandlers = new MessagingContribution.ConnectionHandlers(); @@ -122,6 +126,7 @@ export class MessagingContribution implements BackendApplicationContribution, Me if (allowed) { this.webSocketServer!.handleUpgrade(request, socket, head, client => { this.webSocketServer!.emit('connection', client, request); + this.messagingListener.onDidWebSocketUpgrade(request, client); }); } else { console.error(`refused a websocket connection: ${request.connection.remoteAddress}`); diff --git a/packages/core/src/node/messaging/messaging-listeners.ts b/packages/core/src/node/messaging/messaging-listeners.ts new file mode 100644 index 0000000000000..4804418b2adda --- /dev/null +++ b/packages/core/src/node/messaging/messaging-listeners.ts @@ -0,0 +1,52 @@ +/******************************************************************************** + * Copyright (C) 2021 MayStreet Inc. 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 { inject, injectable, named } from 'inversify'; +import { ContributionProvider, MaybePromise } from '../../common'; + +import * as http from 'http'; +import * as ws from 'ws'; + +/** + * Bind components to this symbol to subscribe to WebSocket events. + */ +export const MessagingListenerContribution = Symbol('MessagingListenerContribution'); +export interface MessagingListenerContribution { + /** + * Function invoked when a HTTP connection is upgraded to a websocket. + * + * @param request The HTTP connection upgrade request received by the server. + * @param socket The WebSocket that the connection was upgraded to. + */ + onDidWebSocketUpgrade(request: http.IncomingMessage, socket: ws): MaybePromise; +} + +/** + * Handler of Theia messaging system events, dispatching to MessagingListenerContribution instances. + */ +@injectable() +export class MessagingListener { + + @inject(ContributionProvider) @named(MessagingListenerContribution) + protected readonly messagingListenerContributions: ContributionProvider; + + /** + * Notify all the subscribed `MessagingListenerContribution`s that the Websocket was upgraded. + */ + async onDidWebSocketUpgrade(request: http.IncomingMessage, socket: ws): Promise { + await Promise.all(Array.from(this.messagingListenerContributions.getContributions(), async messagingListener => messagingListener.onDidWebSocketUpgrade(request, socket))); + } +}