Repository: sanic-org/sanic

Issue:
There is an obvious bug in ASGI WebsocketConnection of Sanic
### Is there an existing issue for this?

- [X] I have searched the existing issues

### Describe the bug

I started my sanic app with UvicornWorker. The original websocket will become WebsocketConnection. When I call
the ws.recv function will report an error if bytes data is received at this time.
`KeyError：‘text’`
[https://github.com/sanic-org/sanic/blob/main/sanic/server/websockets/connection.py](url)
`    async def recv(self, *args, **kwargs) -> Optional[str]:
        message = await self._receive()

        if message["type"] == "websocket.receive":
            return message["text"]
        elif message["type"] == "websocket.disconnect":
            pass

        return None`
There is no data of bytes type processed here.

### Code snippet

_No response_

### Expected Behavior

_No response_

### How do you run Sanic?

ASGI

### Operating System

ubuntu

### Sanic Version

22.3

### Additional context

_No response_


Discussion:
I cannot reproduce this on `main`, or even on `22.3` using uvicorn.


```py
from sanic import Request, Sanic

app = Sanic("TestApp")


@app.websocket("/feed")
async def feed(request: Request, ws):
    while True:
        data = await ws.recv()
        await ws.send(data)
```

I am closing this now as I see no bug. If you have a reproducible snippet, please send.
> I cannot reproduce this on `main`, or even on `22.3` using uvicorn.
> 
> ```python
> from sanic import Request, Sanic
> 
> app = Sanic("TestApp")
> 
> 
> @app.websocket("/feed")
> async def feed(request: Request, ws):
>     while True:
>         data = await ws.recv()
>         await ws.send(data)
> ```
> 
> I am closing this now as I see no bug. If you have a reproducible snippet, please send.

### Is there an existing issue for this?

- [X] I have searched the existing issues

### Describe the bug

I started my sanic app with UvicornWorker. The original websocket will become WebsocketConnection. When I call
the ws.recv function will report an error if bytes data is received at this time.
`KeyError：‘text’`
[https://github.com/sanic-org/sanic/blob/main/sanic/server/websockets/connection.py](url)
`    async def recv(self, *args, **kwargs) -> Optional[str]:
        message = await self._receive()

        if message["type"] == "websocket.receive":
            return message["text"]
        elif message["type"] == "websocket.disconnect":
            pass

        return None`
There is no data of bytes type processed here.

### Code snippet

_No response_

### Expected Behavior

_No response_

### How do you run Sanic?

ASGI

### Operating System

ubuntu

### Sanic Version

22.3

### Additional context

_No response_

> I cannot reproduce this on `main`, or even on `22.3` using uvicorn.
> 
> ```python
> from sanic import Request, Sanic
> 
> app = Sanic("TestApp")
> 
> 
> @app.websocket("/feed")
> async def feed(request: Request, ws):
>     while True:
>         data = await ws.recv()
>         await ws.send(data)
> ```
> 
> I am closing this now as I see no bug. If you have a reproducible snippet, please send.

main.py
```
from sanic import Sanic

app = Sanic('ws')

@app.websocket('/feed')
async def feed(req,ws):
    while True:
        data =await ws.recv()
        print(data)
        if data is None:
            break

if __name__ == '__main__':
    import websockets
    import asyncio
    async def main():
        async with websockets.connect('ws://127.0.0.1::8889/feed') as ws:
            await ws.send('123')
            await ws.send(bytes(123))
    asyncio.get_event_loop().run_until_complete(main())
```
run: gunicorn  main:app --bind 0.0.0.0:8889 --worker-class uvicorn.workers.UvicornWorker
Nope. Still works fine for me. 
> Nope. Still works fine for me.

That's weird. However, you can look at this code in sanic, and it does seem to have a problem. It seems to only handle text, right?
[connection.py](https://github.com/sanic-org/sanic/blob/main/sanic/server/websockets/connection.py)
```
    async def recv(self, *args, **kwargs) -> Optional[str]:
        message = await self._receive()

        if message["type"] == "websocket.receive":
            return message["text"]
        elif message["type"] == "websocket.disconnect":
            pass

        return None
```
Yeah, will go back and look at the spec to see exactly what it's supposed to be.

Resolving patch:
diff --git a/sanic/server/websockets/connection.py b/sanic/server/websockets/connection.py
--- a/sanic/server/websockets/connection.py
+++ b/sanic/server/websockets/connection.py
@@ -9,8 +9,10 @@
     Union,
 )
 
+from sanic.exceptions import InvalidUsage
 
-ASIMessage = MutableMapping[str, Any]
+
+ASGIMessage = MutableMapping[str, Any]
 
 
 class WebSocketConnection:
@@ -25,8 +27,8 @@ class WebSocketConnection:
 
     def __init__(
         self,
-        send: Callable[[ASIMessage], Awaitable[None]],
-        receive: Callable[[], Awaitable[ASIMessage]],
+        send: Callable[[ASGIMessage], Awaitable[None]],
+        receive: Callable[[], Awaitable[ASGIMessage]],
         subprotocols: Optional[List[str]] = None,
     ) -> None:
         self._send = send
@@ -47,7 +49,13 @@ async def recv(self, *args, **kwargs) -> Optional[str]:
         message = await self._receive()
 
         if message["type"] == "websocket.receive":
-            return message["text"]
+            try:
+                return message["text"]
+            except KeyError:
+                try:
+                    return message["bytes"].decode()
+                except KeyError:
+                    raise InvalidUsage("Bad ASGI message received")
         elif message["type"] == "websocket.disconnect":
             pass
 


Test patch: