Rework LuCI build system
[project/luci.git] / modules / luci-base / luasrc / http.lua
1 --[[
2 LuCI - HTTP-Interaction
3
4 Description:
5 HTTP-Header manipulator and form variable preprocessor
6
7 License:
8 Copyright 2008 Steven Barth <steven@midlink.org>
9
10 Licensed under the Apache License, Version 2.0 (the "License");
11 you may not use this file except in compliance with the License.
12 You may obtain a copy of the License at
13
14 http://www.apache.org/licenses/LICENSE-2.0
15
16 Unless required by applicable law or agreed to in writing, software
17 distributed under the License is distributed on an "AS IS" BASIS,
18 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 See the License for the specific language governing permissions and
20 limitations under the License.
21
22 ]]--
23
24 local ltn12 = require "luci.ltn12"
25 local protocol = require "luci.http.protocol"
26 local util = require "luci.util"
27 local string = require "string"
28 local coroutine = require "coroutine"
29 local table = require "table"
30
31 local ipairs, pairs, next, type, tostring, error =
32 ipairs, pairs, next, type, tostring, error
33
34 --- LuCI Web Framework high-level HTTP functions.
35 module "luci.http"
36
37 context = util.threadlocal()
38
39 Request = util.class()
40 function Request.__init__(self, env, sourcein, sinkerr)
41 self.input = sourcein
42 self.error = sinkerr
43
44
45 -- File handler nil by default to let .content() work
46 self.filehandler = nil
47
48 -- HTTP-Message table
49 self.message = {
50 env = env,
51 headers = {},
52 params = protocol.urldecode_params(env.QUERY_STRING or ""),
53 }
54
55 self.parsed_input = false
56 end
57
58 function Request.formvalue(self, name, noparse)
59 if not noparse and not self.parsed_input then
60 self:_parse_input()
61 end
62
63 if name then
64 return self.message.params[name]
65 else
66 return self.message.params
67 end
68 end
69
70 function Request.formvaluetable(self, prefix)
71 local vals = {}
72 prefix = prefix and prefix .. "." or "."
73
74 if not self.parsed_input then
75 self:_parse_input()
76 end
77
78 local void = self.message.params[nil]
79 for k, v in pairs(self.message.params) do
80 if k:find(prefix, 1, true) == 1 then
81 vals[k:sub(#prefix + 1)] = tostring(v)
82 end
83 end
84
85 return vals
86 end
87
88 function Request.content(self)
89 if not self.parsed_input then
90 self:_parse_input()
91 end
92
93 return self.message.content, self.message.content_length
94 end
95
96 function Request.getcookie(self, name)
97 local c = string.gsub(";" .. (self:getenv("HTTP_COOKIE") or "") .. ";", "%s*;%s*", ";")
98 local p = ";" .. name .. "=(.-);"
99 local i, j, value = c:find(p)
100 return value and urldecode(value)
101 end
102
103 function Request.getenv(self, name)
104 if name then
105 return self.message.env[name]
106 else
107 return self.message.env
108 end
109 end
110
111 function Request.setfilehandler(self, callback)
112 self.filehandler = callback
113 end
114
115 function Request._parse_input(self)
116 protocol.parse_message_body(
117 self.input,
118 self.message,
119 self.filehandler
120 )
121 self.parsed_input = true
122 end
123
124 --- Close the HTTP-Connection.
125 function close()
126 if not context.eoh then
127 context.eoh = true
128 coroutine.yield(3)
129 end
130
131 if not context.closed then
132 context.closed = true
133 coroutine.yield(5)
134 end
135 end
136
137 --- Return the request content if the request was of unknown type.
138 -- @return HTTP request body
139 -- @return HTTP request body length
140 function content()
141 return context.request:content()
142 end
143
144 --- Get a certain HTTP input value or a table of all input values.
145 -- @param name Name of the GET or POST variable to fetch
146 -- @param noparse Don't parse POST data before getting the value
147 -- @return HTTP input value or table of all input value
148 function formvalue(name, noparse)
149 return context.request:formvalue(name, noparse)
150 end
151
152 --- Get a table of all HTTP input values with a certain prefix.
153 -- @param prefix Prefix
154 -- @return Table of all HTTP input values with given prefix
155 function formvaluetable(prefix)
156 return context.request:formvaluetable(prefix)
157 end
158
159 --- Get the value of a certain HTTP-Cookie.
160 -- @param name Cookie Name
161 -- @return String containing cookie data
162 function getcookie(name)
163 return context.request:getcookie(name)
164 end
165
166 --- Get the value of a certain HTTP environment variable
167 -- or the environment table itself.
168 -- @param name Environment variable
169 -- @return HTTP environment value or environment table
170 function getenv(name)
171 return context.request:getenv(name)
172 end
173
174 --- Set a handler function for incoming user file uploads.
175 -- @param callback Handler function
176 function setfilehandler(callback)
177 return context.request:setfilehandler(callback)
178 end
179
180 --- Send a HTTP-Header.
181 -- @param key Header key
182 -- @param value Header value
183 function header(key, value)
184 if not context.headers then
185 context.headers = {}
186 end
187 context.headers[key:lower()] = value
188 coroutine.yield(2, key, value)
189 end
190
191 --- Set the mime type of following content data.
192 -- @param mime Mimetype of following content
193 function prepare_content(mime)
194 if not context.headers or not context.headers["content-type"] then
195 if mime == "application/xhtml+xml" then
196 if not getenv("HTTP_ACCEPT") or
197 not getenv("HTTP_ACCEPT"):find("application/xhtml+xml", nil, true) then
198 mime = "text/html; charset=UTF-8"
199 end
200 header("Vary", "Accept")
201 end
202 header("Content-Type", mime)
203 end
204 end
205
206 --- Get the RAW HTTP input source
207 -- @return HTTP LTN12 source
208 function source()
209 return context.request.input
210 end
211
212 --- Set the HTTP status code and status message.
213 -- @param code Status code
214 -- @param message Status message
215 function status(code, message)
216 code = code or 200
217 message = message or "OK"
218 context.status = code
219 coroutine.yield(1, code, message)
220 end
221
222 --- Send a chunk of content data to the client.
223 -- This function is as a valid LTN12 sink.
224 -- If the content chunk is nil this function will automatically invoke close.
225 -- @param content Content chunk
226 -- @param src_err Error object from source (optional)
227 -- @see close
228 function write(content, src_err)
229 if not content then
230 if src_err then
231 error(src_err)
232 else
233 close()
234 end
235 return true
236 elseif #content == 0 then
237 return true
238 else
239 if not context.eoh then
240 if not context.status then
241 status()
242 end
243 if not context.headers or not context.headers["content-type"] then
244 header("Content-Type", "text/html; charset=utf-8")
245 end
246 if not context.headers["cache-control"] then
247 header("Cache-Control", "no-cache")
248 header("Expires", "0")
249 end
250
251
252 context.eoh = true
253 coroutine.yield(3)
254 end
255 coroutine.yield(4, content)
256 return true
257 end
258 end
259
260 --- Splice data from a filedescriptor to the client.
261 -- @param fp File descriptor
262 -- @param size Bytes to splice (optional)
263 function splice(fd, size)
264 coroutine.yield(6, fd, size)
265 end
266
267 --- Redirects the client to a new URL and closes the connection.
268 -- @param url Target URL
269 function redirect(url)
270 status(302, "Found")
271 header("Location", url)
272 close()
273 end
274
275 --- Create a querystring out of a table of key - value pairs.
276 -- @param table Query string source table
277 -- @return Encoded HTTP query string
278 function build_querystring(q)
279 local s = { "?" }
280
281 for k, v in pairs(q) do
282 if #s > 1 then s[#s+1] = "&" end
283
284 s[#s+1] = urldecode(k)
285 s[#s+1] = "="
286 s[#s+1] = urldecode(v)
287 end
288
289 return table.concat(s, "")
290 end
291
292 --- Return the URL-decoded equivalent of a string.
293 -- @param str URL-encoded string
294 -- @param no_plus Don't decode + to " "
295 -- @return URL-decoded string
296 -- @see urlencode
297 urldecode = protocol.urldecode
298
299 --- Return the URL-encoded equivalent of a string.
300 -- @param str Source string
301 -- @return URL-encoded string
302 -- @see urldecode
303 urlencode = protocol.urlencode
304
305 --- Send the given data as JSON encoded string.
306 -- @param data Data to send
307 function write_json(x)
308 if x == nil then
309 write("null")
310 elseif type(x) == "table" then
311 local k, v
312 if type(next(x)) == "number" then
313 write("[ ")
314 for k, v in ipairs(x) do
315 write_json(v)
316 if next(x, k) then
317 write(", ")
318 end
319 end
320 write(" ]")
321 else
322 write("{ ")
323 for k, v in pairs(x) do
324 write("%q: " % k)
325 write_json(v)
326 if next(x, k) then
327 write(", ")
328 end
329 end
330 write(" }")
331 end
332 elseif type(x) == "number" or type(x) == "boolean" then
333 if (x ~= x) then
334 -- NaN is the only value that doesn't equal to itself.
335 write("Number.NaN")
336 else
337 write(tostring(x))
338 end
339 else
340 write('"%s"' % tostring(x):gsub('["%z\1-\31]', function(c)
341 return '\\u%04x' % c:byte(1)
342 end))
343 end
344 end