Mercurial > libervia-web
comparison 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 |
comparison
equal
deleted
inserted
replaced
1036:f5661761b1b9 | 1037:dc6c8c4d8ff6 |
---|---|
105 """Specialized resource for Libervia root | 105 """Specialized resource for Libervia root |
106 | 106 |
107 handle redirections declared in sat.conf | 107 handle redirections declared in sat.conf |
108 """ | 108 """ |
109 | 109 |
110 def __init__(self, options, *args, **kwargs): | 110 def _initRedirections(self, options): |
111 """ | |
112 @param options(dict): configuration options, same as Libervia.options | |
113 """ | |
114 super(LiberviaRootResource, self).__init__(*args, **kwargs) | |
115 | |
116 ## redirections | 111 ## redirections |
117 self.redirections = {} | 112 self.redirections = {} |
113 self.inv_redirections = {} # new URL to old URL map | |
114 | |
118 if options['url_redirections_dict'] and not options['url_redirections_profile']: | 115 if options['url_redirections_dict'] and not options['url_redirections_profile']: |
119 # FIXME: url_redirections_profile should not be needed. It is currently used to | 116 # FIXME: url_redirections_profile should not be needed. It is currently used to |
120 # redirect to an URL which associate the profile with the service, but this | 117 # redirect to an URL which associate the profile with the service, but this |
121 # is not clean, and service should be explicitly specified | 118 # is not clean, and service should be explicitly specified |
122 raise ValueError(u"url_redirections_profile need to be filled if you want to use url_redirections_dict") | 119 raise ValueError(u"url_redirections_profile need to be filled if you want to use url_redirections_dict") |
155 for k,v in new['query_args'].iteritems(): | 152 for k,v in new['query_args'].iteritems(): |
156 if isinstance(v, basestring): | 153 if isinstance(v, basestring): |
157 new['query_args'][k] = [v] | 154 new['query_args'][k] = [v] |
158 elif 'path' in new_data: | 155 elif 'path' in new_data: |
159 new = 'file:{}'.format(urllib.quote(new_data['path'])) | 156 new = 'file:{}'.format(urllib.quote(new_data['path'])) |
160 else: | 157 elif isinstance(new_data, basestring): |
161 new = new_data | 158 new = new_data |
162 new_data = {} | 159 new_data = {} |
160 else: | |
161 log.error(_(u"ignoring invalid redirection value: {new_data}").format(new_data=new_data)) | |
162 continue | |
163 | 163 |
164 # some normalization | 164 # some normalization |
165 if not old.strip(): | 165 if not old.strip(): |
166 # root URL special case | 166 # root URL special case |
167 old = '' | 167 old = '' |
168 elif not old.startswith('/'): | 168 elif not old.startswith('/'): |
169 raise ValueError(u"redirected url must start with '/', got {}".format(old)) | 169 log.error(_(u"redirected url must start with '/', got {value}. Ignoring").format(value=old)) |
170 continue | |
170 else: | 171 else: |
171 old = self._normalizeURL(old) | 172 old = self._normalizeURL(old) |
172 | 173 |
173 if isinstance(new, dict): | 174 if isinstance(new, dict): |
174 # dict are handled differently, they contain data | 175 # dict are handled differently, they contain data |
175 # which are use dynamically when the request is done | 176 # which ared use dynamically when the request is done |
176 self.redirections[old] = new | 177 self.redirections[old] = new |
177 if not old: | 178 if not old: |
178 if new['type'] == 'page': | 179 if new[u'type'] == u'page': |
179 log.info(_(u"Root URL redirected to page {name}").format(name=new['page'])) | 180 log.info(_(u"Root URL redirected to page {name}").format(name=new[u'page'])) |
181 else: | |
182 if new[u'type'] == u'page': | |
183 page = LiberviaPage.getPageByName(new[u'page']) | |
184 url = page.getURL(*new.get(u'path_args', [])) | |
185 self.inv_redirections[url] = old | |
180 continue | 186 continue |
181 | 187 |
182 # at this point we have a rediction URL in new, we can parse it | 188 # at this point we have a redirection URL in new, we can parse it |
183 new_url = urlparse.urlsplit(new.encode('utf-8')) | 189 new_url = urlparse.urlsplit(new.encode('utf-8')) |
184 | 190 |
185 # we handle the known URL schemes | 191 # we handle the known URL schemes |
186 if new_url.scheme == 'xmpp': | 192 if new_url.scheme == 'xmpp': |
187 # XMPP URI | 193 # XMPP URI |
195 location = "/blog/{profile}/{item}".format( | 201 location = "/blog/{profile}/{item}".format( |
196 profile=quote(options['url_redirections_profile']), | 202 profile=quote(options['url_redirections_profile']), |
197 item = urllib.quote_plus(item), | 203 item = urllib.quote_plus(item), |
198 ).decode('utf-8') | 204 ).decode('utf-8') |
199 request_data = self._getRequestData(location) | 205 request_data = self._getRequestData(location) |
206 if old: | |
207 self.inv_redirections[location] = old | |
200 | 208 |
201 elif new_url.scheme in ('', 'http', 'https'): | 209 elif new_url.scheme in ('', 'http', 'https'): |
202 # direct redirection | 210 # direct redirection |
203 if new_url.netloc: | 211 if new_url.netloc: |
204 raise NotImplementedError(u"netloc ({netloc}) is not implemented yet for url_redirections_dict, it is not possible to redirect to an external website".format( | 212 raise NotImplementedError(u"netloc ({netloc}) is not implemented yet for url_redirections_dict, it is not possible to redirect to an external website".format( |
205 netloc = new_url.netloc)) | 213 netloc = new_url.netloc)) |
206 location = urlparse.urlunsplit(('', '', new_url.path, new_url.query, new_url.fragment)).decode('utf-8') | 214 location = urlparse.urlunsplit(('', '', new_url.path, new_url.query, new_url.fragment)).decode('utf-8') |
207 request_data = self._getRequestData(location) | 215 request_data = self._getRequestData(location) |
216 if old: | |
217 self.inv_redirections[location] = old | |
208 | 218 |
209 elif new_url.scheme in ('file'): | 219 elif new_url.scheme in ('file'): |
210 # file or directory | 220 # file or directory |
211 if new_url.netloc: | 221 if new_url.netloc: |
212 raise NotImplementedError(u"netloc ({netloc}) is not implemented for url redirection to file system, it is not possible to redirect to an external host".format( | 222 raise NotImplementedError(u"netloc ({netloc}) is not implemented for url redirection to file system, it is not possible to redirect to an external host".format( |
222 resource = web_resource.NoResource() | 232 resource = web_resource.NoResource() |
223 current.putChild(segment, resource) | 233 current.putChild(segment, resource) |
224 current = resource | 234 current = resource |
225 resource_class = ProtectedFile if new_data.get('protected',True) else static.File | 235 resource_class = ProtectedFile if new_data.get('protected',True) else static.File |
226 current.putChild(last_segment, resource_class(path)) | 236 current.putChild(last_segment, resource_class(path)) |
227 log.debug(u"Added redirection from /{old} to file system path {path}".format(old=old.decode('utf-8'), path=path.decode('utf-8'))) | 237 log.info(u"Added redirection from /{old} to file system path {path}".format(old=old.decode('utf-8'), path=path.decode('utf-8'))) |
228 continue # we don't want to use redirection system, so we continue here | 238 continue # we don't want to use redirection system, so we continue here |
229 | 239 |
230 else: | 240 else: |
231 raise NotImplementedError(u"{scheme}: scheme is not managed for url_redirections_dict".format(scheme=new_url.scheme)) | 241 raise NotImplementedError(u"{scheme}: scheme is not managed for url_redirections_dict".format(scheme=new_url.scheme)) |
232 | 242 |
295 else: | 305 else: |
296 try: | 306 try: |
297 dummy, uri, dummy, dummy = request_data | 307 dummy, uri, dummy, dummy = request_data |
298 except ValueError: | 308 except ValueError: |
299 uri = u'' | 309 uri = u'' |
300 log.warning(D_(u"recursive redirection, please fix this URL:\n{old} ==> {new}").format( | 310 log.error(D_(u"recursive redirection, please fix this URL:\n{old} ==> {new}").format( |
301 old=request.uri.decode('utf-8'), | 311 old=request.uri.decode('utf-8'), |
302 new=uri.decode('utf-8'), | 312 new=uri.decode('utf-8'), |
303 )) | 313 )) |
304 return web_resource.NoResource() | 314 return web_resource.NoResource() |
305 | 315 |
329 log.debug(u"Redirecting URL {old} to {new}".format( | 339 log.debug(u"Redirecting URL {old} to {new}".format( |
330 old=request.uri.decode('utf-8'), | 340 old=request.uri.decode('utf-8'), |
331 new=uri.decode('utf-8'), | 341 new=uri.decode('utf-8'), |
332 )) | 342 )) |
333 # we change the request to reflect the new url | 343 # we change the request to reflect the new url |
334 request.postpath = path_list[1:] | 344 request.postpath = path_list[1:] + request.postpath |
335 request.uri = uri | 345 request.uri = '/'.join([uri] + request.postpath) |
336 request.path = path | 346 request.path = '/'.join([path] + request.postpath) |
337 request.args = args | 347 request.args = args |
338 | 348 |
339 # we start again to look for a child with the new url | 349 # we start again to look for a child with the new url |
340 return self.getChildWithDefault(path_list[0], request) | 350 return self.getChildWithDefault(path_list[0], request) |
341 | 351 |
1431 | 1441 |
1432 def _namespacesGetEb(self, failure_): | 1442 def _namespacesGetEb(self, failure_): |
1433 log.error(_(u"Can't get namespaces map: {msg}").format(msg=failure_)) | 1443 log.error(_(u"Can't get namespaces map: {msg}").format(msg=failure_)) |
1434 | 1444 |
1435 def backendReady(self, dummy): | 1445 def backendReady(self, dummy): |
1436 self.root = root = LiberviaRootResource(self.options, self.html_dir) | 1446 self.root = root = LiberviaRootResource(self.html_dir) |
1437 _register = Register(self) | 1447 _register = Register(self) |
1438 _upload_radiocol = UploadManagerRadioCol(self) | 1448 _upload_radiocol = UploadManagerRadioCol(self) |
1439 _upload_avatar = UploadManagerAvatar(self) | 1449 _upload_avatar = UploadManagerAvatar(self) |
1440 d = self.bridgeCall('namespacesGet') | 1450 d = self.bridgeCall('namespacesGet') |
1441 d.addCallback(self._namespacesGetCb) | 1451 d.addCallback(self._namespacesGetCb) |
1499 self.putChild('radiocol', ProtectedFile(_upload_radiocol.getTmpDir(), defaultType="audio/ogg")) # FIXME: We cheat for PoC because we know we are on the same host, so we use directly upload dir | 1509 self.putChild('radiocol', ProtectedFile(_upload_radiocol.getTmpDir(), defaultType="audio/ogg")) # FIXME: We cheat for PoC because we know we are on the same host, so we use directly upload dir |
1500 # pyjamas tests, redirected only for dev versions | 1510 # pyjamas tests, redirected only for dev versions |
1501 if self.version[-1] == 'D': | 1511 if self.version[-1] == 'D': |
1502 self.putChild('test', web_util.Redirect('/libervia_test.html')) | 1512 self.putChild('test', web_util.Redirect('/libervia_test.html')) |
1503 | 1513 |
1514 # redirections | |
1515 root._initRedirections(self.options) | |
1504 | 1516 |
1505 server.Request.defaultContentType = 'text/html; charset=utf-8' | 1517 server.Request.defaultContentType = 'text/html; charset=utf-8' |
1506 wrapped = web_resource.EncodingResourceWrapper(root, [server.GzipEncoderFactory()]) | 1518 wrapped = web_resource.EncodingResourceWrapper(root, [server.GzipEncoderFactory()]) |
1507 self.site = server.Site(wrapped) | 1519 self.site = server.Site(wrapped) |
1508 self.site.sessionFactory = LiberviaSession | 1520 self.site.sessionFactory = LiberviaSession |
1867 split_result.scheme.decode('utf-8') if scheme is None else scheme, | 1879 split_result.scheme.decode('utf-8') if scheme is None else scheme, |
1868 split_result.netloc.decode('utf-8'), | 1880 split_result.netloc.decode('utf-8'), |
1869 os.path.join(split_result.path, path), | 1881 os.path.join(split_result.path, path), |
1870 query, fragment)) | 1882 query, fragment)) |
1871 | 1883 |
1884 def checkRedirection(self, url): | |
1885 """check is a part of the URL prefix is redirected then replace it | |
1886 | |
1887 @param url(unicode): url to check | |
1888 @return (unicode): possibly redirected URL which should link to the same location | |
1889 """ | |
1890 inv_redirections = self.root.inv_redirections | |
1891 url_parts = url.strip(u'/').split(u'/') | |
1892 for idx in xrange(len(url), 0, -1): | |
1893 test_url = u'/' + u'/'.join(url_parts[:idx]) | |
1894 if test_url in inv_redirections: | |
1895 rem_url = url_parts[idx:] | |
1896 return u'/'.join([inv_redirections[test_url]] + rem_url) | |
1897 return url | |
1872 | 1898 |
1873 ## Sessions ## | 1899 ## Sessions ## |
1874 | 1900 |
1875 def purgeSession(self, request): | 1901 def purgeSession(self, request): |
1876 """helper method to purge a session during request handling""" | 1902 """helper method to purge a session during request handling""" |