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