Repository: django/django
File: django/core/handlers/asgi.py (Python)

Code before:
            await signals.request_started.asend(sender=self.__class__, scope=scope)
            # Get the request and check for basic issues.
            request, error_response = self.create_request(scope, body_file)
            if request is None:
                body_file.close()
                await self.send_response(error_response, send)
                await sync_to_async(error_response.close)()
                return

            class RequestProcessed(Exception):
                pass

            response = None
            try:
                try:
                    async with asyncio.TaskGroup() as tg:
                        tg.create_task(self.listen_for_disconnect(receive))
                        response = await self.run_get_response(request)
                        await self.send_response(response, send)
                        raise RequestProcessed
                except* (RequestProcessed, RequestAborted):
                    pass
            except BaseExceptionGroup as exception_group:
                if len(exception_group.exceptions) == 1:
                    raise exception_group.exceptions[0]
                raise

            if response is None:
                await signals.request_finished.asend(sender=self.__class__)
            else:
                await sync_to_async(response.close)()

    async def listen_for_disconnect(self, receive):
        """Listen for disconnect from the client."""
        message = await receive()
        if message["type"] == "http.disconnect":
            raise RequestAborted()
        # This should never happen.
        assert False, "Invalid ASGI message after request body: %s" % message["type"]

    async def run_get_response(self, request):
        """Get async response."""
        # Use the async mode of BaseHandler.
        response = await self.get_response_async(request)
        response._handler_class = self.__class__
        # Increase chunk size on file responses (ASGI servers handles low-level
        # chunking).
        if isinstance(response, FileResponse):
            response.block_size = self.chunk_size
        return response


Review comment (suggestion):
Wouldn't simply cancelling the background task achieve the same result, without triggering the exception handling mechanism. It would also make this code more readable, since it seems like the exception is being raised only to cancel the task.

```python
                    async with asyncio.TaskGroup() as tg:
                        disconnect_task = tg.create_task(self.listen_for_disconnect(receive))
                        response = await self.run_get_response(request)
                        await self.send_response(response, send)
                        disconnect_task.cancel()
                except* RequestAborted:
                    pass
```

Code after:

        with closing(body_file):
            # Request is complete and can be served.
            set_script_prefix(get_script_prefix(scope))
            await signals.request_started.asend(sender=self.__class__, scope=scope)
            # Get the request and check for basic issues.
            request, error_response = self.create_request(scope, body_file)
            if request is None:
                body_file.close()
                await self.send_response(error_response, send)
                await sync_to_async(error_response.close)()
                return

            class RequestProcessed(Exception):
                pass

            response = None
            try:
                try:
                    async with asyncio.TaskGroup() as tg:
                        tg.create_task(self.listen_for_disconnect(receive))
                        response = await self.run_get_response(request)
                        await self.send_response(response, send)
                        raise RequestProcessed
                except* (RequestProcessed, RequestAborted):
                    pass
            except BaseExceptionGroup as exception_group:
                if len(exception_group.exceptions) == 1:
                    raise exception_group.exceptions[0]
                raise

            if response is None:
                await signals.request_finished.asend(sender=self.__class__)
            else:
                await sync_to_async(response.close)()

    async def listen_for_disconnect(self, receive):
        """Listen for disconnect from the client."""
        message = await receive()
        if message["type"] == "http.disconnect":
            raise RequestAborted()
        # This should never happen.
        assert False, "Invalid ASGI message after request body: %s" % message["type"]

    async def run_get_response(self, request):
        """Get async response."""
        # Use the async mode of BaseHandler.
        response = await self.get_response_async(request)
        response._handler_class = self.__class__
        # Increase chunk size on file responses (ASGI servers handles low-level
        # chunking).