Make large attachments fail fast

This commit is contained in:
Philipp Heckel 2022-02-12 13:51:41 -05:00
parent 9afdf5e6e7
commit 29a40080db
4 changed files with 53 additions and 25 deletions

View file

@ -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<String>, 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<String> = 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})"

View file

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

View file

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

View file

@ -129,6 +129,7 @@
<string name="detail_test_message_error">Cannot send message: %1$s</string>
<string name="detail_test_message_error_unauthorized_anon">Cannot send message: Anonymous publishing not allowed</string>
<string name="detail_test_message_error_unauthorized_user">Cannot send message: User %1$s not authorized</string>
<string name="detail_test_message_error_too_large">Cannot send message: Attachment too large</string>
<string name="detail_copied_to_clipboard_message">Copied to clipboard</string>
<string name="detail_instant_delivery_enabled">Instant delivery enabled</string>
<string name="detail_instant_delivery_disabled">Instant delivery disabled</string>