e8430b727650025f0343a8330a36562c829ab4e5
[project/luci.git] / modules / 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