2 LuCI - HTTP-Interaction
5 HTTP-Header manipulator and form variable preprocessor
11 Copyright 2008 Steven Barth <steven@midlink.org>
13 Licensed under the Apache License, Version 2.0 (the "License");
14 you may not use this file except in compliance with the License.
15 You may obtain a copy of the License at
17 http://www.apache.org/licenses/LICENSE-2.0
19 Unless required by applicable law or agreed to in writing, software
20 distributed under the License is distributed on an "AS IS" BASIS,
21 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 See the License for the specific language governing permissions and
23 limitations under the License.
27 local ltn12 = require "luci.ltn12"
28 local protocol = require "luci.http.protocol"
29 local util = require "luci.util"
30 local string = require "string"
31 local coroutine = require "coroutine"
32 local table = require "table"
34 local ipairs, pairs, next, type, tostring, error =
35 ipairs, pairs, next, type, tostring, error
37 --- LuCI Web Framework high-level HTTP functions.
40 context = util.threadlocal()
42 Request = util.class()
43 function Request.__init__(self, env, sourcein, sinkerr)
49 self.filehandler = function() end
55 params = protocol.urldecode_params(env.QUERY_STRING or ""),
58 self.parsed_input = false
61 function Request.formvalue(self, name, noparse)
62 if not noparse and not self.parsed_input then
67 return self.message.params[name]
69 return self.message.params
73 function Request.formvaluetable(self, prefix)
75 prefix = prefix and prefix .. "." or "."
77 if not self.parsed_input then
81 local void = self.message.params[nil]
82 for k, v in pairs(self.message.params) do
83 if k:find(prefix, 1, true) == 1 then
84 vals[k:sub(#prefix + 1)] = tostring(v)
91 function Request.content(self)
92 if not self.parsed_input then
96 return self.message.content, self.message.content_length
99 function Request.getcookie(self, name)
100 local c = string.gsub(";" .. (self:getenv("HTTP_COOKIE") or "") .. ";", "%s*;%s*", ";")
101 local p = ";" .. name .. "=(.-);"
102 local i, j, value = c:find(p)
103 return value and urldecode(value)
106 function Request.getenv(self, name)
108 return self.message.env[name]
110 return self.message.env
114 function Request.setfilehandler(self, callback)
115 self.filehandler = callback
118 function Request._parse_input(self)
119 protocol.parse_message_body(
124 self.parsed_input = true
127 --- Close the HTTP-Connection.
129 if not context.eoh then
134 if not context.closed then
135 context.closed = true
140 --- Return the request content if the request was of unknown type.
141 -- @return HTTP request body
142 -- @return HTTP request body length
144 return context.request:content()
147 --- Get a certain HTTP input value or a table of all input values.
148 -- @param name Name of the GET or POST variable to fetch
149 -- @param noparse Don't parse POST data before getting the value
150 -- @return HTTP input value or table of all input value
151 function formvalue(name, noparse)
152 return context.request:formvalue(name, noparse)
155 --- Get a table of all HTTP input values with a certain prefix.
156 -- @param prefix Prefix
157 -- @return Table of all HTTP input values with given prefix
158 function formvaluetable(prefix)
159 return context.request:formvaluetable(prefix)
162 --- Get the value of a certain HTTP-Cookie.
163 -- @param name Cookie Name
164 -- @return String containing cookie data
165 function getcookie(name)
166 return context.request:getcookie(name)
169 --- Get the value of a certain HTTP environment variable
170 -- or the environment table itself.
171 -- @param name Environment variable
172 -- @return HTTP environment value or environment table
173 function getenv(name)
174 return context.request:getenv(name)
177 --- Set a handler function for incoming user file uploads.
178 -- @param callback Handler function
179 function setfilehandler(callback)
180 return context.request:setfilehandler(callback)
183 --- Send a HTTP-Header.
184 -- @param key Header key
185 -- @param value Header value
186 function header(key, value)
187 if not context.headers then
190 context.headers[key:lower()] = value
191 coroutine.yield(2, key, value)
194 --- Set the mime type of following content data.
195 -- @param mime Mimetype of following content
196 function prepare_content(mime)
197 if not context.headers or not context.headers["content-type"] then
198 if mime == "application/xhtml+xml" then
199 if not getenv("HTTP_ACCEPT") or
200 not getenv("HTTP_ACCEPT"):find("application/xhtml+xml", nil, true) then
201 mime = "text/html; charset=UTF-8"
203 header("Vary", "Accept")
205 header("Content-Type", mime)
209 --- Get the RAW HTTP input source
210 -- @return HTTP LTN12 source
212 return context.request.input
215 --- Set the HTTP status code and status message.
216 -- @param code Status code
217 -- @param message Status message
218 function status(code, message)
220 message = message or "OK"
221 context.status = code
222 coroutine.yield(1, code, message)
225 --- Send a chunk of content data to the client.
226 -- This function is as a valid LTN12 sink.
227 -- If the content chunk is nil this function will automatically invoke close.
228 -- @param content Content chunk
229 -- @param src_err Error object from source (optional)
231 function write(content, src_err)
239 elseif #content == 0 then
242 if not context.eoh then
243 if not context.status then
246 if not context.headers or not context.headers["content-type"] then
247 header("Content-Type", "text/html; charset=utf-8")
249 if not context.headers["cache-control"] then
250 header("Cache-Control", "no-cache")
251 header("Expires", "0")
258 coroutine.yield(4, content)
263 --- Splice data from a filedescriptor to the client.
264 -- @param fp File descriptor
265 -- @param size Bytes to splice (optional)
266 function splice(fd, size)
267 coroutine.yield(6, fd, size)
270 --- Redirects the client to a new URL and closes the connection.
271 -- @param url Target URL
272 function redirect(url)
274 header("Location", url)
278 --- Create a querystring out of a table of key - value pairs.
279 -- @param table Query string source table
280 -- @return Encoded HTTP query string
281 function build_querystring(q)
284 for k, v in pairs(q) do
285 if #s > 1 then s[#s+1] = "&" end
287 s[#s+1] = urldecode(k)
289 s[#s+1] = urldecode(v)
292 return table.concat(s, "")
295 --- Return the URL-decoded equivalent of a string.
296 -- @param str URL-encoded string
297 -- @param no_plus Don't decode + to " "
298 -- @return URL-decoded string
300 urldecode = protocol.urldecode
302 --- Return the URL-encoded equivalent of a string.
303 -- @param str Source string
304 -- @return URL-encoded string
306 urlencode = protocol.urlencode
308 --- Send the given data as JSON encoded string.
309 -- @param data Data to send
310 function write_json(x)
313 elseif type(x) == "table" then
315 if type(next(x)) == "number" then
317 for k, v in ipairs(x) do
326 for k, v in pairs(x) do
335 elseif type(x) == "number" or type(x) == "boolean" then
337 elseif type(x) == "string" then
338 write("%q" % tostring(x))