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