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>
53 lines
2.3 KiB
Markdown
53 lines
2.3 KiB
Markdown
# 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
|
|
|
|
```text
|
|
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.
|