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
|