fcgi

FastCGI Responder server.

Types

Listen address set on a Builder via listen_unix or listen_tcp.

pub opaque type Address

Streaming reader for the request body. The handler receives one in req.body; call it to obtain the first Read, then advance via the consume continuation returned in each Chunk.

pub type BodyReader =
  fn() -> Result(Read, ReadError)

Server configuration produced by new. Pass it to start to begin listening.

pub opaque type Builder(address)

Trusted CGI metadata supplied by the upstream proxy and handed to every request handler alongside the Request.

pub type Context {
  Context(
    remote_addr: option.Option(String),
    remote_port: option.Option(Int),
    remote_host: option.Option(String),
    remote_user: option.Option(String),
    auth_type: option.Option(String),
    script_name: option.Option(String),
    server_protocol: option.Option(String),
    server_software: option.Option(String),
    extra: dict.Dict(String, String),
  )
}

Constructors

  • Context(
      remote_addr: option.Option(String),
      remote_port: option.Option(Int),
      remote_host: option.Option(String),
      remote_user: option.Option(String),
      auth_type: option.Option(String),
      script_name: option.Option(String),
      server_protocol: option.Option(String),
      server_software: option.Option(String),
      extra: dict.Dict(String, String),
    )

    Arguments

    remote_addr

    Client address as reported by the proxy (CGI REMOTE_ADDR).

    remote_port

    Client port parsed as Int; absent if the proxy did not supply a numeric value (CGI REMOTE_PORT).

    remote_host

    Reverse-DNS hostname of the client, when the proxy resolved one (CGI REMOTE_HOST).

    remote_user

    Authenticated identity, when the proxy set one (CGI REMOTE_USER).

    auth_type

    Authentication scheme, e.g. "Basic" (CGI AUTH_TYPE).

    script_name

    Mount prefix assigned to the app by the proxy (CGI SCRIPT_NAME).

    server_protocol

    HTTP protocol version reported by the proxy, e.g. "HTTP/1.1" (CGI SERVER_PROTOCOL).

    server_software

    Identification string from the upstream proxy (CGI SERVER_SOFTWARE).

    extra

    Any other CGI variables, keyed by their original uppercase name. Typical entries include "DOCUMENT_ROOT" and "REQUEST_URI".

Why send_file could not produce a File body.

pub type FileError {
  FileNotFound(path: String)
  FileAccessDenied(path: String)
  FileIsDirectory(path: String)
  FileOther(path: String, reason: String)
  InvalidRange(offset: Int, limit: option.Option(Int))
}

Constructors

  • FileNotFound(path: String)

    No file exists at the given path.

  • FileAccessDenied(path: String)

    The process lacks permission to read the file.

  • FileIsDirectory(path: String)

    The path resolved to a directory, not a file.

  • FileOther(path: String, reason: String)

    Any other filesystem error, with the underlying reason as a human-readable string.

  • InvalidRange(offset: Int, limit: option.Option(Int))

    offset is negative, or limit is Some(n) with n < 0.

What a BodyReader produced.

pub type Read {
  Chunk(data: BitArray, consume: fn() -> Result(Read, ReadError))
  End
}

Constructors

  • Chunk(data: BitArray, consume: fn() -> Result(Read, ReadError))

    A chunk of body bytes plus a consume continuation that returns the next chunk when called.

  • End

    The body has been fully delivered.

Why a body read failed.

pub type ReadError {
  ClientDisconnected
  ReadTimeout
  BodyTooLarge
  RequestAborted
}

Constructors

  • ClientDisconnected

    The connection from the upstream proxy was closed before the body was fully delivered.

  • ReadTimeout

    No chunk arrived within the configured body_read_timeout.

  • BodyTooLarge

    The body exceeded max_body_size.

  • RequestAborted

    The upstream proxy sent FCGI_ABORT_REQUEST or an unrecoverable framing error while the handler was reading the body. The connection will be closed after the handler returns and the handler’s response, if any, is discarded.

What the server should send back as a response body. Construct with bytes, send_file, or stream.

pub opaque type ResponseData

Running server returned by start. bound_port is Some(port) for a TCP listener (useful when listen_tcp was given port 0 and the kernel chose an ephemeral port) and None for a Unix-domain listener.

pub type Server {
  Server(
    supervisor: static_supervisor.Supervisor,
    bound_port: option.Option(Int),
  )
}

Constructors

Why the listener could not start.

pub type StartError {
  ListenerError(reason: String)
  InvalidMaxBodySize(bytes: Int)
  InvalidBodyReadTimeout(milliseconds: Int)
}

Constructors

  • ListenerError(reason: String)

    Wraps a failure from the underlying listener, such as bind or listen failures, an unparseable TCP host, or a Unix-socket path that is already in use.

  • InvalidMaxBodySize(bytes: Int)

    max_body_size was set to a negative value.

  • InvalidBodyReadTimeout(milliseconds: Int)

    body_read_timeout was set to zero or a negative value.

Use send_chunk to emit body bytes; each call writes one or more FastCGI STDOUT records on the open connection.

pub opaque type StreamSender

Values

pub fn body_read_timeout(
  builder: Builder(address),
  milliseconds: Int,
) -> Builder(address)

Set how long the server waits for the next stdin or params record before giving up. Must be > 0. Applies between successive socket reads, including the wait for the first record after accept, not to the request as a whole. Returns Error(ReadTimeout) to body readers. Default: 30,000 ms.

pub fn bytes(content: bytes_tree.BytesTree) -> ResponseData

Build an in-memory response body. The whole BytesTree is sent in one or more STDOUT records.

pub fn listen_tcp(
  builder: Builder(address),
  host host: String,
  port port: Int,
) -> Builder(Address)

Set the TCP host and port the server listens on. host must be a numeric IP literal: either IPv4 (e.g. "127.0.0.1", "0.0.0.0") or IPv6 (e.g. "::1", "::").

pub fn listen_unix(
  builder: Builder(address),
  path: String,
) -> Builder(Address)

Set the Unix domain socket path the server listens on.

pub fn max_body_size(
  builder: Builder(address),
  bytes: Int,
) -> Builder(address)

Set the maximum body bytes the server will deliver to the handler in total across all body reads. Must be >= 0; start returns InvalidMaxBodySize(bytes) for negative values.

When the peer sends more than this many bytes, the next body read returns Error(BodyTooLarge). The handler may respond as it sees fit, but the connection is closed after the response is sent because remaining body bytes cannot be safely drained.

Default: 256 MiB.

pub fn new(
  handler: fn(
    request.Request(fn() -> Result(Read, ReadError)),
    Context,
  ) -> response.Response(ResponseData),
) -> Builder(Nil)

Build a new FastCGI server with the given handler.

The handler is invoked once Params is fully received. The request body is delivered incrementally via a BodyReader in req.body; call it to obtain the first Read and thread consume to advance, or pass it to read_all for the buffered case.

Default: 256 MiB max body, 30 s body read timeout.

pub fn read_all(
  read: fn() -> Result(Read, ReadError),
) -> Result(bytes_tree.BytesTree, ReadError)

Buffer the entire body into a BytesTree. The server’s max_body_size setting is the upper bound; the underlying reader returns BodyTooLarge if the peer exceeds it.

The buffered length reflects what the upstream proxy actually sent, not what CONTENT_LENGTH advertised.

pub fn send_chunk(
  sender: StreamSender,
  data: bytes_tree.BytesTree,
) -> Result(Nil, Nil)

Emit a chunk of body bytes from inside a Stream producer.

Returns Ok(Nil) when the chunk is written, or Error(Nil) when the underlying socket write fails (for example, the upstream proxy has disconnected).

pub fn send_file(
  path path: String,
  offset offset: Int,
  limit limit: option.Option(Int),
) -> Result(ResponseData, FileError)

Open a file and return a response body that streams it via file:sendfile/5 when the response is sent.

Returns FileError if the file is missing, inaccessible, a directory, or if offset or limit is negative.

pub fn start(
  builder: Builder(Address),
) -> Result(actor.Started(Server), StartError)

Start the server.

pub fn stream(producer: fn(StreamSender) -> Nil) -> ResponseData

Build a streaming response body. The server calls producer(sender) after sending the response headers; each call to send_chunk(sender, data) writes one or more STDOUT records to the upstream proxy.

A panic raised by producer is caught; the response end records are still emitted so the upstream proxy sees a clean end-of-request.

pub fn string(content: String) -> ResponseData

Build a response body from a String.

pub fn supervised(
  builder: Builder(Address),
) -> supervision.ChildSpecification(Server)

Build a supervision.ChildSpecification so the server runs under an OTP supervisor.

Search Document