mirror of
https://github.com/wylieconlon/jsonrpc-ws-proxy.git
synced 2025-05-17 23:36:36 +00:00
Allow configuration of multiple servers by yaml
This commit is contained in:
parent
1d450c6a53
commit
e170d52250
23
README.md
23
README.md
@ -1,11 +1,26 @@
|
||||
Sets up a websocket proxy to a JSON-RPC server
|
||||
Sets up a websocket proxy for any number of language servers.
|
||||
|
||||
Each server is run as a subprocess which is connected to by sending the client
|
||||
to the URL /<language> based on a configuration file defined locally. For example,
|
||||
with the following defined as `servers.yml`:
|
||||
|
||||
```
|
||||
langservers:
|
||||
python:
|
||||
- python
|
||||
- python-langserver.py
|
||||
- --stdio
|
||||
go:
|
||||
- /usr/local/bin/go
|
||||
- langserver.go
|
||||
```
|
||||
|
||||
The client would connect to `ws://localhost/python` to get a python language server
|
||||
|
||||
Usage:
|
||||
|
||||
```
|
||||
npm install
|
||||
npm run prepare
|
||||
node dist/server.js --port 3000 --remotePath localhost --remotePort 2089
|
||||
node dist/server.js --port 3000 --languageServers servers.yml
|
||||
```
|
||||
|
||||
The only required flag to the server is `--remotePort`
|
||||
|
166
package-lock.json
generated
166
package-lock.json
generated
@ -4,25 +4,47 @@
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@types/events": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "http://registry.npmjs.org/@types/events/-/events-1.2.0.tgz",
|
||||
"integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "7.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-7.10.1.tgz",
|
||||
"integrity": "sha512-fZvabBkcFJzc+eJN2XTuhKhop1RKdlGQgjmQuxYuQJ6K5rMNoHr6tomb6q0E8Axe+WPyfe/lr7CnnkGvzNh5mA==",
|
||||
"version": "10.12.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.3.tgz",
|
||||
"integrity": "sha512-sfGmOtSMSbQ/AKG8V9xD1gmjquC9awIIZ/Kj309pHb2n3bcRAcGMQv5nJ6gCXZVsneGE4+ve8DXKRCsrg3TFzg==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/ws": {
|
||||
"version": "0.0.39",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-0.0.39.tgz",
|
||||
"integrity": "sha1-0jhsNHXrZOVhE3okWk0dE7H2n9E=",
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-6.0.1.tgz",
|
||||
"integrity": "sha512-EzH8k1gyZ4xih/MaZTXwT2xOkPiIMSrhQ9b8wrlX88L0T02eYsddatQlwVFlEPyEqV0ChpdpNnE51QPH6NVT4Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/events": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"argparse": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||
"requires": {
|
||||
"sprintf-js": "~1.0.2"
|
||||
}
|
||||
},
|
||||
"async-limiter": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
|
||||
"integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg=="
|
||||
"integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==",
|
||||
"dev": true
|
||||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
|
||||
"dev": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
@ -32,14 +54,6 @@
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"concat-map": {
|
||||
@ -48,6 +62,11 @@
|
||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
||||
"dev": true
|
||||
},
|
||||
"esprima": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
|
||||
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
|
||||
},
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
@ -84,6 +103,15 @@
|
||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
|
||||
"dev": true
|
||||
},
|
||||
"js-yaml": {
|
||||
"version": "3.12.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz",
|
||||
"integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==",
|
||||
"requires": {
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
@ -110,7 +138,7 @@
|
||||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
|
||||
"dev": true
|
||||
},
|
||||
@ -123,6 +151,11 @@
|
||||
"glob": "^7.0.5"
|
||||
}
|
||||
},
|
||||
"sprintf-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
|
||||
},
|
||||
"typescript": {
|
||||
"version": "3.1.6",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.1.6.tgz",
|
||||
@ -130,17 +163,97 @@
|
||||
"dev": true
|
||||
},
|
||||
"vscode-ws-jsonrpc": {
|
||||
"version": "0.0.2-2",
|
||||
"resolved": "https://registry.npmjs.org/vscode-ws-jsonrpc/-/vscode-ws-jsonrpc-0.0.2-2.tgz",
|
||||
"integrity": "sha512-hViHObJHtxD0KX8tvP6QL8fJGfH9mmDrEkdfLKj6Mf1uaxypoMBnjcZDCU3N4l7VriQiNRbohe/FlMrC3/0r7Q==",
|
||||
"version": "file:../vscode-ws-jsonrpc",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"vscode-jsonrpc": "^3.6.0"
|
||||
"vscode-jsonrpc": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "7.0.56",
|
||||
"bundled": true
|
||||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true
|
||||
},
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.1.2",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.0.4",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"inflight": {
|
||||
"version": "1.0.6",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"once": "^1.3.0",
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "2.6.2",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"glob": "^7.0.5"
|
||||
}
|
||||
},
|
||||
"typescript": {
|
||||
"version": "2.7.2",
|
||||
"bundled": true
|
||||
},
|
||||
"vscode-jsonrpc": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.6.2.tgz",
|
||||
"integrity": "sha512-T24Jb5V48e4VgYliUXMnZ379ItbrXgOimweKaJshD84z+8q7ZOZjJan0MeDe+Ugb+uqERDVV8SBmemaGMSMugA=="
|
||||
"version": "4.0.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -151,9 +264,10 @@
|
||||
"dev": true
|
||||
},
|
||||
"ws": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz",
|
||||
"integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==",
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-6.1.0.tgz",
|
||||
"integrity": "sha512-H3dGVdGvW2H8bnYpIDc3u3LH8Wue3Qh+Zto6aXXFzvESkTVT6rAfKR6tR/+coaUvxs8yHtmNV0uioBF62ZGSTg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"async-limiter": "~1.0.0"
|
||||
}
|
||||
|
11
package.json
11
package.json
@ -3,15 +3,16 @@
|
||||
"name": "jsonrpc-ws-proxy",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"vscode-ws-jsonrpc": "^0.0.2-1",
|
||||
"ws": "^5.0.0"
|
||||
"js-yaml": "^3.12.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^7.10.1",
|
||||
"@types/ws": "0.0.39",
|
||||
"@types/node": "^10.12.3",
|
||||
"@types/ws": "^6.0.1",
|
||||
"minimist": "^1.2.0",
|
||||
"rimraf": "^2.6.2",
|
||||
"typescript": "^3.0.1"
|
||||
"typescript": "^3.0.1",
|
||||
"vscode-ws-jsonrpc": "file:../vscode-ws-jsonrpc",
|
||||
"ws": "^6.1.0"
|
||||
},
|
||||
"scripts": {
|
||||
"prepare": "npm run clean && npm run build",
|
||||
|
@ -1,45 +1,77 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import * as net from 'net';
|
||||
import * as ws from 'ws';
|
||||
import * as rpc from 'vscode-ws-jsonrpc';
|
||||
import * as rpcServer from 'vscode-ws-jsonrpc/lib/server';
|
||||
import * as parseArgs from 'minimist';
|
||||
import * as http from 'http';
|
||||
import * as yaml from 'js-yaml';
|
||||
import * as fs from 'fs';
|
||||
|
||||
let argv = parseArgs(process.argv.slice(2));
|
||||
|
||||
if (argv.help) {
|
||||
console.log(`Usage: server.js --port 3000 --remotePath localhost -- 2089`);
|
||||
if (argv.help || !argv.languageServers) {
|
||||
console.log(`Usage: server.js --port 3000 --languageServers config.yml`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let serverPath : number = parseInt(argv.port) || 3000;
|
||||
let remotePath : string = argv.remotePath || '127.0.0.1';
|
||||
let remotePort : number = parseInt(argv.remotePort);
|
||||
let serverPort : number = parseInt(argv.port) || 3000;
|
||||
|
||||
if (!remotePort) {
|
||||
console.log('--remotePort is required');
|
||||
let languageServers;
|
||||
try {
|
||||
let parsed = yaml.safeLoad(fs.readFileSync(argv.languageServers), 'utf8');
|
||||
if (!parsed.langservers) {
|
||||
console.log('Your langservers file is not a valid format, see README.md');
|
||||
process.exit(1);
|
||||
}
|
||||
languageServers = parsed.langservers;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const wss : ws.Server = new ws.Server({
|
||||
port: serverPath,
|
||||
port: serverPort,
|
||||
perMessageDeflate: false
|
||||
}, () => {
|
||||
console.log(`Listening to http and ws requests on ${serverPath}`);
|
||||
console.log(`Listening to http and ws requests on ${serverPort}`);
|
||||
});
|
||||
|
||||
const localSocket = net.connect(remotePort, remotePath, () => {
|
||||
console.log(`Connected to local server ${remotePath}:${remotePort}`);
|
||||
});
|
||||
const localReader : rpc.SocketMessageReader = new rpc.SocketMessageReader(localSocket);
|
||||
const localWriter : rpc.SocketMessageWriter = new rpc.SocketMessageWriter(localSocket);
|
||||
const localConnection : rpcServer.IConnection = rpcServer.createConnection(localReader, localWriter, () => {});
|
||||
function toSocket(webSocket: ws): rpc.IWebSocket {
|
||||
return {
|
||||
send: content => webSocket.send(content),
|
||||
onMessage: cb => webSocket.onmessage = event => cb(event.data),
|
||||
onError: cb => webSocket.onerror = event => {
|
||||
if ('message' in event) {
|
||||
cb((event as any).message)
|
||||
}
|
||||
},
|
||||
onClose: cb => webSocket.onclose = event => cb(event.code, event.reason),
|
||||
dispose: () => webSocket.close()
|
||||
}
|
||||
}
|
||||
|
||||
wss.on('connection', (client : ws) => {
|
||||
// The ws interface is compatible with the expected socket interface, except binaryType support
|
||||
const socket : rpc.IWebSocket = rpc.toSocket(client as any as WebSocket);
|
||||
const connection : rpcServer.IConnection = rpcServer.createWebSocketConnection(socket);
|
||||
wss.on('connection', (client : ws, request : http.IncomingMessage) => {
|
||||
let langServer : string[];
|
||||
|
||||
Object.keys(languageServers).forEach((key) => {
|
||||
if (request.url === '/' + key) {
|
||||
langServer = languageServers[key];
|
||||
}
|
||||
});
|
||||
if (!langServer || !langServer.length) {
|
||||
console.error('Invalid language server', request.url);
|
||||
client.close();
|
||||
return;
|
||||
}
|
||||
|
||||
let localConnection = rpcServer.createServerProcess('Example', langServer[0], langServer.slice(1));
|
||||
let socket : rpc.IWebSocket = toSocket(client);
|
||||
let connection = rpcServer.createWebSocketConnection(socket);
|
||||
rpcServer.forward(connection, localConnection);
|
||||
console.log(`Forwarding new client`);
|
||||
socket.onClose((code, reason) => {
|
||||
console.log('Client closed', reason);
|
||||
localConnection.dispose();
|
||||
});
|
||||
});
|
||||
|
@ -1,9 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"outDir": "dist",
|
||||
"baseUrl": ".",
|
||||
"skipLibCheck": true
|
||||
"lib": ["es2016", "dom"]
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
|
Loading…
Reference in New Issue
Block a user