NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

caching.py (9335B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\carbon\common\lib\cherrypy\lib\caching.py
      2 import datetime
      3 import sys
      4 import threading
      5 import time
      6 import cherrypy
      7 from cherrypy.lib import cptools, httputil
      8 from cherrypy._cpcompat import copyitems, ntob, set_daemon, sorted
      9 
     10 class Cache(object):
     11 
     12     def get(self):
     13         raise NotImplemented
     14 
     15     def put(self, obj, size):
     16         raise NotImplemented
     17 
     18     def delete(self):
     19         raise NotImplemented
     20 
     21     def clear(self):
     22         raise NotImplemented
     23 
     24 
     25 class AntiStampedeCache(dict):
     26 
     27     def wait(self, key, timeout = 5, debug = False):
     28         value = self.get(key)
     29         if isinstance(value, threading._Event):
     30             if timeout is None:
     31                 if debug:
     32                     cherrypy.log('No timeout', 'TOOLS.CACHING')
     33                 return
     34             if debug:
     35                 cherrypy.log('Waiting up to %s seconds' % timeout, 'TOOLS.CACHING')
     36             value.wait(timeout)
     37             if value.result is not None:
     38                 if debug:
     39                     cherrypy.log('Result!', 'TOOLS.CACHING')
     40                 return value.result
     41             if debug:
     42                 cherrypy.log('Timed out', 'TOOLS.CACHING')
     43             e = threading.Event()
     44             e.result = None
     45             dict.__setitem__(self, key, e)
     46             return
     47         if value is None:
     48             if debug:
     49                 cherrypy.log('Timed out', 'TOOLS.CACHING')
     50             e = threading.Event()
     51             e.result = None
     52             dict.__setitem__(self, key, e)
     53         return value
     54 
     55     def __setitem__(self, key, value):
     56         existing = self.get(key)
     57         dict.__setitem__(self, key, value)
     58         if isinstance(existing, threading._Event):
     59             existing.result = value
     60             existing.set()
     61 
     62 
     63 class MemoryCache(Cache):
     64     maxobjects = 1000
     65     maxobj_size = 100000
     66     maxsize = 10000000
     67     delay = 600
     68     antistampede_timeout = 5
     69     expire_freq = 0.1
     70     debug = False
     71 
     72     def __init__(self):
     73         self.clear()
     74         t = threading.Thread(target=self.expire_cache, name='expire_cache')
     75         self.expiration_thread = t
     76         set_daemon(t, True)
     77         t.start()
     78 
     79     def clear(self):
     80         self.store = {}
     81         self.expirations = {}
     82         self.tot_puts = 0
     83         self.tot_gets = 0
     84         self.tot_hist = 0
     85         self.tot_expires = 0
     86         self.tot_non_modified = 0
     87         self.cursize = 0
     88 
     89     def expire_cache(self):
     90         while time:
     91             now = time.time()
     92             for expiration_time, objects in copyitems(self.expirations):
     93                 if expiration_time <= now:
     94                     for obj_size, uri, sel_header_values in objects:
     95                         try:
     96                             del self.store[uri][tuple(sel_header_values)]
     97                             self.tot_expires += 1
     98                             self.cursize -= obj_size
     99                         except KeyError:
    100                             pass
    101 
    102                     del self.expirations[expiration_time]
    103 
    104             time.sleep(self.expire_freq)
    105 
    106     def get(self):
    107         request = cherrypy.serving.request
    108         self.tot_gets += 1
    109         uri = cherrypy.url(qs=request.query_string)
    110         uricache = self.store.get(uri)
    111         if uricache is None:
    112             return
    113         header_values = [ request.headers.get(h, '') for h in uricache.selecting_headers ]
    114         variant = uricache.wait(key=tuple(sorted(header_values)), timeout=self.antistampede_timeout, debug=self.debug)
    115         if variant is not None:
    116             self.tot_hist += 1
    117         return variant
    118 
    119     def put(self, variant, size):
    120         request = cherrypy.serving.request
    121         response = cherrypy.serving.response
    122         uri = cherrypy.url(qs=request.query_string)
    123         uricache = self.store.get(uri)
    124         if uricache is None:
    125             uricache = AntiStampedeCache()
    126             uricache.selecting_headers = [ e.value for e in response.headers.elements('Vary') ]
    127             self.store[uri] = uricache
    128         if len(self.store) < self.maxobjects:
    129             total_size = self.cursize + size
    130             if size < self.maxobj_size and total_size < self.maxsize:
    131                 expiration_time = response.time + self.delay
    132                 bucket = self.expirations.setdefault(expiration_time, [])
    133                 bucket.append((size, uri, uricache.selecting_headers))
    134                 header_values = [ request.headers.get(h, '') for h in uricache.selecting_headers ]
    135                 uricache[tuple(sorted(header_values))] = variant
    136                 self.tot_puts += 1
    137                 self.cursize = total_size
    138 
    139     def delete(self):
    140         uri = cherrypy.url(qs=cherrypy.serving.request.query_string)
    141         self.store.pop(uri, None)
    142 
    143 
    144 def get(invalid_methods = ('POST', 'PUT', 'DELETE'), debug = False, **kwargs):
    145     request = cherrypy.serving.request
    146     response = cherrypy.serving.response
    147     if not hasattr(cherrypy, '_cache'):
    148         cherrypy._cache = kwargs.pop('cache_class', MemoryCache)()
    149         for k, v in kwargs.items():
    150             setattr(cherrypy._cache, k, v)
    151 
    152         cherrypy._cache.debug = debug
    153     if request.method in invalid_methods:
    154         if debug:
    155             cherrypy.log('request.method %r in invalid_methods %r' % (request.method, invalid_methods), 'TOOLS.CACHING')
    156         cherrypy._cache.delete()
    157         request.cached = False
    158         request.cacheable = False
    159         return False
    160     if 'no-cache' in [ e.value for e in request.headers.elements('Pragma') ]:
    161         request.cached = False
    162         request.cacheable = True
    163         return False
    164     cache_data = cherrypy._cache.get()
    165     request.cached = bool(cache_data)
    166     request.cacheable = not request.cached
    167     if request.cached:
    168         max_age = cherrypy._cache.delay
    169         for v in [ e.value for e in request.headers.elements('Cache-Control') ]:
    170             atoms = v.split('=', 1)
    171             directive = atoms.pop(0)
    172             if directive == 'max-age':
    173                 if len(atoms) != 1 or not atoms[0].isdigit():
    174                     raise cherrypy.HTTPError(400, 'Invalid Cache-Control header')
    175                 max_age = int(atoms[0])
    176                 break
    177             elif directive == 'no-cache':
    178                 if debug:
    179                     cherrypy.log('Ignoring cache due to Cache-Control: no-cache', 'TOOLS.CACHING')
    180                 request.cached = False
    181                 request.cacheable = True
    182                 return False
    183 
    184         if debug:
    185             cherrypy.log('Reading response from cache', 'TOOLS.CACHING')
    186         s, h, b, create_time = cache_data
    187         age = int(response.time - create_time)
    188         if age > max_age:
    189             if debug:
    190                 cherrypy.log('Ignoring cache due to age > %d' % max_age, 'TOOLS.CACHING')
    191             request.cached = False
    192             request.cacheable = True
    193             return False
    194         response.headers = rh = httputil.HeaderMap()
    195         for k in h:
    196             dict.__setitem__(rh, k, dict.__getitem__(h, k))
    197 
    198         response.headers['Age'] = str(age)
    199         try:
    200             cptools.validate_since()
    201         except cherrypy.HTTPRedirect:
    202             x = sys.exc_info()[1]
    203             if x.status == 304:
    204                 cherrypy._cache.tot_non_modified += 1
    205             raise
    206 
    207         response.status = s
    208         response.body = b
    209     elif debug:
    210         cherrypy.log('request is not cached', 'TOOLS.CACHING')
    211     return request.cached
    212 
    213 
    214 def tee_output():
    215     request = cherrypy.serving.request
    216     if 'no-store' in request.headers.values('Cache-Control'):
    217         return
    218 
    219     def tee(body):
    220         if 'no-cache' in response.headers.values('Pragma') or 'no-store' in response.headers.values('Cache-Control'):
    221             for chunk in body:
    222                 yield chunk
    223 
    224             return
    225         output = []
    226         for chunk in body:
    227             output.append(chunk)
    228             yield chunk
    229 
    230         body = ntob('').join(output)
    231         cherrypy._cache.put((response.status,
    232          response.headers or {},
    233          body,
    234          response.time), len(body))
    235 
    236     response = cherrypy.serving.response
    237     response.body = tee(response.body)
    238 
    239 
    240 def expires(secs = 0, force = False, debug = False):
    241     response = cherrypy.serving.response
    242     headers = response.headers
    243     cacheable = False
    244     if not force:
    245         for indicator in ('Etag', 'Last-Modified', 'Age', 'Expires'):
    246             if indicator in headers:
    247                 cacheable = True
    248                 break
    249 
    250     if not cacheable and not force:
    251         if debug:
    252             cherrypy.log('request is not cacheable', 'TOOLS.EXPIRES')
    253     else:
    254         if debug:
    255             cherrypy.log('request is cacheable', 'TOOLS.EXPIRES')
    256         if isinstance(secs, datetime.timedelta):
    257             secs = 86400 * secs.days + secs.seconds
    258         if secs == 0:
    259             if force or 'Pragma' not in headers:
    260                 headers['Pragma'] = 'no-cache'
    261             if cherrypy.serving.request.protocol >= (1, 1):
    262                 if force or 'Cache-Control' not in headers:
    263                     headers['Cache-Control'] = 'no-cache, must-revalidate'
    264             expiry = httputil.HTTPDate(1169942400.0)
    265         else:
    266             expiry = httputil.HTTPDate(response.time + secs)
    267         if force or 'Expires' not in headers:
    268             headers['Expires'] = expiry