HTTP Client (ltf.http)
ltf.http is a low-level HTTP client built as a thin wrapper around libcurl. You create a handle, configure it using setopt(), perform the transfer, and then clean up the handle.
Getting started
http is exposed as a submodule of ltf:
local ltf = require("ltf")
local http = ltf.http
API reference
The typical request flow is:
- Create a handle with
http.new() - Configure it with
handle:setopt(...) - Execute with
handle:perform() - Release resources with
handle:cleanup()(recommended vialtf.defer)
Handle management
ltf.http.new()
Creates and returns a new HTTP handle.
Returns:
- (
http_handle): a newhttp_handleobject
The http_handle object
handle:setopt(curlopt, value) -> http_handle
Sets a libcurl option on the handle. This method is chainable.
Parameters:
curlopt(integer): one of thehttp.OPT_*constantsvalue(boolean|integer|string|string[]|function): option value
Returns:
- (
http_handle): the same handle (for chaining)
handle:perform()
Performs the transfer (blocking).
handle:cleanup()
Frees resources associated with the handle. You should call this for every handle you create.
Option constants (http.OPT_*)
ltf.http exports many OPT_* constants that map directly to libcurl CURLOPT_* options.
For detailed meaning of each option, refer to the official curl docs for curl_easy_setopt.
Below are some commonly used options:
OPT_URL(string) — request URLOPT_FOLLOWLOCATION(boolean|integer) — follow redirectsOPT_VERBOSE(boolean|integer) — enable verbose curl outputOPT_TIMEOUT_MS(integer) — total timeout in millisecondsOPT_POST(boolean|integer) — enable POSTOPT_POSTFIELDS(string) — POST bodyOPT_HTTPHEADER(string[]) — list of headers ({ "Header: value", ... })OPT_CUSTOMREQUEST(string) — custom method ("PUT","DELETE", ...)OPT_WRITEFUNCTION(function) — body callbackOPT_HEADERFUNCTION(function) — header callbackOPT_SSL_VERIFYPEER(boolean|integer) — enable/disable cert verification
Note: some options are defined as “long” by curl and may expect
0/1instead of Lua booleans. In practice, both are commonly accepted by wrappers—if you hit type issues, pass0or1.
Examples
Simple GET request
This example captures the response body into a Lua string.
local ltf = require("ltf")
local http = ltf.http
ltf.test({
name = "Simple GET request",
body = function()
local parts = {}
local handle = http.new()
ltf.defer(handle.cleanup, handle)
handle:setopt(http.OPT_URL, "https://api.github.com/zen")
handle:setopt(http.OPT_USERAGENT, "LTF-HTTP-Client/1.0")
handle:setopt(http.OPT_WRITEFUNCTION, function(data)
table.insert(parts, data)
return #data
end)
ltf.log_info("Performing GET request...")
handle:perform()
ltf.log_info("Request finished.")
local body = table.concat(parts)
ltf.print("Response:", body)
end,
})
POST JSON with headers
This example posts JSON and captures both headers and body.
local ltf = require("ltf")
local http = ltf.http
ltf.test({
name = "POST JSON data",
body = function()
local response_headers = {}
local response_body_parts = {}
local post_data = ltf.json.serialize({
title = "My LTF Test Post",
body = "This is a test from the LTF framework.",
userId = 1,
})
local handle = http.new()
ltf.defer(handle.cleanup, handle)
handle:setopt(http.OPT_URL, "https://jsonplaceholder.typicode.com/posts")
:setopt(http.OPT_POST, 1)
:setopt(http.OPT_POSTFIELDS, post_data)
:setopt(http.OPT_HTTPHEADER, {
"Content-Type: application/json; charset=UTF-8",
"Accept: application/json",
})
:setopt(http.OPT_HEADERFUNCTION, function(line)
table.insert(response_headers, (line:gsub("[\r\n]", "")))
return true
end)
:setopt(http.OPT_WRITEFUNCTION, function(data)
table.insert(response_body_parts, data)
return #data
end)
ltf.log_info("Performing POST request...")
handle:perform()
ltf.log_info("Request finished.")
local response_body = table.concat(response_body_parts)
ltf.log_debug("--- Response Headers ---")
for _, h in ipairs(response_headers) do
if #h > 0 then
ltf.log_debug(h)
end
end
ltf.log_debug("--- Response Body ---")
ltf.print(response_body)
local decoded = ltf.json.deserialize(response_body)
if decoded and decoded.id then
ltf.log_info("Successfully created post with ID:", decoded.id)
else
ltf.log_error("Failed to parse response or find ID.")
end
end,
})
Low-level access
ltf.http.low
http.low exposes the underlying low-level module (require("ltf-http")). Most users should use http.new() + handle:setopt() + handle:perform().
Type reference (Lua annotations)
--- @class http_handle
--- @field setopt fun(self: http_handle, curlopt: integer, value: boolean|integer|string|string[]|function): http_handle
--- @field perform fun(self: http_handle)
--- @field cleanup fun(self: http_handle)