libs/web: Corrected HTTP-Headers
[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 --- LuCI Web Framework high-level HTTP functions.
28 module("luci.http", package.seeall)
29 local ltn12 = require("luci.ltn12")
30 require("luci.http.protocol")
31 require("luci.util")
32
33 context = luci.util.threadlocal()
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, noparse)
55         if not noparse and 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 --- Close the HTTP-Connection.
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 --- Get a certain HTTP input value or a table of all input values.
126 -- @param name          Name of the GET or POST variable to fetch
127 -- @param noparse       Don't parse POST data before getting the value
128 -- @return                      HTTP input value or table of all input value
129 function formvalue(name, noparse)
130         return context.request:formvalue(name, noparse)
131 end
132
133 --- Get a table of all HTTP input values with a certain prefix.
134 -- @param prefix        Prefix
135 -- @return                      Table of all HTTP input values with given prefix
136 function formvaluetable(prefix)
137         return context.request:formvaluetable(prefix)
138 end
139
140 --- Get the value of a certain HTTP-Cookie.
141 -- @param name          Cookie Name
142 -- @return                      String containing cookie data
143 function getcookie(name)
144         return context.request:getcookie(name)
145 end
146
147 --- Get the value of a certain HTTP environment variable 
148 -- or the environment table itself.
149 -- @param name          Environment variable
150 -- @return                      HTTP environment value or environment table
151 function getenv(name)
152         return context.request:getenv(name)
153 end
154
155 --- Set a handler function for incoming user file uploads.
156 -- @param callback      Handler function
157 function setfilehandler(callback)
158         return context.request:setfilehandler(callback)
159 end
160
161 --- Send a HTTP-Header.
162 -- @param key   Header key
163 -- @param value Header value
164 function header(key, value)
165         if not context.headers then
166                 context.headers = {}
167         end
168         context.headers[key:lower()] = value
169         coroutine.yield(2, key, value)
170 end
171
172 --- Set the mime type of following content data.
173 -- @param mime  Mimetype of following content
174 function prepare_content(mime)
175         if mime == "application/xhtml+xml" then
176                 if not getenv("HTTP_ACCEPT") or
177                   not getenv("HTTP_ACCEPT"):find("application/xhtml+xml", nil, true) then
178                         mime = "text/html; charset=UTF-8"
179                 end
180                 header("Vary", "Accept")
181         end
182         header("Content-Type", mime)
183 end
184
185 --- Set the HTTP status code and status message.
186 -- @param code          Status code
187 -- @param message       Status message
188 function status(code, message)
189         code = code or 200
190         message = message or "OK"
191         context.status = code
192         coroutine.yield(1, code, message)
193 end
194
195 --- Send a chunk of content data to the client.
196 -- This function is as a valid LTN12 sink.
197 -- If the content chunk is nil this function will automatically invoke close.
198 -- @param content       Content chunk
199 -- @param src_err       Error object from source (optional)
200 -- @see close
201 function write(content, src_err)
202         if not content then
203                 if src_err then
204                         error(src_err)
205                 else
206                         close()
207                 end
208                 return true
209         elseif #content == 0 then
210                 return true
211         else
212                 if not context.eoh then
213                         if not context.status then
214                                 status()
215                         end
216                         if not context.headers or not context.headers["content-type"] then
217                                 header("Content-Type", "text/html; charset=utf-8")
218                         end
219                         if not context.headers["cache-control"] then
220                                 header("Cache-Control", "no-cache")
221                                 header("Expires", "0")
222                         end
223                         
224                         
225                         context.eoh = true
226                         coroutine.yield(3)
227                 end
228                 coroutine.yield(4, content)
229                 return true
230         end
231 end
232
233 --- Redirects the client to a new URL and closes the connection.
234 -- @param url   Target URL
235 function redirect(url)
236         status(302, "Found")
237         header("Location", url)
238         close()
239 end
240
241 --- Create a querystring out of a table of key - value pairs.
242 -- @param table         Query string source table
243 -- @return                      Encoded HTTP query string
244 function build_querystring(table)
245         local s="?"
246         
247         for k, v in pairs(table) do
248                 s = s .. urlencode(k) .. "=" .. urlencode(v) .. "&"
249         end
250         
251         return s
252 end
253
254 --- Return the URL-decoded equivalent of a string.
255 -- @param str           URL-encoded string
256 -- @param no_plus       Don't decode + to " "
257 -- @return                      URL-decoded string
258 -- @see urlencode
259 urldecode = luci.http.protocol.urldecode
260
261 --- Return the URL-encoded equivalent of a string.
262 -- @param str           Source string
263 -- @return                      URL-encoded string
264 -- @see urldecode
265 urlencode = luci.http.protocol.urlencode