diff src/server/server.py @ 1037:dc6c8c4d8ff6

server: redirection rework: - redirection parsing is now launched after LiberviaPages are imported, this way the pages are accessible and their methods are usable - inv_redirections attribute in LiberviaRootResource keep a map from new URL to old URL, allowing to check if a redirected path can lead to the same page - LiberviaPage methods are used to retrieve their URL - fixed remaining args when going to a redirected page - new checkRedirection method to see if a redirection can lead to the same URL
author Goffi <goffi@goffi.org>
date Wed, 24 Jan 2018 09:57:38 +0100
parents 34240d08f682
children 6b906b1f419a
line wrap: on
line diff
--- a/src/server/server.py	Mon Jan 22 22:25:37 2018 +0100
+++ b/src/server/server.py	Wed Jan 24 09:57:38 2018 +0100
@@ -107,14 +107,11 @@
     handle redirections declared in sat.conf
     """
 
-    def __init__(self, options, *args, **kwargs):
-        """
-        @param options(dict): configuration options, same as Libervia.options
-        """
-        super(LiberviaRootResource, self).__init__(*args, **kwargs)
-
+    def _initRedirections(self, options):
         ## redirections
         self.redirections = {}
+        self.inv_redirections = {}  # new URL to old URL map
+
         if options['url_redirections_dict'] and not options['url_redirections_profile']:
             # FIXME: url_redirections_profile should not be needed. It is currently used to
             #        redirect to an URL which associate the profile with the service, but this
@@ -157,29 +154,38 @@
                             new['query_args'][k] = [v]
                 elif 'path' in new_data:
                     new = 'file:{}'.format(urllib.quote(new_data['path']))
-            else:
+            elif isinstance(new_data, basestring):
                 new = new_data
                 new_data = {}
+            else:
+                log.error(_(u"ignoring invalid redirection value: {new_data}").format(new_data=new_data))
+                continue
 
             # some normalization
             if not old.strip():
                 # root URL special case
                 old = ''
             elif not old.startswith('/'):
-                raise ValueError(u"redirected url must start with '/', got {}".format(old))
+                log.error(_(u"redirected url must start with '/', got {value}. Ignoring").format(value=old))
+                continue
             else:
                 old = self._normalizeURL(old)
 
             if isinstance(new, dict):
                 # dict are handled differently, they contain data
-                # which are use dynamically when the request is done
+                # which ared use dynamically when the request is done
                 self.redirections[old] = new
                 if not old:
-                    if new['type'] == 'page':
-                        log.info(_(u"Root URL redirected to page {name}").format(name=new['page']))
+                    if new[u'type'] == u'page':
+                        log.info(_(u"Root URL redirected to page {name}").format(name=new[u'page']))
+                else:
+                    if new[u'type'] == u'page':
+                        page = LiberviaPage.getPageByName(new[u'page'])
+                        url = page.getURL(*new.get(u'path_args', []))
+                        self.inv_redirections[url] = old
                 continue
 
-            # at this point we have a rediction URL in new, we can parse it
+            # at this point we have a redirection URL in new, we can parse it
             new_url = urlparse.urlsplit(new.encode('utf-8'))
 
             # we handle the known URL schemes
@@ -197,6 +203,8 @@
                     item = urllib.quote_plus(item),
                     ).decode('utf-8')
                 request_data = self._getRequestData(location)
+                if old:
+                    self.inv_redirections[location] = old
 
             elif new_url.scheme in ('', 'http', 'https'):
                 # direct redirection
@@ -205,6 +213,8 @@
                         netloc = new_url.netloc))
                 location = urlparse.urlunsplit(('', '', new_url.path, new_url.query, new_url.fragment)).decode('utf-8')
                 request_data = self._getRequestData(location)
+                if old:
+                    self.inv_redirections[location] = old
 
             elif new_url.scheme in ('file'):
                 # file or directory
@@ -224,7 +234,7 @@
                     current = resource
                 resource_class = ProtectedFile if new_data.get('protected',True) else static.File
                 current.putChild(last_segment, resource_class(path))
-                log.debug(u"Added redirection from /{old} to file system path {path}".format(old=old.decode('utf-8'), path=path.decode('utf-8')))
+                log.info(u"Added redirection from /{old} to file system path {path}".format(old=old.decode('utf-8'), path=path.decode('utf-8')))
                 continue # we don't want to use redirection system, so we continue here
 
             else:
@@ -297,7 +307,7 @@
                 dummy, uri, dummy, dummy = request_data
             except ValueError:
                 uri = u''
-            log.warning(D_(u"recursive redirection, please fix this URL:\n{old} ==> {new}").format(
+            log.error(D_(u"recursive redirection, please fix this URL:\n{old} ==> {new}").format(
                 old=request.uri.decode('utf-8'),
                 new=uri.decode('utf-8'),
                 ))
@@ -331,9 +341,9 @@
                 new=uri.decode('utf-8'),
                 ))
             # we change the request to reflect the new url
-            request.postpath = path_list[1:]
-            request.uri = uri
-            request.path = path
+            request.postpath = path_list[1:] + request.postpath
+            request.uri = '/'.join([uri] + request.postpath)
+            request.path = '/'.join([path] + request.postpath)
             request.args = args
 
         # we start again to look for a child with the new url
@@ -1433,7 +1443,7 @@
         log.error(_(u"Can't get namespaces map: {msg}").format(msg=failure_))
 
     def backendReady(self, dummy):
-        self.root = root = LiberviaRootResource(self.options, self.html_dir)
+        self.root = root = LiberviaRootResource(self.html_dir)
         _register = Register(self)
         _upload_radiocol = UploadManagerRadioCol(self)
         _upload_avatar = UploadManagerAvatar(self)
@@ -1501,6 +1511,8 @@
         if self.version[-1] == 'D':
             self.putChild('test', web_util.Redirect('/libervia_test.html'))
 
+        # redirections
+        root._initRedirections(self.options)
 
         server.Request.defaultContentType = 'text/html; charset=utf-8'
         wrapped = web_resource.EncodingResourceWrapper(root, [server.GzipEncoderFactory()])
@@ -1869,6 +1881,20 @@
             os.path.join(split_result.path, path),
             query, fragment))
 
+    def checkRedirection(self, url):
+        """check is a part of the URL prefix is redirected then replace it
+
+        @param url(unicode): url to check
+        @return (unicode): possibly redirected URL which should link to the same location
+        """
+        inv_redirections = self.root.inv_redirections
+        url_parts = url.strip(u'/').split(u'/')
+        for idx in xrange(len(url), 0, -1):
+            test_url = u'/' + u'/'.join(url_parts[:idx])
+            if test_url in inv_redirections:
+                rem_url = url_parts[idx:]
+                return u'/'.join([inv_redirections[test_url]] + rem_url)
+        return url
 
     ## Sessions ##