http.server — Lilush API

←index

← http

Overview

Coroutine-per-connection HTTP/1.1 server using LEV async I/O, with keep-alive, chunked transfer encoding, response compression (gzip/deflate), and optional TLS with SNI support. Runs inside a lev.run() event loop.

Functions

NameSignature
server:process_requestserver:process_request(client, client_ip, count, redis_module) -> connection_state, err
server:serveserver:serve() -> nil, err
server:configureserver:configure(config) -> ok, err
newnew(config, handle) -> server, err

server:process_request(client, client_ip, count, redis_module) -> connection_state, err

Process a single HTTP request on an accepted connection

Reads one HTTP/1.1 request from client, invokes self.handle, sends the response, and returns the connection disposition string.

Return values:

count is the number of requests handled on this connection. When it reaches cfg.requests_per_connection the response is sent with Connection: close.

The context table passed to self.handle contains:

If handle returns all-nil values the request is assumed to have been proxied directly on the socket and the connection is kept alive without writing a response.

server:serve() -> nil, err

Start accepting connections in a coroutine-per-connection event loop

Enters the LEV event loop, binds the configured address/port, then accepts connections. Each connection is handled in a spawned coroutine.

For each accepted connection:

  1. Performs the TLS handshake if cfg.ssl is set

  2. Calls process_request in a keep-alive loop until the client closes or cfg.requests_per_connection is reached

  3. Closes the socket

The connection_limit setting controls the maximum number of concurrent connections (default 64).

server:configure(config) -> ok, err

Apply configuration and validate TLS cert/key paths

Merges config into the server's current configuration table and updates the logger level. If config.ssl is present, validates that cert/key files exist. TLS identities are pre-parsed once before the accept loop, while the handshake itself still happens per-connection inside starttls().

The ssl field format:

ssl = {
    default = { cert = "path/to/cert.pem", key = "path/to/key.pem" },
    hosts   = {
        ["example.com"] = { cert = "...", key = "..." },
    },
}

Returns nil, err if a certificate or key file cannot be found.

new(config, handle) -> server, err

Create a new HTTP server instance

Creates and configures an HTTP server. The handle function is called for each request with (method, query, args, headers, body, context) and must return content, status, response_headers.

The config table may override these defaults:

TLS configuration (ssl field):

{
  default = { cert = "path/to/cert", key = "path/to/key" },
  hosts = {
    ["domain.com"] = { cert = "...", key = "..." },
  },
}

The server uses a coroutine-per-connection model: each accepted connection is handled in a spawned coroutine within lev.run().

local server = require("http.server")

local srv, err = server.new(
    {
        ip   = "0.0.0.0",
        port = 8443,
        ssl  = {
            default = {
                cert = "/etc/tls/cert.pem",
                key  = "/etc/tls/key.pem",
            },
        },
    },
    function(method, query, args, headers, body, ctx)
        return "Hello!", 200, { ["content-type"] = "text/plain" }
    end
)
if not srv then error(err) end
srv:serve()  -- blocks forever