def action(self, time_expire=DEFAULT_TIME_EXPIRE, cache_model=None, prefix=None, session=False, vars=True, lang=True, user_agent=False, public=True, valid_statuses=None, quick=None): """ Experimental! Currently only HTTP 1.1 compliant reference : http://code.google.com/p/doctype-mirror/wiki/ArticleHttpCaching time_expire: same as @cache cache_model: same as @cache prefix: add a prefix to the calculated key session: adds response.session_id to the key vars: adds request.env.query_string lang: adds T.accepted_language user_agent: if True, adds is_mobile and is_tablet to the key. Pass a dict to use all the needed values (uses str(.items())) (e.g. user_agent=request.user_agent()) used only if session is not True public: if False forces the Cache-Control to be 'private' valid_statuses: by default only status codes starting with 1,2,3 will be cached. pass an explicit list of statuses on which turn the cache on quick: Session,Vars,Lang,User-agent,Public: fast overrides with initial strings, e.g. 'SVLP' or 'VLP', or 'VLP' """ from gluon import current from gluon.http import HTTP def wrap(func): def wrapped_f(): if current.request.env.request_method == 'GET': if time_expire: cache_control = 'max-age=%(time_expire)s, s-maxage=%(time_expire)s' % dict(time_expire=time_expire) if quick: session_ = True if 'S' in quick else False vars_ = True if 'V' in quick else False lang_ = True if 'L' in quick else False user_agent_ = True if 'U' in quick else False public_ = True if 'P' in quick else False else: session_, vars_, lang_, user_agent_, public_ = session, vars, lang, user_agent, public if not session_ and public_: cache_control += ', public' expires = (current.request.utcnow + datetime.timedelta(seconds=time_expire)).strftime('%a, %d %b %Y %H:%M:%S GMT') vary = None else: cache_control += ', private' expires = 'Fri, 01 Jan 1990 00:00:00 GMT' if cache_model: cache_key = [current.request.env.path_info, current.response.view] if session_: cache_key.append(current.response.session_id) elif user_agent_: if user_agent_ is True: cache_key.append("%(is_mobile)s_%(is_tablet)s" % current.request.user_agent()) else: cache_key.append(str(user_agent_.items())) if vars_: cache_key.append(current.request.env.query_string) if lang_: cache_key.append(current.T.accepted_language) cache_key = hashlib.md5('__'.join(cache_key)).hexdigest() if prefix: cache_key = prefix + cache_key try: rtn = cache_model(cache_key, lambda : func(), time_expire=time_expire) http, status = None, current.response.status except HTTP, e: rtn = cache_model(cache_key, lambda : e.body, time_expire=time_expire) status = e.status http = HTTP(e.status, rtn, **e.headers) else: http = None else: try: http = None rtn = func() status = current.response.status except HTTP, e: status = e.status http = HTTP(e.status, e.body, **e.headers) else: http = None send_headers = False if http and isinstance(valid_statuses, list): if status in valid_statuses: send_headers = True elif valid_statuses is None: if str(status)[0] in '123': send_headers = True if send_headers: headers = {'Pragma' : None, 'Expires' : expires, 'Cache-Control' : cache_control } current.response.headers.update(headers) if cache_model and not send_headers: cache_model(cache_key, None) if http: if send_headers: http.headers.update(current.response.headers) raise http return rtn return func() wrapped_f.__name__ = func.__name__ wrapped_f.__doc__ = func.__doc__ return wrapped_f return wrap