webmcp
diff 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 diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/libraries/rocketcgi/rocketcgi.lua Sun Oct 25 12:00:00 2009 +0100 1.3 @@ -0,0 +1,219 @@ 1.4 +#!/usr/bin/env lua 1.5 + 1.6 +local assert = assert 1.7 +local error = error 1.8 +local getfenv = getfenv 1.9 +local getmetatable = getmetatable 1.10 +local ipairs = ipairs 1.11 +local next = next 1.12 +local pairs = pairs 1.13 +local pcall = pcall 1.14 +local print = print 1.15 +local rawequal = rawequal 1.16 +local rawget = rawget 1.17 +local rawset = rawset 1.18 +local select = select 1.19 +local setfenv = setfenv 1.20 +local setmetatable = setmetatable 1.21 +local tonumber = tonumber 1.22 +local tostring = tostring 1.23 +local type = type 1.24 +local unpack = unpack 1.25 +local xpcall = xpcall 1.26 + 1.27 +local io = io 1.28 +local math = math 1.29 +local os = os 1.30 +local string = string 1.31 +local table = table 1.32 + 1.33 +module(...) 1.34 + 1.35 +data_sent = false 1.36 + 1.37 +function add_header(...) 1.38 + if data_sent then 1.39 + error("Can not add header after data has been sent.", 2) 1.40 + end 1.41 + io.stdout:write(...) 1.42 + io.stdout:write("\r\n") 1.43 +end 1.44 + 1.45 +function send_data(...) 1.46 + if not data_sent then 1.47 + io.stdout:write("\r\n") 1.48 + data_sent = true 1.49 + end 1.50 + io.stdout:write(...) 1.51 +end 1.52 + 1.53 +function set_status(status) 1.54 + add_header("Status: ", status) 1.55 +end 1.56 + 1.57 +function redirect(location) 1.58 + set_status("303 See Other") 1.59 + add_header("Location: ", location) 1.60 +end 1.61 + 1.62 +function set_content_type(content_type) 1.63 + add_header("Content-Type: ", content_type) 1.64 +end 1.65 + 1.66 +method = os.getenv("REQUEST_METHOD") or false 1.67 +query = os.getenv("QUERY_STRING") or false 1.68 +cookie_data = os.getenv("HTTP_COOKIE") or false 1.69 +post_data = io.stdin:read("*a") or false 1.70 +post_contenttype = os.getenv("CONTENT_TYPE") or false 1.71 +params = {} 1.72 +get_params = {} 1.73 +cookies = {} 1.74 +post_params = {} 1.75 +post_filenames = {} 1.76 +post_types = {} 1.77 + 1.78 +local urldecode 1.79 +do 1.80 + local b0 = string.byte("0") 1.81 + local b9 = string.byte("9") 1.82 + local bA = string.byte("A") 1.83 + local bF = string.byte("F") 1.84 + local ba = string.byte("a") 1.85 + local bf = string.byte("f") 1.86 + function urldecode(str) 1.87 + return ( 1.88 + string.gsub( 1.89 + string.gsub(str, "%+", " "), 1.90 + "%%([0-9A-Fa-f][0-9A-Fa-f])", 1.91 + function(hex) 1.92 + local n1, n2 = string.byte(hex, 1, 2) 1.93 + if n1 >= b0 and n1 <= b9 then n1 = n1 - b0 1.94 + elseif n1 >= bA and n1 <= bF then n1 = n1 - bA + 10 1.95 + elseif n1 >= ba and n1 <= bf then n1 = n1 - ba + 10 1.96 + else return end 1.97 + if n2 >= b0 and n2 <= b9 then n2 = n2 - b0 1.98 + elseif n2 >= bA and n2 <= bF then n2 = n2 - bA + 10 1.99 + elseif n2 >= ba and n2 <= bf then n2 = n2 - ba + 10 1.100 + else return end 1.101 + return string.char(n1 * 16 + n2) 1.102 + end 1.103 + ) 1.104 + ) 1.105 + end 1.106 +end 1.107 + 1.108 +local function proc_param(tbl, key, value) 1.109 + if string.find(key, "%[%]$") then 1.110 + if tbl[key] then 1.111 + table.insert(tbl[key], value) 1.112 + else 1.113 + local list = { value } 1.114 + params[key] = list 1.115 + tbl[key] = list 1.116 + end 1.117 + else 1.118 + params[key] = value 1.119 + tbl[key] = value 1.120 + end 1.121 +end 1.122 + 1.123 +local function read_urlencoded_form(tbl, data) 1.124 + for rawkey, rawvalue in string.gmatch(data, "([^=&]*)=([^=&]*)") do 1.125 + proc_param(tbl, urldecode(rawkey), urldecode(rawvalue)) 1.126 + end 1.127 +end 1.128 + 1.129 +if query then 1.130 + read_urlencoded_form(get_params, query) 1.131 +end 1.132 + 1.133 +if cookie_data then 1.134 + for rawkey, rawvalue in string.gmatch(cookie_data, "([^=; ]*)=([^=; ]*)") do 1.135 + cookies[urldecode(rawkey)] = urldecode(rawvalue) 1.136 + end 1.137 +end 1.138 + 1.139 +if post_contenttype == "application/x-www-form-urlencoded" then 1.140 + read_urlencoded_form(post_params, post_data) 1.141 +elseif post_contenttype then 1.142 + local boundary = string.match( 1.143 + post_contenttype, 1.144 + '^multipart/form%-data[ \t]*;[ \t]*boundary="([^"]+)"' 1.145 + ) or string.match( 1.146 + post_contenttype, 1.147 + '^multipart/form%-data[ \t]*;[ \t]*boundary=([^"; \t]+)' 1.148 + ) 1.149 + if boundary then 1.150 + local parts = {} 1.151 + do 1.152 + local boundary = "\r\n--" .. boundary 1.153 + local post_data = "\r\n" .. post_data 1.154 + local pos1, pos2 = string.find(post_data, boundary, 1, true) 1.155 + while true do 1.156 + local ind = string.sub(post_data, pos2 + 1, pos2 + 2) 1.157 + if ind == "\r\n" then 1.158 + local pos3, pos4 = string.find(post_data, boundary, pos2 + 1, true) 1.159 + if pos3 then 1.160 + parts[#parts + 1] = string.sub(post_data, pos2 + 3, pos3 - 1) 1.161 + else 1.162 + error("Illegal POST data.") 1.163 + end 1.164 + pos1, pos2 = pos3, pos4 1.165 + elseif ind == "--" then 1.166 + break 1.167 + else 1.168 + error("Illegal POST data.") 1.169 + end 1.170 + end 1.171 + end 1.172 + for i, part in ipairs(parts) do 1.173 + local pos = 1 1.174 + local name, filename, contenttype 1.175 + while true do 1.176 + local header 1.177 + do 1.178 + local oldpos = pos 1.179 + pos = string.find(part, "\r\n", oldpos, true) 1.180 + if not pos then 1.181 + error("Illegal POST data.") 1.182 + end 1.183 + if pos == oldpos then break end 1.184 + header = string.sub(part, oldpos, pos - 1) 1.185 + pos = pos + 2 1.186 + end 1.187 + if string.find( 1.188 + string.lower(header), 1.189 + "^content%-disposition:[ \t]*form%-data[ \t]*;" 1.190 + ) then 1.191 + -- TODO: handle all cases correctly 1.192 + name = string.match(header, ';[ \t]*name="([^"]*)"') or 1.193 + string.match(header, ';[ \t]*name=([^"; \t]+)') 1.194 + filename = string.match(header, ';[ \t]*filename="([^"]*)"') or 1.195 + string.match(header, ';[ \t]*filename=([^"; \t]+)') 1.196 + else 1.197 + local dummy, subpos = string.find( 1.198 + string.lower(header), 1.199 + "^content%-type:[ \t]*" 1.200 + ) 1.201 + if subpos then 1.202 + contenttype = string.sub(header, subpos + 1, #header) 1.203 + end 1.204 + end 1.205 + end 1.206 + local content = string.sub(part, pos + 2, #part) 1.207 + if not name then 1.208 + error("Illegal POST data.") 1.209 + end 1.210 + proc_param(post_params, name, content) 1.211 + post_filenames[name] = filename 1.212 + post_types[name] = contenttype 1.213 + end 1.214 + end 1.215 +end 1.216 + 1.217 +if post_data and #post_data > 262144 then 1.218 + post_data = nil 1.219 + collectgarbage("collect") 1.220 +else 1.221 + post_data = nil 1.222 +end