@@ -3,13 +3,12 @@ Data structures useful for HSTS (HTTP Strict Transport Security)
33HSTS is described in RFC 6797
44]]
55
6- local EOF = require " lpeg" .P (- 1 )
7- local IPv4address = require " lpeg_patterns.IPv4" .IPv4address
8- local IPv6address = require " lpeg_patterns.IPv6" .IPv6address
9- local IPaddress = (IPv4address + IPv6address ) * EOF
6+ local binaryheap = require " binaryheap"
7+ local http_util = require " http.util"
108
119local store_methods = {
1210 time = function () return os.time () end ;
11+ max_items = (1e999 );
1312}
1413
1514local store_mt = {
@@ -23,25 +22,22 @@ local store_item_mt = {
2322 __index = store_item_methods ;
2423}
2524
26- local function host_is_ip (host )
27- if IPaddress :match (host ) then
28- return true
29- else
30- return false
31- end
32- end
33-
3425local function new_store ()
3526 return setmetatable ({
3627 domains = {};
28+ expiry_heap = binaryheap .minUnique ();
29+ n_items = 0 ;
3730 }, store_mt )
3831end
3932
4033function store_methods :clone ()
4134 local r = new_store ()
4235 r .time = rawget (self , " time" )
36+ r .n_items = rawget (self , " n_items" )
37+ r .expiry_heap = binaryheap .minUnique ()
4338 for host , item in pairs (self .domains ) do
4439 r .domains [host ] = item
40+ r .expiry_heap :insert (item .expires , item )
4541 end
4642 return r
4743end
@@ -56,34 +52,62 @@ function store_methods:store(host, directives)
5652 else
5753 max_age = tonumber (max_age , 10 )
5854 end
59- if host_is_ip (host ) then
60- return false
61- end
55+
56+ -- Clean now so that we can assume there are no expired items in store
57+ self :clean ()
58+
6259 if max_age == 0 then
63- -- delete from store
64- self .domains [host ] = nil
60+ return self :remove (host )
6561 else
62+ if http_util .is_ip (host ) then
63+ return false
64+ end
6665 -- add to store
67- self .domains [host ] = setmetatable ({
66+ local old_item = self .domains [host ]
67+ if old_item then
68+ self .expiry_heap :remove (old_item )
69+ else
70+ local n_items = self .n_items
71+ if n_items >= self .max_items then
72+ return false
73+ end
74+ self .n_items = n_items + 1
75+ end
76+ local expires = now + max_age
77+ local item = setmetatable ({
78+ host = host ;
6879 includeSubdomains = directives .includeSubdomains ;
69- expires = now + max_age ;
80+ expires = expires ;
7081 }, store_item_mt )
82+ self .domains [host ] = item
83+ self .expiry_heap :insert (expires , item )
84+ end
85+ return true
86+ end
87+
88+ function store_methods :remove (host )
89+ local item = self .domains [host ]
90+ if item then
91+ self .expiry_heap :remove (item )
92+ self .domains [host ] = nil
93+ self .n_items = self .n_items - 1
7194 end
7295 return true
7396end
7497
7598function store_methods :check (host )
76- if host_is_ip (host ) then
99+ if http_util . is_ip (host ) then
77100 return false
78101 end
79- local now = self .time ()
102+
103+ -- Clean now so that we can assume there are no expired items in store
104+ self :clean ()
105+
80106 local h = host
81107 repeat
82108 local item = self .domains [h ]
83109 if item then
84- if item .expires < now then
85- self :clean ()
86- elseif host == h or item .includeSubdomains then
110+ if host == h or item .includeSubdomains then
87111 return true
88112 end
89113 end
@@ -93,12 +117,20 @@ function store_methods:check(host)
93117 return false
94118end
95119
120+ function store_methods :clean_due ()
121+ local next_expiring = self .expiry_heap :peek ()
122+ if not next_expiring then
123+ return (1e999 )
124+ end
125+ return next_expiring .expires
126+ end
127+
96128function store_methods :clean ()
97129 local now = self .time ()
98- for host , item in pairs ( self . domains ) do
99- if item . expires < now then
100- self .domains [host ] = nil
101- end
130+ while self : clean_due () < now do
131+ local item = self . expiry_heap : pop ()
132+ self .domains [item . host ] = nil
133+ self . n_items = self . n_items - 1
102134 end
103135 return true
104136end
0 commit comments