http: Lua HTTP 客户端 cosocket 驱动程序用于 nginx-module-lua
安装
如果您尚未设置 RPM 仓库订阅,请 注册。然后您可以继续以下步骤。
CentOS/RHEL 7 或 Amazon Linux 2
yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install lua-resty-http
CentOS/RHEL 8+、Fedora Linux、Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install lua5.1-resty-http
要在 NGINX 中使用此 Lua 库,请确保已安装 nginx-module-lua。
本文档描述了 lua-resty-http v0.18.0,于 2026 年 7 月 6 日发布。
Lua HTTP 客户端 cosocket 驱动程序用于 OpenResty / ngx_lua。
特性
- HTTP 1.0 和 1.1
- SSL
- 响应体的流式接口,以便可预测的内存使用
- 适用于单次请求的替代简单接口,无需手动连接步骤
- 分块和非分块传输编码
- 连接保持活动
- 请求流水线
- 尾部
- HTTP 代理连接
- mTLS(需要
ngx_lua_http_module>= v0.10.23) - 通过
traceparent头部传播 W3C 跟踪上下文(>= v0.18.0)
API
- new
- connect
- set_proxy_options
- set_timeout
- set_timeouts
- set_keepalive
- get_reused_times
- close
- request
- request_uri
- request_pipeline
- parse_uri
- get_client_body_reader
- Response
已弃用
这些方法可能会在未来版本中被移除。
用法
有两种基本操作模式:
-
简单的单次请求,不需要手动连接管理,但会缓冲整个响应,并将连接关闭或返回连接池中。
-
流式请求,在单独建立连接后发送请求,按块读取主体流,最后手动关闭连接或保持活动。此技术需要更多代码,但提供了在 Lua 端丢弃潜在大响应体的能力,以及通过单个连接流水线多个请求。
单次请求
local httpc = require("resty.http").new()
-- 单次请求使用 `request_uri` 接口。
local res, err = httpc:request_uri("http://example.com/helloworld", {
method = "POST",
body = "a=1&b=2",
headers = {
["Content-Type"] = "application/x-www-form-urlencoded",
},
})
if not res then
ngx.log(ngx.ERR, "请求失败: ", err)
return
end
-- 此时,整个请求/响应已完成,连接将被关闭或返回连接池。
-- `res` 表包含预期的 `status`、`headers` 和 `body` 字段。
local status = res.status
local length = res.headers["Content-Length"]
local body = res.body
流式请求
local httpc = require("resty.http").new()
-- 首先建立连接
local ok, err, ssl_session = httpc:connect({
scheme = "https",
host = "127.0.0.1",
port = 8080,
})
if not ok then
ngx.log(ngx.ERR, "连接失败: ", err)
return
end
-- 然后使用 `request` 发送请求,提供路径和 `Host` 头,而不是完整的 URI。
local res, err = httpc:request({
path = "/helloworld",
headers = {
["Host"] = "example.com",
},
})
if not res then
ngx.log(ngx.ERR, "请求失败: ", err)
return
end
-- 此时,状态和头部将可用于 `res` 表中,但主体和任何尾部仍在传输中。
-- 我们可以使用 `body_reader` 迭代器,按我们期望的缓冲大小流式读取主体。
local reader = res.body_reader
local buffer_size = 8192
repeat
local buffer, err = reader(buffer_size)
if err then
ngx.log(ngx.ERR, err)
break
end
if buffer then
-- 处理
end
until not buffer
local ok, err = httpc:set_keepalive()
if not ok then
ngx.say("设置保持活动失败: ", err)
return
end
-- 此时,连接将安全地返回池中或关闭。
````
## 连接
## new
`语法: httpc, err = http.new()`
创建 HTTP 连接对象。如果失败,返回 `nil` 和描述错误的字符串。
## connect
`语法: ok, err, ssl_session = httpc:connect(options)`
尝试连接到 Web 服务器,同时执行以下活动:
- TCP 连接
- SSL 握手
- HTTP 代理配置
这样会创建一个独特的连接池名称,适合与 SSL 和/或基于代理的连接一起使用,因此这种语法强烈推荐使用,而不是原始的(现已弃用的)[仅 TCP 连接语法](#TCP-only-connect)。
选项表具有以下字段:
* `scheme`: 要使用的方案,或 unix 域套接字的 nil
* `host`: 目标主机,或 unix 域套接字的路径
* `port`: 目标主机上的端口,默认值为 `80` 或 `443`,具体取决于方案
* `pool`: 自定义连接池名称。根据 [OpenResty 文档](https://github.com/openresty/lua-nginx-module#tcpsockconnect) 的选项,默认值将成为使用 SSL/代理属性构造的池名称,这对于安全的连接重用非常重要。若不确定,请留空!
* `pool_debug`: 设置为 `true` 以在 `DEBUG` 级别记录连接池名称,默认关闭
* `pool_size`: 根据 [OpenResty 文档](https://github.com/openresty/lua-nginx-module#tcpsockconnect) 的选项
* `backlog`: 根据 [OpenResty 文档](https://github.com/openresty/lua-nginx-module#tcpsockconnect) 的选项
* `proxy_opts`: 子表,默认为全局代理选项,见 [set\_proxy\_options](#set_proxy_options)。
* `ssl_reused_session`: 根据 [OpenResty 文档](https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake) 的选项
* `ssl_verify`: 根据 [OpenResty 文档](https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake) 的选项,默认值为 `true`。
* `ssl_server_name`: 根据 [OpenResty 文档](https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake) 的选项
* `ssl_send_status_req`: 根据 [OpenResty 文档](https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake) 的选项
* `ssl_client_cert`: 将传递给 `tcpsock:setclientcert`。需要 `ngx_lua_http_module` >= v0.10.23。
* `ssl_client_priv_key`: 同上。
## set\_timeout
`语法: httpc:set_timeout(time)`
设置后续操作的套接字超时(以毫秒为单位)。有关更声明性的方法,请参见下面的 [set\_timeouts](#set_timeouts)。
## set\_timeouts
`语法: httpc:set_timeouts(connect_timeout, send_timeout, read_timeout)`
分别设置连接超时阈值、发送超时阈值和读取超时阈值,以毫秒为单位,用于后续的套接字操作(连接、发送、接收和从 receiveuntil 返回的迭代器)。
## set\_keepalive
`语法: ok, err = httpc:set_keepalive(max_idle_timeout, pool_size)`
将当前连接放入池中以供将来重用,或关闭连接。调用此方法而不是 [close](#close) 是“安全的”,因为它将根据请求类型有条件地关闭。具体而言,未带 `Connection: Keep-Alive` 的 `1.0` 请求将被关闭,带有 `Connection: Close` 的 `1.1` 请求也将被关闭。
成功时返回 `1`。发生错误时返回 `nil, err`。在上述情况下,连接被有条件地关闭时,返回 `2` 和错误字符串 `connection must be closed`,以便与意外错误区分。
有关参数文档,请参见 [OpenResty 文档](https://github.com/openresty/lua-nginx-module#tcpsocksetkeepalive)。
## set\_proxy\_options
`语法: httpc:set_proxy_options(opts)`
配置要与此客户端实例一起使用的 HTTP 代理。`opts` 表期望以下字段:
* `http_proxy`: 用于 HTTP 请求的代理服务器 URI
* `http_proxy_authorization`: 用于 `http_proxy` 的默认 `Proxy-Authorization` 头部值,例如 `Basic ZGVtbzp0ZXN0`,如果存在 `Proxy-Authorization` 请求头,则将被覆盖。
* `https_proxy`: 用于 HTTPS 请求的代理服务器 URI
* `https_proxy_authorization`: 用于 `https_proxy` 的 `http_proxy_authorization`,由于在 HTTPS 中授权是在连接时完成的,因此不能通过传递 `Proxy-Authorization` 请求头来覆盖。
* `no_proxy`: 不应通过代理的主机的逗号分隔列表。
请注意,当使用已弃用的 [仅 TCP 连接](#TCP-only-connect) 连接语法时,此方法无效。
## get\_reused\_times
`语法: times, err = httpc:get_reused_times()`
请参见 [OpenResty 文档](https://github.com/openresty/lua-nginx-module#tcpsockgetreusedtimes)。
## close
`语法: ok, err = httpc:close()`
请参见 [OpenResty 文档](https://github.com/openresty/lua-nginx-module#tcpsockclose)。
## 请求
## request
`语法: res, err = httpc:request(params)`
通过已经建立的连接发送 HTTP 请求。返回一个 `res` 表或 `nil` 和错误消息。
`params` 表期望以下字段:
* `version`: HTTP 版本号。默认为 `1.1`。
* `method`: HTTP 方法字符串。默认为 `GET`。
* `path`: 路径字符串。默认为 `/`。
* `query`: 查询字符串,可以是字面字符串或 Lua 表。
* `headers`: 请求头的表。
* `body`: 请求主体,可以是字符串、字符串表或一个迭代器函数,直到耗尽时返回字符串,返回 nil。请注意,您必须为请求主体指定 `Content-Length`,或指定 `Transfer-Encoding: chunked` 并让您的函数实现编码。另请参见: [get\_client\_body\_reader](#get_client_body_reader)。
当请求成功时,`res` 将包含以下字段:
* `status`: 状态码。
* `reason`: 状态原因短语。
* `headers`: 头部的表。多个具有相同字段名称的头部将以值的表形式呈现。
* `has_body`: 布尔标志,指示是否有主体可供读取。
* `body_reader`: 用于以流式方式读取主体的迭代器函数。
* `read_body`: 将整个主体读取到字符串中的方法。
* `read_trailers`: 合并任何尾部到头部下方的方法,在读取主体后调用。
如果响应有主体,则在同一连接可以用于另一个请求之前,必须使用 `read_body` 或 `body_reader` 读取主体。
## request\_uri
`语法: res, err = httpc:request_uri(uri, params)`
单次接口(见 [用法](#Usage))。由于此方法执行整个端到端请求,因此在 `params` 中指定的选项可以包括上述 [connect](#connect) 和 [request](#request) 中找到的任何内容。还请注意,`params` 中的字段 `path` 和 `query` 将覆盖 `uri` 的相关组件(`scheme`、`host` 和 `port` 将始终从 `uri` 中获取)。
有 3 个附加参数用于控制保持活动:
* `keepalive`: 设置为 `false` 以禁用保持活动并立即关闭连接。默认为 `true`。
* `keepalive_timeout`: 最大空闲超时(毫秒)。默认为 `lua_socket_keepalive_timeout`。
* `keepalive_pool`: 池中的最大连接数。默认为 `lua_socket_pool_size`。
如果请求成功,`res` 将包含以下字段:
* `status`: 状态码。
* `headers`: 头部的表。
* `body`: 整个响应主体作为字符串。
## request\_pipeline
`语法: responses, err = httpc:request_pipeline(params)`
此方法的工作方式与上述 [request](#request) 方法相同,但 `params` 是嵌套的参数表。每个请求按顺序发送,`responses` 返回为响应句柄的表。例如:
```lua
local responses = httpc:request_pipeline({
{ path = "/b" },
{ path = "/c" },
{ path = "/d" },
})
for _, r in ipairs(responses) do
if not r.status then
ngx.log(ngx.ERR, "套接字读取错误")
break
end
ngx.say(r.status)
ngx.say(r:read_body())
end
由于流水线的性质,直到您尝试使用响应字段(状态/头等)之前,实际上不会读取任何响应。由于响应是按顺序读取的,因此在尝试读取下一个响应之前,您必须读取整个主体(以及任何尾部,如果有的话)。
请注意,这并不排除使用流式响应主体读取器。响应仍然可以流式传输,只要在尝试访问下一个响应之前,整个主体都被流式传输。
确保在尝试使用其他字段之前至少测试一个字段(例如状态),以防发生套接字读取错误。
响应
res.body_reader
body_reader 迭代器可用于按您选择的块大小流式传输响应主体,如下所示:
local reader = res.body_reader
local buffer_size = 8192
repeat
local buffer, err = reader(buffer_size)
if err then
ngx.log(ngx.ERR, err)
break
end
if buffer then
-- 处理
end
until not buffer
如果读取器在没有参数的情况下被调用,则行为取决于连接类型。如果响应被编码为分块,则迭代器将按到达的块返回。如果没有,它将简单地返回整个主体。
请注意,提供的大小实际上是一个 最大 大小。因此,在分块传输的情况下,您可能会得到小于您请求的大小的缓冲区,因为这是实际编码块的剩余部分。
res:read_body
语法: body, err = res:read_body()
将整个主体读取到本地字符串中。
res:read_trailers
语法: res:read_trailers()
这将合并任何尾部到 res.headers 表下方。必须在读取主体后调用。
工具
parse_uri
语法: local scheme, host, port, path, query? = unpack(httpc:parse_uri(uri, query_in_path?))
这是一个便利函数,允许用户在输入数据为 URI 时更轻松地使用通用接口。
自版本 0.10 起,添加了可选的 query_in_path 参数,该参数指定查询字符串是否应包含在 path 返回值中,或单独作为其自己的返回值。默认为 true 以保持向后兼容性。当设置为 false 时,path 只会包含路径,而 query 将包含 URI 参数,不包括 ? 分隔符。
get_client_body_reader
语法: reader, err = httpc:get_client_body_reader(chunksize?, sock?)
返回一个迭代器函数,可用于以流式方式读取下游客户端请求主体。您还可以指定一个可选的默认块大小(默认值为 65536),或在客户端请求的地方指定一个已经建立的套接字。
示例:
local req_reader = httpc:get_client_body_reader()
local buffer_size = 8192
repeat
local buffer, err = req_reader(buffer_size)
if err then
ngx.log(ngx.ERR, err)
break
end
if buffer then
-- 处理
end
until not buffer
此迭代器还可以用作请求参数中主体字段的值,允许将请求主体流式传输到代理的上游请求中。
local client_body_reader, err = httpc:get_client_body_reader()
local res, err = httpc:request({
path = "/helloworld",
body = client_body_reader,
})
已弃用
这些功能仍然保留以兼容旧版,但可能会在未来版本中被移除。
仅 TCP 连接
以下版本的 connect 方法签名已弃用,取而代之的是单个 table 参数 上面文档。
语法: ok, err = httpc:connect(host, port, options_table?)
语法: ok, err = httpc:connect("unix:/path/to/unix.sock", options_table?)
注意:默认池名称将仅包含 IP 和端口信息,因此在 SSL 和/或代理连接的情况下使用不安全。请指定您自己的池,或更好的是,不要使用这些签名。
connect_proxy
语法: ok, err = httpc:connect_proxy(proxy_uri, scheme, host, port, proxy_authorization)
手动调用此方法不再必要,因为它已包含在 connect 中。为了与旧版 仅 TCP 连接 语法的用户兼容,暂时保留此方法。
尝试通过给定的代理服务器连接到 Web 服务器。该方法接受以下参数:
proxy_uri- 要使用的代理服务器的完整 URI(例如http://proxy.example.com:3128/)。注意:仅支持http协议。scheme- 在代理服务器和远程主机之间使用的协议(http或https)。如果指定https作为方案,connect_proxy()将发出CONNECT请求以通过代理服务器建立与远程主机的 TCP 隧道。host- 要连接的远程主机的主机名。port- 要连接的远程主机的端口。proxy_authorization- 当scheme为https时,通过CONNECT发送到代理服务器的Proxy-Authorization头部值。
如果在连接尝试期间发生错误,则此方法返回 nil 和描述错误的字符串。如果成功建立连接,则该方法返回 1。
使用此 API 时需要记住几个关键点:
- 如果方案为
https,则需要使用ssl_handshake()方法手动与远程服务器执行 TLS 握手,然后才能通过代理隧道发送任何请求。 - 如果方案为
http,则需要确保通过连接发送的请求符合 RFC 7230,特别是 第 5.3.2 节,其中指出请求目标必须为绝对形式。在实践中,这意味着当您使用send_request()时,path必须是资源的绝对 URI(例如http://example.com/index.html而不仅仅是/index.html)。
ssl_handshake
语法: session, err = httpc:ssl_handshake(session, host, verify)
手动调用此方法不再必要,因为它已包含在 connect 中。为了与旧版 仅 TCP 连接 语法的用户兼容,暂时保留此方法。
请参见 OpenResty 文档。
proxy_request / proxy_response
这两个便利方法旨在简单地演示实现反向代理的常见用例,作者对将它们包含在模块中表示遗憾。鼓励用户自行实现,而不是依赖这些可能在后续版本中被移除的函数。
proxy_request
语法: local res, err = httpc:proxy_request(request_body_chunk_size?)
使用当前客户端请求参数执行请求,有效地代理到连接的上游。请求主体将根据 request_body_chunk_size 以流式方式读取(请参见 客户端主体读取器文档)。
proxy_response
语法: httpc:proxy_response(res, chunksize?)
根据给定的 res 设置当前响应。确保不会将逐跳头部发送到下游,并将根据 chunksize 读取响应(请参见 主体读取器文档)。
GitHub
您可以在 nginx-module-http 的 GitHub 仓库 中找到此模块的其他配置提示和文档。