diff --git a/browser/lib/util/bufferutils.js b/browser/lib/util/bufferutils.js index ba55818ce7..2f694d669d 100644 --- a/browser/lib/util/bufferutils.js +++ b/browser/lib/util/bufferutils.js @@ -3,8 +3,8 @@ var BufferUtils = (function() { var ArrayBuffer = window.ArrayBuffer; var TextDecoder = window.TextDecoder; - function isWordArray(ob) { return ob.sigBytes !== undefined; } - function isArrayBuffer(ob) { return ob.constructor === ArrayBuffer; } + function isWordArray(ob) { return ob !== null && ob !== undefined && ob.sigBytes !== undefined; } + function isArrayBuffer(ob) { return ob !== null && ob !== undefined && ob.constructor === ArrayBuffer; } // https://gist.githubusercontent.com/jonleighton/958841/raw/f200e30dfe95212c0165ccf1ae000ca51e9de803/gistfile1.js function arrayBufferToBase64(ArrayBuffer) { diff --git a/browser/static/ably.js b/browser/static/ably.js index 0a08cc5d67..c372f457be 100644 --- a/browser/static/ably.js +++ b/browser/static/ably.js @@ -2505,8 +2505,8 @@ var BufferUtils = (function() { var ArrayBuffer = window.ArrayBuffer; var TextDecoder = window.TextDecoder; - function isWordArray(ob) { return ob.sigBytes !== undefined; } - function isArrayBuffer(ob) { return ob.constructor === ArrayBuffer; } + function isWordArray(ob) { return ob !== null && ob !== undefined && ob.sigBytes !== undefined; } + function isArrayBuffer(ob) { return ob !== null && ob !== undefined && ob.constructor === ArrayBuffer; } // https://gist.githubusercontent.com/jonleighton/958841/raw/f200e30dfe95212c0165ccf1ae000ca51e9de803/gistfile1.js function arrayBufferToBase64(ArrayBuffer) { @@ -2626,6 +2626,7 @@ var BufferUtils = (function() { return BufferUtils; })(); + var Cookie = (function() { var isBrowser = (typeof(window) == 'object'); function noop() {} @@ -4143,6 +4144,11 @@ var Utils = (function() { return Object.prototype.toString.call(ob) == '[object Array]'; }; + /* ...Or an Object (in the narrow sense) */ + Utils.isObject = function(ob) { + return Object.prototype.toString.call(ob) == '[object Object]'; + }; + /* * Determine whether or not an object contains * any enumerable properties. @@ -4477,11 +4483,18 @@ var Message = (function() { }; Message.encode = function(msg, options) { - var data = msg.data, encoding; - if(data !== null && data !== undefined && typeof(data) != 'string' && !BufferUtils.isBuffer(data)) { - msg.data = JSON.stringify(data); - msg.encoding = (encoding = msg.encoding) ? (encoding + '/json') : 'json'; + var data = msg.data, encoding, + nativeDataType = typeof(data) == 'string' || BufferUtils.isBuffer(data) || data === null || data === undefined; + + if (!nativeDataType) { + if (Utils.isObject(data) || Utils.isArray(data)) { + msg.data = JSON.stringify(data); + msg.encoding = (encoding = msg.encoding) ? (encoding + '/json') : 'json'; + } else { + throw new ErrorInfo('Data type is unsupported', 40011, 400); + } } + if(options != null && options.encrypted) Message.encrypt(msg, options); }; diff --git a/browser/static/ably.noencryption.js b/browser/static/ably.noencryption.js index 9c63691fb9..0534c92b77 100644 --- a/browser/static/ably.noencryption.js +++ b/browser/static/ably.noencryption.js @@ -1157,8 +1157,8 @@ var BufferUtils = (function() { var ArrayBuffer = window.ArrayBuffer; var TextDecoder = window.TextDecoder; - function isWordArray(ob) { return ob.sigBytes !== undefined; } - function isArrayBuffer(ob) { return ob.constructor === ArrayBuffer; } + function isWordArray(ob) { return ob !== null && ob !== undefined && ob.sigBytes !== undefined; } + function isArrayBuffer(ob) { return ob !== null && ob !== undefined && ob.constructor === ArrayBuffer; } // https://gist.githubusercontent.com/jonleighton/958841/raw/f200e30dfe95212c0165ccf1ae000ca51e9de803/gistfile1.js function arrayBufferToBase64(ArrayBuffer) { @@ -1278,6 +1278,7 @@ var BufferUtils = (function() { return BufferUtils; })(); + var Cookie = (function() { var isBrowser = (typeof(window) == 'object'); function noop() {} @@ -2795,6 +2796,11 @@ var Utils = (function() { return Object.prototype.toString.call(ob) == '[object Array]'; }; + /* ...Or an Object (in the narrow sense) */ + Utils.isObject = function(ob) { + return Object.prototype.toString.call(ob) == '[object Object]'; + }; + /* * Determine whether or not an object contains * any enumerable properties. @@ -3129,11 +3135,18 @@ var Message = (function() { }; Message.encode = function(msg, options) { - var data = msg.data, encoding; - if(data !== null && data !== undefined && typeof(data) != 'string' && !BufferUtils.isBuffer(data)) { - msg.data = JSON.stringify(data); - msg.encoding = (encoding = msg.encoding) ? (encoding + '/json') : 'json'; + var data = msg.data, encoding, + nativeDataType = typeof(data) == 'string' || BufferUtils.isBuffer(data) || data === null || data === undefined; + + if (!nativeDataType) { + if (Utils.isObject(data) || Utils.isArray(data)) { + msg.data = JSON.stringify(data); + msg.encoding = (encoding = msg.encoding) ? (encoding + '/json') : 'json'; + } else { + throw new ErrorInfo('Data type is unsupported', 40011, 400); + } } + if(options != null && options.encrypted) Message.encrypt(msg, options); }; diff --git a/browser/static/compat-pusher.js b/browser/static/compat-pusher.js index 274ccb984c..4aef7835a1 100644 --- a/browser/static/compat-pusher.js +++ b/browser/static/compat-pusher.js @@ -189,6 +189,11 @@ var Utils = (function() { return Object.prototype.toString.call(ob) == '[object Array]'; }; + /* ...Or an Object (in the narrow sense) */ + Utils.isObject = function(ob) { + return Object.prototype.toString.call(ob) == '[object Object]'; + }; + /* * Determine whether or not an object contains * any enumerable properties. diff --git a/browser/static/iframe-0.8.0.html b/browser/static/iframe-0.8.0.html index 6a95a346f8..28900a9e8e 100644 --- a/browser/static/iframe-0.8.0.html +++ b/browser/static/iframe-0.8.0.html @@ -359,6 +359,11 @@ return Object.prototype.toString.call(ob) == '[object Array]'; }; + /* ...Or an Object (in the narrow sense) */ + Utils.isObject = function(ob) { + return Object.prototype.toString.call(ob) == '[object Object]'; + }; + /* * Determine whether or not an object contains * any enumerable properties. diff --git a/browser/static/iframe.js b/browser/static/iframe.js index c75c8a21ea..0f19511811 100644 --- a/browser/static/iframe.js +++ b/browser/static/iframe.js @@ -354,6 +354,11 @@ var Utils = (function() { return Object.prototype.toString.call(ob) == '[object Array]'; }; + /* ...Or an Object (in the narrow sense) */ + Utils.isObject = function(ob) { + return Object.prototype.toString.call(ob) == '[object Object]'; + }; + /* * Determine whether or not an object contains * any enumerable properties. diff --git a/common/lib/types/message.js b/common/lib/types/message.js index 6569bd4ff4..0a19eda0e8 100644 --- a/common/lib/types/message.js +++ b/common/lib/types/message.js @@ -78,11 +78,18 @@ var Message = (function() { }; Message.encode = function(msg, options) { - var data = msg.data, encoding; - if(data !== null && data !== undefined && typeof(data) != 'string' && !BufferUtils.isBuffer(data)) { - msg.data = JSON.stringify(data); - msg.encoding = (encoding = msg.encoding) ? (encoding + '/json') : 'json'; + var data = msg.data, encoding, + nativeDataType = typeof(data) == 'string' || BufferUtils.isBuffer(data) || data === null || data === undefined; + + if (!nativeDataType) { + if (Utils.isObject(data) || Utils.isArray(data)) { + msg.data = JSON.stringify(data); + msg.encoding = (encoding = msg.encoding) ? (encoding + '/json') : 'json'; + } else { + throw new ErrorInfo('Data type is unsupported', 40011, 400); + } } + if(options != null && options.encrypted) Message.encrypt(msg, options); }; diff --git a/common/lib/util/utils.js b/common/lib/util/utils.js index dc35c2caaa..7f761d987e 100644 --- a/common/lib/util/utils.js +++ b/common/lib/util/utils.js @@ -35,6 +35,11 @@ var Utils = (function() { return Object.prototype.toString.call(ob) == '[object Array]'; }; + /* ...Or an Object (in the narrow sense) */ + Utils.isObject = function(ob) { + return Object.prototype.toString.call(ob) == '[object Object]'; + }; + /* * Determine whether or not an object contains * any enumerable properties. diff --git a/spec/realtime/message.test.js b/spec/realtime/message.test.js index b17c23745e..3bb8e2df9e 100644 --- a/spec/realtime/message.test.js +++ b/spec/realtime/message.test.js @@ -83,11 +83,9 @@ define(['ably', 'shared_helper'], function(Ably, helper) { [{name: 'objectWithNameAndNullData', data: null}], [{name: 'objectWithNameAndUndefinedData', data: undefined}], [{name: 'objectWithNameAndEmptyStringData', data: ''}], - [{name: 'objectWithNameAndFalseData', data: false}], ['nameAndNullData', null], ['nameAndUndefinedData', undefined], ['nameAndEmptyStringData', ''], - ['nameAndFalseData', false], ['nameAndData', testData], ['nameAndDataAndCallback', testData, errorCallback], [{name: 'objectWithNameAndData', data: testData}], @@ -190,6 +188,66 @@ define(['ably', 'shared_helper'], function(Ably, helper) { } }; + exports.publishDisallowed = function(test) { + var transport = 'binary'; + var testData = 'Some data' + var testArguments = [ + [{name: 'objectAndBoolData', data: false}], + ['nameAndBoolData', false], + [{name: 'objectAndNumericData', data: 0}], + ['nameAndNumericData', 0], + [{name: 'objectAndOtherObjectData', data: new Date()}], + ['nameAndOtherObjectData', new Date()], + ]; + + test.expect(testArguments.length * 2); + try { + /* set up realtime */ + var realtime = helper.AblyRealtime(); + var rest = helper.AblyRest(); + + /* connect and attach */ + realtime.connection.on('connected', function() { + var rtChannel = realtime.channels.get('publishDisallowed'); + rtChannel.attach(function(err) { + if(err) { + test.ok(false, 'Attach failed with error: ' + err); + test.done(); + realtime.close(); + return; + } + + /* publish events */ + var restChannel = rest.channels.get('publishDisallowed'); + for(var i = 0; i < testArguments.length; i++) { + try { + restChannel.publish.apply(restChannel, testArguments[i]); + test.ok(false, "Exception was not raised"); + } catch (e) { + test.ok(true, "Exception correctly raised"); + test.equal(e.code, 40011, "Invalid data type exception raised"); + } + } + test.done(); + realtime.close(); + }); + }); + var exitOnState = function(state) { + realtime.connection.on(state, function () { + test.ok(false, transport + ' connection to server failed'); + test.done(); + realtime.close(); + }); + }; + exitOnState('failed'); + exitOnState('suspended'); + } catch(e) { + test.ok(false, 'Channel attach failed with exception: ' + e.stack); + test.done(); + realtime.close(); + } + }; + exports.restpublish = function(test) { var count = 10; var rest = helper.AblyRest(); diff --git a/spec/rest/history.test.js b/spec/rest/history.test.js index 27e8e284c5..7946e80a32 100644 --- a/spec/rest/history.test.js +++ b/spec/rest/history.test.js @@ -2,7 +2,23 @@ define(['ably', 'shared_helper', 'async'], function(Ably, helper, async) { var currentTime, rest, exports = {}, - displayError = helper.displayError; + displayError = helper.displayError, + testMessages = [ + { name: 'event0', + data: 'some data' }, + { name: 'event1', + data: 'some more data' }, + { name: 'event2', + data: 'and more' }, + { name: 'event3', + data: 'and more' }, + { name: 'event4', + data: [1,2,3] }, + { name: 'event5', + data: {one: 1, two: 2, three: 3} }, + { name: 'event6', + data: {foo: 'bar'} } + ]; exports.setup_history = function(test) { test.expect(1); @@ -18,22 +34,7 @@ define(['ably', 'shared_helper', 'async'], function(Ably, helper, async) { var testchannel = rest.channels.get('persisted:history_simple'); /* first, send a number of events to this channel */ - var testMessages = [ - { name: 'event0', - data: true }, - { name: 'event1', - data: false }, - { name: 'event2', - data: 24 }, - { name: 'event3', - data: 'this is a string' }, - { name: 'event4', - data: [1,2,3] }, - { name: 'event5', - data: {one: 1, two: 2, three: 3} }, - { name: 'event6', - data: Date.now() } - ]; + var publishTasks = testMessages.map(function(event) { return function(publishCb) { testchannel.publish(event.name, event.data, publishCb); @@ -79,22 +80,6 @@ define(['ably', 'shared_helper', 'async'], function(Ably, helper, async) { var testchannel = rest.channels.get('persisted:history_multiple'); /* first, send a number of events to this channel */ - var testMessages = [ - { name: 'event0', - data: true }, - { name: 'event1', - data: false }, - { name: 'event2', - data: 24 }, - { name: 'event3', - data: 'this is a string' }, - { name: 'event4', - data: [1,2,3] }, - { name: 'event5', - data: {one: 1, two: 2, three: 3} }, - { name: 'event6', - data: Date.now() } - ]; var publishTasks = [function(publishCb) { testchannel.publish(testMessages, publishCb); }]; @@ -137,23 +122,6 @@ define(['ably', 'shared_helper', 'async'], function(Ably, helper, async) { var testchannel = rest.channels.get('persisted:history_simple_paginated_b'); /* first, send a number of events to this channel */ - var testMessages = [ - { name: 'event0', - data: true }, - { name: 'event1', - data: false }, - { name: 'event2', - data: 24 }, - { name: 'event3', - data: 'this is a string' }, - { name: 'event4', - data: [1,2,3] }, - { name: 'event5', - data: {one: 1, two: 2, three: 3} }, - { name: 'event6', - data: Date.now() } - ]; - test.expect(4 * testMessages.length); var publishTasks = testMessages.map(function(event) { return function(publishCb) { @@ -218,23 +186,6 @@ define(['ably', 'shared_helper', 'async'], function(Ably, helper, async) { var testchannel = rest.channels.get('persisted:history_simple_paginated_f'); /* first, send a number of events to this channel */ - var testMessages = [ - { name: 'event0', - data: true }, - { name: 'event1', - data: false }, - { name: 'event2', - data: 24 }, - { name: 'event3', - data: 'this is a string' }, - { name: 'event4', - data: [1,2,3] }, - { name: 'event5', - data: {one: 1, two: 2, three: 3} }, - { name: 'event6', - data: Date.now() } - ]; - test.expect(4 * testMessages.length); var publishTasks = testMessages.map(function(event) { return function(publishCb) { @@ -298,23 +249,6 @@ define(['ably', 'shared_helper', 'async'], function(Ably, helper, async) { var testchannel = rest.channels.get('persisted:history_multiple_paginated_b'); /* first, send a number of events to this channel */ - var testMessages = [ - { name: 'event0', - data: true }, - { name: 'event1', - data: false }, - { name: 'event2', - data: 24 }, - { name: 'event3', - data: 'this is a string' }, - { name: 'event4', - data: [1,2,3] }, - { name: 'event5', - data: {one: 1, two: 2, three: 3} }, - { name: 'event6', - data: Date.now() } - ]; - test.expect(4 * testMessages.length); var publishTasks = [function(publishCb) { testchannel.publish(testMessages, publishCb); @@ -377,23 +311,6 @@ define(['ably', 'shared_helper', 'async'], function(Ably, helper, async) { var testchannel = rest.channels.get('persisted:history_multiple_paginated_f'); /* first, send a number of events to this channel */ - var testMessages = [ - { name: 'event0', - data: true }, - { name: 'event1', - data: false }, - { name: 'event2', - data: 24 }, - { name: 'event3', - data: 'this is a string' }, - { name: 'event4', - data: [1,2,3] }, - { name: 'event5', - data: {one: 1, two: 2, three: 3} }, - { name: 'event6', - data: Date.now() } - ]; - test.expect(4 * testMessages.length); var publishTasks = [function(publishCb) { testchannel.publish(testMessages, publishCb);