|
| 1 | +package com.braintreepayments.api.sharedutils |
| 2 | + |
| 3 | +import androidx.annotation.RestrictTo |
| 4 | +import java.io.ByteArrayOutputStream |
| 5 | +import java.io.IOException |
| 6 | +import java.io.InputStream |
| 7 | +import java.net.HttpURLConnection |
| 8 | +import java.net.HttpURLConnection.HTTP_ACCEPTED |
| 9 | +import java.net.HttpURLConnection.HTTP_BAD_REQUEST |
| 10 | +import java.net.HttpURLConnection.HTTP_CREATED |
| 11 | +import java.net.HttpURLConnection.HTTP_FORBIDDEN |
| 12 | +import java.net.HttpURLConnection.HTTP_INTERNAL_ERROR |
| 13 | +import java.net.HttpURLConnection.HTTP_OK |
| 14 | +import java.net.HttpURLConnection.HTTP_UNAUTHORIZED |
| 15 | +import java.net.HttpURLConnection.HTTP_UNAVAILABLE |
| 16 | +import java.nio.charset.StandardCharsets |
| 17 | +import java.util.zip.GZIPInputStream |
| 18 | + |
| 19 | +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) |
| 20 | +class BaseHttpResponseParser : HttpResponseParser { |
| 21 | + |
| 22 | + companion object { |
| 23 | + private const val HTTP_UNPROCESSABLE_ENTITY = 422 |
| 24 | + private const val HTTP_UPGRADE_REQUIRED = 426 |
| 25 | + private const val HTTP_TOO_MANY_REQUESTS = 429 |
| 26 | + |
| 27 | + private const val BYTE_ARRAY_SIZE = 1024 |
| 28 | + } |
| 29 | + |
| 30 | + @Throws(Exception::class) |
| 31 | + override fun parse(responseCode: Int, connection: HttpURLConnection): String { |
| 32 | + val responseBody = parseBody(responseCode, connection) ?: "empty responseBody" |
| 33 | + return when (responseCode) { |
| 34 | + HTTP_OK, HTTP_CREATED, HTTP_ACCEPTED -> responseBody |
| 35 | + HTTP_BAD_REQUEST, HTTP_UNPROCESSABLE_ENTITY -> throw UnprocessableEntityException(responseBody) |
| 36 | + HTTP_UNAUTHORIZED -> throw AuthenticationException(responseBody) |
| 37 | + HTTP_FORBIDDEN -> throw AuthorizationException(responseBody) |
| 38 | + HTTP_UPGRADE_REQUIRED -> throw UpgradeRequiredException(responseBody) |
| 39 | + HTTP_TOO_MANY_REQUESTS -> { |
| 40 | + throw RateLimitException("You are being rate-limited. Please try again in a few minutes.") |
| 41 | + } |
| 42 | + HTTP_INTERNAL_ERROR -> throw ServerException(responseBody) |
| 43 | + HTTP_UNAVAILABLE -> throw ServiceUnavailableException(responseBody) |
| 44 | + else -> throw UnexpectedException(responseBody) |
| 45 | + } |
| 46 | + } |
| 47 | + |
| 48 | + @Throws(IOException::class) |
| 49 | + private fun parseBody(responseCode: Int, connection: HttpURLConnection): String? { |
| 50 | + val gzip = "gzip" == connection.contentEncoding |
| 51 | + return when (responseCode) { |
| 52 | + HTTP_OK, HTTP_CREATED, HTTP_ACCEPTED -> readStream(connection.inputStream, gzip) |
| 53 | + HTTP_TOO_MANY_REQUESTS -> null |
| 54 | + HTTP_UNAUTHORIZED, |
| 55 | + HTTP_FORBIDDEN, |
| 56 | + HTTP_BAD_REQUEST, |
| 57 | + HTTP_UNPROCESSABLE_ENTITY, |
| 58 | + HTTP_UPGRADE_REQUIRED, |
| 59 | + HTTP_INTERNAL_ERROR, |
| 60 | + HTTP_UNAVAILABLE -> readStream(connection.errorStream, gzip) |
| 61 | + else -> readStream(connection.errorStream, gzip) |
| 62 | + } |
| 63 | + } |
| 64 | + |
| 65 | + @Throws(IOException::class) |
| 66 | + private fun readStream(inputStream: InputStream?, gzip: Boolean): String? { |
| 67 | + if (inputStream == null) { |
| 68 | + return null |
| 69 | + } |
| 70 | + |
| 71 | + val out = ByteArrayOutputStream() |
| 72 | + var stream = inputStream |
| 73 | + try { |
| 74 | + if (gzip) { |
| 75 | + stream = GZIPInputStream(inputStream) |
| 76 | + } |
| 77 | + |
| 78 | + val buffer = ByteArray(BYTE_ARRAY_SIZE) |
| 79 | + var count: Int |
| 80 | + while (stream.read(buffer).also { count = it } != -1) { |
| 81 | + out.write(buffer, 0, count) |
| 82 | + } |
| 83 | + return String(out.toByteArray(), StandardCharsets.UTF_8) |
| 84 | + } finally { |
| 85 | + try { |
| 86 | + inputStream.close() |
| 87 | + } catch (_: IOException) { |
| 88 | + } |
| 89 | + } |
| 90 | + } |
| 91 | +} |
0 commit comments