libs/json: Completed JSON library
[project/luci.git] / libs / web / luasrc / http.lua
1 --[[
2 LuCI - HTTP-Interaction
3
4 Description:
5 HTTP-Header manipulator and form variable preprocessor
6
7 FileId:
8 $Id$
9
10 License:
11 Copyright 2008 Steven Barth <steven@midlink.org>
12
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 
16
17         http://www.apache.org/licenses/LICENSE-2.0 
18
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.
24
25 ]]--
26
27 --- LuCI Web Framework high-level HTTP functions.
28 module("luci.http", package.seeall)
29 local ltn12 = require("luci.ltn12")
30 require("luci.http.protocol")
31 require("luci.util")
32
33 context = luci.util.threadlocal()
34
35 Request = luci.util.class()
36 function Request.__init__(self, env, sourcein, sinkerr)
37         self.input = sourcein
38         self.error = sinkerr
39
40
41         -- File handler
42         self.filehandler = function() end
43         
44         -- HTTP-Message table
45         self.message = {
46                 env = env,
47                 headers = {},
48                 params = luci.http.protocol.urldecode_params(env.QUERY_STRING or ""),
49         }
50         
51         self.parsed_input = false
52 end
53
54 function Request.formvalue(self, name, noparse)
55         if not noparse and not self.parsed_input then
56                 self:_parse_input()
57         end
58         
59         if name then
60                 return self.message.params[name]
61         else
62                 return self.message.params
63         end
64 end
65
66 function Request.formvaluetable(self, prefix)
67         local vals = {}
68         prefix = prefix and prefix .. "." or "."
69         
70         if not self.parsed_input then
71                 self:_parse_input()
72         end
73         
74         local void = self.message.params[nil]
75         for k, v in pairs(self.message.params) do
76                 if k:find(prefix, 1, true) == 1 then
77                         vals[k:sub(#prefix + 1)] = tostring(v)
78                 end
79         end
80         
81         return vals
82 end
83
84 function Request.content(self)
85         if not self.parsed_input then
86                 self:_parse_input()
87         end
88         
89         return self.message.content, self.message.content_length
90 end
91
92 function Request.getcookie(self, name)
93   local c = string.gsub(";" .. (self:getenv("HTTP_COOKIE") or "") .. ";", "%s*;%s*", ";")
94   local p = ";" .. name .. "=(.-);"
95   local i, j, value = c:find(p)
96   return value and urldecode(value)
97 end
98
99 function Request.getenv(self, name)
100         if name then
101                 return self.message.env[name]
102         else
103                 return self.message.env
104         end
105 end
106
107 function Request.setfilehandler(self, callback)
108         self.filehandler = callback
109 end
110
111 function Request._parse_input(self)
112         luci.http.protocol.parse_message_body(
113                  self.input,
114                  self.message,
115                  self.filehandler
116         )
117         self.parsed_input = true
118 end
119
120 --- Close the HTTP-Connection.
121 function close()
122         if not context.eoh then
123                 context.eoh = true
124                 coroutine.yield(3)
125         end
126         
127         if not context.closed then
128                 context.closed = true
129                 coroutine.yield(5)
130         end
131 end
132
133 --- Return the request content if the request was of unknown type.
134 -- @return      HTTP request body
135 -- @return      HTTP request body length
136 function content()
137         return context.request:content()
138 end
139
140 --- Get a certain HTTP input value or a table of all input values.
141 -- @param name          Name of the GET or POST variable to fetch
142 -- @param noparse       Don't parse POST data before getting the value
143 -- @return                      HTTP input value or table of all input value
144 function formvalue(name, noparse)
145         return context.request:formvalue(name, noparse)
146 end
147
148 --- Get a table of all HTTP input values with a certain prefix.
149 -- @param prefix        Prefix
150 -- @return                      Table of all HTTP input values with given prefix
151 function formvaluetable(prefix)
152         return context.request:formvaluetable(prefix)
153 end
154
155 --- Get the value of a certain HTTP-Cookie.
156 -- @param name          Cookie Name
157 -- @return                      String containing cookie data
158 function getcookie(name)
159         return context.request:getcookie(name)
160 end
161
162 --- Get the value of a certain HTTP environment variable 
163 -- or the environment table itself.
164 -- @param name          Environment variable
165 -- @return                      HTTP environment value or environment table
166 function getenv(name)
167         return context.request:getenv(name)
168 end
169
170 --- Set a handler function for incoming user file uploads.
171 -- @param callback      Handler function
172 function setfilehandler(callback)
173         return context.request:setfilehandler(callback)
174 end
175
176 --- Send a HTTP-Header.
177 -- @param key   Header key
178 -- @param value Header value
179 function header(key, value)
180         if not context.headers then
181                 context.headers = {}
182         end
183         context.headers[key:lower()] = value
184         coroutine.yield(2, key, value)
185 end
186
187 --- Set the mime type of following content data.
188 -- @param mime  Mimetype of following content
189 function prepare_content(mime)
190         if mime == "application/xhtml+xml" then
191                 if not getenv("HTTP_ACCEPT") or
192                   not getenv("HTTP_ACCEPT"):find("application/xhtml+xml", nil, true) then
193                         mime = "text/html; charset=UTF-8"
194                 end
195                 header("Vary", "Accept")
196         end
197         header("Content-Type", mime)
198 end
199
200 --- Get the RAW HTTP input source
201 -- @return      HTTP LTN12 source
202 function source()
203         return context.request.input
204 end
205
206 --- Set the HTTP status code and status message.
207 -- @param code          Status code
208 -- @param message       Status message
209 function status(code, message)
210         code = code or 200
211         message = message or "OK"
212         context.status = code
213         coroutine.yield(1, code, message)
214 end
215
216 --- Send a chunk of content data to the client.
217 -- This function is as a valid LTN12 sink.
218 -- If the content chunk is nil this function will automatically invoke close.
219 -- @param content       Content chunk
220 -- @param src_err       Error object from source (optional)
221 -- @see close
222 function write(content, src_err)
223         if not content then
224                 if src_err then
225                         error(src_err)
226                 else
227                         close()
228                 end
229                 return true
230         elseif #content == 0 then
231                 return true
232         else
233                 if not context.eoh then
234                         if not context.status then
235                                 status()
236                         end
237                         if not context.headers or not context.headers["content-type"] then
238                                 header("Content-Type", "text/html; charset=utf-8")
239                         end
240                         if not context.headers["cache-control"] then
241                                 header("Cache-Control", "no-cache")
242                                 header("Expires", "0")
243                         end
244                         
245                         
246                         context.eoh = true
247                         coroutine.yield(3)
248                 end
249                 coroutine.yield(4, content)
250                 return true
251         end
252 end
253
254 --- Redirects the client to a new URL and closes the connection.
255 -- @param url   Target URL
256 function redirect(url)
257         status(302, "Found")
258         header("Location", url)
259         close()
260 end
261
262 --- Create a querystring out of a table of key - value pairs.
263 -- @param table         Query string source table
264 -- @return                      Encoded HTTP query string
265 function build_querystring(table)
266         local s="?"
267         
268         for k, v in pairs(table) do
269                 s = s .. urlencode(k) .. "=" .. urlencode(v) .. "&"
270         end
271         
272         return s
273 end
274
275 --- Return the URL-decoded equivalent of a string.
276 -- @param str           URL-encoded string
277 -- @param no_plus       Don't decode + to " "
278 -- @return                      URL-decoded string
279 -- @see urlencode
280 urldecode = luci.http.protocol.urldecode
281
282 --- Return the URL-encoded equivalent of a string.
283 -- @param str           Source string
284 -- @return                      URL-encoded string
285 -- @see urldecode
286 urlencode = luci.http.protocol.urlencode