From 9bba7b29a416b2617af033a778ef13a512024502 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 25 Jan 2024 23:37:53 +0100 Subject: refactor ProviderApiConnector, allow to inject dummy implementations for unit tests --- .../providersetup/ProviderApiConnector.java | 134 +++++++++++++-------- .../providersetup/ProviderApiManagerBase.java | 13 +- 2 files changed, 94 insertions(+), 53 deletions(-) diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiConnector.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiConnector.java index 35ad9cd2..cc875c79 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiConnector.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiConnector.java @@ -18,6 +18,8 @@ package se.leap.bitmaskclient.providersetup; import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; + import android.util.Pair; import java.io.IOException; @@ -26,6 +28,9 @@ import java.util.List; import java.util.Locale; import java.util.Scanner; +import javax.net.ssl.SSLHandshakeException; + +import de.blinkt.openvpn.core.NativeUtils; import de.blinkt.openvpn.core.VpnStatus; import okhttp3.MediaType; import okhttp3.OkHttpClient; @@ -39,72 +44,103 @@ import okhttp3.Response; public class ProviderApiConnector { - private static final MediaType JSON - = MediaType.parse("application/json; charset=utf-8"); + public interface ProviderApiConnectorInterface { + boolean delete(OkHttpClient okHttpClient, String deleteUrl) throws RuntimeException, IOException; + boolean canConnect(@NonNull OkHttpClient okHttpClient, String url) throws RuntimeException, IOException; + String requestStringFromServer(@NonNull String url, @NonNull String requestMethod, String jsonString, @NonNull List> headerArgs, @NonNull OkHttpClient okHttpClient) throws RuntimeException, IOException; + + } + public static class DefaultProviderApiCpnnector implements ProviderApiConnectorInterface { + + @Override + public boolean delete(OkHttpClient okHttpClient, String deleteUrl) { + try { + Request.Builder requestBuilder = new Request.Builder() + .url(deleteUrl) + .delete(); + Request request = requestBuilder.build(); + + Response response = okHttpClient.newCall(request).execute(); + //response code 401: already logged out + if (response.isSuccessful() || response.code() == 401) { + return true; + } + } catch (IOException | RuntimeException e) { + return false; + } + + return false; + } - public static boolean delete(OkHttpClient okHttpClient, String deleteUrl) { - try { + @Override + public boolean canConnect(@NonNull OkHttpClient okHttpClient, String url) throws RuntimeException, IOException { Request.Builder requestBuilder = new Request.Builder() - .url(deleteUrl) - .delete(); + .url(url) + .method("GET", null); Request request = requestBuilder.build(); Response response = okHttpClient.newCall(request).execute(); - //response code 401: already logged out - if (response.isSuccessful() || response.code() == 401) { - return true; + if (!response.isSuccessful()) { + VpnStatus.logWarning("[API] API request failed canConnect(): " + url); } - } catch (IOException | RuntimeException e) { - return false; + return response.isSuccessful(); } - return false; - } + @Override + public String requestStringFromServer(@NonNull String url, @NonNull String requestMethod, String jsonString, @NonNull List> headerArgs, @NonNull OkHttpClient okHttpClient) throws RuntimeException, IOException { + RequestBody jsonBody = jsonString != null ? RequestBody.create(JSON, jsonString) : null; + Request.Builder requestBuilder = new Request.Builder() + .url(url) + .method(requestMethod, jsonBody); + for (Pair keyValPair : headerArgs) { + requestBuilder.addHeader(keyValPair.first, keyValPair.second); + } - public static boolean canConnect(@NonNull OkHttpClient okHttpClient, String url) throws RuntimeException, IOException { - Request.Builder requestBuilder = new Request.Builder() - .url(url) - .method("GET", null); - Request request = requestBuilder.build(); - - Response response = okHttpClient.newCall(request).execute(); - if (!response.isSuccessful()) { - VpnStatus.logWarning("[API] API request failed canConnect(): " + url); + //TODO: move to getHeaderArgs()? + String locale = Locale.getDefault().getLanguage() + Locale.getDefault().getCountry(); + requestBuilder.addHeader("Accept-Language", locale); + Request request = requestBuilder.build(); + + Response response = okHttpClient.newCall(request).execute(); + if (!response.isSuccessful()) { + VpnStatus.logWarning("[API] API request failed: " + url); + } + + if (response.body() != null) { + InputStream inputStream = response.body().byteStream(); + Scanner scanner = new Scanner(inputStream).useDelimiter("\\A"); + if (scanner.hasNext()) { + String result = scanner.next(); + response.body().close(); + return result; + } + } + return null; } - return response.isSuccessful(); + } + private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); + private static ProviderApiConnectorInterface instance = new DefaultProviderApiCpnnector(); + @VisibleForTesting + public ProviderApiConnector(ProviderApiConnectorInterface connectorInterface) { + if (!NativeUtils.isUnitTest()) { + throw new IllegalStateException("ProviderApiConnector injected with ProviderApiConnectorInterface outside of an unit test"); + } + instance = connectorInterface; } - public static String requestStringFromServer(@NonNull String url, @NonNull String request_method, String jsonString, @NonNull List> headerArgs, @NonNull OkHttpClient okHttpClient) throws RuntimeException, IOException { - RequestBody jsonBody = jsonString != null ? RequestBody.create(JSON, jsonString) : null; - Request.Builder requestBuilder = new Request.Builder() - .url(url) - .method(request_method, jsonBody); - for (Pair keyValPair : headerArgs) { - requestBuilder.addHeader(keyValPair.first, keyValPair.second); - } + public static boolean delete(OkHttpClient okHttpClient, String deleteUrl) throws RuntimeException, IOException { + return instance.delete(okHttpClient, deleteUrl); + } - //TODO: move to getHeaderArgs()? - String locale = Locale.getDefault().getLanguage() + Locale.getDefault().getCountry(); - requestBuilder.addHeader("Accept-Language", locale); - Request request = requestBuilder.build(); + public static boolean canConnect(@NonNull OkHttpClient okHttpClient, String url) throws RuntimeException, IOException { + return instance.canConnect(okHttpClient, url); - Response response = okHttpClient.newCall(request).execute(); - if (!response.isSuccessful()) { - VpnStatus.logWarning("[API] API request failed: " + url); - } + } - if (response.body() != null) { - InputStream inputStream = response.body().byteStream(); - Scanner scanner = new Scanner(inputStream).useDelimiter("\\A"); - if (scanner.hasNext()) { - String result = scanner.next(); - response.body().close(); - return result; - } - } - return null; + public static String requestStringFromServer(@NonNull String url, @NonNull String requestMethod, String jsonString, @NonNull List> headerArgs, @NonNull OkHttpClient okHttpClient) throws RuntimeException, IOException { + return instance.requestStringFromServer(url, requestMethod, jsonString, headerArgs, okHttpClient); } } diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java index 05c5448a..1f737b0c 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java @@ -45,6 +45,7 @@ import static se.leap.bitmaskclient.base.models.Provider.CA_CERT; import static se.leap.bitmaskclient.base.models.Provider.GEOIP_URL; import static se.leap.bitmaskclient.base.models.Provider.PROVIDER_API_IP; import static se.leap.bitmaskclient.base.models.Provider.PROVIDER_IP; +import static se.leap.bitmaskclient.base.utils.ConfigHelper.getTorTimeout; import static se.leap.bitmaskclient.base.utils.RSAHelper.parseRsaKeyFromString; import static se.leap.bitmaskclient.base.utils.ConfigHelper.getDomainFromMainURL; import static se.leap.bitmaskclient.base.utils.CertificateHelper.getFingerprintFromCertificate; @@ -380,7 +381,7 @@ public abstract class ProviderApiManagerBase { if (TorStatusObservable.getStatus() == ON) { return; } - TorStatusObservable.waitUntil(this::isTorOnOrCancelled, 180); + TorStatusObservable.waitUntil(this::isTorOnOrCancelled, getTorTimeout()); } private boolean isTorOnOrCancelled() { @@ -1138,9 +1139,13 @@ public abstract class ProviderApiManagerBase { String deleteUrl = provider.getApiUrlWithVersion() + "/logout"; - if (ProviderApiConnector.delete(okHttpClient, deleteUrl)) { - LeapSRPSession.setToken(""); - return true; + try { + if (ProviderApiConnector.delete(okHttpClient, deleteUrl)) { + LeapSRPSession.setToken(""); + return true; + } + } catch (IOException e) { + // eat me } return false; } -- cgit v1.2.3