Compare commits

...

35 Commits

Author SHA1 Message Date
James Coglan 4f1eced5e0 Bump version to 0.9.0 and document how to add extensions. 2014-12-13 14:03:37 +00:00
James Coglan ca0544fa6a Revert 29d07c5; see https://github.com/faye/faye-websocket-node/pull/34 2014-12-13 01:25:33 +00:00
James Coglan 091d98ef0c Merge pull request #34 from lpinca/fix/close-on-handshake
Handle cases where the socket is closed before being passed to the server
2014-12-13 00:15:22 +00:00
James Coglan 8d1f652056 Stylistic tweaks to the Autobahn client. 2014-12-06 10:51:18 +00:00
Luigi Pinca b779876f07 Handle cases where the socket is closed before being passed to the server
See #32
2014-11-26 16:08:05 +01:00
James Coglan 243e39b7b7 Refactor the Autobahn client script. 2014-11-24 22:05:56 +00:00
James Coglan 04eda7bc4f Allow the user to pass extensions through to websocket-driver via the WebSocket constructor. 2014-11-24 00:37:12 +00:00
James Coglan 43ac090594 Add vanilla request support to the proxy server. 2014-11-23 21:56:51 +00:00
James Coglan 1120828003 Bump version to 0.8.1. 2014-11-12 19:35:37 +00:00
James Coglan 59911312ac Merge pull request #33 from meteor/specify-servername
Fix proxy connections to non-localhost SSL origins
2014-11-12 19:27:41 +00:00
David Glasser 98298acb36 Fix proxy connections to non-localhost SSL origins
The socket form of tls.connect needs to be told the server name it is
connecting to in order to validate the server certificate (and to
perform SNI). Without this change, just about any attempt to proxy to an
SSL origin will fail.

This name defaults to 'localhost':
  https://github.com/joyent/node/blob/v0.10/lib/tls.js#L1349
