/* * ws.js: Macros for proxying Websocket requests * * (C) 2010 Nodejitsu Inc. * MIT LICENCE * */ var assert = require('assert'), fs = require('fs'), async = require('async'), io = require('socket.io-client'), WebSocket = require('ws'), helpers = require('../helpers/index'); // // ### function assertSendRecieve (options) // #### @options {Object} Options for creating this assertion. // #### @raw {boolean} Enables raw `ws.WebSocket`. // #### @uri {string} URI of the proxy server. // #### @input {string} Input to assert sent to the target ws server. // #### @output {string} Output to assert from the taget ws server. // // Creates a `socket.io` or raw `WebSocket` connection and asserts that // `options.input` is sent to and `options.output` is received from the // connection. // exports.assertSendReceive = function (options) { if (!options.raw) { return { topic: function () { var socket = io.connect(options.uri); socket.on('outgoing', this.callback.bind(this, null)); socket.emit('incoming', options.input); }, "should send input and receive output": function (_, data) { assert.equal(data, options.output); } }; } return { topic: function () { var socket = new WebSocket(options.uri); socket.on('message', this.callback.bind(this, null)); socket.on('open', function () { socket.send(options.input); }); }, "should send input and recieve output": function (_, data, flags) { assert.equal(data, options.output); } }; }; // // ### function assertProxied (options) // #### @options {Object} Options for this test // #### @latency {number} Latency in milliseconds for the proxy server. // #### @ports {Object} Ports for the request (target, proxy). // #### @input {string} Input to assert sent to the target ws server. // #### @output {string} Output to assert from the taget ws server. // #### @raw {boolean} Enables raw `ws.Server` usage. // // Creates a complete end-to-end test for requesting against an // http proxy. // exports.assertProxied = function (options) { options = options || {}; var ports = options.ports || helpers.nextPortPair, input = options.input || 'hello world to ' + ports.target, output = options.output || 'hello world from ' + ports.target, protocol = helpers.protocols.proxy; if (options.raw) { protocol = helpers.protocols.proxy === 'https' ? 'wss' : 'ws'; } return { topic: function () { helpers.ws.createServerPair({ target: { input: input, output: output, port: ports.target, raw: options.raw }, proxy: { latency: options.latency, port: ports.proxy, proxy: { target: { https: helpers.protocols.target === 'https', host: '127.0.0.1', port: ports.target } } } }, this.callback); }, "the proxy Websocket connection": exports.assertSendReceive({ uri: protocol + '://127.0.0.1:' + ports.proxy, input: input, output: output, raw: options.raw }) }; }; // // ### function assertProxiedtoRoutes (options, nested) // #### @options {Object} Options for this ProxyTable-based test // #### @raw {boolean} Enables ws.Server usage. // #### @routes {Object|string} Routes to use for the proxy. // #### @hostnameOnly {boolean} Enables hostnameOnly routing. // #### @nested {Object} Nested vows to add to the returned context. // // Creates a complete end-to-end test for requesting against an // http proxy using `options.routes`: // // 1. Creates target servers for all routes in `options.routes.` // 2. Creates a proxy server. // 3. Ensure Websocket connections to the proxy server for all route targets // can send input and recieve output. // exports.assertProxiedToRoutes = function (options, nested) { // // Assign dynamic ports to the routes to use. // options.routes = helpers.http.assignPortsToRoutes(options.routes); // // Parse locations from routes for making assertion requests. // var locations = helpers.http.parseRoutes(options), protocol = helpers.protocols.proxy, port = helpers.nextPort, context, proxy; if (options.raw) { protocol = helpers.protocols.proxy === 'https' ? 'wss' : 'ws'; } if (options.filename) { // // If we've been passed a filename write the routes to it // and setup the proxy options to use that file. // fs.writeFileSync(options.filename, JSON.stringify({ router: options.routes })); proxy = { router: options.filename }; } else { // // Otherwise just use the routes themselves. // proxy = { hostnameOnly: options.hostnameOnly, router: options.routes }; } // // Create the test context which creates all target // servers for all routes and a proxy server. // context = { topic: function () { var that = this; async.waterfall([ // // 1. Create all the target servers // async.apply( async.forEach, locations, function createRouteTarget(location, next) { helpers.ws.createServer({ raw: options.raw, port: location.target.port, output: 'hello from ' + location.source.href, input: 'hello to ' + location.source.href }, next); } ), // // 2. Create the proxy server // async.apply( helpers.http.createProxyServer, { port: port, latency: options.latency, routing: true, proxy: proxy } ) ], function (_, server) { // // 3. Set the proxy server for later use // that.proxyServer = server; that.callback(); }); // // 4. Assign the port to the context for later use // this.port = port; } }; // // Add test assertions for each of the route locations. // locations.forEach(function (location) { context[location.source.href] = exports.assertSendRecieve({ uri: protocol + '://127.0.0.1:' + port + location.source.path, output: 'hello from ' + location.source.href, input: 'hello to ' + location.source.href, raw: options.raw }); }); return context; };