From 53d99e46e6b34d4437f46266cb797a65c0736803 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Thu, 27 Aug 2020 16:42:03 -0300 Subject: [cashier] don't crash on unexpected network input --- .../src/main/java/net/taler/cashier/Response.kt | 86 ++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 cashier/src/main/java/net/taler/cashier/Response.kt (limited to 'cashier/src/main/java/net/taler/cashier/Response.kt') diff --git a/cashier/src/main/java/net/taler/cashier/Response.kt b/cashier/src/main/java/net/taler/cashier/Response.kt new file mode 100644 index 0000000..0ad39d0 --- /dev/null +++ b/cashier/src/main/java/net/taler/cashier/Response.kt @@ -0,0 +1,86 @@ +/* + * This file is part of GNU Taler + * (C) 2020 Taler Systems S.A. + * + * GNU Taler is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 3, or (at your option) any later version. + * + * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * GNU Taler; see the file COPYING. If not, see + */ + +package net.taler.cashier + +import android.content.Context +import android.util.Log +import io.ktor.client.call.receive +import io.ktor.client.features.ResponseException +import io.ktor.http.HttpStatusCode +import kotlinx.serialization.Serializable +import net.taler.common.isOnline +import java.net.UnknownHostException + +class Response private constructor( + private val value: Any? +) { + companion object { + suspend fun response(request: suspend () -> T): Response { + return try { + Response(request()) + } catch (e: Throwable) { + Log.e("HttpClient", "Error getting request", e) + Response(getFailure(e)) + } + } + + private suspend fun getFailure(e: Throwable): Failure = when (e) { + is ResponseException -> Failure(e, getExceptionString(e), e.response?.status) + else -> Failure(e, e.toString()) + } + + private suspend fun getExceptionString(e: ResponseException): String { + val response = e.response ?: return e.toString() + return try { + Log.e("TEST", "TRY RECEIVE $response") + val error: Error = response.receive() + "Error ${error.code}: ${error.hint}" + } catch (ex: Exception) { + "Status code: ${response.status.value}" + } + } + } + + private val isFailure: Boolean get() = value is Failure + + suspend fun onSuccess(block: suspend (result: T) -> Unit): Response { + @Suppress("UNCHECKED_CAST") + if (!isFailure) block(value as T) + return this + } + + suspend fun onError(block: suspend (failure: Failure) -> Unit): Response { + if (value is Failure) block(value) + return this + } + + data class Failure( + val exception: Throwable, + val msg: String, + val statusCode: HttpStatusCode? = null + ) { + fun isOffline(context: Context): Boolean { + return exception is UnknownHostException && !context.isOnline() + } + } + + @Serializable + private class Error( + val code: Int?, + val hint: String? + ) +} -- cgit v1.2.3