mirror of
https://github.com/silkimen/cordova-plugin-advanced-http.git
synced 2026-02-11 00:00:06 +08:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6339b9b83d | ||
|
|
389534d661 | ||
|
|
c10722eca0 | ||
|
|
6a60058fc6 | ||
|
|
fcedfae1ac | ||
|
|
aca165b900 | ||
|
|
09c2b383ff | ||
|
|
6918a2ed15 | ||
|
|
5b827d500d | ||
|
|
1c27b62148 | ||
|
|
f33a911e7e | ||
|
|
097faad07a | ||
|
|
62c400c6db | ||
|
|
f823d24438 | ||
|
|
64a7148444 | ||
|
|
f6736d9150 | ||
|
|
bc90ae85fb | ||
|
|
389e860125 | ||
|
|
2367d264c1 | ||
|
|
269d5d4c8a | ||
|
|
b6ee4de379 | ||
|
|
5f327dc82a | ||
|
|
1639efe8d0 | ||
|
|
9bb0c58e35 | ||
|
|
57562a0dcf | ||
|
|
ad4079625e | ||
|
|
98d3d38e07 |
@@ -1,5 +1,14 @@
|
||||
# Changelog
|
||||
|
||||
## 3.1.0
|
||||
|
||||
- Feature #272: add support for aborting requests (thanks russaa)
|
||||
|
||||
## 3.0.1
|
||||
|
||||
- Fixed #359: memory leakage leads to app crashes on Android
|
||||
- Fixed #355: responseType "json" not working with valid JSON response on browser (thanks millerg6711)
|
||||
|
||||
## 3.0.0
|
||||
|
||||
- Feature #158: support removing headers which were previously set via "setHeader"
|
||||
|
||||
40
README.md
40
README.md
@@ -404,6 +404,46 @@ cordova.plugin.http.downloadFile("https://google.com/", {
|
||||
});
|
||||
```
|
||||
|
||||
### abort<a name="abort"></a>
|
||||
Abort a HTTP request. Takes the `requestId` which is returned by [sendRequest](#sendRequest) and its shorthand functions ([post](#post), [get](#get), [put](#put), [patch](#patch), [delete](#delete), [head](#head), [uploadFile](#uploadFile) and [downloadFile](#downloadFile)).
|
||||
|
||||
If the request already has finished, the request will finish normally and the abort call result will be `{ aborted: false }`.
|
||||
|
||||
If the request is still in progress, the request's `failure` callback will be invoked with response `{ status: -8 }`, and the abort call result `{ aborted: true }`.
|
||||
|
||||
:warning: Not supported for Android < 6 (API level < 23). For Android 5.1 and below, calling `abort(reqestId)` will have no effect, i.e. the requests will finish as if the request was not cancelled.
|
||||
|
||||
```js
|
||||
// start a request and get its requestId
|
||||
var requestId = cordova.plugin.http.downloadFile("https://google.com/", {
|
||||
id: '12',
|
||||
message: 'test'
|
||||
}, { Authorization: 'OAuth2: token' }, 'file:///somepicture.jpg', function(entry) {
|
||||
// prints the filename
|
||||
console.log(entry.name);
|
||||
|
||||
// prints the filePath
|
||||
console.log(entry.fullPath);
|
||||
}, function(response) {
|
||||
// if request was actually aborted, failure callback with status -8 will be invoked
|
||||
if(response.status === -8){
|
||||
console.log('download aborted');
|
||||
} else {
|
||||
console.error(response.error);
|
||||
}
|
||||
});
|
||||
|
||||
//...
|
||||
|
||||
// abort request
|
||||
cordova.plugin.http.abort(requestId, function(result) {
|
||||
// prints if request was aborted: true | false
|
||||
console.log(result.aborted);
|
||||
}, function(response) {
|
||||
console.error(response.error);
|
||||
});
|
||||
```
|
||||
|
||||
## Browser support<a name="browserSupport"></a>
|
||||
|
||||
This plugin supports a very restricted set of functions on the browser platform.
|
||||
|
||||
26
package-lock.json
generated
26
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cordova-plugin-advanced-http",
|
||||
"version": "3.0.0",
|
||||
"version": "3.1.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -423,9 +423,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"bl": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-4.0.2.tgz",
|
||||
"integrity": "sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ==",
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz",
|
||||
"integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"buffer": "^5.5.0",
|
||||
@@ -2489,12 +2489,6 @@
|
||||
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.15",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
|
||||
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
|
||||
"dev": true
|
||||
},
|
||||
"mute-stream": {
|
||||
"version": "0.0.7",
|
||||
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
|
||||
@@ -2623,12 +2617,6 @@
|
||||
"sshpk": "^1.7.0"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.15",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
|
||||
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
|
||||
"dev": true
|
||||
},
|
||||
"oauth-sign": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
|
||||
@@ -3157,9 +3145,9 @@
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.15",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
|
||||
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
|
||||
"version": "4.17.19",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
|
||||
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.debounce": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cordova-plugin-advanced-http",
|
||||
"version": "3.0.0",
|
||||
"version": "3.1.0",
|
||||
"description": "Cordova / Phonegap plugin for communicating with HTTP servers using SSL pinning",
|
||||
"scripts": {
|
||||
"updatecert": "node ./scripts/update-e2e-server-cert.js && node ./scripts/update-e2e-client-cert.js",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<plugin xmlns="http://www.phonegap.com/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android" id="cordova-plugin-advanced-http" version="3.0.0">
|
||||
<plugin xmlns="http://www.phonegap.com/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android" id="cordova-plugin-advanced-http" version="3.1.0">
|
||||
<name>Advanced HTTP plugin</name>
|
||||
<description>
|
||||
Cordova / Phonegap plugin for communicating with HTTP servers using SSL pinning
|
||||
@@ -74,6 +74,7 @@
|
||||
<source-file src="src/android/com/silkimen/cordovahttp/CordovaHttpPlugin.java" target-dir="src/com/silkimen/cordovahttp"/>
|
||||
<source-file src="src/android/com/silkimen/cordovahttp/CordovaHttpResponse.java" target-dir="src/com/silkimen/cordovahttp"/>
|
||||
<source-file src="src/android/com/silkimen/cordovahttp/CordovaHttpUpload.java" target-dir="src/com/silkimen/cordovahttp"/>
|
||||
<source-file src="src/android/com/silkimen/cordovahttp/CordovaObservableCallbackContext.java" target-dir="src/com/silkimen/cordovahttp"/>
|
||||
<source-file src="src/android/com/silkimen/cordovahttp/CordovaServerTrust.java" target-dir="src/com/silkimen/cordovahttp"/>
|
||||
<source-file src="src/android/com/silkimen/http/HttpBodyDecoder.java" target-dir="src/com/silkimen/http"/>
|
||||
<source-file src="src/android/com/silkimen/http/HttpRequest.java" target-dir="src/com/silkimen/http"/>
|
||||
|
||||
@@ -5,6 +5,7 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.io.InterruptedIOException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
@@ -18,8 +19,6 @@ import com.silkimen.http.HttpRequest.HttpRequestException;
|
||||
import com.silkimen.http.JsonUtils;
|
||||
import com.silkimen.http.TLSConfiguration;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
@@ -39,11 +38,11 @@ abstract class CordovaHttpBase implements Runnable {
|
||||
protected int timeout;
|
||||
protected boolean followRedirects;
|
||||
protected TLSConfiguration tlsConfiguration;
|
||||
protected CallbackContext callbackContext;
|
||||
protected CordovaObservableCallbackContext callbackContext;
|
||||
|
||||
public CordovaHttpBase(String method, String url, String serializer, Object data, JSONObject headers, int timeout,
|
||||
boolean followRedirects, String responseType, TLSConfiguration tlsConfiguration,
|
||||
CallbackContext callbackContext) {
|
||||
CordovaObservableCallbackContext callbackContext) {
|
||||
|
||||
this.method = method;
|
||||
this.url = url;
|
||||
@@ -58,7 +57,7 @@ abstract class CordovaHttpBase implements Runnable {
|
||||
}
|
||||
|
||||
public CordovaHttpBase(String method, String url, JSONObject headers, int timeout, boolean followRedirects,
|
||||
String responseType, TLSConfiguration tlsConfiguration, CallbackContext callbackContext) {
|
||||
String responseType, TLSConfiguration tlsConfiguration, CordovaObservableCallbackContext callbackContext) {
|
||||
|
||||
this.method = method;
|
||||
this.url = url;
|
||||
@@ -74,11 +73,13 @@ abstract class CordovaHttpBase implements Runnable {
|
||||
public void run() {
|
||||
CordovaHttpResponse response = new CordovaHttpResponse();
|
||||
|
||||
HttpRequest request = null;
|
||||
try {
|
||||
HttpRequest request = this.createRequest();
|
||||
request = this.createRequest();
|
||||
this.prepareRequest(request);
|
||||
this.sendBody(request);
|
||||
this.processResponse(request, response);
|
||||
request.disconnect();
|
||||
} catch (HttpRequestException e) {
|
||||
if (e.getCause() instanceof SSLException) {
|
||||
response.setStatus(-2);
|
||||
@@ -93,10 +94,17 @@ abstract class CordovaHttpBase implements Runnable {
|
||||
response.setErrorMessage("Request timed out: " + e.getMessage());
|
||||
Log.w(TAG, "Request timed out", e);
|
||||
} else {
|
||||
response.setStatus(-1);
|
||||
response.setErrorMessage("There was an error with the request: " + e.getCause().getMessage());
|
||||
Log.w(TAG, "Generic request error", e);
|
||||
String cause = e.getCause().getMessage();
|
||||
if(e.getCause() instanceof InterruptedIOException && "thread interrupted".equals(cause.toLowerCase())){
|
||||
this.setAborted(request, response);
|
||||
} else {
|
||||
response.setStatus(-1);
|
||||
response.setErrorMessage("There was an error with the request: " + cause);
|
||||
Log.w(TAG, "Generic request error", e);
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException ie) {
|
||||
this.setAborted(request, response);
|
||||
} catch (Exception e) {
|
||||
response.setStatus(-1);
|
||||
response.setErrorMessage(e.getMessage());
|
||||
@@ -201,4 +209,17 @@ abstract class CordovaHttpBase implements Runnable {
|
||||
response.setErrorMessage(HttpBodyDecoder.decodeBody(outputStream.toByteArray(), request.charset()));
|
||||
}
|
||||
}
|
||||
|
||||
protected void setAborted(HttpRequest request, CordovaHttpResponse response) {
|
||||
response.setStatus(-8);
|
||||
response.setErrorMessage("Request was aborted");
|
||||
if(request != null){
|
||||
try{
|
||||
request.disconnect();
|
||||
} catch(Exception any){
|
||||
Log.w(TAG, "Failed to close aborted request", any);
|
||||
}
|
||||
}
|
||||
Log.i(TAG, "Request was aborted");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import javax.net.ssl.SSLSocketFactory;
|
||||
import com.silkimen.http.HttpRequest;
|
||||
import com.silkimen.http.TLSConfiguration;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.file.FileUtils;
|
||||
import org.json.JSONObject;
|
||||
|
||||
@@ -17,7 +16,7 @@ class CordovaHttpDownload extends CordovaHttpBase {
|
||||
private String filePath;
|
||||
|
||||
public CordovaHttpDownload(String url, JSONObject headers, String filePath, int timeout, boolean followRedirects,
|
||||
TLSConfiguration tlsConfiguration, CallbackContext callbackContext) {
|
||||
TLSConfiguration tlsConfiguration, CordovaObservableCallbackContext callbackContext) {
|
||||
|
||||
super("GET", url, headers, timeout, followRedirects, "text", tlsConfiguration, callbackContext);
|
||||
this.filePath = filePath;
|
||||
|
||||
@@ -5,20 +5,19 @@ import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
import com.silkimen.http.TLSConfiguration;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.json.JSONObject;
|
||||
|
||||
class CordovaHttpOperation extends CordovaHttpBase {
|
||||
public CordovaHttpOperation(String method, String url, String serializer, Object data, JSONObject headers,
|
||||
int timeout, boolean followRedirects, String responseType, TLSConfiguration tlsConfiguration,
|
||||
CallbackContext callbackContext) {
|
||||
CordovaObservableCallbackContext callbackContext) {
|
||||
|
||||
super(method, url, serializer, data, headers, timeout, followRedirects, responseType, tlsConfiguration,
|
||||
callbackContext);
|
||||
}
|
||||
|
||||
public CordovaHttpOperation(String method, String url, JSONObject headers, int timeout, boolean followRedirects,
|
||||
String responseType, TLSConfiguration tlsConfiguration, CallbackContext callbackContext) {
|
||||
String responseType, TLSConfiguration tlsConfiguration, CordovaObservableCallbackContext callbackContext) {
|
||||
|
||||
super(method, url, headers, timeout, followRedirects, responseType, tlsConfiguration, callbackContext);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package com.silkimen.cordovahttp;
|
||||
|
||||
import java.security.KeyStore;
|
||||
import java.util.HashMap;
|
||||
import java.util.Observable;
|
||||
import java.util.Observer;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import com.silkimen.http.TLSConfiguration;
|
||||
|
||||
@@ -17,17 +21,22 @@ import android.util.Base64;
|
||||
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
|
||||
public class CordovaHttpPlugin extends CordovaPlugin {
|
||||
public class CordovaHttpPlugin extends CordovaPlugin implements Observer {
|
||||
private static final String TAG = "Cordova-Plugin-HTTP";
|
||||
|
||||
private TLSConfiguration tlsConfiguration;
|
||||
|
||||
private HashMap<Integer, Future<?>> reqMap;
|
||||
private final Object reqMapLock = new Object();
|
||||
|
||||
@Override
|
||||
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
|
||||
super.initialize(cordova, webView);
|
||||
|
||||
this.tlsConfiguration = new TLSConfiguration();
|
||||
|
||||
this.reqMap = new HashMap<Integer, Future<?>>();
|
||||
|
||||
try {
|
||||
KeyStore store = KeyStore.getInstance("AndroidCAStore");
|
||||
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
|
||||
@@ -73,6 +82,8 @@ public class CordovaHttpPlugin extends CordovaPlugin {
|
||||
return this.setServerTrustMode(args, callbackContext);
|
||||
} else if ("setClientAuthMode".equals(action)) {
|
||||
return this.setClientAuthMode(args, callbackContext);
|
||||
} else if ("abort".equals(action)) {
|
||||
return this.abort(args, callbackContext);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -87,10 +98,13 @@ public class CordovaHttpPlugin extends CordovaPlugin {
|
||||
boolean followRedirect = args.getBoolean(3);
|
||||
String responseType = args.getString(4);
|
||||
|
||||
CordovaHttpOperation request = new CordovaHttpOperation(method.toUpperCase(), url, headers, timeout, followRedirect,
|
||||
responseType, this.tlsConfiguration, callbackContext);
|
||||
Integer reqId = args.getInt(5);
|
||||
CordovaObservableCallbackContext observableCallbackContext = new CordovaObservableCallbackContext(callbackContext, reqId);
|
||||
|
||||
cordova.getThreadPool().execute(request);
|
||||
CordovaHttpOperation request = new CordovaHttpOperation(method.toUpperCase(), url, headers, timeout, followRedirect,
|
||||
responseType, this.tlsConfiguration, observableCallbackContext);
|
||||
|
||||
startRequest(reqId, observableCallbackContext, request);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -106,10 +120,13 @@ public class CordovaHttpPlugin extends CordovaPlugin {
|
||||
boolean followRedirect = args.getBoolean(5);
|
||||
String responseType = args.getString(6);
|
||||
|
||||
CordovaHttpOperation request = new CordovaHttpOperation(method.toUpperCase(), url, serializer, data, headers,
|
||||
timeout, followRedirect, responseType, this.tlsConfiguration, callbackContext);
|
||||
Integer reqId = args.getInt(7);
|
||||
CordovaObservableCallbackContext observableCallbackContext = new CordovaObservableCallbackContext(callbackContext, reqId);
|
||||
|
||||
cordova.getThreadPool().execute(request);
|
||||
CordovaHttpOperation request = new CordovaHttpOperation(method.toUpperCase(), url, serializer, data, headers,
|
||||
timeout, followRedirect, responseType, this.tlsConfiguration, observableCallbackContext);
|
||||
|
||||
startRequest(reqId, observableCallbackContext, request);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -123,10 +140,13 @@ public class CordovaHttpPlugin extends CordovaPlugin {
|
||||
boolean followRedirect = args.getBoolean(5);
|
||||
String responseType = args.getString(6);
|
||||
|
||||
CordovaHttpUpload upload = new CordovaHttpUpload(url, headers, filePaths, uploadNames, timeout, followRedirect,
|
||||
responseType, this.tlsConfiguration, this.cordova.getActivity().getApplicationContext(), callbackContext);
|
||||
Integer reqId = args.getInt(7);
|
||||
CordovaObservableCallbackContext observableCallbackContext = new CordovaObservableCallbackContext(callbackContext, reqId);
|
||||
|
||||
cordova.getThreadPool().execute(upload);
|
||||
CordovaHttpUpload upload = new CordovaHttpUpload(url, headers, filePaths, uploadNames, timeout, followRedirect,
|
||||
responseType, this.tlsConfiguration, this.cordova.getActivity().getApplicationContext(), observableCallbackContext);
|
||||
|
||||
startRequest(reqId, observableCallbackContext, upload);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -138,14 +158,25 @@ public class CordovaHttpPlugin extends CordovaPlugin {
|
||||
int timeout = args.getInt(3) * 1000;
|
||||
boolean followRedirect = args.getBoolean(4);
|
||||
|
||||
CordovaHttpDownload download = new CordovaHttpDownload(url, headers, filePath, timeout, followRedirect,
|
||||
this.tlsConfiguration, callbackContext);
|
||||
Integer reqId = args.getInt(5);
|
||||
CordovaObservableCallbackContext observableCallbackContext = new CordovaObservableCallbackContext(callbackContext, reqId);
|
||||
|
||||
cordova.getThreadPool().execute(download);
|
||||
CordovaHttpDownload download = new CordovaHttpDownload(url, headers, filePath, timeout, followRedirect,
|
||||
this.tlsConfiguration, observableCallbackContext);
|
||||
|
||||
startRequest(reqId, observableCallbackContext, download);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void startRequest(Integer reqId, CordovaObservableCallbackContext observableCallbackContext, CordovaHttpBase request) {
|
||||
synchronized (reqMapLock) {
|
||||
observableCallbackContext.setObserver(this);
|
||||
Future<?> task = cordova.getThreadPool().submit(request);
|
||||
this.addReq(reqId, task, observableCallbackContext);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean setServerTrustMode(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
CordovaServerTrust runnable = new CordovaServerTrust(args.getString(0), this.cordova.getActivity(),
|
||||
this.tlsConfiguration, callbackContext);
|
||||
@@ -166,4 +197,44 @@ public class CordovaHttpPlugin extends CordovaPlugin {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean abort(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
|
||||
int reqId = args.getInt(0);
|
||||
boolean result = false;
|
||||
// NOTE no synchronized (reqMapLock), since even if the req was already removed from reqMap,
|
||||
// the worst that would happen calling task.cancel(true) is a result of false
|
||||
// (i.e. same result as locking & not finding the req in reqMap)
|
||||
Future<?> task = this.reqMap.get(reqId);
|
||||
if (task != null && !task.isDone()) {
|
||||
result = task.cancel(true);
|
||||
}
|
||||
callbackContext.success(new JSONObject().put("aborted", result));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void addReq(final Integer reqId, final Future<?> task, final CordovaObservableCallbackContext observableCallbackContext) {
|
||||
synchronized (reqMapLock) {
|
||||
if(!task.isDone()){
|
||||
this.reqMap.put(reqId, task);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void removeReq(final Integer reqId) {
|
||||
synchronized (reqMapLock) {
|
||||
this.reqMap.remove(reqId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Observable o, Object arg) {
|
||||
synchronized (reqMapLock) {
|
||||
CordovaObservableCallbackContext c = (CordovaObservableCallbackContext) arg;
|
||||
if (c.getCallbackContext().isFinished()) {
|
||||
removeReq(c.getRequestId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ import java.net.URI;
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
@@ -28,7 +27,7 @@ class CordovaHttpUpload extends CordovaHttpBase {
|
||||
|
||||
public CordovaHttpUpload(String url, JSONObject headers, JSONArray filePaths, JSONArray uploadNames, int timeout,
|
||||
boolean followRedirects, String responseType, TLSConfiguration tlsConfiguration,
|
||||
Context applicationContext, CallbackContext callbackContext) {
|
||||
Context applicationContext, CordovaObservableCallbackContext callbackContext) {
|
||||
|
||||
super("POST", url, headers, timeout, followRedirects, responseType, tlsConfiguration, callbackContext);
|
||||
this.filePaths = filePaths;
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.silkimen.cordovahttp;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.Observer;
|
||||
|
||||
public class CordovaObservableCallbackContext {
|
||||
|
||||
private CallbackContext callbackContext;
|
||||
private Integer requestId;
|
||||
private Observer observer;
|
||||
|
||||
public CordovaObservableCallbackContext(CallbackContext callbackContext, Integer requestId) {
|
||||
this.callbackContext = callbackContext;
|
||||
this.requestId = requestId;
|
||||
}
|
||||
|
||||
public void success(JSONObject message) {
|
||||
this.callbackContext.success(message);
|
||||
this.notifyObserver();
|
||||
}
|
||||
|
||||
public void error(JSONObject message) {
|
||||
this.callbackContext.error(message);
|
||||
this.notifyObserver();
|
||||
}
|
||||
|
||||
public Integer getRequestId() {
|
||||
return this.requestId;
|
||||
}
|
||||
|
||||
public CallbackContext getCallbackContext() {
|
||||
return callbackContext;
|
||||
}
|
||||
|
||||
public Observer getObserver() {
|
||||
return observer;
|
||||
}
|
||||
|
||||
protected void notifyObserver() {
|
||||
if(this.observer != null){
|
||||
this.observer.update(null, this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an observer that is notified, when {@link #success(JSONObject)}
|
||||
* or {@link #error(JSONObject)} are called.
|
||||
*
|
||||
* NOTE the observer is notified with
|
||||
* <pre>observer.update(null, cordovaObservableCallbackContext)</pre>
|
||||
* @param observer
|
||||
*/
|
||||
public void setObserver(Observer observer) {
|
||||
this.observer = observer;
|
||||
}
|
||||
}
|
||||
83
src/browser/cordova-http-plugin.js
vendored
83
src/browser/cordova-http-plugin.js
vendored
@@ -3,6 +3,8 @@ var pluginId = module.id.slice(0, module.id.lastIndexOf('.'));
|
||||
var cordovaProxy = require('cordova/exec/proxy');
|
||||
var jsUtil = require(pluginId + '.js-util');
|
||||
|
||||
var reqMap = {};
|
||||
|
||||
function serializeJsonData(data) {
|
||||
try {
|
||||
return JSON.stringify(data);
|
||||
@@ -20,7 +22,7 @@ function serializePrimitive(key, value) {
|
||||
}
|
||||
|
||||
function serializeArray(key, values) {
|
||||
return values.map(function(value) {
|
||||
return values.map(function (value) {
|
||||
return encodeURIComponent(key) + '[]=' + encodeURIComponent(value);
|
||||
}).join('&');
|
||||
}
|
||||
@@ -28,7 +30,7 @@ function serializeArray(key, values) {
|
||||
function serializeParams(params) {
|
||||
if (params === null) return '';
|
||||
|
||||
return Object.keys(params).map(function(key) {
|
||||
return Object.keys(params).map(function (key) {
|
||||
if (jsUtil.getTypeOf(params[key]) === 'Array') {
|
||||
return serializeArray(key, params[key]);
|
||||
}
|
||||
@@ -38,11 +40,11 @@ function serializeParams(params) {
|
||||
}
|
||||
|
||||
function decodeB64(dataString) {
|
||||
var binarString = atob(dataString);
|
||||
var bytes = new Uint8Array(binarString.length);
|
||||
var binaryString = atob(dataString);
|
||||
var bytes = new Uint8Array(binaryString.length);
|
||||
|
||||
for (var i = 0; i < binarString.length; ++i) {
|
||||
bytes[i] = binarString.charCodeAt(i);
|
||||
for (var i = 0; i < binaryString.length; ++i) {
|
||||
bytes[i] = binaryString.charCodeAt(i);
|
||||
}
|
||||
|
||||
return bytes.buffer;
|
||||
@@ -60,7 +62,7 @@ function processMultipartData(data) {
|
||||
var type = data.types[i];
|
||||
|
||||
if (fileName) {
|
||||
fd.append(name, new Blob([decodeB64(buffer)], {type: type}), fileName);
|
||||
fd.append(name, new Blob([decodeB64(buffer)], { type: type }), fileName);
|
||||
} else {
|
||||
// we assume it's plain text if no filename was given
|
||||
fd.append(name, atob(buffer));
|
||||
@@ -115,10 +117,17 @@ function createXhrFailureObject(xhr) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
function injectRequestIdHandler(reqId, cb) {
|
||||
return function (response) {
|
||||
delete reqMap[reqId];
|
||||
cb(response);
|
||||
}
|
||||
}
|
||||
|
||||
function getHeaderValue(headers, headerName) {
|
||||
let result = null;
|
||||
|
||||
Object.keys(headers).forEach(function(key) {
|
||||
Object.keys(headers).forEach(function (key) {
|
||||
if (key.toLowerCase() === headerName.toLowerCase()) {
|
||||
result = headers[key];
|
||||
}
|
||||
@@ -134,7 +143,7 @@ function setDefaultContentType(headers, contentType) {
|
||||
}
|
||||
|
||||
function setHeaders(xhr, headers) {
|
||||
Object.keys(headers).forEach(function(key) {
|
||||
Object.keys(headers).forEach(function (key) {
|
||||
if (key.toLowerCase() === 'cookie') return;
|
||||
|
||||
xhr.setRequestHeader(key, headers[key]);
|
||||
@@ -142,7 +151,7 @@ function setHeaders(xhr, headers) {
|
||||
}
|
||||
|
||||
function sendRequest(method, withData, opts, success, failure) {
|
||||
var data, serializer, headers, timeout, followRedirect, responseType;
|
||||
var data, serializer, headers, timeout, followRedirect, responseType, reqId;
|
||||
var url = opts[0];
|
||||
|
||||
if (withData) {
|
||||
@@ -152,25 +161,31 @@ function sendRequest(method, withData, opts, success, failure) {
|
||||
timeout = opts[4];
|
||||
followRedirect = opts[5];
|
||||
responseType = opts[6];
|
||||
reqId = opts[7];
|
||||
} else {
|
||||
headers = opts[1];
|
||||
timeout = opts[2];
|
||||
followRedirect = opts[3];
|
||||
responseType = opts[4];
|
||||
|
||||
reqId = opts[5];
|
||||
}
|
||||
|
||||
var onSuccess = injectRequestIdHandler(reqId, success);
|
||||
var onFail = injectRequestIdHandler(reqId, failure);
|
||||
|
||||
var processedData = null;
|
||||
var xhr = new XMLHttpRequest();
|
||||
|
||||
reqMap[reqId] = xhr;
|
||||
|
||||
xhr.open(method, url);
|
||||
|
||||
if (headers.Cookie && headers.Cookie.length > 0) {
|
||||
return failure('advanced-http: custom cookies not supported on browser platform');
|
||||
return onFail('advanced-http: custom cookies not supported on browser platform');
|
||||
}
|
||||
|
||||
if (!followRedirect) {
|
||||
return failure('advanced-http: disabling follow redirect not supported on browser platform');
|
||||
return onFail('advanced-http: disabling follow redirect not supported on browser platform');
|
||||
}
|
||||
|
||||
switch (serializer) {
|
||||
@@ -179,7 +194,7 @@ function sendRequest(method, withData, opts, success, failure) {
|
||||
processedData = serializeJsonData(data);
|
||||
|
||||
if (processedData === null) {
|
||||
return failure('advanced-http: failed serializing data');
|
||||
return onFail('advanced-http: failed serializing data');
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -196,7 +211,7 @@ function sendRequest(method, withData, opts, success, failure) {
|
||||
|
||||
case 'multipart':
|
||||
const contentType = getHeaderValue(headers, 'Content-Type');
|
||||
|
||||
|
||||
// intentionally don't set a default content type
|
||||
// it's set by the browser together with the content disposition string
|
||||
if (contentType) {
|
||||
@@ -212,16 +227,26 @@ function sendRequest(method, withData, opts, success, failure) {
|
||||
break;
|
||||
}
|
||||
|
||||
// requesting text instead of JSON because it's parsed in the response handler
|
||||
xhr.responseType = responseType === 'json' ? 'text' : responseType;
|
||||
xhr.timeout = timeout * 1000;
|
||||
xhr.responseType = responseType;
|
||||
setHeaders(xhr, headers);
|
||||
|
||||
xhr.onerror = function () {
|
||||
return failure(createXhrFailureObject(xhr));
|
||||
return onFail(createXhrFailureObject(xhr));
|
||||
};
|
||||
|
||||
xhr.ontimeout = function () {
|
||||
return failure({
|
||||
xhr.onabort = function () {
|
||||
return onFail({
|
||||
status: -8,
|
||||
error: 'Request was aborted',
|
||||
url: url,
|
||||
headers: {}
|
||||
});
|
||||
};
|
||||
|
||||
xhr.ontimeout = function () {
|
||||
return onFail({
|
||||
status: -4,
|
||||
error: 'Request timed out',
|
||||
url: url,
|
||||
@@ -233,15 +258,28 @@ function sendRequest(method, withData, opts, success, failure) {
|
||||
if (xhr.readyState !== xhr.DONE) return;
|
||||
|
||||
if (xhr.status < 200 || xhr.status > 299) {
|
||||
return failure(createXhrFailureObject(xhr));
|
||||
return onFail(createXhrFailureObject(xhr));
|
||||
}
|
||||
|
||||
return success(createXhrSuccessObject(xhr));
|
||||
return onSuccess(createXhrSuccessObject(xhr));
|
||||
};
|
||||
|
||||
xhr.send(processedData);
|
||||
}
|
||||
|
||||
function abort(opts, success, failure) {
|
||||
var reqId = opts[0];
|
||||
var result = false;
|
||||
|
||||
var xhr = reqMap[reqId];
|
||||
if(xhr && xhr.readyState !== xhr.DONE){
|
||||
xhr.abort();
|
||||
result = true;
|
||||
}
|
||||
|
||||
success({aborted: result});
|
||||
}
|
||||
|
||||
var browserInterface = {
|
||||
get: function (success, failure, opts) {
|
||||
return sendRequest('get', false, opts, success, failure);
|
||||
@@ -261,6 +299,9 @@ var browserInterface = {
|
||||
patch: function (success, failure, opts) {
|
||||
return sendRequest('patch', true, opts, success, failure);
|
||||
},
|
||||
abort: function (success, failure, opts) {
|
||||
return abort(opts, success, failure);
|
||||
},
|
||||
uploadFile: function (success, failure, opts) {
|
||||
return failure('advanced-http: function "uploadFile" not supported on browser platform');
|
||||
},
|
||||
|
||||
@@ -15,5 +15,6 @@
|
||||
- (void)options:(CDVInvokedUrlCommand*)command;
|
||||
- (void)uploadFiles:(CDVInvokedUrlCommand*)command;
|
||||
- (void)downloadFile:(CDVInvokedUrlCommand*)command;
|
||||
- (void)abort:(CDVInvokedUrlCommand*)command;
|
||||
|
||||
@end
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
@interface CordovaHttpPlugin()
|
||||
|
||||
- (void)addRequest:(NSNumber*)reqId forTask:(NSURLSessionDataTask*)task;
|
||||
- (void)removeRequest:(NSNumber*)reqId;
|
||||
- (void)setRequestHeaders:(NSDictionary*)headers forManager:(AFHTTPSessionManager*)manager;
|
||||
- (void)handleSuccess:(NSMutableDictionary*)dictionary withResponse:(NSHTTPURLResponse*)response andData:(id)data;
|
||||
- (void)handleError:(NSMutableDictionary*)dictionary withResponse:(NSHTTPURLResponse*)response error:(NSError*)error;
|
||||
@@ -22,10 +24,20 @@
|
||||
@implementation CordovaHttpPlugin {
|
||||
AFSecurityPolicy *securityPolicy;
|
||||
NSURLCredential *x509Credential;
|
||||
NSMutableDictionary *reqDict;
|
||||
}
|
||||
|
||||
- (void)pluginInitialize {
|
||||
securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
|
||||
reqDict = [NSMutableDictionary dictionary];
|
||||
}
|
||||
|
||||
- (void)addRequest:(NSNumber*)reqId forTask:(NSURLSessionDataTask*)task {
|
||||
[reqDict setObject:task forKey:reqId];
|
||||
}
|
||||
|
||||
- (void)removeRequest:(NSNumber*)reqId {
|
||||
[reqDict removeObjectForKey:reqId];
|
||||
}
|
||||
|
||||
- (void)setRequestSerializer:(NSString*)serializerName forManager:(AFHTTPSessionManager*)manager {
|
||||
@@ -52,7 +64,7 @@
|
||||
if (![self->securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
|
||||
return NSURLSessionAuthChallengeRejectProtectionSpace;
|
||||
}
|
||||
|
||||
|
||||
if (credential) {
|
||||
return NSURLSessionAuthChallengeUseCredential;
|
||||
}
|
||||
@@ -62,7 +74,7 @@
|
||||
*credential = self->x509Credential;
|
||||
return NSURLSessionAuthChallengeUseCredential;
|
||||
}
|
||||
|
||||
|
||||
return NSURLSessionAuthChallengePerformDefaultHandling;
|
||||
}];
|
||||
}
|
||||
@@ -111,14 +123,21 @@
|
||||
}
|
||||
|
||||
- (void)handleError:(NSMutableDictionary*)dictionary withResponse:(NSHTTPURLResponse*)response error:(NSError*)error {
|
||||
bool aborted = error.code == NSURLErrorCancelled;
|
||||
if(aborted){
|
||||
[dictionary setObject:[NSNumber numberWithInt:-8] forKey:@"status"];
|
||||
[dictionary setObject:@"Request was aborted" forKey:@"error"];
|
||||
}
|
||||
if (response != nil) {
|
||||
[dictionary setValue:response.URL.absoluteString forKey:@"url"];
|
||||
[dictionary setObject:[NSNumber numberWithInt:(int)response.statusCode] forKey:@"status"];
|
||||
[dictionary setObject:[self copyHeaderFields:response.allHeaderFields] forKey:@"headers"];
|
||||
if (error.userInfo[AFNetworkingOperationFailingURLResponseBodyErrorKey]) {
|
||||
[dictionary setObject:error.userInfo[AFNetworkingOperationFailingURLResponseBodyErrorKey] forKey:@"error"];
|
||||
if(!aborted){
|
||||
[dictionary setObject:[NSNumber numberWithInt:(int)response.statusCode] forKey:@"status"];
|
||||
if (error.userInfo[AFNetworkingOperationFailingURLResponseBodyErrorKey]) {
|
||||
[dictionary setObject:error.userInfo[AFNetworkingOperationFailingURLResponseBodyErrorKey] forKey:@"error"];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} else if(!aborted) {
|
||||
[dictionary setObject:[self getStatusCode:error] forKey:@"status"];
|
||||
[dictionary setObject:[error localizedDescription] forKey:@"error"];
|
||||
}
|
||||
@@ -181,6 +200,7 @@
|
||||
NSTimeInterval timeoutInSeconds = [[command.arguments objectAtIndex:2] doubleValue];
|
||||
bool followRedirect = [[command.arguments objectAtIndex:3] boolValue];
|
||||
NSString *responseType = [command.arguments objectAtIndex:4];
|
||||
NSNumber *reqId = [command.arguments objectAtIndex:5];
|
||||
|
||||
[self setRequestSerializer: @"default" forManager: manager];
|
||||
[self setupAuthChallengeBlock: manager];
|
||||
@@ -194,30 +214,35 @@
|
||||
|
||||
@try {
|
||||
void (^onSuccess)(NSURLSessionTask *, id) = ^(NSURLSessionTask *task, id responseObject) {
|
||||
[weakSelf removeRequest:reqId];
|
||||
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
||||
|
||||
|
||||
// no 'body' for HEAD request, omitting 'data'
|
||||
if ([method isEqualToString:@"HEAD"]) {
|
||||
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:nil];
|
||||
} else {
|
||||
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:responseObject];
|
||||
}
|
||||
|
||||
|
||||
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
};
|
||||
|
||||
|
||||
void (^onFailure)(NSURLSessionTask *, NSError *) = ^(NSURLSessionTask *task, NSError *error) {
|
||||
[weakSelf removeRequest:reqId];
|
||||
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
||||
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
|
||||
|
||||
|
||||
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
};
|
||||
|
||||
[manager downloadTaskWithHTTPMethod:method URLString:url parameters:nil progress:nil success:onSuccess failure:onFailure];
|
||||
|
||||
NSURLSessionDataTask *task = [manager downloadTaskWithHTTPMethod:method URLString:url parameters:nil progress:nil success:onSuccess failure:onFailure];
|
||||
[self addRequest:reqId forTask:task];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
@@ -235,6 +260,7 @@
|
||||
NSTimeInterval timeoutInSeconds = [[command.arguments objectAtIndex:4] doubleValue];
|
||||
bool followRedirect = [[command.arguments objectAtIndex:5] boolValue];
|
||||
NSString *responseType = [command.arguments objectAtIndex:6];
|
||||
NSNumber *reqId = [command.arguments objectAtIndex:7];
|
||||
|
||||
[self setRequestSerializer: serializerName forManager: manager];
|
||||
[self setupAuthChallengeBlock: manager];
|
||||
@@ -245,30 +271,32 @@
|
||||
|
||||
CordovaHttpPlugin* __weak weakSelf = self;
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
|
||||
|
||||
|
||||
@try {
|
||||
void (^constructBody)(id<AFMultipartFormData>) = ^(id<AFMultipartFormData> formData) {
|
||||
NSArray *buffers = [data mutableArrayValueForKey:@"buffers"];
|
||||
NSArray *fileNames = [data mutableArrayValueForKey:@"fileNames"];
|
||||
NSArray *names = [data mutableArrayValueForKey:@"names"];
|
||||
NSArray *types = [data mutableArrayValueForKey:@"types"];
|
||||
|
||||
|
||||
NSError *error;
|
||||
|
||||
|
||||
for (int i = 0; i < [buffers count]; ++i) {
|
||||
NSData *decodedBuffer = [[NSData alloc] initWithBase64EncodedString:[buffers objectAtIndex:i] options:0];
|
||||
NSString *fileName = [fileNames objectAtIndex:i];
|
||||
NSString *partName = [names objectAtIndex:i];
|
||||
NSString *partType = [types objectAtIndex:i];
|
||||
|
||||
|
||||
if (![fileName isEqual:[NSNull null]]) {
|
||||
[formData appendPartWithFileData:decodedBuffer name:partName fileName:fileName mimeType:partType];
|
||||
} else {
|
||||
[formData appendPartWithFormData:decodedBuffer name:[names objectAtIndex:i]];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (error) {
|
||||
[weakSelf removeRequest:reqId];
|
||||
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
||||
[dictionary setObject:[NSNumber numberWithInt:400] forKey:@"status"];
|
||||
[dictionary setObject:@"Could not add part to multipart request body." forKey:@"error"];
|
||||
@@ -278,30 +306,36 @@
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void (^onSuccess)(NSURLSessionTask *, id) = ^(NSURLSessionTask *task, id responseObject) {
|
||||
[weakSelf removeRequest:reqId];
|
||||
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
||||
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:responseObject];
|
||||
|
||||
|
||||
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
};
|
||||
|
||||
|
||||
void (^onFailure)(NSURLSessionTask *, NSError *) = ^(NSURLSessionTask *task, NSError *error) {
|
||||
[weakSelf removeRequest:reqId];
|
||||
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
||||
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
|
||||
|
||||
|
||||
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
};
|
||||
|
||||
|
||||
NSURLSessionDataTask *task;
|
||||
if ([serializerName isEqualToString:@"multipart"]) {
|
||||
[manager uploadTaskWithHTTPMethod:method URLString:url parameters:nil constructingBodyWithBlock:constructBody progress:nil success:onSuccess failure:onFailure];
|
||||
task = [manager uploadTaskWithHTTPMethod:method URLString:url parameters:nil constructingBodyWithBlock:constructBody progress:nil success:onSuccess failure:onFailure];
|
||||
} else {
|
||||
[manager uploadTaskWithHTTPMethod:method URLString:url parameters:data progress:nil success:onSuccess failure:onFailure];
|
||||
task = [manager uploadTaskWithHTTPMethod:method URLString:url parameters:data progress:nil success:onSuccess failure:onFailure];
|
||||
}
|
||||
[self addRequest:reqId forTask:task];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
@@ -333,27 +367,27 @@
|
||||
- (void)setClientAuthMode:(CDVInvokedUrlCommand*)command {
|
||||
CDVPluginResult* pluginResult;
|
||||
NSString *mode = [command.arguments objectAtIndex:0];
|
||||
|
||||
|
||||
if ([mode isEqualToString:@"none"]) {
|
||||
x509Credential = nil;
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
|
||||
}
|
||||
|
||||
|
||||
if ([mode isEqualToString:@"systemstore"]) {
|
||||
NSString *alias = [command.arguments objectAtIndex:1];
|
||||
|
||||
|
||||
// TODO
|
||||
|
||||
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"mode 'systemstore' is not supported on iOS"];
|
||||
}
|
||||
|
||||
|
||||
if ([mode isEqualToString:@"buffer"]) {
|
||||
CFDataRef container = (__bridge CFDataRef) [command.arguments objectAtIndex:2];
|
||||
CFStringRef password = (__bridge CFStringRef) [command.arguments objectAtIndex:3];
|
||||
|
||||
|
||||
const void *keys[] = { kSecImportExportPassphrase };
|
||||
const void *values[] = { password };
|
||||
|
||||
|
||||
CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
|
||||
CFArrayRef items;
|
||||
OSStatus securityError = SecPKCS12Import(container, options, &items);
|
||||
@@ -367,7 +401,7 @@
|
||||
|
||||
self->x509Credential = [NSURLCredential credentialWithIdentity:identity certificates: nil persistence:NSURLCredentialPersistenceForSession];
|
||||
CFRelease(items);
|
||||
|
||||
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
|
||||
}
|
||||
}
|
||||
@@ -413,6 +447,7 @@
|
||||
NSTimeInterval timeoutInSeconds = [[command.arguments objectAtIndex:4] doubleValue];
|
||||
bool followRedirect = [[command.arguments objectAtIndex:5] boolValue];
|
||||
NSString *responseType = [command.arguments objectAtIndex:6];
|
||||
NSNumber *reqId = [command.arguments objectAtIndex:7];
|
||||
|
||||
[self setRequestHeaders: headers forManager: manager];
|
||||
[self setupAuthChallengeBlock: manager];
|
||||
@@ -424,7 +459,7 @@
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
|
||||
|
||||
@try {
|
||||
[manager POST:url parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
|
||||
NSURLSessionDataTask *task = [manager POST:url parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
|
||||
NSError *error;
|
||||
for (int i = 0; i < [filePaths count]; i++) {
|
||||
NSString *filePath = (NSString *) [filePaths objectAtIndex:i];
|
||||
@@ -433,6 +468,8 @@
|
||||
[formData appendPartWithFileURL:fileURL name:uploadName error:&error];
|
||||
}
|
||||
if (error) {
|
||||
[weakSelf removeRequest:reqId];
|
||||
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
||||
[dictionary setObject:[NSNumber numberWithInt:500] forKey:@"status"];
|
||||
[dictionary setObject:@"Could not add file to post body." forKey:@"error"];
|
||||
@@ -442,6 +479,8 @@
|
||||
return;
|
||||
}
|
||||
} progress:nil success:^(NSURLSessionTask *task, id responseObject) {
|
||||
[weakSelf removeRequest:reqId];
|
||||
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
||||
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:responseObject];
|
||||
|
||||
@@ -449,6 +488,8 @@
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
} failure:^(NSURLSessionTask *task, NSError *error) {
|
||||
[weakSelf removeRequest:reqId];
|
||||
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
||||
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
|
||||
|
||||
@@ -456,6 +497,7 @@
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
}];
|
||||
[self addRequest:reqId forTask:task];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
@@ -472,6 +514,7 @@
|
||||
NSString *filePath = [command.arguments objectAtIndex: 2];
|
||||
NSTimeInterval timeoutInSeconds = [[command.arguments objectAtIndex:3] doubleValue];
|
||||
bool followRedirect = [[command.arguments objectAtIndex:4] boolValue];
|
||||
NSNumber *reqId = [command.arguments objectAtIndex:5];
|
||||
|
||||
[self setRequestHeaders: headers forManager: manager];
|
||||
[self setupAuthChallengeBlock: manager];
|
||||
@@ -486,7 +529,8 @@
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
|
||||
|
||||
@try {
|
||||
[manager GET:url parameters:nil progress: nil success:^(NSURLSessionTask *task, id responseObject) {
|
||||
NSURLSessionDataTask *task = [manager GET:url parameters:nil progress: nil success:^(NSURLSessionTask *task, id responseObject) {
|
||||
[weakSelf removeRequest:reqId];
|
||||
/*
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
@@ -547,6 +591,7 @@
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
} failure:^(NSURLSessionTask *task, NSError *error) {
|
||||
[weakSelf removeRequest:reqId];
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
||||
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
|
||||
[dictionary setObject:@"There was an error downloading the file" forKey:@"error"];
|
||||
@@ -555,6 +600,7 @@
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
}];
|
||||
[self addRequest:reqId forTask:task];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
@@ -562,4 +608,32 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)abort:(CDVInvokedUrlCommand*)command {
|
||||
|
||||
NSNumber *reqId = [command.arguments objectAtIndex:0];
|
||||
|
||||
CDVPluginResult *pluginResult;
|
||||
bool removed = false;
|
||||
NSURLSessionDataTask *task = [reqDict objectForKey:reqId];
|
||||
if(task){
|
||||
@try{
|
||||
[task cancel];
|
||||
removed = true;
|
||||
} @catch (NSException *exception) {
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
||||
[dictionary setValue:exception.userInfo forKey:@"error"];
|
||||
[dictionary setObject:[NSNumber numberWithInt:-1] forKey:@"status"];
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
|
||||
}
|
||||
}
|
||||
|
||||
if(!pluginResult){
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
||||
[dictionary setObject:[NSNumber numberWithBool:removed] forKey:@"aborted"];
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
|
||||
}
|
||||
|
||||
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
<allow-intent href="mailto:*" />
|
||||
<allow-intent href="geo:*" />
|
||||
<platform name="android">
|
||||
<edit-config file="app/src/main/AndroidManifest.xml" mode="merge" target="/manifest/application" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<application android:networkSecurityConfig="@xml/network_security_config" />
|
||||
</edit-config>
|
||||
<resource-file src="network_security_config.xml" target="app/src/main/res/xml/network_security_config.xml" />
|
||||
<allow-intent href="market:*" />
|
||||
</platform>
|
||||
<platform name="ios">
|
||||
|
||||
7
test/e2e-app-template/network_security_config.xml
Normal file
7
test/e2e-app-template/network_security_config.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<domain-config cleartextTrafficPermitted="true">
|
||||
<domain includeSubdomains="true">localhost</domain>
|
||||
<domain includeSubdomains="true">httpbin.org</domain>
|
||||
</domain-config>
|
||||
</network-security-config>
|
||||
@@ -20,7 +20,12 @@
|
||||
"platforms": [
|
||||
"android",
|
||||
"ios"
|
||||
]
|
||||
],
|
||||
"plugins": {
|
||||
"cordova-plugin-device": {}
|
||||
}
|
||||
},
|
||||
"devDependencies": {}
|
||||
}
|
||||
"devDependencies": {
|
||||
"cordova-plugin-device": "2.0.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,12 +10,12 @@ const app = {
|
||||
|
||||
var onlyFlaggedTests = [];
|
||||
var enabledTests = [];
|
||||
|
||||
|
||||
tests.forEach(function (test) {
|
||||
if (test.only) {
|
||||
onlyFlaggedTests.push(test);
|
||||
}
|
||||
|
||||
|
||||
if (!test.disabled) {
|
||||
enabledTests.push(test);
|
||||
}
|
||||
@@ -50,6 +50,16 @@ const app = {
|
||||
};
|
||||
},
|
||||
|
||||
skip: function (content) {
|
||||
document.getElementById('statusInput').value = 'finished';
|
||||
app.printResult('result - skipped', content);
|
||||
|
||||
app.lastResult = {
|
||||
type: 'skipped',
|
||||
data: content
|
||||
};
|
||||
},
|
||||
|
||||
throw: function (error) {
|
||||
document.getElementById('statusInput').value = 'finished';
|
||||
app.printResult('result - throwed', error.message);
|
||||
@@ -126,7 +136,7 @@ const app = {
|
||||
|
||||
const execTest = function () {
|
||||
try {
|
||||
testDefinition.func(app.resolve, app.reject);
|
||||
testDefinition.func(app.resolve, app.reject, app.skip);
|
||||
} catch (error) {
|
||||
app.throw(error);
|
||||
}
|
||||
|
||||
@@ -83,7 +83,16 @@ const helpers = {
|
||||
}
|
||||
|
||||
result.type.should.be.equal(expected);
|
||||
}
|
||||
},
|
||||
isAbortSupported: function () {
|
||||
if (window.cordova && window.cordova.platformId === 'android') {
|
||||
var version = device.version; //NOTE will throw error if cordova is present without cordova-plugin-device
|
||||
var major = parseInt(/^(\d+)(\.|$)/.exec(version)[1], 10);
|
||||
return isFinite(major) && major >= 6;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
getAbortDelay: function () { return 10; },
|
||||
};
|
||||
|
||||
const messageFactory = {
|
||||
@@ -988,6 +997,118 @@ const tests = [
|
||||
JSON.parse(result.data.data).cookies.should.be.eql({});
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should be able to abort (POST)',
|
||||
expected: 'rejected: {"status":-8, "error": "Request ...}',
|
||||
before: helpers.setRawSerializer,
|
||||
func: function (resolve, reject, skip) {
|
||||
if (!helpers.isAbortSupported()) {
|
||||
skip();
|
||||
return;
|
||||
}
|
||||
helpers.getWithXhr(function (buffer) {
|
||||
var reqId = cordova.plugin.http.post('http://httpbin.org/anything', buffer, {}, resolve, reject);
|
||||
|
||||
setTimeout(function () {
|
||||
cordova.plugin.http.abort(reqId);
|
||||
}, helpers.getAbortDelay());
|
||||
|
||||
}, './res/cordova_logo.png', 'arraybuffer');
|
||||
},
|
||||
validationFunc: function (driver, result) {
|
||||
helpers.checkResult(result, 'rejected');
|
||||
result.data.status.should.be.equal(-8);
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should be able to abort (GET)',
|
||||
expected: 'rejected: {"status":-8, "error": "Request ...}',
|
||||
func: function (resolve, reject, skip) {
|
||||
if (!helpers.isAbortSupported()) {
|
||||
skip();
|
||||
return;
|
||||
}
|
||||
var url = 'https://httpbin.org/image/jpeg';
|
||||
var options = { method: 'get', responseType: 'blob' };
|
||||
var success = function (response) {
|
||||
resolve({
|
||||
isBlob: response.data.constructor === Blob,
|
||||
type: response.data.type,
|
||||
byteLength: response.data.size
|
||||
});
|
||||
};
|
||||
|
||||
var reqId = cordova.plugin.http.sendRequest(url, options, success, reject);
|
||||
setTimeout(function () {
|
||||
cordova.plugin.http.abort(reqId);
|
||||
}, helpers.getAbortDelay());
|
||||
},
|
||||
validationFunc: function (driver, result) {
|
||||
helpers.checkResult(result, 'rejected');
|
||||
result.data.status.should.be.equal(-8);
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should be able to abort downloading a file',
|
||||
expected: 'rejected: {"status":-8, "error": "Request ...}',
|
||||
func: function (resolve, reject, skip) {
|
||||
if (!helpers.isAbortSupported()) {
|
||||
skip();
|
||||
return;
|
||||
}
|
||||
var sourceUrl = 'http://httpbin.org/xml';
|
||||
var targetPath = cordova.file.cacheDirectory + 'test.xml';
|
||||
|
||||
var reqId = cordova.plugin.http.downloadFile(sourceUrl, {}, {}, targetPath, function (entry) {
|
||||
helpers.getWithXhr(function (content) {
|
||||
resolve({
|
||||
sourceUrl: sourceUrl,
|
||||
targetPath: targetPath,
|
||||
fullPath: entry.fullPath,
|
||||
name: entry.name,
|
||||
content: content
|
||||
});
|
||||
}, targetPath);
|
||||
}, reject);
|
||||
|
||||
setTimeout(function () {
|
||||
cordova.plugin.http.abort(reqId);
|
||||
}, helpers.getAbortDelay());
|
||||
|
||||
},
|
||||
validationFunc: function (driver, result) {
|
||||
helpers.checkResult(result, 'rejected');
|
||||
result.data.status.should.be.equal(-8);
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should be able to abort uploading a file',
|
||||
expected: 'rejected: {"status":-8, "error": "Request ...}',
|
||||
func: function (resolve, reject, skip) {
|
||||
if (!helpers.isAbortSupported()) {
|
||||
skip();
|
||||
return;
|
||||
}
|
||||
var fileName = 'test-file.txt';
|
||||
var fileContent = 'I am a dummy file. I am used for testing purposes!';
|
||||
var sourcePath = cordova.file.cacheDirectory + fileName;
|
||||
var targetUrl = 'http://httpbin.org/post';
|
||||
|
||||
helpers.writeToFile(function () {
|
||||
|
||||
var reqId = cordova.plugin.http.uploadFile(targetUrl, {}, {}, sourcePath, fileName, resolve, reject);
|
||||
|
||||
setTimeout(function () {
|
||||
cordova.plugin.http.abort(reqId);
|
||||
}, helpers.getAbortDelay());
|
||||
|
||||
}, fileName, fileContent);
|
||||
},
|
||||
validationFunc: function (driver, result) {
|
||||
helpers.checkResult(result, 'rejected');
|
||||
result.data.status.should.be.equal(-8);
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
|
||||
@@ -49,12 +49,19 @@ describe('Advanced HTTP e2e test suite', function () {
|
||||
});
|
||||
|
||||
const defineTestForMocha = (test, index) => {
|
||||
it(index + ': ' + test.description, async () => {
|
||||
it(index + ': ' + test.description, async function () {
|
||||
await clickNext(driver);
|
||||
await validateTestIndex(driver, index);
|
||||
await validateTestTitle(driver, test.description);
|
||||
await waitToBeFinished(driver, test.timeout || 10000);
|
||||
await validateResult(driver, test.validationFunc, targetInfo);
|
||||
|
||||
const skipped = await checkSkipped(driver);
|
||||
|
||||
if (skipped) {
|
||||
this.skip();
|
||||
} else {
|
||||
await validateResult(driver, test.validationFunc, targetInfo);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -117,6 +124,11 @@ async function validateResult(driver, validationFunc, targetInfo) {
|
||||
validationFunc(driver, result, targetInfo);
|
||||
}
|
||||
|
||||
async function checkSkipped(driver) {
|
||||
const result = await driver.safeExecute('app.lastResult');
|
||||
return result.type === 'skipped';
|
||||
}
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
@@ -676,6 +676,20 @@ describe('Common helpers', function () {
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
describe('nextRequestId()', function () {
|
||||
const helpers = require('../www/helpers')(null, null, null, null, null, null);
|
||||
|
||||
it('returns number requestIds', () => {
|
||||
helpers.nextRequestId().should.be.a('number');
|
||||
});
|
||||
|
||||
it('returns unique requestIds', () => {
|
||||
const ids = [helpers.nextRequestId(), helpers.nextRequestId(), helpers.nextRequestId()];
|
||||
const set = new Set(ids);
|
||||
ids.should.to.deep.equal(Array.from(set));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Dependency Validator', function () {
|
||||
|
||||
@@ -6,4 +6,5 @@ module.exports = {
|
||||
UNSUPPORTED_URL: -5,
|
||||
NOT_CONNECTED: -6,
|
||||
POST_PROCESSING_FAILED: -7,
|
||||
ABORTED: -8,
|
||||
};
|
||||
|
||||
@@ -5,6 +5,13 @@ module.exports = function init(global, jsUtil, cookieHandler, messages, base64,
|
||||
var validHttpMethods = ['get', 'put', 'post', 'patch', 'head', 'delete', 'options', 'upload', 'download'];
|
||||
var validResponseTypes = ['text', 'json', 'arraybuffer', 'blob'];
|
||||
|
||||
var nextRequestId = (function(){
|
||||
var currReqId = 0;
|
||||
return function nextRequestId() {
|
||||
return ++currReqId;
|
||||
}
|
||||
})();
|
||||
|
||||
var interface = {
|
||||
b64EncodeUnicode: b64EncodeUnicode,
|
||||
checkClientAuthMode: checkClientAuthMode,
|
||||
@@ -24,6 +31,7 @@ module.exports = function init(global, jsUtil, cookieHandler, messages, base64,
|
||||
injectCookieHandler: injectCookieHandler,
|
||||
injectFileEntryHandler: injectFileEntryHandler,
|
||||
injectRawResponseHandler: injectRawResponseHandler,
|
||||
nextRequestId: nextRequestId,
|
||||
};
|
||||
|
||||
// expose all functions for testing purposes
|
||||
|
||||
@@ -26,6 +26,7 @@ module.exports = function init(exec, cookieHandler, urlUtil, helpers, globalConf
|
||||
options: options,
|
||||
uploadFile: uploadFile,
|
||||
downloadFile: downloadFile,
|
||||
abort: abort,
|
||||
ErrorCode: errorCodes,
|
||||
ponyfills: ponyfills
|
||||
};
|
||||
@@ -143,23 +144,31 @@ module.exports = function init(exec, cookieHandler, urlUtil, helpers, globalConf
|
||||
var onFail = helpers.injectCookieHandler(url, failure);
|
||||
var onSuccess = helpers.injectCookieHandler(url, helpers.injectRawResponseHandler(options.responseType, success, failure));
|
||||
|
||||
var reqId = helpers.nextRequestId();
|
||||
|
||||
switch (options.method) {
|
||||
case 'post':
|
||||
case 'put':
|
||||
case 'patch':
|
||||
return helpers.processData(options.data, options.serializer, function (data) {
|
||||
exec(onSuccess, onFail, 'CordovaHttpPlugin', options.method, [url, data, options.serializer, headers, options.timeout, options.followRedirect, options.responseType]);
|
||||
helpers.processData(options.data, options.serializer, function (data) {
|
||||
exec(onSuccess, onFail, 'CordovaHttpPlugin', options.method, [url, data, options.serializer, headers, options.timeout, options.followRedirect, options.responseType, reqId]);
|
||||
});
|
||||
break;
|
||||
case 'upload':
|
||||
var fileOptions = helpers.checkUploadFileOptions(options.filePath, options.name);
|
||||
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'uploadFiles', [url, headers, fileOptions.filePaths, fileOptions.names, options.timeout, options.followRedirect, options.responseType]);
|
||||
exec(onSuccess, onFail, 'CordovaHttpPlugin', 'uploadFiles', [url, headers, fileOptions.filePaths, fileOptions.names, options.timeout, options.followRedirect, options.responseType, reqId]);
|
||||
break;
|
||||
case 'download':
|
||||
var filePath = helpers.checkDownloadFilePath(options.filePath);
|
||||
var onDownloadSuccess = helpers.injectCookieHandler(url, helpers.injectFileEntryHandler(success));
|
||||
return exec(onDownloadSuccess, onFail, 'CordovaHttpPlugin', 'downloadFile', [url, headers, filePath, options.timeout, options.followRedirect]);
|
||||
exec(onDownloadSuccess, onFail, 'CordovaHttpPlugin', 'downloadFile', [url, headers, filePath, options.timeout, options.followRedirect, reqId]);
|
||||
break;
|
||||
default:
|
||||
return exec(onSuccess, onFail, 'CordovaHttpPlugin', options.method, [url, headers, options.timeout, options.followRedirect, options.responseType]);
|
||||
exec(onSuccess, onFail, 'CordovaHttpPlugin', options.method, [url, headers, options.timeout, options.followRedirect, options.responseType, reqId]);
|
||||
break;
|
||||
}
|
||||
|
||||
return reqId;
|
||||
}
|
||||
|
||||
function post(url, data, headers, success, failure) {
|
||||
@@ -198,5 +207,9 @@ module.exports = function init(exec, cookieHandler, urlUtil, helpers, globalConf
|
||||
return publicInterface.sendRequest(url, { method: 'download', params: params, headers: headers, filePath: filePath }, success, failure);
|
||||
}
|
||||
|
||||
function abort(requestId , success, failure) {
|
||||
return exec(success, failure, 'CordovaHttpPlugin', 'abort', [requestId]);
|
||||
}
|
||||
|
||||
return publicInterface;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user