nginx openresty lua-resty-http 使用的一些问题记录

需求背景

需求是使用 nginx 做一个 https 服务的代理

nginx 收到 http 请求后,需要修改 body 中的某些参数值,然后将修改后的数据发送到目标服务器(https)

本来以为很简单的需求,结果中间出现了不少岔子,这里记录一下

知识点汇总

  1. location = xxx {} 是精确匹配,location xxx {} 是前缀匹配。
  2. 用 lua 脚本处理请求时,如果脚本找不到,客户端看到的是 404 not found,和路由配置错了一个样。
  3. lua-resty-http 模块管理员权限直接安装就行(不需要和 nginx 一起编译)。
  4. httpc:request_uri 请求时,必要的配置是 resolver(做 dns 解析);https 请求时,加上 ssl_verify = false 可以避免 https 证书带来的问题(python requests 包也有这个参数,但是不推荐使用,不安全)。
  5. 像这种靠近底层的工具如果修改了 http 请求的 body 时,一定要关注是否已经设置过 Content-Length 这个 header 了,可以去掉或者重新设置。否则可能造成的问题是 数据异常截断 或者 莫名奇妙的等待

详细问题

问题1:配置了 location 但是显示 404

代码如下

复制代码
location = /aigw {
    content_by_lua_file conf/lua/aigw.lua;
}

这个代码先后改了两次,实际有两个原因客户端都会显示 404

  1. location = 是精确匹配,请求有后缀就找不到了
  2. 如果 conf/lua/aigw.lua 文件找不到,也不会提示内部错误信息,客户端看到的也是 404 问题

问题 2:module 'resty.http' not found:

使用如下命令安装,注意必须是 sudo 权限下,否则不行

复制代码
sudo apt-get update
sudo apt-get install -y luarocks
sudo luarocks install lua-resty-http

问题 3:no resolver defined to resolve "xxxxx"

问题代码如下,这个错误是下面 err 的值

复制代码
local res, err = httpc:request_uri(forwardUrl, {
...
})

解决方案:这个错误表明 NGINX 没有配置 DNS 解析器来解析 xxxxx 域名。你需要在 NGINX 配置文件中添加一个 resolver 指令来指定 DNS 服务器。

在 nginx.conf 文件的 http 或 server 块中添加以下内容:

复制代码
http {
    resolver 8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout 5s;

    # 其他配置...
}

8.8.8.8 和 8.8.4.4 是 Google 的公共 DNS 服务器,你也可以使用其他 DNS 服务器。

添加后,重新加载 NGINX 配置:

复制代码
sudo nginx -s reload

问题 4:unable to get local issuer certificate

这也是发送请求时的错误信息。

这个错误表明 NGINX 无法验证目标 HTTPS 服务器的 SSL 证书。下面是两种解决方案,我才用了第一种

  1. 忽略 SSL 证书验证(不推荐用于生产环境): 在 httpc:request_uri 调用中添加 ssl_verify = false 参数。

    lua 复制代码
    local res, err = httpc:request_uri(forwardUrl, {
    	method = ngx.req.get_method(),
        body = body_data,
    	headers = ngx.req.get_headers(),
        ssl_verify = false  -- 这里是新增加的参数
    })
  2. 指定 CA 证书: 下载并指定一个可信的 CA 证书文件。(我没试过)

    首先,下载 CA 证书文件(例如 ca-certificates.crt),然后在 httpc:request_uri 调用中指定 ssl_ca_cert 参数。

    lua 复制代码
    local res, err = httpc:request_uri(forwardUrl, {
        method = ngx.req.get_method(),
        body = body_data,
        headers = ngx.req.get_headers(),
        ssl_ca_cert = "/path/to/ca-certificates.crt"
    })

问题 5:修改请求 body 值之后,请求就卡住了

代码依旧是这个代码

lua 复制代码
local res, err = httpc:request_uri(forwardUrl, {
   	method = ngx.req.get_method(),
    body = body_data,
   	headers = ngx.req.get_headers(),
    ssl_verify = false
})

当我把 body_data 转为 json,修改其中的值,又转为字符串之后,这个网络请求就莫名奇妙的卡住了。

废了很大的力气查了很多资料也没有结果。自己调试打印了两组数据,对比了数据类型,还是没找到原因。

最后我打印了 ngx.req.get_method() 发现其中有一个头信息是

复制代码
content-length: 216

猛然想到:因为我把 body 改小了,但是 content-length 值很大,这个 client 就会一直等着剩余的数据传输,所以就会一直卡着不动了。

修改方案是:

lua 复制代码
local new_headers = ngx.req.get_headers()
-- 删除 Content-Length 头,否则会导致 http client 卡住
new_headers["Content-Length"] = nil
new_headers["content-length"] = nil

local res, err = httpc:request_uri(forwardUrl, {
    method = ngx.req.get_method(),
    body = new_body,
    headers = new_headers,
    ssl_verify = false
})

问题 6:最终返回给客户端的数据是截断的

问题代码如下

lua 复制代码
-- res 是 httpc:request_uri 的返回值
ngx.status = res.status
for k, v in pairs(res.headers) do
    if k ~= "Transfer-Encoding" then
        ngx.header[k] = v
    end
end

-- 将 body 中的 model 替换回去
local ok, res_body_json = pcall(cjson.decode, res.body)
if not ok then
    ngx.log(ngx.ERR, "Failed to decode JSON: ", res.body)
    ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end

res_body_json.model = model_prefix .. model_suffix
local new_res_body = cjson.encode(res_body_json)

ngx.print(new_res_body)
ngx.exit(ngx.HTTP_OK)

上面的代码返回的 json 数据,一直只有一部分。

这个答案也很简单,和之前卡住的问题一样,因为我修改了返回的 body 值,而 ngx 在最开始逐一设置了 resheader 值,显然又把 Content-Length 带进去了。加上这么两句以后,问题解决:

lua 复制代码
ngx.header["Content-Length"] = #new_res_body
ngx.header["content-length"] = #new_res_body
相关推荐
xujiangyan_7 小时前
nginx的反向代理和负载均衡
服务器·网络·nginx
monstercl12 小时前
Lua中基础函数使用详解
lua·脚本语言
爱的叹息12 小时前
Spring Boot 集成Redis 的Lua脚本详解
spring boot·redis·lua
viqecel21 小时前
网站改版html页面 NGINX 借用伪静态和PHP脚本 实现301重定向跳转
nginx·php·nginx重定向·301重定向·html页面重定向
硪就是硪1 天前
内网环境将nginx的http改完https访问
nginx·http·https
ak啊1 天前
Nginx 安全加固详细配置指南
nginx
沐土Arvin1 天前
Nginx 核心配置详解与性能优化最佳实践
运维·开发语言·前端·nginx·性能优化
haoranyyy2 天前
mac环境中Nginx安装使用 反向代理
linux·服务器·nginx
爱学测试的雨果2 天前
Postman —— postman实现参数化
软件测试·功能测试·测试工具·lua·postman
ak啊2 天前
Nginx 高级缓存配置与优化
nginx