cgi-bin/tek/class/fastcgi.lua
author Timm S. Mueller <tmueller@neoscientists.org>
Fri, 23 Nov 2007 01:46:43 +0100
changeset 201 d52b05a9fe9c
child 212 a1aad6415384
permissions -rw-r--r--
Added permission for lua.cgi in Makefile; FastCGI class added; improved
configuration and statistics output in FastCGI runner; versionstring format
changed; inheritance is now based on tek.class - Atom class removed; Debug
library added; removed "attr" permission
     1 
     2 --
     3 --	tek.class.fastcgi
     4 --	Written by Timm S. Mueller <tmueller@neoscientists.org>
     5 --
     6 --	Lua FastCGI protocol implementation - specifically written for
     7 --	the "external" server configurations (via IP socket)
     8 --
     9 --	Methods that must be overwritten by the user:
    10 --		fcgi:have_params(req, params) -- all parameters transferred
    11 --
    12 --	Methods that can be overwritten by the user:
    13 --		fcgi:abort(req) -- request aborted by webserver
    14 --		fcgi:update_stream(req, type, stream) -- stream created/updated
    15 --		fcgi:have_stream(req, type, stream) -- stream completed
    16 --
    17 
    18 local Class = require "tek.class"
    19 local socket = require "socket"
    20 local bit = require "bit"
    21 
    22 local insert, remove, concat, pairs, ipairs, error, assert =
    23 	table.insert, table.remove, table.concat, pairs, ipairs, error, assert
    24 local tonumber, setmetatable, unpack, type =
    25 	tonumber, setmetatable, unpack, type
    26 local char = string.char
    27 local math = math
    28 
    29 module("tek.class.fastcgi", Class)
    30 _VERSION = "FastCGI 0.2"
    31 
    32 -------------------------------------------------------------------------------
    33 -- local FIFO class:
    34 -------------------------------------------------------------------------------
    35 
    36 local FIFO = Class:newClass()
    37 
    38 function FIFO.new(class, self)
    39 	self = Class.new(class, self or { })
    40 	self.buf = self.buf or { }
    41 	return self
    42 end
    43 
    44 function FIFO:write(s)
    45 	if s and s ~= -1 then
    46 		insert(self.buf, s)
    47 	elseif self.buf[#self.buf] ~= -1 then
    48 		insert(self.buf, -1) -- EOF
    49 	end
    50 end
    51 
    52 function FIFO:readn(len)
    53 	local t, p, l = { }
    54 	while len > 0 do
    55 		p = remove(self.buf, 1)
    56 		if not p then
    57 			break -- no more data
    58 		end
    59 		if p == -1 then
    60 			insert(self.buf, -1) -- push back EOF
    61 			break -- end of stream, EOF in next turn
    62 		end
    63 		l = p:len()
    64 		if l > len then -- break buffer fragment in two
    65 			insert(t, p:sub(1, len))
    66 			insert(self.buf, 1, p:sub(len + 1))
    67 			break
    68 		end
    69 		insert(t, p)
    70 		len = len - l
    71 	end
    72 	return concat(t)
    73 end
    74 
    75 function FIFO:reada()
    76 	local last = remove(self.buf)
    77 	if last ~= -1 then -- not EOF
    78 		insert(self.buf, last) -- push back
    79 		last = nil
    80 	end
    81 	local s = concat(self.buf)
    82 	self.buf = { last }
    83 	return s
    84 end
    85 
    86 function FIFO:read(...)
    87 	if self.buf[1] == -1 then
    88 		return -- EOF
    89 	end
    90 	local t, s = { }
    91 	for _, what in ipairs(arg) do
    92 		if what == "*a" then
    93 			s = self:reada()
    94 		elseif type(what) == "number" then
    95 			s = self:readn(tonumber(what))
    96 		else
    97 			error("unknwon format")
    98 		end
    99 		insert(t, s)
   100 	end
   101 	return unpack(t)
   102 end
   103 
   104 --	FCGI encoded lengths and key/value pairs:
   105 
   106 function FIFO:readlen()
   107 	local l = self:read(1)
   108 	if not l then return end
   109 	l = l:byte()
   110 	if l < 128 then
   111 		return l
   112 	end
   113 	local t = self:read(3)
   114 	if not t then return end
   115 	return (l % 128) * 16777216 + t:byte(1) * 65536 +
   116 		t:byte(2) * 256 + t:byte(3)
   117 end
   118 
   119 function FIFO:readkeyvals()
   120 	local t = { }
   121 	local l1, l2, key, val
   122 	while true do
   123 		l1 = self:readlen()
   124 		if not l1 then break end
   125 		l2 = self:readlen()
   126 		if not l2 then break end
   127  		key = self:read(l1)
   128  		val = self:read(l2)
   129 		t[key] = val
   130 	end
   131 	return t
   132 end
   133 
   134 -------------------------------------------------------------------------------
   135 -- FCGI class:
   136 -------------------------------------------------------------------------------
   137 
   138 FCGI_BEGIN_REQUEST = 1
   139 FCGI_ABORT_REQUEST = 2
   140 FCGI_END_REQUEST = 3
   141 FCGI_PARAMS = 4
   142 FCGI_STDIN = 5
   143 FCGI_STDOUT = 6
   144 FCGI_STDERR = 7
   145 FCGI_DATA = 8
   146 FCGI_GET_VALUES = 9
   147 FCGI_GET_VALUES_RESULT = 10
   148 FCGI_UNKNOWN_TYPE = 11
   149 
   150 local FCGI_RESPONDER = 1
   151 local FCGI_REQUEST_COMPLETE = 0
   152 local FCGI_UNKNOWN_ROLE = 3
   153 
   154 local function encodelen(buf, l)
   155 	if l > 127 then
   156 		insert(buf, char(bit.rshift(bit.band(l, 0x7f000000), 24) + 128))
   157 		insert(buf, char(bit.rshift(bit.band(l, 0x00ff0000), 16)))
   158 		insert(buf, char(bit.rshift(bit.band(l, 0x0000ff00), 8)))
   159 	end
   160 	insert(buf, char(bit.band(l, 0xff)))
   161 end
   162 
   163 local function encodekeyvals(t)
   164 	local buf = { }
   165 	for key, val in pairs(t) do
   166 		encodelen(buf, key:len())
   167 		encodelen(buf, val:len())
   168 		insert(buf, key)
   169 		insert(buf, val)
   170 	end
   171 	return concat(buf)
   172 end
   173 
   174 -- local FCGI = Class:newClass(_M)
   175 local FCGI = _M
   176 
   177 function FCGI.new(class, self)
   178 	self = Class.new(class, self or { })
   179 	self.requests = { }
   180 	return self
   181 end
   182 
   183 function FCGI:readrecord()
   184 	local t, err = self.socket:receive(8)
   185 	if not t then return end
   186 	if err then error(err) end
   187 	local r = {
   188 		ver = t:byte(1),
   189 		type = t:byte(2),
   190 		id = t:byte(3) * 256 + t:byte(4),
   191 		len = t:byte(5) * 256 + t:byte(6)
   192 	}
   193 	if r.len == 0 then
   194 		r.content = ""
   195 	else
   196 		r.content, err = self.socket:receive(r.len)
   197 		if not r.content then return end
   198 		if err then error(err) end
   199 	end
   200 	local pad = t:byte(7)
   201 	if pad > 0 then
   202 		t, err = self.socket:receive(pad)
   203 		if not t then return end
   204 		if err then error(err) end
   205 	end
   206 	return r
   207 end
   208 
   209 function FCGI:write(type, id, s)
   210 	local totlen = s:len()
   211 	local totpos = 1
   212 	while totlen > 0 do
   213 		local len = math.min(totlen, 65535)
   214 		local buf = concat {
   215 			char(1), -- version
   216 			char(type), -- type
   217 			char(bit.rshift(id, 8)), -- id1
   218 			char(id % 256), -- id0
   219 			char(bit.rshift(len, 8)), -- len1
   220 			char(len % 256), -- len0
   221 			char(0), -- pad = 0
   222 			char(0), -- reserved
   223 			s:sub(totpos, totpos + len - 1) -- content
   224 		}
   225 		totpos = totpos + len
   226 		totlen = totlen - len
   227 
   228 		len = buf:len()
   229 		local pos, res, err = 1
   230 		while pos <= len do
   231 			res, err = self.socket:send(buf, pos)
   232 			if not res then
   233 				return nil, err
   234 			end
   235 			pos = res + 1
   236 		end
   237 	end
   238 	return true
   239 end
   240 
   241 function FCGI:write_stdout(id, s)
   242 	return self:write(FCGI_STDOUT, id, s)
   243 end
   244 
   245 function FCGI:collectstream(r)
   246 	local req = self.requests[r.id]
   247 	local s = req.streams[r.type]
   248 	if not s then
   249 		s = FIFO:new()
   250 		req.streams[r.type] = s
   251 	end
   252 	if r.len == 0 then
   253 		s:write() -- append EOF
   254 		return s, true -- finished
   255 	end
   256 	s:write(r.content)
   257 	return s
   258 end
   259 
   260 function FCGI:endrequest(req, protstatus, appstatus)
   261 	protstatus = protstatus or req.protstatus or FCGI_REQUEST_COMPLETE
   262 	appstatus = appstatus or req.appstatus or 0
   263 	self.requests[req.id] = nil -- delete request
   264 	return self:write(FCGI_END_REQUEST, req.id, concat {
   265 		char(bit.rshift(bit.band(appstatus, 0x7f000000), 24)),
   266 		char(bit.rshift(bit.band(appstatus, 0x00ff0000), 16)),
   267 		char(bit.rshift(bit.band(appstatus, 0x0000ff00), 8)),
   268 		char(bit.band(appstatus, 0xff)),
   269 		char(protstatus),
   270 		char(0), char(0), char(0)
   271 	})
   272 end
   273 
   274 function FCGI:newrequest(id, role, flags)
   275 	assert(not self.requests[id])
   276 	local req = { id = id, role = role, flags = flags, streams = { } }
   277 	self.requests[id] = req
   278 	return req
   279 end
   280 
   281 function FCGI:processrecord(r)
   282 
   283 	local c = r.content
   284 	local req = self.requests[r.id]
   285 
   286 	if r.type == FCGI_BEGIN_REQUEST then
   287 		assert(not req)
   288 		local role = c:byte(1) * 256 + c:byte(2)
   289 		if role == FCGI_RESPONDER then
   290 			-- new request
   291 			local flags = c:byte(3)
   292 			req = self:newrequest(r.id, role, flags)
   293 			return true -- continue
   294 		end
   295 		-- unknown role
   296 		return self:endrequest(req, FCGI_UNKNOWN_ROLE)
   297 	end
   298 
   299 	if not req then -- request already closed
   300 		return true -- continue
   301 	end
   302 
   303 	if r.type == FCGI_ABORT_REQUEST then
   304 		return self:abortrequest(req)
   305 
   306 	elseif r.type == FCGI_GET_VALUES then
   307 		local s, fin = self:collectstream(r)
   308 		if s and fin then
   309 			local res = { }
   310 			for k in pairs(s:readkeyvals()) do
   311 				if k == "FCGI_MAX_CONNS" then
   312 					res.FCGI_MAX_CONNS = "16"
   313 				elseif k == "FCGI_MAX_REQS" then
   314 					res.FCGI_MAX_CONNS = "32"
   315 				elseif k == "FCGI_MAX_REQS" then
   316 					res.FCGI_MAX_CONNS = "1"
   317 				end
   318 			end
   319 			res = encodekeyvals(res)
   320 			return self:write(FCGI_GET_VALUES_RESULT, 0, res)
   321 		end
   322 
   323 	elseif r.type == FCGI_PARAMS then
   324 		local s, fin = self:collectstream(r)
   325 		if s and fin then
   326 			req.params = s:readkeyvals()
   327 			return self:have_params(req, req.params)
   328 		end
   329 
   330 	elseif r.type == self.FCGI_STDIN or r.type == self.FCGI_DATA then
   331 		local s, fin = self:collectstream(r)
   332 		if fin then
   333 			if self.have_stream then
   334 				return self:have_stream(req, r.type, s)
   335 			end
   336 		else
   337 			if self.update_stream then
   338 				return self:update_stream(req, r.type, s)
   339 			end
   340 		end
   341 
   342 	else
   343 		-- unknown record
   344 		local buf = char(r.type) .. char(0):rep(7)
   345 		return self:write(FCGI_UNKNOWN_TYPE, 0, buf)
   346 	end
   347 
   348 	return true -- continue
   349 end
   350 
   351 function FCGI:serve(socket)
   352 	assert(socket)
   353 	self.socket = socket
   354 	self.serve = true
   355 	while self.serve do
   356 		local r = self:readrecord()
   357 		if not r then
   358 			break
   359 		end
   360 		if not self:processrecord(r) then
   361 			break
   362 		end
   363 	end
   364 end
   365 
   366 function FCGI:stop()
   367 	self.serve = false
   368 end
   369 
   370 function FCGI:abortrequest(req)
   371 	-- request aborted by webserver, confirm:
   372 	return self:endrequest(req)
   373 end