Stop sending media in an infinite loop in case of error (part of #587)

Not sure how this commit fix it, but the issue is not observed anymore with it
This commit is contained in:
Benoit Marty 2019-09-25 14:09:26 +02:00
parent f3039601bf
commit f077cc8467
4 changed files with 77 additions and 83 deletions

View File

@ -17,7 +17,6 @@
package im.vector.matrix.android.internal.crypto.attachments package im.vector.matrix.android.internal.crypto.attachments
import android.util.Base64 import android.util.Base64
import arrow.core.Try
import im.vector.matrix.android.internal.crypto.model.rest.EncryptedFileInfo import im.vector.matrix.android.internal.crypto.model.rest.EncryptedFileInfo
import im.vector.matrix.android.internal.crypto.model.rest.EncryptedFileKey import im.vector.matrix.android.internal.crypto.model.rest.EncryptedFileKey
import timber.log.Timber import timber.log.Timber
@ -50,7 +49,7 @@ object MXEncryptedAttachments {
* @param mimetype the mime type * @param mimetype the mime type
* @return the encryption file info * @return the encryption file info
*/ */
fun encryptAttachment(attachmentStream: InputStream, mimetype: String): Try<EncryptionResult> { fun encryptAttachment(attachmentStream: InputStream, mimetype: String): EncryptionResult {
val t0 = System.currentTimeMillis() val t0 = System.currentTimeMillis()
val secureRandom = SecureRandom() val secureRandom = SecureRandom()
@ -70,7 +69,7 @@ object MXEncryptedAttachments {
val outStream = ByteArrayOutputStream() val outStream = ByteArrayOutputStream()
try { outStream.use {
val encryptCipher = Cipher.getInstance(CIPHER_ALGORITHM) val encryptCipher = Cipher.getInstance(CIPHER_ALGORITHM)
val secretKeySpec = SecretKeySpec(key, SECRET_KEY_SPEC_ALGORITHM) val secretKeySpec = SecretKeySpec(key, SECRET_KEY_SPEC_ALGORITHM)
val ivParameterSpec = IvParameterSpec(initVectorBytes) val ivParameterSpec = IvParameterSpec(initVectorBytes)
@ -114,19 +113,7 @@ object MXEncryptedAttachments {
) )
Timber.v("Encrypt in ${System.currentTimeMillis() - t0} ms") Timber.v("Encrypt in ${System.currentTimeMillis() - t0} ms")
return Try.just(result) return result
} catch (oom: OutOfMemoryError) {
Timber.e(oom, "## encryptAttachment failed")
return Try.Failure(oom)
} catch (e: Exception) {
Timber.e(e, "## encryptAttachment failed")
return Try.Failure(e)
} finally {
try {
outStream.close()
} catch (e: Exception) {
Timber.e(e, "## encryptAttachment() : fail to close outStream")
}
} }
} }

View File

@ -30,6 +30,7 @@ import retrofit2.Call
import retrofit2.Callback import retrofit2.Callback
import retrofit2.Response import retrofit2.Response
import timber.log.Timber import timber.log.Timber
import java.io.IOException
import kotlin.coroutines.resume import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException import kotlin.coroutines.resumeWithException
@ -50,6 +51,24 @@ internal suspend fun <T> Call<T>.awaitResponse(): Response<T> {
} }
} }
internal suspend fun okhttp3.Call.awaitResponse(): okhttp3.Response {
return suspendCancellableCoroutine { continuation ->
continuation.invokeOnCancellation {
cancel()
}
enqueue(object : okhttp3.Callback {
override fun onResponse(call: okhttp3.Call, response: okhttp3.Response) {
continuation.resume(response)
}
override fun onFailure(call: okhttp3.Call, e: IOException) {
continuation.resumeWithException(e)
}
})
}
}
/** /**
* Convert a retrofit Response to a Failure, and eventually parse errorBody to convert it to a MatrixError * Convert a retrofit Response to a Failure, and eventually parse errorBody to convert it to a MatrixError
*/ */

