Repository navigation
Conversation
Capturing a response body for session replay peeked it up front with `Response.peekBody(MAX_NETWORK_BODY_SIZE + 1)`. OkHttp implements a peek as `request(byteCount)`, which keeps reading until that many bytes are buffered or the stream ends. A response with no Content-Length never satisfies either condition, so for server-sent events, long-poll and any chunked endpoint that stays open the interceptor never returned and the caller never received the response at all. Bodies with a known length still end on their own, so they keep the existing up-front capture. Bodies of unknown length are now wrapped in a body that copies what the application consumes into a capped buffer and reports it once no more bytes can arrive: when the stream ends, when the application closes the body, or when the cap is reached. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Split the capture wrapping into its own function to keep a single return, and suppress TooManyFunctions on the interceptor as done elsewhere in this module.
A response body has a single consumer, and neither Http1ExchangeCodec.cancel() nor Http2ExchangeCodec.cancel() closes the body, so nothing reaches this class from another thread. The AtomicBoolean already guarantees the capture is reported exactly once.
A streamed body is only known once it has been consumed, which can be after the NetworkRequestData carrying it was handed to the scope, so the replay thread can read it while it is still being written. Keeping the three response values behind one volatile reference means a reader sees either nothing or the complete set.
📲 Install BuildsAndroid
|
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
📜 Description
SentryOkHttpInterceptorcaptured the response body withpeekBody(MAX_NETWORK_BODY_SIZE + 1). OkHttpimplements a peek as
request(n), which reads untilnbytes are buffered or the stream ends — a bodywith no
Content-Lengthsatisfies neither, so the interceptor never returned and the application neverreceived the response.
Every response body now goes through the new
NetworkBodyCapturingResponseBody, which copies the bytesas the application consumes them into a capped buffer and reports the capture once it can no longer
grow. A body of known length that nobody read is taken while it is closed, where the read is bounded,
so the peek is replaced rather than kept beside the new path. The status code and the headers are
recorded before anything is consumed, so a stream that stays open still produces a usable breadcrumb.
💡 Motivation and Context
With Session Replay network details enabled for a URL, every Server-Sent Events, long-poll or open
chunked endpoint hung forever or failed with a
SocketTimeoutException. Gzipped responses wereaffected too: OkHttp strips
Content-Lengthwhen it decompresses, so an application interceptor sees-1.Continues carlonzo#1 by @carlonzo.
💚 How did you test it?
returns the response even though the body never endsis the regression test — a real chunked origin,a
TimeoutExceptionwithout the fix. The suite also covers gzip, HTTP/2,enqueue(), a droppedconnection, a cancelled stream, the cap with its truncation warning, and the known-length behaviour
that must not change.
:sentry-okhttp:test139,:sentry:test3689,:sentry-android-replay:testReleaseUnitTest265 — green, as aredetektandapiCheck.📝 Checklist
sendDefaultPIIis enabled.On the internal
NetworkRequestData:setResponseDetails(int, ...)becamesetResponseDetails(ResponseDetails);ResponseDetailsandgetResponseDetails()are new.🔮 Next steps
Content-Lengthand then trickles still blocks in the peek.inside
intercept. The status code and the headers still arrive immediately.