* Introducing LuCI HTTPD as testing environment
[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("ltn12")
32 require("luci.http.protocol")
33 require("luci.util")
34
35 context = luci.util.threadlocal()
36
37
38 Request = luci.util.class()
39 function Request.__init__(self, env, sourcein, sinkerr)
40         self.input = sourcein
41         self.error = sinkerr
42
43
44         -- File handler
45         self.filehandler = function() end
46         
47         -- HTTP-Message table
48         self.message = {
49                 env = env,
50                 headers = {},
51                 params = luci.http.protocol.urldecode_params(env.QUERY_STRING or ""),
52         }
53         
54         setmetatable(self.message.params, {__index =
55                 function(tbl, key)
56                         setmetatable(tbl, nil)
57
58                         luci.http.protocol.parse_message_body(
59                          self.input,
60                          self.message,
61                          self.filehandler
62                         )
63
64                         return rawget(tbl, key)
65                 end
66         })
67 end
68
69 function Request.formvalue(self, name, default)
70         if name then
71                 return self.message.params[name] and tostring(self.message.params[name]) or default
72         else
73                 return self.message.params
74         end
75 end
76
77 function Request.formvaluetable(self, prefix)
78         local vals = {}
79         prefix = prefix and prefix .. "." or "."
80         
81         local void = self.message.params[nil]
82         for k, v in pairs(self.message.params) do
83                 if k:find(prefix, 1, true) == 1 then
84                         vals[k:sub(#prefix + 1)] = tostring(v)
85                 end
86         end
87         
88         return vals
89 end
90
91 function Request.getenv(self, name)
92         return name and self.message.env[name] or self.message.env
93 end
94
95 function Request.setfilehandler(self, callback)
96         self.filehandler = callback
97 end
98
99
100 function close()
101         if not context.eoh then
102                 context.eoh = true
103                 coroutine.yield(3)
104         end
105         
106         if not context.closed then
107                 context.closed = true
108                 coroutine.yield(5)
109         end
110 end
111
112 function formvalue(...)
113         return context.request:formvalue(...)
114 end
115
116 function formvaluetable(...)
117         return context.request:formvaluetable(...)
118 end
119
120 function getvalue(...)
121         return context.request:getvalue(...)
122 end
123
124 function postvalue(...)
125         return context.request:postvalue(...)
126 end
127
128 function getenv(...)
129         return context.request:getenv(...)
130 end
131
132 function setfilehandler(...)
133         return context.request:setfilehandler(...)
134 end
135
136 function header(key, value)
137         if not context.status then
138                 status()
139         end
140         if not context.headers then
141                 context.headers = {}
142         end
143         context.headers[key:lower()] = value
144         coroutine.yield(2, key, value)
145 end
146
147 function prepare_content(mime)
148         header("Content-Type", mime)
149 end
150
151 function status(code, message)
152         code = code or 200
153         message = message or "OK"
154         context.status = code
155         coroutine.yield(1, code, message)
156 end
157
158 function write(content)
159         if not content or #content == 0 then
160                 return
161         end
162         if not context.eoh then
163                 if not context.status then
164                         status()
165                 end
166                 if not context.headers or not context.headers["content-type"] then
167                         header("Content-Type", "text/html; charset=utf-8")
168                 end
169                 
170                 context.eoh = true
171                 coroutine.yield(3)
172         end
173         coroutine.yield(4, content)
174 end
175
176
177 function basic_auth(realm, errorpage)
178         header("Status", "401 Unauthorized")
179         header("WWW-Authenticate", string.format('Basic realm="%s"', realm or ""))
180         
181         if errorpage then
182                 errorpage()
183         end
184         
185         close()
186 end
187
188 function redirect(url)
189         header("Status", "302 Found")
190         header("Location", url)
191         close()
192 end
193
194 function build_querystring(table)
195         local s="?"
196         
197         for k, v in pairs(table) do
198                 s = s .. urlencode(k) .. "=" .. urlencode(v) .. "&"
199         end
200         
201         return s
202 end
203
204 urldecode = luci.http.protocol.urldecode
205 urlencode = luci.http.protocol.urlencode