Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dd-sdk-android-core/api/apiSurface
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ interface com.datadog.android.api.instrumentation.network.HttpResponseInfo
val headers: Map<String, List<String>>
val contentType: String?
val contentLength: Long?
val request: HttpRequestInfo?
interface com.datadog.android.api.instrumentation.network.MutableHttpRequestInfo
fun newBuilder(): HttpRequestInfoBuilder
data class com.datadog.android.api.net.Request
Expand Down
1 change: 1 addition & 0 deletions dd-sdk-android-core/api/dd-sdk-android-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,7 @@ public abstract interface class com/datadog/android/api/instrumentation/network/
public abstract fun getContentLength ()Ljava/lang/Long;
public abstract fun getContentType ()Ljava/lang/String;
public abstract fun getHeaders ()Ljava/util/Map;
public abstract fun getRequest ()Lcom/datadog/android/api/instrumentation/network/HttpRequestInfo;
public abstract fun getStatusCode ()I
public abstract fun getUrl ()Ljava/lang/String;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,9 @@ interface HttpResponseInfo {
*/
@get:WorkerThread
val contentLength: Long?

/**
* Represents the HTTP request associated with the response, or null when not available.
*/
val request: HttpRequestInfo?
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import com.datadog.android.internal.network.HttpSpec
import org.chromium.net.UrlResponseInfo

internal data class CronetHttpResponseInfo(
private val response: UrlResponseInfo
private val response: UrlResponseInfo,
override val request: CronetHttpRequestInfo?
) : HttpResponseInfo {

override val url: String get() = response.url
Expand All @@ -35,7 +36,7 @@ internal data class CronetHttpResponseInfo(
get() {
/*
* Content-Length is expected to be declared once or in case of multiple repetitions should have same value.
* Otherwise it's assumed that Content-Length is invalid
* Otherwise, it's assumed that Content-Length is invalid
* according to https://www.rfc-editor.org/rfc/rfc9110.pdf, page 63:
*
* Likewise, a sender forward a message with a Content-Length header field value that does not match the ABNF
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,19 +102,25 @@ internal class CronetRequestCallback(
) = delegate.onReadCompleted(request, info, byteBuffer)

private fun finishSuccessRequestTracing(response: UrlResponseInfo?) {
val responseInfo = response?.let { CronetHttpResponseInfo(it) }

apmTracingStateHolder.getAndSet(null)?.let { apmTracingState ->
if (responseInfo != null) {
apmNetworkInstrumentation?.onResponseSucceeded(apmTracingState, responseInfo)
if (response != null) {
val requestInfo = apmTracingState.createRequestInfo() as? CronetHttpRequestInfo
apmNetworkInstrumentation?.onResponseSucceeded(
apmTracingState,
CronetHttpResponseInfo(response, requestInfo)
)
}
} ?: apmNetworkInstrumentation?.reportInstrumentationError {
"Request tracing state not found for ${response?.url}. Instrumentation may be broken."
}

distributedTracingStateHolder.getAndSet(null)?.let { distributedTracingState ->
if (responseInfo != null) {
distributedTracingInstrumentation?.onResponseSucceeded(distributedTracingState, responseInfo)
if (response != null) {
val requestInfo = distributedTracingState.createRequestInfo() as? CronetHttpRequestInfo
distributedTracingInstrumentation?.onResponseSucceeded(
distributedTracingState,
CronetHttpResponseInfo(response, requestInfo)
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
package com.datadog.android.cronet.internal

import androidx.annotation.WorkerThread
import com.datadog.android.api.instrumentation.network.HttpRequestInfo
import com.datadog.android.rum.RumAttributes
import com.datadog.android.rum.internal.domain.event.ResourceTiming
import com.datadog.android.rum.internal.net.RumNetworkInstrumentation
Expand All @@ -25,7 +24,7 @@ internal class CronetRequestFinishedInfoListener(

@WorkerThread
override fun onRequestFinished(finishedInfo: RequestFinishedInfo) {
val requestInfo = finishedInfo.annotations?.filterIsInstance<HttpRequestInfo>()?.firstOrNull()
val requestInfo = finishedInfo.annotations?.filterIsInstance<CronetHttpRequestInfo>()?.firstOrNull()
val distributingTracingState = finishedInfo.annotations?.filterIsInstance<RequestTracingState>()?.firstOrNull()
if (requestInfo == null) {
rumNetworkInstrumentation.reportInstrumentationError {
Expand Down Expand Up @@ -53,7 +52,7 @@ internal class CronetRequestFinishedInfoListener(
}

RequestFinishedInfo.SUCCEEDED -> {
val responseInfo = finishedInfo.responseInfo?.let { CronetHttpResponseInfo(it) }
val responseInfo = finishedInfo.responseInfo?.let { CronetHttpResponseInfo(it, requestInfo) }

if (responseInfo == null) {
rumNetworkInstrumentation.stopResourceWithError(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
*/
package com.datadog.android.cronet.internal

import com.datadog.android.api.instrumentation.network.HttpRequestInfo
import com.datadog.android.cronet.internal.CronetRequestFinishedInfoListener.Companion.minus
import com.datadog.android.rum.internal.domain.event.ResourceTiming
import com.datadog.android.rum.internal.net.RumNetworkInstrumentation
Expand Down Expand Up @@ -54,8 +53,8 @@ internal class CronetRequestFinishedInfoListenerTest {
@Mock
private lateinit var mockResponseInfo: UrlResponseInfo

@Forgery
private lateinit var fakeRequestInfo: HttpRequestInfo
@Mock
private lateinit var fakeRequestInfo: CronetHttpRequestInfo

private lateinit var testedListener: CronetRequestFinishedInfoListener

Expand Down Expand Up @@ -136,7 +135,7 @@ internal class CronetRequestFinishedInfoListenerTest {
// Then
verify(mockRumNetworkInstrumentation).stopResource(
requestInfo = fakeRequestInfo,
responseInfo = CronetHttpResponseInfo(mockResponseInfo)
responseInfo = CronetHttpResponseInfo(mockResponseInfo, fakeRequestInfo)
Comment thread
satween marked this conversation as resolved.
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

package com.datadog.android.cronet.internal

import com.datadog.android.api.InternalLogger
import com.datadog.android.internal.network.HttpSpec
import com.datadog.android.utils.forge.Configurator
import com.datadog.tools.unit.forge.exhaustiveAttributes
Expand Down Expand Up @@ -40,7 +39,7 @@ internal class CronetResponseInfoTest {
lateinit var mockUrlResponseInfo: UrlResponseInfo

@Mock
lateinit var mockInternalLogger: InternalLogger
lateinit var mockRequestInfo: CronetHttpRequestInfo

lateinit var fakeHeaders: Map<String, List<String>>

Expand All @@ -55,7 +54,7 @@ internal class CronetResponseInfoTest {
) {
// Given
whenever(mockUrlResponseInfo.url).thenReturn(fakeUrl)
val responseInfo = CronetHttpResponseInfo(mockUrlResponseInfo)
val responseInfo = CronetHttpResponseInfo(mockUrlResponseInfo, mockRequestInfo)

// When
val result = responseInfo.url
Expand All @@ -70,7 +69,7 @@ internal class CronetResponseInfoTest {
) {
// Given
whenever(mockUrlResponseInfo.httpStatusCode).thenReturn(fakeStatusCode)
val responseInfo = CronetHttpResponseInfo(mockUrlResponseInfo)
val responseInfo = CronetHttpResponseInfo(mockUrlResponseInfo, mockRequestInfo)

// When
val result = responseInfo.statusCode
Expand All @@ -84,7 +83,7 @@ internal class CronetResponseInfoTest {
// Given

whenever(mockUrlResponseInfo.allHeaders).thenReturn(fakeHeaders)
val responseInfo = CronetHttpResponseInfo(mockUrlResponseInfo)
val responseInfo = CronetHttpResponseInfo(mockUrlResponseInfo, mockRequestInfo)

// When
val result = responseInfo.headers
Expand All @@ -100,7 +99,7 @@ internal class CronetResponseInfoTest {
// Given
val fakeHeaders = fakeHeaders + mapOf(HttpSpec.Header.CONTENT_TYPE to listOf(fakeContentType))
whenever(mockUrlResponseInfo.allHeaders).thenReturn(fakeHeaders)
val responseInfo = CronetHttpResponseInfo(mockUrlResponseInfo)
val responseInfo = CronetHttpResponseInfo(mockUrlResponseInfo, mockRequestInfo)

// When
val result = responseInfo.contentType
Expand All @@ -113,7 +112,7 @@ internal class CronetResponseInfoTest {
fun `M return null W contentType property { no content type header }`() {
// Given
whenever(mockUrlResponseInfo.allHeaders).thenReturn(emptyMap())
val responseInfo = CronetHttpResponseInfo(mockUrlResponseInfo)
val responseInfo = CronetHttpResponseInfo(mockUrlResponseInfo, mockRequestInfo)

// When
val result = responseInfo.contentType
Expand All @@ -122,14 +121,38 @@ internal class CronetResponseInfoTest {
assertThat(result).isNull()
}

@Test
fun `M return request W request property`() {
// Given
val responseInfo = CronetHttpResponseInfo(mockUrlResponseInfo, mockRequestInfo)

// When
val result = responseInfo.request

// Then
assertThat(result).isSameAs(mockRequestInfo)
}

@Test
fun `M return null W request property { null request }`() {
// Given
val responseInfo = CronetHttpResponseInfo(mockUrlResponseInfo, null)

// When
val result = responseInfo.request

// Then
assertThat(result).isNull()
}

@Test
fun `M return content length from header W computeContentLength() { header present }`(
@LongForgery(min = 1) fakeContentLength: Long
) {
// Given
val fakeHeaders = fakeHeaders + mapOf(HttpSpec.Header.CONTENT_LENGTH to listOf(fakeContentLength.toString()))
whenever(mockUrlResponseInfo.allHeaders).thenReturn(fakeHeaders)
val responseInfo = CronetHttpResponseInfo(mockUrlResponseInfo)
val responseInfo = CronetHttpResponseInfo(mockUrlResponseInfo, mockRequestInfo)

// When
val result = responseInfo.contentLength
Expand All @@ -145,7 +168,7 @@ internal class CronetResponseInfoTest {
// Given
whenever(mockUrlResponseInfo.allHeaders).thenReturn(emptyMap())
whenever(mockUrlResponseInfo.receivedByteCount).thenReturn(fakeByteCount)
val responseInfo = CronetHttpResponseInfo(mockUrlResponseInfo)
val responseInfo = CronetHttpResponseInfo(mockUrlResponseInfo, mockRequestInfo)

// When
val result = responseInfo.contentLength
Expand All @@ -159,7 +182,7 @@ internal class CronetResponseInfoTest {
// Given
whenever(mockUrlResponseInfo.allHeaders).thenReturn(emptyMap())
whenever(mockUrlResponseInfo.receivedByteCount).thenReturn(0L)
val responseInfo = CronetHttpResponseInfo(mockUrlResponseInfo)
val responseInfo = CronetHttpResponseInfo(mockUrlResponseInfo, mockRequestInfo)

// When
val result = responseInfo.contentLength
Expand All @@ -173,7 +196,7 @@ internal class CronetResponseInfoTest {
// Given
whenever(mockUrlResponseInfo.allHeaders).thenReturn(emptyMap())
whenever(mockUrlResponseInfo.receivedByteCount).thenReturn(-1L)
val responseInfo = CronetHttpResponseInfo(mockUrlResponseInfo)
val responseInfo = CronetHttpResponseInfo(mockUrlResponseInfo, mockRequestInfo)

// When
val result = responseInfo.contentLength
Expand All @@ -192,7 +215,7 @@ internal class CronetResponseInfoTest {
)
whenever(mockUrlResponseInfo.allHeaders).thenReturn(fakeHeaders)
whenever(mockUrlResponseInfo.receivedByteCount).thenReturn(100L)
val responseInfo = CronetHttpResponseInfo(mockUrlResponseInfo)
val responseInfo = CronetHttpResponseInfo(mockUrlResponseInfo, mockRequestInfo)

// When
val result = responseInfo.contentLength
Expand All @@ -210,7 +233,7 @@ internal class CronetResponseInfoTest {
val fakeHeaders = fakeHeaders + mapOf(HttpSpec.Header.CONTENT_LENGTH to listOf(fakeHeaderLength.toString()))
whenever(mockUrlResponseInfo.allHeaders).thenReturn(fakeHeaders)
whenever(mockUrlResponseInfo.receivedByteCount).thenReturn(fakeByteCount)
val responseInfo = CronetHttpResponseInfo(mockUrlResponseInfo)
val responseInfo = CronetHttpResponseInfo(mockUrlResponseInfo, mockRequestInfo)

// When
val result = responseInfo.contentLength
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package com.datadog.android.okhttp.internal

import androidx.annotation.WorkerThread
import com.datadog.android.api.InternalLogger
import com.datadog.android.api.instrumentation.network.HttpRequestInfo
import com.datadog.android.api.instrumentation.network.HttpResponseInfo
import okhttp3.Response
import okhttp3.ResponseBody
Expand Down Expand Up @@ -65,6 +66,8 @@ internal class OkHttpResponseInfo(
null
}

override val request: HttpRequestInfo get() = OkHttpRequestInfo(originalResponse.request)

internal companion object {

// We need to limit this value as the body will be loaded in memory
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,20 @@ internal class OkHttpResponseInfoTest {
assertThat(result).isEqualTo(fakeHeaders)
}

@Test
fun `M delegate W request property`() {
// Given
val mockRequest = mock<Request>()
val mockResponse = mock<Response> { on { request } doReturn mockRequest }

// When
val result = OkHttpResponseInfo(mockResponse, mockInternalLogger).request

// Then
assertThat(result).isInstanceOf(OkHttpRequestInfo::class.java)
assertThat(result.toOkHttpRequest()).isSameAs(mockRequest)
}

@Test
fun `M delegate W contentType property { content type header present }`(
@StringForgery fakeContentType: String,
Expand Down
Loading