socket.io
Advanced tools
Comparing version 0.7.11 to 0.8.0
0.8.0 / 2011-08-28 | ||
================== | ||
* Updated to work with two-level websocket versioning. [einaros] | ||
* Added hybi07 support. [einaros] | ||
* Added hybi10 support. [einaros] | ||
* Added http referrer verification to manager.js verifyOrigin. [einaors] | ||
0.7.11 / 2011-08-27 | ||
@@ -3,0 +11,0 @@ =================== |
@@ -869,3 +869,3 @@ /*! | ||
Manager.prototype.verifyOrigin = function (request) { | ||
var origin = request.headers.origin | ||
var origin = request.headers.origin || request.headers.referer | ||
, origins = this.get('origins'); | ||
@@ -882,10 +882,15 @@ | ||
var parts = url.parse(origin); | ||
return | ||
~origins.indexOf(parts.host + ':' + parts.port) || | ||
~origins.indexOf(parts.host + ':*') || | ||
var ok = | ||
~origins.indexOf(parts.hostname + ':' + parts.port) || | ||
~origins.indexOf(parts.hostname + ':*') || | ||
~origins.indexOf('*:' + parts.port); | ||
} catch (ex) {} | ||
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 handshake, yet required by config'); | ||
} | ||
return false; | ||
@@ -892,0 +897,0 @@ }; |
@@ -18,3 +18,3 @@ | ||
exports.version = '0.7.11'; | ||
exports.version = '0.8.0'; | ||
@@ -21,0 +21,0 @@ /** |
@@ -27,3 +27,3 @@ | ||
function FlashSocket (mng, data, req) { | ||
WebSocket.call(this, mng, data, req); | ||
return WebSocket.call(this, mng, data, req); | ||
} | ||
@@ -30,0 +30,0 @@ |
@@ -12,6 +12,3 @@ | ||
var Transport = require('../transport') | ||
, EventEmitter = process.EventEmitter | ||
, crypto = require('crypto') | ||
, parser = require('../parser'); | ||
var protocolVersions = require('./websocket/'); | ||
@@ -32,321 +29,7 @@ /** | ||
function WebSocket (mng, data, req) { | ||
// parser | ||
var self = this; | ||
this.parser = new Parser(); | ||
this.parser.on('data', function (packet) { | ||
self.log.debug(self.name + ' received data packet', packet); | ||
self.onMessage(parser.decodePacket(packet)); | ||
}); | ||
this.parser.on('close', function () { | ||
self.end(); | ||
}); | ||
this.parser.on('error', function () { | ||
self.end(); | ||
}); | ||
Transport.call(this, mng, data, req); | ||
}; | ||
/** | ||
* Inherits from Transport. | ||
*/ | ||
WebSocket.prototype.__proto__ = Transport.prototype; | ||
/** | ||
* Transport name | ||
* | ||
* @api public | ||
*/ | ||
WebSocket.prototype.name = 'websocket'; | ||
/** | ||
* Called when the socket connects. | ||
* | ||
* @api private | ||
*/ | ||
WebSocket.prototype.onSocketConnect = function () { | ||
var self = this; | ||
this.socket.setNoDelay(true); | ||
this.buffer = true; | ||
this.buffered = []; | ||
if (this.req.headers.upgrade !== 'WebSocket') { | ||
this.log.warn(this.name + ' connection invalid'); | ||
this.end(); | ||
return; | ||
var version = req.headers['sec-websocket-version']; | ||
if (typeof version !== 'undefined' && typeof protocolVersions[version] !== 'undefined') { | ||
return new protocolVersions[version](mng, data, req); | ||
} | ||
var origin = this.req.headers.origin | ||
, location = (this.socket.encrypted ? 'wss' : 'ws') | ||
+ '://' + this.req.headers.host + this.req.url | ||
, waitingForNonce = false; | ||
if (this.req.headers['sec-websocket-key1']) { | ||
// If we don't have the nonce yet, wait for it (HAProxy compatibility). | ||
if (! (this.req.head && this.req.head.length >= 8)) { | ||
waitingForNonce = true; | ||
} | ||
var headers = [ | ||
'HTTP/1.1 101 WebSocket Protocol Handshake' | ||
, 'Upgrade: WebSocket' | ||
, 'Connection: Upgrade' | ||
, 'Sec-WebSocket-Origin: ' + origin | ||
, 'Sec-WebSocket-Location: ' + location | ||
]; | ||
if (this.req.headers['sec-websocket-protocol']){ | ||
headers.push('Sec-WebSocket-Protocol: ' | ||
+ this.req.headers['sec-websocket-protocol']); | ||
} | ||
} else { | ||
var headers = [ | ||
'HTTP/1.1 101 Web Socket Protocol Handshake' | ||
, 'Upgrade: WebSocket' | ||
, 'Connection: Upgrade' | ||
, 'WebSocket-Origin: ' + origin | ||
, 'WebSocket-Location: ' + location | ||
]; | ||
} | ||
try { | ||
this.socket.write(headers.concat('', '').join('\r\n')); | ||
this.socket.setTimeout(0); | ||
this.socket.setNoDelay(true); | ||
this.socket.setEncoding('utf8'); | ||
} catch (e) { | ||
this.end(); | ||
return; | ||
} | ||
if (waitingForNonce) { | ||
this.socket.setEncoding('binary'); | ||
} else if (this.proveReception(headers)) { | ||
self.flush(); | ||
} | ||
var headBuffer = ''; | ||
this.socket.on('data', function (data) { | ||
if (waitingForNonce) { | ||
headBuffer += data; | ||
if (headBuffer.length < 8) { | ||
return; | ||
} | ||
// Restore the connection to utf8 encoding after receiving the nonce | ||
self.socket.setEncoding('utf8'); | ||
waitingForNonce = false; | ||
// Stuff the nonce into the location where it's expected to be | ||
self.req.head = headBuffer.substr(0, 8); | ||
headBuffer = ''; | ||
if (self.proveReception(headers)) { | ||
self.flush(); | ||
} | ||
return; | ||
} | ||
self.parser.add(data); | ||
}); | ||
return new protocolVersions['default'](mng, data, req); | ||
}; | ||
/** | ||
* Writes to the socket. | ||
* | ||
* @api private | ||
*/ | ||
WebSocket.prototype.write = function (data) { | ||
if (this.open) { | ||
this.drained = false; | ||
if (this.buffer) { | ||
this.buffered.push(data); | ||
return this; | ||
} | ||
var length = Buffer.byteLength(data) | ||
, buffer = new Buffer(2 + length); | ||
buffer.write('\u0000', 'binary'); | ||
buffer.write(data, 1, 'utf8'); | ||
buffer.write('\uffff', 1 + length, 'binary'); | ||
try { | ||
if (this.socket.write(buffer)) { | ||
this.drained = true; | ||
} | ||
} catch (e) { | ||
this.end(); | ||
} | ||
this.log.debug(this.name + ' writing', data); | ||
} | ||
}; | ||
/** | ||
* Flushes the internal buffer | ||
* | ||
* @api private | ||
*/ | ||
WebSocket.prototype.flush = function () { | ||
this.buffer = false; | ||
for (var i = 0, l = this.buffered.length; i < l; i++) { | ||
this.write(this.buffered.splice(0, 1)[0]); | ||
} | ||
}; | ||
/** | ||
* Finishes the handshake. | ||
* | ||
* @api private | ||
*/ | ||
WebSocket.prototype.proveReception = function (headers) { | ||
var self = this | ||
, k1 = this.req.headers['sec-websocket-key1'] | ||
, k2 = this.req.headers['sec-websocket-key2']; | ||
if (k1 && k2){ | ||
var md5 = crypto.createHash('md5'); | ||
[k1, k2].forEach(function (k) { | ||
var n = parseInt(k.replace(/[^\d]/g, '')) | ||
, spaces = k.replace(/[^ ]/g, '').length; | ||
if (spaces === 0 || n % spaces !== 0){ | ||
self.log.warn('Invalid ' + self.name + ' key: "' + k + '".'); | ||
self.end(); | ||
return false; | ||
} | ||
n /= spaces; | ||
md5.update(String.fromCharCode( | ||
n >> 24 & 0xFF, | ||
n >> 16 & 0xFF, | ||
n >> 8 & 0xFF, | ||
n & 0xFF)); | ||
}); | ||
md5.update(this.req.head.toString('binary')); | ||
try { | ||
this.socket.write(md5.digest('binary'), 'binary'); | ||
} catch (e) { | ||
this.end(); | ||
} | ||
} | ||
return true; | ||
}; | ||
/** | ||
* 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; | ||
}; | ||
/** | ||
* Closes the connection. | ||
* | ||
* @api private | ||
*/ | ||
WebSocket.prototype.doClose = function () { | ||
this.socket.end(); | ||
}; | ||
/** | ||
* WebSocket parser | ||
* | ||
* @api public | ||
*/ | ||
function Parser () { | ||
this.buffer = ''; | ||
this.i = 0; | ||
}; | ||
/** | ||
* Inherits from EventEmitter. | ||
*/ | ||
Parser.prototype.__proto__ = EventEmitter.prototype; | ||
/** | ||
* Adds data to the buffer. | ||
* | ||
* @api public | ||
*/ | ||
Parser.prototype.add = function (data) { | ||
this.buffer += data; | ||
this.parse(); | ||
}; | ||
/** | ||
* Parses the buffer. | ||
* | ||
* @api private | ||
*/ | ||
Parser.prototype.parse = function () { | ||
for (var i = this.i, chr, l = this.buffer.length; i < l; i++){ | ||
chr = this.buffer[i]; | ||
if (this.buffer.length == 2 && this.buffer[1] == '\u0000') { | ||
this.emit('close'); | ||
this.buffer = ''; | ||
this.i = 0; | ||
return; | ||
} | ||
if (i === 0){ | ||
if (chr != '\u0000') | ||
this.error('Bad framing. Expected null byte as first frame'); | ||
else | ||
continue; | ||
} | ||
if (chr == '\ufffd'){ | ||
this.emit('data', this.buffer.substr(1, i - 1)); | ||
this.buffer = this.buffer.substr(i + 1); | ||
this.i = 0; | ||
return this.parse(); | ||
} | ||
} | ||
}; | ||
/** | ||
* Handles an error | ||
* | ||
* @api private | ||
*/ | ||
Parser.prototype.error = function (reason) { | ||
this.buffer = ''; | ||
this.i = 0; | ||
this.emit('error', reason); | ||
return this; | ||
}; |
@@ -26,1 +26,26 @@ | ||
}; | ||
/** | ||
* Unpacks a buffer to a number. | ||
* | ||
* @api public | ||
*/ | ||
exports.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; | ||
} | ||
/** | ||
* Left pads a string. | ||
* | ||
* @api public | ||
*/ | ||
exports.padl = function (s,n,c) { | ||
return new Array(1 + n - s.length).join(c) + s; | ||
} | ||
{ | ||
"name": "socket.io" | ||
, "version": "0.7.11" | ||
, "version": "0.8.0" | ||
, "description": "Real-time apps made cross-browser & easy with a WebSocket-like API" | ||
@@ -12,2 +12,3 @@ , "homepage": "http://socket.io" | ||
, { "name": "Vladimir Dronnikov", "email": "dronnikov@gmail.com" } | ||
, { "name": "Einar Otto Stangvik", "email": "einaros@gmail.com" } | ||
] | ||
@@ -19,3 +20,3 @@ , "repository":{ | ||
, "dependencies": { | ||
"socket.io-client": "0.7.11" | ||
"socket.io-client": "0.8.0" | ||
, "policyfile": "0.0.4" | ||
@@ -22,0 +23,0 @@ , "redis": "0.6.6" |
@@ -475,2 +475,87 @@ /*! | ||
'test that a referer is accepted for *:* origin': function (done) { | ||
var port = ++ports | ||
, io = sio.listen(port) | ||
, cl = client(port); | ||
io.configure(function () { | ||
io.set('origins', '*:*'); | ||
}); | ||
cl.get('/socket.io/{protocol}', { headers: { referer: 'http://foo.bar.com:82/something' } }, function (res, data) { | ||
res.statusCode.should.eql(200); | ||
cl.end(); | ||
io.server.close(); | ||
done(); | ||
}); | ||
}, | ||
'test that valid referer is accepted for addr:* origin': function (done) { | ||
var port = ++ports | ||
, io = sio.listen(port) | ||
, cl = client(port); | ||
io.configure(function () { | ||
io.set('origins', 'foo.bar.com:*'); | ||
}); | ||
cl.get('/socket.io/{protocol}', { headers: { referer: 'http://foo.bar.com/something' } }, function (res, data) { | ||
res.statusCode.should.eql(200); | ||
cl.end(); | ||
io.server.close(); | ||
done(); | ||
}); | ||
}, | ||
'test that erroneous referer is denied for addr:* origin': function (done) { | ||
var port = ++ports | ||
, io = sio.listen(port) | ||
, cl = client(port); | ||
io.configure(function () { | ||
io.set('origins', 'foo.bar.com:*'); | ||
}); | ||
cl.get('/socket.io/{protocol}', { headers: { referer: 'http://baz.bar.com/something' } }, function (res, data) { | ||
res.statusCode.should.eql(403); | ||
cl.end(); | ||
io.server.close(); | ||
done(); | ||
}); | ||
}, | ||
'test that valid referer port is accepted for addr:port origin': function (done) { | ||
var port = ++ports | ||
, io = sio.listen(port) | ||
, cl = client(port); | ||
io.configure(function () { | ||
io.set('origins', 'foo.bar.com:81'); | ||
}); | ||
cl.get('/socket.io/{protocol}', { headers: { referer: 'http://foo.bar.com:81/something' } }, function (res, data) { | ||
res.statusCode.should.eql(200); | ||
cl.end(); | ||
io.server.close(); | ||
done(); | ||
}); | ||
}, | ||
'test that erroneous referer port is denied for addr:port origin': function (done) { | ||
var port = ++ports | ||
, io = sio.listen(port) | ||
, cl = client(port); | ||
io.configure(function () { | ||
io.set('origins', 'foo.bar.com:81'); | ||
}); | ||
cl.get('/socket.io/{protocol}', { headers: { referer: 'http://foo.bar.com/something' } }, function (res, data) { | ||
res.statusCode.should.eql(403); | ||
cl.end(); | ||
io.server.close(); | ||
done(); | ||
}); | ||
}, | ||
'test limiting the supported transports for a manager': function (done) { | ||
@@ -477,0 +562,0 @@ var port = ++ports |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
385369
74
12600
+ Addedsocket.io-client@0.8.0(transitive)
- Removedsocket.io-client@0.7.11(transitive)
Updatedsocket.io-client@0.8.0