comparison src/server/pages.py @ 1018:78af5457d3f8

Pages: added url_cache setting: when url_cache is set, result of parse_url is cached (per profile, because in some pages date retrieved may differ between profiles), and then reused. This is useful if some data are fetched during parse_url (e.g. bridge/XMPP call).
author Goffi <goffi@goffi.org>
date Sun, 21 Jan 2018 13:08:54 +0100
parents 16d52917666c
children 34240d08f682
comparison
equal deleted inserted replaced
1017:8e7897b1008a 1018:78af5457d3f8
40 import time 40 import time
41 41
42 WebsocketMeta = namedtuple("WebsocketMeta", ('url', 'token', 'debug')) 42 WebsocketMeta = namedtuple("WebsocketMeta", ('url', 'token', 'debug'))
43 43
44 44
45 class Cache(object): 45
46 class CacheBase(object):
47
48 def __init__(self):
49 self._created = time.time()
50 self._last_access = self._created
51
52 @property
53 def created(self):
54 return self._created
55
56 @property
57 def last_access(self):
58 return self._last_access
59
60 @last_access.setter
61 def last_access(self, timestamp):
62 self._last_access = timestamp
63
64
65 class CachePage(CacheBase):
46 66
47 def __init__(self, rendered): 67 def __init__(self, rendered):
68 super(CachePage, self).__init__()
48 self._created = time.time() 69 self._created = time.time()
49 self._last_access = self._created 70 self._last_access = self._created
50 self._rendered = rendered 71 self._rendered = rendered
51 72
52 @property 73 @property
53 def created(self):
54 return self._created
55
56 @property
57 def last_access(self):
58 return self._last_access
59
60 @last_access.setter
61 def last_access(self, timestamp):
62 self._last_access = timestamp
63
64 @property
65 def rendered(self): 74 def rendered(self):
66 return self._rendered 75 return self._rendered
76
77
78
79 class CacheURL(CacheBase):
80
81 def __init__(self, request):
82 super(CacheURL, self).__init__()
83 try:
84 self._data = request.data.copy()
85 except AttributeError:
86 self._data = {}
87 self._template_data = request.template_data.copy()
88 self._prepath = request.prepath[:]
89 self._postpath = request.postpath[:]
90 del self._template_data['csrf_token']
91
92 def use(self, request):
93 self.last_access = time.time()
94 request.data = self._data.copy()
95 request.template_data.update(self._template_data)
96 request.prepath = self._prepath[:]
97 request.postpath = self._postpath[:]
67 98
68 99
69 class LiberviaPage(web_resource.Resource): 100 class LiberviaPage(web_resource.Resource):
70 isLeaf = True # we handle subpages ourself 101 isLeaf = True # we handle subpages ourself
71 named_pages = {} 102 named_pages = {}
72 uri_callbacks = {} 103 uri_callbacks = {}
73 signals_handlers = {} 104 signals_handlers = {}
74 pages_redirects = {} 105 pages_redirects = {}
75 cache = {} 106 cache = {}
107 cached_urls = {}
76 # Set of tuples (service/node/sub_id) of nodes subscribed for caching 108 # Set of tuples (service/node/sub_id) of nodes subscribed for caching
77 # sub_id can be empty string if not handled by service 109 # sub_id can be empty string if not handled by service
78 cache_pubsub_sub = set() 110 cache_pubsub_sub = set()
79 main_menu = None 111 main_menu = None
80 112
81 def __init__(self, host, root_dir, url, name=None, redirect=None, access=None, dynamic=False, parse_url=None, 113 def __init__(self, host, root_dir, url, name=None, redirect=None, access=None, dynamic=False, parse_url=None,
82 prepare_render=None, render=None, template=None, 114 prepare_render=None, render=None, template=None,
83 on_data_post=None, on_data=None, on_signal=None): 115 on_data_post=None, on_data=None, on_signal=None,
116 url_cache=False):
84 """initiate LiberviaPages 117 """initiate LiberviaPages
85 118
86 LiberviaPages are the main resources of Libervia, using easy to set python files 119 LiberviaPages are the main resources of Libervia, using easy to set python files
87 The arguments are the variables found in page_meta.py 120 The arguments are the variables found in page_meta.py
88 @param host(Libervia): the running instance of Libervia 121 @param host(Libervia): the running instance of Libervia
157 self.template = template 190 self.template = template
158 self.render_method = render 191 self.render_method = render
159 self.on_data_post = on_data_post 192 self.on_data_post = on_data_post
160 self.on_data = on_data 193 self.on_data = on_data
161 self.on_signal = on_signal 194 self.on_signal = on_signal
195 self.url_cache = url_cache
162 if access == C.PAGES_ACCESS_NONE: 196 if access == C.PAGES_ACCESS_NONE:
163 # none pages just return a 404, no further check is needed 197 # none pages just return a 404, no further check is needed
164 return 198 return
165 if template is None: 199 if template is None:
166 if not callable(render): 200 if not callable(render):
220 render=page_data.get('render'), 254 render=page_data.get('render'),
221 template=page_data.get('template'), 255 template=page_data.get('template'),
222 on_data_post=page_data.get('on_data_post'), 256 on_data_post=page_data.get('on_data_post'),
223 on_data=page_data.get('on_data'), 257 on_data=page_data.get('on_data'),
224 on_signal=page_data.get('on_signal'), 258 on_signal=page_data.get('on_signal'),
259 url_cache=page_data.get('url_cache', False),
225 ) 260 )
226 parent.putChild(d, resource) 261 parent.putChild(d, resource)
227 log.info(u"Added /{path} page".format(path=u'[...]/'.join(new_path))) 262 log.info(u"Added /{path} page".format(path=u'[...]/'.join(new_path)))
228 if 'uri_handlers' in page_data: 263 if 'uri_handlers' in page_data:
229 if not isinstance(page_data, dict): 264 if not isinstance(page_data, dict):
514 cache.last_access = time.time() 549 cache.last_access = time.time()
515 request.write(cache.rendered) 550 request.write(cache.rendered)
516 request.finish() 551 request.finish()
517 raise failure.Failure(exceptions.CancelError(u'cache is used')) 552 raise failure.Failure(exceptions.CancelError(u'cache is used'))
518 553
554 def _cacheURL(self, dummy, request, profile):
555 self.cached_urls.setdefault(profile, {})[request.uri] = CacheURL(request)
556
519 @classmethod 557 @classmethod
520 def onNodeEvent(cls, host, service, node, event_type, items, profile): 558 def onNodeEvent(cls, host, service, node, event_type, items, profile):
521 """Invalidate cache for all pages linked to this node""" 559 """Invalidate cache for all pages linked to this node"""
522 try: 560 try:
523 cache = cls.cache[profile][C.CACHE_PUBSUB][jid.JID(service)][node] 561 cache = cls.cache[profile][C.CACHE_PUBSUB][jid.JID(service)][node]
970 1008
971 if self.redirect is not None: 1009 if self.redirect is not None:
972 d.addCallback(lambda dummy: self.pageRedirect(self.redirect, request, skip_parse_url=False)) 1010 d.addCallback(lambda dummy: self.pageRedirect(self.redirect, request, skip_parse_url=False))
973 1011
974 if self.parse_url is not None and not skip_parse_url: 1012 if self.parse_url is not None and not skip_parse_url:
975 d.addCallback(self.parse_url, request) 1013 if self.url_cache:
1014 profile = self.getProfile(request)
1015 try:
1016 cache_url = self.cached_urls[profile][request.uri]
1017 except KeyError:
1018 # no cache for this URI yet
1019 # we do normal URL parsing, and then the cache
1020 d.addCallback(self.parse_url, request)
1021 d.addCallback(self._cacheURL, request, profile)
1022 else:
1023 log.debug(_(u"using URI cache for {page}").format(page=self))
1024 cache_url.use(request)
1025 else:
1026 d.addCallback(self.parse_url, request)
976 1027
977 d.addCallback(self._subpagesHandler, request) 1028 d.addCallback(self._subpagesHandler, request)
978 1029
979 if request.method not in (C.HTTP_METHOD_GET, C.HTTP_METHOD_POST): 1030 if request.method not in (C.HTTP_METHOD_GET, C.HTTP_METHOD_POST):
980 # only HTTP GET and POST are handled so far 1031 # only HTTP GET and POST are handled so far