Repository: sanic-org/sanic

Issue:
Websocket disconnection and server task pending error
**Describe the bug**
I am a complete newbie trying to use sanic with websocket. I was following the tutorial of websockets in sanic document, and by the time I disconnected the websocket connection I found this in the console.
```
[2022-05-23 19:43:15 +0800] [14879] [ERROR] Exception occurred while handling uri: 'ws://10.215.220.4:4017/test/'
Traceback (most recent call last):
  File "/home/yuzixin/workspace/sanicserver/venv/lib/python3.7/site-packages/sanic/app.py", line 994, in _websocket_handler
    await fut
concurrent.futures._base.CancelledError
```
followed by this error message when the server was shut down.
```
Task was destroyed but it is pending!
task: <Task pending coro=<WebsocketFrameAssembler.get() done, defined at /home/yuzixin/workspace/sanicserver/venv/lib/python3.7/site-packages/sanic/server/websockets/frame.py:91> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x7ff6e9bafa98>()]> cb=[_wait.<locals>._on_completion() at /usr/lib/python3.7/asyncio/tasks.py:440]>
```
So I checked the lines where error was triggered and found this at sanic/app.py line 993~999:
```python
        try:
            await fut
        except Exception as e:
            self.error_handler.log(request, e)
        except (CancelledError, ConnectionClosed):
            cancelled = True
        finally:
...
```
It seemed like the CancelledError exception would never be reached.
I tried switching the two exceptions and it all worked fine. Wondering if the modification is correct, or the order of the exceptions were just meant to be.

**Code snippet**
```python
from sanic import Sanic

app = Sanic("MyHelloWorldApp")


@app.websocket("/test")
async def handler(request, ws):
    while True:
        message = await ws.recv()
        await ws.send(message)
```


**Expected behavior**
Expected no errors at disconnect behavior.


**Environment (please complete the following information):**
 - OS: Debian
 - Version 10



Discussion:
Hi @jrayu 
~What version of Python are you using?~
I see you are using Python v3.7. 

In Python v3.8 and newer, `CancelledError` is a subclass of `BaseException`, not `Exception` so it should not be caught by the first Exception handler. I think all of our testing on the new websockets implementation was done in Python 3.8.

Having said that, I know Sanic should still be Python 3.7 compatible, so this is still a bug, and those two lines could certainly be swapped around without hurting anything.
> Hi @jrayu What version of Python are you using?
> 
> In python v3.8 and newer, `CancelledError` is a subclass of `BaseException`, not `Exception` so it should not be caught by the first Exception handler.
> 
> Having said that, I know Sanic should still be Python 3.7 compatible, so this is still a bug, and those two lines could certainly be swapped around without hurting anything.

Thanks for the reply. I am using python3.7 as you have guessed. I will try to upgrade it and see if it works.


Resolving patch:
diff --git a/sanic/app.py b/sanic/app.py
--- a/sanic/app.py
+++ b/sanic/app.py
@@ -992,10 +992,10 @@ async def _websocket_handler(
         cancelled = False
         try:
             await fut
-        except Exception as e:
-            self.error_handler.log(request, e)
         except (CancelledError, ConnectionClosed):
             cancelled = True
+        except Exception as e:
+            self.error_handler.log(request, e)
         finally:
             self.websocket_tasks.remove(fut)
             if cancelled:


Test patch: