背景
我使用openresty+lua部署的nginx服務器,其中使用gzip針對數據包進行解壓縮處理,然後再對部分數據修改完畢後,使用gzip進行壓縮,然後再發送給接收端。
zlib參考如下
https://github.com/hamishforbes/lua-ffi-zlib
問題復現
在使用上述github的方法後,針對大部分請求都是能夠成功進行解壓與壓縮,但是又部分頁面一直會提示解壓失敗,錯誤log如下
INFLATE: Data error, no input bytes
解決思路
經過源碼分析,可以確認zlib解壓及壓縮的過程是ok的,經過debug發現,web的請求包是Transfer-Encoding: chunked的方式,該方式會把數據包切割成一個個chunked包然後進行發送。
問題就出現在chunked包上,針對大部分的請求包,每一個chunked包就是一個完整的gzip包(完整的意思是該包有gzip的開始和結束標誌位)。但是仍然有小部分數據是把一個完整的gzip包切割成了小塊數據包,導致針對這些小塊的數據包無法單獨進行解壓縮。
因此,需要對這些數據包進行整合拼接,拼接完整後,再進行解壓縮操作即可
解決方法
ngx.arg 在 body filter 階段用來讀取、更新應答數據。其中 ngx.arg[1]是待發送的 body,ngx.arg[2]
指示後續是否還有待發送數據
因此可以利用ngx.arg[2]來判斷是否是完整的數據包
local Content_Encoding = ngx.resp.get_headers()["Content-Encoding"]
if ngx.re.find(Content_Encoding, [=[gzip]=],"oij") then
-- 此處爲了防止數據包不完整導致gzip解壓失敗
if not ngx.ctx.ngx_all_content then
ngx.ctx.ngx_all_content = ngx.arg[1]
else
ngx.ctx.ngx_all_content = ngx.ctx.ngx_all_content .. ngx.arg[1]
end
ngx.arg[1] = nil --pass原有的數據
if ngx.arg[2] then
-- 當數據包標誌爲true時,證明一個完整的gzip包已經拼接完成,
-- 此時就可以進行解壓縮操作
html = zlib_uncompress(ngx.ctx.ngx_all_content) or ""
end
end