moonbitstack/moonasgi/http does not have a README file

    Handler

    type Handler = (Request) -> Response

    The ergonomic request→response function most handlers are written as. The synchronous sugar the suite lifts onto @spec.AsgiApp at the server boundary.

    Middleware

    type Middleware = ((Request) -> Response) -> ((Request) -> Response)

    A handler transformer that wraps a downstream Handler to add cross-cutting behaviour. Composed as an onion where the first registered is outermost.

    StreamHandler

    type StreamHandler = (Request) -> StreamingResponse

    A streaming handler: produces a StreamingResponse (multi-chunk body, optional trailers) instead of a single buffered Response. Driven by run_http_stream and the TestClient.

    Drain

    pub(all) enum Drain {
    Complete
    Disconnected
    Truncated
    } derive(Eq,
    Debug
    )

    Why a body drain stopped: the request body finished, the client went away mid-request, or the stream ran out without either. A long-polling or streaming handler cares about the difference — Disconnected means nothing it sends will arrive, so cleanup is all that is left to do.

    Drain::equal

    fn Drain::equal(Drain, Drain) -> Bool

    Drain::not_equal

    fn Drain::not_equal(x : Drain, y : Drain) -> Bool

    Drain::to_repr

    Request

    pub(all) struct Request {
    http_method : String
    path : String
    query_string : Bytes
    headers : Array[(String, String)]
    body : Bytes
    } derive(Eq)

    An inbound HTTP request in ergonomic form: the request line, headers, and the fully-read body. The sugar over @spec.HttpScope plus a drained Receive, so a Handler never touches the async transport directly.

    Request::equal

    fn Request::equal(Request, Request) -> Bool

    Request::header

    fn Request::header(self : Request, name : String) -> String?

    Look up the first header matching name, following ASGI's lowercased-name convention.

    Request::not_equal

    fn Request::not_equal(x : Request, y : Request) -> Bool

    Response

    pub(all) struct Response {
    status : Int
    headers : Array[(String, String)]
    body : Bytes
    } derive(Eq)

    An outbound HTTP response: status, headers, and the full body. Mutable so middleware can decorate it before the server serialises it into HttpResponseStart + HttpResponseBody.

    Response::equal

    fn Response::equal(Response, Response) -> Bool

    Response::events

    Serialise a Response into the outbound event pair a server sends: an HttpResponseStart carrying status and headers (no trailers), then a single HttpResponseBody with the whole body and more_body: false. The public Response::events for callers that want the lowered form directly.

    Response::header

    fn Response::header(self : Response, name : String) -> String?

    Look up the first response header matching name, same convention as Request::header.

    Response::json

    fn Response::json(status? : Int, value : Json) -> Response

    An application/json response whose body is the UTF-8 encoding of value serialised with Json::stringify. The ergonomic constructor for a JSON reply.

    Response::new

    fn Response::new(status : Int, headers : Array[(String, String)], body : Bytes) -> Response

    Build a response from raw bytes.

    Response::not_equal

    fn Response::not_equal(x : Response, y : Response) -> Bool

    Response::text

    fn Response::text(status? : Int, body : String) -> Response

    A text/plain; charset=utf-8 response whose body is the UTF-8 encoding of body. The ergonomic constructor for the common string reply.

    StreamingResponse

    pub(all) struct StreamingResponse {
    status : Int
    headers : Array[(String, String)]
    chunks : Array[Bytes]
    trailers : Array[(String, String)]
    early_hints : Array[Array[String]]
    } derive(Eq)

    A streamed outbound response: the status line, headers, an ordered list of body chunks each emitted as its own HttpResponseBody, and optional trailing trailers. Models ASGI response streaming (multiple body messages with more_body: true) and the http.response.trailers extension without an async transport. events lowers it to the exact event sequence a server would send. early_hints (the http.response.early_hint extension) are the 103 Early Hints messages emitted ahead of the final response, each element a list of Link header values.

    StreamingResponse::equal

    StreamingResponse::events

    Lower a streamed response to the outbound events a server sends: one HttpResponseEarlyHint per early-hint message (before the response starts), then an HttpResponseStart (with trailers set when any are present), then one HttpResponseBody per chunk — more_body: true on all but the last — and, if there are trailers, a terminating HttpResponseTrailers. An empty chunks still yields a single empty final body, so the stream is always well-formed.

    StreamingResponse::new

    fn StreamingResponse::new(chunks~ : Array[Bytes], status? : Int, headers? : Array[(String, String)], trailers? : Array[(String, String)], early_hints? : Array[Array[String]]) -> StreamingResponse

    Build a StreamingResponse. status defaults to 200, headers, trailers and early_hints to empty; chunks is the ordered body, each element becoming one HttpResponseBody frame.

    StreamingResponse::not_equal

    fn StreamingResponse::not_equal(x : StreamingResponse, y : StreamingResponse) -> Bool

    compose

    fn compose(middlewares : Array[((Request) -> Response) -> ((Request) -> Response)], base : (Request) -> Response) -> ((Request) -> Response)

    Compose middlewares over a base handler. The first element is the outermost wrapper, matching registration order.

    run_http

    The synchronous counterpart of to_asgi: drive a Handler over an already materialised inbound event sequence and return the outbound events a server would send — [HttpResponseStart, HttpResponseBody] for an http scope, [] otherwise. A thin run_http_app wrapper over Response::events, the sans-transport core to_asgi mirrors with async receive/send.

    run_http_app

    The Request-level sans-transport core: drain the http request body from inbound, hand the assembled Request to app, and return the outbound events app emits. The ergonomic wrapper over run_http_scoped for apps that only need the request line, headers, and body — every server and the TestClient drive a Handler through it. Non-http scopes yield [].

    run_http_scoped

    The scope-aware sans-transport core: drain the http request body from inbound (accumulating HttpRequest chunks until more_body is false) and hand app the full @spec.HttpScope alongside the assembled body, returning the outbound events app emits. This is the seam a framework binds to — a framework reads what the ergonomic Request drops: root_path (for mounted sub-apps), extensions (to gate a feature on what the server advertises), client/server peers, the asgi handshake, and state (the map a lifespan startup seeds and the server copies onto every request scope). It is the synchronous analog of the ASGI (scope, receive, send) callable for the common drain-then-handle shape; run_http_app is the Request-level wrapper over it. Non-http scopes yield [].

    run_http_scoped_drain

    run_http_scoped, but the app also learns why the body drain stopped. A handler that holds a connection open — long polling, a slow upload — needs to tell "the client sent everything" from "the client went away", which the plain form cannot: it runs the handler to completion either way and the response goes nowhere. Non-http scopes yield [].

    run_http_stream

    Drive a streaming StreamHandler over an inbound event sequence, returning the full outbound stream — HttpResponseStart, one HttpResponseBody per chunk (more_body: true on all but the last), and a trailing HttpResponseTrailers when the response carries trailers. The streaming counterpart of run_http.

    to_asgi

    Lift a synchronous Handler onto the load-bearing @spec.AsgiApp the server binds to. For an http scope it drains the request body — looping receive() and accumulating HttpRequest chunks until more_body is false — assembles a Request, runs the handler, then emits HttpResponseStart followed by a single HttpResponseBody. Non-http scopes (websocket, lifespan) are no-ops: this sugar covers request→response handlers only. Shares its drain, request assembly, and response serialisation with run_http, which tests the same logic without the async transport.

    Source Files