On 08/23/2012 12:02 AM, Tim Bannister wrote:
> On 22 Aug 2012, at 22:25, Daniel Gruno <rum...@cord.dk> wrote:
> 
>>> Would your concept meaningfully generalise beyond application-level filters?
>>
>> I'm not entirely sure what you mean by this, could you elaborate?
>> If you want some more sophisticated examples of what could be achieved with 
>> Lua filtering, I'd be happy to provide some more details on how this concept 
>> could be utilised.
> 
> I don't know if this is another way of phrasing Nick's question or not, but 
> would I be able to implement gzip Transfer-Encoding: just using Lua and this 
> new directive?
> 
> I found (bug 52860) it a bit tricky to achieve in C, so I think it could be 
> harder still with the extra limitations of the Lua environment. My C code 
> uses AP_FTYPE_TRANSCODE which I think is the right choice but few modules get 
> involved at this filtering stage.
> 
The filter phases are as follows (as with any other filter mod):
1) mod_lua calls a setup function before buckets are sent, to allow for
headers to be changed and data to be inserted before the actual content,
fx. appending an advertisment a'la old Geocities or some such.
2) The Lua function yields to let mod_lua know it's ready to receive buckets
3) Buckets are sent, one by one to the function, which processes them,
and again yields with the new content for each bucket.
4) once all buckets have passed through, mod_lua makes a final call with
an empty (nil) buffer, allowing the Lua script to add any additional
tail calls/data to the filter (it adds tail data by yielding with
whatever should be added). Thus, an example Lua function could be:

>-------------------------------------------------------------------<
function filter(r)
    -- Initial phase
    r.content_type = "text/plain" -- set up some stuff
    coroutine.yield() -- End initial phase

    -- bucket phase
    while (buffer) do -- for each bucket sent, do...
        local output = mangle(buffer) -- transformation
        coroutine.yield(output) -- Send the output down the chain
    end

    -- Final phase once all buckets have been sent
    clean_up()
    coroutine.yield("This line will be appended to all files")
end
>-------------------------------------------------------------------<

So yes, theoretically you should be able to implement decompression this
way, by doing something along the lines of this (totally just making it up):

>-----------------------------------------------------<
local zip = require "zlib" -- or something...
function gzip_handle(r)
    r.headers_out['Transfer-Encoding'] = "gzip" -- or ?
    do_magic_header_stuff_here() -- add header data
    coroutine.yield() -- yield and wait for buckets
    while (buffer) do  -- for each bucket, deflate it
        local deflated = zip.deflate(buffer)
        coroutine.yield(deflated) -- pass on new data
    end
    append_tail_to_output() -- pass on a tail if needed
end
>-----------------------------------------------------<

I haven't tried this out with gzip, but I have tried with
base64_encoding a lot of files through a test filter that works similar
to what I wrote above. I'm using AP_FTYPE_RESOURCE for the testing, but
surely we could either change that or add some additional optional
argument to the directive to control where it should place itself.

As a 'fun' side effect of doing this in Lua, you can use the same filter
functions for both input and output filtering, as they work the same
way, although I don't know of any filters that would benefit from it ;)

In any case, I'll get started on adding an input filter hook as well,
and see if I can get it working just as well as the output filter I have
in place, and then commit something snazzy for review.

With regards,
Daniel.

Reply via email to