diff --git a/test/app-test-definitions.js b/test/app-test-definitions.js index 0503053..89874cb 100644 --- a/test/app-test-definitions.js +++ b/test/app-test-definitions.js @@ -288,14 +288,14 @@ const tests = [ .should.be.equal('http://httpbin.org/get?myArray[]=val1&myArray[]=val2&myArray[]=val3&myString=testString'); } },{ - description: 'should reject non-string values in local header object #54', - expected: 'rejected: {"status": 0, "error": "advanced-http: header values must be strings" ...', + description: 'should throw on non-string values in local header object #54', + expected: 'throwed: {"message": "advanced-http: header values must be strings"}', func: function(resolve, reject) { cordova.plugin.http.get('http://httpbin.org/get', {}, { myTestHeader: 1 }, resolve, reject); }, validationFunc: function(driver, result) { - result.type.should.be.equal('rejected'); - result.data.error.should.be.equal('advanced-http: header values must be strings'); + result.type.should.be.equal('throwed'); + result.message.should.be.equal('advanced-http: header values must be strings'); } },{ description: 'should throw an error while setting non-string value as global header #54', diff --git a/test/js-mocha-specs.js b/test/js-mocha-specs.js index e0a215c..9ec8fd6 100644 --- a/test/js-mocha-specs.js +++ b/test/js-mocha-specs.js @@ -27,7 +27,6 @@ describe('Advanced HTTP www interface', function() { mock('cordova/exec', noop); mock(`${PLUGIN_ID}.cookie-handler`, {}); mock(`${HELPERS_ID}.cookie-handler`, {}); - mock(`${PLUGIN_ID}.messages`, require('../www/messages')); mock(`${HELPERS_ID}.messages`, require('../www/messages')); mock(`${PLUGIN_ID}.angular-integration`, { registerService: noop }); diff --git a/www/advanced-http.js b/www/advanced-http.js index 9d340f2..ed36d21 100644 --- a/www/advanced-http.js +++ b/www/advanced-http.js @@ -1,30 +1,5 @@ -/*global angular*/ - /* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - * Modified by Andrew Stephan for Sync OnSet - * Modified by Sefa Ilkimen -*/ - -/* - * An HTTP Plugin for PhoneGap. + * A native HTTP Plugin for Cordova / PhoneGap. */ var pluginId = module.id.slice(0, module.id.lastIndexOf('.')); @@ -33,222 +8,124 @@ var exec = require('cordova/exec'); var angularIntegration = require(pluginId +'.angular-integration'); var cookieHandler = require(pluginId + '.cookie-handler'); var helpers = require(pluginId + '.helpers'); -var messages = require(pluginId + '.messages'); -var internals = { +var globalConfigs = { headers: {}, - dataSerializer: 'urlencoded', - timeoutInSeconds: 60.0, + serializer: 'urlencoded', + timeout: 60.0, }; var publicInterface = { - getBasicAuthHeader: function (username, password) { - return {'Authorization': 'Basic ' + helpers.b64EncodeUnicode(username + ':' + password)}; - }, - useBasicAuth: function (username, password) { - this.setHeader('*', 'Authorization', 'Basic ' + helpers.b64EncodeUnicode(username + ':' + password)); - }, - getHeaders: function (host) { - return internals.headers[host || '*'] || null; - }, - setHeader: function () { - // this one is for being backward compatible - var host = '*'; - var header = arguments[0]; - var value = arguments[1]; + getBasicAuthHeader: function (username, password) { + return {'Authorization': 'Basic ' + helpers.b64EncodeUnicode(username + ':' + password)}; + }, + useBasicAuth: function (username, password) { + this.setHeader('*', 'Authorization', 'Basic ' + helpers.b64EncodeUnicode(username + ':' + password)); + }, + getHeaders: function (host) { + return globalConfigs.headers[host || '*'] || null; + }, + setHeader: function () { + // this one is for being backward compatible + var host = '*'; + var header = arguments[0]; + var value = arguments[1]; - if (arguments.length === 3) { - host = arguments[0]; - header = arguments[1]; - value = arguments[2]; - } - - if (header.toLowerCase() === 'cookie') { - throw new Error(messages.ADDING_COOKIES_NOT_SUPPORTED); - } - - if (helpers.getTypeOf(value) !== 'String') { - throw new Error(messages.HEADER_VALUE_MUST_BE_STRING); - } - - internals.headers[host] = internals.headers[host] || {}; - internals.headers[host][header] = value; - }, - getDataSerializer: function () { - return internals.dataSerializer; - }, - setDataSerializer: function (serializer) { - internals.dataSerializer = helpers.checkSerializer(serializer); - }, - setCookie: function (url, cookie, options) { - cookieHandler.setCookie(url, cookie, options); - }, - clearCookies: function () { - cookieHandler.clearCookies(); - }, - removeCookies: function (url, callback) { - cookieHandler.removeCookies(url, callback); - }, - getCookieString: function (url) { - return cookieHandler.getCookieString(url); - }, - getRequestTimeout: function () { - return internals.timeoutInSeconds; - }, - setRequestTimeout: function (timeout) { - internals.timeoutInSeconds = timeout; - }, - enableSSLPinning: function (enable, success, failure) { - return exec(success, failure, 'CordovaHttpPlugin', 'enableSSLPinning', [enable]); - }, - acceptAllCerts: function (allow, success, failure) { - return exec(success, failure, 'CordovaHttpPlugin', 'acceptAllCerts', [allow]); - }, - disableRedirect: function (disable, success, failure) { - return exec(success, failure, 'CordovaHttpPlugin', 'disableRedirect', [disable]); - }, - validateDomainName: function (validate, success, failure) { - failure(messages.DEPRECATED_VDN); - }, - sendRequest: function (url, options, success, failure) { - helpers.handleMissingCallbacks(success, failure); - - options = helpers.handleMissingOptions(options, internals); - - var headers = helpers.getMergedHeaders(url, options.headers, internals.headers); - var onSuccess = helpers.injectCookieHandler(url, success); - var onFail = helpers.injectCookieHandler(url, failure); - var payload; - - if ([ 'get', 'delete', 'head' ].indexOf(method) < 0) { - payload = helpers.getProcessedData(options.data, options.serializer); - } else { - payload = params; - } - - return exec(onSuccess, onFail, 'CordovaHttpPlugin', options.method, [ url, payload, options.serializer, headers, options.timeout ]); - }, - post: function (url, data, headers, success, failure) { - helpers.handleMissingCallbacks(success, failure); - - data = helpers.getProcessedData(data, internals.dataSerializer); - headers = helpers.getMergedHeaders(url, headers, internals.headers); - - if (!helpers.checkHeaders(headers)) { - return helpers.onInvalidHeader(failure); - } - - var onSuccess = helpers.injectCookieHandler(url, success); - var onFail = helpers.injectCookieHandler(url, failure); - - return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'post', [url, data, internals.dataSerializer, headers, internals.timeoutInSeconds]); - }, - get: function (url, params, headers, success, failure) { - helpers.handleMissingCallbacks(success, failure); - - params = params || {}; - headers = helpers.getMergedHeaders(url, headers, internals.headers); - - if (!helpers.checkHeaders(headers)) { - return helpers.onInvalidHeader(failure); - } - - var onSuccess = helpers.injectCookieHandler(url, success); - var onFail = helpers.injectCookieHandler(url, failure); - - return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'get', [url, params, headers, internals.timeoutInSeconds]); - }, - put: function (url, data, headers, success, failure) { - helpers.handleMissingCallbacks(success, failure); - - data = helpers.getProcessedData(data, internals.dataSerializer); - headers = helpers.getMergedHeaders(url, headers, internals.headers); - - if (!helpers.checkHeaders(headers)) { - return helpers.onInvalidHeader(failure); - } - - var onSuccess = helpers.injectCookieHandler(url, success); - var onFail = helpers.injectCookieHandler(url, failure); - - return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'put', [url, data, internals.dataSerializer, headers, internals.timeoutInSeconds]); - }, - patch: function (url, data, headers, success, failure) { - helpers.handleMissingCallbacks(success, failure); - - data = helpers.getProcessedData(data, internals.dataSerializer); - headers = helpers.getMergedHeaders(url, headers, internals.headers); - - if (!helpers.checkHeaders(headers)) { - return helpers.onInvalidHeader(failure); - } - - var onSuccess = helpers.injectCookieHandler(url, success); - var onFail = helpers.injectCookieHandler(url, failure); - - return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'patch', [url, data, internals.dataSerializer, headers, internals.timeoutInSeconds]); - }, - delete: function (url, params, headers, success, failure) { - helpers.handleMissingCallbacks(success, failure); - - params = params || {}; - headers = helpers.getMergedHeaders(url, headers, internals.headers); - - if (!helpers.checkHeaders(headers)) { - return helpers.onInvalidHeader(failure); - } - - var onSuccess = helpers.injectCookieHandler(url, success); - var onFail = helpers.injectCookieHandler(url, failure); - - return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'delete', [url, params, headers, internals.timeoutInSeconds]); - }, - head: function (url, params, headers, success, failure) { - helpers.handleMissingCallbacks(success, failure); - - params = params || {}; - headers = helpers.getMergedHeaders(url, headers, internals.headers); - - if (!helpers.checkHeaders(headers)) { - return helpers.onInvalidHeader(failure); - } - - var onSuccess = helpers.injectCookieHandler(url, success); - var onFail = helpers.injectCookieHandler(url, failure); - - return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'head', [url, params, headers, internals.timeoutInSeconds]); - }, - uploadFile: function (url, params, headers, filePath, name, success, failure) { - helpers.handleMissingCallbacks(success, failure); - - params = params || {}; - headers = helpers.getMergedHeaders(url, headers, internals.headers); - - if (!helpers.checkHeaders(headers)) { - return helpers.onInvalidHeader(failure); - } - - var onSuccess = helpers.injectCookieHandler(url, success); - var onFail = helpers.injectCookieHandler(url, failure); - - return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'uploadFile', [url, params, headers, filePath, name, internals.timeoutInSeconds]); - }, - downloadFile: function (url, params, headers, filePath, success, failure) { - helpers.handleMissingCallbacks(success, failure); - - params = params || {}; - headers = helpers.getMergedHeaders(url, headers, internals.headers); - - if (!helpers.checkHeaders(headers)) { - return helpers.onInvalidHeader(failure); - } - - var onSuccess = helpers.injectCookieHandler(url, helpers.injectFileEntryHandler(success)); - var onFail = helpers.injectCookieHandler(url, failure); - - return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'downloadFile', [url, params, headers, filePath, internals.timeoutInSeconds]); + if (arguments.length === 3) { + host = arguments[0]; + header = arguments[1]; + value = arguments[2]; } + + helpers.checkForBlacklistedHeaderKey(header); + helpers.checkForInvalidHeaderValue(value); + + globalConfigs.headers[host] = globalConfigs.headers[host] || {}; + globalConfigs.headers[host][header] = value; + }, + getDataSerializer: function () { + return globalConfigs.serializer; + }, + setDataSerializer: function (serializer) { + globalConfigs.serializer = helpers.checkSerializer(serializer); + }, + setCookie: function (url, cookie, options) { + cookieHandler.setCookie(url, cookie, options); + }, + clearCookies: function () { + cookieHandler.clearCookies(); + }, + removeCookies: function (url, callback) { + cookieHandler.removeCookies(url, callback); + }, + getCookieString: function (url) { + return cookieHandler.getCookieString(url); + }, + getRequestTimeout: function () { + return globalConfigs.timeout; + }, + setRequestTimeout: function (timeout) { + globalConfigs.timeout = timeout; + }, + enableSSLPinning: function (enable, success, failure) { + return exec(success, failure, 'CordovaHttpPlugin', 'enableSSLPinning', [enable]); + }, + acceptAllCerts: function (allow, success, failure) { + return exec(success, failure, 'CordovaHttpPlugin', 'acceptAllCerts', [allow]); + }, + disableRedirect: function (disable, success, failure) { + return exec(success, failure, 'CordovaHttpPlugin', 'disableRedirect', [disable]); + }, + sendRequest: function (url, options, success, failure) { + helpers.handleMissingCallbacks(success, failure); + + options = helpers.handleMissingOptions(options, globalConfigs); + + var headers = helpers.getMergedHeaders(url, options.headers, globalConfigs.headers); + var onSuccess = helpers.injectCookieHandler(url, success); + var onFail = helpers.injectCookieHandler(url, failure); + + switch(options.method) { + case 'post': + case 'put': + case 'patch': + var data = helpers.getProcessedData(options.data, options.serializer); + return exec(onSuccess, onFail, 'CordovaHttpPlugin', options.method, [ url, data, options.serializer, headers, options.timeout ]); + case 'upload': + return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'uploadFile', [ url, options.params, headers, options.filePath, options.name, options.timeout ]); + case 'download': + var onDownloadSuccess = helpers.injectCookieHandler(url, helpers.injectFileEntryHandler(success)); + return exec(onDownloadSuccess, onFail, 'CordovaHttpPlugin', 'downloadFile', [ url, options.params, headers, options.filePath, options.timeout ]); + default: + return exec(onSuccess, onFail, 'CordovaHttpPlugin', options.method, [ url, options.params, headers, options.timeout ]); + } + }, + post: function (url, data, headers, success, failure) { + return publicInterface.sendRequest(url, { method: 'post', data: data, headers: headers }, success, failure); + }, + get: function (url, params, headers, success, failure) { + return publicInterface.sendRequest(url, { method: 'get', params: params, headers: headers }, success, failure); + }, + put: function (url, data, headers, success, failure) { + return publicInterface.sendRequest(url, { method: 'put', data: data, headers: headers }, success, failure); + }, + patch: function (url, data, headers, success, failure) { + return publicInterface.sendRequest(url, { method: 'patch', data: data, headers: headers }, success, failure); + }, + delete: function (url, params, headers, success, failure) { + return publicInterface.sendRequest(url, { method: 'delete', params: params, headers: headers }, success, failure); + }, + head: function (url, params, headers, success, failure) { + return publicInterface.sendRequest(url, { method: 'head', params: params, headers: headers }, success, failure); + }, + uploadFile: function (url, params, headers, filePath, name, success, failure) { + return publicInterface.sendRequest(url, { method: 'upload', params: params, headers: headers, filePath: filePath, name: name }, success, failure); + }, + downloadFile: function (url, params, headers, filePath, success, failure) { + return publicInterface.sendRequest(url, { method: 'download', params: params, headers: headers, filePath: filePath }, success, failure); + } }; +// angular service is deprecated and will be removed anytime soon angularIntegration.registerService(publicInterface); module.exports = publicInterface; diff --git a/www/helpers.js b/www/helpers.js index b8688c3..979d7e0 100644 --- a/www/helpers.js +++ b/www/helpers.js @@ -3,19 +3,20 @@ var cookieHandler = require(pluginId + '.cookie-handler'); var messages = require(pluginId + '.messages'); var validSerializers = [ 'urlencoded', 'json', 'utf8' ]; -var validHttpMethods = [ 'get', 'put', 'post', 'patch', 'head', 'delete']; +var validHttpMethods = [ 'get', 'put', 'post', 'patch', 'head', 'delete', 'upload', 'download' ]; module.exports = { b64EncodeUnicode: b64EncodeUnicode, getTypeOf: getTypeOf, - checkHeaders: checkHeaders, - onInvalidHeader: onInvalidHeader, checkSerializer: checkSerializer, + checkForBlacklistedHeaderKey: checkForBlacklistedHeaderKey, + checkForInvalidHeaderValue: checkForInvalidHeaderValue, injectCookieHandler: injectCookieHandler, injectFileEntryHandler: injectFileEntryHandler, getMergedHeaders: getMergedHeaders, getProcessedData: getProcessedData, - handleMissingCallbacks: handleMissingCallbacks + handleMissingCallbacks: handleMissingCallbacks, + handleMissingOptions: handleMissingOptions }; // Thanks Mozilla: https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_.22Unicode_Problem.22 @@ -40,29 +41,6 @@ function mergeHeaders(globalHeaders, localHeaders) { return localHeaders; } -function checkHeaders(headers) { - var keys = Object.keys(headers); - var key; - - for (var i = 0; i < keys.length; i++) { - key = keys[i]; - - if (getTypeOf(headers[key]) !== 'String') { - return false; - } - } - - return true; -} - -function onInvalidHeader(handler) { - handler({ - status: 0, - error: messages.HEADER_VALUE_MUST_BE_STRING, - headers: {} - }); -} - function checkForValidStringValue(list, value, onInvalidValueMessage) { if (getTypeOf(value) !== 'String') { throw new Error(onInvalidValueMessage + ' ' + list.join(', ')); @@ -70,14 +48,14 @@ function checkForValidStringValue(list, value, onInvalidValueMessage) { value = value.trim().toLowerCase(); - if (list.indexOf(value) > -1) { - return value; + if (list.indexOf(value) === -1) { + throw new Error(onInvalidValueMessage + ' ' + list.join(', ')); } - throw new Error(onInvalidValueMessage + ' ' + list.join(', ')); + return value; } -function checkKeyValuePairObject(obj, onInvalidValueMessage) { +function checkKeyValuePairObject(obj, allowedChildren, onInvalidValueMessage) { if (getTypeOf(obj) !== 'Object') { throw new Error(onInvalidValueMessage); } @@ -85,9 +63,7 @@ function checkKeyValuePairObject(obj, onInvalidValueMessage) { var keys = Object.keys(obj); for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - - if (getTypeOf(obj[key]) !== 'String') { + if (allowedChildren.indexOf(getTypeOf(obj[keys[i]])) === -1) { throw new Error(onInvalidValueMessage); } } @@ -103,6 +79,22 @@ function checkSerializer(serializer) { return checkForValidStringValue(validSerializers, serializer, messages.INVALID_DATA_SERIALIZER); } +function checkForBlacklistedHeaderKey(key) { + if (key.toLowerCase() === 'cookie') { + throw new Error(messages.ADDING_COOKIES_NOT_SUPPORTED); + } + + return key; +} + +function checkForInvalidHeaderValue(value) { + if (getTypeOf(value) !== 'String') { + throw new Error(messages.INVALID_HEADERS_VALUE); + } + + return value; +} + function checkTimeoutValue(timeout) { if (getTypeOf(timeout) !== 'Number' || timeout < 0) { throw new Error(messages.INVALID_TIMEOUT_VALUE); @@ -112,11 +104,11 @@ function checkTimeoutValue(timeout) { } function checkHeadersObject(headers) { - checkKeyValuePairObject(headers, messages.INVALID_HEADERS_VALUE); + return checkKeyValuePairObject(headers, [ 'String' ], messages.INVALID_HEADERS_VALUE); } function checkParamsObject(params) { - checkKeyValuePairObject(params, messages.INVALID_PARAMS_VALUE); + return checkKeyValuePairObject(params, [ 'String', 'Array' ], messages.INVALID_PARAMS_VALUE); } function resolveCookieString(headers) { @@ -245,11 +237,13 @@ function handleMissingOptions(options, globals) { options = options || {}; return { - method: checkHttpMethod(options.method || validHttpMethods[0]); - serializer: checkSerializer(options.serializer || globals.dataSerializer); - timeout: checkTimeoutValue(options.timeout || globals.timeoutInSeconds); - headers: checkHeadersObject(options.headers || {}); - params: checkParamsObject(options.params || {}); - data: options.data || null; + method: checkHttpMethod(options.method || validHttpMethods[0]), + serializer: checkSerializer(options.serializer || globals.serializer), + timeout: checkTimeoutValue(options.timeout || globals.timeout), + headers: checkHeadersObject(options.headers || {}), + params: checkParamsObject(options.params || {}), + data: options.data || null, + filePath: options.filePath || '', + name: options.name || '' }; } diff --git a/www/messages.js b/www/messages.js index 0697320..e30afe1 100644 --- a/www/messages.js +++ b/www/messages.js @@ -1,13 +1,11 @@ module.exports = { ADDING_COOKIES_NOT_SUPPORTED: 'advanced-http: "setHeader" does not support adding cookies, please use "setCookie" function instead', DATA_TYPE_MISMATCH: 'advanced-http: "data" argument supports only following data types:', - DEPRECATED_VDN: 'advanced-http: "validateDomainName" is no more supported, please see change log for further info', - HEADER_VALUE_MUST_BE_STRING: 'advanced-http: header values must be strings', MANDATORY_SUCCESS: 'advanced-http: missing mandatory "onSuccess" callback function', MANDATORY_FAIL: 'advanced-http: missing mandatory "onFail" callback function', INVALID_HTTP_METHOD: 'advanced-http: invalid HTTP method, supported methods are:', INVALID_DATA_SERIALIZER: 'advanced-http: invalid serializer, supported serializers are:', - INVALID_TIMEOUT_VALUE: 'advanced-http: invalid timeout value, needs to be a positive float value', - INVALID_HEADERS_VALUE: 'advanced-http: invalid headers object, needs to be an object with strings', + INVALID_HEADERS_VALUE: 'advanced-http: header values must be strings', + INVALID_TIMEOUT_VALUE: 'advanced-http: invalid timeout value, needs to be a positive numeric value', INVALID_PARAMS_VALUE: 'advanced-http: invalid params object, needs to be an object with strings' };