All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.pandulapeter.beagle.logOkHttp.OkHttpInterceptor.kt Maven / Gradle / Ivy

Go to download

A smart, reliable, and highly customizable debug menu library for Android apps that supports screen recording, network activity logging, and many other useful features.

The newest version!
package com.pandulapeter.beagle.logOkHttp

import okhttp3.Headers
import okhttp3.Interceptor
import okhttp3.Response
import okhttp3.ResponseBody.Companion.toResponseBody
import okio.Buffer
import java.io.EOFException
import java.nio.charset.Charset
import java.util.concurrent.TimeUnit

/**
 * Modified version of https://github.com/square/okhttp/tree/master/okhttp-logging-interceptor
 */
internal class OkHttpInterceptor : Interceptor {

    private val utf8 = Charset.forName("UTF-8")

    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        val requestBody = request.body
        val requestJson = if (requestBody == null) {
            "No payload"
        } else if (bodyHasUnknownEncoding(request.headers)) {
            "Encoded payload"
        } else if (requestBody.contentLength() > MAX_STRING_LENGTH) {
            "Payload too large"
        } else {
            val buffer = Buffer()
            requestBody.writeTo(buffer)
            var charset: Charset? = utf8
            val contentType = requestBody.contentType()
            if (contentType != null) {
                charset = contentType.charset(utf8)
            }
            if (isPlaintext(buffer)) {
                charset?.let { buffer.readString(it) } ?: ""
            } else {
                "Binary payload, ${requestBody.contentLength()}-byte body"
            }
        }
        BeagleOkHttpLogger.logNetwork(
            isOutgoing = true,
            url = "[${request.method}] ${request.url}",
            payload = requestJson,
            headers = request.headers.map { "[${it.first}] ${it.second}" }
        )
        val startNs = System.nanoTime()
        val response: Response
        try {
            response = chain.proceed(request)
        } catch (exception: Exception) {
            BeagleOkHttpLogger.logNetwork(
                isOutgoing = false,
                url = "[${request.method}] FAIL ${request.url}",
                payload = exception.message ?: "HTTP Failed"
            )
            throw exception
        }
        val tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs)
        val responseBody = response.body
        val contentType = responseBody?.contentType()
        val responseJson = if ((contentType == null || contentType.subtype == "json") && responseBody?.source()?.buffer?.isProbablyUtf8() == true) response.body?.string() else null
        BeagleOkHttpLogger.logNetwork(
            isOutgoing = false,
            url = "[${request.method}] ${response.code} ${request.url}",
            payload = responseJson ?: response.message,
            headers = response.headers.map { "[${it.first}] ${it.second}" },
            duration = tookMs
        )
        return response.newBuilder().body(responseJson?.toResponseBody(responseBody?.contentType()) ?: responseBody).build()
    }

    private fun isPlaintext(buffer: Buffer): Boolean {
        try {
            val prefix = Buffer()
            val byteCount = if (buffer.size < 64) buffer.size else 64
            buffer.copyTo(prefix, 0, byteCount)
            for (i in 0..15) {
                if (prefix.exhausted()) {
                    break
                }
                val codePoint = prefix.readUtf8CodePoint()
                if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) {
                    return false
                }
            }
            return true
        } catch (e: EOFException) {
            return false
        }
    }

    private fun bodyHasUnknownEncoding(headers: Headers) = headers["Content-Encoding"].let { contentEncoding ->
        (contentEncoding != null
                && !contentEncoding.equals("identity", ignoreCase = true)
                && !contentEncoding.equals("gzip", ignoreCase = true))
    }

    private fun Buffer.isProbablyUtf8(): Boolean {
        try {
            val prefix = Buffer()
            val byteCount = size.coerceAtMost(64)
            copyTo(prefix, 0, byteCount)
            for (i in 0 until 16) {
                if (prefix.exhausted()) {
                    break
                }
                val codePoint = prefix.readUtf8CodePoint()
                if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) {
                    return false
                }
            }
            return true
        } catch (_: EOFException) {
            return false
        }
    }

    companion object {
        private const val MAX_STRING_LENGTH = 4096L
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy