From 29a40080dbc86ebea6d51f5adf90c34308023553 Mon Sep 17 00:00:00 2001 From: Philipp Heckel Date: Sat, 12 Feb 2022 13:51:41 -0500 Subject: [PATCH] Make large attachments fail fast --- .../java/io/heckel/ntfy/msg/ApiService.kt | 36 +++++++++++++------ .../java/io/heckel/ntfy/ui/ShareActivity.kt | 29 +++++++++------ app/src/main/java/io/heckel/ntfy/util/Util.kt | 12 ++++--- app/src/main/res/values/strings.xml | 1 + 4 files changed, 53 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/io/heckel/ntfy/msg/ApiService.kt b/app/src/main/java/io/heckel/ntfy/msg/ApiService.kt index df48066..911bcb6 100644 --- a/app/src/main/java/io/heckel/ntfy/msg/ApiService.kt +++ b/app/src/main/java/io/heckel/ntfy/msg/ApiService.kt @@ -1,15 +1,10 @@ package io.heckel.ntfy.msg -import android.net.Uri import android.os.Build import io.heckel.ntfy.BuildConfig import io.heckel.ntfy.db.Notification import io.heckel.ntfy.db.User -import io.heckel.ntfy.util.Log -import io.heckel.ntfy.util.topicUrl -import io.heckel.ntfy.util.topicUrlAuth -import io.heckel.ntfy.util.topicUrlJson -import io.heckel.ntfy.util.topicUrlJsonPoll +import io.heckel.ntfy.util.* import okhttp3.* import okhttp3.RequestBody.Companion.toRequestBody import java.io.IOException @@ -24,12 +19,29 @@ class ApiService { .readTimeout(15, TimeUnit.SECONDS) .writeTimeout(15, TimeUnit.SECONDS) .build() + private val publishClient = OkHttpClient.Builder() + .callTimeout(5, TimeUnit.MINUTES) // Total timeout for entire request + .connectTimeout(15, TimeUnit.SECONDS) + .readTimeout(15, TimeUnit.SECONDS) + .writeTimeout(15, TimeUnit.SECONDS) + .build() private val subscriberClient = OkHttpClient.Builder() .readTimeout(77, TimeUnit.SECONDS) // Assuming that keepalive messages are more frequent than this .build() private val parser = NotificationParser() - fun publish(baseUrl: String, topic: String, user: User?, message: String, title: String, priority: Int, tags: List, delay: String, body: RequestBody? = null, filename: String = "") { + fun publish( + baseUrl: String, + topic: String, + user: User? = null, + message: String, + title: String = "", + priority: Int = 3, + tags: List = emptyList(), + delay: String = "", + body: RequestBody? = null, + filename: String = "" + ) { val url = topicUrl(baseUrl, topic) Log.d(TAG, "Publishing to $url") @@ -56,11 +68,14 @@ class ApiService { } else { builder.put(message.toRequestBody()) } - client.newCall(builder.build()).execute().use { response -> + val request = builder.build() + Log.d(TAG, request.toString()) + publishClient.newCall(request).execute().use { response -> if (response.code == 401 || response.code == 403) { throw UnauthorizedException(user) - } - if (!response.isSuccessful) { + } else if (response.code == 413) { + throw EntityTooLargeException() + } else if (!response.isSuccessful) { throw Exception("Unexpected response ${response.code} when publishing to $url") } Log.d(TAG, "Successfully published to $url") @@ -149,6 +164,7 @@ class ApiService { } class UnauthorizedException(val user: User?) : Exception() + class EntityTooLargeException() : Exception() companion object { val USER_AGENT = "ntfy/${BuildConfig.VERSION_NAME} (${BuildConfig.FLAVOR}; Android ${Build.VERSION.RELEASE}; SDK ${Build.VERSION.SDK_INT})" diff --git a/app/src/main/java/io/heckel/ntfy/ui/ShareActivity.kt b/app/src/main/java/io/heckel/ntfy/ui/ShareActivity.kt index 7d43079..e11d151 100644 --- a/app/src/main/java/io/heckel/ntfy/ui/ShareActivity.kt +++ b/app/src/main/java/io/heckel/ntfy/ui/ShareActivity.kt @@ -177,16 +177,12 @@ class ShareActivity : AppCompatActivity() { lifecycleScope.launch(Dispatchers.IO) { val user = repository.getUser(baseUrl) try { - val filename = if (fileUri != null) { - fileStat(this@ShareActivity, fileUri).filename + val (filename, body) = if (fileUri != null) { + val stat = fileStat(this@ShareActivity, fileUri) + val body = ContentUriRequestBody(applicationContext.contentResolver, fileUri!!, stat.size) + Pair(stat.filename, body) } else { - "" - } - val body = if (fileUri != null) { - val resolver = applicationContext.contentResolver - ContentUriRequestBody(resolver, fileUri!!) - } else { - null + Pair("", null) } api.publish( baseUrl = baseUrl, @@ -198,7 +194,7 @@ class ShareActivity : AppCompatActivity() { tags = emptyList(), delay = "", body = body, // May be null - filename = filename // May be empty + filename = filename, // May be empty ) runOnUiThread { finish() @@ -207,9 +203,20 @@ class ShareActivity : AppCompatActivity() { .show() } } catch (e: Exception) { + val message = if (e is ApiService.UnauthorizedException) { + if (e.user != null) { + getString(R.string.detail_test_message_error_unauthorized_user, e.user.username) + } else { + getString(R.string.detail_test_message_error_unauthorized_anon) + } + } else if (e is ApiService.EntityTooLargeException) { + getString(R.string.detail_test_message_error_too_large) + } else { + getString(R.string.detail_test_message_error, e.message) + } runOnUiThread { progress.visibility = View.GONE - errorText.text = e.message + errorText.text = message errorImage.visibility = View.VISIBLE errorText.visibility = View.VISIBLE } diff --git a/app/src/main/java/io/heckel/ntfy/util/Util.kt b/app/src/main/java/io/heckel/ntfy/util/Util.kt index 37907b2..635819b 100644 --- a/app/src/main/java/io/heckel/ntfy/util/Util.kt +++ b/app/src/main/java/io/heckel/ntfy/util/Util.kt @@ -253,15 +253,19 @@ fun isDarkThemeOn(context: Context): Boolean { // https://cketti.de/2020/05/23/content-uris-and-okhttp/ class ContentUriRequestBody( - private val contentResolver: ContentResolver, - private val contentUri: Uri + private val resolver: ContentResolver, + private val uri: Uri, + private val size: Long ) : RequestBody() { + override fun contentLength(): Long { + return size + } override fun contentType(): MediaType? { - val contentType = contentResolver.getType(contentUri) + val contentType = resolver.getType(uri) return contentType?.toMediaTypeOrNull() } override fun writeTo(sink: BufferedSink) { - val inputStream = contentResolver.openInputStream(contentUri) ?: throw IOException("Couldn't open content URI for reading") + val inputStream = resolver.openInputStream(uri) ?: throw IOException("Couldn't open content URI for reading") inputStream.source().use { source -> sink.writeAll(source) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b112dee..0ef1147 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -129,6 +129,7 @@ Cannot send message: %1$s Cannot send message: Anonymous publishing not allowed Cannot send message: User %1$s not authorized + Cannot send message: Attachment too large Copied to clipboard Instant delivery enabled Instant delivery disabled