+ return posixpath.normpath(Join(redirected_dirname, redirect))
+
+ def _RedirectDirectory(self, real_url):
+ ''' Returns the final redirected directory after all directory hops.
+ If there is a circular redirection, it skips the redirection that would
+ cause the infinite loop.
+ If no redirection rule is matched, the base directory is returned.
+ '''
+ seen_redirects = set()
+
+ def lookup_redirect(url):
+ sub_url = url
+
+ for sub_url, _ in Segment(url):
+ for base, filename in Segment(sub_url):
+ try:
+ redirects = self._cache.GetFromFile(posixpath.normpath(
+ posixpath.join(base, 'redirects.json'))).Get()
+ except FileNotFoundError:
+ continue
+
+ redirect = redirects.get(posixpath.join(filename, '...'))
+
+ if redirect is None:
+ continue
+
+ redirect = Join(base, redirect.rstrip('...'))
+
+ # Avoid infinite redirection loops by breaking if seen before.
+ if redirect in seen_redirects:
+ break
+ seen_redirects.add(redirect)
+ return lookup_redirect(
+ Join(redirect, posixpath.relpath(url, sub_url)))
+ return url
+
+ return lookup_redirect(real_url)