root/luci/trunk/libs/web/luasrc/http.lua @ 5140

Revision 5140, 7.1 KB (checked in by Cyrus, 4 years ago)

Add: luci.http.splice to allow direct copying of data from a file
descriptor

  • Property svn:keywords set to Id
Line 
1--[[
2LuCI - HTTP-Interaction
3
4Description:
5HTTP-Header manipulator and form variable preprocessor
6
7FileId:
8$Id$
9
10License:
11Copyright 2008 Steven Barth <steven@midlink.org>
12
13Licensed under the Apache License, Version 2.0 (the "License");
14you may not use this file except in compliance with the License.
15You may obtain a copy of the License at
16
17    http://www.apache.org/licenses/LICENSE-2.0
18
19Unless required by applicable law or agreed to in writing, software
20distributed under the License is distributed on an "AS IS" BASIS,
21WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22See the License for the specific language governing permissions and
23limitations under the License.
24
25]]--
26
27local ltn12 = require "luci.ltn12"
28local protocol = require "luci.http.protocol"
29local util  = require "luci.util"
30local string = require "string"
31local coroutine = require "coroutine"
32
33local pairs, tostring, error = pairs, tostring, error
34
35--- LuCI Web Framework high-level HTTP functions.
36module "luci.http"
37
38context = util.threadlocal()
39
40Request = util.class()
41function Request.__init__(self, env, sourcein, sinkerr)
42    self.input = sourcein
43    self.error = sinkerr
44
45
46    -- File handler
47    self.filehandler = function() end
48   
49    -- HTTP-Message table
50    self.message = {
51        env = env,
52        headers = {},
53        params = protocol.urldecode_params(env.QUERY_STRING or ""),
54    }
55   
56    self.parsed_input = false
57end
58
59function Request.formvalue(self, name, noparse)
60    if not noparse and not self.parsed_input then
61        self:_parse_input()
62    end
63   
64    if name then
65        return self.message.params[name]
66    else
67        return self.message.params
68    end
69end
70
71function Request.formvaluetable(self, prefix)
72    local vals = {}
73    prefix = prefix and prefix .. "." or "."
74   
75    if not self.parsed_input then
76        self:_parse_input()
77    end
78   
79    local void = self.message.params[nil]
80    for k, v in pairs(self.message.params) do
81        if k:find(prefix, 1, true) == 1 then
82            vals[k:sub(#prefix + 1)] = tostring(v)
83        end
84    end
85   
86    return vals
87end
88
89function Request.content(self)
90    if not self.parsed_input then
91        self:_parse_input()
92    end
93   
94    return self.message.content, self.message.content_length
95end
96
97function Request.getcookie(self, name)
98  local c = string.gsub(";" .. (self:getenv("HTTP_COOKIE") or "") .. ";", "%s*;%s*", ";")
99  local p = ";" .. name .. "=(.-);"
100  local i, j, value = c:find(p)
101  return value and urldecode(value)
102end
103
104function Request.getenv(self, name)
105    if name then
106        return self.message.env[name]
107    else
108        return self.message.env
109    end
110end
111
112function Request.setfilehandler(self, callback)
113    self.filehandler = callback
114end
115
116function Request._parse_input(self)
117    protocol.parse_message_body(
118         self.input,
119         self.message,
120         self.filehandler
121    )
122    self.parsed_input = true
123end
124
125--- Close the HTTP-Connection.
126function close()
127    if not context.eoh then
128        context.eoh = true
129        coroutine.yield(3)
130    end
131   
132    if not context.closed then
133        context.closed = true
134        coroutine.yield(5)
135    end
136end
137
138--- Return the request content if the request was of unknown type.
139-- @return  HTTP request body
140-- @return  HTTP request body length
141function content()
142    return context.request:content()
143end
144
145--- Get a certain HTTP input value or a table of all input values.
146-- @param name      Name of the GET or POST variable to fetch
147-- @param noparse   Don't parse POST data before getting the value
148-- @return          HTTP input value or table of all input value
149function formvalue(name, noparse)
150    return context.request:formvalue(name, noparse)
151end
152
153--- Get a table of all HTTP input values with a certain prefix.
154-- @param prefix    Prefix
155-- @return          Table of all HTTP input values with given prefix
156function formvaluetable(prefix)
157    return context.request:formvaluetable(prefix)
158end
159
160--- Get the value of a certain HTTP-Cookie.
161-- @param name      Cookie Name
162-- @return          String containing cookie data
163function getcookie(name)
164    return context.request:getcookie(name)
165end
166
167--- Get the value of a certain HTTP environment variable
168-- or the environment table itself.
169-- @param name      Environment variable
170-- @return          HTTP environment value or environment table
171function getenv(name)
172    return context.request:getenv(name)
173end
174
175--- Set a handler function for incoming user file uploads.
176-- @param callback  Handler function
177function setfilehandler(callback)
178    return context.request:setfilehandler(callback)
179end
180
181--- Send a HTTP-Header.
182-- @param key   Header key
183-- @param value Header value
184function header(key, value)
185    if not context.headers then
186        context.headers = {}
187    end
188    context.headers[key:lower()] = value
189    coroutine.yield(2, key, value)
190end
191
192--- Set the mime type of following content data.
193-- @param mime  Mimetype of following content
194function prepare_content(mime)
195    if not context.headers or not context.headers["content-type"] then
196        if mime == "application/xhtml+xml" then
197            if not getenv("HTTP_ACCEPT") or
198              not getenv("HTTP_ACCEPT"):find("application/xhtml+xml", nil, true) then
199                mime = "text/html; charset=UTF-8"
200            end
201            header("Vary", "Accept")
202        end
203        header("Content-Type", mime)
204    end
205end
206
207--- Get the RAW HTTP input source
208-- @return  HTTP LTN12 source
209function source()
210    return context.request.input
211end
212
213--- Set the HTTP status code and status message.
214-- @param code      Status code
215-- @param message   Status message
216function status(code, message)
217    code = code or 200
218    message = message or "OK"
219    context.status = code
220    coroutine.yield(1, code, message)
221end
222
223--- Send a chunk of content data to the client.
224-- This function is as a valid LTN12 sink.
225-- If the content chunk is nil this function will automatically invoke close.
226-- @param content   Content chunk
227-- @param src_err   Error object from source (optional)
228-- @see close
229function write(content, src_err)
230    if not content then
231        if src_err then
232            error(src_err)
233        else
234            close()
235        end
236        return true
237    elseif #content == 0 then
238        return true
239    else
240        if not context.eoh then
241            if not context.status then
242                status()
243            end
244            if not context.headers or not context.headers["content-type"] then
245                header("Content-Type", "text/html; charset=utf-8")
246            end
247            if not context.headers["cache-control"] then
248                header("Cache-Control", "no-cache")
249                header("Expires", "0")
250            end
251           
252           
253            context.eoh = true
254            coroutine.yield(3)
255        end
256        coroutine.yield(4, content)
257        return true
258    end
259end
260
261--- Splice data from a filedescriptor to the client.
262-- @param fp    File descriptor
263-- @param size  Bytes to splice (optional)
264function splice(fd, size)
265    coroutine.yield(6, fd, size)
266end
267
268--- Redirects the client to a new URL and closes the connection.
269-- @param url   Target URL
270function redirect(url)
271    status(302, "Found")
272    header("Location", url)
273    close()
274end
275
276--- Create a querystring out of a table of key - value pairs.
277-- @param table     Query string source table
278-- @return          Encoded HTTP query string
279function build_querystring(table)
280    local s="?"
281   
282    for k, v in pairs(table) do
283        s = s .. urlencode(k) .. "=" .. urlencode(v) .. "&"
284    end
285   
286    return s
287end
288
289--- Return the URL-decoded equivalent of a string.
290-- @param str       URL-encoded string
291-- @param no_plus   Don't decode + to " "
292-- @return          URL-decoded string
293-- @see urlencode
294urldecode = protocol.urldecode
295
296--- Return the URL-encoded equivalent of a string.
297-- @param str       Source string
298-- @return          URL-encoded string
299-- @see urldecode
300urlencode = protocol.urlencode
Note: See TracBrowser for help on using the browser.