From ae002b3ecf5bdf5ba19b44006fc1b4b4bca763cd Mon Sep 17 00:00:00 2001 From: Stefan Date: Tue, 5 Dec 2023 12:18:39 +0100 Subject: [PATCH] revert(index.ts): removed multiplexing --- WebSocketLanguageServer/src/index.ts | 260 ++++++--------------------- 1 file changed, 59 insertions(+), 201 deletions(-) diff --git a/WebSocketLanguageServer/src/index.ts b/WebSocketLanguageServer/src/index.ts index 0f9bbf3..384382d 100644 --- a/WebSocketLanguageServer/src/index.ts +++ b/WebSocketLanguageServer/src/index.ts @@ -2,182 +2,56 @@ * Copyright (c) 2018-2022 TypeFox GmbH (http://www.typefox.io). All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. * ------------------------------------------------------------------------------------------ */ -import {WebSocketServer} from 'ws'; -import {IncomingMessage} from 'http'; -import {URL} from 'url'; -import {Socket} from 'net'; +import { WebSocketServer } from 'ws'; +import { IncomingMessage } from 'http'; +import { URL } from 'url'; +import { Socket } from 'net'; import express from 'express'; -import {IWebSocket, WebSocketMessageReader, WebSocketMessageWriter} from 'vscode-ws-jsonrpc'; -import {createConnection, createServerProcess, IConnection} from 'vscode-ws-jsonrpc/server'; -import {Message, NotificationMessage, RequestMessage, ResponseMessage} from 'vscode-languageserver'; +import { resolve } from 'path'; +import { IWebSocket, WebSocketMessageReader, WebSocketMessageWriter } from 'vscode-ws-jsonrpc'; +import { createConnection, createServerProcess, forward } from 'vscode-ws-jsonrpc/server'; +import { Message, InitializeRequest, InitializeParams } from 'vscode-languageserver'; import config from './config.js'; -let serverConnection: IConnection; -/* the protocol uses messages without an ID, but they need to be delivered to a specific client -This is a hack around: we map uris (that are unique to client) to a connection and deliver messages -without id but with a uri to the correct client - */ -const uriToConnectionMap = new Map(); -const clientScopeToServerScope = new Map(); -const serverScopeToClientScope = new Map(); -const connectionToClientId = new Map(); -const clientIdToConnection = new Map(); -const serverScopeIdToClientId = new Map(); -const clientIdAndClientScopeIdToServerScopeId = new Map<[Number, Number], Number>; -let nextFreeClientId = 0; -let nextFreeServerScopeId = 0; -let initMessage1: Message | undefined; -let initMessage2: Message | undefined; -let initialized = false; - -function getClientScopeIdFromServerScopeId(serverScopeId: Number): Number | undefined { - const clientScopeId = serverScopeToClientScope.get(serverScopeId); - return clientScopeId; -} - -function getServerScopeIdFromClientScopeId(clientScopeId: Number, clientId: Number): Number { - let serverScopeId = clientIdAndClientScopeIdToServerScopeId.get([clientId, clientScopeId]); - if (serverScopeId === undefined) { - clientScopeToServerScope.set(clientScopeId, nextFreeServerScopeId); - serverScopeToClientScope.set(nextFreeServerScopeId, clientScopeId); - serverScopeId = nextFreeServerScopeId; - nextFreeServerScopeId++; - serverScopeIdToClientId.set(serverScopeId, clientId); - clientIdAndClientScopeIdToServerScopeId.set([clientId, clientScopeId], serverScopeId); - } - return serverScopeId; -} - -function getConnectionFromServerScopeId(serverScopeId: Number): IConnection | undefined { - const clientId = serverScopeIdToClientId.get(serverScopeId); - let connection; - if (clientId !== undefined) { - connection = clientIdToConnection.get(clientId); - } - return connection; -} - -function addNewConnection(connection: IConnection) { - const clientId = nextFreeClientId; - clientIdToConnection.set(clientId, connection); - nextFreeClientId++; - return clientId; -} - -const initUVLS = () => { +const launchLanguageServer = (socket: IWebSocket) => { const serverName: string = 'UVLS'; const ls = config.languageServerBinary; - serverConnection = createServerProcess(serverName, ls)!; - - if (!serverConnection) { - console.error("Server could not be started"); - process.exit(1); - } - - serverConnection.reader.listen((message: Message) => { - let socketConnection: IConnection | undefined; - let newMessage: Message = message; - if (Message.isRequest(message)) { - const typedMessage = message as RequestMessage; - const serverScopeId = Number(typedMessage.id); - const clientConnection = getConnectionFromServerScopeId(serverScopeId); - const clientScopeId = getClientScopeIdFromServerScopeId(serverScopeId); - if (clientScopeId !== undefined) { - newMessage["id"] = clientScopeId; - } - if (clientConnection !== undefined) { - socketConnection = clientConnection; - } - if (typedMessage.method === "client/registerCapability") { - initMessage2 = newMessage; - } else if (typedMessage.method === "workspace/executeCommand") { - const fileUri = typedMessage.params?.["arguments"][0]?.["uri"].split("create/").pop(); - if(fileUri !== undefined){ - socketConnection = uriToConnectionMap.get(fileUri); - } - } - - } else if (Message.isResponse(message)) { - const typedMessage = message as ResponseMessage; - if (typedMessage.id === 0) { - initMessage1 = message; - } - const serverScopeId = Number(typedMessage.id); - const clientConnection = getConnectionFromServerScopeId(serverScopeId); - const clientScopeId = getClientScopeIdFromServerScopeId(serverScopeId); - if (clientScopeId !== undefined) { - newMessage["id"] = clientScopeId; - } - if (clientConnection !== undefined) { - socketConnection = clientConnection; - } - } else if (Message.isNotification(message)) { - const typedMessage = message as NotificationMessage; - const uri: string | undefined = typedMessage.params?.["uri"]; - if (uri !== undefined && uri.split("///").length === 2) { - socketConnection = uriToConnectionMap.get(uri.split("///")[1]); + const serverConnection = createServerProcess(serverName, ls); - } - } - if (socketConnection) { - socketConnection.writer.write(newMessage); - } else { - console.log(`Could not resolve destination of server message to right client\nMessage: ${JSON.stringify(message)}`) - } - }) -}; - -function multiplexHandler(socket: IWebSocket) { const reader = new WebSocketMessageReader(socket); const writer = new WebSocketMessageWriter(socket); const socketConnection = createConnection(reader, writer, () => socket.dispose()); - const clientId = addNewConnection(socketConnection); - - - socketConnection.reader.listen((message) => { - let newMessage = message; - if (Message.isRequest(message)) { - const typedMessage = message as RequestMessage; - if (typedMessage.method === "initialize" && initMessage1 !== undefined) { - socketConnection.writer.write(initMessage1); - return; - } else if (typedMessage.method === "workspace/executeCommand") { - const serverScopeId = getServerScopeIdFromClientScopeId(Number(typedMessage.id), clientId); - newMessage["id"] = serverScopeId; - } else { - const serverScopeId = getServerScopeIdFromClientScopeId(Number(typedMessage.id), clientId); - newMessage["id"] = serverScopeId; - } - - } else if (Message.isResponse(message)) { - const typedMessage = message as ResponseMessage; - const serverScopeId = getServerScopeIdFromClientScopeId(Number(typedMessage.id), clientId); - newMessage["id"] = serverScopeId; - - } else if (Message.isNotification(message)) { - const typedMessage = message as NotificationMessage; - if (typedMessage.method === "initialized" && !initialized) { - initialized = true; - serverConnection.writer.write(newMessage); - return; - } else if (typedMessage.method === "textDocument/didOpen") { - const uri: string | undefined = typedMessage.params?.["textDocument"]?.["uri"]; - if (uri !== undefined && uri.split("///").length === 2) { - uriToConnectionMap.set(uri.split("///")[1], socketConnection); + if (serverConnection) { + forward(socketConnection, serverConnection, message => { + if (Message.isRequest(message)) { + console.log(`${serverName} Server received:`); + console.log(message); + if (message.method === InitializeRequest.type.method) { + const initializeParams = message.params as InitializeParams; + initializeParams.processId = process.pid; } + } + if (Message.isResponse(message)) { + console.log(`${serverName} Server sent:`); + logObjectRecursively(message); + } + return message; + }); + } +}; - } else if (typedMessage.method === "$/cancelRequest") { - newMessage["params"]["id"] = getServerScopeIdFromClientScopeId(newMessage["params"]["id"], clientId); - } else if (typedMessage.method === "initialized" && initMessage2 !== undefined) { - socketConnection.writer.write(initMessage2); - return; +function logObjectRecursively(obj, depth = 0) { + const indent = ' '.repeat(depth); + for (const key in obj) { + if (obj.hasOwnProperty(key)) { + if (typeof obj[key] === 'object' && obj[key] !== null) { + console.log(`${indent}${key}:`); + logObjectRecursively(obj[key], depth + 1); } else { - // other messages are just forwarded to the uvls + console.log(`${indent}${key}: ${obj[key]}`); } - } - serverConnection.writer.write(newMessage); - }) + } } export const runUVLServer = () => { @@ -188,60 +62,44 @@ export const runUVLServer = () => { } }); - initUVLS(); - // create the express application const app = express(); // start the server const server = app.listen(config.port); // create the web socket const wss = new WebSocketServer({ - noServer: true, perMessageDeflate: false, clientTracking: true, + noServer: true, + perMessageDeflate: false, + clientTracking: true, }); server.on('upgrade', (request: IncomingMessage, socket: Socket, head: Buffer) => { const baseURL = `http://${request.headers.host}/`; const pathname = request.url ? new URL(request.url, baseURL).pathname : undefined; - wss.handleUpgrade(request, socket, head, webSocket => { - const socket: IWebSocket = { - send: content => { - webSocket.send(content, error => { + wss.handleUpgrade(request, socket, head, webSocket => { + const socket: IWebSocket = { + send: content => webSocket.send(content, error => { if (error) { throw error; } - }) - }, - onMessage: cb => webSocket.on('message', (data) => { - cb(data); - }), - onError: cb => webSocket.on('error', cb), - onClose: cb => webSocket.on('close', cb), - dispose: () => webSocket.close() - }; - // launch the server when the web socket is opened - if (webSocket.readyState === webSocket.OPEN) { - multiplexHandler(socket); - } else { - webSocket.on('open', () => { - multiplexHandler(socket); - }); - } - }); + }), + onMessage: cb => webSocket.on('message', (data) => { + cb(data); + }), + onError: cb => webSocket.on('error', cb), + onClose: cb => webSocket.on('close', cb), + dispose: () => webSocket.close() + }; + // launch the server when the web socket is opened + if (webSocket.readyState === webSocket.OPEN) { + launchLanguageServer(socket); + } else { + webSocket.on('open', () => { + launchLanguageServer(socket); + }); + } + }); }); }; -function logObjectRecursively(obj, depth = 0) { - const indent = ' '.repeat(depth); - for (const key in obj) { - if (obj.hasOwnProperty(key)) { - if (typeof obj[key] === 'object' && obj[key] !== null) { - console.log(`${indent}${key}:`); - logObjectRecursively(obj[key], depth + 1); - } else { - console.log(`${indent}${key}: ${obj[key]}`); - } - } - } -} - runUVLServer(); \ No newline at end of file