* Generalized HTTP-API
[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 ToDo:
11 - Cookie handling
12
13 License:
14 Copyright 2008 Steven Barth <steven@midlink.org>
15
16 Licensed under the Apache License, Version 2.0 (the "License");
17 you may not use this file except in compliance with the License.
18 You may obtain a copy of the License at
19
20 http://www.apache.org/licenses/LICENSE-2.0
21
22 Unless required by applicable law or agreed to in writing, software
23 distributed under the License is distributed on an "AS IS" BASIS,
24 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
25 See the License for the specific language governing permissions and
26 limitations under the License.
27
28 ]]--
29
30 module("luci.http", package.seeall)
31 require("luci.http.protocol")
32 require("luci.util")
33
34 context = luci.util.threadlocal()
35
36
37 Request = luci.util.class()
38 function Request.__init__(self, env, instream, errstream)
39 self.input = instream
40 self.error = errstream
41
42 -- Formdata tables
43 self.get = {}
44 self.post = {}
45
46 -- File handler
47 self.filehandler = function() end
48
49 -- Environment table
50 self.env = env
51
52 setmetatable(self.get, {__index =
53 function(tbl, key)
54 tbl = luci.http.protocol.urldecode_params(self.env.QUERY_STRING)
55 setmetatable(tbl, nil)
56 return rawget(tbl, key)
57 end })
58
59 setmetatable(self.post, {__index =
60 function(tbl, key)
61 tbl = luci.http.protocol.
62 setmetatable(tbl, nil)
63 return rawget(tbl, key)
64 end })
65 end
66
67 function Request.formvalue(self, name, default)
68 return tostring(self.post[name] or self.get[name] or default)
69 end
70
71 function Request.formvaluetable(self, prefix)
72 local vals = {}
73 prefix = prefix and prefix .. "." or "."
74
75 for k, v in pairs(self.getvalue()) do
76 if k:find(prefix, 1, true) == 1 then
77 vals[k:sub(#prefix + 1)] = tostring(v)
78 end
79 end
80
81 for k, v in pairs(self.postvalue()) do
82 if k:find(prefix, 1, true) == 1 then
83 vals[k:sub(#prefix + 1)] = tostring(v)
84 end
85 end
86
87 return vals
88 end
89
90 function Request.getenv(self, name)
91 return name and self.env[name] or self.env
92 end
93
94 function Request.getvalue(self, name)
95 local void = self.get[nil]
96 return name and self.get[name] or self.get
97 end
98
99 function Request.postvalue(self, name)
100 local void = self.post[nil]
101 return name and self.post[name] or self.post
102 end
103
104 function Request.setfilehandler(self, callback)
105 self.filehandler = callback
106 end
107
108
109 function close()
110 if not context.eoh then
111 context.eoh = true
112 coroutine.yield(3)
113 end
114
115 if not context.closed then
116 context.closed = true
117 coroutine.yield(5)
118 end
119 end
120
121 function formvalue(...)
122 return context.request:formvalue(...)
123 end
124
125 function formvaluetable(...)
126 return context.request:formvaluetable(...)
127 end
128
129 function getvalue(...)
130 return context.request:getvalue(...)
131 end
132
133 function postvalue(...)
134 return context.request:postvalue(...)
135 end
136
137 function getenv(...)
138 return context.request:getenv(...)
139 end
140
141 function setfilehandler(...)
142 return context.request:setfilehandler(...)
143 end
144
145 function header(key, value)
146 if not context.status then
147 status()
148 end
149 if not context.headers then
150 context.headers = {}
151 end
152 context.headers[key:lower()] = value
153 coroutine.yield(2, key, value)
154 end
155
156 function prepare_content(mime)
157 header("Content-Type", mime)
158 end
159
160 function status(code, message)
161 code = code or 200
162 message = message or "OK"
163 context.status = code
164 coroutine.yield(1, code, message)
165 end
166
167 function write(content)
168 if not content or #content == 0 then
169 return
170 end
171 if not context.eoh then
172 if not context.status then
173 status()
174 end
175 if not context.headers or not context.headers["content-type"] then
176 header("Content-Type", "text/html; charset=utf-8")
177 end
178
179 context.eoh = true
180 coroutine.yield(3)
181 end
182 coroutine.yield(4, content)
183 end
184
185
186 function basic_auth(realm, errorpage)
187 header("Status", "401 Unauthorized")
188 header("WWW-Authenticate", string.format('Basic realm="%s"', realm or ""))
189
190 if errorpage then
191 errorpage()
192 end
193
194 close()
195 end
196
197 function redirect(url)
198 header("Status", "302 Found")
199 header("Location", url)
200 close()
201 end
202
203 function build_querystring(table)
204 local s="?"
205
206 for k, v in pairs(table) do
207 s = s .. urlencode(k) .. "=" .. urlencode(v) .. "&"
208 end
209
210 return s
211 end
212
213 urldecode = luci.http.protocol.urldecode
214 urlencode = luci.http.protocol.urlencode
215 --[[
216 function urldecode(str)
217 str = str:gsub("+", " ")
218 str = str:gsub("%%(%x%x)",
219 function(h) return string.char(tonumber(h,16)) end)
220 str = str:gsub("\r\n", "\n")
221 return str
222 end
223
224 function urlencode(str)
225 str = str:gsub("\n", "\r\n")
226 str = str:gsub("([^%w ])",
227 function (c) return string.format ("%%%02X", string.byte(c)) end)
228 str = str:gsub(" ", "+")
229 return str
230 end
231 ]]--