Files
DongHyeonkaandClaude Opus 5 5f10b791d3 chore: record pre-existing uncommitted repository state
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>
2026-08-11 16:48:43 +09:00

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 stream
  • streamingIdleTimeout — silence once it is open
  • maxStreamDuration — 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.