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.17.2,于 2024 年 2 月 29 日发布。
Lua HTTP 客户端 cosocket 驱动程序用于 OpenResty / ngx_lua。
特性
- HTTP 1.0 和 1.1
- SSL
- 对响应体的流式接口,以便可预测的内存使用
- 无需手动连接步骤的单次请求的替代简单接口
- 分块和非分块传输编码
- 连接保持活动
- 请求流水线
- 尾部
- HTTP 代理连接
- mTLS(需要
ngx_lua_http_module>= v0.10.23)
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
syntax: httpc, err = http.new()
创建 HTTP 连接对象。如果失败,返回 nil 和描述错误的字符串。
connect
syntax: ok, err, ssl_session = httpc:connect(options)
尝试连接到 Web 服务器,同时进行以下操作:
- TCP 连接
- SSL 握手
- HTTP 代理配置
这样将创建一个独特的连接池名称,适合与 SSL 和/或基于代理的连接一起使用,因此此语法强烈推荐,而不是原始的(现已弃用) 仅 TCP 连接语法。
选项表具有以下字段:
scheme: 要使用的方案,或 nil 表示 Unix 域套接字host: 目标主机,或 Unix 域套接字的路径port: 目标主机的端口,默认为80或443,具体取决于方案pool: 自定义连接池名称。选项参见 OpenResty 文档,默认将成为使用 SSL/代理属性构造的池名称,这对于安全的连接重用很重要。如有疑问,请留空!pool_size: 选项参见 OpenResty 文档backlog: 选项参见 OpenResty 文档proxy_opts: 子表,默认为全局代理选项设置,见 set_proxy_options。ssl_reused_session: 选项参见 OpenResty 文档ssl_verify: 选项参见 OpenResty 文档,默认为true。ssl_server_name: 选项参见 OpenResty 文档ssl_send_status_req: 选项参见 OpenResty 文档ssl_client_cert: 将传递给tcpsock:setclientcert。需要ngx_lua_http_module>= v0.10.23。ssl_client_priv_key: 同上。
set_timeout
syntax: httpc:set_timeout(time)
设置后续操作的套接字超时(以毫秒为单位)。有关更具声明性的方法,请参见下面的 set_timeouts。
set_timeouts
syntax: httpc:set_timeouts(connect_timeout, send_timeout, read_timeout)
分别设置连接超时阈值、发送超时阈值和读取超时阈值(以毫秒为单位),用于后续套接字操作(连接、发送、接收和从 receiveuntil 返回的迭代器)。
set_keepalive
syntax: ok, err = httpc:set_keepalive(max_idle_timeout, pool_size)
将当前连接放入池中以供将来重用,或关闭连接。调用此方法而不是 close 是“安全的”,因为它将根据请求类型有条件地关闭。具体来说,1.0 请求没有 Connection: Keep-Alive 将被关闭,1.1 请求带有 Connection: Close 也将被关闭。
成功时返回 1。出错时返回 nil, err。在上述情况下,如果连接被有条件地关闭,则返回 2 和错误字符串 connection must be closed,以便与意外错误区分开。
有关参数文档,请参见 OpenResty 文档。
set_proxy_options
syntax: httpc:set_proxy_options(opts)
配置与此客户端实例一起使用的 HTTP 代理。opts 表期望以下字段:
http_proxy: 用于 HTTP 请求的代理服务器 URIhttp_proxy_authorization: 用于http_proxy的默认Proxy-Authorization头值,例如Basic ZGVtbzp0ZXN0,如果存在Proxy-Authorization请求头,将被覆盖。https_proxy: 用于 HTTPS 请求的代理服务器 URIhttps_proxy_authorization: 用于https_proxy的http_proxy_authorization,由于 HTTPS 在连接时进行授权,因此无法通过传递Proxy-Authorization请求头进行覆盖。no_proxy: 不应被代理的主机的逗号分隔列表。
请注意,当使用已弃用的 仅 TCP 连接 连接语法时,此方法无效。
get_reused_times
syntax: times, err = httpc:get_reused_times()
参见 OpenResty 文档。
close
syntax: ok, err = httpc:close()
参见 OpenResty 文档。
请求
request
syntax: 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。
当请求成功时,res 将包含以下字段:
status: 状态码。reason: 状态原因短语。headers: 头部表。具有相同字段名的多个头部将以值表的形式呈现。has_body: 布尔标志,指示是否有主体可读。body_reader: 用于以流式方式读取主体的迭代器函数。read_body: 将整个主体读取到字符串中的方法。read_trailers: 在读取主体后,合并任何尾部到headers表下的方法。
如果响应有主体,则在同一连接用于另一个请求之前,您必须使用 read_body 或 body_reader 读取主体。
request_uri
syntax: res, err = httpc:request_uri(uri, params)
单次接口(见 用法)。由于此方法执行整个端到端请求,因此 params 中指定的选项可以包括上述 connect 和 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
syntax: responses, err = httpc:request_pipeline(params)
此方法的工作方式与上述 request 方法相同,但 params 是一个嵌套的参数表。每个请求按顺序发送,responses 作为响应句柄的表返回。例如:
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
syntax: body, err = res:read_body()
将整个主体读取到本地字符串中。
res:read_trailers
syntax: res:read_trailers()
将任何尾部合并到 res.headers 表中。必须在读取主体后调用。
实用工具
parse_uri
syntax: 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
syntax: 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 参数 如上所述。
syntax: ok, err = httpc:connect(host, port, options_table?)
syntax: ok, err = httpc:connect("unix:/path/to/unix.sock", options_table?)
注意:默认池名称将仅包含 IP 和端口信息,因此在 SSL 和/或代理连接的情况下不安全。请指定您自己的池,或者更好的是,不要使用这些签名。
connect_proxy
syntax: 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
syntax: session, err = httpc:ssl_handshake(session, host, verify)
手动调用此方法不再必要,因为它已包含在 connect 中。它目前保留以兼容旧的 仅 TCP 连接 语法的用户。
参见 OpenResty 文档。
proxy_request / proxy_response
这两个便利方法仅用于演示实现反向代理的常见用例,作者对将其包含在模块中表示遗憾。鼓励用户自行实现,而不是依赖这些可能在后续版本中被移除的函数。
proxy_request
syntax: local res, err = httpc:proxy_request(request_body_chunk_size?)
使用当前客户端请求参数执行请求,实际上是代理到连接的上游。请求主体将根据 request_body_chunk_size 以流式方式读取(请参见 客户端主体读取器文档)。
proxy_response
syntax: httpc:proxy_response(res, chunksize?)
根据给定的 res 设置当前响应。确保不会将逐跳头部发送到下游,并将根据 chunksize 读取响应(请参见 主体读取器文档)。
GitHub
您可以在 nginx-module-http 的 GitHub 仓库 中找到此模块的其他配置提示和文档。