Skip to content

Commit

Permalink
get rid of direct usages of web sockets on the backend
Browse files Browse the repository at this point in the history
Signed-off-by: Anton Kosiakov <anton.kosyakov@typefox.io>
  • Loading branch information
akosyakov committed Apr 25, 2018
1 parent c1c24b3 commit 0bad58b
Show file tree
Hide file tree
Showing 14 changed files with 224 additions and 236 deletions.
2 changes: 2 additions & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"@types/lodash.debounce": "4.0.3",
"@types/lodash.throttle": "^4.1.3",
"@types/perfect-scrollbar": "^1.3.0",
"@types/route-parser": "^0.1.1",
"@types/ws": "^3.0.2",
"@types/yargs": "^8.0.2",
"ajv": "^5.2.2",
Expand All @@ -29,6 +30,7 @@
"perfect-scrollbar": "^1.3.0",
"reconnecting-websocket": "^3.0.7",
"reflect-metadata": "^0.1.10",
"route-parser": "^0.0.5",
"vscode-languageserver-types": "^3.6.1",
"vscode-uri": "^1.0.1",
"vscode-ws-jsonrpc": "^0.0.2-1",
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/browser/messaging/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,15 @@ export class WebSocketConnectionProvider {
/**
* Creates a websocket URL to the current location
*/
createWebSocketUrl(path: string): string {
protected createWebSocketUrl(path: string): string {
const endpoint = new Endpoint({ path });
return endpoint.getWebSocketUrl().toString();
}

/**
* Creates a web socket for the given url
*/
createWebSocket(url: string, options?: WebSocketOptions): WebSocket {
protected createWebSocket(url: string, options?: WebSocketOptions): WebSocket {
if (options === undefined || options.reconnecting) {
const socketOptions = {
maxReconnectionDelay: 10000,
Expand Down
113 changes: 0 additions & 113 deletions packages/core/src/node/messaging/connection.ts

This file was deleted.

2 changes: 1 addition & 1 deletion packages/core/src/node/messaging/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*/

export * from './connection';
export * from './messaging-service';
export * from './ipc-connection-provider';
34 changes: 6 additions & 28 deletions packages/core/src/node/messaging/messaging-backend-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,14 @@
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*/

import * as http from 'http';
import * as https from 'https';
import { ContainerModule, injectable, inject, named } from "inversify";
import { bindContributionProvider, ContributionProvider, ConnectionHandler } from '../../common';
import { ContainerModule } from "inversify";
import { bindContributionProvider, ConnectionHandler } from '../../common';
import { BackendApplicationContribution } from "../backend-application";
import { createServerWebSocketConnection } from "./connection";
import { MessagingContribution } from './messaging-contribution';
import { MessagingService } from "./messaging-service";

export const messagingBackendModule = new ContainerModule(bind => {
bind<BackendApplicationContribution>(BackendApplicationContribution).to(MessagingContribution);
bind(BackendApplicationContribution).to(MessagingContribution).inSingletonScope();
bindContributionProvider(bind, ConnectionHandler);
bindContributionProvider(bind, MessagingService.Contribution);
});

@injectable()
export class MessagingContribution implements BackendApplicationContribution {

constructor( @inject(ContributionProvider) @named(ConnectionHandler) protected readonly handlers: ContributionProvider<ConnectionHandler>) {
}

onStart(server: http.Server | https.Server): void {
for (const handler of this.handlers.getContributions()) {
const path = handler.path;
try {
createServerWebSocketConnection({
server,
path
}, connection => handler.onConnection(connection));
} catch (error) {
console.error(error);
}
}
}

}
131 changes: 131 additions & 0 deletions packages/core/src/node/messaging/messaging-contribution.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
* Copyright (C) 2018 TypeFox and others.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*/

import * as ws from 'ws';
import * as url from 'url';
import * as http from 'http';
import * as https from 'https';
import { injectable, inject, named, postConstruct } from "inversify";
import { MessageConnection } from 'vscode-jsonrpc';
import { createWebSocketConnection } from 'vscode-ws-jsonrpc/lib/socket/connection';
import { IConnection } from 'vscode-ws-jsonrpc/lib/server/connection';
import * as launch from 'vscode-ws-jsonrpc/lib/server/launch';
import { IWebSocket } from 'vscode-ws-jsonrpc/lib/socket/socket';
import { ContributionProvider, ConnectionHandler } from '../../common';
import { BackendApplicationContribution } from "../backend-application";
import { MessagingService } from './messaging-service';
import { ConsoleLogger } from "./logger";

import Route = require('route-parser');

@injectable()
export class MessagingContribution implements BackendApplicationContribution, MessagingService {

@inject(ContributionProvider) @named(ConnectionHandler)
protected readonly handlers: ContributionProvider<ConnectionHandler>;

@inject(ContributionProvider) @named(MessagingService.Contribution)
protected readonly contributions: ContributionProvider<MessagingService.Contribution>;

@postConstruct()
protected init(): void {
for (const contribution of this.contributions.getContributions()) {
contribution.configure(this);
}
for (const handler of this.handlers.getContributions()) {
this.listen(handler.path, (params, connection) =>
handler.onConnection(connection)
);
}
}

listen(spec: string, callback: (params: MessagingService.Params, connection: MessageConnection) => void): void {
return this.pushAcceptor(spec, (params, socket) => {
const connection = createWebSocketConnection(this.toIWebSocket(socket), new ConsoleLogger());
callback(params, connection);
});
}

forward(spec: string, callback: (params: MessagingService.Params, connection: IConnection) => void): void {
return this.pushAcceptor(spec, (params, socket) => {
const connection = launch.createWebSocketConnection(this.toIWebSocket(socket));
callback(params, connection);
});
}

protected readonly acceptors: ((path: string, socket: ws) => boolean)[] = [];
protected pushAcceptor(spec: string, callback: (params: MessagingService.Params, socket: ws) => void): void {
const route = new Route(spec);
this.acceptors.push((path, socket) => {
const params = route.match(path);
if (!params) {
return false;
}
callback(params, socket);
return true;
});
}
protected dispatch(socket: ws, request: http.IncomingMessage): void {
const pathname = request.url && url.parse(request.url).pathname;
if (!pathname) {
return;
}
for (const acceptor of this.acceptors) {
try {
if (acceptor(pathname, socket)) {
return;
}
} catch (e) {
console.error(e);
}
}
}

protected checkAliveTimeout = 30000;
onStart(server: http.Server | https.Server): void {
const wss = new ws.Server({
server,
perMessageDeflate: false
});
interface CheckAliveWS extends ws {
alive: boolean;
}
wss.on('connection', (socket: CheckAliveWS, request) => {
socket.alive = true;
socket.on('pong', () => socket.alive = true);
this.dispatch(socket, request);
});
setInterval(() => {
wss.clients.forEach((socket: CheckAliveWS) => {
if (socket.alive === false) {
return socket.terminate();
}
socket.alive = false;
socket.ping();
});
}, this.checkAliveTimeout);
}

protected toIWebSocket(webSocket: ws): IWebSocket {
return <IWebSocket>{
send: content => webSocket.send(content, error => {
if (error) {
console.log(error);
}
}),
onMessage: cb => webSocket.on('message', cb),
onError: cb => webSocket.on('error', cb),
onClose: cb => webSocket.on('close', cb),
dispose: () => {
if (webSocket.readyState < ws.CLOSING) {
webSocket.close();
}
}
};
}

}
24 changes: 24 additions & 0 deletions packages/core/src/node/messaging/messaging-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@

/*
* Copyright (C) 2018 TypeFox and others.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*/

import { MessageConnection } from "vscode-jsonrpc";
import { IConnection } from "vscode-ws-jsonrpc/lib/server/connection";

export interface MessagingService {
listen(path: string, callback: (params: MessagingService.Params, connection: MessageConnection) => void): void;
forward(path: string, callback: (params: MessagingService.Params, connection: IConnection) => void): void;
}
export namespace MessagingService {
export interface Params {
[name: string]: string
}
export const Contribution = Symbol('MessagingService.Contribution');
export interface Contribution {
configure(service: MessagingService): void;
}
}
Loading

0 comments on commit 0bad58b

Please sign in to comment.