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