Add http retry back on unauthorized request
This commit is contained in:
parent
9860ccd576
commit
8ecc6f9b69
|
@ -7,6 +7,7 @@ import com.github.kittinunf.fuel.Fuel
|
||||||
import com.github.kittinunf.fuel.core.FuelError
|
import com.github.kittinunf.fuel.core.FuelError
|
||||||
import com.github.kittinunf.fuel.core.ResponseDeserializable
|
import com.github.kittinunf.fuel.core.ResponseDeserializable
|
||||||
import com.github.kittinunf.fuel.coroutines.awaitObjectResponseResult
|
import com.github.kittinunf.fuel.coroutines.awaitObjectResponseResult
|
||||||
|
import com.github.kittinunf.fuel.coroutines.awaitObjectResult
|
||||||
import com.github.kittinunf.result.Result
|
import com.github.kittinunf.result.Result
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import kotlinx.coroutines.Dispatchers.IO
|
import kotlinx.coroutines.Dispatchers.IO
|
||||||
|
@ -32,6 +33,8 @@ class HttpUpstream<D : Any, R : OtterResponse<D>>(
|
||||||
Progressive
|
Progressive
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val http = HTTP(context, oAuth)
|
||||||
|
|
||||||
override fun fetch(size: Int): Flow<Repository.Response<D>> = flow<Repository.Response<D>> {
|
override fun fetch(size: Int): Flow<Repository.Response<D>> = flow<Repository.Response<D>> {
|
||||||
|
|
||||||
context?.let {
|
context?.let {
|
||||||
|
@ -89,10 +92,36 @@ class HttpUpstream<D : Any, R : OtterResponse<D>>(
|
||||||
val request = Fuel.get(mustNormalizeUrl(url)).apply {
|
val request = Fuel.get(mustNormalizeUrl(url)).apply {
|
||||||
authorize(context, oAuth)
|
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
|
result
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Result.error(FuelError.wrap(e))
|
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")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,14 @@
|
||||||
package audio.funkwhale.ffa.utils
|
package audio.funkwhale.ffa.utils
|
||||||
|
|
||||||
import android.content.Context
|
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.BufferedReader
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.charset.Charset
|
import java.nio.charset.Charset
|
||||||
|
@ -8,6 +16,78 @@ import java.security.MessageDigest
|
||||||
|
|
||||||
object RefreshError : Throwable()
|
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 {
|
object FFACache {
|
||||||
private fun key(key: String): String {
|
private fun key(key: String): String {
|
||||||
val md = MessageDigest.getInstance("SHA-1")
|
val md = MessageDigest.getInstance("SHA-1")
|
||||||
|
|
Loading…
Reference in New Issue