Mercurial > libervia-web
comparison src/server/server.py @ 979:1d558dfb32ca
server: pages redirection:
when using a redirection dict, a new "page" key can be used to redirect to a named page. "args" can be added to specified named arguments to set (will be put in request.args, in addition to existing ones).
The redirection is done dynamically, during the request workflow.
author | Goffi <goffi@goffi.org> |
---|---|
date | Sun, 12 Nov 2017 12:56:46 +0100 |
parents | c8cafe316f6f |
children | bcacf970f970 |
comparison
equal
deleted
inserted
replaced
978:c8cafe316f6f | 979:1d558dfb32ca |
---|---|
115 super(LiberviaRootResource, self).__init__(*args, **kwargs) | 115 super(LiberviaRootResource, self).__init__(*args, **kwargs) |
116 | 116 |
117 ## redirections | 117 ## redirections |
118 self.redirections = {} | 118 self.redirections = {} |
119 if options['url_redirections_dict'] and not options['url_redirections_profile']: | 119 if options['url_redirections_dict'] and not options['url_redirections_profile']: |
120 # FIXME: url_redirections_profile should not be needed. It is currently used to | |
121 # redirect to an URL which associate the profile with the service, but this | |
122 # is not clean, and service should be explicitly specified | |
120 raise ValueError(u"url_redirections_profile need to be filled if you want to use url_redirections_dict") | 123 raise ValueError(u"url_redirections_profile need to be filled if you want to use url_redirections_dict") |
121 | 124 |
122 for old, new_data in options['url_redirections_dict'].iteritems(): | 125 for old, new_data in options['url_redirections_dict'].iteritems(): |
123 # new_data can be a dictionary or a unicode url | 126 # new_data can be a dictionary or a unicode url |
124 if isinstance(new_data, dict): | 127 if isinstance(new_data, dict): |
125 # new_data dict must contain either "url" or "path" key (exclusive) | 128 # new_data dict must contain either "url", "page" or "path" key (exclusive) |
126 # if "path" is used, a file url is constructed with it | 129 # if "path" is used, a file url is constructed with it |
127 try: | 130 if len({'path', 'url', 'page'}.intersection(new_data.keys())) != 1: |
131 raise ValueError(u'You must have one and only one of "url", "page" or "path" key in your url_redirections_dict data') | |
132 if 'url' in new_data: | |
128 new = new_data['url'] | 133 new = new_data['url'] |
129 except KeyError: | 134 elif 'page' in new_data: |
130 try: | 135 new = new_data |
131 path = new_data['path'] | 136 new['type'] = 'page' |
132 except KeyError: | 137 if 'args' in new: |
133 raise ValueError(u'if you use a dict for url_redirections data, it must contain the "url" or a "file" key') | 138 # we need lists in args because it will be used |
134 else: | 139 # as it in request.args |
135 new = 'file:{}'.format(urllib.quote(path)) | 140 for k,v in new['args'].iteritems(): |
136 else: | 141 if isinstance(v, basestring): |
137 if 'path' in new_data: | 142 new['args'][k] = [v] |
138 raise ValueError(u'You can\'t have "url" and "path" keys at the same time in url_redirections') | 143 elif 'path' in new_data: |
144 new = 'file:{}'.format(urllib.quote(new_data['path'])) | |
139 else: | 145 else: |
140 new = new_data | 146 new = new_data |
141 new_data = {} | 147 new_data = {} |
142 | 148 |
143 # some normalization | 149 # some normalization |
146 old = '' | 152 old = '' |
147 elif not old.startswith('/'): | 153 elif not old.startswith('/'): |
148 raise ValueError(u"redirected url must start with '/', got {}".format(old)) | 154 raise ValueError(u"redirected url must start with '/', got {}".format(old)) |
149 else: | 155 else: |
150 old = self._normalizeURL(old) | 156 old = self._normalizeURL(old) |
157 | |
158 if isinstance(new, dict): | |
159 # dict are handled differently, they contain data | |
160 # which are use dynamically when the request is done | |
161 self.redirections[old] = new | |
162 if not old: | |
163 if new['type'] == 'page': | |
164 log.info(_(u"Root URL redirected to page {name}").format(name=new['page'])) | |
165 continue | |
166 | |
167 # at this point we have a rediction URL in new, we can parse it | |
151 new_url = urlparse.urlsplit(new.encode('utf-8')) | 168 new_url = urlparse.urlsplit(new.encode('utf-8')) |
152 | 169 |
153 # we handle the known URL schemes | 170 # we handle the known URL schemes |
154 if new_url.scheme == 'xmpp': | 171 if new_url.scheme == 'xmpp': |
155 # XMPP URI | 172 # XMPP URI |
198 else: | 215 else: |
199 raise NotImplementedError(u"{scheme}: scheme is not managed for url_redirections_dict".format(scheme=new_url.scheme)) | 216 raise NotImplementedError(u"{scheme}: scheme is not managed for url_redirections_dict".format(scheme=new_url.scheme)) |
200 | 217 |
201 self.redirections[old] = request_data | 218 self.redirections[old] = request_data |
202 if not old: | 219 if not old: |
203 log.info(u"Root URL redirected to {uri}".format(uri=request_data[1].decode('utf-8'))) | 220 log.info(_(u"Root URL redirected to {uri}").format(uri=request_data[1].decode('utf-8'))) |
204 | 221 |
205 # no need to keep url_redirections*, they will not be used anymore | 222 # no need to keep url_redirections*, they will not be used anymore |
206 del options['url_redirections_dict'] | 223 del options['url_redirections_dict'] |
207 del options['url_redirections_profile'] | 224 del options['url_redirections_profile'] |
208 | 225 |
253 this is *NOT* a HTTP redirection, but equivalent to URL rewritting | 270 this is *NOT* a HTTP redirection, but equivalent to URL rewritting |
254 @param request(web.http.request): original request | 271 @param request(web.http.request): original request |
255 @param request_data(tuple): data returned by self._getRequestData | 272 @param request_data(tuple): data returned by self._getRequestData |
256 @return (web_resource.Resource): resource to use | 273 @return (web_resource.Resource): resource to use |
257 """ | 274 """ |
258 path_list, uri, path, args = request_data | 275 # recursion check |
259 try: | 276 try: |
260 request._redirected | 277 request._redirected |
261 except AttributeError: | 278 except AttributeError: |
262 pass | 279 pass |
263 else: | 280 else: |
281 try: | |
282 dummy, uri, dummy, dummy = request_data | |
283 except ValueError: | |
284 uri = u'' | |
264 log.warning(D_(u"recursive redirection, please fix this URL:\n{old} ==> {new}").format( | 285 log.warning(D_(u"recursive redirection, please fix this URL:\n{old} ==> {new}").format( |
265 old=request.uri.decode('utf-8'), | 286 old=request.uri.decode('utf-8'), |
266 new=uri.decode('utf-8'), | 287 new=uri.decode('utf-8'), |
267 )) | 288 )) |
268 return web_resource.NoResource() | 289 return web_resource.NoResource() |
269 log.debug(u"Redirecting URL {old} to {new}".format( | 290 |
270 old=request.uri.decode('utf-8'), | |
271 new=uri.decode('utf-8'), | |
272 )) | |
273 # we change the request to reflect the new url | |
274 request._redirected = True # here to avoid recursive redirections | 291 request._redirected = True # here to avoid recursive redirections |
275 request.postpath = path_list[1:] | 292 |
276 request.uri = uri | 293 if isinstance(request_data, dict): |
277 request.path = path | 294 if request_data['type'] == 'page': |
278 request.args = args | 295 try: |
279 # and we start again to look for a child with the new url | 296 page = LiberviaPage.getPageByName(request_data['page']) |
297 except KeyError: | |
298 log.error(_(u"Can't find page named \"{name}\" requested in redirection").format( | |
299 name = request_data['page'])) | |
300 return web_resource.NoResource() | |
301 try: | |
302 request.args.update(request_data['args']) | |
303 except KeyError: | |
304 pass | |
305 except Exception: | |
306 log.error(_(u"Invalid args in redirection: {args}").format(request_data['args'])) | |
307 return web_resource.NoResource() | |
308 return page | |
309 else: | |
310 raise exceptions.InternalError(u'unknown request_data type') | |
311 else: | |
312 path_list, uri, path, args = request_data | |
313 log.debug(u"Redirecting URL {old} to {new}".format( | |
314 old=request.uri.decode('utf-8'), | |
315 new=uri.decode('utf-8'), | |
316 )) | |
317 # we change the request to reflect the new url | |
318 request.postpath = path_list[1:] | |
319 request.uri = uri | |
320 request.path = path | |
321 request.args = args | |
322 | |
323 # we start again to look for a child with the new url | |
280 return self.getChildWithDefault(path_list[0], request) | 324 return self.getChildWithDefault(path_list[0], request) |
281 | 325 |
282 def getChildWithDefault(self, name, request): | 326 def getChildWithDefault(self, name, request): |
283 # XXX: this method is overriden only for root url | 327 # XXX: this method is overriden only for root url |
284 # which is the only ones who need to be handled before other children | 328 # which is the only ones who need to be handled before other children |
290 resource = super(LiberviaRootResource, self).getChild(name, request) | 334 resource = super(LiberviaRootResource, self).getChild(name, request) |
291 | 335 |
292 if isinstance(resource, web_resource.NoResource): | 336 if isinstance(resource, web_resource.NoResource): |
293 # if nothing was found, we try our luck with redirections | 337 # if nothing was found, we try our luck with redirections |
294 # XXX: we want redirections to happen only if everything else failed | 338 # XXX: we want redirections to happen only if everything else failed |
295 current_url = '/'.join([name] + request.postpath).lower() | 339 |
296 try: | 340 # first we check with current URL |
297 request_data = self.redirections[current_url] | 341 # there our last try with the remaining URL |
298 except KeyError: | 342 for path_elts in (request.prepath, |
299 # no redirection for this url | 343 [name] + request.postpath): |
300 pass | 344 current_url = '/'.join(path_elts).lower() |
301 else: | 345 try: |
302 return self._redirect(request, request_data) | 346 request_data = self.redirections[current_url] |
347 except KeyError: | |
348 # no redirection for this url | |
349 pass | |
350 else: | |
351 return self._redirect(request, request_data) | |
303 | 352 |
304 return resource | 353 return resource |
305 | 354 |
306 def createSimilarFile(self, path): | 355 def createSimilarFile(self, path): |
307 # XXX: this method need to be overriden to avoid recreating a LiberviaRootResource | 356 # XXX: this method need to be overriden to avoid recreating a LiberviaRootResource |
1473 return | 1522 return |
1474 else: | 1523 else: |
1475 url = os.path.join(u'/', u'/'.join(callback_data['pre_path']), callback_data['callback'](self, uri_data)) | 1524 url = os.path.join(u'/', u'/'.join(callback_data['pre_path']), callback_data['callback'](self, uri_data)) |
1476 return url | 1525 return url |
1477 | 1526 |
1478 def getPageByName(self, name): | 1527 @classmethod |
1528 def getPageByName(cls, name): | |
1479 """retrieve page instance from its name | 1529 """retrieve page instance from its name |
1480 | 1530 |
1481 @param name(unicode): name of the page | 1531 @param name(unicode): name of the page |
1482 @return (LiberviaPage): page instance | 1532 @return (LiberviaPage): page instance |
1483 @raise KeyError: the page doesn't exist | 1533 @raise KeyError: the page doesn't exist |
1484 """ | 1534 """ |
1485 return self.named_pages[name] | 1535 return cls.named_pages[name] |
1486 | 1536 |
1487 def getPageRedirectURL(self, request, page_name=u'login', url=None): | 1537 def getPageRedirectURL(self, request, page_name=u'login', url=None): |
1488 """generate URL for a page with redirect_url parameter set | 1538 """generate URL for a page with redirect_url parameter set |
1489 | 1539 |
1490 mainly used for login page with redirection to current page | 1540 mainly used for login page with redirection to current page |