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