Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import io.sentry.SentryFeedbackOptions;
import io.sentry.SentryIntegrationPackageStorage;
import io.sentry.SentryLevel;
import io.sentry.SentryReplayOptions;
import io.sentry.protocol.SdkVersion;
import io.sentry.util.Objects;
import java.util.ArrayList;
Expand Down Expand Up @@ -639,47 +638,20 @@ static void applyMetadata(
options
.getSessionReplay()
.setNetworkCaptureBodies(
readBool(
readBoolNullable(
metadata,
logger,
REPLAYS_NETWORK_CAPTURE_BODIES,
options.getSessionReplay().isNetworkCaptureBodies() /* defaultValue */));

if (options.getSessionReplay().getNetworkRequestHeaders().size()
== SentryReplayOptions.getNetworkDetailsDefaultHeaders().size()) { // Only has defaults
final @Nullable List<String> requestHeaders =
readList(metadata, logger, REPLAYS_NETWORK_REQUEST_HEADERS);
if (requestHeaders != null) {
final List<String> filteredHeaders = new ArrayList<>();
for (String header : requestHeaders) {
final String trimmedHeader = header.trim();
if (!trimmedHeader.isEmpty()) {
filteredHeaders.add(trimmedHeader);
}
}
if (!filteredHeaders.isEmpty()) {
options.getSessionReplay().setNetworkRequestHeaders(filteredHeaders);
}
}
}
options.getSessionReplay().getNetworkCaptureBodies()));

if (options.getSessionReplay().getNetworkResponseHeaders().size()
== SentryReplayOptions.getNetworkDetailsDefaultHeaders().size()) { // Only has defaults
final @Nullable List<String> responseHeaders =
readList(metadata, logger, REPLAYS_NETWORK_RESPONSE_HEADERS);
if (responseHeaders != null && !responseHeaders.isEmpty()) {
final List<String> filteredHeaders = new ArrayList<>();
for (String header : responseHeaders) {
final String trimmedHeader = header.trim();
if (!trimmedHeader.isEmpty()) {
filteredHeaders.add(trimmedHeader);
}
}
if (!filteredHeaders.isEmpty()) {
options.getSessionReplay().setNetworkResponseHeaders(filteredHeaders);
}
}
}
options
.getSessionReplay()
.setNetworkRequestHeaders(
readTrimmedList(metadata, logger, REPLAYS_NETWORK_REQUEST_HEADERS));
options
.getSessionReplay()
.setNetworkResponseHeaders(
readTrimmedList(metadata, logger, REPLAYS_NETWORK_RESPONSE_HEADERS));

options.setIgnoredErrors(readList(metadata, logger, IGNORED_ERRORS));

Expand Down Expand Up @@ -783,6 +755,21 @@ private static boolean readBool(
return value;
}

private static @Nullable Boolean readBoolNullable(
final @NotNull Bundle metadata,
final @NotNull ILogger logger,
final @NotNull String key,
final @Nullable Boolean defaultValue) {
final @Nullable Boolean value;
if (metadata.containsKey(key)) {
value = metadata.getBoolean(key);
} else {
value = defaultValue;
}
logger.log(SentryLevel.DEBUG, key + " read: " + value);
return value;
}

private static @Nullable String readString(
final @NotNull Bundle metadata,
final @NotNull ILogger logger,
Expand Down Expand Up @@ -814,6 +801,23 @@ private static boolean readBool(
}
}

private static @Nullable List<String> readTrimmedList(
final @NotNull Bundle metadata, final @NotNull ILogger logger, final @NotNull String key) {
final @Nullable List<String> values = readList(metadata, logger, key);
if (values == null) {
return null;
}

final @NotNull List<String> filteredValues = new ArrayList<>();
for (final String value : values) {
final @NotNull String trimmedValue = value.trim();
if (!trimmedValue.isEmpty()) {
filteredValues.add(trimmedValue);
}
}
return filteredValues.isEmpty() ? null : filteredValues;
}

