post.lua
来自「cgi for lua, you can build your personal」· LUA 代码 · 共 292 行
LUA
292 行
------------------------------------------------------------------------------ Process POST data.-- This library depends on some functions that read POST data and other-- HTTP information. A beginning is:-- require"post"-- local params = {}-- post.parsedata {-- read = ap.get_client_block or io.input,-- discardinput = ap.discard_request_body,-- content_type = ap.get_header"content-type" or os.getenv"CONTENT_TYPE",-- content_length = ap.get_header"content-length" or os.getenv"CONTENT_LENGTH",-- maxinput = 1024 * 1024,-- maxfilesize = 512 * 1024,-- args = params,-- }---- @release $Id: post.lua,v 1.12 2007/04/16 14:01:32 tomas Exp $----------------------------------------------------------------------------require"cgilua.readuntil"require"cgilua.urlcode"local assert, error, pairs, tonumber, tostring, type = assert, error, pairs, tonumber, tostring, typelocal tmpfile = io.tmpfilelocal getn, tinsert = table.getn, table.insertlocal format, gsub, strfind, strlower, strlen = string.format, string.gsub, string.find, string.lower, string.lenlocal min = math.minlocal iterate = cgilua.readuntil.iteratelocal urlcode = cgilua.urlcode-- environment for processing multipart/form-data inputlocal boundary = nil -- boundary string that separates each 'part' of inputlocal maxfilesize = nil -- maximum size for file uploadlocal maxinput = nil -- maximum size of total POST datalocal inputfile = nil -- temporary file for inputting form-datalocal bytesleft = nil -- number of bytes yet to be readlocal content_type = nil -- request's content-type-- local functionslocal discardinput = nil -- discard all remaining inputlocal readuntil = nil -- read until delimiterlocal read = nil -- basic read functionmodule ("cgilua.post")---- Extract the boundary string from CONTENT_TYPE metavariable--local function getboundary () local _,_,boundary = strfind (content_type, "boundary%=(.-)$") return "--"..boundary end---- Create a table containing the headers of a multipart/form-data field--local function breakheaders (hdrdata) local headers = {} gsub (hdrdata, '([^%c%s:]+):%s+([^\n]+)', function(type,val) type = strlower(type) headers[type] = val end) return headersend---- Read the headers of the next multipart/form-data field ---- This function returns a table containing the headers values. Each header-- value is indexed by the corresponding header "type". -- If end of input is reached (no more fields to process) it returns nil.--local function readfieldheaders () local EOH = "\r\n\r\n" -- <CR><LF><CR><LF> local hdrdata = "" local out = function (str) hdrdata = hdrdata..str end if readuntil (EOH, out) then -- parse headers return breakheaders (hdrdata) else -- no header found return nil endend---- Extract a field name (and possible filename) from its disposition header--local function getfieldnames (headers) local disposition_hdr = headers["content-disposition"] local attrs = {} if disposition_hdr then gsub(disposition_hdr, ';%s*([^%s=]+)="(.-)"', function(attr, val) attrs[attr] = val end) else error("Error processing multipart/form-data.".. "\nMissing content-disposition header") end return attrs.name, attrs.filenameend---- Read the contents of a 'regular' field to a string--local function readfieldcontents () local value = "" local boundaryline = "\r\n"..boundary local out = function (str) value = value..str end if readuntil (boundaryline, out) then return value else error("Error processing multipart/form-data.\nUnexpected end of input\n") endend---- Read the contents of a 'file' field to a temporary file (file upload)--local function fileupload (filename) -- create a temporary file for uploading the file field local file, err = tmpfile() if file == nil then discardinput(bytesleft) error("Cannot create a temporary file.\n"..err) end local bytesread = 0 local boundaryline = "\r\n"..boundary local out = function (str) local sl = strlen (str) if bytesread + sl > maxfilesize then discardinput (bytesleft) error (format ("Maximum file size (%d kbytes) exceeded while uploading `%s'", maxfilesize / 1024, filename)) end file:write (str) bytesread = bytesread + sl end if readuntil (boundaryline, out) then file:seek ("set", 0) return file, bytesread else error (format ("Error processing multipart/form-data.\nUnexpected end of input while uploading %s", filename)) endend---- Compose a file field 'value' --local function filevalue (filehandle, filename, filesize, headers) -- the temporary file handle local value = { file = filehandle, filename = filename, filesize = filesize } -- copy additional header values for hdr, hdrval in pairs(headers) do if hdr ~= "content-disposition" then value[hdr] = hdrval end end return valueend---- Process multipart/form-data ---- This function receives the total size of the incoming multipart/form-data, -- the maximum size for a file upload, and a reference to a table where the -- form fields should be stored.---- For every field in the incoming form-data a (name=value) pair is -- inserted into the given table. [[name]] is the field name extracted-- from the content-disposition header.---- If a field is of type 'file' (i.e., a 'filename' attribute was found-- in its content-disposition header) a temporary file is created -- and the field contents are written to it. In this case,-- [[value]] has a table that contains the temporary file handle -- (key 'file') and the file name (key 'filename'). Optional headers-- included in the field description are also inserted into this table,-- as (header_type=value) pairs.---- If the field is not of type 'file', [[value]] contains the field -- contents.--local function Main (inputsize, args) -- create a temporary file for processing input data local inputf,err = tmpfile() if inputf == nil then discardinput(inputsize) error("Cannot create a temporary file.\n"..err) end -- set the environment for processing the multipart/form-data inputfile = inputf bytesleft = inputsize maxfilesize = maxfilesize or inputsize boundary = getboundary() while true do -- read the next field header(s) local headers = readfieldheaders() if not headers then break end -- end of input -- get the name attributes for the form field (name and filename) local name, filename = getfieldnames(headers) -- get the field contents local value if filename then local filehandle, filesize = fileupload(filename) value = filevalue(filehandle, filename, filesize, headers) else value = readfieldcontents() end -- insert the form field into table [[args]] urlcode.insertfield(args, name, value) endend---- Initialize the library by setting the dependent functions:-- content_type = value of "Content-type" header-- content_length = value of "Content-length" header-- read = function that can read POST data-- discardinput (optional) = function that discard POST data-- maxinput (optional) = limit of POST data (in bytes)-- maxfilesize (optional) = limit of uploaded file(s) (in bytes)--local function init (defs) assert (defs.read) assert (defs.content_type) read = defs.read readuntil = iterate (function () if bytesleft <= 0 then return nil end local n = min (bytesleft, 2^13) -- 2^13 == 8192 bytesleft = bytesleft - n return read (n) end) if defs.discard_function then discardinput = defs.discardinput else discardinput = function (inputsize) readuntil ('\0', function()end) end end content_type = defs.content_type if defs.maxinput then maxinput = defs.maxinput end if defs.maxfilesize then maxfilesize = defs.maxfilesize endend------------------------------------------------------------------------------ Parse the POST REQUEST incoming data according to its "content type"-- as defined by the metavariable CONTENT_TYPE (RFC CGI)---- An error is issued if the "total" size of the incoming data-- (defined by the metavariable CONTENT_LENGTH) exceeds the-- maximum input size allowed----------------------------------------------------------------------------function parsedata (defs) assert (type(defs.args) == "table", "field `args' must be a table") init (defs) -- get the "total" size of the incoming data local inputsize = tonumber(defs.content_length) or 0 if inputsize > maxinput then -- some Web Servers (like IIS) require that all the incoming data is read bytesleft = inputsize discardinput(inputsize) error(format("Total size of incoming data (%d KB) exceeds configured maximum (%d KB)", inputsize /1024, maxinput / 1024)) end -- process the incoming data according to its content type local contenttype = content_type if not contenttype then error("Undefined Media Type") end if strfind(contenttype, "x%-www%-form%-urlencoded") then urlcode.parsequery (read (inputsize), defs.args) elseif strfind(contenttype, "multipart/form%-data") then Main (inputsize, defs.args) elseif strfind (contenttype, "text/xml") or strfind (contenttype, "text/plain") then tinsert (defs.args, read (inputsize)) else error("Unsupported Media Type: "..contenttype) endend
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?