HTTP Applications with Luna Framework
Build powerful HTTP servers and web applications with Luna Framework. Full-featured HTTP server implementation for Lua with routing, middleware, and SSL support.
Important Note
HTTP applications are not available in the Python version of Luna Framework. If
you're using Luna with Python, please use standard HTTP libraries for Python instead of the
new_http_app and remove_http_app methods.
Luna HTTP Applications Features
Luna Framework provides a complete HTTP server implementation for Lua with SSL support, middleware system, routing, and a simple API for creating web applications of any complexity.
Advantages
Full HTTP Server
Complete HTTP/1.1 protocol implementation for creating web applications in Lua.
Flexible Routing
Support for all HTTP methods and route grouping with prefixes.
Built-in SSL Support
Secure connections with SSL/TLS support for data protection.
Middleware System
Extensible middleware architecture for request processing.
Built-in Templates
Pre-built middleware for common tasks like CORS, rate limiting, and static files.
High Performance
Optimized code for handling hundreds of simultaneous connections.
Basic Example
Simple example of an HTTP server with routing and middleware.
-- Create a new HTTP application
local app = luna.new_http_app({
name = "My HTTP Server",
debug = true,
no_errors = true,
error_handler = function(err)
print("HTTP Server Error:", err)
end,
new_client = function(ip, client_data)
print("New client connected from:", ip)
end,
close_client = function(ip, client_data, reason)
print("Client disconnected:", ip, reason)
end
})
-- Add middleware
app:use(body_parser())
app:use(logger())
-- Define routes
app:get("/", function(req, res, client_data)
res:status(200)
res:send("<h1>Hello, World!</h1>")
end)
app:get("/api/users", function(req, res, client_data)
res:json({users = {"Alice", "Bob", "Charlie"}})
end)
app:post("/api/users", function(req, res, client_data)
local user = req.body
-- Process user creation
res:status(201)
res:json({message = "User created", user = user})
end)
-- Start the server
app:listen(8080, "0.0.0.0")
Core Methods
Creates a new HTTP application with the specified configuration.
Configuration Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Application name (for debugging) |
| error_handler | function | No | Function for error handling |
| no_errors | boolean | No | If true, errors won't stop the server |
| debug | boolean | No | Enable debug mode (default: true) |
| new_client | function | No | Callback when new client connects: function(ip, client_data) |
| close_client | function | No | Callback when client disconnects: function(ip, client_data, reason) |
| timeout_client | function | No | Callback when client timeout: function(ip, client_data, inactive_time) |
| error_client | function | No | Callback when client error: function(ip, client_data, error_msg) |
Example:
local app = luna.new_http_app({
name = "My HTTP Server",
error_handler = function(err)
print("Server error:", err)
end,
no_errors = true,
debug = true,
new_client = function(ip, client_data)
print("New client:", ip)
end,
close_client = function(ip, client_data, reason)
print("Client disconnected:", ip, reason)
end,
timeout_client = function(ip, client_data, inactive_time)
print("Client timeout:", ip, inactive_time)
end,
error_client = function(ip, client_data, error_msg)
print("Client error:", ip, error_msg)
end
})
Removes an HTTP application and releases resources.
luna.remove_http_app("My HTTP Server")
-- or
luna.remove_http_app(app)
Application Methods
Starts the HTTP server and begins listening for connections.
Parameters:
| port | number | Yes | Port to listen on |
| host | string | Yes | Host to bind to (e.g., "0.0.0.0" or "127.0.0.1") |
| protocol | string | No | Protocol ("http" or "https") |
| ssl_config | table | No | SSL configuration for HTTPS |
Example:
-- Start HTTP server
app:listen(8080, "0.0.0.0")
-- Start HTTPS server
app:listen(8443, "0.0.0.0", "https", {
cert = "path/to/cert.pem",
key = "path/to/key.pem"
})
Stops the HTTP server.
app:stop()
print("HTTP server stopped")
Returns whether the server is currently running.
if app:is_running() then
print("Server is running")
else
print("Server is stopped")
end
Returns the number of currently connected clients.
local count = app:get_client_count()
print("Connected clients:", count)
Default Settings
Sets the default timeout for new clients (in seconds).
app:set_default_timeout(30) -- 30 seconds
Sets the default maximum header size for new clients (in bytes).
app:set_default_max_header_size(8192) -- 8KB
Sets the default maximum body size for new clients (in bytes).
app:set_default_max_body_size(1048576) -- 1MB
Middleware
Adds a middleware function to the application. Middleware functions are executed in the order they are added.
Parameters:
| middleware_function | function | Yes | Middleware function: function(req, res, next) |
Example:
app:use(function(req, res, next)
-- Log request
print("Request:", req.method, req.path)
-- Continue to next middleware/route
next()
end)
app:use(function(req, res, next)
-- Add custom header
res:setHeader("X-Powered-By", "Luna Framework")
next()
end)
Routing
Adds a GET route handler.
app:get("/users", function(req, res, client_data)
res:json({users = {"Alice", "Bob"}})
end)
Adds a POST route handler.
app:post("/users", function(req, res, client_data)
local user = req.body
-- Create user
res:status(201):json({message = "User created"})
end)
Adds a PUT route handler.
app:put("/users", function(req, res, client_data)
local userId = req.params.id
-- Update user
res:json({message = "User updated"})
end)
Adds a DELETE route handler.
app:delete("/users", function(req, res, client_data)
local userId = req.params.id
-- Delete user
res:status(204):send()
end)
Adds a PATCH route handler.
app:patch("/users", function(req, res, client_data)
local userId = req.params.id
-- Partially update user
res:json({message = "User updated"})
end)
Adds a HEAD route handler.
app:head("/health", function(req, res, client_data)
res:status(200):send()
end)
Adds an OPTIONS route handler.
app:options("/users", function(req, res, client_datas)
res:setHeader("Allow", "GET, POST, OPTIONS")
res:status(200):send()
end)
Route Groups
Creates a route group with a common prefix.
Parameters:
| prefix | string | Yes | Prefix for all routes in the group |
Example:
local api = app:group("/api")
api:get("/users", function(req, res, client_data)
-- Handles GET /api/users
end)
api:post("/users", function(req, res, client_data)
-- Handles POST /api/users
end)
-- Nested groups
local v1 = api:group("/v1")
v1:get("/status", function(req, res, client_data)
-- Handles GET /api/v1/status
end)
Template: static()
Serves static files from a directory.
Parameters:
| directory | string | Yes | Directory path to serve files from |
Example:
app:use(static(arg[1] .. "/public"))
-- Serves files from ./public directory
-- e.g., GET /style.css serves ./public/style.css
Template: cors()
Adds CORS (Cross-Origin Resource Sharing) headers to responses.
Options:
| origins | table | No | Allowed origins (default: {"*"}) |
| methods | table | No | Allowed HTTP methods |
| headers | table | No | Allowed headers |
| max_age | number | No | Max age in seconds (default: 86400) |
| credentials | boolean | No | Allow credentials (default: false) |
Example:
app:use(cors({
origins = {"https://example.com", "https://app.example.com"},
methods = {"GET", "POST", "PUT", "DELETE"},
headers = {"Content-Type", "Authorization"},
max_age = 3600,
credentials = true
}))
Template: rate_limited()
Adds rate limiting to prevent abuse.
Options:
| window_ms | number | No | Time window in milliseconds (default: 60000) |
| max_requests | number | No | Max requests per window (default: 100) |
| skip | function | No | Function to skip rate limiting: function(req) return boolean end |
| key_generator | function | No | Function to generate rate limit key: function(req) return string end |
| message | string | No | Error message (default: "Too many requests") |
| status_code | number | No | HTTP status code (default: 429) |
Example:
app:use(rate_limited({
window_ms = 60000, -- 1 minute
max_requests = 10, -- 10 requests per minute
skip = function(req)
-- Skip rate limiting for health checks
return req.path == "/health"
end,
key_generator = function(req)
-- Rate limit by IP
return req.headers["x-forwarded-for"] or req.client_data.ip or "unknown"
end
}))
Template: body_parser()
Parses request body for JSON and form data.
Automatically parses application/json and application/x-www-form-urlencoded request bodies.
Example:
app:use(body_parser())
app:post("/api/users", function(req, res, client_data)
-- req.body is automatically parsed
local user = req.body
res:json({message = "User created", user = user})
end)
Template: logger()
Logs HTTP requests.
Options:
| format | string | No | Log format (default: ":method :url :status :response-time ms") |
| stream | table | No | Output stream: { write = function(msg) end } |
| skip | function | No | Function to skip logging: function(req) return boolean end |
Example:
app:use(logger({
format = ":method :url :status :response-time ms",
stream = {
write = function(msg)
print(msg)
end
},
skip = function(req)
-- Skip logging for health checks
return req.path == "/health"
end
}))
Request Object
Properties and methods available in the request object passed to route handlers.
| Property | Type | Description |
|---|---|---|
| method | string | HTTP method (e.g., "GET", "POST") |
| path | string | Request path |
| query | table | Query parameters |
| headers | table | HTTP headers |
| body | string | Request body (parsed by body_parser) |
| client | client_data.socket | Client connection socket |
| client_data | client_data | Client connection data (direct reference) |
| rawRequest | string | Raw HTTP request string |
Example:
app:get("/search", function(req, res, client_data)
local query = req.query.q
local userAgent = req.headers["User-Agent"]
-- Access client data directly
print("Client IP:", req.client_data.ip)
print("Using SSL:", req.client_data.is_ssl)
print("Search query:", query)
print("User agent:", userAgent)
res:json({results = {}})
end)
Response Object
Methods available in the response object passed to route handlers.
Sets the HTTP status code.
res:status(404)
res:send("Not found")
Sets an HTTP header.
res:setHeader("Content-Type", "application/json")
res:setHeader("X-Custom-Header", "value")
Sends a response. Automatically sets Content-Type to "text/html" if not already set.
res:send("<h1>Hello World</h1>")
-- or
res:status(200):send("OK")
Sends a file as response. Automatically detects MIME type from file extension if not specified.
-- Send file with automatic MIME type detection
res:sendFile("/path/to/image.png")
-- Send file with custom MIME type
res:sendFile("/path/to/data.custom", "application/x-custom-format")
-- Send file with status code
res:status(200):sendFile("/path/to/document.pdf")
Sends a JSON response. Automatically sets Content-Type to "application/json".
res:json({message = "Success", data = {}})
-- or
res:status(201):json({id = 123, name = "John"})
Builds the HTTP response string. Usually called automatically.
local httpResponse = res:build()
Client Data
Properties and methods available in the client_data object.
| Property/Method | Type | Description |
|---|---|---|
| socket | socket | LuaSocket TCP socket (может быть SSL-оберткой) |
| raw_socket | socket | Исходный сокет (до SSL-обертки) |
| buffer | string | Client buffer |
| is_ssl | boolean | True if client uses SSL (HTTPS) |
| connect_time | number | Connection timestamp |
| last_activity | number | Last activity timestamp |
| ip | string | Client IP address |
| set_timeout(timeout) | function | Sets client timeout |
| set_max_header_size(size) | function | Sets max header size |
| set_max_body_size(size) | function | Sets max body size |
| timeout | number | Current timeout value |
| max_header_size | number | Current max header size |
| max_body_size | number | Current max body size |
Example:
app:use(function(req, res, next)
-- Set custom timeout for this client
req.client_data:set_timeout(60) -- 60 seconds
-- Log client info
print("Client IP:", req.client_data.ip)
print("Connected at:", req.client_data.connect_time)
print("Using SSL:", req.client_data.is_ssl)
print("Raw socket available:", req.client_data.raw_socket ~= nil)
-- Access raw socket if needed
if req.client_data.raw_socket then
print("Raw socket type:", type(req.client_data.raw_socket))
end
next()
end)
Constants
Luna Framework provides built-in constants for HTTP status codes and MIME types to make your code more readable and maintainable.
A table mapping HTTP status codes to their descriptive messages.
Available Status Codes:
| Code | Message | Description |
|---|---|---|
| 100 | Continue | Client should continue with request |
| 101 | Switching Protocols | Server is switching protocols |
| 200 | OK | Request succeeded |
| 201 | Created | Resource created successfully |
| 202 | Accepted | Request accepted for processing |
| 204 | No Content | Success but no content to return |
| 206 | Partial Content | Partial content delivered |
| 301 | Moved Permanently | Resource moved permanently |
| 302 | Found | Resource found at different location |
| 304 | Not Modified | Resource not modified since last request |
| 307 | Temporary Redirect | Temporary redirect |
| 400 | Bad Request | Malformed request |
| 401 | Unauthorized | Authentication required |
| 403 | Forbidden | Access denied |
| 404 | Not Found | Resource not found |
| 405 | Method Not Allowed | HTTP method not allowed |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Server encountered an error |
| 502 | Bad Gateway | Invalid response from upstream |
| 503 | Service Unavailable | Service temporarily unavailable |
| 504 | Gateway Timeout | Upstream server timeout |
Example Usage:
app:get("/api/users", function(req, res, client_data)
-- Using status codes constants
res:status(app.STATUS_CODES[200])
res:json({users = {"Alice", "Bob"}})
end)
app:post("/api/users", function(req, res, client_data)
-- Create user logic here
res:status(app.STATUS_CODES[201])
res:json({message = "User created successfully"})
end)
A table mapping file extensions to their corresponding MIME types.
Available MIME Types:
| Category | Extension | MIME Type |
|---|---|---|
| Text | html | text/html |
| htm | text/html | |
| css | text/css | |
| js | application/javascript | |
| json | application/json | |
| txt | text/plain | |
| md | text/markdown | |
| xml | application/xml | |
| Images | png | image/png |
| jpg | image/jpeg | |
| jpeg | image/jpeg | |
| gif | image/gif | |
| svg | image/svg+xml | |
| ico | image/x-icon | |
| webp | image/webp | |
| Audio/Video | mp3 | audio/mpeg |
| mp4 | video/mp4 | |
| csv | text/csv | |
| bin | application/octet-stream | |
| Fonts | woff | font/woff |
| woff2 | font/woff2 | |
| ttf | font/ttf | |
| eot | application/vnd.ms-fontobject | |
| Application | application/pdf | |
| zip | application/zip | |
| tar | application/x-tar | |
| gz | application/gzip |
Example Usage:
-- Using MIME types with static file serving
app:use(static("public"))
-- Setting correct Content-Type for API responses
app:get("/api/data", function(req, res, client_data)
res:setHeader("Content-Type", app.MIME_TYPES.json)
res:json({data = "some data"})
end)
-- Serving different content types based on request
app:get("/content", function(req, res, client_data)
local format = req.query.format or "json"
if format == "xml" then
res:setHeader("Content-Type", app.MIME_TYPES.xml)
res:send("<data>Some XML data</data>")
elseif format == "txt" then
res:setHeader("Content-Type", app.MIME_TYPES.txt)
res:send("Some plain text data")
else
res:setHeader("Content-Type", app.MIME_TYPES.json)
res:json({data = "Some JSON data"})
end
end)
HTTP/HTTPS Client
Lunac теперь включает встроенный HTTP/HTTPS клиент для выполнения веб-запросов. Поддерживает как синхронные, так и асинхронные запросы.
Инициализирует HTTP клиент. Должен быть вызван перед использованием методов fetch.
Configuration Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
| luna | table | No | Luna объект, если сервер находится в том же приложении (для fetch) |
| lunac | table | No | Lunac объект, если нужно работать параллельно при fetch |
Example:
-- Initialize HTTP client
lunac.http.init({
luna = luna, -- if server is in same app
lunac = lunac -- for parallel operation
})
Synchronous HTTP Requests
Выполняет синхронный HTTP/HTTPS запрос и возвращает результат.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
| url | string | Yes | URL для запроса (http:// или https://) |
| options | table | No | Опции запроса (method, headers, sink, etc.) |
Returns:
| result | mixed | Результат запроса или nil при ошибке |
| code | number | HTTP статус код или описание ошибки |
| headers | table | Заголовки ответа |
| status | string | Статус ответа |
Example:
local response_data = {}
local sync_data, sync_code, sync_headers, sync_status = lunac.http.fetch("https://www.google.com", {
sink = ltn12.sink.table(response_data)
})
if sync_data then
print("HTTPS -------------------")
print("Success:", true)
print("HTTP Code:", sync_code)
print("Status:", sync_status)
print("Data size:", #table.concat(response_data))
print("-------------------")
else
print("Error:", sync_code)
end
Asynchronous HTTP Requests
Выполняет асинхронный HTTP/HTTPS запрос и вызывает callback при завершении.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
| url | string | Yes | URL для запроса (http:// или https://) |
| options | table | No | Опции запроса (method, headers, sink, etc.) |
| callback | function | Yes | Функция обратного вызова: function(result, code, headers, status) |
Example:
local response_data = {}
lunac.http.noawait_fetch("http://www.google.com", {
sink = ltn12.sink.table(response_data)
}, function(result, code, headers, status)
print("HTTP -----------")
print("HTTP Code:", code)
print("Headers:", headers)
print("Status:", status)
print("Data size:", #table.concat(response_data))
end)
Advanced Examples
GET Request with Headers
-- GET request with custom headers
local response_data = {}
local result, code, headers = lunac.http.fetch("https://api.example.com/data", {
method = "GET",
headers = {
["Authorization"] = "Bearer token123",
["Content-Type"] = "application/json"
},
sink = ltn12.sink.table(response_data)
})
if result then
local data = table.concat(response_data)
print("Response:", data)
else
print("Request failed:", code)
end
POST Request with JSON Data
-- POST request with JSON body
local json = require("json") -- assuming JSON library is available
local request_data = json.encode({
name = "John Doe",
email = "john@example.com"
})
local response_data = {}
local result, code = lunac.http.fetch("https://api.example.com/users", {
method = "POST",
headers = {
["Content-Type"] = "application/json",
["Content-Length"] = tostring(#request_data)
},
source = ltn12.source.string(request_data),
sink = ltn12.sink.table(response_data)
})
if result then
print("User created successfully")
else
print("Failed to create user:", code)
end
Multiple Async Requests
-- Multiple asynchronous requests
local urls = {
"https://api.example.com/users",
"https://api.example.com/posts",
"https://api.example.com/comments"
}
local completed = 0
local total = #urls
for i, url in ipairs(urls) do
local response_data = {}
lunac.http.noawait_fetch(url, {
sink = ltn12.sink.table(response_data)
}, function(result, code, headers, status)
completed = completed + 1
print("Request", i, "completed:", code)
if completed == total then
print("All requests completed!")
end
end)
end