Files
tech-log-backend/docs/httpclient/streaming.md
T

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.