* Rewrote Luci to be coroutine-safe allowing the use of non-forking webservers
[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.util")
32 context = luci.util.threadlocal()
33
34
35 Request = luci.util.class()
36 function Request.__init__(self)
37         self.headers = {}
38         self.request = {}
39         self.uploads = {}
40         self.env = {}
41         self.data = ""
42 end
43
44 function Request.formvalue(self, name, default)
45         return self.request[name] or default
46 end
47
48 function Request.formvalues(self)
49         return self.request
50 end
51
52 function Request.formvaluetable(self, prefix)
53         local vals = {}
54         prefix = prefix and prefix .. "." or "."
55         
56         for k, v in pairs(self.request) do
57                 if k:find(prefix, 1, true) == 1 then
58                         vals[k:sub(#prefix + 1)] = v
59                 end
60         end
61         
62         return vals
63 end
64
65 function Request.getenv(self, name)
66         return self.env[name]
67 end
68
69 function Request.upload(self, name)
70         return self.uploads[name]
71 end
72
73
74 function close()
75         if not context.eoh then
76                 context.eoh = true
77                 coroutine.yield(3)
78         end
79         
80         if not context.closed then
81                 context.closed = true
82                 coroutine.yield(5)
83         end
84 end
85
86 function formvalue(...)
87         return context.request:formvalue(...)
88 end
89
90 function formvalues(...)
91         return context.request:formvalues(...)
92 end
93
94 function formvaluetable(...)
95         return context.request:formvaluetable(...)
96 end
97
98 function getenv(...)
99         return context.request:getenv(...)
100 end
101
102 function header(key, value)
103         if not context.status then
104                 status()
105         end
106         if not context.headers then
107                 context.headers = {}
108         end
109         context.headers[key:lower()] = value
110         coroutine.yield(2, key, value)
111 end
112
113 function prepare_content(mime)
114         header("Content-Type", mime)
115 end
116
117 function status(code, message)
118         code = code or 200
119         message = message or "OK"
120         context.status = code
121         coroutine.yield(1, code, message)
122 end
123
124 function write(content)
125         if not content or #content == 0 then
126                 return
127         end
128         if not context.eoh then
129                 if not context.status then
130                         status()
131                 end
132                 if not context.headers or not context.headers["content-type"] then
133                         header("Content-Type", "text/html; charset=utf-8")
134                 end
135                 
136                 context.eoh = true
137                 coroutine.yield(3)
138         end
139         coroutine.yield(4, content)
140 end
141
142
143 function basic_auth(realm, errorpage)
144         header("Status", "401 Unauthorized")
145         header("WWW-Authenticate", string.format('Basic realm="%s"', realm or ""))
146         
147         if errorpage then
148                 errorpage()
149         end
150         
151         close()
152 end
153
154 function redirect(url)
155         header("Status", "302 Found")
156         header("Location", url)
157         close()
158 end
159
160 function upload(...)
161         return context.request:upload(...)
162 end
163
164
165
166 function build_querystring(table)
167         local s="?"
168         
169         for k, v in pairs(table) do
170                 s = s .. k .. "=" .. v .. "&"
171         end
172         
173         return s
174 end
175
176 function urldecode(str)
177         str = str:gsub("+", " ")
178         str = str:gsub("%%(%x%x)",
179                 function(h) return string.char(tonumber(h,16)) end)
180         str = str:gsub("\r\n", "\n")
181         return str      
182 end
183
184 function urlencode(str)
185         str = str:gsub("\n", "\r\n")
186         str = str:gsub("([^%w ])",
187                 function (c) return string.format ("%%%02X", string.byte(c)) end)
188         str = str:gsub(" ", "+")
189         return str      
190 end