diff --git a/app/src/main/java/audio/funkwhale/ffa/repositories/HttpUpstream.kt b/app/src/main/java/audio/funkwhale/ffa/repositories/HttpUpstream.kt index eca5b76..08b53ad 100644 --- a/app/src/main/java/audio/funkwhale/ffa/repositories/HttpUpstream.kt +++ b/app/src/main/java/audio/funkwhale/ffa/repositories/HttpUpstream.kt @@ -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>( Progressive } + private val http = HTTP(context, oAuth) + override fun fetch(size: Int): Flow> = flow> { context?.let { @@ -89,10 +92,36 @@ class HttpUpstream>( val request = Fuel.get(mustNormalizeUrl(url)).apply { authorize(context, oAuth) } - val (_, _, result) = request.awaitObjectResponseResult(GenericDeserializer(type)) + val (_, response, result) = request.awaitObjectResponseResult(GenericDeserializer(type)) + + if (response.statusCode == 401) { + return retryGet(url) + } + result } catch (e: Exception) { Result.error(FuelError.wrap(e)) } } + + private suspend fun retryGet(url: String): Result { + 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") + } } diff --git a/app/src/main/java/audio/funkwhale/ffa/utils/Data.kt b/app/src/main/java/audio/funkwhale/ffa/utils/Data.kt index 65fc281..02c82d6 100644 --- a/app/src/main/java/audio/funkwhale/ffa/utils/Data.kt +++ b/app/src/main/java/audio/funkwhale/ffa/utils/Data.kt @@ -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 get(url: String): Result { + + 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 retryGet( + url: String + ): Result { + 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")