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