- some refactoring for future features
- move params serializer into JS code
This commit is contained in:
Sefa Ilkimen
2019-04-02 12:04:49 +02:00
parent 0af726d8ce
commit 230de36618
15 changed files with 864 additions and 600 deletions

View File

@@ -8,12 +8,15 @@
<engine name="cordova" version=">=4.0.0"/>
</engines>
<dependency id="cordova-plugin-file" version=">=2.0.0"/>
<js-module src="www/lodash.js" name="lodash"/>
<js-module src="www/umd-tough-cookie.js" name="tough-cookie"/>
<js-module src="www/messages.js" name="messages"/>
<js-module src="www/local-storage-store.js" name="local-storage-store"/>
<js-module src="www/cookie-handler.js" name="cookie-handler"/>
<js-module src="www/global-configs.js" name="global-configs"/>
<js-module src="www/helpers.js" name="helpers"/>
<js-module src="www/local-storage-store.js" name="local-storage-store"/>
<js-module src="www/lodash.js" name="lodash"/>
<js-module src="www/messages.js" name="messages"/>
<js-module src="www/public-interface.js" name="public-interface"/>
<js-module src="www/umd-tough-cookie.js" name="tough-cookie"/>
<js-module src="www/url-util.js" name="url-util"/>
<js-module src="www/advanced-http.js" name="http">
<clobbers target="cordova.plugin.http"/>
</js-module>

View File