private static double readDouble(
final @NotNull Bundle metadata, final @NotNull ILogger logger, final @NotNull String key) {
// manifest meta-data only reads float
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import androidx.core.os.bundleOf
import androidx.test.ext.junit.runners.AndroidJUnit4
import io.sentry.FilterString
import io.sentry.ILogger
import io.sentry.KeyValueCollectionBehavior
import io.sentry.ProfileLifecycle
import io.sentry.SentryLevel
import io.sentry.SentryReplayOptions
Expand Down Expand Up @@ -2347,36 +2348,31 @@ class ManifestMetadataReaderTest {
ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider)

// Assert
assertFalse(fixture.options.sessionReplay.isNetworkCaptureBodies)
assertEquals(false, fixture.options.sessionReplay.networkCaptureBodies)
}

@Test
fun `applyMetadata keeps default networkCaptureBodies as true when not present`() {
fun `applyMetadata keeps networkCaptureBodies unset when not present`() {
// Arrange
val context = fixture.getContext()

// Act
ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider)

// Assert
assertTrue(fixture.options.sessionReplay.isNetworkCaptureBodies)
assertNull(fixture.options.sessionReplay.networkCaptureBodies)
}

@Test
fun `applyMetadata keeps the default networkRequestHeaders`() {
fun `applyMetadata keeps networkRequestHeaderBehavior unset when not present`() {
// Arrange
val context = fixture.getContext()

// Act
ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider)

// Assert
val headers = fixture.options.sessionReplay.networkRequestHeaders
val defaultHeaders = SentryReplayOptions.getNetworkDetailsDefaultHeaders()

// Should have exactly the default headers
assertEquals(defaultHeaders.size, headers.size)
defaultHeaders.forEach { defaultHeader -> assertTrue(headers.contains(defaultHeader)) }
assertNull(fixture.options.sessionReplay.networkRequestHeaderBehavior)
}

@Test
Expand All @@ -2390,33 +2386,24 @@ class ManifestMetadataReaderTest {
ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider)

// Assert
val allHeaders = fixture.options.sessionReplay.networkRequestHeaders
val defaultHeaders = SentryReplayOptions.getNetworkDetailsDefaultHeaders()

// Should include default headers + additional headers
defaultHeaders.forEach { defaultHeader ->
assertTrue(allHeaders.contains(defaultHeader)) // default
}
assertTrue(allHeaders.contains("Authorization")) // additional
assertTrue(allHeaders.contains("X-Custom-Header")) // additional
assertTrue(allHeaders.contains("X-Request-Id")) // additional
val behavior = fixture.options.sessionReplay.networkRequestHeaderBehavior
assertEquals(KeyValueCollectionBehavior.Mode.ALLOW_LIST, behavior?.mode)
assertTrue(behavior!!.terms.contains("Content-Type"))
assertTrue(behavior.terms.contains("Authorization"))
assertTrue(behavior.terms.contains("X-Custom-Header"))
assertTrue(behavior.terms.contains("X-Request-Id"))
}

@Test
fun `applyMetadata keeps the default networkResponseHeaders`() {
fun `applyMetadata keeps networkResponseHeaderBehavior unset when not present`() {
// Arrange
val context = fixture.getContext()

// Act
ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider)

// Assert
val headers = fixture.options.sessionReplay.networkResponseHeaders
val defaultHeaders = SentryReplayOptions.getNetworkDetailsDefaultHeaders()

// Should have exactly the default headers
assertEquals(defaultHeaders.size, headers.size)
defaultHeaders.forEach { defaultHeader -> assertTrue(headers.contains(defaultHeader)) }
assertNull(fixture.options.sessionReplay.networkResponseHeaderBehavior)
}

@Test
Expand All @@ -2431,13 +2418,12 @@ class ManifestMetadataReaderTest {
ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider)

// Assert
val allHeaders = fixture.options.sessionReplay.networkResponseHeaders
// Should include default headers + additional headers
val defaultHeaders = SentryReplayOptions.getNetworkDetailsDefaultHeaders()
defaultHeaders.forEach { defaultHeader -> assertTrue(allHeaders.contains(defaultHeader)) }
assertTrue(allHeaders.contains("X-Response-Time")) // additional
assertTrue(allHeaders.contains("X-Cache-Status")) // additional
assertTrue(allHeaders.contains("X-Server-Id")) // additional
val behavior = fixture.options.sessionReplay.networkResponseHeaderBehavior
assertEquals(KeyValueCollectionBehavior.Mode.ALLOW_LIST, behavior?.mode)
assertTrue(behavior!!.terms.contains("Content-Type"))
assertTrue(behavior.terms.contains("X-Response-Time"))
assertTrue(behavior.terms.contains("X-Cache-Status"))
assertTrue(behavior.terms.contains("X-Server-Id"))
}

