'use strict'; var JX = require('./javelin').JX; require('./AphlictListenerList'); require('./AphlictLog'); var url = require('url'); var util = require('util'); var WebSocket = require('ws'); JX.install('AphlictClientServer', { construct: function(server) { server.on('request', JX.bind(this, this._onrequest)); this._server = server; this._lists = {}; }, properties: { logger: null, }, members: { _server: null, _lists: null, getListenerList: function(instance) { if (!this._lists[instance]) { this._lists[instance] = new JX.AphlictListenerList(instance); } return this._lists[instance]; }, log: function() { var logger = this.getLogger(); if (!logger) { return; } logger.log.apply(logger, arguments); return this; }, _onrequest: function(request, response) { // The websocket code upgrades connections before they get here, so // this only handles normal HTTP connections. We just fail them with // a 501 response. response.writeHead(501); response.end('HTTP/501 Use Websockets\n'); }, _parseInstanceFromPath: function(path) { // If there's no "~" marker in the path, it's not an instance name. // Users sometimes configure nginx or Apache to proxy based on the // path. if (path.indexOf('~') === -1) { return 'default'; } var instance = path.split('~')[1]; // Remove any "/" characters. instance = instance.replace(/\//g, ''); if (!instance.length) { return 'default'; } return instance; }, listen: function() { var self = this; var server = this._server.listen.apply(this._server, arguments); var wss = new WebSocket.Server({server: server}); wss.on('connection', function(ws) { var path = url.parse(ws.upgradeReq.url).pathname; var instance = self._parseInstanceFromPath(path); var listener = self.getListenerList(instance).addListener(ws); function log() { self.log( util.format('<%s>', listener.getDescription()) + ' ' + util.format.apply(null, arguments)); } log('Connected from %s.', ws._socket.remoteAddress); ws.on('message', function(data) { log('Received message: %s', data); var message; try { message = JSON.parse(data); } catch (err) { log('Message is invalid: %s', err.message); return; } switch (message.command) { case 'subscribe': log( 'Subscribed to: %s', JSON.stringify(message.data)); listener.subscribe(message.data); break; case 'unsubscribe': log( 'Unsubscribed from: %s', JSON.stringify(message.data)); listener.unsubscribe(message.data); break; default: log( 'Unrecognized command "%s".', message.command || '<undefined>'); } }); ws.on('close', function() { self.getListenerList(instance).removeListener(listener); log('Disconnected.'); }); }); } } });