Consolidate S3 requests into one interface.

fork-5.53.8
Cody Henthorne 2022-05-11 19:21:55 -04:00 zatwierdzone przez Alex Hart
rodzic bb963f9210
commit ecc358ef40
10 zmienionych plików z 109 dodań i 177 usunięć

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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"
}

Wyświetl plik

@ -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<FontDownloadKey, ListenableFutureTask<Typeface>>())
@ -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

Wyświetl plik

@ -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<Badge, InputStream> {
}
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<Badge, InputStream> {

Wyświetl plik

@ -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())
}
}
}

Wyświetl plik

@ -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());

Wyświetl plik

@ -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<EmojiSearchData> searchIndex = downloadSearchIndex(client, manifest.getVersion(), remoteLanguage);
List<EmojiSearchData> 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<EmojiSearchData> 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<EmojiSearchData> 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<String> languages) {

Wyświetl plik

@ -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)

Wyświetl plik

@ -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 {

Wyświetl plik

@ -34,6 +34,7 @@ class SpinnerApplicationContext : ApplicationContext() {
ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.penaltyLog()
.build()
)