13
13
from django .contrib .auth .models import User
14
14
from django .contrib .sessions .middleware import SessionMiddleware
15
15
from django .core .files .uploadedfile import SimpleUploadedFile
16
+ from django .http .request import RawPostDataException
16
17
from django .test import TestCase , override_settings
17
18
from django .utils import six
18
19
@@ -137,6 +138,11 @@ def post(self, request):
137
138
return Response (status = status .HTTP_500_INTERNAL_SERVER_ERROR )
138
139
139
140
141
+ class EchoView (APIView ):
142
+ def post (self , request ):
143
+ return Response (status = status .HTTP_200_OK , data = request .data )
144
+
145
+
140
146
class FileUploadView (APIView ):
141
147
def post (self , request ):
142
148
filenames = [file .temporary_file_path () for file in request .FILES .values ()]
@@ -149,6 +155,7 @@ def post(self, request):
149
155
150
156
urlpatterns = [
151
157
url (r'^$' , MockView .as_view ()),
158
+ url (r'^echo/$' , EchoView .as_view ()),
152
159
url (r'^upload/$' , FileUploadView .as_view ())
153
160
]
154
161
@@ -271,24 +278,64 @@ def test_default_secure_true(self):
271
278
assert request .scheme == 'https'
272
279
273
280
274
- class TestWSGIRequestProxy (TestCase ):
275
- def test_attribute_access (self ):
276
- wsgi_request = factory .get ('/' )
277
- request = Request (wsgi_request )
281
+ class TestHttpRequest (TestCase ):
282
+ def test_attribute_access_proxy (self ):
283
+ http_request = factory .get ('/' )
284
+ request = Request (http_request )
278
285
279
286
inner_sentinel = object ()
280
- wsgi_request .inner_property = inner_sentinel
287
+ http_request .inner_property = inner_sentinel
281
288
assert request .inner_property is inner_sentinel
282
289
283
290
outer_sentinel = object ()
284
291
request .inner_property = outer_sentinel
285
292
assert request .inner_property is outer_sentinel
286
293
287
- def test_exception (self ):
294
+ def test_exception_proxy (self ):
288
295
# ensure the exception message is not for the underlying WSGIRequest
289
- wsgi_request = factory .get ('/' )
290
- request = Request (wsgi_request )
296
+ http_request = factory .get ('/' )
297
+ request = Request (http_request )
291
298
292
299
message = "'Request' object has no attribute 'inner_property'"
293
300
with self .assertRaisesMessage (AttributeError , message ):
294
301
request .inner_property
302
+
303
+ @override_settings (ROOT_URLCONF = 'tests.test_request' )
304
+ def test_duplicate_request_stream_parsing_exception (self ):
305
+ """
306
+ Check assumption that duplicate stream parsing will result in a
307
+ `RawPostDataException` being raised.
308
+ """
309
+ response = APIClient ().post ('/echo/' , data = {'a' : 'b' }, format = 'json' )
310
+ request = response .renderer_context ['request' ]
311
+
312
+ # ensure that request stream was consumed by json parser
313
+ assert request .content_type .startswith ('application/json' )
314
+ assert response .data == {'a' : 'b' }
315
+
316
+ # pass same HttpRequest to view, stream already consumed
317
+ with pytest .raises (RawPostDataException ):
318
+ EchoView .as_view ()(request ._request )
319
+
320
+ @override_settings (ROOT_URLCONF = 'tests.test_request' )
321
+ def test_duplicate_request_form_data_access (self ):
322
+ """
323
+ Form data is copied to the underlying django request for middleware
324
+ and file closing reasons. Duplicate processing of a request with form
325
+ data is 'safe' in so far as accessing `request.POST` does not trigger
326
+ the duplicate stream parse exception.
327
+ """
328
+ response = APIClient ().post ('/echo/' , data = {'a' : 'b' })
329
+ request = response .renderer_context ['request' ]
330
+
331
+ # ensure that request stream was consumed by form parser
332
+ assert request .content_type .startswith ('multipart/form-data' )
333
+ assert response .data == {'a' : ['b' ]}
334
+
335
+ # pass same HttpRequest to view, form data set on underlying request
336
+ response = EchoView .as_view ()(request ._request )
337
+ request = response .renderer_context ['request' ]
338
+
339
+ # ensure that request stream was consumed by form parser
340
+ assert request .content_type .startswith ('multipart/form-data' )
341
+ assert response .data == {'a' : ['b' ]}
0 commit comments