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.