|
27 | 27 | )
|
28 | 28 | from readthedocs.core.utils import get_cache_tag
|
29 | 29 | from readthedocs.projects.models import Feature, Project
|
| 30 | +from readthedocs.proxito.cache import add_cache_tags, cache_response, private_response |
| 31 | +from readthedocs.proxito.redirects import redirect_to_https |
30 | 32 |
|
31 | 33 | log = structlog.get_logger(__name__)
|
32 | 34 |
|
@@ -63,15 +65,14 @@ def add_proxito_headers(self, request, response):
|
63 | 65 | response['X-RTD-Path'] = path
|
64 | 66 |
|
65 | 67 | # Include the project & project-version so we can do larger purges if needed
|
66 |
| - cache_tag = response.get('Cache-Tag') |
67 |
| - cache_tags = [cache_tag] if cache_tag else [] |
| 68 | + cache_tags = [] |
68 | 69 | if project_slug:
|
69 | 70 | cache_tags.append(project_slug)
|
70 | 71 | if version_slug:
|
71 | 72 | cache_tags.append(get_cache_tag(project_slug, version_slug))
|
72 | 73 |
|
73 | 74 | if cache_tags:
|
74 |
| - response['Cache-Tag'] = ','.join(cache_tags) |
| 75 | + add_cache_tags(response, cache_tags) |
75 | 76 |
|
76 | 77 | unresolved_domain = request.unresolved_domain
|
77 | 78 | if unresolved_domain:
|
@@ -167,22 +168,20 @@ def add_cache_headers(self, request, response):
|
167 | 168 |
|
168 | 169 | See https://developers.cloudflare.com/cache/about/cdn-cache-control.
|
169 | 170 | """
|
170 |
| - cdn_cache_header = "CDN-Cache-Control" |
171 | 171 | unresolved_domain = request.unresolved_domain
|
172 | 172 | # Never trust projects resolving from the X-RTD-Slug header,
|
173 | 173 | # we don't want to cache their content on domains from other
|
174 | 174 | # projects, see GHSA-mp38-vprc-7hf5.
|
175 | 175 | if unresolved_domain and unresolved_domain.is_from_http_header:
|
176 |
| - response.headers[cdn_cache_header] = "private" |
| 176 | + private_response(response, force=True) |
177 | 177 | # SECURITY: Return early, we never want to cache this response.
|
178 | 178 | return
|
179 | 179 |
|
180 |
| - # Set the key only if it hasn't already been set by the view. |
181 |
| - if cdn_cache_header not in response.headers: |
182 |
| - default_cache_level = ( |
183 |
| - "private" if settings.ALLOW_PRIVATE_REPOS else "public" |
184 |
| - ) |
185 |
| - response.headers[cdn_cache_header] = default_cache_level |
| 180 | + # Mark the response as private or cache it, if it hasn't been marked as so already. |
| 181 | + if settings.ALLOW_PRIVATE_REPOS: |
| 182 | + private_response(response, force=False) |
| 183 | + else: |
| 184 | + cache_response(response, force=False) |
186 | 185 |
|
187 | 186 | def _set_request_attributes(self, request, unresolved_domain):
|
188 | 187 | """
|
@@ -233,6 +232,10 @@ def process_request(self, request): # noqa
|
233 | 232 |
|
234 | 233 | self._set_request_attributes(request, unresolved_domain)
|
235 | 234 |
|
| 235 | + response = self._get_https_redirect(request) |
| 236 | + if response: |
| 237 | + return response |
| 238 | + |
236 | 239 | # Remove multiple slashes from URL's
|
237 | 240 | if '//' in request.path:
|
238 | 241 | url_parsed = urlparse(request.get_full_path())
|
@@ -286,6 +289,36 @@ def add_hosting_integrations_headers(self, request, response):
|
286 | 289 | if project.has_feature(Feature.HOSTING_INTEGRATIONS):
|
287 | 290 | response["X-RTD-Hosting-Integrations"] = "true"
|
288 | 291 |
|
| 292 | + def _get_https_redirect(self, request): |
| 293 | + """ |
| 294 | + Get a redirect response if the request should be redirected to HTTPS. |
| 295 | +
|
| 296 | + A request should be redirected to HTTPS if any of the following conditions are met: |
| 297 | +
|
| 298 | + - It's from a custom domain and the domain has HTTPS enabled. |
| 299 | + - It's from a public domain, and the public domain uses HTTPS. |
| 300 | + """ |
| 301 | + if request.is_secure(): |
| 302 | + return None |
| 303 | + |
| 304 | + unresolved_domain = request.unresolved_domain |
| 305 | + |
| 306 | + # HTTPS redirect for custom domains. |
| 307 | + if unresolved_domain.is_from_custom_domain: |
| 308 | + domain = unresolved_domain.domain |
| 309 | + if domain.https: |
| 310 | + return redirect_to_https(request, project=unresolved_domain.project) |
| 311 | + return None |
| 312 | + |
| 313 | + # HTTPS redirect for public domains. |
| 314 | + if ( |
| 315 | + unresolved_domain.is_from_public_domain |
| 316 | + or unresolved_domain.is_from_external_domain |
| 317 | + ) and settings.PUBLIC_DOMAIN_USES_HTTPS: |
| 318 | + return redirect_to_https(request, project=unresolved_domain.project) |
| 319 | + |
| 320 | + return None |
| 321 | + |
289 | 322 | def process_response(self, request, response): # noqa
|
290 | 323 | self.add_proxito_headers(request, response)
|
291 | 324 | self.add_cache_headers(request, response)
|
|
0 commit comments