Rename 'clean_headers' to 'header_control'.
This is part of adding the ability to forcefully set headers through the middleware in addition to removing specific headers.
This commit is contained in:
@@ -1,15 +0,0 @@
|
||||
"""
|
||||
This middleware is used for cleaning headers from a response before it is sent to the end user.
|
||||
|
||||
Due to the nature of how middleware runs, a piece of middleware high in the chain cannot ensure
|
||||
that response headers won't be present on the final response body, as middleware further down
|
||||
the chain could be adding them.
|
||||
|
||||
This middleware is intended to sit as close as possible to the top of the list, so that it has
|
||||
a chance on the reponse going out to strip the intended headers.
|
||||
"""
|
||||
|
||||
|
||||
def remove_headers_from_response(response, *headers):
|
||||
"""Removes the given headers from the response using the clean_headers middleware."""
|
||||
response.clean_headers = headers
|
||||
@@ -1,36 +0,0 @@
|
||||
"""
|
||||
Middleware decorator for removing headers.
|
||||
"""
|
||||
|
||||
from functools import wraps
|
||||
|
||||
|
||||
def clean_headers(*headers):
|
||||
"""
|
||||
Decorator that removes any headers specified from the response.
|
||||
Usage:
|
||||
@clean_headers("Vary")
|
||||
def myview(request):
|
||||
...
|
||||
|
||||
The CleanHeadersMiddleware must be used and placed as closely as possible to the top
|
||||
of the middleware chain, ideally after any caching middleware but before everything else.
|
||||
|
||||
This decorator is not safe for multiple uses: each call will overwrite any previously set values.
|
||||
"""
|
||||
def _decorator(func):
|
||||
"""
|
||||
Decorates the given function.
|
||||
"""
|
||||
@wraps(func)
|
||||
def _inner(*args, **kwargs):
|
||||
"""
|
||||
Alters the response.
|
||||
"""
|
||||
response = func(*args, **kwargs)
|
||||
response.clean_headers = headers
|
||||
return response
|
||||
|
||||
return _inner
|
||||
|
||||
return _decorator
|
||||
@@ -1,25 +0,0 @@
|
||||
"""
|
||||
Middleware used for cleaning headers from a response before it is sent to the end user.
|
||||
"""
|
||||
|
||||
|
||||
class CleanHeadersMiddleware(object):
|
||||
"""
|
||||
Middleware that can drop headers present in a response.
|
||||
|
||||
This can be used, for example, to remove headers i.e. drop any Vary headers to improve cache performance.
|
||||
"""
|
||||
|
||||
def process_response(self, _request, response):
|
||||
"""
|
||||
Processes the given response, potentially stripping out any unwanted headers.
|
||||
"""
|
||||
|
||||
if len(getattr(response, 'clean_headers', [])) > 0:
|
||||
for header in response.clean_headers:
|
||||
try:
|
||||
del response[header]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
return response
|
||||
@@ -1,20 +0,0 @@
|
||||
"""Tests for clean_headers decorator. """
|
||||
from django.http import HttpResponse, HttpRequest
|
||||
from django.test import TestCase
|
||||
from clean_headers.decorators import clean_headers
|
||||
|
||||
|
||||
def fake_view(_request):
|
||||
"""Fake view that returns an empty response."""
|
||||
return HttpResponse()
|
||||
|
||||
|
||||
class TestCleanHeaders(TestCase):
|
||||
"""Test the `clean_headers` decorator."""
|
||||
|
||||
def test_clean_headers(self):
|
||||
request = HttpRequest()
|
||||
wrapper = clean_headers('Vary', 'Accept-Encoding')
|
||||
wrapped_view = wrapper(fake_view)
|
||||
response = wrapped_view(request)
|
||||
self.assertEqual(len(response.clean_headers), 2)
|
||||
@@ -1,34 +0,0 @@
|
||||
"""Tests for clean_headers middleware."""
|
||||
from django.http import HttpResponse, HttpRequest
|
||||
from django.test import TestCase
|
||||
from clean_headers.middleware import CleanHeadersMiddleware
|
||||
|
||||
|
||||
class TestCleanHeadersMiddlewareProcessResponse(TestCase):
|
||||
"""Test the `clean_headers` middleware. """
|
||||
def setUp(self):
|
||||
super(TestCleanHeadersMiddlewareProcessResponse, self).setUp()
|
||||
self.middleware = CleanHeadersMiddleware()
|
||||
|
||||
def test_cleans_intended_headers(self):
|
||||
fake_request = HttpRequest()
|
||||
|
||||
fake_response = HttpResponse()
|
||||
fake_response['Vary'] = 'Cookie'
|
||||
fake_response['Accept-Encoding'] = 'gzip'
|
||||
fake_response.clean_headers = ['Vary']
|
||||
|
||||
result = self.middleware.process_response(fake_request, fake_response)
|
||||
self.assertNotIn('Vary', result)
|
||||
self.assertEquals('gzip', result['Accept-Encoding'])
|
||||
|
||||
def test_does_not_mangle_undecorated_response(self):
|
||||
fake_request = HttpRequest()
|
||||
|
||||
fake_response = HttpResponse()
|
||||
fake_response['Vary'] = 'Cookie'
|
||||
fake_response['Accept-Encoding'] = 'gzip'
|
||||
|
||||
result = self.middleware.process_response(fake_request, fake_response)
|
||||
self.assertEquals('Cookie', result['Vary'])
|
||||
self.assertEquals('gzip', result['Accept-Encoding'])
|
||||
21
common/djangoapps/header_control/__init__.py
Normal file
21
common/djangoapps/header_control/__init__.py
Normal file
@@ -0,0 +1,21 @@
|
||||
"""
|
||||
This middleware is used for adjusting the headers in a response before it is sent to the end user.
|
||||
|
||||
This middleware is intended to sit as close as possible to the top of the middleare list as possible,
|
||||
so that it is one of the last pieces of middleware to touch the response, and thus can most accurately
|
||||
adjust/control the headers of the response.
|
||||
"""
|
||||
|
||||
|
||||
def remove_headers_from_response(response, *headers):
|
||||
"""Removes the given headers from the response using the header_control middleware."""
|
||||
response.remove_headers = headers
|
||||
|
||||
def force_header_for_response(response, header, value):
|
||||
"""Forces the given header for the given response using the header_control middleware."""
|
||||
force_headers = {}
|
||||
if hasattr(response, 'force_headers'):
|
||||
force_headers = response.force_headers
|
||||
force_headers[header] = value
|
||||
|
||||
response.force_headers = force_headers
|
||||
67
common/djangoapps/header_control/decorators.py
Normal file
67
common/djangoapps/header_control/decorators.py
Normal file
@@ -0,0 +1,67 @@
|
||||
"""
|
||||
Middleware decorator for removing headers.
|
||||
"""
|
||||
|
||||
from functools import wraps
|
||||
from header_control import remove_headers_from_response, force_header_for_response
|
||||
|
||||
def remove_headers(*headers):
|
||||
"""
|
||||
Decorator that removes specific headers from the response.
|
||||
Usage:
|
||||
@remove_headers("Vary")
|
||||
def myview(request):
|
||||
...
|
||||
|
||||
The HeaderControlMiddleware must be used and placed as closely as possible to the top
|
||||
of the middleware chain, ideally after any caching middleware but before everything else.
|
||||
|
||||
This decorator is not safe for multiple uses: each call will overwrite any previously set values.
|
||||
"""
|
||||
def _decorator(func):
|
||||
"""
|
||||
Decorates the given function.
|
||||
"""
|
||||
@wraps(func)
|
||||
def _inner(*args, **kwargs):
|
||||
"""
|
||||
Alters the response.
|
||||
"""
|
||||
response = func(*args, **kwargs)
|
||||
remove_headers_from_response(response, *headers)
|
||||
return response
|
||||
|
||||
return _inner
|
||||
|
||||
return _decorator
|
||||
|
||||
|
||||
def force_header(header, value):
|
||||
"""
|
||||
Decorator that forces a header in the response to have a specific value.
|
||||
Usage:
|
||||
@force_header("Vary", "Origin")
|
||||
def myview(request):
|
||||
...
|
||||
|
||||
The HeaderControlMiddleware must be used and placed as closely as possible to the top
|
||||
of the middleware chain, ideally after any caching middleware but before everything else.
|
||||
|
||||
This decorator is not safe for multiple uses: each call will overwrite any previously set values.
|
||||
"""
|
||||
def _decorator(func):
|
||||
"""
|
||||
Decorates the given function.
|
||||
"""
|
||||
@wraps(func)
|
||||
def _inner(*args, **kwargs):
|
||||
"""
|
||||
Alters the response.
|
||||
"""
|
||||
response = func(*args, **kwargs)
|
||||
force_header_for_response(response, header, value)
|
||||
return response
|
||||
|
||||
return _inner
|
||||
|
||||
return _decorator
|
||||
34
common/djangoapps/header_control/middleware.py
Normal file
34
common/djangoapps/header_control/middleware.py
Normal file
@@ -0,0 +1,34 @@
|
||||
"""
|
||||
Middleware used for adjusting headers in a response before it is sent to the end user.
|
||||
"""
|
||||
|
||||
|
||||
class HeaderControlMiddleware(object):
|
||||
"""
|
||||
Middleware that can modify/remove headers in a response.
|
||||
|
||||
This can be used, for example, to remove headers i.e. drop any Vary headers to improve cache performance.
|
||||
"""
|
||||
|
||||
def process_response(self, _request, response):
|
||||
"""
|
||||
Processes the given response, potentially remove or modifying headers.
|
||||
"""
|
||||
|
||||
if len(getattr(response, 'remove_headers', [])) > 0:
|
||||
for header in response.remove_headers:
|
||||
try:
|
||||
del response[header]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
if len(getattr(response, 'force_headers', {})) > 0:
|
||||
for header, value in response.force_headers.iteritems():
|
||||
try:
|
||||
del response[header]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
response[header] = value
|
||||
|
||||
return response
|
||||
32
common/djangoapps/header_control/tests/test_decorators.py
Normal file
32
common/djangoapps/header_control/tests/test_decorators.py
Normal file
@@ -0,0 +1,32 @@
|
||||
"""Tests for remove_headers and force_header decorator. """
|
||||
from django.http import HttpResponse, HttpRequest
|
||||
from django.test import TestCase
|
||||
from header_control.decorators import remove_headers, force_header
|
||||
|
||||
|
||||
def fake_view(_request):
|
||||
"""Fake view that returns an empty response."""
|
||||
return HttpResponse()
|
||||
|
||||
|
||||
class TestRemoveHeaders(TestCase):
|
||||
"""Test the `remove_headers` decorator."""
|
||||
|
||||
def test_remove_headers(self):
|
||||
request = HttpRequest()
|
||||
wrapper = remove_headers('Vary', 'Accept-Encoding')
|
||||
wrapped_view = wrapper(fake_view)
|
||||
response = wrapped_view(request)
|
||||
self.assertEqual(len(response.remove_headers), 2)
|
||||
|
||||
|
||||
class TestForceHeader(TestCase):
|
||||
"""Test the `force_header` decorator."""
|
||||
|
||||
def test_force_header(self):
|
||||
request = HttpRequest()
|
||||
wrapper = force_header('Vary', 'Origin')
|
||||
wrapped_view = wrapper(fake_view)
|
||||
response = wrapped_view(request)
|
||||
self.assertEqual(len(response.force_headers), 1)
|
||||
self.assertEqual(response.force_headers['Vary'], 'Origin')
|
||||
47
common/djangoapps/header_control/tests/test_middleware.py
Normal file
47
common/djangoapps/header_control/tests/test_middleware.py
Normal file
@@ -0,0 +1,47 @@
|
||||
"""Tests for header_control middleware."""
|
||||
from django.http import HttpResponse, HttpRequest
|
||||
from django.test import TestCase
|
||||
from header_control import remove_headers_from_response, force_header_for_response
|
||||
from header_control.middleware import HeaderControlMiddleware
|
||||
|
||||
|
||||
class TestHeaderControlMiddlewareProcessResponse(TestCase):
|
||||
"""Test the `header_control` middleware. """
|
||||
def setUp(self):
|
||||
super(TestHeaderControlMiddlewareProcessResponse, self).setUp()
|
||||
self.middleware = HeaderControlMiddleware()
|
||||
|
||||
def test_removes_intended_headers(self):
|
||||
fake_request = HttpRequest()
|
||||
|
||||
fake_response = HttpResponse()
|
||||
fake_response['Vary'] = 'Cookie'
|
||||
fake_response['Accept-Encoding'] = 'gzip'
|
||||
remove_headers_from_response(fake_response, 'Vary')
|
||||
|
||||
result = self.middleware.process_response(fake_request, fake_response)
|
||||
self.assertNotIn('Vary', result)
|
||||
self.assertEquals('gzip', result['Accept-Encoding'])
|
||||
|
||||
def test_forces_intended_header(self):
|
||||
fake_request = HttpRequest()
|
||||
|
||||
fake_response = HttpResponse()
|
||||
fake_response['Vary'] = 'Cookie'
|
||||
fake_response['Accept-Encoding'] = 'gzip'
|
||||
force_header_for_response(fake_response, 'Vary', 'Origin')
|
||||
|
||||
result = self.middleware.process_response(fake_request, fake_response)
|
||||
self.assertEquals('Origin', result['Vary'])
|
||||
self.assertEquals('gzip', result['Accept-Encoding'])
|
||||
|
||||
def test_does_not_mangle_undecorated_response(self):
|
||||
fake_request = HttpRequest()
|
||||
|
||||
fake_response = HttpResponse()
|
||||
fake_response['Vary'] = 'Cookie'
|
||||
fake_response['Accept-Encoding'] = 'gzip'
|
||||
|
||||
result = self.middleware.process_response(fake_request, fake_response)
|
||||
self.assertEquals('Cookie', result['Vary'])
|
||||
self.assertEquals('gzip', result['Accept-Encoding'])
|
||||
@@ -1087,7 +1087,7 @@ simplefilter('ignore')
|
||||
|
||||
MIDDLEWARE_CLASSES = (
|
||||
'request_cache.middleware.RequestCache',
|
||||
'clean_headers.middleware.CleanHeadersMiddleware',
|
||||
'header_control.middleware.HeaderControlMiddleware',
|
||||
'microsite_configuration.middleware.MicrositeMiddleware',
|
||||
'django_comment_client.middleware.AjaxExceptionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
|
||||
Reference in New Issue
Block a user