From dbe140c1173cbb55b77aa959aca39b64cee3738a Mon Sep 17 00:00:00 2001 From: Tony Kovanen Date: Mon, 20 Jan 2014 10:49:20 +0200 Subject: [PATCH 1/9] Binary support --- README.md | 11 +- lib/server.js | 1 + lib/transports/flashsocket.js | 6 ++ lib/transports/polling-jsonp.js | 8 ++ lib/transports/polling-xhr.js | 18 +++- lib/transports/polling.js | 21 ++-- lib/transports/websocket.js | 6 ++ test/jsonp.js | 21 ++++ test/server.js | 180 +++++++++++++++++++++++++++++++- 9 files changed, 258 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 3b3d3ae47..43f7b1875 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ var engine = require('engine.io') server.on('connection', function (socket) { socket.send('utf 8 string'); + socket.send(new Buffer([0, 1, 2, 3, 4, 5])); // binary data }); ``` @@ -60,7 +61,10 @@ httpServer.on('request', function (req, res) { ``` +Sending and receiving binary + +```html + + +``` + For more information on the client refer to the [engine-client](http://github.com/learnboost/engine.io-client) repository. diff --git a/lib/transports/flashsocket.js b/lib/transports/flashsocket.js index 87f6d0e21..2b10dd0af 100644 --- a/lib/transports/flashsocket.js +++ b/lib/transports/flashsocket.js @@ -48,6 +48,7 @@ FlashSocket.prototype.supportsFraming = true; * Supports sending binary data. * @api public */ + FlashSocket.prototype.supportsBinary = false; /** diff --git a/lib/transports/polling-jsonp.js b/lib/transports/polling-jsonp.js index 4608708b7..fc6d1cb17 100644 --- a/lib/transports/polling-jsonp.js +++ b/lib/transports/polling-jsonp.js @@ -38,7 +38,6 @@ JSONP.prototype.__proto__ = Polling.prototype; JSONP.prototype.supportsBinary = false; - /** * Handles incoming data. * Due to a bug in \n handling by browsers, we expect a escaped string. diff --git a/lib/transports/polling-xhr.js b/lib/transports/polling-xhr.js index 676512a66..f682a2b0f 100644 --- a/lib/transports/polling-xhr.js +++ b/lib/transports/polling-xhr.js @@ -44,11 +44,11 @@ XHR.prototype.supportsBinary = true; XHR.prototype.doWrite = function(data){ // explicit UTF-8 is required for pages not served under utf - var isString = typeof data === 'string' || data === undefined; - var contentType = (isString) + var isString = typeof data == 'string'; + var contentType = isString ? 'text/plain; charset=UTF-8' : 'application/octet-stream'; - var contentLength = '' + ((isString) ? Buffer.byteLength(data) : data.length); + var contentLength = '' + (isString ? Buffer.byteLength(data) : data.length); var headers = { 'Content-Type': contentType, diff --git a/lib/transports/polling.js b/lib/transports/polling.js index 881ef622b..541f9596e 100644 --- a/lib/transports/polling.js +++ b/lib/transports/polling.js @@ -118,12 +118,12 @@ Polling.prototype.onDataRequest = function (req, res) { this.onError('data request overlap from client'); res.writeHead(500); } else { - var isBinary = req.headers['content-type'] === 'application/octet-stream'; + var isBinary = 'application/octet-stream' == req.headers['content-type']; this.dataReq = req; this.dataRes = res; - var chunks = (isBinary) ? new Buffer(0) : '' + var chunks = isBinary ? new Buffer(0) : '' , self = this function cleanup () { @@ -140,8 +140,8 @@ Polling.prototype.onDataRequest = function (req, res) { }; function onData (data) { - if (typeof data === 'string') chunks += data; - else chunks = Buffer.concat([chunks, data]); + if (typeof data == 'string') { chunks += data; } + else { chunks = Buffer.concat([chunks, data]); } }; function onEnd () { @@ -160,7 +160,7 @@ Polling.prototype.onDataRequest = function (req, res) { req.on('close', onClose); req.on('data', onData); req.on('end', onEnd); - if (!isBinary) req.setEncoding('utf8'); + if (!isBinary) { req.setEncoding('utf8'); } } }; From d9d85483411300f87509f399be25d9d9ec6c2309 Mon Sep 17 00:00:00 2001 From: Tony Kovanen Date: Sat, 1 Feb 2014 14:34:33 +0200 Subject: [PATCH 3/9] Parser now has to figure out whether binary or string data is sent --- lib/transports/polling.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/transports/polling.js b/lib/transports/polling.js index 541f9596e..f41e9c34b 100644 --- a/lib/transports/polling.js +++ b/lib/transports/polling.js @@ -184,8 +184,7 @@ Polling.prototype.onData = function (data) { self.onPacket(packet); }; - if (typeof data === 'string') parser.decodePayload(data, callback); - else parser.decodePayloadAsBinary(data, callback); + parser.decodePayload(data, callback); }; /** @@ -203,8 +202,7 @@ Polling.prototype.send = function (packets) { this.shouldClose = null; } - if (!this.supportsBinary) this.write(parser.encodePayload(packets)); - else this.write(parser.encodePayloadAsBinary(packets)); + this.write(parser.encodePayload(packets, this.supportsBinary)); }; /** From 7723e8a2db5cf2ea99aec0178783131181e790a9 Mon Sep 17 00:00:00 2001 From: Tony Kovanen Date: Sun, 2 Feb 2014 14:42:16 +0200 Subject: [PATCH 4/9] Upgrade packet can now set transport binary support capabilities --- lib/socket.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/socket.js b/lib/socket.js index 1272c5809..3ed76dfa2 100644 --- a/lib/socket.js +++ b/lib/socket.js @@ -177,6 +177,7 @@ Socket.prototype.maybeUpgrade = function (transport) { transport.send([{ type: 'pong', data: 'probe' }]); self.checkIntervalTimer = setInterval(check, 100); } else if ('upgrade' == packet.type && self.readyState == 'open') { + if (packet.data === 'b64') { transport.supportsBinary = false; } debug('got upgrade packet - upgrading'); self.upgraded = true; self.emit('upgrade', transport); From 44159592c7e21500a7a8c42db17c1a200822aca9 Mon Sep 17 00:00:00 2001 From: Tony Kovanen Date: Mon, 3 Feb 2014 02:31:48 +0200 Subject: [PATCH 5/9] Using url parameter for communicating the need for base64 instead of upgrade packet for WebSockets --- lib/server.js | 3 ++- lib/socket.js | 1 - lib/transports/websocket.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/server.js b/lib/server.js index 5b669ad9f..d92a9dfbd 100644 --- a/lib/server.js +++ b/lib/server.js @@ -205,7 +205,7 @@ Server.prototype.handshake = function(transport, req){ try { var transport = new transports[transport](req); - if (req.query && req.query.b64) transport.supportsBinary = false; + if (req.query && req.query.b64) { transport.supportsBinary = false; } } catch (e) { sendErrorMessage(req.res, Server.errors.BAD_REQUEST); @@ -283,6 +283,7 @@ Server.prototype.onWebSocket = function(req, socket){ } else { debug('upgrading existing transport'); var transport = new transports[req.query.transport](req); + if (req.query && req.query.b64) { transport.supportsBinary = false; } this.clients[id].maybeUpgrade(transport); } } else { diff --git a/lib/socket.js b/lib/socket.js index 3ed76dfa2..1272c5809 100644 --- a/lib/socket.js +++ b/lib/socket.js @@ -177,7 +177,6 @@ Socket.prototype.maybeUpgrade = function (transport) { transport.send([{ type: 'pong', data: 'probe' }]); self.checkIntervalTimer = setInterval(check, 100); } else if ('upgrade' == packet.type && self.readyState == 'open') { - if (packet.data === 'b64') { transport.supportsBinary = false; } debug('got upgrade packet - upgrading'); self.upgraded = true; self.emit('upgrade', transport); diff --git a/lib/transports/websocket.js b/lib/transports/websocket.js index a07759a35..01eece0dc 100644 --- a/lib/transports/websocket.js +++ b/lib/transports/websocket.js @@ -90,7 +90,7 @@ WebSocket.prototype.onData = function (data) { WebSocket.prototype.send = function (packets) { for (var i = 0, l = packets.length; i < l; i++) { - var data = parser.encodePacket(packets[i]); + var data = parser.encodePacket(packets[i], this.supportsBinary); debug('writing "%s"', data); this.writable = false; var self = this; From d71fde22fdfbed32ae5d7e0964cadbe10517443a Mon Sep 17 00:00:00 2001 From: Tony Kovanen Date: Tue, 4 Feb 2014 00:04:05 +0200 Subject: [PATCH 6/9] Making encoding work with callbacks. Not completely there yet. --- lib/transports/polling.js | 5 ++++- lib/transports/websocket.js | 17 +++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/lib/transports/polling.js b/lib/transports/polling.js index f41e9c34b..3b4221586 100644 --- a/lib/transports/polling.js +++ b/lib/transports/polling.js @@ -202,7 +202,10 @@ Polling.prototype.send = function (packets) { this.shouldClose = null; } - this.write(parser.encodePayload(packets, this.supportsBinary)); + var self = this; + parser.encodePayload(packets, this.supportsBinary, function(data) { + self.write(data); + }); }; /** diff --git a/lib/transports/websocket.js b/lib/transports/websocket.js index 01eece0dc..690a768c9 100644 --- a/lib/transports/websocket.js +++ b/lib/transports/websocket.js @@ -89,15 +89,16 @@ WebSocket.prototype.onData = function (data) { */ WebSocket.prototype.send = function (packets) { + var self = this; for (var i = 0, l = packets.length; i < l; i++) { - var data = parser.encodePacket(packets[i], this.supportsBinary); - debug('writing "%s"', data); - this.writable = false; - var self = this; - this.socket.send(data, function (err){ - if (err) return self.onError('write error', err.stack); - self.writable = true; - self.emit('drain'); + parser.encodePacket(packets[i], this.supportsBinary, function(data) { + debug('writing "%s"', data); + self.writable = false; + self.socket.send(data, function (err){ + if (err) return self.onError('write error', err.stack); + self.writable = true; + self.emit('drain'); + }); }); } }; From 8cd37f08390242f9895d822c1d639dee4551e225 Mon Sep 17 00:00:00 2001 From: Tony Kovanen Date: Sat, 15 Feb 2014 09:15:12 +0200 Subject: [PATCH 7/9] Style --- lib/server.js | 4 ++- lib/transports/polling.js | 7 +++-- test/server.js | 54 ++++++++++++++++++++++++--------------- 3 files changed, 42 insertions(+), 23 deletions(-) diff --git a/lib/server.js b/lib/server.js index d92a9dfbd..f8db1968b 100644 --- a/lib/server.js +++ b/lib/server.js @@ -205,7 +205,9 @@ Server.prototype.handshake = function(transport, req){ try { var transport = new transports[transport](req); - if (req.query && req.query.b64) { transport.supportsBinary = false; } + if (req.query && req.query.b64) { + transport.supportsBinary = false; + } } catch (e) { sendErrorMessage(req.res, Server.errors.BAD_REQUEST); diff --git a/lib/transports/polling.js b/lib/transports/polling.js index 3b4221586..a504b0b8c 100644 --- a/lib/transports/polling.js +++ b/lib/transports/polling.js @@ -140,8 +140,11 @@ Polling.prototype.onDataRequest = function (req, res) { }; function onData (data) { - if (typeof data == 'string') { chunks += data; } - else { chunks = Buffer.concat([chunks, data]); } + if (typeof data == 'string') { + chunks += data; + } else { + chunks = Buffer.concat([chunks, data]); + } }; function onEnd () { diff --git a/test/server.js b/test/server.js index 6a379bb3b..7593b91af 100644 --- a/test/server.js +++ b/test/server.js @@ -835,19 +835,21 @@ describe('server', function () { it('should arrive when binary data is sent as Int8Array (ws)', function (done) { var binaryData = new Int8Array(5); - for (var i = 0; i < binaryData.length; i++) binaryData[i] = i; - + for (var i = 0; i < binaryData.length; i++) { + binaryData[i] = i; + } + var opts = { allowUpgrades: false, transports: ['websocket'] }; var engine = listen(opts, function(port) { var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] }) - + engine.on('connection', function (conn) { conn.send(binaryData); }); socket.on('open', function () { socket.on('message', function(msg) { - for (var i = 0; i < msg.length; i++) { + for (var i = 0; i < binaryData.length; i++) { var num = msg.readInt8(i); expect(num).to.be(i); } @@ -859,19 +861,21 @@ describe('server', function () { it('should arrive when binary data is sent as Int32Array (ws)', function (done) { var binaryData = new Int32Array(5); - for (var i = 0; i < binaryData.length; i++) binaryData[i] = (i + 100) * 9823; - + for (var i = 0; i < binaryData.length; i++) { + binaryData[i] = (i + 100) * 9823; + } + var opts = { allowUpgrades: false, transports: ['websocket'] }; var engine = listen(opts, function(port) { var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] }) - + engine.on('connection', function (conn) { conn.send(binaryData); }); socket.on('open', function () { socket.on('message', function(msg) { - for (var i = 0, ii = 0; i < msg.length; i += 4, ii++) { + for (var i = 0, ii = 0; i < binaryData.length; i += 4, ii++) { var num = msg.readInt32LE(i); expect(num).to.be((ii + 100) * 9823); } @@ -883,19 +887,21 @@ describe('server', function () { it('should arrive when binary data is sent as Int32Array, given as ArrayBuffer(ws)', function (done) { var binaryData = new Int32Array(5); - for (var i = 0; i < binaryData.length; i++) binaryData[i] = (i + 100) * 9823; - + for (var i = 0; i < binaryData.length; i++) { + binaryData[i] = (i + 100) * 9823; + } + var opts = { allowUpgrades: false, transports: ['websocket'] }; var engine = listen(opts, function(port) { var socket = new eioc.Socket('ws://localhost:%d'.s(port), { transports: ['websocket'] }) - + engine.on('connection', function (conn) { conn.send(binaryData.buffer); }); socket.on('open', function () { socket.on('message', function(msg) { - for (var i = 0, ii = 0; i < msg.length; i += 4, ii++) { + for (var i = 0, ii = 0; i < binaryData.length; i += 4, ii++) { var num = msg.readInt32LE(i); expect(num).to.be((ii + 100) * 9823); } @@ -907,7 +913,9 @@ describe('server', function () { it('should arrive when binary data is sent as Buffer (ws)', function (done) { var binaryData = Buffer(5); - for (var i = 0; i < binaryData.length; i++) binaryData.writeInt8(i, i); + for (var i = 0; i < binaryData.length; i++) { + binaryData.writeInt8(i, i); + } var opts = { allowUpgrades: false, transports: ['websocket'] }; var engine = listen(opts, function(port) { @@ -919,7 +927,7 @@ describe('server', function () { socket.on('open', function () { socket.on('message', function(msg) { - for (var i = 0; i < msg.length; i++) { + for (var i = 0; i < binaryData.length; i++) { var num = msg.readInt8(i); expect(num).to.be(i); } @@ -931,7 +939,9 @@ describe('server', function () { it('should arrive when binary data sent as Buffer (polling)', function (done) { var binaryData = Buffer(5); - for (var i = 0; i < binaryData.length; i++) binaryData.writeInt8(i, i); + for (var i = 0; i < binaryData.length; i++) { + binaryData.writeInt8(i, i); + } var opts = { allowUpgrades: false, transports: ['polling'] }; var engine = listen(opts, function(port) { @@ -943,7 +953,7 @@ describe('server', function () { socket.on('open', function() { socket.on('message', function(msg) { - for (var i = 0; i < msg.length; i++) { + for (var i = 0; i < binaryData.length; i++) { var num = msg.readInt8(i); expect(num).to.be(i); } @@ -956,7 +966,9 @@ describe('server', function () { it('should arrive as ArrayBuffer if requested when binary data sent as Buffer (ws)', function (done) { var binaryData = Buffer(5); - for (var i = 0; i < binaryData.length; i++) binaryData.writeInt8(i, i); + for (var i = 0; i < binaryData.length; i++) { + binaryData.writeInt8(i, i); + } var opts = { allowUpgrades: false, transports: ['websocket'] }; var engine = listen(opts, function(port) { @@ -971,7 +983,7 @@ describe('server', function () { socket.on('message', function(msg) { expect(msg instanceof ArrayBuffer).to.be(true); var intArray = new Int8Array(msg); - for (var i = 0; i < intArray.length; i++) { + for (var i = 0; i < binaryData.length; i++) { expect(intArray[i]).to.be(i); } @@ -983,7 +995,9 @@ describe('server', function () { it('should arrive as ArrayBuffer if requested when binary data sent as Buffer (polling)', function (done) { var binaryData = Buffer(5); - for (var i = 0; i < binaryData.length; i++) binaryData.writeInt8(i, i); + for (var i = 0; i < binaryData.length; i++) { + binaryData.writeInt8(i, i); + } var opts = { allowUpgrades: false, transports: ['polling'] }; var engine = listen(opts, function(port) { @@ -998,7 +1012,7 @@ describe('server', function () { socket.on('message', function(msg) { expect(msg instanceof ArrayBuffer).to.be(true); var intArray = new Int8Array(msg); - for (var i = 0; i < intArray.length; i++) { + for (var i = 0; i < binaryData.length; i++) { expect(intArray[i]).to.be(i); } From a9a6f25e7682bc64cfeb379b403d41e61d0248cb Mon Sep 17 00:00:00 2001 From: Tony Kovanen Date: Tue, 18 Feb 2014 23:33:22 +0200 Subject: [PATCH 8/9] style --- lib/server.js | 4 +++- lib/transports/polling.js | 2 +- lib/transports/websocket.js | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/server.js b/lib/server.js index f8db1968b..3b0885e5a 100644 --- a/lib/server.js +++ b/lib/server.js @@ -285,7 +285,9 @@ Server.prototype.onWebSocket = function(req, socket){ } else { debug('upgrading existing transport'); var transport = new transports[req.query.transport](req); - if (req.query && req.query.b64) { transport.supportsBinary = false; } + if (req.query && req.query.b64) { + transport.supportsBinary = false; + } this.clients[id].maybeUpgrade(transport); } } else { diff --git a/lib/transports/polling.js b/lib/transports/polling.js index a504b0b8c..72d435319 100644 --- a/lib/transports/polling.js +++ b/lib/transports/polling.js @@ -127,7 +127,7 @@ Polling.prototype.onDataRequest = function (req, res) { , self = this function cleanup () { - chunks = (isBinary) ? new Buffer(0) : ''; + chunks = isBinary ? new Buffer(0) : ''; req.removeListener('data', onData); req.removeListener('end', onEnd); req.removeListener('close', onClose); diff --git a/lib/transports/websocket.js b/lib/transports/websocket.js index 690a768c9..4157bff29 100644 --- a/lib/transports/websocket.js +++ b/lib/transports/websocket.js @@ -67,6 +67,7 @@ WebSocket.prototype.supportsFraming = true; * Supports sending binary data. * @api public */ + WebSocket.prototype.supportsBinary = true; /** From 20d06700c312e52789aa4bd34356356b65fddedc Mon Sep 17 00:00:00 2001 From: Tony Kovanen Date: Tue, 18 Feb 2014 23:56:01 +0200 Subject: [PATCH 9/9] Removed the need to set any default values for binary support in transports --- lib/server.js | 4 ++++ lib/transports/flashsocket.js | 7 ------- lib/transports/polling-jsonp.js | 7 ------- lib/transports/polling-xhr.js | 7 ------- lib/transports/websocket.js | 7 ------- 5 files changed, 4 insertions(+), 28 deletions(-) diff --git a/lib/server.js b/lib/server.js index 3b0885e5a..84607f2ec 100644 --- a/lib/server.js +++ b/lib/server.js @@ -207,6 +207,8 @@ Server.prototype.handshake = function(transport, req){ var transport = new transports[transport](req); if (req.query && req.query.b64) { transport.supportsBinary = false; + } else { + transport.supportsBinary = true; } } catch (e) { @@ -287,6 +289,8 @@ Server.prototype.onWebSocket = function(req, socket){ var transport = new transports[req.query.transport](req); if (req.query && req.query.b64) { transport.supportsBinary = false; + } else { + transport.supportsBinary = true; } this.clients[id].maybeUpgrade(transport); } diff --git a/lib/transports/flashsocket.js b/lib/transports/flashsocket.js index 2b10dd0af..794bb29fa 100644 --- a/lib/transports/flashsocket.js +++ b/lib/transports/flashsocket.js @@ -44,13 +44,6 @@ FlashSocket.prototype.name = 'flashsocket'; FlashSocket.prototype.supportsFraming = true; -/** - * Supports sending binary data. - * @api public - */ - -FlashSocket.prototype.supportsBinary = false; - /** * Listens for new configuration changes of the Manager * this way we can enable and disable the flash server. diff --git a/lib/transports/polling-jsonp.js b/lib/transports/polling-jsonp.js index fc6d1cb17..b42b755e4 100644 --- a/lib/transports/polling-jsonp.js +++ b/lib/transports/polling-jsonp.js @@ -31,13 +31,6 @@ function JSONP (req) { JSONP.prototype.__proto__ = Polling.prototype; -/** - * Supports sending binary data. - * @api public - */ - -JSONP.prototype.supportsBinary = false; - /** * Handles incoming data. * Due to a bug in \n handling by browsers, we expect a escaped string. diff --git a/lib/transports/polling-xhr.js b/lib/transports/polling-xhr.js index f682a2b0f..8bc1d50fa 100644 --- a/lib/transports/polling-xhr.js +++ b/lib/transports/polling-xhr.js @@ -29,13 +29,6 @@ function XHR(req){ XHR.prototype.__proto__ = Polling.prototype; -/** - * Supports sending binary data. - * @api public - */ - -XHR.prototype.supportsBinary = true; - /** * Frames data prior to write. * diff --git a/lib/transports/websocket.js b/lib/transports/websocket.js index 4157bff29..59b8eb7b1 100644 --- a/lib/transports/websocket.js +++ b/lib/transports/websocket.js @@ -63,13 +63,6 @@ WebSocket.prototype.handlesUpgrades = true; WebSocket.prototype.supportsFraming = true; -/** - * Supports sending binary data. - * @api public - */ - -WebSocket.prototype.supportsBinary = true; - /** * Processes the incoming data. *