View File

@ -16,12 +16,11 @@
package im.vector.matrix.android.internal.session.content package im.vector.matrix.android.internal.session.content
import arrow.core.Try
import arrow.core.Try.Companion.raise
import com.squareup.moshi.Moshi import com.squareup.moshi.Moshi
import im.vector.matrix.android.api.auth.data.SessionParams import im.vector.matrix.android.api.auth.data.SessionParams
import im.vector.matrix.android.internal.di.Authenticated import im.vector.matrix.android.internal.di.Authenticated
import im.vector.matrix.android.internal.network.ProgressRequestBody import im.vector.matrix.android.internal.network.ProgressRequestBody
import im.vector.matrix.android.internal.network.awaitResponse
import im.vector.matrix.android.internal.network.toFailure import im.vector.matrix.android.internal.network.toFailure
import okhttp3.* import okhttp3.*
import java.io.File import java.io.File
@ -38,28 +37,26 @@ internal class FileUploader @Inject constructor(@Authenticated
private val responseAdapter = moshi.adapter(ContentUploadResponse::class.java) private val responseAdapter = moshi.adapter(ContentUploadResponse::class.java)
fun uploadFile(file: File, suspend fun uploadFile(file: File,
filename: String?, filename: String?,
mimeType: String, mimeType: String,
progressListener: ProgressRequestBody.Listener? = null): Try<ContentUploadResponse> { progressListener: ProgressRequestBody.Listener? = null): ContentUploadResponse {
val uploadBody = RequestBody.create(MediaType.parse(mimeType), file) val uploadBody = RequestBody.create(MediaType.parse(mimeType), file)
return upload(uploadBody, filename, progressListener) return upload(uploadBody, filename, progressListener)
} }
fun uploadByteArray(byteArray: ByteArray, suspend fun uploadByteArray(byteArray: ByteArray,
filename: String?, filename: String?,
mimeType: String, mimeType: String,
progressListener: ProgressRequestBody.Listener? = null): Try<ContentUploadResponse> { progressListener: ProgressRequestBody.Listener? = null): ContentUploadResponse {
val uploadBody = RequestBody.create(MediaType.parse(mimeType), byteArray) val uploadBody = RequestBody.create(MediaType.parse(mimeType), byteArray)
return upload(uploadBody, filename, progressListener) return upload(uploadBody, filename, progressListener)
} }
private fun upload(uploadBody: RequestBody, filename: String?, progressListener: ProgressRequestBody.Listener?): Try<ContentUploadResponse> { private suspend fun upload(uploadBody: RequestBody, filename: String?, progressListener: ProgressRequestBody.Listener?): ContentUploadResponse {
val urlBuilder = HttpUrl.parse(uploadUrl)?.newBuilder() ?: return raise(RuntimeException()) val urlBuilder = HttpUrl.parse(uploadUrl)?.newBuilder() ?: throw RuntimeException()
val httpUrl = urlBuilder val httpUrl = urlBuilder
.addQueryParameter("filename", filename) .addQueryParameter("filename", filename)
@ -72,19 +69,15 @@ internal class FileUploader @Inject constructor(@Authenticated
.post(requestBody) .post(requestBody)
.build() .build()
return Try { return okHttpClient.newCall(request).awaitResponse().use { response ->
okHttpClient.newCall(request).execute().use { response -> if (!response.isSuccessful) {
if (!response.isSuccessful) { throw response.toFailure()
throw response.toFailure() } else {
} else { response.body()?.source()?.let {
response.body()?.source()?.let { responseAdapter.fromJson(it)
responseAdapter.fromJson(it)
}
?: throw IOException()
} }
?: throw IOException()
} }
} }
} }
} }

View File

