Opaque token proving a response was sent.
private mk ensures that application code cannot fabricate this token.
Only server implementations (via ResponseReceived.done) and the
AppM.respond combinator produce it. Combined with the AppM indexed
monad, this means: an application that type-checks has necessarily
invoked the response callback exactly once.
Instances For
Construct a ResponseReceived token. Intended for use by server
implementations (e.g., Warp) that provide the response callback.
Application code should not call this directly.
Equations
Instances For
The size of the request body. In the case of chunked transfer encoding, the size is unknown. $$\text{RequestBodyLength} = \text{ChunkedBody} \mid \text{KnownLength}\ \mathbb{N}$$ @since 1.4.0
- chunkedBody : RequestBodyLength
Chunked transfer encoding — size unknown.
- knownLength
(bytes : Nat)
: RequestBodyLength
Content-Length header present — size known. $$\text{KnownLength}\ n,\; n : \mathbb{N}$$
Instances For
Equations
- Network.Wai.instBEqRequestBodyLength.beq Network.Wai.RequestBodyLength.chunkedBody Network.Wai.RequestBodyLength.chunkedBody = true
- Network.Wai.instBEqRequestBodyLength.beq (Network.Wai.RequestBodyLength.knownLength a) (Network.Wai.RequestBodyLength.knownLength b) = (a == b)
- Network.Wai.instBEqRequestBodyLength.beq x✝¹ x✝ = false
Instances For
Equations
Equations
Equations
- One or more equations did not get rendered due to their size.
Instances For
An HTTP request with all parsed information. $$\text{Request} = \{ \text{method}, \text{version}, \text{path}, \text{query}, \text{headers}, \ldots \}$$
- requestMethod : HTTP.Types.Method
The HTTP method (GET, POST, etc.).
- httpVersion : HTTP.Types.HttpVersion
The HTTP version.
- rawPathInfo : String
The raw path info (e.g., "/foo/bar"). Middlewares should not modify this — modify
pathInfoinstead. - rawQueryString : String
The raw query string (e.g., "?key=val"), including leading '?'. Do not modify this raw value — modify
queryStringinstead. - requestHeaders : HTTP.Types.RequestHeaders
The request headers.
- isSecure : Bool
Whether the current connection is secure (HTTPS/TLS). Note: does not reflect whether the original client connection was secure (e.g., behind a reverse proxy). Use
Network.Wai.Request.appearsSecurefor a more complete check. - remoteHost : Socket.SockAddr
The remote client address.
Parsed path segments (e.g., ["foo", "bar"]).
- queryString : HTTP.Types.Query
Parsed query string.
IO action to read the next chunk of the request body. Returns empty ByteArray when body is exhausted. Each call consumes a chunk — this is not idempotent.
- vault : Data.Vault
Per-request extensible storage.
- requestBodyLength : RequestBodyLength
The size of the request body — chunked or known length. @since 1.4.0
The Host header value. @since 2.0.0
The Range header value. @since 2.0.0
The Referer header value. @since 3.2.0
The User-Agent header value. @since 3.2.0
Instances For
An HTTP response.
- responseFile
(status : HTTP.Types.Status)
(headers : HTTP.Types.ResponseHeaders)
(path : String)
(part : Option Sendfile.FilePart)
: Response
Respond with a file.
- responseBuilder
(status : HTTP.Types.Status)
(headers : HTTP.Types.ResponseHeaders)
(body : ByteArray)
: Response
Respond with a ByteArray body (built in memory).
- responseStream
(status : HTTP.Types.Status)
(headers : HTTP.Types.ResponseHeaders)
(body : StreamingBody)
: Response
Respond with a streaming body.
- responseRaw
(rawAction : IO ByteArray → (ByteArray → IO Unit) → IO Unit)
(fallback : Response)
: Response
Respond with raw data sent directly to the socket.
Instances For
Get the status from a response.
Equations
- (Network.Wai.Response.responseFile s headers path part).status = s
- (Network.Wai.Response.responseBuilder s headers body).status = s
- (Network.Wai.Response.responseStream s headers body).status = s
- (Network.Wai.Response.responseRaw rawAction fb).status = fb.status
Instances For
Get the headers from a response.
Equations
- (Network.Wai.Response.responseFile s headers path part).headers = headers
- (Network.Wai.Response.responseBuilder s headers body).headers = headers
- (Network.Wai.Response.responseStream s headers body).headers = headers
- (Network.Wai.Response.responseRaw rawAction fb).headers = fb.headers
Instances For
Map over the response headers.
Equations
- Network.Wai.Response.mapResponseHeaders f (Network.Wai.Response.responseFile s headers path part) = Network.Wai.Response.responseFile s (f headers) path part
- Network.Wai.Response.mapResponseHeaders f (Network.Wai.Response.responseBuilder s headers body) = Network.Wai.Response.responseBuilder s (f headers) body
- Network.Wai.Response.mapResponseHeaders f (Network.Wai.Response.responseStream s headers body) = Network.Wai.Response.responseStream s (f headers) body
- Network.Wai.Response.mapResponseHeaders f (Network.Wai.Response.responseRaw rawAction fb) = Network.Wai.Response.responseRaw rawAction (Network.Wai.Response.mapResponseHeaders f fb)
Instances For
Map over the response status.
Equations
- Network.Wai.Response.mapResponseStatus f (Network.Wai.Response.responseFile s headers path part) = Network.Wai.Response.responseFile (f s) headers path part
- Network.Wai.Response.mapResponseStatus f (Network.Wai.Response.responseBuilder s headers body) = Network.Wai.Response.responseBuilder (f s) headers body
- Network.Wai.Response.mapResponseStatus f (Network.Wai.Response.responseStream s headers body) = Network.Wai.Response.responseStream (f s) headers body
- Network.Wai.Response.mapResponseStatus f (Network.Wai.Response.responseRaw rawAction fb) = Network.Wai.Response.responseRaw rawAction (Network.Wai.Response.mapResponseStatus f fb)
Instances For
Whether a response has an empty body. Used for RFC 9110 §6.4.1 compliance checks. Files and streams are conservatively assumed non-empty; raw responses are opaque.
Equations
- (Network.Wai.Response.responseBuilder status headers body).bodyIsEmpty = body.isEmpty
- (Network.Wai.Response.responseFile status headers path part).bodyIsEmpty = false
- (Network.Wai.Response.responseStream status headers body).bodyIsEmpty = false
- (Network.Wai.Response.responseRaw rawAction fallback).bodyIsEmpty = false
Instances For
Status accessor returns the status of a builder response.
Status accessor returns the status of a file response.
Status accessor returns the status of a stream response.
Headers accessor returns the headers of a builder response.
Headers accessor returns the headers of a file response.
mapResponseHeaders id is identity for builder responses.
mapResponseHeaders id is identity for file responses.
mapResponseHeaders id is identity for stream responses.
mapResponseStatus id is identity for builder responses.
mapResponseStatus id is identity for file responses.
mapResponseStatus id is identity for stream responses.
Response lifecycle state for the indexed AppM monad.
This two-element type is the key to compile-time exactly-once enforcement.
The AppM monad is parameterised by a pre-state and a post-state drawn
from ResponseState. Since .pending ≠ .sent (proven below), the
compiler can distinguish "haven't responded yet" from "already responded"
and reject any code path that would respond twice or not at all.
$$\text{ResponseState} = \text{pending} \mid \text{sent}$$
- pending : ResponseState
No response has been sent yet.
- sent : ResponseState
A response has been sent.
Instances For
Equations
- Network.Wai.instBEqResponseState.beq x✝ y✝ = (x✝.ctorIdx == y✝.ctorIdx)
Instances For
Equations
Equations
Equations
- One or more equations did not get rendered due to their size.
Instances For
Response states are distinct.
Indexed Green monad tracking response lifecycle state transitions.
This is the core dependent-type mechanism that enforces the WAI exactly-once-response contract at compile time.
AppM pre post α represents a Green computation that transitions the
response lifecycle from state pre to state post. The state indices
are checked by the Lean 4 kernel during type-checking and erased at
runtime -- AppM compiles to exactly the same code as plain Green.
Why private mk? The constructor is private, so the only ways to
build an AppM value are through the provided combinators:
respond, respondIO, liftIO, ipure, ibind, ioThen.
Application code cannot fabricate AppM .pending .sent without actually
invoking the response callback -- the type system forces the real work.
Why double-respond is impossible:
AppM.respondhas typeAppM .pending .sent ResponseReceived.- After one call the state is
.sent. AppM.ibindchains:AppM s₁ s₂ α → (α → AppM s₂ s₃ β) → AppM s₁ s₃ β.- A second
respondwould requireAppM .sent _ _with pre-state.sent, butrespond's pre-state is.pending-- type mismatch, compile-time error.
Trust boundary: Server code (Warp) extracts the Green via .run
at the edge. Middleware that needs full control can use the protected
escape hatch AppM.unsafeLift, which requires an explicit open.
$$\text{AppM}\ s_1\ s_2\ \alpha = \text{Green}(\alpha) \text{ (opaque, indexed by state)}$$
- run : Control.Concurrent.Green.Green α
Extract the underlying Green computation. For server/framework code only.
Instances For
Lift a pure value without changing state. $$\text{ipure} : \alpha \to \text{AppM}\ s\ s\ \alpha$$
Equations
- Network.Wai.AppM.ipure a = { run := pure a }
Instances For
Indexed bind: chains state transitions. $$\text{ibind} : \text{AppM}\ s_1\ s_2\ \alpha \to (\alpha \to \text{AppM}\ s_2\ s_3\ \beta) \to \text{AppM}\ s_1\ s_3\ \beta$$
Instances For
Send a response, transitioning from .pending to .sent.
This is the only way to produce AppM .pending .sent ResponseReceived.
The callback returns Green ResponseReceived — all response sending
happens on the non-blocking Green monad.
$$\text{respond} : (\text{Response} \to \text{Green}\ \text{ResponseReceived}) \to \text{Response} \to \text{AppM}\ \texttt{.pending}\ \texttt{.sent}\ \text{ResponseReceived}$$
Equations
- Network.Wai.AppM.respond callback resp = { run := callback resp }
Instances For
Perform IO then respond with the computed Response.
Convenient for the common pattern of "compute a response, then send it."
Uses do notation naturally inside the IO Response block.
$$\text{respondIO} : (\text{Response} \to \text{Green}\ \text{ResponseReceived}) \to \text{IO}(\text{Response}) \to \text{AppM}\ \texttt{.pending}\ \texttt{.sent}\ \text{ResponseReceived}$$
Instances For
Perform an IO action then continue with a state-changing computation. Useful for middleware that needs IO before deciding whether to delegate or respond. $$\text{ioThen} : \text{IO}(\alpha) \to (\alpha \to \text{AppM}\ s_1\ s_2\ \beta) \to \text{AppM}\ s_1\ s_2\ \beta$$
Instances For
Escape hatch for framework/middleware code that needs full IO control over state transitions. Not for application code. The caller is responsible for ensuring exactly-once response semantics. $$\text{unsafeLift} : \text{IO}(\alpha) \to \text{AppM}\ s_1\ s_2\ \alpha$$
Equations
- Network.Wai.AppM.unsafeLift action = { run := liftM action }
Instances For
AppM s s is a standard Monad (for non-state-changing operations).
Enables do notation inside AppM.respondIO or AppM.ioThen blocks.
Equations
- One or more equations did not get rendered due to their size.
IO actions lift into AppM s s automatically (via IO → Green → AppM).
Equations
- Network.Wai.instMonadLiftTIOAppM = { monadLift := fun {α : Type} => Network.Wai.AppM.liftIO }
A WAI application. $$\text{Application} = \text{Request} \to (\text{Response} \to \text{Green}(\text{ResponseReceived})) \to \text{AppM}\ \texttt{.pending}\ \texttt{.sent}\ \text{ResponseReceived}$$
The return type AppM .pending .sent ResponseReceived is the key
dependent-type innovation over Haskell's WAI. It encodes three
compile-time guarantees simultaneously:
- Response was sent (post-state is
.sent, not.pending). - Response was sent exactly once (no path from
.sentback to.pending; no secondrespondaccepted after.sent). - Response was sent through the callback (
private mkprevents fabricating theAppMvalue without callingrespond).
Server implementations (Warp) extract the underlying Green via .run
at the trust boundary. Application code never touches .run.
The callback takes Response → Green ResponseReceived — all response
sending happens on the non-blocking Green monad, freeing pool threads
while awaiting socket I/O.
Equations
- One or more equations did not get rendered due to their size.
Instances For
A WAI middleware transforms an application. $$\text{Middleware} = \text{Application} \to \text{Application}$$