Snapshot of the in-flight state that already existed, identically, in both this worktree and the main checkout before this session began: the initial HTTP Client platform implementation (previously untracked), the redis-lab removal, and the JPA / object-storage / notification integration work. Kept separate from this session's HTTP Client review response, which lands in the following commit, so the two bodies of work stay reviewable apart. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2.3 KiB
Streaming and Large Bodies
Response lifecycle
A blocking streaming download returns BlockingStreamingResponse, never a bare InputStream.
Closing is idempotent and always releases the connection — after a full read, a partial read, a
decode failure, or a size rejection. The status is validated before any body byte is delivered, so a
failed download never becomes a half-consumed stream the caller has to reason about.
A reactive download emits bounded DataBuffer values. Buffers are released on completion, error,
and cancellation; a dropped buffer is direct memory nobody returns.
Wire bytes and decoded bytes are bounded independently, because a compressed payload passes a wire check and then expands. Limits are enforced while reading, not after buffering.
The first-byte boundary
response headers received
→ nothing delivered yet
→ a read-only operation may still be retried
→ first InputStream read or first Flux onNext
→ transparent retry is permanently disabled
FirstByteDeliveryGuard latches once and never resets. Retrying after delivery would replay a
stream the caller has already partly consumed, producing duplicated or reordered data that no
downstream code can detect.
Request bodies
A reopenable body is opened once per attempt, which is what makes it replayable; reusing the
previous stream would silently send an empty body on the retry. A one-shot stream or publisher
instance is never retried. ReactiveBodySource takes a publisher factory rather than a publisher
so a reactive body can honestly declare itself replayable.
A multipart body is exactly as replayable as its weakest part.
Server-sent events
Three budgets stay separate:
setupDeadline— establishing the streamstreamingIdleTimeout— silence once it is openmaxStreamDuration— optional total lifetime
Applying the request-shaped total-call timeout to an SSE subscription would terminate a perfectly
healthy stream on schedule, so it is not applied.
Last-Event-ID is opt-in. Replaying from an id is only correct when the producer guarantees it;
sending it blindly can skip or duplicate events. Reconnects consume the retry budget like any other
physical attempt, and cancelling the subscription stops both the stream and any pending reconnect.