module.exports = function init(global, jsUtil, cookieHandler, messages, base64, errorCodes, dependencyValidator, ponyfills) { var validSerializers = ['urlencoded', 'json', 'utf8', 'raw', 'multipart']; var validCertModes = ['default', 'nocheck', 'pinned', 'legacy']; var validClientAuthModes = ['none', 'systemstore', 'buffer']; var validHttpMethods = ['get', 'put', 'post', 'patch', 'head', 'delete', 'options', 'upload', 'download']; var validResponseTypes = ['text', 'json', 'arraybuffer', 'blob']; var interface = { b64EncodeUnicode: b64EncodeUnicode, checkClientAuthMode: checkClientAuthMode, checkClientAuthOptions: checkClientAuthOptions, checkDownloadFilePath: checkDownloadFilePath, checkFollowRedirectValue: checkFollowRedirectValue, checkForBlacklistedHeaderKey: checkForBlacklistedHeaderKey, checkForInvalidHeaderValue: checkForInvalidHeaderValue, checkSerializer: checkSerializer, checkSSLCertMode: checkSSLCertMode, checkTimeoutValue: checkTimeoutValue, checkUploadFileOptions: checkUploadFileOptions, getMergedHeaders: getMergedHeaders, processData: processData, handleMissingCallbacks: handleMissingCallbacks, handleMissingOptions: handleMissingOptions, injectCookieHandler: injectCookieHandler, injectFileEntryHandler: injectFileEntryHandler, injectRawResponseHandler: injectRawResponseHandler, }; // expose all functions for testing purposes if (init.debug) { interface.mergeHeaders = mergeHeaders; interface.checkForValidStringValue = checkForValidStringValue; interface.checkKeyValuePairObject = checkKeyValuePairObject; interface.checkHttpMethod = checkHttpMethod; interface.checkResponseType = checkResponseType; interface.checkHeadersObject = checkHeadersObject; interface.checkParamsObject = checkParamsObject; interface.resolveCookieString = resolveCookieString; interface.createFileEntry = createFileEntry; interface.getCookieHeader = getCookieHeader; interface.getMatchingHostHeaders = getMatchingHostHeaders; interface.getAllowedDataTypes = getAllowedDataTypes; } return interface; // 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) { return String.fromCharCode('0x' + p1); })); } 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 checkForValidStringValue(list, value, onInvalidValueMessage) { if (jsUtil.getTypeOf(value) !== 'String') { throw new Error(onInvalidValueMessage + ' ' + list.join(', ')); } value = value.trim().toLowerCase(); if (list.indexOf(value) === -1) { throw new Error(onInvalidValueMessage + ' ' + list.join(', ')); } return value; } function checkKeyValuePairObject(obj, allowedChildren, onInvalidValueMessage) { if (jsUtil.getTypeOf(obj) !== 'Object') { throw new Error(onInvalidValueMessage); } var keys = Object.keys(obj); for (var i = 0; i < keys.length; i++) { if (allowedChildren.indexOf(jsUtil.getTypeOf(obj[keys[i]])) === -1) { throw new Error(onInvalidValueMessage); } } return obj; } function checkArray(array, allowedDataTypes, onInvalidValueMessage) { if (jsUtil.getTypeOf(array) !== 'Array') { throw new Error(onInvalidValueMessage); } for (var i = 0; i < array.length; ++i) { if (allowedDataTypes.indexOf(jsUtil.getTypeOf(array[i])) === -1) { throw new Error(onInvalidValueMessage); } } return array; } function checkHttpMethod(method) { return checkForValidStringValue(validHttpMethods, method, messages.INVALID_HTTP_METHOD); } function checkResponseType(type) { return checkForValidStringValue(validResponseTypes, type, messages.INVALID_RESPONSE_TYPE); } function checkSerializer(serializer) { return checkForValidStringValue(validSerializers, serializer, messages.INVALID_DATA_SERIALIZER); } function checkSSLCertMode(mode) { return checkForValidStringValue(validCertModes, mode, messages.INVALID_SSL_CERT_MODE); } function checkClientAuthMode(mode) { return checkForValidStringValue(validClientAuthModes, mode, messages.INVALID_CLIENT_AUTH_MODE); } function checkClientAuthOptions(mode, options) { options = options || {}; // none if (mode === validClientAuthModes[0]) { return { alias: null, rawPkcs: null, pkcsPassword: '' }; } if (jsUtil.getTypeOf(options) !== 'Object') { throw new Error(messages.INVALID_CLIENT_AUTH_OPTIONS); } // systemstore if (mode === validClientAuthModes[1]) { if (jsUtil.getTypeOf(options.alias) !== 'String' && jsUtil.getTypeOf(options.alias) !== 'Undefined') { throw new Error(messages.INVALID_CLIENT_AUTH_ALIAS); } return { alias: jsUtil.getTypeOf(options.alias) === 'Undefined' ? null : options.alias, rawPkcs: null, pkcsPassword: '' }; } // buffer if (mode === validClientAuthModes[2]) { if (jsUtil.getTypeOf(options.rawPkcs) !== 'ArrayBuffer') { throw new Error(messages.INVALID_CLIENT_AUTH_RAW_PKCS); } if (jsUtil.getTypeOf(options.pkcsPassword) !== 'String') { throw new Error(messages.INVALID_CLIENT_AUTH_PKCS_PASSWORD); } return { alias: null, rawPkcs: options.rawPkcs, pkcsPassword: options.pkcsPassword } } } function checkForBlacklistedHeaderKey(key) { if (key.toLowerCase() === 'cookie') { throw new Error(messages.ADDING_COOKIES_NOT_SUPPORTED); } return key; } function checkForInvalidHeaderValue(value) { var type = jsUtil.getTypeOf(value); if (type !== 'String' && type !== 'Null') { throw new Error(messages.INVALID_HEADER_VALUE); } return value; } function checkTimeoutValue(timeout) { if (jsUtil.getTypeOf(timeout) !== 'Number' || timeout < 0) { throw new Error(messages.INVALID_TIMEOUT_VALUE); } return timeout; } function checkFollowRedirectValue(follow) { if (jsUtil.getTypeOf(follow) !== 'Boolean') { throw new Error(messages.INVALID_FOLLOW_REDIRECT_VALUE); } return follow; } function checkHeadersObject(headers) { return checkKeyValuePairObject(headers, ['String'], messages.TYPE_MISMATCH_HEADERS); } function checkParamsObject(params) { return checkKeyValuePairObject(params, ['String', 'Array'], messages.TYPE_MISMATCH_PARAMS); } function checkDownloadFilePath(filePath) { if (!filePath || jsUtil.getTypeOf(filePath) !== 'String') { throw new Error(messages.INVALID_DOWNLOAD_FILE_PATH); } return filePath; } function checkUploadFileOptions(filePaths, names) { if (jsUtil.getTypeOf(filePaths) === 'String') { filePaths = [filePaths]; } if (jsUtil.getTypeOf(names) === 'String') { names = [names]; } var opts = { filePaths: checkArray(filePaths, ['String'], messages.TYPE_MISMATCH_FILE_PATHS), names: checkArray(names, ['String'], messages.TYPE_MISMATCH_NAMES) }; if (!opts.filePaths.length) { throw new Error(messages.EMPTY_FILE_PATHS); } if (!opts.names.length) { throw new Error(messages.EMPTY_NAMES); } return opts; } 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 createFileEntry(rawEntry) { var entry = new (require('cordova-plugin-file.FileEntry'))(); entry.isDirectory = rawEntry.isDirectory; entry.isFile = rawEntry.isFile; entry.name = rawEntry.name; entry.fullPath = rawEntry.fullPath; entry.filesystem = new FileSystem(rawEntry.filesystemName || (rawEntry.filesystem == global.PERSISTENT ? 'persistent' : 'temporary')); entry.nativeURL = rawEntry.nativeURL; return entry; } function injectCookieHandler(url, cb) { return function (response) { cookieHandler.setCookieFromString(url, resolveCookieString(response.headers)); cb(response); } } function injectRawResponseHandler(responseType, success, failure) { return function (response) { var dataType = jsUtil.getTypeOf(response.data); // don't need post-processing if it's already binary type (on browser platform) if (dataType === 'ArrayBuffer' || dataType === 'Blob') { return success(response); } try { // json if (responseType === validResponseTypes[1]) { response.data = response.data === '' ? undefined : JSON.parse(response.data); } // arraybuffer else if (responseType === validResponseTypes[2]) { response.data = response.data === '' ? null : base64.toArrayBuffer(response.data); } // blob else if (responseType === validResponseTypes[3]) { if (response.data === '') { response.data = null; } else { var buffer = base64.toArrayBuffer(response.data); var type = response.headers['content-type'] || ''; var blob = new Blob([buffer], { type: type }); response.data = blob; } } success(response); } catch (error) { failure({ status: errorCodes.POST_PROCESSING_FAILED, error: messages.POST_PROCESSING_FAILED + ' ' + error.message, url: response.url, headers: response.headers }); } } } function injectFileEntryHandler(cb) { return function (response) { cb(createFileEntry(response.file)); } } function getCookieHeader(url) { var cookieString = cookieHandler.getCookieString(url); if (cookieString.length) { return { Cookie: cookieHandler.getCookieString(url) }; } return {}; } function getMatchingHostHeaders(url, headersList) { var matches = url.match(/^https?\:\/\/([^\/?#]+)(?:[\/?#]|$)/i); var domain = matches && matches[1]; return headersList[domain] || null; } function getMergedHeaders(url, requestHeaders, predefinedHeaders) { var globalHeaders = predefinedHeaders['*'] || {}; var hostHeaders = getMatchingHostHeaders(url, predefinedHeaders) || {}; var mergedHeaders = mergeHeaders(globalHeaders, hostHeaders); mergedHeaders = mergeHeaders(mergedHeaders, requestHeaders); mergedHeaders = mergeHeaders(mergedHeaders, getCookieHeader(url)); return mergedHeaders; } function getAllowedDataTypes(dataSerializer) { switch (dataSerializer) { case 'utf8': return ['String']; case 'urlencoded': return ['Object']; case 'json': return ['Array', 'Object']; case 'raw': return ['Uint8Array', 'ArrayBuffer']; default: return []; } } function getAllowedInstanceTypes(dataSerializer) { return dataSerializer === 'multipart' ? ['FormData'] : null; } function processData(data, dataSerializer, cb) { var currentDataType = jsUtil.getTypeOf(data); var allowedDataTypes = getAllowedDataTypes(dataSerializer); var allowedInstanceTypes = getAllowedInstanceTypes(dataSerializer); if (allowedInstanceTypes) { var isCorrectInstanceType = false; allowedInstanceTypes.forEach(function (type) { if ((global[type] && data instanceof global[type]) || (ponyfills[type] && data instanceof ponyfills[type])) { isCorrectInstanceType = true; } }); if (!isCorrectInstanceType) { throw new Error(messages.INSTANCE_TYPE_MISMATCH_DATA + ' ' + allowedInstanceTypes.join(', ')); } } if (!allowedInstanceTypes && allowedDataTypes.indexOf(currentDataType) === -1) { throw new Error(messages.TYPE_MISMATCH_DATA + ' ' + allowedDataTypes.join(', ')); } switch (dataSerializer) { case 'utf8': return cb({ text: data }); case 'raw': return cb(currentDataType === 'Uint8Array' ? data.buffer : data); case 'multipart': return processFormData(data, cb); default: return cb(data); } } function processFormData(data, cb) { dependencyValidator.checkBlobApi(); dependencyValidator.checkFileReaderApi(); dependencyValidator.checkTextEncoderApi(); dependencyValidator.checkFormDataInstance(data); var textEncoder = new global.TextEncoder('utf8'); var iterator = data.entries(); var result = { buffers: [], names: [], fileNames: [], types: [] }; processFormDataIterator(iterator, textEncoder, result, cb); } function processFormDataIterator(iterator, textEncoder, result, onFinished) { var entry = iterator.next(); if (entry.done) { return onFinished(result); } if (entry.value[1] instanceof global.Blob || entry.value[1] instanceof global.File) { var reader = new global.FileReader(); reader.onload = function () { result.buffers.push(base64.fromArrayBuffer(reader.result)); result.names.push(entry.value[0]); result.fileNames.push(entry.value[1].name !== undefined ? entry.value[1].name : 'blob'); result.types.push(entry.value[1].type || ''); processFormDataIterator(iterator, textEncoder, result, onFinished); }; return reader.readAsArrayBuffer(entry.value[1]); } if (jsUtil.getTypeOf(entry.value[1]) === 'String') { result.buffers.push(base64.fromArrayBuffer(textEncoder.encode(entry.value[1]).buffer)); result.names.push(entry.value[0]); result.fileNames.push(null); result.types.push('text/plain'); return processFormDataIterator(iterator, textEncoder, result, onFinished) } // skip items which are not supported processFormDataIterator(iterator, textEncoder, result, onFinished); } function handleMissingCallbacks(successFn, failFn) { if (jsUtil.getTypeOf(successFn) !== 'Function') { throw new Error(messages.MANDATORY_SUCCESS); } if (jsUtil.getTypeOf(failFn) !== 'Function') { throw new Error(messages.MANDATORY_FAIL); } } function handleMissingOptions(options, globals) { options = options || {}; return { data: jsUtil.getTypeOf(options.data) === 'Undefined' ? null : options.data, filePath: options.filePath, followRedirect: checkFollowRedirectValue(options.followRedirect || globals.followRedirect), headers: checkHeadersObject(options.headers || {}), method: checkHttpMethod(options.method || validHttpMethods[0]), name: options.name, params: checkParamsObject(options.params || {}), responseType: checkResponseType(options.responseType || validResponseTypes[0]), serializer: checkSerializer(options.serializer || globals.serializer), timeout: checkTimeoutValue(options.timeout || globals.timeout), }; } };