// What a request turned away by a middleware receives.
//
// go-zero answers 503 when a breaker is open, a connection budget is spent or a
// deadline passes, 429 when a rate limit bites, and 413 when a body is too
// large. Those are the defaults here; every one of them is a parameter, because
// which status a refusal carries is a deployment's decision — an API gateway in
// front may want 429 with `Retry-After` where the service alone wants 503.
///|
/// The response a refused request receives: a status, a body, and the headers
/// sent with it.
pub(all) struct Refusal {
status : Int32
body : Bytes
headers : Array[(String, String)]
}
///|
/// The headers a refusal carries unless told otherwise.
pub let plain_text : Array[(String, String)] = [
("content-type", "text/plain; charset=utf-8"),
]
///|
/// Build a refusal. Left alone it is the `503 Service Unavailable` the breaker,
/// the shedder and the connection limiter all answer with.
pub fn Refusal::new(
status~ : Int32 = 503,
body~ : Bytes = b"503 Service Unavailable",
headers~ : Array[(String, String)] = plain_text,
) -> Refusal {
{ status, body, headers, }
}
///|
/// The generic refusal: `503 Service Unavailable`.
pub let refusal : Refusal = Refusal::new()
///|
/// What an open breaker, a shed request and a spent connection budget answer
/// with (← go-zero's `breaker`, `sheddingHandler`, `maxConns`).
pub let unavailable : Refusal = refusal
///|
/// What a rate-limited request answers with (← go-zero's `periodlimit` and
/// `tokenlimit` handlers).
pub let too_many : Refusal = Refusal::new(
status=429,
body=b"429 Too Many Requests",
)
///|
/// What a request past its deadline answers with. go-zero's timeout middleware
/// answers `503` with this body rather than `504`, so that is the default.
pub let timed_out : Refusal = Refusal::new(body=b"service timeout")
///|
/// What an over-large request body answers with (← go-zero's `maxBytes`).
pub let too_large : Refusal = Refusal::new(
status=413,
body=b"413 Payload Too Large",
)
///|
/// The refusal as the ASGI events that send it. A pure value, so a middleware's
/// answer is testable without driving the async transport.
pub fn Refusal::events(self : Refusal) -> Array[@moonasgi.Event] {
[
@moonasgi.Event::HttpResponseStart(
status=self.status,
headers=self.headers,
trailers=false,
),
@moonasgi.Event::HttpResponseBody(body=self.body, more_body=false),
]
}