Skip to content

Commit

Permalink
[api doc test] node-http-proxy now emits websocket:* on important W…
Browse files Browse the repository at this point in the history
…ebSocket events. Added tests for these features and updated some code docs
  • Loading branch information
indexzero committed Jun 26, 2011
1 parent fcfe846 commit 4f85ca0
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 101 deletions.
104 changes: 74 additions & 30 deletions lib/node-http-proxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -553,17 +553,29 @@ HttpProxy.prototype._forwardRequest = function (req) {
};

HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options) {
var self = this, outgoing, errState = false, CRLF = '\r\n';
var self = this,
listeners = {},
errState = false,
CRLF = '\r\n',
outgoing;

// WebSocket requests has method = GET
//
// WebSocket requests must have the `GET` method and
// the `upgrade:websocket` header
//
if (req.method !== 'GET' || req.headers.upgrade.toLowerCase() !== 'websocket') {
//
// This request is not WebSocket request
//
return;
}

// Turn of all bufferings
// For server set KeepAlive
// For client set encoding
//
// Helper function for setting appropriate socket values:
// 1. Turn of all bufferings
// 2. For server set KeepAlive
// 3. For client set encoding
//
function _socket(socket, keepAlive) {
socket.setTimeout(0);
socket.setNoDelay(true);
Expand All @@ -580,20 +592,25 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options
}
}

