Repository: sanic-org/sanic

Issue:
When calling abort() in a stream=True handler, error occurs
**Describe the bug**

When calling abort() inside a request handler with stream=True, this exception occurs in the logs:
```
Task exception was never retrieved
future: <Task finished name='Task-3' coro=<HttpProtocol.body_append() done, defined at /usr/local/lib/python3.8/site-packages/sanic/server.py:356> exception=AttributeError("'NoneType' object has no attribute 'stream'")>
Traceback (most recent call last):
  File "/usr/local/lib/python3.8/site-packages/sanic/server.py", line 357, in body_append
    if self.request.stream.is_full():
AttributeError: 'NoneType' object has no attribute 'stream'
```
**Code snippet**

```
import cgi

from sanic import Sanic
from sanic.response import stream
from sanic.exceptions import abort

app = Sanic('request_stream')


@app.put('/stream', stream=True)
async def handler(request):
    mimetype, options = cgi.parse_header(request.headers['content-type'])
    charset = options.get('charset')

    if charset not in {'utf-8'} and mimetype != 'text/plain':
        abort(400, f'Content-Type must be "text/plain; charset=utf-8')

    async def streaming(response):
        while (body := await request.stream.read()) is not None:
            body = body.decode(charset).upper()
            await response.write(body)

    return stream(streaming, content_type="text/plain; charset=utf-8")
```


**Expected behavior**
On the client side, behavior is as expected, but it seems that request is None in this case.  It should not be


**Environment (please complete the following information):**
Python3.8 official docker image
Sanic 19.9.0

**Additional context**
/



Discussion:
@djoek We still haven't officially started supporting `sanic` on python 3.8. There is a PR opened to introduce this via #1709 

Is this behavior the same with  python3.7  + sanic as well?
@harshanarayana yes, same behavior:

```
Task exception was never retrieved
future: <Task finished coro=<HttpProtocol.body_append() done, defined at /usr/local/lib/python3.7/site-packages/sanic/server.py:356> exception=AttributeError("'NoneType' object has no attribute 'stream'")>
Traceback (most recent call last):
  File "/usr/local/lib/python3.7/site-packages/sanic/server.py", line 357, in body_append
    if self.request.stream.is_full():
AttributeError: 'NoneType' object has no attribute 'stream'
```

using 3.7 and replacing walrus with:

```
    async def streaming(response):
        while True:
            body = await request.stream.read()
            if body is None:
                break
            body = body.decode(charset).upper()
            await response.write(body)
```

probably can take a look over weekend

Resolving patch:
diff --git a/sanic/server.py b/sanic/server.py
--- a/sanic/server.py
+++ b/sanic/server.py
@@ -364,6 +364,21 @@ def on_body(self, body):
         else:
             self.request.body_push(body)
 
+    async def body_append(self, body):
+        if (
+            self.request is None
+            or self._request_stream_task is None
+            or self._request_stream_task.cancelled()
+        ):
+            return
+
+        if self.request.stream.is_full():
+            self.transport.pause_reading()
+            await self.request.stream.put(body)
+            self.transport.resume_reading()
+        else:
+            await self.request.stream.put(body)
+
     async def stream_append(self):
         while self._body_chunks:
             body = self._body_chunks.popleft()


Test patch: