diff --git a/.gitignore b/.gitignore index f935a41..02f1b08 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ +node_modules/** tags -.zedstate \ No newline at end of file +.zedstate +/www/umd-tough-cookie.js diff --git a/README.md b/README.md index df55aee..768c4bd 100644 --- a/README.md +++ b/README.md @@ -208,8 +208,7 @@ We made a few modifications to http-request. They can be found in a separate re ## Cookies -- a cookie set by a request isn't sent in subsequent requests - +Cookies are not persisted, they are hold in memory. They are gone when you close the app or even if you do a `window.location` change. Take this into account when using this plugin in your application. ## License diff --git a/package.json b/package.json index 37ec21e..1a5a563 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,9 @@ "name": "cordova-plugin-advanced-http", "version": "1.4.0", "description": "Cordova / Phonegap plugin for communicating with HTTP servers using SSL pinning", + "scripts": { + "build": "cp node_modules/umd-tough-cookie/lib/umd-tough-cookie.js www/umd-tough-cookie.js" + }, "cordova": { "id": "cordova-plugin-advanced-http", "platforms": [ @@ -44,5 +47,8 @@ "bugs": { "url": "https://github.com/silkimen/cordova-plugin-advanced-http/issues" }, - "homepage": "https://github.com/silkimen/cordova-plugin-advanced-http#readme" + "homepage": "https://github.com/silkimen/cordova-plugin-advanced-http#readme", + "devDependencies": { + "umd-tough-cookie": "2.3.2" + } } diff --git a/plugin.xml b/plugin.xml index e21afb7..9ba1ff9 100644 --- a/plugin.xml +++ b/plugin.xml @@ -16,7 +16,13 @@ - + + + + + + + diff --git a/www/cordovaHttp.js b/www/advanced-http.js similarity index 55% rename from www/cordovaHttp.js rename to www/advanced-http.js index ac16312..8d83ecb 100644 --- a/www/cordovaHttp.js +++ b/www/advanced-http.js @@ -29,9 +29,13 @@ * An HTTP Plugin for PhoneGap. */ -var exec = require('cordova/exec'); +var pluginId = module.id.slice(0, module.id.indexOf('.')); var validSerializers = ['urlencoded', 'json']; +var exec = require('cordova/exec'); +var angularIntegration = require(pluginId +'.angular-integration'); +var cookieHandler = require(pluginId + '.cookie-handler'); + // Thanks Mozilla: https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_.22Unicode_Problem.22 function b64EncodeUnicode(str) { return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) { @@ -42,24 +46,50 @@ function b64EncodeUnicode(str) { function mergeHeaders(globalHeaders, localHeaders) { var globalKeys = Object.keys(globalHeaders); var key; + for (var i = 0; i < globalKeys.length; i++) { key = globalKeys[i]; + if (!localHeaders.hasOwnProperty(key)) { localHeaders[key] = globalHeaders[key]; } } + return localHeaders; } function checkSerializer(serializer) { - serializer = serializer || ''; - serializer = serializer.trim().toLowerCase(); + serializer = serializer || ''; + serializer = serializer.trim().toLowerCase(); - if (validSerializers.indexOf(serializer) > -1) { - return serializer; - } + if (validSerializers.indexOf(serializer) > -1) { + return serializer; + } - return serializer[0]; + return serializer[0]; +} + +function resolveCookieString(headers) { + var keys = Object.keys(headers); + + for (var i = 0; i < keys.length; ++i) { + if (keys[i].match(/^set-cookie$/i)) { + return headers[keys[i]]; + } + } + + return null; +} + +function getSuccessHandler(url, cb) { + return function(response) { + cookieHandler.setCookie(url, resolveCookieString(response.headers)); + cb(response); + } +} + +function getCookieHeader(url) { + return { Cookie: cookieHandler.getCookie(url) }; } var http = { @@ -91,36 +121,50 @@ var http = { data = data || {}; headers = headers || {}; headers = mergeHeaders(this.headers, headers); - return exec(success, failure, 'CordovaHttpPlugin', 'post', [url, data, this.dataSerializer, headers]); + headers = mergeHeaders(getCookieHeader(url), headers); + + return exec(getSuccessHandler(url, success), failure, 'CordovaHttpPlugin', 'post', [url, data, this.dataSerializer, headers]); }, get: function (url, params, headers, success, failure) { params = params || {}; headers = headers || {}; headers = mergeHeaders(this.headers, headers); - return exec(success, failure, 'CordovaHttpPlugin', 'get', [url, params, headers]); + headers = mergeHeaders(getCookieHeader(url), headers); + + return exec(getSuccessHandler(url, success), failure, 'CordovaHttpPlugin', 'get', [url, params, headers]); }, put: function (url, data, headers, success, failure) { data = data || {}; headers = headers || {}; headers = mergeHeaders(this.headers, headers); - return exec(success, failure, 'CordovaHttpPlugin', 'put', [url, data, this.dataSerializer, headers]); + headers = mergeHeaders(getCookieHeader(url), headers); + + return exec(getSuccessHandler(url, success), failure, 'CordovaHttpPlugin', 'put', [url, data, this.dataSerializer, headers]); }, delete: function (url, params, headers, success, failure) { params = params || {}; headers = headers || {}; headers = mergeHeaders(this.headers, headers); - return exec(success, failure, 'CordovaHttpPlugin', 'delete', [url, params, headers]); + headers = mergeHeaders(getCookieHeader(url), headers); + + return exec(getSuccessHandler(url, success), failure, 'CordovaHttpPlugin', 'delete', [url, params, headers]); }, head: function (url, params, headers, success, failure) { headers = mergeHeaders(this.headers, headers); - return exec(success, failure, 'CordovaHttpPlugin', 'head', [url, params, headers]); + headers = mergeHeaders(getCookieHeader(url), headers); + + return exec(getSuccessHandler(url, success), failure, 'CordovaHttpPlugin', 'head', [url, params, headers]); }, uploadFile: function (url, params, headers, filePath, name, success, failure) { headers = mergeHeaders(this.headers, headers); - return exec(success, failure, 'CordovaHttpPlugin', 'uploadFile', [url, params, headers, filePath, name]); + headers = mergeHeaders(getCookieHeader(url), headers); + + return exec(getSuccessHandler(url, success), failure, 'CordovaHttpPlugin', 'uploadFile', [url, params, headers, filePath, name]); }, downloadFile: function (url, params, headers, filePath, success, failure) { headers = mergeHeaders(this.headers, headers); + headers = mergeHeaders(getCookieHeader(url), headers); + var win = function (result) { var entry = new (require('cordova-plugin-file.FileEntry'))(); entry.isDirectory = false; @@ -131,87 +175,10 @@ var http = { entry.nativeURL = result.file.nativeURL; success(entry); }; + return exec(win, failure, 'CordovaHttpPlugin', 'downloadFile', [url, params, headers, filePath]); } }; -if (typeof angular !== 'undefined') { - angular.module('cordovaHTTP', []).factory('cordovaHTTP', function ($timeout, $q) { - function makePromise(fn, args, async) { - var deferred = $q.defer(); - - var success = function (response) { - if (async) { - $timeout(function () { - deferred.resolve(response); - }); - } else { - deferred.resolve(response); - } - }; - - var fail = function (response) { - if (async) { - $timeout(function () { - deferred.reject(response); - }); - } else { - deferred.reject(response); - } - }; - - args.push(success); - args.push(fail); - - fn.apply(http, args); - - return deferred.promise; - } - - var cordovaHTTP = { - getBasicAuthHeader: http.getBasicAuthHeader, - useBasicAuth: function (username, password) { - return http.useBasicAuth(username, password); - }, - setHeader: function (header, value) { - return http.setHeader(header, value); - }, - setDataSerializer: function (serializer) { - return http.setParamSerializer(serializer); - }, - enableSSLPinning: function (enable) { - return makePromise(http.enableSSLPinning, [enable]); - }, - acceptAllCerts: function (allow) { - return makePromise(http.acceptAllCerts, [allow]); - }, - validateDomainName: function (validate) { - return makePromise(http.validateDomainName, [validate]); - }, - post: function (url, data, headers) { - return makePromise(http.post, [url, data, headers], true); - }, - get: function (url, params, headers) { - return makePromise(http.get, [url, params, headers], true); - }, - put: function (url, data, headers) { - return makePromise(http.put, [url, data, headers], true); - }, - delete: function (url, params, headers) { - return makePromise(http.delete, [url, params, headers], true); - }, - head: function (url, params, headers) { - return makePromise(http.head, [url, params, headers], true); - }, - uploadFile: function (url, params, headers, filePath, name) { - return makePromise(http.uploadFile, [url, params, headers, filePath, name], true); - }, - downloadFile: function (url, params, headers, filePath) { - return makePromise(http.downloadFile, [url, params, headers, filePath], true); - } - }; - return cordovaHTTP; - }); -} - +angularIntegration.registerService(http); module.exports = http; diff --git a/www/angular-integration.js b/www/angular-integration.js new file mode 100644 index 0000000..9e897d2 --- /dev/null +++ b/www/angular-integration.js @@ -0,0 +1,84 @@ +function registerService(http) { + if (typeof angular === 'undefined') return; + + angular.module('cordovaHTTP', []).factory('cordovaHTTP', function ($timeout, $q) { + function makePromise(fn, args, async) { + var deferred = $q.defer(); + + var success = function (response) { + if (async) { + $timeout(function () { + deferred.resolve(response); + }); + } else { + deferred.resolve(response); + } + }; + + var fail = function (response) { + if (async) { + $timeout(function () { + deferred.reject(response); + }); + } else { + deferred.reject(response); + } + }; + + args.push(success); + args.push(fail); + + fn.apply(http, args); + + return deferred.promise; + } + + var cordovaHTTP = { + getBasicAuthHeader: http.getBasicAuthHeader, + useBasicAuth: function (username, password) { + return http.useBasicAuth(username, password); + }, + setHeader: function (header, value) { + return http.setHeader(header, value); + }, + setDataSerializer: function (serializer) { + return http.setParamSerializer(serializer); + }, + enableSSLPinning: function (enable) { + return makePromise(http.enableSSLPinning, [enable]); + }, + acceptAllCerts: function (allow) { + return makePromise(http.acceptAllCerts, [allow]); + }, + validateDomainName: function (validate) { + return makePromise(http.validateDomainName, [validate]); + }, + post: function (url, data, headers) { + return makePromise(http.post, [url, data, headers], true); + }, + get: function (url, params, headers) { + return makePromise(http.get, [url, params, headers], true); + }, + put: function (url, data, headers) { + return makePromise(http.put, [url, data, headers], true); + }, + delete: function (url, params, headers) { + return makePromise(http.delete, [url, params, headers], true); + }, + head: function (url, params, headers) { + return makePromise(http.head, [url, params, headers], true); + }, + uploadFile: function (url, params, headers, filePath, name) { + return makePromise(http.uploadFile, [url, params, headers, filePath, name], true); + }, + downloadFile: function (url, params, headers, filePath) { + return makePromise(http.downloadFile, [url, params, headers, filePath], true); + } + }; + return cordovaHTTP; + }); +} + +module.exports = { + registerService: registerService +}; diff --git a/www/cookie-handler.js b/www/cookie-handler.js new file mode 100644 index 0000000..549d84d --- /dev/null +++ b/www/cookie-handler.js @@ -0,0 +1,17 @@ +var pluginId = module.id.slice(0, module.id.indexOf('.')); +var ToughCookie = require(pluginId + '.tough-cookie'); +var cookieJar = new ToughCookie.CookieJar(); + +module.exports = { + setCookie: setCookie, + getCookie: getCookie +} + +function setCookie(url, cookieStr) { + if (!cookieStr) return; + cookieJar.setCookieSync(cookieStr, url); +} + +function getCookie(url) { + return cookieJar.getCookieStringSync(url); +}