Skip to content
Snippets Groups Projects
Unverified Commit d6500521 authored by Matthias Urhahn's avatar Matthias Urhahn Committed by GitHub
Browse files

Log http error bodies (DEV) #2407


* Log http error bodies.

* 2048Byte are enough.

Co-authored-by: default avatarchris-cwa <69595386+chris-cwa@users.noreply.github.com>
Co-authored-by: default avatarRalf Gehrer <ralfgehrer@users.noreply.github.com>
parent 97174ce3
No related branches found
No related tags found
No related merge requests found
......@@ -29,54 +29,81 @@ import okhttp3.Response
import timber.log.Timber
import java.net.SocketTimeoutException
import java.net.UnknownHostException
import javax.net.ssl.HttpsURLConnection
import javax.net.ssl.HttpsURLConnection.HTTP_ACCEPTED
import javax.net.ssl.HttpsURLConnection.HTTP_BAD_GATEWAY
import javax.net.ssl.HttpsURLConnection.HTTP_BAD_REQUEST
import javax.net.ssl.HttpsURLConnection.HTTP_CONFLICT
import javax.net.ssl.HttpsURLConnection.HTTP_CREATED
import javax.net.ssl.HttpsURLConnection.HTTP_FORBIDDEN
import javax.net.ssl.HttpsURLConnection.HTTP_GATEWAY_TIMEOUT
import javax.net.ssl.HttpsURLConnection.HTTP_GONE
import javax.net.ssl.HttpsURLConnection.HTTP_INTERNAL_ERROR
import javax.net.ssl.HttpsURLConnection.HTTP_NOT_FOUND
import javax.net.ssl.HttpsURLConnection.HTTP_NOT_IMPLEMENTED
import javax.net.ssl.HttpsURLConnection.HTTP_NO_CONTENT
import javax.net.ssl.HttpsURLConnection.HTTP_OK
import javax.net.ssl.HttpsURLConnection.HTTP_UNAUTHORIZED
import javax.net.ssl.HttpsURLConnection.HTTP_UNAVAILABLE
import javax.net.ssl.HttpsURLConnection.HTTP_UNSUPPORTED_TYPE
import javax.net.ssl.HttpsURLConnection.HTTP_VERSION
class HttpErrorParser : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
try {
val response = chain.proceed(chain.request())
val message: String? = try {
if (response.isSuccessful) {
null
} else {
response.message
}
if (response.isSuccessful) {
return response
}
val statusMessage: String? = try {
response.message
} catch (e: Exception) {
Timber.w("Failed to get http error message.")
Timber.w("Failed to get http status-message.")
null
}
val body: String? = try {
response.peekBody(2048).string()
} catch (e: Exception) {
Timber.w("Failed to get http error body.")
null
}
val errorDetails = "$statusMessage body=$body'"
return when (val code = response.code) {
HttpsURLConnection.HTTP_OK -> response
HttpsURLConnection.HTTP_CREATED -> response
HttpsURLConnection.HTTP_ACCEPTED -> response
HttpsURLConnection.HTTP_NO_CONTENT -> response
HttpsURLConnection.HTTP_BAD_REQUEST -> throw BadRequestException(message)
HttpsURLConnection.HTTP_UNAUTHORIZED -> throw UnauthorizedException(message)
HttpsURLConnection.HTTP_FORBIDDEN -> throw ForbiddenException(message)
HttpsURLConnection.HTTP_NOT_FOUND -> throw NotFoundException(message)
HttpsURLConnection.HTTP_CONFLICT -> throw ConflictException(message)
HttpsURLConnection.HTTP_GONE -> throw GoneException(message)
HttpsURLConnection.HTTP_UNSUPPORTED_TYPE -> throw UnsupportedMediaTypeException(message)
429 -> throw TooManyRequestsException(message)
HttpsURLConnection.HTTP_INTERNAL_ERROR -> throw InternalServerErrorException(message)
HttpsURLConnection.HTTP_NOT_IMPLEMENTED -> throw NotImplementedException(message)
HttpsURLConnection.HTTP_BAD_GATEWAY -> throw BadGatewayException(message)
HttpsURLConnection.HTTP_UNAVAILABLE -> throw ServiceUnavailableException(message)
HttpsURLConnection.HTTP_GATEWAY_TIMEOUT -> throw GatewayTimeoutException(message)
HttpsURLConnection.HTTP_VERSION -> throw HTTPVersionNotSupported(message)
511 -> throw NetworkAuthenticationRequiredException(message)
598 -> throw NetworkReadTimeoutException(message)
599 -> throw NetworkConnectTimeoutException(message)
HTTP_OK -> response
HTTP_CREATED -> response
HTTP_ACCEPTED -> response
HTTP_NO_CONTENT -> response
HTTP_BAD_REQUEST -> throw BadRequestException(errorDetails)
HTTP_UNAUTHORIZED -> throw UnauthorizedException(errorDetails)
HTTP_FORBIDDEN -> throw ForbiddenException(errorDetails)
HTTP_NOT_FOUND -> throw NotFoundException(errorDetails)
HTTP_CONFLICT -> throw ConflictException(errorDetails)
HTTP_GONE -> throw GoneException(errorDetails)
HTTP_UNSUPPORTED_TYPE -> throw UnsupportedMediaTypeException(errorDetails)
429 -> throw TooManyRequestsException(errorDetails)
HTTP_INTERNAL_ERROR -> throw InternalServerErrorException(errorDetails)
HTTP_NOT_IMPLEMENTED -> throw NotImplementedException(errorDetails)
HTTP_BAD_GATEWAY -> throw BadGatewayException(errorDetails)
HTTP_UNAVAILABLE -> throw ServiceUnavailableException(errorDetails)
HTTP_GATEWAY_TIMEOUT -> throw GatewayTimeoutException(errorDetails)
HTTP_VERSION -> throw HTTPVersionNotSupported(errorDetails)
511 -> throw NetworkAuthenticationRequiredException(errorDetails)
598 -> throw NetworkReadTimeoutException(errorDetails)
599 -> throw NetworkConnectTimeoutException(errorDetails)
else -> {
if (code in 100..199) throw CwaInformationalNotSupportedError(code, message)
if (code in 100..199) throw CwaInformationalNotSupportedError(code, errorDetails)
if (code in 200..299) throw CwaSuccessResponseWithCodeMismatchNotSupportedError(
code, message
code,
errorDetails
)
if (code in 300..399) throw CwaRedirectNotSupportedError(code, message)
if (code in 400..499) throw CwaClientError(code, message)
if (code in 500..599) throw CwaServerError(code, message)
throw CwaWebException(code, message)
if (code in 300..399) throw CwaRedirectNotSupportedError(code, errorDetails)
if (code in 400..499) throw CwaClientError(code, errorDetails)
if (code in 500..599) throw CwaServerError(code, errorDetails)
throw CwaWebException(code, errorDetails)
}
}
} catch (err: SocketTimeoutException) {
......
package de.rki.coronawarnapp.http
import de.rki.coronawarnapp.exception.http.CwaWebException
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.matchers.shouldBe
import io.kotest.matchers.string.shouldContain
import io.mockk.MockKAnnotations
import io.mockk.clearAllMocks
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.mockk
import okhttp3.Interceptor
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.Protocol
import okhttp3.Request
import okhttp3.Response
import okhttp3.ResponseBody.Companion.toResponseBody
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import testhelpers.BaseTest
class HttpErrorParserTest : BaseTest() {
@MockK lateinit var chain: Interceptor.Chain
@BeforeEach
fun setup() {
MockKAnnotations.init(this)
every { chain.request() } returns mockk()
}
@AfterEach
fun teardown() {
clearAllMocks()
}
private val baseResponse: Response.Builder
get() = Response.Builder().apply {
protocol(Protocol.HTTP_1_1)
Request.Builder().apply {
url("http://url.url")
}.build().let { request(it) }
}
@Test
fun `normal response`() {
val response = baseResponse.apply {
code(200)
message("")
}.build()
every { chain.proceed(any()) } returns response
HttpErrorParser().intercept(chain) shouldBe response
}
@Test
fun `error without message`() {
val response = baseResponse.apply {
code(404)
message("")
}.build()
every { chain.proceed(any()) } returns response
val exception = shouldThrow<CwaWebException> { HttpErrorParser().intercept(chain) }
exception.statusCode shouldBe 404
exception.message shouldContain "code=404 message= body=null"
}
@Test
fun `error with message`() {
val response = baseResponse.apply {
code(403)
message("Forbidden")
}.build()
every { chain.proceed(any()) } returns response
val exception = shouldThrow<CwaWebException> { HttpErrorParser().intercept(chain) }
exception.statusCode shouldBe 403
exception.message shouldContain "message=Forbidden"
exception.message shouldContain "body=null"
}
@Test
fun `error in body`() {
val response = baseResponse.apply {
code(500)
message("")
body("{\"errorCode\":\"APK_CERTIFICATE_MISMATCH\"}".toResponseBody("application/json".toMediaTypeOrNull()))
}.build()
every { chain.proceed(any()) } returns response
val exception = shouldThrow<CwaWebException> { HttpErrorParser().intercept(chain) }
exception.statusCode shouldBe 500
exception.message shouldContain "message= "
exception.message shouldContain "body={\"errorCode\":\"APK_CERTIFICATE_MISMATCH\"}"
}
@Test
fun `error in message and body`() {
val response = baseResponse.apply {
code(501)
message("Error")
body("{\"errorCode\":\"APK_CERTIFICATE_MISMATCH\"}".toResponseBody("application/json".toMediaTypeOrNull()))
}.build()
every { chain.proceed(any()) } returns response
val exception = shouldThrow<CwaWebException> { HttpErrorParser().intercept(chain) }
exception.statusCode shouldBe 501
exception.message shouldContain "message=Error"
exception.message shouldContain "body={\"errorCode\":\"APK_CERTIFICATE_MISMATCH\"}"
}
@Test
fun `oversized errors are handled`() {
val response = baseResponse.apply {
code(501)
message("")
body(
(1..5000).joinToString { "1" }.toResponseBody()
)
}.build()
every { chain.proceed(any()) } returns response
val exception = shouldThrow<CwaWebException> { HttpErrorParser().intercept(chain) }
exception.statusCode shouldBe 501
val start = exception.message!!.indexOf("body=")
val body = exception.message!!.substring(start + "body=".length)
body.length shouldBe 2049
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment