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.
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

Impressum / About Us