Skip to content

Commit 671cd7e

Browse files
committed
http/hsts: Use a binary heap for more efficient cleaning; adds :clean_due() method
1 parent c242bf8 commit 671cd7e

File tree

3 files changed

+50
-9
lines changed

3 files changed

+50
-9
lines changed

doc/modules/http.hsts.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ Add new directives to the store about the given `host`. `directives` should be a
2222
Returns a boolean indicating if the given `host` is a known HSTS host.
2323

2424

25+
### `hsts_store:clean_due()` <!-- --> {#http.hsts:clean_due}
26+
27+
Returns the number of seconds until the next item in the store expires.
28+
29+
2530
### `hsts_store:clean()` <!-- --> {#http.hsts:clean}
2631

2732
Removes expired entries from the store.

http/hsts.lua

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ Data structures useful for HSTS (HTTP Strict Transport Security)
33
HSTS is described in RFC 6797
44
]]
55

6+
local binaryheap = require "binaryheap"
67
local http_util = require "http.util"
78

89
local store_methods = {
@@ -23,14 +24,17 @@ local store_item_mt = {
2324
local function new_store()
2425
return setmetatable({
2526
domains = {};
27+
expiry_heap = binaryheap.minUnique();
2628
}, store_mt)
2729
end
2830

2931
function store_methods:clone()
3032
local r = new_store()
3133
r.time = rawget(self, "time")
34+
r.expiry_heap = binaryheap.minUnique()
3235
for host, item in pairs(self.domains) do
3336
r.domains[host] = item
37+
r.expiry_heap:insert(item.expires, item)
3438
end
3539
return r
3640
end
@@ -50,14 +54,25 @@ function store_methods:store(host, directives)
5054
end
5155
if max_age == 0 then
5256
-- delete from store
53-
self.domains[host] = nil
57+
local item = self.domains[host]
58+
if item then
59+
self.expiry_heap:remove(item)
60+
self.domains[host] = nil
61+
end
5462
else
5563
-- add to store
56-
self.domains[host] = setmetatable({
64+
local old_item = self.domains[host]
65+
if old_item then
66+
self.expiry_heap:remove(old_item)
67+
end
68+
local expires = now + max_age
69+
local item = setmetatable({
5770
host = host;
5871
includeSubdomains = directives.includeSubdomains;
59-
expires = now + max_age;
72+
expires = expires;
6073
}, store_item_mt)
74+
self.domains[host] = item
75+
self.expiry_heap:insert(expires, item)
6176
end
6277
return true
6378
end
@@ -83,12 +98,19 @@ function store_methods:check(host)
8398
return false
8499
end
85100

101+
function store_methods:clean_due()
102+
local next_expiring = self.expiry_heap:peek()
103+
if not next_expiring then
104+
return (1e999)
105+
end
106+
return next_expiring.expires
107+
end
108+
86109
function store_methods:clean()
87110
local now = self.time()
88-
for host, item in pairs(self.domains) do
89-
if item.expires < now then
90-
self.domains[host] = nil
91-
end
111+
while self:clean_due() < now do
112+
local item = self.expiry_heap:pop()
113+
self.domains[item.host] = nil
92114
end
93115
return true
94116
end

spec/hsts_spec.lua

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,26 @@ describe("hsts module", function()
99
end)
1010
it("can be cloned", function()
1111
local s = http_hsts.new_store()
12-
assert.same(s, s:clone())
12+
do
13+
local clone = s:clone()
14+
local old_heap = s.expiry_heap
15+
s.expiry_heap = nil
16+
clone.expiry_heap = nil
17+
assert.same(s, clone)
18+
s.expiry_heap = old_heap
19+
end
1320
assert.truthy(s:store("foo.example.com", {
1421
["max-age"] = "100";
1522
}))
23+
do
24+
local clone = s:clone()
25+
local old_heap = s.expiry_heap
26+
s.expiry_heap = nil
27+
clone.expiry_heap = nil
28+
assert.same(s, clone)
29+
s.expiry_heap = old_heap
30+
end
1631
local clone = s:clone()
17-
assert.same(s, clone)
1832
assert.truthy(s:check("foo.example.com"))
1933
assert.truthy(clone:check("foo.example.com"))
2034
end)

0 commit comments

Comments
 (0)