Skip to content

Commit fefa054

Browse files
markushiclaude
andcommitted
fix: improve network body parsing and truncation handling
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
1 parent dba088c commit fefa054

File tree

5 files changed

+162
-180
lines changed

5 files changed

+162
-180
lines changed

sentry-android-replay/src/main/java/io/sentry/android/replay/DefaultReplayBreadcrumbConverter.kt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,10 @@ public open class DefaultReplayBreadcrumbConverter() : ReplayBreadcrumbConverter
241241
networkData.request?.let { request ->
242242
val requestData = mutableMapOf<String, Any?>()
243243
request.size?.let { requestData["size"] = it }
244-
request.body?.let { requestData["body"] = it.value }
244+
request.body?.let {
245+
requestData["body"] = it.body
246+
requestData["warnings"] = it.warnings?.map { w -> w.value }
247+
}
245248

246249
if (request.headers.isNotEmpty()) {
247250
requestData["headers"] = request.headers
@@ -255,7 +258,10 @@ public open class DefaultReplayBreadcrumbConverter() : ReplayBreadcrumbConverter
255258
networkData.response?.let { response ->
256259
val responseData = mutableMapOf<String, Any?>()
257260
response.size?.let { responseData["size"] = it }
258-
response.body?.let { responseData["body"] = it.value }
261+
response.body?.let {
262+
responseData["body"] = it.body
263+
responseData["warnings"] = it.warnings?.map { w -> w.value }
264+
}
259265

260266
if (response.headers.isNotEmpty()) {
261267
responseData["headers"] = response.headers

sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpInterceptor.kt

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -317,14 +317,10 @@ public open class SentryOkHttpInterceptor(
317317
val contentTypeString = contentType?.toString()
318318
val maxBodySize = SentryReplayOptions.MAX_NETWORK_BODY_SIZE
319319

320-
val contentLength = responseBody.contentLength()
321-
if (contentLength > maxBodySize * 2) {
322-
return NetworkBody.fromString("[Response body too large: $contentLength bytes]")
323-
}
324-
325320
// Peek at the body (doesn't consume it)
326-
val peekBody = peekBody(maxBodySize.toLong())
321+
val peekBody = peekBody(maxBodySize.toLong() + 1)
327322
val bodyBytes = peekBody.bytes()
323+
val truncated = bodyBytes.size > maxBodySize
328324

329325
val charset = contentType?.charset(Charsets.UTF_8)?.name() ?: "UTF-8"
330326
return NetworkBodyParser.fromBytes(

sentry/api/sentry.api

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7479,29 +7479,22 @@ public final class io/sentry/util/UrlUtils$UrlDetails {
74797479
public fun getUrlOrFallback ()Ljava/lang/String;
74807480
}
74817481

7482-
public abstract interface class io/sentry/util/network/NetworkBody {
7483-
public static fun fromJsonArray (Ljava/util/List;)Lio/sentry/util/network/NetworkBody;
7484-
public static fun fromJsonObject (Ljava/util/Map;)Lio/sentry/util/network/NetworkBody;
7485-
public static fun fromString (Ljava/lang/String;)Lio/sentry/util/network/NetworkBody;
7486-
public abstract fun getValue ()Ljava/lang/Object;
7487-
}
7488-
7489-
public final class io/sentry/util/network/NetworkBody$JsonArrayImpl : io/sentry/util/network/NetworkBody {
7490-
public synthetic fun getValue ()Ljava/lang/Object;
7491-
public fun getValue ()Ljava/util/List;
7492-
public fun toString ()Ljava/lang/String;
7493-
}
7494-
7495-
public final class io/sentry/util/network/NetworkBody$JsonObjectImpl : io/sentry/util/network/NetworkBody {
7496-
public synthetic fun getValue ()Ljava/lang/Object;
7497-
public fun getValue ()Ljava/util/Map;
7482+
public final class io/sentry/util/network/NetworkBody {
7483+
public fun <init> (Ljava/lang/Object;)V
7484+
public fun <init> (Ljava/lang/Object;Ljava/util/List;)V
7485+
public fun getBody ()Ljava/lang/Object;
7486+
public fun getWarnings ()Ljava/util/List;
74987487
public fun toString ()Ljava/lang/String;
74997488
}
75007489

7501-
public final class io/sentry/util/network/NetworkBody$StringBodyImpl : io/sentry/util/network/NetworkBody {
7502-
public synthetic fun getValue ()Ljava/lang/Object;
7490+
public final class io/sentry/util/network/NetworkBody$NetworkBodyWarning : java/lang/Enum {
7491+
public static final field BODY_PARSE_ERROR Lio/sentry/util/network/NetworkBody$NetworkBodyWarning;
7492+
public static final field INVALID_JSON Lio/sentry/util/network/NetworkBody$NetworkBodyWarning;
7493+
public static final field JSON_TRUNCATED Lio/sentry/util/network/NetworkBody$NetworkBodyWarning;
7494+
public static final field TEXT_TRUNCATED Lio/sentry/util/network/NetworkBody$NetworkBodyWarning;
75037495
public fun getValue ()Ljava/lang/String;
7504-
public fun toString ()Ljava/lang/String;
7496+
public static fun valueOf (Ljava/lang/String;)Lio/sentry/util/network/NetworkBody$NetworkBodyWarning;
7497+
public static fun values ()[Lio/sentry/util/network/NetworkBody$NetworkBodyWarning;
75057498
}
75067499

75077500
public final class io/sentry/util/network/NetworkBodyParser {
Lines changed: 29 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package io.sentry.util.network;
22

33
import java.util.List;
4-
import java.util.Map;
5-
import org.jetbrains.annotations.NotNull;
4+
import org.jetbrains.annotations.ApiStatus;
5+
import org.jetbrains.annotations.Nullable;
66

77
/**
88
* Represents the body content of a network request or response. Can be one of: JSON object, JSON
@@ -12,102 +12,49 @@
1212
* href="https://github.com/getsentry/sentry-javascript/blob/develop/packages/replay-internal/src/types/request.ts">Javascript
1313
* types</a>
1414
*/
15-
public interface NetworkBody {
15+
@ApiStatus.Internal
16+
public final class NetworkBody {
1617

17-
/**
18-
* Creates a NetworkBody from a JSON object.
19-
*
20-
* @param value The map representing the JSON object
21-
* @return A NetworkBody instance for the JSON object
22-
*/
23-
static @NotNull NetworkBody fromJsonObject(@NotNull final Map<String, Object> value) {
24-
return new JsonObjectImpl(value);
25-
}
18+
private final @Nullable Object body;
19+
private final @Nullable List<NetworkBodyWarning> warnings;
2620

27-
/**
28-
* Creates a NetworkBody from a JSON array.
29-
*
30-
* @param value The list representing the JSON array
31-
* @return A NetworkBody instance for the JSON array
32-
*/
33-
static @NotNull NetworkBody fromJsonArray(@NotNull final List<Object> value) {
34-
return new JsonArrayImpl(value);
21+
public NetworkBody(final @Nullable Object body) {
22+
this(body, null);
3523
}
3624

37-
/**
38-
* Creates a NetworkBody from string content.
39-
*
40-
* @param value The string content
41-
* @return A NetworkBody instance for the string
42-
*/
43-
static @NotNull NetworkBody fromString(@NotNull final String value) {
44-
return new StringBodyImpl(value);
25+
public NetworkBody(
26+
final @Nullable Object body, final @Nullable List<NetworkBodyWarning> warnings) {
27+
this.body = body;
28+
this.warnings = warnings;
4529
}
4630

47-
/**
48-
* Gets the underlying value of this NetworkBody.
49-
*
50-
* @return The value as an Object (could be Map, List, or String)
51-
*/
52-
@NotNull
53-
Object getValue();
54-
55-
// Private implementation classes
56-
57-
/** Implementation for JSON object bodies */
58-
final class JsonObjectImpl implements NetworkBody {
59-
private final @NotNull Map<String, Object> value;
60-
61-
JsonObjectImpl(@NotNull final Map<String, Object> value) {
62-
this.value = value;
63-
}
64-
65-
@Override
66-
public @NotNull Map<String, Object> getValue() {
67-
return value;
68-
}
69-
70-
@Override
71-
public String toString() {
72-
return "NetworkBody.JsonObject{" + value + '}';
73-
}
31+
public @Nullable Object getBody() {
32+
return body;
7433
}
7534

76-
/** Implementation for JSON array bodies */
77-
final class JsonArrayImpl implements NetworkBody {
78-
private final @NotNull List<Object> value;
79-
80-
JsonArrayImpl(@NotNull final List<Object> value) {
81-
this.value = value;
82-
}
83-
84-
@Override
85-
public @NotNull List<Object> getValue() {
86-
return value;
87-
}
88-
89-
@Override
90-
public String toString() {
91-
return "NetworkBody.JsonArray{" + value + '}';
92-
}
35+
public @Nullable List<NetworkBodyWarning> getWarnings() {
36+
return warnings;
9337
}
9438

95-
/** Implementation for string bodies */
96-
final class StringBodyImpl implements NetworkBody {
97-
private final @NotNull String value;
39+
public enum NetworkBodyWarning {
40+
JSON_TRUNCATED("JSON_TRUNCATED"),
41+
TEXT_TRUNCATED("TEXT_TRUNCATED"),
42+
INVALID_JSON("INVALID_JSON"),
43+
BODY_PARSE_ERROR("BODY_PARSE_ERROR");
44+
45+
private final String value;
9846

99-
StringBodyImpl(@NotNull final String value) {
47+
NetworkBodyWarning(String value) {
10048
this.value = value;
10149
}
10250

103-
@Override
104-
public @NotNull String getValue() {
51+
public String getValue() {
10552
return value;
10653
}
54+
}
10755

108-
@Override
109-
public String toString() {
110-
return "NetworkBody.StringBody{" + value + '}';
111-
}
56+
@Override
57+
public String toString() {
58+
return "NetworkBody{" + "body=" + body + ", warnings=" + warnings + '}';
11259
}
11360
}

0 commit comments

Comments
 (0)