@Test
Expand Down Expand Up @@ -2472,16 +2458,8 @@ class ManifestMetadataReaderTest {
ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider)

// Assert
// Should still have default headers even with empty string
val defaultHeaders = SentryReplayOptions.getNetworkDetailsDefaultHeaders()

val requestHeaders = fixture.options.sessionReplay.networkRequestHeaders
assertEquals(defaultHeaders.size, requestHeaders.size)
defaultHeaders.forEach { defaultHeader -> assertTrue(requestHeaders.contains(defaultHeader)) }

val responseHeaders = fixture.options.sessionReplay.networkResponseHeaders
assertEquals(defaultHeaders.size, responseHeaders.size)
defaultHeaders.forEach { defaultHeader -> assertTrue(responseHeaders.contains(defaultHeader)) }
assertNull(fixture.options.sessionReplay.networkRequestHeaderBehavior)
assertNull(fixture.options.sessionReplay.networkResponseHeaderBehavior)
}

@Test
Expand Down Expand Up @@ -2518,9 +2496,9 @@ class ManifestMetadataReaderTest {
ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider)

// Assert
val headers = fixture.options.sessionReplay.networkRequestHeaders
assertTrue(headers.contains("Authorization"))
assertTrue(headers.contains("X-Custom-Header"))
val behavior = fixture.options.sessionReplay.networkRequestHeaderBehavior
assertTrue(behavior!!.terms.contains("Authorization"))
assertTrue(behavior.terms.contains("X-Custom-Header"))
}

