diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/logic/CharonVpnService.java b/src/frontends/android/app/src/main/java/org/strongswan/android/logic/CharonVpnService.java index 2f402de5d..34e793894 100644 --- a/src/frontends/android/app/src/main/java/org/strongswan/android/logic/CharonVpnService.java +++ b/src/frontends/android/app/src/main/java/org/strongswan/android/logic/CharonVpnService.java @@ -283,6 +283,7 @@ public class CharonVpnService extends VpnService implements Runnable, VpnStateSe startConnection(mCurrentProfile); mIsDisconnecting = false; + SimpleFetcher.enable(); addNotification(); mBuilderAdapter.setProfile(mCurrentProfile); if (initializeCharon(mBuilderAdapter, mLogFile, mAppDir, mCurrentProfile.getVpnType().has(VpnTypeFeature.BYOD))) @@ -350,6 +351,7 @@ public class CharonVpnService extends VpnService implements Runnable, VpnStateSe { setState(State.DISCONNECTING); mIsDisconnecting = true; + SimpleFetcher.disable(); deinitializeCharon(); Log.i(TAG, "charon stopped"); mCurrentProfile = null; diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/logic/SimpleFetcher.java b/src/frontends/android/app/src/main/java/org/strongswan/android/logic/SimpleFetcher.java index b7334cee1..6eaccc4d8 100644 --- a/src/frontends/android/app/src/main/java/org/strongswan/android/logic/SimpleFetcher.java +++ b/src/frontends/android/app/src/main/java/org/strongswan/android/logic/SimpleFetcher.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 Tobias Brunner + * Copyright (C) 2017-2018 Tobias Brunner * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -23,36 +23,118 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; +import java.net.SocketTimeoutException; import java.net.URL; +import java.util.ArrayList; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; @Keep public class SimpleFetcher { - public static byte[] fetch(String uri, byte[] data, String contentType) throws IOException + private static ExecutorService mExecutor = Executors.newCachedThreadPool(); + private static Object mLock = new Object(); + private static ArrayList mFutures = new ArrayList<>(); + private static boolean mDisabled; + + public static byte[] fetch(String uri, byte[] data, String contentType) { - URL url = new URL(uri); - HttpURLConnection conn = (HttpURLConnection)url.openConnection(); - conn.setConnectTimeout(10000); - conn.setReadTimeout(10000); + Future future; + + synchronized (mLock) + { + if (mDisabled) + { + return null; + } + future = mExecutor.submit(() -> { + URL url = new URL(uri); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setConnectTimeout(10000); + conn.setReadTimeout(10000); + try + { + if (contentType != null) + { + conn.setRequestProperty("Content-Type", contentType); + } + if (data != null) + { + conn.setDoOutput(true); + conn.setFixedLengthStreamingMode(data.length); + OutputStream out = new BufferedOutputStream(conn.getOutputStream()); + out.write(data); + out.close(); + } + return streamToArray(conn.getInputStream()); + } + catch (SocketTimeoutException e) + { + return null; + } + finally + { + conn.disconnect(); + } + }); + + mFutures.add(future); + } + try { - if (contentType != null) - { - conn.setRequestProperty("Content-Type", contentType); - } - if (data != null) - { - conn.setDoOutput(true); - conn.setFixedLengthStreamingMode(data.length); - OutputStream out = new BufferedOutputStream(conn.getOutputStream()); - out.write(data); - out.close(); - } - return streamToArray(conn.getInputStream()); + /* this enforces a timeout as the ones set on HttpURLConnection might not work reliably */ + return future.get(10000, TimeUnit.MILLISECONDS); + } + catch (InterruptedException|ExecutionException|TimeoutException|CancellationException e) + { + return null; } finally { - conn.disconnect(); + synchronized (mLock) + { + mFutures.remove(future); + } + } + } + + /** + * Enable fetching after it has been disabled. + */ + public static void enable() + { + synchronized (mLock) + { + mDisabled = false; + } + } + + /** + * Disable the fetcher and abort any future requests. + * + * The native thread is not cancelable as it is working on an IKE_SA (cancelling the methods of + * HttpURLConnection is not reliably possible anyway), so to abort while fetching we cancel the + * Future (causing a return from fetch() immediately) and let the executor thread continue its + * thing in the background. + * + * Also prevents future fetches until enabled again (e.g. if we aborted OCSP but would then + * block in the subsequent fetch for a CRL). + */ + public static void disable() + { + synchronized (mLock) + { + mDisabled = true; + for (Future future : mFutures) + { + future.cancel(true); + } } }