Repository: readthedocs/readthedocs.org

Issue:
Page redirects incorrectly append .html to redirected URLs containing fragments
## Details
- Project URL: urllib3.readthedocs.io
- Build URL (if applicable): Any
- Read the Docs username (if applicable): Lukasa
## Action Taken

In settings, I configured a page redirect: `Page Redirect: /security.html -> /advanced-usage.html#ssl-warnings`.
## Expected Result

When visiting https://urllib3.readthedocs.io/en/latest/security.html, I expect to be redirected to https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings.
## Actual Result

When visiting https://urllib3.readthedocs.io/en/latest/security.html, I am redirected to https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings.html (note the extra HTML).

I suspect this is just a misbehaving convenience feature: RTD is spotting that the URL doesn't end in `.html` and is helpfully adding it for me, even though it's syntactically not in the right place. I think this change requires URL parsing the target URL and confirming that the _path_ ends in `.html`, and then restoring the fragment (if present).



Discussion:

Resolving patch:
diff --git a/readthedocs/core/resolver.py b/readthedocs/core/resolver.py
--- a/readthedocs/core/resolver.py
+++ b/readthedocs/core/resolver.py
@@ -195,6 +195,9 @@ def _fix_filename(self, project, filename):
                 path = "index.html#document-" + filename
             elif project.documentation_type in ["sphinx_htmldir", "mkdocs"]:
                 path = filename + "/"
+            elif '#' in filename:
+                # do nothing if the filename contains URL fragments
+                path = filename
             else:
                 path = filename + ".html"
         else:


Test patch:
diff --git a/readthedocs/rtd_tests/tests/test_redirects.py b/readthedocs/rtd_tests/tests/test_redirects.py
--- a/readthedocs/rtd_tests/tests/test_redirects.py
+++ b/readthedocs/rtd_tests/tests/test_redirects.py
@@ -215,6 +215,36 @@ def test_redirect_htmldir(self):
             r['Location'], 'http://pip.readthedocs.org/en/latest/faq/')
 
 
+class CustomRedirectTests(TestCase):
+
+    @classmethod
+    def setUpTestData(cls):
+        cls.pip = Project.objects.create(**{
+            'repo_type': 'git',
+            'name': 'Pip',
+            'default_branch': '',
+            'project_url': 'http://pip.rtfd.org',
+            'repo': 'https://github.com/fail/sauce',
+            'default_version': LATEST,
+            'privacy_level': 'public',
+            'version_privacy_level': 'public',
+            'description': 'wat',
+            'documentation_type': 'sphinx',
+        })
+        Redirect.objects.create(
+            project=cls.pip,
+            redirect_type='page',
+            from_url='/install.html',
+            to_url='/install.html#custom-fragment',
+        )
+
+    def test_redirect_fragment(self):
+        redirect = Redirect.objects.get(project=self.pip)
+        path = redirect.get_redirect_path('/install.html')
+        expected_path = '/docs/pip/en/latest/install.html#custom-fragment'
+        self.assertEqual(path, expected_path)
+
+
 @override_settings(PUBLIC_DOMAIN='readthedocs.org', USE_SUBDOMAIN=False)
 class RedirectBuildTests(TestCase):
     fixtures = ["eric", "test_data"]