From 58e68999b26fcaa69fc70910a9f4e3a86a42c4c8 Mon Sep 17 00:00:00 2001 From: Notespeak Date: Wed, 28 May 2025 02:49:20 +0200 Subject: [PATCH] initial commit --- CHANGELOG.md | 8 ++++ .../com/instana/android/core/InstanaConfig.kt | 9 +++- .../okhttp3/OkHttp3GlobalInterceptor.kt | 11 ++++- .../okhttp3/OkHttp3GlobalInterceptorTest.kt | 45 +++++++++++++++++++ 4 files changed, 70 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e6dd738..814cc346 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ Changelog ========== +## Unreleased + +_TBD_ + +- Fixed duplicate network request issue in OkHttp3GlobalInterceptor +- Added `autoRetryOnNetworkException` configuration flag to control automatic retry behavior +- Default behavior now prevents unexpected duplicate requests by disabling automatic retries + ## Version 6.2.1 _2024-03-27_ diff --git a/runtime/src/main/java/com/instana/android/core/InstanaConfig.kt b/runtime/src/main/java/com/instana/android/core/InstanaConfig.kt index 170e4433..6260f1af 100644 --- a/runtime/src/main/java/com/instana/android/core/InstanaConfig.kt +++ b/runtime/src/main/java/com/instana/android/core/InstanaConfig.kt @@ -128,7 +128,14 @@ class InstanaConfig * If true, this option adds W3C-compliant headers to the request headers of all outgoing application requests, * ensuring compatibility with W3C tracing standards. */ - var enableW3CHeaders: Boolean = false + var enableW3CHeaders: Boolean = false, + + /** + * When enabled, automatic retries will be performed when exceptions occur during HTTP requests. + * When disabled, exceptions will propagate normally without retrying. + * Default is false to prevent unexpected duplicate requests. + */ + var autoRetryOnNetworkException: Boolean = false ) { //This need to be provided in Performance config If the n/w analytic is need to be diff --git a/runtime/src/main/java/com/instana/android/instrumentation/okhttp3/OkHttp3GlobalInterceptor.kt b/runtime/src/main/java/com/instana/android/instrumentation/okhttp3/OkHttp3GlobalInterceptor.kt index 86a4cf65..20799061 100644 --- a/runtime/src/main/java/com/instana/android/instrumentation/okhttp3/OkHttp3GlobalInterceptor.kt +++ b/runtime/src/main/java/com/instana/android/instrumentation/okhttp3/OkHttp3GlobalInterceptor.kt @@ -84,8 +84,15 @@ object OkHttp3GlobalInterceptor : Interceptor { finish(request, e) httpMarkers.remove(marker.headerValue(), marker) } - // Proceed with the original request to avoid throwing - chain.proceed(intercepted) + + // Only retry if the flag is enabled + if (Instana.config?.autoRetryOnNetworkException == true) { + // Proceed with the original request to retry + chain.proceed(intercepted) + } else { + // Otherwise rethrow the exception to let it propagate normally + throw e + } } } diff --git a/runtime/src/test/java/com/instana/android/instrumentation/okhttp3/OkHttp3GlobalInterceptorTest.kt b/runtime/src/test/java/com/instana/android/instrumentation/okhttp3/OkHttp3GlobalInterceptorTest.kt index ab423fe8..eefb3d36 100644 --- a/runtime/src/test/java/com/instana/android/instrumentation/okhttp3/OkHttp3GlobalInterceptorTest.kt +++ b/runtime/src/test/java/com/instana/android/instrumentation/okhttp3/OkHttp3GlobalInterceptorTest.kt @@ -222,4 +222,49 @@ class OkHttp3GlobalInterceptorTest:BaseTest() { verify(mockRequest, never()).headers() } + @Test(expected = IOException::class) + fun `test intercept propagates exception when autoRetryOnNetworkException is false`() { + config.httpCaptureConfig = HTTPCaptureConfig.AUTO + config.autoRetryOnNetworkException = false + Instana.setup(app, config) + + `when`(mockChain.request()).thenReturn(mockRequest) + `when`(mockChain.request().headers()).thenReturn(mockHeaders) + `when`(mockChain.request().newBuilder()).thenReturn(mockBuilders) + `when`(mockHeaders.toMap()).thenReturn(emptyMap()) + `when`(mockRequest.url()).thenReturn(HttpUrl.parse("https://www.example.com")) + `when`(mockRequest.header(TRACKING_HEADER_KEY)).thenReturn(null) + `when`(mockChain.proceed(any(Request::class.java))).thenThrow(IOException("Network error")) + + // This should throw IOException + OkHttp3GlobalInterceptor.intercept(mockChain) + } + + @Test + fun `test intercept retries request when autoRetryOnNetworkException is true`() { + config.httpCaptureConfig = HTTPCaptureConfig.AUTO + config.autoRetryOnNetworkException = true + Instana.setup(app, config) + + `when`(mockChain.request()).thenReturn(mockRequest) + `when`(mockChain.request().headers()).thenReturn(mockHeaders) + `when`(mockChain.request().newBuilder()).thenReturn(mockBuilders) + `when`(mockHeaders.toMap()).thenReturn(emptyMap()) + `when`(mockRequest.url()).thenReturn(HttpUrl.parse("https://www.example.com")) + `when`(mockRequest.header(TRACKING_HEADER_KEY)).thenReturn(null) + `when`(mockBuilders.header(any(String::class.java), any(String::class.java))).thenReturn(mockBuilders) + `when`(mockBuilders.build()).thenReturn(mockRequest) + + // First call throws exception, second call succeeds + `when`(mockChain.proceed(any(Request::class.java))) + .thenThrow(IOException("Network error")) + .thenReturn(mockResponse) + + val result = OkHttp3GlobalInterceptor.intercept(mockChain) + + // Verify chain.proceed was called twice (once for error, once for retry) + verify(mockChain, org.mockito.Mockito.times(2)).proceed(any(Request::class.java)) + assert(result == mockResponse) + } + }