Changeset View
Changeset View
Standalone View
Standalone View
support/aphlict/server/aphlict_server.js
| /** | |||||
| * Notification server. Launch with: | |||||
| * | |||||
| * sudo node aphlict_server.js --user=aphlict | |||||
| * | |||||
| * You can also specify `port`, `admin`, `host` and `log`. | |||||
| */ | |||||
| var JX = require('./lib/javelin').JX; | var JX = require('./lib/javelin').JX; | ||||
| var http = require('http'); | |||||
| var util = require('util'); | |||||
| var WebSocket = require('ws'); | |||||
joshuaspence: Maybe we should `try`/`catch` this and provide installation instructions if it is missing. | |||||
| JX.require('lib/AphlictFlashPolicyServer', __dirname); | |||||
| JX.require('lib/AphlictListenerList', __dirname); | |||||
| JX.require('lib/AphlictLog', __dirname); | JX.require('lib/AphlictLog', __dirname); | ||||
| function parse_command_line_arguments(argv) { | function parse_command_line_arguments(argv) { | ||||
| var config = { | var config = { | ||||
| port: 22280, | port: 8080, | ||||
| admin: 22281, | admin: 8081, | ||||
| host: '127.0.0.1', | host: '127.0.0.1', | ||||
| user: null, | user: null, | ||||
| log: '/var/log/aphlict.log' | log: '/var/log/aphlict.log' | ||||
| }; | }; | ||||
Not Done Inline ActionsSort of ugly, maybe just cert and key would suffice. joshuaspence: Sort of ugly, maybe just `cert` and `key` would suffice. | |||||
| for (var ii = 2; ii < argv.length; ii++) { | for (var ii = 2; ii < argv.length; ii++) { | ||||
| var arg = argv[ii]; | var arg = argv[ii]; | ||||
| var matches = arg.match(/^--([^=]+)=(.*)$/); | var matches = arg.match(/^--([^=]+)=(.*)$/); | ||||
| if (!matches) { | if (!matches) { | ||||
| throw new Error("Unknown argument '" + arg + "'!"); | throw new Error("Unknown argument '" + arg + "'!"); | ||||
| } | } | ||||
| if (!(matches[1] in config)) { | if (!(matches[1] in config)) { | ||||
| throw new Error("Unknown argument '" + matches[1] + "'!"); | throw new Error("Unknown argument '" + matches[1] + "'!"); | ||||
| } | } | ||||
| config[matches[1]] = matches[2]; | config[matches[1]] = matches[2]; | ||||
| } | } | ||||
| config.port = parseInt(config.port, 10); | config.port = parseInt(config.port, 10); | ||||
| config.admin = parseInt(config.admin, 10); | config.admin = parseInt(config.admin, 10); | ||||
| return config; | return config; | ||||
| } | } | ||||
| var debug = new JX.AphlictLog() | var debug = new JX.AphlictLog() | ||||
| .addConsole(console); | .addConsole(console); | ||||
| var clients = new JX.AphlictListenerList(); | |||||
| var config = parse_command_line_arguments(process.argv); | var config = parse_command_line_arguments(process.argv); | ||||
| if (config.logfile) { | if (config.logfile) { | ||||
| debug.addLogfile(config.logfile); | debug.addLogfile(config.logfile); | ||||
| } | } | ||||
| if (process.getuid() !== 0) { | |||||
| console.log( | |||||
| "ERROR: " + | |||||
| "This server must be run as root because it needs to bind to privileged " + | |||||
| "port 843 to start a Flash policy server. It will downgrade to run as a " + | |||||
| "less-privileged user after binding if you pass a user in the command " + | |||||
| "line arguments with '--user=alincoln'."); | |||||
| process.exit(1); | |||||
| } | |||||
| var net = require('net'); | |||||
| var http = require('http'); | |||||
| process.on('uncaughtException', function(err) { | process.on('uncaughtException', function(err) { | ||||
| debug.log('\n<<< UNCAUGHT EXCEPTION! >>>\n' + err.stack); | debug.log('\n<<< UNCAUGHT EXCEPTION! >>>\n' + err.stack); | ||||
| process.exit(1); | process.exit(1); | ||||
| }); | }); | ||||
| new JX.AphlictFlashPolicyServer() | // WEBSOCKETS | ||||
| .setDebugLog(debug) | var ws = new WebSocket.Server({host: config.host, port: config.port}); | ||||
| .setAccessPort(config.port) | |||||
| .start(); | |||||
| net.createServer(function(socket) { | |||||
| var listener = clients.addListener(socket); | |||||
| debug.log('<%s> Connected from %s', | |||||
| listener.getDescription(), | |||||
| socket.remoteAddress); | |||||
| var buffer = new Buffer([]); | |||||
| var length = 0; | |||||
| socket.on('data', function(data) { | |||||
| buffer = Buffer.concat([buffer, new Buffer(data)]); | |||||
| while (buffer.length) { | ws.on('connection', function(ws) { | ||||
| if (!length) { | function log() { | ||||
| length = buffer.readUInt16BE(0); | debug.log( | ||||
| buffer = buffer.slice(2); | util.format('<%s:%d>', ws._socket.remoteAddress, ws._socket.remotePort) + | ||||
| ' ' + | |||||
| util.format.apply(null, arguments)); | |||||
| } | } | ||||
| if (buffer.length < length) { | log('Connected.'); | ||||
| // We need to wait for the rest of the data. | |||||
| return; | |||||
| } | |||||
| var message; | ws.on('message', function(data) { | ||||
| try { | try { | ||||
| message = JSON.parse(buffer.toString('utf8', 0, length)); | data = JSON.parse(data); | ||||
| log('Received data: %s', JSON.stringify(data)); | |||||
| } catch (err) { | } catch (err) { | ||||
| debug.log('<%s> Received invalid data.', listener.getDescription()); | log('Received invalid data: %s', err.message); | ||||
| continue; | |||||
| } finally { | |||||
| buffer = buffer.slice(length); | |||||
| length = 0; | |||||
| } | } | ||||
Not Done Inline ActionsMaybe include ws._socket.remotePort as well? joshuaspence: Maybe include `ws._socket.remotePort` as well? | |||||
| debug.log('<%s> Received data: %s', | |||||
| listener.getDescription(), | |||||
| JSON.stringify(message)); | |||||
| switch (message.command) { | switch (message.command) { | ||||
| case 'subscribe': | case 'subscribe': | ||||
| debug.log( | debug.log( | ||||
| '<%s> Subscribed to: %s', | '<%s> Subscribed to: %s', | ||||
| listener.getDescription(), | listener.getDescription(), | ||||
| JSON.stringify(message.data)); | JSON.stringify(message.data)); | ||||
| listener.subscribe(message.data); | listener.subscribe(message.data); | ||||
| break; | break; | ||||
| case 'unsubscribe': | case 'unsubscribe': | ||||
| debug.log( | debug.log( | ||||
| '<%s> Unsubscribed from: %s', | '<%s> Unsubscribed from: %s', | ||||
| listener.getDescription(), | listener.getDescription(), | ||||
| JSON.stringify(message.data)); | JSON.stringify(message.data)); | ||||
| listener.unsubscribe(message.data); | listener.unsubscribe(message.data); | ||||
| break; | break; | ||||
| default: | default: | ||||
| debug.log('<s> Unrecognized command.', listener.getDescription()); | debug.log('<s> Unrecognized command.', listener.getDescription()); | ||||
| } | } | ||||
| } | |||||
| }); | }); | ||||
| socket.on('close', function() { | ws.on('close', function(ws) { | ||||
| clients.removeListener(listener); | log('Closed.'); | ||||
| debug.log('<%s> Disconnected', listener.getDescription()); | |||||
| }); | }); | ||||
| socket.on('timeout', function() { | ws.on('error', function(err) { | ||||
| debug.log('<%s> Timed Out', listener.getDescription()); | log('Error: %s', err.message); | ||||
| }); | }); | ||||
| socket.on('end', function() { | |||||
| debug.log('<%s> Ended Connection', listener.getDescription()); | |||||
| }); | |||||
| socket.on('error', function(e) { | |||||
| debug.log('<%s> Error: %s', listener.getDescription(), e); | |||||
| }); | }); | ||||
| }).listen(config.port); | ws.broadcast = function broadcast(data) { | ||||
| for (var i in this.clients) { | |||||
| this.clients[i].send(data); | |||||
| var messages_out = 0; | } | ||||
| var messages_in = 0; | }; | ||||
| var start_time = new Date().getTime(); | |||||
| function transmit(msg) { | |||||
| var listeners = clients.getListeners().filter(function(client) { | |||||
| return client.isSubscribedToAny(msg.subscribers); | |||||
| }); | |||||
| for (var i = 0; i < listeners.length; i++) { | // If we're configured to drop permissions, get rid of them now that we've | ||||
| var listener = listeners[i]; | // bound to the ports we need and opened logfiles. | ||||
| if (config.user) { | |||||
| process.setuid(config.user); | |||||
| } | |||||
| try { | debug.log('Started Server (PID %d)', process.pid); | ||||
| listener.writeMessage(msg); | |||||
| ++messages_out; | |||||
| debug.log('<%s> Wrote Message', listener.getDescription()); | |||||
| } catch (error) { | |||||
| clients.removeListener(listener); | |||||
| debug.log('<%s> Write Error: %s', listener.getDescription(), error); | |||||
| } | |||||
| } | |||||
| } | |||||
| http.createServer(function(request, response) { | http.createServer(function(request, response) { | ||||
| // Publishing a notification. | // Publishing a notification. | ||||
| if (request.url == '/') { | if (request.url == '/') { | ||||
| if (request.method == 'POST') { | if (request.method == 'POST') { | ||||
| var body = ''; | var body = ''; | ||||
| request.on('data', function(data) { | request.on('data', function(data) { | ||||
| ▲ Show 20 Lines • Show All 56 Lines • ▼ Show 20 Lines | request.on('end', function() { | ||||
| response.end(); | response.end(); | ||||
| }); | }); | ||||
| } else { | } else { | ||||
| response.statusCode = 404; | response.statusCode = 404; | ||||
| response.write('404 Not Found\n'); | response.write('404 Not Found\n'); | ||||
| response.end(); | response.end(); | ||||
| } | } | ||||
| }).listen(config.admin, config.host); | }).listen(config.admin, config.host); | ||||
| // If we're configured to drop permissions, get rid of them now that we've | |||||
| // bound to the ports we need and opened logfiles. | |||||
| if (config.user) { | |||||
| process.setuid(config.user); | |||||
| } | |||||
| debug.log('Started Server (PID %d)', process.pid); | |||||
Maybe we should try/catch this and provide installation instructions if it is missing. Otherwise, users who blindly update may not understand why Aphlict stopped working.