Create HttpUtil
This commit is contained in:
parent
4a51d04ec4
commit
88dacc17da
8 changed files with 87 additions and 108 deletions
|
|
@ -9,7 +9,7 @@ import io.heckel.ntfy.db.Notification
|
|||
import io.heckel.ntfy.db.Repository
|
||||
import io.heckel.ntfy.db.User
|
||||
import io.heckel.ntfy.util.ALL_PRIORITIES
|
||||
import io.heckel.ntfy.util.CertUtil
|
||||
import io.heckel.ntfy.util.HttpUtil
|
||||
import io.heckel.ntfy.util.Log
|
||||
import io.heckel.ntfy.util.PRIORITY_DEFAULT
|
||||
import io.heckel.ntfy.util.topicUrl
|
||||
|
|
@ -19,7 +19,6 @@ import io.heckel.ntfy.util.topicUrlJsonPoll
|
|||
import okhttp3.Call
|
||||
import okhttp3.Callback
|
||||
import okhttp3.Credentials
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
|
|
@ -27,39 +26,13 @@ import okhttp3.Response
|
|||
import java.io.IOException
|
||||
import java.net.URLEncoder
|
||||
import java.nio.charset.StandardCharsets.UTF_8
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.random.Random
|
||||
|
||||
class ApiService(private val context: Context) {
|
||||
private val repository = Repository.getInstance(context)
|
||||
private val sslManager = CertUtil.getInstance(context)
|
||||
private val gson = Gson()
|
||||
private val parser = NotificationParser()
|
||||
|
||||
private fun createClient(baseUrl: String): OkHttpClient {
|
||||
return sslManager.getOkHttpClientBuilder(baseUrl)
|
||||
.callTimeout(15, TimeUnit.SECONDS)
|
||||
.connectTimeout(15, TimeUnit.SECONDS)
|
||||
.readTimeout(15, TimeUnit.SECONDS)
|
||||
.writeTimeout(15, TimeUnit.SECONDS)
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun createPublishClient(baseUrl: String): OkHttpClient {
|
||||
return sslManager.getOkHttpClientBuilder(baseUrl)
|
||||
.callTimeout(5, TimeUnit.MINUTES)
|
||||
.connectTimeout(15, TimeUnit.SECONDS)
|
||||
.readTimeout(15, TimeUnit.SECONDS)
|
||||
.writeTimeout(15, TimeUnit.SECONDS)
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun createSubscriberClient(baseUrl: String): OkHttpClient {
|
||||
return sslManager.getOkHttpClientBuilder(baseUrl)
|
||||
.readTimeout(77, TimeUnit.SECONDS)
|
||||
.build()
|
||||
}
|
||||
|
||||
suspend fun publish(
|
||||
baseUrl: String,
|
||||
topic: String,
|
||||
|
|
@ -123,7 +96,7 @@ class ApiService(private val context: Context) {
|
|||
.put(body ?: message.toRequestBody())
|
||||
.build()
|
||||
Log.d(TAG, "Publishing to $request")
|
||||
val httpCall = createPublishClient(baseUrl).newCall(request)
|
||||
val httpCall = HttpUtil.longCallClient(context, baseUrl).newCall(request)
|
||||
onCancelAvailable?.invoke { httpCall.cancel() } // Notify caller that HTTP request can now be canceled
|
||||
httpCall.execute().use { response ->
|
||||
if (response.code == 401 || response.code == 403) {
|
||||
|
|
@ -154,7 +127,7 @@ class ApiService(private val context: Context) {
|
|||
|
||||
val customHeaders = repository.getCustomHeaders(baseUrl)
|
||||
val request = requestBuilder(url, user, customHeaders).build()
|
||||
createClient(baseUrl).newCall(request).execute().use { response ->
|
||||
HttpUtil.defaultClient(context, baseUrl).newCall(request).execute().use { response ->
|
||||
if (!response.isSuccessful) {
|
||||
throw Exception("Unexpected response ${response.code} when polling topic $url")
|
||||
}
|
||||
|
|
@ -182,7 +155,7 @@ class ApiService(private val context: Context) {
|
|||
Log.d(TAG, "Opening subscription connection to $url")
|
||||
val customHeaders = repository.getCustomHeaders(baseUrl)
|
||||
val request = requestBuilder(url, user, customHeaders).build()
|
||||
val call = createSubscriberClient(baseUrl).newCall(request)
|
||||
val call = HttpUtil.subscriberClient(context, baseUrl).newCall(request)
|
||||
call.enqueue(object : Callback {
|
||||
override fun onResponse(call: Call, response: Response) {
|
||||
try {
|
||||
|
|
@ -221,7 +194,7 @@ class ApiService(private val context: Context) {
|
|||
val url = topicUrlAuth(baseUrl, topic)
|
||||
val customHeaders = repository.getCustomHeaders(baseUrl)
|
||||
val request = requestBuilder(url, user, customHeaders).build()
|
||||
createClient(baseUrl).newCall(request).execute().use { response ->
|
||||
HttpUtil.defaultClient(context, baseUrl).newCall(request).execute().use { response ->
|
||||
if (response.isSuccessful) {
|
||||
return true
|
||||
} else if (user == null && response.code == 404) {
|
||||
|
|
|
|||
|
|
@ -20,29 +20,16 @@ import io.heckel.ntfy.db.Attachment
|
|||
import io.heckel.ntfy.db.Notification
|
||||
import io.heckel.ntfy.db.Repository
|
||||
import io.heckel.ntfy.db.Subscription
|
||||
import io.heckel.ntfy.util.CertUtil
|
||||
import io.heckel.ntfy.util.HttpUtil
|
||||
import io.heckel.ntfy.util.Log
|
||||
import io.heckel.ntfy.util.ensureSafeNewFile
|
||||
import io.heckel.ntfy.util.extractBaseUrl
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import java.io.File
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class DownloadAttachmentWorker(private val context: Context, params: WorkerParameters) : Worker(context, params) {
|
||||
private val certUtil = CertUtil.getInstance(context)
|
||||
|
||||
private fun createClient(url: String): OkHttpClient {
|
||||
val baseUrl = extractBaseUrl(url)
|
||||
return certUtil.getOkHttpClientBuilder(baseUrl)
|
||||
.callTimeout(15, TimeUnit.MINUTES)
|
||||
.connectTimeout(15, TimeUnit.SECONDS)
|
||||
.readTimeout(15, TimeUnit.SECONDS)
|
||||
.writeTimeout(15, TimeUnit.SECONDS)
|
||||
.build()
|
||||
}
|
||||
|
||||
private val notifier = NotificationService(context)
|
||||
private lateinit var repository: Repository
|
||||
private lateinit var subscription: Subscription
|
||||
|
|
@ -80,7 +67,9 @@ class DownloadAttachmentWorker(private val context: Context, params: WorkerParam
|
|||
.url(attachment.url)
|
||||
.addHeader("User-Agent", ApiService.USER_AGENT)
|
||||
.build()
|
||||
createClient(attachment.url).newCall(request).execute().use { response ->
|
||||
val client = HttpUtil
|
||||
.longCallClient(context, extractBaseUrl(attachment.url))
|
||||
client.newCall(request).execute().use { response ->
|
||||
Log.d(TAG, "Download: headers received: $response")
|
||||
if (!response.isSuccessful) {
|
||||
throw Exception("Unexpected response: ${response.code}")
|
||||
|
|
|
|||
|
|
@ -11,30 +11,16 @@ import io.heckel.ntfy.db.Icon
|
|||
import io.heckel.ntfy.db.Notification
|
||||
import io.heckel.ntfy.db.Repository
|
||||
import io.heckel.ntfy.db.Subscription
|
||||
import io.heckel.ntfy.util.CertUtil
|
||||
import io.heckel.ntfy.util.HttpUtil
|
||||
import io.heckel.ntfy.util.Log
|
||||
import io.heckel.ntfy.util.extractBaseUrl
|
||||
import io.heckel.ntfy.util.sha256
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import java.io.File
|
||||
import java.util.Date
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class DownloadIconWorker(private val context: Context, params: WorkerParameters) : Worker(context, params) {
|
||||
private val certUtil = CertUtil.getInstance(context)
|
||||
|
||||
private fun createClient(url: String): OkHttpClient {
|
||||
val baseUrl = extractBaseUrl(url)
|
||||
return certUtil.getOkHttpClientBuilder(baseUrl)
|
||||
.callTimeout(1, TimeUnit.MINUTES)
|
||||
.connectTimeout(15, TimeUnit.SECONDS)
|
||||
.readTimeout(15, TimeUnit.SECONDS)
|
||||
.writeTimeout(15, TimeUnit.SECONDS)
|
||||
.build()
|
||||
}
|
||||
|
||||
private val notifier = NotificationService(context)
|
||||
private lateinit var repository: Repository
|
||||
private lateinit var subscription: Subscription
|
||||
|
|
@ -79,7 +65,8 @@ class DownloadIconWorker(private val context: Context, params: WorkerParameters)
|
|||
.url(icon.url)
|
||||
.addHeader("User-Agent", ApiService.USER_AGENT)
|
||||
.build()
|
||||
createClient(icon.url).newCall(request).execute().use { response ->
|
||||
val client = HttpUtil.defaultClient(context, extractBaseUrl(icon.url))
|
||||
client.newCall(request).execute().use { response ->
|
||||
Log.d(TAG, "Headers received: $response, Content-Length: ${response.headers["Content-Length"]}")
|
||||
if (!response.isSuccessful) {
|
||||
throw Exception("Unexpected response: ${response.code}")
|
||||
|
|
|
|||
|
|
@ -14,28 +14,14 @@ import io.heckel.ntfy.db.Repository
|
|||
import io.heckel.ntfy.db.Subscription
|
||||
import io.heckel.ntfy.msg.NotificationService.Companion.ACTION_BROADCAST
|
||||
import io.heckel.ntfy.msg.NotificationService.Companion.ACTION_HTTP
|
||||
import io.heckel.ntfy.util.CertUtil
|
||||
import io.heckel.ntfy.util.HttpUtil
|
||||
import io.heckel.ntfy.util.Log
|
||||
import io.heckel.ntfy.util.extractBaseUrl
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class UserActionWorker(private val context: Context, params: WorkerParameters) : Worker(context, params) {
|
||||
private val certUtil = CertUtil.getInstance(context)
|
||||
|
||||
private fun createClient(url: String): OkHttpClient {
|
||||
val baseUrl = extractBaseUrl(url)
|
||||
return certUtil.getOkHttpClientBuilder(baseUrl)
|
||||
.callTimeout(60, TimeUnit.SECONDS)
|
||||
.connectTimeout(15, TimeUnit.SECONDS)
|
||||
.readTimeout(15, TimeUnit.SECONDS)
|
||||
.writeTimeout(15, TimeUnit.SECONDS)
|
||||
.build()
|
||||
}
|
||||
|
||||
private val notifier = NotificationService(context)
|
||||
private val broadcaster = BroadcastService(context)
|
||||
private lateinit var repository: Repository
|
||||
|
|
@ -93,9 +79,10 @@ class UserActionWorker(private val context: Context, params: WorkerParameters) :
|
|||
builder.addHeader(key, value)
|
||||
}
|
||||
val request = builder.build()
|
||||
val client = HttpUtil.defaultClient(context, extractBaseUrl(url))
|
||||
|
||||
Log.d(TAG, "Executing HTTP request: ${method.uppercase(Locale.getDefault())} ${action.url}")
|
||||
createClient(url).newCall(request).execute().use { response ->
|
||||
client.newCall(request).execute().use { response ->
|
||||
if (response.isSuccessful) {
|
||||
save(action.copy(progress = ACTION_PROGRESS_SUCCESS, error = null))
|
||||
return
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import io.heckel.ntfy.db.Subscription
|
|||
import io.heckel.ntfy.db.User
|
||||
import io.heckel.ntfy.msg.ApiService.Companion.requestBuilder
|
||||
import io.heckel.ntfy.msg.NotificationParser
|
||||
import io.heckel.ntfy.util.CertUtil
|
||||
import io.heckel.ntfy.util.HttpUtil
|
||||
import io.heckel.ntfy.util.Log
|
||||
import io.heckel.ntfy.util.topicShortUrl
|
||||
import io.heckel.ntfy.util.topicUrlWs
|
||||
|
|
@ -20,7 +20,6 @@ import okhttp3.Response
|
|||
import okhttp3.WebSocket
|
||||
import okhttp3.WebSocketListener
|
||||
import java.util.Calendar
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import kotlin.random.Random
|
||||
|
|
@ -47,13 +46,8 @@ class WsConnection(
|
|||
private val alarmManager: AlarmManager
|
||||
) : Connection {
|
||||
private val parser = NotificationParser()
|
||||
private val certUtil = CertUtil.getInstance(context)
|
||||
private val client: OkHttpClient by lazy {
|
||||
certUtil.getOkHttpClientBuilder(connectionId.baseUrl)
|
||||
.readTimeout(0, TimeUnit.MILLISECONDS)
|
||||
.pingInterval(1, TimeUnit.MINUTES) // The server pings us too, so this doesn't matter much
|
||||
.connectTimeout(10, TimeUnit.SECONDS)
|
||||
.build()
|
||||
HttpUtil.wsClient(context, connectionId.baseUrl)
|
||||
}
|
||||
private var errorCount = 0
|
||||
private var webSocket: WebSocket? = null
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ import io.heckel.ntfy.service.SubscriberServiceManager
|
|||
import io.heckel.ntfy.util.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import java.text.SimpleDateFormat
|
||||
|
|
@ -774,12 +773,8 @@ class SettingsActivity : AppCompatActivity(), PreferenceFragmentCompat.OnPrefere
|
|||
.url(EXPORT_LOGS_UPLOAD_URL)
|
||||
.put(log.toRequestBody())
|
||||
.build()
|
||||
val client = OkHttpClient.Builder()
|
||||
.callTimeout(1, TimeUnit.MINUTES) // Total timeout for entire request
|
||||
.connectTimeout(15, TimeUnit.SECONDS)
|
||||
.readTimeout(15, TimeUnit.SECONDS)
|
||||
.writeTimeout(15, TimeUnit.SECONDS)
|
||||
.build()
|
||||
val client = HttpUtil
|
||||
.longCallClient(context, extractBaseUrl(EXPORT_LOGS_UPLOAD_URL))
|
||||
try {
|
||||
client.newCall(request).execute().use { response ->
|
||||
if (!response.isSuccessful) {
|
||||
|
|
@ -1117,6 +1112,7 @@ class SettingsActivity : AppCompatActivity(), PreferenceFragmentCompat.OnPrefere
|
|||
private const val EXPORT_LOGS_UPLOAD_ORIGINAL = "upload_original"
|
||||
private const val EXPORT_LOGS_UPLOAD_SCRUBBED = "upload_scrubbed"
|
||||
private const val EXPORT_LOGS_UPLOAD_URL = "https://nopaste.net/?f=json" // Run by binwiederhier; see https://github.com/binwiederhier/pcopy
|
||||
private const val EXPORT_LOGS_UPLOAD_TIMEOUT_MINUTES = 1L
|
||||
private const val EXPORT_LOGS_UPLOAD_NOTIFY_SIZE_THRESHOLD = 100 * 1024 // Show "Uploading ..." if log larger than X
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,21 +37,9 @@ import kotlin.collections.addAll
|
|||
*/
|
||||
class CertUtil private constructor(context: Context) {
|
||||
private val appContext: Context = context.applicationContext
|
||||
private val repository: Repository by lazy { Repository.Companion.getInstance(appContext) }
|
||||
private val repository: Repository by lazy { Repository.getInstance(appContext) }
|
||||
|
||||
/**
|
||||
* Get an OkHttpClient.Builder configured with custom SSL for a specific server
|
||||
*/
|
||||
fun getOkHttpClientBuilder(baseUrl: String): OkHttpClient.Builder {
|
||||
val builder = OkHttpClient.Builder()
|
||||
applySSLConfiguration(builder, baseUrl)
|
||||
return builder
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply SSL configuration to an OkHttpClient.Builder for a specific server
|
||||
*/
|
||||
fun applySSLConfiguration(builder: OkHttpClient.Builder, baseUrl: String) {
|
||||
fun withTLSConfig(builder: OkHttpClient.Builder, baseUrl: String): OkHttpClient.Builder {
|
||||
try {
|
||||
val trustManagers = mutableListOf<TrustManager>()
|
||||
val keyManagers = mutableListOf<KeyManager>()
|
||||
|
|
@ -119,6 +107,7 @@ class CertUtil private constructor(context: Context) {
|
|||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to configure SSL for $baseUrl", e)
|
||||
}
|
||||
return builder
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
64
app/src/main/java/io/heckel/ntfy/util/HttpUtil.kt
Normal file
64
app/src/main/java/io/heckel/ntfy/util/HttpUtil.kt
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package io.heckel.ntfy.util
|
||||
|
||||
import android.content.Context
|
||||
import okhttp3.OkHttpClient
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
* Utility class for creating OkHttpClient instances with appropriate configurations.
|
||||
* All clients are configured with SSL/TLS settings from CertUtil for custom certificate support.
|
||||
*/
|
||||
object HttpUtil {
|
||||
/**
|
||||
* Client for regular API calls (auth, poll, etc.).
|
||||
*/
|
||||
fun defaultClient(context: Context, baseUrl: String): OkHttpClient {
|
||||
return defaultBuilder(context, baseUrl).build()
|
||||
}
|
||||
|
||||
/**
|
||||
* Client with a longer call timeout (5 minutes).
|
||||
* Allows for large file uploads or downloads.
|
||||
*/
|
||||
fun longCallClient(context: Context, baseUrl: String): OkHttpClient {
|
||||
return defaultBuilder(context, baseUrl)
|
||||
.callTimeout(5, TimeUnit.MINUTES)
|
||||
.build()
|
||||
}
|
||||
|
||||
/**
|
||||
* Client for long-polling/streaming subscriptions.
|
||||
*/
|
||||
fun subscriberClient(context: Context, baseUrl: String): OkHttpClient {
|
||||
return emptyBuilder(context, baseUrl)
|
||||
.readTimeout(77, TimeUnit.SECONDS) // Long enough to allow for server-side keepalive messages
|
||||
.build()
|
||||
}
|
||||
|
||||
/**
|
||||
* Client for WebSocket connections.
|
||||
* No read timeout, 1 minute ping interval, 10s connect timeout.
|
||||
*/
|
||||
fun wsClient(context: Context, baseUrl: String): OkHttpClient {
|
||||
return emptyBuilder(context, baseUrl)
|
||||
.readTimeout(0, TimeUnit.MILLISECONDS)
|
||||
.pingInterval(1, TimeUnit.MINUTES) // Technically not necessary, the server also pings us
|
||||
.connectTimeout(10, TimeUnit.SECONDS)
|
||||
.build()
|
||||
}
|
||||
|
||||
fun defaultBuilder(context: Context, baseUrl: String): OkHttpClient.Builder {
|
||||
return emptyBuilder(context, baseUrl)
|
||||
.callTimeout(60, TimeUnit.SECONDS) // Increased to 60s (from 15s) to reduce client variance
|
||||
.connectTimeout(15, TimeUnit.SECONDS)
|
||||
.readTimeout(15, TimeUnit.SECONDS)
|
||||
.writeTimeout(15, TimeUnit.SECONDS)
|
||||
}
|
||||
|
||||
fun emptyBuilder(context: Context, baseUrl: String): OkHttpClient.Builder {
|
||||
return CertUtil
|
||||
.getInstance(context)
|
||||
.withTLSConfig(OkHttpClient.Builder(), baseUrl)
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Add table
Reference in a new issue