1 import datetime
2 import threading
3 import time
4
5 import cherrypy
6 from cherrypy.lib import cptools, http
7
8
9 _missing = object()
10
11
13
15 self.clear()
16 t = threading.Thread(target=self.expire_cache, name='expire_cache')
17 self.expiration_thread = t
18 t.setDaemon(True)
19 t.start()
20
22 """Reset the cache to its initial, empty state."""
23 self.cache = {}
24 self.expirations = {}
25 self.tot_puts = 0
26 self.tot_gets = 0
27 self.tot_hist = 0
28 self.tot_expires = 0
29 self.tot_non_modified = 0
30 self.cursize = 0
31
38 key = property(_key)
39
41
42
43
44
45 while time:
46 now = time.time()
47 for expiration_time, objects in self.expirations.items():
48 if expiration_time <= now:
49 for obj_size, obj_key in objects:
50 try:
51 del self.cache[obj_key]
52 self.tot_expires += 1
53 self.cursize -= obj_size
54 except KeyError:
55
56 pass
57 del self.expirations[expiration_time]
58 time.sleep(0.1)
59
61 """Return the object if in the cache, else None."""
62 self.tot_gets += 1
63 cache_item = self.cache.get(self.key, None)
64 if cache_item:
65 self.tot_hist += 1
66 return cache_item
67 else:
68 return None
69
71 conf = cherrypy.request.config.get
72
73 if len(self.cache) < conf("tools.caching.maxobjects", 1000):
74
75 obj_size = len(obj[2])
76 maxobj_size = conf("tools.caching.maxobj_size", 100000)
77
78 total_size = self.cursize + obj_size
79 maxsize = conf("tools.caching.maxsize", 10000000)
80
81
82 if (obj_size < maxobj_size and total_size < maxsize):
83
84 expiration_time = cherrypy.response.time + conf("tools.caching.delay", 600)
85 obj_key = self.key
86 bucket = self.expirations.setdefault(expiration_time, [])
87 bucket.append((obj_size, obj_key))
88 self.cache[obj_key] = obj
89 self.tot_puts += 1
90 self.cursize = total_size
91
94
95
96 -def get(invalid_methods=("POST", "PUT", "DELETE"), cache_class=MemoryCache, **kwargs):
97 """Try to obtain cached output. If fresh enough, raise HTTPError(304).
98
99 If POST, PUT, or DELETE:
100 * invalidates (deletes) any cached response for this resource
101 * sets request.cached = False
102 * sets request.cacheable = False
103
104 else if a cached copy exists:
105 * sets request.cached = True
106 * sets request.cacheable = False
107 * sets response.headers to the cached values
108 * checks the cached Last-Modified response header against the
109 current If-(Un)Modified-Since request headers; raises 304
110 if necessary.
111 * sets response.status and response.body to the cached values
112 * returns True
113
114 otherwise:
115 * sets request.cached = False
116 * sets request.cacheable = True
117 * returns False
118 """
119 if not hasattr(cherrypy, "_cache"):
120 cherrypy._cache = cache_class()
121
122 request = cherrypy.request
123
124
125
126 if request.method in invalid_methods:
127 cherrypy._cache.delete()
128 request.cached = False
129 request.cacheable = False
130 return False
131
132 cache_data = cherrypy._cache.get()
133 request.cached = c = bool(cache_data)
134 request.cacheable = not c
135 if c:
136 response = cherrypy.response
137 s, h, b, create_time, original_req_headers = cache_data
138
139
140
141
142
143
144
145
146
147
148
149
150
151 for header_element in h.elements('Vary'):
152 key = header_element.value
153 if original_req_headers[key] != request.headers.get(key, 'missing'):
154 request.cached = False
155 request.cacheable = True
156 return False
157
158
159 response.headers = h
160 response.headers["Age"] = str(int(response.time - create_time))
161
162 try:
163
164
165
166 cptools.validate_since()
167 except cherrypy.HTTPError, x:
168 if x.status == 304:
169 cherrypy._cache.tot_non_modified += 1
170 raise
171
172
173 response.status = s
174 response.body = b
175 return c
176
177
201 response.body = tee(response.body)
202
203
205 """Tool for influencing cache mechanisms using the 'Expires' header.
206
207 'secs' must be either an int or a datetime.timedelta, and indicates the
208 number of seconds between response.time and when the response should
209 expire. The 'Expires' header will be set to (response.time + secs).
210
211 If 'secs' is zero, the following "cache prevention" headers are also set:
212 'Pragma': 'no-cache'
213 'Cache-Control': 'no-cache, must-revalidate'
214
215 If 'force' is False (the default), the following headers are checked:
216 'Etag', 'Last-Modified', 'Age', 'Expires'. If any are already present,
217 none of the above response headers are set.
218 """
219
220 response = cherrypy.response
221 headers = response.headers
222
223 cacheable = False
224 if not force:
225
226 for indicator in ('Etag', 'Last-Modified', 'Age', 'Expires'):
227 if indicator in headers:
228 cacheable = True
229 break
230
231 if not cacheable:
232 if isinstance(secs, datetime.timedelta):
233 secs = (86400 * secs.days) + secs.seconds
234
235 if secs == 0:
236 if force or "Pragma" not in headers:
237 headers["Pragma"] = "no-cache"
238 if cherrypy.request.protocol >= (1, 1):
239 if force or "Cache-Control" not in headers:
240 headers["Cache-Control"] = "no-cache, must-revalidate"
241
242 expiry = http.HTTPDate(response.time + secs)
243 if force or "Expires" not in headers:
244 headers["Expires"] = expiry
245