Skip to content

Commit 4f85ca0

Browse files
committed
[api doc test] node-http-proxy now emits websocket:* on important WebSocket events. Added tests for these features and updated some code docs
1 parent fcfe846 commit 4f85ca0

File tree

5 files changed

+201
-101
lines changed

5 files changed

+201
-101
lines changed

lib/node-http-proxy.js

+74-30
Original file line numberDiff line numberDiff line change
@@ -553,17 +553,29 @@ HttpProxy.prototype._forwardRequest = function (req) {
553553
};
554554

555555
HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options) {
556-
var self = this, outgoing, errState = false, CRLF = '\r\n';
556+
var self = this,
557+
listeners = {},
558+
errState = false,
559+
CRLF = '\r\n',
560+
outgoing;
557561

558-
// WebSocket requests has method = GET
562+
//
563+
// WebSocket requests must have the `GET` method and
564+
// the `upgrade:websocket` header
565+
//
559566
if (req.method !== 'GET' || req.headers.upgrade.toLowerCase() !== 'websocket') {
567+
//
560568
// This request is not WebSocket request
569+
//
561570
return;
562571
}
563572

564-
// Turn of all bufferings
565-
// For server set KeepAlive
566-
// For client set encoding
573+
//
574+
// Helper function for setting appropriate socket values:
575+
// 1. Turn of all bufferings
576+
// 2. For server set KeepAlive
577+
// 3. For client set encoding
578+
//
567579
function _socket(socket, keepAlive) {
568580
socket.setTimeout(0);
569581
socket.setNoDelay(true);
@@ -580,20 +592,25 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options
580592
}
581593
}
582594

