* 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 ]]--