Cloned DMOJ
This commit is contained in:
parent
f623974b58
commit
49dc9ff10c
513 changed files with 132349 additions and 39 deletions
206
websocket/daemon.js
Normal file
206
websocket/daemon.js
Normal file
|
@ -0,0 +1,206 @@
|
|||
var config = require('./config');
|
||||
var set = require('simplesets').Set;
|
||||
var queue = require('qu');
|
||||
var WebSocketServer = require('ws').Server;
|
||||
var wss_receiver = new WebSocketServer({host: config.get_host, port: config.get_port});
|
||||
var wss_sender = new WebSocketServer({host: config.post_host, port: config.post_port});
|
||||
var messages = new queue();
|
||||
var followers = new set();
|
||||
var pollers = new set();
|
||||
var max_queue = config.max_queue || 50;
|
||||
var long_poll_timeout = config.long_poll_timeout || 60000;
|
||||
var message_id = Date.now();
|
||||
|
||||
if (typeof String.prototype.startsWith != 'function') {
|
||||
String.prototype.startsWith = function (str){
|
||||
return this.slice(0, str.length) == str;
|
||||
};
|
||||
}
|
||||
|
||||
messages.catch_up = function (client) {
|
||||
this.each(function (message) {
|
||||
if (message.id > client.last_msg)
|
||||
client.got_message(message);
|
||||
});
|
||||
};
|
||||
|
||||
messages.post = function (channel, message) {
|
||||
message = {
|
||||
id: ++message_id,
|
||||
channel: channel,
|
||||
message: message
|
||||
};
|
||||
this.push(message);
|
||||
if (this.length > max_queue)
|
||||
this.shift();
|
||||
followers.each(function (client) {
|
||||
client.got_message(message);
|
||||
});
|
||||
pollers.each(function (request) {
|
||||
request.got_message(message);
|
||||
});
|
||||
return message.id;
|
||||
};
|
||||
|
||||
messages.last = function () {
|
||||
return this.tail().id;
|
||||
};
|
||||
|
||||
wss_receiver.on('connection', function (socket) {
|
||||
socket.channel = null;
|
||||
socket.last_msg = 0;
|
||||
|
||||
var commands = {
|
||||
start_msg: function (request) {
|
||||
socket.last_msg = request.start;
|
||||
},
|
||||
set_filter: function (request) {
|
||||
var filter = {};
|
||||
if (Array.isArray(request.filter) && request.filter.length > 0 &&
|
||||
request.filter.every(function (channel, index, array) {
|
||||
if (typeof channel != 'string')
|
||||
return false;
|
||||
filter[channel] = true;
|
||||
return true;
|
||||
})) {
|
||||
socket.filter = filter;
|
||||
followers.add(socket);
|
||||
messages.catch_up(socket);
|
||||
} else {
|
||||
socket.send(JSON.stringify({
|
||||
status: 'error',
|
||||
code: 'invalid-filter',
|
||||
message: 'invalid filter: ' + request.filter
|
||||
}));
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
socket.got_message = function (message) {
|
||||
if (message.channel in socket.filter)
|
||||
socket.send(JSON.stringify(message));
|
||||
socket.last_msg = message.id;
|
||||
};
|
||||
|
||||
socket.on('message', function (request) {
|
||||
try {
|
||||
request = JSON.parse(request);
|
||||
} catch (err) {
|
||||
socket.send(JSON.stringify({
|
||||
status: 'error',
|
||||
code: 'syntax-error',
|
||||
message: err.message
|
||||
}));
|
||||
return;
|
||||
}
|
||||
request.command = request.command.replace(/-/g, '_');
|
||||
if (request.command in commands)
|
||||
commands[request.command](request);
|
||||
else
|
||||
socket.send(JSON.stringify({
|
||||
status: 'error',
|
||||
code: 'bad-command',
|
||||
message: 'bad command: ' + request.command
|
||||
}));
|
||||
});
|
||||
|
||||
socket.on('close', function(code, message) {
|
||||
followers.remove(socket);
|
||||
});
|
||||
});
|
||||
|
||||
wss_sender.on('connection', function (socket) {
|
||||
var commands = {
|
||||
post: function (request) {
|
||||
if (typeof request.channel != 'string')
|
||||
return {
|
||||
status: 'error',
|
||||
code: 'invalid-channel'
|
||||
};
|
||||
return {
|
||||
status: 'success',
|
||||
id: messages.post(request.channel, request.message)
|
||||
};
|
||||
},
|
||||
last_msg: function (request) {
|
||||
return {
|
||||
status: 'success',
|
||||
id: message_id,
|
||||
};
|
||||
}
|
||||
};
|
||||
socket.on('message', function (request) {
|
||||
try {
|
||||
request = JSON.parse(request);
|
||||
} catch (err) {
|
||||
socket.send(JSON.stringify({
|
||||
status: 'error',
|
||||
code: 'syntax-error',
|
||||
message: err.message
|
||||
}));
|
||||
return;
|
||||
}
|
||||
request.command = request.command.replace(/-/g, '_');
|
||||
if (request.command in commands)
|
||||
socket.send(JSON.stringify(commands[request.command](request)));
|
||||
else
|
||||
socket.send(JSON.stringify({
|
||||
status: 'error',
|
||||
code: 'bad-command',
|
||||
message: 'bad command: ' + request.command
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
var url = require('url');
|
||||
require('http').createServer(function (req, res) {
|
||||
var parts = url.parse(req.url, true);
|
||||
|
||||
if (!parts.pathname.startsWith('/channels/')) {
|
||||
res.writeHead(404, {'Content-Type': 'text/plain'});
|
||||
res.end('404 Not Found');
|
||||
return;
|
||||
}
|
||||
|
||||
var channels = parts.pathname.slice(10).split('|');
|
||||
if (channels.length == 1 && !channels[0].length) {
|
||||
res.writeHead(400, {'Content-Type': 'text/plain'});
|
||||
res.end('400 Bad Request');
|
||||
return;
|
||||
}
|
||||
|
||||
req.channels = {};
|
||||
req.last_msg = parseInt(parts.query.last);
|
||||
if (isNaN(req.last_msg)) req.last_msg = 0;
|
||||
|
||||
channels.forEach(function (channel) {
|
||||
req.channels[channel] = true;
|
||||
});
|
||||
|
||||
req.on('close', function () {
|
||||
pollers.remove(req);
|
||||
});
|
||||
|
||||
req.got_message = function (message) {
|
||||
if (message.channel in req.channels) {
|
||||
res.writeHead(200, {'Content-Type': 'application/json'});
|
||||
res.end(JSON.stringify(message));
|
||||
pollers.remove(req);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
var got = false;
|
||||
messages.each(function (message) {
|
||||
if (!got && message.id > req.last_msg)
|
||||
got = req.got_message(message);
|
||||
});
|
||||
if (!got) {
|
||||
pollers.add(req);
|
||||
res.setTimeout(long_poll_timeout, function () {
|
||||
pollers.remove(req);
|
||||
res.writeHead(504, {'Content-Type': 'application/json'});
|
||||
res.end('{"error": "timeout"}');
|
||||
});
|
||||
}
|
||||
}).listen(config.http_port, config.http_host);
|
223
websocket/wscat.js
Normal file
223
websocket/wscat.js
Normal file
|
@ -0,0 +1,223 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/*!
|
||||
* ws: a node.js websocket client
|
||||
* Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var WebSocket = require('ws')
|
||||
, fs = require('fs')
|
||||
, program = require('commander')
|
||||
, util = require('util')
|
||||
, events = require('events')
|
||||
, readline = require('readline');
|
||||
|
||||
/**
|
||||
* InputReader - processes console input
|
||||
*/
|
||||
|
||||
function Console() {
|
||||
this.stdin = process.stdin;
|
||||
this.stdout = process.stdout;
|
||||
|
||||
this.readlineInterface = readline.createInterface(this.stdin, this.stdout);
|
||||
|
||||
var self = this;
|
||||
this.readlineInterface.on('line', function(data) {
|
||||
self.emit('line', data);
|
||||
});
|
||||
this.readlineInterface.on('close', function() {
|
||||
self.emit('close');
|
||||
});
|
||||
|
||||
this._resetInput = function() {
|
||||
self.clear();
|
||||
}
|
||||
}
|
||||
util.inherits(Console, events.EventEmitter);
|
||||
|
||||
Console.Colors = {
|
||||
Red: '\033[31m',
|
||||
Green: '\033[32m',
|
||||
Yellow: '\033[33m',
|
||||
Cyan: '\033[36m',
|
||||
Blue: '\033[34m',
|
||||
Default: '\033[39m'
|
||||
};
|
||||
|
||||
Console.prototype.prompt = function() {
|
||||
this.readlineInterface.prompt();
|
||||
}
|
||||
|
||||
Console.prototype.print = function(msg, color) {
|
||||
this.clear();
|
||||
color = color || Console.Colors.Default;
|
||||
this.stdout.write(color + msg + Console.Colors.Default + '\n');
|
||||
this.prompt();
|
||||
}
|
||||
|
||||
Console.prototype.clear = function() {
|
||||
this.stdout.write('\033[2K\033[E');
|
||||
}
|
||||
|
||||
Console.prototype.pause = function() {
|
||||
this.stdin.on('keypress', this._resetInput);
|
||||
}
|
||||
|
||||
Console.prototype.resume = function() {
|
||||
this.stdin.removeListener('keypress', this._resetInput);
|
||||
}
|
||||
|
||||
function appender(xs) {
|
||||
xs = xs || [];
|
||||
return function (x) {
|
||||
xs.push(x);
|
||||
return xs;
|
||||
}
|
||||
}
|
||||
|
||||
function into(obj, kvals) {
|
||||
kvals.forEach(function (kv) {
|
||||
obj[kv[0]] = kv[1];
|
||||
});
|
||||
return obj;
|
||||
}
|
||||
|
||||
function splitOnce(sep, str) { // sep can be either String or RegExp
|
||||
var tokens = str.split(sep);
|
||||
return [tokens[0], str.replace(sep, '').substr(tokens[0].length)];
|
||||
}
|
||||
|
||||
/**
|
||||
* The actual application
|
||||
*/
|
||||
|
||||
var version = '1.0';//JSON.parse(fs.readFileSync(__dirname + '/../package.json', 'utf8')).version;
|
||||
program
|
||||
.version(version)
|
||||
.usage('[options] <url>')
|
||||
.option('-l, --listen <port>', 'listen on port')
|
||||
.option('-c, --connect <url>', 'connect to a websocket server')
|
||||
.option('-p, --protocol <version>', 'optional protocol version')
|
||||
.option('-o, --origin <origin>', 'optional origin')
|
||||
.option('--host <host>', 'optional host')
|
||||
.option('-s, --subprotocol <protocol>', 'optional subprotocol')
|
||||
.option('-n, --no-check', 'Do not check for unauthorized certificates')
|
||||
.option('-H, --header <header:value>', 'Set an HTTP header. Repeat to set multiple. (--connect only)', appender(), [])
|
||||
.option('--auth <username:password>', 'Add basic HTTP authentication header. (--connect only)')
|
||||
.parse(process.argv);
|
||||
|
||||
if (program.listen && program.connect) {
|
||||
console.error('\033[33merror: use either --listen or --connect\033[39m');
|
||||
process.exit(-1);
|
||||
}
|
||||
else if (program.listen) {
|
||||
var wsConsole = new Console();
|
||||
wsConsole.pause();
|
||||
var options = {};
|
||||
if (program.protocol) options.protocolVersion = program.protocol;
|
||||
if (program.origin) options.origin = program.origin;
|
||||
if (program.subprotocol) options.protocol = program.subprotocol;
|
||||
if (!program.check) options.rejectUnauthorized = program.check;
|
||||
var ws = null;
|
||||
var wss = new WebSocket.Server({port: program.listen}, function() {
|
||||
wsConsole.print('listening on port ' + program.listen + ' (press CTRL+C to quit)', Console.Colors.Green);
|
||||
wsConsole.clear();
|
||||
});
|
||||
wsConsole.on('close', function() {
|
||||
if (ws) {
|
||||
try {
|
||||
ws.close();
|
||||
}
|
||||
catch (e) {}
|
||||
}
|
||||
process.exit(0);
|
||||
});
|
||||
wsConsole.on('line', function(data) {
|
||||
if (ws) {
|
||||
ws.send(data, {mask: false});
|
||||
wsConsole.prompt();
|
||||
}
|
||||
});
|
||||
wss.on('connection', function(newClient) {
|
||||
if (ws) {
|
||||
// limit to one client
|
||||
newClient.terminate();
|
||||
return;
|
||||
};
|
||||
ws = newClient;
|
||||
wsConsole.resume();
|
||||
wsConsole.prompt();
|
||||
wsConsole.print('client connected', Console.Colors.Green);
|
||||
ws.on('close', function() {
|
||||
wsConsole.print('disconnected', Console.Colors.Green);
|
||||
wsConsole.clear();
|
||||
wsConsole.pause();
|
||||
ws = null;
|
||||
});
|
||||
ws.on('error', function(code, description) {
|
||||
wsConsole.print('error: ' + code + (description ? ' ' + description : ''), Console.Colors.Yellow);
|
||||
});
|
||||
ws.on('message', function(data, flags) {
|
||||
wsConsole.print('< ' + data, Console.Colors.Blue);
|
||||
});
|
||||
});
|
||||
wss.on('error', function(error) {
|
||||
wsConsole.print('error: ' + error.toString(), Console.Colors.Yellow);
|
||||
process.exit(-1);
|
||||
});
|
||||
}
|
||||
else if (program.connect) {
|
||||
var wsConsole = new Console();
|
||||
var options = {};
|
||||
if (program.protocol) options.protocolVersion = program.protocol;
|
||||
if (program.origin) options.origin = program.origin;
|
||||
if (program.subprotocol) options.protocol = program.subprotocol;
|
||||
if (program.host) options.host = program.host;
|
||||
if (!program.check) options.rejectUnauthorized = program.check;
|
||||
var headers = into({}, (program.header || []).map(function (s) {
|
||||
return splitOnce(':', s)
|
||||
}));
|
||||
if (program.auth) {
|
||||
headers['Authorization'] = 'Basic ' + new Buffer(program.auth).toString('base64');
|
||||
}
|
||||
options.headers = headers;
|
||||
var ws = new WebSocket(program.connect, options);
|
||||
ws.on('open', function() {
|
||||
wsConsole.print('connected (press CTRL+C to quit)', Console.Colors.Green);
|
||||
wsConsole.on('line', function(data) {
|
||||
ws.send(data, {mask: true});
|
||||
wsConsole.prompt();
|
||||
});
|
||||
});
|
||||
ws.on('close', function() {
|
||||
wsConsole.print('disconnected', Console.Colors.Green);
|
||||
wsConsole.clear();
|
||||
process.exit();
|
||||
});
|
||||
ws.on('error', function(code, description) {
|
||||
wsConsole.print('error: ' + code + (description ? ' ' + description : ''), Console.Colors.Yellow);
|
||||
process.exit(-1);
|
||||
});
|
||||
ws.on('message', function(data, flags) {
|
||||
wsConsole.print('< ' + data, Console.Colors.Cyan);
|
||||
});
|
||||
wsConsole.on('close', function() {
|
||||
if (ws) {
|
||||
try {
|
||||
ws.close();
|
||||
}
|
||||
catch(e) {}
|
||||
process.exit();
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
console.error('\033[33merror: use either --listen or --connect\033[39m');
|
||||
process.exit(-1);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue