HttpServer
HttpServer module: server-side HTTP types and pipeline runner.
Built on top of Http (for Method, Header types) and WebSocket modules. Provides the Conn type, pipeline composition, and server startup.
Usage: HttpServer.new(4000) |> HttpServer.max_connections(500) |> HttpServer.idle_timeout(30) |> HttpServer.plug(logger) |> HttpServer.plug(router) |> HttpServer.listen()
Types
Upgrade type for WebSocket connections
The Conn type: holds everything about a request-response cycle. Modeled on Plug.Conn from Elixir. The method field is an Atom (e.g. :get, :post) — interned i64 hash, allocation-free and safe for pattern matching in compiled mode.
Functions
Store a key/value pair in the conn's assigns (a per-request store plugs use to pass data along the pipeline). Later assignments to the same key shadow earlier ones for get_assign; all are kept in assigns.
All assigns as a List((String, String)), most recent first (see assign).
Bind later servers to host, a dotted IPv4 address such as "127.0.0.1", instead of every interface. Call it before listen or spawn_n; it panics on anything that isn't an IPv4 address.
The conn's upgrade request: NoUpgrade (the default), or WebSocketUpgrade(handler) to hand the socket to a WebSocket handler after the pipeline.
The underlying TCP socket file descriptor of the conn.
Look up a value stored with assign: Some(value) for the most recent assignment to key, or None.
Examples
let conn = HttpServer.assign(conn, "user", "ann")
HttpServer.get_assign(conn, "user") -- Some("ann")Look up a request header by name, case-insensitively. Returns Some(value) for the first match in req_headers order, or None.
Examples
match HttpServer.get_req_header(conn, "Authorization") do
Some(v) -> check_token(conn, v)
None -> HttpServer.text(conn, 401, "unauthorized")
endLook up a response header set so far, case-insensitively. Because put_resp_header prepends, this returns the most recently put value.
Mark the conn halted without setting a response, so remaining plugs are skipped. The status stays whatever it was (0 if never set). A later send_resp on the halted conn panics.
True once the conn has been responded to or halted; run_pipeline then skips the remaining plugs.
Respond with an HTML body: adds content-type: text/html; charset=utf-8 and calls send_resp. Panics if the conn is already halted.
Set the idle keep-alive timeout in seconds (default 60).
Respond with a JSON body (already-encoded String): adds content-type: application/json and calls send_resp. Panics if the conn is already halted.
Bind the configured port and serve requests forever with the plug pipeline (via the runtime's http_server_listen). Each request becomes a Conn; the pipeline's result is written back as the response, or handed to the WebSocket handler if conn_upgrade is a WebSocketUpgrade. Does not return in normal operation. Requires IO.NetListen.
Set the maximum number of concurrent connections (default 1000).
Return the request method atom (:get, :post, …) of a Conn.
Convert a request-method atom to its uppercase string name, suitable for logging, response bodies, or rebuilding a request line. Returns "UNKNOWN" for any atom that is not one of the recognized HTTP verbs. Atoms use lowercase by convention: :get, :post, :put, :patch, :delete, :head, :options, :trace, :connect.
Examples
march> HttpServer.method_to_string(:get)
"GET"
march> HttpServer.method_to_string(:patch)
"PATCH"
march> HttpServer.method_to_string(:not_a_verb)
"UNKNOWN"A server config listening on port with no plugs, max_connections
- Nothing is bound until
listenor
spawn_n.
Examples
HttpServer.new(4000)
|> HttpServer.plug(fn conn -> HttpServer.text(conn, 200, "ok"))
|> HttpServer.listen()The raw request path without the query string, e.g. /users/42.
The request path split on / with empty segments removed: /users/42/ gives a two-element list of users and 42.
Return the numeric IP address of the connected peer (the socket's remote address), e.g. "203.0.113.7" or "2001:db8::1".
This is the transport-level address — it cannot be spoofed by request headers, unlike X-Forwarded-For. Behind a reverse proxy it is the proxy's address, not the end client's.
Returns "" when no live socket backs the conn (e.g. test conns).
Append a plug (Conn -> Conn) to the end of the server's pipeline. Plugs run in the order they were added.
Add a response header. The header is prepended and existing headers with the same name are NOT replaced, so putting a name twice sends it twice.
The raw query string after the ?, e.g. page=1&limit=10; empty when the URL has none.
Respond 302 Found with a location: url header and an empty body. Panics if the conn is already halted.
The request body (the Content-Length bytes) as a String; empty when there is none.
All request headers as a List(Header). The runtime builds the list by prepending, so it is in reverse wire order.
The response body set so far; empty until a response is sent.
The response headers set so far, most recently put first.
Run a list of plugs (Conn -> Conn functions) in order, threading the conn through. Before each plug the conn is checked: once it is halted the remaining plugs are skipped and the halted conn is returned.
Examples
HttpServer.run_pipeline(conn, [logger, auth, router])Set the response status and body and mark the conn halted. This is the primary way to respond; later plugs in the pipeline are skipped. The response is written by the runtime after the pipeline returns.
Panics if the conn is already halted (by send_resp, a helper such as text/json, or halt): a double response is always a handler bug, and silently overwriting the first reply would mask it.
Examples
conn
|> HttpServer.put_resp_header("cache-control", "no-store")
|> HttpServer.send_resp(200, "ok")Start the server in a forked subprocess that handles exactly n requests and then exits. Returns a handle (the child PID) for wait_for. A readiness pipe makes the call return only after the child has bound the port, so the caller can connect immediately. Intended for tests.
Examples
let h = HttpServer.new(18080)
|> HttpServer.plug(fn conn -> HttpServer.text(conn, 200, "pong"))
|> HttpServer.spawn_n(1)
let resp = HttpClient.get(HttpClient.new_client(), "http://127.0.0.1:18080/")
HttpServer.wait_for(h)The response status set so far: 0 until send_resp or a helper sets it. A conn returned with status 0 is sent as 200.
Respond with a plain-text body: adds content-type: text/plain; charset=utf-8 and calls send_resp. Panics if the conn is already halted.
Examples
fn hello(conn) do
HttpServer.text(conn, 200, "hello, " ++ HttpServer.path(conn))
endBlock until the server subprocess started by spawn_n (given its handle) exits.