@ -93,32 +93,28 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) :
} }
} }
val contentUploadResponse = if (params.isRoomEncrypted) { try {
Timber.v("Encrypt thumbnail") val contentUploadResponse = if (params.isRoomEncrypted) {
contentUploadStateTracker.setEncryptingThumbnail(eventId) Timber.v("Encrypt thumbnail")
MXEncryptedAttachments.encryptAttachment(ByteArrayInputStream(thumbnailData.bytes), thumbnailData.mimeType) contentUploadStateTracker.setEncryptingThumbnail(eventId)
.flatMap { encryptionResult -> val encryptionResult = MXEncryptedAttachments.encryptAttachment(ByteArrayInputStream(thumbnailData.bytes), thumbnailData.mimeType)
uploadedThumbnailEncryptedFileInfo = encryptionResult.encryptedFileInfo uploadedThumbnailEncryptedFileInfo = encryptionResult.encryptedFileInfo
fileUploader.uploadByteArray(encryptionResult.encryptedByteArray,
"thumb_${attachment.name}",
"application/octet-stream",
thumbnailProgressListener)
} else {
fileUploader.uploadByteArray(thumbnailData.bytes,
"thumb_${attachment.name}",
thumbnailData.mimeType,
thumbnailProgressListener)
}
fileUploader uploadedThumbnailUrl = contentUploadResponse.contentUri
.uploadByteArray(encryptionResult.encryptedByteArray, } catch (t: Throwable) {
"thumb_${attachment.name}", Timber.e(t)
"application/octet-stream", return handleFailure(params, t)
thumbnailProgressListener)
}
} else {
fileUploader
.uploadByteArray(thumbnailData.bytes,
"thumb_${attachment.name}",
thumbnailData.mimeType,
thumbnailProgressListener)
} }
contentUploadResponse
.fold(
{ Timber.e(it) },
{ uploadedThumbnailUrl = it.contentUri }
)
} }
val progressListener = object : ProgressRequestBody.Listener { val progressListener = object : ProgressRequestBody.Listener {
@ -133,27 +129,26 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) :
var uploadedFileEncryptedFileInfo: EncryptedFileInfo? = null var uploadedFileEncryptedFileInfo: EncryptedFileInfo? = null
val contentUploadResponse = if (params.isRoomEncrypted) { return try {
Timber.v("Encrypt file") val contentUploadResponse = if (params.isRoomEncrypted) {
contentUploadStateTracker.setEncrypting(eventId) Timber.v("Encrypt file")
contentUploadStateTracker.setEncrypting(eventId)
MXEncryptedAttachments.encryptAttachment(FileInputStream(attachmentFile), attachment.mimeType) val encryptionResult = MXEncryptedAttachments.encryptAttachment(FileInputStream(attachmentFile), attachment.mimeType)
.flatMap { encryptionResult -> uploadedFileEncryptedFileInfo = encryptionResult.encryptedFileInfo
uploadedFileEncryptedFileInfo = encryptionResult.encryptedFileInfo
fileUploader fileUploader
.uploadByteArray(encryptionResult.encryptedByteArray, attachment.name, "application/octet-stream", progressListener) .uploadByteArray(encryptionResult.encryptedByteArray, attachment.name, "application/octet-stream", progressListener)
} } else {
} else { fileUploader
fileUploader .uploadFile(attachmentFile, attachment.name, attachment.mimeType, progressListener)
.uploadFile(attachmentFile, attachment.name, attachment.mimeType, progressListener) }
handleSuccess(params, contentUploadResponse.contentUri, uploadedFileEncryptedFileInfo, uploadedThumbnailUrl, uploadedThumbnailEncryptedFileInfo)
} catch (t: Throwable) {
Timber.e(t)
handleFailure(params, t)
} }
return contentUploadResponse
.fold(
{ handleFailure(params, it) },
{ handleSuccess(params, it.contentUri, uploadedFileEncryptedFileInfo, uploadedThumbnailUrl, uploadedThumbnailEncryptedFileInfo) }
)
} }
private fun handleFailure(params: Params, failure: Throwable): Result { private fun handleFailure(params: Params, failure: Throwable): Result {