Skip to content

Commit 0dd5b61

Browse files
authored
Merge pull request #6888 from readthedocs/fix-subproject-no-slash
Fix URLs like `/projects/subproject` from 404ing when they don't end with a slash
2 parents e988f2a + b397973 commit 0dd5b61

File tree

4 files changed

+45
-2
lines changed

4 files changed

+45
-2
lines changed

readthedocs/proxito/tests/test_redirects.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@
1212
)
1313
class RedirectTests(BaseDocServing):
1414

15+
def test_root_url_no_slash(self):
16+
r = self.client.get('', HTTP_HOST='project.dev.readthedocs.io')
17+
self.assertEqual(r.status_code, 302)
18+
self.assertEqual(
19+
r['Location'], 'https://project.dev.readthedocs.io/en/latest/',
20+
)
21+
1522
def test_root_url(self):
1623
r = self.client.get('/', HTTP_HOST='project.dev.readthedocs.io')
1724
self.assertEqual(r.status_code, 302)
@@ -26,6 +33,22 @@ def test_subproject_root_url(self):
2633
r['Location'], 'https://project.dev.readthedocs.io/projects/subproject/en/latest/',
2734
)
2835

36+
def test_subproject_root_url_no_slash(self):
37+
r = self.client.get('/projects/subproject', HTTP_HOST='project.dev.readthedocs.io')
38+
self.assertEqual(r.status_code, 302)
39+
self.assertEqual(
40+
r['Location'], 'https://project.dev.readthedocs.io/projects/subproject/en/latest/',
41+
)
42+
43+
def test_single_version_subproject_root_url_no_slash(self):
44+
self.subproject.single_version = True
45+
self.subproject.save()
46+
r = self.client.get('/projects/subproject', HTTP_HOST='project.dev.readthedocs.io')
47+
self.assertEqual(r.status_code, 302)
48+
self.assertEqual(
49+
r['Location'], 'https://project.dev.readthedocs.io/projects/subproject/',
50+
)
51+
2952
def test_root_redirect_with_query_params(self):
3053
r = self.client.get('/?foo=bar', HTTP_HOST='project.dev.readthedocs.io')
3154
self.assertEqual(r.status_code, 302)

readthedocs/proxito/tests/test_urls.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ def test_root(self):
1717
self.assertEqual(
1818
match.kwargs, {
1919
'subproject_slug': None,
20+
'subproject_slash': None,
2021
'filename': '',
2122
},
2223
)
@@ -80,6 +81,7 @@ def test_subproject_single_version(self):
8081
self.assertEqual(
8182
match.kwargs, {
8283
'subproject_slug': 'bar',
84+
'subproject_slash': '/',
8385
'filename': 'index.html',
8486
},
8587
)
@@ -91,6 +93,7 @@ def test_subproject_root(self):
9193
self.assertEqual(
9294
match.kwargs, {
9395
'subproject_slug': 'bar',
96+
'subproject_slash': '/',
9497
'filename': '',
9598
},
9699
)
@@ -101,6 +104,7 @@ def test_single_version(self):
101104
self.assertEqual(match.args, ())
102105
self.assertEqual(
103106
match.kwargs, {
107+
'subproject_slash': None,
104108
'subproject_slug': None,
105109
'filename': 'some/path/index.html',
106110
},

readthedocs/proxito/urls.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,9 @@
147147
# (Sub)project single version
148148
url(
149149
(
150-
r'^(?:projects/(?P<subproject_slug>{project_slug})/)?'
150+
# subproject_slash variable at the end of this regex is for ``/projects/subproject``
151+
# so that it will get captured here and redirect properly.
152+
r'^(?:projects/(?P<subproject_slug>{project_slug})(?P<subproject_slash>/?))?'
151153
r'(?P<filename>{filename_slug})$'.format(**pattern_opts)
152154
),
153155
ServeDocs.as_view(),

readthedocs/proxito/views/serve.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,17 @@ def get(self,
5555
request,
5656
project_slug=None,
5757
subproject_slug=None,
58+
subproject_slash=None,
5859
lang_slug=None,
5960
version_slug=None,
6061
filename='',
6162
): # noqa
62-
"""Take the incoming parsed URL's and figure out what file to serve."""
63+
"""
64+
Take the incoming parsed URL's and figure out what file to serve.
65+
66+
``subproject_slash`` is used to determine if the subproject URL has a slash,
67+
so that we can decide if we need to serve docs or add a /.
68+
"""
6369

6470
version_slug = self.get_version_from_host(request, version_slug)
6571
final_project, lang_slug, version_slug, filename = _get_project_data_from_request( # noqa
@@ -87,6 +93,14 @@ def get(self,
8793
]):
8894
return self.system_redirect(request, final_project, lang_slug, version_slug, filename)
8995

96+
# Handle `/projects/subproject` URL redirection
97+
if all([
98+
final_project.single_version,
99+
filename == '',
100+
not subproject_slash,
101+
]):
102+
return self.system_redirect(request, final_project, lang_slug, version_slug, filename)
103+
90104
if all([
91105
(lang_slug is None or version_slug is None),
92106
not final_project.single_version,

0 commit comments

Comments
 (0)