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

sessions.py (15093B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\carbon\common\lib\cherrypy\lib\sessions.py
      2 import datetime
      3 import os
      4 import random
      5 import time
      6 import threading
      7 import types
      8 from warnings import warn
      9 import cherrypy
     10 from cherrypy._cpcompat import copyitems, pickle, random20
     11 from cherrypy.lib import httputil
     12 missing = object()
     13 
     14 class Session(object):
     15     _id = None
     16     id_observers = None
     17 
     18     def _get_id(self):
     19         return self._id
     20 
     21     def _set_id(self, value):
     22         self._id = value
     23         for o in self.id_observers:
     24             o(value)
     25 
     26     id = property(_get_id, _set_id, doc='The current session ID.')
     27     timeout = 60
     28     locked = False
     29     loaded = False
     30     clean_thread = None
     31     clean_freq = 5
     32     originalid = None
     33     missing = False
     34     regenerated = False
     35     debug = False
     36 
     37     def __init__(self, id = None, **kwargs):
     38         self.id_observers = []
     39         self._data = {}
     40         for k, v in kwargs.items():
     41             setattr(self, k, v)
     42 
     43         self.originalid = id
     44         self.missing = False
     45         if id is None:
     46             if self.debug:
     47                 cherrypy.log('No id given; making a new one', 'TOOLS.SESSIONS')
     48             self._regenerate()
     49         else:
     50             self.id = id
     51             if not self._exists():
     52                 if self.debug:
     53                     cherrypy.log('Expired or malicious session %r; making a new one' % id, 'TOOLS.SESSIONS')
     54                 self.id = None
     55                 self.missing = True
     56                 self._regenerate()
     57 
     58     def regenerate(self):
     59         self.regenerated = True
     60         self._regenerate()
     61 
     62     def _regenerate(self):
     63         if self.id is not None:
     64             self.delete()
     65         old_session_was_locked = self.locked
     66         if old_session_was_locked:
     67             self.release_lock()
     68         self.id = None
     69         while self.id is None:
     70             self.id = self.generate_id()
     71             if self._exists():
     72                 self.id = None
     73 
     74         if old_session_was_locked:
     75             self.acquire_lock()
     76 
     77     def clean_up(self):
     78         pass
     79 
     80     def generate_id(self):
     81         return random20()
     82 
     83     def save(self):
     84         try:
     85             if self.loaded:
     86                 t = datetime.timedelta(seconds=self.timeout * 60)
     87                 expiration_time = datetime.datetime.now() + t
     88                 if self.debug:
     89                     cherrypy.log('Saving with expiry %s' % expiration_time, 'TOOLS.SESSIONS')
     90                 self._save(expiration_time)
     91         finally:
     92             if self.locked:
     93                 self.release_lock()
     94 
     95     def load(self):
     96         data = self._load()
     97         if data is None or data[1] < datetime.datetime.now():
     98             if self.debug:
     99                 cherrypy.log('Expired session, flushing data', 'TOOLS.SESSIONS')
    100             self._data = {}
    101         else:
    102             self._data = data[0]
    103         self.loaded = True
    104         cls = self.__class__
    105         if self.clean_freq and not cls.clean_thread:
    106             t = cherrypy.process.plugins.Monitor(cherrypy.engine, self.clean_up, self.clean_freq * 60, name='Session cleanup')
    107             t.subscribe()
    108             cls.clean_thread = t
    109             t.start()
    110 
    111     def delete(self):
    112         self._delete()
    113 
    114     def __getitem__(self, key):
    115         if not self.loaded:
    116             self.load()
    117         return self._data[key]
    118 
    119     def __setitem__(self, key, value):
    120         if not self.loaded:
    121             self.load()
    122         self._data[key] = value
    123 
    124     def __delitem__(self, key):
    125         if not self.loaded:
    126             self.load()
    127         del self._data[key]
    128 
    129     def pop(self, key, default = missing):
    130         if not self.loaded:
    131             self.load()
    132         if default is missing:
    133             return self._data.pop(key)
    134         else:
    135             return self._data.pop(key, default)
    136 
    137     def __contains__(self, key):
    138         if not self.loaded:
    139             self.load()
    140         return key in self._data
    141 
    142     def has_key(self, key):
    143         if not self.loaded:
    144             self.load()
    145         return key in self._data
    146 
    147     def get(self, key, default = None):
    148         if not self.loaded:
    149             self.load()
    150         return self._data.get(key, default)
    151 
    152     def update(self, d):
    153         if not self.loaded:
    154             self.load()
    155         self._data.update(d)
    156 
    157     def setdefault(self, key, default = None):
    158         if not self.loaded:
    159             self.load()
    160         return self._data.setdefault(key, default)
    161 
    162     def clear(self):
    163         if not self.loaded:
    164             self.load()
    165         self._data.clear()
    166 
    167     def keys(self):
    168         if not self.loaded:
    169             self.load()
    170         return self._data.keys()
    171 
    172     def items(self):
    173         if not self.loaded:
    174             self.load()
    175         return self._data.items()
    176 
    177     def values(self):
    178         if not self.loaded:
    179             self.load()
    180         return self._data.values()
    181 
    182 
    183 class RamSession(Session):
    184     cache = {}
    185     locks = {}
    186 
    187     def clean_up(self):
    188         now = datetime.datetime.now()
    189         for id, (data, expiration_time) in copyitems(self.cache):
    190             if expiration_time <= now:
    191                 try:
    192                     del self.cache[id]
    193                 except KeyError:
    194                     pass
    195 
    196                 try:
    197                     del self.locks[id]
    198                 except KeyError:
    199                     pass
    200 
    201     def _exists(self):
    202         return self.id in self.cache
    203 
    204     def _load(self):
    205         return self.cache.get(self.id)
    206 
    207     def _save(self, expiration_time):
    208         self.cache[self.id] = (self._data, expiration_time)
    209 
    210     def _delete(self):
    211         self.cache.pop(self.id, None)
    212 
    213     def acquire_lock(self):
    214         self.locked = True
    215         self.locks.setdefault(self.id, threading.RLock()).acquire()
    216 
    217     def release_lock(self):
    218         self.locks[self.id].release()
    219         self.locked = False
    220 
    221     def __len__(self):
    222         return len(self.cache)
    223 
    224 
    225 class FileSession(Session):
    226     SESSION_PREFIX = 'session-'
    227     LOCK_SUFFIX = '.lock'
    228     pickle_protocol = pickle.HIGHEST_PROTOCOL
    229 
    230     def __init__(self, id = None, **kwargs):
    231         kwargs['storage_path'] = os.path.abspath(kwargs['storage_path'])
    232         Session.__init__(self, id=id, **kwargs)
    233 
    234     def setup(cls, **kwargs):
    235         kwargs['storage_path'] = os.path.abspath(kwargs['storage_path'])
    236         for k, v in kwargs.items():
    237             setattr(cls, k, v)
    238 
    239         lockfiles = [ fname for fname in os.listdir(cls.storage_path) if fname.startswith(cls.SESSION_PREFIX) and fname.endswith(cls.LOCK_SUFFIX) ]
    240         if lockfiles:
    241             plural = ('', 's')[len(lockfiles) > 1]
    242             warn('%s session lockfile%s found at startup. If you are only running one process, then you may need to manually delete the lockfiles found at %r.' % (len(lockfiles), plural, cls.storage_path))
    243 
    244     setup = classmethod(setup)
    245 
    246     def _get_file_path(self):
    247         f = os.path.join(self.storage_path, self.SESSION_PREFIX + self.id)
    248         if not os.path.abspath(f).startswith(self.storage_path):
    249             raise cherrypy.HTTPError(400, 'Invalid session id in cookie.')
    250         return f
    251 
    252     def _exists(self):
    253         path = self._get_file_path()
    254         return os.path.exists(path)
    255 
    256     def _load(self, path = None):
    257         if path is None:
    258             path = self._get_file_path()
    259         try:
    260             f = open(path, 'rb')
    261             try:
    262                 return pickle.load(f)
    263             finally:
    264                 f.close()
    265 
    266         except (IOError, EOFError):
    267             return
    268 
    269     def _save(self, expiration_time):
    270         f = open(self._get_file_path(), 'wb')
    271         try:
    272             pickle.dump((self._data, expiration_time), f, self.pickle_protocol)
    273         finally:
    274             f.close()
    275 
    276     def _delete(self):
    277         try:
    278             os.unlink(self._get_file_path())
    279         except OSError:
    280             pass
    281 
    282     def acquire_lock(self, path = None):
    283         if path is None:
    284             path = self._get_file_path()
    285         path += self.LOCK_SUFFIX
    286         while True:
    287             try:
    288                 lockfd = os.open(path, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
    289             except OSError:
    290                 time.sleep(0.1)
    291             else:
    292                 os.close(lockfd)
    293                 break
    294 
    295         self.locked = True
    296 
    297     def release_lock(self, path = None):
    298         if path is None:
    299             path = self._get_file_path()
    300         os.unlink(path + self.LOCK_SUFFIX)
    301         self.locked = False
    302 
    303     def clean_up(self):
    304         now = datetime.datetime.now()
    305         for fname in os.listdir(self.storage_path):
    306             if fname.startswith(self.SESSION_PREFIX) and not fname.endswith(self.LOCK_SUFFIX):
    307                 path = os.path.join(self.storage_path, fname)
    308                 self.acquire_lock(path)
    309                 try:
    310                     contents = self._load(path)
    311                     if contents is not None:
    312                         data, expiration_time = contents
    313                         if expiration_time < now:
    314                             os.unlink(path)
    315                 finally:
    316                     self.release_lock(path)
    317 
    318     def __len__(self):
    319         return len([ fname for fname in os.listdir(self.storage_path) if fname.startswith(self.SESSION_PREFIX) and not fname.endswith(self.LOCK_SUFFIX) ])
    320 
    321 
    322 class PostgresqlSession(Session):
    323     pickle_protocol = pickle.HIGHEST_PROTOCOL
    324 
    325     def __init__(self, id = None, **kwargs):
    326         Session.__init__(self, id, **kwargs)
    327         self.cursor = self.db.cursor()
    328 
    329     def setup(cls, **kwargs):
    330         for k, v in kwargs.items():
    331             setattr(cls, k, v)
    332 
    333         self.db = self.get_db()
    334 
    335     setup = classmethod(setup)
    336 
    337     def __del__(self):
    338         if self.cursor:
    339             self.cursor.close()
    340         self.db.commit()
    341 
    342     def _exists(self):
    343         self.cursor.execute('select data, expiration_time from session where id=%s', (self.id,))
    344         rows = self.cursor.fetchall()
    345         return bool(rows)
    346 
    347     def _load(self):
    348         self.cursor.execute('select data, expiration_time from session where id=%s', (self.id,))
    349         rows = self.cursor.fetchall()
    350         if not rows:
    351             return None
    352         pickled_data, expiration_time = rows[0]
    353         data = pickle.loads(pickled_data)
    354         return (data, expiration_time)
    355 
    356     def _save(self, expiration_time):
    357         pickled_data = pickle.dumps(self._data, self.pickle_protocol)
    358         self.cursor.execute('update session set data = %s, expiration_time = %s where id = %s', (pickled_data, expiration_time, self.id))
    359 
    360     def _delete(self):
    361         self.cursor.execute('delete from session where id=%s', (self.id,))
    362 
    363     def acquire_lock(self):
    364         self.locked = True
    365         self.cursor.execute('select id from session where id=%s for update', (self.id,))
    366 
    367     def release_lock(self):
    368         self.cursor.close()
    369         self.locked = False
    370 
    371     def clean_up(self):
    372         self.cursor.execute('delete from session where expiration_time < %s', (datetime.datetime.now(),))
    373 
    374 
    375 class MemcachedSession(Session):
    376     mc_lock = threading.RLock()
    377     locks = {}
    378     servers = ['127.0.0.1:11211']
    379 
    380     def setup(cls, **kwargs):
    381         for k, v in kwargs.items():
    382             setattr(cls, k, v)
    383 
    384         import memcache
    385         cls.cache = memcache.Client(cls.servers)
    386 
    387     setup = classmethod(setup)
    388 
    389     def _exists(self):
    390         self.mc_lock.acquire()
    391         try:
    392             return bool(self.cache.get(self.id))
    393         finally:
    394             self.mc_lock.release()
    395 
    396     def _load(self):
    397         self.mc_lock.acquire()
    398         try:
    399             return self.cache.get(self.id)
    400         finally:
    401             self.mc_lock.release()
    402 
    403     def _save(self, expiration_time):
    404         td = int(time.mktime(expiration_time.timetuple()))
    405         self.mc_lock.acquire()
    406         try:
    407             if not self.cache.set(self.id, (self._data, expiration_time), td):
    408                 raise AssertionError('Session data for id %r not set.' % self.id)
    409         finally:
    410             self.mc_lock.release()
    411 
    412     def _delete(self):
    413         self.cache.delete(self.id)
    414 
    415     def acquire_lock(self):
    416         self.locked = True
    417         self.locks.setdefault(self.id, threading.RLock()).acquire()
    418 
    419     def release_lock(self):
    420         self.locks[self.id].release()
    421         self.locked = False
    422 
    423     def __len__(self):
    424         raise NotImplementedError
    425 
    426 
    427 def save():
    428     if not hasattr(cherrypy.serving, 'session'):
    429         return
    430     request = cherrypy.serving.request
    431     response = cherrypy.serving.response
    432     if hasattr(request, '_sessionsaved'):
    433         return
    434     request._sessionsaved = True
    435     if response.stream:
    436         request.hooks.attach('on_end_request', cherrypy.session.save)
    437     else:
    438         if isinstance(response.body, types.GeneratorType):
    439             response.collapse_body()
    440         cherrypy.session.save()
    441 
    442 
    443 save.failsafe = True
    444 
    445 def close():
    446     sess = getattr(cherrypy.serving, 'session', None)
    447     if getattr(sess, 'locked', False):
    448         sess.release_lock()
    449 
    450 
    451 close.failsafe = True
    452 close.priority = 90
    453 
    454 def init(storage_type = 'ram', path = None, path_header = None, name = 'session_id', timeout = 60, domain = None, secure = False, clean_freq = 5, persistent = True, debug = False, **kwargs):
    455     request = cherrypy.serving.request
    456     if hasattr(request, '_session_init_flag'):
    457         return
    458     request._session_init_flag = True
    459     id = None
    460     if name in request.cookie:
    461         id = request.cookie[name].value
    462         if debug:
    463             cherrypy.log('ID obtained from request.cookie: %r' % id, 'TOOLS.SESSIONS')
    464     storage_class = storage_type.title() + 'Session'
    465     storage_class = globals()[storage_class]
    466     if not hasattr(cherrypy, 'session'):
    467         if hasattr(storage_class, 'setup'):
    468             storage_class.setup(**kwargs)
    469     kwargs['timeout'] = timeout
    470     kwargs['clean_freq'] = clean_freq
    471     cherrypy.serving.session = sess = storage_class(id, **kwargs)
    472     sess.debug = debug
    473 
    474     def update_cookie(id):
    475         cherrypy.serving.response.cookie[name] = id
    476 
    477     sess.id_observers.append(update_cookie)
    478     if not hasattr(cherrypy, 'session'):
    479         cherrypy.session = cherrypy._ThreadLocalProxy('session')
    480     if persistent:
    481         cookie_timeout = timeout
    482     else:
    483         cookie_timeout = None
    484     set_response_cookie(path=path, path_header=path_header, name=name, timeout=cookie_timeout, domain=domain, secure=secure)
    485 
    486 
    487 def set_response_cookie(path = None, path_header = None, name = 'session_id', timeout = 60, domain = None, secure = False):
    488     cookie = cherrypy.serving.response.cookie
    489     cookie[name] = cherrypy.serving.session.id
    490     cookie[name]['path'] = path or cherrypy.serving.request.headers.get(path_header) or '/'
    491     if timeout:
    492         e = time.time() + timeout * 60
    493         cookie[name]['expires'] = httputil.HTTPDate(e)
    494     if domain is not None:
    495         cookie[name]['domain'] = domain
    496     if secure:
    497         cookie[name]['secure'] = 1
    498 
    499 
    500 def expire():
    501     name = cherrypy.serving.request.config.get('tools.sessions.name', 'session_id')
    502     one_year = 31536000
    503     e = time.time() - one_year
    504     cherrypy.serving.response.cookie[name]['expires'] = httputil.HTTPDate(e)