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