KtorClient.kt 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. package com.example.modifier.http
  2. import com.example.modifier.http.response.ErrorResponse
  3. import com.example.modifier.repo.AppPrefsRepo
  4. import io.ktor.client.HttpClient
  5. import io.ktor.client.call.body
  6. import io.ktor.client.engine.okhttp.OkHttp
  7. import io.ktor.client.network.sockets.ConnectTimeoutException
  8. import io.ktor.client.network.sockets.SocketTimeoutException
  9. import io.ktor.client.plugins.ClientRequestException
  10. import io.ktor.client.plugins.HttpRequestRetry
  11. import io.ktor.client.plugins.HttpRequestTimeoutException
  12. import io.ktor.client.plugins.HttpResponseValidator
  13. import io.ktor.client.plugins.HttpSend
  14. import io.ktor.client.plugins.HttpTimeout
  15. import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
  16. import io.ktor.client.plugins.defaultRequest
  17. import io.ktor.client.plugins.logging.LogLevel
  18. import io.ktor.client.plugins.logging.Logging
  19. import io.ktor.client.plugins.resources.Resources
  20. import io.ktor.client.utils.unwrapCancellationException
  21. import io.ktor.serialization.kotlinx.json.json
  22. import kotlinx.coroutines.CancellationException
  23. import kotlinx.serialization.ExperimentalSerializationApi
  24. import kotlinx.serialization.json.Json
  25. private fun Throwable.isTimeoutException(): Boolean {
  26. val exception = unwrapCancellationException()
  27. return exception is HttpRequestTimeoutException ||
  28. exception is ConnectTimeoutException ||
  29. exception is SocketTimeoutException
  30. }
  31. @OptIn(ExperimentalSerializationApi::class)
  32. var ktorClient = HttpClient(OkHttp) {
  33. defaultRequest {
  34. val server = AppPrefsRepo.instance.appPrefs.value.server
  35. url(server.endsWith("/").let {
  36. if (it) server else "$server/"
  37. })
  38. }
  39. install(HttpSend) {
  40. maxSendCount = 200
  41. }
  42. install(Logging) {
  43. level = LogLevel.INFO
  44. }
  45. install(HttpRequestRetry) {
  46. maxRetries = 3
  47. retryOnExceptionIf { _, cause ->
  48. when {
  49. cause.isTimeoutException() -> true
  50. cause is CancellationException -> false
  51. else -> true
  52. }
  53. }
  54. retryIf { _, response ->
  55. response.status.value.let {
  56. when (it) {
  57. 425, 429, 502, 503, 504 -> true
  58. else -> false
  59. }
  60. }
  61. }
  62. delayMillis { 1000 }
  63. }
  64. install(HttpTimeout) {
  65. requestTimeoutMillis = 30 * 1000
  66. socketTimeoutMillis = 30 * 1000
  67. }
  68. install(Resources)
  69. install(ContentNegotiation) {
  70. json(Json {
  71. prettyPrint = true
  72. isLenient = true
  73. ignoreUnknownKeys = true
  74. explicitNulls = false
  75. })
  76. }
  77. HttpResponseValidator {
  78. validateResponse { response ->
  79. if (response.status.value !in 200..299) {
  80. val error = response.body<ErrorResponse>()
  81. throw ClientRequestException(response, error.message ?: "Unknown error")
  82. }
  83. }
  84. }
  85. }