A lightweight, efficient HTTP client for LuaJIT using FFI bindings to libcurl. This library provides a simple and elegant API for making HTTP requests without external dependencies (except libcurl itself).
- Pure LuaJIT implementation using FFI (no C compilation needed)
- Support for all common HTTP methods (GET, POST, PUT, DELETE, PATCH, HEAD)
- Custom headers support
- Request/response body handling
- Timeout configuration
- SSL/TLS support
- Redirect following
- JSON support (with optional lua-cjson)
- Streaming response handling
- Thread-safe and high-performance
- LuaJIT 2.0+
- libcurl (shared library)
- Windows:
libcurl.dllorlibcurl-4.dll - Linux:
libcurl.so - macOS:
libcurl.dylib
- Windows:
- Optional:
lua-cjsonfor JSON encoding/decoding
Windows (MSYS2/MinGW):
pacman -S mingw-w64-x86_64-curlLinux (Debian/Ubuntu):
sudo apt-get install libcurl4-openssl-devLinux (Fedora/RHEL):
sudo dnf install libcurl-develmacOS:
brew install curlSimply copy the files to your project or add them to your LUA_PATH:
# Copy to your project
cp curl_ffi.lua http_client.lua /path/to/your/project/
# Or set LUA_PATH
export LUA_PATH="$LUA_PATH;/path/to/c_lua_http_client/?.lua"local http = require("http_client")
-- Simple GET request
local response, err = http.get("https://api.example.com/users")
if response then
print("Status: " .. response.status)
print("Body: " .. response.body)
print("Content-Type: " .. response.content_type)
else
print("Error: " .. err)
endPerform a GET request.
local response, err = http.get("https://example.com", {
headers = {
["Authorization"] = "Bearer token123"
},
timeout = 10
})Perform a POST request.
local response, err = http.post("https://example.com/api", {
body = "key=value",
headers = {
["Content-Type"] = "application/x-www-form-urlencoded"
}
})Perform a PUT request.
Perform a DELETE request.
Perform a PATCH request.
Perform a HEAD request.
Perform a request with JSON encoding/decoding (requires lua-cjson).
local response, err = http.json("POST", "https://api.example.com/users", {
name = "John Doe",
email = "john@example.com"
})
if response and response.json then
print("User ID: " .. response.json.id)
endCreate a custom client instance with default options:
local client = http.new({
timeout = 30,
connect_timeout = 10,
follow_redirects = true,
max_redirects = 10,
user_agent = "MyApp/1.0",
verify_ssl = true,
verbose = false
})
local response, err = client:get("https://example.com")All request methods accept an options table:
{
-- Request body (string)
body = "request data",
-- Custom headers (table)
headers = {
["Content-Type"] = "application/json",
["Authorization"] = "Bearer token"
},
-- Timeout in seconds
timeout = 30,
-- Connection timeout in seconds
connect_timeout = 10,
-- Follow redirects
follow_redirects = true,
-- Maximum number of redirects
max_redirects = 10,
-- Custom User-Agent
user_agent = "MyApp/1.0",
-- Verify SSL certificates
verify_ssl = true,
-- Verbose output (for debugging)
verbose = false
}Successful requests return a response table:
{
status = 200, -- HTTP status code
headers = { -- Response headers (lowercase keys)
["content-type"] = "application/json",
["content-length"] = "1234"
},
body = "response body", -- Response body as string
content_type = "application/json", -- Content-Type header value
url = "https://final.url", -- Final URL (after redirects)
json = { ... } -- Parsed JSON (only with http.json())
}Failed requests return nil, error_message, partial_response.
local http = require("http_client")
local response, err = http.get("https://httpbin.org/get")
if response then
print("Status:", response.status)
print("Body:", response.body)
endlocal response, err = http.post("https://httpbin.org/post", {
body = "name=John&email=john@example.com",
headers = {
["Content-Type"] = "application/x-www-form-urlencoded"
}
})local response, err = http.json("POST", "https://httpbin.org/post", {
name = "Alice",
age = 30
})
if response and response.json then
-- Access parsed JSON response
print(response.json.data)
endlocal response, err = http.get("https://api.example.com/data", {
headers = {
["Authorization"] = "Bearer your-token",
["Accept"] = "application/json",
["X-Custom-Header"] = "value"
}
})local response, err = http.get("https://slow-server.com", {
timeout = 5, -- 5 second timeout
connect_timeout = 2
})
if not response then
print("Request failed:", err)
endlocal client = http.new({
timeout = 60,
user_agent = "MyBot/2.0",
verify_ssl = false -- Disable SSL verification (not recommended)
})
local response = client:get("https://self-signed-cert.com")local response, err = http.get("https://httpbin.org/redirect/5", {
follow_redirects = true,
max_redirects = 10
})
if response then
print("Final URL:", response.url)
print("Status:", response.status)
endAlways check for errors:
local response, err, partial = http.get("https://invalid-url")
if not response then
print("Error:", err)
if partial then
print("Partial response available")
print("Status:", partial.status)
end
else
print("Success:", response.status)
endFor advanced usage, you can use the low-level FFI bindings directly:
local curl_ffi = require("curl_ffi")
local handle = curl_ffi.init()
curl_ffi.setopt(handle, ffi.C.CURLOPT_URL, "https://example.com")
-- ... more options
local result = curl_ffi.perform(handle)
curl_ffi.cleanup(handle)- curl_ffi.lua - Low-level FFI bindings to libcurl
- http_client.lua - High-level HTTP client wrapper
- example.lua - Usage examples and test cases
This library uses LuaJIT's FFI which provides near-native performance for libcurl operations. The overhead is minimal compared to pure C implementations.
Each request creates its own CURL handle, making it safe to use from multiple coroutines. However, the global libcurl initialization is done once at module load time.
- Binary file uploads require manual setup (use low-level API)
- No built-in progress callbacks (can be added using low-level API)
- Multipart form data requires manual construction
This project is provided as-is for educational and commercial use.
Contributions are welcome. Please ensure code follows the existing style and includes examples.
Make sure libcurl is installed and in your system's library path:
Windows: Add the directory containing libcurl.dll to your PATH
Linux/macOS: Use ldconfig or set LD_LIBRARY_PATH
If you encounter SSL certificate verification errors:
-- Disable verification (not recommended for production)
local response = http.get(url, { verify_ssl = false })Adjust timeout values based on your network conditions:
local response = http.get(url, {
timeout = 60, -- Total request timeout
connect_timeout = 10 -- Connection timeout
})libcurl version can be checked with:
local http = require("http_client")
print(http.version())