Skip to content

Commit c2d784b

Browse files
authored
Proxito V2 (#10044)
This is an initial usage of the new proxito implementation to serve files. It's under a feature flag, so it can be enabled per-project. - We still need to adapt our 404 handler to make use of this new implementation (it could be simplified with #10061) - We are still using our re paths to match the URLs, but we don't make use of the captured parameters, we use the unresolver for that. This also means that things like #2292 won't be solved till we do the whole migration. - There is a lot of repetition from the original get method, but some of it could be simplified with #10065. Tests will pass once we have merged one private PR that we have pending. > **Note** > We don't support custom urlconfs yet, so we shouldn't enable it for those projects yet.
1 parent c3e54ba commit c2d784b

File tree

8 files changed

+305
-18
lines changed

8 files changed

+305
-18
lines changed

readthedocs/core/unresolver.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def __init__(self, project, language, filename):
5858
self.filename = filename
5959

6060

61-
class UnresolvedPathError(UnresolverError):
61+
class InvalidPathForVersionedProjectError(UnresolverError):
6262
def __init__(self, project, path):
6363
self.project = project
6464
self.path = path
@@ -411,7 +411,10 @@ def _unresolve_path_with_parent_project(
411411
if response:
412412
return response
413413

414-
raise UnresolvedPathError(project=parent_project, path=path)
414+
raise InvalidPathForVersionedProjectError(
415+
project=parent_project,
416+
path=self._normalize_filename(path),
417+
)
415418

416419
@staticmethod
417420
def get_domain_from_host(host):

readthedocs/projects/models.py

+5
Original file line numberDiff line numberDiff line change
@@ -1854,6 +1854,7 @@ def add_features(sender, **kwargs):
18541854
DISABLE_PAGEVIEWS = "disable_pageviews"
18551855
DISABLE_SPHINX_DOMAINS = "disable_sphinx_domains"
18561856
RESOLVE_PROJECT_FROM_HEADER = "resolve_project_from_header"
1857+
USE_UNRESOLVER_WITH_PROXITO = "use_unresolver_with_proxito"
18571858

18581859
# Versions sync related features
18591860
SKIP_SYNC_TAGS = 'skip_sync_tags'
@@ -1969,6 +1970,10 @@ def add_features(sender, **kwargs):
19691970
RESOLVE_PROJECT_FROM_HEADER,
19701971
_("Allow usage of the X-RTD-Slug header"),
19711972
),
1973+
(
1974+
USE_UNRESOLVER_WITH_PROXITO,
1975+
_("Use new unresolver implementation for serving documentation files."),
1976+
),
19721977

19731978
# Versions sync related features
19741979
(

readthedocs/proxito/tests/test_full.py

+48
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,18 @@ def test_custom_domain_response_hsts(self):
364364
)
365365

366366

367+
class ProxitoV2TestFullDocServing(TestFullDocServing):
368+
# TODO: remove this class once the new implementation is the default.
369+
def setUp(self):
370+
super().setUp()
371+
get(
372+
Feature,
373+
feature_id=Feature.USE_UNRESOLVER_WITH_PROXITO,
374+
default_true=True,
375+
future_default_true=True,
376+
)
377+
378+
367379
class TestDocServingBackends(BaseDocServing):
368380
# Test that nginx and python backends both work
369381

@@ -555,6 +567,18 @@ def test_track_downloads(self, is_audit_enabled):
555567
self.assertEqual(log.action, AuditLog.DOWNLOAD)
556568

557569

570+
class ProxitoV2TestDocServingBackends(TestDocServingBackends):
571+
# TODO: remove this class once the new implementation is the default.
572+
def setUp(self):
573+
super().setUp()
574+
get(
575+
Feature,
576+
feature_id=Feature.USE_UNRESOLVER_WITH_PROXITO,
577+
default_true=True,
578+
future_default_true=True,
579+
)
580+
581+
558582
@override_settings(
559583
PYTHON_MEDIA=False,
560584
PUBLIC_DOMAIN='readthedocs.io',
@@ -1333,6 +1357,18 @@ def test_serve_invalid_static_file(self, staticfiles_storage):
13331357
self.assertEqual(resp.status_code, 404)
13341358

13351359

1360+
class ProxitoV2TestAdditionalDocViews(TestAdditionalDocViews):
1361+
# TODO: remove this class once the new implementation is the default.
1362+
def setUp(self):
1363+
super().setUp()
1364+
get(
1365+
Feature,
1366+
feature_id=Feature.USE_UNRESOLVER_WITH_PROXITO,
1367+
default_true=True,
1368+
future_default_true=True,
1369+
)
1370+
1371+
13361372
@override_settings(
13371373
ALLOW_PRIVATE_REPOS=True,
13381374
PUBLIC_DOMAIN='dev.readthedocs.io',
@@ -1587,3 +1623,15 @@ def test_cache_on_plan(self):
15871623
# Add project to plan, so we're using that to enable CDN
15881624
self.organization.projects.add(self.project)
15891625
self._test_cache_control_header_project(expected_value="public")
1626+
1627+
1628+
class ProxitoV2TestCDNCache(TestCDNCache):
1629+
# TODO: remove this class once the new implementation is the default.
1630+
def setUp(self):
1631+
super().setUp()
1632+
get(
1633+
Feature,
1634+
feature_id=Feature.USE_UNRESOLVER_WITH_PROXITO,
1635+
default_true=True,
1636+
future_default_true=True,
1637+
)

readthedocs/proxito/tests/test_headers.py

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import django_dynamic_fixture as fixture
22
from django.test import override_settings
33
from django.urls import reverse
4+
from django_dynamic_fixture import get
45

56
from readthedocs.builds.constants import LATEST
6-
from readthedocs.projects.models import Domain, HTTPHeader
7+
from readthedocs.projects.models import Domain, Feature, HTTPHeader
78

89
from .base import BaseDocServing
910

@@ -143,3 +144,15 @@ def test_cache_headers_public_version_with_private_projects_allowed(self):
143144
r = self.client.get('/en/latest/', HTTP_HOST='project.dev.readthedocs.io')
144145
self.assertEqual(r.status_code, 200)
145146
self.assertEqual(r["CDN-Cache-Control"], "public")
147+
148+
149+
class ProxitoV2HeaderTests(ProxitoHeaderTests):
150+
# TODO: remove this class once the new implementation is the default.
151+
def setUp(self):
152+
super().setUp()
153+
get(
154+
Feature,
155+
feature_id=Feature.USE_UNRESOLVER_WITH_PROXITO,
156+
default_true=True,
157+
future_default_true=True,
158+
)

readthedocs/proxito/tests/test_middleware.py

+12
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,18 @@ def test_front_slash_url(self):
259259
)
260260

261261

262+
class ProxitoV2MiddlewareTests(MiddlewareTests):
263+
# TODO: remove this class once the new implementation is the default.
264+
def setUp(self):
265+
super().setUp()
266+
get(
267+
Feature,
268+
feature_id=Feature.USE_UNRESOLVER_WITH_PROXITO,
269+
default_true=True,
270+
future_default_true=True,
271+
)
272+
273+
262274
@pytest.mark.proxito
263275
@override_settings(PUBLIC_DOMAIN='dev.readthedocs.io')
264276
class MiddlewareURLConfTests(TestCase):

readthedocs/proxito/tests/test_old_redirects.py

+50
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from django.http import Http404
1515
from django.test.utils import override_settings
1616
from django.urls import Resolver404
17+
from django_dynamic_fixture import get
1718

1819
from readthedocs.builds.models import Version
1920
from readthedocs.projects.models import Feature
@@ -153,6 +154,18 @@ def test_root_redirect_with_query_params(self):
153154
)
154155

155156

157+
class ProxitoV2InternalRedirectTests(InternalRedirectTests):
158+
# TODO: remove this class once the new implementation is the default.
159+
def setUp(self):
160+
super().setUp()
161+
get(
162+
Feature,
163+
feature_id=Feature.USE_UNRESOLVER_WITH_PROXITO,
164+
default_true=True,
165+
future_default_true=True,
166+
)
167+
168+
156169
# Use ``PYTHON_MEDIA`` here to raise a 404 when trying to serve the file
157170
# from disk and execute the code for the handler404 (the file does not
158171
# exist). On production, this will happen when trying to serve from
@@ -649,6 +662,18 @@ def test_not_found_page_without_trailing_slash(self):
649662
)
650663

