diff --git a/readthedocs/core/views/__init__.py b/readthedocs/core/views/__init__.py index e97a7247b..342eb5304 100644 --- a/readthedocs/core/views/__init__.py +++ b/readthedocs/core/views/__init__.py @@ -116,8 +116,16 @@ def server_error_404(request, exception=None, template_name='404.html'): # pyli Marking exception as optional to make /404/ testing page to work. """ response = get_redirect_response(request, path=request.get_full_path()) + if response: - return response + if response.url == request.build_absolute_uri(): + # check that we do have a response and avoid infinite redirect + log.warning( + 'Infinite Redirect: FROM URL is the same than TO URL. url=%s', + response.url, + ) + else: + return response r = render(request, template_name) r.status_code = 404 return r diff --git a/readthedocs/rtd_tests/tests/test_redirects.py b/readthedocs/rtd_tests/tests/test_redirects.py index 7595d1475..335c8feba 100644 --- a/readthedocs/rtd_tests/tests/test_redirects.py +++ b/readthedocs/rtd_tests/tests/test_redirects.py @@ -127,6 +127,34 @@ class RedirectAppTests(TestCase): self.pip = Project.objects.get(slug='pip') self.pip.versions.create_latest() + @override_settings(USE_SUBDOMAIN=True) + def test_redirect_prefix_infinite(self): + """ + Avoid infinite redirects. + + If the URL hit is the same that the URL returned for redirection, we + return a 404. + + These examples comes from this issue: + * https://github.com/rtfd/readthedocs.org/issues/4673 + """ + Redirect.objects.create( + project=self.pip, redirect_type='prefix', + from_url='/', + ) + r = self.client.get('/redirect', HTTP_HOST='pip.readthedocs.org') + self.assertEqual(r.status_code, 302) + self.assertEqual( + r['Location'], 'http://pip.readthedocs.org/en/latest/redirect.html') + + r = self.client.get('/redirect/', HTTP_HOST='pip.readthedocs.org') + self.assertEqual(r.status_code, 302) + self.assertEqual( + r['Location'], 'http://pip.readthedocs.org/en/latest/redirect/') + + r = self.client.get('/en/latest/redirect/', HTTP_HOST='pip.readthedocs.org') + self.assertEqual(r.status_code, 404) + @override_settings(USE_SUBDOMAIN=True) def test_redirect_root(self): Redirect.objects.create(