The request.body object should be available and readable in view code, even if the ASGIHandler cancels processing part way through.
(self)
| 295 | self.assertNotIn("HTTP_SOME_HEADER", request.META) |
| 296 | |
| 297 | async def test_cancel_post_request_with_sync_processing(self): |
| 298 | """ |
| 299 | The request.body object should be available and readable in view |
| 300 | code, even if the ASGIHandler cancels processing part way through. |
| 301 | """ |
| 302 | loop = asyncio.get_event_loop() |
| 303 | # Events to monitor the view processing from the parent test code. |
| 304 | view_started_event = asyncio.Event() |
| 305 | view_finished_event = asyncio.Event() |
| 306 | # Record received request body or exceptions raised in the test view |
| 307 | outcome = [] |
| 308 | |
| 309 | # This view will run in a new thread because it is wrapped in |
| 310 | # sync_to_async. The view consumes the POST body data after a short |
| 311 | # delay. The test will cancel the request using http.disconnect during |
| 312 | # the delay, but because this is a sync view the code runs to |
| 313 | # completion. There should be no exceptions raised inside the view |
| 314 | # code. |
| 315 | @csrf_exempt |
| 316 | @sync_to_async |
| 317 | def post_view(request): |
| 318 | try: |
| 319 | loop.call_soon_threadsafe(view_started_event.set) |
| 320 | time.sleep(0.1) |
| 321 | # Do something to read request.body after pause |
| 322 | outcome.append({"request_body": request.body}) |
| 323 | return HttpResponse("ok") |
| 324 | except Exception as e: |
| 325 | outcome.append({"exception": e}) |
| 326 | finally: |
| 327 | loop.call_soon_threadsafe(view_finished_event.set) |
| 328 | |
| 329 | # Request class to use the view. |
| 330 | class TestASGIRequest(ASGIRequest): |
| 331 | urlconf = (path("post/", post_view),) |
| 332 | |
| 333 | # Handler to use request class. |
| 334 | class TestASGIHandler(ASGIHandler): |
| 335 | request_class = TestASGIRequest |
| 336 | |
| 337 | application = TestASGIHandler() |
| 338 | scope = self.async_request_factory._base_scope( |
| 339 | method="POST", |
| 340 | path="/post/", |
| 341 | ) |
| 342 | communicator = ApplicationCommunicator(application, scope) |
| 343 | |
| 344 | await communicator.send_input({"type": "http.request", "body": b"Body data!"}) |
| 345 | |
| 346 | # Wait until the view code has started, then send http.disconnect. |
| 347 | await view_started_event.wait() |
| 348 | await communicator.send_input({"type": "http.disconnect"}) |
| 349 | # Wait until view code has finished. |
| 350 | await view_finished_event.wait() |
| 351 | with self.assertRaises(asyncio.TimeoutError): |
| 352 | await communicator.receive_output() |
| 353 | |
| 354 | self.assertEqual(outcome, [{"request_body": b"Body data!"}]) |
nothing calls this directly
no test coverage detected