@@ -31,7 +31,6 @@ abstract class CordovaHttpBase implements Runnable {
protected String url;
protected String serializer = "none";
protected Object data;
protected JSONObject params;
protected JSONObject headers;
protected int timeout;
protected boolean followRedirects;
@@ -55,13 +54,12 @@ abstract class CordovaHttpBase implements Runnable {
this.callbackContext = callbackContext;
}
public CordovaHttpBase(String method, String url, JSONObject params, JSONObject headers, int timeout,
public CordovaHttpBase(String method, String url, JSONObject headers, int timeout,
boolean followRedirects, SSLSocketFactory customSSLSocketFactory, HostnameVerifier customHostnameVerifier,
CallbackContext callbackContext) {
this.method = method;
this.url = url;
this.params = params;
this.headers = headers;
this.timeout = timeout;
this.followRedirects = followRedirects;
@@ -115,10 +113,7 @@ abstract class CordovaHttpBase implements Runnable {
}
protected HttpRequest createRequest() throws JSONException {
String processedUrl = HttpRequest.encode(HttpRequest.append(this.url, JsonUtils.getObjectMap(this.params)));
HttpRequest request = new HttpRequest(processedUrl, this.method);
return request;
return new HttpRequest(this.url, this.method);
}
protected void prepareRequest(HttpRequest request) throws JSONException {

View File

@@ -15,11 +15,11 @@ import org.json.JSONObject;
class CordovaHttpDownload extends CordovaHttpBase {
private String filePath;
public CordovaHttpDownload(String url, JSONObject params, JSONObject headers, String filePath, int timeout,
public CordovaHttpDownload(String url, JSONObject headers, String filePath, int timeout,
boolean followRedirects, SSLSocketFactory customSSLSocketFactory, HostnameVerifier customHostnameVerifier,
CallbackContext callbackContext) {
super("GET", url, params, headers, timeout, followRedirects, customSSLSocketFactory, customHostnameVerifier,
super("GET", url, headers, timeout, followRedirects, customSSLSocketFactory, customHostnameVerifier,
callbackContext);
this.filePath = filePath;
}

View File

@@ -15,11 +15,11 @@ class CordovaHttpOperation extends CordovaHttpBase {
customHostnameVerifier, callbackContext);
}
public CordovaHttpOperation(String method, String url, JSONObject params, JSONObject headers, int timeout,
boolean followRedirects, SSLSocketFactory customSSLSocketFactory, HostnameVerifier customHostnameVerifier,
public CordovaHttpOperation(String method, String url, JSONObject headers, int timeout, boolean followRedirects,
SSLSocketFactory customSSLSocketFactory, HostnameVerifier customHostnameVerifier,
CallbackContext callbackContext) {
super(method, url, params, headers, timeout, followRedirects, customSSLSocketFactory, customHostnameVerifier,
super(method, url, headers, timeout, followRedirects, customSSLSocketFactory, customHostnameVerifier,
callbackContext);
}
}

View File

@@ -66,7 +66,7 @@ public class CordovaHttpPlugin extends CordovaPlugin {
switch (action) {
case "get":
return this.executeHttpRequestWithParams(action, args, callbackContext);
return this.executeHttpRequestWithoutData(action, args, callbackContext);
case "post":
return this.executeHttpRequestWithData(action, args, callbackContext);
case "put":
@@ -74,9 +74,9 @@ public class CordovaHttpPlugin extends CordovaPlugin {
case "patch":
return this.executeHttpRequestWithData(action, args, callbackContext);
case "head":
return this.executeHttpRequestWithParams(action, args, callbackContext);
return this.executeHttpRequestWithoutData(action, args, callbackContext);
case "delete":
return this.executeHttpRequestWithParams(action, args, callbackContext);
return this.executeHttpRequestWithoutData(action, args, callbackContext);
case "uploadFile":
return this.uploadFile(args, callbackContext);
case "downloadFile":
@@ -90,15 +90,14 @@ public class CordovaHttpPlugin extends CordovaPlugin {
}
}
private boolean executeHttpRequestWithParams(final String method, final JSONArray args,
private boolean executeHttpRequestWithoutData(final String method, final JSONArray args,
final CallbackContext callbackContext) throws JSONException {
String url = args.getString(0);
JSONObject params = args.getJSONObject(1);
JSONObject headers = args.getJSONObject(2);
int timeout = args.getInt(3) * 1000;
JSONObject headers = args.getJSONObject(1);
int timeout = args.getInt(2) * 1000;
CordovaHttpOperation request = new CordovaHttpOperation(method.toUpperCase(), url, params, headers, timeout,
CordovaHttpOperation request = new CordovaHttpOperation(method.toUpperCase(), url, headers, timeout,
this.followRedirects, this.customSSLSocketFactory, this.customHostnameVerifier, callbackContext);
cordova.getThreadPool().execute(request);
@@ -125,13 +124,12 @@ public class CordovaHttpPlugin extends CordovaPlugin {
private boolean uploadFile(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
String url = args.getString(0);
JSONObject params = args.getJSONObject(1);
JSONObject headers = args.getJSONObject(2);
String filePath = args.getString(3);
String uploadName = args.getString(4);
int timeout = args.getInt(5) * 1000;
JSONObject headers = args.getJSONObject(1);
String filePath = args.getString(2);
String uploadName = args.getString(3);
int timeout = args.getInt(4) * 1000;
CordovaHttpUpload upload = new CordovaHttpUpload(url, params, headers, filePath, uploadName, timeout,
CordovaHttpUpload upload = new CordovaHttpUpload(url, headers, filePath, uploadName, timeout,
this.followRedirects, this.customSSLSocketFactory, this.customHostnameVerifier, callbackContext);
cordova.getThreadPool().execute(upload);
@@ -141,12 +139,11 @@ public class CordovaHttpPlugin extends CordovaPlugin {
private boolean downloadFile(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
String url = args.getString(0);
JSONObject params = args.getJSONObject(1);
JSONObject headers = args.getJSONObject(2);
String filePath = args.getString(3);
int timeout = args.getInt(4) * 1000;
JSONObject headers = args.getJSONObject(1);
String filePath = args.getString(2);
int timeout = args.getInt(3) * 1000;
CordovaHttpDownload download = new CordovaHttpDownload(url, params, headers, filePath, timeout,
CordovaHttpDownload download = new CordovaHttpDownload(url, headers, filePath, timeout,
this.followRedirects, this.customSSLSocketFactory, this.customHostnameVerifier, callbackContext);
cordova.getThreadPool().execute(download);

View File

@@ -17,11 +17,11 @@ class CordovaHttpUpload extends CordovaHttpBase {
private String filePath;
private String uploadName;
public CordovaHttpUpload(String url, JSONObject params, JSONObject headers, String filePath, String uploadName,
public CordovaHttpUpload(String url, JSONObject headers, String filePath, String uploadName,
int timeout, boolean followRedirects, SSLSocketFactory customSSLSocketFactory,
HostnameVerifier customHostnameVerifier, CallbackContext callbackContext) {
super("POST", url, params, headers, timeout, followRedirects, customSSLSocketFactory, customHostnameVerifier,
super("POST", url, headers, timeout, followRedirects, customSSLSocketFactory, customHostnameVerifier,
callbackContext);
this.filePath = filePath;
this.uploadName = uploadName;

View File

@@ -51,7 +51,8 @@ const tests = [
result.type.should.be.equal('rejected');
result.data.should.be.eql({ status: -2, error: targetInfo.isAndroid ? messageFactory.sslTrustAnchor() : messageFactory.invalidCertificate('self-signed.badssl.com') });
}
},{
},
{
description: 'should reject self signed cert (PUT)',
expected: 'rejected: {"status":-2, ...',
func: function(resolve, reject) { cordova.plugin.http.put('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
@@ -59,7 +60,8 @@ const tests = [
result.type.should.be.equal('rejected');
result.data.should.be.eql({ status: -2, error: targetInfo.isAndroid ? messageFactory.sslTrustAnchor() : messageFactory.invalidCertificate('self-signed.badssl.com') });
}
},{
},
{
description: 'should reject self signed cert (POST)',
expected: 'rejected: {"status":-2, ...',
func: function(resolve, reject) { cordova.plugin.http.post('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
@@ -67,7 +69,8 @@ const tests = [
result.type.should.be.equal('rejected');
result.data.should.be.eql({ status: -2, error: targetInfo.isAndroid ? messageFactory.sslTrustAnchor() : messageFactory.invalidCertificate('self-signed.badssl.com') });
}
},{
},
{
description: 'should reject self signed cert (PATCH)',
expected: 'rejected: {"status":-2, ...',
func: function(resolve, reject) { cordova.plugin.http.patch('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
@@ -75,7 +78,8 @@ const tests = [
result.type.should.be.equal('rejected');
result.data.should.be.eql({ status: -2, error: targetInfo.isAndroid ? messageFactory.sslTrustAnchor() : messageFactory.invalidCertificate('self-signed.badssl.com') });
}
},{
},
{
description: 'should reject self signed cert (DELETE)',
expected: 'rejected: {"status":-2, ...',
func: function(resolve, reject) { cordova.plugin.http.delete('https://self-signed.badssl.com/', {}, {}, resolve, reject); },
@@ -83,7 +87,8 @@ const tests = [
result.type.should.be.equal('rejected');
result.data.should.be.eql({ status: -2, error: targetInfo.isAndroid ? messageFactory.sslTrustAnchor() : messageFactory.invalidCertificate('self-signed.badssl.com') });
}
},{
},
{
description: 'should accept bad cert (GET)',
expected: 'resolved: {"status":200, ...',
before: helpers.setNoCheckCertMode,
@@ -92,7 +97,8 @@ const tests = [
result.type.should.be.equal('resolved');
result.data.should.include({ status: 200 });
}
},{
},
{
description: 'should accept bad cert (PUT)',
expected: 'rejected: {"status":405, ... // will be rejected because PUT is not allowed',
before: helpers.setNoCheckCertMode,
@@ -101,7 +107,8 @@ const tests = [
result.type.should.be.equal('rejected');
result.data.should.include({ status: 405 });
}
},{
},
{
description: 'should accept bad cert (POST)',
expected: 'rejected: {"status":405, ... // will be rejected because POST is not allowed',
before: helpers.setNoCheckCertMode,
@@ -110,7 +117,8 @@ const tests = [
result.type.should.be.equal('rejected');
result.data.should.include({ status: 405 });
}
},{
},
{
description: 'should accept bad cert (PATCH)',
expected: 'rejected: {"status":405, ... // will be rejected because PATCH is not allowed',
before: helpers.setNoCheckCertMode,
@@ -119,7 +127,8 @@ const tests = [
result.type.should.be.equal('rejected');
result.data.should.include({ status: 405 });
}
},{
},
{
description: 'should accept bad cert (DELETE)',
expected: 'rejected: {"status":405, ... // will be rejected because DELETE is not allowed',
before: helpers.setNoCheckCertMode,
@@ -128,7 +137,8 @@ const tests = [
result.type.should.be.equal('rejected');
result.data.should.include({ status: 405 });
}
},{
},
{
description: 'should fetch data from http://httpbin.org/ (GET)',
expected: 'resolved: {"status":200, ...',
before: helpers.setNoCheckCertMode,
@@ -137,7 +147,8 @@ const tests = [
result.type.should.be.equal('resolved');
result.data.should.include({ status: 200 });
}
},{
},
{
description: 'should send JSON object correctly (POST)',
expected: 'resolved: {"status": 200, "data": "{\\"json\\":\\"test\\": \\"testString\\"}\" ...',
before: helpers.setJsonSerializer,
@@ -146,7 +157,8 @@ const tests = [
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).json.should.eql({ test: 'testString' });
}
},{
},
{
description: 'should send JSON object correctly (PUT)',
expected: 'resolved: {"status": 200, "data": "{\\"json\\":\\"test\\": \\"testString\\"}\" ...',
before: helpers.setJsonSerializer,
@@ -155,7 +167,8 @@ const tests = [
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).json.should.eql({ test: 'testString' });
}
},{
},
{
description: 'should send JSON object correctly (PATCH)',
expected: 'resolved: {"status": 200, "data": "{\\"json\\":\\"test\\": \\"testString\\"}\" ...',
before: helpers.setJsonSerializer,
@@ -164,7 +177,8 @@ const tests = [
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).json.should.eql({ test: 'testString' });
}
},{
},
{
description: 'should send JSON array correctly (POST) #26',
expected: 'resolved: {"status": 200, "data": "[ 1, 2, 3 ]\" ...',
before: helpers.setJsonSerializer,
@@ -173,7 +187,8 @@ const tests = [
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).json.should.eql([ 1, 2, 3 ]);
}
},{
},
{
description: 'should send JSON array correctly (PUT) #26',
expected: 'resolved: {"status": 200, "data": "[ 1, 2, 3 ]\" ...',
before: helpers.setJsonSerializer,
@@ -182,7 +197,8 @@ const tests = [
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).json.should.eql([ 1, 2, 3 ]);
}
},{
},
{
description: 'should send JSON array correctly (PATCH) #26',
expected: 'resolved: {"status": 200, "data": "[ 1, 2, 3 ]\" ...',
before: helpers.setJsonSerializer,
@@ -192,7 +208,8 @@ const tests = [
result.data.data.should.be.a('string');
JSON.parse(result.data.data).json.should.eql([ 1, 2, 3 ]);
}
},{
},
{
description: 'should send url encoded data correctly (POST) #41',
expected: 'resolved: {"status": 200, "data": "{\\"form\\":\\"test\\": \\"testString\\"}\" ...',
before: helpers.setUrlEncodedSerializer,
@@ -201,7 +218,8 @@ const tests = [
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).form.should.eql({ test: 'testString' });
}
},{
},
{
description: 'should send url encoded data correctly (PUT)',
expected: 'resolved: {"status": 200, "data": "{\\"form\\":\\"test\\": \\"testString\\"}\" ...',
before: helpers.setUrlEncodedSerializer,
@@ -210,7 +228,8 @@ const tests = [
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).form.should.eql({ test: 'testString' });
}
},{
},
{
description: 'should send url encoded data correctly (PATCH)',
expected: 'resolved: {"status": 200, "data": "{\\"form\\":\\"test\\": \\"testString\\"}\" ...',
before: helpers.setUrlEncodedSerializer,
@@ -219,7 +238,8 @@ const tests = [
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).form.should.eql({ test: 'testString' });
}
},{
},
{
description: 'should resolve correct URL after redirect (GET) #33',
expected: 'resolved: {"status": 200, url: "http://httpbin.org/anything", ...',
func: function(resolve, reject) { cordova.plugin.http.get('http://httpbin.org/redirect-to?url=http://httpbin.org/anything', {}, {}, resolve, reject); },
@@ -227,7 +247,8 @@ const tests = [
result.type.should.be.equal('resolved');
result.data.url.should.be.equal('http://httpbin.org/anything');
}
},{
},
{
description: 'should download a file from given URL to given path in local filesystem',
expected: 'resolved: {"content": "<?xml version=\'1.0\' encoding=\'us-ascii\'?>\\n\\n<!-- A SAMPLE set of slides -->" ...',
func: function(resolve, reject) {
@@ -251,7 +272,8 @@ const tests = [
result.data.name.should.be.equal('test.xml');
result.data.content.should.be.equal("<?xml version='1.0' encoding='us-ascii'?>\n\n<!-- A SAMPLE set of slides -->\n\n<slideshow \n title=\"Sample Slide Show\"\n date=\"Date of publication\"\n author=\"Yours Truly\"\n >\n\n <!-- TITLE SLIDE -->\n <slide type=\"all\">\n <title>Wake up to WonderWidgets!</title>\n </slide>\n\n <!-- OVERVIEW -->\n <slide type=\"all\">\n <title>Overview</title>\n <item>Why <em>WonderWidgets</em> are great</item>\n <item/>\n <item>Who <em>buys</em> WonderWidgets</item>\n </slide>\n\n</slideshow>");
}
},{
},
{
description: 'should upload a file from given path in local filesystem to given URL #27',
expected: 'resolved: {"status": 200, "data": "files": {"test-file.txt": "I am a dummy file. I am used ...',
func: function(resolve, reject) {
@@ -276,7 +298,8 @@ const tests = [
.files[fileName]
.should.be.equal(fileContent);
}
},{
},
{
description: 'should encode HTTP array params correctly (GET) #45',
expected: 'resolved: {"status": 200, "data": "{\\"url\\":\\"http://httpbin.org/get?myArray[]=val1&myArray[]=val2&myArray[]=val3\\"}\" ...',
func: function(resolve, reject) {
@@ -291,7 +314,8 @@ const tests = [
.url
.should.include('httpbin.org/get?myArray[]=val1&myArray[]=val2&myArray[]=val3&myString=testString');
}
},{
},
{
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) {
@@ -301,7 +325,8 @@ const tests = [
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',
expected: 'throwed: "advanced-http: header values must be strings"',
func: function(resolve, reject) {
@@ -311,7 +336,8 @@ const tests = [
result.type.should.be.equal('throwed');
result.message.should.be.equal('advanced-http: header values must be strings');
}
},{
},
{
description: 'should accept content-type "application/xml" #58',
expected: 'resolved: {"status": 200, ...',
func: function(resolve, reject) {
@@ -321,7 +347,8 @@ const tests = [
result.type.should.be.equal('resolved');
result.data.status.should.be.equal(200);
}
},{
},
{
description: 'should send programmatically set cookies correctly (GET)',
expected: 'resolved: {"status": 200, ...',
func: function(resolve, reject) {
@@ -339,7 +366,8 @@ const tests = [
.Cookie
.should.be.equal('myCookie=myValue; mySecondCookie=mySecondValue');
}
},{
},
{
description: 'should not send any cookies after running "clearCookies" (GET) #59',
expected: 'resolved: {"status": 200, "data": "{\"headers\": {\"Cookie\": \"\"...',
func: function(resolve, reject) {
@@ -358,7 +386,8 @@ const tests = [
.Cookie
.should.be.equal('');
}
},{
},
{
description: 'should send programmatically set cookies correctly (DOWNLOAD) #57',
expected: 'resolved: {"content":{"cookies":{"myCookie":"myValue ...',
func: function(resolve, reject) {
@@ -390,7 +419,8 @@ const tests = [
cookies.myCookie.should.be.equal('myValue');
cookies.mySecondCookie.should.be.equal('mySecondValue');
}
},{
},
{
description: 'should send UTF-8 encoded raw string correctly (POST) #34',
expected: 'resolved: {"status": 200, "data": "{\\"data\\": \\"this is a test string\\"...',
before: helpers.setUtf8StringSerializer,
@@ -401,7 +431,8 @@ const tests = [
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).data.should.be.equal('this is a test string');
}
},{
},
{
description: 'should encode spaces in query string (params object) correctly (GET) #71',
expected: 'resolved: {"status": 200, "data": "{\\"args\\": \\"query param\\": \\"and value with spaces\\"...',
func: function(resolve, reject) {
@@ -411,7 +442,8 @@ const tests = [
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).args['query param'].should.be.equal('and value with spaces');
}
},{
},
{
description: 'should decode latin1 (iso-8859-1) encoded body correctly (GET) #72',
expected: 'resolved: {"status": 200, "data": "<!DOCTYPE HTML PUBLIC \\"-//W3C//DTD HTML 4.01 Transitional//EN\\"> ...',
func: function(resolve, reject) {
@@ -421,7 +453,8 @@ const tests = [
result.type.should.be.equal('resolved');
result.data.data.should.include('[¡] 161 10/01 241 A1 INVERTED EXCLAMATION MARK\n[¢] 162 10/02 242 A2 CENT SIGN');
}
},{
},
{
description: 'should return empty body string correctly (GET)',
expected: 'resolved: {"status": 200, "data": "" ...',
func: function(resolve, reject) {
@@ -431,7 +464,8 @@ const tests = [
result.type.should.be.equal('resolved');
result.data.data.should.be.equal('');
}
},{
},
{
description: 'should pin SSL cert correctly (GET)',
expected: 'resolved: {"status": 200 ...',
before: helpers.setPinnedCertMode,
@@ -442,7 +476,8 @@ const tests = [
result.type.should.be.equal('resolved');
result.data.status.should.be.equal(200);
}
},{
},
{
description: 'should reject when pinned cert does not match received server cert (GET)',
expected: 'rejected: {"status": -2 ...',
before: helpers.setPinnedCertMode,
@@ -453,7 +488,8 @@ const tests = [
result.type.should.be.equal('rejected');
result.data.should.be.eql({ status: -2, error: targetInfo.isAndroid ? messageFactory.sslTrustAnchor() : messageFactory.invalidCertificate('sha512.badssl.com') });
}
},{
},
{
description: 'should send deeply structured JSON object correctly (POST) #65',
expected: 'resolved: {"status": 200, "data": "{\\"data\\": \\"{\\\\"outerObj\\\\":{\\\\"innerStr\\\\":\\\\"testString\\\\",\\\\"innerArr\\\\":[1,2,3]}}\\" ...',
before: helpers.setJsonSerializer,
@@ -462,7 +498,8 @@ const tests = [
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).json.should.eql({ outerObj: { innerStr: 'testString', innerArr: [1, 2, 3] }});
}
},{
},
{
description: 'should override header "content-type" correctly (POST) #78',
expected: 'resolved: {"status": 200, "headers": "{\\"Content-Type\\": \\"text/plain\\" ...',
before: helpers.setJsonSerializer,
@@ -471,7 +508,8 @@ const tests = [
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).headers['Content-Type'].should.be.equal('text/plain');
}
},{
},
{
description: 'should handle error during file download correctly (DOWNLOAD) #83',
expected: 'rejected: {"status": 403, "error": "There was an error downloading the file" ...',
func: function(resolve, reject) { cordova.plugin.http.downloadFile('http://httpbin.org/status/403', {}, {}, cordova.file.tempDirectory + 'testfile.txt', resolve, reject); },
@@ -480,7 +518,8 @@ const tests = [
result.data.status.should.be.equal(403);
result.data.error.should.be.equal('There was an error downloading the file');
}
},{
},
{
description: 'should handle gzip encoded response correctly',
expected: 'resolved: {"status": 200, "headers": "{\\"Content-Encoding\\": \\"gzip\\" ...',
func: function(resolve, reject) { cordova.plugin.http.get('http://httpbin.org/gzip', {}, {}, resolve, reject); },
@@ -489,7 +528,8 @@ const tests = [
result.data.status.should.be.equal(200);
JSON.parse(result.data.data).gzipped.should.be.equal(true);
}
},{
},
{
description: 'should send empty string correctly',
expected: 'resolved: {"status": 200, "data": "{\\"json\\":\\"\\" ...',
before: helpers.setUtf8StringSerializer,
@@ -498,16 +538,34 @@ const tests = [
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).data.should.be.equal('');
}
},{
},
{
description: 'shouldn\'t escape forward slashes #184',
expected: 'resolved: {"status": 200, "data": "{\\"json\\":\\"/\\" ...',
before: helpers.setJsonSerializer,
func: function(resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', { testString: '/' }, {}, resolve, reject); },
validationFunc: function(driver, result) {
result.type.should.be.equal('resolved');
console.log(result.data.data);
JSON.parse(result.data.data).json.testString.should.be.equal('/');
}
},
{
description: 'should not double encode spaces in url path #195',
expected: '',
func: function(resolve, reject) { cordova.plugin.http.get('https://httpbin.org/anything/containing%20spaces%20in%20url', {}, {}, resolve, reject); },
validationFunc: function(driver, result) {
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).url.should.be.equal('https://httpbin.org/anything/containing spaces in url');
}
},
{
description: 'should encode spaces in url query correctly',
expected: '',
func: function(resolve, reject) { cordova.plugin.http.get('https://httpbin.org/anything', { 'query key': 'very long query value with spaces' }, {}, resolve, reject); },
validationFunc: function(driver, result) {
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).url.should.be.equal('https://httpbin.org/anything?query key=very long query value with spaces');
}
}
];

View File

@@ -1,21 +1,27 @@
const chai = require('chai');
const mock = require('mock-require');
const path = require('path');
const should = chai.should();
const HELPERS_ID = path.resolve(__dirname, '..', 'www', 'helpers');
const PLUGIN_ID = path.resolve(__dirname, '..', 'www', 'advanced-http');
describe('Advanced HTTP www interface', function() {
describe('Advanced HTTP public interface', function () {
let http = {};
let helpers = {};
const noop = () => { /* intentionally doing nothing */ };
const loadHttp = () => {
mock(`${PLUGIN_ID}.helpers`, mock.reRequire('../www/helpers'));
http = mock.reRequire('../www/advanced-http');
const getDependenciesBlueprint = () => {
const messages = require('../www/messages');
const globalConfigs = require('../www/global-configs');
const ToughCookie = require('../www/umd-tough-cookie');
const lodash = require('../www/lodash');
const WebStorageCookieStore = require('../www/local-storage-store')(ToughCookie, lodash);
const cookieHandler = require('../www/cookie-handler')(null, ToughCookie, WebStorageCookieStore);
const helpers = require('../www/helpers')(cookieHandler, messages);
const urlUtil = require('../www/url-util')(helpers);
return { exec: noop, cookieHandler, urlUtil: urlUtil, helpers, globalConfigs };
};
const loadHttp = (deps) => {
http = require('../www/public-interface')(deps.exec, deps.cookieHandler, deps.urlUtil, deps.helpers, deps.globalConfigs);
};
this.timeout(900000);
@@ -23,13 +29,7 @@ describe('Advanced HTTP www interface', function() {
beforeEach(() => {
// mocked btoa function (base 64 encoding strings)
global.btoa = decoded => new Buffer(decoded).toString('base64');
mock('cordova/exec', noop);
mock(`${PLUGIN_ID}.cookie-handler`, {});
mock(`${HELPERS_ID}.cookie-handler`, {});
mock(`${HELPERS_ID}.messages`, require('../www/messages'));
loadHttp();
loadHttp(getDependenciesBlueprint());
});
it('sets global headers correctly with two args (old interface)', () => {
@@ -48,76 +48,76 @@ describe('Advanced HTTP www interface', function() {
});
it('resolves global headers correctly #24', () => {
mock(`${HELPERS_ID}.cookie-handler`, {
getCookieString: () => 'fakeCookieString'
});
const deps = getDependenciesBlueprint();
mock('cordova/exec', (onSuccess, onFail, namespace, method, params) => {
const headers = params[2];
deps.cookieHandler.getCookieString = () => 'fakeCookieString';
deps.exec = (onSuccess, onFail, namespace, method, params) => {
const headers = params[1];
headers.should.eql({
Cookie: 'fakeCookieString',
myKey: 'myValue'
});
});
};
loadHttp();
loadHttp(deps);
http.setHeader('*', 'myKey', 'myValue');
http.get('url', {}, {}, noop, noop);
});
it('resolves host headers correctly (set without port number) #37', () => {
mock(`${HELPERS_ID}.cookie-handler`, {
getCookieString: () => 'fakeCookieString'
});
const deps = getDependenciesBlueprint();
mock('cordova/exec', (onSuccess, onFail, namespace, method, params) => {
const headers = params[2];
deps.cookieHandler.getCookieString = () => 'fakeCookieString';
deps.exec = (onSuccess, onFail, namespace, method, params) => {
const headers = params[1];
headers.should.eql({
Cookie: 'fakeCookieString',
myKey: 'myValue'
});
});
};
loadHttp();
loadHttp(deps);
http.setHeader('www.google.de', 'myKey', 'myValue');
http.get('https://www.google.de/?gws_rd=ssl', {}, {}, noop, noop);
});
it('resolves host headers correctly (set with port number) #37', () => {
mock(`${HELPERS_ID}.cookie-handler`, {
getCookieString: () => 'fakeCookieString'
});
const deps = getDependenciesBlueprint();
mock('cordova/exec', (onSuccess, onFail, namespace, method, params) => {
const headers = params[2];
deps.cookieHandler.getCookieString = () => 'fakeCookieString';
deps.exec = (onSuccess, onFail, namespace, method, params) => {
const headers = params[1];
headers.should.eql({
Cookie: 'fakeCookieString',
myKey: 'myValue'
});
});
};
loadHttp();
loadHttp(deps);
http.setHeader('www.google.de:8080', 'myKey', 'myValue');
http.get('https://www.google.de:8080/?gws_rd=ssl', {}, {}, noop, noop);
});
it('resolves request headers correctly', () => {
mock(`${HELPERS_ID}.cookie-handler`, {
getCookieString: () => 'fakeCookieString'
});
const deps = getDependenciesBlueprint();
mock('cordova/exec', (onSuccess, onFail, namespace, method, params) => {
const headers = params[2];
deps.cookieHandler.getCookieString = () => 'fakeCookieString';
deps.exec = (onSuccess, onFail, namespace, method, params) => {
const headers = params[1];
headers.should.eql({
Cookie: 'fakeCookieString',
myKey: 'myValue'
});
});
};
loadHttp();
loadHttp(deps);
http.get('https://www.google.de/?gws_rd=ssl', {}, { myKey: 'myValue' }, noop, noop);
});
@@ -128,6 +128,95 @@ describe('Advanced HTTP www interface', function() {
});
it('throws an Error when you try to add a cookie by using "setHeader" #46', () => {
(function() { http.setHeader('*', 'cookie', 'value') }).should.throw();
(function () { http.setHeader('*', 'cookie', 'value') }).should.throw();
});
});
describe('URL handler', function () {
const helpers = require('../www/helpers')(null, null);
const handler = require('../www/url-util')(helpers);
it('parses URL with protocol, hostname and path correctly', () => {
handler.parseUrl('http://ilkimen.net/test').should.include({
protocol: 'http:',
host: 'ilkimen.net',
hostname: 'ilkimen.net',
pathname: '/test',
port: '',
search: '',
hash: ''
});
});
it('parses URL with protocol, hostname, port and path correctly', () => {
handler.parseUrl('http://ilkimen.net:8080/test').should.include({
protocol: 'http:',
host: 'ilkimen.net:8080',
hostname: 'ilkimen.net',
pathname: '/test',
port: '8080',
search: '',
hash: ''
});
});
it('parses URL with protocol, hostname, port, path and query string correctly', () => {
handler.parseUrl('http://ilkimen.net:8080/test?param=value').should.include({
protocol: 'http:',
host: 'ilkimen.net:8080',
hostname: 'ilkimen.net',
pathname: '/test',
port: '8080',
search: '?param=value',
hash: ''
});
});
it('parses URL with protocol, hostname, port, path, query string and hash param correctly', () => {
handler.parseUrl('http://ilkimen.net:8080/test?param=value#myHash').should.include({
protocol: 'http:',
host: 'ilkimen.net:8080',
hostname: 'ilkimen.net',
pathname: '/test',
port: '8080',
search: '?param=value',
hash: '#myHash'
});
});
it('serializes query params correctly without URL encoding', () => {
handler.serializeQueryParams({
param1: 'value with spaces',
param2: 'value with special character äöü%'
}, false).should.equal('param1=value with spaces&param2=value with special character äöü%');
});
it('serializes array of query params correctly without URL encoding', () => {
handler.serializeQueryParams({
myArray: ['val1', 'val2', 'val3'],
myString: 'testString'
}, false).should.equal('myArray[]=val1&myArray[]=val2&myArray[]=val3&myString=testString');
});
it('serializes query params correctly with URL encoding enabled', () => {
handler.serializeQueryParams({
param1: 'value with spaces',
param2: 'value with special character äöü%&'
}, true).should.equal('param1=value%20with%20spaces&param2=value%20with%20special%20character%20%C3%A4%C3%B6%C3%BC%25%26');
});
it('appends query params string correctly to given URL without query parameters', () => {
handler.appendQueryParamsString('http://ilkimen.net/', 'param1=value1')
.should.equal('http://ilkimen.net/?param1=value1');
});
it('appends query params string correctly to given URL with existing query parameters', () => {
handler.appendQueryParamsString('http://ilkimen.net/?myParam=myValue', 'param1=value1')
.should.equal('http://ilkimen.net/?myParam=myValue&param1=value1');
});
it('appends query params string correctly to given URL with existing query parameters and hash value', () => {
handler.appendQueryParamsString('http://ilkimen.net/?myParam=myValue#myHash', 'param1=value1')
.should.equal('http://ilkimen.net/?myParam=myValue&param1=value1#myHash');
});
});

View File

@@ -5,121 +5,14 @@
var pluginId = module.id.slice(0, module.id.lastIndexOf('.'));
var exec = require('cordova/exec');
var cookieHandler = require(pluginId + '.cookie-handler');
var helpers = require(pluginId + '.helpers');
var globalConfigs = {
headers: {},
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 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];
}
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;
},
setSSLCertMode: function (mode, success, failure) {
return exec(success, failure, 'CordovaHttpPlugin', 'setSSLCertMode', [ helpers.checkSSLCertMode(mode) ]);
},
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);
}
};
var messages = require(pluginId + '.messages');
var globalConfigs = require(pluginId + '.global-configs');
var ToughCookie = require(pluginId + '.tough-cookie');
var lodash = require(pluginId + '.lodash');
var WebStorageCookieStore = require(pluginId + '.local-storage-store')(ToughCookie, lodash);
var cookieHandler = require(pluginId + '.cookie-handler')(window.localStorage, ToughCookie, WebStorageCookieStore);
var helpers = require(pluginId + '.helpers')(cookieHandler, messages);
var urlUtil = require(pluginId + '.url-util')(helpers);
var publicInterface = require(pluginId + '.public-interface')(exec, cookieHandler, urlUtil, helpers, globalConfigs);
module.exports = publicInterface;

View File

@@ -1,73 +1,70 @@
var pluginId = module.id.slice(0, module.id.lastIndexOf('.'));
var ToughCookie = require(pluginId + '.tough-cookie');
var WebStorageCookieStore = require(pluginId + '.local-storage-store');
module.exports = function init(storage, ToughCookie, WebStorageCookieStore) {
var storeKey = '__advancedHttpCookieStore__';
var storage = window.localStorage;
var storeKey = '__advancedHttpCookieStore__';
var store = new WebStorageCookieStore(storage, storeKey);
var cookieJar = new ToughCookie.CookieJar(store);
var store = new WebStorageCookieStore(storage, storeKey);
var cookieJar = new ToughCookie.CookieJar(store);
module.exports = {
return {
setCookieFromString: setCookieFromString,
setCookie: setCookie,
getCookieString: getCookieString,
clearCookies: clearCookies,
removeCookies: removeCookies
}
};
function splitCookieString(cookieStr) {
function splitCookieString(cookieStr) {
var cookieParts = cookieStr.split(',');
var splitCookies = [];
var processedCookie = null;
for (var i = 0; i < cookieParts.length; ++i) {
if (cookieParts[i].substr(-11, 8).toLowerCase() === 'expires=') {
processedCookie = cookieParts[i] + ',' + cookieParts[i + 1];
i++;
} else {
processedCookie = cookieParts[i];
}
if (cookieParts[i].substr(-11, 8).toLowerCase() === 'expires=') {
processedCookie = cookieParts[i] + ',' + cookieParts[i + 1];
i++;
} else {
processedCookie = cookieParts[i];
}
processedCookie = processedCookie.trim();
splitCookies.push(processedCookie);
processedCookie = processedCookie.trim();
splitCookies.push(processedCookie);
}
return splitCookies;
}
}
function setCookieFromString(url, cookieStr) {
function setCookieFromString(url, cookieStr) {
if (!cookieStr) return;
var cookies = splitCookieString(cookieStr);
for (var i = 0; i < cookies.length; ++i) {
cookieJar.setCookieSync(cookies[i], url, { ignoreError: true });
cookieJar.setCookieSync(cookies[i], url, { ignoreError: true });
}
}
}
function setCookie(url, cookie, options) {
options = options || {};
options.ignoreError = false;
cookieJar.setCookieSync(cookie, url, options);
}
function setCookie(url, cookie, options) {
options = options || {};
options.ignoreError = false;
cookieJar.setCookieSync(cookie, url, options);
}
function getCookieString(url) {
function getCookieString(url) {
return cookieJar.getCookieStringSync(url);
}
}
function clearCookies() {
function clearCookies() {
window.localStorage.removeItem(storeKey);
}
}
function removeCookies(url, cb) {
cookieJar.getCookies(url, function(error, cookies) {
if (!cookies || cookies.length === 0) {
function removeCookies(url, cb) {
cookieJar.getCookies(url, function (error, cookies) {
if (!cookies || cookies.length === 0) {
return cb(null, []);
}
}
var domain = cookies[0].domain;
var domain = cookies[0].domain;
cookieJar.store.removeCookies(domain, null, cb);
cookieJar.store.removeCookies(domain, null, cb);
});
}
}
};

7
www/global-configs.js Normal file
View File

@@ -0,0 +1,7 @@
var globalConfigs = {
headers: {},
serializer: 'urlencoded',
timeout: 60.0,
};
module.exports = globalConfigs;

View File

@@ -1,253 +1,251 @@
var pluginId = module.id.slice(0, module.id.lastIndexOf('.'));
var cookieHandler = require(pluginId + '.cookie-handler');
var messages = require(pluginId + '.messages');
var validSerializers = [ 'urlencoded', 'json', 'utf8' ];
var validCertModes = [ 'default', 'nocheck', 'pinned', 'legacy' ];
var validHttpMethods = [ 'get', 'put', 'post', 'patch', 'head', 'delete', 'upload', 'download' ];
module.exports = {
b64EncodeUnicode: b64EncodeUnicode,
getTypeOf: getTypeOf,
checkSerializer: checkSerializer,
checkSSLCertMode: checkSSLCertMode,
checkForBlacklistedHeaderKey: checkForBlacklistedHeaderKey,
checkForInvalidHeaderValue: checkForInvalidHeaderValue,
injectCookieHandler: injectCookieHandler,
injectFileEntryHandler: injectFileEntryHandler,
getMergedHeaders: getMergedHeaders,
getProcessedData: getProcessedData,
handleMissingCallbacks: handleMissingCallbacks,
handleMissingOptions: handleMissingOptions
};
// 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 (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 (getTypeOf(obj) !== 'Object') {
throw new Error(onInvalidValueMessage);
}
var keys = Object.keys(obj);
for (var i = 0; i < keys.length; i++) {
if (allowedChildren.indexOf(getTypeOf(obj[keys[i]])) === -1) {
throw new Error(onInvalidValueMessage);
}
}
return obj;
}
function checkHttpMethod(method) {
return checkForValidStringValue(validHttpMethods, method, messages.INVALID_HTTP_METHOD);
}
function checkSerializer(serializer) {
return checkForValidStringValue(validSerializers, serializer, messages.INVALID_DATA_SERIALIZER);
}
function checkSSLCertMode(mode) {
return checkForValidStringValue(validCertModes, mode, messages.INVALID_SSL_CERT_MODE);
}
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);
}
return timeout;
}
function checkHeadersObject(headers) {
return checkKeyValuePairObject(headers, [ 'String' ], messages.INVALID_HEADERS_VALUE);
}
function checkParamsObject(params) {
return checkKeyValuePairObject(params, [ 'String', 'Array' ], messages.INVALID_PARAMS_VALUE);
}
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 == window.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 injectFileEntryHandler(cb) {
return function(response) {
cb(createFileEntry(response.file));
}
}
function getCookieHeader(url) {
return { Cookie: cookieHandler.getCookieString(url) };
}
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;
}
// typeof is not working reliably in JS
function getTypeOf(object) {
switch (Object.prototype.toString.call(object)) {
case '[object Array]':
return 'Array';
case '[object Boolean]':
return 'Boolean';
case '[object Function]':
return 'Function';
case '[object Null]':
return 'Null';
case '[object Number]':
return 'Number';
case '[object Object]':
return 'Object';
case '[object String]':
return 'String';
case '[object Undefined]':
return 'Undefined';
default:
return 'Unknown';
}
}
function getAllowedDataTypes(dataSerializer) {
switch (dataSerializer) {
case 'utf8':
return ['String'];
case 'urlencoded':
return ['Object'];
default:
return ['Array', 'Object'];
}
}
function getProcessedData(data, dataSerializer) {
var currentDataType = getTypeOf(data);
var allowedDataTypes = getAllowedDataTypes(dataSerializer);
if (allowedDataTypes.indexOf(currentDataType) === -1) {
throw new Error(messages.DATA_TYPE_MISMATCH + ' ' + allowedDataTypes.join(', '));
}
if (dataSerializer === 'utf8') {
data = { text: data };
}
return data;
}
function handleMissingCallbacks(successFn, failFn) {
if (getTypeOf(successFn) !== 'Function') {
throw new Error(messages.MANDATORY_SUCCESS);
}
if (getTypeOf(failFn) !== 'Function') {
throw new Error(messages.MANDATORY_FAIL);
}
}
function handleMissingOptions(options, globals) {
options = options || {};
module.exports = function init(cookieHandler, messages) {
var validSerializers = ['urlencoded', 'json', 'utf8'];
var validCertModes = ['default', 'nocheck', 'pinned', 'legacy'];
var validHttpMethods = ['get', 'put', 'post', 'patch', 'head', 'delete', 'upload', 'download'];
return {
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: getTypeOf(options.data) === 'Undefined' ? null : options.data,
filePath: options.filePath || '',
name: options.name || ''
b64EncodeUnicode: b64EncodeUnicode,
getTypeOf: getTypeOf,
checkSerializer: checkSerializer,
checkSSLCertMode: checkSSLCertMode,
checkForBlacklistedHeaderKey: checkForBlacklistedHeaderKey,
checkForInvalidHeaderValue: checkForInvalidHeaderValue,
injectCookieHandler: injectCookieHandler,
injectFileEntryHandler: injectFileEntryHandler,
getMergedHeaders: getMergedHeaders,
getProcessedData: getProcessedData,
handleMissingCallbacks: handleMissingCallbacks,
handleMissingOptions: handleMissingOptions
};
}
// 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 (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 (getTypeOf(obj) !== 'Object') {
throw new Error(onInvalidValueMessage);
}
var keys = Object.keys(obj);
for (var i = 0; i < keys.length; i++) {
if (allowedChildren.indexOf(getTypeOf(obj[keys[i]])) === -1) {
throw new Error(onInvalidValueMessage);
}
}
return obj;
}
function checkHttpMethod(method) {
return checkForValidStringValue(validHttpMethods, method, messages.INVALID_HTTP_METHOD);
}
function checkSerializer(serializer) {
return checkForValidStringValue(validSerializers, serializer, messages.INVALID_DATA_SERIALIZER);
}
function checkSSLCertMode(mode) {
return checkForValidStringValue(validCertModes, mode, messages.INVALID_SSL_CERT_MODE);
}
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);
}
return timeout;
}
function checkHeadersObject(headers) {
return checkKeyValuePairObject(headers, ['String'], messages.INVALID_HEADERS_VALUE);
}
function checkParamsObject(params) {
return checkKeyValuePairObject(params, ['String', 'Array'], messages.INVALID_PARAMS_VALUE);
}
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 == window.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 injectFileEntryHandler(cb) {
return function (response) {
cb(createFileEntry(response.file));
}
}
function getCookieHeader(url) {
return { Cookie: cookieHandler.getCookieString(url) };
}
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;
}
// typeof is not working reliably in JS
function getTypeOf(object) {
switch (Object.prototype.toString.call(object)) {
case '[object Array]':
return 'Array';
case '[object Boolean]':
return 'Boolean';
case '[object Function]':
return 'Function';
case '[object Null]':
return 'Null';
case '[object Number]':
return 'Number';
case '[object Object]':
return 'Object';
case '[object String]':
return 'String';
case '[object Undefined]':
return 'Undefined';
default:
return 'Unknown';
}
}
function getAllowedDataTypes(dataSerializer) {
switch (dataSerializer) {
case 'utf8':
return ['String'];
case 'urlencoded':
return ['Object'];
default:
return ['Array', 'Object'];
}
}
function getProcessedData(data, dataSerializer) {
var currentDataType = getTypeOf(data);
var allowedDataTypes = getAllowedDataTypes(dataSerializer);
if (allowedDataTypes.indexOf(currentDataType) === -1) {
throw new Error(messages.DATA_TYPE_MISMATCH + ' ' + allowedDataTypes.join(', '));
}
if (dataSerializer === 'utf8') {
data = { text: data };
}
return data;
}
function handleMissingCallbacks(successFn, failFn) {
if (getTypeOf(successFn) !== 'Function') {
throw new Error(messages.MANDATORY_SUCCESS);
}
if (getTypeOf(failFn) !== 'Function') {
throw new Error(messages.MANDATORY_FAIL);
}
}
function handleMissingOptions(options, globals) {
options = options || {};
return {
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: getTypeOf(options.data) === 'Undefined' ? null : options.data,
filePath: options.filePath || '',
name: options.name || ''
};
}
};

View File

@@ -30,30 +30,27 @@
'use strict';
var pluginId = module.id.slice(0, module.id.lastIndexOf('.'));
var ToughCookie = require(pluginId + '.tough-cookie');
var _ = require(pluginId + '.lodash');
function WebStorageCookieStore(storage, storeKey) {
module.exports = function init(ToughCookie, _) {
function WebStorageCookieStore(storage, storeKey) {
ToughCookie.Store.call(this);
this._storage = storage || window.localStorage;
this._storage = storage;
this._storeKey = storeKey || '__cookieStore__';
this.synchronous = true;
}
}
WebStorageCookieStore.prototype = Object.create(ToughCookie.Store);
WebStorageCookieStore.prototype = Object.create(ToughCookie.Store);
WebStorageCookieStore.prototype.findCookie = function(domain, path, key, callback) {
WebStorageCookieStore.prototype.findCookie = function (domain, path, key, callback) {
var store = this._readStore();
var cookie = _.get(store, [domain, path, key], null);
callback(null, ToughCookie.Cookie.fromJSON(cookie));
};
};
WebStorageCookieStore.prototype.findCookies = function(domain, path, callback) {
WebStorageCookieStore.prototype.findCookies = function (domain, path, callback) {
if (!domain) {
callback(null, []);
return;
callback(null, []);
return;
}
var that = this;
@@ -61,123 +58,124 @@ WebStorageCookieStore.prototype.findCookies = function(domain, path, callback) {
var store = this._readStore();
var domains = ToughCookie.permuteDomain(domain) || [domain];
domains.forEach(function(domain) {
if (!store[domain]) {
return;
}
domains.forEach(function (domain) {
if (!store[domain]) {
return;
}
var matchingPaths = Object.keys(store[domain]);
var matchingPaths = Object.keys(store[domain]);
if (path != null) {
matchingPaths = matchingPaths.filter(function(cookiePath) {
return that._isOnPath(cookiePath, path);
});
}
matchingPaths.forEach(function(path) {
Array.prototype.push.apply(cookies, _.values(store[domain][path]));
if (path != null) {
matchingPaths = matchingPaths.filter(function (cookiePath) {
return that._isOnPath(cookiePath, path);
});
}
matchingPaths.forEach(function (path) {
Array.prototype.push.apply(cookies, _.values(store[domain][path]));
});
});
cookies = cookies.map(function(cookie) {
return ToughCookie.Cookie.fromJSON(cookie);
cookies = cookies.map(function (cookie) {
return ToughCookie.Cookie.fromJSON(cookie);
});
callback(null, cookies);
};
};
/**
* Returns whether `cookiePath` is on the given `urlPath`
*/
WebStorageCookieStore.prototype._isOnPath = function(cookiePath, urlPath) {
/**
* Returns whether `cookiePath` is on the given `urlPath`
*/
WebStorageCookieStore.prototype._isOnPath = function (cookiePath, urlPath) {
if (!cookiePath) {
return false;
return false;
}
if (cookiePath === urlPath) {
return true;
return true;
}
if (urlPath.indexOf(cookiePath) !== 0) {
return false;
return false;
}
if (cookiePath[cookiePath.length - 1] !== '/' && urlPath[cookiePath.length] !== '/') {
return false;
return false;
}
return true;
};
};
WebStorageCookieStore.prototype.putCookie = function(cookie, callback) {
var store = this._readStore();
WebStorageCookieStore.prototype.putCookie = function (cookie, callback) {
var store = this._readStore();
_.set(store, [cookie.domain, cookie.path, cookie.key], cookie);
this._writeStore(store);
callback(null);
};
_.set(store, [cookie.domain, cookie.path, cookie.key], cookie);
this._writeStore(store);
callback(null);
};
WebStorageCookieStore.prototype.updateCookie = function(oldCookie, newCookie, callback) {
WebStorageCookieStore.prototype.updateCookie = function (oldCookie, newCookie, callback) {
this.putCookie(newCookie, callback);
};
};
WebStorageCookieStore.prototype.removeCookie = function(domain, path, key, callback) {
WebStorageCookieStore.prototype.removeCookie = function (domain, path, key, callback) {
var store = this._readStore();
_.unset(store, [domain, path, key]);
this._writeStore(store);
callback(null);
};
};
WebStorageCookieStore.prototype.removeCookies = function(domain, path, callback) {
WebStorageCookieStore.prototype.removeCookies = function (domain, path, callback) {
var store = this._readStore();
if (path == null) {
_.unset(store, [domain]);
_.unset(store, [domain]);
} else {
_.unset(store, [domain, path]);
_.unset(store, [domain, path]);
}
this._writeStore(store);
callback(null);
};
};
WebStorageCookieStore.prototype.getAllCookies = function(callback) {
WebStorageCookieStore.prototype.getAllCookies = function (callback) {
var cookies = [];
var store = this._readStore();
Object.keys(store).forEach(function(domain) {
Object.keys(store[domain]).forEach(function(path) {
Array.protype.push.apply(cookies, _.values(store[domain][path]));
});
Object.keys(store).forEach(function (domain) {
Object.keys(store[domain]).forEach(function (path) {
Array.protype.push.apply(cookies, _.values(store[domain][path]));
});
});
cookies = cookies.map(function(cookie) {
return ToughCookie.Cookie.fromJSON(cookie);
cookies = cookies.map(function (cookie) {
return ToughCookie.Cookie.fromJSON(cookie);
});
cookies.sort(function(c1, c2) {
return (c1.creationIndex || 0) - (c2.creationIndex || 0);
cookies.sort(function (c1, c2) {
return (c1.creationIndex || 0) - (c2.creationIndex || 0);
});
callback(null, cookies);
};
};
WebStorageCookieStore.prototype._readStore = function() {
WebStorageCookieStore.prototype._readStore = function () {
var json = this._storage.getItem(this._storeKey);
if (json !== null) {
try {
return JSON.parse(json);
} catch (e) { }
try {
return JSON.parse(json);
} catch (e) { }
}
return {};
};
};
WebStorageCookieStore.prototype._writeStore = function(store) {
WebStorageCookieStore.prototype._writeStore = function (store) {
this._storage.setItem(this._storeKey, JSON.stringify(store));
};
};
module.exports = WebStorageCookieStore;
return WebStorageCookieStore;
};

158
www/public-interface.js Normal file
View File

@@ -0,0 +1,158 @@
module.exports = function init(exec, cookieHandler, urlUtil, helpers, globalConfigs) {
const publicInterface = {
getBasicAuthHeader: getBasicAuthHeader,
useBasicAuth: useBasicAuth,
getHeaders: getHeaders,
setHeader: setHeader,
getDataSerializer: getDataSerializer,
setDataSerializer: setDataSerializer,
setCookie: setCookie,
clearCookies: clearCookies,
removeCookies: removeCookies,
getCookieString: getCookieString,
getRequestTimeout: getRequestTimeout,
setRequestTimeout: setRequestTimeout,
setSSLCertMode: setSSLCertMode,
disableRedirect: disableRedirect,
sendRequest: sendRequest,
post: post,
get: get,
put: put,
patch: patch,
delete: del,
head: head,
uploadFile: uploadFile,
downloadFile: downloadFile
};
function getBasicAuthHeader(username, password) {
return { 'Authorization': 'Basic ' + helpers.b64EncodeUnicode(username + ':' + password) };
}
function useBasicAuth(username, password) {
this.setHeader('*', 'Authorization', 'Basic ' + helpers.b64EncodeUnicode(username + ':' + password));
}
function getHeaders(host) {
return globalConfigs.headers[host || '*'] || null;
}
function setHeader() {
// 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];
}
helpers.checkForBlacklistedHeaderKey(header);
helpers.checkForInvalidHeaderValue(value);
globalConfigs.headers[host] = globalConfigs.headers[host] || {};
globalConfigs.headers[host][header] = value;
}
function getDataSerializer() {
return globalConfigs.serializer;
}
function setDataSerializer(serializer) {
globalConfigs.serializer = helpers.checkSerializer(serializer);
}
function setCookie(url, cookie, options) {
cookieHandler.setCookie(url, cookie, options);
}
function clearCookies() {
cookieHandler.clearCookies();
}
function removeCookies(url, callback) {
cookieHandler.removeCookies(url, callback);
}
function getCookieString(url) {
return cookieHandler.getCookieString(url);
}
function getRequestTimeout() {
return globalConfigs.timeout;
}
function setRequestTimeout(timeout) {
globalConfigs.timeout = timeout;
}
function setSSLCertMode(mode, success, failure) {
return exec(success, failure, 'CordovaHttpPlugin', 'setSSLCertMode', [helpers.checkSSLCertMode(mode)]);
}
function disableRedirect(disable, success, failure) {
return exec(success, failure, 'CordovaHttpPlugin', 'disableRedirect', [!!disable]);
}
function sendRequest(url, options, success, failure) {
helpers.handleMissingCallbacks(success, failure);
options = helpers.handleMissingOptions(options, globalConfigs);
url = urlUtil.appendQueryParamsString(url, urlUtil.serializeQueryParams(options.params, true));
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, headers, options.filePath, options.name, options.timeout]);
case 'download':
var onDownloadSuccess = helpers.injectCookieHandler(url, helpers.injectFileEntryHandler(success));
return exec(onDownloadSuccess, onFail, 'CordovaHttpPlugin', 'downloadFile', [url, headers, options.filePath, options.timeout]);
default:
return exec(onSuccess, onFail, 'CordovaHttpPlugin', options.method, [url, headers, options.timeout]);
}
}
function post(url, data, headers, success, failure) {
return publicInterface.sendRequest(url, { method: 'post', data: data, headers: headers }, success, failure);
};
function get(url, params, headers, success, failure) {
return publicInterface.sendRequest(url, { method: 'get', params: params, headers: headers }, success, failure);
};
function put(url, data, headers, success, failure) {
return publicInterface.sendRequest(url, { method: 'put', data: data, headers: headers }, success, failure);
}
function patch(url, data, headers, success, failure) {
return publicInterface.sendRequest(url, { method: 'patch', data: data, headers: headers }, success, failure);
}
function del(url, params, headers, success, failure) {
return publicInterface.sendRequest(url, { method: 'delete', params: params, headers: headers }, success, failure);
}
function head(url, params, headers, success, failure) {
return publicInterface.sendRequest(url, { method: 'head', params: params, headers: headers }, success, failure);
}
function uploadFile(url, params, headers, filePath, name, success, failure) {
return publicInterface.sendRequest(url, { method: 'upload', params: params, headers: headers, filePath: filePath, name: name }, success, failure);
}
function downloadFile(url, params, headers, filePath, success, failure) {
return publicInterface.sendRequest(url, { method: 'download', params: params, headers: headers, filePath: filePath }, success, failure);
}
return publicInterface;
}

71
www/url-util.js Normal file
View File

@@ -0,0 +1,71 @@
module.exports = function init(helpers) {
return {
parseUrl: parseUrl,
serializeQueryParams: serializeQueryParams,
appendQueryParamsString: appendQueryParamsString
}
function parseUrl(url) {
var match = url.match(/^(https?\:)\/\/(([^:\/?#]*)(?:\:([0-9]+))?)([\/]{0,1}[^?#]*)(\?[^#]*|)(#.*|)$/);
return match && {
protocol: match[1],
host: match[2],
hostname: match[3],
port: match[4] || '',
pathname: match[5],
search: match[6],
hash: match[7]
}
}
function serializeQueryParams(params, encode) {
return serializeObject(params, encode);
}
function serializeObject(object, encode) {
var fragments = [];
for (var key in object) {
if (!object.hasOwnProperty(key)) {
continue;
}
if (helpers.getTypeOf(object[key]) === 'Array') {
fragments.push(serializeArray(object[key], encode));
continue;
} else if (helpers.getTypeOf(object[key]) === 'Object') {
fragments.push(serializeObject(object[key], encode));
continue;
}
if (encode) {
fragments.push(encodeURIComponent(key) + '=' + encodeURIComponent(object[key]));
} else {
fragments.push(key + '=' + object[key]);
}
}
return fragments.join('&');
}
function serializeArray(array, encode) {
}
function appendQueryParamsString(url, params) {
if (!url.length || !params.length) {
return url;
}
var parsed = parseUrl(url);
return parsed.protocol
+ '//'
+ parsed.host
+ parsed.pathname
+ (parsed.search.length ? parsed.search + '&' + params : '?' + params)
+ parsed.hash;
}
};