Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #16004 - csrf_protect does not send cookie if view returns Temp…

…lateResponse

The root bug was in decorator_from_middleware, and the fix also corrects
bugs with gzip_page and other decorators.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@16276 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit a482cc0ba38febda15194dc121989eed3b6deec2 1 parent d7036e5
Luke Plant authored
2  django/core/handlers/base.py
@@ -133,7 +133,7 @@ def get_response(self, request):
133 133
                 if hasattr(response, 'render') and callable(response.render):
134 134
                     for middleware_method in self._template_response_middleware:
135 135
                         response = middleware_method(request, response)
136  
-                    response.render()
  136
+                    response = response.render()
137 137
 
138 138
             except http.Http404, e:
139 139
                 logger.warning('Not Found: %s' % request.path,
7  django/template/response.py
@@ -92,11 +92,14 @@ def render(self):
92 92
 
93 93
         Returns the baked response instance.
94 94
         """
  95
+        retval = self
95 96
         if not self._is_rendered:
96 97
             self._set_content(self.rendered_content)
97 98
             for post_callback in self._post_render_callbacks:
98  
-                post_callback(self)
99  
-        return self
  99
+                newretval = post_callback(retval)
  100
+                if newretval is not None:
  101
+                    retval = newretval
  102
+        return retval
100 103
 
101 104
     is_rendered = property(lambda self: self._is_rendered)
102 105
 
15  django/utils/decorators.py
@@ -95,10 +95,17 @@ def _wrapped_view(request, *args, **kwargs):
95 95
                         if result is not None:
96 96
                             return result
97 97
                     raise
98  
-                if hasattr(middleware, 'process_response'):
99  
-                    result = middleware.process_response(request, response)
100  
-                    if result is not None:
101  
-                        return result
  98
+                if hasattr(response, 'render') and callable(response.render):
  99
+                    if hasattr(middleware, 'process_template_response'):
  100
+                        response = middleware.process_template_response(request, response)
  101
+                    # Defer running of process_response until after the template
  102
+                    # has been rendered:
  103
+                    if hasattr(middleware, 'process_response'):
  104
+                        callback = lambda response: middleware.process_response(request, response)
  105
+                        response.add_post_render_callback(callback)
  106
+                else:
  107
+                    if hasattr(middleware, 'process_response'):
  108
+                        return middleware.process_response(request, response)
102 109
                 return response
103 110
             return _wrapped_view
104 111
         return _decorator
4  docs/ref/template-response.txt
@@ -119,6 +119,10 @@ Methods
119 119
     rendered :class:`~django.template.response.SimpleTemplateResponse`
120 120
     instance.
121 121
 
  122
+    If the callback returns a value that is not `None`, this will be
  123
+    used as the response instead of the original response object (and
  124
+    will be passed to the next post rendering callback etc.)
  125
+
122 126
 .. method:: SimpleTemplateResponse.render():
123 127
 
124 128
     Sets :attr:`response.content` to the result obtained by
67  tests/regressiontests/utils/decorators.py
... ...
@@ -1,5 +1,7 @@
1 1
 from django.http import HttpResponse
2 2
 from django.middleware.doc import XViewMiddleware
  3
+from django.template import Template, Context
  4
+from django.template.response import TemplateResponse
3 5
 from django.test import TestCase, RequestFactory
4 6
 from django.utils.decorators import decorator_from_middleware
5 7
 
@@ -19,6 +21,26 @@ def __call__(self, request):
19 21
 class_xview = xview_dec(ClassXView())
20 22
 
21 23
 
  24
+class FullMiddleware(object):
  25
+    def process_request(self, request):
  26
+        request.process_request_reached = True
  27
+
  28
+    def process_view(sef, request, view_func, view_args, view_kwargs):
  29
+        request.process_view_reached = True
  30
+
  31
+    def process_template_response(self, request, response):
  32
+        request.process_template_response_reached = True
  33
+        return response
  34
+
  35
+    def process_response(self, request, response):
  36
+        # This should never receive unrendered content.
  37
+        request.process_response_content = response.content
  38
+        request.process_response_reached = True
  39
+        return response
  40
+
  41
+full_dec = decorator_from_middleware(FullMiddleware)
  42
+
  43
+
22 44
 class DecoratorFromMiddlewareTests(TestCase):
23 45
     """
24 46
     Tests for view decorators created using
@@ -37,3 +59,48 @@ def test_callable_process_view_middleware(self):
37 59
         Test a middleware that implements process_view, operating on a callable class.
38 60
         """
39 61
         class_xview(self.rf.get('/'))
  62
+
  63
+    def test_full_dec_normal(self):
  64
+        """
  65
+        Test that all methods of middleware are called for normal HttpResponses
  66
+        """
  67
+
  68
+        @full_dec
  69
+        def normal_view(request):
  70
+            t = Template("Hello world")
  71
+            return HttpResponse(t.render(Context({})))
  72
+
  73
+        request = self.rf.get('/')
  74
+        response = normal_view(request)
  75
+        self.assertTrue(getattr(request, 'process_request_reached', False))
  76
+        self.assertTrue(getattr(request, 'process_view_reached', False))
  77
+        # process_template_response must not be called for HttpResponse
  78
+        self.assertFalse(getattr(request, 'process_template_response_reached', False))
  79
+        self.assertTrue(getattr(request, 'process_response_reached', False))
  80
+
  81
+    def test_full_dec_templateresponse(self):
  82
+        """
  83
+        Test that all methods of middleware are called for TemplateResponses in
  84
+        the right sequence.
  85
+        """
  86
+
  87
+        @full_dec
  88
+        def template_response_view(request):
  89
+            t = Template("Hello world")
  90
+            return TemplateResponse(request, t, {})
  91
+
  92
+        request = self.rf.get('/')
  93
+        response = template_response_view(request)
  94
+        self.assertTrue(getattr(request, 'process_request_reached', False))
  95
+        self.assertTrue(getattr(request, 'process_view_reached', False))
  96
+        self.assertTrue(getattr(request, 'process_template_response_reached', False))
  97
+        # response must not be rendered yet.
  98
+        self.assertFalse(response._is_rendered)
  99
+        # process_response must not be called until after response is rendered,
  100
+        # otherwise some decorators like csrf_protect and gzip_page will not
  101
+        # work correctly. See #16004
  102
+        self.assertFalse(getattr(request, 'process_response_reached', False))
  103
+        response.render()
  104
+        self.assertTrue(getattr(request, 'process_response_reached', False))
  105
+        # Check that process_response saw the rendered content
  106
+        self.assertEqual(request.process_response_content, "Hello world")

0 notes on commit a482cc0

Please sign in to comment.
Something went wrong with that request. Please try again.