Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
95fde6d5
Commit
95fde6d5
authored
Apr 12, 2016
by
Toby Lawrence
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Revert "[PERF-274] Add NewRelic instrumentation to contentserver."
This reverts commit
09586d8a
.
parent
8f25c81a
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
8 additions
and
164 deletions
+8
-164
common/djangoapps/contentserver/admin.py
+1
-21
common/djangoapps/contentserver/middleware.py
+6
-48
common/djangoapps/contentserver/migrations/0002_cdnuseragentsconfig.py
+0
-27
common/djangoapps/contentserver/models.py
+1
-24
common/djangoapps/contentserver/test/test_contentserver.py
+0
-44
No files found.
common/djangoapps/contentserver/admin.py
View file @
95fde6d5
...
@@ -4,7 +4,7 @@ that gets used when sending cachability headers back with request course assets.
...
@@ -4,7 +4,7 @@ that gets used when sending cachability headers back with request course assets.
"""
"""
from
django.contrib
import
admin
from
django.contrib
import
admin
from
config_models.admin
import
ConfigurationModelAdmin
from
config_models.admin
import
ConfigurationModelAdmin
from
.models
import
CourseAssetCacheTtlConfig
,
CdnUserAgentsConfig
from
.models
import
CourseAssetCacheTtlConfig
class
CourseAssetCacheTtlConfigAdmin
(
ConfigurationModelAdmin
):
class
CourseAssetCacheTtlConfigAdmin
(
ConfigurationModelAdmin
):
...
@@ -26,24 +26,4 @@ class CourseAssetCacheTtlConfigAdmin(ConfigurationModelAdmin):
...
@@ -26,24 +26,4 @@ class CourseAssetCacheTtlConfigAdmin(ConfigurationModelAdmin):
return
self
.
list_display
return
self
.
list_display
class
CdnUserAgentsConfigAdmin
(
ConfigurationModelAdmin
):
"""
Basic configuration for CDN user agent whitelist.
"""
list_display
=
[
'cdn_user_agents'
]
def
get_list_display
(
self
,
request
):
"""
Restore default list_display behavior.
ConfigurationModelAdmin overrides this, but in a way that doesn't
respect the ordering. This lets us customize it the usual Django admin
way.
"""
return
self
.
list_display
admin
.
site
.
register
(
CourseAssetCacheTtlConfig
,
CourseAssetCacheTtlConfigAdmin
)
admin
.
site
.
register
(
CourseAssetCacheTtlConfig
,
CourseAssetCacheTtlConfigAdmin
)
admin
.
site
.
register
(
CdnUserAgentsConfig
,
CdnUserAgentsConfigAdmin
)
common/djangoapps/contentserver/middleware.py
View file @
95fde6d5
...
@@ -3,14 +3,13 @@ Middleware to serve assets.
...
@@ -3,14 +3,13 @@ Middleware to serve assets.
"""
"""
import
logging
import
logging
import
datetime
import
newrelic.agent
import
datetime
from
django.http
import
(
from
django.http
import
(
HttpResponse
,
HttpResponseNotModified
,
HttpResponseForbidden
,
HttpResponse
,
HttpResponseNotModified
,
HttpResponseForbidden
,
HttpResponseBadRequest
,
HttpResponseNotFound
)
HttpResponseBadRequest
,
HttpResponseNotFound
)
from
student.models
import
CourseEnrollment
from
student.models
import
CourseEnrollment
from
contentserver.models
import
CourseAssetCacheTtlConfig
,
CdnUserAgentsConfig
from
contentserver.models
import
CourseAssetCacheTtlConfig
from
header_control
import
force_header_for_response
from
header_control
import
force_header_for_response
from
xmodule.assetstore.assetmgr
import
AssetManager
from
xmodule.assetstore.assetmgr
import
AssetManager
...
@@ -56,19 +55,6 @@ class StaticContentServer(object):
...
@@ -56,19 +55,6 @@ class StaticContentServer(object):
except
(
ItemNotFoundError
,
NotFoundError
):
except
(
ItemNotFoundError
,
NotFoundError
):
return
HttpResponseNotFound
()
return
HttpResponseNotFound
()
# Set the basics for this request.
newrelic
.
agent
.
add_custom_parameter
(
'course_id'
,
loc
.
course_key
)
newrelic
.
agent
.
add_custom_parameter
(
'org'
,
loc
.
org
)
newrelic
.
agent
.
add_custom_parameter
(
'contentserver.path'
,
loc
.
path
)
# Figure out if this is a CDN using us as the origin.
is_from_cdn
=
StaticContentServer
.
is_cdn_request
(
request
)
newrelic
.
agent
.
add_custom_parameter
(
'contentserver.from_cdn'
,
True
if
is_from_cdn
else
False
)
# Check if this content is locked or not.
locked
=
self
.
is_content_locked
(
content
)
newrelic
.
agent
.
add_custom_parameter
(
'contentserver.locked'
,
True
if
locked
else
False
)
# Check that user has access to the content.
# Check that user has access to the content.
if
not
self
.
is_user_authorized
(
request
,
content
,
loc
):
if
not
self
.
is_user_authorized
(
request
,
content
,
loc
):
return
HttpResponseForbidden
(
'Unauthorized'
)
return
HttpResponseForbidden
(
'Unauthorized'
)
...
@@ -121,11 +107,8 @@ class StaticContentServer(object):
...
@@ -121,11 +107,8 @@ class StaticContentServer(object):
response
[
'Content-Range'
]
=
'bytes {first}-{last}/{length}'
.
format
(
response
[
'Content-Range'
]
=
'bytes {first}-{last}/{length}'
.
format
(
first
=
first
,
last
=
last
,
length
=
content
.
length
first
=
first
,
last
=
last
,
length
=
content
.
length
)
)
range_len
=
last
-
first
+
1
response
[
'Content-Length'
]
=
str
(
last
-
first
+
1
)
response
[
'Content-Length'
]
=
str
(
range_len
)
response
.
status_code
=
206
# Partial Content
response
.
status_code
=
206
# Partial Content
newrelic
.
agent
.
add_custom_parameter
(
'contentserver.range_len'
,
range_len
)
else
:
else
:
log
.
warning
(
log
.
warning
(
u"Cannot satisfy ranges in Range header:
%
s for content:
%
s"
,
header_value
,
unicode
(
loc
)
u"Cannot satisfy ranges in Range header:
%
s for content:
%
s"
,
header_value
,
unicode
(
loc
)
...
@@ -137,9 +120,6 @@ class StaticContentServer(object):
...
@@ -137,9 +120,6 @@ class StaticContentServer(object):
response
=
HttpResponse
(
content
.
stream_data
())
response
=
HttpResponse
(
content
.
stream_data
())
response
[
'Content-Length'
]
=
content
.
length
response
[
'Content-Length'
]
=
content
.
length
newrelic
.
agent
.
add_custom_parameter
(
'contentserver.content_len'
,
content
.
length
)
newrelic
.
agent
.
add_custom_parameter
(
'contentserver.content_type'
,
content
.
content_type
)
# "Accept-Ranges: bytes" tells the user that only "bytes" ranges are allowed
# "Accept-Ranges: bytes" tells the user that only "bytes" ranges are allowed
response
[
'Accept-Ranges'
]
=
'bytes'
response
[
'Accept-Ranges'
]
=
'bytes'
response
[
'Content-Type'
]
=
content
.
content_type
response
[
'Content-Type'
]
=
content
.
content_type
...
@@ -166,11 +146,9 @@ class StaticContentServer(object):
...
@@ -166,11 +146,9 @@ class StaticContentServer(object):
# indicate there should be no caching whatsoever.
# indicate there should be no caching whatsoever.
cache_ttl
=
CourseAssetCacheTtlConfig
.
get_cache_ttl
()
cache_ttl
=
CourseAssetCacheTtlConfig
.
get_cache_ttl
()
if
cache_ttl
>
0
and
not
is_locked
:
if
cache_ttl
>
0
and
not
is_locked
:
newrelic
.
agent
.
add_custom_parameter
(
'contentserver.cacheable'
,
True
)
response
[
'Expires'
]
=
StaticContentServer
.
get_expiration_value
(
datetime
.
datetime
.
utcnow
(),
cache_ttl
)
response
[
'Expires'
]
=
StaticContentServer
.
get_expiration_value
(
datetime
.
datetime
.
utcnow
(),
cache_ttl
)
response
[
'Cache-Control'
]
=
"public, max-age={ttl}, s-maxage={ttl}"
.
format
(
ttl
=
cache_ttl
)
response
[
'Cache-Control'
]
=
"public, max-age={ttl}, s-maxage={ttl}"
.
format
(
ttl
=
cache_ttl
)
elif
is_locked
:
elif
is_locked
:
newrelic
.
agent
.
add_custom_parameter
(
'contentserver.cacheable'
,
False
)
response
[
'Cache-Control'
]
=
"private, no-cache, no-store"
response
[
'Cache-Control'
]
=
"private, no-cache, no-store"
response
[
'Last-Modified'
]
=
content
.
last_modified_at
.
strftime
(
HTTP_DATE_FORMAT
)
response
[
'Last-Modified'
]
=
content
.
last_modified_at
.
strftime
(
HTTP_DATE_FORMAT
)
...
@@ -181,38 +159,18 @@ class StaticContentServer(object):
...
@@ -181,38 +159,18 @@ class StaticContentServer(object):
force_header_for_response
(
response
,
'Vary'
,
'Origin'
)
force_header_for_response
(
response
,
'Vary'
,
'Origin'
)
@staticmethod
@staticmethod
def
is_cdn_request
(
request
):
"""
Attempts to determine whether or not the given request is coming from a CDN.
Currently, this is a static check because edx.org only uses CloudFront, but may
be expanded in the future.
"""
cdn_user_agents
=
CdnUserAgentsConfig
.
get_cdn_user_agents
()
user_agent
=
request
.
META
.
get
(
'HTTP_USER_AGENT'
,
''
)
if
user_agent
in
cdn_user_agents
:
# This is a CDN request.
return
True
return
False
@staticmethod
def
get_expiration_value
(
now
,
cache_ttl
):
def
get_expiration_value
(
now
,
cache_ttl
):
"""Generates an RFC1123 datetime string based on a future offset."""
"""Generates an RFC1123 datetime string based on a future offset."""
expire_dt
=
now
+
datetime
.
timedelta
(
seconds
=
cache_ttl
)
expire_dt
=
now
+
datetime
.
timedelta
(
seconds
=
cache_ttl
)
return
expire_dt
.
strftime
(
HTTP_DATE_FORMAT
)
return
expire_dt
.
strftime
(
HTTP_DATE_FORMAT
)
def
is_content_locked
(
self
,
content
):
"""
Determines whether or not the given content is locked.
"""
return
getattr
(
content
,
"locked"
,
False
)
def
is_user_authorized
(
self
,
request
,
content
,
location
):
def
is_user_authorized
(
self
,
request
,
content
,
location
):
"""
"""
Determines whether or not the user for this request is authorized to view the given asset.
Determines whether or not the user for this request is authorized to view the given asset.
"""
"""
if
not
self
.
is_content_locked
(
content
):
is_locked
=
getattr
(
content
,
"locked"
,
False
)
if
not
is_locked
:
return
True
return
True
if
not
hasattr
(
request
,
"user"
)
or
not
request
.
user
.
is_authenticated
():
if
not
hasattr
(
request
,
"user"
)
or
not
request
.
user
.
is_authenticated
():
...
...
common/djangoapps/contentserver/migrations/0002_cdnuseragentsconfig.py
deleted
100644 → 0
View file @
8f25c81a
# -*- coding: utf-8 -*-
from
__future__
import
unicode_literals
from
django.db
import
migrations
,
models
import
django.db.models.deletion
from
django.conf
import
settings
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
migrations
.
swappable_dependency
(
settings
.
AUTH_USER_MODEL
),
(
'contentserver'
,
'0001_initial'
),
]
operations
=
[
migrations
.
CreateModel
(
name
=
'CdnUserAgentsConfig'
,
fields
=
[
(
'id'
,
models
.
AutoField
(
verbose_name
=
'ID'
,
serialize
=
False
,
auto_created
=
True
,
primary_key
=
True
)),
(
'change_date'
,
models
.
DateTimeField
(
auto_now_add
=
True
,
verbose_name
=
'Change date'
)),
(
'enabled'
,
models
.
BooleanField
(
default
=
False
,
verbose_name
=
'Enabled'
)),
(
'cdn_user_agents'
,
models
.
TextField
(
default
=
b
'Amazon CloudFront'
,
help_text
=
b
'A newline-separated list of user agents that should be considered CDNs.'
)),
(
'changed_by'
,
models
.
ForeignKey
(
on_delete
=
django
.
db
.
models
.
deletion
.
PROTECT
,
editable
=
False
,
to
=
settings
.
AUTH_USER_MODEL
,
null
=
True
,
verbose_name
=
'Changed by'
)),
],
),
]
common/djangoapps/contentserver/models.py
View file @
95fde6d5
...
@@ -2,7 +2,7 @@
...
@@ -2,7 +2,7 @@
Models for contentserver
Models for contentserver
"""
"""
from
django.db.models.fields
import
PositiveIntegerField
,
TextField
from
django.db.models.fields
import
PositiveIntegerField
from
config_models.models
import
ConfigurationModel
from
config_models.models
import
ConfigurationModel
...
@@ -27,26 +27,3 @@ class CourseAssetCacheTtlConfig(ConfigurationModel):
...
@@ -27,26 +27,3 @@ class CourseAssetCacheTtlConfig(ConfigurationModel):
def
__unicode__
(
self
):
def
__unicode__
(
self
):
return
unicode
(
repr
(
self
))
return
unicode
(
repr
(
self
))
class
CdnUserAgentsConfig
(
ConfigurationModel
):
"""Configuration for the user agents we expect to see from CDNs."""
class
Meta
(
object
):
app_label
=
'contentserver'
cdn_user_agents
=
TextField
(
default
=
'Amazon CloudFront'
,
help_text
=
"A newline-separated list of user agents that should be considered CDNs."
)
@classmethod
def
get_cdn_user_agents
(
cls
):
"""Gets the list of CDN user agents, if present"""
return
cls
.
current
()
.
cdn_user_agents
def
__repr__
(
self
):
return
'<WhitelistedCdnConfig(cdn_user_agents={})>'
.
format
(
self
.
get_cdn_user_agents
())
def
__unicode__
(
self
):
return
unicode
(
repr
(
self
))
common/djangoapps/contentserver/test/test_contentserver.py
View file @
95fde6d5
...
@@ -10,7 +10,6 @@ import unittest
...
@@ -10,7 +10,6 @@ import unittest
from
uuid
import
uuid4
from
uuid
import
uuid4
from
django.conf
import
settings
from
django.conf
import
settings
from
django.test
import
RequestFactory
from
django.test.client
import
Client
from
django.test.client
import
Client
from
django.test.utils
import
override_settings
from
django.test.utils
import
override_settings
from
mock
import
patch
from
mock
import
patch
...
@@ -272,49 +271,6 @@ class ContentStoreToyCourseTest(SharedModuleStoreTestCase):
...
@@ -272,49 +271,6 @@ class ContentStoreToyCourseTest(SharedModuleStoreTestCase):
near_expire_dt
=
StaticContentServer
.
get_expiration_value
(
start_dt
,
55
)
near_expire_dt
=
StaticContentServer
.
get_expiration_value
(
start_dt
,
55
)
self
.
assertEqual
(
"Thu, 01 Dec 1983 20:00:55 GMT"
,
near_expire_dt
)
self
.
assertEqual
(
"Thu, 01 Dec 1983 20:00:55 GMT"
,
near_expire_dt
)
@patch
(
'contentserver.models.CdnUserAgentsConfig.get_cdn_user_agents'
)
def
test_cache_is_cdn_with_normal_request
(
self
,
mock_get_cdn_user_agents
):
"""
Tests that when a normal request is made -- i.e. from an end user with their
browser -- that we don't classify the request as coming from a CDN.
"""
mock_get_cdn_user_agents
.
return_value
=
'Amazon CloudFront'
request_factory
=
RequestFactory
()
browser_request
=
request_factory
.
get
(
'/fake'
,
HTTP_USER_AGENT
=
'Chrome 1234'
)
is_from_cdn
=
StaticContentServer
.
is_cdn_request
(
browser_request
)
self
.
assertEqual
(
is_from_cdn
,
False
)
@patch
(
'contentserver.models.CdnUserAgentsConfig.get_cdn_user_agents'
)
def
test_cache_is_cdn_with_cdn_request
(
self
,
mock_get_cdn_user_agents
):
"""
Tests that when a CDN request is made -- i.e. from an edge node back to the
origin -- that we classify the request as coming from a CDN.
"""
mock_get_cdn_user_agents
.
return_value
=
'Amazon CloudFront'
request_factory
=
RequestFactory
()
browser_request
=
request_factory
.
get
(
'/fake'
,
HTTP_USER_AGENT
=
'Amazon CloudFront'
)
is_from_cdn
=
StaticContentServer
.
is_cdn_request
(
browser_request
)
self
.
assertEqual
(
is_from_cdn
,
True
)
@patch
(
'contentserver.models.CdnUserAgentsConfig.get_cdn_user_agents'
)
def
test_cache_is_cdn_with_cdn_request_multiple_user_agents
(
self
,
mock_get_cdn_user_agents
):
"""
Tests that when a CDN request is made -- i.e. from an edge node back to the
origin -- that we classify the request as coming from a CDN when multiple UAs
are configured.
"""
mock_get_cdn_user_agents
.
return_value
=
'Amazon CloudFront
\n
Akamai GHost'
request_factory
=
RequestFactory
()
browser_request
=
request_factory
.
get
(
'/fake'
,
HTTP_USER_AGENT
=
'Amazon CloudFront'
)
is_from_cdn
=
StaticContentServer
.
is_cdn_request
(
browser_request
)
self
.
assertEqual
(
is_from_cdn
,
True
)
@ddt.ddt
@ddt.ddt
class
ParseRangeHeaderTestCase
(
unittest
.
TestCase
):
class
ParseRangeHeaderTestCase
(
unittest
.
TestCase
):
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment