Skip to content

Instantly share code, notes, and snippets.

@ondras
Last active November 25, 2022 17:29
Show Gist options
  • Save ondras/dce6d991a2097b0bc271e5e6f575086d to your computer and use it in GitHub Desktop.
Save ondras/dce6d991a2097b0bc271e5e6f575086d to your computer and use it in GitHub Desktop.
TeaJS Websocket API, backed by node's websocket module
/* tiny glue to brige v8cgi/teajs-based apps with a node websocket module */
var WebSocketServer = require('websocket').server;
var HTTP = require('http');
var HTTPS = require('https');
var fs = require('fs');
var Server = function(ip, port, options) {
this.setDebug(true);
this._options = {
idle: 15,
certificate: "",
privateKey: ""
}
for (var p in options) { this._options[p] = options[p]; }
this._ip = ip || "127.0.0.1";
this._port = port || 80;
this._applications = [];
this._clients = {};
if (this._options.certificate) {
var options = {
key: fs.readFileSync(this._options.privateKey),
cert: fs.readFileSync(this._options.certificate)
};
this._httpServer = HTTPS.createServer(options, this._httpResponse);
} else {
this._httpServer = HTTP.createServer(this._httpResponse);
}
this._wsServer = new WebSocketServer({
httpServer: this._httpServer,
autoAcceptConnections: false,
maxReceivedFrameSize: 0x100000
});
this._wsServer.on('request', this._onRequest.bind(this));
}
/**
* @param {object} application
* @param {string || null} [host] When falsy, all hosts are okay
* @param {string || null} [path] When falsy, all paths are okay
*/
Server.prototype.addApplication = function(application, host, path) {
var a = {
onmessage: function(client, data) {},
onconnect: function(client, headers) {},
ondisconnect: function(client, code, message) {},
onidle: function() {},
acceptsOrigin: function(origin) { return true; },
chooseProtocol: function(protocols) { return false; },
host: host,
path: path
};
for (var p in application) { a[p] = application[p]; }
this._applications.push(a);
}
Server.prototype.run = function() {
this._httpServer.listen(this._port, this._ip, function() {
console.log(Date.now(), 'Server is listening on ' + this._ip + ':' + this._port);
}.bind(this));
setInterval(function() {
this._applications.forEach(function(app) {
app.onidle();
});
}.bind(this), this._options.idle);
}
Server.prototype.send = function(id, data) {
if (!(id in this._clients)) { return; }
this._clients[id].connection.send(data);
}
/**
* Send a "connection close" frame to client, terminate connection
* @param {id} id Client ID
* @param {null || int} code
* @param {null || string || Buffer} message Optional disconnect message
*/
Server.prototype.disconnect = function(id, code, message) {
var client = this._clients[id];
if (!client) { return; }
client.app.ondisconnect(id, code || 1005, message); /* notify the app */
client.connection.close(code, message);
}
Server.prototype.setDebug = function() {}
Server.prototype._onRequest = function(request) {
var app = this._findApp(request.httpRequest.url, request.httpRequest.headers);
if (!app) {
request.reject();
console.log(Date.now(), "No app found for", request.httpRequest.url);
return;
}
console.log(Date.now(), "Connect");
var connection = request.accept(null, request.origin);
var id = Math.random().toString().replace("0.", "");
this._clients[id] = {
connection: connection,
app: app
}
app.onconnect(id, request.httpRequest.headers);
connection.on('message', function(message) {
if (message.type === 'utf8') {
app.onmessage(id, message.utf8Data);
}
});
connection.on('close', function(reasonCode, description) {
console.log(Date.now(), "Disconnect");
if (id in this._clients) { delete this._clients[id]; }
app.ondisconnect(id, reasonCode, description);
}.bind(this));
connection.on('error', function(err) {
console.log(Date.now(), 'Error: ' + err);
});
}
Server.prototype._findApp = function(path, headers) {
var host = headers["host"];
var origin = headers["sec-websocket-origin"] || headers["origin"];
var protocol = headers["sec-websocket-protocol"];
var protocols = (protocol ? protocol.split(/, */) : []);
for (var i=0;i<this._applications.length;i++) {
var app = this._applications[i];
if (app.host && app.host != host) { continue; }
if (app.path && app.path != path) { continue; }
if (app.acceptsOrigin(origin)) { return app; }
}
return null;
}
Server.prototype._httpResponse = function(request, response) {
console.log(Date.now(), 'Received request for ' + request.url);
response.writeHead(404);
response.end();
}
exports.Server = Server;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment