mirror of
https://github.com/silkimen/cordova-plugin-advanced-http.git
synced 2026-01-31 00:00:03 +08:00
added support for abort() on android platform
This commit is contained in:
@@ -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"/>
|
||||
@@ -92,4 +93,4 @@
|
||||
<runs/>
|
||||
</js-module>
|
||||
</platform>
|
||||
</plugin>
|
||||
</plugin>
|
||||
|
||||
@@ -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,8 +73,9 @@ 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);
|
||||
@@ -94,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());
|
||||
@@ -202,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,14 @@ 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);
|
||||
|
||||
Future<?> task = cordova.getThreadPool().submit(request);
|
||||
this.addReq(reqId, task, observableCallbackContext);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -106,10 +121,14 @@ 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);
|
||||
|
||||
Future<?> task = cordova.getThreadPool().submit(request);
|
||||
this.addReq(reqId, task, observableCallbackContext);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -123,10 +142,14 @@ 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);
|
||||
|
||||
Future<?> task = cordova.getThreadPool().submit(upload);
|
||||
this.addReq(reqId, task, observableCallbackContext);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -138,10 +161,14 @@ 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);
|
||||
|
||||
Future<?> task = cordova.getThreadPool().submit(download);
|
||||
this.addReq(reqId, task, observableCallbackContext);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -166,4 +193,49 @@ 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) {
|
||||
// NOTE there is a small chance that the task may already have tried to remove itself before
|
||||
// done-status was set (within the request run-thread)
|
||||
// to prevent that, the synchronized()-lock would need to be set around starting the
|
||||
// request and adding the entry to reqMap (which seems overkill given that is seems very unlikely)
|
||||
if(!task.isDone()){
|
||||
observableCallbackContext.setObserver(this);
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user