-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Start conversion of StaticContentServer from middleware into view
See #34702 This necessarily involves switching from calling `StaticContent.is_versioned_asset_path` to determine whether to handle the request to having a hardcoded urlpattern. I've made the choice to hardcode the other two patterns similarly rather than using imported constants. The mapping of URL patterns to database records should be explicit (even though we don't expect those constants to change out from under us.) I've renamed the middleware rather than choosing a new name for the implementation because there are other references in tests and other code. This was the smaller change. A note on HTTP methods: The middleware currently completely ignores the request's HTTP method, so I wanted to confirm that only GETs were being used in practice. This query reveals that 99.8% of requests that this middleware handles are GET, with just a smattering of PROPFIND and OPTIONS and a tiny number of HEAD and POST: ``` from Transaction select count(*) facet request.method where name = 'WebTransaction/Function/openedx.core.djangoapps.contentserver.middleware:StaticContentServer' since 4 weeks ago ```
- Loading branch information
Showing
7 changed files
with
113 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
""" | ||
URL patterns for course asset serving. | ||
""" | ||
|
||
from django.urls import path, re_path | ||
|
||
from . import views | ||
|
||
# These patterns are incomplete and do not capture the variable | ||
# components of the URLs. That's because the view itself is separately | ||
# parsing the paths, for historical reasons. See docstring on views.py. | ||
urlpatterns = [ | ||
path("c4x/", views.course_assets_view), | ||
re_path("^asset-v1:", views.course_assets_view), | ||
re_path("^assets/courseware/", views.course_assets_view), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
""" | ||
Views for serving course assets. | ||
Historically, this was implemented as a *middleware* (StaticContentServer) that | ||
intercepted requests with paths matching certain patterns, rather than using | ||
urlpatterns and a view. There wasn't any good reason for this, as far as I can | ||
tell. It causes some problems for telemetry: When the code-owner middleware asks | ||
Django what view handled the request, it does so by looking at the result of the | ||
`resolve` utility, but these URLs get a Resolver404 (because there's no | ||
registered urlpattern). | ||
We'd like to turn this into a proper view: | ||
https://github.com/openedx/edx-platform/issues/34702 | ||
The first step, seen here, is to have urlpatterns (redundant with the | ||
middleware's `is_asset_request` method) and a view, but the view just calls into | ||
the same code the middleware uses. The implementation of the middleware has been | ||
moved into StaticContentServerImpl, leaving the middleware as just a shell | ||
around the latter. | ||
A waffle flag chooses whether to allow the middleware to handle the request, or | ||
whether to pass the request along to the view. Why? Because we might be relying | ||
by accident on some weird behavior inherent to misusing a middleware this way, | ||
and we need a way to quickly switch back if we encounter problems. | ||
If the view works, we can move all of StaticContentServerImpl directly into the | ||
view and drop the middleware and the waffle flag. | ||
""" | ||
from django.http import HttpResponseNotFound | ||
from django.views.decorators.http import require_safe | ||
from edx_django_utils.monitoring import set_custom_attribute | ||
|
||
from .middleware import CONTENT_SERVER_USE_VIEW, IMPL | ||
|
||
|
||
@require_safe | ||
def course_assets_view(request): | ||
""" | ||
Serve course assets to end users. Colloquially referred to as "contentserver." | ||
""" | ||
set_custom_attribute('content_server.handled_by.view', True) | ||
|
||
if not CONTENT_SERVER_USE_VIEW.is_enabled(): | ||
# Should never happen; keep track of occurrences. | ||
set_custom_attribute('content_server.view.called_when_disabled', True) | ||
# But handle the request anyhow. | ||
|
||
# We'll delegate request handling to an instance of the middleware | ||
# until we can verify that the behavior is identical when requests | ||
# come all the way through to the view. | ||
response = IMPL.process_request(request) | ||
|
||
if response is None: | ||
# Shouldn't happen | ||
set_custom_attribute('content_server.view.no_response_from_impl', True) | ||
return HttpResponseNotFound() | ||
else: | ||
return response |