* libs/httpd: added ETag, Date, Content-Type and Last-Modified header support to...
[project/luci.git] / libs / httpd / luasrc / httpd / handler / file.lua
1 --[[
2
3 HTTP server implementation for LuCI - luci handler
4 (c) 2008 Steven Barth <steven@midlink.org>
5
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
9
10         http://www.apache.org/licenses/LICENSE-2.0
11
12 $Id$
13
14 ]]--
15
16 module("luci.httpd.handler.file", package.seeall)
17
18 require("luci.httpd.module")
19 require("luci.http.protocol.date")
20 require("luci.http.protocol.mime")
21 require("luci.fs")
22 require("ltn12")
23
24 Simple = luci.util.class(luci.httpd.module.Handler)
25 Response = luci.httpd.module.Response
26
27 function Simple.__init__(self, docroot, dirlist)
28         luci.httpd.module.Handler.__init__(self)
29         self.docroot = docroot
30         self.dirlist = dirlist and true or false
31         self.mime    = luci.http.protocol.mime
32         self.date    = luci.http.protocol.date
33 end
34
35 function Simple.getfile(self, uri)
36         local file = self.docroot .. uri:gsub("%.%./", "")
37         local stat = luci.fs.stat(file)
38
39         return file, stat
40 end
41
42
43 function Simple._mk_etag(self, stat)
44         return string.format( "%x-%x-%x", stat.ino, stat.size, stat.mtime )
45 end
46
47 function Simple._cmp_etag(self, stat, etag)
48         return ( self:_mk_etag(stat) == etag )
49 end
50
51
52 function Simple.handle_get(self, request, sourcein, sinkerr)
53         local file, stat = self:getfile(request.env.PATH_INFO)
54
55         if stat then
56                 if stat.type == "regular" then
57                         return Response(
58                                 200, {
59                                         ["Date"]           = self.date.to_http( os.time() );
60                                         ["Last-Modified"]  = self.date.to_http( stat.mtime );
61                                         ["Content-Type"]   = self.mime.to_mime( file );
62                                         ["Content-Length"] = stat.size;
63                                         ["ETag"]           = self:_mk_etag( stat );
64                                 }
65                         ), ltn12.source.file(io.open(file))
66                 else
67                         return self:failure(403, "Unable to transmit " .. stat.type .. " " .. file)
68                 end
69         else
70                 return self:failure(404, "No such file: " .. file)
71         end
72 end
73
74 function Simple.handle_head(self, ...)
75         local response, sourceout = self:handle_get(...)
76         return response
77 end