webmcp
view libraries/rocketcgi/rocketcgi.lua @ 2:72860d232f32
Version 1.0.2
Fixed bug with explicit garbage collection (requests > 256kB caused an error)
Views prefixed with an underscore can't be called externally
ui.paginate now displays the last page, if the selected page number is too high.
Fixed bug with explicit garbage collection (requests > 256kB caused an error)
Views prefixed with an underscore can't be called externally
ui.paginate now displays the last page, if the selected page number is too high.
| author | jbe/bsw | 
|---|---|
| date | Thu Dec 10 12:00:00 2009 +0100 (2009-12-10) | 
| parents | 9fdfb27f8e67 | 
| children | d76a8857ba62 | 
 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 function add_header(...)
    36   if data_sent then
    37     error("Can not add header after data has been sent.", 2)
    38   end
    39   io.stdout:write(...)
    40   io.stdout:write("\r\n")
    41 end
    43 function send_data(...)
    44   if not data_sent then
    45     io.stdout:write("\r\n")
    46     data_sent = true
    47   end
    48   io.stdout:write(...)
    49 end
    51 function set_status(status)
    52   add_header("Status: ", status)
    53 end
    55 function redirect(location)
    56   set_status("303 See Other")
    57   add_header("Location: ", location)
    58 end
    60 function set_content_type(content_type)
    61   add_header("Content-Type: ", content_type)
    62 end
    64 method = os.getenv("REQUEST_METHOD") or false
    65 query = os.getenv("QUERY_STRING") or false
    66 cookie_data = os.getenv("HTTP_COOKIE") or false
    67 post_data = io.stdin:read("*a") or false
    68 post_contenttype = os.getenv("CONTENT_TYPE") or false
    69 params = {}
    70 get_params = {}
    71 cookies = {}
    72 post_params = {}
    73 post_filenames = {}
    74 post_types = {}
    76 local urldecode
    77 do
    78   local b0 = string.byte("0")
    79   local b9 = string.byte("9")
    80   local bA = string.byte("A")
    81   local bF = string.byte("F")
    82   local ba = string.byte("a")
    83   local bf = string.byte("f")
    84   function urldecode(str)
    85     return (
    86       string.gsub(
    87         string.gsub(str, "%+", " "),
    88         "%%([0-9A-Fa-f][0-9A-Fa-f])",
    89         function(hex)
    90           local n1, n2 = string.byte(hex, 1, 2)
    91           if n1 >= b0 and n1 <= b9 then n1 = n1 - b0
    92           elseif n1 >= bA and n1 <= bF then n1 = n1 - bA + 10
    93           elseif n1 >= ba and n1 <= bf then n1 = n1 - ba + 10
    94           else return end
    95           if n2 >= b0 and n2 <= b9 then n2 = n2 - b0
    96           elseif n2 >= bA and n2 <= bF then n2 = n2 - bA + 10
    97           elseif n2 >= ba and n2 <= bf then n2 = n2 - ba + 10
    98           else return end
    99           return string.char(n1 * 16 + n2)
   100         end
   101       )
   102     )
   103   end
   104 end
   106 local function proc_param(tbl, key, value)
   107   if string.find(key, "%[%]$") then
   108     if tbl[key] then
   109       table.insert(tbl[key], value)
   110     else
   111       local list = { value }
   112       params[key] = list
   113       tbl[key] = list
   114     end
   115   else
   116     params[key] = value
   117     tbl[key] = value
   118   end
   119 end
   121 local function read_urlencoded_form(tbl, data)
   122   for rawkey, rawvalue in string.gmatch(data, "([^=&]*)=([^=&]*)") do
   123     proc_param(tbl, urldecode(rawkey), urldecode(rawvalue))
   124   end
   125 end
   127 if query then
   128   read_urlencoded_form(get_params, query)
   129 end
   131 if cookie_data then
   132   for rawkey, rawvalue in string.gmatch(cookie_data, "([^=; ]*)=([^=; ]*)") do
   133     cookies[urldecode(rawkey)] = urldecode(rawvalue)
   134   end
   135 end
   137 if post_contenttype == "application/x-www-form-urlencoded" then
   138   read_urlencoded_form(post_params, post_data)
   139 elseif post_contenttype then
   140   local boundary = string.match(
   141     post_contenttype,
   142     '^multipart/form%-data[ \t]*;[ \t]*boundary="([^"]+)"'
   143   ) or string.match(
   144       post_contenttype,
   145       '^multipart/form%-data[ \t]*;[ \t]*boundary=([^"; \t]+)'
   146   )
   147   if boundary then
   148     local parts = {}
   149     do
   150       local boundary = "\r\n--" .. boundary
   151       local post_data = "\r\n" .. post_data
   152       local pos1, pos2 = string.find(post_data, boundary, 1, true)
   153       while true do
   154         local ind = string.sub(post_data, pos2 + 1, pos2 + 2)
   155         if ind == "\r\n" then
   156           local pos3, pos4 = string.find(post_data, boundary, pos2 + 1, true)
   157           if pos3 then
   158             parts[#parts + 1] = string.sub(post_data, pos2 + 3, pos3 - 1)
   159           else
   160             error("Illegal POST data.")
   161           end
   162           pos1, pos2 = pos3, pos4
   163         elseif ind == "--" then
   164           break
   165         else
   166           error("Illegal POST data.")
   167         end
   168       end
   169     end
   170     for i, part in ipairs(parts) do
   171       local pos = 1
   172       local name, filename, contenttype
   173       while true do
   174         local header
   175         do
   176           local oldpos = pos
   177           pos = string.find(part, "\r\n", oldpos, true)
   178           if not pos then
   179             error("Illegal POST data.")
   180           end
   181           if pos == oldpos then break end
   182           header = string.sub(part, oldpos, pos - 1)
   183           pos = pos + 2
   184         end
   185         if string.find(
   186           string.lower(header),
   187           "^content%-disposition:[ \t]*form%-data[ \t]*;"
   188         ) then
   189           -- TODO: handle all cases correctly
   190           name = string.match(header, ';[ \t]*name="([^"]*)"') or
   191             string.match(header, ';[ \t]*name=([^"; \t]+)')
   192           filename = string.match(header, ';[ \t]*filename="([^"]*)"') or
   193             string.match(header, ';[ \t]*filename=([^"; \t]+)')
   194         else
   195           local dummy, subpos = string.find(
   196             string.lower(header),
   197             "^content%-type:[ \t]*"
   198           )
   199           if subpos then
   200             contenttype = string.sub(header, subpos + 1, #header)
   201           end
   202         end
   203       end
   204       local content = string.sub(part, pos + 2, #part)
   205       if not name then
   206         error("Illegal POST data.")
   207       end
   208       proc_param(post_params, name, content)
   209       post_filenames[name] = filename
   210       post_types[name] = contenttype
   211     end
   212   end
   213 end
   215 if post_data and #post_data > 262144 then
   216   post_data = nil
   217   collectgarbage("collect")
   218 else
   219   post_data = nil
   220 end
