跳转至

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

已弃用

这些方法可能会在未来版本中被移除。

用法

有两种基本操作模式:

  1. 简单的单次请求,不需要手动连接管理,但会缓冲整个响应,并将连接关闭或返回连接池中。

  2. 流式请求,在单独建立连接后发送请求,按块读取主体流,最后手动关闭连接或保持活动。此技术需要更多代码,但提供了在 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 - 在代理服务器和远程主机之间使用的协议(httphttps)。如果指定 https 作为方案,connect_proxy() 将发出 CONNECT 请求以通过代理服务器建立与远程主机的 TCP 隧道。
  • host - 要连接的远程主机的主机名。
  • port - 要连接的远程主机的端口。
  • proxy_authorization - 当 schemehttps 时,通过 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 仓库 中找到此模块的其他配置提示和文档。