Add http retry back on unauthorized request

This commit is contained in:
Ryan Harg 2021-08-20 11:50:32 +02:00
parent 9860ccd576
commit 8ecc6f9b69
No known key found for this signature in database
GPG Key ID: 89106F3A84E6958C
2 changed files with 110 additions and 1 deletions

View File

@ -7,6 +7,7 @@ import com.github.kittinunf.fuel.Fuel
import com.github.kittinunf.fuel.core.FuelError
import com.github.kittinunf.fuel.core.ResponseDeserializable
import com.github.kittinunf.fuel.coroutines.awaitObjectResponseResult
import com.github.kittinunf.fuel.coroutines.awaitObjectResult
import com.github.kittinunf.result.Result
import com.google.gson.Gson
import kotlinx.coroutines.Dispatchers.IO
@ -32,6 +33,8 @@ class HttpUpstream<D : Any, R : OtterResponse<D>>(
Progressive
}
private val http = HTTP(context, oAuth)
override fun fetch(size: Int): Flow<Repository.Response<D>> = flow<Repository.Response<D>> {
context?.let {
@ -89,10 +92,36 @@ class HttpUpstream<D : Any, R : OtterResponse<D>>(
val request = Fuel.get(mustNormalizeUrl(url)).apply {
authorize(context, oAuth)
}
val (_, _, result) = request.awaitObjectResponseResult(GenericDeserializer<R>(type))
val (_, response, result) = request.awaitObjectResponseResult(GenericDeserializer<R>(type))
if (response.statusCode == 401) {
return retryGet(url)
}
result
} catch (e: Exception) {
Result.error(FuelError.wrap(e))
}
}
private suspend fun retryGet(url: String): Result<R, FuelError> {
context?.let {
return try {
return if (http.refresh()) {
val request = Fuel.get(mustNormalizeUrl(url)).apply {
if (!Settings.isAnonymous()) {
header("Authorization", "Bearer ${oAuth.state().accessToken}")
}
}
request.awaitObjectResult(GenericDeserializer(type))
} else {
Result.Failure(FuelError.wrap(RefreshError))
}
} catch (e: Exception) {
Result.error(FuelError.wrap(e))
}
}
throw IllegalStateException("Illegal state: context is null")
}
}

View File

@ -1,6 +1,14 @@
package audio.funkwhale.ffa.utils
import android.content.Context
import audio.funkwhale.ffa.activities.FwCredentials
import com.github.kittinunf.fuel.Fuel
import com.github.kittinunf.fuel.core.FuelError
import com.github.kittinunf.fuel.coroutines.awaitObjectResponseResult
import com.github.kittinunf.fuel.coroutines.awaitObjectResult
import com.github.kittinunf.fuel.gson.gsonDeserializerOf
import com.github.kittinunf.result.Result
import com.preference.PowerPreference
import java.io.BufferedReader
import java.io.File
import java.nio.charset.Charset
@ -8,6 +16,78 @@ import java.security.MessageDigest
object RefreshError : Throwable()
class HTTP(
val context: Context?,
val oAuth: OAuth
) {
suspend fun refresh(): Boolean {
context?.let {
val body = mapOf(
"username" to PowerPreference.getFileByName(AppContext.PREFS_CREDENTIALS)
.getString("username"),
"password" to PowerPreference.getFileByName(AppContext.PREFS_CREDENTIALS)
.getString("password")
).toList()
val result = Fuel.post(mustNormalizeUrl("/api/v1/token"), body).apply {
if (!Settings.isAnonymous()) {
authorize(it, oAuth)
header("Authorization", "Bearer ${oAuth.state().accessToken}")
}
}
.awaitObjectResult(gsonDeserializerOf(FwCredentials::class.java))
return result.fold(
{ data ->
PowerPreference.getFileByName(AppContext.PREFS_CREDENTIALS)
.setString("access_token", data.token)
true
},
{ false }
)
}
throw IllegalStateException("Illegal state: context is null")
}
suspend inline fun <reified T : Any> get(url: String): Result<T, FuelError> {
context?.let {
val request = Fuel.get(mustNormalizeUrl(url)).apply {
if (!Settings.isAnonymous()) {
authorize(it, oAuth)
header("Authorization", "Bearer ${oAuth.state().accessToken}")
}
}
val (_, response, result) = request.awaitObjectResponseResult(gsonDeserializerOf(T::class.java))
if (response.statusCode == 401) {
return retryGet(url)
} else {
return result
}
}
throw IllegalStateException("Illegal state: context is null")
}
suspend inline fun <reified T : Any> retryGet(
url: String
): Result<T, FuelError> {
context?.let {
val request = Fuel.get(mustNormalizeUrl(url)).apply {
if (!Settings.isAnonymous()) {
authorize(context,oAuth)
header("Authorization", "Bearer ${oAuth.state().accessToken}")
}
}
request.awaitObjectResult(gsonDeserializerOf(T::class.java))
}
throw IllegalStateException("Illegal state: context is null")
}
}
object FFACache {
private fun key(key: String): String {
val md = MessageDigest.getInstance("SHA-1")