ecluse:ecluse-core
Safe HaskellNone
LanguageGHC2021

Ecluse.Core.Server.Stream

Description

Bounded-memory artifact streaming: the constant-memory serve path.

The proxy serves an artifact by streaming it through from upstream, never buffering it whole, so a multi-hundred-megabyte tarball never becomes a local memory spike. The mirror worker's whole-artifact fetch (fetchArtifactBytes) is the separate, buffered mirroring concern.

Resource lifetime

A WAI streaming body runs after the handler returns, so an upstream connection released lexically is already gone by the time the body streams. Raw WAI avoids that: the relay opens the connection with responseOpen before it commits the response, and closes it through finally around the whole relay. The open-to-finally handoff is masked, so the request timeout's kill or Warp's teardown on client disconnect cannot strand the connection between responseOpen returning and finally arming responseClose.

Backpressure

pumpBody writes each chunk through the sink's bounded output buffer before pulling the next, and the write blocks once that buffer spills. The proxy therefore reads upstream only as fast as the client drains, in constant memory whatever the artifact's size. Only the first chunk is flushed, for a prompt first byte: at relay byte rates a per-chunk flush degenerates into one socket send per upstream read (see docs/architecture/web-layer.md → "Streaming and resource lifetime").

Progress

The pump reads under the Ecluse.Core.Registry.Progress watchdog, which counts only the time spent waiting on upstream. An upstream that falls below the floor aborts the relay mid-stream, and a client that drains slowly does not.

Synopsis

A typed relay responder

data RelayResponder response Source #

The two ways an upstream relay can answer, over the caller's route-scoped response value. WAI construction stays out of this module, so no pipeline holds an unrestricted WAI responder.

Constructors

RelayResponder 

Fields

Relaying an upstream response through

data UpstreamBody Source #

Whether a relay pumps the upstream body through, or answers bodiless.

Constructors

StreamBody

Stream the body through. A 304 still answers bodiless, because it carries no body (RFC 9110 15.4.5) and upstream's reader is never read.

NoBody

Answer bodiless and never read upstream's body reader, so a HEAD cannot make the proxy stream a whole artifact to nowhere.

Instances

Instances details
Show UpstreamBody Source # 
Instance details

Defined in Ecluse.Core.Server.Stream

Eq UpstreamBody Source # 
Instance details

Defined in Ecluse.Core.Server.Stream

withUpstreamWhen Source #

Arguments

:: Manager 
-> ProgressFloor 
-> Request 
-> UpstreamBody 
-> (Status -> Bool)

Whether upstream's status is a hit. A rejected status is a clean miss.

-> (Status -> ResponseHeaders -> IO (Status, ResponseHeaders, verdict))

Run once, pre-commit, on the accepted status and headers: the client-facing status and headers, plus the caller's own verdict on the relay.

-> RelayResponder response 
-> IO (Maybe (verdict, response)) 

Relay an upstream response when its status passes accept. Nothing is a recoverable miss that commits nothing, and a failure after the commit propagates rather than re-answering.

The pump

pumpBody :: BodyReader -> (Builder -> IO ()) -> IO () -> IO () Source #

Pump a chunked body from a reader to a WAI stream sink in constant memory. An empty chunk is http-client's BodyReader end-of-body terminator, and the pump never writes it.