ecluse:ecluse-core
Safe HaskellNone
LanguageGHC2021

Ecluse.Core.Server.Response

Description

Map policy and upstream outcomes to HTTP statuses. Artifact requests use one outcome, while packuments choose a status from the surviving versions. Ecosystem contracts own response bodies.

Synopsis

Serve outcomes

data ServeDecision Source #

The outcome of deciding a request: serve it, or refuse it with a reason. Every client-facing reply renders one of these.

Constructors

Admit

Serve the request (the 200 stream for an artifact).

Reject Rejection

Refuse the request, with the reason and a client-facing message.

data Rejection Source #

A refusal: why the request was refused, and an intuitive message for the client.

Constructors

Rejection 

Fields

Instances

Instances details
Show Rejection Source # 
Instance details

Defined in Ecluse.Core.Server.Response

Eq Rejection Source # 
Instance details

Defined in Ecluse.Core.Server.Response

data RejectReason Source #

Why a request was refused. A policy refusal is final for this request. An unavailability is an inability to decide, whose Transience separates a retryable 503 from a terminal 500.

Constructors

ByPolicy RuleName

A rule denied the version (deny-by-default included). The RuleName is the rule that decided, for the audit trail and the denial body.

Unavailable Transience

The version could not be vetted. Refuse it, with transience indicating whether a retry can help.

MissingIntegrity

A public artifact lacks a digest, so admission cannot verify its bytes and refuses with 403. Trusted private artifacts are exempt.

BelowIntegrityFloor

A public artifact's strongest digest falls below the configured floor, so admission refuses with 403. Trusted private artifacts are exempt.

UpstreamInvalid

An upstream packument names a different package and cannot enter the merge. When no valid origin remains, the packument request returns 502.

Instances

Instances details
Show RejectReason Source # 
Instance details

Defined in Ecluse.Core.Server.Response

Eq RejectReason Source # 
Instance details

Defined in Ecluse.Core.Server.Response

data Transience Source #

Serve transient outages, rate limits, timeouts, and open breakers as 503. Serve internal or parse faults as 500. WillResolve and WontResolve encode that distinction.

Constructors

WillResolve (Maybe RetryAfter)

A retry may succeed after an outage, timeout, or open breaker. The optional RetryAfter suggests a client delay.

WontResolve

Not expected to self-heal (an internal or parse error). Retrying cannot help, so the request is a 500, never a 503.

Instances

Instances details
Show Transience Source # 
Instance details

Defined in Ecluse.Core.Rules.Types

Eq Transience Source # 
Instance details

Defined in Ecluse.Core.Rules.Types

newtype RetryAfter Source #

A Retry-After delay, in whole seconds. A 'newtype' so a raw count of seconds is never confused with some other integer when it reaches a response header or a sweep's wait.

Constructors

RetryAfter Int 

newtype RuleName Source #

The name of the rule that decided a refusal, for the audit trail and the denial body.

Constructors

RuleName Text 

Instances

Instances details
Show RuleName Source # 
Instance details

Defined in Ecluse.Core.Server.Response

Eq RuleName Source # 
Instance details

Defined in Ecluse.Core.Server.Response

Ord RuleName Source # 
Instance details

Defined in Ecluse.Core.Server.Response

rejectUnavailable :: Transience -> Text -> ServeDecision Source #

Refuse a request that could not be decided. The Transience it carries is what artifactStatus renders as a 503 or a 500, so a caller states that rather than a status.

serveDecisionOf :: PackageDetails -> Decision -> ServeDecision Source #

Project a rules Decision into a serve outcome. An Undecidable decision rejects as Unavailable, which is fail-closed: a version no rule could vet is never admitted.

Concrete-artifact status

data ArtifactStatus Source #

The HTTP status a concrete-artifact request renders to. A packument request has no single status, because the pipeline chooses one over the survivors: PackumentStatus models that.

Constructors

Ok

200: admitted, so the proxy streams the artifact.

Forbidden

403: refused by policy. The route's response contract shapes the body.

Unavailable' (Maybe RetryAfter)

503: a transient inability to decide. A known RetryAfter becomes the header.

ServerError

500: a permanent or internal inability to decide. Not retryable.

NotFound

404: the upstream did not have the artifact (forwarded miss).

artifactStatus :: ServeDecision -> ArtifactStatus Source #

Map a serve outcome to its concrete-artifact status: 503 only where it will resolve, so a WontResolve unavailability is a 500. An upstream 404 is no serve decision and never appears.

artifactHttpStatus :: ArtifactStatus -> Status Source #

The HTTP status an ArtifactStatus renders as.

Packument status (over the merged survivor set)

data PackumentStatus Source #

The HTTP status a packument request renders to, chosen over the merged survivor set. There is no 404: the package exists, and a genuine absence is decided before the merge.

Constructors

PackumentOk

200: at least one version survived, so the proxy serves the merged, filtered packument.

PackumentForbidden

403: no version survived and every exclusion was a policy denial. The response body collects the denial reasons.

PackumentUnavailable (Maybe RetryAfter)

503: no version survived and an exclusion can recover. A suggested delay becomes the Retry-After header.

PackumentBadGateway

502: no valid origin remained and an upstream packument named a different package. This gateway fault differs from absence or a retryable outage.

PackumentServerError

500: no version survived, no exclusion is retryable, and at least one is a permanent or internal inability to decide. Retrying cannot help.

packumentStatus :: [ServeDecision] -> PackumentStatus Source #

A packument's status from the per-version outcomes: with no survivor the most recoverable cause wins, 502 under 503 as a transient origin may yet answer, and an empty input is a 403.

longestRetry :: [Maybe RetryAfter] -> Maybe RetryAfter Source #

The longest suggested RetryAfter among transient causes, or Nothing when none of them suggested a delay.

Denial help text

data HelpMessage Source #

An operator-configured message appended to every denial, typically where to ask for help. Stored trimmed, so an all-blank value contributes nothing.

Instances

Instances details
Show HelpMessage Source # 
Instance details

Defined in Ecluse.Core.Server.Response

Eq HelpMessage Source # 
Instance details

Defined in Ecluse.Core.Server.Response

mkHelpMessage :: Text -> HelpMessage Source #

Build a HelpMessage, trimming surrounding whitespace.

appendHelp :: Maybe HelpMessage -> Text -> Text Source #

Append a non-blank operator HelpMessage to a denial message, separated by a single space. A blank or absent help message contributes nothing.

A refusal's two parts

data Refusal Source #

A refusal's text in its two parts, so an ecosystem renders whichever its own denial surface carries and the help message is not dropped for one with no envelope to hold both.

Constructors

Refusal 

Fields

Instances

Instances details
Show Refusal Source # 
Instance details

Defined in Ecluse.Core.Server.Response

Eq Refusal Source # 
Instance details

Defined in Ecluse.Core.Server.Response

Methods

(==) :: Refusal -> Refusal -> Bool #

(/=) :: Refusal -> Refusal -> Bool #

mkRefusal :: Maybe HelpMessage -> Text -> Refusal Source #

Pair a decided reason with the mount's configured help message, if it has a non-blank one.

renderRefusal :: Refusal -> Text Source #

The refusal as one line: the reason, with the help message appended after a single space.