mirror of
https://github.com/socketio/socket.io.git
synced 2026-01-11 16:08:24 -05:00
Compare commits
93 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dfebed38ab | ||
|
|
51782fc5d7 | ||
|
|
11f1a7c491 | ||
|
|
1d743cfc84 | ||
|
|
21c01558fd | ||
|
|
6d57445167 | ||
|
|
52f6a5b124 | ||
|
|
2c3c73f045 | ||
|
|
54fc513fc9 | ||
|
|
0b1e43cb87 | ||
|
|
c8dabb225c | ||
|
|
245dc12ade | ||
|
|
7a405232a5 | ||
|
|
00f7ca1d02 | ||
|
|
f1cea7e788 | ||
|
|
050fcf7a83 | ||
|
|
dfa350bea7 | ||
|
|
e5d5b99f0e | ||
|
|
ed7cedd78f | ||
|
|
a22eb70cfb | ||
|
|
2a81b25a5b | ||
|
|
7db146df47 | ||
|
|
6182dfff39 | ||
|
|
1ccd8cea6b | ||
|
|
f9ea04eb6b | ||
|
|
ab9a5a1578 | ||
|
|
3364a73a97 | ||
|
|
d02a7f415b | ||
|
|
1874fd7c30 | ||
|
|
3c4a04ea02 | ||
|
|
1468917743 | ||
|
|
6df152cc5d | ||
|
|
c6b3549b61 | ||
|
|
f80ab2aae8 | ||
|
|
b2f9f19d99 | ||
|
|
cb7304837c | ||
|
|
9e6f58fe27 | ||
|
|
e3fb39da3d | ||
|
|
cc0a96a8d9 | ||
|
|
7b2b302022 | ||
|
|
d88575dadf | ||
|
|
93c963e30f | ||
|
|
72a79e5cec | ||
|
|
140ed41907 | ||
|
|
12fc168516 | ||
|
|
2c3dc42ae8 | ||
|
|
48dadd8e10 | ||
|
|
ce4c46b37d | ||
|
|
c8938a99b2 | ||
|
|
b7998e815a | ||
|
|
444229a9dc | ||
|
|
b69dac6f4d | ||
|
|
94fdbadaec | ||
|
|
c4b23246b4 | ||
|
|
5186788969 | ||
|
|
169ad8245f | ||
|
|
71e013a197 | ||
|
|
7e60d37171 | ||
|
|
a9e9e64eab | ||
|
|
39ae8d4629 | ||
|
|
a075870308 | ||
|
|
186649102d | ||
|
|
bb2e100e7f | ||
|
|
dd30de3c5a | ||
|
|
e269fcaf0d | ||
|
|
a8c61b0001 | ||
|
|
4708480e7d | ||
|
|
30284944b1 | ||
|
|
559d36601d | ||
|
|
1fa158c663 | ||
|
|
a631f86b3b | ||
|
|
831f1baa4a | ||
|
|
0d3441d8b3 | ||
|
|
23e14223bd | ||
|
|
4b94f2b8bf | ||
|
|
f88eedc3c8 | ||
|
|
5c24bf8c1d | ||
|
|
9cd51b1f6b | ||
|
|
2f8eb63557 | ||
|
|
4bdc30734c | ||
|
|
4743744efc | ||
|
|
c2d98bde72 | ||
|
|
9e97e6c691 | ||
|
|
afdfdb815e | ||
|
|
d043c33351 | ||
|
|
168b207c6d | ||
|
|
f6ebb7b8d6 | ||
|
|
dfebed6c2f | ||
|
|
3a2545b497 | ||
|
|
ffa17e1205 | ||
|
|
59e250b186 | ||
|
|
9bf10ed051 | ||
|
|
9cfdb8ed97 |
21
History.md
21
History.md
@@ -1,4 +1,25 @@
|
||||
|
||||
0.8.5 / 2011-10-07
|
||||
==================
|
||||
|
||||
* Added websocket draft HyBi-16 support. [einaros]
|
||||
* Fixed websocket continuation bugs. [einaros]
|
||||
* Fixed flashsocket transport name.
|
||||
* Fixed websocket tests.
|
||||
* Ensured `parser#decodePayload` doesn't choke.
|
||||
* Added http referrer verification to manager verifyOrigin.
|
||||
* Added access control for cross domain xhr handshakes [3rd-Eden]
|
||||
* Added support for automatic generation of socket.io files [3rd-Eden]
|
||||
* Added websocket binary support [einaros]
|
||||
* Added gzip support for socket.io.js [3rd-Eden]
|
||||
* Expose socket.transport [3rd-Eden]
|
||||
* Updated client.
|
||||
|
||||
0.8.4 / 2011-09-06
|
||||
==================
|
||||
|
||||
* Client build
|
||||
|
||||
0.8.3 / 2011-09-03
|
||||
==================
|
||||
|
||||
|
||||
@@ -222,7 +222,7 @@ io.sockets.on('connection', function (socket) {
|
||||
```html
|
||||
<script>
|
||||
var socket = io.connect(); // TIP: .connect with no args does auto-discovery
|
||||
socket.on('connection', function () {
|
||||
socket.on('connect', function () { // TIP: you can avoid listening on `connect` and listen on events directly too!
|
||||
socket.emit('ferret', 'tobi', function (data) {
|
||||
console.log(data); // data will be 'woot'
|
||||
});
|
||||
|
||||
143
lib/manager.js
143
lib/manager.js
@@ -18,6 +18,7 @@ var fs = require('fs')
|
||||
, Socket = require('./socket')
|
||||
, MemoryStore = require('./stores/memory')
|
||||
, SocketNamespace = require('./namespace')
|
||||
, Static = require('./static')
|
||||
, EventEmitter = process.EventEmitter;
|
||||
|
||||
/**
|
||||
@@ -61,6 +62,7 @@ function Manager (server, options) {
|
||||
, log: true
|
||||
, store: new MemoryStore
|
||||
, logger: new Logger
|
||||
, static: new Static(this)
|
||||
, heartbeats: true
|
||||
, resource: '/socket.io'
|
||||
, transports: defaultTransports
|
||||
@@ -74,8 +76,10 @@ function Manager (server, options) {
|
||||
, 'flash policy port': 10843
|
||||
, 'destroy upgrade': true
|
||||
, 'browser client': true
|
||||
, 'browser client cache': true
|
||||
, 'browser client minification': false
|
||||
, 'browser client etag': false
|
||||
, 'browser client gzip': false
|
||||
, 'browser client handler': false
|
||||
, 'client store expiration': 15
|
||||
};
|
||||
@@ -84,14 +88,18 @@ function Manager (server, options) {
|
||||
this.settings[i] = options[i];
|
||||
}
|
||||
|
||||
var self = this;
|
||||
|
||||
this.initStore();
|
||||
|
||||
this.on('set:store', function() {
|
||||
self.initStore();
|
||||
});
|
||||
|
||||
// reset listeners
|
||||
this.oldListeners = server.listeners('request');
|
||||
server.removeAllListeners('request');
|
||||
|
||||
var self = this;
|
||||
|
||||
server.on('request', function (req, res) {
|
||||
self.handleRequest(req, res);
|
||||
});
|
||||
@@ -146,6 +154,16 @@ Manager.prototype.__defineGetter__('log', function () {
|
||||
return logger;
|
||||
});
|
||||
|
||||
/**
|
||||
* Static accessor.
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Manager.prototype.__defineGetter__('static', function () {
|
||||
return this.get('static');
|
||||
});
|
||||
|
||||
/**
|
||||
* Get settings.
|
||||
*
|
||||
@@ -516,7 +534,7 @@ Manager.prototype.handleRequest = function (req, res) {
|
||||
|
||||
if (data.static || !data.transport && !data.protocol) {
|
||||
if (data.static && this.enabled('browser client')) {
|
||||
this.handleClientRequest(req, res, data);
|
||||
this.static.write(data.path, req, res);
|
||||
} else {
|
||||
res.writeHead(200);
|
||||
res.end('Welcome to socket.io.');
|
||||
@@ -652,104 +670,6 @@ Manager.prototype.handleClient = function (data, req) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Dictionary for static file serving
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Manager.static = {
|
||||
cache: {}
|
||||
, paths: {
|
||||
'/static/flashsocket/WebSocketMain.swf': client.dist + '/WebSocketMain.swf'
|
||||
, '/static/flashsocket/WebSocketMainInsecure.swf':
|
||||
client.dist + '/WebSocketMainInsecure.swf'
|
||||
, '/socket.io.js': client.dist + '/socket.io.js'
|
||||
, '/socket.io.js.min': client.dist + '/socket.io.min.js'
|
||||
}
|
||||
, mime: {
|
||||
'js': {
|
||||
contentType: 'application/javascript'
|
||||
, encoding: 'utf8'
|
||||
}
|
||||
, 'swf': {
|
||||
contentType: 'application/x-shockwave-flash'
|
||||
, encoding: 'binary'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Serves the client.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Manager.prototype.handleClientRequest = function (req, res, data) {
|
||||
var static = Manager.static
|
||||
, extension = data.path.split('.').pop()
|
||||
, file = data.path + (this.enabled('browser client minification')
|
||||
&& extension == 'js' ? '.min' : '')
|
||||
, location = static.paths[file]
|
||||
, cache = static.cache[file];
|
||||
|
||||
var self = this;
|
||||
|
||||
/**
|
||||
* Writes a response, safely
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function write (status, headers, content, encoding) {
|
||||
try {
|
||||
res.writeHead(status, headers || null);
|
||||
res.end(content || '', encoding || null);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
function serve () {
|
||||
if (req.headers['if-none-match'] === cache.Etag) {
|
||||
return write(304);
|
||||
}
|
||||
|
||||
var mime = static.mime[extension]
|
||||
, headers = {
|
||||
'Content-Type': mime.contentType
|
||||
, 'Content-Length': cache.length
|
||||
};
|
||||
|
||||
if (self.enabled('browser client etag') && cache.Etag) {
|
||||
headers.Etag = cache.Etag;
|
||||
}
|
||||
|
||||
write(200, headers, cache.content, mime.encoding);
|
||||
self.log.debug('served static ' + data.path);
|
||||
}
|
||||
|
||||
if (this.get('browser client handler')) {
|
||||
this.get('browser client handler').call(this, req, res);
|
||||
} else if (!cache) {
|
||||
fs.readFile(location, function (err, data) {
|
||||
if (err) {
|
||||
write(500, null, 'Error serving static ' + data.path);
|
||||
self.log.warn('Can\'t cache '+ data.path +', ' + err.message);
|
||||
return;
|
||||
}
|
||||
|
||||
cache = Manager.static.cache[file] = {
|
||||
content: data
|
||||
, length: data.length
|
||||
, Etag: client.version
|
||||
};
|
||||
|
||||
serve();
|
||||
});
|
||||
} else {
|
||||
serve();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates a session id.
|
||||
*
|
||||
@@ -768,14 +688,18 @@ Manager.prototype.generateId = function () {
|
||||
*/
|
||||
|
||||
Manager.prototype.handleHandshake = function (data, req, res) {
|
||||
var self = this;
|
||||
var self = this
|
||||
, origin = req.headers.origin
|
||||
, headers = {
|
||||
'Content-Type': 'text/plain'
|
||||
};
|
||||
|
||||
function writeErr (status, message) {
|
||||
if (data.query.jsonp) {
|
||||
res.writeHead(200, { 'Content-Type': 'application/javascript' });
|
||||
res.end('io.j[' + data.query.jsonp + '](new Error("' + message + '"));');
|
||||
} else {
|
||||
res.writeHead(status, { 'Content-Type': 'text/plain' });
|
||||
res.writeHead(status, headers);
|
||||
res.end(message);
|
||||
}
|
||||
};
|
||||
@@ -792,6 +716,15 @@ Manager.prototype.handleHandshake = function (data, req, res) {
|
||||
|
||||
var handshakeData = this.handshakeData(data);
|
||||
|
||||
if (origin) {
|
||||
// https://developer.mozilla.org/En/HTTP_Access_Control
|
||||
headers['Access-Control-Allow-Origin'] = '*';
|
||||
|
||||
if (req.headers.cookie) {
|
||||
headers['Access-Control-Allow-Credentials'] = 'true';
|
||||
}
|
||||
}
|
||||
|
||||
this.authorize(handshakeData, function (err, authorized, newData) {
|
||||
if (err) return error(err);
|
||||
|
||||
@@ -808,7 +741,7 @@ Manager.prototype.handleHandshake = function (data, req, res) {
|
||||
hs = 'io.j[' + data.query.jsonp + '](' + JSON.stringify(hs) + ');';
|
||||
res.writeHead(200, { 'Content-Type': 'application/javascript' });
|
||||
} else {
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||
res.writeHead(200, headers);
|
||||
}
|
||||
|
||||
res.end(hs);
|
||||
@@ -979,7 +912,7 @@ Manager.prototype.checkRequest = function (req) {
|
||||
data.protocol = Number(pieces[1]);
|
||||
data.transport = pieces[2];
|
||||
data.id = pieces[3];
|
||||
data.static = !!Manager.static.paths[path];
|
||||
data.static = !!this.static.has(path);
|
||||
};
|
||||
|
||||
return data;
|
||||
|
||||
@@ -223,6 +223,10 @@ exports.decodePacket = function (data) {
|
||||
*/
|
||||
|
||||
exports.decodePayload = function (data) {
|
||||
if (undefined == data || null == data) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (data[0] == '\ufffd') {
|
||||
var ret = [];
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ var client = require('socket.io-client');
|
||||
* Version.
|
||||
*/
|
||||
|
||||
exports.version = '0.8.3';
|
||||
exports.version = '0.8.5';
|
||||
|
||||
/**
|
||||
* Supported protocol version.
|
||||
@@ -95,6 +95,14 @@ exports.Transport = require('./transport');
|
||||
|
||||
exports.Socket = require('./socket');
|
||||
|
||||
/**
|
||||
* Static constructor.
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.Static = require('./static');
|
||||
|
||||
/**
|
||||
* Store constructor.
|
||||
*
|
||||
|
||||
@@ -64,6 +64,16 @@ Socket.prototype.__defineGetter__('handshake', function () {
|
||||
return this.manager.handshaken[this.id];
|
||||
});
|
||||
|
||||
/**
|
||||
* Accessor shortcut for the transport type
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Socket.prototype.__defineGetter__('transport', function () {
|
||||
return this.manager.transports[this.id].name;
|
||||
});
|
||||
|
||||
/**
|
||||
* Accessor shortcut for the logger.
|
||||
*
|
||||
@@ -167,7 +177,7 @@ Socket.prototype.join = function (name, fn) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Joins a user to a room.
|
||||
* Un-joins a user from a room.
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
374
lib/static.js
Normal file
374
lib/static.js
Normal file
@@ -0,0 +1,374 @@
|
||||
|
||||
/*!
|
||||
* socket.io-node
|
||||
* Copyright(c) 2011 LearnBoost <dev@learnboost.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var client = require('socket.io-client')
|
||||
, cp = require('child_process')
|
||||
, fs = require('fs')
|
||||
, util = require('./util');
|
||||
|
||||
/**
|
||||
* File type details.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
var mime = {
|
||||
js: {
|
||||
type: 'application/javascript'
|
||||
, encoding: 'utf8'
|
||||
, gzip: true
|
||||
}
|
||||
, swf: {
|
||||
type: 'application/x-shockwave-flash'
|
||||
, encoding: 'binary'
|
||||
, gzip: false
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Regexp for matching custom transport patterns. Users can configure their own
|
||||
* socket.io bundle based on the url structure. Different transport names are
|
||||
* concatinated using the `+` char. /socket.io/socket.io+websocket.js should
|
||||
* create a bundle that only contains support for the websocket.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
var bundle = /\+((?:\+)?[\w\-]+)*(?:\.js)$/g;
|
||||
|
||||
/**
|
||||
* Export the constructor
|
||||
*/
|
||||
|
||||
exports = module.exports = Static;
|
||||
|
||||
/**
|
||||
* Static constructor
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function Static (manager) {
|
||||
this.manager = manager;
|
||||
this.cache = {};
|
||||
this.paths = {};
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the Static by adding default file paths.
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Static.prototype.init = function () {
|
||||
/**
|
||||
* Generates a unique id based the supplied transports array
|
||||
*
|
||||
* @param {Array} transports The array with transport types
|
||||
* @api private
|
||||
*/
|
||||
function id (transports) {
|
||||
var id = transports.join('').split('').map(function (char) {
|
||||
return ('' + char.charCodeAt(0)).split('').pop();
|
||||
}).reduce(function (char, id) {
|
||||
return char +id;
|
||||
});
|
||||
|
||||
return client.version + ':' + id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a socket.io-client file based on the supplied transports.
|
||||
*
|
||||
* @param {Array} transports The array with transport types
|
||||
* @param {Function} callback Callback for the static.write
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function build (transports, callback) {
|
||||
client.builder(transports, {
|
||||
minify: self.manager.enabled('browser client minification')
|
||||
}, function (err, content) {
|
||||
callback(err, content ? new Buffer(content) : null, id(transports));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
|
||||
// add our default static files
|
||||
this.add('/static/flashsocket/WebSocketMain.swf', {
|
||||
file: client.dist + '/WebSocketMain.swf'
|
||||
});
|
||||
|
||||
this.add('/static/flashsocket/WebSocketMainInsecure.swf', {
|
||||
file: client.dist + '/WebSocketMainInsecure.swf'
|
||||
});
|
||||
|
||||
// generates dedicated build based on the available transports
|
||||
this.add('/socket.io.js', function (path, callback) {
|
||||
build(self.manager.get('transports'), callback);
|
||||
});
|
||||
|
||||
// allow custom builds based on url paths
|
||||
this.add('/socket.io+', { mime: mime.js }, function (path, callback) {
|
||||
var available = self.manager.get('transports')
|
||||
, matches = bundle.exec(path)
|
||||
, transports = [];
|
||||
|
||||
if (!matches) return callback('No valid transports');
|
||||
|
||||
// make sure they valid transports
|
||||
matches[0].split('.')[0].split('+').slice(1).forEach(function (transport) {
|
||||
if (!!~available.indexOf(transport)) {
|
||||
transports.push(transport);
|
||||
}
|
||||
});
|
||||
|
||||
if (!transports.length) return callback('No valid transports');
|
||||
build(transports, callback);
|
||||
});
|
||||
|
||||
// clear cache when transports change
|
||||
this.manager.on('set:transports', function (key, value) {
|
||||
delete self.cache['/socket.io.js'];
|
||||
Object.keys(self.cache).forEach(function (key) {
|
||||
if (bundle.test(key)) {
|
||||
delete self.cache[key];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Gzip compress buffers.
|
||||
*
|
||||
* @param {Buffer} data The buffer that needs gzip compression
|
||||
* @param {Function} callback
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Static.prototype.gzip = function (data, callback) {
|
||||
var gzip = cp.spawn('gzip', ['-9', '-c', '-f', '-n'])
|
||||
, encoding = Buffer.isBuffer(data) ? 'binary' : 'utf8'
|
||||
, buffer = []
|
||||
, err;
|
||||
|
||||
gzip.stdout.on('data', function (data) {
|
||||
buffer.push(data);
|
||||
});
|
||||
|
||||
gzip.stderr.on('data', function (data) {
|
||||
err = data +'';
|
||||
buffer.length = 0;
|
||||
});
|
||||
|
||||
gzip.on('exit', function () {
|
||||
if (err) return callback(err);
|
||||
|
||||
var size = 0
|
||||
, index = 0
|
||||
, i = buffer.length
|
||||
, content;
|
||||
|
||||
while (i--) {
|
||||
size += buffer[i].length;
|
||||
}
|
||||
|
||||
content = new Buffer(size);
|
||||
i = buffer.length;
|
||||
|
||||
buffer.forEach(function (buffer) {
|
||||
var length = buffer.length;
|
||||
|
||||
buffer.copy(content, index, 0, length);
|
||||
index += length;
|
||||
});
|
||||
|
||||
buffer.length = 0;
|
||||
callback(null, content);
|
||||
});
|
||||
|
||||
gzip.stdin.end(data, encoding);
|
||||
};
|
||||
|
||||
/**
|
||||
* Is the path a static file?
|
||||
*
|
||||
* @param {String} path The path that needs to be checked
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Static.prototype.has = function (path) {
|
||||
// fast case
|
||||
if (this.paths[path]) return this.paths[path];
|
||||
|
||||
var keys = Object.keys(this.paths)
|
||||
, i = keys.length;
|
||||
|
||||
while (i--) {
|
||||
if (!!~path.indexOf(keys[i])) return this.paths[keys[i]];
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add new paths new paths that can be served using the static provider.
|
||||
*
|
||||
* @param {String} path The path to respond to
|
||||
* @param {Options} options Options for writing out the response
|
||||
* @param {Function} [callback] Optional callback if no options.file is
|
||||
* supplied this would be called instead.
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Static.prototype.add = function (path, options, callback) {
|
||||
var extension = /(?:\.(\w{1,4}))$/.exec(path);
|
||||
|
||||
if (!callback && typeof options == 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
options.mime = options.mime || (extension ? mime[extension[1]] : false);
|
||||
|
||||
if (callback) options.callback = callback;
|
||||
if (!(options.file || options.callback) || !options.mime) return false;
|
||||
|
||||
this.paths[path] = options;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Writes a static response.
|
||||
*
|
||||
* @param {String} path The path for the static content
|
||||
* @param {HTTPRequest} req The request object
|
||||
* @param {HTTPResponse} res The response object
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Static.prototype.write = function (path, req, res) {
|
||||
/**
|
||||
* Write a response without throwing errors because can throw error if the
|
||||
* response is no longer writable etc.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function write (status, headers, content, encoding) {
|
||||
try {
|
||||
res.writeHead(status, headers || undefined);
|
||||
res.end(content || '', encoding || undefined);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Answers requests depending on the request properties and the reply object.
|
||||
*
|
||||
* @param {Object} reply The details and content to reply the response with
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function answer (reply) {
|
||||
var cached = req.headers['if-none-match'] === reply.etag;
|
||||
if (cached && self.manager.enabled('browser client etag')) {
|
||||
return write(304);
|
||||
}
|
||||
|
||||
var accept = req.headers['accept-encoding'] || ''
|
||||
, gzip = !!~accept.toLowerCase().indexOf('gzip')
|
||||
, mime = reply.mime
|
||||
, headers = {
|
||||
'Content-Type': mime.type
|
||||
};
|
||||
|
||||
// check if we can add a etag
|
||||
if (self.manager.enabled('browser client etag') && reply.etag) {
|
||||
headers['Etag'] = reply.etag;
|
||||
}
|
||||
|
||||
// check if we can send gzip data
|
||||
if (gzip && reply.gzip) {
|
||||
headers['Content-Length'] = reply.gzip.length;
|
||||
headers['Content-Encoding'] = 'gzip';
|
||||
write(200, headers, reply.gzip.content, mime.encoding);
|
||||
} else {
|
||||
headers['Content-Length'] = reply.length;
|
||||
write(200, headers, reply.content, mime.encoding);
|
||||
}
|
||||
|
||||
self.manager.log.debug('served static content ' + path);
|
||||
}
|
||||
|
||||
var self = this
|
||||
, details;
|
||||
|
||||
// most common case first
|
||||
if (this.manager.enabled('browser client cache') && this.cache[path]) {
|
||||
return answer(this.cache[path]);
|
||||
} else if (this.manager.get('browser client handler')) {
|
||||
return this.manager.get('browser client handler').call(this, req, res);
|
||||
} else if ((details = this.has(path))) {
|
||||
/**
|
||||
* A small helper function that will let us deal with fs and dynamic files
|
||||
*
|
||||
* @param {Object} err Optional error
|
||||
* @param {Buffer} content The data
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function ready (err, content, etag) {
|
||||
if (err) {
|
||||
self.manager.log.warn('Unable to serve file. ' + (err.message || err));
|
||||
return write(500, null, 'Error serving static ' + path);
|
||||
}
|
||||
|
||||
// store the result in the cache
|
||||
var reply = self.cache[path] = {
|
||||
content: content
|
||||
, length: content.length
|
||||
, mime: details.mime
|
||||
, etag: etag || client.version
|
||||
};
|
||||
|
||||
// check if gzip is enabled
|
||||
if (details.mime.gzip && self.manager.enabled('browser client gzip')) {
|
||||
self.gzip(content, function (err, content) {
|
||||
if (!err) {
|
||||
reply.gzip = {
|
||||
content: content
|
||||
, length: content.length
|
||||
}
|
||||
}
|
||||
|
||||
answer(reply);
|
||||
});
|
||||
} else {
|
||||
answer(reply);
|
||||
}
|
||||
}
|
||||
|
||||
if (details.file) {
|
||||
fs.readFile(details.file, ready);
|
||||
} else if(details.callback) {
|
||||
details.callback.call(this, path, ready);
|
||||
} else {
|
||||
write(404, null, 'File handle not found');
|
||||
}
|
||||
} else {
|
||||
write(404, null, 'File not found');
|
||||
}
|
||||
};
|
||||
@@ -60,12 +60,28 @@ function Redis (opts) {
|
||||
}
|
||||
}
|
||||
|
||||
var redis = opts.redis || require('redis');
|
||||
var redis = opts.redis || require('redis')
|
||||
, RedisClient = redis.RedisClient;
|
||||
|
||||
// initialize a pubsub client and a regular client
|
||||
this.pub = redis.createClient(opts.redisPub);
|
||||
this.sub = redis.createClient(opts.redisSub);
|
||||
this.cmd = redis.createClient(opts.redisClient);
|
||||
if (opts.redisPub instanceof RedisClient) {
|
||||
this.pub = opts.redisPub;
|
||||
} else {
|
||||
opts.redisPub || (opts.redisPub = {});
|
||||
this.pub = redis.createClient(opts.redisPub.port, opts.redisPub.host, opts.redisPub);
|
||||
}
|
||||
if (opts.redisSub instanceof RedisClient) {
|
||||
this.sub = opts.redisSub;
|
||||
} else {
|
||||
opts.redisSub || (opts.redisSub = {});
|
||||
this.sub = redis.createClient(opts.redisSub.port, opts.redisSub.host, opts.redisSub);
|
||||
}
|
||||
if (opts.redisClient instanceof RedisClient) {
|
||||
this.cmd = opts.redisClient;
|
||||
} else {
|
||||
opts.redisClient || (opts.redisClient = {});
|
||||
this.cmd = redis.createClient(opts.redisClient.port, opts.redisClient.host, opts.redisClient);
|
||||
}
|
||||
|
||||
Store.call(this, opts);
|
||||
};
|
||||
@@ -118,7 +134,7 @@ Redis.prototype.subscribe = function (name, consumer, fn) {
|
||||
self.on('unsubscribe', function unsubscribe (ch) {
|
||||
if (name == ch) {
|
||||
self.sub.removeListener('message', message);
|
||||
self.removeEvent('unsubscribe', unsubscribe);
|
||||
self.removeListener('unsubscribe', unsubscribe);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -25,9 +25,12 @@ exports = module.exports = WebSocket;
|
||||
*/
|
||||
|
||||
function WebSocket (mng, data, req) {
|
||||
var version = req.headers['sec-websocket-version'];
|
||||
var transport
|
||||
, version = req.headers['sec-websocket-version'];
|
||||
if (typeof version !== 'undefined' && typeof protocolVersions[version] !== 'undefined') {
|
||||
return new protocolVersions[version](mng, data, req);
|
||||
transport = new protocolVersions[version](mng, data, req);
|
||||
}
|
||||
return new protocolVersions['default'](mng, data, req);
|
||||
else transport = new protocolVersions['default'](mng, data, req);
|
||||
if (typeof this.name !== 'undefined') transport.name = this.name;
|
||||
return transport;
|
||||
};
|
||||
|
||||
@@ -60,6 +60,14 @@ WebSocket.prototype.__proto__ = Transport.prototype;
|
||||
|
||||
WebSocket.prototype.name = 'websocket';
|
||||
|
||||
/**
|
||||
* Websocket draft version
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
WebSocket.prototype.protocolVersion = 'hixie-76';
|
||||
|
||||
/**
|
||||
* Called when the socket connects.
|
||||
*
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
var Transport = require('../../transport')
|
||||
, EventEmitter = process.EventEmitter
|
||||
, crypto = require('crypto')
|
||||
, url = require('url')
|
||||
, parser = require('../../parser')
|
||||
, util = require('../../util');
|
||||
|
||||
@@ -33,6 +34,7 @@ function WebSocket (mng, data, req) {
|
||||
// parser
|
||||
var self = this;
|
||||
|
||||
this.manager = mng;
|
||||
this.parser = new Parser();
|
||||
this.parser.on('data', function (packet) {
|
||||
self.onMessage(parser.decodePacket(packet));
|
||||
@@ -66,6 +68,14 @@ WebSocket.prototype.__proto__ = Transport.prototype;
|
||||
|
||||
WebSocket.prototype.name = 'websocket';
|
||||
|
||||
/**
|
||||
* Websocket draft version
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
WebSocket.prototype.protocolVersion = '07-12';
|
||||
|
||||
/**
|
||||
* Called when the socket connects.
|
||||
*
|
||||
@@ -81,10 +91,16 @@ WebSocket.prototype.onSocketConnect = function () {
|
||||
return;
|
||||
}
|
||||
|
||||
var origin = this.req.headers.origin
|
||||
var origin = this.req.headers['sec-websocket-origin']
|
||||
, location = (this.socket.encrypted ? 'wss' : 'ws')
|
||||
+ '://' + this.req.headers.host + this.req.url;
|
||||
|
||||
|
||||
if (!this.verifyOrigin(origin)) {
|
||||
this.log.warn(this.name + ' connection invalid: origin mismatch');
|
||||
this.end();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.req.headers['sec-websocket-key']) {
|
||||
this.log.warn(this.name + ' connection invalid: received no key');
|
||||
this.end();
|
||||
@@ -118,6 +134,40 @@ WebSocket.prototype.onSocketConnect = function () {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Verifies the origin of a request.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
WebSocket.prototype.verifyOrigin = function (origin) {
|
||||
var origins = this.manager.get('origins');
|
||||
|
||||
if (origin === 'null') origin = '*';
|
||||
|
||||
if (origins.indexOf('*:*') !== -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (origin) {
|
||||
try {
|
||||
var parts = url.parse(origin);
|
||||
var ok =
|
||||
~origins.indexOf(parts.hostname + ':' + parts.port) ||
|
||||
~origins.indexOf(parts.hostname + ':*') ||
|
||||
~origins.indexOf('*:' + parts.port);
|
||||
if (!ok) this.log.warn('illegal origin: ' + origin);
|
||||
return ok;
|
||||
} catch (ex) {
|
||||
this.log.warn('error parsing origin');
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.log.warn('origin missing from websocket call, yet required by config');
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Writes to the socket.
|
||||
*
|
||||
@@ -132,6 +182,20 @@ WebSocket.prototype.write = function (data) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Writes a payload.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
WebSocket.prototype.payload = function (msgs) {
|
||||
for (var i = 0, l = msgs.length; i < l; i++) {
|
||||
this.write(msgs[i]);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Frame server-to-client output as a text packet.
|
||||
*
|
||||
@@ -233,7 +297,56 @@ function Parser () {
|
||||
if (firstLength < 126) {
|
||||
expectData(firstLength);
|
||||
}
|
||||
else if (firstLength == 126) {
|
||||
else if (firstLength == 126) {
|
||||
self.expect('Length', 2, function(data) {
|
||||
expectData(util.unpack(data));
|
||||
});
|
||||
}
|
||||
else if (firstLength == 127) {
|
||||
self.expect('Length', 8, function(data) {
|
||||
if (util.unpack(data.slice(0, 4)) != 0) {
|
||||
self.error('packets with length spanning more than 32 bit is currently not supported');
|
||||
return;
|
||||
}
|
||||
var lengthBytes = data.slice(4); // note: cap to 32 bit length
|
||||
expectData(util.unpack(data));
|
||||
});
|
||||
}
|
||||
},
|
||||
// binary
|
||||
'2': function(data) {
|
||||
var finish = function(mask, data) {
|
||||
if (typeof self.currentMessage == 'string') self.currentMessage = []; // build a buffer list
|
||||
self.currentMessage.push(self.unmask(mask, data, true));
|
||||
if (self.state.lastFragment) {
|
||||
self.emit('binary', self.concatBuffers(self.currentMessage));
|
||||
self.currentMessage = '';
|
||||
}
|
||||
self.endPacket();
|
||||
}
|
||||
|
||||
var expectData = function(length) {
|
||||
if (self.state.masked) {
|
||||
self.expect('Mask', 4, function(data) {
|
||||
var mask = data;
|
||||
self.expect('Data', length, function(data) {
|
||||
finish(mask, data);
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
self.expect('Data', length, function(data) {
|
||||
finish(null, data);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// decode length
|
||||
var firstLength = data[1] & 0x7f;
|
||||
if (firstLength < 126) {
|
||||
expectData(firstLength);
|
||||
}
|
||||
else if (firstLength == 126) {
|
||||
self.expect('Length', 2, function(data) {
|
||||
expectData(util.unpack(data));
|
||||
});
|
||||
@@ -290,7 +403,7 @@ function Parser () {
|
||||
else if (firstLength < 126) {
|
||||
expectData(firstLength);
|
||||
}
|
||||
else if (firstLength == 126) {
|
||||
else if (firstLength == 126) {
|
||||
self.expect('Length', 2, function(data) {
|
||||
expectData(util.unpack(data));
|
||||
});
|
||||
@@ -379,19 +492,24 @@ Parser.prototype.expect = function(what, length, handler) {
|
||||
*/
|
||||
|
||||
Parser.prototype.processPacket = function (data) {
|
||||
if ((data[0] & 0x70) != 0) this.error('reserved fields not empty');
|
||||
if ((data[0] & 0x70) != 0) this.error('reserved fields must be empty');
|
||||
this.state.lastFragment = (data[0] & 0x80) == 0x80;
|
||||
this.state.masked = (data[1] & 0x80) == 0x80;
|
||||
var opcode = data[0] & 0xf;
|
||||
if (opcode == 0) {
|
||||
if (opcode == 0) {
|
||||
// continuation frame
|
||||
if (this.state.opcode != 1 || this.state.opcode != 2) {
|
||||
this.state.opcode = this.state.activeFragmentedOperation;
|
||||
if (!(this.state.opcode == 1 || this.state.opcode == 2)) {
|
||||
this.error('continuation frame cannot follow current opcode')
|
||||
return;
|
||||
}
|
||||
}
|
||||
else this.state.opcode = opcode;
|
||||
this.state.opcode = data[0] & 0xf;
|
||||
else {
|
||||
this.state.opcode = opcode;
|
||||
if (this.state.lastFragment === false) {
|
||||
this.state.activeFragmentedOperation = opcode;
|
||||
}
|
||||
}
|
||||
var handler = this.opcodeHandlers[this.state.opcode];
|
||||
if (typeof handler == 'undefined') this.error('no handler for opcode ' + this.state.opcode);
|
||||
else handler(data);
|
||||
@@ -412,7 +530,7 @@ Parser.prototype.endPacket = function() {
|
||||
this.state.activeFragmentedOperation = null;
|
||||
}
|
||||
this.state.lastFragment = false;
|
||||
this.state.opcode = this.state.activeFragmentedOperation != null ? this.state.activeFragmentedOperation : 0;
|
||||
this.state.opcode = this.state.activeFragmentedOperation != null ? this.state.activeFragmentedOperation : 0;
|
||||
this.state.masked = false;
|
||||
this.expect('Opcode', 2, this.processPacket);
|
||||
}
|
||||
@@ -443,15 +561,36 @@ Parser.prototype.reset = function() {
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Parser.prototype.unmask = function (mask, buf) {
|
||||
Parser.prototype.unmask = function (mask, buf, binary) {
|
||||
if (mask != null) {
|
||||
for (var i = 0, ll = buf.length; i < ll; i++) {
|
||||
buf[i] ^= mask[i % 4];
|
||||
}
|
||||
}
|
||||
if (binary) return buf;
|
||||
return buf != null ? buf.toString('utf8') : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenates a list of buffers.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Parser.prototype.concatBuffers = function(buffers) {
|
||||
var length = 0;
|
||||
for (var i = 0, l = buffers.length; i < l; ++i) {
|
||||
length += buffers[i].length;
|
||||
}
|
||||
var mergedBuffer = new Buffer(length);
|
||||
var offset = 0;
|
||||
for (var i = 0, l = buffers.length; i < l; ++i) {
|
||||
buffers[i].copy(mergedBuffer, offset);
|
||||
offset += buffers[i].length;
|
||||
}
|
||||
return mergedBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles an error
|
||||
*
|
||||
|
||||
604
lib/transports/websocket/hybi-16.js
Normal file
604
lib/transports/websocket/hybi-16.js
Normal file
@@ -0,0 +1,604 @@
|
||||
|
||||
/*!
|
||||
* socket.io-node
|
||||
* Copyright(c) 2011 LearnBoost <dev@learnboost.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module requirements.
|
||||
*/
|
||||
|
||||
var Transport = require('../../transport')
|
||||
, EventEmitter = process.EventEmitter
|
||||
, crypto = require('crypto')
|
||||
, url = require('url')
|
||||
, parser = require('../../parser')
|
||||
, util = require('../../util');
|
||||
|
||||
/**
|
||||
* Export the constructor.
|
||||
*/
|
||||
|
||||
exports = module.exports = WebSocket;
|
||||
exports.Parser = Parser;
|
||||
|
||||
/**
|
||||
* HTTP interface constructor. Interface compatible with all transports that
|
||||
* depend on request-response cycles.
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function WebSocket (mng, data, req) {
|
||||
// parser
|
||||
var self = this;
|
||||
|
||||
this.manager = mng;
|
||||
this.parser = new Parser();
|
||||
this.parser.on('data', function (packet) {
|
||||
self.onMessage(parser.decodePacket(packet));
|
||||
});
|
||||
this.parser.on('ping', function () {
|
||||
// version 8 ping => pong
|
||||
self.socket.write('\u008a\u0000');
|
||||
});
|
||||
this.parser.on('close', function () {
|
||||
self.end();
|
||||
});
|
||||
this.parser.on('error', function (reason) {
|
||||
self.log.warn(self.name + ' parser error: ' + reason);
|
||||
self.end();
|
||||
});
|
||||
|
||||
Transport.call(this, mng, data, req);
|
||||
};
|
||||
|
||||
/**
|
||||
* Inherits from Transport.
|
||||
*/
|
||||
|
||||
WebSocket.prototype.__proto__ = Transport.prototype;
|
||||
|
||||
/**
|
||||
* Transport name
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
WebSocket.prototype.name = 'websocket';
|
||||
|
||||
/**
|
||||
* Websocket draft version
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
WebSocket.prototype.protocolVersion = '16';
|
||||
|
||||
/**
|
||||
* Called when the socket connects.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
WebSocket.prototype.onSocketConnect = function () {
|
||||
var self = this;
|
||||
|
||||
if (this.req.headers.upgrade !== 'websocket') {
|
||||
this.log.warn(this.name + ' connection invalid');
|
||||
this.end();
|
||||
return;
|
||||
}
|
||||
|
||||
var origin = this.req.headers['origin']
|
||||
, location = (this.socket.encrypted ? 'wss' : 'ws')
|
||||
+ '://' + this.req.headers.host + this.req.url;
|
||||
|
||||
if (!this.verifyOrigin(origin)) {
|
||||
this.log.warn(this.name + ' connection invalid: origin mismatch');
|
||||
this.end();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.req.headers['sec-websocket-key']) {
|
||||
this.log.warn(this.name + ' connection invalid: received no key');
|
||||
this.end();
|
||||
return;
|
||||
}
|
||||
|
||||
// calc key
|
||||
var key = this.req.headers['sec-websocket-key'];
|
||||
var shasum = crypto.createHash('sha1');
|
||||
shasum.update(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
|
||||
key = shasum.digest('base64');
|
||||
|
||||
var headers = [
|
||||
'HTTP/1.1 101 Switching Protocols'
|
||||
, 'Upgrade: websocket'
|
||||
, 'Connection: Upgrade'
|
||||
, 'Sec-WebSocket-Accept: ' + key
|
||||
];
|
||||
|
||||
try {
|
||||
this.socket.write(headers.concat('', '').join('\r\n'));
|
||||
this.socket.setTimeout(0);
|
||||
this.socket.setNoDelay(true);
|
||||
} catch (e) {
|
||||
this.end();
|
||||
return;
|
||||
}
|
||||
|
||||
this.socket.on('data', function (data) {
|
||||
self.parser.add(data);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Verifies the origin of a request.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
WebSocket.prototype.verifyOrigin = function (origin) {
|
||||
var origins = this.manager.get('origins');
|
||||
|
||||
if (origin === 'null') origin = '*';
|
||||
|
||||
if (origins.indexOf('*:*') !== -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (origin) {
|
||||
try {
|
||||
var parts = url.parse(origin);
|
||||
var ok =
|
||||
~origins.indexOf(parts.hostname + ':' + parts.port) ||
|
||||
~origins.indexOf(parts.hostname + ':*') ||
|
||||
~origins.indexOf('*:' + parts.port);
|
||||
if (!ok) this.log.warn('illegal origin: ' + origin);
|
||||
return ok;
|
||||
} catch (ex) {
|
||||
this.log.warn('error parsing origin');
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.log.warn('origin missing from websocket call, yet required by config');
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Writes to the socket.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
WebSocket.prototype.write = function (data) {
|
||||
if (this.open) {
|
||||
var buf = this.frame(0x81, data);
|
||||
this.socket.write(buf, 'binary');
|
||||
this.log.debug(this.name + ' writing', data);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Writes a payload.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
WebSocket.prototype.payload = function (msgs) {
|
||||
for (var i = 0, l = msgs.length; i < l; i++) {
|
||||
this.write(msgs[i]);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Frame server-to-client output as a text packet.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
WebSocket.prototype.frame = function (opcode, str) {
|
||||
var dataBuffer = new Buffer(str)
|
||||
, dataLength = dataBuffer.length
|
||||
, startOffset = 2
|
||||
, secondByte = dataLength;
|
||||
if (dataLength > 65536) {
|
||||
startOffset = 10;
|
||||
secondByte = 127;
|
||||
}
|
||||
else if (dataLength > 125) {
|
||||
startOffset = 4;
|
||||
secondByte = 126;
|
||||
}
|
||||
var outputBuffer = new Buffer(dataLength + startOffset);
|
||||
outputBuffer[0] = opcode;
|
||||
outputBuffer[1] = secondByte;
|
||||
dataBuffer.copy(outputBuffer, startOffset);
|
||||
switch (secondByte) {
|
||||
case 126:
|
||||
outputBuffer[2] = dataLength >>> 8;
|
||||
outputBuffer[3] = dataLength % 256;
|
||||
break;
|
||||
case 127:
|
||||
var l = dataLength;
|
||||
for (var i = 1; i <= 8; ++i) {
|
||||
outputBuffer[startOffset - i] = l & 0xff;
|
||||
l >>>= 8;
|
||||
}
|
||||
}
|
||||
return outputBuffer;
|
||||
};
|
||||
|
||||
/**
|
||||
* Closes the connection.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
WebSocket.prototype.doClose = function () {
|
||||
this.socket.end();
|
||||
};
|
||||
|
||||
/**
|
||||
* WebSocket parser
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function Parser () {
|
||||
this.state = {
|
||||
activeFragmentedOperation: null,
|
||||
lastFragment: false,
|
||||
masked: false,
|
||||
opcode: 0
|
||||
};
|
||||
this.overflow = null;
|
||||
this.expectOffset = 0;
|
||||
this.expectBuffer = null;
|
||||
this.expectHandler = null;
|
||||
this.currentMessage = '';
|
||||
|
||||
var self = this;
|
||||
this.opcodeHandlers = {
|
||||
// text
|
||||
'1': function(data) {
|
||||
var finish = function(mask, data) {
|
||||
self.currentMessage += self.unmask(mask, data);
|
||||
if (self.state.lastFragment) {
|
||||
self.emit('data', self.currentMessage);
|
||||
self.currentMessage = '';
|
||||
}
|
||||
self.endPacket();
|
||||
}
|
||||
|
||||
var expectData = function(length) {
|
||||
if (self.state.masked) {
|
||||
self.expect('Mask', 4, function(data) {
|
||||
var mask = data;
|
||||
self.expect('Data', length, function(data) {
|
||||
finish(mask, data);
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
self.expect('Data', length, function(data) {
|
||||
finish(null, data);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// decode length
|
||||
var firstLength = data[1] & 0x7f;
|
||||
if (firstLength < 126) {
|
||||
expectData(firstLength);
|
||||
}
|
||||
else if (firstLength == 126) {
|
||||
self.expect('Length', 2, function(data) {
|
||||
expectData(util.unpack(data));
|
||||
});
|
||||
}
|
||||
else if (firstLength == 127) {
|
||||
self.expect('Length', 8, function(data) {
|
||||
if (util.unpack(data.slice(0, 4)) != 0) {
|
||||
self.error('packets with length spanning more than 32 bit is currently not supported');
|
||||
return;
|
||||
}
|
||||
var lengthBytes = data.slice(4); // note: cap to 32 bit length
|
||||
expectData(util.unpack(data));
|
||||
});
|
||||
}
|
||||
},
|
||||
// binary
|
||||
'2': function(data) {
|
||||
var finish = function(mask, data) {
|
||||
if (typeof self.currentMessage == 'string') self.currentMessage = []; // build a buffer list
|
||||
self.currentMessage.push(self.unmask(mask, data, true));
|
||||
if (self.state.lastFragment) {
|
||||
self.emit('binary', self.concatBuffers(self.currentMessage));
|
||||
self.currentMessage = '';
|
||||
}
|
||||
self.endPacket();
|
||||
}
|
||||
|
||||
var expectData = function(length) {
|
||||
if (self.state.masked) {
|
||||
self.expect('Mask', 4, function(data) {
|
||||
var mask = data;
|
||||
self.expect('Data', length, function(data) {
|
||||
finish(mask, data);
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
self.expect('Data', length, function(data) {
|
||||
finish(null, data);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// decode length
|
||||
var firstLength = data[1] & 0x7f;
|
||||
if (firstLength < 126) {
|
||||
expectData(firstLength);
|
||||
}
|
||||
else if (firstLength == 126) {
|
||||
self.expect('Length', 2, function(data) {
|
||||
expectData(util.unpack(data));
|
||||
});
|
||||
}
|
||||
else if (firstLength == 127) {
|
||||
self.expect('Length', 8, function(data) {
|
||||
if (util.unpack(data.slice(0, 4)) != 0) {
|
||||
self.error('packets with length spanning more than 32 bit is currently not supported');
|
||||
return;
|
||||
}
|
||||
var lengthBytes = data.slice(4); // note: cap to 32 bit length
|
||||
expectData(util.unpack(data));
|
||||
});
|
||||
}
|
||||
},
|
||||
// close
|
||||
'8': function(data) {
|
||||
self.emit('close');
|
||||
self.reset();
|
||||
},
|
||||
// ping
|
||||
'9': function(data) {
|
||||
if (self.state.lastFragment == false) {
|
||||
self.error('fragmented ping is not supported');
|
||||
return;
|
||||
}
|
||||
|
||||
var finish = function(mask, data) {
|
||||
self.emit('ping', self.unmask(mask, data));
|
||||
self.endPacket();
|
||||
}
|
||||
|
||||
var expectData = function(length) {
|
||||
if (self.state.masked) {
|
||||
self.expect('Mask', 4, function(data) {
|
||||
var mask = data;
|
||||
self.expect('Data', length, function(data) {
|
||||
finish(mask, data);
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
self.expect('Data', length, function(data) {
|
||||
finish(null, data);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// decode length
|
||||
var firstLength = data[1] & 0x7f;
|
||||
if (firstLength == 0) {
|
||||
finish(null, null);
|
||||
}
|
||||
else if (firstLength < 126) {
|
||||
expectData(firstLength);
|
||||
}
|
||||
else if (firstLength == 126) {
|
||||
self.expect('Length', 2, function(data) {
|
||||
expectData(util.unpack(data));
|
||||
});
|
||||
}
|
||||
else if (firstLength == 127) {
|
||||
self.expect('Length', 8, function(data) {
|
||||
expectData(util.unpack(data));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.expect('Opcode', 2, this.processPacket);
|
||||
};
|
||||
|
||||
/**
|
||||
* Inherits from EventEmitter.
|
||||
*/
|
||||
|
||||
Parser.prototype.__proto__ = EventEmitter.prototype;
|
||||
|
||||
/**
|
||||
* Add new data to the parser.
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Parser.prototype.add = function(data) {
|
||||
if (this.expectBuffer == null) {
|
||||
this.addToOverflow(data);
|
||||
return;
|
||||
}
|
||||
var toRead = Math.min(data.length, this.expectBuffer.length - this.expectOffset);
|
||||
data.copy(this.expectBuffer, this.expectOffset, 0, toRead);
|
||||
this.expectOffset += toRead;
|
||||
if (toRead < data.length) {
|
||||
// at this point the overflow buffer shouldn't at all exist
|
||||
this.overflow = new Buffer(data.length - toRead);
|
||||
data.copy(this.overflow, 0, toRead, toRead + this.overflow.length);
|
||||
}
|
||||
if (this.expectOffset == this.expectBuffer.length) {
|
||||
var bufferForHandler = this.expectBuffer;
|
||||
this.expectBuffer = null;
|
||||
this.expectOffset = 0;
|
||||
this.expectHandler.call(this, bufferForHandler);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a piece of data to the overflow.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Parser.prototype.addToOverflow = function(data) {
|
||||
if (this.overflow == null) this.overflow = data;
|
||||
else {
|
||||
var prevOverflow = this.overflow;
|
||||
this.overflow = new Buffer(this.overflow.length + data.length);
|
||||
prevOverflow.copy(this.overflow, 0);
|
||||
data.copy(this.overflow, prevOverflow.length);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for a certain amount of bytes to be available, then fires a callback.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Parser.prototype.expect = function(what, length, handler) {
|
||||
this.expectBuffer = new Buffer(length);
|
||||
this.expectOffset = 0;
|
||||
this.expectHandler = handler;
|
||||
if (this.overflow != null) {
|
||||
var toOverflow = this.overflow;
|
||||
this.overflow = null;
|
||||
this.add(toOverflow);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start processing a new packet.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Parser.prototype.processPacket = function (data) {
|
||||
if ((data[0] & 0x70) != 0) this.error('reserved fields must be empty');
|
||||
this.state.lastFragment = (data[0] & 0x80) == 0x80;
|
||||
this.state.masked = (data[1] & 0x80) == 0x80;
|
||||
var opcode = data[0] & 0xf;
|
||||
if (opcode == 0) {
|
||||
// continuation frame
|
||||
this.state.opcode = this.state.activeFragmentedOperation;
|
||||
if (!(this.state.opcode == 1 || this.state.opcode == 2)) {
|
||||
this.error('continuation frame cannot follow current opcode')
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.state.opcode = opcode;
|
||||
if (this.state.lastFragment === false) {
|
||||
this.state.activeFragmentedOperation = opcode;
|
||||
}
|
||||
}
|
||||
var handler = this.opcodeHandlers[this.state.opcode];
|
||||
if (typeof handler == 'undefined') this.error('no handler for opcode ' + this.state.opcode);
|
||||
else handler(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Endprocessing a packet.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Parser.prototype.endPacket = function() {
|
||||
this.expectOffset = 0;
|
||||
this.expectBuffer = null;
|
||||
this.expectHandler = null;
|
||||
if (this.state.lastFragment && this.state.opcode == this.state.activeFragmentedOperation) {
|
||||
// end current fragmented operation
|
||||
this.state.activeFragmentedOperation = null;
|
||||
}
|
||||
this.state.lastFragment = false;
|
||||
this.state.opcode = this.state.activeFragmentedOperation != null ? this.state.activeFragmentedOperation : 0;
|
||||
this.state.masked = false;
|
||||
this.expect('Opcode', 2, this.processPacket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the parser state.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Parser.prototype.reset = function() {
|
||||
this.state = {
|
||||
activeFragmentedOperation: null,
|
||||
lastFragment: false,
|
||||
masked: false,
|
||||
opcode: 0
|
||||
};
|
||||
this.expectOffset = 0;
|
||||
this.expectBuffer = null;
|
||||
this.expectHandler = null;
|
||||
this.overflow = null;
|
||||
this.currentMessage = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmask received data.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Parser.prototype.unmask = function (mask, buf, binary) {
|
||||
if (mask != null) {
|
||||
for (var i = 0, ll = buf.length; i < ll; i++) {
|
||||
buf[i] ^= mask[i % 4];
|
||||
}
|
||||
}
|
||||
if (binary) return buf;
|
||||
return buf != null ? buf.toString('utf8') : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenates a list of buffers.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Parser.prototype.concatBuffers = function(buffers) {
|
||||
var length = 0;
|
||||
for (var i = 0, l = buffers.length; i < l; ++i) {
|
||||
length += buffers[i].length;
|
||||
}
|
||||
var mergedBuffer = new Buffer(length);
|
||||
var offset = 0;
|
||||
for (var i = 0, l = buffers.length; i < l; ++i) {
|
||||
buffers[i].copy(mergedBuffer, offset);
|
||||
offset += buffers[i].length;
|
||||
}
|
||||
return mergedBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles an error
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Parser.prototype.error = function (reason) {
|
||||
this.reset();
|
||||
this.emit('error', reason);
|
||||
return this;
|
||||
};
|
||||
@@ -6,5 +6,6 @@
|
||||
module.exports = {
|
||||
7: require('./hybi-07-12'),
|
||||
8: require('./hybi-07-12'),
|
||||
13: require('./hybi-16'),
|
||||
default: require('./default')
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "socket.io"
|
||||
, "version": "0.8.3"
|
||||
, "version": "0.8.5"
|
||||
, "description": "Real-time apps made cross-browser & easy with a WebSocket-like API"
|
||||
, "homepage": "http://socket.io"
|
||||
, "keywords": ["websocket", "socket", "realtime", "socket.io", "comet", "ajax"]
|
||||
@@ -16,7 +16,7 @@
|
||||
, "url": "https://github.com/LearnBoost/socket.io.git"
|
||||
}
|
||||
, "dependencies": {
|
||||
"socket.io-client": "0.8.3"
|
||||
"socket.io-client": "0.8.5"
|
||||
, "policyfile": "0.0.4"
|
||||
, "redis": "0.6.6"
|
||||
}
|
||||
|
||||
@@ -191,14 +191,14 @@ create = function (cl) {
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function WSClient (port, sid) {
|
||||
function WSClient (port, sid, transport) {
|
||||
this.sid = sid;
|
||||
this.port = port;
|
||||
|
||||
this.transportName = transport || 'websocket';
|
||||
WebSocket.call(
|
||||
this
|
||||
, 'ws://localhost:' + port + '/socket.io/'
|
||||
+ io.protocol + '/websocket/' + sid
|
||||
+ io.protocol + '/' + this.transportName + '/' + sid
|
||||
);
|
||||
};
|
||||
|
||||
@@ -239,6 +239,6 @@ WSClient.prototype.packet = function (pack) {
|
||||
* @api public
|
||||
*/
|
||||
|
||||
websocket = function (cl, sid) {
|
||||
return new WSClient(cl.port, sid);
|
||||
websocket = function (cl, sid, transport) {
|
||||
return new WSClient(cl.port, sid, transport);
|
||||
};
|
||||
|
||||
99
test/hybi-common.js
Normal file
99
test/hybi-common.js
Normal file
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* Returns a Buffer from a "ff 00 ff"-type hex string.
|
||||
*/
|
||||
|
||||
getBufferFromHexString = function(byteStr) {
|
||||
var bytes = byteStr.split(' ');
|
||||
var buf = new Buffer(bytes.length);
|
||||
for (var i = 0; i < bytes.length; ++i) {
|
||||
buf[i] = parseInt(bytes[i], 16);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a hex string from a Buffer.
|
||||
*/
|
||||
|
||||
getHexStringFromBuffer = function(data) {
|
||||
var s = '';
|
||||
for (var i = 0; i < data.length; ++i) {
|
||||
s += padl(data[i].toString(16), 2, '0') + ' ';
|
||||
}
|
||||
return s.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits a buffer in two parts.
|
||||
*/
|
||||
|
||||
splitBuffer = function(buffer) {
|
||||
var b1 = new Buffer(Math.ceil(buffer.length / 2));
|
||||
buffer.copy(b1, 0, 0, b1.length);
|
||||
var b2 = new Buffer(Math.floor(buffer.length / 2));
|
||||
buffer.copy(b2, 0, b1.length, b1.length + b2.length);
|
||||
return [b1, b2];
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs hybi07+ type masking on a hex string or buffer.
|
||||
*/
|
||||
|
||||
mask = function(buf, maskString) {
|
||||
if (typeof buf == 'string') buf = new Buffer(buf);
|
||||
var mask = getBufferFromHexString(maskString || '34 83 a8 68');
|
||||
for (var i = 0; i < buf.length; ++i) {
|
||||
buf[i] ^= mask[i % 4];
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a hex string representing the length of a message
|
||||
*/
|
||||
|
||||
getHybiLengthAsHexString = function(len, masked) {
|
||||
if (len < 126) {
|
||||
var buf = new Buffer(1);
|
||||
buf[0] = (masked ? 0x80 : 0) | len;
|
||||
}
|
||||
else if (len < 65536) {
|
||||
var buf = new Buffer(3);
|
||||
buf[0] = (masked ? 0x80 : 0) | 126;
|
||||
getBufferFromHexString(pack(4, len)).copy(buf, 1);
|
||||
}
|
||||
else {
|
||||
var buf = new Buffer(9);
|
||||
buf[0] = (masked ? 0x80 : 0) | 127;
|
||||
getBufferFromHexString(pack(16, len)).copy(buf, 1);
|
||||
}
|
||||
return getHexStringFromBuffer(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpacks a Buffer into a number.
|
||||
*/
|
||||
|
||||
unpack = function(buffer) {
|
||||
var n = 0;
|
||||
for (var i = 0; i < buffer.length; ++i) {
|
||||
n = (i == 0) ? buffer[i] : (n * 256) + buffer[i];
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a hex string, representing a specific byte count 'length', from a number.
|
||||
*/
|
||||
|
||||
pack = function(length, number) {
|
||||
return padl(number.toString(16), length, '0').replace(/(\d\d)/g, '$1 ').trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Left pads the string 's' to a total length of 'n' with char 'c'.
|
||||
*/
|
||||
|
||||
padl = function(s, n, c) {
|
||||
return new Array(1 + n - s.length).join(c) + s;
|
||||
}
|
||||
@@ -147,236 +147,6 @@ module.exports = {
|
||||
});
|
||||
},
|
||||
|
||||
'test that the client is served': function (done) {
|
||||
var port = ++ports
|
||||
, io = sio.listen(port)
|
||||
, cl = client(port);
|
||||
|
||||
cl.get('/socket.io/socket.io.js', function (res, data) {
|
||||
res.headers['content-type'].should.eql('application/javascript');
|
||||
res.headers['content-length'].should.match(/([0-9]+)/);
|
||||
should.strictEqual(res.headers.etag, undefined);
|
||||
|
||||
data.should.match(/XMLHttpRequest/);
|
||||
|
||||
cl.end();
|
||||
io.server.close();
|
||||
done();
|
||||
});
|
||||
},
|
||||
|
||||
'test that the client etag is served': function (done) {
|
||||
var port = ++ports
|
||||
, io = sio.listen(port)
|
||||
, cl = client(port);
|
||||
|
||||
io.configure(function () {
|
||||
io.enable('browser client etag');
|
||||
});
|
||||
|
||||
cl.get('/socket.io/socket.io.js', function (res, data) {
|
||||
res.headers['content-type'].should.eql('application/javascript');
|
||||
res.headers['content-length'].should.match(/([0-9]+)/);
|
||||
res.headers.etag.should.match(/([0-9]+)\.([0-9]+)\.([0-9]+)/);
|
||||
|
||||
data.should.match(/XMLHttpRequest/);
|
||||
|
||||
cl.end();
|
||||
io.server.close();
|
||||
done();
|
||||
});
|
||||
},
|
||||
|
||||
'test that the cached client is served': function (done) {
|
||||
var port = ++ports
|
||||
, io = sio.listen(port)
|
||||
, cl = client(port);
|
||||
|
||||
cl.get('/socket.io/socket.io.js', function (res, data) {
|
||||
res.headers['content-type'].should.eql('application/javascript');
|
||||
res.headers['content-length'].should.match(/([0-9]+)/);
|
||||
should.strictEqual(res.headers.etag, undefined);
|
||||
|
||||
data.should.match(/XMLHttpRequest/);
|
||||
var static = sio.Manager.static;
|
||||
static.cache['/socket.io.js'].content.should.match(/XMLHttpRequest/);
|
||||
|
||||
cl.get('/socket.io/socket.io.js', function (res, data) {
|
||||
res.headers['content-type'].should.eql('application/javascript');
|
||||
res.headers['content-length'].should.match(/([0-9]+)/);
|
||||
should.strictEqual(res.headers.etag, undefined);
|
||||
|
||||
data.should.match(/XMLHttpRequest/);
|
||||
|
||||
cl.end();
|
||||
io.server.close();
|
||||
done();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
'test that the cached client etag is served': function (done) {
|
||||
var port = ++ports
|
||||
, io = sio.listen(port)
|
||||
, cl = client(port);
|
||||
|
||||
io.configure(function () {
|
||||
io.enable('browser client etag');
|
||||
});
|
||||
|
||||
cl.get('/socket.io/socket.io.js', function (res, data) {
|
||||
res.headers['content-type'].should.eql('application/javascript');
|
||||
res.headers['content-length'].should.match(/([0-9]+)/);
|
||||
res.headers.etag.should.match(/([0-9]+)\.([0-9]+)\.([0-9]+)/);
|
||||
|
||||
data.should.match(/XMLHttpRequest/);
|
||||
var static = sio.Manager.static
|
||||
, cache = static.cache['/socket.io.js'];
|
||||
|
||||
cache.content.toString().should.match(/XMLHttpRequest/);
|
||||
Buffer.isBuffer(cache.content).should.be.true;
|
||||
|
||||
cl.get('/socket.io/socket.io.js', function (res, data) {
|
||||
res.headers['content-type'].should.eql('application/javascript');
|
||||
res.headers['content-length'].should.match(/([0-9]+)/);
|
||||
res.headers.etag.should.match(/([0-9]+)\.([0-9]+)\.([0-9]+)/);
|
||||
|
||||
data.should.match(/XMLHttpRequest/);
|
||||
|
||||
cl.end();
|
||||
io.server.close();
|
||||
done();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
'test that the cached client sends a 304 header': function (done) {
|
||||
var port = ++ports
|
||||
, io = sio.listen(port)
|
||||
, cl = client(port);
|
||||
|
||||
io.configure(function () {
|
||||
io.enable('browser client etag');
|
||||
});
|
||||
|
||||
cl.get('/socket.io/socket.io.js', function (res, data) {
|
||||
cl.get('/socket.io/socket.io.js', {headers:{'if-none-match':res.headers.etag}}, function (res, data) {
|
||||
res.statusCode.should.eql(304);
|
||||
|
||||
cl.end();
|
||||
io.server.close();
|
||||
done();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
'test that client minification works': function (done) {
|
||||
// server 1
|
||||
var port = ++ports
|
||||
, io = sio.listen(port)
|
||||
, cl = client(port);
|
||||
|
||||
// server 2
|
||||
var port = ++ports
|
||||
, io2 = sio.listen(port)
|
||||
, cl2 = client(port);
|
||||
|
||||
io.configure(function () {
|
||||
io.enable('browser client minification');
|
||||
});
|
||||
|
||||
cl.get('/socket.io/socket.io.js', function (res, data) {
|
||||
var length = data.length;
|
||||
|
||||
cl.end();
|
||||
io.server.close();
|
||||
|
||||
cl2.get('/socket.io/socket.io.js', function (res, data) {
|
||||
res.headers['content-type'].should.eql('application/javascript');
|
||||
res.headers['content-length'].should.match(/([0-9]+)/);
|
||||
should.strictEqual(res.headers.etag, undefined);
|
||||
|
||||
data.should.match(/XMLHttpRequest/);
|
||||
data.length.should.be.greaterThan(length);
|
||||
|
||||
cl2.end();
|
||||
io2.server.close();
|
||||
done();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
'test that the WebSocketMain.swf is served': function (done) {
|
||||
var port = ++ports
|
||||
, io = sio.listen(port)
|
||||
, cl = client(port);
|
||||
|
||||
cl.get('/socket.io/static/flashsocket/WebSocketMain.swf', function (res, data) {
|
||||
res.headers['content-type'].should.eql('application/x-shockwave-flash');
|
||||
res.headers['content-length'].should.match(/([0-9]+)/);
|
||||
should.strictEqual(res.headers.etag, undefined);
|
||||
|
||||
var static = sio.Manager.static
|
||||
, cache = static.cache['/static/flashsocket/WebSocketMain.swf'];
|
||||
|
||||
Buffer.isBuffer(cache.content).should.be.true;
|
||||
|
||||
cl.end();
|
||||
io.server.close();
|
||||
done();
|
||||
});
|
||||
},
|
||||
|
||||
'test that the WebSocketMainInsecure.swf is served': function (done) {
|
||||
var port = ++ports
|
||||
, io = sio.listen(port)
|
||||
, cl = client(port);
|
||||
|
||||
cl.get('/socket.io/static/flashsocket/WebSocketMainInsecure.swf', function (res, data) {
|
||||
res.headers['content-type'].should.eql('application/x-shockwave-flash');
|
||||
res.headers['content-length'].should.match(/([0-9]+)/);
|
||||
should.strictEqual(res.headers.etag, undefined);
|
||||
|
||||
var static = sio.Manager.static
|
||||
, cache = static.cache['/static/flashsocket/WebSocketMain.swf'];
|
||||
|
||||
Buffer.isBuffer(cache.content).should.be.true;
|
||||
|
||||
cl.end();
|
||||
io.server.close();
|
||||
done();
|
||||
});
|
||||
},
|
||||
|
||||
'test that you can serve custom clients': function (done) {
|
||||
var port = ++ports
|
||||
, io = sio.listen(port)
|
||||
, cl = client(port);
|
||||
|
||||
io.configure(function () {
|
||||
io.set('browser client handler', function (req, res) {
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'application/javascript'
|
||||
, 'Content-Length': 13
|
||||
, 'ETag': '1.0'
|
||||
});
|
||||
res.end('custom_client');
|
||||
});
|
||||
});
|
||||
|
||||
cl.get('/socket.io/socket.io.js', function (res, data) {
|
||||
res.headers['content-type'].should.eql('application/javascript');
|
||||
res.headers['content-length'].should.eql(13);
|
||||
res.headers.etag.should.eql('1.0');
|
||||
|
||||
data.should.eql('custom_client');
|
||||
|
||||
cl.end();
|
||||
io.server.close();
|
||||
done();
|
||||
});
|
||||
},
|
||||
|
||||
'test that you can disable clients': function (done) {
|
||||
var port = ++ports
|
||||
, io = sio.listen(port)
|
||||
@@ -557,6 +327,26 @@ module.exports = {
|
||||
});
|
||||
},
|
||||
|
||||
'test handshake cross domain access control': function (done) {
|
||||
var port = ++ports
|
||||
, io = sio.listen(port)
|
||||
, cl = client(port)
|
||||
, headers = {
|
||||
Origin: 'http://example.org:1337'
|
||||
, Cookie: 'name=value'
|
||||
};
|
||||
|
||||
cl.get('/socket.io/{protocol}/', { headers:headers }, function (res, data) {
|
||||
res.statusCode.should.eql(200);
|
||||
res.headers['access-control-allow-origin'].should.eql('*');
|
||||
res.headers['access-control-allow-credentials'].should.eql('true');
|
||||
|
||||
cl.end();
|
||||
io.server.close();
|
||||
done();
|
||||
});
|
||||
},
|
||||
|
||||
'test limiting the supported transports for a manager': function (done) {
|
||||
var port = ++ports
|
||||
, io = sio.listen(port)
|
||||
|
||||
460
test/static.test.js
Normal file
460
test/static.test.js
Normal file
@@ -0,0 +1,460 @@
|
||||
|
||||
/*!
|
||||
* socket.io-node
|
||||
* Copyright(c) 2011 LearnBoost <dev@learnboost.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Test dependencies.
|
||||
*/
|
||||
|
||||
var sio = require('socket.io')
|
||||
, should = require('./common')
|
||||
, ports = 15400;
|
||||
|
||||
/**
|
||||
* Test.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
|
||||
'test that the default static files are available': function (done) {
|
||||
var port = ++ports
|
||||
, io = sio.listen(port);
|
||||
|
||||
(!!io.static.has('/socket.io.js')).should.be.true;
|
||||
(!!io.static.has('/socket.io+')).should.be.true;
|
||||
(!!io.static.has('/static/flashsocket/WebSocketMain.swf')).should.be.true;
|
||||
(!!io.static.has('/static/flashsocket/WebSocketMainInsecure.swf')).should.be.true;
|
||||
|
||||
io.server.close();
|
||||
done();
|
||||
},
|
||||
|
||||
'test that static files are correctly looked up': function (done) {
|
||||
var port = ++ports
|
||||
, io = sio.listen(port);
|
||||
|
||||
(!!io.static.has('/socket.io.js')).should.be.true;
|
||||
(!!io.static.has('/invalidfilehereplease.js')).should.be.false;
|
||||
|
||||
io.server.close();
|
||||
done();
|
||||
},
|
||||
|
||||
'test that the client is served': function (done) {
|
||||
var port = ++ports
|
||||
, io = sio.listen(port)
|
||||
, cl = client(port);
|
||||
|
||||
cl.get('/socket.io/socket.io.js', function (res, data) {
|
||||
res.headers['content-type'].should.eql('application/javascript');
|
||||
res.headers['content-length'].should.match(/([0-9]+)/);
|
||||
should.strictEqual(res.headers.etag, undefined);
|
||||
|
||||
data.should.match(/XMLHttpRequest/);
|
||||
|
||||
cl.end();
|
||||
io.server.close();
|
||||
done();
|
||||
});
|
||||
},
|
||||
|
||||
'test that the custom build client is served': function (done) {
|
||||
var port = ++ports
|
||||
, io = sio.listen(port)
|
||||
, cl = client(port);
|
||||
|
||||
io.enable('browser client etag');
|
||||
|
||||
cl.get('/socket.io/socket.io+websocket.js', function (res, data) {
|
||||
res.headers['content-type'].should.eql('application/javascript');
|
||||
res.headers['content-length'].should.match(/([0-9]+)/);
|
||||
res.headers.etag.should.match(/([0-9]+)\.([0-9]+)\.([0-9]+)/);
|
||||
|
||||
data.should.match(/XMLHttpRequest/);
|
||||
data.should.match(/WS\.prototype\.name/);
|
||||
data.should.not.match(/Flashsocket\.prototype\.name/);
|
||||
data.should.not.match(/HTMLFile\.prototype\.name/);
|
||||
data.should.not.match(/JSONPPolling\.prototype\.name/);
|
||||
data.should.not.match(/XHRPolling\.prototype\.name/);
|
||||
|
||||
cl.end();
|
||||
io.server.close();
|
||||
done();
|
||||
});
|
||||
},
|
||||
|
||||
'test that the client is build with the enabled transports': function (done) {
|
||||
var port = ++ports
|
||||
, io = sio.listen(port)
|
||||
, cl = client(port);
|
||||
|
||||
io.set('transports', ['websocket']);
|
||||
|
||||
cl.get('/socket.io/socket.io.js', function (res, data) {
|
||||
res.headers['content-type'].should.eql('application/javascript');
|
||||
res.headers['content-length'].should.match(/([0-9]+)/);
|
||||
|
||||
data.should.match(/XMLHttpRequest/);
|
||||
data.should.match(/WS\.prototype\.name/);
|
||||
data.should.not.match(/Flashsocket\.prototype\.name/);
|
||||
data.should.not.match(/HTMLFile\.prototype\.name/);
|
||||
data.should.not.match(/JSONPPolling\.prototype\.name/);
|
||||
data.should.not.match(/XHRPolling\.prototype\.name/);
|
||||
|
||||
cl.end();
|
||||
io.server.close();
|
||||
done();
|
||||
});
|
||||
},
|
||||
|
||||
'test that the client cache is cleared when transports change': function (done) {
|
||||
var port = ++ports
|
||||
, io = sio.listen(port)
|
||||
, cl = client(port);
|
||||
|
||||
io.set('transports', ['websocket']);
|
||||
|
||||
cl.get('/socket.io/socket.io.js', function (res, data) {
|
||||
res.headers['content-type'].should.eql('application/javascript');
|
||||
res.headers['content-length'].should.match(/([0-9]+)/);
|
||||
|
||||
data.should.match(/XMLHttpRequest/);
|
||||
data.should.match(/WS\.prototype\.name/);
|
||||
data.should.not.match(/Flashsocket\.prototype\.name/);
|
||||
data.should.not.match(/HTMLFile\.prototype\.name/);
|
||||
data.should.not.match(/JSONPPolling\.prototype\.name/);
|
||||
data.should.not.match(/XHRPolling\.prototype\.name/);
|
||||
|
||||
io.set('transports', ['xhr-polling']);
|
||||
should.strictEqual(io.static.cache['/socket.io.js'], undefined);
|
||||
|
||||
cl.get('/socket.io/socket.io.js', function (res, data) {
|
||||
res.headers['content-type'].should.eql('application/javascript');
|
||||
res.headers['content-length'].should.match(/([0-9]+)/);
|
||||
|
||||
data.should.match(/XMLHttpRequest/);
|
||||
data.should.match(/XHRPolling\.prototype\.name/);
|
||||
data.should.not.match(/Flashsocket\.prototype\.name/);
|
||||
data.should.not.match(/HTMLFile\.prototype\.name/);
|
||||
data.should.not.match(/JSONPPolling\.prototype\.name/);
|
||||
data.should.not.match(/WS\.prototype\.name/);
|
||||
|
||||
cl.end();
|
||||
io.server.close();
|
||||
done();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
'test that the client etag is served': function (done) {
|
||||
var port = ++ports
|
||||
, io = sio.listen(port)
|
||||
, cl = client(port);
|
||||
|
||||
io.enable('browser client etag');
|
||||
|
||||
cl.get('/socket.io/socket.io.js', function (res, data) {
|
||||
res.headers['content-type'].should.eql('application/javascript');
|
||||
res.headers['content-length'].should.match(/([0-9]+)/);
|
||||
res.headers.etag.should.match(/([0-9]+)\.([0-9]+)\.([0-9]+)/);
|
||||
|
||||
data.should.match(/XMLHttpRequest/);
|
||||
|
||||
cl.end();
|
||||
io.server.close();
|
||||
done();
|
||||
});
|
||||
},
|
||||
|
||||
'test that the client etag is changed for new transports': function (done) {
|
||||
var port = ++ports
|
||||
, io = sio.listen(port)
|
||||
, cl = client(port);
|
||||
|
||||
io.set('transports', ['websocket']);
|
||||
io.enable('browser client etag');
|
||||
|
||||
cl.get('/socket.io/socket.io.js', function (res, data) {
|
||||
var wsEtag = res.headers.etag;
|
||||
|
||||
io.set('transports', ['xhr-polling']);
|
||||
cl.get('/socket.io/socket.io.js', function (res, data) {
|
||||
res.headers.etag.should.not.equal(wsEtag);
|
||||
|
||||
cl.end();
|
||||
io.server.close();
|
||||
done();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
'test that the client is served with gzip': function (done) {
|
||||
var port = ++ports
|
||||
, io = sio.listen(port)
|
||||
, cl = client(port);
|
||||
|
||||
io.enable('browser client gzip');
|
||||
|
||||
cl.get('/socket.io/socket.io.js', {
|
||||
headers: {
|
||||
'accept-encoding': 'deflate, gzip'
|
||||
}
|
||||
}
|
||||
, function (res, data) {
|
||||
res.headers['content-type'].should.eql('application/javascript');
|
||||
res.headers['content-encoding'].should.eql('gzip');
|
||||
res.headers['content-length'].should.match(/([0-9]+)/);
|
||||
|
||||
cl.end();
|
||||
io.server.close();
|
||||
done();
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
'test that the cached client is served': function (done) {
|
||||
var port = ++ports
|
||||
, io = sio.listen(port)
|
||||
, cl = client(port);
|
||||
|
||||
cl.get('/socket.io/socket.io.js', function (res, data) {
|
||||
res.headers['content-type'].should.eql('application/javascript');
|
||||
res.headers['content-length'].should.match(/([0-9]+)/);
|
||||
should.strictEqual(res.headers.etag, undefined);
|
||||
|
||||
data.should.match(/XMLHttpRequest/);
|
||||
var static = io.static;
|
||||
static.cache['/socket.io.js'].content.should.match(/XMLHttpRequest/);
|
||||
|
||||
cl.get('/socket.io/socket.io.js', function (res, data) {
|
||||
res.headers['content-type'].should.eql('application/javascript');
|
||||
res.headers['content-length'].should.match(/([0-9]+)/);
|
||||
should.strictEqual(res.headers.etag, undefined);
|
||||
|
||||
data.should.match(/XMLHttpRequest/);
|
||||
|
||||
cl.end();
|
||||
io.server.close();
|
||||
done();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
'test that the client is not cached': function (done) {
|
||||
var port = ++ports
|
||||
, io = sio.listen(port)
|
||||
, cl = client(port);
|
||||
|
||||
io.static.add('/random.js', function (path, callback) {
|
||||
var random = Math.floor(Date.now() * Math.random()).toString();
|
||||
callback(null, new Buffer(random));
|
||||
});
|
||||
|
||||
io.disable('browser client cache');
|
||||
|
||||
cl.get('/socket.io/random.js', function (res, data) {
|
||||
res.headers['content-type'].should.eql('application/javascript');
|
||||
res.headers['content-length'].should.match(/([0-9]+)/);
|
||||
should.strictEqual(res.headers.etag, undefined);
|
||||
|
||||
cl.get('/socket.io/random.js', function (res, random) {
|
||||
res.headers['content-type'].should.eql('application/javascript');
|
||||
res.headers['content-length'].should.match(/([0-9]+)/);
|
||||
should.strictEqual(res.headers.etag, undefined);
|
||||
|
||||
data.should.not.equal(random);
|
||||
|
||||
cl.end();
|
||||
io.server.close();
|
||||
done();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
'test that the cached client etag is served': function (done) {
|
||||
var port = ++ports
|
||||
, io = sio.listen(port)
|
||||
, cl = client(port);
|
||||
|
||||
io.enable('browser client etag');
|
||||
|
||||
cl.get('/socket.io/socket.io.js', function (res, data) {
|
||||
res.headers['content-type'].should.eql('application/javascript');
|
||||
res.headers['content-length'].should.match(/([0-9]+)/);
|
||||
res.headers.etag.should.match(/([0-9]+)\.([0-9]+)\.([0-9]+)/);
|
||||
|
||||
data.should.match(/XMLHttpRequest/);
|
||||
var static = io.static
|
||||
, cache = static.cache['/socket.io.js'];
|
||||
|
||||
cache.content.toString().should.match(/XMLHttpRequest/);
|
||||
Buffer.isBuffer(cache.content).should.be.true;
|
||||
|
||||
cl.get('/socket.io/socket.io.js', function (res, data) {
|
||||
res.headers['content-type'].should.eql('application/javascript');
|
||||
res.headers['content-length'].should.match(/([0-9]+)/);
|
||||
res.headers.etag.should.match(/([0-9]+)\.([0-9]+)\.([0-9]+)/);
|
||||
|
||||
data.should.match(/XMLHttpRequest/);
|
||||
|
||||
cl.end();
|
||||
io.server.close();
|
||||
done();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
'test that the cached client sends a 304 header': function (done) {
|
||||
var port = ++ports
|
||||
, io = sio.listen(port)
|
||||
, cl = client(port);
|
||||
|
||||
io.enable('browser client etag');
|
||||
|
||||
cl.get('/socket.io/socket.io.js', function (res, data) {
|
||||
cl.get('/socket.io/socket.io.js', {
|
||||
headers: {
|
||||
'if-none-match': res.headers.etag
|
||||
}
|
||||
}, function (res, data) {
|
||||
res.statusCode.should.eql(304);
|
||||
|
||||
cl.end();
|
||||
io.server.close();
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
'test that client minification works': function (done) {
|
||||
// server 1
|
||||
var port = ++ports
|
||||
, io = sio.listen(port)
|
||||
, cl = client(port);
|
||||
|
||||
// server 2
|
||||
var port = ++ports
|
||||
, io2 = sio.listen(port)
|
||||
, cl2 = client(port);
|
||||
|
||||
io.enable('browser client minification');
|
||||
|
||||
cl.get('/socket.io/socket.io.js', function (res, data) {
|
||||
var length = data.length;
|
||||
|
||||
cl.end();
|
||||
io.server.close();
|
||||
|
||||
cl2.get('/socket.io/socket.io.js', function (res, data) {
|
||||
res.headers['content-type'].should.eql('application/javascript');
|
||||
res.headers['content-length'].should.match(/([0-9]+)/);
|
||||
should.strictEqual(res.headers.etag, undefined);
|
||||
|
||||
data.should.match(/XMLHttpRequest/);
|
||||
data.length.should.be.greaterThan(length);
|
||||
|
||||
cl2.end();
|
||||
io2.server.close();
|
||||
done();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
'test that the WebSocketMain.swf is served': function (done) {
|
||||
var port = ++ports
|
||||
, io = sio.listen(port)
|
||||
, cl = client(port);
|
||||
|
||||
cl.get('/socket.io/static/flashsocket/WebSocketMain.swf', function (res, data) {
|
||||
res.headers['content-type'].should.eql('application/x-shockwave-flash');
|
||||
res.headers['content-length'].should.match(/([0-9]+)/);
|
||||
should.strictEqual(res.headers.etag, undefined);
|
||||
|
||||
var static = io.static
|
||||
, cache = static.cache['/static/flashsocket/WebSocketMain.swf'];
|
||||
|
||||
Buffer.isBuffer(cache.content).should.be.true;
|
||||
|
||||
cl.end();
|
||||
io.server.close();
|
||||
done();
|
||||
});
|
||||
},
|
||||
|
||||
'test that the WebSocketMainInsecure.swf is served': function (done) {
|
||||
var port = ++ports
|
||||
, io = sio.listen(port)
|
||||
, cl = client(port);
|
||||
|
||||
cl.get('/socket.io/static/flashsocket/WebSocketMainInsecure.swf', function (res, data) {
|
||||
res.headers['content-type'].should.eql('application/x-shockwave-flash');
|
||||
res.headers['content-length'].should.match(/([0-9]+)/);
|
||||
should.strictEqual(res.headers.etag, undefined);
|
||||
|
||||
var static = io.static
|
||||
, cache = static.cache['/static/flashsocket/WebSocketMainInsecure.swf'];
|
||||
|
||||
Buffer.isBuffer(cache.content).should.be.true;
|
||||
|
||||
cl.end();
|
||||
io.server.close();
|
||||
done();
|
||||
});
|
||||
},
|
||||
|
||||
'test that swf files are not served with gzip': function (done) {
|
||||
var port = ++ports
|
||||
, io = sio.listen(port)
|
||||
, cl = client(port);
|
||||
|
||||
io.enable('browser client gzip');
|
||||
|
||||
cl.get('/socket.io/static/flashsocket/WebSocketMain.swf', {
|
||||
headers: {
|
||||
'accept-encoding': 'deflate, gzip'
|
||||
}
|
||||
}
|
||||
, function (res, data) {
|
||||
res.headers['content-type'].should.eql('application/x-shockwave-flash');
|
||||
res.headers['content-length'].should.match(/([0-9]+)/);
|
||||
should.strictEqual(res.headers['content-encoding'], undefined);
|
||||
|
||||
cl.end();
|
||||
io.server.close();
|
||||
done();
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
'test that you can serve custom clients': function (done) {
|
||||
var port = ++ports
|
||||
, io = sio.listen(port)
|
||||
, cl = client(port);
|
||||
|
||||
io.set('browser client handler', function (req, res) {
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'application/javascript'
|
||||
, 'Content-Length': 13
|
||||
, 'ETag': '1.0'
|
||||
});
|
||||
res.end('custom_client');
|
||||
});
|
||||
|
||||
cl.get('/socket.io/socket.io.js', function (res, data) {
|
||||
res.headers['content-type'].should.eql('application/javascript');
|
||||
res.headers['content-length'].should.eql(13);
|
||||
res.headers.etag.should.eql('1.0');
|
||||
|
||||
data.should.eql('custom_client');
|
||||
|
||||
cl.end();
|
||||
io.server.close();
|
||||
done();
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
@@ -163,6 +163,20 @@ module.exports = {
|
||||
|
||||
io.flashPolicyServer.close();
|
||||
done();
|
||||
}
|
||||
},
|
||||
|
||||
'flashsocket identifies as flashsocket': function (done) {
|
||||
var cl = client(++ports)
|
||||
, io = create(cl)
|
||||
, messages = 0
|
||||
, ws;
|
||||
io.set('transports', ['flashsocket']);
|
||||
io.sockets.on('connection', function (socket) {
|
||||
socket.manager.transports[socket.id].name.should.equal('flashsocket');
|
||||
done();
|
||||
});
|
||||
cl.handshake(function (sid) {
|
||||
ws = websocket(cl, sid, 'flashsocket');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -4,84 +4,7 @@
|
||||
|
||||
var assert = require('assert');
|
||||
var Parser = require('../lib/transports/websocket/hybi-07-12.js').Parser;
|
||||
|
||||
/**
|
||||
* Returns a Buffer from a "ff 00 ff"-type hex string.
|
||||
*/
|
||||
|
||||
function makeBufferFromHexString(byteStr) {
|
||||
var bytes = byteStr.split(' ');
|
||||
var buf = new Buffer(bytes.length);
|
||||
for (var i = 0; i < bytes.length; ++i) {
|
||||
buf[i] = parseInt(bytes[i], 16);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits a buffer in two parts.
|
||||
*/
|
||||
|
||||
function splitBuffer(buffer) {
|
||||
var b1 = new Buffer(Math.ceil(buffer.length / 2));
|
||||
buffer.copy(b1, 0, 0, b1.length);
|
||||
var b2 = new Buffer(Math.floor(buffer.length / 2));
|
||||
buffer.copy(b2, 0, b1.length, b1.length + b2.length);
|
||||
return [b1, b2];
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs hybi07+ type masking on a hex string.
|
||||
*/
|
||||
|
||||
function mask(str, maskString) {
|
||||
var buf = new Buffer(str);
|
||||
var mask = makeBufferFromHexString(maskString || '34 83 a8 68');
|
||||
for (var i = 0; i < buf.length; ++i) {
|
||||
buf[i] ^= mask[i % 4];
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpacks a Buffer into a number.
|
||||
*/
|
||||
|
||||
function unpack(buffer) {
|
||||
var n = 0;
|
||||
for (var i = 0; i < buffer.length; ++i) {
|
||||
n = (i == 0) ? buffer[i] : (n * 256) + buffer[i];
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a hex string, representing a specific byte count 'length', from a number.
|
||||
*/
|
||||
|
||||
function pack(length, number) {
|
||||
return padl(number.toString(16), length, '0').replace(/(\d\d)/g, '$1 ').trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Left pads the string 's' to a total length of 'n' with char 'c'.
|
||||
*/
|
||||
|
||||
function padl(s, n, c) {
|
||||
return new Array(1 + n - s.length).join(c) + s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a hex string from a Buffer.
|
||||
*/
|
||||
|
||||
function dump(data) {
|
||||
var s = '';
|
||||
for (var i = 0; i < data.length; ++i) {
|
||||
s += padl(data[i].toString(16), 2, '0') + ' ';
|
||||
}
|
||||
return s.trim();
|
||||
}
|
||||
require('./hybi-common');
|
||||
|
||||
/**
|
||||
* Tests.
|
||||
@@ -98,7 +21,7 @@ module.exports = {
|
||||
assert.equal('Hello', data);
|
||||
});
|
||||
|
||||
p.add(makeBufferFromHexString(packet));
|
||||
p.add(getBufferFromHexString(packet));
|
||||
assert.ok(gotData);
|
||||
},
|
||||
'can parse close message': function() {
|
||||
@@ -110,7 +33,7 @@ module.exports = {
|
||||
gotClose = true;
|
||||
});
|
||||
|
||||
p.add(makeBufferFromHexString(packet));
|
||||
p.add(getBufferFromHexString(packet));
|
||||
assert.ok(gotClose);
|
||||
},
|
||||
'can parse masked text message': function() {
|
||||
@@ -123,14 +46,14 @@ module.exports = {
|
||||
assert.equal('5:::{"name":"echo"}', data);
|
||||
});
|
||||
|
||||
p.add(makeBufferFromHexString(packet));
|
||||
p.add(getBufferFromHexString(packet));
|
||||
assert.ok(gotData);
|
||||
},
|
||||
'can parse a masked text message longer than 125 bytes': function() {
|
||||
var p = new Parser();
|
||||
var message = 'A';
|
||||
for (var i = 0; i < 300; ++i) message += (i % 5).toString();
|
||||
var packet = '81 FE ' + pack(4, message.length) + ' 34 83 a8 68 ' + dump(mask(message, '34 83 a8 68'));
|
||||
var packet = '81 FE ' + pack(4, message.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68'));
|
||||
|
||||
var gotData = false;
|
||||
p.on('data', function(data) {
|
||||
@@ -138,14 +61,14 @@ module.exports = {
|
||||
assert.equal(message, data);
|
||||
});
|
||||
|
||||
p.add(makeBufferFromHexString(packet));
|
||||
p.add(getBufferFromHexString(packet));
|
||||
assert.ok(gotData);
|
||||
},
|
||||
'can parse a really long masked text message': function() {
|
||||
var p = new Parser();
|
||||
var message = 'A';
|
||||
for (var i = 0; i < 64*1024; ++i) message += (i % 5).toString();
|
||||
var packet = '81 FF ' + pack(16, message.length) + ' 34 83 a8 68 ' + dump(mask(message, '34 83 a8 68'));
|
||||
var packet = '81 FF ' + pack(16, message.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68'));
|
||||
|
||||
var gotData = false;
|
||||
p.on('data', function(data) {
|
||||
@@ -153,7 +76,7 @@ module.exports = {
|
||||
assert.equal(message, data);
|
||||
});
|
||||
|
||||
p.add(makeBufferFromHexString(packet));
|
||||
p.add(getBufferFromHexString(packet));
|
||||
assert.ok(gotData);
|
||||
},
|
||||
'can parse a fragmented masked text message of 300 bytes': function() {
|
||||
@@ -162,8 +85,8 @@ module.exports = {
|
||||
for (var i = 0; i < 300; ++i) message += (i % 5).toString();
|
||||
var msgpiece1 = message.substr(0, 150);
|
||||
var msgpiece2 = message.substr(150);
|
||||
var packet1 = '01 FE ' + pack(4, msgpiece1.length) + ' 34 83 a8 68 ' + dump(mask(msgpiece1, '34 83 a8 68'));
|
||||
var packet2 = '81 FE ' + pack(4, msgpiece2.length) + ' 34 83 a8 68 ' + dump(mask(msgpiece2, '34 83 a8 68'));
|
||||
var packet1 = '01 FE ' + pack(4, msgpiece1.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece1, '34 83 a8 68'));
|
||||
var packet2 = '80 FE ' + pack(4, msgpiece2.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece2, '34 83 a8 68'));
|
||||
|
||||
var gotData = false;
|
||||
p.on('data', function(data) {
|
||||
@@ -171,14 +94,14 @@ module.exports = {
|
||||
assert.equal(message, data);
|
||||
});
|
||||
|
||||
p.add(makeBufferFromHexString(packet1));
|
||||
p.add(makeBufferFromHexString(packet2));
|
||||
p.add(getBufferFromHexString(packet1));
|
||||
p.add(getBufferFromHexString(packet2));
|
||||
assert.ok(gotData);
|
||||
},
|
||||
'can parse a ping message': function() {
|
||||
var p = new Parser();
|
||||
var message = 'Hello';
|
||||
var packet = '89 FE ' + pack(4, message.length) + ' 34 83 a8 68 ' + dump(mask(message, '34 83 a8 68'));
|
||||
var packet = '89 FE ' + pack(4, message.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68'));
|
||||
|
||||
var gotPing = false;
|
||||
p.on('ping', function(data) {
|
||||
@@ -186,7 +109,7 @@ module.exports = {
|
||||
assert.equal(message, data);
|
||||
});
|
||||
|
||||
p.add(makeBufferFromHexString(packet));
|
||||
p.add(getBufferFromHexString(packet));
|
||||
assert.ok(gotPing);
|
||||
},
|
||||
'can parse a ping with no data': function() {
|
||||
@@ -198,7 +121,7 @@ module.exports = {
|
||||
gotPing = true;
|
||||
});
|
||||
|
||||
p.add(makeBufferFromHexString(packet));
|
||||
p.add(getBufferFromHexString(packet));
|
||||
assert.ok(gotPing);
|
||||
},
|
||||
'can parse a fragmented masked text message of 300 bytes with a ping in the middle': function() {
|
||||
@@ -207,13 +130,13 @@ module.exports = {
|
||||
for (var i = 0; i < 300; ++i) message += (i % 5).toString();
|
||||
|
||||
var msgpiece1 = message.substr(0, 150);
|
||||
var packet1 = '01 FE ' + pack(4, msgpiece1.length) + ' 34 83 a8 68 ' + dump(mask(msgpiece1, '34 83 a8 68'));
|
||||
var packet1 = '01 FE ' + pack(4, msgpiece1.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece1, '34 83 a8 68'));
|
||||
|
||||
var pingMessage = 'Hello';
|
||||
var pingPacket = '89 FE ' + pack(4, pingMessage.length) + ' 34 83 a8 68 ' + dump(mask(pingMessage, '34 83 a8 68'));
|
||||
var pingPacket = '89 FE ' + pack(4, pingMessage.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(pingMessage, '34 83 a8 68'));
|
||||
|
||||
var msgpiece2 = message.substr(150);
|
||||
var packet2 = '81 FE ' + pack(4, msgpiece2.length) + ' 34 83 a8 68 ' + dump(mask(msgpiece2, '34 83 a8 68'));
|
||||
var packet2 = '80 FE ' + pack(4, msgpiece2.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece2, '34 83 a8 68'));
|
||||
|
||||
var gotData = false;
|
||||
p.on('data', function(data) {
|
||||
@@ -226,9 +149,9 @@ module.exports = {
|
||||
assert.equal(pingMessage, data);
|
||||
});
|
||||
|
||||
p.add(makeBufferFromHexString(packet1));
|
||||
p.add(makeBufferFromHexString(pingPacket));
|
||||
p.add(makeBufferFromHexString(packet2));
|
||||
p.add(getBufferFromHexString(packet1));
|
||||
p.add(getBufferFromHexString(pingPacket));
|
||||
p.add(getBufferFromHexString(packet2));
|
||||
assert.ok(gotData);
|
||||
assert.ok(gotPing);
|
||||
},
|
||||
@@ -238,13 +161,13 @@ module.exports = {
|
||||
for (var i = 0; i < 300; ++i) message += (i % 5).toString();
|
||||
|
||||
var msgpiece1 = message.substr(0, 150);
|
||||
var packet1 = '01 FE ' + pack(4, msgpiece1.length) + ' 34 83 a8 68 ' + dump(mask(msgpiece1, '34 83 a8 68'));
|
||||
var packet1 = '01 FE ' + pack(4, msgpiece1.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece1, '34 83 a8 68'));
|
||||
|
||||
var pingMessage = 'Hello';
|
||||
var pingPacket = '89 FE ' + pack(4, pingMessage.length) + ' 34 83 a8 68 ' + dump(mask(pingMessage, '34 83 a8 68'));
|
||||
var pingPacket = '89 FE ' + pack(4, pingMessage.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(pingMessage, '34 83 a8 68'));
|
||||
|
||||
var msgpiece2 = message.substr(150);
|
||||
var packet2 = '81 FE ' + pack(4, msgpiece2.length) + ' 34 83 a8 68 ' + dump(mask(msgpiece2, '34 83 a8 68'));
|
||||
var packet2 = '80 FE ' + pack(4, msgpiece2.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece2, '34 83 a8 68'));
|
||||
|
||||
var gotData = false;
|
||||
p.on('data', function(data) {
|
||||
@@ -258,14 +181,82 @@ module.exports = {
|
||||
});
|
||||
|
||||
var buffers = [];
|
||||
buffers = buffers.concat(splitBuffer(makeBufferFromHexString(packet1)));
|
||||
buffers = buffers.concat(splitBuffer(makeBufferFromHexString(pingPacket)));
|
||||
buffers = buffers.concat(splitBuffer(makeBufferFromHexString(packet2)));
|
||||
buffers = buffers.concat(splitBuffer(getBufferFromHexString(packet1)));
|
||||
buffers = buffers.concat(splitBuffer(getBufferFromHexString(pingPacket)));
|
||||
buffers = buffers.concat(splitBuffer(getBufferFromHexString(packet2)));
|
||||
for (var i = 0; i < buffers.length; ++i) {
|
||||
p.add(buffers[i]);
|
||||
}
|
||||
assert.ok(gotData);
|
||||
assert.ok(gotPing);
|
||||
},
|
||||
'can parse a 100 byte long masked binary message': function() {
|
||||
var p = new Parser();
|
||||
var length = 100;
|
||||
var message = new Buffer(length);
|
||||
for (var i = 0; i < length; ++i) message[i] = i % 256;
|
||||
var originalMessage = getHexStringFromBuffer(message);
|
||||
var packet = '82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68'));
|
||||
|
||||
var gotData = false;
|
||||
p.on('binary', function(data) {
|
||||
gotData = true;
|
||||
assert.equal(originalMessage, getHexStringFromBuffer(data));
|
||||
});
|
||||
|
||||
p.add(getBufferFromHexString(packet));
|
||||
assert.ok(gotData);
|
||||
},
|
||||
'can parse a 256 byte long masked binary message': function() {
|
||||
var p = new Parser();
|
||||
var length = 256;
|
||||
var message = new Buffer(length);
|
||||
for (var i = 0; i < length; ++i) message[i] = i % 256;
|
||||
var originalMessage = getHexStringFromBuffer(message);
|
||||
var packet = '82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68'));
|
||||
|
||||
var gotData = false;
|
||||
p.on('binary', function(data) {
|
||||
gotData = true;
|
||||
assert.equal(originalMessage, getHexStringFromBuffer(data));
|
||||
});
|
||||
|
||||
p.add(getBufferFromHexString(packet));
|
||||
assert.ok(gotData);
|
||||
},
|
||||
'can parse a 200kb long masked binary message': function() {
|
||||
var p = new Parser();
|
||||
var length = 200 * 1024;
|
||||
var message = new Buffer(length);
|
||||
for (var i = 0; i < length; ++i) message[i] = i % 256;
|
||||
var originalMessage = getHexStringFromBuffer(message);
|
||||
var packet = '82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68'));
|
||||
|
||||
var gotData = false;
|
||||
p.on('binary', function(data) {
|
||||
gotData = true;
|
||||
assert.equal(originalMessage, getHexStringFromBuffer(data));
|
||||
});
|
||||
|
||||
p.add(getBufferFromHexString(packet));
|
||||
assert.ok(gotData);
|
||||
},
|
||||
'can parse a 200kb long unmasked binary message': function() {
|
||||
var p = new Parser();
|
||||
var length = 200 * 1024;
|
||||
var message = new Buffer(length);
|
||||
for (var i = 0; i < length; ++i) message[i] = i % 256;
|
||||
var originalMessage = getHexStringFromBuffer(message);
|
||||
var packet = '82 ' + getHybiLengthAsHexString(length, false) + ' ' + getHexStringFromBuffer(message);
|
||||
|
||||
var gotData = false;
|
||||
p.on('binary', function(data) {
|
||||
gotData = true;
|
||||
assert.equal(originalMessage, getHexStringFromBuffer(data));
|
||||
});
|
||||
|
||||
p.add(getBufferFromHexString(packet));
|
||||
assert.ok(gotData);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
262
test/transports.websocket.hybi16.parser.test.js
Normal file
262
test/transports.websocket.hybi16.parser.test.js
Normal file
@@ -0,0 +1,262 @@
|
||||
/**
|
||||
* Test dependencies.
|
||||
*/
|
||||
|
||||
var assert = require('assert');
|
||||
var Parser = require('../lib/transports/websocket/hybi-16.js').Parser;
|
||||
require('./hybi-common');
|
||||
|
||||
/**
|
||||
* Tests.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
'can parse unmasked text message': function() {
|
||||
var p = new Parser();
|
||||
var packet = '81 05 48 65 6c 6c 6f';
|
||||
|
||||
var gotData = false;
|
||||
p.on('data', function(data) {
|
||||
gotData = true;
|
||||
assert.equal('Hello', data);
|
||||
});
|
||||
|
||||
p.add(getBufferFromHexString(packet));
|
||||
assert.ok(gotData);
|
||||
},
|
||||
'can parse close message': function() {
|
||||
var p = new Parser();
|
||||
var packet = '88 00';
|
||||
|
||||
var gotClose = false;
|
||||
p.on('close', function(data) {
|
||||
gotClose = true;
|
||||
});
|
||||
|
||||
p.add(getBufferFromHexString(packet));
|
||||
assert.ok(gotClose);
|
||||
},
|
||||
'can parse masked text message': function() {
|
||||
var p = new Parser();
|
||||
var packet = '81 93 34 83 a8 68 01 b9 92 52 4f a1 c6 09 59 e6 8a 52 16 e6 cb 00 5b a1 d5';
|
||||
|
||||
var gotData = false;
|
||||
p.on('data', function(data) {
|
||||
gotData = true;
|
||||
assert.equal('5:::{"name":"echo"}', data);
|
||||
});
|
||||
|
||||
p.add(getBufferFromHexString(packet));
|
||||
assert.ok(gotData);
|
||||
},
|
||||
'can parse a masked text message longer than 125 bytes': function() {
|
||||
var p = new Parser();
|
||||
var message = 'A';
|
||||
for (var i = 0; i < 300; ++i) message += (i % 5).toString();
|
||||
var packet = '81 FE ' + pack(4, message.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68'));
|
||||
|
||||
var gotData = false;
|
||||
p.on('data', function(data) {
|
||||
gotData = true;
|
||||
assert.equal(message, data);
|
||||
});
|
||||
|
||||
p.add(getBufferFromHexString(packet));
|
||||
assert.ok(gotData);
|
||||
},
|
||||
'can parse a really long masked text message': function() {
|
||||
var p = new Parser();
|
||||
var message = 'A';
|
||||
for (var i = 0; i < 64*1024; ++i) message += (i % 5).toString();
|
||||
var packet = '81 FF ' + pack(16, message.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68'));
|
||||
|
||||
var gotData = false;
|
||||
p.on('data', function(data) {
|
||||
gotData = true;
|
||||
assert.equal(message, data);
|
||||
});
|
||||
|
||||
p.add(getBufferFromHexString(packet));
|
||||
assert.ok(gotData);
|
||||
},
|
||||
'can parse a fragmented masked text message of 300 bytes': function() {
|
||||
var p = new Parser();
|
||||
var message = 'A';
|
||||
for (var i = 0; i < 300; ++i) message += (i % 5).toString();
|
||||
var msgpiece1 = message.substr(0, 150);
|
||||
var msgpiece2 = message.substr(150);
|
||||
var packet1 = '01 FE ' + pack(4, msgpiece1.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece1, '34 83 a8 68'));
|
||||
var packet2 = '80 FE ' + pack(4, msgpiece2.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece2, '34 83 a8 68'));
|
||||
|
||||
var gotData = false;
|
||||
p.on('data', function(data) {
|
||||
gotData = true;
|
||||
assert.equal(message, data);
|
||||
});
|
||||
|
||||
p.add(getBufferFromHexString(packet1));
|
||||
p.add(getBufferFromHexString(packet2));
|
||||
assert.ok(gotData);
|
||||
},
|
||||
'can parse a ping message': function() {
|
||||
var p = new Parser();
|
||||
var message = 'Hello';
|
||||
var packet = '89 FE ' + pack(4, message.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68'));
|
||||
|
||||
var gotPing = false;
|
||||
p.on('ping', function(data) {
|
||||
gotPing = true;
|
||||
assert.equal(message, data);
|
||||
});
|
||||
|
||||
p.add(getBufferFromHexString(packet));
|
||||
assert.ok(gotPing);
|
||||
},
|
||||
'can parse a ping with no data': function() {
|
||||
var p = new Parser();
|
||||
var packet = '89 00';
|
||||
|
||||
var gotPing = false;
|
||||
p.on('ping', function(data) {
|
||||
gotPing = true;
|
||||
});
|
||||
|
||||
p.add(getBufferFromHexString(packet));
|
||||
assert.ok(gotPing);
|
||||
},
|
||||
'can parse a fragmented masked text message of 300 bytes with a ping in the middle': function() {
|
||||
var p = new Parser();
|
||||
var message = 'A';
|
||||
for (var i = 0; i < 300; ++i) message += (i % 5).toString();
|
||||
|
||||
var msgpiece1 = message.substr(0, 150);
|
||||
var packet1 = '01 FE ' + pack(4, msgpiece1.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece1, '34 83 a8 68'));
|
||||
|
||||
var pingMessage = 'Hello';
|
||||
var pingPacket = '89 FE ' + pack(4, pingMessage.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(pingMessage, '34 83 a8 68'));
|
||||
|
||||
var msgpiece2 = message.substr(150);
|
||||
var packet2 = '80 FE ' + pack(4, msgpiece2.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece2, '34 83 a8 68'));
|
||||
|
||||
var gotData = false;
|
||||
p.on('data', function(data) {
|
||||
gotData = true;
|
||||
assert.equal(message, data);
|
||||
});
|
||||
var gotPing = false;
|
||||
p.on('ping', function(data) {
|
||||
gotPing = true;
|
||||
assert.equal(pingMessage, data);
|
||||
});
|
||||
|
||||
p.add(getBufferFromHexString(packet1));
|
||||
p.add(getBufferFromHexString(pingPacket));
|
||||
p.add(getBufferFromHexString(packet2));
|
||||
assert.ok(gotData);
|
||||
assert.ok(gotPing);
|
||||
},
|
||||
'can parse a fragmented masked text message of 300 bytes with a ping in the middle, which is delievered over sevaral tcp packets': function() {
|
||||
var p = new Parser();
|
||||
var message = 'A';
|
||||
for (var i = 0; i < 300; ++i) message += (i % 5).toString();
|
||||
|
||||
var msgpiece1 = message.substr(0, 150);
|
||||
var packet1 = '01 FE ' + pack(4, msgpiece1.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece1, '34 83 a8 68'));
|
||||
|
||||
var pingMessage = 'Hello';
|
||||
var pingPacket = '89 FE ' + pack(4, pingMessage.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(pingMessage, '34 83 a8 68'));
|
||||
|
||||
var msgpiece2 = message.substr(150);
|
||||
var packet2 = '80 FE ' + pack(4, msgpiece2.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece2, '34 83 a8 68'));
|
||||
|
||||
var gotData = false;
|
||||
p.on('data', function(data) {
|
||||
gotData = true;
|
||||
assert.equal(message, data);
|
||||
});
|
||||
var gotPing = false;
|
||||
p.on('ping', function(data) {
|
||||
gotPing = true;
|
||||
assert.equal(pingMessage, data);
|
||||
});
|
||||
|
||||
var buffers = [];
|
||||
buffers = buffers.concat(splitBuffer(getBufferFromHexString(packet1)));
|
||||
buffers = buffers.concat(splitBuffer(getBufferFromHexString(pingPacket)));
|
||||
buffers = buffers.concat(splitBuffer(getBufferFromHexString(packet2)));
|
||||
for (var i = 0; i < buffers.length; ++i) {
|
||||
p.add(buffers[i]);
|
||||
}
|
||||
assert.ok(gotData);
|
||||
assert.ok(gotPing);
|
||||
},
|
||||
'can parse a 100 byte long masked binary message': function() {
|
||||
var p = new Parser();
|
||||
var length = 100;
|
||||
var message = new Buffer(length);
|
||||
for (var i = 0; i < length; ++i) message[i] = i % 256;
|
||||
var originalMessage = getHexStringFromBuffer(message);
|
||||
var packet = '82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68'));
|
||||
|
||||
var gotData = false;
|
||||
p.on('binary', function(data) {
|
||||
gotData = true;
|
||||
assert.equal(originalMessage, getHexStringFromBuffer(data));
|
||||
});
|
||||
|
||||
p.add(getBufferFromHexString(packet));
|
||||
assert.ok(gotData);
|
||||
},
|
||||
'can parse a 256 byte long masked binary message': function() {
|
||||
var p = new Parser();
|
||||
var length = 256;
|
||||
var message = new Buffer(length);
|
||||
for (var i = 0; i < length; ++i) message[i] = i % 256;
|
||||
var originalMessage = getHexStringFromBuffer(message);
|
||||
var packet = '82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68'));
|
||||
|
||||
var gotData = false;
|
||||
p.on('binary', function(data) {
|
||||
gotData = true;
|
||||
assert.equal(originalMessage, getHexStringFromBuffer(data));
|
||||
});
|
||||
|
||||
p.add(getBufferFromHexString(packet));
|
||||
assert.ok(gotData);
|
||||
},
|
||||
'can parse a 200kb long masked binary message': function() {
|
||||
var p = new Parser();
|
||||
var length = 200 * 1024;
|
||||
var message = new Buffer(length);
|
||||
for (var i = 0; i < length; ++i) message[i] = i % 256;
|
||||
var originalMessage = getHexStringFromBuffer(message);
|
||||
var packet = '82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68'));
|
||||
|
||||
var gotData = false;
|
||||
p.on('binary', function(data) {
|
||||
gotData = true;
|
||||
assert.equal(originalMessage, getHexStringFromBuffer(data));
|
||||
});
|
||||
|
||||
p.add(getBufferFromHexString(packet));
|
||||
assert.ok(gotData);
|
||||
},
|
||||
'can parse a 200kb long unmasked binary message': function() {
|
||||
var p = new Parser();
|
||||
var length = 200 * 1024;
|
||||
var message = new Buffer(length);
|
||||
for (var i = 0; i < length; ++i) message[i] = i % 256;
|
||||
var originalMessage = getHexStringFromBuffer(message);
|
||||
var packet = '82 ' + getHybiLengthAsHexString(length, false) + ' ' + getHexStringFromBuffer(message);
|
||||
|
||||
var gotData = false;
|
||||
p.on('binary', function(data) {
|
||||
gotData = true;
|
||||
assert.equal(originalMessage, getHexStringFromBuffer(data));
|
||||
});
|
||||
|
||||
p.add(getBufferFromHexString(packet));
|
||||
assert.ok(gotData);
|
||||
},
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user