* luci/libs/http: added inline documentation to luci.http.protocol & friends, fixed...
[project/luci.git] / libs / http / luasrc / http / protocol / conditionals.lua
1 --[[
2
3 HTTP protocol implementation for LuCI - RFC2616 / 14.19, 14.24 - 14.28
4 (c) 2008 Freifunk Leipzig / Jo-Philipp Wich <xm@leipzig.freifunk.net>
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 --- LuCI http protocol implementation - HTTP/1.1 bits.
17 -- This class provides basic ETag handling and implements most of the
18 -- conditional HTTP/1.1 headers specified in RFC2616 Sct. 14.24 - 14.28 .
19 module("luci.http.protocol.conditionals", package.seeall)
20
21 local date = require("luci.http.protocol.date")
22
23
24 --- Implement 14.19 / ETag.
25 -- @param stat A file.stat structure
26 -- @return String containing the generated tag suitable for ETag headers
27 function mk_etag( stat )
28 if stat ~= nil then
29 return string.format( '"%x-%x-%x"', stat.ino, stat.size, stat.mtime )
30 end
31 end
32
33 --- 14.24 / If-Match
34 -- Test whether the given message object contains an "If-Match" header and
35 -- compare it against the given stat object.
36 -- @param req HTTP request message object
37 -- @param stat A file.stat object
38 -- @return Boolean indicating wheather the precondition is ok
39 -- @return Alternative status code if the precondition failed
40 function if_match( req, stat )
41 local h = req.headers
42 local etag = mk_etag( stat )
43
44 -- Check for matching resource
45 if type(h['If-Match']) == "string" then
46 for ent in h['If-Match']:gmatch("([^, ]+)") do
47 if ( ent == '*' or ent == etag ) and stat ~= nil then
48 return true
49 end
50 end
51
52 return false, 412
53 end
54
55 return true
56 end
57
58 --- 14.25 / If-Modified-Since
59 -- Test whether the given message object contains an "If-Modified-Since" header
60 -- and compare it against the given stat object.
61 -- @param req HTTP request message object
62 -- @param stat A file.stat object
63 -- @return Boolean indicating wheather the precondition is ok
64 -- @return Alternative status code if the precondition failed
65 -- @return Table containing extra HTTP headers if the precondition failed
66 function if_modified_since( req, stat )
67 local h = req.headers
68
69 -- Compare mtimes
70 if type(h['If-Modified-Since']) == "string" then
71 local since = date.to_unix( h['If-Modified-Since'] )
72
73 if stat == nil or since < stat.mtime then
74 return true
75 end
76
77 return false, 304, {
78 ["ETag"] = mk_etag( stat );
79 ["Date"] = date.to_http( os.time() );
80 ["Last-Modified"] = date.to_http( stat.mtime )
81 }
82 end
83
84 return true
85 end
86
87 --- 14.26 / If-None-Match
88 -- Test whether the given message object contains an "If-None-Match" header and
89 -- compare it against the given stat object.
90 -- @param req HTTP request message object
91 -- @param stat A file.stat object
92 -- @return Boolean indicating wheather the precondition is ok
93 -- @return Alternative status code if the precondition failed
94 -- @return Table containing extra HTTP headers if the precondition failed
95 function if_none_match( req, stat )
96 local h = req.headers
97 local etag = mk_etag( stat )
98
99 -- Check for matching resource
100 if type(h['If-None-Match']) == "string" then
101 for ent in h['If-None-Match']:gmatch("([^, ]+)") do
102 if ( ent == '*' or ent == etag ) and stat ~= nil then
103 if req.request_method == "get" or
104 req.request_method == "head"
105 then
106 return false, 304, {
107 ["ETag"] = mk_etag( stat );
108 ["Date"] = date.to_http( os.time() );
109 ["Last-Modified"] = date.to_http( stat.mtime )
110 }
111 else
112 return false, 412
113 end
114 end
115 end
116 end
117
118 return true
119 end
120
121 -- 14.27 / If-Range
122 -- The If-Range header is currently not implemented due to the lack of general
123 -- byte range stuff in luci.http.protocol . This function will always return
124 -- false, 412 to indicate a failed precondition.
125 -- @param req HTTP request message object
126 -- @param stat A file.stat object
127 -- @return Boolean indicating wheather the precondition is ok
128 -- @return Alternative status code if the precondition failed
129 function if_range( req, stat )
130 -- Sorry, no subranges (yet)
131 return false, 412
132 end
133
134 -- 14.28 / If-Unmodified-Since
135 -- Test whether the given message object contains an "If-Unmodified-Since"
136 -- header and compare it against the given stat object.
137 -- @param req HTTP request message object
138 -- @param stat A file.stat object
139 -- @return Boolean indicating wheather the precondition is ok
140 -- @return Alternative status code if the precondition failed
141 function if_unmodified_since( req, stat )
142 local h = req.headers
143
144 -- Compare mtimes
145 if type(h['If-Unmodified-Since']) == "string" then
146 local since = date.to_unix( h['If-Unmodified-Since'] )
147
148 if stat ~= nil and since <= stat.mtime then
149 return false, 412
150 end
151 end
152
153 return true
154 end