webmcp

annotate libraries/rocketcgi/rocketcgi.lua @ 194:654ddbcc49d0

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

Impressum / About Us