General optimizations, simplifications and improvements
[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 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
205 --- Get the RAW HTTP input source
206 -- @return HTTP LTN12 source
207 function source()
208 return context.request.input
209 end
210
211 --- Set the HTTP status code and status message.
212 -- @param code Status code
213 -- @param message Status message
214 function status(code, message)
215 code = code or 200
216 message = message or "OK"
217 context.status = code
218 coroutine.yield(1, code, message)
219 end
220
221 --- Send a chunk of content data to the client.
222 -- This function is as a valid LTN12 sink.
223 -- If the content chunk is nil this function will automatically invoke close.
224 -- @param content Content chunk
225 -- @param src_err Error object from source (optional)
226 -- @see close
227 function write(content, src_err)
228 if not content then
229 if src_err then
230 error(src_err)
231 else
232 close()
233 end
234 return true
235 elseif #content == 0 then
236 return true
237 else
238 if not context.eoh then
239 if not context.status then
240 status()
241 end
242 if not context.headers or not context.headers["content-type"] then
243 header("Content-Type", "text/html; charset=utf-8")
244 end
245 if not context.headers["cache-control"] then
246 header("Cache-Control", "no-cache")
247 header("Expires", "0")
248 end
249
250
251 context.eoh = true
252 coroutine.yield(3)
253 end
254 coroutine.yield(4, content)
255 return true
256 end
257 end
258
259 --- Redirects the client to a new URL and closes the connection.
260 -- @param url Target URL
261 function redirect(url)
262 status(302, "Found")
263 header("Location", url)
264 close()
265 end
266
267 --- Create a querystring out of a table of key - value pairs.
268 -- @param table Query string source table
269 -- @return Encoded HTTP query string
270 function build_querystring(table)
271 local s="?"
272
273 for k, v in pairs(table) do
274 s = s .. urlencode(k) .. "=" .. urlencode(v) .. "&"
275 end
276
277 return s
278 end
279
280 --- Return the URL-decoded equivalent of a string.
281 -- @param str URL-encoded string
282 -- @param no_plus Don't decode + to " "
283 -- @return URL-decoded string
284 -- @see urlencode
285 urldecode = protocol.urldecode
286
287 --- Return the URL-encoded equivalent of a string.
288 -- @param str Source string
289 -- @return URL-encoded string
290 -- @see urldecode
291 urlencode = protocol.urlencode