var http = require('http') , util = require('util') , crypto = require('crypto') , events = require('events') , Sender = require('../lib/Sender') , Receiver = require('../lib/Receiver'); module.exports = { handlers: { valid: validServer, invalidKey: invalidRequestHandler, closeAfterConnect: closeAfterConnectHandler, return401: return401 }, createServer: function(port, handler, cb) { if (handler && !cb) { cb = handler; handler = null; } var webServer = http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('okay'); }); var srv = new Server(webServer); webServer.on('upgrade', function(req, socket) { webServer._socket = socket; (handler || validServer)(srv, req, socket); }); webServer.listen(port, '127.0.0.1', function() { cb(srv); }); } }; /** * Test strategies */ function validServer(server, req, socket) { if (typeof req.headers.upgrade === 'undefined' || req.headers.upgrade.toLowerCase() !== 'websocket') { throw new Error('invalid headers'); return; } if (!req.headers['sec-websocket-key']) { socket.end(); throw new Error('websocket key is missing'); } // calc key var key = req.headers['sec-websocket-key']; var shasum = crypto.createHash('sha1'); shasum.update(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); key = shasum.digest('base64'); var headers = [ 'HTTP/1.1 101 Switching Protocols' , 'Upgrade: websocket' , 'Connection: Upgrade' , 'Sec-WebSocket-Accept: ' + key ]; socket.write(headers.concat('', '').join('\r\n')); socket.setTimeout(0); socket.setNoDelay(true); var sender = new Sender(socket); var receiver = new Receiver(); receiver.ontext = function (message, flags) { server.emit('message', message, flags); sender.send(message); }; receiver.onbinary = function (message, flags) { flags = flags || {}; flags.binary = true; server.emit('message', message, flags); sender.send(message, {binary: true}); }; receiver.onping = function (message, flags) { flags = flags || {}; server.emit('ping', message, flags); }; receiver.onpong = function (message, flags) { flags = flags || {}; server.emit('pong', message, flags); }; receiver.onclose = function (code, message, flags) { flags = flags || {}; server.emit('close', code, message, flags); }; socket.on('data', function (data) { receiver.add(data); }); socket.on('end', function() { socket.end(); }); } function invalidRequestHandler(server, req, socket) { if (typeof req.headers.upgrade === 'undefined' || req.headers.upgrade.toLowerCase() !== 'websocket') { throw new Error('invalid headers'); return; } if (!req.headers['sec-websocket-key']) { socket.end(); throw new Error('websocket key is missing'); } // calc key var key = req.headers['sec-websocket-key']; var shasum = crypto.createHash('sha1'); shasum.update(key + "bogus"); key = shasum.digest('base64'); var headers = [ 'HTTP/1.1 101 Switching Protocols' , 'Upgrade: websocket' , 'Connection: Upgrade' , 'Sec-WebSocket-Accept: ' + key ]; socket.write(headers.concat('', '').join('\r\n')); socket.end(); } function closeAfterConnectHandler(server, req, socket) { if (typeof req.headers.upgrade === 'undefined' || req.headers.upgrade.toLowerCase() !== 'websocket') { throw new Error('invalid headers'); return; } if (!req.headers['sec-websocket-key']) { socket.end(); throw new Error('websocket key is missing'); } // calc key var key = req.headers['sec-websocket-key']; var shasum = crypto.createHash('sha1'); shasum.update(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); key = shasum.digest('base64'); var headers = [ 'HTTP/1.1 101 Switching Protocols' , 'Upgrade: websocket' , 'Connection: Upgrade' , 'Sec-WebSocket-Accept: ' + key ]; socket.write(headers.concat('', '').join('\r\n')); socket.end(); } function return401(server, req, socket) { var headers = [ 'HTTP/1.1 401 Unauthorized' , 'Content-type: text/html' ]; socket.write(headers.concat('', '').join('\r\n')); socket.end(); } /** * Server object, which will do the actual emitting */ function Server(webServer) { this.webServer = webServer; } util.inherits(Server, events.EventEmitter); Server.prototype.close = function() { this.webServer.close(); if (this._socket) this._socket.end(); }