LuaExamples » History » Revision 2
Revision 1 (stbuehler, 2009-12-21 00:00) → Revision 2/4 (stbuehler, 2010-01-29 17:02)
h1. LuaExamples
{{>toc}}
h2. Secdownload
See [[Howto_secdownload]].
h2. X-Sendfile version 1
This config snippet inserts a lua filter to check for a "X-Sendfile" header; if a non-empty value is found, it will be used as pathname to a file which replaces the content.
The drawback is that "XSendfile:handle" will be called for every data block going to the user, even if there was no "X-Sendfile" header (lua doesn't read the data, it just forwards the chunks, but it still is not optimal).
But this filter can be inserted at any point in your config, even before the content handler.
<pre>
-- create new class XSendfile
local XSendfile = { }
XSendfile.__index = XSendfile
-- "classmethod" to create new instance
function XSendfile:new(vr)
-- vr:debug("New XSendfile instance")
local o = { handling = 0 }
setmetatable(o, self)
return o
end
-- normal method to handle content
function XSendfile:handle(vr, outq, inq)
-- vr:debug("XSendfile:handle "..self.handling)
if self.handling == 1 then
-- already sent the file, ignore further input (we closed it already)
inq:skip_all()
return lighty.HANDLER_GO_ON
elseif self.handling == -1 then
-- no X-Sendfile header, so just forward content and make sure to close chunkqueues if done
outq:steal_all(inq)
if inq.is_closed then -- no more input from backend
outq.is_closed = true
elseif outq.is_closed then -- client (or other filter after us) closed stream
inq.is_closed = true
end
return lighty.HANDLER_GO_ON
else
if outq.is_closed then -- client (or other filter after us) closed stream
inq:skip_all()
inq.is_closed = true
return lighty.HANDLER_GO_ON
end
-- check x-sendfile header
local xs = vr.resp.headers["X-Sendfile"]
if xs and xs ~= "" then -- file specified
vr.resp.headers["X-Sendfile"] = nil -- remove header from response
-- Add checks for the pathname here
vr:debug("XSendfile:handle: pushing file '" .. xs .. "' as content")
outq:add({ filename = xs })
outq.is_closed = true
inq:skip_all()
inq.is_closed = true
self.handling = 1
else
self.handling = -1
end
return lighty.HANDLER_GO_ON
end
end
actions = lighty.filter_out(XSendfile)
</pre>
h2. X-Sendfile version 2
This config snippet waits for the response headers, and replaces the content with the filename found in the "X-Sendfile" header (if it isn't empty).
This version does not need much cpu; it is assumed that the real backend doesn't send the content anyway, and file is sent in one step, so "XFilterDrop:handle" is probably not called very often.
But this version will wait for the response headers in the action, so you need a content handler before it (static, fastcgi, ...).
<pre>
-- create new class XFilterDrop
local XFilterDrop = { }
XFilterDrop.__index = XFilterDrop
-- "classmethod" to create new instance
function XFilterDrop:new(vr)
-- vr:debug("New XSendfile instance")
local o = { }
setmetatable(o, self)
return o
end
-- normal method to handle content
function XFilterDrop:handle(vr, outq, inq)
-- drop further input (we closed it already)
inq:skip_all()
return lighty.HANDLER_GO_ON
end
-- create a new filter which drops all input and adds it to the vrequest
-- returns the filter object so you can insert your own content in f.out (it is already closed)
local function add_drop_filter(vr)
local f = vr:add_filter_out(XFilterDrop:new())
f['in'].is_closed = true
f['in']:skip_all()
f.out.is_closed = true
return f
end
local function handle_x_sendfile(vr)
-- vr:debug("handle x-sendfile")
-- wait for response
if not vr.has_response then
if vr.is_handled then
-- vr:debug("x-sendfile: waiting for response headers")
return lighty.HANDLER_WAIT_FOR_EVENT
else
-- vr:debug("No response handler yet, cannot handle X-Sendfile")
return lighty.HANDLER_GO_ON
end
end
-- vr:debug("handle x-sendfile: headers available")
-- add filter if x-sendfile header is not empty
local xs = vr.resp.headers["X-Sendfile"]
if xs and xs ~= "" then
-- make sure to drop all other content from the backend
local f = add_drop_filter(vr)
vr.resp.headers["X-Sendfile"] = nil -- remove header from response
-- Add checks for the pathname here
vr:debug("XSendfile:handle: pushing file '" .. xs .. "' as content")
f.out:add({ filename = xs })
end
end
actions = handle_x_sendfile
</pre>