function onUpgrade(reverseProxy, proxySocket) {
//
// On `upgrade` from the Agent socket, listen to
// the appropriate events.
//
function onUpgrade (reverseProxy, proxySocket) {
if (!reverseProxy) {
proxySocket.end();
socket.end();
return;
}

var listeners = {};

// We're now connected to the server, so lets change server socket
proxySocket.on('data', listeners._r_data = function(data) {
// Pass data to client
//
// Any incoming data on this WebSocket to the proxy target
// will be written to the `reverseProxy` socket.
//
proxySocket.on('data', listeners.onIncoming = function (data) {
if (reverseProxy.incoming.socket.writable) {
try {
self.emit('websocket:outgoing', req, socket, head, data);
reverseProxy.incoming.socket.write(data);
}
catch (e) {
Expand All @@ -603,62 +620,88 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options
}
});

reverseProxy.incoming.socket.on('data', listeners._data = function(data) {
// Pass data from client to server
//
// Any outgoing data on this Websocket from the proxy target
// will be written to the `proxySocket` socket.
//
reverseProxy.incoming.socket.on('data', listeners.onOutgoing = function(data) {
try {
self.emit('websocket:incoming', reverseProxy, reverseProxy.incoming, head, data);
proxySocket.write(data);
}
catch (e) {
proxySocket.end();
socket.end();
}
});

// Detach event listeners from reverseProxy

//
// Helper function to detach all event listeners
// from `reverseProxy` and `proxySocket`.
//
function detach() {
proxySocket.removeListener('end', listeners._r_close);
proxySocket.removeListener('data', listeners._r_data);
reverseProxy.incoming.socket.removeListener('data', listeners._data);
reverseProxy.incoming.socket.removeListener('end', listeners._close);
proxySocket.removeListener('end', listeners.onIncomingClose);
proxySocket.removeListener('data', listeners.onIncoming);
reverseProxy.incoming.socket.removeListener('end', listeners.onOutgoingClose);
reverseProxy.incoming.socket.removeListener('data', listeners.onOutgoing);
}

// Hook disconnections
proxySocket.on('end', listeners._r_close = function() {
//
// If the incoming `proxySocket` socket closes, then
// detach all event listeners.
//
proxySocket.on('end', listeners.onIncomingClose = function() {
reverseProxy.incoming.socket.end();
detach();

// Emit the `end` event now that we have completed proxying
self.emit('websocket:end', req, socket, head);
});

reverseProxy.incoming.socket.on('end', listeners._close = function() {
//
// If the `reverseProxy` socket closes, then detach all
// event listeners.
//
reverseProxy.incoming.socket.on('end', listeners.onOutgoingClose = function() {
proxySocket.end();
detach();
});
};

// Client socket
// Setup the incoming client socket.
_socket(socket);

// Remote host address
//
// Get the protocol, and host for this request and create an instance
// of `http.Agent` or `https.Agent` from the pool managed by `node-http-proxy`.
//
var protocolName = options.https || this.target.https ? 'https' : 'http',
agent = _getAgent(options.host, options.port, options.https || this.target.https),
remoteHost = options.host + (options.port - 80 === 0 ? '' : ':' + options.port);
remoteHost = options.host + (options.port - 80 === 0 ? '' : ':' + options.port),
agent = _getAgent(options.host, options.port, options.https || this.target.https);

// Change headers
// Change headers (if requested).
if (this.changeOrigin) {
req.headers.host = remoteHost;
req.headers.origin = protocolName + '://' + remoteHost;
}

//
// Make the outgoing WebSocket request
//
outgoing = {
host: options.host,
port: options.port,
method: 'GET',
path: req.url,
headers: req.headers,
};

// Make the outgoing WebSocket request
var reverseProxy = agent.appendMessage(outgoing);

//
// On any errors from the `reverseProxy` emit the
// `webSocketProxyError` and close the appropriate
// connections.
//
function proxyError (err) {
reverseProxy.end();
if (self.emit('webSocketProxyError', req, socket, head)) {
Expand Down Expand Up @@ -703,7 +746,7 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options

//
// If the reverseProxy connection has an underlying socket,
// then behing the handshake.
// then execute the WebSocket handshake.
//
if (typeof reverseProxy.socket !== 'undefined') {
reverseProxy.socket.on('data', function handshake (data) {
Expand Down Expand Up @@ -741,6 +784,7 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options
// Write the printable and non-printable data to the socket
// from the original incoming request.
//
self.emit('websocket:handshake', req, socket, head, sdata, data);
socket.write(sdata);
socket.write(data);
}
Expand Down
34 changes: 34 additions & 0 deletions test/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ var fs = require('fs'),
vows = require('vows'),
assert = require('assert'),
request = require('request'),
websocket = require('./../vendor/websocket'),
httpProxy = require('./../lib/node-http-proxy');

function merge (target) {
Expand Down Expand Up @@ -120,6 +121,39 @@ TestRunner.prototype.assertResponseCode = function (proxyPort, statusCode, creat
return test;
};

TestRunner.prototype.webSocketTest = function (options) {
var self = this;

this.startTargetServer(options.ports.target, 'hello websocket', function (err, target) {
var socket = options.io.listen(target);

if (options.onListen) {
options.onListen(socket);
}

self.startProxyServer(
options.ports.proxy,
options.ports.target,
options.host,
function (err, proxy) {
if (options.onServer) { options.onServer(proxy) }

//
// Setup the web socket against our proxy
//
var uri = options.wsprotocol + '://' + options.host + ':' + options.ports.proxy;
var ws = new websocket.WebSocket(uri + '/socket.io/websocket/', 'borf', {
origin: options.protocol + '://' + options.host
});

if (options.onWsupgrade) { ws.on('wsupgrade', options.onWsupgrade) }
if (options.onMessage) { ws.on('message', options.onMessage) }
if (options.onOpen) { ws.on('open', function () { options.onOpen(ws) }) }
}
);
});
}

//
// Creates the reverse proxy server
//
Expand Down
6 changes: 3 additions & 3 deletions test/node-http-proxy-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@
*/

var vows = require('vows'),
var assert = require('assert'),
util = require('util'),
request = require('request'),
assert = require('assert'),
argv = require('optimist').argv,
request = require('request'),
vows = require('vows'),
helpers = require('./helpers');

var forwardOptions = {
Expand Down
10 changes: 5 additions & 5 deletions test/proxy-table-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
*
*/

var fs = require('fs'),
vows = require('vows'),
util = require('util'),
var assert = require('assert'),
fs = require('fs'),
path = require('path'),
util = require('util'),
argv = require('optimist').argv,
request = require('request'),
assert = require('assert'),
vows = require('vows'),
helpers = require('./helpers'),
argv = require('optimist').argv,
TestRunner = helpers.TestRunner;

var protocol = argv.https ? 'https' : 'http',
Expand Down
Loading

0 comments on commit 4f85ca0

Please sign in to comment.