From 54de4afe5f44ee9bbeccdef9af22cce818fbecdc Mon Sep 17 00:00:00 2001 From: Ville Lautanala Date: Wed, 25 Jan 2012 23:26:37 +0200 Subject: [PATCH 1/3] Store data to buffers in default WebSocket protocol Properly detect 0xFF as end-of-message marker. This makes sure that partial UTF-8 characters or code points outside BMP do not cause connection breakages (although they are still not decoded correctly). --- lib/transports/websocket/default.js | 34 ++++++++++++++--------------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/lib/transports/websocket/default.js b/lib/transports/websocket/default.js index 2e861a7470..afccebbc74 100644 --- a/lib/transports/websocket/default.js +++ b/lib/transports/websocket/default.js @@ -127,30 +127,25 @@ WebSocket.prototype.onSocketConnect = function () { 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)) { + if (this.proveReception(headers)) { self.flush(); } var headBuffer = ''; - this.socket.on('data', function (data) { + this.socket.on('data', function (buffer) { if (waitingForNonce) { - headBuffer += data; + headBuffer += buffer.toString('binary'); 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 @@ -164,7 +159,7 @@ WebSocket.prototype.onSocketConnect = function () { return; } - self.parser.add(data); + self.parser.add(buffer); }); }; @@ -292,7 +287,7 @@ WebSocket.prototype.doClose = function () { */ function Parser () { - this.buffer = ''; + this.buffer = new Buffer(0); this.i = 0; }; @@ -309,7 +304,10 @@ Parser.prototype.__proto__ = EventEmitter.prototype; */ Parser.prototype.add = function (data) { - this.buffer += data; + var buffer = new Buffer(this.buffer.length + data.length); + this.buffer.copy(buffer); + data.copy(buffer, this.buffer.length); + this.buffer = buffer; this.parse(); }; @@ -323,23 +321,23 @@ 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') { + if (this.buffer.length == 2 && this.buffer[1] == 0x00) { this.emit('close'); - this.buffer = ''; + this.buffer = new Buffer(0); this.i = 0; return; } if (i === 0){ - if (chr != '\u0000') + if (chr != 0x00) 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); + if (chr == 0xFF){ + this.emit('data', this.buffer.slice(1, i).toString('utf8')); + this.buffer = this.buffer.slice(i + 1); this.i = 0; return this.parse(); } @@ -353,7 +351,7 @@ Parser.prototype.parse = function () { */ Parser.prototype.error = function (reason) { - this.buffer = ''; + this.buffer = new Buffer(0); this.i = 0; this.emit('error', reason); return this; From fd8d787c73f6170c1d138fd82a708c5df04ba855 Mon Sep 17 00:00:00 2001 From: Ville Lautanala Date: Thu, 26 Jan 2012 00:16:27 +0200 Subject: [PATCH 2/3] Add test case for default websocket parsing --- lib/transports/websocket/default.js | 1 + ...ransports.websocket.default.parser.test.js | 71 +++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 test/transports.websocket.default.parser.test.js diff --git a/lib/transports/websocket/default.js b/lib/transports/websocket/default.js index afccebbc74..765f2f0497 100644 --- a/lib/transports/websocket/default.js +++ b/lib/transports/websocket/default.js @@ -19,6 +19,7 @@ var Transport = require('../../transport') */ exports = module.exports = WebSocket; +exports.Parser = Parser; /** * HTTP interface constructor. Interface compatible with all transports that diff --git a/test/transports.websocket.default.parser.test.js b/test/transports.websocket.default.parser.test.js new file mode 100644 index 0000000000..1ca76dbc3e --- /dev/null +++ b/test/transports.websocket.default.parser.test.js @@ -0,0 +1,71 @@ +/** + * Test dependencies. + */ + +var assert = require('assert'); +var Parser = require('../lib/transports/websocket/default.js').Parser; +require('./hybi-common'); + +/** + * Tests. + */ + +module.exports = { + 'can parse message': function() { + var p = new Parser(); + var packet = '00 48 65 6c 6c 6f ff'; + + var gotData = false; + p.on('data', function(data) { + gotData = true; + assert.equal('Hello', data); + }); + + p.add(getBufferFromHexString(packet)); + assert.ok(gotData); + }, + 'can parse message from multiple chunks': function() { + var p = new Parser(); + var packet1 = '00 48 65'; + var packet2 = '6c 6c 6f ff'; + + var gotData = false; + p.on('data', function(data) { + gotData = true; + assert.equal('Hello', data); + }); + + p.add(getBufferFromHexString(packet1)); + p.add(getBufferFromHexString(packet2)); + assert.ok(gotData); + }, + 'can parse multibyte UTF-8 from multiple chunks': function() { + var p = new Parser(); + var packet1 = '00 c3'; + var packet2 = 'b6 ff'; + + var gotData = false; + p.on('data', function(data) { + gotData = true; + assert.equal('รถ', data); + }); + + p.add(getBufferFromHexString(packet1)); + p.add(getBufferFromHexString(packet2)); + assert.ok(gotData); + }, + 'can parse message containing 4 byte UTF-8': function() { + var p = new Parser(); + var packet = '00 f0 9d 9b a2 ff'; + + var gotData = false; + p.on('data', function(data) { + gotData = true; + assert.equal(1, data.length); // Parsed as replacement character + }); + + p.add(getBufferFromHexString(packet)); + assert.ok(gotData); + }, +}; + From 844bc62b0bec48c37a0b15a07fd2780e5aac99f3 Mon Sep 17 00:00:00 2001 From: Ville Lautanala Date: Fri, 10 Feb 2012 17:47:01 +0200 Subject: [PATCH 3/3] Check that socket is not waiting for nonce before flushing --- lib/transports/websocket/default.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/transports/websocket/default.js b/lib/transports/websocket/default.js index 765f2f0497..2ab1fab2e8 100644 --- a/lib/transports/websocket/default.js +++ b/lib/transports/websocket/default.js @@ -133,7 +133,7 @@ WebSocket.prototype.onSocketConnect = function () { return; } - if (this.proveReception(headers)) { + if (!waitingForNonce && this.proveReception(headers)) { self.flush(); }