diff --git a/library/src/main/java/com/chuckerteam/chucker/api/ChuckerInterceptor.kt b/library/src/main/java/com/chuckerteam/chucker/api/ChuckerInterceptor.kt index b33b2fc54..61ec4ff48 100755 --- a/library/src/main/java/com/chuckerteam/chucker/api/ChuckerInterceptor.kt +++ b/library/src/main/java/com/chuckerteam/chucker/api/ChuckerInterceptor.kt @@ -1,21 +1,18 @@ package com.chuckerteam.chucker.api import android.content.Context -import android.util.Log -import com.chuckerteam.chucker.api.Chucker.LOG_TAG import com.chuckerteam.chucker.internal.data.entity.HttpTransaction import com.chuckerteam.chucker.internal.support.IOUtils import com.chuckerteam.chucker.internal.support.hasBody import java.io.IOException import java.nio.charset.Charset -import java.nio.charset.UnsupportedCharsetException import okhttp3.Headers import okhttp3.Interceptor import okhttp3.Request import okhttp3.Response import okhttp3.ResponseBody import okio.Buffer -import okio.BufferedSource +import okio.GzipSource /** * An OkHttp Interceptor which persists and displays HTTP activity @@ -108,7 +105,7 @@ class ChuckerInterceptor @JvmOverloads constructor( * Processes a [Response] and populates corresponding fields of a [HttpTransaction]. */ private fun processResponse(response: Response, transaction: HttpTransaction) { - val responseBody = response.body() + val responseBody = response.body()!! val responseEncodingIsSupported = io.bodyHasSupportedEncoding(response.headers().get(CONTENT_ENCODING)) transaction.apply { @@ -123,8 +120,8 @@ class ChuckerInterceptor @JvmOverloads constructor( responseCode = response.code() responseMessage = response.message() - responseContentType = responseBody?.contentType()?.toString() - responseContentLength = responseBody?.contentLength() ?: 0L + responseContentType = responseBody.contentType()?.toString() + responseContentLength = responseBody.contentLength() tookMs = (response.receivedResponseAtMillis() - response.sentRequestAtMillis()) } @@ -137,32 +134,36 @@ class ChuckerInterceptor @JvmOverloads constructor( /** * Processes a [ResponseBody] and populates corresponding fields of a [HttpTransaction]. */ - private fun processResponseBody(response: Response, responseBody: ResponseBody?, transaction: HttpTransaction) { - getNativeSource(response)?.use { source -> - source.request(java.lang.Long.MAX_VALUE) - val buffer = source.buffer() - var charset: Charset = UTF8 - val contentType = responseBody?.contentType() - if (contentType != null) { - try { - charset = contentType.charset(UTF8) ?: UTF8 - } catch (e: UnsupportedCharsetException) { - return - } + private fun processResponseBody(response: Response, responseBody: ResponseBody, transaction: HttpTransaction) { + val contentType = responseBody.contentType() + val charset: Charset = contentType?.charset(UTF8) ?: UTF8 + val contentLength = responseBody.contentLength() + + val source = responseBody.source() + source.request(Long.MAX_VALUE) // Buffer the entire body. + var buffer = source.buffer() + + if (io.bodyIsGzipped(response.headers()[CONTENT_ENCODING])) { + GzipSource(buffer.clone()).use { gzippedResponseBody -> + buffer = Buffer() + buffer.writeAll(gzippedResponseBody) } - if (io.isPlaintext(buffer)) { - val content = io.readFromBuffer(buffer, charset, maxContentLength) - transaction.responseBody = content - } else { - transaction.isResponseBodyPlainText = false + } + + if (io.isPlaintext(buffer)) { + transaction.isResponseBodyPlainText = true + if (contentLength != 0L) { + transaction.responseBody = buffer.clone().readString(charset) + } + } else { + transaction.isResponseBodyPlainText = false - val isImageContentType = (transaction.responseContentType?.contains(CONTENT_TYPE_IMAGE) == true) + val isImageContentType = + (contentType?.toString()?.contains(CONTENT_TYPE_IMAGE, ignoreCase = true) == true) - if (isImageContentType && buffer.size() < MAX_BLOB_SIZE) { - transaction.responseImageData = buffer.readByteArray() - } + if (isImageContentType && buffer.size() < MAX_BLOB_SIZE) { + transaction.responseImageData = buffer.readByteArray() } - transaction.responseContentLength = buffer.size() } } @@ -177,24 +178,6 @@ class ChuckerInterceptor @JvmOverloads constructor( return builder.build() } - /** - * Returns a [BufferedSource] of a [Response] and UnGzip it if necessary. - */ - @Throws(IOException::class) - private fun getNativeSource(response: Response): BufferedSource? { - if (io.bodyIsGzipped(response.headers().get(CONTENT_ENCODING))) { - val source = response.peekBody(maxContentLength).source() - if (source.buffer().size() < maxContentLength) { - return io.getNativeSource(source, true) - } else { - Log.w(LOG_TAG, "gzip encoded response was too long") - } - } - // Let's clone the response Buffer in order to don't cause an IllegalStateException: closed - // if others interceptors are manipulating the body (see #192). - return response.body()?.source()?.buffer()?.clone() - } - companion object { private val UTF8 = Charset.forName("UTF-8") diff --git a/library/src/main/java/com/chuckerteam/chucker/internal/support/IOUtils.kt b/library/src/main/java/com/chuckerteam/chucker/internal/support/IOUtils.kt index 50d947b88..b5422d3b8 100644 --- a/library/src/main/java/com/chuckerteam/chucker/internal/support/IOUtils.kt +++ b/library/src/main/java/com/chuckerteam/chucker/internal/support/IOUtils.kt @@ -67,5 +67,9 @@ internal class IOUtils(private val context: Context) { contentEncoding.equals("identity", ignoreCase = true) || contentEncoding.equals("gzip", ignoreCase = true) - fun bodyIsGzipped(contentEncoding: String?) = contentEncoding?.equals("gzip", ignoreCase = true) ?: false + fun bodyIsGzipped(contentEncoding: String?) = CONTENT_ENCODING_GZIP.equals(contentEncoding, ignoreCase = true) + + private companion object { + const val CONTENT_ENCODING_GZIP = "gzip" + } } diff --git a/sample/src/main/java/com/chuckerteam/chucker/sample/HttpBinClient.kt b/sample/src/main/java/com/chuckerteam/chucker/sample/HttpBinClient.kt index e727dd226..63928e81e 100644 --- a/sample/src/main/java/com/chuckerteam/chucker/sample/HttpBinClient.kt +++ b/sample/src/main/java/com/chuckerteam/chucker/sample/HttpBinClient.kt @@ -36,8 +36,8 @@ class HttpBinClient( private val httpClient = OkHttpClient.Builder() // Add a ChuckerInterceptor instance to your OkHttp client - .addInterceptor(chuckerInterceptor) .addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)) + .addInterceptor(chuckerInterceptor) .build() private val api: HttpBinApi by lazy {