From 5446ba6f23bf9e9df136aba9b80a0bccd26d9721 Mon Sep 17 00:00:00 2001 From: Hanson Ho Date: Fri, 27 Oct 2023 15:48:29 -0700 Subject: [PATCH] Only rebuild response in OkHttp interceptor if we modify the body --- .../EmbraceOkHttp3NetworkInterceptor.kt | 124 +++++++++++------- 1 file changed, 73 insertions(+), 51 deletions(-) diff --git a/embrace-android-okhttp3/src/main/java/io/embrace/android/embracesdk/okhttp3/EmbraceOkHttp3NetworkInterceptor.kt b/embrace-android-okhttp3/src/main/java/io/embrace/android/embracesdk/okhttp3/EmbraceOkHttp3NetworkInterceptor.kt index efb455423c..1aeec2981c 100644 --- a/embrace-android-okhttp3/src/main/java/io/embrace/android/embracesdk/okhttp3/EmbraceOkHttp3NetworkInterceptor.kt +++ b/embrace-android-okhttp3/src/main/java/io/embrace/android/embracesdk/okhttp3/EmbraceOkHttp3NetworkInterceptor.kt @@ -40,14 +40,14 @@ public class EmbraceOkHttp3NetworkInterceptor internal constructor( ) : Interceptor { public constructor() : this(Embrace.getInstance(), Clock { System.currentTimeMillis() }) - @Suppress("ComplexMethod") @Throws(IOException::class) override fun intercept(chain: Interceptor.Chain): Response { + // If the SDK has not started, don't do anything val originalRequest: Request = chain.request() if (!embrace.isStarted || embrace.internalInterface.isInternalNetworkCaptureDisabled()) { return chain.proceed(originalRequest) } - val offset = sdkClockOffset() + val networkSpanForwardingEnabled = embrace.internalInterface.isNetworkSpanForwardingEnabled() var traceparent: String? = null if (networkSpanForwardingEnabled && originalRequest.header(TRACEPARENT_HEADER_NAME) == null) { @@ -55,66 +55,54 @@ public class EmbraceOkHttp3NetworkInterceptor internal constructor( } val request = if (traceparent == null) originalRequest else originalRequest.newBuilder().header(TRACEPARENT_HEADER_NAME, traceparent).build() + + // Send request along the change val networkResponse: Response = chain.proceed(request) - val responseBuilder: Response.Builder = networkResponse.newBuilder().request(request) - var contentLength: Long? = null - // Try to get the content length from the header - val contentLengthHeaderValue = networkResponse.header(CONTENT_LENGTH_HEADER_NAME) - if (contentLengthHeaderValue != null) { - try { - contentLength = contentLengthHeaderValue.toLong() - } catch (ex: Exception) { - // Ignore - } - } - // If we get the body for a server-sent events stream, then we will wait forever - val contentType = networkResponse.header(CONTENT_TYPE_HEADER_NAME) + // Get response and determine the size of the body + var contentLength: Long? = getContentLengthFromHeader(networkResponse) - // Tolerant of a charset specified in header, - // e.g. Content-Type: text/event-stream;charset=UTF-8 - val serverSentEvent = contentType != null && contentType.startsWith(CONTENT_TYPE_EVENT_STREAM) - if (!serverSentEvent && contentLength == null) { - try { - val body = networkResponse.body - if (body != null) { - val source = body.source() - source.request(Long.MAX_VALUE) - contentLength = source.buffer.size - } - } catch (ex: Exception) { - // Ignore - } + if (contentLength == null) { + // If we get the body for a server-sent events stream, then we will wait forever + contentLength = getContentLengthFromBody(networkResponse, networkResponse.header(CONTENT_TYPE_HEADER_NAME)) } + if (contentLength == null) { - // Otherwise default to zero + // Set the content length to 0 if we can't determine it contentLength = 0L } - val shouldCaptureNetworkData = embrace.internalInterface.shouldCaptureNetworkBody(request.url.toString(), request.method) - if (shouldCaptureNetworkData && - ENCODING_GZIP.equals(networkResponse.header(CONTENT_ENCODING_HEADER_NAME), ignoreCase = true) && - networkResponse.promisesBody() - ) { - val body = networkResponse.body - if (body != null) { - val strippedHeaders = networkResponse.headers.newBuilder() - .removeAll(CONTENT_ENCODING_HEADER_NAME) - .removeAll(CONTENT_LENGTH_HEADER_NAME) - .build() - val realResponseBody = RealResponseBody( - contentType, - -1L, - GzipSource(body.source()).buffer() - ) - responseBuilder.headers(strippedHeaders) - responseBuilder.body(realResponseBody) - } - } - val response: Response = responseBuilder.build() + + var response: Response = networkResponse var networkCaptureData: NetworkCaptureData? = null + val shouldCaptureNetworkData = embrace.internalInterface.shouldCaptureNetworkBody(request.url.toString(), request.method) + + // If we need to capture the network response body, if (shouldCaptureNetworkData) { + if (ENCODING_GZIP.equals(networkResponse.header(CONTENT_ENCODING_HEADER_NAME), ignoreCase = true) && + networkResponse.promisesBody() + ) { + val body = networkResponse.body + if (body != null) { + val strippedHeaders = networkResponse.headers.newBuilder() + .removeAll(CONTENT_ENCODING_HEADER_NAME) + .removeAll(CONTENT_LENGTH_HEADER_NAME) + .build() + val realResponseBody = RealResponseBody( + networkResponse.header(CONTENT_TYPE_HEADER_NAME), + -1L, + GzipSource(body.source()).buffer() + ) + val responseBuilder = networkResponse.newBuilder().request(request) + responseBuilder.headers(strippedHeaders) + responseBuilder.body(realResponseBody) + response = responseBuilder.build() + } + } + networkCaptureData = getNetworkCaptureData(request, response) } + + val offset = sdkClockOffset() embrace.recordNetworkRequest( EmbraceNetworkRequest.fromCompletedRequest( EmbraceHttpPathOverride.getURLString(EmbraceOkHttp3PathOverrideRequest(request)), @@ -132,6 +120,40 @@ public class EmbraceOkHttp3NetworkInterceptor internal constructor( return response } + private fun getContentLengthFromHeader(networkResponse: Response): Long? { + var contentLength: Long? = null + val contentLengthHeaderValue = networkResponse.header(CONTENT_LENGTH_HEADER_NAME) + if (contentLengthHeaderValue != null) { + try { + contentLength = contentLengthHeaderValue.toLong() + } catch (ex: Exception) { + // Ignore + } + } + return contentLength + } + + private fun getContentLengthFromBody(networkResponse: Response, contentType: String?): Long? { + var contentLength: Long? = null + + // Tolerant of a charset specified in header, e.g. Content-Type: text/event-stream;charset=UTF-8 + val serverSentEvent = contentType != null && contentType.startsWith(CONTENT_TYPE_EVENT_STREAM) + if (!serverSentEvent) { + try { + val body = networkResponse.body + if (body != null) { + val source = body.source() + source.request(Long.MAX_VALUE) + contentLength = source.buffer.size + } + } catch (ex: Exception) { + // Ignore + } + } + + return contentLength + } + private fun getNetworkCaptureData(request: Request, response: Response): NetworkCaptureData { var requestHeaders: Map? = null var requestQueryParams: String? = null