[Pkg-javascript-commits] [node-engine.io-parser] 01/02: Imported Upstream version 1.2.1
Sebastiaan Couwenberg
sebastic at moszumanska.debian.org
Sun Mar 29 16:19:24 UTC 2015
This is an automated email from the git hooks/post-receive script.
sebastic pushed a commit to branch master
in repository node-engine.io-parser.
commit 9a6c51987a316ca77853bf9d3d8f73d47439d216
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date: Sun Mar 29 16:32:29 2015 +0200
Imported Upstream version 1.2.1
---
.gitignore | 1 +
.npmignore | 17 ++
.travis.yml | 45 ++++
.zuul.yml | 18 ++
History.md | 100 +++++++
LICENSE | 22 ++
Makefile | 26 ++
Readme.md | 202 ++++++++++++++
index.js | 2 +
lib/browser.js | 594 ++++++++++++++++++++++++++++++++++++++++++
lib/index.js | 467 +++++++++++++++++++++++++++++++++
lib/keys.js | 19 ++
package.json | 33 +++
test/browser/arraybuffer.js | 84 ++++++
test/browser/base64_object.js | 41 +++
test/browser/blob.js | 74 ++++++
test/browser/index.js | 29 +++
test/index.js | 11 +
test/node/index.js | 92 +++++++
test/parser.js | 264 +++++++++++++++++++
test/support/env.js | 5 +
21 files changed, 2146 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3c3629e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+node_modules
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 0000000..f841db0
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1,17 @@
+lib-cov
+*.seed
+*.log
+*.csv
+*.dat
+*.out
+*.pid
+*.gz
+
+pids
+logs
+results
+
+npm-debug.log
+node_modules/*
+
+test
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..de84f50
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,45 @@
+language: node_js
+node_js:
+- 0.10
+notifications:
+ irc: irc.freenode.org##socket.io
+env:
+ global:
+ - secure: ZS6AzV1n1K1exomhZoK0SorBHEy4/7/qdk1p5/dm0tQdStSVwRDJyQk5wUIgJFsJaVlN8O/MH8LBkiLlalohR/DdE2ZtkNJqfMnetE/ZqPX7r8mzwDasnHJNJnKWJlBVqOpy7hciiPV2yZGIJoe2OQfwWxFEcLJ6NGOXCEkyLAg=
+ - secure: hcqk+nIqzrwJSQs+5T1sKN4YiCghQdP849RjH64bb7Ayslh+o0DmihTE3Wl+cCWFcRBvwBJdmDV2gJpsVcODTc2VdOrJnv8ezfQ8zvAyDZFYxno47PtbjQUi0By2wBPp6zlfigcnXxQ2z6997EDRvsI4VgQVqKsGLot4cMU9oz0=
+matrix:
+ include:
+ - node_js: '0.10'
+ env: BROWSER_NAME=chrome BROWSER_VERSION=latest
+ - node_js: '0.10'
+ env: BROWSER_NAME=safari BROWSER_VERSION=latest
+ - node_js: '0.10'
+ env: BROWSER_NAME=ie BROWSER_VERSION=6
+ - node_js: '0.10'
+ env: BROWSER_NAME=ie BROWSER_VERSION=7
+ - node_js: '0.10'
+ env: BROWSER_NAME=ie BROWSER_VERSION=8
+ - node_js: '0.10'
+ env: BROWSER_NAME=ie BROWSER_VERSION=9
+ - node_js: '0.10'
+ env: BROWSER_NAME=ie BROWSER_VERSION=10 BROWSER_PLATFORM="Windows 2012"
+ - node_js: '0.10'
+ env: BROWSER_NAME=ie BROWSER_VERSION=latest BROWSER_PLATFORM="Windows 2012"
+ - node_js: '0.10'
+ env: BROWSER_NAME=iphone BROWSER_VERSION=4.3
+ - node_js: '0.10'
+ env: BROWSER_NAME=iphone BROWSER_VERSION=5.1
+ - node_js: '0.10'
+ env: BROWSER_NAME=iphone BROWSER_VERSION=6.1
+ - node_js: '0.10'
+ env: BROWSER_NAME=iphone BROWSER_VERSION=7.1
+ - node_js: '0.10'
+ env: BROWSER_NAME=android BROWSER_VERSION=4.0
+ - node_js: '0.10'
+ env: BROWSER_NAME=android BROWSER_VERSION=4.1
+ - node_js: '0.10'
+ env: BROWSER_NAME=android BROWSER_VERSION=4.2
+ - node_js: '0.10'
+ env: BROWSER_NAME=android BROWSER_VERSION=4.3
+ - node_js: '0.10'
+ env: BROWSER_NAME=android BROWSER_VERSION=4.4
diff --git a/.zuul.yml b/.zuul.yml
new file mode 100644
index 0000000..fb9da14
--- /dev/null
+++ b/.zuul.yml
@@ -0,0 +1,18 @@
+ui: mocha-bdd
+browsers:
+ - name: chrome
+ version: 29..latest
+ - name: firefox
+ version: latest
+ - name: safari
+ version: latest
+ - name: ie
+ version: 10
+ platform: Windows 2012
+ - name: ie
+ version: 9
+ version: [6..9, latest]
+ - name: iphone
+ version: oldest..latest
+ - name: android
+ version: latest
diff --git a/History.md b/History.md
new file mode 100644
index 0000000..824ca6e
--- /dev/null
+++ b/History.md
@@ -0,0 +1,100 @@
+
+1.2.1 / 2015-01-17
+==================
+
+ * pass has-binary result to encodePacket [rase-]
+ * Fix parse error [rase-]
+
+1.2.0 / 2015-01-11
+==================
+
+ * fix return type for decodePacket
+ * README fixes
+ * use travis matrix for better test runs
+ * encode into binary only if needed
+ * add test cases for base64 object encoding.
+ * add encodeBase64Object to encoder for browser
+ * avoid sending Blobs on PhantomJS (as on Android)
+ * test that utf8 encoding is not on by default but can be switched on manually
+
+1.1.0 / 2014-07-16
+==================
+
+ * make utf8 encoding/decoding optional
+
+1.0.8 / 2014-07-16
+==================
+
+ * adjust protocol revision
+ * handle invalid utf8 errors gracefully
+ * fix memory leak on browser
+
+1.0.7 / 2014-06-24
+==================
+
+ * fix decodePayloadAsBinary memory leak [christophwitzko]
+ * README improvements
+
+1.0.6 / 2014-05-30
+==================
+
+ * utf8 fixes when using binary encoding [nkzawa]
+
+1.0.5 / 2014-05-06
+==================
+
+ * fix range error
+
+1.0.4 / 2014-04-13
+==================
+
+ * fix `encodePayloadAsBinary` method encodes packets to base64
+
+1.0.3 / 2014-04-10
+==================
+
+ * Fix length calculation when encoding as binary [binlain]
+
+1.0.2 / 2014-03-16
+==================
+
+ * fix binary for android due to a bug in Blob XHR2 implementation [Rase-]
+
+1.0.1 / 2014-03-06
+==================
+
+ * implement `blob` module to simplify code
+ * bump `arraybuffer.slice`
+ * style fixes
+
+1.0.0 / 2014-02-18
+==================
+
+ * parser: added binary encoding [Rase-]
+ * parser: switched to an async interface [Rase-]
+
+0.3.0 / 2013-03-16
+==================
+
+ * parser: if callback returns `false` ignore rest of payload
+ * test: fixed all broken tests
+
+0.2.1 / 2013-03-16
+==================
+
+ * added protocol version to index.js [albertyfwu]
+
+0.2.0 / 2013-02-26
+==================
+
+ * Changed `decodePayload` to use a callback instead of returning an array [sweetieSong, albertyfwu]
+
+0.1.1 / 2013-01-26
+==================
+
+ * package.json fixes
+
+0.1.0 / 2013-01-19
+==================
+
+ * Initial release
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..9006d30
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+(The MIT License)
+
+Copyright (c) 2014 Automattic <dev at cloudup.com>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..068eb5f
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,26 @@
+
+REPORTER = dot
+
+test:
+ @if [ "x$(BROWSER_NAME)" = "x" ]; then make test-node; else make test-zuul; fi
+
+test-node:
+ @./node_modules/.bin/mocha \
+ --reporter $(REPORTER) \
+ test/index.js
+
+test-zuul:
+ @if [ "x$(BROWSER_PLATFORM)" = "x" ]; then \
+ ./node_modules/zuul/bin/zuul \
+ --browser-name $(BROWSER_NAME) \
+ --browser-version $(BROWSER_VERSION) \
+ test/index.js; \
+ else \
+ ./node_modules/zuul/bin/zuul \
+ --browser-name $(BROWSER_NAME) \
+ --browser-version $(BROWSER_VERSION) \
+ --browser-platform "$(BROWSER_PLATFORM)" \
+ test/index.js; \
+ fi
+
+.PHONY: test
diff --git a/Readme.md b/Readme.md
new file mode 100644
index 0000000..33d3f23
--- /dev/null
+++ b/Readme.md
@@ -0,0 +1,202 @@
+
+# engine.io-parser
+
+[![Build Status](https://secure.travis-ci.org/Automattic/engine.io-parser.svg)](http://travis-ci.org/Automattic/engine.io-parser)
+[![NPM version](https://badge.fury.io/js/engine.io-parser.svg)](http://badge.fury.io/js/engine.io-parser)
+
+This is the JavaScript parser for the engine.io protocol encoding,
+shared by both
+[engine.io-client](https://github.com/Automattic/engine.io-client) and
+[engine.io](https://github.com/Automattic/engine.io).
+
+## How to use
+
+### Standalone
+
+The parser can encode/decode packets, payloads, and payloads as binary
+with the following methods: `encodePacket`, `decodePacket`, `encodePayload`,
+`decodePayload`, `encodePayloadAsBinary`, `decodePayloadAsBinary`.
+
+The browser-side parser also includes `encodePayloadAsArrayBuffer` and `encodePayloadAsBlob`.
+
+Example:
+
+```js
+var parser = require('engine.io-parser');
+
+var data = new Buffer(5);
+for (var i = 0; i < data.length; i++) { data[i] = i; }
+
+parser.encodePacket({ type: 'message', data: data }, function(encoded) {
+ var decodedData = parser.decodePacket(encoded); // { type: 'message', data: data }
+});
+```
+
+### With browserify
+
+Engine.IO Parser is a commonjs module, which means you can include it by using
+`require` on the browser and package using [browserify](http://browserify.org/):
+
+1. install the parser package
+
+ ```shell
+ npm install engine.io-parser
+ ```
+
+1. write your app code
+
+ ```js
+ var parser = require('engine.io-parser');
+
+ var testBuffer = new Int8Array(10);
+ for (var i = 0; i < testBuffer.length; i++) testBuffer[i] = i;
+
+ var packets = [{ type: 'message', data: testBuffer.buffer }, { type: 'message', data: 'hello' }];
+
+ parser.encodePayload(packets, function(encoded) {
+ parser.decodePayload(encoded,
+ function(packet, index, total) {
+ var isLast = index + 1 == total;
+ if (!isLast) {
+ var buffer = new Int8Array(packet.data); // testBuffer
+ } else {
+ var message = packet.data; // 'hello'
+ }
+ });
+ });
+ ```
+
+1. build your app bundle
+
+ ```bash
+ $ browserify app.js > bundle.js
+ ```
+
+1. include on your page
+
+ ```html
+ <script src="/path/to/bundle.js"></script>
+ ```
+
+## Features
+
+- Runs on browser and node.js seamlessly
+- Runs inside HTML5 WebWorker
+- Can encode and decode packets
+ - Encodes from/to ArrayBuffer or Blob when in browser, and Buffer or ArrayBuffer in Node
+
+## API
+
+Note: `cb(type)` means the type is a callback function that contains a parameter of type `type` when called.
+
+### Node
+
+- `encodePacket`
+ - Encodes a packet.
+ - **Parameters**
+ - `Object`: the packet to encode, has `type` and `data`.
+ - `data`: can be a `String`, `Number`, `Buffer`, `ArrayBuffer`
+ - `Boolean`: optional, binary support
+ - `Function`: callback, returns the encoded packet (`cb(String)`)
+- `decodePacket`
+ - Decodes a packet. Data also available as an ArrayBuffer if requested.
+ - Returns data as `String` or (`Blob` on browser, `ArrayBuffer` on Node)
+ - **Parameters**
+ - `String` | `ArrayBuffer`: the packet to decode, has `type` and `data`
+ - `String`: optional, the binary type
+
+- `encodeBase64Packet`
+ - Encodes a packet with binary data in a base64 string (`String`)
+ - **Parameters**
+ - `Object`: the packet to encode, has `type` and `data`
+ - `Function`: callback, returns the base64 encoded message (`cb(String)`)
+- `decodeBase64Packet`
+ - Decodes a packet encoded in a base64 string.
+ - **Parameters**
+ - `String`: the base64 encoded message
+ - `String`: optional, the binary type
+
+- `encodePayload`
+ - Encodes multiple messages (payload).
+ - If any contents are binary, they will be encoded as base64 strings. Base64
+ encoded strings are marked with a b before the length specifier
+ - **Parameters**
+ - `Array`: an array of packets
+ - `Boolean`: optional, binary support
+ - `Function`: callback, returns the encoded payload (`cb(String)`)
+- `decodePayload`
+ - Decodes data when a payload is maybe expected. Possible binary contents are
+ decoded from their base64 representation.
+ - **Parameters**
+ - `String`: the payload
+ - `String`: optional, the binary type
+ - `Function`: callback, returns (cb(`Object`: packet, `Number`:packet index, `Number`:packet total))
+
+- `encodePayloadAsBinary`
+ - Encodes multiple messages (payload) as binary.
+ - **Parameters**
+ - `Array`: an array of packets
+ - `Function`: callback, returns the encoded payload (`cb(Buffer)`)
+- `decodePayloadAsBinary`
+ - Decodes data when a payload is maybe expected. Strings are decoded by
+ interpreting each byte as a key code for entries marked to start with 0. See
+ description of encodePayloadAsBinary.
+ - **Parameters**
+ - `Buffer`: the buffer
+ - `String`: optional, the binary type
+ - `Function`: callback, returns the decoded packet (`cb(Object)`)
+
+### Browser
+
+- `encodePayloadAsArrayBuffer`
+ - Encodes multiple messages (payload) as binary.
+ - **Parameters**
+ - `Array`: an array of packets
+ - `Function`: callback, returns the encoded payload (`cb(ArrayBuffer)`)
+- `encodePayloadAsBlob`
+ - Encodes multiple messages (payload) as blob.
+ - **Parameters**
+ - `Array`: an array of packets
+ - `Function`: callback, returns the encoded payload (`cb(Blob)`)
+
+## Tests
+
+Standalone tests can be run with `make test` which will run both node.js and browser tests.
+
+Browser tests are run using [zuul](https://github.com/defunctzombie/zuul).
+(You must have zuul setup with a saucelabs account.)
+
+You can run the tests locally using the following command:
+
+```
+./node_modules/.bin/zuul --local 8080 -- test/index.js
+```
+
+## Support
+
+The support channels for `engine.io-parser` are the same as `socket.io`:
+ - irc.freenode.net **#socket.io**
+ - [Google Groups](http://groups.google.com/group/socket_io)
+ - [Website](http://socket.io)
+
+## Development
+
+To contribute patches, run tests or benchmarks, make sure to clone the
+repository:
+
+```bash
+git clone git://github.com/LearnBoost/engine.io-parser.git
+```
+
+Then:
+
+```bash
+cd engine.io-parser
+npm install
+```
+
+See the `Tests` section above for how to run tests before submitting any patches.
+
+## License
+
+MIT
diff --git a/index.js b/index.js
new file mode 100644
index 0000000..887727f
--- /dev/null
+++ b/index.js
@@ -0,0 +1,2 @@
+
+module.exports = require('./lib/');
diff --git a/lib/browser.js b/lib/browser.js
new file mode 100644
index 0000000..ee39cbf
--- /dev/null
+++ b/lib/browser.js
@@ -0,0 +1,594 @@
+/**
+ * Module dependencies.
+ */
+
+var keys = require('./keys');
+var hasBinary = require('has-binary');
+var sliceBuffer = require('arraybuffer.slice');
+var base64encoder = require('base64-arraybuffer');
+var after = require('after');
+var utf8 = require('utf8');
+
+/**
+ * Check if we are running an android browser. That requires us to use
+ * ArrayBuffer with polling transports...
+ *
+ * http://ghinda.net/jpeg-blob-ajax-android/
+ */
+
+var isAndroid = navigator.userAgent.match(/Android/i);
+
+/**
+ * Check if we are running in PhantomJS.
+ * Uploading a Blob with PhantomJS does not work correctly, as reported here:
+ * https://github.com/ariya/phantomjs/issues/11395
+ * @type boolean
+ */
+var isPhantomJS = /PhantomJS/i.test(navigator.userAgent);
+
+/**
+ * When true, avoids using Blobs to encode payloads.
+ * @type boolean
+ */
+var dontSendBlobs = isAndroid || isPhantomJS;
+
+/**
+ * Current protocol version.
+ */
+
+exports.protocol = 3;
+
+/**
+ * Packet types.
+ */
+
+var packets = exports.packets = {
+ open: 0 // non-ws
+ , close: 1 // non-ws
+ , ping: 2
+ , pong: 3
+ , message: 4
+ , upgrade: 5
+ , noop: 6
+};
+
+var packetslist = keys(packets);
+
+/**
+ * Premade error packet.
+ */
+
+var err = { type: 'error', data: 'parser error' };
+
+/**
+ * Create a blob api even for blob builder when vendor prefixes exist
+ */
+
+var Blob = require('blob');
+
+/**
+ * Encodes a packet.
+ *
+ * <packet type id> [ <data> ]
+ *
+ * Example:
+ *
+ * 5hello world
+ * 3
+ * 4
+ *
+ * Binary is encoded in an identical principle
+ *
+ * @api private
+ */
+
+exports.encodePacket = function (packet, supportsBinary, utf8encode, callback) {
+ if ('function' == typeof supportsBinary) {
+ callback = supportsBinary;
+ supportsBinary = false;
+ }
+
+ if ('function' == typeof utf8encode) {
+ callback = utf8encode;
+ utf8encode = null;
+ }
+
+ var data = (packet.data === undefined)
+ ? undefined
+ : packet.data.buffer || packet.data;
+
+ if (global.ArrayBuffer && data instanceof ArrayBuffer) {
+ return encodeArrayBuffer(packet, supportsBinary, callback);
+ } else if (Blob && data instanceof global.Blob) {
+ return encodeBlob(packet, supportsBinary, callback);
+ }
+
+ // might be an object with { base64: true, data: dataAsBase64String }
+ if (data && data.base64) {
+ return encodeBase64Object(packet, callback);
+ }
+
+ // Sending data as a utf-8 string
+ var encoded = packets[packet.type];
+
+ // data fragment is optional
+ if (undefined !== packet.data) {
+ encoded += utf8encode ? utf8.encode(String(packet.data)) : String(packet.data);
+ }
+
+ return callback('' + encoded);
+
+};
+
+function encodeBase64Object(packet, callback) {
+ // packet data is an object { base64: true, data: dataAsBase64String }
+ var message = 'b' + exports.packets[packet.type] + packet.data.data;
+ return callback(message);
+}
+
+/**
+ * Encode packet helpers for binary types
+ */
+
+function encodeArrayBuffer(packet, supportsBinary, callback) {
+ if (!supportsBinary) {
+ return exports.encodeBase64Packet(packet, callback);
+ }
+
+ var data = packet.data;
+ var contentArray = new Uint8Array(data);
+ var resultBuffer = new Uint8Array(1 + data.byteLength);
+
+ resultBuffer[0] = packets[packet.type];
+ for (var i = 0; i < contentArray.length; i++) {
+ resultBuffer[i+1] = contentArray[i];
+ }
+
+ return callback(resultBuffer.buffer);
+}
+
+function encodeBlobAsArrayBuffer(packet, supportsBinary, callback) {
+ if (!supportsBinary) {
+ return exports.encodeBase64Packet(packet, callback);
+ }
+
+ var fr = new FileReader();
+ fr.onload = function() {
+ packet.data = fr.result;
+ exports.encodePacket(packet, supportsBinary, true, callback);
+ };
+ return fr.readAsArrayBuffer(packet.data);
+}
+
+function encodeBlob(packet, supportsBinary, callback) {
+ if (!supportsBinary) {
+ return exports.encodeBase64Packet(packet, callback);
+ }
+
+ if (dontSendBlobs) {
+ return encodeBlobAsArrayBuffer(packet, supportsBinary, callback);
+ }
+
+ var length = new Uint8Array(1);
+ length[0] = packets[packet.type];
+ var blob = new Blob([length.buffer, packet.data]);
+
+ return callback(blob);
+}
+
+/**
+ * Encodes a packet with binary data in a base64 string
+ *
+ * @param {Object} packet, has `type` and `data`
+ * @return {String} base64 encoded message
+ */
+
+exports.encodeBase64Packet = function(packet, callback) {
+ var message = 'b' + exports.packets[packet.type];
+ if (Blob && packet.data instanceof Blob) {
+ var fr = new FileReader();
+ fr.onload = function() {
+ var b64 = fr.result.split(',')[1];
+ callback(message + b64);
+ };
+ return fr.readAsDataURL(packet.data);
+ }
+
+ var b64data;
+ try {
+ b64data = String.fromCharCode.apply(null, new Uint8Array(packet.data));
+ } catch (e) {
+ // iPhone Safari doesn't let you apply with typed arrays
+ var typed = new Uint8Array(packet.data);
+ var basic = new Array(typed.length);
+ for (var i = 0; i < typed.length; i++) {
+ basic[i] = typed[i];
+ }
+ b64data = String.fromCharCode.apply(null, basic);
+ }
+ message += global.btoa(b64data);
+ return callback(message);
+};
+
+/**
+ * Decodes a packet. Changes format to Blob if requested.
+ *
+ * @return {Object} with `type` and `data` (if any)
+ * @api private
+ */
+
+exports.decodePacket = function (data, binaryType, utf8decode) {
+ // String data
+ if (typeof data == 'string' || data === undefined) {
+ if (data.charAt(0) == 'b') {
+ return exports.decodeBase64Packet(data.substr(1), binaryType);
+ }
+
+ if (utf8decode) {
+ try {
+ data = utf8.decode(data);
+ } catch (e) {
+ return err;
+ }
+ }
+ var type = data.charAt(0);
+
+ if (Number(type) != type || !packetslist[type]) {
+ return err;
+ }
+
+ if (data.length > 1) {
+ return { type: packetslist[type], data: data.substring(1) };
+ } else {
+ return { type: packetslist[type] };
+ }
+ }
+
+ var asArray = new Uint8Array(data);
+ var type = asArray[0];
+ var rest = sliceBuffer(data, 1);
+ if (Blob && binaryType === 'blob') {
+ rest = new Blob([rest]);
+ }
+ return { type: packetslist[type], data: rest };
+};
+
+/**
+ * Decodes a packet encoded in a base64 string
+ *
+ * @param {String} base64 encoded message
+ * @return {Object} with `type` and `data` (if any)
+ */
+
+exports.decodeBase64Packet = function(msg, binaryType) {
+ var type = packetslist[msg.charAt(0)];
+ if (!global.ArrayBuffer) {
+ return { type: type, data: { base64: true, data: msg.substr(1) } };
+ }
+
+ var data = base64encoder.decode(msg.substr(1));
+
+ if (binaryType === 'blob' && Blob) {
+ data = new Blob([data]);
+ }
+
+ return { type: type, data: data };
+};
+
+/**
+ * Encodes multiple messages (payload).
+ *
+ * <length>:data
+ *
+ * Example:
+ *
+ * 11:hello world2:hi
+ *
+ * If any contents are binary, they will be encoded as base64 strings. Base64
+ * encoded strings are marked with a b before the length specifier
+ *
+ * @param {Array} packets
+ * @api private
+ */
+
+exports.encodePayload = function (packets, supportsBinary, callback) {
+ if (typeof supportsBinary == 'function') {
+ callback = supportsBinary;
+ supportsBinary = null;
+ }
+
+ var isBinary = hasBinary(packets);
+
+ if (supportsBinary && isBinary) {
+ if (Blob && !dontSendBlobs) {
+ return exports.encodePayloadAsBlob(packets, callback);
+ }
+
+ return exports.encodePayloadAsArrayBuffer(packets, callback);
+ }
+
+ if (!packets.length) {
+ return callback('0:');
+ }
+
+ function setLengthHeader(message) {
+ return message.length + ':' + message;
+ }
+
+ function encodeOne(packet, doneCallback) {
+ exports.encodePacket(packet, !isBinary ? false : supportsBinary, true, function(message) {
+ doneCallback(null, setLengthHeader(message));
+ });
+ }
+
+ map(packets, encodeOne, function(err, results) {
+ return callback(results.join(''));
+ });
+};
+
+/**
+ * Async array map using after
+ */
+
+function map(ary, each, done) {
+ var result = new Array(ary.length);
+ var next = after(ary.length, done);
+
+ var eachWithIndex = function(i, el, cb) {
+ each(el, function(error, msg) {
+ result[i] = msg;
+ cb(error, result);
+ });
+ };
+
+ for (var i = 0; i < ary.length; i++) {
+ eachWithIndex(i, ary[i], next);
+ }
+}
+
+/*
+ * Decodes data when a payload is maybe expected. Possible binary contents are
+ * decoded from their base64 representation
+ *
+ * @param {String} data, callback method
+ * @api public
+ */
+
+exports.decodePayload = function (data, binaryType, callback) {
+ if (typeof data != 'string') {
+ return exports.decodePayloadAsBinary(data, binaryType, callback);
+ }
+
+ if (typeof binaryType === 'function') {
+ callback = binaryType;
+ binaryType = null;
+ }
+
+ var packet;
+ if (data == '') {
+ // parser error - ignoring payload
+ return callback(err, 0, 1);
+ }
+
+ var length = ''
+ , n, msg;
+
+ for (var i = 0, l = data.length; i < l; i++) {
+ var chr = data.charAt(i);
+
+ if (':' != chr) {
+ length += chr;
+ } else {
+ if ('' == length || (length != (n = Number(length)))) {
+ // parser error - ignoring payload
+ return callback(err, 0, 1);
+ }
+
+ msg = data.substr(i + 1, n);
+
+ if (length != msg.length) {
+ // parser error - ignoring payload
+ return callback(err, 0, 1);
+ }
+
+ if (msg.length) {
+ packet = exports.decodePacket(msg, binaryType, true);
+
+ if (err.type == packet.type && err.data == packet.data) {
+ // parser error in individual packet - ignoring payload
+ return callback(err, 0, 1);
+ }
+
+ var ret = callback(packet, i + n, l);
+ if (false === ret) return;
+ }
+
+ // advance cursor
+ i += n;
+ length = '';
+ }
+ }
+
+ if (length != '') {
+ // parser error - ignoring payload
+ return callback(err, 0, 1);
+ }
+
+};
+
+/**
+ * Encodes multiple messages (payload) as binary.
+ *
+ * <1 = binary, 0 = string><number from 0-9><number from 0-9>[...]<number
+ * 255><data>
+ *
+ * Example:
+ * 1 3 255 1 2 3, if the binary contents are interpreted as 8 bit integers
+ *
+ * @param {Array} packets
+ * @return {ArrayBuffer} encoded payload
+ * @api private
+ */
+
+exports.encodePayloadAsArrayBuffer = function(packets, callback) {
+ if (!packets.length) {
+ return callback(new ArrayBuffer(0));
+ }
+
+ function encodeOne(packet, doneCallback) {
+ exports.encodePacket(packet, true, true, function(data) {
+ return doneCallback(null, data);
+ });
+ }
+
+ map(packets, encodeOne, function(err, encodedPackets) {
+ var totalLength = encodedPackets.reduce(function(acc, p) {
+ var len;
+ if (typeof p === 'string'){
+ len = p.length;
+ } else {
+ len = p.byteLength;
+ }
+ return acc + len.toString().length + len + 2; // string/binary identifier + separator = 2
+ }, 0);
+
+ var resultArray = new Uint8Array(totalLength);
+
+ var bufferIndex = 0;
+ encodedPackets.forEach(function(p) {
+ var isString = typeof p === 'string';
+ var ab = p;
+ if (isString) {
+ var view = new Uint8Array(p.length);
+ for (var i = 0; i < p.length; i++) {
+ view[i] = p.charCodeAt(i);
+ }
+ ab = view.buffer;
+ }
+
+ if (isString) { // not true binary
+ resultArray[bufferIndex++] = 0;
+ } else { // true binary
+ resultArray[bufferIndex++] = 1;
+ }
+
+ var lenStr = ab.byteLength.toString();
+ for (var i = 0; i < lenStr.length; i++) {
+ resultArray[bufferIndex++] = parseInt(lenStr[i]);
+ }
+ resultArray[bufferIndex++] = 255;
+
+ var view = new Uint8Array(ab);
+ for (var i = 0; i < view.length; i++) {
+ resultArray[bufferIndex++] = view[i];
+ }
+ });
+
+ return callback(resultArray.buffer);
+ });
+};
+
+/**
+ * Encode as Blob
+ */
+
+exports.encodePayloadAsBlob = function(packets, callback) {
+ function encodeOne(packet, doneCallback) {
+ exports.encodePacket(packet, true, true, function(encoded) {
+ var binaryIdentifier = new Uint8Array(1);
+ binaryIdentifier[0] = 1;
+ if (typeof encoded === 'string') {
+ var view = new Uint8Array(encoded.length);
+ for (var i = 0; i < encoded.length; i++) {
+ view[i] = encoded.charCodeAt(i);
+ }
+ encoded = view.buffer;
+ binaryIdentifier[0] = 0;
+ }
+
+ var len = (encoded instanceof ArrayBuffer)
+ ? encoded.byteLength
+ : encoded.size;
+
+ var lenStr = len.toString();
+ var lengthAry = new Uint8Array(lenStr.length + 1);
+ for (var i = 0; i < lenStr.length; i++) {
+ lengthAry[i] = parseInt(lenStr[i]);
+ }
+ lengthAry[lenStr.length] = 255;
+
+ if (Blob) {
+ var blob = new Blob([binaryIdentifier.buffer, lengthAry.buffer, encoded]);
+ doneCallback(null, blob);
+ }
+ });
+ }
+
+ map(packets, encodeOne, function(err, results) {
+ return callback(new Blob(results));
+ });
+};
+
+/*
+ * Decodes data when a payload is maybe expected. Strings are decoded by
+ * interpreting each byte as a key code for entries marked to start with 0. See
+ * description of encodePayloadAsBinary
+ *
+ * @param {ArrayBuffer} data, callback method
+ * @api public
+ */
+
+exports.decodePayloadAsBinary = function (data, binaryType, callback) {
+ if (typeof binaryType === 'function') {
+ callback = binaryType;
+ binaryType = null;
+ }
+
+ var bufferTail = data;
+ var buffers = [];
+
+ var numberTooLong = false;
+ while (bufferTail.byteLength > 0) {
+ var tailArray = new Uint8Array(bufferTail);
+ var isString = tailArray[0] === 0;
+ var msgLength = '';
+
+ for (var i = 1; ; i++) {
+ if (tailArray[i] == 255) break;
+
+ if (msgLength.length > 310) {
+ numberTooLong = true;
+ break;
+ }
+
+ msgLength += tailArray[i];
+ }
+
+ if(numberTooLong) return callback(err, 0, 1);
+
+ bufferTail = sliceBuffer(bufferTail, 2 + msgLength.length);
+ msgLength = parseInt(msgLength);
+
+ var msg = sliceBuffer(bufferTail, 0, msgLength);
+ if (isString) {
+ try {
+ msg = String.fromCharCode.apply(null, new Uint8Array(msg));
+ } catch (e) {
+ // iPhone Safari doesn't let you apply to typed arrays
+ var typed = new Uint8Array(msg);
+ msg = '';
+ for (var i = 0; i < typed.length; i++) {
+ msg += String.fromCharCode(typed[i]);
+ }
+ }
+ }
+
+ buffers.push(msg);
+ bufferTail = sliceBuffer(bufferTail, msgLength);
+ }
+
+ var total = buffers.length;
+ buffers.forEach(function(buffer, i) {
+ callback(exports.decodePacket(buffer, binaryType, true), i, total);
+ });
+};
diff --git a/lib/index.js b/lib/index.js
new file mode 100644
index 0000000..bd7ea9a
--- /dev/null
+++ b/lib/index.js
@@ -0,0 +1,467 @@
+/**
+ * Module dependencies.
+ */
+
+var utf8 = require('utf8');
+var after = require('after');
+var keys = require('./keys');
+
+/**
+ * Current protocol version.
+ */
+exports.protocol = 3;
+
+/**
+ * Packet types.
+ */
+
+var packets = exports.packets = {
+ open: 0 // non-ws
+ , close: 1 // non-ws
+ , ping: 2
+ , pong: 3
+ , message: 4
+ , upgrade: 5
+ , noop: 6
+};
+
+var packetslist = keys(packets);
+
+/**
+ * Premade error packet.
+ */
+
+var err = { type: 'error', data: 'parser error' };
+
+/**
+ * Encodes a packet.
+ *
+ * <packet type id> [ <data> ]
+ *
+ * Example:
+ *
+ * 5hello world
+ * 3
+ * 4
+ *
+ * Binary is encoded in an identical principle
+ *
+ * @api private
+ */
+
+exports.encodePacket = function (packet, supportsBinary, utf8encode, callback) {
+ if ('function' == typeof supportsBinary) {
+ callback = supportsBinary;
+ supportsBinary = null;
+ }
+
+ if ('function' == typeof utf8encode ) {
+ callback = utf8encode;
+ utf8encode = null;
+ }
+
+ var data = (packet.data === undefined)
+ ? undefined
+ : packet.data.buffer || packet.data;
+
+ if (Buffer.isBuffer(data)) {
+ return encodeBuffer(packet, supportsBinary, callback);
+ } else if (data instanceof ArrayBuffer) {
+ return encodeArrayBuffer(packet, supportsBinary, callback);
+ }
+
+ // Sending data as a utf-8 string
+ var encoded = packets[packet.type];
+
+ // data fragment is optional
+ if (undefined !== packet.data) {
+ encoded += utf8encode ? utf8.encode(String(packet.data)) : String(packet.data);
+ }
+
+ return callback('' + encoded);
+};
+
+/**
+ * Encode Buffer data
+ */
+
+function encodeBuffer(packet, supportsBinary, callback) {
+ var data = packet.data;
+ if (!supportsBinary) {
+ return exports.encodeBase64Packet(packet, callback);
+ }
+
+ var typeBuffer = new Buffer(1);
+ typeBuffer[0] = packets[packet.type];
+ return callback(Buffer.concat([typeBuffer, data]));
+}
+
+function encodeArrayBuffer(packet, supportsBinary, callback) {
+ var data = (packet.data === undefined)
+ ? undefined
+ : packet.data.buffer || packet.data;
+
+ if (!supportsBinary) {
+ return exports.encodeBase64Packet(packet, callback);
+ }
+
+ var contentArray = new Uint8Array(data);
+ var resultBuffer = new Buffer(1 + data.byteLength);
+
+ resultBuffer[0] = packets[packet.type];
+ for (var i = 0; i < contentArray.length; i++){
+ resultBuffer[i+1] = contentArray[i];
+ }
+ return callback(resultBuffer);
+}
+
+/**
+ * Encodes a packet with binary data in a base64 string
+ *
+ * @param {Object} packet, has `type` and `data`
+ * @return {String} base64 encoded message
+ */
+
+exports.encodeBase64Packet = function(packet, callback){
+ var data = packet.data.buffer || packet.data;
+ if (data instanceof ArrayBuffer) {
+ var buf = new Buffer(data.byteLength);
+ for (var i = 0; i < buf.length; i++) {
+ buf[i] = data[i];
+ }
+ packet.data = buf;
+ }
+
+ var message = 'b' + packets[packet.type];
+ message += packet.data.toString('base64');
+ return callback(message);
+};
+
+/**
+ * Decodes a packet. Data also available as an ArrayBuffer if requested.
+ *
+ * @return {Object} with `type` and `data` (if any)
+ * @api private
+ */
+
+exports.decodePacket = function (data, binaryType, utf8decode) {
+ // String data
+ if (typeof data == 'string' || data === undefined) {
+ if (data.charAt(0) == 'b') {
+ return exports.decodeBase64Packet(data.substr(1), binaryType);
+ }
+
+ var type = data.charAt(0);
+ if (utf8decode) {
+ try {
+ data = utf8.decode(data);
+ } catch (e) {
+ return err;
+ }
+ }
+
+ if (Number(type) != type || !packetslist[type]) {
+ return err;
+ }
+
+ if (data.length > 1) {
+ return { type: packetslist[type], data: data.substring(1) };
+ } else {
+ return { type: packetslist[type] };
+ }
+ }
+
+ // Binary data
+ if (binaryType === 'arraybuffer') {
+ var type = data[0];
+ var intArray = new Uint8Array(data.length - 1);
+ for (var i = 1; i < data.length; i++) {
+ intArray[i - 1] = data[i];
+ }
+ return { type: packetslist[type], data: intArray.buffer };
+ }
+ var type = data[0];
+ return { type: packetslist[type], data: data.slice(1) };
+};
+
+/**
+ * Decodes a packet encoded in a base64 string.
+ *
+ * @param {String} base64 encoded message
+ * @return {Object} with `type` and `data` (if any)
+ */
+
+exports.decodeBase64Packet = function(msg, binaryType) {
+ var type = packetslist[msg.charAt(0)];
+ var data = new Buffer(msg.substr(1), 'base64');
+ if (binaryType === 'arraybuffer') {
+ var abv = new Uint8Array(data.length);
+ for (var i = 0; i < abv.length; i++){
+ abv[i] = data[i];
+ }
+ data = abv.buffer;
+ }
+ return { type: type, data: data };
+};
+
+/**
+ * Encodes multiple messages (payload).
+ *
+ * <length>:data
+ *
+ * Example:
+ *
+ * 11:hello world2:hi
+ *
+ * If any contents are binary, they will be encoded as base64 strings. Base64
+ * encoded strings are marked with a b before the length specifier
+ *
+ * @param {Array} packets
+ * @api private
+ */
+
+exports.encodePayload = function (packets, supportsBinary, callback) {
+ if (typeof supportsBinary == 'function') {
+ callback = supportsBinary;
+ supportsBinary = null;
+ }
+
+ if (supportsBinary) {
+ return exports.encodePayloadAsBinary(packets, callback);
+ }
+
+ if (!packets.length) {
+ return callback('0:');
+ }
+
+ function setLengthHeader(message) {
+ return message.length + ':' + message;
+ }
+
+ function encodeOne(packet, doneCallback) {
+ exports.encodePacket(packet, supportsBinary, true, function(message) {
+ doneCallback(null, setLengthHeader(message));
+ });
+ }
+
+ map(packets, encodeOne, function(err, results) {
+ return callback(results.join(''));
+ });
+};
+
+/**
+ * Async array map using after
+ */
+
+function map(ary, each, done) {
+ var result = new Array(ary.length);
+ var next = after(ary.length, done);
+
+ var eachWithIndex = function(i, el, cb) {
+ each(el, function(error, msg) {
+ result[i] = msg;
+ cb(error, result);
+ });
+ };
+
+ for (var i = 0; i < ary.length; i++) {
+ eachWithIndex(i, ary[i], next);
+ }
+}
+
+/*
+ * Decodes data when a payload is maybe expected. Possible binary contents are
+ * decoded from their base64 representation
+ *
+ * @param {String} data, callback method
+ * @api public
+ */
+
+exports.decodePayload = function (data, binaryType, callback) {
+ if ('string' != typeof data) {
+ return exports.decodePayloadAsBinary(data, binaryType, callback);
+ }
+
+ if (typeof binaryType === 'function') {
+ callback = binaryType;
+ binaryType = null;
+ }
+
+ var packet;
+ if (data == '') {
+ // parser error - ignoring payload
+ return callback(err, 0, 1);
+ }
+
+ var length = ''
+ , n, msg;
+
+ for (var i = 0, l = data.length; i < l; i++) {
+ var chr = data.charAt(i);
+
+ if (':' != chr) {
+ length += chr;
+ } else {
+ if ('' == length || (length != (n = Number(length)))) {
+ // parser error - ignoring payload
+ return callback(err, 0, 1);
+ }
+
+ msg = data.substr(i + 1, n);
+
+ if (length != msg.length) {
+ // parser error - ignoring payload
+ return callback(err, 0, 1);
+ }
+
+ if (msg.length) {
+ packet = exports.decodePacket(msg, binaryType, true);
+
+ if (err.type == packet.type && err.data == packet.data) {
+ // parser error in individual packet - ignoring payload
+ return callback(err, 0, 1);
+ }
+
+ var ret = callback(packet, i + n, l);
+ if (false === ret) return;
+ }
+
+ // advance cursor
+ i += n;
+ length = '';
+ }
+ }
+
+ if (length != '') {
+ // parser error - ignoring payload
+ return callback(err, 0, 1);
+ }
+
+};
+
+/**
+ *
+ * Converts a buffer to a utf8.js encoded string
+ *
+ * @api private
+ */
+
+function bufferToString(buffer) {
+ var str = '';
+ for (var i = 0; i < buffer.length; i++) {
+ str += String.fromCharCode(buffer[i]);
+ }
+ return str;
+}
+
+/**
+ *
+ * Converts a utf8.js encoded string to a buffer
+ *
+ * @api private
+ */
+
+function stringToBuffer(string) {
+ var buf = new Buffer(string.length);
+ for (var i = 0; i < string.length; i++) {
+ buf.writeUInt8(string.charCodeAt(i), i);
+ }
+ return buf;
+}
+
+/**
+ * Encodes multiple messages (payload) as binary.
+ *
+ * <1 = binary, 0 = string><number from 0-9><number from 0-9>[...]<number
+ * 255><data>
+ *
+ * Example:
+ * 1 3 255 1 2 3, if the binary contents are interpreted as 8 bit integers
+ *
+ * @param {Array} packets
+ * @return {Buffer} encoded payload
+ * @api private
+ */
+
+exports.encodePayloadAsBinary = function (packets, callback) {
+ if (!packets.length) {
+ return callback(new Buffer(0));
+ }
+
+ function encodeOne(p, doneCallback) {
+ exports.encodePacket(p, true, true, function(packet) {
+
+ if (typeof packet === 'string') {
+ var encodingLength = '' + packet.length;
+ var sizeBuffer = new Buffer(encodingLength.length + 2);
+ sizeBuffer[0] = 0; // is a string (not true binary = 0)
+ for (var i = 0; i < encodingLength.length; i++) {
+ sizeBuffer[i + 1] = parseInt(encodingLength[i], 10);
+ }
+ sizeBuffer[sizeBuffer.length - 1] = 255;
+ return doneCallback(null, Buffer.concat([sizeBuffer, stringToBuffer(packet)]));
+ }
+
+ var encodingLength = '' + packet.length;
+ var sizeBuffer = new Buffer(encodingLength.length + 2);
+ sizeBuffer[0] = 1; // is binary (true binary = 1)
+ for (var i = 0; i < encodingLength.length; i++) {
+ sizeBuffer[i + 1] = parseInt(encodingLength[i], 10);
+ }
+ sizeBuffer[sizeBuffer.length - 1] = 255;
+ doneCallback(null, Buffer.concat([sizeBuffer, packet]));
+ });
+ }
+
+ map(packets, encodeOne, function(err, results) {
+ return callback(Buffer.concat(results));
+ });
+};
+
+/*
+ * Decodes data when a payload is maybe expected. Strings are decoded by
+ * interpreting each byte as a key code for entries marked to start with 0. See
+ * description of encodePayloadAsBinary
+
+ * @param {Buffer} data, callback method
+ * @api public
+ */
+
+exports.decodePayloadAsBinary = function (data, binaryType, callback) {
+ if (typeof binaryType === 'function') {
+ callback = binaryType;
+ binaryType = null;
+ }
+
+ var bufferTail = data;
+ var buffers = [];
+
+ while (bufferTail.length > 0) {
+ var strLen = '';
+ var isString = bufferTail[0] === 0;
+ var numberTooLong = false;
+ for (var i = 1; ; i++) {
+ if (bufferTail[i] == 255) break;
+ // 310 = char length of Number.MAX_VALUE
+ if (strLen.length > 310) {
+ numberTooLong = true;
+ break;
+ }
+ strLen += '' + bufferTail[i];
+ }
+ if(numberTooLong) return callback(err, 0, 1);
+ bufferTail = bufferTail.slice(strLen.length + 1);
+
+ var msgLength = parseInt(strLen, 10);
+
+ var msg = bufferTail.slice(1, msgLength + 1);
+ if (isString) msg = bufferToString(msg);
+ buffers.push(msg);
+ bufferTail = bufferTail.slice(msgLength + 1);
+ }
+
+ var total = buffers.length;
+ buffers.forEach(function(buffer, i) {
+ callback(exports.decodePacket(buffer, binaryType, true), i, total);
+ });
+};
diff --git a/lib/keys.js b/lib/keys.js
new file mode 100644
index 0000000..947dafd
--- /dev/null
+++ b/lib/keys.js
@@ -0,0 +1,19 @@
+
+/**
+ * Gets the keys for an object.
+ *
+ * @return {Array} keys
+ * @api private
+ */
+
+module.exports = Object.keys || function keys (obj){
+ var arr = [];
+ var has = Object.prototype.hasOwnProperty;
+
+ for (var i in obj) {
+ if (has.call(obj, i)) {
+ arr.push(i);
+ }
+ }
+ return arr;
+};
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..61dce88
--- /dev/null
+++ b/package.json
@@ -0,0 +1,33 @@
+{
+ "name": "engine.io-parser",
+ "description": "Parser for the client for the realtime Engine",
+ "version": "1.2.1",
+ "homepage": "https://github.com/LearnBoost/engine.io-protocol",
+ "devDependencies": {
+ "expect.js": "0.3.1",
+ "mocha": "2.1.0",
+ "zuul": "1.10.2"
+ },
+ "dependencies": {
+ "after": "0.8.1",
+ "arraybuffer.slice": "0.0.6",
+ "base64-arraybuffer": "0.1.2",
+ "blob": "0.0.2",
+ "has-binary": "0.1.5",
+ "utf8": "2.0.0"
+ },
+ "scripts": {
+ "test": "make test"
+ },
+ "component": {
+ "scripts": {
+ "engine.io-parser/index.js": "lib/index.js",
+ "engine.io-parser/keys.js": "lib/keys.js"
+ }
+ },
+ "repository": {
+ "type": "git",
+ "url": "git at github.com:LearnBoost/engine.io-parser.git"
+ },
+ "browser": "./lib/browser.js"
+}
diff --git a/test/browser/arraybuffer.js b/test/browser/arraybuffer.js
new file mode 100644
index 0000000..003e2db
--- /dev/null
+++ b/test/browser/arraybuffer.js
@@ -0,0 +1,84 @@
+
+/**
+ * Test dependencies.
+ */
+
+var parser = require('../../lib/browser.js');
+var expect = require('expect.js');
+
+/**
+ * Shortcuts
+ */
+
+var encode = parser.encodePacket;
+var decode = parser.decodePacket;
+var encPayload = parser.encodePayload;
+var decPayload = parser.decodePayload;
+var encPayloadAB = parser.encodePayloadAsArrayBuffer;
+var decPayloadB = parser.decodePayloadAsBinary;
+
+/**
+ * Tests.
+ */
+
+describe('parser', function() {
+ it('should encode/decode mixed binary and string contents as b64', function(done) {
+ var data = new Int8Array(5);
+ for (var i = 0; i < data.length; i++) data[i] = i;
+ encPayload([{ type: 'message', data: data.buffer }, { type: 'message', data: 'hello' }], function(encoded) {
+ decPayload(encoded,
+ function(packet, index, total) {
+ var isLast = index + 1 == total;
+ expect(packet.type).to.eql('message');
+ if (!isLast) {
+ expect(new Int8Array(packet.data)).to.eql(data);
+ } else {
+ expect(packet.data).to.eql('hello');
+ done();
+ }
+ });
+ });
+ });
+
+ it('should encode binary contents as arraybuffer', function(done) {
+ var firstBuffer = new Int8Array(5);
+ for (var i = 0; i < firstBuffer.length; i++) firstBuffer[i] = i;
+ var secondBuffer = new Int8Array(4);
+ for (var i = 0; i < secondBuffer.length; i++) secondBuffer[i] = firstBuffer.length + i;
+
+ encPayloadAB([{ type: 'message', data: firstBuffer.buffer }, { type: 'message', data: secondBuffer.buffer }], function(data) {
+ decPayloadB(data,
+ function(packet, index, total) {
+ var isLast = index + 1 == total;
+ expect(packet.type).to.eql('message');
+ if (!isLast) {
+ expect(new Int8Array(packet.data)).to.eql(firstBuffer);
+ } else {
+ expect(new Int8Array(packet.data)).to.eql(secondBuffer);
+ done();
+ }
+ });
+ });
+ });
+
+ it('should encode mixed binary and string contents as arraybuffer', function(done) {
+ var firstBuffer = new Int8Array(123);
+ for (var i = 0; i < firstBuffer.length; i++) firstBuffer[i] = i;
+
+ encPayloadAB([{ type: 'message', data: firstBuffer.buffer }, { type: 'message', data: 'hello' }, { type: 'close' } ], function(data) {
+ decPayloadB(data,
+ function(packet, index, total) {
+ if (index == 0) {
+ expect(packet.type).to.eql('message');
+ expect(new Int8Array(packet.data)).to.eql(firstBuffer);
+ } else if (index == 1) {
+ expect(packet.type).to.eql('message');
+ expect(packet.data).to.eql('hello');
+ } else {
+ expect(packet.type).to.eql('close');
+ done();
+ }
+ });
+ });
+ });
+});
diff --git a/test/browser/base64_object.js b/test/browser/base64_object.js
new file mode 100644
index 0000000..f9ec5d2
--- /dev/null
+++ b/test/browser/base64_object.js
@@ -0,0 +1,41 @@
+
+/**
+ * Test dependencies.
+ */
+
+var parser = require('../../lib/browser.js');
+var expect = require('expect.js');
+
+/**
+ * Shortcuts
+ */
+var encPayload = parser.encodePayload;
+var decPayload = parser.decodePayload;
+
+/**
+ * Tests.
+ */
+describe('parser', function() {
+ it('should encode/decode mixed base64 object and string', function(done) {
+ var data = new Buffer(5);
+ for (var i = 0; i < data.length; i++) data[i] = i;
+ var msg = { base64: true, data: new Buffer(data).toString('base64') };
+ encPayload([{ type: 'message', data: msg }, { type: 'message', data: 'hello' }], function(encoded) {
+ decPayload(encoded,
+ function(packet, index, total) {
+ var isLast = index + 1 == total;
+ expect(packet.type).to.eql('message');
+ if (!isLast) {
+ if (!global.ArrayBuffer) {
+ expect(packet.data).to.eql(msg);
+ } else {
+ expect(new Int8Array(packet.data)).to.eql(new Int8Array(data));
+ }
+ } else {
+ expect(packet.data).to.eql('hello');
+ done();
+ }
+ });
+ });
+ });
+});
diff --git a/test/browser/blob.js b/test/browser/blob.js
new file mode 100644
index 0000000..1977aaa
--- /dev/null
+++ b/test/browser/blob.js
@@ -0,0 +1,74 @@
+
+/**
+ * Test dependencies.
+ */
+
+var parser = require('../../lib/browser.js');
+var expect = require('expect.js');
+
+/**
+ * Shortcuts
+ */
+
+var encode = parser.encodePacket;
+var decode = parser.decodePacket;
+var encPayload = parser.encodePayload;
+var decPayload = parser.decodePayload;
+var encPayloadB = parser.encodePayloadAsBlob;
+var decPayloadB = parser.decodePayloadAsBinary;
+
+/**
+ * Tests.
+ */
+
+describe('parser', function() {
+ it('should encode binary contents as blob', function(done) {
+ var firstBuffer = new Int8Array(5);
+ for (var i = 0; i < firstBuffer.length; i++) firstBuffer[i] = i;
+ var secondBuffer = new Int8Array(4);
+ for (var i = 0; i < secondBuffer.length; i++) secondBuffer[i] = firstBuffer.length + i;
+
+ encPayloadB([{ type: 'message', data: firstBuffer.buffer }, { type: 'message', data: secondBuffer.buffer }], function(data) {
+ var fr = new FileReader();
+ fr.onload = function() {
+ decPayloadB(this.result,
+ function(packet, index, total) {
+ var isLast = index + 1 == total;
+ expect(packet.type).to.eql('message');
+ if (!isLast) {
+ expect(new Int8Array(packet.data)).to.eql(firstBuffer);
+ } else {
+ expect(new Int8Array(packet.data)).to.eql(secondBuffer);
+ done();
+ }
+ });
+ };
+ fr.readAsArrayBuffer(data);
+ });
+ });
+
+ it('should encode mixed binary and string contents as blob', function(done) {
+ var firstBuffer = new Int8Array(123);
+ for (var i = 0; i < firstBuffer.length; i++) firstBuffer[i] = i;
+
+ encPayloadB([{ type: 'message', data: firstBuffer.buffer }, { type: 'message', data: 'hello' }, { type: 'close' } ], function(data) {
+ var fr = new FileReader();
+ fr.onload = function() {
+ decPayloadB(this.result,
+ function(packet, index, total) {
+ if (index == 0) {
+ expect(packet.type).to.eql('message');
+ expect(new Int8Array(packet.data)).to.eql(firstBuffer);
+ } else if (index == 1) {
+ expect(packet.type).to.eql('message');
+ expect(packet.data).to.eql('hello');
+ } else {
+ expect(packet.type).to.eql('close');
+ done();
+ }
+ });
+ };
+ fr.readAsArrayBuffer(data);
+ });
+ });
+});
diff --git a/test/browser/index.js b/test/browser/index.js
new file mode 100644
index 0000000..7a60125
--- /dev/null
+++ b/test/browser/index.js
@@ -0,0 +1,29 @@
+var Blob = require('blob');
+
+if (global.ArrayBuffer) {
+ require('./arraybuffer.js');
+}
+
+if (Blob) {
+ require('./blob.js');
+}
+
+require('./base64_object.js');
+
+// General browser only tests
+var parser = require('../../');
+var encode = parser.encodePacket;
+var decode = parser.decodePacket;
+var encPayload = parser.encodePayload;
+var decPayload = parser.decodePayload;
+
+describe('basic functionality', function () {
+ it('should encode string payloads as strings even if binary supported', function (done) {
+ encPayload([{ type: 'ping' }, { type: 'post' }], true, function(data) {
+ expect(data).to.be.a('string');
+ done();
+ });
+ });
+});
+
+
diff --git a/test/index.js b/test/index.js
new file mode 100644
index 0000000..f8ca2ad
--- /dev/null
+++ b/test/index.js
@@ -0,0 +1,11 @@
+var env = require('./support/env');
+var parser = env.browser ? require('../lib/browser.js') : require('../lib');
+
+// General parsing tests and encoding/decoding strings
+require('./parser.js')(parser);
+
+if (env.browser) {
+ require('./browser');
+} else {
+ require('./node');
+}
diff --git a/test/node/index.js b/test/node/index.js
new file mode 100644
index 0000000..65ddc3e
--- /dev/null
+++ b/test/node/index.js
@@ -0,0 +1,92 @@
+
+/**
+ * Test dependencies.
+ */
+
+var parser = require('../../lib/');
+var expect = require('expect.js');
+
+/**
+ * Shortcuts
+ */
+var encode = parser.encodePacket;
+var decode = parser.decodePacket;
+var encPayload = parser.encodePayload;
+var decPayload = parser.decodePayload;
+var encPayloadB = parser.encodePayloadAsBinary;
+var decPayloadB = parser.decodePayloadAsBinary;
+
+/**
+ * Tests.
+ */
+
+describe('parser', function() {
+ it('should encode a binary message', function(done) {
+ var data = new Buffer(5);
+ for (var i = 0; i < data.length; i++) { data[i] = i; }
+ encode({ type: 'message', data: data }, function(encoded) {
+ expect(decode(encoded)).to.eql({ type: 'message', data: data });
+ done();
+ });
+ });
+
+ it('should encode/decode mixed binary and string contents as b64', function(done) {
+ var data = new Buffer(5);
+ for (var i = 0; i < data.length; i++) data[i] = i;
+ encPayload([{ type: 'message', data: data }, { type: 'message', data: 'hello' }], function(encoded) {
+ decPayload(encoded,
+ function(packet, index, total) {
+ var isLast = index + 1 == total;
+ expect(packet.type).to.eql('message');
+ if (!isLast) {
+ expect(packet.data).to.eql(data);
+ } else {
+ expect(packet.data).to.eql('hello');
+ done();
+ }
+ });
+ });
+ });
+
+ it('should encode binary contents as binary', function(done) {
+ var firstBuffer = new Buffer(5);
+ for (var i = 0; i < firstBuffer.length; i++) firstBuffer[i] = i;
+ var secondBuffer = new Buffer(4);
+ for (var i = 0; i < secondBuffer.length; i++) secondBuffer[i] = firstBuffer.length + i;
+
+ encPayloadB([{ type: 'message', data: firstBuffer }, { type: 'message', data: secondBuffer }], function(data) {
+ decPayloadB(data,
+ function(packet, index, total) {
+ var isLast = index + 1 == total;
+ expect(packet.type).to.eql('message');
+ if (!isLast) {
+ expect(packet.data).to.eql(firstBuffer);
+ } else {
+ expect(packet.data).to.eql(secondBuffer);
+ done();
+ }
+ });
+ });
+ });
+
+ it('should encode mixed binary and string contents as binary', function(done) {
+ var firstBuffer = new Buffer(123);
+ for (var i = 0; i < firstBuffer.length; i++) firstBuffer[i] = i;
+
+ encPayloadB([{ type: 'message', data: firstBuffer }, { type: 'message', data: 'hello' }, { type: 'close' } ], function(data) {
+ decPayloadB(data,
+ function(packet, index, total) {
+ if (index == 0) {
+ expect(packet.type).to.eql('message');
+ expect(packet.data).to.eql(firstBuffer);
+ } else if (index == 1) {
+ expect(packet.type).to.eql('message');
+ expect(packet.data).to.eql('hello');
+ } else {
+ expect(packet.type).to.eql('close');
+ done();
+ }
+ });
+ });
+ });
+});
diff --git a/test/parser.js b/test/parser.js
new file mode 100644
index 0000000..da852d8
--- /dev/null
+++ b/test/parser.js
@@ -0,0 +1,264 @@
+module.exports = function(parser) {
+ /**
+ * Test dependencies.
+ */
+
+ //var parser = require('../lib/');
+ var expect = require('expect.js');
+
+ /**
+ * Shortcuts
+ */
+
+ var encode = parser.encodePacket;
+ var decode = parser.decodePacket;
+ var encPayload = parser.encodePayload;
+ var decPayload = parser.decodePayload;
+
+ /**
+ * Tests.
+ */
+
+ describe('parser', function () {
+
+ describe('packets', function () {
+ describe('basic functionality', function () {
+ it('should encode packets as strings', function (done) {
+ encode({ type: 'message', data: 'test' }, function(data) {
+ expect(data).to.be.a('string');
+ done();
+ });
+ });
+
+ it('should decode packets as objects', function (done) {
+ encode({ type: 'message', data: 'test' }, function(data) {
+ expect(decode(data)).to.be.an('object');
+ done();
+ });
+ });
+ });
+
+ describe('encoding and decoding', function () {
+ it('should allow no data', function (done) {
+ encode({ type: 'message' }, function(data) {
+ expect(decode(data)).to.eql({ type: 'message' });
+ done();
+ });
+ });
+
+ it('should encode an open packet', function (done) {
+ encode({ type: 'open', data: '{"some":"json"}' }, function(data) {
+ expect(decode(data)).to.eql({ type: 'open', data: '{"some":"json"}' });
+ done();
+ });
+ });
+
+ it('should encode a close packet', function (done) {
+ encode({ type: 'close' }, function(data) {
+ expect(decode(data)).to.eql({ type: 'close' });
+ done();
+ });
+ });
+
+ it('should encode a ping packet', function (done) {
+ encode({ type: 'ping', data: '1' }, function(data) {
+ expect(decode(data)).to.eql({ type: 'ping', data: '1' });
+ done();
+ });
+ });
+
+ it('should encode a pong packet', function (done) {
+ encode({ type: 'pong', data: '1' }, function(data) {
+ expect(decode(data)).to.eql({ type: 'pong', data: '1' });
+ done();
+ });
+ });
+
+ it('should encode a message packet', function (done) {
+ encode({ type: 'message', data: 'aaa' }, function(data) {
+ expect(decode(data)).to.eql({ type: 'message', data: 'aaa' });
+ done();
+ });
+ });
+
+ it('should encode a utf8 special chars message packet', function (done) {
+ encode({ type: 'message', data: 'utf8 — string' }, function(data) {
+ expect(decode(data)).to.eql({ type: 'message', data: 'utf8 — string' });
+ done();
+ });
+ });
+
+ it('should not utf8 encode by default', function(done) {
+ encode({ type: 'message', data: '€€€' }, function(data) {
+ expect(data).to.be('4€€€');
+ done();
+ });
+ });
+
+ it('should not utf8 encode by default', function(done) {
+ encode({ type: 'message', data: '€€€' }, true, true, function(data) {
+ expect(data).to.be('4â¬â¬â¬');
+ done();
+ });
+ });
+
+ it('should encode a message packet coercing to string', function (done) {
+ encode({ type: 'message', data: 1 }, function(data) {
+ expect(decode(data)).to.eql({ type: 'message', data: 1 });
+ done();
+ });
+ });
+
+ it('should encode an upgrade packet', function (done) {
+ encode({ type: 'upgrade' }, function(data) {
+ expect(decode(data)).to.eql({ type: 'upgrade' });
+ done();
+ });
+ });
+
+ it('should match the encoding format', function () {
+ encode({ type: 'message', data: 'test' }, function(data) {
+ expect(data).to.match(/^[0-9]/);
+ });
+ encode({ type: 'message' }, function(data) {
+ expect(data).to.match(/^[0-9]$/);
+ });
+ });
+ });
+
+ describe('decoding error handing', function () {
+ var err = { type: 'error', data: 'parser error' };
+
+ it('should disallow bad format', function () {
+ expect(decode(':::')).to.eql(err);
+ });
+
+ it('should disallow inexistent types', function () {
+ expect(decode('94103')).to.eql(err);
+ });
+
+ it('should disallow invalid utf8', function () {
+ expect(decode('4\uffff', false, true)).to.eql(err);
+ });
+ });
+ });
+
+ describe('payloads', function () {
+ describe('basic functionality', function () {
+ it('should encode payloads as strings', function (done) {
+ encPayload([{ type: 'ping' }, { type: 'post' }], function(data) {
+ expect(data).to.be.a('string');
+ done();
+ });
+ });
+ });
+
+ describe('encoding and decoding', function () {
+ var seen = 0;
+ it('should encode/decode packets', function (done) {
+ encPayload([{ type: 'message', data: 'a' }], function(data) {
+ decPayload(data,
+ function(packet, index, total) {
+ var isLast = index + 1 == total;
+ expect(isLast).to.eql(true);
+ seen++;
+ });
+ });
+ encPayload([{type: 'message', data: 'a'}, {type: 'ping'}], function(data) {
+ decPayload(data,
+ function(packet, index, total) {
+ var isLast = index + 1 == total;
+ if (!isLast) {
+ expect(packet.type).to.eql('message');
+ } else {
+ expect(packet.type).to.eql('ping');
+ if (seen == 2) { done(); }
+ }
+ seen++;
+ });
+ });
+ });
+
+ it('should encode/decode empty payloads', function () {
+ encPayload([], function(data) {
+ decPayload(data,
+ function (packet, index, total) {
+ expect(packet.type).to.eql('open');
+ var isLast = index + 1 == total;
+ expect(isLast).to.eql(true);
+ });
+ });
+ });
+ });
+
+ describe('decoding error handling', function () {
+ var err = { type: 'error', data: 'parser error' };
+
+ it('should err on bad payload format', function () {
+ decPayload('1!', function (packet, index, total) {
+ var isLast = index + 1 == total;
+ expect(packet).to.eql(err);
+ expect(isLast).to.eql(true);
+ });
+ decPayload('', function (packet, index, total) {
+ var isLast = index + 1 == total;
+ expect(packet).to.eql(err);
+ expect(isLast).to.eql(true);
+ });
+ decPayload('))', function (packet, index, total) {
+ var isLast = index + 1 == total;
+ expect(packet).to.eql(err);
+ expect(isLast).to.eql(true);
+ });
+
+ if ('undefined' == typeof window || window.Int8Array) {
+ var data = 'undefined' !== typeof window ? (new Int8Array([64])).buffer : new Buffer([64]);
+ decPayload(data, function (packet, index, total) {
+ var isLast = index + 1 == total;
+ expect(packet).to.eql(err);
+ expect(isLast).to.eql(true);
+ });
+ }
+ });
+
+ it('should err on bad payload length', function () {
+ // line 137
+ decPayload('1:', function (packet, index, total) {
+ var isLast = index + 1 == total;
+ expect(packet).to.eql(err);
+ expect(isLast).to.eql(true);
+ });
+ });
+
+ it('should err on bad packet format', function () {
+ // line 137
+ decPayload('3:99:', function (packet, index, total) {
+ var isLast = index + 1 == total;
+ expect(packet).to.eql(err);
+ expect(isLast).to.eql(true);
+ });
+ // line 146
+ decPayload('1:aa', function (packet, index, total) {
+ var isLast = index + 1 == total;
+ expect(packet).to.eql(err);
+ expect(isLast).to.eql(true);
+ });
+ // line 137
+ decPayload('1:a2:b', function (packet, index, total) {
+ var isLast = index + 1 == total;
+ expect(packet).to.eql(err);
+ expect(isLast).to.eql(true);
+ });
+ });
+
+ it('should err on invalid utf8', function () {
+ decPayload('2:4\uffff', function (packet, index, total) {
+ var isLast = index + 1 == total;
+ expect(packet).to.eql(err);
+ expect(isLast).to.eql(true);
+ });
+ });
+ });
+ });
+ });
+};
diff --git a/test/support/env.js b/test/support/env.js
new file mode 100644
index 0000000..c1d494e
--- /dev/null
+++ b/test/support/env.js
@@ -0,0 +1,5 @@
+// WARNING this is bad practice
+// we only do this in our tests because we need to test engine.io-client
+// support in browsers and in node.js
+// some tests do not yet work in both
+module.exports.browser = !!global.window;
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-engine.io-parser.git
More information about the Pkg-javascript-commits
mailing list