Merge pull request #302 from chris5560/master
[project/luci.git] / modules / luci-base / luasrc / http / protocol / conditionals.lua
1 -- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org>
2 -- Licensed to the public under the Apache License 2.0.
3
4 -- This class provides basic ETag handling and implements most of the
5 -- conditional HTTP/1.1 headers specified in RFC2616 Sct. 14.24 - 14.28 .
6 module("luci.http.protocol.conditionals", package.seeall)
7
8 local date = require("luci.http.protocol.date")
9
10
11 function mk_etag( stat )
12         if stat ~= nil then
13                 return string.format( '"%x-%x-%x"', stat.ino, stat.size, stat.mtime )
14         end
15 end
16
17 -- Test whether the given message object contains an "If-Match" header and
18 -- compare it against the given stat object.
19 function if_match( req, stat )
20         local h    = req.headers
21         local etag = mk_etag( stat )
22
23         -- Check for matching resource
24         if type(h['If-Match']) == "string" then
25                 for ent in h['If-Match']:gmatch("([^, ]+)") do
26                         if ( ent == '*' or ent == etag ) and stat ~= nil then
27                                 return true
28                         end
29                 end
30
31                 return false, 412
32         end
33
34         return true
35 end
36
37 -- Test whether the given message object contains an "If-Modified-Since" header
38 -- and compare it against the given stat object.
39 function if_modified_since( req, stat )
40         local h = req.headers
41
42         -- Compare mtimes
43         if type(h['If-Modified-Since']) == "string" then
44                 local since = date.to_unix( h['If-Modified-Since'] )
45
46                 if stat == nil or since < stat.mtime then
47                         return true
48                 end
49
50                 return false, 304, {
51                         ["ETag"]          = mk_etag( stat );
52                         ["Date"]          = date.to_http( os.time() );
53                         ["Last-Modified"] = date.to_http( stat.mtime )
54                 }
55         end
56
57         return true
58 end
59
60 -- Test whether the given message object contains an "If-None-Match" header and
61 -- compare it against the given stat object.
62 function if_none_match( req, stat )
63         local h      = req.headers
64         local etag   = mk_etag( stat )
65         local method = req.env and req.env.REQUEST_METHOD or "GET"
66
67         -- Check for matching resource
68         if type(h['If-None-Match']) == "string" then
69                 for ent in h['If-None-Match']:gmatch("([^, ]+)") do
70                         if ( ent == '*' or ent == etag ) and stat ~= nil then
71                                 if method == "GET" or method == "HEAD" then
72                                         return false, 304, {
73                                                 ["ETag"]          = etag;
74                                                 ["Date"]          = date.to_http( os.time() );
75                                                 ["Last-Modified"] = date.to_http( stat.mtime )
76                                         }
77                                 else
78                                         return false, 412
79                                 end
80                         end
81                 end
82         end
83
84         return true
85 end
86
87 -- The If-Range header is currently not implemented due to the lack of general
88 -- byte range stuff in luci.http.protocol . This function will always return
89 -- false, 412 to indicate a failed precondition.
90 function if_range( req, stat )
91         -- Sorry, no subranges (yet)
92         return false, 412
93 end
94
95 -- Test whether the given message object contains an "If-Unmodified-Since"
96 -- header and compare it against the given stat object.
97 function if_unmodified_since( req, stat )
98         local h = req.headers
99
100         -- Compare mtimes
101         if type(h['If-Unmodified-Since']) == "string" then
102                 local since = date.to_unix( h['If-Unmodified-Since'] )
103
104                 if stat ~= nil and since <= stat.mtime then
105                         return false, 412
106                 end
107         end
108
109         return true
110 end