// Spotlight Configuration Tests
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,9 @@ public open class SentryOkHttpInterceptor(
NetworkDetailCaptureUtils.createRequest(
request,
requestContentLength,
scopes.options.sessionReplay.isNetworkCaptureBodies,
scopes.options.sessionReplay.isNetworkRequestBodyCaptureEnabled(
scopes.options.dataCollectionResolver
),
{ req ->
req.body?.let { originalBody ->
val buffer = okio.Buffer()
Expand All @@ -161,7 +163,9 @@ public open class SentryOkHttpInterceptor(
safeExtractRequestBody(bodyBytes, originalBody.contentType(), scopes.options.logger)
}
},
scopes.options.sessionReplay.networkRequestHeaders,
scopes.options.sessionReplay.resolveNetworkRequestHeaders(
scopes.options.dataCollectionResolver
),
{ req: Request -> req.headers.toMap() },
)
)
Expand Down Expand Up @@ -205,9 +209,13 @@ public open class SentryOkHttpInterceptor(
NetworkDetailCaptureUtils.createResponse(
it,
it.body?.contentLength(),
scopes.options.sessionReplay.isNetworkCaptureBodies,
scopes.options.sessionReplay.isNetworkResponseBodyCaptureEnabled(
scopes.options.dataCollectionResolver
),
{ resp: Response -> resp.extractResponseBody(scopes.options.logger) },
scopes.options.sessionReplay.networkResponseHeaders,
scopes.options.sessionReplay.resolveNetworkResponseHeaders(
scopes.options.dataCollectionResolver
),
{ resp: Response -> resp.headers.toMap() },
),
)
Expand Down
12 changes: 12 additions & 0 deletions sentry/api/sentry.api
Original file line number Diff line number Diff line change
Expand Up @@ -4171,10 +4171,13 @@ public final class io/sentry/SentryReplayOptions : io/sentry/SentryMaskingOption
public fun getErrorReplayDuration ()J
public fun getFrameObserver ()Lio/sentry/SentryReplayOptions$ReplayFrameObserver;
public fun getFrameRate ()I
public fun getNetworkCaptureBodies ()Ljava/lang/Boolean;
public fun getNetworkDetailAllowUrls ()Ljava/util/List;
public fun getNetworkDetailDenyUrls ()Ljava/util/List;
public static fun getNetworkDetailsDefaultHeaders ()Ljava/util/List;
public fun getNetworkRequestHeaderBehavior ()Lio/sentry/KeyValueCollectionBehavior;
public fun getNetworkRequestHeaders ()Ljava/util/List;
public fun getNetworkResponseHeaderBehavior ()Lio/sentry/KeyValueCollectionBehavior;
public fun getNetworkResponseHeaders ()Ljava/util/List;
public fun getOnErrorSampleRate ()Ljava/lang/Double;
public fun getQuality ()Lio/sentry/SentryReplayOptions$SentryReplayQuality;
Expand All @@ -4186,19 +4189,26 @@ public final class io/sentry/SentryReplayOptions : io/sentry/SentryMaskingOption
public fun isCaptureSurfaceViews ()Z
public fun isDebug ()Z
public fun isNetworkCaptureBodies ()Z
public fun isNetworkRequestBodyCaptureEnabled (Lio/sentry/DataCollectionResolver;)Z
public fun isNetworkResponseBodyCaptureEnabled (Lio/sentry/DataCollectionResolver;)Z
public fun isSessionReplayEnabled ()Z
public fun isSessionReplayForErrorsEnabled ()Z
public fun isTrackConfiguration ()Z
public fun resolveNetworkRequestHeaders (Lio/sentry/DataCollectionResolver;)Lio/sentry/KeyValueCollectionBehavior;
public fun resolveNetworkResponseHeaders (Lio/sentry/DataCollectionResolver;)Lio/sentry/KeyValueCollectionBehavior;
public fun setBeforeErrorSampling (Lio/sentry/SentryReplayOptions$BeforeErrorSamplingCallback;)V
public fun setCaptureSurfaceViews (Z)V
public fun setDebug (Z)V
public fun setFrameObserver (Lio/sentry/SentryReplayOptions$ReplayFrameObserver;)V
public fun setMaskAllImages (Z)V
public fun setMaskAllText (Z)V
public fun setNetworkCaptureBodies (Ljava/lang/Boolean;)V
public fun setNetworkCaptureBodies (Z)V
public fun setNetworkDetailAllowUrls (Ljava/util/List;)V
public fun setNetworkDetailDenyUrls (Ljava/util/List;)V
public fun setNetworkRequestHeaderBehavior (Lio/sentry/KeyValueCollectionBehavior;)V
public fun setNetworkRequestHeaders (Ljava/util/List;)V
public fun setNetworkResponseHeaderBehavior (Lio/sentry/KeyValueCollectionBehavior;)V
public fun setNetworkResponseHeaders (Ljava/util/List;)V
public fun setOnErrorSampleRate (Ljava/lang/Double;)V
public fun setQuality (Lio/sentry/SentryReplayOptions$SentryReplayQuality;)V
Expand Down Expand Up @@ -8113,7 +8123,9 @@ public final class io/sentry/util/network/NetworkBodyParser {
}

public final class io/sentry/util/network/NetworkDetailCaptureUtils {
public static fun createRequest (Ljava/lang/Object;Ljava/lang/Long;ZLio/sentry/util/network/NetworkDetailCaptureUtils$NetworkBodyExtractor;Lio/sentry/KeyValueCollectionBehavior;Lio/sentry/util/network/NetworkDetailCaptureUtils$NetworkHeaderExtractor;)Lio/sentry/util/network/ReplayNetworkRequestOrResponse;
public static fun createRequest (Ljava/lang/Object;Ljava/lang/Long;ZLio/sentry/util/network/NetworkDetailCaptureUtils$NetworkBodyExtractor;Ljava/util/List;Lio/sentry/util/network/NetworkDetailCaptureUtils$NetworkHeaderExtractor;)Lio/sentry/util/network/ReplayNetworkRequestOrResponse;
public static fun createResponse (Ljava/lang/Object;Ljava/lang/Long;ZLio/sentry/util/network/NetworkDetailCaptureUtils$NetworkBodyExtractor;Lio/sentry/KeyValueCollectionBehavior;Lio/sentry/util/network/NetworkDetailCaptureUtils$NetworkHeaderExtractor;)Lio/sentry/util/network/ReplayNetworkRequestOrResponse;
public static fun createResponse (Ljava/lang/Object;Ljava/lang/Long;ZLio/sentry/util/network/NetworkDetailCaptureUtils$NetworkBodyExtractor;Ljava/util/List;Lio/sentry/util/network/NetworkDetailCaptureUtils$NetworkHeaderExtractor;)Lio/sentry/util/network/ReplayNetworkRequestOrResponse;
public static fun initializeForUrl (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;)Lio/sentry/util/network/NetworkRequestData;
}
Expand Down
Loading
Loading