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
相关推荐
loong_XL1 小时前
服务器ip:port服务用nginx 域名代理
服务器·tcp/ip·nginx
苹果醋33 小时前
SpringBoot快速入门
java·运维·spring boot·mysql·nginx
染指11104 小时前
50.第二阶段x86游戏实战2-lua获取本地寻路,跨地图寻路和获取当前地图id
c++·windows·lua·游戏安全·反游戏外挂·游戏逆向·luastudio
永卿0014 小时前
nginx学习总结(不包含安装过程)
运维·nginx·负载均衡
小湿哥8 小时前
ubuntu22.04 nginx配置下载目录,亲测成功
运维·nginx
spencer_tseng12 小时前
WeakAuras NES Script(lua)
lua·wow·nes·weakauras
红黑色的圣西罗18 小时前
xlua中自定义lua文件加载的一种方式
lua
BUG研究员_19 小时前
LoadBalancer负载均衡和Nginx负载均衡区别理解
nginx·rpc·负载均衡
法外狂徒张三!20 小时前
Roblox踩坑1——动画无法完整播放
lua·roblox
见欢.20 小时前
Nginx解析漏洞靶场通关(nginx_parsing&CVE-2013-454)
nginx