webmcp
view libraries/rocketcgi/rocketcgi.lua @ 0:9fdfb27f8e67
Version 1.0.0
| author | jbe/bsw | 
|---|---|
| date | Sun Oct 25 12:00:00 2009 +0100 (2009-10-25) | 
| parents | |
| children | 72860d232f32 | 
 line source
     1 #!/usr/bin/env lua
     3 local assert       = assert
     4 local error        = error
     5 local getfenv      = getfenv
     6 local getmetatable = getmetatable
     7 local ipairs       = ipairs
     8 local next         = next
     9 local pairs        = pairs
    10 local pcall        = pcall
    11 local print        = print
    12 local rawequal     = rawequal
    13 local rawget       = rawget
    14 local rawset       = rawset
    15 local select       = select
    16 local setfenv      = setfenv
    17 local setmetatable = setmetatable
    18 local tonumber     = tonumber
    19 local tostring     = tostring
    20 local type         = type
    21 local unpack       = unpack
    22 local xpcall       = xpcall
    24 local io     = io
    25 local math   = math
    26 local os     = os
    27 local string = string
    28 local table  = table
    30 module(...)
    32 data_sent = false
    34 function add_header(...)
    35   if data_sent then
    36     error("Can not add header after data has been sent.", 2)
    37   end
    38   io.stdout:write(...)
    39   io.stdout:write("\r\n")
    40 end
    42 function send_data(...)
    43   if not data_sent then
    44     io.stdout:write("\r\n")
    45     data_sent = true
    46   end
    47   io.stdout:write(...)
    48 end
    50 function set_status(status)
    51   add_header("Status: ", status)
    52 end
    54 function redirect(location)
    55   set_status("303 See Other")
    56   add_header("Location: ", location)
    57 end
    59 function set_content_type(content_type)
    60   add_header("Content-Type: ", content_type)
    61 end
    63 method = os.getenv("REQUEST_METHOD") or false
    64 query = os.getenv("QUERY_STRING") or false
    65 cookie_data = os.getenv("HTTP_COOKIE") or false
    66 post_data = io.stdin:read("*a") or false
    67 post_contenttype = os.getenv("CONTENT_TYPE") or false
    68 params = {}
    69 get_params = {}
    70 cookies = {}
    71 post_params = {}
    72 post_filenames = {}
    73 post_types = {}
    75 local urldecode
    76 do
    77   local b0 = string.byte("0")
    78   local b9 = string.byte("9")
    79   local bA = string.byte("A")
    80   local bF = string.byte("F")
    81   local ba = string.byte("a")
    82   local bf = string.byte("f")
    83   function urldecode(str)
    84     return (
    85       string.gsub(
    86         string.gsub(str, "%+", " "),
    87         "%%([0-9A-Fa-f][0-9A-Fa-f])",
    88         function(hex)
    89           local n1, n2 = string.byte(hex, 1, 2)
    90           if n1 >= b0 and n1 <= b9 then n1 = n1 - b0
    91           elseif n1 >= bA and n1 <= bF then n1 = n1 - bA + 10
    92           elseif n1 >= ba and n1 <= bf then n1 = n1 - ba + 10
    93           else return end
    94           if n2 >= b0 and n2 <= b9 then n2 = n2 - b0
    95           elseif n2 >= bA and n2 <= bF then n2 = n2 - bA + 10
    96           elseif n2 >= ba and n2 <= bf then n2 = n2 - ba + 10
    97           else return end
    98           return string.char(n1 * 16 + n2)
    99         end
   100       )
   101     )
   102   end
   103 end
   105 local function proc_param(tbl, key, value)
   106   if string.find(key, "%[%]$") then
   107     if tbl[key] then
   108       table.insert(tbl[key], value)
   109     else
   110       local list = { value }
   111       params[key] = list
   112       tbl[key] = list
   113     end
   114   else
   115     params[key] = value
   116     tbl[key] = value
   117   end
   118 end
   120 local function read_urlencoded_form(tbl, data)
   121   for rawkey, rawvalue in string.gmatch(data, "([^=&]*)=([^=&]*)") do
   122     proc_param(tbl, urldecode(rawkey), urldecode(rawvalue))
   123   end
   124 end
   126 if query then
   127   read_urlencoded_form(get_params, query)
   128 end
   130 if cookie_data then
   131   for rawkey, rawvalue in string.gmatch(cookie_data, "([^=; ]*)=([^=; ]*)") do
   132     cookies[urldecode(rawkey)] = urldecode(rawvalue)
   133   end
   134 end
   136 if post_contenttype == "application/x-www-form-urlencoded" then
   137   read_urlencoded_form(post_params, post_data)
   138 elseif post_contenttype then
   139   local boundary = string.match(
   140     post_contenttype,
   141     '^multipart/form%-data[ \t]*;[ \t]*boundary="([^"]+)"'
   142   ) or string.match(
   143       post_contenttype,
   144       '^multipart/form%-data[ \t]*;[ \t]*boundary=([^"; \t]+)'
   145   )
   146   if boundary then
   147     local parts = {}
   148     do
   149       local boundary = "\r\n--" .. boundary
   150       local post_data = "\r\n" .. post_data
   151       local pos1, pos2 = string.find(post_data, boundary, 1, true)
   152       while true do
   153         local ind = string.sub(post_data, pos2 + 1, pos2 + 2)
   154         if ind == "\r\n" then
   155           local pos3, pos4 = string.find(post_data, boundary, pos2 + 1, true)
   156           if pos3 then
   157             parts[#parts + 1] = string.sub(post_data, pos2 + 3, pos3 - 1)
   158           else
   159             error("Illegal POST data.")
   160           end
   161           pos1, pos2 = pos3, pos4
   162         elseif ind == "--" then
   163           break
   164         else
   165           error("Illegal POST data.")
   166         end
   167       end
   168     end
   169     for i, part in ipairs(parts) do
   170       local pos = 1
   171       local name, filename, contenttype
   172       while true do
   173         local header
   174         do
   175           local oldpos = pos
   176           pos = string.find(part, "\r\n", oldpos, true)
   177           if not pos then
   178             error("Illegal POST data.")
   179           end
   180           if pos == oldpos then break end
   181           header = string.sub(part, oldpos, pos - 1)
   182           pos = pos + 2
   183         end
   184         if string.find(
   185           string.lower(header),
   186           "^content%-disposition:[ \t]*form%-data[ \t]*;"
   187         ) then
   188           -- TODO: handle all cases correctly
   189           name = string.match(header, ';[ \t]*name="([^"]*)"') or
   190             string.match(header, ';[ \t]*name=([^"; \t]+)')
   191           filename = string.match(header, ';[ \t]*filename="([^"]*)"') or
   192             string.match(header, ';[ \t]*filename=([^"; \t]+)')
   193         else
   194           local dummy, subpos = string.find(
   195             string.lower(header),
   196             "^content%-type:[ \t]*"
   197           )
   198           if subpos then
   199             contenttype = string.sub(header, subpos + 1, #header)
   200           end
   201         end
   202       end
   203       local content = string.sub(part, pos + 2, #part)
   204       if not name then
   205         error("Illegal POST data.")
   206       end
   207       proc_param(post_params, name, content)
   208       post_filenames[name] = filename
   209       post_types[name] = contenttype
   210     end
   211   end
   212 end
   214 if post_data and #post_data > 262144 then
   215   post_data = nil
   216   collectgarbage("collect")
   217 else
   218   post_data = nil
   219 end
