livery_translator (livery v0.13.0)

View Source

The per-request translator shared by the HTTP adapters.

Every wire library (h1, h2, quic_h3) runs Livery's handler fun in a process of its own for each request, and sends that request's body, trailers and resets to it in the library's own message format. This module turns that process into the request's translator:

  • it starts the request worker (livery_req_proc), which runs the middleware and the handler;
  • it turns the library's events into the {livery_body, Ref, _} messages livery_body reads in the worker, so neither the body reader nor the handler ever sees a protocol-specific message;
  • it enforces the listener's max_body before the bytes reach the worker's mailbox;
  • it notices a disconnect (stream reset or connection gone) while the worker is busy in user code, and tells it (livery_disconnect).

It runs until the worker exits. An adapter builds the request, registers the stream with its library if the library needs that, and calls serve/2 with:

  • tag and key: the first two elements of the library's {Tag, Key, Event} messages for this request (h1_stream and the stream id on H1; h2 or quic_h3 and the connection on H2 and H3);
  • classify: turns Event into {data, Chunk, Fin}, {trailers, Headers}, {reset, Reason} or ignore;
  • conn: the connection process, watched for a disconnect;
  • credit: optional, called with the number of body bytes the handler has just read (livery_body:read/2 reports them), so the adapter can return that credit to its wire library's flow control. Without it the library's own window management applies;
  • on_abandoned: what to tell the client when the worker finishes while the body is still coming (unread, or over max_body), or none.

An adapter that hands the stream to another process (a WebSocket or WebTransport session) calls release/1 first, so the translator lets the stream go instead of telling the client to stop.

You do not call it directly.

Summary

Functions

Called from the request worker by an adapter about to hand the stream to another process (accept_ws/4, accept_wt/4): the translator stops treating the body as unfinished, so it does not tell the client to stop when the worker exits.

Start the worker for Req and translate until it exits. Answers 503 through the adapter when the request limit refuses it.

Split a request target into its path and its query string.

Translate for Worker until it exits. serve/2 calls it once the worker is started; it is exported for the unit tests.

Types

event()

-type event() ::
          {data, binary(), boolean()} | {trailers, [{binary(), binary()}]} | {reset, term()} | ignore.

opts()

-type opts() ::
          #{adapter := module(),
            stream := livery_adapter:stream(),
            stack := livery_middleware:stack(),
            handler := livery_middleware:handler(),
            max_body := non_neg_integer() | infinity,
            config := term(),
            raw_query := binary(),
            wire := wire(),
            atom() => term()}.

wire()

-type wire() ::
          #{tag := atom(),
            key := term(),
            classify := fun((term()) -> event()),
            conn := pid(),
            credit => none | fun((non_neg_integer()) -> term()),
            on_abandoned := none | fun(() -> term())}.

Functions

release/1

-spec release(livery_req:req()) -> ok.

Called from the request worker by an adapter about to hand the stream to another process (accept_ws/4, accept_wt/4): the translator stops treating the body as unfinished, so it does not tell the client to stop when the worker exits.

serve/2

-spec serve(livery_req:req(), opts()) -> ok.

Start the worker for Req and translate until it exits. Answers 503 through the adapter when the request limit refuses it.

split_query(Path)

-spec split_query(binary()) -> {binary(), binary()}.

Split a request target into its path and its query string.

translate/2

-spec translate(pid(),
                #{body_ref := reference(),
                  disc_ref := reference(),
                  max_body := non_neg_integer() | infinity,
                  wire := wire()}) ->
                   ok.

Translate for Worker until it exits. serve/2 calls it once the worker is started; it is exported for the unit tests.