651664

665+
class ProxitoV2UserRedirectTests(UserRedirectTests):
666+
# TODO: remove this class once the new implementation is the default.
667+
def setUp(self):
668+
super().setUp()
669+
get(
670+
Feature,
671+
feature_id=Feature.USE_UNRESOLVER_WITH_PROXITO,
672+
default_true=True,
673+
future_default_true=True,
674+
)
675+
676+
652677
@override_settings(PUBLIC_DOMAIN="dev.readthedocs.io")
653678
class UserForcedRedirectTests(BaseDocServing):
654679
def test_no_forced_redirect(self):
@@ -943,6 +968,19 @@ def test_redirect_with_301_status(self):
943968
"http://project.dev.readthedocs.io/en/latest/tutorial/install.html",
944969
)
945970

971+
972+
class ProxitoV2UserForcedRedirectTests(UserForcedRedirectTests):
973+
# TODO: remove this class once the new implementation is the default.
974+
def setUp(self):
975+
super().setUp()
976+
get(
977+
Feature,
978+
feature_id=Feature.USE_UNRESOLVER_WITH_PROXITO,
979+
default_true=True,
980+
future_default_true=True,
981+
)
982+
983+
946984
@override_settings(
947985
PYTHON_MEDIA=True,
948986
PUBLIC_DOMAIN="dev.readthedocs.io",
@@ -1085,3 +1123,15 @@ def test_redirect_sphinx_html_crossdomain(self):
10851123
)
10861124
self.assertEqual(r.status_code, 302, url)
10871125
self.assertEqual(r["Location"], expected_location, url)
1126+
1127+
1128+
class ProxitoV2UserRedirectCrossdomainTest(UserRedirectCrossdomainTest):
1129+
# TODO: remove this class once the new implementation is the default.
1130+
def setUp(self):
1131+
super().setUp()
1132+
get(
1133+
Feature,
1134+
feature_id=Feature.USE_UNRESOLVER_WITH_PROXITO,
1135+
default_true=True,
1136+
future_default_true=True,
1137+
)

0 commit comments

Comments
 (0)