From ecc358ef40935a53cf1263ff18091fd5016ab530 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Wed, 11 May 2022 19:21:55 -0400 Subject: [PATCH] Consolidate S3 requests into one interface. --- .../dependencies/ApplicationDependencies.java | 40 ++++++++++++ .../securesms/emoji/EmojiRemote.kt | 61 +++---------------- .../org/thoughtcrime/securesms/fonts/Fonts.kt | 10 +-- .../securesms/glide/BadgeLoader.java | 30 +-------- .../securesms/glide/GiftBadgeModel.kt | 30 +-------- .../securesms/jobs/AttachmentDownloadJob.java | 11 +--- .../jobs/EmojiSearchIndexDownloadJob.java | 43 +++---------- .../jobs/RetrieveRemoteAnnouncementsJob.kt | 4 +- .../java/org/thoughtcrime/securesms/s3/S3.kt | 56 +++++++++++------ .../securesms/SpinnerApplicationContext.kt | 1 + 10 files changed, 109 insertions(+), 177 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencies.java b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencies.java index 2fe13c63e2..ac936a493b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencies.java +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencies.java @@ -28,6 +28,7 @@ import org.thoughtcrime.securesms.net.StandardUserAgentInterceptor; import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.payments.Payments; import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess; +import org.thoughtcrime.securesms.push.SignalServiceTrustStore; import org.thoughtcrime.securesms.recipients.LiveRecipientCache; import org.thoughtcrime.securesms.revealable.ViewOnceMessageManager; import org.thoughtcrime.securesms.service.ExpiringMessageManager; @@ -50,8 +51,20 @@ import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.SignalWebSocket; import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations; +import org.whispersystems.signalservice.api.push.TrustStore; import org.whispersystems.signalservice.api.services.DonationsService; +import org.whispersystems.signalservice.api.util.Tls12SocketFactory; +import org.whispersystems.signalservice.internal.util.BlacklistingTrustManager; +import org.whispersystems.signalservice.internal.util.Util; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +import okhttp3.ConnectionSpec; import okhttp3.OkHttpClient; /** @@ -97,6 +110,7 @@ public class ApplicationDependencies { private static volatile SignalCallManager signalCallManager; private static volatile ShakeToReport shakeToReport; private static volatile OkHttpClient okHttpClient; + private static volatile OkHttpClient signalOkHttpClient; private static volatile PendingRetryReceiptManager pendingRetryReceiptManager; private static volatile PendingRetryReceiptCache pendingRetryReceiptCache; private static volatile SignalWebSocket signalWebSocket; @@ -509,6 +523,32 @@ public class ApplicationDependencies { return okHttpClient; } + public static @NonNull OkHttpClient getSignalOkHttpClient() { + if (signalOkHttpClient == null) { + synchronized (LOCK) { + if (signalOkHttpClient == null) { + try { + OkHttpClient baseClient = ApplicationDependencies.getOkHttpClient(); + SSLContext sslContext = SSLContext.getInstance("TLS"); + TrustStore trustStore = new SignalServiceTrustStore(ApplicationDependencies.getApplication()); + TrustManager[] trustManagers = BlacklistingTrustManager.createFor(trustStore); + + sslContext.init(null, trustManagers, null); + + signalOkHttpClient = baseClient.newBuilder() + .sslSocketFactory(new Tls12SocketFactory(sslContext.getSocketFactory()), (X509TrustManager) trustManagers[0]) + .connectionSpecs(Util.immutableList(ConnectionSpec.RESTRICTED_TLS)) + .build(); + } catch (NoSuchAlgorithmException | KeyManagementException e) { + throw new AssertionError(e); + } + } + } + } + + return signalOkHttpClient; + } + public static @NonNull AppForegroundObserver getAppForegroundObserver() { return appForegroundObserver; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/emoji/EmojiRemote.kt b/app/src/main/java/org/thoughtcrime/securesms/emoji/EmojiRemote.kt index e3b75d3ade..9e25d30912 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/emoji/EmojiRemote.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/emoji/EmojiRemote.kt @@ -1,42 +1,22 @@ package org.thoughtcrime.securesms.emoji -import okhttp3.Request import okhttp3.Response -import org.signal.core.util.logging.Log -import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.s3.S3 import java.io.IOException -private const val VERSION_URL = "https://updates.signal.org/dynamic/android/emoji/version_v3.txt" -private const val BASE_STATIC_BUCKET_URL = "https://updates.signal.org/static/android/emoji" +private const val BASE_STATIC_BUCKET_URI = "${S3.STATIC_PATH}/android/emoji" /** * Responsible for communicating with S3 to download Emoji related objects. */ object EmojiRemote { - private const val TAG = "EmojiRemote" - - private val okHttpClient = ApplicationDependencies.getOkHttpClient() + private const val VERSION_URI = "${S3.DYNAMIC_PATH}/android/emoji/version_v3.txt" @JvmStatic @Throws(IOException::class) fun getVersion(): Int { - val request = Request.Builder() - .get() - .url(VERSION_URL) - .build() - - try { - okHttpClient.newCall(request).execute().use { response -> - if (!response.isSuccessful) { - throw IOException() - } - - return response.body()?.bytes()?.let { String(it).trim().toIntOrNull() } ?: throw IOException() - } - } catch (e: IOException) { - throw e - } + return S3.getLong(VERSION_URI).toInt() } /** @@ -44,23 +24,7 @@ object EmojiRemote { */ @JvmStatic fun getMd5(emojiRequest: EmojiRequest): ByteArray? { - val request = Request.Builder() - .head() - .url(emojiRequest.url) - .build() - - try { - okHttpClient.newCall(request).execute().use { response -> - if (!response.isSuccessful) { - throw IOException() - } - - return response.header("ETag")?.toByteArray() - } - } catch (e: IOException) { - Log.w(TAG, "Could not retrieve md5", e) - return null - } + return S3.getObjectMD5(emojiRequest.uri) } /** @@ -68,21 +32,16 @@ object EmojiRemote { */ @JvmStatic fun getObject(emojiRequest: EmojiRequest): Response { - val request = Request.Builder() - .get() - .url(emojiRequest.url) - .build() - - return okHttpClient.newCall(request).execute() + return S3.getObject(emojiRequest.uri) } } interface EmojiRequest { - val url: String + val uri: String } class EmojiJsonRequest(version: Int) : EmojiRequest { - override val url: String = "$BASE_STATIC_BUCKET_URL/$version/emoji_data.json" + override val uri: String = "$BASE_STATIC_BUCKET_URI/$version/emoji_data.json" } class EmojiImageRequest( @@ -91,7 +50,7 @@ class EmojiImageRequest( name: String, format: String ) : EmojiRequest { - override val url: String = "$BASE_STATIC_BUCKET_URL/$version/$density/$name.$format" + override val uri: String = "$BASE_STATIC_BUCKET_URI/$version/$density/$name.$format" } class EmojiFileRequest( @@ -99,5 +58,5 @@ class EmojiFileRequest( density: String, name: String, ) : EmojiRequest { - override val url: String = "$BASE_STATIC_BUCKET_URL/$version/$density/$name" + override val uri: String = "$BASE_STATIC_BUCKET_URI/$version/$density/$name" } diff --git a/app/src/main/java/org/thoughtcrime/securesms/fonts/Fonts.kt b/app/src/main/java/org/thoughtcrime/securesms/fonts/Fonts.kt index 1800ea799a..eff04145e9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/fonts/Fonts.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/fonts/Fonts.kt @@ -33,8 +33,8 @@ object Fonts { private val TAG = Log.tag(Fonts::class.java) - private const val VERSION_URL = "https://updates.signal.org/dynamic/story-fonts/version.txt" - private const val BASE_STATIC_BUCKET_URL = "https://updates.signal.org/static/story-fonts" + private const val VERSION_URI = "${S3.DYNAMIC_PATH}/story-fonts/version.txt" + private const val BASE_STATIC_BUCKET_URI = "${S3.STATIC_PATH}/story-fonts" private const val MANIFEST = "manifest.json" private val taskCache = Collections.synchronizedMap(mutableMapOf>()) @@ -201,7 +201,7 @@ object Fonts { */ @WorkerThread fun downloadLatestVersionLong(): Long { - return S3.getLong(VERSION_URL) + return S3.getLong(VERSION_URI) } /** @@ -211,7 +211,7 @@ object Fonts { fun downloadAndVerifyLatestManifest(context: Context, version: FontVersion, manifestPath: String): Boolean { return S3.verifyAndWriteToDisk( context, - "$BASE_STATIC_BUCKET_URL/${version.id}/$MANIFEST", + "$BASE_STATIC_BUCKET_URI/${version.id}/$MANIFEST", File(getDirectory(context), manifestPath) ) } @@ -227,7 +227,7 @@ object Fonts { val script: FontManifest.FontScript = resolveFontScriptFromScriptName(supportedScript, fontManifest) ?: return null val path = getScriptPath(font, script) ?: return null - val networkPath = "$BASE_STATIC_BUCKET_URL/${fontVersion.id}/$path" + val networkPath = "$BASE_STATIC_BUCKET_URI/${fontVersion.id}/$path" val localUUID = UUID.randomUUID().toString() val localPath = "${fontVersion.path}/" + localUUID diff --git a/app/src/main/java/org/thoughtcrime/securesms/glide/BadgeLoader.java b/app/src/main/java/org/thoughtcrime/securesms/glide/BadgeLoader.java index 7e1fced17b..5b8232bbf5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/glide/BadgeLoader.java +++ b/app/src/main/java/org/thoughtcrime/securesms/glide/BadgeLoader.java @@ -11,21 +11,9 @@ import com.bumptech.glide.load.model.MultiModelLoaderFactory; import org.thoughtcrime.securesms.badges.models.Badge; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; -import org.thoughtcrime.securesms.push.SignalServiceTrustStore; -import org.whispersystems.signalservice.api.push.TrustStore; -import org.whispersystems.signalservice.api.util.Tls12SocketFactory; -import org.whispersystems.signalservice.internal.util.BlacklistingTrustManager; -import org.whispersystems.signalservice.internal.util.Util; import java.io.InputStream; -import java.security.KeyManagementException; -import java.security.NoSuchAlgorithmException; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; - -import okhttp3.ConnectionSpec; import okhttp3.OkHttpClient; /** @@ -50,23 +38,7 @@ public class BadgeLoader implements ModelLoader { } public static Factory createFactory() { - try { - OkHttpClient baseClient = ApplicationDependencies.getOkHttpClient(); - SSLContext sslContext = SSLContext.getInstance("TLS"); - TrustStore trustStore = new SignalServiceTrustStore(ApplicationDependencies.getApplication()); - TrustManager[] trustManagers = BlacklistingTrustManager.createFor(trustStore); - - sslContext.init(null, trustManagers, null); - - OkHttpClient client = baseClient.newBuilder() - .sslSocketFactory(new Tls12SocketFactory(sslContext.getSocketFactory()), (X509TrustManager) trustManagers[0]) - .connectionSpecs(Util.immutableList(ConnectionSpec.RESTRICTED_TLS)) - .build(); - - return new Factory(client); - } catch (NoSuchAlgorithmException | KeyManagementException e) { - throw new AssertionError(e); - } + return new Factory(ApplicationDependencies.getSignalOkHttpClient()); } public static class Factory implements ModelLoaderFactory { diff --git a/app/src/main/java/org/thoughtcrime/securesms/glide/GiftBadgeModel.kt b/app/src/main/java/org/thoughtcrime/securesms/glide/GiftBadgeModel.kt index e0d70cd033..1c2ddc41b0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/glide/GiftBadgeModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/glide/GiftBadgeModel.kt @@ -9,25 +9,15 @@ import com.bumptech.glide.load.model.GlideUrl import com.bumptech.glide.load.model.ModelLoader import com.bumptech.glide.load.model.ModelLoaderFactory import com.bumptech.glide.load.model.MultiModelLoaderFactory -import okhttp3.ConnectionSpec import okhttp3.OkHttpClient import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialPresentation import org.thoughtcrime.securesms.badges.Badges import org.thoughtcrime.securesms.database.model.databaseprotos.GiftBadge import org.thoughtcrime.securesms.dependencies.ApplicationDependencies -import org.thoughtcrime.securesms.push.SignalServiceTrustStore -import org.whispersystems.signalservice.api.push.TrustStore -import org.whispersystems.signalservice.api.util.Tls12SocketFactory -import org.whispersystems.signalservice.internal.util.BlacklistingTrustManager -import org.whispersystems.signalservice.internal.util.Util import java.io.InputStream import java.lang.Exception -import java.security.KeyManagementException import java.security.MessageDigest -import java.security.NoSuchAlgorithmException import java.util.Locale -import javax.net.ssl.SSLContext -import javax.net.ssl.X509TrustManager /** * Glide Model allowing the direct loading of a GiftBadge. @@ -102,25 +92,7 @@ data class GiftBadgeModel(val giftBadge: GiftBadge) : Key { companion object { @JvmStatic fun createFactory(): Factory { - return try { - val baseClient = ApplicationDependencies.getOkHttpClient() - val sslContext = SSLContext.getInstance("TLS") - val trustStore: TrustStore = SignalServiceTrustStore(ApplicationDependencies.getApplication()) - val trustManagers = BlacklistingTrustManager.createFor(trustStore) - - sslContext.init(null, trustManagers, null) - - val client = baseClient.newBuilder() - .sslSocketFactory(Tls12SocketFactory(sslContext.socketFactory), trustManagers[0] as X509TrustManager) - .connectionSpecs(Util.immutableList(ConnectionSpec.RESTRICTED_TLS)) - .build() - - Factory(client) - } catch (e: NoSuchAlgorithmException) { - throw AssertionError(e) - } catch (e: KeyManagementException) { - throw AssertionError(e) - } + return Factory(ApplicationDependencies.getSignalOkHttpClient()) } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java index f9c04a76b7..3ba675445d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java @@ -6,6 +6,7 @@ import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import org.greenrobot.eventbus.EventBus; +import org.signal.core.util.Hex; import org.signal.core.util.logging.Log; import org.signal.libsignal.protocol.InvalidMessageException; import org.thoughtcrime.securesms.attachments.Attachment; @@ -23,10 +24,10 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; import org.thoughtcrime.securesms.mms.MmsException; import org.thoughtcrime.securesms.notifications.v2.NotificationThread; import org.thoughtcrime.securesms.releasechannel.ReleaseChannel; +import org.thoughtcrime.securesms.s3.S3; import org.thoughtcrime.securesms.transport.RetryLaterException; import org.thoughtcrime.securesms.util.AttachmentUtil; import org.thoughtcrime.securesms.util.Base64; -import org.signal.core.util.Hex; import org.thoughtcrime.securesms.util.Util; import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer; @@ -43,7 +44,6 @@ import java.util.Objects; import java.util.Optional; import java.util.concurrent.TimeUnit; -import okhttp3.Request; import okhttp3.Response; import okhttp3.ResponseBody; import okio.Okio; @@ -242,12 +242,7 @@ public final class AttachmentDownloadJob extends BaseJob { final Attachment attachment) throws IOException { - Request request = new Request.Builder() - .get() - .url(Objects.requireNonNull(attachment.getFileName())) - .build(); - - try (Response response = ApplicationDependencies.getOkHttpClient().newCall(request).execute()) { + try (Response response = S3.getObject(Objects.requireNonNull(attachment.getFileName()))) { ResponseBody body = response.body(); if (body != null) { SignalDatabase.attachments().insertAttachmentsForPlaceholder(messageId, attachmentId, Okio.buffer(body.source()).inputStream()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/EmojiSearchIndexDownloadJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/EmojiSearchIndexDownloadJob.java index 99776a74d9..0bd49ccca4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/EmojiSearchIndexDownloadJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/EmojiSearchIndexDownloadJob.java @@ -13,6 +13,7 @@ import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; import org.thoughtcrime.securesms.keyvalue.EmojiValues; import org.thoughtcrime.securesms.keyvalue.SignalStore; +import org.thoughtcrime.securesms.s3.S3; import org.thoughtcrime.securesms.util.dynamiclanguage.DynamicLanguageContextWrapper; import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException; import org.whispersystems.signalservice.internal.util.JsonUtil; @@ -24,11 +25,6 @@ import java.util.List; import java.util.Locale; import java.util.concurrent.TimeUnit; -import okhttp3.Call; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; - /** * Downloads a new emoji search index based on our current version and language, if needed. */ @@ -89,9 +85,7 @@ public final class EmojiSearchIndexDownloadJob extends BaseJob { @Override protected void onRun() throws Exception { - OkHttpClient client = ApplicationDependencies.getOkHttpClient(); - - Manifest manifest = downloadManifest(client); + Manifest manifest = downloadManifest(); Locale locale = DynamicLanguageContextWrapper.getUsersSelectedLocale(context); String remoteLanguage = findMatchingLanguage(locale, manifest.getLanguages()); @@ -106,7 +100,7 @@ public final class EmojiSearchIndexDownloadJob extends BaseJob { Log.i(TAG, "Need to get a new search index. Downloading version: " + manifest.getVersion() + ", language: " + remoteLanguage); - List searchIndex = downloadSearchIndex(client, manifest.getVersion(), remoteLanguage); + List searchIndex = downloadSearchIndex(manifest.getVersion(), remoteLanguage); SignalDatabase.emojiSearch().setSearchIndex(searchIndex); SignalStore.emojiValues().onSearchIndexUpdated(manifest.getVersion(), remoteLanguage); @@ -125,33 +119,14 @@ public final class EmojiSearchIndexDownloadJob extends BaseJob { } - private static @NonNull Manifest downloadManifest(@NonNull OkHttpClient client) throws IOException { - String url = "https://updates.signal.org/dynamic/android/emoji/search/manifest.json"; - String body = downloadFile(client, url); - - return JsonUtil.fromJson(body, Manifest.class); + private static @NonNull Manifest downloadManifest() throws IOException { + String manifest = S3.getString(S3.DYNAMIC_PATH + "/android/emoji/search/manifest.json"); + return JsonUtil.fromJson(manifest, Manifest.class); } - private static @NonNull List downloadSearchIndex(@NonNull OkHttpClient client, int version, @NonNull String language) throws IOException { - String url = "https://updates.signal.org/static/android/emoji/search/" + version + "/" + language + ".json"; - String body = downloadFile(client, url); - - return Arrays.asList(JsonUtil.fromJson(body, EmojiSearchData[].class)); - } - - private static @NonNull String downloadFile(@NonNull OkHttpClient client, @NonNull String url) throws IOException { - Call call = client.newCall(new Request.Builder().url(url).build()); - Response response = call.execute(); - - if (response.code() != 200) { - throw new NonSuccessfulResponseCodeException(response.code()); - } - - if (response.body() == null) { - throw new NonSuccessfulResponseCodeException(404, "Missing body!"); - } - - return response.body().string(); + private static @NonNull List downloadSearchIndex(int version, @NonNull String language) throws IOException { + String data = S3.getString(S3.STATIC_PATH + "/android/emoji/search/" + version + "/" + language + ".json"); + return Arrays.asList(JsonUtil.fromJson(data, EmojiSearchData[].class)); } private static @NonNull String findMatchingLanguage(@NonNull Locale locale, List languages) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveRemoteAnnouncementsJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveRemoteAnnouncementsJob.kt index 85c6e20cd4..822acf6f65 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveRemoteAnnouncementsJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveRemoteAnnouncementsJob.kt @@ -40,8 +40,8 @@ class RetrieveRemoteAnnouncementsJob private constructor(private val force: Bool companion object { const val KEY = "RetrieveReleaseChannelJob" - private const val MANIFEST = "https://updates.signal.org/dynamic/release-notes/release-notes.json" - private const val BASE_RELEASE_NOTE = "https://updates.signal.org/static/release-notes" + private const val MANIFEST = "${S3.DYNAMIC_PATH}/release-notes/release-notes.json" + private const val BASE_RELEASE_NOTE = "${S3.STATIC_PATH}/release-notes" private const val KEY_FORCE = "force" private val TAG = Log.tag(RetrieveRemoteAnnouncementsJob::class.java) diff --git a/app/src/main/java/org/thoughtcrime/securesms/s3/S3.kt b/app/src/main/java/org/thoughtcrime/securesms/s3/S3.kt index 47de5b136f..50b8baf3d1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/s3/S3.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/s3/S3.kt @@ -11,6 +11,7 @@ import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.dependencies.ApplicationDependencies import org.thoughtcrime.securesms.util.EncryptedStreamUtils import org.thoughtcrime.securesms.util.JsonUtils +import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException import org.whispersystems.signalservice.internal.ServiceResponse import org.whispersystems.signalservice.internal.websocket.DefaultErrorMapper import org.whispersystems.signalservice.internal.websocket.DefaultResponseMapper @@ -30,7 +31,31 @@ import java.util.regex.Pattern object S3 { private val TAG = Log.tag(S3::class.java) - private val okHttpClient = ApplicationDependencies.getOkHttpClient() + private val okHttpClient = ApplicationDependencies.getSignalOkHttpClient() + + private const val S3_BASE = "https://updates2.signal.org" + const val DYNAMIC_PATH = "/dynamic" + const val STATIC_PATH = "/static" + + /** + * Fetches the content at the given endpoint and attempts to return it as a string. + * + * @param endpoint The endpoint at which to get the long + * @return the string value of the body + * @throws IOException if the call fails or the response body cannot be parsed + */ + @WorkerThread + @JvmStatic + @Throws(IOException::class) + fun getString(endpoint: String): String { + getObject(endpoint).use { response -> + if (!response.isSuccessful) { + throw NonSuccessfulResponseCodeException(response.code()) + } + + return response.body()?.string()?.trim() ?: throw IOException() + } + } /** * Fetches the content at the given endpoint and attempts to convert it into a long. @@ -40,23 +65,14 @@ object S3 { * @throws IOException if the call fails or the response body cannot be parsed as a long */ @WorkerThread + @Throws(IOException::class) fun getLong(endpoint: String): Long { - val request = Request.Builder() - .get() - .url(endpoint) - .build() - - try { - okHttpClient.newCall(request).execute().use { response -> - if (!response.isSuccessful) { - throw IOException() - } - - return response.body()?.bytes()?.let { String(it).trim().toLongOrNull() } ?: throw IOException() - } - } catch (e: IOException) { - Log.w(TAG, "Failed to retreive long value from S3") - throw e + val result = getString(endpoint).toLongOrNull() + return if (result == null) { + Log.w(TAG, "Failed to retrieve long value from S3") + throw IOException("Unable to parse") + } else { + result } } @@ -64,10 +80,12 @@ object S3 { * Retrieves an S3 object from the given endpoint. */ @WorkerThread + @JvmStatic + @Throws(IOException::class) fun getObject(endpoint: String): Response { val request = Request.Builder() .get() - .url(endpoint) + .url("$S3_BASE$endpoint") .build() return okHttpClient.newCall(request).execute() @@ -178,7 +196,7 @@ object S3 { fun getObjectMD5(endpoint: String): ByteArray? { val request = Request.Builder() .head() - .url(endpoint) + .url("$S3_BASE$endpoint") .build() try { diff --git a/app/src/spinner/java/org/thoughtcrime/securesms/SpinnerApplicationContext.kt b/app/src/spinner/java/org/thoughtcrime/securesms/SpinnerApplicationContext.kt index 561cd46339..3298e0363c 100644 --- a/app/src/spinner/java/org/thoughtcrime/securesms/SpinnerApplicationContext.kt +++ b/app/src/spinner/java/org/thoughtcrime/securesms/SpinnerApplicationContext.kt @@ -34,6 +34,7 @@ class SpinnerApplicationContext : ApplicationContext() { ThreadPolicy.Builder() .detectDiskReads() .detectDiskWrites() + .detectNetwork() .penaltyLog() .build() )