Skip to content

Commit

Permalink
authentication and json encoding moved to separate files
Browse files Browse the repository at this point in the history
  • Loading branch information
Robin Gottfried committed Feb 17, 2020
1 parent 36816dc commit 19b5091
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 53 deletions.
16 changes: 5 additions & 11 deletions client/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
//-- vim: ft=javascript tabstop=2 softtabstop=2 expandtab shiftwidth=2
const http = require('http');
const WebSocket = require('ws');
const ws = new WebSocket(`ws://localhost:8080/ws/pill/${process.argv[2]}`);
const ws_ = new WebSocket(`ws://localhost:8080/ws/pill/${process.argv[2]}`);
const WsJsonProtocol = require('../lib/ws-json');
const ws = new WsJsonProtocol(ws_);
const forward_host = process.argv[3];
const forward_port = process.argv[4];

Expand Down Expand Up @@ -55,7 +57,6 @@ class RequestForwarder extends Object {
}

_send(data) {
let message = JSON.stringify(data, undefined, 3);
console.log(`Sending ${message}`)
this._ws.send(message);
}
Expand All @@ -71,16 +72,9 @@ ws.on('open', function open() {
const request_forwarder = new RequestForwarder(ws, forward_host, forward_port);
console.log("Client connection openned.");

ws.send(JSON.stringify({data:"Hallo."}));
ws.send({data:"Hallo."});
ws.on("message", function (message) {
console.log(`Got message\n------\n${message}\n------\n\n`);
let data;
try {
data = JSON.parse(message);
} catch(err) {
console.error(err);
return;
}
request_forwarder.on_message(data);
request_forwarder.on_message(message);
});
});
Empty file added lib/index.js
Empty file.
55 changes: 55 additions & 0 deletions lib/ws-json.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
'use strict';
//-- vim: ft=javascript tabstop=2 softtabstop=2 expandtab shiftwidth=2

class DecodeError extends Error {}

class WsJsonProtocol extends Object {

constructor(ws) {
super();
this._ws = ws;
}

_encode(messageObject) {
return JSON.stringify(messageObject, undefined, 3);
}

_decode(rawMessage) {
try {
return JSON.parse(rawMessage);
} catch(err) {
throw DecodeError(`Could not decode message ... ${err.toString()}`);
}
}

send(messageObject) {
if (! messageObject instanceof Object) {
throw new Error(`Type error, expected Object but got ${typeof messageObject}.`);
}
this._ws.send(this._encode(messageObject));
}

on(eventName, callback) {
let decode_ = this._decode.bind(this);
let decoderCallback = function (message) {
let decodedMessage;
try {
let decodedMessage = decode_(message);
} catch(err) {
console.error(err);
}
if (decodedMessage !== undefined) {
callback(decodedMessage);
}
}
if (eventName == "message") {
return this._ws.on(eventName, decoderCallback);
} else {
return this._ws.on(eventName, callback);
}

}
}


module.exports = WsJsonProtocol;
1 change: 1 addition & 0 deletions server/api.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
'use strict';
//-- vim: ft=javascript tabstop=2 softtabstop=2 expandtab shiftwidth=2
const REQ_SIZE_LIMIT = 1024*1024;
const uuid = require('uuid');
const { HttpError, BadGateway } = require('./HttpError');
Expand Down
33 changes: 33 additions & 0 deletions server/authenticator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
'use strict';
//-- vim: ft=javascript tabstop=2 softtabstop=2 expandtab shiftwidth=2

class KeyAuthenticator extends Object {
constructor(allowed_keys) {
super();
this.allowed_keys = new Set(allowed_keys);
this._clients_by_id = {};
}

authenticate(request, callback) {
console.log(`Authenticating request ${request} on ${request.url}.`);
const match = /^\/ws\/pill\/(?<client_key>.*)$/.exec(request.url);
if (! match) return callback(new Error("Unknown url."));
if (this.allowed_keys.has(match.groups.client_key)) {
console.log(`Key ${match.groups.client_key} accepted`);
callback(null, {"key": match.groups.client_key});
} else {
console.log('Invalid key');
callback(new Error("Invalid key."), null);
}
}

onConnected (ws, client) {
this._clients_by_id[client.key] = ws;
}

clientFromId (id) {
return this._clients_by_id[id];
}
}

module.exports = KeyAuthenticator;
81 changes: 39 additions & 42 deletions server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,55 +4,52 @@
const http = require('http');
const WebSocket = require('ws');
const Api = require('./api');

let CLIENTS_BY_ID = {};
const server = http.createServer(new Api('/api', (id)=>CLIENTS_BY_ID[id]).request_handler);
const KeyAuthenticator = require('./authenticator');
const authenticator = new KeyAuthenticator(['kpz-1', 'kpz-2', 'kpz-3']);
const server = http.createServer(
new Api(
'/api',
authenticator.clientFromId.bind(authenticator)
).request_handler
);
const wss = new WebSocket.Server({ noServer: true });

const registered_keys = new Set(['kpz-1', 'kpz-2', 'kpz-3']);

const authenticate = (request, cb) => {
console.log(`Authenticating request ${request} on ${request.url}.`);
const match = /^\/ws\/pill\/(?<client_key>.*)$/.exec(request.url);
if (! match) return cb(new Error("Unknown url."));
if (registered_keys.has(match.groups.client_key)) {
console.log(`Key ${match.groups.client_key} accepted`);
cb(null, {"name": "one", "key": match.groups.client_key});
} else {
console.log('Invalid key');
cb(new Error("Invalid key."), null);
}
}


/*
* @authenticator ... function (request, callback) which calls callback(err, client).
* `err` is empty on success and client which should be an object with `key` property.
*/
function createServer(authenticator) {
wss.on('connection', function connection(ws, request, client) {

wss.on('connection', function connection(ws, request, client) {

ws.id = client.key;
CLIENTS_BY_ID[client.key] = ws;
wss.clients.forEach(function each(client) {
console.log('Client.ID: ' + client.id);
// TODO: should be run in debug mode only
wss.clients.forEach(function each(client) {
console.log('Client.ID: ' + client.id);
});
ws.on('message', function message(msg) {
console.log(`Received message ${msg} from client ${client.key}`);
});
ws.on('message', function message(msg) {
console.log(`Received message ${msg} from client ${client.key}`);
});
});

server.on('upgrade', function upgrade(request, socket, head) {
console.log("Upgrading protocol.");
authenticate(request, (err, client) => {
if (err || !client) {
console.log("Destroying connection");
socket.destroy();
return;
}

wss.handleUpgrade(request, socket, head, function done(ws) {
console.log("Emitting ws connection");
wss.emit('connection', ws, request, client);
server.on('upgrade', function upgrade(request, socket, head) {
console.log("Upgrading protocol.");
authenticator.authenticate(request, (err, client) => {
if (err || !client) {
console.log("Destroying connection");
socket.destroy();
return;
}

wss.handleUpgrade(request, socket, head, function done(ws) {
console.log("Emitting ws connection");
authenticator.onConnected(ws, client);
ws.client = client;
wss.emit('connection', ws, request, client);
});
});
});
});

return server;
}

server.listen(8080);
createServer(authenticator)
.listen(8080);

0 comments on commit 19b5091

Please sign in to comment.