583-
function onUpgrade(reverseProxy, proxySocket) {
595+
//
596+
// On `upgrade` from the Agent socket, listen to
597+
// the appropriate events.
598+
//
599+
function onUpgrade (reverseProxy, proxySocket) {
584600
if (!reverseProxy) {
585601
proxySocket.end();
586602
socket.end();
587603
return;
588604
}
589605

590-
var listeners = {};
591-
592-
// We're now connected to the server, so lets change server socket
593-
proxySocket.on('data', listeners._r_data = function(data) {
594-
// Pass data to client
606+
//
607+
// Any incoming data on this WebSocket to the proxy target
608+
// will be written to the `reverseProxy` socket.
609+
//
610+
proxySocket.on('data', listeners.onIncoming = function (data) {
595611
if (reverseProxy.incoming.socket.writable) {
596612
try {
613+
self.emit('websocket:outgoing', req, socket, head, data);
597614
reverseProxy.incoming.socket.write(data);
598615
}
599616
catch (e) {
@@ -603,62 +620,88 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options
603620
}
604621
});
605622

606-
reverseProxy.incoming.socket.on('data', listeners._data = function(data) {
607-
// Pass data from client to server
623+
//
624+
// Any outgoing data on this Websocket from the proxy target
625+
// will be written to the `proxySocket` socket.
626+
//
627+
reverseProxy.incoming.socket.on('data', listeners.onOutgoing = function(data) {
608628
try {
629+
self.emit('websocket:incoming', reverseProxy, reverseProxy.incoming, head, data);
609630
proxySocket.write(data);
610631
}
611632
catch (e) {
612633
proxySocket.end();
613634
socket.end();
614635
}
615636
});
616-
617-
// Detach event listeners from reverseProxy
637+
638+
//
639+
// Helper function to detach all event listeners
640+
// from `reverseProxy` and `proxySocket`.
641+
//
618642
function detach() {
619-
proxySocket.removeListener('end', listeners._r_close);
620-
proxySocket.removeListener('data', listeners._r_data);
621-
reverseProxy.incoming.socket.removeListener('data', listeners._data);
622-
reverseProxy.incoming.socket.removeListener('end', listeners._close);
643+
proxySocket.removeListener('end', listeners.onIncomingClose);
644+
proxySocket.removeListener('data', listeners.onIncoming);
645+
reverseProxy.incoming.socket.removeListener('end', listeners.onOutgoingClose);
646+
reverseProxy.incoming.socket.removeListener('data', listeners.onOutgoing);
623647
}
624648

625-
// Hook disconnections
626-
proxySocket.on('end', listeners._r_close = function() {
649+
//
650+
// If the incoming `proxySocket` socket closes, then
651+
// detach all event listeners.
652+
//
653+
proxySocket.on('end', listeners.onIncomingClose = function() {
627654
reverseProxy.incoming.socket.end();
628655
detach();
656+
657+
// Emit the `end` event now that we have completed proxying
658+
self.emit('websocket:end', req, socket, head);
629659
});
630660

631-
reverseProxy.incoming.socket.on('end', listeners._close = function() {
661+
//
662+
// If the `reverseProxy` socket closes, then detach all
663+
// event listeners.
664+
//
665+
reverseProxy.incoming.socket.on('end', listeners.onOutgoingClose = function() {
632666
proxySocket.end();
633667
detach();
634668
});
635669
};
636670

637-
// Client socket
671+
// Setup the incoming client socket.
638672
_socket(socket);
639673

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

645-
// Change headers
682+
// Change headers (if requested).
646683
if (this.changeOrigin) {
647684
req.headers.host = remoteHost;
648685
req.headers.origin = protocolName + '://' + remoteHost;
649686
}
650687

688+
//
689+
// Make the outgoing WebSocket request
690+
//
651691
outgoing = {
652692
host: options.host,
653693
port: options.port,
654694
method: 'GET',
655695
path: req.url,
656696
headers: req.headers,
657697
};
658-
659-
// Make the outgoing WebSocket request
660698
var reverseProxy = agent.appendMessage(outgoing);
661699

700+
//
701+
// On any errors from the `reverseProxy` emit the
702+
// `webSocketProxyError` and close the appropriate
703+
// connections.
704+
//
662705
function proxyError (err) {
663706
reverseProxy.end();
664707
if (self.emit('webSocketProxyError', req, socket, head)) {
@@ -703,7 +746,7 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options
703746

704747
//
705748
// If the reverseProxy connection has an underlying socket,
706-
// then behing the handshake.
749+
// then execute the WebSocket handshake.
707750
//
708751
if (typeof reverseProxy.socket !== 'undefined') {
709752
reverseProxy.socket.on('data', function handshake (data) {
@@ -741,6 +784,7 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options
741784
// Write the printable and non-printable data to the socket
742785
// from the original incoming request.
743786
//
787+
self.emit('websocket:handshake', req, socket, head, sdata, data);
744788
socket.write(sdata);
745789
socket.write(data);
746790
}

test/helpers.js

+34
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ var fs = require('fs'),
1212
vows = require('vows'),
1313
assert = require('assert'),
1414
request = require('request'),
15+
websocket = require('./../vendor/websocket'),
1516
httpProxy = require('./../lib/node-http-proxy');
1617

1718
function merge (target) {
@@ -120,6 +121,39 @@ TestRunner.prototype.assertResponseCode = function (proxyPort, statusCode, creat
120121
return test;
121122
};
122123

124+
TestRunner.prototype.webSocketTest = function (options) {
125+
var self = this;
126+
127+
this.startTargetServer(options.ports.target, 'hello websocket', function (err, target) {
128+
var socket = options.io.listen(target);
129+
130+
if (options.onListen) {
131+
options.onListen(socket);
132+
}
133+
134+
self.startProxyServer(
135+
options.ports.proxy,
136+
options.ports.target,
137+
options.host,
138+
function (err, proxy) {
139+
if (options.onServer) { options.onServer(proxy) }
140+
141+
//
142+
// Setup the web socket against our proxy
143+
//
144+
var uri = options.wsprotocol + '://' + options.host + ':' + options.ports.proxy;
145+
var ws = new websocket.WebSocket(uri + '/socket.io/websocket/', 'borf', {
146+
origin: options.protocol + '://' + options.host
147+
});
148+
149+
if (options.onWsupgrade) { ws.on('wsupgrade', options.onWsupgrade) }
150+
if (options.onMessage) { ws.on('message', options.onMessage) }
151+
if (options.onOpen) { ws.on('open', function () { options.onOpen(ws) }) }
152+
}
153+
);
154+
});
155+
}
156+
123157
//
124158
// Creates the reverse proxy server
125159
//

test/node-http-proxy-test.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@
2424
2525
*/
2626

27-
var vows = require('vows'),
27+
var assert = require('assert'),
2828
util = require('util'),
29-
request = require('request'),
30-
assert = require('assert'),
3129
argv = require('optimist').argv,
30+
request = require('request'),
31+
vows = require('vows'),
3232
helpers = require('./helpers');
3333

3434
var forwardOptions = {

test/proxy-table-test.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
*
66
*/
77

8-
var fs = require('fs'),
9-
vows = require('vows'),
10-
util = require('util'),
8+
var assert = require('assert'),
9+
fs = require('fs'),
1110
path = require('path'),
11+
util = require('util'),
12+
argv = require('optimist').argv,
1213
request = require('request'),
13-
assert = require('assert'),
14+
vows = require('vows'),
1415
helpers = require('./helpers'),
15-
argv = require('optimist').argv,
1616
TestRunner = helpers.TestRunner;
1717

1818
var protocol = argv.https ? 'https' : 'http',

0 commit comments

Comments
 (0)