| Safe Haskell | None |
|---|---|
| Language | GHC2021 |
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
- data RelayResponder response = RelayResponder {
- relayStreamResponse :: Status -> ResponseHeaders -> StreamingBody -> IO response
- relayEmptyResponse :: Status -> ResponseHeaders -> IO response
- data UpstreamBody
- = StreamBody
- | NoBody
- withUpstreamWhen :: Manager -> ProgressFloor -> Request -> UpstreamBody -> (Status -> Bool) -> (Status -> ResponseHeaders -> IO (Status, ResponseHeaders, verdict)) -> RelayResponder response -> IO (Maybe (verdict, response))
- pumpBody :: BodyReader -> (Builder -> IO ()) -> IO () -> IO ()
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 |
| NoBody | Answer bodiless and never read upstream's body reader, so a |
Instances
| Show UpstreamBody Source # | |
Defined in Ecluse.Core.Server.Stream Methods showsPrec :: Int -> UpstreamBody -> ShowS # show :: UpstreamBody -> String # showList :: [UpstreamBody] -> ShowS # | |
| Eq UpstreamBody Source # | |
Defined in Ecluse.Core.Server.Stream | |
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.