webmcp
view libraries/rocketcgi/rocketcgi.lua @ 11:d76a8857ba62
Added ui.partial and other functions, which allow partial content replacement using XMLHttpRequests; Image support for ui.link
Also includes following changes:
- Fix for rocketcgi library to accept POST data content-types, which contain additional charset information.
- Support arrays passed as params to encode.url (only for keys ending with "[]")
- Version information changed to "1.0.7"
Documentation for added functions is not yet complete.
Also includes following changes:
- Fix for rocketcgi library to accept POST data content-types, which contain additional charset information.
- Support arrays passed as params to encode.url (only for keys ending with "[]")
- Version information changed to "1.0.7"
Documentation for added functions is not yet complete.
| author | jbe/bsw | 
|---|---|
| date | Fri Feb 12 18:40:22 2010 +0100 (2010-02-12) | 
| parents | 72860d232f32 | 
| children | 944642a3e488 | 
 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 and (
   138   post_contenttype == "application/x-www-form-urlencoded" or
   139   string.match(post_contenttype, "^application/x%-www%-form%-urlencoded[ ;]")
   140 ) then
   141   read_urlencoded_form(post_params, post_data)
   142 elseif post_contenttype then
   143   local boundary = string.match(
   144     post_contenttype,
   145     '^multipart/form%-data[ \t]*;[ \t]*boundary="([^"]+)"'
   146   ) or string.match(
   147       post_contenttype,
   148       '^multipart/form%-data[ \t]*;[ \t]*boundary=([^"; \t]+)'
   149   )
   150   if boundary then
   151     local parts = {}
   152     do
   153       local boundary = "\r\n--" .. boundary
   154       local post_data = "\r\n" .. post_data
   155       local pos1, pos2 = string.find(post_data, boundary, 1, true)
   156       while true do
   157         local ind = string.sub(post_data, pos2 + 1, pos2 + 2)
   158         if ind == "\r\n" then
   159           local pos3, pos4 = string.find(post_data, boundary, pos2 + 1, true)
   160           if pos3 then
   161             parts[#parts + 1] = string.sub(post_data, pos2 + 3, pos3 - 1)
   162           else
   163             error("Illegal POST data.")
   164           end
   165           pos1, pos2 = pos3, pos4
   166         elseif ind == "--" then
   167           break
   168         else
   169           error("Illegal POST data.")
   170         end
   171       end
   172     end
   173     for i, part in ipairs(parts) do
   174       local pos = 1
   175       local name, filename, contenttype
   176       while true do
   177         local header
   178         do
   179           local oldpos = pos
   180           pos = string.find(part, "\r\n", oldpos, true)
   181           if not pos then
   182             error("Illegal POST data.")
   183           end
   184           if pos == oldpos then break end
   185           header = string.sub(part, oldpos, pos - 1)
   186           pos = pos + 2
   187         end
   188         if string.find(
   189           string.lower(header),
   190           "^content%-disposition:[ \t]*form%-data[ \t]*;"
   191         ) then
   192           -- TODO: handle all cases correctly
   193           name = string.match(header, ';[ \t]*name="([^"]*)"') or
   194             string.match(header, ';[ \t]*name=([^"; \t]+)')
   195           filename = string.match(header, ';[ \t]*filename="([^"]*)"') or
   196             string.match(header, ';[ \t]*filename=([^"; \t]+)')
   197         else
   198           local dummy, subpos = string.find(
   199             string.lower(header),
   200             "^content%-type:[ \t]*"
   201           )
   202           if subpos then
   203             contenttype = string.sub(header, subpos + 1, #header)
   204           end
   205         end
   206       end
   207       local content = string.sub(part, pos + 2, #part)
   208       if not name then
   209         error("Illegal POST data.")
   210       end
   211       proc_param(post_params, name, content)
   212       post_filenames[name] = filename
   213       post_types[name] = contenttype
   214     end
   215   end
   216 end
   218 if post_data and #post_data > 262144 then
   219   post_data = nil
   220   collectgarbage("collect")
   221 else
   222   post_data = nil
   223 end
