HTTP 客户端
向外部服务发起 HTTP 请求。支持所有 HTTP 方法、头部、查询参数、表单数据、文件上传、流式响应和并发批量请求。
加载
local http_client = require("http_client")
HTTP 方法
所有方法具有相同的签名:method(url, options?) 返回 Response, error。
GET 请求
local resp, err = http_client.get("https://api.example.com/users")
if err then
return nil, err
end
print(resp.status_code) -- 200
print(resp.body) -- response body
POST 请求
local resp, err = http_client.post("https://api.example.com/users", {
headers = {["Content-Type"] = "application/json"},
body = json.encode({name = "Alice", email = "alice@example.com"})
})
PUT 请求
local resp, err = http_client.put("https://api.example.com/users/123", {
headers = {["Content-Type"] = "application/json"},
body = json.encode({name = "Alice Smith"})
})
PATCH 请求
local resp, err = http_client.patch("https://api.example.com/users/123", {
body = json.encode({status = "active"})
})
DELETE 请求
local resp, err = http_client.delete("https://api.example.com/users/123", {
headers = {["Authorization"] = "Bearer " .. token}
})
HEAD 请求
仅返回头部,无正文。
local resp, err = http_client.head("https://cdn.example.com/file.zip")
local size = resp.headers["Content-Length"]
自定义方法
local resp, err = http_client.request("PROPFIND", "https://dav.example.com/folder", {
headers = {["Depth"] = "1"}
})
| 参数 | 类型 | 描述 |
|---|---|---|
method |
string | HTTP 方法 |
url |
string | 请求 URL |
options |
table | 请求选项(可选) |
请求选项
| 字段 | 类型 | 描述 |
|---|---|---|
headers |
table | 请求头 {["Name"] = "value"} |
body |
string | 请求正文 |
query |
table | 查询参数 {key = "value"} |
form |
table | 表单数据(自动设置 Content-Type) |
files |
table | 文件上传(文件定义数组) |
cookies |
table | 请求 Cookie {name = "value"} |
auth |
table | Basic 认证 {user = "name", pass = "secret"} |
timeout |
number/string | 超时:数字为秒,或字符串如 "30s"、"1m" |
stream |
boolean | 流式响应正文而非缓冲 |
max_response_body |
number | 最大响应大小(字节)(0 = 默认) |
unix_socket |
string | 通过 Unix socket 路径连接 |
tls |
table | 每请求 TLS 配置(参见 TLS 选项) |
查询参数
local resp, err = http_client.get("https://api.example.com/search", {
query = {
q = "lua programming",
page = "1",
limit = "20"
}
})
头部和认证
local resp, err = http_client.get("https://api.example.com/data", {
headers = {
["Authorization"] = "Bearer " .. token,
["Accept"] = "application/json"
}
})
-- Or use basic auth
local resp, err = http_client.get("https://api.example.com/data", {
auth = {user = "admin", pass = "secret"}
})
表单数据
local resp, err = http_client.post("https://api.example.com/login", {
form = {
username = "alice",
password = "secret123"
}
})
文件上传
local resp, err = http_client.post("https://api.example.com/upload", {
form = {title = "My Document"},
files = {
{
name = "attachment", -- form field name
filename = "report.pdf", -- original filename
content = pdf_data, -- file content
content_type = "application/pdf"
}
}
})
| 文件字段 | 类型 | 必需 | 描述 |
|---|---|---|---|
name |
string | 是 | 表单字段名 |
filename |
string | 否 | 原始文件名 |
content |
string | 是* | 文件内容 |
reader |
userdata | 是* | 替代方案:用于内容的 io.Reader |
content_type |
string | 否 | MIME 类型(默认:application/octet-stream) |
*content 或 reader 必须提供其一。
超时
-- Number: seconds
local resp, err = http_client.get(url, {timeout = 30})
-- String: Go duration format
local resp, err = http_client.get(url, {timeout = "30s"})
local resp, err = http_client.get(url, {timeout = "1m30s"})
local resp, err = http_client.get(url, {timeout = "1h"})
TLS 选项
配置每请求的 TLS 设置,用于 mTLS(双向 TLS)和自定义 CA 证书。
| 字段 | 类型 | 描述 |
|---|---|---|
cert |
string | PEM 格式的客户端证书 |
key |
string | PEM 格式的客户端私钥 |
ca |
string | PEM 格式的自定义 CA 证书 |
server_name |
string | 用于 SNI 验证的服务器名称 |
insecure_skip_verify |
boolean | 跳过 TLS 证书验证 |
cert 和 key 必须同时提供才能使用 mTLS。ca 字段会使用自定义 CA 覆盖系统证书池。
mTLS 认证
local cert_pem = fs.read("/certs/client.crt")
local key_pem = fs.read("/certs/client.key")
local resp, err = http_client.get("https://secure.example.com/api", {
tls = {
cert = cert_pem,
key = key_pem,
}
})
自定义 CA
local ca_pem = fs.read("/certs/internal-ca.crt")
local resp, err = http_client.get("https://internal.example.com/api", {
tls = {
ca = ca_pem,
server_name = "internal.example.com",
}
})
跳过不安全验证
在开发环境中跳过 TLS 验证。需要 http_client.insecure_tls 安全权限。
local resp, err = http_client.get("https://localhost:8443/api", {
tls = {
insecure_skip_verify = true,
}
})
响应对象
| 字段 | 类型 | 描述 |
|---|---|---|
status_code |
number | HTTP 状态码 |
body |
string | 响应正文(非流式时) |
body_size |
number | 正文大小(字节)(流式时为 -1) |
headers |
table | 响应头 |
cookies |
table | 响应 Cookie |
url |
string | 最终 URL(重定向后) |
stream |
Stream | 流对象(stream = true 时) |
local resp, err = http_client.get("https://api.example.com/data")
if err then
return nil, err
end
if resp.status_code == 200 then
local data = json.decode(resp.body)
print("Content-Type:", resp.headers["Content-Type"])
end
流式响应
对于大型响应,使用流式以避免将整个正文加载到内存中。
local resp, err = http_client.get("https://cdn.example.com/large-file.zip", {
stream = true
})
if err then
return nil, err
end
-- Process in chunks
while true do
local chunk, err = resp.stream:read(65536)
if err or not chunk then break end
-- process chunk
end
resp.stream:close()
| 流方法 | 返回 | 描述 |
|---|---|---|
read(size) |
string, error | 读取最多 size 字节 |
close() |
- | 关闭流 |
批量请求
并发执行多个请求。
local responses, errors = http_client.request_batch({
{"GET", "https://api.example.com/users"},
{"GET", "https://api.example.com/products"},
{"POST", "https://api.example.com/log", {body = "event"}}
})
if errors then
for i, err in ipairs(errors) do
if err then
print("Request " .. i .. " failed:", err)
end
end
else
-- All succeeded
for i, resp in ipairs(responses) do
print("Response " .. i .. ":", resp.status_code)
end
end
| 参数 | 类型 | 描述 |
|---|---|---|
requests |
table | {method, url, options?} 数组 |
返回: responses, errors - 按请求位置索引的数组
注意:
- 请求并发执行
- 批量中不支持流式(
stream = true) - 结果数组与请求顺序匹配(从 1 开始索引)
URL 编码
编码
local encoded = http_client.encode_uri("hello world")
-- "hello+world"
local url = "https://api.example.com/search?q=" .. http_client.encode_uri(query)
解码
local decoded, err = http_client.decode_uri("hello+world")
-- "hello world"
权限
HTTP 请求受安全策略评估约束。
安全操作
| 操作 | 资源 | 描述 |
|---|---|---|
http_client.request |
URL | 允许/拒绝对特定 URL 的请求 |
http_client.unix_socket |
Socket 路径 | 允许/拒绝 Unix socket 连接 |
http_client.private_ip |
IP 地址 | 允许/拒绝访问私有 IP 范围 |
http_client.insecure_tls |
URL | 允许/禁止不安全的 TLS(跳过验证) |
检查访问权限
local security = require("security")
if security.can("http_client.request", "https://api.example.com/users") then
local resp = http_client.get("https://api.example.com/users")
end
SSRF 防护
私有 IP 范围(10.x、192.168.x、172.16-31.x、localhost)默认被阻止。访问需要 http_client.private_ip 权限。
local resp, err = http_client.get("http://192.168.1.1/admin")
-- Error: not allowed: private IP 192.168.1.1
参见 安全模型 了解策略配置。
错误
| 条件 | 类型 | 可重试 |
|---|---|---|
| 安全策略拒绝 | errors.PERMISSION_DENIED |
否 |
| 私有 IP 被阻止 | errors.PERMISSION_DENIED |
否 |
| Unix socket 被拒绝 | errors.PERMISSION_DENIED |
否 |
| 不安全 TLS 被拒绝 | errors.PERMISSION_DENIED |
否 |
| 无效的 URL 或选项 | errors.INVALID |
否 |
| 无上下文 | errors.INTERNAL |
否 |
| 网络故障 | errors.INTERNAL |
是 |
| 超时 | errors.INTERNAL |
是 |
local resp, err = http_client.get(url)
if err then
if errors.is(err, errors.PERMISSION_DENIED) then
print("Access denied:", err:message())
elseif err:retryable() then
print("Temporary error:", err:message())
end
return nil, err
end
参见 错误处理 了解错误处理方法。