Hello!
On Tue, Oct 22, 2013 at 5:50 AM, Bhargav wrote:
> I want to make few changes in content before delivering it to client browser
> and for that I tried to modify content with body_filter_by_lua*. When I use
> regex in string.gsub() to match the pattern and replace it, sometimes it
> works and sometimes it doesn't work.
>
The Nginx output body filters always work on data chunks rather than
the complete response body data. That's all the point of streaming
processing and it's crucial for the constant memory usage regardless
of the response body size. The same thing applies to
body_filter_by_lua*, as stated in its official documentation:
https://github.com/chaoslawful/lua-nginx-module#body_filter_by_lua
> I believe the cause of the problem is *chunk*ed data. So is there any proper
> way to handle this case so that we can match the pattern with complete
> response body and do needful changes before final delivery to browser?
>
Generally, you should not buffer all the response data because it has
great impact on memory footprint when the response body is huge. So
the right way is to use streaming regex replacement here. The
ngx_replace_filter module is such an (preliminary) attempt:
https://github.com/agentzh/replace-filter-nginx-module#readme
though my sregex regex engine this module is using is still very young
and lacks a lot of important optimizations :)
If you insist in fully-buffered processing in body_filter_by_lua*,
then you can just buffer all the data chunks yourself there. Let's
consider the following self-contained example that does this:
location = /t {
echo -n he;
echo -n llo;
echo -n ' ';
echo -n 'worl';
echo d;
header_filter_by_lua '
ngx.header.content_length = nil
';
body_filter_by_lua '
-- ngx.arg[1] = string.gsub(ngx.arg[1], "hello world", "HIT!")
-- do return end
local chunk, eof = ngx.arg[1], ngx.arg[2]
local buffered = ngx.ctx.buffered
if not buffered then
buffered = {} -- XXX we can use
table.new here
ngx.ctx.buffered = buffered
end
if chunk ~= "" then
buffered[#buffered + 1] = chunk
ngx.arg[1] = nil
end
if eof then
local whole = table.concat(buffered)
ngx.ctx.buffered = nil
whole = string.gsub(whole, "hello world", "HIT!")
ngx.arg[1] = whole
end
';
}
where we use the "echo" directive from the ngx_echo module to emit
response body data chunks. The commented out Lua code should be what
you're doing and you can see that there is no match if you just do the
regex substitutions on each individual data chunk. Instead, we collect
all the data chunks into our own buffer (as a Lua table in the ngx.ctx
table) and do the regex substitution in a single run when we see the
last chunk in the response body stream. When accessing this /t
interface, we get
$ curl localhost:8080/t
HIT!
Hope these help :)
Best regards,
-agentzh