Documentation

Hale.WAI.Network.Wai.Internal

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
        @[reducible, inline]

        Body streaming callback type. $$\text{StreamingBody} = (\text{ByteArray} \to \text{IO}()) \to \text{IO}() \to \text{IO}()$$

        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
            • 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.).

              • The HTTP version.

              • rawPathInfo : String

                The raw path info (e.g., "/foo/bar"). Middlewares should not modify this — modify pathInfo instead.

              • rawQueryString : String

                The raw query string (e.g., "?key=val"), including leading '?'. Do not modify this raw value — modify queryString instead.

              • 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.appearsSecure for a more complete check.

              • remoteHost : Socket.SockAddr

                The remote client address.

              • pathInfo : List String

                Parsed path segments (e.g., ["foo", "bar"]).

              • queryString : HTTP.Types.Query

                Parsed query string.

              • requestBody : IO ByteArray

                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

              • requestHeaderHost : Option String

                The Host header value. @since 2.0.0

              • requestHeaderRange : Option String

                The Range header value. @since 2.0.0

              • requestHeaderReferer : Option String

                The Referer header value. @since 3.2.0

              • requestHeaderUserAgent : Option String

                The User-Agent header value. @since 3.2.0

              Instances For

                An HTTP response.

                Instances For

                  Get the status from a response.

                  Equations
                  Instances For

                    Get the headers from a response.

                    Equations
                    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
                      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.

                        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}$$

                        Instances For
                          @[implicit_reducible]
                          Equations
                          Equations
                          • One or more equations did not get rendered due to their size.
                          Instances For

                            Response states are distinct.

                            structure Network.Wai.AppM (pre post : ResponseState) (α : Type) :

                            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:

                            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)}$$

                            Instances For
                              @[inline]
                              def Network.Wai.AppM.ipure {α : Type} {s : ResponseState} (a : α) :
                              AppM s s α

                              Lift a pure value without changing state. $$\text{ipure} : \alpha \to \text{AppM}\ s\ s\ \alpha$$

                              Equations
                              Instances For
                                @[inline]
                                def Network.Wai.AppM.liftIO {α : Type} {s : ResponseState} (action : IO α) :
                                AppM s s α

                                Lift a plain IO action without changing state. IO is automatically lifted into Green via MonadLift IO Green. $$\text{liftIO} : \text{IO}(\alpha) \to \text{AppM}\ s\ s\ \alpha$$

                                Equations
                                Instances For
                                  @[inline]
                                  def Network.Wai.AppM.ibind {s₁ s₂ : ResponseState} {α : Type} {s₃ : ResponseState} {β : Type} (ma : AppM s₁ s₂ α) (f : αAppM s₂ s₃ β) :
                                  AppM s₁ s₃ β

                                  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$$

                                  Equations
                                  Instances For
                                    @[inline]

                                    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
                                    Instances For
                                      @[inline]

                                      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}$$

                                      Equations
                                      Instances For
                                        @[inline]
                                        def Network.Wai.AppM.ioThen {α : Type} {s₁ s₂ : ResponseState} {β : Type} (action : IO α) (f : αAppM s₁ s₂ β) :
                                        AppM s₁ s₂ β

                                        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$$

                                        Equations
                                        Instances For
                                          def Network.Wai.AppM.unsafeLift {α : Type} {pre post : ResponseState} (action : IO α) :
                                          AppM pre post α

                                          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
                                          Instances For
                                            @[implicit_reducible]

                                            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.
                                            @[implicit_reducible]

                                            IO actions lift into AppM s s automatically (via IO → Green → AppM).

                                            Equations
                                            @[reducible, inline]

                                            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:

                                            1. Response was sent (post-state is .sent, not .pending).
                                            2. Response was sent exactly once (no path from .sent back to .pending; no second respond accepted after .sent).
                                            3. Response was sent through the callback (private mk prevents fabricating the AppM value without calling respond).

                                            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
                                              @[reducible, inline]

                                              A WAI middleware transforms an application. $$\text{Middleware} = \text{Application} \to \text{Application}$$

                                              Equations
                                              Instances For