diff --git a/data/network/src/commonMain/kotlin/com/wire/kalium/network/api/base/unbound/acme/ACMEApi.kt b/data/network/src/commonMain/kotlin/com/wire/kalium/network/api/base/unbound/acme/ACMEApi.kt index 8e670f1f8990..0fb525eb7eb5 100644 --- a/data/network/src/commonMain/kotlin/com/wire/kalium/network/api/base/unbound/acme/ACMEApi.kt +++ b/data/network/src/commonMain/kotlin/com/wire/kalium/network/api/base/unbound/acme/ACMEApi.kt @@ -31,7 +31,7 @@ import com.wire.kalium.network.tools.KtxSerializer import com.wire.kalium.network.utils.CustomErrors import com.wire.kalium.network.utils.NetworkResponse import com.wire.kalium.network.utils.flatMap -import com.wire.kalium.network.utils.handleUnsuccessfulResponse +import com.wire.kalium.network.utils.interceptUnsuccessfulResponse import com.wire.kalium.network.utils.mapSuccess import com.wire.kalium.network.utils.wrapKaliumResponse import io.ktor.client.call.body @@ -128,7 +128,7 @@ class ACMEApiImpl internal constructor( } else { // FIXME: It doesn't make any sense to do the regular handling of unsuccessful response here, // as ACME is NOT the Wire API, and doesn't follow its error response format. - handleUnsuccessfulResponse(httpResponse) + interceptUnsuccessfulResponse(httpResponse) } override suspend fun sendACMERequest( @@ -210,7 +210,7 @@ class ACMEApiImpl internal constructor( } else { // FIXME: It doesn't make any sense to do the regular handling of unsuccessful response here, // as ACME is NOT the Wire API, and doesn't follow its error response format. - handleUnsuccessfulResponse(httpResponse) + interceptUnsuccessfulResponse(httpResponse) } override suspend fun sendChallengeRequest(url: String, body: ByteArray): NetworkResponse { diff --git a/data/network/src/commonMain/kotlin/com/wire/kalium/network/api/v0/authenticated/AssetApiV0.kt b/data/network/src/commonMain/kotlin/com/wire/kalium/network/api/v0/authenticated/AssetApiV0.kt index 288327c9e9a6..bc2287f758a1 100644 --- a/data/network/src/commonMain/kotlin/com/wire/kalium/network/api/v0/authenticated/AssetApiV0.kt +++ b/data/network/src/commonMain/kotlin/com/wire/kalium/network/api/v0/authenticated/AssetApiV0.kt @@ -25,7 +25,7 @@ import com.wire.kalium.network.api.base.authenticated.asset.AssetApi import com.wire.kalium.network.exceptions.KaliumException import com.wire.kalium.network.kaliumLogger import com.wire.kalium.network.utils.NetworkResponse -import com.wire.kalium.network.utils.handleUnsuccessfulResponse +import com.wire.kalium.network.utils.interceptUnsuccessfulResponse import com.wire.kalium.network.utils.wrapKaliumResponse import io.ktor.client.call.body import io.ktor.client.request.delete @@ -71,7 +71,7 @@ internal open class AssetApiV0 internal constructor( if (httpResponse.status.isSuccess()) { handleAssetContentDownload(httpResponse, tempFileSink) } else { - handleUnsuccessfulResponse(httpResponse).also { + interceptUnsuccessfulResponse(httpResponse).also { if (it.kException is KaliumException.InvalidRequestError && it.kException.errorResponse.code == HttpStatusCode.Unauthorized.value ) { diff --git a/data/network/src/commonMain/kotlin/com/wire/kalium/network/api/v0/authenticated/NomadDeviceSyncApiV0.kt b/data/network/src/commonMain/kotlin/com/wire/kalium/network/api/v0/authenticated/NomadDeviceSyncApiV0.kt index e543ba3e586b..59b0a3b3d444 100644 --- a/data/network/src/commonMain/kotlin/com/wire/kalium/network/api/v0/authenticated/NomadDeviceSyncApiV0.kt +++ b/data/network/src/commonMain/kotlin/com/wire/kalium/network/api/v0/authenticated/NomadDeviceSyncApiV0.kt @@ -31,7 +31,7 @@ import com.wire.kalium.network.exceptions.APINotSupported import com.wire.kalium.network.exceptions.KaliumException import com.wire.kalium.network.exceptions.NetworkErrorLabel import com.wire.kalium.network.utils.NetworkResponse -import com.wire.kalium.network.utils.handleUnsuccessfulResponse +import com.wire.kalium.network.utils.interceptUnsuccessfulResponse import com.wire.kalium.network.utils.setUrl import com.wire.kalium.network.utils.wrapRequest import io.ktor.client.call.body @@ -151,7 +151,7 @@ internal open class NomadDeviceSyncApiV0 internal constructor( httpResponse.status == HttpStatusCode.Forbidden -> handleLabeledError(httpResponse, NetworkErrorLabel.NO_CRYPTO_STATE, FORBIDDEN_CODE) - else -> handleUnsuccessfulResponse(httpResponse) + else -> interceptUnsuccessfulResponse(httpResponse) } } }.getOrElse { unhandledException -> @@ -172,7 +172,7 @@ internal open class NomadDeviceSyncApiV0 internal constructor( ) ) } else { - handleUnsuccessfulResponse(httpResponse) + interceptUnsuccessfulResponse(httpResponse) } } diff --git a/data/network/src/commonMain/kotlin/com/wire/kalium/network/api/v0/authenticated/NotificationApiV0.kt b/data/network/src/commonMain/kotlin/com/wire/kalium/network/api/v0/authenticated/NotificationApiV0.kt index 2eb82b458fae..9eeb81eed764 100644 --- a/data/network/src/commonMain/kotlin/com/wire/kalium/network/api/v0/authenticated/NotificationApiV0.kt +++ b/data/network/src/commonMain/kotlin/com/wire/kalium/network/api/v0/authenticated/NotificationApiV0.kt @@ -28,7 +28,7 @@ import com.wire.kalium.network.api.authenticated.notification.EventResponseToSto import com.wire.kalium.network.api.authenticated.notification.NotificationResponse import com.wire.kalium.network.api.base.authenticated.notification.NotificationApi import com.wire.kalium.network.api.base.authenticated.notification.WebSocketEvent -import com.wire.kalium.network.api.model.ErrorResponse +import com.wire.kalium.network.api.model.GenericAPIErrorResponse import com.wire.kalium.network.api.unbound.configuration.ServerConfigDTO import com.wire.kalium.network.api.v0.authenticated.NotificationApiV0.Hardcoded.NOTIFICATIONS_4O4_ERROR import com.wire.kalium.network.api.v0.authenticated.NotificationApiV0.V0.CLIENT_QUERY_KEY @@ -206,7 +206,7 @@ internal open class NotificationApiV0 internal constructor( } internal object Hardcoded { - val NOTIFICATIONS_4O4_ERROR = ErrorResponse( + val NOTIFICATIONS_4O4_ERROR = GenericAPIErrorResponse( code = HttpStatusCode.NotFound.value, message = "Event or client not found", label = "missing_events_or_client_(hardcoded_v0_response)" diff --git a/data/network/src/commonMain/kotlin/com/wire/kalium/network/api/v5/authenticated/E2EIApiV5.kt b/data/network/src/commonMain/kotlin/com/wire/kalium/network/api/v5/authenticated/E2EIApiV5.kt index 06dec41892f8..f3765530250c 100644 --- a/data/network/src/commonMain/kotlin/com/wire/kalium/network/api/v5/authenticated/E2EIApiV5.kt +++ b/data/network/src/commonMain/kotlin/com/wire/kalium/network/api/v5/authenticated/E2EIApiV5.kt @@ -23,7 +23,7 @@ import com.wire.kalium.network.api.v4.authenticated.E2EIApiV4 import com.wire.kalium.network.serialization.JoseJson import com.wire.kalium.network.utils.CustomErrors import com.wire.kalium.network.utils.NetworkResponse -import com.wire.kalium.network.utils.handleUnsuccessfulResponse +import com.wire.kalium.network.utils.interceptUnsuccessfulResponse import com.wire.kalium.network.utils.wrapKaliumResponse import io.ktor.client.request.post import io.ktor.client.request.prepareHead @@ -53,7 +53,7 @@ internal open class E2EIApiV5 internal constructor( CustomErrors.MISSING_NONCE } else { - handleUnsuccessfulResponse(httpResponse) + interceptUnsuccessfulResponse(httpResponse) } override suspend fun getAccessToken(clientId: String, dpopToken: String): NetworkResponse = wrapKaliumResponse { diff --git a/data/network/src/commonMain/kotlin/com/wire/kalium/network/exceptions/KaliumException.kt b/data/network/src/commonMain/kotlin/com/wire/kalium/network/exceptions/KaliumException.kt index a60751b9073c..2bda90c193d4 100644 --- a/data/network/src/commonMain/kotlin/com/wire/kalium/network/exceptions/KaliumException.kt +++ b/data/network/src/commonMain/kotlin/com/wire/kalium/network/exceptions/KaliumException.kt @@ -23,8 +23,8 @@ package com.wire.kalium.network.exceptions import com.wire.kalium.network.NetworkState import com.wire.kalium.network.api.authenticated.message.QualifiedSendMessageResponse import com.wire.kalium.network.api.authenticated.message.SendMessageResponse -import com.wire.kalium.network.api.model.ErrorResponse import com.wire.kalium.network.api.model.FederationErrorResponse +import com.wire.kalium.network.api.model.GenericAPIErrorResponse import com.wire.kalium.network.api.model.MLSErrorResponse import com.wire.kalium.network.exceptions.NetworkErrorLabel.ACCESS_DENIED import com.wire.kalium.network.exceptions.NetworkErrorLabel.ACCOUNT_PENDING_ACTIVATION @@ -66,17 +66,17 @@ sealed class KaliumException : Exception() { /** * http error 300 .. 399 */ - data class RedirectError(val errorResponse: ErrorResponse) : KaliumException() + data class RedirectError(val errorResponse: GenericAPIErrorResponse) : KaliumException() /** * http error 400 .. 499 */ - data class InvalidRequestError(val errorResponse: ErrorResponse) : KaliumException() + data class InvalidRequestError(val errorResponse: GenericAPIErrorResponse) : KaliumException() /** * http error 500 .. 599 */ - data class ServerError(val errorResponse: ErrorResponse) : KaliumException() + data class ServerError(val errorResponse: GenericAPIErrorResponse) : KaliumException() /** * Generic errors e.g. Serialization errors diff --git a/data/network/src/commonMain/kotlin/com/wire/kalium/network/utils/CustomErrors.kt b/data/network/src/commonMain/kotlin/com/wire/kalium/network/utils/CustomErrors.kt index 120573aba467..92bf675297c3 100644 --- a/data/network/src/commonMain/kotlin/com/wire/kalium/network/utils/CustomErrors.kt +++ b/data/network/src/commonMain/kotlin/com/wire/kalium/network/utils/CustomErrors.kt @@ -18,7 +18,7 @@ package com.wire.kalium.network.utils -import com.wire.kalium.network.api.model.ErrorResponse +import com.wire.kalium.network.api.model.GenericAPIErrorResponse import com.wire.kalium.network.exceptions.KaliumException import com.wire.kalium.network.exceptions.NetworkErrorLabel @@ -31,7 +31,7 @@ object CustomErrors { val MISSING_REFRESH_TOKEN = NetworkResponse.Error( KaliumException.ServerError( - ErrorResponse( + GenericAPIErrorResponse( MISSING_REFRESH_TOKEN_CODE, "no cookie was found", NetworkErrorLabel.KaliumCustom.MISSING_REFRESH_TOKEN @@ -42,7 +42,7 @@ object CustomErrors { val MISSING_NONCE = NetworkResponse.Error( KaliumException.ServerError( - ErrorResponse( + GenericAPIErrorResponse( MISSING_NONCE_CODE, "no nonce found", NetworkErrorLabel.KaliumCustom.MISSING_NONCE @@ -53,7 +53,7 @@ object CustomErrors { val MISSING_CHALLENGE = NetworkResponse.Error( KaliumException.ServerError( - ErrorResponse( + GenericAPIErrorResponse( MISSING_CHALLENGE_TYPE, "no challenge type found", NetworkErrorLabel.KaliumCustom.MISSING_CHALLENGE_TYPE diff --git a/data/network/src/commonMain/kotlin/com/wire/kalium/network/utils/HttpResponseHandler.kt b/data/network/src/commonMain/kotlin/com/wire/kalium/network/utils/HttpResponseHandler.kt index 8fbc39f759bc..adf4c5ef4ff6 100644 --- a/data/network/src/commonMain/kotlin/com/wire/kalium/network/utils/HttpResponseHandler.kt +++ b/data/network/src/commonMain/kotlin/com/wire/kalium/network/utils/HttpResponseHandler.kt @@ -23,6 +23,8 @@ import com.wire.kalium.network.exceptions.FederationError import com.wire.kalium.network.exceptions.KaliumException import com.wire.kalium.network.kaliumLogger import com.wire.kalium.network.tools.KtxSerializer +import io.ktor.client.call.DoubleReceiveException +import io.ktor.client.call.NoTransformationFoundException import io.ktor.client.call.body import io.ktor.client.statement.HttpResponse import io.ktor.client.statement.bodyAsText @@ -168,6 +170,29 @@ internal object BaseErrorResponseInterceptor : ErrorResponseInterceptor { } } +internal suspend fun interceptUnsuccessfulResponse( + result: HttpResponse, + json: Json = KtxSerializer.json +): NetworkResponse.Error { + val bodyText = try { + result.bodyAsText() + } catch (_: NoTransformationFoundException) { + "NoTransformationFoundException" + } catch (_: DoubleReceiveException) { + "DoubleReceiveException" + } + + val responseData = HttpResponseData( + headers = result.headers, + statusCode = result.status, + body = bodyText, + json = json, + ) + + return UnauthorizedResponseInterceptor.intercept(responseData) + ?: BaseErrorResponseInterceptor.intercept(responseData) +} + /** * An interceptor responsible for handling federation-related error responses in an HTTP call. * diff --git a/data/network/src/commonMain/kotlin/com/wire/kalium/network/utils/NetworkUtils.kt b/data/network/src/commonMain/kotlin/com/wire/kalium/network/utils/NetworkUtils.kt index 8744de37363e..713e6927f91e 100644 --- a/data/network/src/commonMain/kotlin/com/wire/kalium/network/utils/NetworkUtils.kt +++ b/data/network/src/commonMain/kotlin/com/wire/kalium/network/utils/NetworkUtils.kt @@ -19,7 +19,7 @@ package com.wire.kalium.network.utils -import com.wire.kalium.network.api.model.ErrorResponse +import com.wire.kalium.network.api.model.GenericAPIErrorResponse import com.wire.kalium.network.exceptions.KaliumException import com.wire.kalium.network.kaliumLogger import com.wire.kalium.network.tools.KtxSerializer @@ -150,7 +150,7 @@ internal inline fun NetworkResponse.onSuccess( * a [NetworkResponse.Success] with the expected [BodyType] will be returned. * * - Unsuccessful response (any other HTTP Status Code): - * a [NetworkResponse.Error] with a [KaliumException]. Will try to read it the body as an [ErrorResponse]. + * a [NetworkResponse.Error] with a [KaliumException]. Will try to read it the body as a [GenericAPIErrorResponse]. * It's possible to intercept this and do the mapping to a [NetworkResponse] through [unsuccessfulResponseOverride]. * * - Exceptions failure to reach server or parse response: @@ -161,7 +161,7 @@ internal inline fun NetworkResponse.onSuccess( * Useful when handling custom ErrorBody, for example. * @param performRequest the block that will result into the [HttpResponse] * @see KaliumException - * @see ErrorResponse + * @see GenericAPIErrorResponse */ @Deprecated( message = "This is being renamed to wrapRequest! Use it instead", @@ -194,10 +194,10 @@ internal suspend fun handleUnsuccessfulResponse( } val errorResponse = try { - KtxSerializer.json.decodeFromString(bodyText) + KtxSerializer.json.decodeFromString(bodyText) } catch (_: IllegalArgumentException) { // When the backend returns something that is not a JSON for whatever reason. - ErrorResponse(code = status.value, label = status.description, message = bodyText) + GenericAPIErrorResponse(code = status.value, label = status.description, message = bodyText) } val kException = toStatusCodeBasedKaliumException(status, result, errorResponse) @@ -207,7 +207,7 @@ internal suspend fun handleUnsuccessfulResponse( private fun toStatusCodeBasedKaliumException( status: HttpStatusCode, result: HttpResponse, - errorResponse: ErrorResponse + errorResponse: GenericAPIErrorResponse ): KaliumException { val kException = when (status.value) { HttpStatusCode.Unauthorized.value -> {