Skip to content

Instantly share code, notes, and snippets.

@cloverstd
Last active October 27, 2021 05:35
Show Gist options
  • Save cloverstd/10712505 to your computer and use it in GitHub Desktop.
Save cloverstd/10712505 to your computer and use it in GitHub Desktop.
tornado cache
# coding: utf-8
try:
import cPickle as pickle
except ImportError:
import pickle
try:
import hashlib
sha1 = hashlib.sha1
except ImportError:
import sha
sha1 = sha.new
import functools
def cache(expires=7200):
def _func(func):
@functools.wraps(func)
def wrapper(handler, *args, **kwargs):
handler.expires = expires
return func(handler, *args, **kwargs)
return wrapper
return _func
class CacheMixin(object):
@property
def cache(self):
return self.application.cache
def prepare(self):
super(CacheMixin, self).prepare()
key = self._generate_key(self.request)
print key
if self.cache.exists(self._prefix(key)):
rv = pickle.loads(self.cache.get(self._prefix(key)))
self.write_cache(rv)
self.finish()
def _generate_key(self, request):
key = pickle.dumps((request.path, request.arguments))
return sha1(key).hexdigest()
def _prefix(self, key):
return "Cache:%s" % key
def write_cache(self, chunk):
super(CacheMixin, self).write(chunk)
def write(self, chunk):
pickled = pickle.dumps(chunk)
key = self._generate_key(self.request)
if hasattr(self, "expires"):
self.cache.set(self._prefix(key), pickled, self.expires)
else:
self.cache.set(self._prefix(key), pickled)
super(CacheMixin, self).write(chunk)
class CacheBackend(object):
"""
The base Cache Backend class
"""
def get(self, key):
raise NotImplementedError
def set(self, key, value, timeout):
raise NotImplementedError
def delitem(self, key):
raise NotImplementedError
def exists(self, key):
raise NotImplementedError
class RedisCacheBackend(CacheBackend):
def __init__(self, redis_connection, **options):
self.options = dict(timeout=86400)
self.options.update(options)
self.redis = redis_connection
def get(self, key):
if self.exists(key):
return self.redis.get(key)
return None
def set(self, key, value, timeout=None):
self.redis.set(key, value)
if timeout:
self.redis.expire(key, timeout)
else:
self.redis.expire(key, self.options["timeout"])
def delitem(self, key):
self.redis.delete(key)
def exists(self, key):
print key
return bool(self.redis.exists(key))
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import tornado.web
import tornado.ioloop
from cache import RedisCacheBackend, CacheMixin
import redis
class CacheHandler(CacheMixin, tornado.web.RequestHandler):
def get(self):
self.expires = 60 # set the cache expires
self.write("test")
class Application(tornado.web.Application):
def __init__(self):
settings = dict(debug=True)
self.redis = redis.Redis()
self.cache = RedisCacheBackend(self.redis)
handlers = [(r'/', CacheHandler)]
super(Application, self).__init__(handlers=handlers, **settings)
if __name__ == '__main__':
application = Application()
application.listen(8080)
tornado.ioloop.IOLoop.instance().start()
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import tornado.web
import tornado.ioloop
from cache import RedisCacheBackend, CacheMixin, cache
import redis
class BaseHandler(CacheMixin, RequestHandler):
def prepare(self):
super(BaseHandler, self).prepare()
class Greetandler(Cornado.web.RequestHandler):
@cache(60) # set the cache expires
def get(self):
self.write("test")
class Application(tornado.web.Application):
def __init__(self):
settings = dict(debug=True)
self.redis = redis.Redis()
self.cache = RedisCacheBackend(self.redis)
handlers = [(r'/', CacheHandler)]
super(Application, self).__init__(handlers=handlers, **settings)
if __name__ == '__main__':
application = Application()
application.listen(8080)
tornado.ioloop.IOLoop.instance().start()
@javalurker
Copy link

有bug:
CacheMixin==>key = pickle.dumps((request.path, request.arguments))
可以改成:
CacheMixin==>key = pickle.dumps((request.path, request.method, request.arguments))

问题在于,如果一个url只有GET方式,但当有人恶意通过POST的方式访问的时候,页面返回错误信息,那么redis就会缓存这个错误信息“HTTPError: HTTP 403: Forbidden ('_xsrf' argument missing from POST)”。导致下次正常的GET方式,无法读取正确的内容(因为redis的key是存在的,value就是那个缓存的错误信息)

@marcelometal
Copy link

marcelometal commented Aug 30, 2018

Hi @cloverstd, thank you for sharing this code. Could you please add a license for it? Can I share using GNU Affero General Public License?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment