webmcp
view libraries/rocketcgi/rocketcgi.lua @ 45:3c12e7dd93e0
Changes of code style in env/trace/_render_sub_tree.lua
| author | jbe | 
|---|---|
| date | Sat Oct 16 18:04:36 2010 +0200 (2010-10-16) | 
| parents | 486ad118c277 | 
| children | 3d43a5cf17c1 | 
 line source
     1 #!/usr/bin/env lua
     3 local assert         = assert
     4 local collectgarbage = collectgarbage
     5 local error          = error
     6 local getfenv        = getfenv
     7 local getmetatable   = getmetatable
     8 local ipairs         = ipairs
     9 local next           = next
    10 local pairs          = pairs
    11 local pcall          = pcall
    12 local print          = print
    13 local rawequal       = rawequal
    14 local rawget         = rawget
    15 local rawset         = rawset
    16 local select         = select
    17 local setfenv        = setfenv
    18 local setmetatable   = setmetatable
    19 local tonumber       = tonumber
    20 local tostring       = tostring
    21 local type           = type
    22 local unpack         = unpack
    23 local xpcall         = xpcall
    25 local io     = io
    26 local math   = math
    27 local os     = os
    28 local string = string
    29 local table  = table
    31 module(...)
    33 data_sent = false
    35 --[[--
    36 rocketcgi.add_header(
    37   string_part1,        -- string
    38   string_part2,        -- optional second part of string to be concatted
    39   ...
    40 )
    42 Sends a header line to the browser. Multiple arguments are concatted to form a single string.
    44 --]]--
    45 function add_header(...)
    46   if data_sent then
    47     error("Can not add header after data has been sent.", 2)
    48   end
    49   io.stdout:write(...)
    50   io.stdout:write("\r\n")
    51 end
    52 --//--
    54 --[[--
    55 rocketcgi.send_data(
    56   string_part1,       -- string
    57   string_part2,       -- optional second part of string to be concatted
    58   ...
    59 )
    61 Sends document data to the browser. Multiple arguments are concatted to form a single string.
    63 --]]--
    64 function send_data(...)
    65   if not data_sent then
    66     io.stdout:write("\r\n")
    67     data_sent = true
    68   end
    69   io.stdout:write(...)
    70 end
    71 --//--
    73 --[[--
    74 rocketcgi.set_status(
    75   status               -- Status code and description, e.g. "404 Not Found"
    76 )
    78 Sends a header line to the browser, indicating a given HTTP status.
    80 --]]--
    81 function set_status(status)
    82   add_header("Status: ", status)
    83 end
    84 --//--
    86 --[[--
    87 rocketcgi.redirect(
    88   status             -- Absolute URL to redirect the browser to
    89 )
    91 Redirects the browser to the given absolute URL, using a 303 Redirect.
    93 --]]--
    94 function redirect(location)
    95   set_status("303 See Other")
    96   add_header("Location: ", location)
    97 end
    98 --//--
   100 --[[--
   101 rocketcgi.set_content_type(
   102   content_type               -- MIME content type
   103 )
   105 Sends a header line specifying the content-type to the browser.
   107 --]]--
   108 function set_content_type(content_type)
   109   add_header("Content-Type: ", content_type)
   110 end
   111 --//--
   113 --[[--
   114 rocketcgi.set_cookie{
   115   name   = name,       -- name of cookie
   116   value  = value,      -- value of cookie
   117   domain = domain,     -- domain where cookie is transmitted
   118   path   = path,       -- path where cookie is transmitted
   119   secure = secure      -- boolean, indicating if cookie should only be transmitted over HTTPS
   120 }
   122 Sends a header line setting a cookie. NOTE: Currently only session cookies are supported.
   124 --]]--
   125 function set_cookie(args)
   126   assert(string.find(args.name, "^[0-9A-Za-z%%._~-]+$"), "Illegal cookie name")
   127   assert(string.find(args.value, "^[0-9A-Za-z%%._~-]+$"), "Illegal cookie value")
   128   local parts = {"Set-Cookie: " .. args.name .. "=" .. args.value}
   129   if args.domain then
   130     assert(
   131       string.find(args.path, "^[0-9A-Za-z%%/._~-]+$"),
   132       "Illegal cookie domain"
   133     )
   134     parts[#parts+1] = "domain=" .. args.domain
   135   end
   136   if args.path then
   137     assert(
   138       string.find(args.path, "^[0-9A-Za-z%%/._~-]+$"),
   139       "Illegal cookie path"
   140     )
   141     parts[#parts+1] = "path=" .. args.path
   142   end
   143   if args.secure then
   144     parts[#parts+1] = "secure"
   145   end
   146   add_header(table.concat(parts, "; "))
   147 end
   148 --//--
   150 method = os.getenv("REQUEST_METHOD") or false
   151 query = os.getenv("QUERY_STRING") or false
   152 cookie_data = os.getenv("HTTP_COOKIE") or false
   153 post_data = io.stdin:read("*a") or false
   154 post_contenttype = os.getenv("CONTENT_TYPE") or false
   155 params = {}
   156 get_params = {}
   157 cookies = {}
   158 post_params = {}
   159 post_filenames = {}
   160 post_types = {}
   162 local urldecode
   163 do
   164   local b0 = string.byte("0")
   165   local b9 = string.byte("9")
   166   local bA = string.byte("A")
   167   local bF = string.byte("F")
   168   local ba = string.byte("a")
   169   local bf = string.byte("f")
   170   function urldecode(str)
   171     return (
   172       string.gsub(
   173         string.gsub(str, "%+", " "),
   174         "%%([0-9A-Fa-f][0-9A-Fa-f])",
   175         function(hex)
   176           local n1, n2 = string.byte(hex, 1, 2)
   177           if n1 >= b0 and n1 <= b9 then n1 = n1 - b0
   178           elseif n1 >= bA and n1 <= bF then n1 = n1 - bA + 10
   179           elseif n1 >= ba and n1 <= bf then n1 = n1 - ba + 10
   180           else return end
   181           if n2 >= b0 and n2 <= b9 then n2 = n2 - b0
   182           elseif n2 >= bA and n2 <= bF then n2 = n2 - bA + 10
   183           elseif n2 >= ba and n2 <= bf then n2 = n2 - ba + 10
   184           else return end
   185           return string.char(n1 * 16 + n2)
   186         end
   187       )
   188     )
   189   end
   190 end
   192 local function proc_param(tbl, key, value)
   193   if string.find(key, "%[%]$") then
   194     if tbl[key] then
   195       table.insert(tbl[key], value)
   196     else
   197       local list = { value }
   198       params[key] = list
   199       tbl[key] = list
   200     end
   201   else
   202     params[key] = value
   203     tbl[key] = value
   204   end
   205 end
   207 local function read_urlencoded_form(tbl, data)
   208   for rawkey, rawvalue in string.gmatch(data, "([^=&]*)=([^=&]*)") do
   209     proc_param(tbl, urldecode(rawkey), urldecode(rawvalue))
   210   end
   211 end
   213 if query then
   214   read_urlencoded_form(get_params, query)
   215 end
   217 if cookie_data then
   218   for rawkey, rawvalue in string.gmatch(cookie_data, "([^=; ]*)=([^=; ]*)") do
   219     cookies[urldecode(rawkey)] = urldecode(rawvalue)
   220   end
   221 end
   223 if post_contenttype and (
   224   post_contenttype == "application/x-www-form-urlencoded" or
   225   string.match(post_contenttype, "^application/x%-www%-form%-urlencoded[ ;]")
   226 ) then
   227   read_urlencoded_form(post_params, post_data)
   228 elseif post_contenttype then
   229   local boundary = string.match(
   230     post_contenttype,
   231     '^multipart/form%-data[ \t]*;[ \t]*boundary="([^"]+)"'
   232   ) or string.match(
   233       post_contenttype,
   234       '^multipart/form%-data[ \t]*;[ \t]*boundary=([^"; \t]+)'
   235   )
   236   if boundary then
   237     local parts = {}
   238     do
   239       local boundary = "\r\n--" .. boundary
   240       local post_data = "\r\n" .. post_data
   241       local pos1, pos2 = string.find(post_data, boundary, 1, true)
   242       while true do
   243         local ind = string.sub(post_data, pos2 + 1, pos2 + 2)
   244         if ind == "\r\n" then
   245           local pos3, pos4 = string.find(post_data, boundary, pos2 + 1, true)
   246           if pos3 then
   247             parts[#parts + 1] = string.sub(post_data, pos2 + 3, pos3 - 1)
   248           else
   249             error("Illegal POST data.")
   250           end
   251           pos1, pos2 = pos3, pos4
   252         elseif ind == "--" then
   253           break
   254         else
   255           error("Illegal POST data.")
   256         end
   257       end
   258     end
   259     for i, part in ipairs(parts) do
   260       local pos = 1
   261       local name, filename, contenttype
   262       while true do
   263         local header
   264         do
   265           local oldpos = pos
   266           pos = string.find(part, "\r\n", oldpos, true)
   267           if not pos then
   268             error("Illegal POST data.")
   269           end
   270           if pos == oldpos then break end
   271           header = string.sub(part, oldpos, pos - 1)
   272           pos = pos + 2
   273         end
   274         if string.find(
   275           string.lower(header),
   276           "^content%-disposition:[ \t]*form%-data[ \t]*;"
   277         ) then
   278           -- TODO: handle all cases correctly
   279           name = string.match(header, ';[ \t]*name="([^"]*)"') or
   280             string.match(header, ';[ \t]*name=([^"; \t]+)')
   281           filename = string.match(header, ';[ \t]*filename="([^"]*)"') or
   282             string.match(header, ';[ \t]*filename=([^"; \t]+)')
   283         else
   284           local dummy, subpos = string.find(
   285             string.lower(header),
   286             "^content%-type:[ \t]*"
   287           )
   288           if subpos then
   289             contenttype = string.sub(header, subpos + 1, #header)
   290           end
   291         end
   292       end
   293       local content = string.sub(part, pos + 2, #part)
   294       if not name then
   295         error("Illegal POST data.")
   296       end
   297       proc_param(post_params, name, content)
   298       post_filenames[name] = filename
   299       post_types[name] = contenttype
   300     end
   301   end
   302 end
   304 if post_data and #post_data > 262144 then
   305   post_data = nil
   306   collectgarbage("collect")
   307 else
   308   post_data = nil
   309 end