so an attempt to proxy to an SSL origin named 'localhost' would have
succeeded, which is why tests passed.
2014-11-10 16:47:56 -08:00
James Coglan 89a9b0d9b8 Update the release date for 0.8.0. 2014-11-08 19:47:25 +00:00
James Coglan 0e00ea5107 Fix the off-by-one error in the Autobahn client's progress bar. 2014-11-08 19:45:47 +00:00
James Coglan d471a6a707 Don't test on v0.6, its tls.connect({socket}) doesn't seem to work. 2014-11-08 15:40:12 +00:00
James Coglan 5dc1d7f682 Migrate back to the driver API from https://github.com/faye/websocket-driver-node/commit/0bb47c17b467980293a9f010de485722c4298b43, and perform all necessary TLS in this module. 2014-11-08 15:34:02 +00:00
James Coglan dfe361a18b Adjust formatting of example log messages. 2014-11-08 12:47:12 +00:00
James Coglan 140171307e Make ProxyServer work on Node 0.6. 2014-11-07 19:25:06 +00:00
James Coglan 6c5ef1c884 Load websocket-driver from git. 2014-11-07 08:57:51 +00:00
James Coglan 7ffe738208 Use an http: endpoint for the proxy example. 2014-11-06 23:35:45 +00:00
James Coglan 2db4ef20ff Pass in the client certificate for navigating https: proxies in the client example, and log the handshake headers. 2014-11-06 23:05:30 +00:00
James Coglan 54cd6f5831 Accientally left part of the proxy tests commented out. 2014-11-06 23:04:56 +00:00
James Coglan 35d9e6998e Log the request headers in the example servers, and log all I/O through the proxy server. 2014-11-06 09:01:39 +00:00
James Coglan e649b62396 Replace the old client example with echo_client.js. 2014-11-06 08:46:44 +00:00
James Coglan d0ec17d6a3 Migrate to the new simplified websocket-driver proxy API, and add integration tests for ws: and wss: sockets via an http: proxy. 2014-11-05 23:36:00 +00:00
James Coglan 1d3878e3ed Remove the stream.resume() call from Client, not needed now that Proxy emits 'close'. 2014-10-30 00:36:45 +00:00
James Coglan 97dfaf71cf Replace an empty on('data') hook with resume(). 2014-10-29 23:09:20 +00:00
James Coglan 723f4ce6a7 Migrate proxy support onto the API introduced in https://github.com/faye/websocket-driver-node/commit/76f41286342e517e7ff562495b249a7fa103bbe9. 2014-10-29 22:26:22 +00:00
James Coglan 97b7342538 [wip] Use the proxy-connect event from the driver to perform a TLS handshake when using wss: via a proxy. 2014-10-29 19:28:55 +00:00
James Coglan 9d8e5384b7 Use 'tls' not 'ssl' to indicate a secure server in the examples. 2014-10-29 19:28:18 +00:00
James Coglan 42ea7fb0a5 Adjust the UA string used for the Autobahn client. 2014-10-29 08:52:10 +00:00
James Coglan 03acb154e2 Set readyState to CLOSED as soon as finalize() is called to prevent reentrant calls from events this method emits. 2014-10-28 09:51:31 +00:00
James Coglan b40fc25444 Fix secure connection via proxies; the client was hard-coded to only treat wss:, not https:, as a secure protocol. 2014-10-27 22:43:38 +00:00
James Coglan 05cc735582 Add support for connecting via an HTTP proxy and bump version to 0.8.0. 2014-10-27 09:39:40 +00:00
James Coglan 29d07c5002 Close the socket if close() is called when we're still waiting for a handshake. 2014-10-26 13:04:06 +00:00
James Coglan 2118283941 Bump the copyright year. 2014-10-04 08:51:29 +01:00
15 changed files with 395 additions and 140 deletions
-4
View File
@@ -1,10 +1,6 @@
language: node_js
node_js:
- "0.6"
- "0.8"
- "0.10"
- "0.11"
before_install:
- '[ "${TRAVIS_NODE_VERSION}" = "0.6" ] && npm conf set strict-ssl false || true'
+16
View File
@@ -1,3 +1,19 @@
### 0.9.0 / 2014-12-13
* Allow protocol extensions to be passed into websocket-extensions
### 0.8.1 / 2014-11-12
* Send the correct hostname when upgrading a connection to TLS
### 0.8.0 / 2014-11-08
* Support connections via HTTP proxies
* Close the connection cleanly if we're still waiting for a handshake response
### 0.7.3 / 2014-10-04
* Allow sockets to be closed when they are in any state other than `CLOSED`
+45 -1
View File
@@ -117,6 +117,23 @@ ws.on('close', function(event) {
The WebSocket client also lets you inspect the status and headers of the
handshake response via its `statusCode` and `headers` properties.
To connect via a proxy, set the `proxy` option to the HTTP origin of the proxy,
including any authorization information, custom headers and TLS config you
require. Only the `origin` setting is required.
```js
var ws = new WebSocket.Client('ws://www.example.com/', null, {
proxy: {
origin: 'http://username:password@proxy.example.com',
headers: {'User-Agent': 'node'},
tls: {cert: fs.readFileSync('client.crt')}
}
});
```
The `tls` value is a Node 'TLS options' object that will be passed to
[`tls.connect()`](http://nodejs.org/api/tls.html#tls_tls_connect_options_callback).
## Subprotocol negotiation
@@ -140,6 +157,22 @@ If the client and server agree on a protocol, both the client- and server-side
socket objects expose the selected protocol through the `ws.protocol` property.
## Protocol extensions
faye-websocket is based on the
[websocket-extensions](https://github.com/faye/websocket-extensions-node)
framework that allows extensions to be negotiated via the
`Sec-WebSocket-Extensions` header. To add extensions to a connection, pass an
array of extensions to the `:extensions` option. For example, to add
[permessage-deflate](https://github.com/faye/permessage-deflate-node):
```js
var deflate = require('permessage-deflate');
var ws = new WebSocket(request, null, {extensions: [deflate]});
```
## Initialization options
Both the server- and client-side classes allow an options object to be passed
@@ -153,6 +186,9 @@ var ws = new WebSocket.Client(url, protocols, options);
`protocols` is an array of subprotocols as described above, or `null`.
`options` is an optional object containing any of these fields:
* `extensions` - an array of
[websocket-extensions](https://github.com/faye/websocket-extensions-node)
compatible extensions, as described above
* `headers` - an object containing key-value pairs representing HTTP headers to
be sent during the handshake process
* `maxLength` - the maximum allowed size of incoming message frames, in bytes.
@@ -160,6 +196,14 @@ var ws = new WebSocket.Client(url, protocols, options);
* `ping` - an integer that sets how often the WebSocket should send ping
frames, measured in seconds
The client accepts some additional options:
* `proxy` - settings for a proxy as described above
* `tls` - a Node 'TLS options' object containing TLS settings for the origin
server, this will be passed to
[`tls.connect()`](http://nodejs.org/api/tls.html#tls_tls_connect_options_callback)
* `ca` - (legacy) a shorthand for passing `{tls: {ca: value}}`
## WebSocket API
@@ -273,7 +317,7 @@ some data over the wire to keep the connection alive.
(The MIT License)
Copyright (c) 2010-2013 James Coglan
Copyright (c) 2010-2014 James Coglan
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the 'Software'), to deal in
+23 -21
View File
@@ -1,13 +1,14 @@
var WebSocket = require('../lib/faye/websocket'),
deflate = require('permessage-deflate'),
pace = require('pace');
var host = 'ws://localhost:9001',
agent = 'Node ' + process.version,
cases = 0,
skip = [];
var host = 'ws://localhost:9001',
agent = encodeURIComponent('node-' + process.version),
cases = 0,
options = {extensions: [deflate]};
var socket = new WebSocket.Client(host + '/getCaseCount'),
progress;
url, progress;
socket.onmessage = function(event) {
console.log('Total cases to run: ' + event.data);
@@ -15,23 +16,24 @@ socket.onmessage = function(event) {
progress = pace(cases);
};
socket.onclose = function() {
var runCase = function(n) {
var runCase = function(n) {
if (n > cases) {
url = host + '/updateReports?agent=' + agent;
socket = new WebSocket.Client(url);
socket.onclose = process.exit;
return;
}
url = host + '/runCase?case=' + n + '&agent=' + agent;
socket = new WebSocket.Client(url, null, options);
socket.pipe(socket);
socket.on('close', function() {
progress.op();
runCase(n + 1);
});
};
if (n > cases) {
socket = new WebSocket.Client(host + '/updateReports?agent=' + encodeURIComponent(agent));
socket.onclose = process.exit;
} else if (skip.indexOf(n) >= 0) {
runCase(n + 1);
} else {
socket = new WebSocket.Client(host + '/runCase?case=' + n + '&agent=' + encodeURIComponent(agent));
socket.pipe(socket);
socket.on('close', function() { runCase(n + 1) });
}
};
socket.onclose = function() {
runCase(1);
};
+17 -15
View File
@@ -1,23 +1,25 @@
var WebSocket = require('../lib/faye/websocket'),
port = process.argv[2] || 7000,
secure = process.argv[3] === 'ssl',
scheme = secure ? 'wss' : 'ws',
url = scheme + '://localhost:' + port + '/',
headers = {Origin: 'http://faye.jcoglan.com'},
ws = new WebSocket.Client(url, null, {headers: headers});
fs = require('fs');
console.log('Connecting to ' + ws.url);
var url = process.argv[2],
headers = {Origin: 'http://faye.jcoglan.com'},
ca = fs.readFileSync(__dirname + '/../spec/server.crt'),
proxy = {origin: process.argv[3], headers: {'User-Agent': 'Echo'}, tls: {ca: ca}},
ws = new WebSocket.Client(url, null, {headers: headers, proxy: proxy, tls: {ca: ca}});
ws.onopen = function(event) {
console.log('open');
ws.send('Hello, WebSocket!');
ws.onopen = function() {
console.log('[open]', ws.headers);
ws.send('mic check');
};
ws.onmessage = function(event) {
console.log('message', event.data);
// ws.close(1002, 'Going away');
ws.onclose = function(close) {
console.log('[close]', close.code, close.reason);
};
ws.onclose = function(event) {
console.log('close', event.code, event.reason);
ws.onerror = function(error) {
console.log('[error]', error.message);
};
ws.onmessage = function(message) {
console.log('[message]', message.data);
};
+7
View File
@@ -0,0 +1,7 @@
var ProxyServer = require('../spec/proxy_server');
var port = process.argv[2],
secure = process.argv[3] === 'tls',
proxy = new ProxyServer({debug: true, tls: secure});
proxy.listen(port);
+9 -7
View File
@@ -1,19 +1,21 @@
var WebSocket = require('../lib/faye/websocket'),
deflate = require('permessage-deflate'),
fs = require('fs'),
http = require('http'),
https = require('https');
var port = process.argv[2] || 7000,
secure = process.argv[3] === 'ssl';
var port = process.argv[2] || 7000,
secure = process.argv[3] === 'tls',
options = {extensions: [deflate], ping: 5};
var upgradeHandler = function(request, socket, head) {
var ws = new WebSocket(request, socket, head, ['irc', 'xmpp'], {ping: 5});
console.log('open', ws.url, ws.version, ws.protocol);
var ws = new WebSocket(request, socket, head, ['irc', 'xmpp'], options);
console.log('[open]', ws.url, ws.version, ws.protocol, request.headers);
ws.pipe(ws);
ws.onclose = function(event) {
console.log('close', event.code, event.reason);
console.log('[close]', event.code, event.reason);
ws = null;
};
};
@@ -25,7 +27,7 @@ var requestHandler = function(request, response) {
var es = new WebSocket.EventSource(request, response),
time = parseInt(es.lastEventId, 10) || 0;
console.log('open', es.url, es.lastEventId);
console.log('[open]', es.url, es.lastEventId);
var loop = setInterval(function() {
time += 1;
@@ -39,7 +41,7 @@ var requestHandler = function(request, response) {
es.onclose = function() {
clearInterval(loop);
console.log('close', es.url);
console.log('[close]', es.url);
es = null;
};
};
+2 -1
View File
@@ -1,5 +1,5 @@
// API references:
//
//
// * http://dev.w3.org/html5/websockets/
// * http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#interface-eventtarget
// * http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#interface-event
@@ -16,6 +16,7 @@ var WebSocket = function(request, socket, body, protocols, options) {
var self = this;
if (!this._stream || !this._stream.writable) return;
if (!this._stream.readable) return this._stream.end();
var catchup = function() { self._stream.removeListener('data', catchup) };
this._stream.on('data', catchup);
+28 -17
View File
@@ -13,6 +13,11 @@ var API = function(options) {
for (var name in headers) this._driver.setHeader(name, headers[name]);
}
var extensions = options.extensions;
if (extensions) {
[].concat(extensions).forEach(this._driver.addExtension, this._driver);
}
this._ping = options.ping;
this._pingId = 0;
this.readyState = API.CONNECTING;
@@ -23,20 +28,6 @@ var API = function(options) {
var self = this;
this._stream.setTimeout(0);
this._stream.setNoDelay(true);
['close', 'end'].forEach(function(event) {
this._stream.on(event, function() { self._finalize('', 1006) });
}, this);
this._stream.on('error', function(error) {
var event = new Event('error', {message: 'Network error: ' + self.url + ': ' + error.message});
event.initEvent('error', false, false);
self.dispatchEvent(event);
self._finalize('', 1006);
});
this._driver.on('open', function(e) { self._open() });
this._driver.on('message', function(e) { self._receiveMessage(e.data) });
this._driver.on('close', function(e) { self._finalize(e.reason, e.code) });
@@ -45,6 +36,7 @@ var API = function(options) {
var event = new Event('error', {message: error.message});
event.initEvent('error', false, false);
self.dispatchEvent(event);
self._finalize('', 1006);
});
this.on('error', function() {});
@@ -58,8 +50,12 @@ var API = function(options) {
self.ping(self._pingId.toString());
}, this._ping * 1000);
this._stream.pipe(this._driver.io);
this._driver.io.pipe(this._stream);
this._configureStream();
if (!this._proxy) {
this._stream.pipe(this._driver.io);
this._driver.io.pipe(this._stream);
}
};
util.inherits(API, Stream);
@@ -102,6 +98,21 @@ var instance = {
this._driver.close();
},
_configureStream: function() {
var self = this;
this._stream.setTimeout(0);
this._stream.setNoDelay(true);
['close', 'end'].forEach(function(event) {
this._stream.on(event, function() { self._finalize('', 1006) });
}, this);
this._stream.on('error', function(error) {
self._driver.emit('error', new Error('Network error: ' + self.url + ': ' + error.message));
});
},
_open: function() {
if (this.readyState !== API.CONNECTING) return;
@@ -125,6 +136,7 @@ var instance = {
_finalize: function(reason, code) {
if (this.readyState === API.CLOSED) return;
this.readyState = API.CLOSED;
if (this._pingTimer) clearInterval(this._pingTimer);
if (this._stream) this._stream.end();
@@ -132,7 +144,6 @@ var instance = {
if (this.readable) this.emit('end');
this.readable = this.writable = false;
this.readyState = API.CLOSED;
var event = new Event('close', {code: code || 1000, reason: reason || ''});
event.initEvent('close', false, false);
this.dispatchEvent(event);
+58 -14
View File
@@ -1,16 +1,20 @@
var util = require('util'),
net = require('net'),
tls = require('tls'),
crypto = require('crypto'),
url = require('url'),
driver = require('websocket-driver'),
API = require('./api'),
Event = require('./api/event');
var Client = function(url, protocols, options) {
var DEFAULT_PORTS = {'http:': 80, 'https:': 443, 'ws:':80, 'wss:': 443},
SECURE_PROTOCOLS = ['https:', 'wss:'];
var Client = function(_url, protocols, options) {
options = options || {};
this.url = url;
this._uri = require('url').parse(url);
this._driver = driver.client(url, {maxLength: options.maxLength, protocols: protocols});
this.url = _url;
this._driver = driver.client(this.url, {maxLength: options.maxLength, protocols: protocols});
['open', 'error'].forEach(function(event) {
this._driver.on(event, function() {
@@ -19,22 +23,62 @@ var Client = function(url, protocols, options) {
});
}, this);
var secure = (this._uri.protocol === 'wss:'),
onConnect = function() { self._driver.start() },
tlsOptions = {},
self = this;
var proxy = options.proxy || {},
endpoint = url.parse(proxy.origin || this.url),
port = endpoint.port || DEFAULT_PORTS[endpoint.protocol],
secure = SECURE_PROTOCOLS.indexOf(endpoint.protocol) >= 0,
onConnect = function() { self._onConnect() },
originTLS = options.tls || {},
socketTLS = proxy.origin ? (proxy.tls || {}) : originTLS,
self = this;
if (options.ca) tlsOptions.ca = options.ca;
originTLS.ca = originTLS.ca || options.ca;
var connection = secure
? tls.connect(this._uri.port || 443, this._uri.hostname, tlsOptions, onConnect)
: net.createConnection(this._uri.port || 80, this._uri.hostname);
this._stream = secure
? tls.connect(port, endpoint.hostname, socketTLS, onConnect)
: net.connect(port, endpoint.hostname, onConnect);
this._stream = connection;
if (!secure) this._stream.on('connect', onConnect);
if (proxy.origin) this._configureProxy(proxy, originTLS);
API.call(this, options);
};
util.inherits(Client, API);
Client.prototype._onConnect = function() {
var worker = this._proxy || this._driver;
worker.start();
};
Client.prototype._configureProxy = function(proxy, originTLS) {
var uri = url.parse(this.url),
secure = SECURE_PROTOCOLS.indexOf(uri.protocol) >= 0,
self = this,
name;
this._proxy = this._driver.proxy(proxy.origin);
if (proxy.headers) {
for (name in proxy.headers) this._proxy.setHeader(name, proxy.headers[name]);
}
this._proxy.pipe(this._stream, {end: false});
this._stream.pipe(this._proxy);
this._proxy.on('connect', function() {
if (secure) {
var options = {socket: self._stream, servername: uri.hostname};
for (name in originTLS) options[name] = originTLS[name];
self._stream = tls.connect(options);
self._configureStream();
}
self._driver.io.pipe(self._stream);
self._stream.pipe(self._driver.io);
self._driver.start();
});
this._proxy.on('error', function(error) {
self._driver.emit('error', error);
});
};
module.exports = Client;
+3 -3
View File
@@ -5,11 +5,11 @@
, "keywords" : ["websocket", "eventsource"]
, "license" : "MIT"
, "version" : "0.7.3"
, "version" : "0.9.0"
, "engines" : {"node": ">=0.4.0"}
, "main" : "./lib/faye/websocket"
, "dependencies" : {"websocket-driver": ">=0.3.6"}
, "devDependencies" : {"jstest": "", "pace": ""}
, "dependencies" : {"websocket-driver": ">=0.5.0"}
, "devDependencies" : {"jstest": "", "pace": "", "permessage-deflate": ""}
, "scripts" : {"test": "jstest spec/runner.js"}
+31
View File
@@ -0,0 +1,31 @@
var fs = require('fs'),
http = require('http'),
https = require('https');
var WebSocket = require('../lib/faye/websocket');
var EchoServer = function(secure) {
var server = secure
? https.createServer({
key: fs.readFileSync(__dirname + '/server.key'),
cert: fs.readFileSync(__dirname + '/server.crt')
})
: http.createServer();
server.on('upgrade', function(request, socket, head) {
var ws = new WebSocket(request, socket, head, ["echo"]);
ws.pipe(ws);
});
this._httpServer = server;
};
EchoServer.prototype.listen = function(port) {
this._httpServer.listen(port);
};
EchoServer.prototype.stop = function() {
this._httpServer.close();
};
module.exports = EchoServer;
+74 -26
View File
@@ -1,17 +1,29 @@
var Client = require('../../../lib/faye/websocket/client'),
test = require('jstest').Test,
fs = require('fs')
var Client = require('../../../lib/faye/websocket/client'),
EchoServer = require('../../echo_server'),
ProxyServer = require('../../proxy_server'),
test = require('jstest').Test,
fs = require('fs')
var WebSocketSteps = test.asyncSteps({
server: function(port, secure, callback) {
this._adapter = new EchoServer()
this._adapter.listen(port, secure)
this._port = port
this._echoServer = new EchoServer(secure)
this._echoServer.listen(port)
process.nextTick(callback)
},
stop: function(callback) {
this._adapter.stop()
this._echoServer.stop()
process.nextTick(callback)
},
proxy: function(port, secure, callback) {
this._proxyServer = new ProxyServer({tls: secure})
this._proxyServer.listen(port)
process.nextTick(callback)
},
stop_proxy: function(callback) {
this._proxyServer.stop()
process.nextTick(callback)
},
@@ -19,6 +31,8 @@ var WebSocketSteps = test.asyncSteps({
var done = false,
self = this,
tlsOptions = { ca: fs.readFileSync(__dirname + '/../../server.crt') },
resume = function(open) {
if (done) return
done = true
@@ -27,7 +41,8 @@ var WebSocketSteps = test.asyncSteps({
}
this._ws = new Client(url, protocols, {
ca: fs.readFileSync(__dirname + '/../../server.crt')
proxy: { origin: this.proxy_url, tls: tlsOptions },
tls: tlsOptions
})
this._ws.onopen = function() { resume(true) }
@@ -125,10 +140,15 @@ test.describe("Client", function() { with(this) {
include(WebSocketSteps)
before(function() {
this.protocols = ["foo", "echo"]
this.plain_text_url = "ws://localhost:4180/bayeux"
this.secure_url = "wss://localhost:4180/bayeux"
this.port = 4180
this.protocols = ["foo", "echo"]
this.plain_text_url = "ws://localhost:4180/bayeux"
this.secure_url = "wss://localhost:4180/bayeux"
this.port = 4180
this.plain_text_proxy_url = "http://localhost:4181"
this.secure_text_proxy_url = "https://localhost:4181"
this.proxy_port = 4181
})
sharedBehavior("socket client", function() { with(this) {
@@ -198,27 +218,55 @@ test.describe("Client", function() { with(this) {
}})
}})
describe("with a plain-text server", function() { with(this) {
before(function() {
this.socket_url = this.plain_text_url
this.blocked_url = this.secure_url
})
sharedBehavior("socket server", function() { with(this) {
describe("with a plain-text server", function() { with(this) {
before(function() {
this.socket_url = this.plain_text_url
this.blocked_url = this.secure_url
})
before(function() { this.server(4180, false) })
after (function() { this.stop() })
before(function() { this.server(this.port, false) })
after (function() { this.stop() })
behavesLike("socket client")
behavesLike("socket client")
}})
describe("with a secure server", function() { with(this) {
before(function() {
this.socket_url = this.secure_url
this.blocked_url = this.plain_text_url
})
before(function() { this.server(this.port, true) })
after (function() { this.stop() })
behavesLike("socket client")
}})
}})
describe("with a secure server", function() { with(this) {
describe("with no proxy", function() { with(this) {
behavesLike("socket server")
}})
describe("with a proxy", function() { with(this) {
before(function() {
this.socket_url = this.secure_url
this.blocked_url = this.plain_text_url
this.proxy_url = this.plain_text_proxy_url
})
before(function() { this.server(4180, true) })
after (function() { this.stop() })
before(function() { this.proxy(this.proxy_port, false) })
after (function() { this.stop_proxy() })
behavesLike("socket client")
behavesLike("socket server")
}})
describe("with a secure proxy", function() { with(this) {
before(function() {
this.proxy_url = this.secure_text_proxy_url
})
before(function() { this.proxy(this.proxy_port, true) })
after (function() { this.stop_proxy() })
behavesLike("socket server")
}})
}})
+82
View File
@@ -0,0 +1,82 @@
var fs = require('fs'),
http = require('http'),
https = require('https'),
net = require('net'),
url = require('url');
var AGENTS = {'http:': http, 'https:': https},
PORTS = {'http:': 80, 'https:': 443};
var ProxyServer = function(options) {
var proxy = options.tls
? https.createServer({
key: fs.readFileSync(__dirname + '/server.key'),
cert: fs.readFileSync(__dirname + '/server.crt')
})
: http.createServer();
options = options || {};
var onRequest = function(request, response) {
if (options.debug) console.log(request.method, request.url, request.headers);
var uri = url.parse(request.url),
agent = AGENTS[uri.protocol],
headers = {};
for (var key in request.headers) {
if (key.split('-')[0] !== 'proxy') headers[key] = request.headers[key];
}
var backend = agent.request({
method: request.method,
host: uri.hostname,
port: uri.port || PORTS[uri.protocol],
path: uri.path,
headers: headers,
rejectUnauthorized: false
});
request.pipe(backend);
backend.on('response', function(resp) {
if (options.debug) console.log(resp.statusCode, resp.headers);
response.writeHead(resp.statusCode, resp.headers);
resp.pipe(response);
});
};
var onConnect = function(request, frontend, body) {
var parts = request.url.split(':'),
backend = net.connect(parts[1], parts[0]);
frontend.pipe(backend);
backend.pipe(frontend);
backend.on('connect', function() {
frontend.write('HTTP/1.1 200 OK\r\n\r\n');
});
if (!options.debug) return;
console.log(request.method, request.url, request.headers);
frontend.on('data', function(data) { console.log('I', data) });
backend.on( 'data', function(data) { console.log('O', data) });
};
proxy.on('request', onRequest);
proxy.on('connect', onConnect);
proxy.on('upgrade', onConnect);
this._proxy = proxy;
};
ProxyServer.prototype.listen = function(port) {
this._proxy.listen(port);
};
ProxyServer.prototype.stop = function() {
this._proxy.close();
};
module.exports = ProxyServer;
-31
View File
@@ -1,32 +1 @@
var WebSocket = require('../lib/faye/websocket'),
fs = require('fs'),
http = require('http'),
https = require('https'),
test = require('jstest').Test
EchoServer = function() {}
EchoServer.prototype.listen = function(port, ssl) {
var server = ssl
? https.createServer({
key: fs.readFileSync(__dirname + '/server.key'),
cert: fs.readFileSync(__dirname + '/server.crt')
})
: http.createServer()
server.on('upgrade', function(request, socket, head) {
var ws = new WebSocket(request, socket, head, ["echo"])
ws.pipe(ws)
})
this._httpServer = server
server.listen(port)
}
EchoServer.prototype.stop = function(callback, scope) {
this._httpServer.on('close', function() {
if (callback) callback.call(scope);
});
this._httpServer.close();
}
require('./faye/websocket/client_spec')