feat(network-details): Implement header and body extraction#7585
feat(network-details): Implement header and body extraction#7585
Conversation
Semver Impact of This PR🟡 Minor (new features) 📋 Changelog PreviewThis is how your changes will appear in the changelog. This PR will not appear in the changelog. 🤖 This preview updates automatically when you update the PR. |
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #7585 +/- ##
=============================================
+ Coverage 85.186% 85.409% +0.223%
=============================================
Files 490 490
Lines 29520 29616 +96
Branches 12761 12813 +52
=============================================
+ Hits 25147 25295 +148
+ Misses 4322 4269 -53
- Partials 51 52 +1
... and 8 files with indirect coverage changes Continue to review full report in Codecov by Sentry.
|
9473efb to
5bc5830
Compare
3547546 to
e7c5abb
Compare
7faf4ef to
6e19c0a
Compare
e7c5abb to
2e9607e
Compare
6e19c0a to
9a8d48d
Compare
6e5c5bb to
41cf944
Compare
9a8d48d to
8584657
Compare
41cf944 to
68fbe88
Compare
c3afd6d to
6e873ec
Compare
68fbe88 to
6a06365
Compare
📲 Install BuildsiOS
|
c73b24f to
dc3a7af
Compare
Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 787537a | 1218.35 ms | 1251.72 ms | 33.38 ms |
| ffb6adc | 1218.60 ms | 1247.47 ms | 28.87 ms |
| b6fa517 | 1218.83 ms | 1257.47 ms | 38.63 ms |
| df67624 | 1225.12 ms | 1259.90 ms | 34.78 ms |
| a7c42d9 | 1217.25 ms | 1253.98 ms | 36.73 ms |
| 29d546e | 1224.06 ms | 1257.05 ms | 32.98 ms |
| 1c5ecda | 1219.35 ms | 1253.76 ms | 34.41 ms |
| ae8cece | 1216.83 ms | 1251.37 ms | 34.55 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 787537a | 24.14 KiB | 1.15 MiB | 1.12 MiB |
| ffb6adc | 24.14 KiB | 1.15 MiB | 1.12 MiB |
| b6fa517 | 24.14 KiB | 1.14 MiB | 1.12 MiB |
| df67624 | 24.14 KiB | 1.14 MiB | 1.12 MiB |
| a7c42d9 | 24.14 KiB | 1.15 MiB | 1.13 MiB |
| 29d546e | 24.14 KiB | 1.15 MiB | 1.13 MiB |
| 1c5ecda | 24.14 KiB | 1.15 MiB | 1.12 MiB |
| ae8cece | 24.14 KiB | 1.15 MiB | 1.13 MiB |
Previous results on branch: mobile-935/extract-network-details
Startup times
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 0390710 | 1214.67 ms | 1245.27 ms | 30.60 ms |
| ef5b57a | 1226.96 ms | 1259.79 ms | 32.83 ms |
| 00b9497 | 1212.87 ms | 1240.63 ms | 27.76 ms |
| 4a5e66b | 1228.77 ms | 1254.27 ms | 25.50 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 0390710 | 24.14 KiB | 1.15 MiB | 1.13 MiB |
| ef5b57a | 24.14 KiB | 1.15 MiB | 1.12 MiB |
| 00b9497 | 24.14 KiB | 1.15 MiB | 1.13 MiB |
| 4a5e66b | 24.14 KiB | 1.15 MiB | 1.13 MiB |
dc3a7af to
5372649
Compare
5372649 to
c1488f7
Compare
c1488f7 to
98d01fc
Compare
|
rebase on main |
1) Extracts bodies that are JSON, formurlencoded, or text. Uses UTType to accurately classify content types as JSON or text without maintaining a manual list. Falls back to a string match for application/x-www-form-urlencoded which has no UTType representation. !Relies on having a valid `contentType` 2) Populates NetworkBodyWarning's for "MAYBE_JSON_TRUNCATED" "TEXT_TRUNCATED" "BODY_PARSE_ERROR" ^when encountered, these show custom dashboard UI.
Uses UTType to classify content types: only content positively identified as text is decoded. Everything else gets a descriptive placeholder: Example - "[Body not captured: contentType=image/png (8 bytes)]" Known text types (where UTType conforms to .text) are reliably classified by UTType's type hierarchy. If a content type header is incorrect (e.g. claims text but contains binary), the resulting decode failure is caught by the existing bodyParseError warning.
UTType (UniformTypeIdentifiers) requires macOS 11+, but the SDK targets macOS 10.14. Extract UTType-based MIME detection into a separate method gated with @available(macOS 11, *) so the code compiles on all macOS targets. Session Replay is not available on macOS, so the fallback placeholder is fine.
Casts to lower-case before comparing headers. ObjC setters now accept raw allHeaders and configuredHeaders instead of pre-filtered headers, keeping the filtering logic in Swift.
#7585 (comment) Parse MIME type and charset from Content-Type using CFStringConvertIANACharSetNameToEncoding instead of hardcoding UTF-8/ISO-Latin-1 fallbacks.
Replace force-unwraps and if-let/XCTFail patterns in SentryReplayNetworkDetailsBodyTests with XCTUnwrap for optional casts and Data(_:) for String-to-Data conversions.
98d01fc to
d0b9491
Compare
📜 Description
SentryNetworkBodyExtract request/response bodies that are either JSON, formurlencoded, binary or text.
contentTypeto decide how to interpret the body (NSData).UTTypeto decide whether something is JSON or text.text:
tries
.utf8, then.isoLatin1then gives up withBODY_PARSE_ERRORwarning.json:
tries
JSONSerialization.jsonObject, gives up withBODY_PARSE_ERRORwarningUTType doesn't know:
extracts a placeholder body E.g.
"[Body not captured: contentType=application/octet-stream (10240 bytes)]"SentryReplayNetworkRequestOrResponse💡 Motivation and Context
See first PR in stack.
💚 How did you test it?
Unit tests
make test-ios ONLY_TESTING="SentryReplayNetworkDetailsBodyTests,SentryReplayNetworkDetailsHeaderTests,SentryReplayNetworkDetailsIntegrationTests"Test Suite 'SentryReplayNetworkDetailsBodyTests' started at 2026-03-19 15:25:09.315.
✔ testInit_withBinaryContentType_shouldCreateArtificialString (0.005 seconds)
✔ testInit_withEmptyData_shouldReturnNil (0.000 seconds)
✔ testInit_withFormURLEncoded_duplicateKeys_shouldPromoteToArray (0.001 seconds)
✔ testInit_withFormURLEncoded_emptyKeys_shouldBeSkipped (0.000 seconds)
✔ testInit_withFormURLEncoded_emptyValue_shouldParseAsEmptyString (0.000 seconds)
Resolving Package Graph
✔ testInit_withFormURLEncoded_equalsInValue_shouldPreserve (0.000 seconds)
✔ testInit_withFormURLEncoded_missingEquals_shouldFallbackToText (0.001 seconds)
✔ testInit_withFormURLEncoded_shouldParseAsForm (0.001 seconds)
✔ testInit_withInvalidJSON_shouldFallbackToString (0.001 seconds)
✔ testInit_withJSONArray_shouldParseCorrectly (0.000 seconds)
✔ testInit_withJSONDictionary_shouldParseCorrectly (0.001 seconds)
✔ testInit_withLargeData_shouldTruncate (0.005 seconds)
✔ testInit_withNilContentType_shouldCreatePlaceholder (0.000 seconds)
✔ testInit_withTextData_shouldStoreAsString (0.000 seconds)
✔ testInit_withUnrecognizedContentType_shouldCreatePlaceholder (0.000 seconds)
✔ testParseMimeAndEncoding_shouldHandleEdgeCases (0.001 seconds)
✔ testSerialize_withJSONArray_shouldReturnArray (0.001 seconds)
✔ testSerialize_withJSONDictionary_shouldReturnDictionary (0.001 seconds)
✔ testSerialize_withNoContentType_shouldCreatePlaceholder (0.001 seconds)
✔ testSerialize_withStringBody_shouldReturnDictionary (0.001 seconds)
Executed 20 tests, with 0 failures (0 unexpected) in 0.021 (0.041) seconds
Test Suite 'SentryTests.xctest' passed at 2026-03-19 15:25:09.356.
Executed 20 tests, with 0 failures (0 unexpected) in 0.021 (0.041) seconds
SentryReplayNetworkDetailsHeaderTests (4 tests):
testExtractHeaders_withNilInputs_returnsEmptyDict— nil headers/config returns emptytestExtractHeaders_unconfiguredHeadersAreExcluded— only configured headers are extractedtestExtractHeaders_caseInsensitiveMatching— header matching is case-insensitivetestExtractHeaders_nonStringValues_convertedToStrings— non-string header values are convertedSentryReplayNetworkDetailsIntegrationTests (4 tests):
testInit_withMethod_shouldSetMethod— HTTP method is storedtestSerialize_withFullData_shouldReturnCompleteDictionary— full detail serializes all fieldstestSerialize_withPartialData_shouldOnlyIncludeSetFields— partial data omits unset fieldstestSerialize_withHeaderFiltering_shouldOnlyIncludeConfiguredHeaders— header filtering works end-to-end📝 Checklist
You have to check all boxes before merging:
sendDefaultPIIis enabled. N/ACloses #7710