serv_info_table = httpd.bind(arg:table)Create an HTTP server with advanced routing, security, and performance features. Returns a serv_info_table with server instance and connection details.
keys in serv_info_table
serv the server instance.
rebind() rebind the same host/port.(e.g. resume back in mobile device, rebind port.)host bind host.port bind port.router Router instance for advanced routing.get(path, handler) Add GET route with optional path parameters (:param)post(path, handler) Add POST route with optional path parametersput(path, handler) Add PUT route with optional path parametersdelete(path, handler) Add DELETE route with optional path parametersuse(middleware) Add middleware function to request pipelineset_not_found(handler) Set custom 404 handlerget_stats() Get performance statisticskeys in the arg:
host: string?http service listening host, default “0.0.0.0”
port: integer?http service listening port, leave empty for random port that available.
onServiceon request callback, arg1 => http_request, arg2 => http_response
certssl support, if using core config.httpd_using_core, this property only available with libevent2.1.5+
keyssl support, if using core config.httpd_using_core, this property only available with libevent2.1.5+
properties:
path:stringquery:stringmethod:stringparams:tableENHANCED: URL parameters from query string AND form data. With new router, also includes path parameters (e.g., /user/:id → params.id)
body:stringRequest body content. NEW: Automatically parsed and validated against configured limits.
headers:tableENHANCED: RFC 7230 compliant header parsing. Format: {singlekey = "value", multikey = {"value1", "value2"}}
remoteip:stringENHANCED: Used for rate limiting and security monitoring.
remoteport:integerversion:stringHTTP version (1.0 or 1.1)
content_length:integerENHANCED: Strict RFC validation with configurable limits
apis:
available():integerreturn available input stream length to read.
read()read data buf from input stream, return nil if no data.
apis:
addheader(k:string, v:string)add one response header.
reply(status_code:integer, msg:string, bodydata:string?)response to client.
reply_start(status_code:integer, msg:string)reply_chunk(data:string)reply_end()The HTTP response now uses a state machine with proper validation:
PENDING → STARTED → COMPLETEDAdd these to your config.lua:
config = {
-- Content limits
max_content_length = 10485760, -- 10MB default
max_header_value_size = 8192, -- 8KB per header
max_uri_length = 2048, -- 2KB URI limit
-- Compression
gzip_min_size = 1024, -- Min size for compression
-- Security
enable_rate_limiting = true, -- Enable rate limiting
rate_limit_requests = 100, -- Requests per window
rate_limit_window = 60, -- Window in seconds
enable_security_middleware = true, -- Security headers
security_headers = { -- Custom headers
["Strict-Transport-Security"] = "max-age=31536000"
},
-- Performance
enable_performance_monitoring = true, -- Enable stats
connection_timeout = 30, -- Keep-alive timeout
}
local httpd = require "modules.fan.httpd.httpd"
local server = httpd.bind({host = "0.0.0.0", port = 8080})
-- Add routes
server:get("/", function(ctx)
ctx:reply(200, "OK", "Hello World!")
end)
server:get("/user/:id", function(ctx)
local user_id = ctx.params.id
ctx:reply(200, "OK", "User: " .. user_id)
end)
-- Add middleware
server:use(function(ctx, next)
print("Request:", ctx.method, ctx.path)
next()
end)
-- Custom 404
server:set_not_found(function(ctx)
ctx:reply(404, "Not Found", "Page not found")
end)
fan.loop()
local server = httpd.bind({host = "0.0.0.0", port = 8080})
server:get("/stats", function(ctx)
local stats = server:get_stats()
ctx:addheader("Content-Type", "application/json")
ctx:reply(200, "OK", json.encode(stats))
end)