From 7fc53fb0524483b88e28450e736d8f378d65fe97 Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Fri, 24 Jan 2020 22:43:17 +0100 Subject: [PATCH 1/6] Rewrite the processResponseBody method as it's broken I've rewrote the processResponseBody following the body of HttpLoggingInterceptor. --- .../chucker/api/ChuckerInterceptor.kt | 72 ++++++++----------- .../main/res/xml/network_security_config.xml | 9 +++ 2 files changed, 38 insertions(+), 43 deletions(-) create mode 100644 sample/src/main/res/xml/network_security_config.xml 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..1f663cc62 100755 --- a/library/src/main/java/com/chuckerteam/chucker/api/ChuckerInterceptor.kt +++ b/library/src/main/java/com/chuckerteam/chucker/api/ChuckerInterceptor.kt @@ -16,6 +16,7 @@ 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 +109,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 +124,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 +138,34 @@ 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 (CONTENT_ENCODING_GZIP.equals(response.headers()[CONTENT_ENCODING], ignoreCase = true)) { + 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 + } - val isImageContentType = (transaction.responseContentType?.contains(CONTENT_TYPE_IMAGE) == true) + if (io.isPlaintext(buffer)) { + transaction.isResponseBodyPlainText = true + if (contentLength != 0L) { + transaction.responseBody = buffer.clone().readString(charset) + } + } else { + transaction.isResponseBodyPlainText = false - if (isImageContentType && buffer.size() < MAX_BLOB_SIZE) { - transaction.responseImageData = buffer.readByteArray() - } + val isImageContentType = (transaction.responseContentType?.contains(CONTENT_TYPE_IMAGE) == true) + if (isImageContentType && buffer.size() < MAX_BLOB_SIZE) { + transaction.responseImageData = buffer.readByteArray() } - transaction.responseContentLength = buffer.size() } } @@ -177,29 +180,12 @@ 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") private const val MAX_BLOB_SIZE = 1000_000L + private const val CONTENT_ENCODING_GZIP = "gzip" private const val CONTENT_TYPE_IMAGE = "image" private const val CONTENT_ENCODING = "Content-Encoding" } diff --git a/sample/src/main/res/xml/network_security_config.xml b/sample/src/main/res/xml/network_security_config.xml new file mode 100644 index 000000000..e1eb89e53 --- /dev/null +++ b/sample/src/main/res/xml/network_security_config.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file From 1bd062c02155abf048c58170552fe2b03109001d Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Fri, 24 Jan 2020 23:24:39 +0100 Subject: [PATCH 2/6] KtLint fix --- .../java/com/chuckerteam/chucker/api/ChuckerInterceptor.kt | 4 ---- 1 file changed, 4 deletions(-) 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 1f663cc62..5657a33ff 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,17 @@ 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 /** From 6ba80619051380811bc0712ef7991cb200f5398d Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Sat, 25 Jan 2020 01:39:16 +0100 Subject: [PATCH 3/6] Make sure ChuckerInterceptor is after the LoggingInterceptor --- .../main/java/com/chuckerteam/chucker/sample/HttpBinClient.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 { From a9b83c1aaa0bb245bba2c50de06453daaf9a733e Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Sat, 25 Jan 2020 16:02:08 +0100 Subject: [PATCH 4/6] Applying @vbuberen suggestions --- .../com/chuckerteam/chucker/api/ChuckerInterceptor.kt | 3 +-- .../com/chuckerteam/chucker/internal/support/IOUtils.kt | 6 +++++- sample/src/main/res/xml/network_security_config.xml | 9 --------- 3 files changed, 6 insertions(+), 12 deletions(-) delete mode 100644 sample/src/main/res/xml/network_security_config.xml 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 5657a33ff..b35a5d72d 100755 --- a/library/src/main/java/com/chuckerteam/chucker/api/ChuckerInterceptor.kt +++ b/library/src/main/java/com/chuckerteam/chucker/api/ChuckerInterceptor.kt @@ -143,7 +143,7 @@ class ChuckerInterceptor @JvmOverloads constructor( source.request(Long.MAX_VALUE) // Buffer the entire body. var buffer = source.buffer() - if (CONTENT_ENCODING_GZIP.equals(response.headers()[CONTENT_ENCODING], ignoreCase = true)) { + if (io.bodyIsGzipped(response.headers()[CONTENT_ENCODING])) { GzipSource(buffer.clone()).use { gzippedResponseBody -> buffer = Buffer() buffer.writeAll(gzippedResponseBody) @@ -181,7 +181,6 @@ class ChuckerInterceptor @JvmOverloads constructor( private const val MAX_BLOB_SIZE = 1000_000L - private const val CONTENT_ENCODING_GZIP = "gzip" private const val CONTENT_TYPE_IMAGE = "image" private const val CONTENT_ENCODING = "Content-Encoding" } 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/res/xml/network_security_config.xml b/sample/src/main/res/xml/network_security_config.xml deleted file mode 100644 index e1eb89e53..000000000 --- a/sample/src/main/res/xml/network_security_config.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file From db8a9cc0da55634914ac8a10646c44f8804051fc Mon Sep 17 00:00:00 2001 From: Volodymyr Buberenko Date: Sat, 25 Jan 2020 19:16:26 +0200 Subject: [PATCH 5/6] Make check for image content type case-insensitive --- .../main/java/com/chuckerteam/chucker/api/ChuckerInterceptor.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 b35a5d72d..b21c21626 100755 --- a/library/src/main/java/com/chuckerteam/chucker/api/ChuckerInterceptor.kt +++ b/library/src/main/java/com/chuckerteam/chucker/api/ChuckerInterceptor.kt @@ -158,7 +158,7 @@ class ChuckerInterceptor @JvmOverloads constructor( } else { transaction.isResponseBodyPlainText = false - val isImageContentType = (transaction.responseContentType?.contains(CONTENT_TYPE_IMAGE) == true) + val isImageContentType = (transaction.responseContentType?.contains(CONTENT_TYPE_IMAGE, ignoreCase = true) == true) if (isImageContentType && buffer.size() < MAX_BLOB_SIZE) { transaction.responseImageData = buffer.readByteArray() } From 808c6b6d27c2a310119a8a6db2adb53e08040d1f Mon Sep 17 00:00:00 2001 From: Volodymyr Buberenko Date: Sat, 25 Jan 2020 19:27:20 +0200 Subject: [PATCH 6/6] Fix detekt issue --- .../java/com/chuckerteam/chucker/api/ChuckerInterceptor.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 b21c21626..61ec4ff48 100755 --- a/library/src/main/java/com/chuckerteam/chucker/api/ChuckerInterceptor.kt +++ b/library/src/main/java/com/chuckerteam/chucker/api/ChuckerInterceptor.kt @@ -158,7 +158,9 @@ class ChuckerInterceptor @JvmOverloads constructor( } else { transaction.isResponseBodyPlainText = false - val isImageContentType = (transaction.responseContentType?.contains(CONTENT_TYPE_IMAGE, ignoreCase = true) == true) + val isImageContentType = + (contentType?.toString()?.contains(CONTENT_TYPE_IMAGE, ignoreCase = true) == true) + if (isImageContentType && buffer.size() < MAX_BLOB_SIZE) { transaction.responseImageData = buffer.readByteArray() }