Project

General

Profile

LuaExamples » History » Revision 3

Revision 2 (stbuehler, 2010-01-29 17:02) → Revision 3/4 (stbuehler, 2012-08-11 10:42)

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>