Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4f1eced5e0 | |||
| ca0544fa6a | |||
| 091d98ef0c | |||
| 8d1f652056 | |||
| b779876f07 | |||
| 243e39b7b7 | |||
| 04eda7bc4f | |||
| 43ac090594 | |||
| 1120828003 | |||
| 59911312ac | |||
| 98298acb36 | |||
| 89a9b0d9b8 | |||
| 0e00ea5107 | |||
| d471a6a707 | |||
| 5dc1d7f682 | |||
| dfe361a18b | |||
| 140171307e | |||
| 6c5ef1c884 | |||
| 7ffe738208 | |||
| 2db4ef20ff | |||
| 54cd6f5831 | |||
| 35d9e6998e | |||
| e649b62396 | |||
| d0ec17d6a3 | |||
| 1d3878e3ed | |||
| 97dfaf71cf | |||
| 723f4ce6a7 | |||
| 97b7342538 | |||
| 9d8e5384b7 | |||
| 42ea7fb0a5 | |||
| 03acb154e2 | |||
| b40fc25444 | |||
| 05cc735582 | |||
| 29d07c5002 | |||
| 2118283941 |
@@ -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'
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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
@@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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"}
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -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")
|
||||
}})
|
||||
}})
|
||||
|
||||
@@ -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;
|
||||
@@ -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')
|
||||
|
||||
Reference in New Issue
Block a user