Websocketの使用#
Websocketを使用するには、ルート関数ではなく、websocket関数を以下のように宣言します。
@app.websocket('/ws')
async def ws():
while True:
data = await websocket.receive()
await websocket.send(data)
websocket は request と同様にグローバルであり、headers など、多くの同じ属性を共有します。
Websocketの手動での拒否または承認#
Websocket接続は、HTTPアップグレードリクエストを受け入れることによって作成されますが、サーバーはWebsocketリクエストを拒否することを選択できます。そのためには、ルート関数の場合と同様に、websocket関数から戻ります。
@app.websocket('/ws')
async def ws():
if (
websocket.authorization.username != USERNAME or
websocket.authorization.password != PASSWORD
):
return 'Invalid password', 403 # or abort(403)
else:
websocket.accept() # Automatically invoked by receive or send
...
送受信の独立化#
最初の例では、サーバーが応答するために、クライアントがメッセージを送信する必要があります。送受信を独立して行うには、独立したタスクが必要です。
async def sending():
while True:
await websocket.send(...)
async def receiving():
while True:
data = await websocket.receive()
...
@app.websocket('/ws')
async def ws():
producer = asyncio.create_task(sending())
consumer = asyncio.create_task(receiving())
await asyncio.gather(producer, consumer)
gather行は重要です。これがないと、websocket関数が戻り、QuartがHTTPレスポンスを送信するトリガーとなります。
切断の検知#
クライアントが切断すると、CancelledError が発生します。これをキャッチして、切断を処理できます。
@app.websocket('/ws')
async def ws():
try:
while True:
data = await websocket.receive()
await websocket.send(data)
except asyncio.CancelledError:
# Handle disconnection here
raise
警告
CancelledError は再送する必要があります。
接続の終了#
適切なWebsocketエラーコードを使用して close メソッドを待つことで、接続を閉じることができます。
@app.websocket('/ws')
async def ws():
await websocket.accept()
await websocket.close(1000)
websocketが承認される前に閉じられた場合、サーバーは403 HTTPレスポンスで応答します。
Websocketのテスト#
Websocketルートをテストするには、以下のようにtest_clientを使用します。
test_client = app.test_client()
async with test_client.websocket('/ws/') as test_websocket:
await test_websocket.send(data)
result = await test_websocket.receive()
Websocketルートがレスポンスを返した場合、test_clientは、response 属性を持つ WebsocketResponseError 例外を発生させます。例えば、
test_client = app.test_client()
try:
async with test_client.websocket('/ws/') as test_websocket:
await test_websocket.send(data)
except WebsocketResponseError as error:
assert error.response.status_code == 401
バイトまたは文字列の送受信#
WebSocketプロトコルでは、バイトまたは文字列をフレームマーカーで送信できます。どのマーカーが送信されたかを示します。receive() メソッドは、クライアントが送信した内容に応じて、bytes または str を返します。つまり、クライアントが文字列を送信した場合、メソッドから返されます。同様に、バイトまたは文字列を送信できます。
WebsocketルートとHTTPルートの混在#
Quartでは、WebsocketとHTTPリクエストの両方に対してルートを定義できます。これにより、リクエストのタイプ(WebSocketアップグレードかどうか)に応じてレスポンスを送信できます。以下のように。
@app.route("/ws")
async def http():
return "A HTTP request"
@app.websocket("/ws")
async def ws():
... # Use the WebSocket
HTTP定義がない場合、Quartは、不足しているルートへのリクエストに対して、404ではなく、400、Bad Requestレスポンスで応答します。