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