From 3a7cbfc4136cd9a5e02821e276a4a434ac337a80 Mon Sep 17 00:00:00 2001 From: Welton Moura Date: Mon, 3 Mar 2025 21:23:35 -0300 Subject: [PATCH] feat: websocket part1 (estudo) --- .../__pycache__/htmx_views.cpython-310.pyc | Bin 3992 -> 4817 bytes gestaoRaul/comandas/htmx_views.py | 23 + gestaoRaul/db.sqlite3 | Bin 331776 -> 335872 bytes .../__pycache__/settings.cpython-310.pyc | Bin 3315 -> 3330 bytes gestaoRaul/gestaoRaul/settings.py | 1 + .../orders/__pycache__/views.cpython-310.pyc | Bin 2217 -> 2743 bytes gestaoRaul/orders/templates/orders.html | 49 +- gestaoRaul/orders/views.py | 15 + gestaoRaul/templates/static/base.js | 35 + .../static/comandas/js/viewcomanda.js | 23 +- .../templates/static/orders/js/orders.js | 7 +- gestaoRaul/websocket/servidor.py | 72 + .../websockets-15.0.dist-info/INSTALLER | 1 + .../websockets-15.0.dist-info/LICENSE | 24 + .../websockets-15.0.dist-info/METADATA | 179 ++ .../websockets-15.0.dist-info/RECORD | 103 ++ .../websockets-15.0.dist-info/REQUESTED | 0 .../websockets-15.0.dist-info/WHEEL | 5 + .../websockets-15.0.dist-info/top_level.txt | 1 + .../Lib/site-packages/websockets/__init__.py | 236 +++ .../Lib/site-packages/websockets/__main__.py | 168 ++ .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 3787 bytes .../__pycache__/__main__.cpython-310.pyc | Bin 0 -> 4829 bytes .../__pycache__/auth.cpython-310.pyc | Bin 0 -> 621 bytes .../__pycache__/client.cpython-310.pyc | Bin 0 -> 11561 bytes .../__pycache__/connection.cpython-310.pyc | Bin 0 -> 495 bytes .../datastructures.cpython-310.pyc | Bin 0 -> 7103 bytes .../__pycache__/exceptions.cpython-310.pyc | Bin 0 -> 14800 bytes .../__pycache__/frames.cpython-310.pyc | Bin 0 -> 10104 bytes .../__pycache__/headers.cpython-310.pyc | Bin 0 -> 13623 bytes .../__pycache__/http.cpython-310.pyc | Bin 0 -> 735 bytes .../__pycache__/http11.cpython-310.pyc | Bin 0 -> 10480 bytes .../__pycache__/imports.cpython-310.pyc | Bin 0 -> 2704 bytes .../__pycache__/protocol.cpython-310.pyc | Bin 0 -> 16887 bytes .../__pycache__/server.cpython-310.pyc | Bin 0 -> 17113 bytes .../__pycache__/streams.cpython-310.pyc | Bin 0 -> 4256 bytes .../__pycache__/typing.cpython-310.pyc | Bin 0 -> 877 bytes .../__pycache__/uri.cpython-310.pyc | Bin 0 -> 5305 bytes .../__pycache__/utils.cpython-310.pyc | Bin 0 -> 1505 bytes .../__pycache__/version.cpython-310.pyc | Bin 0 -> 2009 bytes .../websockets/asyncio/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 184 bytes .../__pycache__/async_timeout.cpython-310.pyc | Bin 0 -> 7470 bytes .../__pycache__/client.cpython-310.pyc | Bin 0 -> 23620 bytes .../__pycache__/compatibility.cpython-310.pyc | Bin 0 -> 824 bytes .../__pycache__/connection.cpython-310.pyc | Bin 0 -> 32848 bytes .../__pycache__/messages.cpython-310.pyc | Bin 0 -> 9367 bytes .../__pycache__/router.cpython-310.pyc | Bin 0 -> 6543 bytes .../__pycache__/server.cpython-310.pyc | Bin 0 -> 29984 bytes .../websockets/asyncio/async_timeout.py | 282 +++ .../websockets/asyncio/client.py | 820 ++++++++ .../websockets/asyncio/compatibility.py | 30 + .../websockets/asyncio/connection.py | 1237 +++++++++++++ .../websockets/asyncio/messages.py | 314 ++++ .../websockets/asyncio/router.py | 198 ++ .../websockets/asyncio/server.py | 981 ++++++++++ .../Lib/site-packages/websockets/auth.py | 18 + .../Lib/site-packages/websockets/client.py | 389 ++++ .../site-packages/websockets/connection.py | 12 + .../websockets/datastructures.py | 187 ++ .../site-packages/websockets/exceptions.py | 473 +++++ .../websockets/extensions/__init__.py | 4 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 288 bytes .../__pycache__/base.cpython-310.pyc | Bin 0 -> 3717 bytes .../permessage_deflate.cpython-310.pyc | Bin 0 -> 13478 bytes .../websockets/extensions/base.py | 123 ++ .../extensions/permessage_deflate.py | 697 +++++++ .../Lib/site-packages/websockets/frames.py | 430 +++++ .../Lib/site-packages/websockets/headers.py | 586 ++++++ .../Lib/site-packages/websockets/http.py | 20 + .../Lib/site-packages/websockets/http11.py | 427 +++++ .../Lib/site-packages/websockets/imports.py | 100 + .../websockets/legacy/__init__.py | 11 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 421 bytes .../legacy/__pycache__/auth.cpython-310.pyc | Bin 0 -> 5899 bytes .../legacy/__pycache__/client.cpython-310.pyc | Bin 0 -> 19876 bytes .../__pycache__/exceptions.cpython-310.pyc | Bin 0 -> 2717 bytes .../__pycache__/framing.cpython-310.pyc | Bin 0 -> 5967 bytes .../__pycache__/handshake.cpython-310.pyc | Bin 0 -> 4722 bytes .../legacy/__pycache__/http.cpython-310.pyc | Bin 0 -> 5196 bytes .../__pycache__/protocol.cpython-310.pyc | Bin 0 -> 41785 bytes .../legacy/__pycache__/server.cpython-310.pyc | Bin 0 -> 34657 bytes .../site-packages/websockets/legacy/auth.py | 190 ++ .../site-packages/websockets/legacy/client.py | 705 +++++++ .../websockets/legacy/exceptions.py | 71 + .../websockets/legacy/framing.py | 225 +++ .../websockets/legacy/handshake.py | 158 ++ .../site-packages/websockets/legacy/http.py | 201 ++ .../websockets/legacy/protocol.py | 1641 +++++++++++++++++ .../site-packages/websockets/legacy/server.py | 1191 ++++++++++++ .../Lib/site-packages/websockets/protocol.py | 758 ++++++++ .../Lib/site-packages/websockets/py.typed | 0 .../Lib/site-packages/websockets/server.py | 587 ++++++ .../Lib/site-packages/websockets/speedups.c | 222 +++ .../websockets/speedups.cp310-win_amd64.pyd | Bin 0 -> 11776 bytes .../Lib/site-packages/websockets/speedups.pyi | 1 + .../Lib/site-packages/websockets/streams.py | 151 ++ .../site-packages/websockets/sync/__init__.py | 0 .../sync/__pycache__/__init__.cpython-310.pyc | Bin 0 -> 181 bytes .../sync/__pycache__/client.cpython-310.pyc | Bin 0 -> 17113 bytes .../__pycache__/connection.cpython-310.pyc | Bin 0 -> 26913 bytes .../sync/__pycache__/messages.cpython-310.pyc | Bin 0 -> 9134 bytes .../sync/__pycache__/router.cpython-310.pyc | Bin 0 -> 6339 bytes .../sync/__pycache__/server.cpython-310.pyc | Bin 0 -> 21762 bytes .../sync/__pycache__/utils.cpython-310.pyc | Bin 0 -> 1442 bytes .../site-packages/websockets/sync/client.py | 648 +++++++ .../websockets/sync/connection.py | 1072 +++++++++++ .../site-packages/websockets/sync/messages.py | 345 ++++ .../site-packages/websockets/sync/router.py | 192 ++ .../site-packages/websockets/sync/server.py | 763 ++++++++ .../site-packages/websockets/sync/utils.py | 45 + .../Lib/site-packages/websockets/typing.py | 74 + .../Lib/site-packages/websockets/uri.py | 225 +++ .../Lib/site-packages/websockets/utils.py | 51 + .../Lib/site-packages/websockets/version.py | 92 + 115 files changed, 17856 insertions(+), 33 deletions(-) create mode 100644 gestaoRaul/websocket/servidor.py create mode 100644 gestao_raul/Lib/site-packages/websockets-15.0.dist-info/INSTALLER create mode 100644 gestao_raul/Lib/site-packages/websockets-15.0.dist-info/LICENSE create mode 100644 gestao_raul/Lib/site-packages/websockets-15.0.dist-info/METADATA create mode 100644 gestao_raul/Lib/site-packages/websockets-15.0.dist-info/RECORD create mode 100644 gestao_raul/Lib/site-packages/websockets-15.0.dist-info/REQUESTED create mode 100644 gestao_raul/Lib/site-packages/websockets-15.0.dist-info/WHEEL create mode 100644 gestao_raul/Lib/site-packages/websockets-15.0.dist-info/top_level.txt create mode 100644 gestao_raul/Lib/site-packages/websockets/__init__.py create mode 100644 gestao_raul/Lib/site-packages/websockets/__main__.py create mode 100644 gestao_raul/Lib/site-packages/websockets/__pycache__/__init__.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/__pycache__/__main__.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/__pycache__/auth.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/__pycache__/client.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/__pycache__/connection.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/__pycache__/datastructures.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/__pycache__/exceptions.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/__pycache__/frames.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/__pycache__/headers.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/__pycache__/http.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/__pycache__/http11.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/__pycache__/imports.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/__pycache__/protocol.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/__pycache__/server.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/__pycache__/streams.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/__pycache__/typing.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/__pycache__/uri.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/__pycache__/utils.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/__pycache__/version.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/asyncio/__init__.py create mode 100644 gestao_raul/Lib/site-packages/websockets/asyncio/__pycache__/__init__.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/asyncio/__pycache__/async_timeout.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/asyncio/__pycache__/client.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/asyncio/__pycache__/compatibility.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/asyncio/__pycache__/connection.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/asyncio/__pycache__/messages.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/asyncio/__pycache__/router.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/asyncio/__pycache__/server.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/asyncio/async_timeout.py create mode 100644 gestao_raul/Lib/site-packages/websockets/asyncio/client.py create mode 100644 gestao_raul/Lib/site-packages/websockets/asyncio/compatibility.py create mode 100644 gestao_raul/Lib/site-packages/websockets/asyncio/connection.py create mode 100644 gestao_raul/Lib/site-packages/websockets/asyncio/messages.py create mode 100644 gestao_raul/Lib/site-packages/websockets/asyncio/router.py create mode 100644 gestao_raul/Lib/site-packages/websockets/asyncio/server.py create mode 100644 gestao_raul/Lib/site-packages/websockets/auth.py create mode 100644 gestao_raul/Lib/site-packages/websockets/client.py create mode 100644 gestao_raul/Lib/site-packages/websockets/connection.py create mode 100644 gestao_raul/Lib/site-packages/websockets/datastructures.py create mode 100644 gestao_raul/Lib/site-packages/websockets/exceptions.py create mode 100644 gestao_raul/Lib/site-packages/websockets/extensions/__init__.py create mode 100644 gestao_raul/Lib/site-packages/websockets/extensions/__pycache__/__init__.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/extensions/__pycache__/base.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/extensions/__pycache__/permessage_deflate.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/extensions/base.py create mode 100644 gestao_raul/Lib/site-packages/websockets/extensions/permessage_deflate.py create mode 100644 gestao_raul/Lib/site-packages/websockets/frames.py create mode 100644 gestao_raul/Lib/site-packages/websockets/headers.py create mode 100644 gestao_raul/Lib/site-packages/websockets/http.py create mode 100644 gestao_raul/Lib/site-packages/websockets/http11.py create mode 100644 gestao_raul/Lib/site-packages/websockets/imports.py create mode 100644 gestao_raul/Lib/site-packages/websockets/legacy/__init__.py create mode 100644 gestao_raul/Lib/site-packages/websockets/legacy/__pycache__/__init__.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/legacy/__pycache__/auth.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/legacy/__pycache__/client.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/legacy/__pycache__/exceptions.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/legacy/__pycache__/framing.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/legacy/__pycache__/handshake.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/legacy/__pycache__/http.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/legacy/__pycache__/protocol.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/legacy/__pycache__/server.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/legacy/auth.py create mode 100644 gestao_raul/Lib/site-packages/websockets/legacy/client.py create mode 100644 gestao_raul/Lib/site-packages/websockets/legacy/exceptions.py create mode 100644 gestao_raul/Lib/site-packages/websockets/legacy/framing.py create mode 100644 gestao_raul/Lib/site-packages/websockets/legacy/handshake.py create mode 100644 gestao_raul/Lib/site-packages/websockets/legacy/http.py create mode 100644 gestao_raul/Lib/site-packages/websockets/legacy/protocol.py create mode 100644 gestao_raul/Lib/site-packages/websockets/legacy/server.py create mode 100644 gestao_raul/Lib/site-packages/websockets/protocol.py create mode 100644 gestao_raul/Lib/site-packages/websockets/py.typed create mode 100644 gestao_raul/Lib/site-packages/websockets/server.py create mode 100644 gestao_raul/Lib/site-packages/websockets/speedups.c create mode 100644 gestao_raul/Lib/site-packages/websockets/speedups.cp310-win_amd64.pyd create mode 100644 gestao_raul/Lib/site-packages/websockets/speedups.pyi create mode 100644 gestao_raul/Lib/site-packages/websockets/streams.py create mode 100644 gestao_raul/Lib/site-packages/websockets/sync/__init__.py create mode 100644 gestao_raul/Lib/site-packages/websockets/sync/__pycache__/__init__.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/sync/__pycache__/client.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/sync/__pycache__/connection.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/sync/__pycache__/messages.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/sync/__pycache__/router.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/sync/__pycache__/server.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/sync/__pycache__/utils.cpython-310.pyc create mode 100644 gestao_raul/Lib/site-packages/websockets/sync/client.py create mode 100644 gestao_raul/Lib/site-packages/websockets/sync/connection.py create mode 100644 gestao_raul/Lib/site-packages/websockets/sync/messages.py create mode 100644 gestao_raul/Lib/site-packages/websockets/sync/router.py create mode 100644 gestao_raul/Lib/site-packages/websockets/sync/server.py create mode 100644 gestao_raul/Lib/site-packages/websockets/sync/utils.py create mode 100644 gestao_raul/Lib/site-packages/websockets/typing.py create mode 100644 gestao_raul/Lib/site-packages/websockets/uri.py create mode 100644 gestao_raul/Lib/site-packages/websockets/utils.py create mode 100644 gestao_raul/Lib/site-packages/websockets/version.py diff --git a/gestaoRaul/comandas/__pycache__/htmx_views.cpython-310.pyc b/gestaoRaul/comandas/__pycache__/htmx_views.cpython-310.pyc index 3bae9cca2a6c168fa8c402e50c150015d3b864f2..363284ec5deb901e2e933e11d35172400552d201 100644 GIT binary patch delta 2673 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 diff --git a/gestaoRaul/comandas/htmx_views.py b/gestaoRaul/comandas/htmx_views.py index d07c216..2aea7a9 100644 --- a/gestaoRaul/comandas/htmx_views.py +++ b/gestaoRaul/comandas/htmx_views.py @@ -9,6 +9,20 @@ from payments.models import Payments from typePay.models import TypePay from gestaoRaul.decorators import group_required +import asyncio +import websockets + +async def enviar_mensagem(msg): + uri = "ws://localhost:8765" # Substitua pela URI do seu servidor WebSocket + async with websockets.connect(uri) as websocket: + await websocket.send(msg) + print(f"> Enviado: {msg}") + + resposta = await websocket.recv() + print(f"< Recebido: {resposta}") + + + def somar(consumo:ProductComanda, comanda:Comanda): parcial = Payments.objects.filter(comanda=comanda) @@ -50,6 +64,15 @@ def addProduct(request, product_id, comanda_id): if product.cuisine == True: order = Order(id_comanda=comanda, id_product=product, productComanda=product_comanda, obs='') order.save() + msg = JsonResponse({ + 'type': 'broadcast', + 'message': f'

{product.name}

{obs}

{comanda.name} - {comanda.mesa.name}

Atendente: {comanda.user.first_name}

{order.queue}

', + 'local':'cozinha', + 'tipo':'add', + 'id':order.id, + 'speak': f'Novo pedido! {product.name}, para {comanda.name}.' + }) + asyncio.run(enviar_mensagem(msg)) consumo = ProductComanda.objects.filter(comanda=comanda_id) valores = somar(consumo,comanda) diff --git a/gestaoRaul/db.sqlite3 b/gestaoRaul/db.sqlite3 index 3381ce3c309e85bbb9205e37cd961438bde709a1..8417e5c7551fb31f402c10797d2f35e5d95b25cb 100644 GIT binary patch delta 7787 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 M%nH6FYq%WiG4{C!iU0rr delta 881 zcmb7CJ#Q015WV%q_U_&Ke0I*{0|yYGC`SYmqCf}~gb;)(6m&=7Sl+`NI)5-1htfi% zrLY$97m#ZTI+`@}bXVjT&{5Dbvmtg!1uN~_nK!#LZ)f*QV_NeDm5O8GXu2P|zvRSg zvX7I^o3DC2;>=Q(DyE|6M2@jbsoRs0M7yl`ffZF^sf2RgGvz9IV7+6JH)ATwGfWkk zs@dbKnqhoZSBrSMrh;cy*q9vM(aY?*&I{kTheDq1v$zJmkG!ll@YevaQJD^>riSMdZ$zy|C0ov z;xxgg{D+3CSb*lS2C_N~;bf2=nxBtKx7f8)uZ7&>umBZYbN}S#tyYrtM`;Sn`YwOO zHuPKGT;Iekuz?Wz^KKotXZFlc*N1#-Wu3A)iE0gDphlg&uxy?a0=+7>gEQTuyf^en zJS>y76V~-t@jwgb$@3-j&BL0Wg1MeiG0$Ku`T#k<*TY(AyhYc}nh26f{GigNmD{|C LUqayI4wK?HwfdSC diff --git a/gestaoRaul/orders/templates/orders.html b/gestaoRaul/orders/templates/orders.html index 0e73a7d..d6c1740 100644 --- a/gestaoRaul/orders/templates/orders.html +++ b/gestaoRaul/orders/templates/orders.html @@ -40,27 +40,28 @@
{% for order in orders %} - {% if order.preparing == None and order.productComanda != Null %} -
-

{{order.id_product.name}}

-

{{order.obs}}

-

{{order.id_comanda.name}} - {{order.id_comanda.mesa.name}}

-

Atendente: {{order.id_comanda.user.first_name}}

-

{{order.queue|date:"D"}} {{order.queue|date:"d/m/Y - H:i"}}

- {% if user|groupUser:"Cozinha" %} - - {% endif %} -
- {% endif %} - {% endfor %} + {% if order.preparing == None and order.productComanda != Null %} +
+

{{order.id_product.name}}

+

{{order.obs}}

+

{{order.id_comanda.name}} - {{order.id_comanda.mesa.name}}

+

Atendente: {{order.id_comanda.user.first_name}}

+

{{order.queue|date:"D"}} {{order.queue|date:"d/m/Y - H:i"}}

+ {% if user|groupUser:"Cozinha" %} + + {% endif %} +
+ {% endif %} + {% endfor %} +
@@ -75,7 +76,7 @@ {% endif %} >

{{order.id_product.name}}

-

{{order.obs}}

+

{{order.obs}}

{{order.id_comanda.name}} - {{order.id_comanda.mesa.name}}

Atendente: {{order.id_comanda.user.first_name}}

{{order.queue|date:"D"}} {{order.queue|date:"d/m/Y - H:i"}}

@@ -100,7 +101,7 @@ {% endif %} >

{{order.id_product.name}}

-

{{order.obs}}

+

{{order.obs}}

{{order.id_comanda.name}} - {{order.id_comanda.mesa.name}}

Atendente: {{order.id_comanda.user.first_name}}

{{order.queue|date:"D"}} {{order.queue|date:"d/m/Y - H:i"}}

@@ -125,7 +126,7 @@ {% endif %} >

{{order.id_product.name}}

-

{{order.obs}}

+

{{order.obs}}

{{order.id_comanda.name}} - {{order.id_comanda.mesa.name}}

Atendente: {{order.id_comanda.user.first_name}}

{{order.queue|date:"D"}} {{order.queue|date:"d/m/Y - H:i"}}

diff --git a/gestaoRaul/orders/views.py b/gestaoRaul/orders/views.py index dea1766..f32332a 100644 --- a/gestaoRaul/orders/views.py +++ b/gestaoRaul/orders/views.py @@ -2,6 +2,8 @@ from django.utils import timezone from django.shortcuts import render from django.http import JsonResponse, HttpResponse +import asyncio +import websockets from orders.models import Order @@ -9,6 +11,18 @@ from django.db.models import Q from gestaoRaul.decorators import group_required +async def enviar_mensagem(message): + uri = "ws://localhost:8765" # Substitua pela URI do seu servidor WebSocket + async with websockets.connect(uri) as websocket: + await websocket.send(message) + print(f"> Enviado: {message}") + + resposta = await websocket.recv() + print(f"< Recebido: {resposta}") + + + + def viewsOrders(request): fifteen_hours_ago = timezone.now() - timezone.timedelta(hours=15) orders = Order.objects.filter(queue__gte=fifteen_hours_ago ) @@ -31,6 +45,7 @@ def finished(request, order_id): order.save() fifteen_hours_ago = timezone.now() - timezone.timedelta(hours=15) orders = Order.objects.filter(queue__gte=fifteen_hours_ago ) + asyncio.run(enviar_mensagem()) return render(request, 'htmx_components/orders/htmx_list_orders_fila.html',{'orders': orders}) @group_required(groupName='Garçom') diff --git a/gestaoRaul/templates/static/base.js b/gestaoRaul/templates/static/base.js index 5f67324..702ee59 100644 --- a/gestaoRaul/templates/static/base.js +++ b/gestaoRaul/templates/static/base.js @@ -1,3 +1,38 @@ + +const websocket = new WebSocket('ws://localhost:8765'); + +websocket.addEventListener('open', (event) => { + console.log('Conectado ao servidor WebSocket'); +}); + +websocket.addEventListener('message', (event) => { + const data = JSON.parse(event.data); + if (data.local === 'cozinha' && data.tipo === 'add'){ + const novoElemento = document.createElement('div'); + novoElemento.innerHTML = data.message; + var fila = document.getElementById('Fila').appendChild(novoElemento); + texto = new SpeechSynthesisUtterance(data.speak); + window.speechSynthesis.speak(texto); + console.log('Mensagem recebida:', data.local); + } + else if (data.local === 'cozinha' && data.tipo === 'edit'){ + var card = document.getElementById('obs-'+data.id).innerHTML = data.message + console.log('Mensagem recebida:', data.local); + } +}); + +websocket.addEventListener('error', (event) => { + console.error('Erro no WebSocket:', event); +}); + +websocket.addEventListener('close', (event) => { + console.log("conexão fechada"); +}); + + + + + function menuShow() { let menuMobile = document.querySelector('.mobile-menu'); if (menuMobile.classList.contains('open')) { diff --git a/gestaoRaul/templates/static/comandas/js/viewcomanda.js b/gestaoRaul/templates/static/comandas/js/viewcomanda.js index f3045e8..e9efba5 100644 --- a/gestaoRaul/templates/static/comandas/js/viewcomanda.js +++ b/gestaoRaul/templates/static/comandas/js/viewcomanda.js @@ -1,5 +1,6 @@ + function openModal() { textField = document.getElementById('search-product') textField.value = ''; @@ -220,8 +221,8 @@ function showToastAdd(message, type ,duration = 3000) { } function addProductComanda(productId,comandaId, cuisine) { obs = document.getElementById('obs'); - console.log(obs.value); - console.log(cuisine); + // console.log(obs.value); + // console.log(cuisine); if(cuisine == 'ggg'){ var obs = openModalObs(); @@ -239,7 +240,9 @@ function addProductComanda(productId,comandaId, cuisine) { var listProductsBalcaoElement = document.getElementById("list-products-comanda"); listProductsBalcaoElement.innerHTML = text; }) - + // websocket.send(JSON.stringify({ type: 'broadcast', message: '

Tapioca de ovo

sem ovo

Joao - mesa 07

Atendente: Lucas

25/02/2025 20:03

' + //})); + showToastAdd('Produto adicionado com sucesso!😁','success'); } @@ -258,11 +261,21 @@ function taxa(){ } } -document.getElementById('taxa').addEventListener("change", taxa); +// document.getElementById('taxa').addEventListener("change", taxa); // document.getElementById('productForm').addEventListener('submit', function(event) { // event.preventDefault(); // }); -// hx-get="{% url 'addProduct' product.id comanda.id %} " hx-trigger="click" hx-target="#list-products-comanda" \ No newline at end of file + + + +// Enviar uma mensagem (exemplo - broadcast): +// websocket.send(JSON.stringify({ type: 'broadcast', message: 'Olá do cliente!' })); + +//enviar uma mensagem (exemplo - echo): +// websocket.send(JSON.stringify({"type": "echo", "message": "Olá Mundo!"})) + +//enviar uma mensagem (exemplo - test): +// websocket.send(JSON.stringify({"type": "test"})) diff --git a/gestaoRaul/templates/static/orders/js/orders.js b/gestaoRaul/templates/static/orders/js/orders.js index ad65c7c..20dab2d 100644 --- a/gestaoRaul/templates/static/orders/js/orders.js +++ b/gestaoRaul/templates/static/orders/js/orders.js @@ -1,4 +1,3 @@ -// document.cookie = `fila=0`; function reloadPage(){ setTimeout(function() { @@ -99,9 +98,9 @@ function notificacao(){ } -setInterval(()=> { - notificacao() -}, 10000) +// setInterval(()=> { +// notificacao() +// }, 10000) diff --git a/gestaoRaul/websocket/servidor.py b/gestaoRaul/websocket/servidor.py new file mode 100644 index 0000000..7975e4a --- /dev/null +++ b/gestaoRaul/websocket/servidor.py @@ -0,0 +1,72 @@ +import asyncio +import websockets +import json +import logging + +# Configure logging +logging.basicConfig(level=logging.INFO) + +connected_clients = set() # Keep track of connected clients + +async def handle_client(websocket): # Remove 'path' argument + """Handles a single client connection.""" + logging.info(f"Client connected: {websocket.remote_address}") + connected_clients.add(websocket) + try: + async for message in websocket: + logging.info(f"Received message from {websocket.remote_address}: {message}") + print(f"Received message from {websocket.remote_address}: {message}") + try: + data = json.loads(message) + # Process the message here + await process_message(data, websocket) + + except json.JSONDecodeError: + logging.error(f"Invalid JSON received from {websocket.remote_address}: {message}") + await websocket.send(json.dumps({"error": "Invalid JSON format"})) + except Exception as e: + logging.error(f"Error processing message from {websocket.remote_address}: {e}") + await websocket.send(json.dumps({"error": "Error processing message"})) + + except websockets.exceptions.ConnectionClosedOK: + logging.info(f"Client disconnected: {websocket.remote_address}") + except websockets.exceptions.ConnectionClosedError: + logging.error(f"Client connection closed with error {websocket.remote_address}") + except Exception as e: + logging.error(f"Error during connection from {websocket.remote_address} with error {e}") + finally: + connected_clients.remove(websocket) + +async def process_message(data, websocket): + """Processes the received message and takes actions.""" + # Example: check if it's a broadcast message + if "type" in data and data["type"] == "broadcast" and "message" in data: + await broadcast_message(data) + print("Broadcast message:", data["message"]) + elif "type" in data and data["type"] == "echo" and "message" in data: + await websocket.send(json.dumps({"response": data['message']})) + elif "type" in data and data['type'] == "test": + await websocket.send(json.dumps({"response": "test is ok"})) + else: + logging.warning(f"Unknown message type or format: {data}") + await websocket.send(json.dumps({"error": "Unknown message type"})) + +async def broadcast_message(data): + """Broadcasts a message to all connected clients.""" + if connected_clients: + logging.info(f"Broadcasting message: {data}") + await asyncio.wait([client.send(json.dumps(data)) for client in connected_clients]) + else: + logging.info("No clients connected. Message not broadcasted.") + + +async def main(): + """Starts the WebSocket server.""" + start_server = websockets.serve(handle_client, "localhost", 8765) + logging.info("WebSocket server started on ws://localhost:8765") + await start_server + await asyncio.Future() # Keep the server running indefinitely + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/gestao_raul/Lib/site-packages/websockets-15.0.dist-info/INSTALLER b/gestao_raul/Lib/site-packages/websockets-15.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets-15.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/gestao_raul/Lib/site-packages/websockets-15.0.dist-info/LICENSE b/gestao_raul/Lib/site-packages/websockets-15.0.dist-info/LICENSE new file mode 100644 index 0000000..5d61ece --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets-15.0.dist-info/LICENSE @@ -0,0 +1,24 @@ +Copyright (c) Aymeric Augustin and contributors + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/gestao_raul/Lib/site-packages/websockets-15.0.dist-info/METADATA b/gestao_raul/Lib/site-packages/websockets-15.0.dist-info/METADATA new file mode 100644 index 0000000..a628329 --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets-15.0.dist-info/METADATA @@ -0,0 +1,179 @@ +Metadata-Version: 2.2 +Name: websockets +Version: 15.0 +Summary: An implementation of the WebSocket Protocol (RFC 6455 & 7692) +Author-email: Aymeric Augustin +License: BSD-3-Clause +Project-URL: Homepage, https://github.com/python-websockets/websockets +Project-URL: Changelog, https://websockets.readthedocs.io/en/stable/project/changelog.html +Project-URL: Documentation, https://websockets.readthedocs.io/ +Project-URL: Funding, https://tidelift.com/subscription/pkg/pypi-websockets?utm_source=pypi-websockets&utm_medium=referral&utm_campaign=readme +Project-URL: Tracker, https://github.com/python-websockets/websockets/issues +Keywords: WebSocket +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Requires-Python: >=3.9 +Description-Content-Type: text/x-rst +License-File: LICENSE +Dynamic: description +Dynamic: description-content-type + +.. image:: logo/horizontal.svg + :width: 480px + :alt: websockets + +|licence| |version| |pyversions| |tests| |docs| |openssf| + +.. |licence| image:: https://img.shields.io/pypi/l/websockets.svg + :target: https://pypi.python.org/pypi/websockets + +.. |version| image:: https://img.shields.io/pypi/v/websockets.svg + :target: https://pypi.python.org/pypi/websockets + +.. |pyversions| image:: https://img.shields.io/pypi/pyversions/websockets.svg + :target: https://pypi.python.org/pypi/websockets + +.. |tests| image:: https://img.shields.io/github/checks-status/python-websockets/websockets/main?label=tests + :target: https://github.com/python-websockets/websockets/actions/workflows/tests.yml + +.. |docs| image:: https://img.shields.io/readthedocs/websockets.svg + :target: https://websockets.readthedocs.io/ + +.. |openssf| image:: https://bestpractices.coreinfrastructure.org/projects/6475/badge + :target: https://bestpractices.coreinfrastructure.org/projects/6475 + +What is ``websockets``? +----------------------- + +websockets is a library for building WebSocket_ servers and clients in Python +with a focus on correctness, simplicity, robustness, and performance. + +.. _WebSocket: https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API + +Built on top of ``asyncio``, Python's standard asynchronous I/O framework, the +default implementation provides an elegant coroutine-based API. + +An implementation on top of ``threading`` and a Sans-I/O implementation are also +available. + +`Documentation is available on Read the Docs. `_ + +.. copy-pasted because GitHub doesn't support the include directive + +Here's an echo server with the ``asyncio`` API: + +.. code:: python + + #!/usr/bin/env python + + import asyncio + from websockets.asyncio.server import serve + + async def echo(websocket): + async for message in websocket: + await websocket.send(message) + + async def main(): + async with serve(echo, "localhost", 8765) as server: + await server.serve_forever() + + asyncio.run(main()) + +Here's how a client sends and receives messages with the ``threading`` API: + +.. code:: python + + #!/usr/bin/env python + + from websockets.sync.client import connect + + def hello(): + with connect("ws://localhost:8765") as websocket: + websocket.send("Hello world!") + message = websocket.recv() + print(f"Received: {message}") + + hello() + + +Does that look good? + +`Get started with the tutorial! `_ + +Why should I use ``websockets``? +-------------------------------- + +The development of ``websockets`` is shaped by four principles: + +1. **Correctness**: ``websockets`` is heavily tested for compliance with + :rfc:`6455`. Continuous integration fails under 100% branch coverage. + +2. **Simplicity**: all you need to understand is ``msg = await ws.recv()`` and + ``await ws.send(msg)``. ``websockets`` takes care of managing connections + so you can focus on your application. + +3. **Robustness**: ``websockets`` is built for production. For example, it was + the only library to `handle backpressure correctly`_ before the issue + became widely known in the Python community. + +4. **Performance**: memory usage is optimized and configurable. A C extension + accelerates expensive operations. It's pre-compiled for Linux, macOS and + Windows and packaged in the wheel format for each system and Python version. + +Documentation is a first class concern in the project. Head over to `Read the +Docs`_ and see for yourself. + +.. _Read the Docs: https://websockets.readthedocs.io/ +.. _handle backpressure correctly: https://vorpus.org/blog/some-thoughts-on-asynchronous-api-design-in-a-post-asyncawait-world/#websocket-servers + +Why shouldn't I use ``websockets``? +----------------------------------- + +* If you prefer callbacks over coroutines: ``websockets`` was created to + provide the best coroutine-based API to manage WebSocket connections in + Python. Pick another library for a callback-based API. + +* If you're looking for a mixed HTTP / WebSocket library: ``websockets`` aims + at being an excellent implementation of :rfc:`6455`: The WebSocket Protocol + and :rfc:`7692`: Compression Extensions for WebSocket. Its support for HTTP + is minimal — just enough for an HTTP health check. + + If you want to do both in the same server, look at HTTP + WebSocket servers + that build on top of ``websockets`` to support WebSocket connections, like + uvicorn_ or Sanic_. + +.. _uvicorn: https://www.uvicorn.org/ +.. _Sanic: https://sanic.dev/en/ + +What else? +---------- + +Bug reports, patches and suggestions are welcome! + +To report a security vulnerability, please use the `Tidelift security +contact`_. Tidelift will coordinate the fix and disclosure. + +.. _Tidelift security contact: https://tidelift.com/security + +For anything else, please open an issue_ or send a `pull request`_. + +.. _issue: https://github.com/python-websockets/websockets/issues/new +.. _pull request: https://github.com/python-websockets/websockets/compare/ + +Participants must uphold the `Contributor Covenant code of conduct`_. + +.. _Contributor Covenant code of conduct: https://github.com/python-websockets/websockets/blob/main/CODE_OF_CONDUCT.md + +``websockets`` is released under the `BSD license`_. + +.. _BSD license: https://github.com/python-websockets/websockets/blob/main/LICENSE diff --git a/gestao_raul/Lib/site-packages/websockets-15.0.dist-info/RECORD b/gestao_raul/Lib/site-packages/websockets-15.0.dist-info/RECORD new file mode 100644 index 0000000..d52590b --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets-15.0.dist-info/RECORD @@ -0,0 +1,103 @@ +websockets-15.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +websockets-15.0.dist-info/LICENSE,sha256=D0RRSZisognTSC0QIEqK3yqkKW_xV6NqXAki8igGMtM,1538 +websockets-15.0.dist-info/METADATA,sha256=o7PpM419xh3D_fXxEQ0OevQfLv0i6h_x1yOQ5VTFMbI,6994 +websockets-15.0.dist-info/RECORD,, +websockets-15.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +websockets-15.0.dist-info/WHEEL,sha256=rzGfZgUcGeKSgIHGYMuqg4xE4VPHxnaldXH6BG0zjVk,101 +websockets-15.0.dist-info/top_level.txt,sha256=CMpdKklxKsvZgCgyltxUWOHibZXZ1uYIVpca9xsQ8Hk,11 +websockets/__init__.py,sha256=gnZ1a8qhrWVVClaf7lGqkSVQAPPm-cSZ3k3525fozaY,7294 +websockets/__main__.py,sha256=sblWoA2fS_Lwbz3d22T-9SXSK2kk_RTMDEtnq8TSMr0,4927 +websockets/__pycache__/__init__.cpython-310.pyc,, +websockets/__pycache__/__main__.cpython-310.pyc,, +websockets/__pycache__/auth.cpython-310.pyc,, +websockets/__pycache__/client.cpython-310.pyc,, +websockets/__pycache__/connection.cpython-310.pyc,, +websockets/__pycache__/datastructures.cpython-310.pyc,, +websockets/__pycache__/exceptions.cpython-310.pyc,, +websockets/__pycache__/frames.cpython-310.pyc,, +websockets/__pycache__/headers.cpython-310.pyc,, +websockets/__pycache__/http.cpython-310.pyc,, +websockets/__pycache__/http11.cpython-310.pyc,, +websockets/__pycache__/imports.cpython-310.pyc,, +websockets/__pycache__/protocol.cpython-310.pyc,, +websockets/__pycache__/server.cpython-310.pyc,, +websockets/__pycache__/streams.cpython-310.pyc,, +websockets/__pycache__/typing.cpython-310.pyc,, +websockets/__pycache__/uri.cpython-310.pyc,, +websockets/__pycache__/utils.cpython-310.pyc,, +websockets/__pycache__/version.cpython-310.pyc,, +websockets/asyncio/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +websockets/asyncio/__pycache__/__init__.cpython-310.pyc,, +websockets/asyncio/__pycache__/async_timeout.cpython-310.pyc,, +websockets/asyncio/__pycache__/client.cpython-310.pyc,, +websockets/asyncio/__pycache__/compatibility.cpython-310.pyc,, +websockets/asyncio/__pycache__/connection.cpython-310.pyc,, +websockets/asyncio/__pycache__/messages.cpython-310.pyc,, +websockets/asyncio/__pycache__/router.cpython-310.pyc,, +websockets/asyncio/__pycache__/server.cpython-310.pyc,, +websockets/asyncio/async_timeout.py,sha256=jMKMQcVKWtn09n6lqefqdpSYoMRlP4Ek60RjJ6B6Eco,9253 +websockets/asyncio/client.py,sha256=u5ek8ZPWuHS1_Z3roZ1fuW26iGeBaZBIjrXack5j2LM,32310 +websockets/asyncio/compatibility.py,sha256=ld5lpbBgJ8YGAi3tc1hMiIxuLeQWDg6pKDYBVbSQMl4,816 +websockets/asyncio/connection.py,sha256=pFbCzpTHzVgwAsEC7ISjjYjmL5cu0v_2DCV0TTHm910,49959 +websockets/asyncio/messages.py,sha256=bzPkRaohSTLKmjZqAriIIMTdIB4DFM4G3y-1NfDAu3k,11307 +websockets/asyncio/router.py,sha256=Ww_6IZbC8CT_-EvVgDjkJgX-QSZRNfuyGzAlJd0saGs,6799 +websockets/asyncio/server.py,sha256=AUB_3DWmUXPEUgGzSpiG70tnWxSpSn3066XszwEOJLo,38366 +websockets/auth.py,sha256=NZ60zoE10wklboBHzj4Wof9piVB09jO1bDn_KHN8ZcE,586 +websockets/client.py,sha256=UCv271PhLtsF1NWct6lBe0bUmQPUoi2q355udXN-QYY,13953 +websockets/connection.py,sha256=uIRi5jeS3zVIWq8DcEDJPousrYNTItPe0ioKxBr2hOs,335 +websockets/datastructures.py,sha256=zOgfJ9Ctt4MA4aWx1MJD7dHEjMiwu0Twj12nFE8tQ-g,5802 +websockets/exceptions.py,sha256=XeYDSFdqdqI6bX66iWOhVhSZRDfI6ZKwMWccFWpgCkg,13284 +websockets/extensions/__init__.py,sha256=HdQaQhOVkCR5RJVRKXG5Xmb6cMpAhLaKgRikYRzO64E,102 +websockets/extensions/__pycache__/__init__.cpython-310.pyc,, +websockets/extensions/__pycache__/base.cpython-310.pyc,, +websockets/extensions/__pycache__/permessage_deflate.cpython-310.pyc,, +websockets/extensions/base.py,sha256=Je1VMBHq7_gqq5idsCqMPD87CI6m7zpDSRj_lCL37Rc,3026 +websockets/extensions/permessage_deflate.py,sha256=nMAfbfogemSMwVWWK0CCO5CF0xcizSyyiSJlPSQ3Sgs,26408 +websockets/frames.py,sha256=KumjH71OoXViDmk4ndpzcWUECHsqyUfJvCzzknt9NN4,13189 +websockets/headers.py,sha256=2XFbUE0BzEIftBR2KS2ygeSRIYzPB-a-q35jfdcZ1MA,16632 +websockets/http.py,sha256=pQmdzRiYK_SuiwvXIrP_rsJZAj8tGIol76e_TvsPUrg,679 +websockets/http11.py,sha256=r8FadvqhWsUz50fz0_Mx_quSiyu2JxilCm4X8fXmwTU,15352 +websockets/imports.py,sha256=oFdWkxypj_diuqneRuA5OWEXLMQ6pAtj-rKqFU6Pmdg,2895 +websockets/legacy/__init__.py,sha256=VS5mRP7oS3kXCcXX7F3vPULmXPFi4VcyKbnv8Lk18MY,287 +websockets/legacy/__pycache__/__init__.cpython-310.pyc,, +websockets/legacy/__pycache__/auth.cpython-310.pyc,, +websockets/legacy/__pycache__/client.cpython-310.pyc,, +websockets/legacy/__pycache__/exceptions.cpython-310.pyc,, +websockets/legacy/__pycache__/framing.cpython-310.pyc,, +websockets/legacy/__pycache__/handshake.cpython-310.pyc,, +websockets/legacy/__pycache__/http.cpython-310.pyc,, +websockets/legacy/__pycache__/protocol.cpython-310.pyc,, +websockets/legacy/__pycache__/server.cpython-310.pyc,, +websockets/legacy/auth.py,sha256=wILIJE28YsQKmQcWdPMzmdM_0Sw674dqUo2XGqyUvH4,6721 +websockets/legacy/client.py,sha256=FEvbnmTV8lS5oHvqcoisgbocnmudEvQ8Rpuui86GOBg,27690 +websockets/legacy/exceptions.py,sha256=KrvUEI31DUZs3gl6M-Q-dgsSnsMXzU_bDRuHU6AcKbc,1995 +websockets/legacy/framing.py,sha256=k2ykZNh2kumsEC1cHk9XVIah9IYioRobziTpSLyDRv0,6591 +websockets/legacy/handshake.py,sha256=CRRxkJ96TIgWuoMIL0iol4jp4X7llMA8buX4gfo0SwE,5443 +websockets/legacy/http.py,sha256=OPu_UljFJH74nxk5V-jnOOkqzdXcpJ6M0LynIpuZ740,7262 +websockets/legacy/protocol.py,sha256=YN1QdGYK1Tm-t902bo5PI0-WvMKklOeUqSdow1FGsbQ,65543 +websockets/legacy/server.py,sha256=j6Q9e53WIwk5ku0xA98abWDa8SdodxAIpp_bn3yTjB4,46441 +websockets/protocol.py,sha256=MYG7Tn7rFRxquLGqRQsGN2BS9mE7KGTyckbLoyznsfk,27295 +websockets/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +websockets/server.py,sha256=95htLVRjeP445WsnWkFw2IzlX6wF6Y609ko1gVm2-xo,22152 +websockets/speedups.c,sha256=j9p_o0aR8U1dt7VXAFTOZzYlGjiSlHVrTujpEzqP19s,5989 +websockets/speedups.cp310-win_amd64.pyd,sha256=fyHwzTKPvpC6z5VRelcLQnzMFwwx1vmbMpg8hUr01nM,11776 +websockets/speedups.pyi,sha256=tYIifoH4Qponj6HT8rhk2MQOVrfBFW8T5SYcaxefOdA,56 +websockets/streams.py,sha256=6D-YosVbTobUxFYtsS5rp53-Ig0CH7EAetoOP9XyD08,4198 +websockets/sync/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +websockets/sync/__pycache__/__init__.cpython-310.pyc,, +websockets/sync/__pycache__/client.cpython-310.pyc,, +websockets/sync/__pycache__/connection.cpython-310.pyc,, +websockets/sync/__pycache__/messages.cpython-310.pyc,, +websockets/sync/__pycache__/router.cpython-310.pyc,, +websockets/sync/__pycache__/server.cpython-310.pyc,, +websockets/sync/__pycache__/utils.cpython-310.pyc,, +websockets/sync/client.py,sha256=NjelUsmGfP6d4t4em8tNf67tzLFyOVJPXvAzV_cuwjI,23296 +websockets/sync/connection.py,sha256=aoY4BfEG13hvWBFWSFH50iY_Er2RRIU6AFgKOvJE4FA,42646 +websockets/sync/messages.py,sha256=W6Y-BZHgyuQ6hURDsZ2itewi5lAMJ9ld7X0wAlV2Zkk,12952 +websockets/sync/router.py,sha256=E6h1ivLUM9DskHQBn7LWZQhvV3ybYqcFIEpsQ4UllD8,6483 +websockets/sync/server.py,sha256=l3u-jxI0dETw69_YmaR0sA21B60lEPQTVgA6oIBZ8Gw,28199 +websockets/sync/utils.py,sha256=JgQVxO_NcQXZd8m6hDeXpSQ_uN6GYF8u9XLHZ7msv3k,1152 +websockets/typing.py,sha256=cLOjWn1jPgxQucORXZTMxSLZZN-fjD0qDcjaVjioZl4,2099 +websockets/uri.py,sha256=_ZhWgzuL_ekGWGu8QFode_0rgLN2toz9u1MPu5opDa8,7211 +websockets/utils.py,sha256=z3lSFywpnE4aifiJwYVPTgcwMLNDAkYDbdZUqkSwhPo,1201 +websockets/version.py,sha256=N3EcOC-xhzmXKb0-dT8rByoqrtIcFemNLJ1osamix14,3294 diff --git a/gestao_raul/Lib/site-packages/websockets-15.0.dist-info/REQUESTED b/gestao_raul/Lib/site-packages/websockets-15.0.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/gestao_raul/Lib/site-packages/websockets-15.0.dist-info/WHEEL b/gestao_raul/Lib/site-packages/websockets-15.0.dist-info/WHEEL new file mode 100644 index 0000000..ae2990e --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets-15.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (75.8.0) +Root-Is-Purelib: false +Tag: cp310-cp310-win_amd64 + diff --git a/gestao_raul/Lib/site-packages/websockets-15.0.dist-info/top_level.txt b/gestao_raul/Lib/site-packages/websockets-15.0.dist-info/top_level.txt new file mode 100644 index 0000000..14774b4 --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets-15.0.dist-info/top_level.txt @@ -0,0 +1 @@ +websockets diff --git a/gestao_raul/Lib/site-packages/websockets/__init__.py b/gestao_raul/Lib/site-packages/websockets/__init__.py new file mode 100644 index 0000000..f90aff5 --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/__init__.py @@ -0,0 +1,236 @@ +from __future__ import annotations + +# Importing the typing module would conflict with websockets.typing. +from typing import TYPE_CHECKING + +from .imports import lazy_import +from .version import version as __version__ # noqa: F401 + + +__all__ = [ + # .asyncio.client + "connect", + "unix_connect", + "ClientConnection", + # .asyncio.router + "route", + "unix_route", + "Router", + # .asyncio.server + "basic_auth", + "broadcast", + "serve", + "unix_serve", + "ServerConnection", + "Server", + # .client + "ClientProtocol", + # .datastructures + "Headers", + "HeadersLike", + "MultipleValuesError", + # .exceptions + "ConcurrencyError", + "ConnectionClosed", + "ConnectionClosedError", + "ConnectionClosedOK", + "DuplicateParameter", + "InvalidHandshake", + "InvalidHeader", + "InvalidHeaderFormat", + "InvalidHeaderValue", + "InvalidMessage", + "InvalidOrigin", + "InvalidParameterName", + "InvalidParameterValue", + "InvalidProxy", + "InvalidProxyMessage", + "InvalidProxyStatus", + "InvalidState", + "InvalidStatus", + "InvalidUpgrade", + "InvalidURI", + "NegotiationError", + "PayloadTooBig", + "ProtocolError", + "ProxyError", + "SecurityError", + "WebSocketException", + # .frames + "Close", + "CloseCode", + "Frame", + "Opcode", + # .http11 + "Request", + "Response", + # .protocol + "Protocol", + "Side", + "State", + # .server + "ServerProtocol", + # .typing + "Data", + "ExtensionName", + "ExtensionParameter", + "LoggerLike", + "StatusLike", + "Origin", + "Subprotocol", +] + +# When type checking, import non-deprecated aliases eagerly. Else, import on demand. +if TYPE_CHECKING: + from .asyncio.client import ClientConnection, connect, unix_connect + from .asyncio.router import Router, route, unix_route + from .asyncio.server import ( + Server, + ServerConnection, + basic_auth, + broadcast, + serve, + unix_serve, + ) + from .client import ClientProtocol + from .datastructures import Headers, HeadersLike, MultipleValuesError + from .exceptions import ( + ConcurrencyError, + ConnectionClosed, + ConnectionClosedError, + ConnectionClosedOK, + DuplicateParameter, + InvalidHandshake, + InvalidHeader, + InvalidHeaderFormat, + InvalidHeaderValue, + InvalidMessage, + InvalidOrigin, + InvalidParameterName, + InvalidParameterValue, + InvalidProxy, + InvalidProxyMessage, + InvalidProxyStatus, + InvalidState, + InvalidStatus, + InvalidUpgrade, + InvalidURI, + NegotiationError, + PayloadTooBig, + ProtocolError, + ProxyError, + SecurityError, + WebSocketException, + ) + from .frames import Close, CloseCode, Frame, Opcode + from .http11 import Request, Response + from .protocol import Protocol, Side, State + from .server import ServerProtocol + from .typing import ( + Data, + ExtensionName, + ExtensionParameter, + LoggerLike, + Origin, + StatusLike, + Subprotocol, + ) +else: + lazy_import( + globals(), + aliases={ + # .asyncio.client + "connect": ".asyncio.client", + "unix_connect": ".asyncio.client", + "ClientConnection": ".asyncio.client", + # .asyncio.router + "route": ".asyncio.router", + "unix_route": ".asyncio.router", + "Router": ".asyncio.router", + # .asyncio.server + "basic_auth": ".asyncio.server", + "broadcast": ".asyncio.server", + "serve": ".asyncio.server", + "unix_serve": ".asyncio.server", + "ServerConnection": ".asyncio.server", + "Server": ".asyncio.server", + # .client + "ClientProtocol": ".client", + # .datastructures + "Headers": ".datastructures", + "HeadersLike": ".datastructures", + "MultipleValuesError": ".datastructures", + # .exceptions + "ConcurrencyError": ".exceptions", + "ConnectionClosed": ".exceptions", + "ConnectionClosedError": ".exceptions", + "ConnectionClosedOK": ".exceptions", + "DuplicateParameter": ".exceptions", + "InvalidHandshake": ".exceptions", + "InvalidHeader": ".exceptions", + "InvalidHeaderFormat": ".exceptions", + "InvalidHeaderValue": ".exceptions", + "InvalidMessage": ".exceptions", + "InvalidOrigin": ".exceptions", + "InvalidParameterName": ".exceptions", + "InvalidParameterValue": ".exceptions", + "InvalidProxy": ".exceptions", + "InvalidProxyMessage": ".exceptions", + "InvalidProxyStatus": ".exceptions", + "InvalidState": ".exceptions", + "InvalidStatus": ".exceptions", + "InvalidUpgrade": ".exceptions", + "InvalidURI": ".exceptions", + "NegotiationError": ".exceptions", + "PayloadTooBig": ".exceptions", + "ProtocolError": ".exceptions", + "ProxyError": ".exceptions", + "SecurityError": ".exceptions", + "WebSocketException": ".exceptions", + # .frames + "Close": ".frames", + "CloseCode": ".frames", + "Frame": ".frames", + "Opcode": ".frames", + # .http11 + "Request": ".http11", + "Response": ".http11", + # .protocol + "Protocol": ".protocol", + "Side": ".protocol", + "State": ".protocol", + # .server + "ServerProtocol": ".server", + # .typing + "Data": ".typing", + "ExtensionName": ".typing", + "ExtensionParameter": ".typing", + "LoggerLike": ".typing", + "Origin": ".typing", + "StatusLike": ".typing", + "Subprotocol": ".typing", + }, + deprecated_aliases={ + # deprecated in 9.0 - 2021-09-01 + "framing": ".legacy", + "handshake": ".legacy", + "parse_uri": ".uri", + "WebSocketURI": ".uri", + # deprecated in 14.0 - 2024-11-09 + # .legacy.auth + "BasicAuthWebSocketServerProtocol": ".legacy.auth", + "basic_auth_protocol_factory": ".legacy.auth", + # .legacy.client + "WebSocketClientProtocol": ".legacy.client", + # .legacy.exceptions + "AbortHandshake": ".legacy.exceptions", + "InvalidStatusCode": ".legacy.exceptions", + "RedirectHandshake": ".legacy.exceptions", + "WebSocketProtocolError": ".legacy.exceptions", + # .legacy.protocol + "WebSocketCommonProtocol": ".legacy.protocol", + # .legacy.server + "WebSocketServer": ".legacy.server", + "WebSocketServerProtocol": ".legacy.server", + }, + ) diff --git a/gestao_raul/Lib/site-packages/websockets/__main__.py b/gestao_raul/Lib/site-packages/websockets/__main__.py new file mode 100644 index 0000000..5043e1e --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/__main__.py @@ -0,0 +1,168 @@ +from __future__ import annotations + +import argparse +import asyncio +import os +import sys +from typing import Generator + +from .asyncio.client import ClientConnection, connect +from .asyncio.messages import SimpleQueue +from .exceptions import ConnectionClosed +from .frames import Close +from .streams import StreamReader +from .version import version as websockets_version + + +def print_during_input(string: str) -> None: + sys.stdout.write( + # Save cursor position + "\N{ESC}7" + # Add a new line + "\N{LINE FEED}" + # Move cursor up + "\N{ESC}[A" + # Insert blank line, scroll last line down + "\N{ESC}[L" + # Print string in the inserted blank line + f"{string}\N{LINE FEED}" + # Restore cursor position + "\N{ESC}8" + # Move cursor down + "\N{ESC}[B" + ) + sys.stdout.flush() + + +def print_over_input(string: str) -> None: + sys.stdout.write( + # Move cursor to beginning of line + "\N{CARRIAGE RETURN}" + # Delete current line + "\N{ESC}[K" + # Print string + f"{string}\N{LINE FEED}" + ) + sys.stdout.flush() + + +class ReadLines(asyncio.Protocol): + def __init__(self) -> None: + self.reader = StreamReader() + self.messages: SimpleQueue[str] = SimpleQueue() + + def parse(self) -> Generator[None, None, None]: + while True: + sys.stdout.write("> ") + sys.stdout.flush() + line = yield from self.reader.read_line(sys.maxsize) + self.messages.put(line.decode().rstrip("\r\n")) + + def connection_made(self, transport: asyncio.BaseTransport) -> None: + self.parser = self.parse() + next(self.parser) + + def data_received(self, data: bytes) -> None: + self.reader.feed_data(data) + next(self.parser) + + def eof_received(self) -> None: + self.reader.feed_eof() + # next(self.parser) isn't useful and would raise EOFError. + + def connection_lost(self, exc: Exception | None) -> None: + self.reader.discard() + self.messages.abort() + + +async def print_incoming_messages(websocket: ClientConnection) -> None: + async for message in websocket: + if isinstance(message, str): + print_during_input("< " + message) + else: + print_during_input("< (binary) " + message.hex()) + + +async def send_outgoing_messages( + websocket: ClientConnection, + messages: SimpleQueue[str], +) -> None: + while True: + try: + message = await messages.get() + except EOFError: + break + try: + await websocket.send(message) + except ConnectionClosed: + break + + +async def interactive_client(uri: str) -> None: + try: + websocket = await connect(uri) + except Exception as exc: + print(f"Failed to connect to {uri}: {exc}.") + sys.exit(1) + else: + print(f"Connected to {uri}.") + + loop = asyncio.get_running_loop() + transport, protocol = await loop.connect_read_pipe(ReadLines, sys.stdin) + incoming = asyncio.create_task( + print_incoming_messages(websocket), + ) + outgoing = asyncio.create_task( + send_outgoing_messages(websocket, protocol.messages), + ) + try: + await asyncio.wait( + [incoming, outgoing], + return_when=asyncio.FIRST_COMPLETED, + ) + except (KeyboardInterrupt, EOFError): # ^C, ^D + pass + finally: + incoming.cancel() + outgoing.cancel() + transport.close() + + await websocket.close() + assert websocket.close_code is not None and websocket.close_reason is not None + close_status = Close(websocket.close_code, websocket.close_reason) + print_over_input(f"Connection closed: {close_status}.") + + +def main() -> None: + parser = argparse.ArgumentParser( + prog="python -m websockets", + description="Interactive WebSocket client.", + add_help=False, + ) + group = parser.add_mutually_exclusive_group() + group.add_argument("--version", action="store_true") + group.add_argument("uri", metavar="", nargs="?") + args = parser.parse_args() + + if args.version: + print(f"websockets {websockets_version}") + return + + if args.uri is None: + parser.error("the following arguments are required: ") + + # Enable VT100 to support ANSI escape codes in Command Prompt on Windows. + # See https://github.com/python/cpython/issues/74261 for why this works. + if sys.platform == "win32": # pragma: no cover + os.system("") + + try: + import readline # noqa: F401 + except ImportError: # readline isn't available on all platforms + pass + + asyncio.run(interactive_client(args.uri)) + + +if __name__ == "__main__": + main() diff --git a/gestao_raul/Lib/site-packages/websockets/__pycache__/__init__.cpython-310.pyc b/gestao_raul/Lib/site-packages/websockets/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..925a01acbe27449264f54f9756ac6aeda2bdc540 GIT binary patch literal 3787 zcmc(hNpIuG6@bN3S(0T*mgT)|xA#R}j{%815tD5N-%a44jQbj1o$g(wQh$U-zs!a@$~*uu1q z)JeI{iqHs&DxqRXk9guf~ znjsn5Oqyv6X`!v8m9~*K+D_VKf86SzoupIB4c0Z83rTN9q#Rb=pgMX&>pM z{iL4`kO4YK2I&wPqQhjEj*t;LN=E4z8KYT}rQ>9rPLK&YNhawOnW8z8qtj%X&X5^8 zOJ?aDnWOV$p57og=uL8y-Xgc?ZE~C5A$O#`q;;1rkOjI(7U>dMqW8!>sZUw==>zhB zE|X>1ZnReDDp{p@lBa8AP3qFtI^7@}Qf?CQd`KSBO|nV1$QIou+jNKQ&|R`i_sE_Y zLykj6ow5I!kQvf5dh_dw-Xhs5*`~J(O^0NsMO7=jPrUpgtrx zEIA@Ms*k;{2s>GL>f@3Vl9Q5Ck~zs~$r;I6$vMe+$s3Y4C2vXImb@c*7x*SvT9EBU z$tB5qlJ_MaNG?mRNUrL6;n|wxy5xrBLw!@|wZ?g8Ia^nLx{(tY?wOlUtQ z?jvy@i~EuO*uP)VUlsSq-$?1NeHK=_$UycJFJXsd72cqgO17)Hrd@JGGx_MX!*w;k zxt`zI+JE>`@dsJj`I&0cvdvsEDEi!Bj+l^LVqAgS*w&Sv!J=gp~f_7z!#>sO3yXR)Hk(~?mX3mC&@q~ z-Q>-enj1Ewn#(hnl+tCM@^yQL<7ma&dk&acG)uh2@2;BO7as7|t6J|}i9pTl7iU!$ zLs-@ZjVIS%xJC&UzKm+7 z+N?&K$9L^w(O`11#l4V7IpXK1;ka^I_Sa}O%<9GVVo^X0Hd>e^K!($dHGm?*;Ot^e zfFvLVXauAMvI=X4N0_yMsuj=%ifgPLj7K`^aA<-{eS_$AYceE z3>X270>%JYz&Kz6FbS9f4g!4kbtS{_J ze7(TS6hNHMqk$O+x0tMpRpGd-QgAEGaBA_F(LZm<8Nu3gKGIakLg;A2I)(ugRNZ%?P zJS)O}RegEmldjs)qy|>iCtAU^*%@iC&boZ~kkrblI4G;do;3MI&?^W~nn=@;p_@#c zF||=`)z=;R6+)K2i)L&}?NZQ78mkjLuP=$Nn;>RQYo`xu;@8bNIXYTh z&z}^uPZOslF{GF?^8mCtx=iQ0O_GVa6w zqcb9y2qnS^lu%+}yxpR-D1AyI)}oY^j_$N*C6qA*#j%1EjL?5@82uY#^e-GiAK)nZ zCyt?i;5hm_Za{y-3G`Q-M1R34^k>|N{)C&*A8|AK18zaT$F1mn+>T%34)iN(U0&5;&>GO5RXNE zfV1fPcpQBXPoVGON%S2&g}#k*=v#OieG|{1Z{Rufbv%#0hHs#E@lEs&zK!0-chFby m0(uKCqOag3{AF|>Atb&jxFcUg+rAtYPq@53DdG3yU-&O)>LT?3 literal 0 HcmV?d00001 diff --git a/gestao_raul/Lib/site-packages/websockets/__pycache__/__main__.cpython-310.pyc b/gestao_raul/Lib/site-packages/websockets/__pycache__/__main__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..28f937d7165fa317b3966913f365bb3c382437f1 GIT binary patch literal 4829 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 literal 0 HcmV?d00001 diff --git a/gestao_raul/Lib/site-packages/websockets/__pycache__/auth.cpython-310.pyc b/gestao_raul/Lib/site-packages/websockets/__pycache__/auth.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a096e1c278858f67cd4f4d0a39fdd4f118bca184 GIT binary patch literal 621 zcmYjO!EV$r5Vf7`Cf(9Ka6m%pA=j;7Bd!IZik1sPAQe!>Dw5@mXA@J$j%-KKa^;Np zh4#pgaN!Sf<4q1 zyfvYeNlEGFs~(nCmzUt|oOP~#lrfbtDVWVx+GIOk2^A~X+aO^x4y}QZoIlMhKvp}~ z+T%Rmp%`G?)xdRW=R)V8a_iVqLSF0M>AY(z!#K=q*T~IlTSF$4b*3vfz0CH838kyf zbp|Lke*S1kduEhSl^v$OfU>6l@k&Ss<{!R)wyn~S<>HRC*x%{IBq3iM;=dy^^;o_Ep_ELbDM|4ZAKu0Z!6Zs={{RcUx(5IN literal 0 HcmV?d00001 diff --git a/gestao_raul/Lib/site-packages/websockets/__pycache__/client.cpython-310.pyc b/gestao_raul/Lib/site-packages/websockets/__pycache__/client.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f84233cdd8eee1cf11a2405328465e8f797e6812 GIT binary patch literal 11561 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 QjdTbamq%~%Z;Idi4~fP-761SM literal 0 HcmV?d00001 diff --git a/gestao_raul/Lib/site-packages/websockets/__pycache__/connection.cpython-310.pyc b/gestao_raul/Lib/site-packages/websockets/__pycache__/connection.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..64d6b3efa31bcc8fbd0e4b940e68d599f1c29765 GIT binary patch literal 495 zcmZ`#%T59@6m81@6~u)r7cOSYLfji-jDRZxh6yp5Ofo6cE159UlC~3~U!zNZ#EpN@ ztt)=P#Kcx5u<3e5{@`7P>ExVeNGAz z6JFy%Q7eNuDC=>fY{t!!#uOtU&j-8?trz03BtqTB#_M*mZ8IUHVJ4SCk2h{9>IRlZ zqwz2uO>Qi`Q_{#x7B+}-4z?aSEIg%am}{9W!RTHlg@DWpglncl1%j2pLnFgI(CSYn zWP*o-e;M0t-L`F|=|g2I1!<~?JIm5FQz91&ZRu7`c|%m)Pg^@t)$rY>?*^V@kB6{Q zko|n}{#WD`sUwf_&*yA#k<2tGovfiSG8xLODnS^XOsChQL9zgCm`oL`isUw*CptH9 pwqn_mxryU!tvKIV(OW%Q+Ak%q3b^#HYQO0aQF20p8o`9nuWxQwlnwv@ literal 0 HcmV?d00001 diff --git a/gestao_raul/Lib/site-packages/websockets/__pycache__/datastructures.cpython-310.pyc b/gestao_raul/Lib/site-packages/websockets/__pycache__/datastructures.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..82e405ac1e07528a3e33a2cd440818723b45a116 GIT binary patch literal 7103 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 Ie=>{z1!@737ytkO literal 0 HcmV?d00001 diff --git a/gestao_raul/Lib/site-packages/websockets/__pycache__/exceptions.cpython-310.pyc b/gestao_raul/Lib/site-packages/websockets/__pycache__/exceptions.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5fbc7ac7421610c3aa043bb5e4cb44dbd5c04200 GIT binary patch literal 14800 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 literal 0 HcmV?d00001 diff --git a/gestao_raul/Lib/site-packages/websockets/__pycache__/frames.cpython-310.pyc b/gestao_raul/Lib/site-packages/websockets/__pycache__/frames.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4f8ad8ec655693ff02164252195322b2b0e9520a GIT binary patch literal 10104 zcmbta-E$k)b>A-li%)_eEy}WGtt?wIZHd-L(m1r@2&5p1H3cvPCEIJM@Dg`PLR{=p zcNda~0*P%p{hB6CryohiNrvo9JL8!?b^4H(&a^LmX#azKYx`2R)3mA6W=50L{hhl2 z2vTyBCLnS4e($~Kp7T5Bo(nIVO-cCt{nLND`qc}P^iPx-{yc-sB}tO~AIOq)Lt+w> z9jPgsvP@~kQA|ZVRZ|sD&D6v*X2!%bZpOtkVJ7fY9le<}lg*Tw68$wN-OQMoX4cFi zA7gPR*BmiNnt3xXOKlw`31_rfFblHuhUAWDl0CLrmL>WVb>mUpTaqi^msTaF|A}lq zaaCeTmijS5)&k z`7;$~&MB4!eSzgbC)o(-i!2ZN5*r0Q%?hAr*cj*(8wY)v zJpp=_9RfYaCP2@#!=M+~lc1krM?gQzo@$JkzrvojKgW(yf&)_?S z@5}g}#rGV(=kdLO?`PIB<|}yi`@FQKnxD6)?N{xKSj87?<<4tZ$usO2R&s1h*^;x?W%f4bSz!gyt)?lC(|R2FKpmGY8EES26|3X>?AnJX6;-wJiAoUc?%qIO}fd^Ji` zkjPvvE){E)h1tsGQZ>xXE-lUva>-f8^X*w0KX!#%O*}+NomM*hQIWYsa{n9>wq(jo zf~3oka^;q4YGw>_ts<2$bu-B{a4W{sXxcszP2ad@IW5~aRy=OJVJ}y``nnw$EA&c# zYEtFI8lC{zRX`@yU7gZNBBYSz$yBJv;v2=c z!ra1QWvMb-nXi==7b}Zl{zkcaV_~7PxKz4aBYCFYwdzuF=|;7-ID7MQm@m#87%wwd zzFC}~>$T4>ROaVq->Ti5tIQXd<|^fIKck$Wx-8ms6cgZ4;(_SUM^a19J>^LBdKV#X?x zv|s~_HP$e$zh9j~gVNsreB}>|=JL&BV z%d*@KS)MFA$wpb>1I^6YBWw(R^b&4 z+85Xul>0^g1SV<036N3-J_oRMDkVybLBJc!t-$uDMV?xE!mLai5k5~1%1KzJjh=}T zTfA4d(r~AZD|2P+YOq(z{d?!8jm7HCbB~mt7v<+4DZe1fFGS_y)fLy-G=epoAgEzG z%r`8^pljRAW>dz}nr&P!R$r$Mrw8)q*zRhuW+uVbU2W5&!vHl z6};g7n(d-H_l4r1AB3&}$!=4=huaa$1WJeTh)eAg!hpZMya^VbHt1n-Zf%}6nsyUn zeXn8Pza47Cx}{KC_Bc5@Xk+$Tn=6*}m>Zq+#O*e*7V_P!PNtQSw zt!w#kUaR*dHrmy|A1&e!c=M0L@9D-n+CH8XCeU_|D-%*zV`JN? zV3f%nxubN{Rdq`NR}vUSy;H!RraKzwcqak+#CPLR_CxK`Kus5gq!^a5)&jbo;kL!d z)dYHO06^ik2rM?d6*Lio-Gc&B{c<}dWLP*{p%lt~n5=tl&~RHeBbzUMOoGQJKz#DY zB=Q^TrG`EP^5eruya!>to#MS$_ z3N&O3$qa_ci>vUWA8hc~{v6qlH4x?9LsHwgLQdjTtp=T_)ut{wUfpv1*QN%|LoI++ z`PA9*s~Cx2BO-jgUmc`GR+!-HDEliaFN1{f<%VnV&9-vtR9l^znrbVjjkaRE{ZPI= zITorw9sYT0{3?-Gh-9goMywBG0wcmWv&qxqIjYGFD|)Eif!XFG)bA3JL)0Wjpk#*y z;0*PG#s!m)X3+Z)I-g;ofR{VZUC_`9)Y5nbUn8Q46>YPvJ3PU zwcZLsy=ey!7T8$9fB_dmIM0H|-$GOWH4rU!QqI9=9amFwTrJ22`J{YEPT?7sm3Tqc zM0nm_mD_BkFmb3ZCyD#opd~*Ho$Rf86f1 zefad_-yr6GyB1!bvA1kV{j-Y6hX-JFx9F5(02 zSfH^q;3xaO!jx|I%h~%l^>-5DoeR+z9c5kRZ=zjhOMYKQUG|dH&2@4t2Q%S4X7(du z_HsAp7SJk-R_~!z{=gbX`>i@S|FvZP^-iv%0s_At#3;3;;9Uy;CVCbIJx2g@xj`<5 z`agvMigiY|ly0mebuvinon$A~NpHowdBCRv!|<1@0LmaiFnd|Lo$QWwM(^mIJl;F@ zHKm)s`~6OSJGle2>Er>BL?hs`_q{0c(lM;xVBieBuK($)_Ox-`deCULngchD6yAUr zFIKm|)1GEd8}ki6AbYh}cG{pwNQ0*8f`51~w$tN))uByU6s+3wYiT~E$`yf?)5e@& z8g|ol?RvyUUz~6-=>0|jmIT=Rm6qc?Dkc4u(Q{wOW-zQUtf&0_E2SN?;lIA~PM;<3 zyt9AR`vN}u2cww=IPyR$U5Fq~YC-R`KD1?s!?hV;$G@~o#&=Q{xQf6_99E%K55joMrEo{& z8K-ViKu2(yQ<#R6i^>TeL%p|EjNhPatiEQ~*Cz|GK90{BHCrJ(79 z^D4ghQVO#50A1H&QC1H*!JIaVXDFt)eOIbY$U#?fyuP z7PEvG44e@%AH)-4&McXYlQ0_{X8xNA`k%BQv zz!W6bC0^}l+vy;)on<=t?487nv;%)#mfT}#kwgo7&_a0i3@ti|9Zj^!4(ig>n`QRu z@YXK}xovpQSc^{WIy&`1`ELe!nngzk@F!hbQL391Es~;zwmrHd0nBsOH85*{G1cBl z?f(Y!MyW`H&gxLA zNyH^`m&hHE1|(E!Yk-w&f_{l_P!)W9iXuM1Ba9=qP0?bd;f0AAIu6fOK2=%yB6TNU zIgANr@_^DBp-7nU?K+OTzL^fZb=!>;xd$Mtb|B)#lPP|ih7fpBrxXDePUWp&qZRNr zmHavp0tDVAB0Td0j%~7k%D2ZMQNp#=3#7<8R9ROtT1Hkr$tcSIWK`v+nx^~&IXwj! zID!^W%SWKN z8x2d}2*`y%07Qg(Ai4LTJazIX#8FYO*dMYhk29r@%AEyMy!Qt3Rw%mCdk48wk<}R* zw>>f3!#A2OSbF#&XLo7K+K0&IanI>R7b$A!cbL>&1p&rd$vj`A*pMW5`Can*sP(J2kOaU^vC}lYWw(j`ADrcbnjHFJx=XH6{nz3 z@%%7hyZ0L0Lo5l(#Bb7MERZl=cP!t>5pd08!YcCLraDsYA)6^Ub&|>`yx754M5j}d zrs#Rnn&_{frWED85?AE>L*;tzC%IQeds^SV5BU?9$o$dy`e#H`$5?{th;PR1I2#xD zjgWd=dptogQ`)vEtn7o==N8HD$LUD28ldeSsy<0{_CcFHyocH86|+5 zHukPd_MEq9)P7x*>&ZF)29_3T)K>@z#qwwhLIm~!gNla4kXd2~K^P)HfCdfmlyzJV zLG>k`cNN%RsDD>>Cm6->`t?sS4fRH^K8E^4Aa9EnEWV>40E4!O7$rIs7u3J2i3p&| zU+pN!6a1*XxFB6=rrI|K5c2$rVCNlJ502`$#MfAdkyA1#t`bdhpw_Vsra3rO0Lozl{L9|A`?H$5-qV zX)~gIfNLhN#{kseIrasfRutvLkzWRmt`=j~jN+UhK^UsI= zG$5Ur8;)}Z+SNpKelS;Zj~)$R4GuUxos5pJ(aGe-(v>r>3@t#MY&ZwA%aN;6M6&(F z(1gXM5{|MwnA9VfC!d11h|qB(Lb7p6y+niz1MZ?XDJ^^hAzCC*IGE6l=Rw(_lfrv_ z*~w%N!Pke$#~FYi52imoOTDIq&%aNv(PvMu4P^R$zK|8tOLd3zRNId2NE77YD($^v zYjxn>{+xR)xCFBzkX!KJt9WOmIkY_f9+BTABG7yf(h2atO)Df6(qo6^_DHlQFhg91 z*qqGJBa0*863Re!mS853?@9jQeeg9#@Z}!~l*n_DrGAQj6A=9_p#9-MV1~SwJ-HO9 zvSv|GCK3|irwAyweksvC@QTN}D*T&R8~-*)d#rza?%kTwyvjf!5}mUtw&n!3 zoY0mc!@Y=?AH@_%gH7GvKv1Q%;YTNg^jwqfwxfV}3~@RK9uNE@$H8wu5_4`)qPf5Z zN&Fa*3=x9}h3ffnA{3hE&k~_%2|qz3PvkiwqeO@&kwZC7sZ&H=ATmkhMIr)ZPE+a( zktrfC6FE!d9Fg-xE)e+)k0i)Vj~pg zfS789d^{0{{fz!nH~cwfX-|GC%?>*ycAt93TkrB{`Z+RD`lectz*8PBW@ zhmg`ip8fv6Z{Gj+{a#vwgZUJmzuo!s+5dh!mHHQ|#2*8dw^J$2`426X5-Fk8Qw^=G zX;jwhdRgb+bUDqxM%m!sOgV$!bUoYXEBEnOqn>N@m-`#}a=tN89za`0Wb1{-V0o}n zEEl=nhcQFtp~i4|xUsIhuCcznzOkXap|P>NQA^FHMDC|rd6S(N{SUSBW|0>If2fIu z*eEuAnJsS-O|eaE|I#RLwKI#`#EvDcyj`@!PVo}jc5vIvXxk~;Vwc#BwwJhV587Vl z(yJ)Fg5KA}Ui6yWwhwK)xU?Uo-QtcoAP%B;kC5V!IE>P(!VyQsF_c~tu6SLXKxr?} zIEfkiL`S?K#?iK4+!d$A8I($5Uc4pVM(KdKC(eoUC><2{#RYK@r4f6iJSsf#1Mwkh zW8&AuN8&O{hwMY;!=fv$h^wd_5ewpDaUG?j_R;b&@tOEUluALOrUAy15uUpbLs+Qw^1BywNbbrJ9 z&AV2;Cf>6xVatmx*|1!HeWLNYRqxoPtUr9d)oj{T+UBD+z5SsJ_guT_ps1$%gHgjJ zt6}@=qv~Z#qQSMLKXA1(-Igu4Rc+P%p^w|M5*uF*8cG9xe!5eui^^QfasA=8B^|p` zjrR2iRZB-r^M_Q`4v*mvso5(H>(mG*w&^-GCf>A(VPLs-CD!WrJJrbf`T2^~ap&x& zTdUHbY0Ifq{cUP#>S@WE$M6rk)Wo&Av}+fEq77Dk-n#EpYRziBBkW4MC0*a(k4`%- zaKsJiTGREjj$Q3Y+c&0Lt-9O^wE09x@tJ-M*Y0kuzE9WkppP02B*J@Ry)u~T>Seu@3YOX!n zwyL)+^kB}k)2iOKUFVSc1ZS*$Uk+p8efV=W;-wE3wSs2o`nUN^LEESoQRWeTR!#BK z?Uu8g2L{cqd8q|%(J?K2s7xqh6m3PJtGTIe>LH;ZC$y_5ceQN*=hjhYFj|va(UEj?XKN4afli*Xn=%ZmGRc}t??;3RW#gg3bQp6 zTPK)tgZ2aP@wSEOw#>F|-!>a9X=4|wX=07oSu8q5Fq7m@x^6d1x;%_8lt<`A*io-T zDEWC>t-`b3N8KT~Z0(oF@#AH0PPpdefdt$LW~AyEZYBzfu)ErlzMyw?p>;H;@ImSU zh;~t5Qt6k9pKB^U z1qi3@s@1_!YHrZu)MlHt8Hkgn8$mAZ_f-VG3(kHe zhcNSp=yizW3i*PT)8r`>y`48lckOxgwY?Mj4jdf&v@+HG?C|It)~NT+XnFFWKcFb6 z(u8o5jL$PgAD9YrCAg>)B!q?3~CT*3#0NmqMl03Yea z^b!QhqOp{kA(fT({xpUc3zT&mGBsM-PwUbp(IMPzK~}yl_^D@rBV-JY*2)eSn&bIIV%KWYVrbFylqcH z>;bPpNI!ZMWKx|h`5A^(mF$G@A)yeG9CF zEhJ(y-W+3AaS&7pZ$X;KF)cFzuYi}X&D9)8Kju?O+J!{(j5$8jX;#Ol*7)kFhzUnS z=8P1eW6rc>u+3;dPB3{}0(7;k?e>g`iHRd%)&ir33}6C(|4gS|XVEGT;*-J3d{9Ez zyQo8HUBu6t{JKGzbP2Tl0R}T#@_jkT+l`w10j-)>-vV9JSl%Dn-!~(%Izr?>Ibted zdrgVj$>qL!&2evDo2-(m3m@Xg2uuHj3JeCQxrb1CFd8Z~@r zj<&2}6($|R5y>DXTgRNQxpPdJGp%~PHBUY8Ot7~cSC%ZPjw5>rr>Z^`P%O28>KWw@ z8%ER;P;SgX1L0ea1AA1MaMsACf-x#x6I4uA81KJgMg;Ts^XZ8}$ebhQ5jI&4ScJ$w z{03NLxWK4T^I)qyE# zDLjCC>0?7>`MoslvNpmp^;%5hGV)>klV!-pjGqM>0WNurT1X(4vhqVJuNa%8as3RR z%Qxt!SHK4BNtE=*80#FstN2P$D?T>Tx;CumwY*k9shA$tih40kJq1J8^nV}T4txJ9 zhA;0_%-ib`Kf!DYjn_vnV=nkY@i1{@dO>qF7)|MidRJo;N%Q_X!2l~;1a|CqqW+iz z5jx!~#PLEbCYY;ayhxHsRzDlW-8M984gNYbRezfVAUYHDRYaI<2HfC*R8{Cn4~T{f#x zP$Ixf$OZ4nS%=pqiJ=x64`2>{AtUvvn|&W^Rnlks`IS$BLL+izxP7fDCP@Nbu0#KS z;qMU{1jRwFduN~KGr%Z4^tArcTgy8Xd-Ozip1>U;o*Z&lp2g-Q9Yhu(Ra4gcyEvjvvY+a<83kL(9#K)==h0CVLflVbh!G_%OKeru zSO1HL=0)U|us2aio6+LIg!vBstA^tUn&k zz+Ta%jX|N{jR^2P4#qx#`a-kBaWOSP18kNknp)7vk%%aaorHk;l-r>tXe|SpF&2%$ z#R%yVvz*Ot26RV;&I(1Ptu6SzI8nip4TJfxb!Tc#4rD$TJ&_>Vzp|Plt%yXOkiJG> z^4c(+*`QbC>wxsgUgv|c#{s#sHg!59FN{8ywKthOSCby0T3)6XTUiB^U*}pH^-Xz-FH)6(G*qGQ?TD=%e9mBI~m&rs+&&>FQN#&|t z4zaDoDc}MkkKX}+?3?gK1KSj^fg-+J6LuKN|6cAKo1)&{f#0^3s*kxj_&=%nkkW_o9&%!AX=`W!#-^E?b z7F?LYK`^{3!#lX%T3ajyWEqAS$hLn{U@IabmhE9=%hTU9tN6<3NuxyeFsycs9n)EO zi<9f%DQbMxkwTEY(AAoJ7bg(=p&PlM=yxc%u3wT>Ol9WA&96RoSOM%}*r?cEUBR3t zu?GLTefIYW%H&FCQ`z*^$JuYa{VLRzJo4D-daG*HowF!}Q?ZM2t5s_%F+|QS@Je!r z@v}k!S$`p9ZD(pF%%8OZ&oXKh>+w&mHMhIrLKH&R6{|Jd8zgMb)H!%1)wn^+&awP~9ki<^M1x?0M{ZGY?-mk_vg|0#DDQ6UaZ5G z2y_qy%j@(?g3J&6uh_9z0Q?D{D1S)f@8Y$*j{U9}_*N4&Nrm$um(b^0OhB$zhh7d6 zD!zjTjU6%c#QqO7z~-a|2+tMDfBO10<^Lvh{ESGA<$qpM-7mz?xV$Nn2u}qZiBtld zL%}GL29y#(;?jG%3wA>BoHyqWBz@Pq{F(<@)djrwh?HSq$7jh(Kqzo5j5y<#X^2 z&z+5e)LT=u&VjjT;&_?HJ_3-uyn3kLinA%hIyd?p9Omu`4q{%WkAsNc^ZfL?DT^mhU~CI%699%>8t zCEUQ3hsi;iHVk`$x#4}$ql#|axH0-pEZry~Kn6AkAJ0(&l1YOC6NG|}D=`y3*SPt< zpj&~Nnl0&`jie(iJXd9KnVbd@%=R)LU%NPZ$}6aKdHNYI52b~C6UTPXfMn@r3y@o& z5AO@wd!e=!o&QQuBmI!;&(TdbwRszFBHI8t2gp1a9jknH@X+MJGY6`KtMDO`jzrv> zs35HLX9*Fi~=j`fj#o+K$_$mx|ug>9Wi6|w= z3`)3P-k>Y3TAl93ARVkrActdgCccM>XQ^<~LsGo?L6!>E&4Ni(mP!VpjK}f|tdU4P zq3h8ESJ7QNZE~K(_&=aPT*a|`xL$Jd0nhE6U z((`^i1(i4Egq%*(>9i>i5+s>O%2C%GgNZZwnQ4-V-p2SW2jtwa$IWHU^mas?sS?1f z8A_v5BRI_B?TJQUDf`6nQO+m8=2dC4B!%~LN$v;vd3&RwA$-T%xZNf2#z&WiIB4i+ zaHDKX*^j9zc1Xm`tjxskXVr#&|MZFD!bTE46 zDkqi=98QciY5p#H?WPx{$g0vFDzSGS1POW05o$H*#r;Q73WJDi_0z~wP6Ziz`dQp_ z8?Q7@BMp^Fv(ic>oS84{e--6tS?SUIIP{$O# z$nD@89S*f~^s~GwN>A<+M};^DQ{LDU-l=f^U0R8}=kgk%2p+MSJ6CycYC1VOY{SbR zQR#Dfk-?^fIfJupu6v-s6(NLZD{kA)VAP=N|{P=!fY^^zWEAU^g_*@qiGdOc7*h*bZ*Abg+(dRUE;Q8iA+ zoE;E9WFdPOa8^fmn7zFuRsX%BpHwC)g6PyU#d9_vp7~qNc{i4IZJczasqNDcqw%Yb`Q~D*Ds6OAP_mqrGvuYiZ~M_PKNM?aITwPWVRt)IQeoR z7|UA4|Imz=fDl*aEvR^brlS1Nn*11?-9B=ziJe(5De0tj*ygT9uwHZB{M_V<|9{GB zC}h4mH=a+sQI;)QZHN2MNz#ERYhS6Qya>t@B2eT+ta-Kto~XIQpTST9zJGM{Ud*2|T$ z%$tpY^?}Nus;nq1{~NV3#H?GxJag;7pDC3QuHQPib%+&Mk&k?(uo55qO8MH@)+&eN zn)05SXZVr0Zh#GLYxmX4(WGRE4a<^aNyz~=B1?|5Np^%C{nDsB%Z;rQ?AW$iIVp3; zk+ZpW>(nKMJzzxMjpqWdxs2P6El2Z3y}4p9yTYzFeJ)yV16P6)e7UyMv^gEJz0N() zS>fZB93dQBa%yW{!|_%HZ#A46|3(e7vL#(mmdP1LRZ+qkzEQ+MydJ(kJ+=6Ohnrg5 z;0@nhyx`W_>%8fEi*s}D&P*?^;EEi#Dx7v>@oIf((X0D>tc7_nFZD?-V%7Islau4E z%}`~pVq{N4Qp||Mls}H-#@MOno`2!R#g|?_J^st;r@`kFV`rVQ&iS#*&C}iyYOr`` z;_Tv`S0~06@4U$u@4Q8s<>i|%*HF3RG4S;co#p~ET&bu`sc0YIOd$g_ZZQ3Ag&BBf z@XkUG<}Af2fz;&%i>lJivou3{JA{XkNDEO zBsY82X)v{ds5d_P^FA(YsSPril0E1`TaLdvWye%5zs}(LYK=Cdi*#GWVot*b z6Nt_6q}OW1Q2mZ^LDWJFVpX9*LyIhBb8+?T!@-$5H7*CW1AEqO^3baCUXd6;<2^)4 z$4Er^?JzRFvZXTB*S0m42vo-*XtSLw@%qP;q3pV6Gu{%s=J=eQv)dlG=eYp0Ul*?L z*4)N;a!RxS?Ai0Nsc($X+;Bt_oA*M4a^diWcpRL>M^TkHhNXaP4UadLC51-GAry!C zWD&o_r$<`D&@@$3jqkEXXK2q-!@R)6)keL^ACC1z*Nq$Cp_^yyM6TVS!#X36>`cUb zH*Y>Xv43?u#myQKckjs~Lu&z&d;V+YqDZsex`_3 zKgUY<)q5%{%kLJ(7@+S!337pXpY+C{^No((6TQ=O-4P%M$Nqkfq1Fqlpe6iw?3JF9 z8C!BZ&g_~i+%^PWPqam?6Rwlogr3cpeEWmBt7DO5V>U!0L~@-GWrFS?C^zj52i%n` zaA9@VAYf=a<{{e=+~#*$yhhz2XKQZLcQB0GY;4-+=clh+Nye$II>JFa;f>qZ{8cXO zeu*3h5?F8#f)qn-)csZ4YeQtOU}ZMOQ*H!e8#aKK5k!G-Z5ajS6oIi9L)id<=g8H z%@r+TS-9)6BM#ZIq3XD^Zad33347-*B5j!7<$1A^5|4DH342cU*>N`oU^2&-Es0RB zUL3-8pGak5exiyq>1A_C#mJ&xwklRXJ&*X4e3!x7o=rrTawwP_c9>X?)L`XZdh zoVj*UBATh*iQ%N0AQ8wcqTO19t!eR}cfVV$E=_&tAQTQ?{q0 z-=&#_&gpa)xeQ_p7dj?kN;ErXueiP)W*o0puZIH(TSZ*;aA-j|O>dctv6&_S6?W7) z<;O)}Xez+*3Qo+BEbw7@8mtax9lOe#EBj%#vqv3a#%nd| zerWaYMj{_Eg3X1cs7owo!UMCee`USZ-~_ZdlQ%h<5A|BZ6VIc4r5H~cO`?N{!)$aG zp(&h=D(U~w?47E327Lq}sND)PBj;U4jYp77yQ@7ntd>D$Vi@=Idga|!PPJJcbwJ5R4RItIwV?i3^u60w!6Q9~XA&@Np%mxmGN0c)e7 zM>X*ytS!vN=ep~h#M`L+E&e>hRKvNlYN&lUk&N<gus<}5lOEF z9n)wbG^^F7v(BqkK(Xq&%i0af7pv9VZKn~}KX)=)f9t)@(`YE>d3dC4s<{7vy3 z#taKJpnESO1u;d_($Z7gAxtj_EJ^%2i;PDUXaOjhrl}TnO)IKe4!8U>*`kKZ9Zv>d z@6cs7koi7n4OpE6#5Mu3^T=Ds7hrA|kuL#m=U|%l0Jh2V`5|_Kosh5P1=G`QA9d=J_p?l~@_5 zzToFmsJ<8&;w#?@te^;qtOff0Ol+N$ni?DEmVoN5pn%%aeM2Jqzt^7{+3#RXVzM6s z?TIbzh%Eu#{s>_CA1h$(3m`h`K5soGicZ-P`(SA4e)_{f^ge5fc>F0B)o%LrhBPdA zb1$%WBV#2pH&S@^{~ECO=$Ov6_dE`<$I68)jPHl+S6z3l-P(=W<01A3?Y;Tw-A9!v zli`WI6LmM+nRqJ5AE_#d_{B?*s^Z5;I!AW^{#2VOTZ~exm#Nk1UaPnPDclrlCl}@l z7ZGxQz|f=qF@R%xjB>F-v479WiHU!``$FmQy*FCiD^tFUF-yefE@C#wzCQf*ZNcJZ35mz+hKX|ycfpuQ6FU8UT6 zln}hC^mIJgp5iA|e3ps_2&YGzl5nJAQ2|DTkQ0~Dg8aquLjw0-P{&=$mhjy_6OIEE8H zrV1QWkbcaAF^E-LlX+xqs~&Yg6Bvoh6atfo8L(OK1cjU z0R~90A@VPnkWiLRge&Fq<6P~Rqd>?`5+zrA0e_N2zeDB=B2Ty#d_*podKjd7LAlxL zYH+~oK}5J39PoM!z>Uop;ea=!5r8le9PkE=0Ih3$ZjccW!1cbSiE%_g5Sr^)fesgW zhN*DJYbj@ZcJoS1{&2hdIY8+ijMvB+@0(lsAOlwT*w4Zpp9@kZL6F_Dn7OTXvwi`s zvS_u2R(Z6__C}Ge`69E>3Z+}fNhiG-m;mmuw{Kg1i4}>(MoZ4`cCwY!i;0~YQGweW zlg{fO?4tgE$!ay1JaBNwC#OYhPtW6+PKK+W!bQ6;Sc^LEkM!g<2~xSUoii!<@Ob}h zyk?1Wq&R$#vxf!&nEIfK+@Qt*?LS5!n#e6`X~O^r7Ft-) zuW*{`5*J1ogb-xpbETa}*nnWc4(~(>1&K!eYs~zG#8R=1Utk(+{1LM817NB(%^Sg+ z1kfRtnJ0~k==HX0C|&%LrLBs;2oT9eEbla8hh~rg4$FTH(S!IL;)Hp{*C;9on_hvy z%LO^;WBfv8TVtgoN;md4u=1KA-Ve-M8FI}1TJ7c`e1?KN#u|>sf*>;4f%{q)o&r|F zT5dGYz{3FH6j+BL+R(p+J|lsJJ_n;3*&9L*f-4*7fz?|DkRZO6LL3iSqW_SsWNW2!{RA^DX|WNHz=iA5x%rHE;ysk76Fs$^j-0tre~Tp5td zu6hUu$k<1vOnluhtdt=jQs1GKsvDMVqd-m-OGF?iEJK`9U?=|GEfE&5xZ5NV2-|CS zUo05vaL!Z*RWg>!co)I>gT(ug6eKN15GEOhL51)CEDxi#R@4UZMSaSurlSY7dn9ON z1PMq2NVjfeAJ9GS4IO;EZ)ufEi~C9&d4$sb*v}@BgeAv(i2@JHZTu_=fd@g7J}gkg zQ$`W+lO@f?AIx9f+w{Z@{gZ{u*Ji8ux!C-b zYqMdFH$STj*vu{l6y)j^1Og?|i6zS2pyW0s^iv}+B@{aXngSS6k(wmTCwrDLkH|tI zY#PaPx~ddmB;M}Od^-x-kj_xN3rLCz#l@n3WN#CB0MY72GpiH9;D;#L=slbrHiivL Uw~AKD09hC{kWtGzR(|b&0SE&5NB{r; literal 0 HcmV?d00001 diff --git a/gestao_raul/Lib/site-packages/websockets/__pycache__/imports.cpython-310.pyc b/gestao_raul/Lib/site-packages/websockets/__pycache__/imports.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c5c4aae3fd5f4976ea7463cbb16eea80fd04acde GIT binary patch literal 2704 zcmZuzTW=dh6rPz~uN}upLz<)ws?aK28cV6UNPra5s8^u^GF1zz2(H%aor$x}de@p6 zH^ewRG(~YA_yrXb;*CGT3;$qWdFo$?RN$Q1b!?Ze<=J!2%$zy5@60B1b7cb0Uzh%D ze?3dc-^ffpHZa!+q3SLWoNyYE4&9*?X%?{^25&2}I`)ojWOh{OI6Dr|g*M$O@+CgU z=bzd;r5QHE_%dJN=Xv32VP}T3`=ol|1)f!<>8u~eiS~7v#HtQ_>5dlCZ$$!V>w0{c z&PIO!&> zRlQax;k`(>$?k+b`SEoTYOuiF-0j7|X4A{F;dxCrPR_(XWjTt%RxP8vOl8yMiBRTP z@q-`e=+M=Bp{l)39oQd4iGs6q0;>eru0_njiI&*&ABTy|O_C5%lG54>eD~kY4h-XV z7SS$*D%NhO;>#L*>tgK)UawlI6^S@4k3C3>Z2{G%Wwju&B4i1N8B|_~#A6YqPUgiQ zM4kuf>Ajnqjc>s~)i@B5P8zq8px1%JRO8;g8@F#Z+CphR@uc628efI2h6=T~0@b6AO<}zPr2u90K-G+dG1xCE-&nLQ>?fapLUeA{79`=H>SjSe6RjE zCB+SETbRi0wN0Qh*yUDkuT87K#kE>(O2N_elK&_4H3x!@>0L)>DyyRg0?0!2VxP?$ zg94yW!~(jHA9Eu!ApyRNnD2`2X7l4uJ{t9+dVle(ZW zX;pFw4~XzS%pOqPICiH*DeIy)bTsG0zOIL zB)BRt+7rRYAQ&B){l#qRJ(QZ{Tt>?5!RX61956c)+pr6N^%e}Ji#Bw8__Gqc7uW(_ z0D6g)E-tc)RV_`JXKpEDsL_gFa$~qvS-lRc$1|o%F5nttjiHQFt01eE@jl$`!Iu)k z7sDmdn)F?xvXl literal 0 HcmV?d00001 diff --git a/gestao_raul/Lib/site-packages/websockets/__pycache__/protocol.cpython-310.pyc b/gestao_raul/Lib/site-packages/websockets/__pycache__/protocol.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eb067b0a712606984f13e171672dcee3a5d5361a GIT binary patch literal 16887 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 VbXLe1`fwthPKPCNd@@1z{ukmDx;+2@ literal 0 HcmV?d00001 diff --git a/gestao_raul/Lib/site-packages/websockets/__pycache__/server.cpython-310.pyc b/gestao_raul/Lib/site-packages/websockets/__pycache__/server.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..57f3c63741fdd16dfe07f39b6f45b55f0b164475 GIT binary patch literal 17113 zcmc&*U2GiJb)K1>o&DkRU!o*icE*xrX>E#<{BKH8BuliVM5bg?w(F6c<#1=X9BOBW zx-%<^+oh|RMv@|R+N5aHGzmhcDOx`yMgM>n=v&dg^Ege zvLs7xO0I0kGF>Z8#ZdTn&dBj^)yVU2&CvLF!6@+WqEW7|-xF1I=gM=ZxoYJ=l-%|Y_j?MSS7xQ^*ax%@RXOZ4TP-gz1IKIm(^Vxby==E^W(FP$i?jC4w%w}R z)m)f6+gc5a=gel)oNwBpS~vaR11y?^M+z5g)3TW#j=s`v2F`NRe%)-gZGVa}k5vod z(8bm*v*}nD%$DUZnm6pQKd#{qh9f;c`NQErRQHPQ`(`73Eyactt^xrCz2%Yv?CI${CB@+Gg) zu-PRCEY>1MmFfAm&+GbTGZYU-tcynid1bR+x0i$34SO{l5FCr|)=6BlA5vX>WdGYJ zZOM==3F<0CRTc5iRxP?#zhAIk_?omJTf42XcL$)tlhy;)gYT+P;*-`x*2B0eSf{K5 z)K?wLcRs~_^(Yz#;b$n*cmVfl(vCBHR-?Ux3C1hUr7Iag8!=9h@>No5_ z_qm+t>FQ6E`Qv9HCv&e!%ARz|#ZBjlSS99Zl`q5LMkZgZd(d)tTLBH%*4>Wd_Y4 zt?lWm?zziO)3)^aRef@y-KtN{F?%AaO*=OmrqjY8roLJ8Oer6=lV%c?On?7jo842sSU^Cig zGlSSo>^-$iUz0lRz4OFy5)7X=;B3GglWj;hU2jYG!J_Be89qQRNIvUL2Pyyov-nRT z_7H+VUo?GDWxBn>;5&^Lwi0IRQHx2}4i+J`+lZah>5ga7{7I-dojsF|CBAo)AtCbv z6Re;i792(r${KCb)(mrKL!5-XI@s@hz1A1)ZKtWV*uKKvJQlt5C#~y>w?3!g73)a>1>p<2o4{n|vdN9P)3zWC6*4N>y*a z&;%2<6McEuI_PN-GtsaE(ZP`eEB3t4cPM{Ctkr?)++1%Tz71i;(oR|)v_9%XZ?qlD z)=y1L9K-*>TXyRHvFP6Exkw8G(`iDH_)MoANg9%1Zwk>S7nW1i2`d?4^TUztOCRR> zdW6N8*P%wE*^E7o?fCBT7*bpl+oRsajy7>c>_OMC|GkA8+Hc}-_5?k4IC)G6Ok(&V zJD#*2r`G$^)~RYFO^#BNOEHqUcCA+g$Mqc_ehc+!Ut4LMK$lFX;uv*acA!~e8tgb# z42xAH8ywY#8ndBFtx%=O)(J?_r+_~)tn{5D<9Z1ffwV3y$$`?%ceQT8lHXHhsVjG- zTCS_0RJ%Ep`L1e7ZJ9k9Xk8$@uDVg^N?mEGxT&n?fcd0Z{#VUSS5-sxArp*KO$2iy z8S$lB!uqwBZD7Az&4Gf|YV2WZL5mmeGH*dpNajfh>u&_|8L6B>; zuvDvYLszR+i=hhHS>XNhC1$j8oQ5rQy)Uyb;MV)n-B-^|UVjb3?_XcBn}K)zyjSNt zgMWQy=EbRV*Bh{!rdMN7r0bWQ`RhKc$kAo&n1GM;Osqi1gaS=0ud+$>Of2?w6xy&N zi+|dOnx@H`T$Gg@)l}r-N9sqNJ-wnb5wj>PFoE^hK6Jw#Kv7pv#2-2X(cd{*Q5|3gp!nWN9rgOPzyQZMjBkPU|c!sq7%i{E}+P0984{ z@#sSf3QNVMlBIU@-^2Ol2f0n<4M~&Ulq_v6|8^d0&c9b=^j%(UMSVZMt?yT(z6;y> z?u+^^Qr~NuRqAR>6{~zo>T35Lsekn^qLIF^ZKSV8BlX221p~ogH@~i|6}pA*No&P! zVQI+HAjvDJ>|U4FO2OFDo~6Bba_~L5Te5~uN#IV2cw?3EJq#Wx_#VM`j%M^5GLxaJC2JHl zYE)D1s;Jp@N(%N7K<<{-a{#Y<%)fz{B$nqKa06_d3ELca0AEYLaP{hyl%y3W zSmb88O$HCv=fv?78FHqYz71p$d`brMvaUnLxw0^y352}gwo5>U;_lSmAU1ry9 z=N6#v0`puU9USy!XrB>Fm>z7X^Atnvz!G53-o=Le2^QkmlHkhB5S^h%fTe_(2}E%f zC(WXLyAA`h;B^ka1e*dP(T}%mw;<%{_$gYN)H}*VdAc*4b`~AAJ730Nw3;|qL1MTv z_z=oWH)6lnEe9M4z<9o_rasTS72KN8>FJgY8nO=wfILwSONr$O3(=9eGn$f-@TjAl z)H{1=4um60&meqRnXoTD0U5US^~ zHNcaQ$bV9dz=!s}9-nwhyg!%Ea?-gdEWVZI#z z8i!@Udkd`Q=Dk{GXK4KMe0;GX)&ynYa0-{>D&iJ*6U7xdVo5RHCX5n(*6NEmk{boO z1P*3zQ2#F`#W@;D6C-)J_V1uj#)^66LqaH;JfIL_(c~dnMOl`IJ}%xFPQS$g%IlEil7jw3OG1oU##6R!S)QNkYR&hO)m*Yg5Vyn4sUkM!zF~A>bUq z&`4L=q@%_tu6NP(b!83UwYD3-$LO~zt@Yveo^G*Q?)EJZc-dvjxh!~DwBfYq#V*1? zk@KMyxoz!?gNDPA%vmC1O}PChw-)U&y5-q^>u{jMvnx!m8E_*MZA#o5CBVtC6B8%M zeYWT{fGHAN`|{M)*p~7^;|}YbX0m5Bn};~?|^vn7t} zWU&8_@xk0&ufY%oW^QgA%<8?98X3Ba`6i4fvbVO5?MuRJxPY)^#E8U}#4LfYEa_{; zbbLGGoTa|wfI^Iy;V$|F5FFu?UUv$0gRzCzGqT7WF$&zPKYjeT{-SB=Gr|Ju+@@s6 zN)Gkd+$1^M=#heu*1#Ex^X`AUPooXPi1gG`sf2Gce z7s{Onq|S-YOmqDFU1eOK-bQlFT(%ew4o2>!>m8_JP2Bi5y6IB!G8N=@VdRHlFQMoR z_e%1Y?A5!2ZWP^$T>Tu!a$pNs@B!f!vWHnH^*Wpt*ryET#8cJ1+@gm$zwL$v@`pf+ z!<^Y#6-Y;2@(|U^HSHE7^Bxv4;uN72M#hnmbquw60G(%_4omaTJ`HCwwvAAO-y8uV#Z&$C)De1WAQl6GzC$hg9*OHyyuv*(;b?p#vjvBJnIF|kihjhb$|+K3 zEgCe35Ry?~<_g7ZkPdl zEA*#Q&3^3xSy3u*&6M$1k(GOjG6u*3SIr&eUPU30(tE#%x{5MHphxLE{K;Y`&=� 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 literal 0 HcmV?d00001 diff --git a/gestao_raul/Lib/site-packages/websockets/__pycache__/typing.cpython-310.pyc b/gestao_raul/Lib/site-packages/websockets/__pycache__/typing.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..54ffaea52c189d1c508dd8542095aa6ec69f2d28 GIT binary patch literal 877 zcmZ8fOK;Oa5Z?9MPMoBrPyz*U%>g(dA%qa3q!C3_X=u^|mLPq@h0pc^}UJj3H?vTe^p3fU9e$zsoZMvF$4SDZn)j>y-1mpk@G|TixCx_FwYAf@}$VC>Btc7;@xZ#j1MQ{ zH?OC!gu0(^ggJ$a#ij&d9hWjOOocUv4>jZ~5c+A3(#RCrLC8bl92Id4>?m1-aOT*! zt{QF}vm{P((V5rFk`=sI6{&D1A35X|)=$F>#P*dt3mFO=80+Gg%$3kX5QA}%=dhAf zw6lo*X&JLHg4t!$(H-Fg!CB2~20_3SS*5+_ycA}Nqa}GP48AGRGF7&f?||tN3<;(L zd@n?pK@g^C5D2y6Oz6uEhl<%UVzfI7x3&9GSjti5dlSy#Vliv?7HX1n){zbW+d}7c znF8x$$m?G%eF(S)V*^>Rh46*2yOK-z^>+N!KdpdO{spAG@DGYrok7kk|K#N5WbDUK z@vsP3Sf~C`vh=Hj!~HT`twW4p&9W+1Yv9$xcJ~M6Mp)0XBC1n(PVs{32||}xibd~H fOYQa)syRDUp#w#sJx!sGJZgIeXb1WqU!%VPtlH!k literal 0 HcmV?d00001 diff --git a/gestao_raul/Lib/site-packages/websockets/__pycache__/uri.cpython-310.pyc b/gestao_raul/Lib/site-packages/websockets/__pycache__/uri.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2f16656dc92ce83a8c751fadd7c4e1e580277acb GIT binary patch literal 5305 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 G8-D}Q6mjDK literal 0 HcmV?d00001 diff --git a/gestao_raul/Lib/site-packages/websockets/__pycache__/version.cpython-310.pyc b/gestao_raul/Lib/site-packages/websockets/__pycache__/version.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d937353951fda92d431a52e1c3f340fede539a6c GIT binary patch literal 2009 zcmY*aOK;mo5Z+xbAEF+1ob-hnjS=(^)sgB3ZG#v=64*(A0)-L8NsF>F1e&{6Xj3G+ zT-vcpUFraB4?Xl81dwyfpXjld{)0XA(o=pyT?FkcX*ntdE{C(jotbaG870l8i{ScW z@%O=>4TS#EFSC~cFSilG***viF&v>G_Au5miHK(m5Hnt2k230+%%sS(;I=)7o7wf! zrsmbkr|Y@pv+mVdi`AI>lzI)`;MNiLY&ORltO=SXZ*q&79%lc#(QXtM~*ait%=R(F&>kzQCy=)I*0JF z{(Xbur2|x2)JhAZFvQxAXa_TsSx=q9xPy-Hm=;v56_gP;(Jah~dkp@-&sqVQjuvEn zw*iRSFEDco3@AD9bAPdRo0HZt8e8DC1zzXCZXWJ)JI3Mmng5Rt5UY=ExiC3j*oAdW z#!lfdlq0b(FR(^oJwwmQ5&i)e&g89Q)Fb2C*e%@2+pNi2VC@#@8Deuq4c7U>1#5E? zmAZ4!bsZKqMotD$rrq>VXfR(9SUmTDZ)Zuc$7S|Ei1j9g`@DvD5rhF8w&jj-fp;~? zI&D(mU&(iH_)A@18v8@uCW>@d6yAA>Rc$AXct7>!V^MK-OEDBJE;D9MxhC>btWd@IVu&1cxPvmI%+m+7s?$&bWvKF?M z+nvh;E%}|Dt?#d08DGA&S~+XqYL6Bl#r#Ri1IgJEgtSyel0BFWQC*@CB$AMkd) z8U>C>64{3el|hS35V_YpEpVA}<~3$tRec6WuQl`12j^z#7^)B4@#6rZ5q#)}G2^17 zs%XObVy=W|hDjGek==*IHQvR>A=z?N65@&;*`dCJ0)&u6yt{2nsx5SW72bM2RCv1@@9Mu`6ONbeWai z?*ozhea{g*;(o^2n@p;}-`5_>oIli3SwbD$Oh#|ga&8P0mPh=io+jlyZ6Jp@2AyjV SYB<0K2fM_%>XKHC{`McTy9-|c literal 0 HcmV?d00001 diff --git a/gestao_raul/Lib/site-packages/websockets/asyncio/__init__.py b/gestao_raul/Lib/site-packages/websockets/asyncio/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gestao_raul/Lib/site-packages/websockets/asyncio/__pycache__/__init__.cpython-310.pyc b/gestao_raul/Lib/site-packages/websockets/asyncio/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..57f01ce034b0bb90ae89f35703fb814eb7cbcc41 GIT binary patch literal 184 zcmd1j<>g`kg3~g`(n0iN5P=LBfgA@QE@lA|DGb33nv8xc8Hzx{2;x_?vsFxJacWU< zOnGWfNq&q=esXDUYF+oetc14X-QRXW=X1UL1J=tB2WgX zC#g6;IXktaSU<72GA}tZUq3!RGcU6wK3=b&@)n0pZhlH>PO2Tq(qbkc!NLFlgi0`i literal 0 HcmV?d00001 diff --git a/gestao_raul/Lib/site-packages/websockets/asyncio/__pycache__/async_timeout.cpython-310.pyc b/gestao_raul/Lib/site-packages/websockets/asyncio/__pycache__/async_timeout.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8afc1ca9ab22a1f7cf88a7736a6d1cc89a6a37da GIT binary patch literal 7470 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 LN2>Eic|rdVu;9m) literal 0 HcmV?d00001 diff --git a/gestao_raul/Lib/site-packages/websockets/asyncio/__pycache__/client.cpython-310.pyc b/gestao_raul/Lib/site-packages/websockets/asyncio/__pycache__/client.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..df66b67c9fa7c078a4b4e6b212a09f95fa26187d GIT binary patch literal 23620 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 aULrP<8`%Sk^=IaA>4)2Qm@kg~@c#fZ8iA<* literal 0 HcmV?d00001 diff --git a/gestao_raul/Lib/site-packages/websockets/asyncio/__pycache__/compatibility.cpython-310.pyc b/gestao_raul/Lib/site-packages/websockets/asyncio/__pycache__/compatibility.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2c6ab7d313b9d8c3e75f300b07363dab05fc3e9d GIT binary patch literal 824 zcmZuu&5qMB5VoBr=}%H%RS-zrQ4X-2R|p}>N+6*GHK$i!X|NLJ3yrs0s-#< z-1a$;xbOzPa_TE^VkYfM<;Ri7Gvm*b@0(F^JS4Dw-2A?{>k;x3gWUx=@_-O(-hxw* zf>xxaoKo~z#W-7#!uv|OC)vwh!HV7|c7qiDsjqKIpZhZ4fed*F=%DJ?13s)rJc8G3 zh804{0Se-NG5AP$Pz;L^oM90GKK>J*#A{@hP&dk&M%m1ksxe22@1kdAEmdm|wN~2s zS!pF6jeKL>rOd3Fyi~$&FmCU!gvjigVO_RHvFlOiyYK=(+&X|us76Rm!CWl#F*(I1 zjlr?xjC`P<=^6P#XXKn7CCv5gYAM}7h>n^N3FYIChCr&>yprccUxvrOUQ7?t=SFIi zzLu3$=_8f5wQQ_OkB=W7PSb@nHd8`pZIwPP=c$1Lx0hLdn!ydcnH!a#N^ADFD))0$ zFQHTOvMTNB&T^%D@SCf!4T>JqF8;5gZ>Z=RfZo$xO;2EOkI9Va7_JhgQFA8^+x?F; zt8^!9W3B*J`(UoaHpqm%o6s(^#zrP%Hx%NewXK#yXk_R-vobD()>V}dE_wyMfw_ya zIZ@p-Issh?9&SZzoS_cTL})@VJl-^XJIx(Z*msT0T>oCJinfyXk&8i&5$UtOx99aJ LeiIr67*D+4om0ko literal 0 HcmV?d00001 diff --git a/gestao_raul/Lib/site-packages/websockets/asyncio/__pycache__/connection.cpython-310.pyc b/gestao_raul/Lib/site-packages/websockets/asyncio/__pycache__/connection.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c034b4e806fa52eefd45c0addc91c8504c72c39f GIT binary patch literal 32848 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 Y3!^CR2MXA7oB3$^pYfscTcvaV7asT^E&u=k literal 0 HcmV?d00001 diff --git a/gestao_raul/Lib/site-packages/websockets/asyncio/__pycache__/messages.cpython-310.pyc b/gestao_raul/Lib/site-packages/websockets/asyncio/__pycache__/messages.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eed65bbef6b65ce7c985f9227f04e375fb721139 GIT binary patch literal 9367 zcmeHNTXP%7m7W_e1i_0Win>}EJF#WN6f8TIrO=9OnwD%i4y{nMoj_860lGng8qB~w z1JOWH6*i{8bTjR2CELzba@@~y^X-YsM7vNav?nW*?P8_ao~lf>rz_KF&xou$)1Ix&az5uC zX&e6Vlu{DGE>Z$|*S|CLimS)9-7dD5joh zVoFZR)2Fn@X5|d(ro{y6CLSA=v%Znpz5aUgKsc zl@t0Z%5$#n%2GBmm%Lt-Uv^yAK}D2%UwTrt8d2_}7Ik-{+*+?AKXvey|3E6&cf{95 zJj?8|?=`wgNw3knu9UAzRy6VPt?IisSC&?P9_5f-{&;1LGi%p>wiab>D5ov)eb#$U z=tL7sK_J_hRrNF7uzBH1q^~t-1?fKvg?DI=R*;ceMHd?OMaQ0;5|+q3pWm*PwlH+*a7LJt+kSuJ?um4rP!G7G;$TyUL5K72lH$JtYjvlkAsC9_Nq=wH^Jb zwyjm6sYuPx+R1P^E416%SNcjR7v%<@T$x>$VO4cKujQ>*UEl9S7N-TO-*RZXqsfNv zx{^OQh%!Qc-jz`XNmqV_T??e!{7O?cX5PKCym6 zy}o>RT?V1!SC!Ls?|#&(-wiPSg^ts>@1OW}r?nmU0Zj)dt-Ptov-1j%cP zljl*4a@8u@tJN1!2uS1wy?<<4v)*TH%I|X4*b%Yk8%V z;YCGRr_+(1P^U3>WC>_er2vhrDj9U$uw1L-JzKE(IkECeK>9czN zAN`s1;?V<#x$a^IBdhNF?!L9tyT2zb?I81|POA@ou&oD$P#4&SUr~)AjII_O`w%vD zP-E^`u{QT-sr@Fb@CTZ%eZ~?a&4z_ENZY*$mRG7~E0%EfDfGT%_1|8VZI~;FP1x~q z8cMLK{PswK3yJ9LRuiYQK}x84<=y!pRMKhl2_~Zz*pQ|aEB$i~wykyp7SqPbdAVda z5;GXOy;3fgfByQ%H`pff4yaQQLuIOgAwyYhc5%LCl*LF52?N{lVS<{&P5D0h?%Nd1 z^a|<&+E3$A*0A)vepD~&22Mr6$m@lF;HQso6W)n3q&30rFtvBq?(N&Gp`u>I z;IvgxPA!8O9ys~b-opN=fimLk{OGa&T*8LUxe7CM7Li<-Ci+f zc-kMLg^kKlV;b7sKa$pM?y=995DM{GPKiiYb0jRXz`;D{jL;;J(HT$9FN?_RUs-nj zK&I7BIjsOE)=u;UQcJ{;R>Tzr;sPPZ>wFpc$`qYr`9(u^2v^hv)E#KUGEUuB;oE5D z#m?a<;~ywc7SY*QI>1TK&B1xPulLT;VsWRYew8k^NC6N=V-74N5LiVvi?eu+$t9 z`sQF3NnW$`LavZ0n8j>idOFLk4K$4X4T;|$P(4N}V3dR@EYPY9rA3pka2$=>~GqCT0r8 zw6!6 z6?@wz4hyd3Z$%m2(A^`6t8wSfy#*W2$2}svB2e!Ci;pcbdKhrlK-pXGX8(Nmw(9wz&5GS5-PT!{T8z|$1 zgQB2p5Zy5Gv$iSCY+FdPB7-!?X`a&w+KEzjUt%}YS`M({ul^8i zdq0!FVhcY$9L6I{@5AvOxR_-~VRt&2{f6TPm_AeQg)%tw1JqAwl9BF{3wqF;eX9Q$ z$?hbv;epz#BdGb-V?RvB9vg`c#gJng5LnEyRX*X@%#*c#m3cB$chf3rPjxVA^8-2} zTFj=mjm>PB1KB&zxjf}Q)JXM(i6eC}X~*F)*7b)b$`e7eKXve^pK0EVrG627}2k=HDmKmVYXlJZ064Nw!xke##e8CrU^P`nrFq&Er9XPjL3@I6K&gk zPa}r)(Aw73FSo6oSz`R#7LH%uxd=NkG~TN)?`(}d@WKQJ2S`I$u-KO2#$pX>Q5(1j zfDxNG83<_tjZn`T9mJ%}`jU9+G|J5;&oMw*LN%Pub#jEISQwjwTr|SGu!YkPph7$$ zjL{Wu4Zm8!1%aW!yXZqcA3?-Ghl8=HMy?GQcL;#?(Uo5w4wk}xfH*}R*mNw|K-Sc^ zRpBFGnJq5}A+wZ-#sQg@8=vAud(p3NF4p)I)ojrU$h8RUPu6Z+xDxjvU`6yJB-zLI zihQx*m(f`C7yD+xUil8jCmT_-o4$fuwtw(_gVkFee7Dqe`-Z3OOs#Jf99*U-2Ype( z#}|{W-A8&hoPjeqA`0~4Ctiyz+2*edf7PD%#kDTWT@3|T)wKuE-k8XsqGP%ZwD zEgvr{xccBr>?Wo5k2G|jFh=S<*xt04nv%ipOR@6>C1;#~D!`?M9y=}(q+a5Zt*zX6 z2BV{mxkkh%=ob(+6Wjk1NBmxvuy#}X6b9gPU~>|4K>&#uC1Ni|`C^n|t=MBtqlv5Q zh(CVOQP=*1h9x*P9?fZed(4i5wfOMAgKOns_tFvd0XnN9CD$oo&*3I=QL*jx>XJhr zZMfZjhxSR$P^T$sH$g(xNy<$xvr7)b8N(_&9@EtEJT+UJK6(=JB-qI)^ci*FISC{8 zEQ!D45$Xd91HpBl&jTrq%sia-lR!=iY-LSDKMf2m;%WRXpMguSXa8b6nw^?sN8d7b zLD^Cf+&fUp{eC>*CXip>OW6*9ttg*t_OJ0$%nS82a3{^XbK?`U0sK}|t14#vEq4g=Hc*G1iWQ7v%T-d@D{_<0N8RA zo&rGT!wCS}CC(KnmjcvlNjseo^^(u%7?Hz zusa;z-B7;gcLPMndoAe-SeK!}N}!c&^M0r$>u*b^v5`P!>;(>xEoH~n@WF&*bipy? z-Oa9xDPj4@zd+bsVl>I2JNo>6pPDkGqqdsutixuP?dzb7fOJG&7HfNrG|&@PncyUi zpKBYaC3*NFz8Yvd_hG#Q=>dOfVI!`P5EOXYpcF&kc5u0JK6bGQV@Cb=V)(z{o8^ajce^0tGjt$(We1pPq!RSpYV5=Hz+GQRh7Himzm{`Mh2+N! z+frm_DY%!!;|~wNl~>lVc8)N751$dUr1vSeM9Dcy>|w%-l_=sWTzf`Tu3%dFyqU#C zNB-~q<0BH765RiJ;d;Dq^$iPO<>1s|D?cpgSH_Xd#1WZ23&)-uyl^_4&tu`LiA;Zf z?_%R1+2N}tS@N;S@U~IlipsdG;p2eq#5VqBsF>GrGe#$1uX~e%REpH4aFU}&wXxeS zzJE;NXf5sX#`wQ5oDq08Kvk$K17_efB}bNf7zo#C44?yqP5iL5zXoM8^Yd&RlqROI^%D>jlprV6h8 z2ss!@b_H$?Ykx|)6RD=vVH3rLk{3q9{32C58pcW0jz*}*wL@b$3Y{vly``eMjE5qR zL(y%QTBPI!N(z)*q=d|Me91ybF2;s+%Gu+|^HikU1rmDjIug2MF^(AIx#-2e8ZY1) z5(de@ZCcSVuBJBsOK?C=#GhHaFB4K{MGnY>l!pp3(g`bYI*o~6d|=ClasJtSrgZ^ K#+-Td;{O0%Z05lL literal 0 HcmV?d00001 diff --git a/gestao_raul/Lib/site-packages/websockets/asyncio/__pycache__/router.cpython-310.pyc b/gestao_raul/Lib/site-packages/websockets/asyncio/__pycache__/router.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..05aaf68e6a9aec919bfd067dcf88babe61af94c8 GIT binary patch literal 6543 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 b%;!g*%zPf#OT3y#@T(Z(W{Jl5(LeaV@}JVz literal 0 HcmV?d00001 diff --git a/gestao_raul/Lib/site-packages/websockets/asyncio/async_timeout.py b/gestao_raul/Lib/site-packages/websockets/asyncio/async_timeout.py new file mode 100644 index 0000000..6ffa899 --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/asyncio/async_timeout.py @@ -0,0 +1,282 @@ +# From https://github.com/aio-libs/async-timeout/blob/master/async_timeout/__init__.py +# Licensed under the Apache License (Apache-2.0) + +import asyncio +import enum +import sys +import warnings +from types import TracebackType +from typing import Optional, Type + + +if sys.version_info >= (3, 11): + from typing import final +else: + # From https://github.com/python/typing_extensions/blob/main/src/typing_extensions.py + # Licensed under the Python Software Foundation License (PSF-2.0) + + # @final exists in 3.8+, but we backport it for all versions + # before 3.11 to keep support for the __final__ attribute. + # See https://bugs.python.org/issue46342 + def final(f): + """This decorator can be used to indicate to type checkers that + the decorated method cannot be overridden, and decorated class + cannot be subclassed. For example: + + class Base: + @final + def done(self) -> None: + ... + class Sub(Base): + def done(self) -> None: # Error reported by type checker + ... + @final + class Leaf: + ... + class Other(Leaf): # Error reported by type checker + ... + + There is no runtime checking of these properties. The decorator + sets the ``__final__`` attribute to ``True`` on the decorated object + to allow runtime introspection. + """ + try: + f.__final__ = True + except (AttributeError, TypeError): + # Skip the attribute silently if it is not writable. + # AttributeError happens if the object has __slots__ or a + # read-only property, TypeError if it's a builtin class. + pass + return f + + # End https://github.com/python/typing_extensions/blob/main/src/typing_extensions.py + + +if sys.version_info >= (3, 11): + + def _uncancel_task(task: "asyncio.Task[object]") -> None: + task.uncancel() + +else: + + def _uncancel_task(task: "asyncio.Task[object]") -> None: + pass + + +__version__ = "4.0.3" + + +__all__ = ("timeout", "timeout_at", "Timeout") + + +def timeout(delay: Optional[float]) -> "Timeout": + """timeout context manager. + + Useful in cases when you want to apply timeout logic around block + of code or in cases when asyncio.wait_for is not suitable. For example: + + >>> async with timeout(0.001): + ... async with aiohttp.get('https://github.com') as r: + ... await r.text() + + + delay - value in seconds or None to disable timeout logic + """ + loop = asyncio.get_running_loop() + if delay is not None: + deadline = loop.time() + delay # type: Optional[float] + else: + deadline = None + return Timeout(deadline, loop) + + +def timeout_at(deadline: Optional[float]) -> "Timeout": + """Schedule the timeout at absolute time. + + deadline argument points on the time in the same clock system + as loop.time(). + + Please note: it is not POSIX time but a time with + undefined starting base, e.g. the time of the system power on. + + >>> async with timeout_at(loop.time() + 10): + ... async with aiohttp.get('https://github.com') as r: + ... await r.text() + + + """ + loop = asyncio.get_running_loop() + return Timeout(deadline, loop) + + +class _State(enum.Enum): + INIT = "INIT" + ENTER = "ENTER" + TIMEOUT = "TIMEOUT" + EXIT = "EXIT" + + +@final +class Timeout: + # Internal class, please don't instantiate it directly + # Use timeout() and timeout_at() public factories instead. + # + # Implementation note: `async with timeout()` is preferred + # over `with timeout()`. + # While technically the Timeout class implementation + # doesn't need to be async at all, + # the `async with` statement explicitly points that + # the context manager should be used from async function context. + # + # This design allows to avoid many silly misusages. + # + # TimeoutError is raised immediately when scheduled + # if the deadline is passed. + # The purpose is to time out as soon as possible + # without waiting for the next await expression. + + __slots__ = ("_deadline", "_loop", "_state", "_timeout_handler", "_task") + + def __init__( + self, deadline: Optional[float], loop: asyncio.AbstractEventLoop + ) -> None: + self._loop = loop + self._state = _State.INIT + + self._task: Optional["asyncio.Task[object]"] = None + self._timeout_handler = None # type: Optional[asyncio.Handle] + if deadline is None: + self._deadline = None # type: Optional[float] + else: + self.update(deadline) + + def __enter__(self) -> "Timeout": + warnings.warn( + "with timeout() is deprecated, use async with timeout() instead", + DeprecationWarning, + stacklevel=2, + ) + self._do_enter() + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> Optional[bool]: + self._do_exit(exc_type) + return None + + async def __aenter__(self) -> "Timeout": + self._do_enter() + return self + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> Optional[bool]: + self._do_exit(exc_type) + return None + + @property + def expired(self) -> bool: + """Is timeout expired during execution?""" + return self._state == _State.TIMEOUT + + @property + def deadline(self) -> Optional[float]: + return self._deadline + + def reject(self) -> None: + """Reject scheduled timeout if any.""" + # cancel is maybe better name but + # task.cancel() raises CancelledError in asyncio world. + if self._state not in (_State.INIT, _State.ENTER): + raise RuntimeError(f"invalid state {self._state.value}") + self._reject() + + def _reject(self) -> None: + self._task = None + if self._timeout_handler is not None: + self._timeout_handler.cancel() + self._timeout_handler = None + + def shift(self, delay: float) -> None: + """Advance timeout on delay seconds. + + The delay can be negative. + + Raise RuntimeError if shift is called when deadline is not scheduled + """ + deadline = self._deadline + if deadline is None: + raise RuntimeError("cannot shift timeout if deadline is not scheduled") + self.update(deadline + delay) + + def update(self, deadline: float) -> None: + """Set deadline to absolute value. + + deadline argument points on the time in the same clock system + as loop.time(). + + If new deadline is in the past the timeout is raised immediately. + + Please note: it is not POSIX time but a time with + undefined starting base, e.g. the time of the system power on. + """ + if self._state == _State.EXIT: + raise RuntimeError("cannot reschedule after exit from context manager") + if self._state == _State.TIMEOUT: + raise RuntimeError("cannot reschedule expired timeout") + if self._timeout_handler is not None: + self._timeout_handler.cancel() + self._deadline = deadline + if self._state != _State.INIT: + self._reschedule() + + def _reschedule(self) -> None: + assert self._state == _State.ENTER + deadline = self._deadline + if deadline is None: + return + + now = self._loop.time() + if self._timeout_handler is not None: + self._timeout_handler.cancel() + + self._task = asyncio.current_task() + if deadline <= now: + self._timeout_handler = self._loop.call_soon(self._on_timeout) + else: + self._timeout_handler = self._loop.call_at(deadline, self._on_timeout) + + def _do_enter(self) -> None: + if self._state != _State.INIT: + raise RuntimeError(f"invalid state {self._state.value}") + self._state = _State.ENTER + self._reschedule() + + def _do_exit(self, exc_type: Optional[Type[BaseException]]) -> None: + if exc_type is asyncio.CancelledError and self._state == _State.TIMEOUT: + assert self._task is not None + _uncancel_task(self._task) + self._timeout_handler = None + self._task = None + raise asyncio.TimeoutError + # timeout has not expired + self._state = _State.EXIT + self._reject() + return None + + def _on_timeout(self) -> None: + assert self._task is not None + self._task.cancel() + self._state = _State.TIMEOUT + # drop the reference early + self._timeout_handler = None + + +# End https://github.com/aio-libs/async-timeout/blob/master/async_timeout/__init__.py diff --git a/gestao_raul/Lib/site-packages/websockets/asyncio/client.py b/gestao_raul/Lib/site-packages/websockets/asyncio/client.py new file mode 100644 index 0000000..38a56dd --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/asyncio/client.py @@ -0,0 +1,820 @@ +from __future__ import annotations + +import asyncio +import logging +import os +import socket +import ssl as ssl_module +import traceback +import urllib.parse +from collections.abc import AsyncIterator, Generator, Sequence +from types import TracebackType +from typing import Any, Callable, Literal, cast + +from ..client import ClientProtocol, backoff +from ..datastructures import Headers, HeadersLike +from ..exceptions import ( + InvalidMessage, + InvalidProxyMessage, + InvalidProxyStatus, + InvalidStatus, + ProxyError, + SecurityError, +) +from ..extensions.base import ClientExtensionFactory +from ..extensions.permessage_deflate import enable_client_permessage_deflate +from ..headers import build_authorization_basic, build_host, validate_subprotocols +from ..http11 import USER_AGENT, Response +from ..protocol import CONNECTING, Event +from ..streams import StreamReader +from ..typing import LoggerLike, Origin, Subprotocol +from ..uri import Proxy, WebSocketURI, get_proxy, parse_proxy, parse_uri +from .compatibility import TimeoutError, asyncio_timeout +from .connection import Connection + + +__all__ = ["connect", "unix_connect", "ClientConnection"] + +MAX_REDIRECTS = int(os.environ.get("WEBSOCKETS_MAX_REDIRECTS", "10")) + + +class ClientConnection(Connection): + """ + :mod:`asyncio` implementation of a WebSocket client connection. + + :class:`ClientConnection` provides :meth:`recv` and :meth:`send` coroutines + for receiving and sending messages. + + It supports asynchronous iteration to receive messages:: + + async for message in websocket: + await process(message) + + The iterator exits normally when the connection is closed with close code + 1000 (OK) or 1001 (going away) or without a close code. It raises a + :exc:`~websockets.exceptions.ConnectionClosedError` when the connection is + closed with any other code. + + The ``ping_interval``, ``ping_timeout``, ``close_timeout``, ``max_queue``, + and ``write_limit`` arguments have the same meaning as in :func:`connect`. + + Args: + protocol: Sans-I/O connection. + + """ + + def __init__( + self, + protocol: ClientProtocol, + *, + ping_interval: float | None = 20, + ping_timeout: float | None = 20, + close_timeout: float | None = 10, + max_queue: int | None | tuple[int | None, int | None] = 16, + write_limit: int | tuple[int, int | None] = 2**15, + ) -> None: + self.protocol: ClientProtocol + super().__init__( + protocol, + ping_interval=ping_interval, + ping_timeout=ping_timeout, + close_timeout=close_timeout, + max_queue=max_queue, + write_limit=write_limit, + ) + self.response_rcvd: asyncio.Future[None] = self.loop.create_future() + + async def handshake( + self, + additional_headers: HeadersLike | None = None, + user_agent_header: str | None = USER_AGENT, + ) -> None: + """ + Perform the opening handshake. + + """ + async with self.send_context(expected_state=CONNECTING): + self.request = self.protocol.connect() + if additional_headers is not None: + self.request.headers.update(additional_headers) + if user_agent_header is not None: + self.request.headers.setdefault("User-Agent", user_agent_header) + self.protocol.send_request(self.request) + + await asyncio.wait( + [self.response_rcvd, self.connection_lost_waiter], + return_when=asyncio.FIRST_COMPLETED, + ) + + # self.protocol.handshake_exc is set when the connection is lost before + # receiving a response, when the response cannot be parsed, or when the + # response fails the handshake. + + if self.protocol.handshake_exc is not None: + raise self.protocol.handshake_exc + + def process_event(self, event: Event) -> None: + """ + Process one incoming event. + + """ + # First event - handshake response. + if self.response is None: + assert isinstance(event, Response) + self.response = event + self.response_rcvd.set_result(None) + # Later events - frames. + else: + super().process_event(event) + + +def process_exception(exc: Exception) -> Exception | None: + """ + Determine whether a connection error is retryable or fatal. + + When reconnecting automatically with ``async for ... in connect(...)``, if a + connection attempt fails, :func:`process_exception` is called to determine + whether to retry connecting or to raise the exception. + + This function defines the default behavior, which is to retry on: + + * :exc:`EOFError`, :exc:`OSError`, :exc:`asyncio.TimeoutError`: network + errors; + * :exc:`~websockets.exceptions.InvalidStatus` when the status code is 500, + 502, 503, or 504: server or proxy errors. + + All other exceptions are considered fatal. + + You can change this behavior with the ``process_exception`` argument of + :func:`connect`. + + Return :obj:`None` if the exception is retryable i.e. when the error could + be transient and trying to reconnect with the same parameters could succeed. + The exception will be logged at the ``INFO`` level. + + Return an exception, either ``exc`` or a new exception, if the exception is + fatal i.e. when trying to reconnect will most likely produce the same error. + That exception will be raised, breaking out of the retry loop. + + """ + # This catches python-socks' ProxyConnectionError and ProxyTimeoutError. + # Remove asyncio.TimeoutError when dropping Python < 3.11. + if isinstance(exc, (OSError, TimeoutError, asyncio.TimeoutError)): + return None + if isinstance(exc, InvalidMessage) and isinstance(exc.__cause__, EOFError): + return None + if isinstance(exc, InvalidStatus) and exc.response.status_code in [ + 500, # Internal Server Error + 502, # Bad Gateway + 503, # Service Unavailable + 504, # Gateway Timeout + ]: + return None + return exc + + +# This is spelled in lower case because it's exposed as a callable in the API. +class connect: + """ + Connect to the WebSocket server at ``uri``. + + This coroutine returns a :class:`ClientConnection` instance, which you can + use to send and receive messages. + + :func:`connect` may be used as an asynchronous context manager:: + + from websockets.asyncio.client import connect + + async with connect(...) as websocket: + ... + + The connection is closed automatically when exiting the context. + + :func:`connect` can be used as an infinite asynchronous iterator to + reconnect automatically on errors:: + + async for websocket in connect(...): + try: + ... + except websockets.exceptions.ConnectionClosed: + continue + + If the connection fails with a transient error, it is retried with + exponential backoff. If it fails with a fatal error, the exception is + raised, breaking out of the loop. + + The connection is closed automatically after each iteration of the loop. + + Args: + uri: URI of the WebSocket server. + origin: Value of the ``Origin`` header, for servers that require it. + extensions: List of supported extensions, in order in which they + should be negotiated and run. + subprotocols: List of supported subprotocols, in order of decreasing + preference. + compression: The "permessage-deflate" extension is enabled by default. + Set ``compression`` to :obj:`None` to disable it. See the + :doc:`compression guide <../../topics/compression>` for details. + additional_headers (HeadersLike | None): Arbitrary HTTP headers to add + to the handshake request. + user_agent_header: Value of the ``User-Agent`` request header. + It defaults to ``"Python/x.y.z websockets/X.Y"``. + Setting it to :obj:`None` removes the header. + proxy: If a proxy is configured, it is used by default. Set ``proxy`` + to :obj:`None` to disable the proxy or to the address of a proxy + to override the system configuration. See the :doc:`proxy docs + <../../topics/proxies>` for details. + process_exception: When reconnecting automatically, tell whether an + error is transient or fatal. The default behavior is defined by + :func:`process_exception`. Refer to its documentation for details. + open_timeout: Timeout for opening the connection in seconds. + :obj:`None` disables the timeout. + ping_interval: Interval between keepalive pings in seconds. + :obj:`None` disables keepalive. + ping_timeout: Timeout for keepalive pings in seconds. + :obj:`None` disables timeouts. + close_timeout: Timeout for closing the connection in seconds. + :obj:`None` disables the timeout. + max_size: Maximum size of incoming messages in bytes. + :obj:`None` disables the limit. + max_queue: High-water mark of the buffer where frames are received. + It defaults to 16 frames. The low-water mark defaults to ``max_queue + // 4``. You may pass a ``(high, low)`` tuple to set the high-water + and low-water marks. If you want to disable flow control entirely, + you may set it to ``None``, although that's a bad idea. + write_limit: High-water mark of write buffer in bytes. It is passed to + :meth:`~asyncio.WriteTransport.set_write_buffer_limits`. It defaults + to 32 KiB. You may pass a ``(high, low)`` tuple to set the + high-water and low-water marks. + logger: Logger for this client. + It defaults to ``logging.getLogger("websockets.client")``. + See the :doc:`logging guide <../../topics/logging>` for details. + create_connection: Factory for the :class:`ClientConnection` managing + the connection. Set it to a wrapper or a subclass to customize + connection handling. + + Any other keyword arguments are passed to the event loop's + :meth:`~asyncio.loop.create_connection` method. + + For example: + + * You can set ``ssl`` to a :class:`~ssl.SSLContext` to enforce TLS settings. + When connecting to a ``wss://`` URI, if ``ssl`` isn't provided, a TLS + context is created with :func:`~ssl.create_default_context`. + + * You can set ``server_hostname`` to override the host name from ``uri`` in + the TLS handshake. + + * You can set ``host`` and ``port`` to connect to a different host and port + from those found in ``uri``. This only changes the destination of the TCP + connection. The host name from ``uri`` is still used in the TLS handshake + for secure connections and in the ``Host`` header. + + * You can set ``sock`` to provide a preexisting TCP socket. You may call + :func:`socket.create_connection` (not to be confused with the event loop's + :meth:`~asyncio.loop.create_connection` method) to create a suitable + client socket and customize it. + + When using a proxy: + + * Prefix keyword arguments with ``proxy_`` for configuring TLS between the + client and an HTTPS proxy: ``proxy_ssl``, ``proxy_server_hostname``, + ``proxy_ssl_handshake_timeout``, and ``proxy_ssl_shutdown_timeout``. + * Use the standard keyword arguments for configuring TLS between the proxy + and the WebSocket server: ``ssl``, ``server_hostname``, + ``ssl_handshake_timeout``, and ``ssl_shutdown_timeout``. + * Other keyword arguments are used only for connecting to the proxy. + + Raises: + InvalidURI: If ``uri`` isn't a valid WebSocket URI. + InvalidProxy: If ``proxy`` isn't a valid proxy. + OSError: If the TCP connection fails. + InvalidHandshake: If the opening handshake fails. + TimeoutError: If the opening handshake times out. + + """ + + def __init__( + self, + uri: str, + *, + # WebSocket + origin: Origin | None = None, + extensions: Sequence[ClientExtensionFactory] | None = None, + subprotocols: Sequence[Subprotocol] | None = None, + compression: str | None = "deflate", + # HTTP + additional_headers: HeadersLike | None = None, + user_agent_header: str | None = USER_AGENT, + proxy: str | Literal[True] | None = True, + process_exception: Callable[[Exception], Exception | None] = process_exception, + # Timeouts + open_timeout: float | None = 10, + ping_interval: float | None = 20, + ping_timeout: float | None = 20, + close_timeout: float | None = 10, + # Limits + max_size: int | None = 2**20, + max_queue: int | None | tuple[int | None, int | None] = 16, + write_limit: int | tuple[int, int | None] = 2**15, + # Logging + logger: LoggerLike | None = None, + # Escape hatch for advanced customization + create_connection: type[ClientConnection] | None = None, + # Other keyword arguments are passed to loop.create_connection + **kwargs: Any, + ) -> None: + self.uri = uri + + if subprotocols is not None: + validate_subprotocols(subprotocols) + + if compression == "deflate": + extensions = enable_client_permessage_deflate(extensions) + elif compression is not None: + raise ValueError(f"unsupported compression: {compression}") + + if logger is None: + logger = logging.getLogger("websockets.client") + + if create_connection is None: + create_connection = ClientConnection + + def protocol_factory(uri: WebSocketURI) -> ClientConnection: + # This is a protocol in the Sans-I/O implementation of websockets. + protocol = ClientProtocol( + uri, + origin=origin, + extensions=extensions, + subprotocols=subprotocols, + max_size=max_size, + logger=logger, + ) + # This is a connection in websockets and a protocol in asyncio. + connection = create_connection( + protocol, + ping_interval=ping_interval, + ping_timeout=ping_timeout, + close_timeout=close_timeout, + max_queue=max_queue, + write_limit=write_limit, + ) + return connection + + self.proxy = proxy + self.protocol_factory = protocol_factory + self.additional_headers = additional_headers + self.user_agent_header = user_agent_header + self.process_exception = process_exception + self.open_timeout = open_timeout + self.logger = logger + self.connection_kwargs = kwargs + + async def create_connection(self) -> ClientConnection: + """Create TCP or Unix connection.""" + loop = asyncio.get_running_loop() + kwargs = self.connection_kwargs.copy() + + ws_uri = parse_uri(self.uri) + + proxy = self.proxy + if kwargs.get("unix", False): + proxy = None + if kwargs.get("sock") is not None: + proxy = None + if proxy is True: + proxy = get_proxy(ws_uri) + + def factory() -> ClientConnection: + return self.protocol_factory(ws_uri) + + if ws_uri.secure: + kwargs.setdefault("ssl", True) + kwargs.setdefault("server_hostname", ws_uri.host) + if kwargs.get("ssl") is None: + raise ValueError("ssl=None is incompatible with a wss:// URI") + else: + if kwargs.get("ssl") is not None: + raise ValueError("ssl argument is incompatible with a ws:// URI") + + if kwargs.pop("unix", False): + _, connection = await loop.create_unix_connection(factory, **kwargs) + elif proxy is not None: + proxy_parsed = parse_proxy(proxy) + if proxy_parsed.scheme[:5] == "socks": + # Connect to the server through the proxy. + sock = await connect_socks_proxy( + proxy_parsed, + ws_uri, + local_addr=kwargs.pop("local_addr", None), + ) + # Initialize WebSocket connection via the proxy. + _, connection = await loop.create_connection( + factory, + sock=sock, + **kwargs, + ) + elif proxy_parsed.scheme[:4] == "http": + # Split keyword arguments between the proxy and the server. + all_kwargs, proxy_kwargs, kwargs = kwargs, {}, {} + for key, value in all_kwargs.items(): + if key.startswith("ssl") or key == "server_hostname": + kwargs[key] = value + elif key.startswith("proxy_"): + proxy_kwargs[key[6:]] = value + else: + proxy_kwargs[key] = value + # Validate the proxy_ssl argument. + if proxy_parsed.scheme == "https": + proxy_kwargs.setdefault("ssl", True) + if proxy_kwargs.get("ssl") is None: + raise ValueError( + "proxy_ssl=None is incompatible with an https:// proxy" + ) + else: + if proxy_kwargs.get("ssl") is not None: + raise ValueError( + "proxy_ssl argument is incompatible with an http:// proxy" + ) + # Connect to the server through the proxy. + transport = await connect_http_proxy( + proxy_parsed, + ws_uri, + user_agent_header=self.user_agent_header, + **proxy_kwargs, + ) + # Initialize WebSocket connection via the proxy. + connection = factory() + transport.set_protocol(connection) + ssl = kwargs.pop("ssl", None) + if ssl is True: + ssl = ssl_module.create_default_context() + if ssl is not None: + new_transport = await loop.start_tls( + transport, connection, ssl, **kwargs + ) + assert new_transport is not None # help mypy + transport = new_transport + connection.connection_made(transport) + else: + raise AssertionError("unsupported proxy") + else: + # Connect to the server directly. + if kwargs.get("sock") is None: + kwargs.setdefault("host", ws_uri.host) + kwargs.setdefault("port", ws_uri.port) + # Initialize WebSocket connection. + _, connection = await loop.create_connection(factory, **kwargs) + return connection + + def process_redirect(self, exc: Exception) -> Exception | str: + """ + Determine whether a connection error is a redirect that can be followed. + + Return the new URI if it's a valid redirect. Else, return an exception. + + """ + if not ( + isinstance(exc, InvalidStatus) + and exc.response.status_code + in [ + 300, # Multiple Choices + 301, # Moved Permanently + 302, # Found + 303, # See Other + 307, # Temporary Redirect + 308, # Permanent Redirect + ] + and "Location" in exc.response.headers + ): + return exc + + old_ws_uri = parse_uri(self.uri) + new_uri = urllib.parse.urljoin(self.uri, exc.response.headers["Location"]) + new_ws_uri = parse_uri(new_uri) + + # If connect() received a socket, it is closed and cannot be reused. + if self.connection_kwargs.get("sock") is not None: + return ValueError( + f"cannot follow redirect to {new_uri} with a preexisting socket" + ) + + # TLS downgrade is forbidden. + if old_ws_uri.secure and not new_ws_uri.secure: + return SecurityError(f"cannot follow redirect to non-secure URI {new_uri}") + + # Apply restrictions to cross-origin redirects. + if ( + old_ws_uri.secure != new_ws_uri.secure + or old_ws_uri.host != new_ws_uri.host + or old_ws_uri.port != new_ws_uri.port + ): + # Cross-origin redirects on Unix sockets don't quite make sense. + if self.connection_kwargs.get("unix", False): + return ValueError( + f"cannot follow cross-origin redirect to {new_uri} " + f"with a Unix socket" + ) + + # Cross-origin redirects when host and port are overridden are ill-defined. + if ( + self.connection_kwargs.get("host") is not None + or self.connection_kwargs.get("port") is not None + ): + return ValueError( + f"cannot follow cross-origin redirect to {new_uri} " + f"with an explicit host or port" + ) + + return new_uri + + # ... = await connect(...) + + def __await__(self) -> Generator[Any, None, ClientConnection]: + # Create a suitable iterator by calling __await__ on a coroutine. + return self.__await_impl__().__await__() + + async def __await_impl__(self) -> ClientConnection: + try: + async with asyncio_timeout(self.open_timeout): + for _ in range(MAX_REDIRECTS): + self.connection = await self.create_connection() + try: + await self.connection.handshake( + self.additional_headers, + self.user_agent_header, + ) + except asyncio.CancelledError: + self.connection.transport.abort() + raise + except Exception as exc: + # Always close the connection even though keep-alive is + # the default in HTTP/1.1 because create_connection ties + # opening the network connection with initializing the + # protocol. In the current design of connect(), there is + # no easy way to reuse the network connection that works + # in every case nor to reinitialize the protocol. + self.connection.transport.abort() + + uri_or_exc = self.process_redirect(exc) + # Response is a valid redirect; follow it. + if isinstance(uri_or_exc, str): + self.uri = uri_or_exc + continue + # Response isn't a valid redirect; raise the exception. + if uri_or_exc is exc: + raise + else: + raise uri_or_exc from exc + + else: + self.connection.start_keepalive() + return self.connection + else: + raise SecurityError(f"more than {MAX_REDIRECTS} redirects") + + except TimeoutError as exc: + # Re-raise exception with an informative error message. + raise TimeoutError("timed out during opening handshake") from exc + + # ... = yield from connect(...) - remove when dropping Python < 3.10 + + __iter__ = __await__ + + # async with connect(...) as ...: ... + + async def __aenter__(self) -> ClientConnection: + return await self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + ) -> None: + await self.connection.close() + + # async for ... in connect(...): + + async def __aiter__(self) -> AsyncIterator[ClientConnection]: + delays: Generator[float] | None = None + while True: + try: + async with self as protocol: + yield protocol + except Exception as exc: + # Determine whether the exception is retryable or fatal. + # The API of process_exception is "return an exception or None"; + # "raise an exception" is also supported because it's a frequent + # mistake. It isn't documented in order to keep the API simple. + try: + new_exc = self.process_exception(exc) + except Exception as raised_exc: + new_exc = raised_exc + + # The connection failed with a fatal error. + # Raise the exception and exit the loop. + if new_exc is exc: + raise + if new_exc is not None: + raise new_exc from exc + + # The connection failed with a retryable error. + # Start or continue backoff and reconnect. + if delays is None: + delays = backoff() + delay = next(delays) + self.logger.info( + "connect failed; reconnecting in %.1f seconds: %s", + delay, + # Remove first argument when dropping Python 3.9. + traceback.format_exception_only(type(exc), exc)[0].strip(), + ) + await asyncio.sleep(delay) + continue + + else: + # The connection succeeded. Reset backoff. + delays = None + + +def unix_connect( + path: str | None = None, + uri: str | None = None, + **kwargs: Any, +) -> connect: + """ + Connect to a WebSocket server listening on a Unix socket. + + This function accepts the same keyword arguments as :func:`connect`. + + It's only available on Unix. + + It's mainly useful for debugging servers listening on Unix sockets. + + Args: + path: File system path to the Unix socket. + uri: URI of the WebSocket server. ``uri`` defaults to + ``ws://localhost/`` or, when a ``ssl`` argument is provided, to + ``wss://localhost/``. + + """ + if uri is None: + if kwargs.get("ssl") is None: + uri = "ws://localhost/" + else: + uri = "wss://localhost/" + return connect(uri=uri, unix=True, path=path, **kwargs) + + +try: + from python_socks import ProxyType + from python_socks.async_.asyncio import Proxy as SocksProxy + + SOCKS_PROXY_TYPES = { + "socks5h": ProxyType.SOCKS5, + "socks5": ProxyType.SOCKS5, + "socks4a": ProxyType.SOCKS4, + "socks4": ProxyType.SOCKS4, + } + + SOCKS_PROXY_RDNS = { + "socks5h": True, + "socks5": False, + "socks4a": True, + "socks4": False, + } + + async def connect_socks_proxy( + proxy: Proxy, + ws_uri: WebSocketURI, + **kwargs: Any, + ) -> socket.socket: + """Connect via a SOCKS proxy and return the socket.""" + socks_proxy = SocksProxy( + SOCKS_PROXY_TYPES[proxy.scheme], + proxy.host, + proxy.port, + proxy.username, + proxy.password, + SOCKS_PROXY_RDNS[proxy.scheme], + ) + # connect() is documented to raise OSError. + # socks_proxy.connect() doesn't raise TimeoutError; it gets canceled. + # Wrap other exceptions in ProxyError, a subclass of InvalidHandshake. + try: + return await socks_proxy.connect(ws_uri.host, ws_uri.port, **kwargs) + except OSError: + raise + except Exception as exc: + raise ProxyError("failed to connect to SOCKS proxy") from exc + +except ImportError: + + async def connect_socks_proxy( + proxy: Proxy, + ws_uri: WebSocketURI, + **kwargs: Any, + ) -> socket.socket: + raise ImportError("python-socks is required to use a SOCKS proxy") + + +def prepare_connect_request( + proxy: Proxy, + ws_uri: WebSocketURI, + user_agent_header: str | None = None, +) -> bytes: + host = build_host(ws_uri.host, ws_uri.port, ws_uri.secure, always_include_port=True) + headers = Headers() + headers["Host"] = build_host(ws_uri.host, ws_uri.port, ws_uri.secure) + if user_agent_header is not None: + headers["User-Agent"] = user_agent_header + if proxy.username is not None: + assert proxy.password is not None # enforced by parse_proxy() + headers["Proxy-Authorization"] = build_authorization_basic( + proxy.username, proxy.password + ) + # We cannot use the Request class because it supports only GET requests. + return f"CONNECT {host} HTTP/1.1\r\n".encode() + headers.serialize() + + +class HTTPProxyConnection(asyncio.Protocol): + def __init__( + self, + ws_uri: WebSocketURI, + proxy: Proxy, + user_agent_header: str | None = None, + ): + self.ws_uri = ws_uri + self.proxy = proxy + self.user_agent_header = user_agent_header + + self.reader = StreamReader() + self.parser = Response.parse( + self.reader.read_line, + self.reader.read_exact, + self.reader.read_to_eof, + include_body=False, + ) + + loop = asyncio.get_running_loop() + self.response: asyncio.Future[Response] = loop.create_future() + + def run_parser(self) -> None: + try: + next(self.parser) + except StopIteration as exc: + response = exc.value + if 200 <= response.status_code < 300: + self.response.set_result(response) + else: + self.response.set_exception(InvalidProxyStatus(response)) + except Exception as exc: + proxy_exc = InvalidProxyMessage( + "did not receive a valid HTTP response from proxy" + ) + proxy_exc.__cause__ = exc + self.response.set_exception(proxy_exc) + + def connection_made(self, transport: asyncio.BaseTransport) -> None: + transport = cast(asyncio.Transport, transport) + self.transport = transport + self.transport.write( + prepare_connect_request(self.proxy, self.ws_uri, self.user_agent_header) + ) + + def data_received(self, data: bytes) -> None: + self.reader.feed_data(data) + self.run_parser() + + def eof_received(self) -> None: + self.reader.feed_eof() + self.run_parser() + + def connection_lost(self, exc: Exception | None) -> None: + self.reader.feed_eof() + if exc is not None: + self.response.set_exception(exc) + + +async def connect_http_proxy( + proxy: Proxy, + ws_uri: WebSocketURI, + user_agent_header: str | None = None, + **kwargs: Any, +) -> asyncio.Transport: + transport, protocol = await asyncio.get_running_loop().create_connection( + lambda: HTTPProxyConnection(ws_uri, proxy, user_agent_header), + proxy.host, + proxy.port, + **kwargs, + ) + + try: + # This raises exceptions if the connection to the proxy fails. + await protocol.response + except Exception: + transport.close() + raise + + return transport diff --git a/gestao_raul/Lib/site-packages/websockets/asyncio/compatibility.py b/gestao_raul/Lib/site-packages/websockets/asyncio/compatibility.py new file mode 100644 index 0000000..e170000 --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/asyncio/compatibility.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +import sys + + +__all__ = ["TimeoutError", "aiter", "anext", "asyncio_timeout", "asyncio_timeout_at"] + + +if sys.version_info[:2] >= (3, 11): + TimeoutError = TimeoutError + aiter = aiter + anext = anext + from asyncio import ( + timeout as asyncio_timeout, # noqa: F401 + timeout_at as asyncio_timeout_at, # noqa: F401 + ) + +else: # Python < 3.11 + from asyncio import TimeoutError + + def aiter(async_iterable): + return type(async_iterable).__aiter__(async_iterable) + + async def anext(async_iterator): + return await type(async_iterator).__anext__(async_iterator) + + from .async_timeout import ( + timeout as asyncio_timeout, # noqa: F401 + timeout_at as asyncio_timeout_at, # noqa: F401 + ) diff --git a/gestao_raul/Lib/site-packages/websockets/asyncio/connection.py b/gestao_raul/Lib/site-packages/websockets/asyncio/connection.py new file mode 100644 index 0000000..1b51e47 --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/asyncio/connection.py @@ -0,0 +1,1237 @@ +from __future__ import annotations + +import asyncio +import collections +import contextlib +import logging +import random +import struct +import sys +import traceback +import uuid +from collections.abc import AsyncIterable, AsyncIterator, Awaitable, Iterable, Mapping +from types import TracebackType +from typing import Any, Literal, cast, overload + +from ..exceptions import ( + ConcurrencyError, + ConnectionClosed, + ConnectionClosedOK, + ProtocolError, +) +from ..frames import DATA_OPCODES, BytesLike, CloseCode, Frame, Opcode +from ..http11 import Request, Response +from ..protocol import CLOSED, OPEN, Event, Protocol, State +from ..typing import Data, LoggerLike, Subprotocol +from .compatibility import ( + TimeoutError, + aiter, + anext, + asyncio_timeout, + asyncio_timeout_at, +) +from .messages import Assembler + + +__all__ = ["Connection"] + + +class Connection(asyncio.Protocol): + """ + :mod:`asyncio` implementation of a WebSocket connection. + + :class:`Connection` provides APIs shared between WebSocket servers and + clients. + + You shouldn't use it directly. Instead, use + :class:`~websockets.asyncio.client.ClientConnection` or + :class:`~websockets.asyncio.server.ServerConnection`. + + """ + + def __init__( + self, + protocol: Protocol, + *, + ping_interval: float | None = 20, + ping_timeout: float | None = 20, + close_timeout: float | None = 10, + max_queue: int | None | tuple[int | None, int | None] = 16, + write_limit: int | tuple[int, int | None] = 2**15, + ) -> None: + self.protocol = protocol + self.ping_interval = ping_interval + self.ping_timeout = ping_timeout + self.close_timeout = close_timeout + if isinstance(max_queue, int) or max_queue is None: + max_queue = (max_queue, None) + self.max_queue = max_queue + if isinstance(write_limit, int): + write_limit = (write_limit, None) + self.write_limit = write_limit + + # Inject reference to this instance in the protocol's logger. + self.protocol.logger = logging.LoggerAdapter( + self.protocol.logger, + {"websocket": self}, + ) + + # Copy attributes from the protocol for convenience. + self.id: uuid.UUID = self.protocol.id + """Unique identifier of the connection. Useful in logs.""" + self.logger: LoggerLike = self.protocol.logger + """Logger for this connection.""" + self.debug = self.protocol.debug + + # HTTP handshake request and response. + self.request: Request | None = None + """Opening handshake request.""" + self.response: Response | None = None + """Opening handshake response.""" + + # Event loop running this connection. + self.loop = asyncio.get_running_loop() + + # Assembler turning frames into messages and serializing reads. + self.recv_messages: Assembler # initialized in connection_made + + # Deadline for the closing handshake. + self.close_deadline: float | None = None + + # Protect sending fragmented messages. + self.fragmented_send_waiter: asyncio.Future[None] | None = None + + # Mapping of ping IDs to pong waiters, in chronological order. + self.pong_waiters: dict[bytes, tuple[asyncio.Future[float], float]] = {} + + self.latency: float = 0 + """ + Latency of the connection, in seconds. + + Latency is defined as the round-trip time of the connection. It is + measured by sending a Ping frame and waiting for a matching Pong frame. + Before the first measurement, :attr:`latency` is ``0``. + + By default, websockets enables a :ref:`keepalive ` mechanism + that sends Ping frames automatically at regular intervals. You can also + send Ping frames and measure latency with :meth:`ping`. + """ + + # Task that sends keepalive pings. None when ping_interval is None. + self.keepalive_task: asyncio.Task[None] | None = None + + # Exception raised while reading from the connection, to be chained to + # ConnectionClosed in order to show why the TCP connection dropped. + self.recv_exc: BaseException | None = None + + # Completed when the TCP connection is closed and the WebSocket + # connection state becomes CLOSED. + self.connection_lost_waiter: asyncio.Future[None] = self.loop.create_future() + + # Adapted from asyncio.FlowControlMixin + self.paused: bool = False + self.drain_waiters: collections.deque[asyncio.Future[None]] = ( + collections.deque() + ) + + # Public attributes + + @property + def local_address(self) -> Any: + """ + Local address of the connection. + + For IPv4 connections, this is a ``(host, port)`` tuple. + + The format of the address depends on the address family. + See :meth:`~socket.socket.getsockname`. + + """ + return self.transport.get_extra_info("sockname") + + @property + def remote_address(self) -> Any: + """ + Remote address of the connection. + + For IPv4 connections, this is a ``(host, port)`` tuple. + + The format of the address depends on the address family. + See :meth:`~socket.socket.getpeername`. + + """ + return self.transport.get_extra_info("peername") + + @property + def state(self) -> State: + """ + State of the WebSocket connection, defined in :rfc:`6455`. + + This attribute is provided for completeness. Typical applications + shouldn't check its value. Instead, they should call :meth:`~recv` or + :meth:`send` and handle :exc:`~websockets.exceptions.ConnectionClosed` + exceptions. + + """ + return self.protocol.state + + @property + def subprotocol(self) -> Subprotocol | None: + """ + Subprotocol negotiated during the opening handshake. + + :obj:`None` if no subprotocol was negotiated. + + """ + return self.protocol.subprotocol + + @property + def close_code(self) -> int | None: + """ + State of the WebSocket connection, defined in :rfc:`6455`. + + This attribute is provided for completeness. Typical applications + shouldn't check its value. Instead, they should inspect attributes + of :exc:`~websockets.exceptions.ConnectionClosed` exceptions. + + """ + return self.protocol.close_code + + @property + def close_reason(self) -> str | None: + """ + State of the WebSocket connection, defined in :rfc:`6455`. + + This attribute is provided for completeness. Typical applications + shouldn't check its value. Instead, they should inspect attributes + of :exc:`~websockets.exceptions.ConnectionClosed` exceptions. + + """ + return self.protocol.close_reason + + # Public methods + + async def __aenter__(self) -> Connection: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + ) -> None: + if exc_type is None: + await self.close() + else: + await self.close(CloseCode.INTERNAL_ERROR) + + async def __aiter__(self) -> AsyncIterator[Data]: + """ + Iterate on incoming messages. + + The iterator calls :meth:`recv` and yields messages asynchronously in an + infinite loop. + + It exits when the connection is closed normally. It raises a + :exc:`~websockets.exceptions.ConnectionClosedError` exception after a + protocol error or a network failure. + + """ + try: + while True: + yield await self.recv() + except ConnectionClosedOK: + return + + @overload + async def recv(self, decode: Literal[True]) -> str: ... + + @overload + async def recv(self, decode: Literal[False]) -> bytes: ... + + @overload + async def recv(self, decode: bool | None = None) -> Data: ... + + async def recv(self, decode: bool | None = None) -> Data: + """ + Receive the next message. + + When the connection is closed, :meth:`recv` raises + :exc:`~websockets.exceptions.ConnectionClosed`. Specifically, it raises + :exc:`~websockets.exceptions.ConnectionClosedOK` after a normal closure + and :exc:`~websockets.exceptions.ConnectionClosedError` after a protocol + error or a network failure. This is how you detect the end of the + message stream. + + Canceling :meth:`recv` is safe. There's no risk of losing data. The next + invocation of :meth:`recv` will return the next message. + + This makes it possible to enforce a timeout by wrapping :meth:`recv` in + :func:`~asyncio.timeout` or :func:`~asyncio.wait_for`. + + When the message is fragmented, :meth:`recv` waits until all fragments + are received, reassembles them, and returns the whole message. + + Args: + decode: Set this flag to override the default behavior of returning + :class:`str` or :class:`bytes`. See below for details. + + Returns: + A string (:class:`str`) for a Text_ frame or a bytestring + (:class:`bytes`) for a Binary_ frame. + + .. _Text: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 + .. _Binary: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 + + You may override this behavior with the ``decode`` argument: + + * Set ``decode=False`` to disable UTF-8 decoding of Text_ frames and + return a bytestring (:class:`bytes`). This improves performance + when decoding isn't needed, for example if the message contains + JSON and you're using a JSON library that expects a bytestring. + * Set ``decode=True`` to force UTF-8 decoding of Binary_ frames + and return a string (:class:`str`). This may be useful for + servers that send binary frames instead of text frames. + + Raises: + ConnectionClosed: When the connection is closed. + ConcurrencyError: If two coroutines call :meth:`recv` or + :meth:`recv_streaming` concurrently. + + """ + try: + return await self.recv_messages.get(decode) + except EOFError: + pass + # fallthrough + except ConcurrencyError: + raise ConcurrencyError( + "cannot call recv while another coroutine " + "is already running recv or recv_streaming" + ) from None + except UnicodeDecodeError as exc: + async with self.send_context(): + self.protocol.fail( + CloseCode.INVALID_DATA, + f"{exc.reason} at position {exc.start}", + ) + # fallthrough + + # Wait for the protocol state to be CLOSED before accessing close_exc. + await asyncio.shield(self.connection_lost_waiter) + raise self.protocol.close_exc from self.recv_exc + + @overload + def recv_streaming(self, decode: Literal[True]) -> AsyncIterator[str]: ... + + @overload + def recv_streaming(self, decode: Literal[False]) -> AsyncIterator[bytes]: ... + + @overload + def recv_streaming(self, decode: bool | None = None) -> AsyncIterator[Data]: ... + + async def recv_streaming(self, decode: bool | None = None) -> AsyncIterator[Data]: + """ + Receive the next message frame by frame. + + This method is designed for receiving fragmented messages. It returns an + asynchronous iterator that yields each fragment as it is received. This + iterator must be fully consumed. Else, future calls to :meth:`recv` or + :meth:`recv_streaming` will raise + :exc:`~websockets.exceptions.ConcurrencyError`, making the connection + unusable. + + :meth:`recv_streaming` raises the same exceptions as :meth:`recv`. + + Canceling :meth:`recv_streaming` before receiving the first frame is + safe. Canceling it after receiving one or more frames leaves the + iterator in a partially consumed state, making the connection unusable. + Instead, you should close the connection with :meth:`close`. + + Args: + decode: Set this flag to override the default behavior of returning + :class:`str` or :class:`bytes`. See below for details. + + Returns: + An iterator of strings (:class:`str`) for a Text_ frame or + bytestrings (:class:`bytes`) for a Binary_ frame. + + .. _Text: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 + .. _Binary: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 + + You may override this behavior with the ``decode`` argument: + + * Set ``decode=False`` to disable UTF-8 decoding of Text_ frames + and return bytestrings (:class:`bytes`). This may be useful to + optimize performance when decoding isn't needed. + * Set ``decode=True`` to force UTF-8 decoding of Binary_ frames + and return strings (:class:`str`). This is useful for servers + that send binary frames instead of text frames. + + Raises: + ConnectionClosed: When the connection is closed. + ConcurrencyError: If two coroutines call :meth:`recv` or + :meth:`recv_streaming` concurrently. + + """ + try: + async for frame in self.recv_messages.get_iter(decode): + yield frame + return + except EOFError: + pass + # fallthrough + except ConcurrencyError: + raise ConcurrencyError( + "cannot call recv_streaming while another coroutine " + "is already running recv or recv_streaming" + ) from None + except UnicodeDecodeError as exc: + async with self.send_context(): + self.protocol.fail( + CloseCode.INVALID_DATA, + f"{exc.reason} at position {exc.start}", + ) + # fallthrough + + # Wait for the protocol state to be CLOSED before accessing close_exc. + await asyncio.shield(self.connection_lost_waiter) + raise self.protocol.close_exc from self.recv_exc + + async def send( + self, + message: Data | Iterable[Data] | AsyncIterable[Data], + text: bool | None = None, + ) -> None: + """ + Send a message. + + A string (:class:`str`) is sent as a Text_ frame. A bytestring or + bytes-like object (:class:`bytes`, :class:`bytearray`, or + :class:`memoryview`) is sent as a Binary_ frame. + + .. _Text: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 + .. _Binary: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 + + You may override this behavior with the ``text`` argument: + + * Set ``text=True`` to send a bytestring or bytes-like object + (:class:`bytes`, :class:`bytearray`, or :class:`memoryview`) as a + Text_ frame. This improves performance when the message is already + UTF-8 encoded, for example if the message contains JSON and you're + using a JSON library that produces a bytestring. + * Set ``text=False`` to send a string (:class:`str`) in a Binary_ + frame. This may be useful for servers that expect binary frames + instead of text frames. + + :meth:`send` also accepts an iterable or an asynchronous iterable of + strings, bytestrings, or bytes-like objects to enable fragmentation_. + Each item is treated as a message fragment and sent in its own frame. + All items must be of the same type, or else :meth:`send` will raise a + :exc:`TypeError` and the connection will be closed. + + .. _fragmentation: https://datatracker.ietf.org/doc/html/rfc6455#section-5.4 + + :meth:`send` rejects dict-like objects because this is often an error. + (If you really want to send the keys of a dict-like object as fragments, + call its :meth:`~dict.keys` method and pass the result to :meth:`send`.) + + Canceling :meth:`send` is discouraged. Instead, you should close the + connection with :meth:`close`. Indeed, there are only two situations + where :meth:`send` may yield control to the event loop and then get + canceled; in both cases, :meth:`close` has the same effect and is + more clear: + + 1. The write buffer is full. If you don't want to wait until enough + data is sent, your only alternative is to close the connection. + :meth:`close` will likely time out then abort the TCP connection. + 2. ``message`` is an asynchronous iterator that yields control. + Stopping in the middle of a fragmented message will cause a + protocol error and the connection will be closed. + + When the connection is closed, :meth:`send` raises + :exc:`~websockets.exceptions.ConnectionClosed`. Specifically, it + raises :exc:`~websockets.exceptions.ConnectionClosedOK` after a normal + connection closure and + :exc:`~websockets.exceptions.ConnectionClosedError` after a protocol + error or a network failure. + + Args: + message: Message to send. + + Raises: + ConnectionClosed: When the connection is closed. + TypeError: If ``message`` doesn't have a supported type. + + """ + # While sending a fragmented message, prevent sending other messages + # until all fragments are sent. + while self.fragmented_send_waiter is not None: + await asyncio.shield(self.fragmented_send_waiter) + + # Unfragmented message -- this case must be handled first because + # strings and bytes-like objects are iterable. + + if isinstance(message, str): + async with self.send_context(): + if text is False: + self.protocol.send_binary(message.encode()) + else: + self.protocol.send_text(message.encode()) + + elif isinstance(message, BytesLike): + async with self.send_context(): + if text is True: + self.protocol.send_text(message) + else: + self.protocol.send_binary(message) + + # Catch a common mistake -- passing a dict to send(). + + elif isinstance(message, Mapping): + raise TypeError("data is a dict-like object") + + # Fragmented message -- regular iterator. + + elif isinstance(message, Iterable): + chunks = iter(message) + try: + chunk = next(chunks) + except StopIteration: + return + + assert self.fragmented_send_waiter is None + self.fragmented_send_waiter = self.loop.create_future() + try: + # First fragment. + if isinstance(chunk, str): + async with self.send_context(): + if text is False: + self.protocol.send_binary(chunk.encode(), fin=False) + else: + self.protocol.send_text(chunk.encode(), fin=False) + encode = True + elif isinstance(chunk, BytesLike): + async with self.send_context(): + if text is True: + self.protocol.send_text(chunk, fin=False) + else: + self.protocol.send_binary(chunk, fin=False) + encode = False + else: + raise TypeError("iterable must contain bytes or str") + + # Other fragments + for chunk in chunks: + if isinstance(chunk, str) and encode: + async with self.send_context(): + self.protocol.send_continuation(chunk.encode(), fin=False) + elif isinstance(chunk, BytesLike) and not encode: + async with self.send_context(): + self.protocol.send_continuation(chunk, fin=False) + else: + raise TypeError("iterable must contain uniform types") + + # Final fragment. + async with self.send_context(): + self.protocol.send_continuation(b"", fin=True) + + except Exception: + # We're half-way through a fragmented message and we can't + # complete it. This makes the connection unusable. + async with self.send_context(): + self.protocol.fail( + CloseCode.INTERNAL_ERROR, + "error in fragmented message", + ) + raise + + finally: + self.fragmented_send_waiter.set_result(None) + self.fragmented_send_waiter = None + + # Fragmented message -- async iterator. + + elif isinstance(message, AsyncIterable): + achunks = aiter(message) + try: + chunk = await anext(achunks) + except StopAsyncIteration: + return + + assert self.fragmented_send_waiter is None + self.fragmented_send_waiter = self.loop.create_future() + try: + # First fragment. + if isinstance(chunk, str): + if text is False: + async with self.send_context(): + self.protocol.send_binary(chunk.encode(), fin=False) + else: + async with self.send_context(): + self.protocol.send_text(chunk.encode(), fin=False) + encode = True + elif isinstance(chunk, BytesLike): + if text is True: + async with self.send_context(): + self.protocol.send_text(chunk, fin=False) + else: + async with self.send_context(): + self.protocol.send_binary(chunk, fin=False) + encode = False + else: + raise TypeError("async iterable must contain bytes or str") + + # Other fragments + async for chunk in achunks: + if isinstance(chunk, str) and encode: + async with self.send_context(): + self.protocol.send_continuation(chunk.encode(), fin=False) + elif isinstance(chunk, BytesLike) and not encode: + async with self.send_context(): + self.protocol.send_continuation(chunk, fin=False) + else: + raise TypeError("async iterable must contain uniform types") + + # Final fragment. + async with self.send_context(): + self.protocol.send_continuation(b"", fin=True) + + except Exception: + # We're half-way through a fragmented message and we can't + # complete it. This makes the connection unusable. + async with self.send_context(): + self.protocol.fail( + CloseCode.INTERNAL_ERROR, + "error in fragmented message", + ) + raise + + finally: + self.fragmented_send_waiter.set_result(None) + self.fragmented_send_waiter = None + + else: + raise TypeError("data must be str, bytes, iterable, or async iterable") + + async def close(self, code: int = 1000, reason: str = "") -> None: + """ + Perform the closing handshake. + + :meth:`close` waits for the other end to complete the handshake and + for the TCP connection to terminate. + + :meth:`close` is idempotent: it doesn't do anything once the + connection is closed. + + Args: + code: WebSocket close code. + reason: WebSocket close reason. + + """ + try: + # The context manager takes care of waiting for the TCP connection + # to terminate after calling a method that sends a close frame. + async with self.send_context(): + if self.fragmented_send_waiter is not None: + self.protocol.fail( + CloseCode.INTERNAL_ERROR, + "close during fragmented message", + ) + else: + self.protocol.send_close(code, reason) + except ConnectionClosed: + # Ignore ConnectionClosed exceptions raised from send_context(). + # They mean that the connection is closed, which was the goal. + pass + + async def wait_closed(self) -> None: + """ + Wait until the connection is closed. + + :meth:`wait_closed` waits for the closing handshake to complete and for + the TCP connection to terminate. + + """ + await asyncio.shield(self.connection_lost_waiter) + + async def ping(self, data: Data | None = None) -> Awaitable[float]: + """ + Send a Ping_. + + .. _Ping: https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.2 + + A ping may serve as a keepalive or as a check that the remote endpoint + received all messages up to this point + + Args: + data: Payload of the ping. A :class:`str` will be encoded to UTF-8. + If ``data`` is :obj:`None`, the payload is four random bytes. + + Returns: + A future that will be completed when the corresponding pong is + received. You can ignore it if you don't intend to wait. The result + of the future is the latency of the connection in seconds. + + :: + + pong_waiter = await ws.ping() + # only if you want to wait for the corresponding pong + latency = await pong_waiter + + Raises: + ConnectionClosed: When the connection is closed. + ConcurrencyError: If another ping was sent with the same data and + the corresponding pong wasn't received yet. + + """ + if isinstance(data, BytesLike): + data = bytes(data) + elif isinstance(data, str): + data = data.encode() + elif data is not None: + raise TypeError("data must be str or bytes-like") + + async with self.send_context(): + # Protect against duplicates if a payload is explicitly set. + if data in self.pong_waiters: + raise ConcurrencyError("already waiting for a pong with the same data") + + # Generate a unique random payload otherwise. + while data is None or data in self.pong_waiters: + data = struct.pack("!I", random.getrandbits(32)) + + pong_waiter = self.loop.create_future() + # The event loop's default clock is time.monotonic(). Its resolution + # is a bit low on Windows (~16ms). This is improved in Python 3.13. + self.pong_waiters[data] = (pong_waiter, self.loop.time()) + self.protocol.send_ping(data) + return pong_waiter + + async def pong(self, data: Data = b"") -> None: + """ + Send a Pong_. + + .. _Pong: https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.3 + + An unsolicited pong may serve as a unidirectional heartbeat. + + Args: + data: Payload of the pong. A :class:`str` will be encoded to UTF-8. + + Raises: + ConnectionClosed: When the connection is closed. + + """ + if isinstance(data, BytesLike): + data = bytes(data) + elif isinstance(data, str): + data = data.encode() + else: + raise TypeError("data must be str or bytes-like") + + async with self.send_context(): + self.protocol.send_pong(data) + + # Private methods + + def process_event(self, event: Event) -> None: + """ + Process one incoming event. + + This method is overridden in subclasses to handle the handshake. + + """ + assert isinstance(event, Frame) + if event.opcode in DATA_OPCODES: + self.recv_messages.put(event) + + if event.opcode is Opcode.PONG: + self.acknowledge_pings(bytes(event.data)) + + def acknowledge_pings(self, data: bytes) -> None: + """ + Acknowledge pings when receiving a pong. + + """ + # Ignore unsolicited pong. + if data not in self.pong_waiters: + return + + pong_timestamp = self.loop.time() + + # Sending a pong for only the most recent ping is legal. + # Acknowledge all previous pings too in that case. + ping_id = None + ping_ids = [] + for ping_id, (pong_waiter, ping_timestamp) in self.pong_waiters.items(): + ping_ids.append(ping_id) + latency = pong_timestamp - ping_timestamp + if not pong_waiter.done(): + pong_waiter.set_result(latency) + if ping_id == data: + self.latency = latency + break + else: + raise AssertionError("solicited pong not found in pings") + + # Remove acknowledged pings from self.pong_waiters. + for ping_id in ping_ids: + del self.pong_waiters[ping_id] + + def abort_pings(self) -> None: + """ + Raise ConnectionClosed in pending pings. + + They'll never receive a pong once the connection is closed. + + """ + assert self.protocol.state is CLOSED + exc = self.protocol.close_exc + + for pong_waiter, _ping_timestamp in self.pong_waiters.values(): + if not pong_waiter.done(): + pong_waiter.set_exception(exc) + # If the exception is never retrieved, it will be logged when ping + # is garbage-collected. This is confusing for users. + # Given that ping is done (with an exception), canceling it does + # nothing, but it prevents logging the exception. + pong_waiter.cancel() + + self.pong_waiters.clear() + + async def keepalive(self) -> None: + """ + Send a Ping frame and wait for a Pong frame at regular intervals. + + """ + assert self.ping_interval is not None + latency = 0.0 + try: + while True: + # If self.ping_timeout > latency > self.ping_interval, + # pings will be sent immediately after receiving pongs. + # The period will be longer than self.ping_interval. + await asyncio.sleep(self.ping_interval - latency) + + # This cannot raise ConnectionClosed when the connection is + # closing because ping(), via send_context(), waits for the + # connection to be closed before raising ConnectionClosed. + # However, connection_lost() cancels keepalive_task before + # it gets a chance to resume excuting. + pong_waiter = await self.ping() + if self.debug: + self.logger.debug("% sent keepalive ping") + + if self.ping_timeout is not None: + try: + async with asyncio_timeout(self.ping_timeout): + # connection_lost cancels keepalive immediately + # after setting a ConnectionClosed exception on + # pong_waiter. A CancelledError is raised here, + # not a ConnectionClosed exception. + latency = await pong_waiter + self.logger.debug("% received keepalive pong") + except asyncio.TimeoutError: + if self.debug: + self.logger.debug("- timed out waiting for keepalive pong") + async with self.send_context(): + self.protocol.fail( + CloseCode.INTERNAL_ERROR, + "keepalive ping timeout", + ) + raise AssertionError( + "send_context() should wait for connection_lost(), " + "which cancels keepalive()" + ) + except Exception: + self.logger.error("keepalive ping failed", exc_info=True) + + def start_keepalive(self) -> None: + """ + Run :meth:`keepalive` in a task, unless keepalive is disabled. + + """ + if self.ping_interval is not None: + self.keepalive_task = self.loop.create_task(self.keepalive()) + + @contextlib.asynccontextmanager + async def send_context( + self, + *, + expected_state: State = OPEN, # CONNECTING during the opening handshake + ) -> AsyncIterator[None]: + """ + Create a context for writing to the connection from user code. + + On entry, :meth:`send_context` checks that the connection is open; on + exit, it writes outgoing data to the socket:: + + async with self.send_context(): + self.protocol.send_text(message.encode()) + + When the connection isn't open on entry, when the connection is expected + to close on exit, or when an unexpected error happens, terminating the + connection, :meth:`send_context` waits until the connection is closed + then raises :exc:`~websockets.exceptions.ConnectionClosed`. + + """ + # Should we wait until the connection is closed? + wait_for_close = False + # Should we close the transport and raise ConnectionClosed? + raise_close_exc = False + # What exception should we chain ConnectionClosed to? + original_exc: BaseException | None = None + + if self.protocol.state is expected_state: + # Let the caller interact with the protocol. + try: + yield + except (ProtocolError, ConcurrencyError): + # The protocol state wasn't changed. Exit immediately. + raise + except Exception as exc: + self.logger.error("unexpected internal error", exc_info=True) + # This branch should never run. It's a safety net in case of + # bugs. Since we don't know what happened, we will close the + # connection and raise the exception to the caller. + wait_for_close = False + raise_close_exc = True + original_exc = exc + else: + # Check if the connection is expected to close soon. + if self.protocol.close_expected(): + wait_for_close = True + # If the connection is expected to close soon, set the + # close deadline based on the close timeout. + # Since we tested earlier that protocol.state was OPEN + # (or CONNECTING), self.close_deadline is still None. + if self.close_timeout is not None: + assert self.close_deadline is None + self.close_deadline = self.loop.time() + self.close_timeout + # Write outgoing data to the socket and enforce flow control. + try: + self.send_data() + await self.drain() + except Exception as exc: + if self.debug: + self.logger.debug("! error while sending data", exc_info=True) + # While the only expected exception here is OSError, + # other exceptions would be treated identically. + wait_for_close = False + raise_close_exc = True + original_exc = exc + + else: # self.protocol.state is not expected_state + # Minor layering violation: we assume that the connection + # will be closing soon if it isn't in the expected state. + wait_for_close = True + # Calculate close_deadline if it wasn't set yet. + if self.close_timeout is not None: + if self.close_deadline is None: + self.close_deadline = self.loop.time() + self.close_timeout + raise_close_exc = True + + # If the connection is expected to close soon and the close timeout + # elapses, close the socket to terminate the connection. + if wait_for_close: + try: + async with asyncio_timeout_at(self.close_deadline): + await asyncio.shield(self.connection_lost_waiter) + except TimeoutError: + # There's no risk to overwrite another error because + # original_exc is never set when wait_for_close is True. + assert original_exc is None + original_exc = TimeoutError("timed out while closing connection") + # Set recv_exc before closing the transport in order to get + # proper exception reporting. + raise_close_exc = True + self.set_recv_exc(original_exc) + + # If an error occurred, close the transport to terminate the connection and + # raise an exception. + if raise_close_exc: + self.transport.abort() + # Wait for the protocol state to be CLOSED before accessing close_exc. + await asyncio.shield(self.connection_lost_waiter) + raise self.protocol.close_exc from original_exc + + def send_data(self) -> None: + """ + Send outgoing data. + + Raises: + OSError: When a socket operations fails. + + """ + for data in self.protocol.data_to_send(): + if data: + self.transport.write(data) + else: + # Half-close the TCP connection when possible i.e. no TLS. + if self.transport.can_write_eof(): + if self.debug: + self.logger.debug("x half-closing TCP connection") + # write_eof() doesn't document which exceptions it raises. + # OSError is plausible. uvloop can raise RuntimeError here. + try: + self.transport.write_eof() + except (OSError, RuntimeError): # pragma: no cover + pass + # Else, close the TCP connection. + else: # pragma: no cover + if self.debug: + self.logger.debug("x closing TCP connection") + self.transport.close() + + def set_recv_exc(self, exc: BaseException | None) -> None: + """ + Set recv_exc, if not set yet. + + """ + if self.recv_exc is None: + self.recv_exc = exc + + # asyncio.Protocol methods + + # Connection callbacks + + def connection_made(self, transport: asyncio.BaseTransport) -> None: + transport = cast(asyncio.Transport, transport) + self.recv_messages = Assembler( + *self.max_queue, + pause=transport.pause_reading, + resume=transport.resume_reading, + ) + transport.set_write_buffer_limits(*self.write_limit) + self.transport = transport + + def connection_lost(self, exc: Exception | None) -> None: + # Calling protocol.receive_eof() is safe because it's idempotent. + # This guarantees that the protocol state becomes CLOSED. + self.protocol.receive_eof() + assert self.protocol.state is CLOSED + + self.set_recv_exc(exc) + + # Abort recv() and pending pings with a ConnectionClosed exception. + self.recv_messages.close() + self.abort_pings() + + if self.keepalive_task is not None: + self.keepalive_task.cancel() + + # If self.connection_lost_waiter isn't pending, that's a bug, because: + # - it's set only here in connection_lost() which is called only once; + # - it must never be canceled. + self.connection_lost_waiter.set_result(None) + + # Adapted from asyncio.streams.FlowControlMixin + if self.paused: # pragma: no cover + self.paused = False + for waiter in self.drain_waiters: + if not waiter.done(): + if exc is None: + waiter.set_result(None) + else: + waiter.set_exception(exc) + + # Flow control callbacks + + def pause_writing(self) -> None: # pragma: no cover + # Adapted from asyncio.streams.FlowControlMixin + assert not self.paused + self.paused = True + + def resume_writing(self) -> None: # pragma: no cover + # Adapted from asyncio.streams.FlowControlMixin + assert self.paused + self.paused = False + for waiter in self.drain_waiters: + if not waiter.done(): + waiter.set_result(None) + + async def drain(self) -> None: # pragma: no cover + # We don't check if the connection is closed because we call drain() + # immediately after write() and write() would fail in that case. + + # Adapted from asyncio.streams.StreamWriter + # Yield to the event loop so that connection_lost() may be called. + if self.transport.is_closing(): + await asyncio.sleep(0) + + # Adapted from asyncio.streams.FlowControlMixin + if self.paused: + waiter = self.loop.create_future() + self.drain_waiters.append(waiter) + try: + await waiter + finally: + self.drain_waiters.remove(waiter) + + # Streaming protocol callbacks + + def data_received(self, data: bytes) -> None: + # Feed incoming data to the protocol. + self.protocol.receive_data(data) + + # This isn't expected to raise an exception. + events = self.protocol.events_received() + + # Write outgoing data to the transport. + try: + self.send_data() + except Exception as exc: + if self.debug: + self.logger.debug("! error while sending data", exc_info=True) + self.set_recv_exc(exc) + + if self.protocol.close_expected(): + # If the connection is expected to close soon, set the + # close deadline based on the close timeout. + if self.close_timeout is not None: + if self.close_deadline is None: + self.close_deadline = self.loop.time() + self.close_timeout + + for event in events: + # This isn't expected to raise an exception. + self.process_event(event) + + def eof_received(self) -> None: + # Feed the end of the data stream to the connection. + self.protocol.receive_eof() + + # This isn't expected to raise an exception. + events = self.protocol.events_received() + + # There is no error handling because send_data() can only write + # the end of the data stream here and it shouldn't raise errors. + self.send_data() + + # This code path is triggered when receiving an HTTP response + # without a Content-Length header. This is the only case where + # reading until EOF generates an event; all other events have + # a known length. Ignore for coverage measurement because tests + # are in test_client.py rather than test_connection.py. + for event in events: # pragma: no cover + # This isn't expected to raise an exception. + self.process_event(event) + + # The WebSocket protocol has its own closing handshake: endpoints close + # the TCP or TLS connection after sending and receiving a close frame. + # As a consequence, they never need to write after receiving EOF, so + # there's no reason to keep the transport open by returning True. + # Besides, that doesn't work on TLS connections. + + +# broadcast() is defined in the connection module even though it's primarily +# used by servers and documented in the server module because it works with +# client connections too and because it's easier to test together with the +# Connection class. + + +def broadcast( + connections: Iterable[Connection], + message: Data, + raise_exceptions: bool = False, +) -> None: + """ + Broadcast a message to several WebSocket connections. + + A string (:class:`str`) is sent as a Text_ frame. A bytestring or bytes-like + object (:class:`bytes`, :class:`bytearray`, or :class:`memoryview`) is sent + as a Binary_ frame. + + .. _Text: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 + .. _Binary: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 + + :func:`broadcast` pushes the message synchronously to all connections even + if their write buffers are overflowing. There's no backpressure. + + If you broadcast messages faster than a connection can handle them, messages + will pile up in its write buffer until the connection times out. Keep + ``ping_interval`` and ``ping_timeout`` low to prevent excessive memory usage + from slow connections. + + Unlike :meth:`~websockets.asyncio.connection.Connection.send`, + :func:`broadcast` doesn't support sending fragmented messages. Indeed, + fragmentation is useful for sending large messages without buffering them in + memory, while :func:`broadcast` buffers one copy per connection as fast as + possible. + + :func:`broadcast` skips connections that aren't open in order to avoid + errors on connections where the closing handshake is in progress. + + :func:`broadcast` ignores failures to write the message on some connections. + It continues writing to other connections. On Python 3.11 and above, you may + set ``raise_exceptions`` to :obj:`True` to record failures and raise all + exceptions in a :pep:`654` :exc:`ExceptionGroup`. + + While :func:`broadcast` makes more sense for servers, it works identically + with clients, if you have a use case for opening connections to many servers + and broadcasting a message to them. + + Args: + websockets: WebSocket connections to which the message will be sent. + message: Message to send. + raise_exceptions: Whether to raise an exception in case of failures. + + Raises: + TypeError: If ``message`` doesn't have a supported type. + + """ + if isinstance(message, str): + send_method = "send_text" + message = message.encode() + elif isinstance(message, BytesLike): + send_method = "send_binary" + else: + raise TypeError("data must be str or bytes") + + if raise_exceptions: + if sys.version_info[:2] < (3, 11): # pragma: no cover + raise ValueError("raise_exceptions requires at least Python 3.11") + exceptions: list[Exception] = [] + + for connection in connections: + exception: Exception + + if connection.protocol.state is not OPEN: + continue + + if connection.fragmented_send_waiter is not None: + if raise_exceptions: + exception = ConcurrencyError("sending a fragmented message") + exceptions.append(exception) + else: + connection.logger.warning( + "skipped broadcast: sending a fragmented message", + ) + continue + + try: + # Call connection.protocol.send_text or send_binary. + # Either way, message is already converted to bytes. + getattr(connection.protocol, send_method)(message) + connection.send_data() + except Exception as write_exception: + if raise_exceptions: + exception = RuntimeError("failed to write message") + exception.__cause__ = write_exception + exceptions.append(exception) + else: + connection.logger.warning( + "skipped broadcast: failed to write message: %s", + traceback.format_exception_only( + # Remove first argument when dropping Python 3.9. + type(write_exception), + write_exception, + )[0].strip(), + ) + + if raise_exceptions and exceptions: + raise ExceptionGroup("skipped broadcast", exceptions) + + +# Pretend that broadcast is actually defined in the server module. +broadcast.__module__ = "websockets.asyncio.server" diff --git a/gestao_raul/Lib/site-packages/websockets/asyncio/messages.py b/gestao_raul/Lib/site-packages/websockets/asyncio/messages.py new file mode 100644 index 0000000..1fd4181 --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/asyncio/messages.py @@ -0,0 +1,314 @@ +from __future__ import annotations + +import asyncio +import codecs +import collections +from collections.abc import AsyncIterator, Iterable +from typing import Any, Callable, Generic, Literal, TypeVar, overload + +from ..exceptions import ConcurrencyError +from ..frames import OP_BINARY, OP_CONT, OP_TEXT, Frame +from ..typing import Data + + +__all__ = ["Assembler"] + +UTF8Decoder = codecs.getincrementaldecoder("utf-8") + +T = TypeVar("T") + + +class SimpleQueue(Generic[T]): + """ + Simplified version of :class:`asyncio.Queue`. + + Provides only the subset of functionality needed by :class:`Assembler`. + + """ + + def __init__(self) -> None: + self.loop = asyncio.get_running_loop() + self.get_waiter: asyncio.Future[None] | None = None + self.queue: collections.deque[T] = collections.deque() + + def __len__(self) -> int: + return len(self.queue) + + def put(self, item: T) -> None: + """Put an item into the queue without waiting.""" + self.queue.append(item) + if self.get_waiter is not None and not self.get_waiter.done(): + self.get_waiter.set_result(None) + + async def get(self, block: bool = True) -> T: + """Remove and return an item from the queue, waiting if necessary.""" + if not self.queue: + if not block: + raise EOFError("stream of frames ended") + assert self.get_waiter is None, "cannot call get() concurrently" + self.get_waiter = self.loop.create_future() + try: + await self.get_waiter + finally: + self.get_waiter.cancel() + self.get_waiter = None + return self.queue.popleft() + + def reset(self, items: Iterable[T]) -> None: + """Put back items into an empty, idle queue.""" + assert self.get_waiter is None, "cannot reset() while get() is running" + assert not self.queue, "cannot reset() while queue isn't empty" + self.queue.extend(items) + + def abort(self) -> None: + """Close the queue, raising EOFError in get() if necessary.""" + if self.get_waiter is not None and not self.get_waiter.done(): + self.get_waiter.set_exception(EOFError("stream of frames ended")) + + +class Assembler: + """ + Assemble messages from frames. + + :class:`Assembler` expects only data frames. The stream of frames must + respect the protocol; if it doesn't, the behavior is undefined. + + Args: + pause: Called when the buffer of frames goes above the high water mark; + should pause reading from the network. + resume: Called when the buffer of frames goes below the low water mark; + should resume reading from the network. + + """ + + # coverage reports incorrectly: "line NN didn't jump to the function exit" + def __init__( # pragma: no cover + self, + high: int | None = None, + low: int | None = None, + pause: Callable[[], Any] = lambda: None, + resume: Callable[[], Any] = lambda: None, + ) -> None: + # Queue of incoming frames. + self.frames: SimpleQueue[Frame] = SimpleQueue() + + # We cannot put a hard limit on the size of the queue because a single + # call to Protocol.data_received() could produce thousands of frames, + # which must be buffered. Instead, we pause reading when the buffer goes + # above the high limit and we resume when it goes under the low limit. + if high is not None and low is None: + low = high // 4 + if high is None and low is not None: + high = low * 4 + if high is not None and low is not None: + if low < 0: + raise ValueError("low must be positive or equal to zero") + if high < low: + raise ValueError("high must be greater than or equal to low") + self.high, self.low = high, low + self.pause = pause + self.resume = resume + self.paused = False + + # This flag prevents concurrent calls to get() by user code. + self.get_in_progress = False + + # This flag marks the end of the connection. + self.closed = False + + @overload + async def get(self, decode: Literal[True]) -> str: ... + + @overload + async def get(self, decode: Literal[False]) -> bytes: ... + + @overload + async def get(self, decode: bool | None = None) -> Data: ... + + async def get(self, decode: bool | None = None) -> Data: + """ + Read the next message. + + :meth:`get` returns a single :class:`str` or :class:`bytes`. + + If the message is fragmented, :meth:`get` waits until the last frame is + received, then it reassembles the message and returns it. To receive + messages frame by frame, use :meth:`get_iter` instead. + + Args: + decode: :obj:`False` disables UTF-8 decoding of text frames and + returns :class:`bytes`. :obj:`True` forces UTF-8 decoding of + binary frames and returns :class:`str`. + + Raises: + EOFError: If the stream of frames has ended. + UnicodeDecodeError: If a text frame contains invalid UTF-8. + ConcurrencyError: If two coroutines run :meth:`get` or + :meth:`get_iter` concurrently. + + """ + if self.get_in_progress: + raise ConcurrencyError("get() or get_iter() is already running") + self.get_in_progress = True + + # Locking with get_in_progress prevents concurrent execution + # until get() fetches a complete message or is canceled. + + try: + # First frame + frame = await self.frames.get(not self.closed) + self.maybe_resume() + assert frame.opcode is OP_TEXT or frame.opcode is OP_BINARY + if decode is None: + decode = frame.opcode is OP_TEXT + frames = [frame] + + # Following frames, for fragmented messages + while not frame.fin: + try: + frame = await self.frames.get(not self.closed) + except asyncio.CancelledError: + # Put frames already received back into the queue + # so that future calls to get() can return them. + self.frames.reset(frames) + raise + self.maybe_resume() + assert frame.opcode is OP_CONT + frames.append(frame) + + finally: + self.get_in_progress = False + + data = b"".join(frame.data for frame in frames) + if decode: + return data.decode() + else: + return data + + @overload + def get_iter(self, decode: Literal[True]) -> AsyncIterator[str]: ... + + @overload + def get_iter(self, decode: Literal[False]) -> AsyncIterator[bytes]: ... + + @overload + def get_iter(self, decode: bool | None = None) -> AsyncIterator[Data]: ... + + async def get_iter(self, decode: bool | None = None) -> AsyncIterator[Data]: + """ + Stream the next message. + + Iterating the return value of :meth:`get_iter` asynchronously yields a + :class:`str` or :class:`bytes` for each frame in the message. + + The iterator must be fully consumed before calling :meth:`get_iter` or + :meth:`get` again. Else, :exc:`ConcurrencyError` is raised. + + This method only makes sense for fragmented messages. If messages aren't + fragmented, use :meth:`get` instead. + + Args: + decode: :obj:`False` disables UTF-8 decoding of text frames and + returns :class:`bytes`. :obj:`True` forces UTF-8 decoding of + binary frames and returns :class:`str`. + + Raises: + EOFError: If the stream of frames has ended. + UnicodeDecodeError: If a text frame contains invalid UTF-8. + ConcurrencyError: If two coroutines run :meth:`get` or + :meth:`get_iter` concurrently. + + """ + if self.get_in_progress: + raise ConcurrencyError("get() or get_iter() is already running") + self.get_in_progress = True + + # Locking with get_in_progress prevents concurrent execution + # until get_iter() fetches a complete message or is canceled. + + # If get_iter() raises an exception e.g. in decoder.decode(), + # get_in_progress remains set and the connection becomes unusable. + + # First frame + try: + frame = await self.frames.get(not self.closed) + except asyncio.CancelledError: + self.get_in_progress = False + raise + self.maybe_resume() + assert frame.opcode is OP_TEXT or frame.opcode is OP_BINARY + if decode is None: + decode = frame.opcode is OP_TEXT + if decode: + decoder = UTF8Decoder() + yield decoder.decode(frame.data, frame.fin) + else: + yield frame.data + + # Following frames, for fragmented messages + while not frame.fin: + # We cannot handle asyncio.CancelledError because we don't buffer + # previous fragments — we're streaming them. Canceling get_iter() + # here will leave the assembler in a stuck state. Future calls to + # get() or get_iter() will raise ConcurrencyError. + frame = await self.frames.get(not self.closed) + self.maybe_resume() + assert frame.opcode is OP_CONT + if decode: + yield decoder.decode(frame.data, frame.fin) + else: + yield frame.data + + self.get_in_progress = False + + def put(self, frame: Frame) -> None: + """ + Add ``frame`` to the next message. + + Raises: + EOFError: If the stream of frames has ended. + + """ + if self.closed: + raise EOFError("stream of frames ended") + + self.frames.put(frame) + self.maybe_pause() + + def maybe_pause(self) -> None: + """Pause the writer if queue is above the high water mark.""" + # Skip if flow control is disabled + if self.high is None: + return + + # Check for "> high" to support high = 0 + if len(self.frames) > self.high and not self.paused: + self.paused = True + self.pause() + + def maybe_resume(self) -> None: + """Resume the writer if queue is below the low water mark.""" + # Skip if flow control is disabled + if self.low is None: + return + + # Check for "<= low" to support low = 0 + if len(self.frames) <= self.low and self.paused: + self.paused = False + self.resume() + + def close(self) -> None: + """ + End the stream of frames. + + Calling :meth:`close` concurrently with :meth:`get`, :meth:`get_iter`, + or :meth:`put` is safe. They will raise :exc:`EOFError`. + + """ + if self.closed: + return + + self.closed = True + + # Unblock get() or get_iter(). + self.frames.abort() diff --git a/gestao_raul/Lib/site-packages/websockets/asyncio/router.py b/gestao_raul/Lib/site-packages/websockets/asyncio/router.py new file mode 100644 index 0000000..047e7ef --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/asyncio/router.py @@ -0,0 +1,198 @@ +from __future__ import annotations + +import http +import ssl as ssl_module +import urllib.parse +from typing import Any, Awaitable, Callable, Literal + +from werkzeug.exceptions import NotFound +from werkzeug.routing import Map, RequestRedirect + +from ..http11 import Request, Response +from .server import Server, ServerConnection, serve + + +__all__ = ["route", "unix_route", "Router"] + + +class Router: + """WebSocket router supporting :func:`route`.""" + + def __init__( + self, + url_map: Map, + server_name: str | None = None, + url_scheme: str = "ws", + ) -> None: + self.url_map = url_map + self.server_name = server_name + self.url_scheme = url_scheme + for rule in self.url_map.iter_rules(): + rule.websocket = True + + def get_server_name(self, connection: ServerConnection, request: Request) -> str: + if self.server_name is None: + return request.headers["Host"] + else: + return self.server_name + + def redirect(self, connection: ServerConnection, url: str) -> Response: + response = connection.respond(http.HTTPStatus.FOUND, f"Found at {url}") + response.headers["Location"] = url + return response + + def not_found(self, connection: ServerConnection) -> Response: + return connection.respond(http.HTTPStatus.NOT_FOUND, "Not Found") + + def route_request( + self, connection: ServerConnection, request: Request + ) -> Response | None: + """Route incoming request.""" + url_map_adapter = self.url_map.bind( + server_name=self.get_server_name(connection, request), + url_scheme=self.url_scheme, + ) + try: + parsed = urllib.parse.urlparse(request.path) + handler, kwargs = url_map_adapter.match( + path_info=parsed.path, + query_args=parsed.query, + ) + except RequestRedirect as redirect: + return self.redirect(connection, redirect.new_url) + except NotFound: + return self.not_found(connection) + connection.handler, connection.handler_kwargs = handler, kwargs + return None + + async def handler(self, connection: ServerConnection) -> None: + """Handle a connection.""" + return await connection.handler(connection, **connection.handler_kwargs) + + +def route( + url_map: Map, + *args: Any, + server_name: str | None = None, + ssl: ssl_module.SSLContext | Literal[True] | None = None, + create_router: type[Router] | None = None, + **kwargs: Any, +) -> Awaitable[Server]: + """ + Create a WebSocket server dispatching connections to different handlers. + + This feature requires the third-party library `werkzeug`_: + + .. code-block:: console + + $ pip install werkzeug + + .. _werkzeug: https://werkzeug.palletsprojects.com/ + + :func:`route` accepts the same arguments as + :func:`~websockets.sync.server.serve`, except as described below. + + The first argument is a :class:`werkzeug.routing.Map` that maps URL patterns + to connection handlers. In addition to the connection, handlers receive + parameters captured in the URL as keyword arguments. + + Here's an example:: + + + from websockets.asyncio.router import route + from werkzeug.routing import Map, Rule + + async def channel_handler(websocket, channel_id): + ... + + url_map = Map([ + Rule("/channel/", endpoint=channel_handler), + ... + ]) + + # set this future to exit the server + stop = asyncio.get_running_loop().create_future() + + async with route(url_map, ...) as server: + await stop + + + Refer to the documentation of :mod:`werkzeug.routing` for details. + + If you define redirects with ``Rule(..., redirect_to=...)`` in the URL map, + when the server runs behind a reverse proxy that modifies the ``Host`` + header or terminates TLS, you need additional configuration: + + * Set ``server_name`` to the name of the server as seen by clients. When not + provided, websockets uses the value of the ``Host`` header. + + * Set ``ssl=True`` to generate ``wss://`` URIs without actually enabling + TLS. Under the hood, this bind the URL map with a ``url_scheme`` of + ``wss://`` instead of ``ws://``. + + There is no need to specify ``websocket=True`` in each rule. It is added + automatically. + + Args: + url_map: Mapping of URL patterns to connection handlers. + server_name: Name of the server as seen by clients. If :obj:`None`, + websockets uses the value of the ``Host`` header. + ssl: Configuration for enabling TLS on the connection. Set it to + :obj:`True` if a reverse proxy terminates TLS connections. + create_router: Factory for the :class:`Router` dispatching requests to + handlers. Set it to a wrapper or a subclass to customize routing. + + """ + url_scheme = "ws" if ssl is None else "wss" + if ssl is not True and ssl is not None: + kwargs["ssl"] = ssl + + if create_router is None: + create_router = Router + + router = create_router(url_map, server_name, url_scheme) + + _process_request: ( + Callable[ + [ServerConnection, Request], + Awaitable[Response | None] | Response | None, + ] + | None + ) = kwargs.pop("process_request", None) + if _process_request is None: + process_request: Callable[ + [ServerConnection, Request], + Awaitable[Response | None] | Response | None, + ] = router.route_request + else: + + async def process_request( + connection: ServerConnection, request: Request + ) -> Response | None: + response = _process_request(connection, request) + if isinstance(response, Awaitable): + response = await response + if response is not None: + return response + return router.route_request(connection, request) + + return serve(router.handler, *args, process_request=process_request, **kwargs) + + +def unix_route( + url_map: Map, + path: str | None = None, + **kwargs: Any, +) -> Awaitable[Server]: + """ + Create a WebSocket Unix server dispatching connections to different handlers. + + :func:`unix_route` combines the behaviors of :func:`route` and + :func:`~websockets.asyncio.server.unix_serve`. + + Args: + url_map: Mapping of URL patterns to connection handlers. + path: File system path to the Unix socket. + + """ + return route(url_map, unix=True, path=path, **kwargs) diff --git a/gestao_raul/Lib/site-packages/websockets/asyncio/server.py b/gestao_raul/Lib/site-packages/websockets/asyncio/server.py new file mode 100644 index 0000000..ec7fc43 --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/asyncio/server.py @@ -0,0 +1,981 @@ +from __future__ import annotations + +import asyncio +import hmac +import http +import logging +import re +import socket +import sys +from collections.abc import Awaitable, Generator, Iterable, Sequence +from types import TracebackType +from typing import Any, Callable, Mapping, cast + +from ..exceptions import InvalidHeader +from ..extensions.base import ServerExtensionFactory +from ..extensions.permessage_deflate import enable_server_permessage_deflate +from ..frames import CloseCode +from ..headers import ( + build_www_authenticate_basic, + parse_authorization_basic, + validate_subprotocols, +) +from ..http11 import SERVER, Request, Response +from ..protocol import CONNECTING, OPEN, Event +from ..server import ServerProtocol +from ..typing import LoggerLike, Origin, StatusLike, Subprotocol +from .compatibility import asyncio_timeout +from .connection import Connection, broadcast + + +__all__ = [ + "broadcast", + "serve", + "unix_serve", + "ServerConnection", + "Server", + "basic_auth", +] + + +class ServerConnection(Connection): + """ + :mod:`asyncio` implementation of a WebSocket server connection. + + :class:`ServerConnection` provides :meth:`recv` and :meth:`send` methods for + receiving and sending messages. + + It supports asynchronous iteration to receive messages:: + + async for message in websocket: + await process(message) + + The iterator exits normally when the connection is closed with close code + 1000 (OK) or 1001 (going away) or without a close code. It raises a + :exc:`~websockets.exceptions.ConnectionClosedError` when the connection is + closed with any other code. + + The ``ping_interval``, ``ping_timeout``, ``close_timeout``, ``max_queue``, + and ``write_limit`` arguments have the same meaning as in :func:`serve`. + + Args: + protocol: Sans-I/O connection. + server: Server that manages this connection. + + """ + + def __init__( + self, + protocol: ServerProtocol, + server: Server, + *, + ping_interval: float | None = 20, + ping_timeout: float | None = 20, + close_timeout: float | None = 10, + max_queue: int | None | tuple[int | None, int | None] = 16, + write_limit: int | tuple[int, int | None] = 2**15, + ) -> None: + self.protocol: ServerProtocol + super().__init__( + protocol, + ping_interval=ping_interval, + ping_timeout=ping_timeout, + close_timeout=close_timeout, + max_queue=max_queue, + write_limit=write_limit, + ) + self.server = server + self.request_rcvd: asyncio.Future[None] = self.loop.create_future() + self.username: str # see basic_auth() + self.handler: Callable[[ServerConnection], Awaitable[None]] # see route() + self.handler_kwargs: Mapping[str, Any] # see route() + + def respond(self, status: StatusLike, text: str) -> Response: + """ + Create a plain text HTTP response. + + ``process_request`` and ``process_response`` may call this method to + return an HTTP response instead of performing the WebSocket opening + handshake. + + You can modify the response before returning it, for example by changing + HTTP headers. + + Args: + status: HTTP status code. + text: HTTP response body; it will be encoded to UTF-8. + + Returns: + HTTP response to send to the client. + + """ + return self.protocol.reject(status, text) + + async def handshake( + self, + process_request: ( + Callable[ + [ServerConnection, Request], + Awaitable[Response | None] | Response | None, + ] + | None + ) = None, + process_response: ( + Callable[ + [ServerConnection, Request, Response], + Awaitable[Response | None] | Response | None, + ] + | None + ) = None, + server_header: str | None = SERVER, + ) -> None: + """ + Perform the opening handshake. + + """ + await asyncio.wait( + [self.request_rcvd, self.connection_lost_waiter], + return_when=asyncio.FIRST_COMPLETED, + ) + + if self.request is not None: + async with self.send_context(expected_state=CONNECTING): + response = None + + if process_request is not None: + try: + response = process_request(self, self.request) + if isinstance(response, Awaitable): + response = await response + except Exception as exc: + self.protocol.handshake_exc = exc + response = self.protocol.reject( + http.HTTPStatus.INTERNAL_SERVER_ERROR, + ( + "Failed to open a WebSocket connection.\n" + "See server log for more information.\n" + ), + ) + + if response is None: + if self.server.is_serving(): + self.response = self.protocol.accept(self.request) + else: + self.response = self.protocol.reject( + http.HTTPStatus.SERVICE_UNAVAILABLE, + "Server is shutting down.\n", + ) + else: + assert isinstance(response, Response) # help mypy + self.response = response + + if server_header: + self.response.headers["Server"] = server_header + + response = None + + if process_response is not None: + try: + response = process_response(self, self.request, self.response) + if isinstance(response, Awaitable): + response = await response + except Exception as exc: + self.protocol.handshake_exc = exc + response = self.protocol.reject( + http.HTTPStatus.INTERNAL_SERVER_ERROR, + ( + "Failed to open a WebSocket connection.\n" + "See server log for more information.\n" + ), + ) + + if response is not None: + assert isinstance(response, Response) # help mypy + self.response = response + + self.protocol.send_response(self.response) + + # self.protocol.handshake_exc is set when the connection is lost before + # receiving a request, when the request cannot be parsed, or when the + # handshake fails, including when process_request or process_response + # raises an exception. + + # It isn't set when process_request or process_response sends an HTTP + # response that rejects the handshake. + + if self.protocol.handshake_exc is not None: + raise self.protocol.handshake_exc + + def process_event(self, event: Event) -> None: + """ + Process one incoming event. + + """ + # First event - handshake request. + if self.request is None: + assert isinstance(event, Request) + self.request = event + self.request_rcvd.set_result(None) + # Later events - frames. + else: + super().process_event(event) + + def connection_made(self, transport: asyncio.BaseTransport) -> None: + super().connection_made(transport) + self.server.start_connection_handler(self) + + +class Server: + """ + WebSocket server returned by :func:`serve`. + + This class mirrors the API of :class:`asyncio.Server`. + + It keeps track of WebSocket connections in order to close them properly + when shutting down. + + Args: + handler: Connection handler. It receives the WebSocket connection, + which is a :class:`ServerConnection`, in argument. + process_request: Intercept the request during the opening handshake. + Return an HTTP response to force the response. Return :obj:`None` to + continue normally. When you force an HTTP 101 Continue response, the + handshake is successful. Else, the connection is aborted. + ``process_request`` may be a function or a coroutine. + process_response: Intercept the response during the opening handshake. + Modify the response or return a new HTTP response to force the + response. Return :obj:`None` to continue normally. When you force an + HTTP 101 Continue response, the handshake is successful. Else, the + connection is aborted. ``process_response`` may be a function or a + coroutine. + server_header: Value of the ``Server`` response header. + It defaults to ``"Python/x.y.z websockets/X.Y"``. Setting it to + :obj:`None` removes the header. + open_timeout: Timeout for opening connections in seconds. + :obj:`None` disables the timeout. + logger: Logger for this server. + It defaults to ``logging.getLogger("websockets.server")``. + See the :doc:`logging guide <../../topics/logging>` for details. + + """ + + def __init__( + self, + handler: Callable[[ServerConnection], Awaitable[None]], + *, + process_request: ( + Callable[ + [ServerConnection, Request], + Awaitable[Response | None] | Response | None, + ] + | None + ) = None, + process_response: ( + Callable[ + [ServerConnection, Request, Response], + Awaitable[Response | None] | Response | None, + ] + | None + ) = None, + server_header: str | None = SERVER, + open_timeout: float | None = 10, + logger: LoggerLike | None = None, + ) -> None: + self.loop = asyncio.get_running_loop() + self.handler = handler + self.process_request = process_request + self.process_response = process_response + self.server_header = server_header + self.open_timeout = open_timeout + if logger is None: + logger = logging.getLogger("websockets.server") + self.logger = logger + + # Keep track of active connections. + self.handlers: dict[ServerConnection, asyncio.Task[None]] = {} + + # Task responsible for closing the server and terminating connections. + self.close_task: asyncio.Task[None] | None = None + + # Completed when the server is closed and connections are terminated. + self.closed_waiter: asyncio.Future[None] = self.loop.create_future() + + @property + def connections(self) -> set[ServerConnection]: + """ + Set of active connections. + + This property contains all connections that completed the opening + handshake successfully and didn't start the closing handshake yet. + It can be useful in combination with :func:`~broadcast`. + + """ + return {connection for connection in self.handlers if connection.state is OPEN} + + def wrap(self, server: asyncio.Server) -> None: + """ + Attach to a given :class:`asyncio.Server`. + + Since :meth:`~asyncio.loop.create_server` doesn't support injecting a + custom ``Server`` class, the easiest solution that doesn't rely on + private :mod:`asyncio` APIs is to: + + - instantiate a :class:`Server` + - give the protocol factory a reference to that instance + - call :meth:`~asyncio.loop.create_server` with the factory + - attach the resulting :class:`asyncio.Server` with this method + + """ + self.server = server + for sock in server.sockets: + if sock.family == socket.AF_INET: + name = "%s:%d" % sock.getsockname() + elif sock.family == socket.AF_INET6: + name = "[%s]:%d" % sock.getsockname()[:2] + elif sock.family == socket.AF_UNIX: + name = sock.getsockname() + # In the unlikely event that someone runs websockets over a + # protocol other than IP or Unix sockets, avoid crashing. + else: # pragma: no cover + name = str(sock.getsockname()) + self.logger.info("server listening on %s", name) + + async def conn_handler(self, connection: ServerConnection) -> None: + """ + Handle the lifecycle of a WebSocket connection. + + Since this method doesn't have a caller that can handle exceptions, + it attempts to log relevant ones. + + It guarantees that the TCP connection is closed before exiting. + + """ + try: + async with asyncio_timeout(self.open_timeout): + try: + await connection.handshake( + self.process_request, + self.process_response, + self.server_header, + ) + except asyncio.CancelledError: + connection.transport.abort() + raise + except Exception: + connection.logger.error("opening handshake failed", exc_info=True) + connection.transport.abort() + return + + if connection.protocol.state is not OPEN: + # process_request or process_response rejected the handshake. + connection.transport.abort() + return + + try: + connection.start_keepalive() + await self.handler(connection) + except Exception: + connection.logger.error("connection handler failed", exc_info=True) + await connection.close(CloseCode.INTERNAL_ERROR) + else: + await connection.close() + + except TimeoutError: + # When the opening handshake times out, there's nothing to log. + pass + + except Exception: # pragma: no cover + # Don't leak connections on unexpected errors. + connection.transport.abort() + + finally: + # Registration is tied to the lifecycle of conn_handler() because + # the server waits for connection handlers to terminate, even if + # all connections are already closed. + del self.handlers[connection] + + def start_connection_handler(self, connection: ServerConnection) -> None: + """ + Register a connection with this server. + + """ + # The connection must be registered in self.handlers immediately. + # If it was registered in conn_handler(), a race condition could + # happen when closing the server after scheduling conn_handler() + # but before it starts executing. + self.handlers[connection] = self.loop.create_task(self.conn_handler(connection)) + + def close(self, close_connections: bool = True) -> None: + """ + Close the server. + + * Close the underlying :class:`asyncio.Server`. + * When ``close_connections`` is :obj:`True`, which is the default, + close existing connections. Specifically: + + * Reject opening WebSocket connections with an HTTP 503 (service + unavailable) error. This happens when the server accepted the TCP + connection but didn't complete the opening handshake before closing. + * Close open WebSocket connections with close code 1001 (going away). + + * Wait until all connection handlers terminate. + + :meth:`close` is idempotent. + + """ + if self.close_task is None: + self.close_task = self.get_loop().create_task( + self._close(close_connections) + ) + + async def _close(self, close_connections: bool) -> None: + """ + Implementation of :meth:`close`. + + This calls :meth:`~asyncio.Server.close` on the underlying + :class:`asyncio.Server` object to stop accepting new connections and + then closes open connections with close code 1001. + + """ + self.logger.info("server closing") + + # Stop accepting new connections. + self.server.close() + + # Wait until all accepted connections reach connection_made() and call + # register(). See https://github.com/python/cpython/issues/79033 for + # details. This workaround can be removed when dropping Python < 3.11. + await asyncio.sleep(0) + + if close_connections: + # Close OPEN connections with close code 1001. After server.close(), + # handshake() closes OPENING connections with an HTTP 503 error. + close_tasks = [ + asyncio.create_task(connection.close(1001)) + for connection in self.handlers + if connection.protocol.state is not CONNECTING + ] + # asyncio.wait doesn't accept an empty first argument. + if close_tasks: + await asyncio.wait(close_tasks) + + # Wait until all TCP connections are closed. + await self.server.wait_closed() + + # Wait until all connection handlers terminate. + # asyncio.wait doesn't accept an empty first argument. + if self.handlers: + await asyncio.wait(self.handlers.values()) + + # Tell wait_closed() to return. + self.closed_waiter.set_result(None) + + self.logger.info("server closed") + + async def wait_closed(self) -> None: + """ + Wait until the server is closed. + + When :meth:`wait_closed` returns, all TCP connections are closed and + all connection handlers have returned. + + To ensure a fast shutdown, a connection handler should always be + awaiting at least one of: + + * :meth:`~ServerConnection.recv`: when the connection is closed, + it raises :exc:`~websockets.exceptions.ConnectionClosedOK`; + * :meth:`~ServerConnection.wait_closed`: when the connection is + closed, it returns. + + Then the connection handler is immediately notified of the shutdown; + it can clean up and exit. + + """ + await asyncio.shield(self.closed_waiter) + + def get_loop(self) -> asyncio.AbstractEventLoop: + """ + See :meth:`asyncio.Server.get_loop`. + + """ + return self.server.get_loop() + + def is_serving(self) -> bool: # pragma: no cover + """ + See :meth:`asyncio.Server.is_serving`. + + """ + return self.server.is_serving() + + async def start_serving(self) -> None: # pragma: no cover + """ + See :meth:`asyncio.Server.start_serving`. + + Typical use:: + + server = await serve(..., start_serving=False) + # perform additional setup here... + # ... then start the server + await server.start_serving() + + """ + await self.server.start_serving() + + async def serve_forever(self) -> None: # pragma: no cover + """ + See :meth:`asyncio.Server.serve_forever`. + + Typical use:: + + server = await serve(...) + # this coroutine doesn't return + # canceling it stops the server + await server.serve_forever() + + This is an alternative to using :func:`serve` as an asynchronous context + manager. Shutdown is triggered by canceling :meth:`serve_forever` + instead of exiting a :func:`serve` context. + + """ + await self.server.serve_forever() + + @property + def sockets(self) -> Iterable[socket.socket]: + """ + See :attr:`asyncio.Server.sockets`. + + """ + return self.server.sockets + + async def __aenter__(self) -> Server: # pragma: no cover + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + ) -> None: # pragma: no cover + self.close() + await self.wait_closed() + + +# This is spelled in lower case because it's exposed as a callable in the API. +class serve: + """ + Create a WebSocket server listening on ``host`` and ``port``. + + Whenever a client connects, the server creates a :class:`ServerConnection`, + performs the opening handshake, and delegates to the ``handler`` coroutine. + + The handler receives the :class:`ServerConnection` instance, which you can + use to send and receive messages. + + Once the handler completes, either normally or with an exception, the server + performs the closing handshake and closes the connection. + + This coroutine returns a :class:`Server` whose API mirrors + :class:`asyncio.Server`. Treat it as an asynchronous context manager to + ensure that the server will be closed:: + + from websockets.asyncio.server import serve + + def handler(websocket): + ... + + # set this future to exit the server + stop = asyncio.get_running_loop().create_future() + + async with serve(handler, host, port): + await stop + + Alternatively, call :meth:`~Server.serve_forever` to serve requests and + cancel it to stop the server:: + + server = await serve(handler, host, port) + await server.serve_forever() + + Args: + handler: Connection handler. It receives the WebSocket connection, + which is a :class:`ServerConnection`, in argument. + host: Network interfaces the server binds to. + See :meth:`~asyncio.loop.create_server` for details. + port: TCP port the server listens on. + See :meth:`~asyncio.loop.create_server` for details. + origins: Acceptable values of the ``Origin`` header, for defending + against Cross-Site WebSocket Hijacking attacks. Values can be + :class:`str` to test for an exact match or regular expressions + compiled by :func:`re.compile` to test against a pattern. Include + :obj:`None` in the list if the lack of an origin is acceptable. + extensions: List of supported extensions, in order in which they + should be negotiated and run. + subprotocols: List of supported subprotocols, in order of decreasing + preference. + select_subprotocol: Callback for selecting a subprotocol among + those supported by the client and the server. It receives a + :class:`ServerConnection` (not a + :class:`~websockets.server.ServerProtocol`!) instance and a list of + subprotocols offered by the client. Other than the first argument, + it has the same behavior as the + :meth:`ServerProtocol.select_subprotocol + ` method. + compression: The "permessage-deflate" extension is enabled by default. + Set ``compression`` to :obj:`None` to disable it. See the + :doc:`compression guide <../../topics/compression>` for details. + process_request: Intercept the request during the opening handshake. + Return an HTTP response to force the response or :obj:`None` to + continue normally. When you force an HTTP 101 Continue response, the + handshake is successful. Else, the connection is aborted. + ``process_request`` may be a function or a coroutine. + process_response: Intercept the response during the opening handshake. + Return an HTTP response to force the response or :obj:`None` to + continue normally. When you force an HTTP 101 Continue response, the + handshake is successful. Else, the connection is aborted. + ``process_response`` may be a function or a coroutine. + server_header: Value of the ``Server`` response header. + It defaults to ``"Python/x.y.z websockets/X.Y"``. Setting it to + :obj:`None` removes the header. + open_timeout: Timeout for opening connections in seconds. + :obj:`None` disables the timeout. + ping_interval: Interval between keepalive pings in seconds. + :obj:`None` disables keepalive. + ping_timeout: Timeout for keepalive pings in seconds. + :obj:`None` disables timeouts. + close_timeout: Timeout for closing connections in seconds. + :obj:`None` disables the timeout. + max_size: Maximum size of incoming messages in bytes. + :obj:`None` disables the limit. + max_queue: High-water mark of the buffer where frames are received. + It defaults to 16 frames. The low-water mark defaults to ``max_queue + // 4``. You may pass a ``(high, low)`` tuple to set the high-water + and low-water marks. If you want to disable flow control entirely, + you may set it to ``None``, although that's a bad idea. + write_limit: High-water mark of write buffer in bytes. It is passed to + :meth:`~asyncio.WriteTransport.set_write_buffer_limits`. It defaults + to 32 KiB. You may pass a ``(high, low)`` tuple to set the + high-water and low-water marks. + logger: Logger for this server. + It defaults to ``logging.getLogger("websockets.server")``. See the + :doc:`logging guide <../../topics/logging>` for details. + create_connection: Factory for the :class:`ServerConnection` managing + the connection. Set it to a wrapper or a subclass to customize + connection handling. + + Any other keyword arguments are passed to the event loop's + :meth:`~asyncio.loop.create_server` method. + + For example: + + * You can set ``ssl`` to a :class:`~ssl.SSLContext` to enable TLS. + + * You can set ``sock`` to provide a preexisting TCP socket. You may call + :func:`socket.create_server` (not to be confused with the event loop's + :meth:`~asyncio.loop.create_server` method) to create a suitable server + socket and customize it. + + * You can set ``start_serving`` to ``False`` to start accepting connections + only after you call :meth:`~Server.start_serving()` or + :meth:`~Server.serve_forever()`. + + """ + + def __init__( + self, + handler: Callable[[ServerConnection], Awaitable[None]], + host: str | None = None, + port: int | None = None, + *, + # WebSocket + origins: Sequence[Origin | re.Pattern[str] | None] | None = None, + extensions: Sequence[ServerExtensionFactory] | None = None, + subprotocols: Sequence[Subprotocol] | None = None, + select_subprotocol: ( + Callable[ + [ServerConnection, Sequence[Subprotocol]], + Subprotocol | None, + ] + | None + ) = None, + compression: str | None = "deflate", + # HTTP + process_request: ( + Callable[ + [ServerConnection, Request], + Awaitable[Response | None] | Response | None, + ] + | None + ) = None, + process_response: ( + Callable[ + [ServerConnection, Request, Response], + Awaitable[Response | None] | Response | None, + ] + | None + ) = None, + server_header: str | None = SERVER, + # Timeouts + open_timeout: float | None = 10, + ping_interval: float | None = 20, + ping_timeout: float | None = 20, + close_timeout: float | None = 10, + # Limits + max_size: int | None = 2**20, + max_queue: int | None | tuple[int | None, int | None] = 16, + write_limit: int | tuple[int, int | None] = 2**15, + # Logging + logger: LoggerLike | None = None, + # Escape hatch for advanced customization + create_connection: type[ServerConnection] | None = None, + # Other keyword arguments are passed to loop.create_server + **kwargs: Any, + ) -> None: + if subprotocols is not None: + validate_subprotocols(subprotocols) + + if compression == "deflate": + extensions = enable_server_permessage_deflate(extensions) + elif compression is not None: + raise ValueError(f"unsupported compression: {compression}") + + if create_connection is None: + create_connection = ServerConnection + + self.server = Server( + handler, + process_request=process_request, + process_response=process_response, + server_header=server_header, + open_timeout=open_timeout, + logger=logger, + ) + + if kwargs.get("ssl") is not None: + kwargs.setdefault("ssl_handshake_timeout", open_timeout) + if sys.version_info[:2] >= (3, 11): # pragma: no branch + kwargs.setdefault("ssl_shutdown_timeout", close_timeout) + + def factory() -> ServerConnection: + """ + Create an asyncio protocol for managing a WebSocket connection. + + """ + # Create a closure to give select_subprotocol access to connection. + protocol_select_subprotocol: ( + Callable[ + [ServerProtocol, Sequence[Subprotocol]], + Subprotocol | None, + ] + | None + ) = None + if select_subprotocol is not None: + + def protocol_select_subprotocol( + protocol: ServerProtocol, + subprotocols: Sequence[Subprotocol], + ) -> Subprotocol | None: + # mypy doesn't know that select_subprotocol is immutable. + assert select_subprotocol is not None + # Ensure this function is only used in the intended context. + assert protocol is connection.protocol + return select_subprotocol(connection, subprotocols) + + # This is a protocol in the Sans-I/O implementation of websockets. + protocol = ServerProtocol( + origins=origins, + extensions=extensions, + subprotocols=subprotocols, + select_subprotocol=protocol_select_subprotocol, + max_size=max_size, + logger=logger, + ) + # This is a connection in websockets and a protocol in asyncio. + connection = create_connection( + protocol, + self.server, + ping_interval=ping_interval, + ping_timeout=ping_timeout, + close_timeout=close_timeout, + max_queue=max_queue, + write_limit=write_limit, + ) + return connection + + loop = asyncio.get_running_loop() + if kwargs.pop("unix", False): + self.create_server = loop.create_unix_server(factory, **kwargs) + else: + # mypy cannot tell that kwargs must provide sock when port is None. + self.create_server = loop.create_server(factory, host, port, **kwargs) # type: ignore[arg-type] + + # async with serve(...) as ...: ... + + async def __aenter__(self) -> Server: + return await self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + ) -> None: + self.server.close() + await self.server.wait_closed() + + # ... = await serve(...) + + def __await__(self) -> Generator[Any, None, Server]: + # Create a suitable iterator by calling __await__ on a coroutine. + return self.__await_impl__().__await__() + + async def __await_impl__(self) -> Server: + server = await self.create_server + self.server.wrap(server) + return self.server + + # ... = yield from serve(...) - remove when dropping Python < 3.10 + + __iter__ = __await__ + + +def unix_serve( + handler: Callable[[ServerConnection], Awaitable[None]], + path: str | None = None, + **kwargs: Any, +) -> Awaitable[Server]: + """ + Create a WebSocket server listening on a Unix socket. + + This function is identical to :func:`serve`, except the ``host`` and + ``port`` arguments are replaced by ``path``. It's only available on Unix. + + It's useful for deploying a server behind a reverse proxy such as nginx. + + Args: + handler: Connection handler. It receives the WebSocket connection, + which is a :class:`ServerConnection`, in argument. + path: File system path to the Unix socket. + + """ + return serve(handler, unix=True, path=path, **kwargs) + + +def is_credentials(credentials: Any) -> bool: + try: + username, password = credentials + except (TypeError, ValueError): + return False + else: + return isinstance(username, str) and isinstance(password, str) + + +def basic_auth( + realm: str = "", + credentials: tuple[str, str] | Iterable[tuple[str, str]] | None = None, + check_credentials: Callable[[str, str], Awaitable[bool] | bool] | None = None, +) -> Callable[[ServerConnection, Request], Awaitable[Response | None]]: + """ + Factory for ``process_request`` to enforce HTTP Basic Authentication. + + :func:`basic_auth` is designed to integrate with :func:`serve` as follows:: + + from websockets.asyncio.server import basic_auth, serve + + async with serve( + ..., + process_request=basic_auth( + realm="my dev server", + credentials=("hello", "iloveyou"), + ), + ): + + If authentication succeeds, the connection's ``username`` attribute is set. + If it fails, the server responds with an HTTP 401 Unauthorized status. + + One of ``credentials`` or ``check_credentials`` must be provided; not both. + + Args: + realm: Scope of protection. It should contain only ASCII characters + because the encoding of non-ASCII characters is undefined. Refer to + section 2.2 of :rfc:`7235` for details. + credentials: Hard coded authorized credentials. It can be a + ``(username, password)`` pair or a list of such pairs. + check_credentials: Function or coroutine that verifies credentials. + It receives ``username`` and ``password`` arguments and returns + whether they're valid. + Raises: + TypeError: If ``credentials`` or ``check_credentials`` is wrong. + ValueError: If ``credentials`` and ``check_credentials`` are both + provided or both not provided. + + """ + if (credentials is None) == (check_credentials is None): + raise ValueError("provide either credentials or check_credentials") + + if credentials is not None: + if is_credentials(credentials): + credentials_list = [cast(tuple[str, str], credentials)] + elif isinstance(credentials, Iterable): + credentials_list = list(cast(Iterable[tuple[str, str]], credentials)) + if not all(is_credentials(item) for item in credentials_list): + raise TypeError(f"invalid credentials argument: {credentials}") + else: + raise TypeError(f"invalid credentials argument: {credentials}") + + credentials_dict = dict(credentials_list) + + def check_credentials(username: str, password: str) -> bool: + try: + expected_password = credentials_dict[username] + except KeyError: + return False + return hmac.compare_digest(expected_password, password) + + assert check_credentials is not None # help mypy + + async def process_request( + connection: ServerConnection, + request: Request, + ) -> Response | None: + """ + Perform HTTP Basic Authentication. + + If it succeeds, set the connection's ``username`` attribute and return + :obj:`None`. If it fails, return an HTTP 401 Unauthorized responss. + + """ + try: + authorization = request.headers["Authorization"] + except KeyError: + response = connection.respond( + http.HTTPStatus.UNAUTHORIZED, + "Missing credentials\n", + ) + response.headers["WWW-Authenticate"] = build_www_authenticate_basic(realm) + return response + + try: + username, password = parse_authorization_basic(authorization) + except InvalidHeader: + response = connection.respond( + http.HTTPStatus.UNAUTHORIZED, + "Unsupported credentials\n", + ) + response.headers["WWW-Authenticate"] = build_www_authenticate_basic(realm) + return response + + valid_credentials = check_credentials(username, password) + if isinstance(valid_credentials, Awaitable): + valid_credentials = await valid_credentials + + if not valid_credentials: + response = connection.respond( + http.HTTPStatus.UNAUTHORIZED, + "Invalid credentials\n", + ) + response.headers["WWW-Authenticate"] = build_www_authenticate_basic(realm) + return response + + connection.username = username + return None + + return process_request diff --git a/gestao_raul/Lib/site-packages/websockets/auth.py b/gestao_raul/Lib/site-packages/websockets/auth.py new file mode 100644 index 0000000..15b70a3 --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/auth.py @@ -0,0 +1,18 @@ +from __future__ import annotations + +import warnings + + +with warnings.catch_warnings(): + # Suppress redundant DeprecationWarning raised by websockets.legacy. + warnings.filterwarnings("ignore", category=DeprecationWarning) + from .legacy.auth import * + from .legacy.auth import __all__ # noqa: F401 + + +warnings.warn( # deprecated in 14.0 - 2024-11-09 + "websockets.auth, an alias for websockets.legacy.auth, is deprecated; " + "see https://websockets.readthedocs.io/en/stable/howto/upgrade.html " + "for upgrade instructions", + DeprecationWarning, +) diff --git a/gestao_raul/Lib/site-packages/websockets/client.py b/gestao_raul/Lib/site-packages/websockets/client.py new file mode 100644 index 0000000..9ea21c3 --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/client.py @@ -0,0 +1,389 @@ +from __future__ import annotations + +import os +import random +import warnings +from collections.abc import Generator, Sequence +from typing import Any + +from .datastructures import Headers, MultipleValuesError +from .exceptions import ( + InvalidHandshake, + InvalidHeader, + InvalidHeaderValue, + InvalidMessage, + InvalidStatus, + InvalidUpgrade, + NegotiationError, +) +from .extensions import ClientExtensionFactory, Extension +from .headers import ( + build_authorization_basic, + build_extension, + build_host, + build_subprotocol, + parse_connection, + parse_extension, + parse_subprotocol, + parse_upgrade, +) +from .http11 import Request, Response +from .imports import lazy_import +from .protocol import CLIENT, CONNECTING, OPEN, Protocol, State +from .typing import ( + ConnectionOption, + ExtensionHeader, + LoggerLike, + Origin, + Subprotocol, + UpgradeProtocol, +) +from .uri import WebSocketURI +from .utils import accept_key, generate_key + + +__all__ = ["ClientProtocol"] + + +class ClientProtocol(Protocol): + """ + Sans-I/O implementation of a WebSocket client connection. + + Args: + uri: URI of the WebSocket server, parsed + with :func:`~websockets.uri.parse_uri`. + origin: Value of the ``Origin`` header. This is useful when connecting + to a server that validates the ``Origin`` header to defend against + Cross-Site WebSocket Hijacking attacks. + extensions: List of supported extensions, in order in which they + should be tried. + subprotocols: List of supported subprotocols, in order of decreasing + preference. + state: Initial state of the WebSocket connection. + max_size: Maximum size of incoming messages in bytes; + :obj:`None` disables the limit. + logger: Logger for this connection; + defaults to ``logging.getLogger("websockets.client")``; + see the :doc:`logging guide <../../topics/logging>` for details. + + """ + + def __init__( + self, + uri: WebSocketURI, + *, + origin: Origin | None = None, + extensions: Sequence[ClientExtensionFactory] | None = None, + subprotocols: Sequence[Subprotocol] | None = None, + state: State = CONNECTING, + max_size: int | None = 2**20, + logger: LoggerLike | None = None, + ) -> None: + super().__init__( + side=CLIENT, + state=state, + max_size=max_size, + logger=logger, + ) + self.uri = uri + self.origin = origin + self.available_extensions = extensions + self.available_subprotocols = subprotocols + self.key = generate_key() + + def connect(self) -> Request: + """ + Create a handshake request to open a connection. + + You must send the handshake request with :meth:`send_request`. + + You can modify it before sending it, for example to add HTTP headers. + + Returns: + WebSocket handshake request event to send to the server. + + """ + headers = Headers() + headers["Host"] = build_host(self.uri.host, self.uri.port, self.uri.secure) + if self.uri.user_info: + headers["Authorization"] = build_authorization_basic(*self.uri.user_info) + if self.origin is not None: + headers["Origin"] = self.origin + headers["Upgrade"] = "websocket" + headers["Connection"] = "Upgrade" + headers["Sec-WebSocket-Key"] = self.key + headers["Sec-WebSocket-Version"] = "13" + if self.available_extensions is not None: + headers["Sec-WebSocket-Extensions"] = build_extension( + [ + (extension_factory.name, extension_factory.get_request_params()) + for extension_factory in self.available_extensions + ] + ) + if self.available_subprotocols is not None: + headers["Sec-WebSocket-Protocol"] = build_subprotocol( + self.available_subprotocols + ) + return Request(self.uri.resource_name, headers) + + def process_response(self, response: Response) -> None: + """ + Check a handshake response. + + Args: + request: WebSocket handshake response received from the server. + + Raises: + InvalidHandshake: If the handshake response is invalid. + + """ + + if response.status_code != 101: + raise InvalidStatus(response) + + headers = response.headers + + connection: list[ConnectionOption] = sum( + [parse_connection(value) for value in headers.get_all("Connection")], [] + ) + if not any(value.lower() == "upgrade" for value in connection): + raise InvalidUpgrade( + "Connection", ", ".join(connection) if connection else None + ) + + upgrade: list[UpgradeProtocol] = sum( + [parse_upgrade(value) for value in headers.get_all("Upgrade")], [] + ) + # For compatibility with non-strict implementations, ignore case when + # checking the Upgrade header. It's supposed to be 'WebSocket'. + if not (len(upgrade) == 1 and upgrade[0].lower() == "websocket"): + raise InvalidUpgrade("Upgrade", ", ".join(upgrade) if upgrade else None) + + try: + s_w_accept = headers["Sec-WebSocket-Accept"] + except KeyError: + raise InvalidHeader("Sec-WebSocket-Accept") from None + except MultipleValuesError: + raise InvalidHeader("Sec-WebSocket-Accept", "multiple values") from None + if s_w_accept != accept_key(self.key): + raise InvalidHeaderValue("Sec-WebSocket-Accept", s_w_accept) + + self.extensions = self.process_extensions(headers) + self.subprotocol = self.process_subprotocol(headers) + + def process_extensions(self, headers: Headers) -> list[Extension]: + """ + Handle the Sec-WebSocket-Extensions HTTP response header. + + Check that each extension is supported, as well as its parameters. + + :rfc:`6455` leaves the rules up to the specification of each + extension. + + To provide this level of flexibility, for each extension accepted by + the server, we check for a match with each extension available in the + client configuration. If no match is found, an exception is raised. + + If several variants of the same extension are accepted by the server, + it may be configured several times, which won't make sense in general. + Extensions must implement their own requirements. For this purpose, + the list of previously accepted extensions is provided. + + Other requirements, for example related to mandatory extensions or the + order of extensions, may be implemented by overriding this method. + + Args: + headers: WebSocket handshake response headers. + + Returns: + List of accepted extensions. + + Raises: + InvalidHandshake: To abort the handshake. + + """ + accepted_extensions: list[Extension] = [] + + extensions = headers.get_all("Sec-WebSocket-Extensions") + + if extensions: + if self.available_extensions is None: + raise NegotiationError("no extensions supported") + + parsed_extensions: list[ExtensionHeader] = sum( + [parse_extension(header_value) for header_value in extensions], [] + ) + + for name, response_params in parsed_extensions: + for extension_factory in self.available_extensions: + # Skip non-matching extensions based on their name. + if extension_factory.name != name: + continue + + # Skip non-matching extensions based on their params. + try: + extension = extension_factory.process_response_params( + response_params, accepted_extensions + ) + except NegotiationError: + continue + + # Add matching extension to the final list. + accepted_extensions.append(extension) + + # Break out of the loop once we have a match. + break + + # If we didn't break from the loop, no extension in our list + # matched what the server sent. Fail the connection. + else: + raise NegotiationError( + f"Unsupported extension: " + f"name = {name}, params = {response_params}" + ) + + return accepted_extensions + + def process_subprotocol(self, headers: Headers) -> Subprotocol | None: + """ + Handle the Sec-WebSocket-Protocol HTTP response header. + + If provided, check that it contains exactly one supported subprotocol. + + Args: + headers: WebSocket handshake response headers. + + Returns: + Subprotocol, if one was selected. + + """ + subprotocol: Subprotocol | None = None + + subprotocols = headers.get_all("Sec-WebSocket-Protocol") + + if subprotocols: + if self.available_subprotocols is None: + raise NegotiationError("no subprotocols supported") + + parsed_subprotocols: Sequence[Subprotocol] = sum( + [parse_subprotocol(header_value) for header_value in subprotocols], [] + ) + if len(parsed_subprotocols) > 1: + raise InvalidHeader( + "Sec-WebSocket-Protocol", + f"multiple values: {', '.join(parsed_subprotocols)}", + ) + + subprotocol = parsed_subprotocols[0] + if subprotocol not in self.available_subprotocols: + raise NegotiationError(f"unsupported subprotocol: {subprotocol}") + + return subprotocol + + def send_request(self, request: Request) -> None: + """ + Send a handshake request to the server. + + Args: + request: WebSocket handshake request event. + + """ + if self.debug: + self.logger.debug("> GET %s HTTP/1.1", request.path) + for key, value in request.headers.raw_items(): + self.logger.debug("> %s: %s", key, value) + + self.writes.append(request.serialize()) + + def parse(self) -> Generator[None]: + if self.state is CONNECTING: + try: + response = yield from Response.parse( + self.reader.read_line, + self.reader.read_exact, + self.reader.read_to_eof, + ) + except Exception as exc: + self.handshake_exc = InvalidMessage( + "did not receive a valid HTTP response" + ) + self.handshake_exc.__cause__ = exc + self.send_eof() + self.parser = self.discard() + next(self.parser) # start coroutine + yield + + if self.debug: + code, phrase = response.status_code, response.reason_phrase + self.logger.debug("< HTTP/1.1 %d %s", code, phrase) + for key, value in response.headers.raw_items(): + self.logger.debug("< %s: %s", key, value) + if response.body: + self.logger.debug("< [body] (%d bytes)", len(response.body)) + + try: + self.process_response(response) + except InvalidHandshake as exc: + response._exception = exc + self.events.append(response) + self.handshake_exc = exc + self.send_eof() + self.parser = self.discard() + next(self.parser) # start coroutine + yield + + assert self.state is CONNECTING + self.state = OPEN + self.events.append(response) + + yield from super().parse() + + +class ClientConnection(ClientProtocol): + def __init__(self, *args: Any, **kwargs: Any) -> None: + warnings.warn( # deprecated in 11.0 - 2023-04-02 + "ClientConnection was renamed to ClientProtocol", + DeprecationWarning, + ) + super().__init__(*args, **kwargs) + + +BACKOFF_INITIAL_DELAY = float(os.environ.get("WEBSOCKETS_BACKOFF_INITIAL_DELAY", "5")) +BACKOFF_MIN_DELAY = float(os.environ.get("WEBSOCKETS_BACKOFF_MIN_DELAY", "3.1")) +BACKOFF_MAX_DELAY = float(os.environ.get("WEBSOCKETS_BACKOFF_MAX_DELAY", "90.0")) +BACKOFF_FACTOR = float(os.environ.get("WEBSOCKETS_BACKOFF_FACTOR", "1.618")) + + +def backoff( + initial_delay: float = BACKOFF_INITIAL_DELAY, + min_delay: float = BACKOFF_MIN_DELAY, + max_delay: float = BACKOFF_MAX_DELAY, + factor: float = BACKOFF_FACTOR, +) -> Generator[float]: + """ + Generate a series of backoff delays between reconnection attempts. + + Yields: + How many seconds to wait before retrying to connect. + + """ + # Add a random initial delay between 0 and 5 seconds. + # See 7.2.3. Recovering from Abnormal Closure in RFC 6455. + yield random.random() * initial_delay + delay = min_delay + while delay < max_delay: + yield delay + delay *= factor + while True: + yield max_delay + + +lazy_import( + globals(), + deprecated_aliases={ + # deprecated in 14.0 - 2024-11-09 + "WebSocketClientProtocol": ".legacy.client", + "connect": ".legacy.client", + "unix_connect": ".legacy.client", + }, +) diff --git a/gestao_raul/Lib/site-packages/websockets/connection.py b/gestao_raul/Lib/site-packages/websockets/connection.py new file mode 100644 index 0000000..5e78e34 --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/connection.py @@ -0,0 +1,12 @@ +from __future__ import annotations + +import warnings + +from .protocol import SEND_EOF, Protocol as Connection, Side, State # noqa: F401 + + +warnings.warn( # deprecated in 11.0 - 2023-04-02 + "websockets.connection was renamed to websockets.protocol " + "and Connection was renamed to Protocol", + DeprecationWarning, +) diff --git a/gestao_raul/Lib/site-packages/websockets/datastructures.py b/gestao_raul/Lib/site-packages/websockets/datastructures.py new file mode 100644 index 0000000..3c5dcbe --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/datastructures.py @@ -0,0 +1,187 @@ +from __future__ import annotations + +from collections.abc import Iterable, Iterator, Mapping, MutableMapping +from typing import Any, Protocol, Union + + +__all__ = [ + "Headers", + "HeadersLike", + "MultipleValuesError", +] + + +class MultipleValuesError(LookupError): + """ + Exception raised when :class:`Headers` has multiple values for a key. + + """ + + def __str__(self) -> str: + # Implement the same logic as KeyError_str in Objects/exceptions.c. + if len(self.args) == 1: + return repr(self.args[0]) + return super().__str__() + + +class Headers(MutableMapping[str, str]): + """ + Efficient data structure for manipulating HTTP headers. + + A :class:`list` of ``(name, values)`` is inefficient for lookups. + + A :class:`dict` doesn't suffice because header names are case-insensitive + and multiple occurrences of headers with the same name are possible. + + :class:`Headers` stores HTTP headers in a hybrid data structure to provide + efficient insertions and lookups while preserving the original data. + + In order to account for multiple values with minimal hassle, + :class:`Headers` follows this logic: + + - When getting a header with ``headers[name]``: + - if there's no value, :exc:`KeyError` is raised; + - if there's exactly one value, it's returned; + - if there's more than one value, :exc:`MultipleValuesError` is raised. + + - When setting a header with ``headers[name] = value``, the value is + appended to the list of values for that header. + + - When deleting a header with ``del headers[name]``, all values for that + header are removed (this is slow). + + Other methods for manipulating headers are consistent with this logic. + + As long as no header occurs multiple times, :class:`Headers` behaves like + :class:`dict`, except keys are lower-cased to provide case-insensitivity. + + Two methods support manipulating multiple values explicitly: + + - :meth:`get_all` returns a list of all values for a header; + - :meth:`raw_items` returns an iterator of ``(name, values)`` pairs. + + """ + + __slots__ = ["_dict", "_list"] + + # Like dict, Headers accepts an optional "mapping or iterable" argument. + def __init__(self, *args: HeadersLike, **kwargs: str) -> None: + self._dict: dict[str, list[str]] = {} + self._list: list[tuple[str, str]] = [] + self.update(*args, **kwargs) + + def __str__(self) -> str: + return "".join(f"{key}: {value}\r\n" for key, value in self._list) + "\r\n" + + def __repr__(self) -> str: + return f"{self.__class__.__name__}({self._list!r})" + + def copy(self) -> Headers: + copy = self.__class__() + copy._dict = self._dict.copy() + copy._list = self._list.copy() + return copy + + def serialize(self) -> bytes: + # Since headers only contain ASCII characters, we can keep this simple. + return str(self).encode() + + # Collection methods + + def __contains__(self, key: object) -> bool: + return isinstance(key, str) and key.lower() in self._dict + + def __iter__(self) -> Iterator[str]: + return iter(self._dict) + + def __len__(self) -> int: + return len(self._dict) + + # MutableMapping methods + + def __getitem__(self, key: str) -> str: + value = self._dict[key.lower()] + if len(value) == 1: + return value[0] + else: + raise MultipleValuesError(key) + + def __setitem__(self, key: str, value: str) -> None: + self._dict.setdefault(key.lower(), []).append(value) + self._list.append((key, value)) + + def __delitem__(self, key: str) -> None: + key_lower = key.lower() + self._dict.__delitem__(key_lower) + # This is inefficient. Fortunately deleting HTTP headers is uncommon. + self._list = [(k, v) for k, v in self._list if k.lower() != key_lower] + + def __eq__(self, other: Any) -> bool: + if not isinstance(other, Headers): + return NotImplemented + return self._dict == other._dict + + def clear(self) -> None: + """ + Remove all headers. + + """ + self._dict = {} + self._list = [] + + def update(self, *args: HeadersLike, **kwargs: str) -> None: + """ + Update from a :class:`Headers` instance and/or keyword arguments. + + """ + args = tuple( + arg.raw_items() if isinstance(arg, Headers) else arg for arg in args + ) + super().update(*args, **kwargs) + + # Methods for handling multiple values + + def get_all(self, key: str) -> list[str]: + """ + Return the (possibly empty) list of all values for a header. + + Args: + key: Header name. + + """ + return self._dict.get(key.lower(), []) + + def raw_items(self) -> Iterator[tuple[str, str]]: + """ + Return an iterator of all values as ``(name, value)`` pairs. + + """ + return iter(self._list) + + +# copy of _typeshed.SupportsKeysAndGetItem. +class SupportsKeysAndGetItem(Protocol): # pragma: no cover + """ + Dict-like types with ``keys() -> str`` and ``__getitem__(key: str) -> str`` methods. + + """ + + def keys(self) -> Iterable[str]: ... + + def __getitem__(self, key: str) -> str: ... + + +# Change to Headers | Mapping[str, str] | ... when dropping Python < 3.10. +HeadersLike = Union[ + Headers, + Mapping[str, str], + Iterable[tuple[str, str]], + SupportsKeysAndGetItem, +] +""" +Types accepted where :class:`Headers` is expected. + +In addition to :class:`Headers` itself, this includes dict-like types where both +keys and values are :class:`str`. + +""" diff --git a/gestao_raul/Lib/site-packages/websockets/exceptions.py b/gestao_raul/Lib/site-packages/websockets/exceptions.py new file mode 100644 index 0000000..ab1a15c --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/exceptions.py @@ -0,0 +1,473 @@ +""" +:mod:`websockets.exceptions` defines the following hierarchy of exceptions. + +* :exc:`WebSocketException` + * :exc:`ConnectionClosed` + * :exc:`ConnectionClosedOK` + * :exc:`ConnectionClosedError` + * :exc:`InvalidURI` + * :exc:`InvalidProxy` + * :exc:`InvalidHandshake` + * :exc:`SecurityError` + * :exc:`ProxyError` + * :exc:`InvalidProxyMessage` + * :exc:`InvalidProxyStatus` + * :exc:`InvalidMessage` + * :exc:`InvalidStatus` + * :exc:`InvalidStatusCode` (legacy) + * :exc:`InvalidHeader` + * :exc:`InvalidHeaderFormat` + * :exc:`InvalidHeaderValue` + * :exc:`InvalidOrigin` + * :exc:`InvalidUpgrade` + * :exc:`NegotiationError` + * :exc:`DuplicateParameter` + * :exc:`InvalidParameterName` + * :exc:`InvalidParameterValue` + * :exc:`AbortHandshake` (legacy) + * :exc:`RedirectHandshake` (legacy) + * :exc:`ProtocolError` (Sans-I/O) + * :exc:`PayloadTooBig` (Sans-I/O) + * :exc:`InvalidState` (Sans-I/O) + * :exc:`ConcurrencyError` + +""" + +from __future__ import annotations + +import warnings + +from .imports import lazy_import + + +__all__ = [ + "WebSocketException", + "ConnectionClosed", + "ConnectionClosedOK", + "ConnectionClosedError", + "InvalidURI", + "InvalidProxy", + "InvalidHandshake", + "SecurityError", + "ProxyError", + "InvalidProxyMessage", + "InvalidProxyStatus", + "InvalidMessage", + "InvalidStatus", + "InvalidHeader", + "InvalidHeaderFormat", + "InvalidHeaderValue", + "InvalidOrigin", + "InvalidUpgrade", + "NegotiationError", + "DuplicateParameter", + "InvalidParameterName", + "InvalidParameterValue", + "ProtocolError", + "PayloadTooBig", + "InvalidState", + "ConcurrencyError", +] + + +class WebSocketException(Exception): + """ + Base class for all exceptions defined by websockets. + + """ + + +class ConnectionClosed(WebSocketException): + """ + Raised when trying to interact with a closed connection. + + Attributes: + rcvd: If a close frame was received, its code and reason are available + in ``rcvd.code`` and ``rcvd.reason``. + sent: If a close frame was sent, its code and reason are available + in ``sent.code`` and ``sent.reason``. + rcvd_then_sent: If close frames were received and sent, this attribute + tells in which order this happened, from the perspective of this + side of the connection. + + """ + + def __init__( + self, + rcvd: frames.Close | None, + sent: frames.Close | None, + rcvd_then_sent: bool | None = None, + ) -> None: + self.rcvd = rcvd + self.sent = sent + self.rcvd_then_sent = rcvd_then_sent + assert (self.rcvd_then_sent is None) == (self.rcvd is None or self.sent is None) + + def __str__(self) -> str: + if self.rcvd is None: + if self.sent is None: + return "no close frame received or sent" + else: + return f"sent {self.sent}; no close frame received" + else: + if self.sent is None: + return f"received {self.rcvd}; no close frame sent" + else: + if self.rcvd_then_sent: + return f"received {self.rcvd}; then sent {self.sent}" + else: + return f"sent {self.sent}; then received {self.rcvd}" + + # code and reason attributes are provided for backwards-compatibility + + @property + def code(self) -> int: + warnings.warn( # deprecated in 13.1 - 2024-09-21 + "ConnectionClosed.code is deprecated; " + "use Protocol.close_code or ConnectionClosed.rcvd.code", + DeprecationWarning, + ) + if self.rcvd is None: + return frames.CloseCode.ABNORMAL_CLOSURE + return self.rcvd.code + + @property + def reason(self) -> str: + warnings.warn( # deprecated in 13.1 - 2024-09-21 + "ConnectionClosed.reason is deprecated; " + "use Protocol.close_reason or ConnectionClosed.rcvd.reason", + DeprecationWarning, + ) + if self.rcvd is None: + return "" + return self.rcvd.reason + + +class ConnectionClosedOK(ConnectionClosed): + """ + Like :exc:`ConnectionClosed`, when the connection terminated properly. + + A close code with code 1000 (OK) or 1001 (going away) or without a code was + received and sent. + + """ + + +class ConnectionClosedError(ConnectionClosed): + """ + Like :exc:`ConnectionClosed`, when the connection terminated with an error. + + A close frame with a code other than 1000 (OK) or 1001 (going away) was + received or sent, or the closing handshake didn't complete properly. + + """ + + +class InvalidURI(WebSocketException): + """ + Raised when connecting to a URI that isn't a valid WebSocket URI. + + """ + + def __init__(self, uri: str, msg: str) -> None: + self.uri = uri + self.msg = msg + + def __str__(self) -> str: + return f"{self.uri} isn't a valid URI: {self.msg}" + + +class InvalidProxy(WebSocketException): + """ + Raised when connecting via a proxy that isn't valid. + + """ + + def __init__(self, proxy: str, msg: str) -> None: + self.proxy = proxy + self.msg = msg + + def __str__(self) -> str: + return f"{self.proxy} isn't a valid proxy: {self.msg}" + + +class InvalidHandshake(WebSocketException): + """ + Base class for exceptions raised when the opening handshake fails. + + """ + + +class SecurityError(InvalidHandshake): + """ + Raised when a handshake request or response breaks a security rule. + + Security limits can be configured with :doc:`environment variables + <../reference/variables>`. + + """ + + +class ProxyError(InvalidHandshake): + """ + Raised when failing to connect to a proxy. + + """ + + +class InvalidProxyMessage(ProxyError): + """ + Raised when an HTTP proxy response is malformed. + + """ + + +class InvalidProxyStatus(ProxyError): + """ + Raised when an HTTP proxy rejects the connection. + + """ + + def __init__(self, response: http11.Response) -> None: + self.response = response + + def __str__(self) -> str: + return f"proxy rejected connection: HTTP {self.response.status_code:d}" + + +class InvalidMessage(InvalidHandshake): + """ + Raised when a handshake request or response is malformed. + + """ + + +class InvalidStatus(InvalidHandshake): + """ + Raised when a handshake response rejects the WebSocket upgrade. + + """ + + def __init__(self, response: http11.Response) -> None: + self.response = response + + def __str__(self) -> str: + return ( + f"server rejected WebSocket connection: HTTP {self.response.status_code:d}" + ) + + +class InvalidHeader(InvalidHandshake): + """ + Raised when an HTTP header doesn't have a valid format or value. + + """ + + def __init__(self, name: str, value: str | None = None) -> None: + self.name = name + self.value = value + + def __str__(self) -> str: + if self.value is None: + return f"missing {self.name} header" + elif self.value == "": + return f"empty {self.name} header" + else: + return f"invalid {self.name} header: {self.value}" + + +class InvalidHeaderFormat(InvalidHeader): + """ + Raised when an HTTP header cannot be parsed. + + The format of the header doesn't match the grammar for that header. + + """ + + def __init__(self, name: str, error: str, header: str, pos: int) -> None: + super().__init__(name, f"{error} at {pos} in {header}") + + +class InvalidHeaderValue(InvalidHeader): + """ + Raised when an HTTP header has a wrong value. + + The format of the header is correct but the value isn't acceptable. + + """ + + +class InvalidOrigin(InvalidHeader): + """ + Raised when the Origin header in a request isn't allowed. + + """ + + def __init__(self, origin: str | None) -> None: + super().__init__("Origin", origin) + + +class InvalidUpgrade(InvalidHeader): + """ + Raised when the Upgrade or Connection header isn't correct. + + """ + + +class NegotiationError(InvalidHandshake): + """ + Raised when negotiating an extension or a subprotocol fails. + + """ + + +class DuplicateParameter(NegotiationError): + """ + Raised when a parameter name is repeated in an extension header. + + """ + + def __init__(self, name: str) -> None: + self.name = name + + def __str__(self) -> str: + return f"duplicate parameter: {self.name}" + + +class InvalidParameterName(NegotiationError): + """ + Raised when a parameter name in an extension header is invalid. + + """ + + def __init__(self, name: str) -> None: + self.name = name + + def __str__(self) -> str: + return f"invalid parameter name: {self.name}" + + +class InvalidParameterValue(NegotiationError): + """ + Raised when a parameter value in an extension header is invalid. + + """ + + def __init__(self, name: str, value: str | None) -> None: + self.name = name + self.value = value + + def __str__(self) -> str: + if self.value is None: + return f"missing value for parameter {self.name}" + elif self.value == "": + return f"empty value for parameter {self.name}" + else: + return f"invalid value for parameter {self.name}: {self.value}" + + +class ProtocolError(WebSocketException): + """ + Raised when receiving or sending a frame that breaks the protocol. + + The Sans-I/O implementation raises this exception when: + + * receiving or sending a frame that contains invalid data; + * receiving or sending an invalid sequence of frames. + + """ + + +class PayloadTooBig(WebSocketException): + """ + Raised when parsing a frame with a payload that exceeds the maximum size. + + The Sans-I/O layer uses this exception internally. It doesn't bubble up to + the I/O layer. + + The :meth:`~websockets.extensions.Extension.decode` method of extensions + must raise :exc:`PayloadTooBig` if decoding a frame would exceed the limit. + + """ + + def __init__( + self, + size_or_message: int | None | str, + max_size: int | None = None, + cur_size: int | None = None, + ) -> None: + if isinstance(size_or_message, str): + assert max_size is None + assert cur_size is None + warnings.warn( # deprecated in 14.0 - 2024-11-09 + "PayloadTooBig(message) is deprecated; " + "change to PayloadTooBig(size, max_size)", + DeprecationWarning, + ) + self.message: str | None = size_or_message + else: + self.message = None + self.size: int | None = size_or_message + assert max_size is not None + self.max_size: int = max_size + self.cur_size: int | None = None + self.set_current_size(cur_size) + + def __str__(self) -> str: + if self.message is not None: + return self.message + else: + message = "frame " + if self.size is not None: + message += f"with {self.size} bytes " + if self.cur_size is not None: + message += f"after reading {self.cur_size} bytes " + message += f"exceeds limit of {self.max_size} bytes" + return message + + def set_current_size(self, cur_size: int | None) -> None: + assert self.cur_size is None + if cur_size is not None: + self.max_size += cur_size + self.cur_size = cur_size + + +class InvalidState(WebSocketException, AssertionError): + """ + Raised when sending a frame is forbidden in the current state. + + Specifically, the Sans-I/O layer raises this exception when: + + * sending a data frame to a connection in a state other + :attr:`~websockets.protocol.State.OPEN`; + * sending a control frame to a connection in a state other than + :attr:`~websockets.protocol.State.OPEN` or + :attr:`~websockets.protocol.State.CLOSING`. + + """ + + +class ConcurrencyError(WebSocketException, RuntimeError): + """ + Raised when receiving or sending messages concurrently. + + WebSocket is a connection-oriented protocol. Reads must be serialized; so + must be writes. However, reading and writing concurrently is possible. + + """ + + +# At the bottom to break import cycles created by type annotations. +from . import frames, http11 # noqa: E402 + + +lazy_import( + globals(), + deprecated_aliases={ + # deprecated in 14.0 - 2024-11-09 + "AbortHandshake": ".legacy.exceptions", + "InvalidStatusCode": ".legacy.exceptions", + "RedirectHandshake": ".legacy.exceptions", + "WebSocketProtocolError": ".legacy.exceptions", + }, +) diff --git a/gestao_raul/Lib/site-packages/websockets/extensions/__init__.py b/gestao_raul/Lib/site-packages/websockets/extensions/__init__.py new file mode 100644 index 0000000..02838b9 --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/extensions/__init__.py @@ -0,0 +1,4 @@ +from .base import * + + +__all__ = ["Extension", "ClientExtensionFactory", "ServerExtensionFactory"] diff --git a/gestao_raul/Lib/site-packages/websockets/extensions/__pycache__/__init__.cpython-310.pyc b/gestao_raul/Lib/site-packages/websockets/extensions/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7035b708f70e090b087efd2e88d4dcca73e31190 GIT binary patch literal 288 zcmZ8c!AiqG5Zz58Rq$JcdI^ieCOd|u+icm{sQn#p{)m_UL9U+s z1y4=|MSL)Cc<(X8j2{kn1!H~i);w^3--y3RSe$dwK9dnfI&mu+VUibFN|Vk+o_$D; za@otnJS}%7&kjUQcQ|y_giLL5kW#(4AA)wo+sx9xZh}*PQT7MZTV6q%r;+ dNMiuo+6ZvGT=j#~g*TnUv&}b@F%86C`UNOiRM`Lk literal 0 HcmV?d00001 diff --git a/gestao_raul/Lib/site-packages/websockets/extensions/__pycache__/base.cpython-310.pyc b/gestao_raul/Lib/site-packages/websockets/extensions/__pycache__/base.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4a3ad7d90df61815b4db33f211156309a128eefd GIT binary patch literal 3717 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 Frame: + """ + Decode an incoming frame. + + Args: + frame: Incoming frame. + max_size: Maximum payload size in bytes. + + Returns: + Decoded frame. + + Raises: + PayloadTooBig: If decoding the payload exceeds ``max_size``. + + """ + raise NotImplementedError + + def encode(self, frame: Frame) -> Frame: + """ + Encode an outgoing frame. + + Args: + frame: Outgoing frame. + + Returns: + Encoded frame. + + """ + raise NotImplementedError + + +class ClientExtensionFactory: + """ + Base class for client-side extension factories. + + """ + + name: ExtensionName + """Extension identifier.""" + + def get_request_params(self) -> Sequence[ExtensionParameter]: + """ + Build parameters to send to the server for this extension. + + Returns: + Parameters to send to the server. + + """ + raise NotImplementedError + + def process_response_params( + self, + params: Sequence[ExtensionParameter], + accepted_extensions: Sequence[Extension], + ) -> Extension: + """ + Process parameters received from the server. + + Args: + params: Parameters received from the server for this extension. + accepted_extensions: List of previously accepted extensions. + + Returns: + An extension instance. + + Raises: + NegotiationError: If parameters aren't acceptable. + + """ + raise NotImplementedError + + +class ServerExtensionFactory: + """ + Base class for server-side extension factories. + + """ + + name: ExtensionName + """Extension identifier.""" + + def process_request_params( + self, + params: Sequence[ExtensionParameter], + accepted_extensions: Sequence[Extension], + ) -> tuple[list[ExtensionParameter], Extension]: + """ + Process parameters received from the client. + + Args: + params: Parameters received from the client for this extension. + accepted_extensions: List of previously accepted extensions. + + Returns: + To accept the offer, parameters to send to the client for this + extension and an extension instance. + + Raises: + NegotiationError: To reject the offer, if parameters received from + the client aren't acceptable. + + """ + raise NotImplementedError diff --git a/gestao_raul/Lib/site-packages/websockets/extensions/permessage_deflate.py b/gestao_raul/Lib/site-packages/websockets/extensions/permessage_deflate.py new file mode 100644 index 0000000..7e9e7a5 --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/extensions/permessage_deflate.py @@ -0,0 +1,697 @@ +from __future__ import annotations + +import zlib +from collections.abc import Sequence +from typing import Any, Literal + +from .. import frames +from ..exceptions import ( + DuplicateParameter, + InvalidParameterName, + InvalidParameterValue, + NegotiationError, + PayloadTooBig, + ProtocolError, +) +from ..typing import ExtensionName, ExtensionParameter +from .base import ClientExtensionFactory, Extension, ServerExtensionFactory + + +__all__ = [ + "PerMessageDeflate", + "ClientPerMessageDeflateFactory", + "enable_client_permessage_deflate", + "ServerPerMessageDeflateFactory", + "enable_server_permessage_deflate", +] + +_EMPTY_UNCOMPRESSED_BLOCK = b"\x00\x00\xff\xff" + +_MAX_WINDOW_BITS_VALUES = [str(bits) for bits in range(8, 16)] + + +class PerMessageDeflate(Extension): + """ + Per-Message Deflate extension. + + """ + + name = ExtensionName("permessage-deflate") + + def __init__( + self, + remote_no_context_takeover: bool, + local_no_context_takeover: bool, + remote_max_window_bits: int, + local_max_window_bits: int, + compress_settings: dict[Any, Any] | None = None, + ) -> None: + """ + Configure the Per-Message Deflate extension. + + """ + if compress_settings is None: + compress_settings = {} + + assert remote_no_context_takeover in [False, True] + assert local_no_context_takeover in [False, True] + assert 8 <= remote_max_window_bits <= 15 + assert 8 <= local_max_window_bits <= 15 + assert "wbits" not in compress_settings + + self.remote_no_context_takeover = remote_no_context_takeover + self.local_no_context_takeover = local_no_context_takeover + self.remote_max_window_bits = remote_max_window_bits + self.local_max_window_bits = local_max_window_bits + self.compress_settings = compress_settings + + if not self.remote_no_context_takeover: + self.decoder = zlib.decompressobj(wbits=-self.remote_max_window_bits) + + if not self.local_no_context_takeover: + self.encoder = zlib.compressobj( + wbits=-self.local_max_window_bits, + **self.compress_settings, + ) + + # To handle continuation frames properly, we must keep track of + # whether that initial frame was encoded. + self.decode_cont_data = False + # There's no need for self.encode_cont_data because we always encode + # outgoing frames, so it would always be True. + + def __repr__(self) -> str: + return ( + f"PerMessageDeflate(" + f"remote_no_context_takeover={self.remote_no_context_takeover}, " + f"local_no_context_takeover={self.local_no_context_takeover}, " + f"remote_max_window_bits={self.remote_max_window_bits}, " + f"local_max_window_bits={self.local_max_window_bits})" + ) + + def decode( + self, + frame: frames.Frame, + *, + max_size: int | None = None, + ) -> frames.Frame: + """ + Decode an incoming frame. + + """ + # Skip control frames. + if frame.opcode in frames.CTRL_OPCODES: + return frame + + # Handle continuation data frames: + # - skip if the message isn't encoded + # - reset "decode continuation data" flag if it's a final frame + if frame.opcode is frames.OP_CONT: + if not self.decode_cont_data: + return frame + if frame.fin: + self.decode_cont_data = False + + # Handle text and binary data frames: + # - skip if the message isn't encoded + # - unset the rsv1 flag on the first frame of a compressed message + # - set "decode continuation data" flag if it's a non-final frame + else: + if not frame.rsv1: + return frame + if not frame.fin: + self.decode_cont_data = True + + # Re-initialize per-message decoder. + if self.remote_no_context_takeover: + self.decoder = zlib.decompressobj(wbits=-self.remote_max_window_bits) + + # Uncompress data. Protect against zip bombs by preventing zlib from + # decompressing more than max_length bytes (except when the limit is + # disabled with max_size = None). + if frame.fin and len(frame.data) < 2044: + # Profiling shows that appending four bytes, which makes a copy, is + # faster than calling decompress() again when data is less than 2kB. + data = bytes(frame.data) + _EMPTY_UNCOMPRESSED_BLOCK + else: + data = frame.data + max_length = 0 if max_size is None else max_size + try: + data = self.decoder.decompress(data, max_length) + if self.decoder.unconsumed_tail: + assert max_size is not None # help mypy + raise PayloadTooBig(None, max_size) + if frame.fin and len(frame.data) >= 2044: + # This cannot generate additional data. + self.decoder.decompress(_EMPTY_UNCOMPRESSED_BLOCK) + except zlib.error as exc: + raise ProtocolError("decompression failed") from exc + + # Allow garbage collection of the decoder if it won't be reused. + if frame.fin and self.remote_no_context_takeover: + del self.decoder + + return frames.Frame( + frame.opcode, + data, + frame.fin, + # Unset the rsv1 flag on the first frame of a compressed message. + False, + frame.rsv2, + frame.rsv3, + ) + + def encode(self, frame: frames.Frame) -> frames.Frame: + """ + Encode an outgoing frame. + + """ + # Skip control frames. + if frame.opcode in frames.CTRL_OPCODES: + return frame + + # Since we always encode messages, there's no "encode continuation + # data" flag similar to "decode continuation data" at this time. + + if frame.opcode is not frames.OP_CONT: + # Re-initialize per-message decoder. + if self.local_no_context_takeover: + self.encoder = zlib.compressobj( + wbits=-self.local_max_window_bits, + **self.compress_settings, + ) + + # Compress data. + data = self.encoder.compress(frame.data) + self.encoder.flush(zlib.Z_SYNC_FLUSH) + if frame.fin: + # Sync flush generates between 5 or 6 bytes, ending with the bytes + # 0x00 0x00 0xff 0xff, which must be removed. + assert data[-4:] == _EMPTY_UNCOMPRESSED_BLOCK + # Making a copy is faster than memoryview(a)[:-4] until 2kB. + if len(data) < 2048: + data = data[:-4] + else: + data = memoryview(data)[:-4] + + # Allow garbage collection of the encoder if it won't be reused. + if frame.fin and self.local_no_context_takeover: + del self.encoder + + return frames.Frame( + frame.opcode, + data, + frame.fin, + # Set the rsv1 flag on the first frame of a compressed message. + frame.opcode is not frames.OP_CONT, + frame.rsv2, + frame.rsv3, + ) + + +def _build_parameters( + server_no_context_takeover: bool, + client_no_context_takeover: bool, + server_max_window_bits: int | None, + client_max_window_bits: int | Literal[True] | None, +) -> list[ExtensionParameter]: + """ + Build a list of ``(name, value)`` pairs for some compression parameters. + + """ + params: list[ExtensionParameter] = [] + if server_no_context_takeover: + params.append(("server_no_context_takeover", None)) + if client_no_context_takeover: + params.append(("client_no_context_takeover", None)) + if server_max_window_bits: + params.append(("server_max_window_bits", str(server_max_window_bits))) + if client_max_window_bits is True: # only in handshake requests + params.append(("client_max_window_bits", None)) + elif client_max_window_bits: + params.append(("client_max_window_bits", str(client_max_window_bits))) + return params + + +def _extract_parameters( + params: Sequence[ExtensionParameter], *, is_server: bool +) -> tuple[bool, bool, int | None, int | Literal[True] | None]: + """ + Extract compression parameters from a list of ``(name, value)`` pairs. + + If ``is_server`` is :obj:`True`, ``client_max_window_bits`` may be + provided without a value. This is only allowed in handshake requests. + + """ + server_no_context_takeover: bool = False + client_no_context_takeover: bool = False + server_max_window_bits: int | None = None + client_max_window_bits: int | Literal[True] | None = None + + for name, value in params: + if name == "server_no_context_takeover": + if server_no_context_takeover: + raise DuplicateParameter(name) + if value is None: + server_no_context_takeover = True + else: + raise InvalidParameterValue(name, value) + + elif name == "client_no_context_takeover": + if client_no_context_takeover: + raise DuplicateParameter(name) + if value is None: + client_no_context_takeover = True + else: + raise InvalidParameterValue(name, value) + + elif name == "server_max_window_bits": + if server_max_window_bits is not None: + raise DuplicateParameter(name) + if value in _MAX_WINDOW_BITS_VALUES: + server_max_window_bits = int(value) + else: + raise InvalidParameterValue(name, value) + + elif name == "client_max_window_bits": + if client_max_window_bits is not None: + raise DuplicateParameter(name) + if is_server and value is None: # only in handshake requests + client_max_window_bits = True + elif value in _MAX_WINDOW_BITS_VALUES: + client_max_window_bits = int(value) + else: + raise InvalidParameterValue(name, value) + + else: + raise InvalidParameterName(name) + + return ( + server_no_context_takeover, + client_no_context_takeover, + server_max_window_bits, + client_max_window_bits, + ) + + +class ClientPerMessageDeflateFactory(ClientExtensionFactory): + """ + Client-side extension factory for the Per-Message Deflate extension. + + Parameters behave as described in `section 7.1 of RFC 7692`_. + + .. _section 7.1 of RFC 7692: https://datatracker.ietf.org/doc/html/rfc7692#section-7.1 + + Set them to :obj:`True` to include them in the negotiation offer without a + value or to an integer value to include them with this value. + + Args: + server_no_context_takeover: Prevent server from using context takeover. + client_no_context_takeover: Prevent client from using context takeover. + server_max_window_bits: Maximum size of the server's LZ77 sliding window + in bits, between 8 and 15. + client_max_window_bits: Maximum size of the client's LZ77 sliding window + in bits, between 8 and 15, or :obj:`True` to indicate support without + setting a limit. + compress_settings: Additional keyword arguments for :func:`zlib.compressobj`, + excluding ``wbits``. + + """ + + name = ExtensionName("permessage-deflate") + + def __init__( + self, + server_no_context_takeover: bool = False, + client_no_context_takeover: bool = False, + server_max_window_bits: int | None = None, + client_max_window_bits: int | Literal[True] | None = True, + compress_settings: dict[str, Any] | None = None, + ) -> None: + """ + Configure the Per-Message Deflate extension factory. + + """ + if not (server_max_window_bits is None or 8 <= server_max_window_bits <= 15): + raise ValueError("server_max_window_bits must be between 8 and 15") + if not ( + client_max_window_bits is None + or client_max_window_bits is True + or 8 <= client_max_window_bits <= 15 + ): + raise ValueError("client_max_window_bits must be between 8 and 15") + if compress_settings is not None and "wbits" in compress_settings: + raise ValueError( + "compress_settings must not include wbits, " + "set client_max_window_bits instead" + ) + + self.server_no_context_takeover = server_no_context_takeover + self.client_no_context_takeover = client_no_context_takeover + self.server_max_window_bits = server_max_window_bits + self.client_max_window_bits = client_max_window_bits + self.compress_settings = compress_settings + + def get_request_params(self) -> Sequence[ExtensionParameter]: + """ + Build request parameters. + + """ + return _build_parameters( + self.server_no_context_takeover, + self.client_no_context_takeover, + self.server_max_window_bits, + self.client_max_window_bits, + ) + + def process_response_params( + self, + params: Sequence[ExtensionParameter], + accepted_extensions: Sequence[Extension], + ) -> PerMessageDeflate: + """ + Process response parameters. + + Return an extension instance. + + """ + if any(other.name == self.name for other in accepted_extensions): + raise NegotiationError(f"received duplicate {self.name}") + + # Request parameters are available in instance variables. + + # Load response parameters in local variables. + ( + server_no_context_takeover, + client_no_context_takeover, + server_max_window_bits, + client_max_window_bits, + ) = _extract_parameters(params, is_server=False) + + # After comparing the request and the response, the final + # configuration must be available in the local variables. + + # server_no_context_takeover + # + # Req. Resp. Result + # ------ ------ -------------------------------------------------- + # False False False + # False True True + # True False Error! + # True True True + + if self.server_no_context_takeover: + if not server_no_context_takeover: + raise NegotiationError("expected server_no_context_takeover") + + # client_no_context_takeover + # + # Req. Resp. Result + # ------ ------ -------------------------------------------------- + # False False False + # False True True + # True False True - must change value + # True True True + + if self.client_no_context_takeover: + if not client_no_context_takeover: + client_no_context_takeover = True + + # server_max_window_bits + + # Req. Resp. Result + # ------ ------ -------------------------------------------------- + # None None None + # None 8≤M≤15 M + # 8≤N≤15 None Error! + # 8≤N≤15 8≤M≤N M + # 8≤N≤15 N self.server_max_window_bits: + raise NegotiationError("unsupported server_max_window_bits") + + # client_max_window_bits + + # Req. Resp. Result + # ------ ------ -------------------------------------------------- + # None None None + # None 8≤M≤15 Error! + # True None None + # True 8≤M≤15 M + # 8≤N≤15 None N - must change value + # 8≤N≤15 8≤M≤N M + # 8≤N≤15 N self.client_max_window_bits: + raise NegotiationError("unsupported client_max_window_bits") + + return PerMessageDeflate( + server_no_context_takeover, # remote_no_context_takeover + client_no_context_takeover, # local_no_context_takeover + server_max_window_bits or 15, # remote_max_window_bits + client_max_window_bits or 15, # local_max_window_bits + self.compress_settings, + ) + + +def enable_client_permessage_deflate( + extensions: Sequence[ClientExtensionFactory] | None, +) -> Sequence[ClientExtensionFactory]: + """ + Enable Per-Message Deflate with default settings in client extensions. + + If the extension is already present, perhaps with non-default settings, + the configuration isn't changed. + + """ + if extensions is None: + extensions = [] + if not any( + extension_factory.name == ClientPerMessageDeflateFactory.name + for extension_factory in extensions + ): + extensions = list(extensions) + [ + ClientPerMessageDeflateFactory( + compress_settings={"memLevel": 5}, + ) + ] + return extensions + + +class ServerPerMessageDeflateFactory(ServerExtensionFactory): + """ + Server-side extension factory for the Per-Message Deflate extension. + + Parameters behave as described in `section 7.1 of RFC 7692`_. + + .. _section 7.1 of RFC 7692: https://datatracker.ietf.org/doc/html/rfc7692#section-7.1 + + Set them to :obj:`True` to include them in the negotiation offer without a + value or to an integer value to include them with this value. + + Args: + server_no_context_takeover: Prevent server from using context takeover. + client_no_context_takeover: Prevent client from using context takeover. + server_max_window_bits: Maximum size of the server's LZ77 sliding window + in bits, between 8 and 15. + client_max_window_bits: Maximum size of the client's LZ77 sliding window + in bits, between 8 and 15. + compress_settings: Additional keyword arguments for :func:`zlib.compressobj`, + excluding ``wbits``. + require_client_max_window_bits: Do not enable compression at all if + client doesn't advertise support for ``client_max_window_bits``; + the default behavior is to enable compression without enforcing + ``client_max_window_bits``. + + """ + + name = ExtensionName("permessage-deflate") + + def __init__( + self, + server_no_context_takeover: bool = False, + client_no_context_takeover: bool = False, + server_max_window_bits: int | None = None, + client_max_window_bits: int | None = None, + compress_settings: dict[str, Any] | None = None, + require_client_max_window_bits: bool = False, + ) -> None: + """ + Configure the Per-Message Deflate extension factory. + + """ + if not (server_max_window_bits is None or 8 <= server_max_window_bits <= 15): + raise ValueError("server_max_window_bits must be between 8 and 15") + if not (client_max_window_bits is None or 8 <= client_max_window_bits <= 15): + raise ValueError("client_max_window_bits must be between 8 and 15") + if compress_settings is not None and "wbits" in compress_settings: + raise ValueError( + "compress_settings must not include wbits, " + "set server_max_window_bits instead" + ) + if client_max_window_bits is None and require_client_max_window_bits: + raise ValueError( + "require_client_max_window_bits is enabled, " + "but client_max_window_bits isn't configured" + ) + + self.server_no_context_takeover = server_no_context_takeover + self.client_no_context_takeover = client_no_context_takeover + self.server_max_window_bits = server_max_window_bits + self.client_max_window_bits = client_max_window_bits + self.compress_settings = compress_settings + self.require_client_max_window_bits = require_client_max_window_bits + + def process_request_params( + self, + params: Sequence[ExtensionParameter], + accepted_extensions: Sequence[Extension], + ) -> tuple[list[ExtensionParameter], PerMessageDeflate]: + """ + Process request parameters. + + Return response params and an extension instance. + + """ + if any(other.name == self.name for other in accepted_extensions): + raise NegotiationError(f"skipped duplicate {self.name}") + + # Load request parameters in local variables. + ( + server_no_context_takeover, + client_no_context_takeover, + server_max_window_bits, + client_max_window_bits, + ) = _extract_parameters(params, is_server=True) + + # Configuration parameters are available in instance variables. + + # After comparing the request and the configuration, the response must + # be available in the local variables. + + # server_no_context_takeover + # + # Config Req. Resp. + # ------ ------ -------------------------------------------------- + # False False False + # False True True + # True False True - must change value to True + # True True True + + if self.server_no_context_takeover: + if not server_no_context_takeover: + server_no_context_takeover = True + + # client_no_context_takeover + # + # Config Req. Resp. + # ------ ------ -------------------------------------------------- + # False False False + # False True True (or False) + # True False True - must change value to True + # True True True (or False) + + if self.client_no_context_takeover: + if not client_no_context_takeover: + client_no_context_takeover = True + + # server_max_window_bits + + # Config Req. Resp. + # ------ ------ -------------------------------------------------- + # None None None + # None 8≤M≤15 M + # 8≤N≤15 None N - must change value + # 8≤N≤15 8≤M≤N M + # 8≤N≤15 N self.server_max_window_bits: + server_max_window_bits = self.server_max_window_bits + + # client_max_window_bits + + # Config Req. Resp. + # ------ ------ -------------------------------------------------- + # None None None + # None True None - must change value + # None 8≤M≤15 M (or None) + # 8≤N≤15 None None or Error! + # 8≤N≤15 True N - must change value + # 8≤N≤15 8≤M≤N M (or None) + # 8≤N≤15 N Sequence[ServerExtensionFactory]: + """ + Enable Per-Message Deflate with default settings in server extensions. + + If the extension is already present, perhaps with non-default settings, + the configuration isn't changed. + + """ + if extensions is None: + extensions = [] + if not any( + ext_factory.name == ServerPerMessageDeflateFactory.name + for ext_factory in extensions + ): + extensions = list(extensions) + [ + ServerPerMessageDeflateFactory( + server_max_window_bits=12, + client_max_window_bits=12, + compress_settings={"memLevel": 5}, + ) + ] + return extensions diff --git a/gestao_raul/Lib/site-packages/websockets/frames.py b/gestao_raul/Lib/site-packages/websockets/frames.py new file mode 100644 index 0000000..ab0869d --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/frames.py @@ -0,0 +1,430 @@ +from __future__ import annotations + +import dataclasses +import enum +import io +import os +import secrets +import struct +from collections.abc import Generator, Sequence +from typing import Callable, Union + +from .exceptions import PayloadTooBig, ProtocolError + + +try: + from .speedups import apply_mask +except ImportError: + from .utils import apply_mask + + +__all__ = [ + "Opcode", + "OP_CONT", + "OP_TEXT", + "OP_BINARY", + "OP_CLOSE", + "OP_PING", + "OP_PONG", + "DATA_OPCODES", + "CTRL_OPCODES", + "CloseCode", + "Frame", + "Close", +] + + +class Opcode(enum.IntEnum): + """Opcode values for WebSocket frames.""" + + CONT, TEXT, BINARY = 0x00, 0x01, 0x02 + CLOSE, PING, PONG = 0x08, 0x09, 0x0A + + +OP_CONT = Opcode.CONT +OP_TEXT = Opcode.TEXT +OP_BINARY = Opcode.BINARY +OP_CLOSE = Opcode.CLOSE +OP_PING = Opcode.PING +OP_PONG = Opcode.PONG + +DATA_OPCODES = OP_CONT, OP_TEXT, OP_BINARY +CTRL_OPCODES = OP_CLOSE, OP_PING, OP_PONG + + +class CloseCode(enum.IntEnum): + """Close code values for WebSocket close frames.""" + + NORMAL_CLOSURE = 1000 + GOING_AWAY = 1001 + PROTOCOL_ERROR = 1002 + UNSUPPORTED_DATA = 1003 + # 1004 is reserved + NO_STATUS_RCVD = 1005 + ABNORMAL_CLOSURE = 1006 + INVALID_DATA = 1007 + POLICY_VIOLATION = 1008 + MESSAGE_TOO_BIG = 1009 + MANDATORY_EXTENSION = 1010 + INTERNAL_ERROR = 1011 + SERVICE_RESTART = 1012 + TRY_AGAIN_LATER = 1013 + BAD_GATEWAY = 1014 + TLS_HANDSHAKE = 1015 + + +# See https://www.iana.org/assignments/websocket/websocket.xhtml +CLOSE_CODE_EXPLANATIONS: dict[int, str] = { + CloseCode.NORMAL_CLOSURE: "OK", + CloseCode.GOING_AWAY: "going away", + CloseCode.PROTOCOL_ERROR: "protocol error", + CloseCode.UNSUPPORTED_DATA: "unsupported data", + CloseCode.NO_STATUS_RCVD: "no status received [internal]", + CloseCode.ABNORMAL_CLOSURE: "abnormal closure [internal]", + CloseCode.INVALID_DATA: "invalid frame payload data", + CloseCode.POLICY_VIOLATION: "policy violation", + CloseCode.MESSAGE_TOO_BIG: "message too big", + CloseCode.MANDATORY_EXTENSION: "mandatory extension", + CloseCode.INTERNAL_ERROR: "internal error", + CloseCode.SERVICE_RESTART: "service restart", + CloseCode.TRY_AGAIN_LATER: "try again later", + CloseCode.BAD_GATEWAY: "bad gateway", + CloseCode.TLS_HANDSHAKE: "TLS handshake failure [internal]", +} + + +# Close code that are allowed in a close frame. +# Using a set optimizes `code in EXTERNAL_CLOSE_CODES`. +EXTERNAL_CLOSE_CODES = { + CloseCode.NORMAL_CLOSURE, + CloseCode.GOING_AWAY, + CloseCode.PROTOCOL_ERROR, + CloseCode.UNSUPPORTED_DATA, + CloseCode.INVALID_DATA, + CloseCode.POLICY_VIOLATION, + CloseCode.MESSAGE_TOO_BIG, + CloseCode.MANDATORY_EXTENSION, + CloseCode.INTERNAL_ERROR, + CloseCode.SERVICE_RESTART, + CloseCode.TRY_AGAIN_LATER, + CloseCode.BAD_GATEWAY, +} + + +OK_CLOSE_CODES = { + CloseCode.NORMAL_CLOSURE, + CloseCode.GOING_AWAY, + CloseCode.NO_STATUS_RCVD, +} + + +BytesLike = bytes, bytearray, memoryview + + +@dataclasses.dataclass +class Frame: + """ + WebSocket frame. + + Attributes: + opcode: Opcode. + data: Payload data. + fin: FIN bit. + rsv1: RSV1 bit. + rsv2: RSV2 bit. + rsv3: RSV3 bit. + + Only these fields are needed. The MASK bit, payload length and masking-key + are handled on the fly when parsing and serializing frames. + + """ + + opcode: Opcode + data: Union[bytes, bytearray, memoryview] + fin: bool = True + rsv1: bool = False + rsv2: bool = False + rsv3: bool = False + + # Configure if you want to see more in logs. Should be a multiple of 3. + MAX_LOG_SIZE = int(os.environ.get("WEBSOCKETS_MAX_LOG_SIZE", "75")) + + def __str__(self) -> str: + """ + Return a human-readable representation of a frame. + + """ + coding = None + length = f"{len(self.data)} byte{'' if len(self.data) == 1 else 's'}" + non_final = "" if self.fin else "continued" + + if self.opcode is OP_TEXT: + # Decoding only the beginning and the end is needlessly hard. + # Decode the entire payload then elide later if necessary. + data = repr(bytes(self.data).decode()) + elif self.opcode is OP_BINARY: + # We'll show at most the first 16 bytes and the last 8 bytes. + # Encode just what we need, plus two dummy bytes to elide later. + binary = self.data + if len(binary) > self.MAX_LOG_SIZE // 3: + cut = (self.MAX_LOG_SIZE // 3 - 1) // 3 # by default cut = 8 + binary = b"".join([binary[: 2 * cut], b"\x00\x00", binary[-cut:]]) + data = " ".join(f"{byte:02x}" for byte in binary) + elif self.opcode is OP_CLOSE: + data = str(Close.parse(self.data)) + elif self.data: + # We don't know if a Continuation frame contains text or binary. + # Ping and Pong frames could contain UTF-8. + # Attempt to decode as UTF-8 and display it as text; fallback to + # binary. If self.data is a memoryview, it has no decode() method, + # which raises AttributeError. + try: + data = repr(bytes(self.data).decode()) + coding = "text" + except (UnicodeDecodeError, AttributeError): + binary = self.data + if len(binary) > self.MAX_LOG_SIZE // 3: + cut = (self.MAX_LOG_SIZE // 3 - 1) // 3 # by default cut = 8 + binary = b"".join([binary[: 2 * cut], b"\x00\x00", binary[-cut:]]) + data = " ".join(f"{byte:02x}" for byte in binary) + coding = "binary" + else: + data = "''" + + if len(data) > self.MAX_LOG_SIZE: + cut = self.MAX_LOG_SIZE // 3 - 1 # by default cut = 24 + data = data[: 2 * cut] + "..." + data[-cut:] + + metadata = ", ".join(filter(None, [coding, length, non_final])) + + return f"{self.opcode.name} {data} [{metadata}]" + + @classmethod + def parse( + cls, + read_exact: Callable[[int], Generator[None, None, bytes]], + *, + mask: bool, + max_size: int | None = None, + extensions: Sequence[extensions.Extension] | None = None, + ) -> Generator[None, None, Frame]: + """ + Parse a WebSocket frame. + + This is a generator-based coroutine. + + Args: + read_exact: Generator-based coroutine that reads the requested + bytes or raises an exception if there isn't enough data. + mask: Whether the frame should be masked i.e. whether the read + happens on the server side. + max_size: Maximum payload size in bytes. + extensions: List of extensions, applied in reverse order. + + Raises: + EOFError: If the connection is closed without a full WebSocket frame. + PayloadTooBig: If the frame's payload size exceeds ``max_size``. + ProtocolError: If the frame contains incorrect values. + + """ + # Read the header. + data = yield from read_exact(2) + head1, head2 = struct.unpack("!BB", data) + + # While not Pythonic, this is marginally faster than calling bool(). + fin = True if head1 & 0b10000000 else False + rsv1 = True if head1 & 0b01000000 else False + rsv2 = True if head1 & 0b00100000 else False + rsv3 = True if head1 & 0b00010000 else False + + try: + opcode = Opcode(head1 & 0b00001111) + except ValueError as exc: + raise ProtocolError("invalid opcode") from exc + + if (True if head2 & 0b10000000 else False) != mask: + raise ProtocolError("incorrect masking") + + length = head2 & 0b01111111 + if length == 126: + data = yield from read_exact(2) + (length,) = struct.unpack("!H", data) + elif length == 127: + data = yield from read_exact(8) + (length,) = struct.unpack("!Q", data) + if max_size is not None and length > max_size: + raise PayloadTooBig(length, max_size) + if mask: + mask_bytes = yield from read_exact(4) + + # Read the data. + data = yield from read_exact(length) + if mask: + data = apply_mask(data, mask_bytes) + + frame = cls(opcode, data, fin, rsv1, rsv2, rsv3) + + if extensions is None: + extensions = [] + for extension in reversed(extensions): + frame = extension.decode(frame, max_size=max_size) + + frame.check() + + return frame + + def serialize( + self, + *, + mask: bool, + extensions: Sequence[extensions.Extension] | None = None, + ) -> bytes: + """ + Serialize a WebSocket frame. + + Args: + mask: Whether the frame should be masked i.e. whether the write + happens on the client side. + extensions: List of extensions, applied in order. + + Raises: + ProtocolError: If the frame contains incorrect values. + + """ + self.check() + + if extensions is None: + extensions = [] + for extension in extensions: + self = extension.encode(self) + + output = io.BytesIO() + + # Prepare the header. + head1 = ( + (0b10000000 if self.fin else 0) + | (0b01000000 if self.rsv1 else 0) + | (0b00100000 if self.rsv2 else 0) + | (0b00010000 if self.rsv3 else 0) + | self.opcode + ) + + head2 = 0b10000000 if mask else 0 + + length = len(self.data) + if length < 126: + output.write(struct.pack("!BB", head1, head2 | length)) + elif length < 65536: + output.write(struct.pack("!BBH", head1, head2 | 126, length)) + else: + output.write(struct.pack("!BBQ", head1, head2 | 127, length)) + + if mask: + mask_bytes = secrets.token_bytes(4) + output.write(mask_bytes) + + # Prepare the data. + if mask: + data = apply_mask(self.data, mask_bytes) + else: + data = self.data + output.write(data) + + return output.getvalue() + + def check(self) -> None: + """ + Check that reserved bits and opcode have acceptable values. + + Raises: + ProtocolError: If a reserved bit or the opcode is invalid. + + """ + if self.rsv1 or self.rsv2 or self.rsv3: + raise ProtocolError("reserved bits must be 0") + + if self.opcode in CTRL_OPCODES: + if len(self.data) > 125: + raise ProtocolError("control frame too long") + if not self.fin: + raise ProtocolError("fragmented control frame") + + +@dataclasses.dataclass +class Close: + """ + Code and reason for WebSocket close frames. + + Attributes: + code: Close code. + reason: Close reason. + + """ + + code: int + reason: str + + def __str__(self) -> str: + """ + Return a human-readable representation of a close code and reason. + + """ + if 3000 <= self.code < 4000: + explanation = "registered" + elif 4000 <= self.code < 5000: + explanation = "private use" + else: + explanation = CLOSE_CODE_EXPLANATIONS.get(self.code, "unknown") + result = f"{self.code} ({explanation})" + + if self.reason: + result = f"{result} {self.reason}" + + return result + + @classmethod + def parse(cls, data: bytes) -> Close: + """ + Parse the payload of a close frame. + + Args: + data: Payload of the close frame. + + Raises: + ProtocolError: If data is ill-formed. + UnicodeDecodeError: If the reason isn't valid UTF-8. + + """ + if len(data) >= 2: + (code,) = struct.unpack("!H", data[:2]) + reason = data[2:].decode() + close = cls(code, reason) + close.check() + return close + elif len(data) == 0: + return cls(CloseCode.NO_STATUS_RCVD, "") + else: + raise ProtocolError("close frame too short") + + def serialize(self) -> bytes: + """ + Serialize the payload of a close frame. + + """ + self.check() + return struct.pack("!H", self.code) + self.reason.encode() + + def check(self) -> None: + """ + Check that the close code has a valid value for a close frame. + + Raises: + ProtocolError: If the close code is invalid. + + """ + if not (self.code in EXTERNAL_CLOSE_CODES or 3000 <= self.code < 5000): + raise ProtocolError("invalid status code") + + +# At the bottom to break import cycles created by type annotations. +from . import extensions # noqa: E402 diff --git a/gestao_raul/Lib/site-packages/websockets/headers.py b/gestao_raul/Lib/site-packages/websockets/headers.py new file mode 100644 index 0000000..e05ff5b --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/headers.py @@ -0,0 +1,586 @@ +from __future__ import annotations + +import base64 +import binascii +import ipaddress +import re +from collections.abc import Sequence +from typing import Callable, TypeVar, cast + +from .exceptions import InvalidHeaderFormat, InvalidHeaderValue +from .typing import ( + ConnectionOption, + ExtensionHeader, + ExtensionName, + ExtensionParameter, + Subprotocol, + UpgradeProtocol, +) + + +__all__ = [ + "build_host", + "parse_connection", + "parse_upgrade", + "parse_extension", + "build_extension", + "parse_subprotocol", + "build_subprotocol", + "validate_subprotocols", + "build_www_authenticate_basic", + "parse_authorization_basic", + "build_authorization_basic", +] + + +T = TypeVar("T") + + +def build_host( + host: str, + port: int, + secure: bool, + *, + always_include_port: bool = False, +) -> str: + """ + Build a ``Host`` header. + + """ + # https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2 + # IPv6 addresses must be enclosed in brackets. + try: + address = ipaddress.ip_address(host) + except ValueError: + # host is a hostname + pass + else: + # host is an IP address + if address.version == 6: + host = f"[{host}]" + + if always_include_port or port != (443 if secure else 80): + host = f"{host}:{port}" + + return host + + +# To avoid a dependency on a parsing library, we implement manually the ABNF +# described in https://datatracker.ietf.org/doc/html/rfc6455#section-9.1 and +# https://datatracker.ietf.org/doc/html/rfc7230#appendix-B. + + +def peek_ahead(header: str, pos: int) -> str | None: + """ + Return the next character from ``header`` at the given position. + + Return :obj:`None` at the end of ``header``. + + We never need to peek more than one character ahead. + + """ + return None if pos == len(header) else header[pos] + + +_OWS_re = re.compile(r"[\t ]*") + + +def parse_OWS(header: str, pos: int) -> int: + """ + Parse optional whitespace from ``header`` at the given position. + + Return the new position. + + The whitespace itself isn't returned because it isn't significant. + + """ + # There's always a match, possibly empty, whose content doesn't matter. + match = _OWS_re.match(header, pos) + assert match is not None + return match.end() + + +_token_re = re.compile(r"[-!#$%&\'*+.^_`|~0-9a-zA-Z]+") + + +def parse_token(header: str, pos: int, header_name: str) -> tuple[str, int]: + """ + Parse a token from ``header`` at the given position. + + Return the token value and the new position. + + Raises: + InvalidHeaderFormat: On invalid inputs. + + """ + match = _token_re.match(header, pos) + if match is None: + raise InvalidHeaderFormat(header_name, "expected token", header, pos) + return match.group(), match.end() + + +_quoted_string_re = re.compile( + r'"(?:[\x09\x20-\x21\x23-\x5b\x5d-\x7e]|\\[\x09\x20-\x7e\x80-\xff])*"' +) + + +_unquote_re = re.compile(r"\\([\x09\x20-\x7e\x80-\xff])") + + +def parse_quoted_string(header: str, pos: int, header_name: str) -> tuple[str, int]: + """ + Parse a quoted string from ``header`` at the given position. + + Return the unquoted value and the new position. + + Raises: + InvalidHeaderFormat: On invalid inputs. + + """ + match = _quoted_string_re.match(header, pos) + if match is None: + raise InvalidHeaderFormat(header_name, "expected quoted string", header, pos) + return _unquote_re.sub(r"\1", match.group()[1:-1]), match.end() + + +_quotable_re = re.compile(r"[\x09\x20-\x7e\x80-\xff]*") + + +_quote_re = re.compile(r"([\x22\x5c])") + + +def build_quoted_string(value: str) -> str: + """ + Format ``value`` as a quoted string. + + This is the reverse of :func:`parse_quoted_string`. + + """ + match = _quotable_re.fullmatch(value) + if match is None: + raise ValueError("invalid characters for quoted-string encoding") + return '"' + _quote_re.sub(r"\\\1", value) + '"' + + +def parse_list( + parse_item: Callable[[str, int, str], tuple[T, int]], + header: str, + pos: int, + header_name: str, +) -> list[T]: + """ + Parse a comma-separated list from ``header`` at the given position. + + This is appropriate for parsing values with the following grammar: + + 1#item + + ``parse_item`` parses one item. + + ``header`` is assumed not to start or end with whitespace. + + (This function is designed for parsing an entire header value and + :func:`~websockets.http.read_headers` strips whitespace from values.) + + Return a list of items. + + Raises: + InvalidHeaderFormat: On invalid inputs. + + """ + # Per https://datatracker.ietf.org/doc/html/rfc7230#section-7, "a recipient + # MUST parse and ignore a reasonable number of empty list elements"; + # hence while loops that remove extra delimiters. + + # Remove extra delimiters before the first item. + while peek_ahead(header, pos) == ",": + pos = parse_OWS(header, pos + 1) + + items = [] + while True: + # Loop invariant: a item starts at pos in header. + item, pos = parse_item(header, pos, header_name) + items.append(item) + pos = parse_OWS(header, pos) + + # We may have reached the end of the header. + if pos == len(header): + break + + # There must be a delimiter after each element except the last one. + if peek_ahead(header, pos) == ",": + pos = parse_OWS(header, pos + 1) + else: + raise InvalidHeaderFormat(header_name, "expected comma", header, pos) + + # Remove extra delimiters before the next item. + while peek_ahead(header, pos) == ",": + pos = parse_OWS(header, pos + 1) + + # We may have reached the end of the header. + if pos == len(header): + break + + # Since we only advance in the header by one character with peek_ahead() + # or with the end position of a regex match, we can't overshoot the end. + assert pos == len(header) + + return items + + +def parse_connection_option( + header: str, pos: int, header_name: str +) -> tuple[ConnectionOption, int]: + """ + Parse a Connection option from ``header`` at the given position. + + Return the protocol value and the new position. + + Raises: + InvalidHeaderFormat: On invalid inputs. + + """ + item, pos = parse_token(header, pos, header_name) + return cast(ConnectionOption, item), pos + + +def parse_connection(header: str) -> list[ConnectionOption]: + """ + Parse a ``Connection`` header. + + Return a list of HTTP connection options. + + Args + header: value of the ``Connection`` header. + + Raises: + InvalidHeaderFormat: On invalid inputs. + + """ + return parse_list(parse_connection_option, header, 0, "Connection") + + +_protocol_re = re.compile( + r"[-!#$%&\'*+.^_`|~0-9a-zA-Z]+(?:/[-!#$%&\'*+.^_`|~0-9a-zA-Z]+)?" +) + + +def parse_upgrade_protocol( + header: str, pos: int, header_name: str +) -> tuple[UpgradeProtocol, int]: + """ + Parse an Upgrade protocol from ``header`` at the given position. + + Return the protocol value and the new position. + + Raises: + InvalidHeaderFormat: On invalid inputs. + + """ + match = _protocol_re.match(header, pos) + if match is None: + raise InvalidHeaderFormat(header_name, "expected protocol", header, pos) + return cast(UpgradeProtocol, match.group()), match.end() + + +def parse_upgrade(header: str) -> list[UpgradeProtocol]: + """ + Parse an ``Upgrade`` header. + + Return a list of HTTP protocols. + + Args: + header: Value of the ``Upgrade`` header. + + Raises: + InvalidHeaderFormat: On invalid inputs. + + """ + return parse_list(parse_upgrade_protocol, header, 0, "Upgrade") + + +def parse_extension_item_param( + header: str, pos: int, header_name: str +) -> tuple[ExtensionParameter, int]: + """ + Parse a single extension parameter from ``header`` at the given position. + + Return a ``(name, value)`` pair and the new position. + + Raises: + InvalidHeaderFormat: On invalid inputs. + + """ + # Extract parameter name. + name, pos = parse_token(header, pos, header_name) + pos = parse_OWS(header, pos) + # Extract parameter value, if there is one. + value: str | None = None + if peek_ahead(header, pos) == "=": + pos = parse_OWS(header, pos + 1) + if peek_ahead(header, pos) == '"': + pos_before = pos # for proper error reporting below + value, pos = parse_quoted_string(header, pos, header_name) + # https://datatracker.ietf.org/doc/html/rfc6455#section-9.1 says: + # the value after quoted-string unescaping MUST conform to + # the 'token' ABNF. + if _token_re.fullmatch(value) is None: + raise InvalidHeaderFormat( + header_name, "invalid quoted header content", header, pos_before + ) + else: + value, pos = parse_token(header, pos, header_name) + pos = parse_OWS(header, pos) + + return (name, value), pos + + +def parse_extension_item( + header: str, pos: int, header_name: str +) -> tuple[ExtensionHeader, int]: + """ + Parse an extension definition from ``header`` at the given position. + + Return an ``(extension name, parameters)`` pair, where ``parameters`` is a + list of ``(name, value)`` pairs, and the new position. + + Raises: + InvalidHeaderFormat: On invalid inputs. + + """ + # Extract extension name. + name, pos = parse_token(header, pos, header_name) + pos = parse_OWS(header, pos) + # Extract all parameters. + parameters = [] + while peek_ahead(header, pos) == ";": + pos = parse_OWS(header, pos + 1) + parameter, pos = parse_extension_item_param(header, pos, header_name) + parameters.append(parameter) + return (cast(ExtensionName, name), parameters), pos + + +def parse_extension(header: str) -> list[ExtensionHeader]: + """ + Parse a ``Sec-WebSocket-Extensions`` header. + + Return a list of WebSocket extensions and their parameters in this format:: + + [ + ( + 'extension name', + [ + ('parameter name', 'parameter value'), + .... + ] + ), + ... + ] + + Parameter values are :obj:`None` when no value is provided. + + Raises: + InvalidHeaderFormat: On invalid inputs. + + """ + return parse_list(parse_extension_item, header, 0, "Sec-WebSocket-Extensions") + + +parse_extension_list = parse_extension # alias for backwards compatibility + + +def build_extension_item( + name: ExtensionName, parameters: Sequence[ExtensionParameter] +) -> str: + """ + Build an extension definition. + + This is the reverse of :func:`parse_extension_item`. + + """ + return "; ".join( + [cast(str, name)] + + [ + # Quoted strings aren't necessary because values are always tokens. + name if value is None else f"{name}={value}" + for name, value in parameters + ] + ) + + +def build_extension(extensions: Sequence[ExtensionHeader]) -> str: + """ + Build a ``Sec-WebSocket-Extensions`` header. + + This is the reverse of :func:`parse_extension`. + + """ + return ", ".join( + build_extension_item(name, parameters) for name, parameters in extensions + ) + + +build_extension_list = build_extension # alias for backwards compatibility + + +def parse_subprotocol_item( + header: str, pos: int, header_name: str +) -> tuple[Subprotocol, int]: + """ + Parse a subprotocol from ``header`` at the given position. + + Return the subprotocol value and the new position. + + Raises: + InvalidHeaderFormat: On invalid inputs. + + """ + item, pos = parse_token(header, pos, header_name) + return cast(Subprotocol, item), pos + + +def parse_subprotocol(header: str) -> list[Subprotocol]: + """ + Parse a ``Sec-WebSocket-Protocol`` header. + + Return a list of WebSocket subprotocols. + + Raises: + InvalidHeaderFormat: On invalid inputs. + + """ + return parse_list(parse_subprotocol_item, header, 0, "Sec-WebSocket-Protocol") + + +parse_subprotocol_list = parse_subprotocol # alias for backwards compatibility + + +def build_subprotocol(subprotocols: Sequence[Subprotocol]) -> str: + """ + Build a ``Sec-WebSocket-Protocol`` header. + + This is the reverse of :func:`parse_subprotocol`. + + """ + return ", ".join(subprotocols) + + +build_subprotocol_list = build_subprotocol # alias for backwards compatibility + + +def validate_subprotocols(subprotocols: Sequence[Subprotocol]) -> None: + """ + Validate that ``subprotocols`` is suitable for :func:`build_subprotocol`. + + """ + if not isinstance(subprotocols, Sequence): + raise TypeError("subprotocols must be a list") + if isinstance(subprotocols, str): + raise TypeError("subprotocols must be a list, not a str") + for subprotocol in subprotocols: + if not _token_re.fullmatch(subprotocol): + raise ValueError(f"invalid subprotocol: {subprotocol}") + + +def build_www_authenticate_basic(realm: str) -> str: + """ + Build a ``WWW-Authenticate`` header for HTTP Basic Auth. + + Args: + realm: Identifier of the protection space. + + """ + # https://datatracker.ietf.org/doc/html/rfc7617#section-2 + realm = build_quoted_string(realm) + charset = build_quoted_string("UTF-8") + return f"Basic realm={realm}, charset={charset}" + + +_token68_re = re.compile(r"[A-Za-z0-9-._~+/]+=*") + + +def parse_token68(header: str, pos: int, header_name: str) -> tuple[str, int]: + """ + Parse a token68 from ``header`` at the given position. + + Return the token value and the new position. + + Raises: + InvalidHeaderFormat: On invalid inputs. + + """ + match = _token68_re.match(header, pos) + if match is None: + raise InvalidHeaderFormat(header_name, "expected token68", header, pos) + return match.group(), match.end() + + +def parse_end(header: str, pos: int, header_name: str) -> None: + """ + Check that parsing reached the end of header. + + """ + if pos < len(header): + raise InvalidHeaderFormat(header_name, "trailing data", header, pos) + + +def parse_authorization_basic(header: str) -> tuple[str, str]: + """ + Parse an ``Authorization`` header for HTTP Basic Auth. + + Return a ``(username, password)`` tuple. + + Args: + header: Value of the ``Authorization`` header. + + Raises: + InvalidHeaderFormat: On invalid inputs. + InvalidHeaderValue: On unsupported inputs. + + """ + # https://datatracker.ietf.org/doc/html/rfc7235#section-2.1 + # https://datatracker.ietf.org/doc/html/rfc7617#section-2 + scheme, pos = parse_token(header, 0, "Authorization") + if scheme.lower() != "basic": + raise InvalidHeaderValue( + "Authorization", + f"unsupported scheme: {scheme}", + ) + if peek_ahead(header, pos) != " ": + raise InvalidHeaderFormat( + "Authorization", "expected space after scheme", header, pos + ) + pos += 1 + basic_credentials, pos = parse_token68(header, pos, "Authorization") + parse_end(header, pos, "Authorization") + + try: + user_pass = base64.b64decode(basic_credentials.encode()).decode() + except binascii.Error: + raise InvalidHeaderValue( + "Authorization", + "expected base64-encoded credentials", + ) from None + try: + username, password = user_pass.split(":", 1) + except ValueError: + raise InvalidHeaderValue( + "Authorization", + "expected username:password credentials", + ) from None + + return username, password + + +def build_authorization_basic(username: str, password: str) -> str: + """ + Build an ``Authorization`` header for HTTP Basic Auth. + + This is the reverse of :func:`parse_authorization_basic`. + + """ + # https://datatracker.ietf.org/doc/html/rfc7617#section-2 + assert ":" not in username + user_pass = f"{username}:{password}" + basic_credentials = base64.b64encode(user_pass.encode()).decode() + return "Basic " + basic_credentials diff --git a/gestao_raul/Lib/site-packages/websockets/http.py b/gestao_raul/Lib/site-packages/websockets/http.py new file mode 100644 index 0000000..0d860e5 --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/http.py @@ -0,0 +1,20 @@ +from __future__ import annotations + +import warnings + +from .datastructures import Headers, MultipleValuesError # noqa: F401 + + +with warnings.catch_warnings(): + # Suppress redundant DeprecationWarning raised by websockets.legacy. + warnings.filterwarnings("ignore", category=DeprecationWarning) + from .legacy.http import read_request, read_response # noqa: F401 + + +warnings.warn( # deprecated in 9.0 - 2021-09-01 + "Headers and MultipleValuesError were moved " + "from websockets.http to websockets.datastructures" + "and read_request and read_response were moved " + "from websockets.http to websockets.legacy.http", + DeprecationWarning, +) diff --git a/gestao_raul/Lib/site-packages/websockets/http11.py b/gestao_raul/Lib/site-packages/websockets/http11.py new file mode 100644 index 0000000..530ac3d --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/http11.py @@ -0,0 +1,427 @@ +from __future__ import annotations + +import dataclasses +import os +import re +import sys +import warnings +from collections.abc import Generator +from typing import Callable + +from .datastructures import Headers +from .exceptions import SecurityError +from .version import version as websockets_version + + +__all__ = [ + "SERVER", + "USER_AGENT", + "Request", + "Response", +] + + +PYTHON_VERSION = "{}.{}".format(*sys.version_info) + +# User-Agent header for HTTP requests. +USER_AGENT = os.environ.get( + "WEBSOCKETS_USER_AGENT", + f"Python/{PYTHON_VERSION} websockets/{websockets_version}", +) + +# Server header for HTTP responses. +SERVER = os.environ.get( + "WEBSOCKETS_SERVER", + f"Python/{PYTHON_VERSION} websockets/{websockets_version}", +) + +# Maximum total size of headers is around 128 * 8 KiB = 1 MiB. +MAX_NUM_HEADERS = int(os.environ.get("WEBSOCKETS_MAX_NUM_HEADERS", "128")) + +# Limit request line and header lines. 8KiB is the most common default +# configuration of popular HTTP servers. +MAX_LINE_LENGTH = int(os.environ.get("WEBSOCKETS_MAX_LINE_LENGTH", "8192")) + +# Support for HTTP response bodies is intended to read an error message +# returned by a server. It isn't designed to perform large file transfers. +MAX_BODY_SIZE = int(os.environ.get("WEBSOCKETS_MAX_BODY_SIZE", "1_048_576")) # 1 MiB + + +def d(value: bytes) -> str: + """ + Decode a bytestring for interpolating into an error message. + + """ + return value.decode(errors="backslashreplace") + + +# See https://datatracker.ietf.org/doc/html/rfc7230#appendix-B. + +# Regex for validating header names. + +_token_re = re.compile(rb"[-!#$%&\'*+.^_`|~0-9a-zA-Z]+") + +# Regex for validating header values. + +# We don't attempt to support obsolete line folding. + +# Include HTAB (\x09), SP (\x20), VCHAR (\x21-\x7e), obs-text (\x80-\xff). + +# The ABNF is complicated because it attempts to express that optional +# whitespace is ignored. We strip whitespace and don't revalidate that. + +# See also https://www.rfc-editor.org/errata_search.php?rfc=7230&eid=4189 + +_value_re = re.compile(rb"[\x09\x20-\x7e\x80-\xff]*") + + +@dataclasses.dataclass +class Request: + """ + WebSocket handshake request. + + Attributes: + path: Request path, including optional query. + headers: Request headers. + """ + + path: str + headers: Headers + # body isn't useful is the context of this library. + + _exception: Exception | None = None + + @property + def exception(self) -> Exception | None: # pragma: no cover + warnings.warn( # deprecated in 10.3 - 2022-04-17 + "Request.exception is deprecated; use ServerProtocol.handshake_exc instead", + DeprecationWarning, + ) + return self._exception + + @classmethod + def parse( + cls, + read_line: Callable[[int], Generator[None, None, bytes]], + ) -> Generator[None, None, Request]: + """ + Parse a WebSocket handshake request. + + This is a generator-based coroutine. + + The request path isn't URL-decoded or validated in any way. + + The request path and headers are expected to contain only ASCII + characters. Other characters are represented with surrogate escapes. + + :meth:`parse` doesn't attempt to read the request body because + WebSocket handshake requests don't have one. If the request contains a + body, it may be read from the data stream after :meth:`parse` returns. + + Args: + read_line: Generator-based coroutine that reads a LF-terminated + line or raises an exception if there isn't enough data + + Raises: + EOFError: If the connection is closed without a full HTTP request. + SecurityError: If the request exceeds a security limit. + ValueError: If the request isn't well formatted. + + """ + # https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.1 + + # Parsing is simple because fixed values are expected for method and + # version and because path isn't checked. Since WebSocket software tends + # to implement HTTP/1.1 strictly, there's little need for lenient parsing. + + try: + request_line = yield from parse_line(read_line) + except EOFError as exc: + raise EOFError("connection closed while reading HTTP request line") from exc + + try: + method, raw_path, protocol = request_line.split(b" ", 2) + except ValueError: # not enough values to unpack (expected 3, got 1-2) + raise ValueError(f"invalid HTTP request line: {d(request_line)}") from None + if protocol != b"HTTP/1.1": + raise ValueError( + f"unsupported protocol; expected HTTP/1.1: {d(request_line)}" + ) + if method != b"GET": + raise ValueError(f"unsupported HTTP method; expected GET; got {d(method)}") + path = raw_path.decode("ascii", "surrogateescape") + + headers = yield from parse_headers(read_line) + + # https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.3 + + if "Transfer-Encoding" in headers: + raise NotImplementedError("transfer codings aren't supported") + + if "Content-Length" in headers: + raise ValueError("unsupported request body") + + return cls(path, headers) + + def serialize(self) -> bytes: + """ + Serialize a WebSocket handshake request. + + """ + # Since the request line and headers only contain ASCII characters, + # we can keep this simple. + request = f"GET {self.path} HTTP/1.1\r\n".encode() + request += self.headers.serialize() + return request + + +@dataclasses.dataclass +class Response: + """ + WebSocket handshake response. + + Attributes: + status_code: Response code. + reason_phrase: Response reason. + headers: Response headers. + body: Response body. + + """ + + status_code: int + reason_phrase: str + headers: Headers + body: bytes = b"" + + _exception: Exception | None = None + + @property + def exception(self) -> Exception | None: # pragma: no cover + warnings.warn( # deprecated in 10.3 - 2022-04-17 + "Response.exception is deprecated; " + "use ClientProtocol.handshake_exc instead", + DeprecationWarning, + ) + return self._exception + + @classmethod + def parse( + cls, + read_line: Callable[[int], Generator[None, None, bytes]], + read_exact: Callable[[int], Generator[None, None, bytes]], + read_to_eof: Callable[[int], Generator[None, None, bytes]], + include_body: bool = True, + ) -> Generator[None, None, Response]: + """ + Parse a WebSocket handshake response. + + This is a generator-based coroutine. + + The reason phrase and headers are expected to contain only ASCII + characters. Other characters are represented with surrogate escapes. + + Args: + read_line: Generator-based coroutine that reads a LF-terminated + line or raises an exception if there isn't enough data. + read_exact: Generator-based coroutine that reads the requested + bytes or raises an exception if there isn't enough data. + read_to_eof: Generator-based coroutine that reads until the end + of the stream. + + Raises: + EOFError: If the connection is closed without a full HTTP response. + SecurityError: If the response exceeds a security limit. + LookupError: If the response isn't well formatted. + ValueError: If the response isn't well formatted. + + """ + # https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2 + + try: + status_line = yield from parse_line(read_line) + except EOFError as exc: + raise EOFError("connection closed while reading HTTP status line") from exc + + try: + protocol, raw_status_code, raw_reason = status_line.split(b" ", 2) + except ValueError: # not enough values to unpack (expected 3, got 1-2) + raise ValueError(f"invalid HTTP status line: {d(status_line)}") from None + if protocol != b"HTTP/1.1": + raise ValueError( + f"unsupported protocol; expected HTTP/1.1: {d(status_line)}" + ) + try: + status_code = int(raw_status_code) + except ValueError: # invalid literal for int() with base 10 + raise ValueError( + f"invalid status code; expected integer; got {d(raw_status_code)}" + ) from None + if not 100 <= status_code < 600: + raise ValueError( + f"invalid status code; expected 100–599; got {d(raw_status_code)}" + ) + if not _value_re.fullmatch(raw_reason): + raise ValueError(f"invalid HTTP reason phrase: {d(raw_reason)}") + reason = raw_reason.decode("ascii", "surrogateescape") + + headers = yield from parse_headers(read_line) + + if include_body: + body = yield from read_body( + status_code, headers, read_line, read_exact, read_to_eof + ) + else: + body = b"" + + return cls(status_code, reason, headers, body) + + def serialize(self) -> bytes: + """ + Serialize a WebSocket handshake response. + + """ + # Since the status line and headers only contain ASCII characters, + # we can keep this simple. + response = f"HTTP/1.1 {self.status_code} {self.reason_phrase}\r\n".encode() + response += self.headers.serialize() + response += self.body + return response + + +def parse_line( + read_line: Callable[[int], Generator[None, None, bytes]], +) -> Generator[None, None, bytes]: + """ + Parse a single line. + + CRLF is stripped from the return value. + + Args: + read_line: Generator-based coroutine that reads a LF-terminated line + or raises an exception if there isn't enough data. + + Raises: + EOFError: If the connection is closed without a CRLF. + SecurityError: If the response exceeds a security limit. + + """ + try: + line = yield from read_line(MAX_LINE_LENGTH) + except RuntimeError: + raise SecurityError("line too long") + # Not mandatory but safe - https://datatracker.ietf.org/doc/html/rfc7230#section-3.5 + if not line.endswith(b"\r\n"): + raise EOFError("line without CRLF") + return line[:-2] + + +def parse_headers( + read_line: Callable[[int], Generator[None, None, bytes]], +) -> Generator[None, None, Headers]: + """ + Parse HTTP headers. + + Non-ASCII characters are represented with surrogate escapes. + + Args: + read_line: Generator-based coroutine that reads a LF-terminated line + or raises an exception if there isn't enough data. + + Raises: + EOFError: If the connection is closed without complete headers. + SecurityError: If the request exceeds a security limit. + ValueError: If the request isn't well formatted. + + """ + # https://datatracker.ietf.org/doc/html/rfc7230#section-3.2 + + # We don't attempt to support obsolete line folding. + + headers = Headers() + for _ in range(MAX_NUM_HEADERS + 1): + try: + line = yield from parse_line(read_line) + except EOFError as exc: + raise EOFError("connection closed while reading HTTP headers") from exc + if line == b"": + break + + try: + raw_name, raw_value = line.split(b":", 1) + except ValueError: # not enough values to unpack (expected 2, got 1) + raise ValueError(f"invalid HTTP header line: {d(line)}") from None + if not _token_re.fullmatch(raw_name): + raise ValueError(f"invalid HTTP header name: {d(raw_name)}") + raw_value = raw_value.strip(b" \t") + if not _value_re.fullmatch(raw_value): + raise ValueError(f"invalid HTTP header value: {d(raw_value)}") + + name = raw_name.decode("ascii") # guaranteed to be ASCII at this point + value = raw_value.decode("ascii", "surrogateescape") + headers[name] = value + + else: + raise SecurityError("too many HTTP headers") + + return headers + + +def read_body( + status_code: int, + headers: Headers, + read_line: Callable[[int], Generator[None, None, bytes]], + read_exact: Callable[[int], Generator[None, None, bytes]], + read_to_eof: Callable[[int], Generator[None, None, bytes]], +) -> Generator[None, None, bytes]: + # https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.3 + + # Since websockets only does GET requests (no HEAD, no CONNECT), all + # responses except 1xx, 204, and 304 include a message body. + if 100 <= status_code < 200 or status_code == 204 or status_code == 304: + return b"" + + # MultipleValuesError is sufficiently unlikely that we don't attempt to + # handle it when accessing headers. Instead we document that its parent + # class, LookupError, may be raised. + # Conversions from str to int are protected by sys.set_int_max_str_digits.. + + elif (coding := headers.get("Transfer-Encoding")) is not None: + if coding != "chunked": + raise NotImplementedError(f"transfer coding {coding} isn't supported") + + body = b"" + while True: + chunk_size_line = yield from parse_line(read_line) + raw_chunk_size = chunk_size_line.split(b";", 1)[0] + # Set a lower limit than default_max_str_digits; 1 EB is plenty. + if len(raw_chunk_size) > 15: + str_chunk_size = raw_chunk_size.decode(errors="backslashreplace") + raise SecurityError(f"chunk too large: 0x{str_chunk_size} bytes") + chunk_size = int(raw_chunk_size, 16) + if chunk_size == 0: + break + if len(body) + chunk_size > MAX_BODY_SIZE: + raise SecurityError( + f"chunk too large: {chunk_size} bytes after {len(body)} bytes" + ) + body += yield from read_exact(chunk_size) + if (yield from read_exact(2)) != b"\r\n": + raise ValueError("chunk without CRLF") + # Read the trailer. + yield from parse_headers(read_line) + return body + + elif (raw_content_length := headers.get("Content-Length")) is not None: + # Set a lower limit than default_max_str_digits; 1 EiB is plenty. + if len(raw_content_length) > 18: + raise SecurityError(f"body too large: {raw_content_length} bytes") + content_length = int(raw_content_length) + if content_length > MAX_BODY_SIZE: + raise SecurityError(f"body too large: {content_length} bytes") + return (yield from read_exact(content_length)) + + else: + try: + return (yield from read_to_eof(MAX_BODY_SIZE)) + except RuntimeError: + raise SecurityError(f"body too large: over {MAX_BODY_SIZE} bytes") diff --git a/gestao_raul/Lib/site-packages/websockets/imports.py b/gestao_raul/Lib/site-packages/websockets/imports.py new file mode 100644 index 0000000..c63fb21 --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/imports.py @@ -0,0 +1,100 @@ +from __future__ import annotations + +import warnings +from collections.abc import Iterable +from typing import Any + + +__all__ = ["lazy_import"] + + +def import_name(name: str, source: str, namespace: dict[str, Any]) -> Any: + """ + Import ``name`` from ``source`` in ``namespace``. + + There are two use cases: + + - ``name`` is an object defined in ``source``; + - ``name`` is a submodule of ``source``. + + Neither :func:`__import__` nor :func:`~importlib.import_module` does + exactly this. :func:`__import__` is closer to the intended behavior. + + """ + level = 0 + while source[level] == ".": + level += 1 + assert level < len(source), "importing from parent isn't supported" + module = __import__(source[level:], namespace, None, [name], level) + return getattr(module, name) + + +def lazy_import( + namespace: dict[str, Any], + aliases: dict[str, str] | None = None, + deprecated_aliases: dict[str, str] | None = None, +) -> None: + """ + Provide lazy, module-level imports. + + Typical use:: + + __getattr__, __dir__ = lazy_import( + globals(), + aliases={ + "": "", + ... + }, + deprecated_aliases={ + ..., + } + ) + + This function defines ``__getattr__`` and ``__dir__`` per :pep:`562`. + + """ + if aliases is None: + aliases = {} + if deprecated_aliases is None: + deprecated_aliases = {} + + namespace_set = set(namespace) + aliases_set = set(aliases) + deprecated_aliases_set = set(deprecated_aliases) + + assert not namespace_set & aliases_set, "namespace conflict" + assert not namespace_set & deprecated_aliases_set, "namespace conflict" + assert not aliases_set & deprecated_aliases_set, "namespace conflict" + + package = namespace["__name__"] + + def __getattr__(name: str) -> Any: + assert aliases is not None # mypy cannot figure this out + try: + source = aliases[name] + except KeyError: + pass + else: + return import_name(name, source, namespace) + + assert deprecated_aliases is not None # mypy cannot figure this out + try: + source = deprecated_aliases[name] + except KeyError: + pass + else: + warnings.warn( + f"{package}.{name} is deprecated", + DeprecationWarning, + stacklevel=2, + ) + return import_name(name, source, namespace) + + raise AttributeError(f"module {package!r} has no attribute {name!r}") + + namespace["__getattr__"] = __getattr__ + + def __dir__() -> Iterable[str]: + return sorted(namespace_set | aliases_set | deprecated_aliases_set) + + namespace["__dir__"] = __dir__ diff --git a/gestao_raul/Lib/site-packages/websockets/legacy/__init__.py b/gestao_raul/Lib/site-packages/websockets/legacy/__init__.py new file mode 100644 index 0000000..ad9aa25 --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/legacy/__init__.py @@ -0,0 +1,11 @@ +from __future__ import annotations + +import warnings + + +warnings.warn( # deprecated in 14.0 - 2024-11-09 + "websockets.legacy is deprecated; " + "see https://websockets.readthedocs.io/en/stable/howto/upgrade.html " + "for upgrade instructions", + DeprecationWarning, +) diff --git a/gestao_raul/Lib/site-packages/websockets/legacy/__pycache__/__init__.cpython-310.pyc b/gestao_raul/Lib/site-packages/websockets/legacy/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..437de6c774c7b5fce22303d178b9e4e1a2cddf62 GIT binary patch literal 421 zcmYjOyH3L}6t&$TqS7wRNMvB?K)e@(5G@;GC_+e8B+HF&6RU|G*^Z(jJ_bg9goQuI z%ET`q2AsA?y~EMDFWqx=^>ElnK0Z%AvQv!ESC{NZ;FD{Fus!ocAb|^1;yK10Ck2@k zAg?{qgW!$$8hJpY;Je!&;r56tr5$%tD|`R^(!j#%bOp}F1!O#3N76c-0#eCDN;`3bP3MR&gIORGw1T1 z38$ya3Z8$T{d#-l8AbUIeT+W_KHgFkHToJAS8-Ke>8PfvQeE>kQg&W*3Rd`EBhJVbP+}!kTUsBdM!(-eQ#O?yCDsONtnX{#S=2PD zZ7L77^>?a9Qo7mi^0$QuMN*c}{A*N&o{ zP`D2jaTZ;NAFkH7ZlO2Y+Tng2Ze0(ZUI)^Swl+4dy}i1%%|S1;gx&ME-t(GUkr(re zUE8^DqX%=EkrZom$>-a))4xQARqOVX885ON!Cg8y+m9BpR#ZWusTFlj&8ym@MO~an zgM_mf&=sf2dS0PY0XXf2Jz6$9VETi~W%{}{>(o)?y zcm80;EI@Y`-ILJpqI+JpKjWS`sFNm zGaj@;;qZu&Zl#*TNOfvuIXh9rY0ye?g4=#){jiJ^U<0-u23){B;OqpW{&}a(o%`d` zNeo+TM^cT%^B8itoEglWJ0P;mP~2T+BMZAjDt7noka&{6za!_LAnFFKH&Q>cG!iJe zqFEB^i>M4B(w-^`19hNS%0NS@#@fDePao)>GBCbU|0JCh)JN$Ljj}@p>eMF8p%RE5+xblvAY7^r#Kk@kZtY>Vw4e9xgtymA6_v($CqQ@J| zYr%pzYukYvwe9DoLD=Z`3~GhI@OD_@h#i)*^U>tY5&p+U%C7e zgLFeN#tvMhDyleh_`hYmr1hnGLOyRy`JTroIb^AP+9&0?1=Y}YuRKxt9ybM}Ysc+m zR`8E{@R(LRjckJ4{^5!7STr=+31!V=6si9{DeFH_`BX)S&|LMjvZo_NoT3oXKbMAw zSiPs+gDZ>;D0FTgfn!^B^_HGT;GnRvTa5DqeP2a@G4^u9*@gbqADC?%&4y_d2gV>L zO(SU+pv%&TIdPU^7_v!a z0*?4GZeGbWx!Y01-<`XC`}Rd@%8W(EncsL3!r=C}#g?O|A=O&}{8cv;F^A?I#isJQ zoHj%t6Ft>RQhb;9)97lnN5UIY>ZN7%j_wkQtmiCEO zsvxlDwHZ}Yi{ELFw8GrP_d+45-f?RwW zz)n!@XDA-#Q}!t+rq(BGvY-4lHZS)rTWK58T7>L>iTbA~vJh;stfXvNozU(1RIgap zM?Kpg_7p734IRW3hB?WsWsLz-v`XPqkV%;(xh2!XK;&@W&w`N@trW79FQEdeM5j=c z6+^A$09^{2s^PuxZMkylq+C#t9FnOqbvk@;^+>m1?HL%pGuE7i?jr#vvmikLH-j99 zuZ}Q4tyWupB2djp9$xxC=Q$K6vxm-?{yKRgN?U*x)E+PbmG?DKi4iLI)!(bKT@~-e zsO_ubObmdE8hp{75xfB3q{g{@P1NH&dUCOT8kF`ibI*tiuI?HK@E+Hdeawws2hH3- z{fqK9NjWG?e#h`f`|2MQcUA&8q%jATJ#C;9M`9x`?de#d>`uoeN^0QwG_*|Js|@r3 z@jdfLc&h50J%epYm-N99#IqrY|54bk-s%NTy@BZKIp3cPAjn0`<&n1?z`evFLVe7) z1&&k(EV=ZM%I}U~b1j9NuEbbFd019GZ;P=_e=IYuSbK15*7KY4av;c9}k_W*+$jf@+xK%u+Q z$e5xAv7IK36I(cn@Da0FT|!=%5K@;$jlBw1t$6WoHP=;|{gX-bu6e)Zo+JNGeL^b8GAxDSr#eZoDiFa72uVLS3?5 zJSl!+hY;WJWCic~w!_^CV$D8;l4SxZ(;K!I@i7!VO7iP$Ed|hzi;E=qJtE3Z1Uyfy zx+B71dkB@+cP|Yk$T%SNu^b0E#$$w?@mmc_EfFS^h(ZOyt521Ia<{Oj#^mbN4{?+6 z5pK9P6>$NbK;)F|<7P&yT}}+#B6LzTGlDcp>FPEQ_{UxG=2t|P!jksMDP_0z1756l z)ejvc3UAg%lXuU1>0Q#eW{*^~&W?i;i8o~aNIS|N+8IIeKhy3AoDd}c96JOgr(2t{ zgyi$bZfw*qa8ol-zeF}EPGS-gkeBgFjCRL%k{KuL;Npr~u1D7@=#=1u5?FH{8;7gm zu9exXF|UblidtC^5(jB|7f+5<_xGz)4U-lawfC#xVCNDPY@l4^+(wM*VM5 zK|2uFP)zQV2&Vh|H4TtxY#y}-_(yb8F!w@HEowz|MlGug_yO}in$e5ej8-fds%BJF z%GOWcW7Do#*w9YysVVbN9( zcYVO3f^#}4nx3s#v-B!w)il$i!48A(0`4uX_G%u);BFb)GKIT!bEtst+Y#Jki@_9t(< z1c`IZ`i@Mncr^vtZ<3)$gy|~^iMoh8yG6C4(cRsgTGT6AS)VK623rbw?%V$YhBqs{ literal 0 HcmV?d00001 diff --git a/gestao_raul/Lib/site-packages/websockets/legacy/__pycache__/client.cpython-310.pyc b/gestao_raul/Lib/site-packages/websockets/legacy/__pycache__/client.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e8b25d41992628e526f8e5a4d0dc8b853e3fd6d8 GIT binary patch literal 19876 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 m)X_{)eMBwh_CpV+e=N|aMpMc+ly3~1lp6;Ql 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 literal 0 HcmV?d00001 diff --git a/gestao_raul/Lib/site-packages/websockets/legacy/__pycache__/http.cpython-310.pyc b/gestao_raul/Lib/site-packages/websockets/legacy/__pycache__/http.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..23cec3e267eb69796f151bf52e306607e9e2a3d9 GIT binary patch literal 5196 zcmdT|O>7&-72erhE|=7gZP~HopCp?<$4o56G6K{lf};MBW1)^yNOls?w8<4`Br;m= zGP6U=BuK-y+G8)h76qhZuRZn}peWEo?|bc~KK0<64{^UYyOb%}4$=ZeTM9dxoq6+S z=FNNGd~c(vsiJ}3UrzjK?am3q_#3^9|8jWwnPIT_52$d1v%qMx3S(3^1G8f4|5nA) z|Luy+Ep7+7wo`E+WAR*&Z@U#2b%*EI%t}ENh4szE)+Bd%VcXnfm8qSUBA?JL)BG*I zpC9<8UD+q>t^Isvn^g|z+CkK2xW|uh2fYXR+x#fcqjreD!;kY5sJ+JD zQ`uYG!DvxA_SM40tmxr)Su`RpJm1sWlx&4--fAShR;YyRL;-e<7Q98CA9{i|=(R;0`)gvZs5K<} zSNz6$9Qbilica7+1We*+NivSZ^$aB@U992vhTMmLPrtlWuHKAczUqbuR8+kjHM(sP zs<^tebaCNQbq%KTqnh-)LG@Z|r5d-CnC)O5%u8cd;;6AMRQz!u*8E2At=GN--FA#U$F z1==))$#--Le@5jY>lYNxVv{Z6G_D#$_LUJHunom}$C)uSnbFTFoYE#6n2$`(hUR^9 zgKZf7i2>U(DMYr-sx`ER_Ar-?w0h^5L4OKCqwE3OWWP0j2MwG(GB=EY#hrCaUO{gj zz1FDr5gX*Vi=Jil6ws3!>#?_TTnE+AQO?+E^8;s)U#0Mv^q;`Y+6BlMh)6{Uo}sA%`A56^~{Otb?T_sy;dB4puC$)*Je|{;T{|g z4&UOw5*&^QeLZi(@9iB()70ykVkq*Z@Wj22XwWctZzBqokKs`m^t=npmlhYjM$?x* z`ei()xABE)3OUxQb-<^kh~b)0v(ZvbFYY1;*035+#0|eAFj$W+uXe*mxt@i)4*t5w zBcV6sD<#?;MGGMT4>Ro;XeHu3Z$&ixZY;E>uf)o-c5knVUByj*eMdNkMlw*-zbmj6 zG3PC=?oyU*GxmI~igcg$TFPttG)Fr5s*KvORqEJes#VwkT3RuzB%>}uo+RnWMw`1J z*W$9)NT899EPIP#r~C3f(G;0omi$&M#s)8ZaYf@KhNnx%l0zI5&=sfeHG(M4OpTQQ zm{z+%pgn73u=KEK9lLOMN8iZt#Jz?P9G39n44=KA)o!Wh zb~=$Hztig4LNy~^_MQ-=j3Vvnu@~g-!k=NwvX|uixY24Q`*u7wb=XoqDQGXHg(dFj zJd#}830f*CKDUYnP&tkHvP*3gx2~K)V+A*z_Vb$7WWnHxlP)uHrN2?5m`?K9f)YD@ z&2;E{AT9Pomvm6f;)ze9FdvvFX4o_L*c*H=P2z)&-p zJ-CkRT6N={sF|!)cw~XJ6p$p2(we|&q4Q9 zpq;Ja-$d+odazfFuRF zJ8^e4ni|CAn-D4$QtY0{B9@?AzC|kDK~dR9xSe&;awc)f!s{saZU{OGH|+ys=T(0G^}yH(Jr`?W&OivDp*?Q9jf zyA7mQ6OeL%&FhdVK*||mJW+Dww?#;ym1>mN4&8w}V1xXp#x&CV^iokY}B#axSG_5@<$w@z46Un|ksrRcxs!&tW zx(E>gvIO-YNyw2q@)XTS#7NRtjx45hC?u?w*gHX$@*vtsr9gqC*PGbXp!4CqfkP4c z8$4OyI1~GsiJ%xUA=fmSebD^Jp)6>MI%p_tismG-B}>QnuE3#Om{_F_oQtSDWCLT% z*fvJ_r(%>bDs$bIGaw7d8{vj*3qb-GJ+5*b6ogvv?I8Cn^QZxW;Ph`jcRuiIa9*%q z+Vv~E@6yt>E5z=#^*W%m+DX$aNT-I{Fe6v(Pia+jl0g)%X%Cnx_7CV*O2Q~JAeE25 zfA;KI$$^y-1=O2Zfe6#%9s6*i4=Hg4k|`Rkv9n}q9F&AmdryG?_K$dCI$`s{v^#0K zu&@iOYfmZJ>ERJ3NurjLkO{5Cisiwb)l47m4+J&7&ttRO_ zEBWkeL}Es9VoFh&Mtw05@5t0ib=zT Y^XPNAGHelmUPpJ{5JOaK4? literal 0 HcmV?d00001 diff --git a/gestao_raul/Lib/site-packages/websockets/legacy/__pycache__/protocol.cpython-310.pyc b/gestao_raul/Lib/site-packages/websockets/legacy/__pycache__/protocol.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7e21bedc4319e37b4da04cc1c631cd98480d411d GIT binary patch literal 41785 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⁣3S9))yMQK&u66c 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 BBMATi literal 0 HcmV?d00001 diff --git a/gestao_raul/Lib/site-packages/websockets/legacy/auth.py b/gestao_raul/Lib/site-packages/websockets/legacy/auth.py new file mode 100644 index 0000000..a262fcd --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/legacy/auth.py @@ -0,0 +1,190 @@ +from __future__ import annotations + +import functools +import hmac +import http +from collections.abc import Awaitable, Iterable +from typing import Any, Callable, cast + +from ..datastructures import Headers +from ..exceptions import InvalidHeader +from ..headers import build_www_authenticate_basic, parse_authorization_basic +from .server import HTTPResponse, WebSocketServerProtocol + + +__all__ = ["BasicAuthWebSocketServerProtocol", "basic_auth_protocol_factory"] + +Credentials = tuple[str, str] + + +def is_credentials(value: Any) -> bool: + try: + username, password = value + except (TypeError, ValueError): + return False + else: + return isinstance(username, str) and isinstance(password, str) + + +class BasicAuthWebSocketServerProtocol(WebSocketServerProtocol): + """ + WebSocket server protocol that enforces HTTP Basic Auth. + + """ + + realm: str = "" + """ + Scope of protection. + + If provided, it should contain only ASCII characters because the + encoding of non-ASCII characters is undefined. + """ + + username: str | None = None + """Username of the authenticated user.""" + + def __init__( + self, + *args: Any, + realm: str | None = None, + check_credentials: Callable[[str, str], Awaitable[bool]] | None = None, + **kwargs: Any, + ) -> None: + if realm is not None: + self.realm = realm # shadow class attribute + self._check_credentials = check_credentials + super().__init__(*args, **kwargs) + + async def check_credentials(self, username: str, password: str) -> bool: + """ + Check whether credentials are authorized. + + This coroutine may be overridden in a subclass, for example to + authenticate against a database or an external service. + + Args: + username: HTTP Basic Auth username. + password: HTTP Basic Auth password. + + Returns: + :obj:`True` if the handshake should continue; + :obj:`False` if it should fail with an HTTP 401 error. + + """ + if self._check_credentials is not None: + return await self._check_credentials(username, password) + + return False + + async def process_request( + self, + path: str, + request_headers: Headers, + ) -> HTTPResponse | None: + """ + Check HTTP Basic Auth and return an HTTP 401 response if needed. + + """ + try: + authorization = request_headers["Authorization"] + except KeyError: + return ( + http.HTTPStatus.UNAUTHORIZED, + [("WWW-Authenticate", build_www_authenticate_basic(self.realm))], + b"Missing credentials\n", + ) + + try: + username, password = parse_authorization_basic(authorization) + except InvalidHeader: + return ( + http.HTTPStatus.UNAUTHORIZED, + [("WWW-Authenticate", build_www_authenticate_basic(self.realm))], + b"Unsupported credentials\n", + ) + + if not await self.check_credentials(username, password): + return ( + http.HTTPStatus.UNAUTHORIZED, + [("WWW-Authenticate", build_www_authenticate_basic(self.realm))], + b"Invalid credentials\n", + ) + + self.username = username + + return await super().process_request(path, request_headers) + + +def basic_auth_protocol_factory( + realm: str | None = None, + credentials: Credentials | Iterable[Credentials] | None = None, + check_credentials: Callable[[str, str], Awaitable[bool]] | None = None, + create_protocol: Callable[..., BasicAuthWebSocketServerProtocol] | None = None, +) -> Callable[..., BasicAuthWebSocketServerProtocol]: + """ + Protocol factory that enforces HTTP Basic Auth. + + :func:`basic_auth_protocol_factory` is designed to integrate with + :func:`~websockets.legacy.server.serve` like this:: + + serve( + ..., + create_protocol=basic_auth_protocol_factory( + realm="my dev server", + credentials=("hello", "iloveyou"), + ) + ) + + Args: + realm: Scope of protection. It should contain only ASCII characters + because the encoding of non-ASCII characters is undefined. + Refer to section 2.2 of :rfc:`7235` for details. + credentials: Hard coded authorized credentials. It can be a + ``(username, password)`` pair or a list of such pairs. + check_credentials: Coroutine that verifies credentials. + It receives ``username`` and ``password`` arguments + and returns a :class:`bool`. One of ``credentials`` or + ``check_credentials`` must be provided but not both. + create_protocol: Factory that creates the protocol. By default, this + is :class:`BasicAuthWebSocketServerProtocol`. It can be replaced + by a subclass. + Raises: + TypeError: If the ``credentials`` or ``check_credentials`` argument is + wrong. + + """ + if (credentials is None) == (check_credentials is None): + raise TypeError("provide either credentials or check_credentials") + + if credentials is not None: + if is_credentials(credentials): + credentials_list = [cast(Credentials, credentials)] + elif isinstance(credentials, Iterable): + credentials_list = list(cast(Iterable[Credentials], credentials)) + if not all(is_credentials(item) for item in credentials_list): + raise TypeError(f"invalid credentials argument: {credentials}") + else: + raise TypeError(f"invalid credentials argument: {credentials}") + + credentials_dict = dict(credentials_list) + + async def check_credentials(username: str, password: str) -> bool: + try: + expected_password = credentials_dict[username] + except KeyError: + return False + return hmac.compare_digest(expected_password, password) + + if create_protocol is None: + create_protocol = BasicAuthWebSocketServerProtocol + + # Help mypy and avoid this error: "type[BasicAuthWebSocketServerProtocol] | + # Callable[..., BasicAuthWebSocketServerProtocol]" not callable [misc] + create_protocol = cast( + Callable[..., BasicAuthWebSocketServerProtocol], create_protocol + ) + return functools.partial( + create_protocol, + realm=realm, + check_credentials=check_credentials, + ) diff --git a/gestao_raul/Lib/site-packages/websockets/legacy/client.py b/gestao_raul/Lib/site-packages/websockets/legacy/client.py new file mode 100644 index 0000000..29141f3 --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/legacy/client.py @@ -0,0 +1,705 @@ +from __future__ import annotations + +import asyncio +import functools +import logging +import os +import random +import traceback +import urllib.parse +import warnings +from collections.abc import AsyncIterator, Generator, Sequence +from types import TracebackType +from typing import Any, Callable, cast + +from ..asyncio.compatibility import asyncio_timeout +from ..datastructures import Headers, HeadersLike +from ..exceptions import ( + InvalidHeader, + InvalidHeaderValue, + InvalidMessage, + NegotiationError, + SecurityError, +) +from ..extensions import ClientExtensionFactory, Extension +from ..extensions.permessage_deflate import enable_client_permessage_deflate +from ..headers import ( + build_authorization_basic, + build_extension, + build_host, + build_subprotocol, + parse_extension, + parse_subprotocol, + validate_subprotocols, +) +from ..http11 import USER_AGENT +from ..typing import ExtensionHeader, LoggerLike, Origin, Subprotocol +from ..uri import WebSocketURI, parse_uri +from .exceptions import InvalidStatusCode, RedirectHandshake +from .handshake import build_request, check_response +from .http import read_response +from .protocol import WebSocketCommonProtocol + + +__all__ = ["connect", "unix_connect", "WebSocketClientProtocol"] + + +class WebSocketClientProtocol(WebSocketCommonProtocol): + """ + WebSocket client connection. + + :class:`WebSocketClientProtocol` provides :meth:`recv` and :meth:`send` + coroutines for receiving and sending messages. + + It supports asynchronous iteration to receive messages:: + + async for message in websocket: + await process(message) + + The iterator exits normally when the connection is closed with close code + 1000 (OK) or 1001 (going away) or without a close code. It raises + a :exc:`~websockets.exceptions.ConnectionClosedError` when the connection + is closed with any other code. + + See :func:`connect` for the documentation of ``logger``, ``origin``, + ``extensions``, ``subprotocols``, ``extra_headers``, and + ``user_agent_header``. + + See :class:`~websockets.legacy.protocol.WebSocketCommonProtocol` for the + documentation of ``ping_interval``, ``ping_timeout``, ``close_timeout``, + ``max_size``, ``max_queue``, ``read_limit``, and ``write_limit``. + + """ + + is_client = True + side = "client" + + def __init__( + self, + *, + logger: LoggerLike | None = None, + origin: Origin | None = None, + extensions: Sequence[ClientExtensionFactory] | None = None, + subprotocols: Sequence[Subprotocol] | None = None, + extra_headers: HeadersLike | None = None, + user_agent_header: str | None = USER_AGENT, + **kwargs: Any, + ) -> None: + if logger is None: + logger = logging.getLogger("websockets.client") + super().__init__(logger=logger, **kwargs) + self.origin = origin + self.available_extensions = extensions + self.available_subprotocols = subprotocols + self.extra_headers = extra_headers + self.user_agent_header = user_agent_header + + def write_http_request(self, path: str, headers: Headers) -> None: + """ + Write request line and headers to the HTTP request. + + """ + self.path = path + self.request_headers = headers + + if self.debug: + self.logger.debug("> GET %s HTTP/1.1", path) + for key, value in headers.raw_items(): + self.logger.debug("> %s: %s", key, value) + + # Since the path and headers only contain ASCII characters, + # we can keep this simple. + request = f"GET {path} HTTP/1.1\r\n" + request += str(headers) + + self.transport.write(request.encode()) + + async def read_http_response(self) -> tuple[int, Headers]: + """ + Read status line and headers from the HTTP response. + + If the response contains a body, it may be read from ``self.reader`` + after this coroutine returns. + + Raises: + InvalidMessage: If the HTTP message is malformed or isn't an + HTTP/1.1 GET response. + + """ + try: + status_code, reason, headers = await read_response(self.reader) + except Exception as exc: + raise InvalidMessage("did not receive a valid HTTP response") from exc + + if self.debug: + self.logger.debug("< HTTP/1.1 %d %s", status_code, reason) + for key, value in headers.raw_items(): + self.logger.debug("< %s: %s", key, value) + + self.response_headers = headers + + return status_code, self.response_headers + + @staticmethod + def process_extensions( + headers: Headers, + available_extensions: Sequence[ClientExtensionFactory] | None, + ) -> list[Extension]: + """ + Handle the Sec-WebSocket-Extensions HTTP response header. + + Check that each extension is supported, as well as its parameters. + + Return the list of accepted extensions. + + Raise :exc:`~websockets.exceptions.InvalidHandshake` to abort the + connection. + + :rfc:`6455` leaves the rules up to the specification of each + :extension. + + To provide this level of flexibility, for each extension accepted by + the server, we check for a match with each extension available in the + client configuration. If no match is found, an exception is raised. + + If several variants of the same extension are accepted by the server, + it may be configured several times, which won't make sense in general. + Extensions must implement their own requirements. For this purpose, + the list of previously accepted extensions is provided. + + Other requirements, for example related to mandatory extensions or the + order of extensions, may be implemented by overriding this method. + + """ + accepted_extensions: list[Extension] = [] + + header_values = headers.get_all("Sec-WebSocket-Extensions") + + if header_values: + if available_extensions is None: + raise NegotiationError("no extensions supported") + + parsed_header_values: list[ExtensionHeader] = sum( + [parse_extension(header_value) for header_value in header_values], [] + ) + + for name, response_params in parsed_header_values: + for extension_factory in available_extensions: + # Skip non-matching extensions based on their name. + if extension_factory.name != name: + continue + + # Skip non-matching extensions based on their params. + try: + extension = extension_factory.process_response_params( + response_params, accepted_extensions + ) + except NegotiationError: + continue + + # Add matching extension to the final list. + accepted_extensions.append(extension) + + # Break out of the loop once we have a match. + break + + # If we didn't break from the loop, no extension in our list + # matched what the server sent. Fail the connection. + else: + raise NegotiationError( + f"Unsupported extension: " + f"name = {name}, params = {response_params}" + ) + + return accepted_extensions + + @staticmethod + def process_subprotocol( + headers: Headers, available_subprotocols: Sequence[Subprotocol] | None + ) -> Subprotocol | None: + """ + Handle the Sec-WebSocket-Protocol HTTP response header. + + Check that it contains exactly one supported subprotocol. + + Return the selected subprotocol. + + """ + subprotocol: Subprotocol | None = None + + header_values = headers.get_all("Sec-WebSocket-Protocol") + + if header_values: + if available_subprotocols is None: + raise NegotiationError("no subprotocols supported") + + parsed_header_values: Sequence[Subprotocol] = sum( + [parse_subprotocol(header_value) for header_value in header_values], [] + ) + + if len(parsed_header_values) > 1: + raise InvalidHeaderValue( + "Sec-WebSocket-Protocol", + f"multiple values: {', '.join(parsed_header_values)}", + ) + + subprotocol = parsed_header_values[0] + + if subprotocol not in available_subprotocols: + raise NegotiationError(f"unsupported subprotocol: {subprotocol}") + + return subprotocol + + async def handshake( + self, + wsuri: WebSocketURI, + origin: Origin | None = None, + available_extensions: Sequence[ClientExtensionFactory] | None = None, + available_subprotocols: Sequence[Subprotocol] | None = None, + extra_headers: HeadersLike | None = None, + ) -> None: + """ + Perform the client side of the opening handshake. + + Args: + wsuri: URI of the WebSocket server. + origin: Value of the ``Origin`` header. + extensions: List of supported extensions, in order in which they + should be negotiated and run. + subprotocols: List of supported subprotocols, in order of decreasing + preference. + extra_headers: Arbitrary HTTP headers to add to the handshake request. + + Raises: + InvalidHandshake: If the handshake fails. + + """ + request_headers = Headers() + + request_headers["Host"] = build_host(wsuri.host, wsuri.port, wsuri.secure) + + if wsuri.user_info: + request_headers["Authorization"] = build_authorization_basic( + *wsuri.user_info + ) + + if origin is not None: + request_headers["Origin"] = origin + + key = build_request(request_headers) + + if available_extensions is not None: + extensions_header = build_extension( + [ + (extension_factory.name, extension_factory.get_request_params()) + for extension_factory in available_extensions + ] + ) + request_headers["Sec-WebSocket-Extensions"] = extensions_header + + if available_subprotocols is not None: + protocol_header = build_subprotocol(available_subprotocols) + request_headers["Sec-WebSocket-Protocol"] = protocol_header + + if self.extra_headers is not None: + request_headers.update(self.extra_headers) + + if self.user_agent_header: + request_headers.setdefault("User-Agent", self.user_agent_header) + + self.write_http_request(wsuri.resource_name, request_headers) + + status_code, response_headers = await self.read_http_response() + if status_code in (301, 302, 303, 307, 308): + if "Location" not in response_headers: + raise InvalidHeader("Location") + raise RedirectHandshake(response_headers["Location"]) + elif status_code != 101: + raise InvalidStatusCode(status_code, response_headers) + + check_response(response_headers, key) + + self.extensions = self.process_extensions( + response_headers, available_extensions + ) + + self.subprotocol = self.process_subprotocol( + response_headers, available_subprotocols + ) + + self.connection_open() + + +class Connect: + """ + Connect to the WebSocket server at ``uri``. + + Awaiting :func:`connect` yields a :class:`WebSocketClientProtocol` which + can then be used to send and receive messages. + + :func:`connect` can be used as a asynchronous context manager:: + + async with connect(...) as websocket: + ... + + The connection is closed automatically when exiting the context. + + :func:`connect` can be used as an infinite asynchronous iterator to + reconnect automatically on errors:: + + async for websocket in connect(...): + try: + ... + except websockets.exceptions.ConnectionClosed: + continue + + The connection is closed automatically after each iteration of the loop. + + If an error occurs while establishing the connection, :func:`connect` + retries with exponential backoff. The backoff delay starts at three + seconds and increases up to one minute. + + If an error occurs in the body of the loop, you can handle the exception + and :func:`connect` will reconnect with the next iteration; or you can + let the exception bubble up and break out of the loop. This lets you + decide which errors trigger a reconnection and which errors are fatal. + + Args: + uri: URI of the WebSocket server. + create_protocol: Factory for the :class:`asyncio.Protocol` managing + the connection. It defaults to :class:`WebSocketClientProtocol`. + Set it to a wrapper or a subclass to customize connection handling. + logger: Logger for this client. + It defaults to ``logging.getLogger("websockets.client")``. + See the :doc:`logging guide <../../topics/logging>` for details. + compression: The "permessage-deflate" extension is enabled by default. + Set ``compression`` to :obj:`None` to disable it. See the + :doc:`compression guide <../../topics/compression>` for details. + origin: Value of the ``Origin`` header, for servers that require it. + extensions: List of supported extensions, in order in which they + should be negotiated and run. + subprotocols: List of supported subprotocols, in order of decreasing + preference. + extra_headers: Arbitrary HTTP headers to add to the handshake request. + user_agent_header: Value of the ``User-Agent`` request header. + It defaults to ``"Python/x.y.z websockets/X.Y"``. + Setting it to :obj:`None` removes the header. + open_timeout: Timeout for opening the connection in seconds. + :obj:`None` disables the timeout. + + See :class:`~websockets.legacy.protocol.WebSocketCommonProtocol` for the + documentation of ``ping_interval``, ``ping_timeout``, ``close_timeout``, + ``max_size``, ``max_queue``, ``read_limit``, and ``write_limit``. + + Any other keyword arguments are passed the event loop's + :meth:`~asyncio.loop.create_connection` method. + + For example: + + * You can set ``ssl`` to a :class:`~ssl.SSLContext` to enforce TLS + settings. When connecting to a ``wss://`` URI, if ``ssl`` isn't + provided, a TLS context is created + with :func:`~ssl.create_default_context`. + + * You can set ``host`` and ``port`` to connect to a different host and + port from those found in ``uri``. This only changes the destination of + the TCP connection. The host name from ``uri`` is still used in the TLS + handshake for secure connections and in the ``Host`` header. + + Raises: + InvalidURI: If ``uri`` isn't a valid WebSocket URI. + OSError: If the TCP connection fails. + InvalidHandshake: If the opening handshake fails. + ~asyncio.TimeoutError: If the opening handshake times out. + + """ + + MAX_REDIRECTS_ALLOWED = int(os.environ.get("WEBSOCKETS_MAX_REDIRECTS", "10")) + + def __init__( + self, + uri: str, + *, + create_protocol: Callable[..., WebSocketClientProtocol] | None = None, + logger: LoggerLike | None = None, + compression: str | None = "deflate", + origin: Origin | None = None, + extensions: Sequence[ClientExtensionFactory] | None = None, + subprotocols: Sequence[Subprotocol] | None = None, + extra_headers: HeadersLike | None = None, + user_agent_header: str | None = USER_AGENT, + open_timeout: float | None = 10, + ping_interval: float | None = 20, + ping_timeout: float | None = 20, + close_timeout: float | None = None, + max_size: int | None = 2**20, + max_queue: int | None = 2**5, + read_limit: int = 2**16, + write_limit: int = 2**16, + **kwargs: Any, + ) -> None: + # Backwards compatibility: close_timeout used to be called timeout. + timeout: float | None = kwargs.pop("timeout", None) + if timeout is None: + timeout = 10 + else: + warnings.warn("rename timeout to close_timeout", DeprecationWarning) + # If both are specified, timeout is ignored. + if close_timeout is None: + close_timeout = timeout + + # Backwards compatibility: create_protocol used to be called klass. + klass: type[WebSocketClientProtocol] | None = kwargs.pop("klass", None) + if klass is None: + klass = WebSocketClientProtocol + else: + warnings.warn("rename klass to create_protocol", DeprecationWarning) + # If both are specified, klass is ignored. + if create_protocol is None: + create_protocol = klass + + # Backwards compatibility: recv() used to return None on closed connections + legacy_recv: bool = kwargs.pop("legacy_recv", False) + + # Backwards compatibility: the loop parameter used to be supported. + _loop: asyncio.AbstractEventLoop | None = kwargs.pop("loop", None) + if _loop is None: + loop = asyncio.get_event_loop() + else: + loop = _loop + warnings.warn("remove loop argument", DeprecationWarning) + + wsuri = parse_uri(uri) + if wsuri.secure: + kwargs.setdefault("ssl", True) + elif kwargs.get("ssl") is not None: + raise ValueError( + "connect() received a ssl argument for a ws:// URI, " + "use a wss:// URI to enable TLS" + ) + + if compression == "deflate": + extensions = enable_client_permessage_deflate(extensions) + elif compression is not None: + raise ValueError(f"unsupported compression: {compression}") + + if subprotocols is not None: + validate_subprotocols(subprotocols) + + # Help mypy and avoid this error: "type[WebSocketClientProtocol] | + # Callable[..., WebSocketClientProtocol]" not callable [misc] + create_protocol = cast(Callable[..., WebSocketClientProtocol], create_protocol) + factory = functools.partial( + create_protocol, + logger=logger, + origin=origin, + extensions=extensions, + subprotocols=subprotocols, + extra_headers=extra_headers, + user_agent_header=user_agent_header, + ping_interval=ping_interval, + ping_timeout=ping_timeout, + close_timeout=close_timeout, + max_size=max_size, + max_queue=max_queue, + read_limit=read_limit, + write_limit=write_limit, + host=wsuri.host, + port=wsuri.port, + secure=wsuri.secure, + legacy_recv=legacy_recv, + loop=_loop, + ) + + if kwargs.pop("unix", False): + path: str | None = kwargs.pop("path", None) + create_connection = functools.partial( + loop.create_unix_connection, factory, path, **kwargs + ) + else: + host: str | None + port: int | None + if kwargs.get("sock") is None: + host, port = wsuri.host, wsuri.port + else: + # If sock is given, host and port shouldn't be specified. + host, port = None, None + if kwargs.get("ssl"): + kwargs.setdefault("server_hostname", wsuri.host) + # If host and port are given, override values from the URI. + host = kwargs.pop("host", host) + port = kwargs.pop("port", port) + create_connection = functools.partial( + loop.create_connection, factory, host, port, **kwargs + ) + + self.open_timeout = open_timeout + if logger is None: + logger = logging.getLogger("websockets.client") + self.logger = logger + + # This is a coroutine function. + self._create_connection = create_connection + self._uri = uri + self._wsuri = wsuri + + def handle_redirect(self, uri: str) -> None: + # Update the state of this instance to connect to a new URI. + old_uri = self._uri + old_wsuri = self._wsuri + new_uri = urllib.parse.urljoin(old_uri, uri) + new_wsuri = parse_uri(new_uri) + + # Forbid TLS downgrade. + if old_wsuri.secure and not new_wsuri.secure: + raise SecurityError("redirect from WSS to WS") + + same_origin = ( + old_wsuri.secure == new_wsuri.secure + and old_wsuri.host == new_wsuri.host + and old_wsuri.port == new_wsuri.port + ) + + # Rewrite secure, host, and port for cross-origin redirects. + # This preserves connection overrides with the host and port + # arguments if the redirect points to the same host and port. + if not same_origin: + factory = self._create_connection.args[0] + # Support TLS upgrade. + if not old_wsuri.secure and new_wsuri.secure: + factory.keywords["secure"] = True + self._create_connection.keywords.setdefault("ssl", True) + # Replace secure, host, and port arguments of the protocol factory. + factory = functools.partial( + factory.func, + *factory.args, + **dict(factory.keywords, host=new_wsuri.host, port=new_wsuri.port), + ) + # Replace secure, host, and port arguments of create_connection. + self._create_connection = functools.partial( + self._create_connection.func, + *(factory, new_wsuri.host, new_wsuri.port), + **self._create_connection.keywords, + ) + + # Set the new WebSocket URI. This suffices for same-origin redirects. + self._uri = new_uri + self._wsuri = new_wsuri + + # async for ... in connect(...): + + BACKOFF_INITIAL = float(os.environ.get("WEBSOCKETS_BACKOFF_INITIAL_DELAY", "5")) + BACKOFF_MIN = float(os.environ.get("WEBSOCKETS_BACKOFF_MIN_DELAY", "3.1")) + BACKOFF_MAX = float(os.environ.get("WEBSOCKETS_BACKOFF_MAX_DELAY", "90.0")) + BACKOFF_FACTOR = float(os.environ.get("WEBSOCKETS_BACKOFF_FACTOR", "1.618")) + + async def __aiter__(self) -> AsyncIterator[WebSocketClientProtocol]: + backoff_delay = self.BACKOFF_MIN / self.BACKOFF_FACTOR + while True: + try: + async with self as protocol: + yield protocol + except Exception as exc: + # Add a random initial delay between 0 and 5 seconds. + # See 7.2.3. Recovering from Abnormal Closure in RFC 6455. + if backoff_delay == self.BACKOFF_MIN: + initial_delay = random.random() * self.BACKOFF_INITIAL + self.logger.info( + "connect failed; reconnecting in %.1f seconds: %s", + initial_delay, + # Remove first argument when dropping Python 3.9. + traceback.format_exception_only(type(exc), exc)[0].strip(), + ) + await asyncio.sleep(initial_delay) + else: + self.logger.info( + "connect failed again; retrying in %d seconds: %s", + int(backoff_delay), + # Remove first argument when dropping Python 3.9. + traceback.format_exception_only(type(exc), exc)[0].strip(), + ) + await asyncio.sleep(int(backoff_delay)) + # Increase delay with truncated exponential backoff. + backoff_delay = backoff_delay * self.BACKOFF_FACTOR + backoff_delay = min(backoff_delay, self.BACKOFF_MAX) + continue + else: + # Connection succeeded - reset backoff delay + backoff_delay = self.BACKOFF_MIN + + # async with connect(...) as ...: + + async def __aenter__(self) -> WebSocketClientProtocol: + return await self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + ) -> None: + await self.protocol.close() + + # ... = await connect(...) + + def __await__(self) -> Generator[Any, None, WebSocketClientProtocol]: + # Create a suitable iterator by calling __await__ on a coroutine. + return self.__await_impl__().__await__() + + async def __await_impl__(self) -> WebSocketClientProtocol: + async with asyncio_timeout(self.open_timeout): + for _redirects in range(self.MAX_REDIRECTS_ALLOWED): + _transport, protocol = await self._create_connection() + try: + await protocol.handshake( + self._wsuri, + origin=protocol.origin, + available_extensions=protocol.available_extensions, + available_subprotocols=protocol.available_subprotocols, + extra_headers=protocol.extra_headers, + ) + except RedirectHandshake as exc: + protocol.fail_connection() + await protocol.wait_closed() + self.handle_redirect(exc.uri) + # Avoid leaking a connected socket when the handshake fails. + except (Exception, asyncio.CancelledError): + protocol.fail_connection() + await protocol.wait_closed() + raise + else: + self.protocol = protocol + return protocol + else: + raise SecurityError("too many redirects") + + # ... = yield from connect(...) - remove when dropping Python < 3.10 + + __iter__ = __await__ + + +connect = Connect + + +def unix_connect( + path: str | None = None, + uri: str = "ws://localhost/", + **kwargs: Any, +) -> Connect: + """ + Similar to :func:`connect`, but for connecting to a Unix socket. + + This function builds upon the event loop's + :meth:`~asyncio.loop.create_unix_connection` method. + + It is only available on Unix. + + It's mainly useful for debugging servers listening on Unix sockets. + + Args: + path: File system path to the Unix socket. + uri: URI of the WebSocket server; the host is used in the TLS + handshake for secure connections and in the ``Host`` header. + + """ + return connect(uri=uri, path=path, unix=True, **kwargs) diff --git a/gestao_raul/Lib/site-packages/websockets/legacy/exceptions.py b/gestao_raul/Lib/site-packages/websockets/legacy/exceptions.py new file mode 100644 index 0000000..29a2525 --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/legacy/exceptions.py @@ -0,0 +1,71 @@ +import http + +from .. import datastructures +from ..exceptions import ( + InvalidHandshake, + # InvalidMessage was incorrectly moved here in versions 14.0 and 14.1. + InvalidMessage, # noqa: F401 + ProtocolError as WebSocketProtocolError, # noqa: F401 +) +from ..typing import StatusLike + + +class InvalidStatusCode(InvalidHandshake): + """ + Raised when a handshake response status code is invalid. + + """ + + def __init__(self, status_code: int, headers: datastructures.Headers) -> None: + self.status_code = status_code + self.headers = headers + + def __str__(self) -> str: + return f"server rejected WebSocket connection: HTTP {self.status_code}" + + +class AbortHandshake(InvalidHandshake): + """ + Raised to abort the handshake on purpose and return an HTTP response. + + This exception is an implementation detail. + + The public API is + :meth:`~websockets.legacy.server.WebSocketServerProtocol.process_request`. + + Attributes: + status (~http.HTTPStatus): HTTP status code. + headers (Headers): HTTP response headers. + body (bytes): HTTP response body. + """ + + def __init__( + self, + status: StatusLike, + headers: datastructures.HeadersLike, + body: bytes = b"", + ) -> None: + # If a user passes an int instead of an HTTPStatus, fix it automatically. + self.status = http.HTTPStatus(status) + self.headers = datastructures.Headers(headers) + self.body = body + + def __str__(self) -> str: + return ( + f"HTTP {self.status:d}, {len(self.headers)} headers, {len(self.body)} bytes" + ) + + +class RedirectHandshake(InvalidHandshake): + """ + Raised when a handshake gets redirected. + + This exception is an implementation detail. + + """ + + def __init__(self, uri: str) -> None: + self.uri = uri + + def __str__(self) -> str: + return f"redirect to {self.uri}" diff --git a/gestao_raul/Lib/site-packages/websockets/legacy/framing.py b/gestao_raul/Lib/site-packages/websockets/legacy/framing.py new file mode 100644 index 0000000..add0c6e --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/legacy/framing.py @@ -0,0 +1,225 @@ +from __future__ import annotations + +import struct +from collections.abc import Awaitable, Sequence +from typing import Any, Callable, NamedTuple + +from .. import extensions, frames +from ..exceptions import PayloadTooBig, ProtocolError +from ..frames import BytesLike +from ..typing import Data + + +try: + from ..speedups import apply_mask +except ImportError: + from ..utils import apply_mask + + +class Frame(NamedTuple): + fin: bool + opcode: frames.Opcode + data: bytes + rsv1: bool = False + rsv2: bool = False + rsv3: bool = False + + @property + def new_frame(self) -> frames.Frame: + return frames.Frame( + self.opcode, + self.data, + self.fin, + self.rsv1, + self.rsv2, + self.rsv3, + ) + + def __str__(self) -> str: + return str(self.new_frame) + + def check(self) -> None: + return self.new_frame.check() + + @classmethod + async def read( + cls, + reader: Callable[[int], Awaitable[bytes]], + *, + mask: bool, + max_size: int | None = None, + extensions: Sequence[extensions.Extension] | None = None, + ) -> Frame: + """ + Read a WebSocket frame. + + Args: + reader: Coroutine that reads exactly the requested number of + bytes, unless the end of file is reached. + mask: Whether the frame should be masked i.e. whether the read + happens on the server side. + max_size: Maximum payload size in bytes. + extensions: List of extensions, applied in reverse order. + + Raises: + PayloadTooBig: If the frame exceeds ``max_size``. + ProtocolError: If the frame contains incorrect values. + + """ + + # Read the header. + data = await reader(2) + head1, head2 = struct.unpack("!BB", data) + + # While not Pythonic, this is marginally faster than calling bool(). + fin = True if head1 & 0b10000000 else False + rsv1 = True if head1 & 0b01000000 else False + rsv2 = True if head1 & 0b00100000 else False + rsv3 = True if head1 & 0b00010000 else False + + try: + opcode = frames.Opcode(head1 & 0b00001111) + except ValueError as exc: + raise ProtocolError("invalid opcode") from exc + + if (True if head2 & 0b10000000 else False) != mask: + raise ProtocolError("incorrect masking") + + length = head2 & 0b01111111 + if length == 126: + data = await reader(2) + (length,) = struct.unpack("!H", data) + elif length == 127: + data = await reader(8) + (length,) = struct.unpack("!Q", data) + if max_size is not None and length > max_size: + raise PayloadTooBig(length, max_size) + if mask: + mask_bits = await reader(4) + + # Read the data. + data = await reader(length) + if mask: + data = apply_mask(data, mask_bits) + + new_frame = frames.Frame(opcode, data, fin, rsv1, rsv2, rsv3) + + if extensions is None: + extensions = [] + for extension in reversed(extensions): + new_frame = extension.decode(new_frame, max_size=max_size) + + new_frame.check() + + return cls( + new_frame.fin, + new_frame.opcode, + new_frame.data, + new_frame.rsv1, + new_frame.rsv2, + new_frame.rsv3, + ) + + def write( + self, + write: Callable[[bytes], Any], + *, + mask: bool, + extensions: Sequence[extensions.Extension] | None = None, + ) -> None: + """ + Write a WebSocket frame. + + Args: + frame: Frame to write. + write: Function that writes bytes. + mask: Whether the frame should be masked i.e. whether the write + happens on the client side. + extensions: List of extensions, applied in order. + + Raises: + ProtocolError: If the frame contains incorrect values. + + """ + # The frame is written in a single call to write in order to prevent + # TCP fragmentation. See #68 for details. This also makes it safe to + # send frames concurrently from multiple coroutines. + write(self.new_frame.serialize(mask=mask, extensions=extensions)) + + +def prepare_data(data: Data) -> tuple[int, bytes]: + """ + Convert a string or byte-like object to an opcode and a bytes-like object. + + This function is designed for data frames. + + If ``data`` is a :class:`str`, return ``OP_TEXT`` and a :class:`bytes` + object encoding ``data`` in UTF-8. + + If ``data`` is a bytes-like object, return ``OP_BINARY`` and a bytes-like + object. + + Raises: + TypeError: If ``data`` doesn't have a supported type. + + """ + if isinstance(data, str): + return frames.Opcode.TEXT, data.encode() + elif isinstance(data, BytesLike): + return frames.Opcode.BINARY, data + else: + raise TypeError("data must be str or bytes-like") + + +def prepare_ctrl(data: Data) -> bytes: + """ + Convert a string or byte-like object to bytes. + + This function is designed for ping and pong frames. + + If ``data`` is a :class:`str`, return a :class:`bytes` object encoding + ``data`` in UTF-8. + + If ``data`` is a bytes-like object, return a :class:`bytes` object. + + Raises: + TypeError: If ``data`` doesn't have a supported type. + + """ + if isinstance(data, str): + return data.encode() + elif isinstance(data, BytesLike): + return bytes(data) + else: + raise TypeError("data must be str or bytes-like") + + +# Backwards compatibility with previously documented public APIs +encode_data = prepare_ctrl + +# Backwards compatibility with previously documented public APIs +from ..frames import Close # noqa: E402 F401, I001 + + +def parse_close(data: bytes) -> tuple[int, str]: + """ + Parse the payload from a close frame. + + Returns: + Close code and reason. + + Raises: + ProtocolError: If data is ill-formed. + UnicodeDecodeError: If the reason isn't valid UTF-8. + + """ + close = Close.parse(data) + return close.code, close.reason + + +def serialize_close(code: int, reason: str) -> bytes: + """ + Serialize the payload for a close frame. + + """ + return Close(code, reason).serialize() diff --git a/gestao_raul/Lib/site-packages/websockets/legacy/handshake.py b/gestao_raul/Lib/site-packages/websockets/legacy/handshake.py new file mode 100644 index 0000000..6a7157c --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/legacy/handshake.py @@ -0,0 +1,158 @@ +from __future__ import annotations + +import base64 +import binascii + +from ..datastructures import Headers, MultipleValuesError +from ..exceptions import InvalidHeader, InvalidHeaderValue, InvalidUpgrade +from ..headers import parse_connection, parse_upgrade +from ..typing import ConnectionOption, UpgradeProtocol +from ..utils import accept_key as accept, generate_key + + +__all__ = ["build_request", "check_request", "build_response", "check_response"] + + +def build_request(headers: Headers) -> str: + """ + Build a handshake request to send to the server. + + Update request headers passed in argument. + + Args: + headers: Handshake request headers. + + Returns: + ``key`` that must be passed to :func:`check_response`. + + """ + key = generate_key() + headers["Upgrade"] = "websocket" + headers["Connection"] = "Upgrade" + headers["Sec-WebSocket-Key"] = key + headers["Sec-WebSocket-Version"] = "13" + return key + + +def check_request(headers: Headers) -> str: + """ + Check a handshake request received from the client. + + This function doesn't verify that the request is an HTTP/1.1 or higher GET + request and doesn't perform ``Host`` and ``Origin`` checks. These controls + are usually performed earlier in the HTTP request handling code. They're + the responsibility of the caller. + + Args: + headers: Handshake request headers. + + Returns: + ``key`` that must be passed to :func:`build_response`. + + Raises: + InvalidHandshake: If the handshake request is invalid. + Then, the server must return a 400 Bad Request error. + + """ + connection: list[ConnectionOption] = sum( + [parse_connection(value) for value in headers.get_all("Connection")], [] + ) + + if not any(value.lower() == "upgrade" for value in connection): + raise InvalidUpgrade("Connection", ", ".join(connection)) + + upgrade: list[UpgradeProtocol] = sum( + [parse_upgrade(value) for value in headers.get_all("Upgrade")], [] + ) + + # For compatibility with non-strict implementations, ignore case when + # checking the Upgrade header. The RFC always uses "websocket", except + # in section 11.2. (IANA registration) where it uses "WebSocket". + if not (len(upgrade) == 1 and upgrade[0].lower() == "websocket"): + raise InvalidUpgrade("Upgrade", ", ".join(upgrade)) + + try: + s_w_key = headers["Sec-WebSocket-Key"] + except KeyError as exc: + raise InvalidHeader("Sec-WebSocket-Key") from exc + except MultipleValuesError as exc: + raise InvalidHeader("Sec-WebSocket-Key", "multiple values") from exc + + try: + raw_key = base64.b64decode(s_w_key.encode(), validate=True) + except binascii.Error as exc: + raise InvalidHeaderValue("Sec-WebSocket-Key", s_w_key) from exc + if len(raw_key) != 16: + raise InvalidHeaderValue("Sec-WebSocket-Key", s_w_key) + + try: + s_w_version = headers["Sec-WebSocket-Version"] + except KeyError as exc: + raise InvalidHeader("Sec-WebSocket-Version") from exc + except MultipleValuesError as exc: + raise InvalidHeader("Sec-WebSocket-Version", "multiple values") from exc + + if s_w_version != "13": + raise InvalidHeaderValue("Sec-WebSocket-Version", s_w_version) + + return s_w_key + + +def build_response(headers: Headers, key: str) -> None: + """ + Build a handshake response to send to the client. + + Update response headers passed in argument. + + Args: + headers: Handshake response headers. + key: Returned by :func:`check_request`. + + """ + headers["Upgrade"] = "websocket" + headers["Connection"] = "Upgrade" + headers["Sec-WebSocket-Accept"] = accept(key) + + +def check_response(headers: Headers, key: str) -> None: + """ + Check a handshake response received from the server. + + This function doesn't verify that the response is an HTTP/1.1 or higher + response with a 101 status code. These controls are the responsibility of + the caller. + + Args: + headers: Handshake response headers. + key: Returned by :func:`build_request`. + + Raises: + InvalidHandshake: If the handshake response is invalid. + + """ + connection: list[ConnectionOption] = sum( + [parse_connection(value) for value in headers.get_all("Connection")], [] + ) + + if not any(value.lower() == "upgrade" for value in connection): + raise InvalidUpgrade("Connection", " ".join(connection)) + + upgrade: list[UpgradeProtocol] = sum( + [parse_upgrade(value) for value in headers.get_all("Upgrade")], [] + ) + + # For compatibility with non-strict implementations, ignore case when + # checking the Upgrade header. The RFC always uses "websocket", except + # in section 11.2. (IANA registration) where it uses "WebSocket". + if not (len(upgrade) == 1 and upgrade[0].lower() == "websocket"): + raise InvalidUpgrade("Upgrade", ", ".join(upgrade)) + + try: + s_w_accept = headers["Sec-WebSocket-Accept"] + except KeyError as exc: + raise InvalidHeader("Sec-WebSocket-Accept") from exc + except MultipleValuesError as exc: + raise InvalidHeader("Sec-WebSocket-Accept", "multiple values") from exc + + if s_w_accept != accept(key): + raise InvalidHeaderValue("Sec-WebSocket-Accept", s_w_accept) diff --git a/gestao_raul/Lib/site-packages/websockets/legacy/http.py b/gestao_raul/Lib/site-packages/websockets/legacy/http.py new file mode 100644 index 0000000..a7c8a92 --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/legacy/http.py @@ -0,0 +1,201 @@ +from __future__ import annotations + +import asyncio +import os +import re + +from ..datastructures import Headers +from ..exceptions import SecurityError + + +__all__ = ["read_request", "read_response"] + +MAX_NUM_HEADERS = int(os.environ.get("WEBSOCKETS_MAX_NUM_HEADERS", "128")) +MAX_LINE_LENGTH = int(os.environ.get("WEBSOCKETS_MAX_LINE_LENGTH", "8192")) + + +def d(value: bytes) -> str: + """ + Decode a bytestring for interpolating into an error message. + + """ + return value.decode(errors="backslashreplace") + + +# See https://datatracker.ietf.org/doc/html/rfc7230#appendix-B. + +# Regex for validating header names. + +_token_re = re.compile(rb"[-!#$%&\'*+.^_`|~0-9a-zA-Z]+") + +# Regex for validating header values. + +# We don't attempt to support obsolete line folding. + +# Include HTAB (\x09), SP (\x20), VCHAR (\x21-\x7e), obs-text (\x80-\xff). + +# The ABNF is complicated because it attempts to express that optional +# whitespace is ignored. We strip whitespace and don't revalidate that. + +# See also https://www.rfc-editor.org/errata_search.php?rfc=7230&eid=4189 + +_value_re = re.compile(rb"[\x09\x20-\x7e\x80-\xff]*") + + +async def read_request(stream: asyncio.StreamReader) -> tuple[str, Headers]: + """ + Read an HTTP/1.1 GET request and return ``(path, headers)``. + + ``path`` isn't URL-decoded or validated in any way. + + ``path`` and ``headers`` are expected to contain only ASCII characters. + Other characters are represented with surrogate escapes. + + :func:`read_request` doesn't attempt to read the request body because + WebSocket handshake requests don't have one. If the request contains a + body, it may be read from ``stream`` after this coroutine returns. + + Args: + stream: Input to read the request from. + + Raises: + EOFError: If the connection is closed without a full HTTP request. + SecurityError: If the request exceeds a security limit. + ValueError: If the request isn't well formatted. + + """ + # https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.1 + + # Parsing is simple because fixed values are expected for method and + # version and because path isn't checked. Since WebSocket software tends + # to implement HTTP/1.1 strictly, there's little need for lenient parsing. + + try: + request_line = await read_line(stream) + except EOFError as exc: + raise EOFError("connection closed while reading HTTP request line") from exc + + try: + method, raw_path, version = request_line.split(b" ", 2) + except ValueError: # not enough values to unpack (expected 3, got 1-2) + raise ValueError(f"invalid HTTP request line: {d(request_line)}") from None + + if method != b"GET": + raise ValueError(f"unsupported HTTP method: {d(method)}") + if version != b"HTTP/1.1": + raise ValueError(f"unsupported HTTP version: {d(version)}") + path = raw_path.decode("ascii", "surrogateescape") + + headers = await read_headers(stream) + + return path, headers + + +async def read_response(stream: asyncio.StreamReader) -> tuple[int, str, Headers]: + """ + Read an HTTP/1.1 response and return ``(status_code, reason, headers)``. + + ``reason`` and ``headers`` are expected to contain only ASCII characters. + Other characters are represented with surrogate escapes. + + :func:`read_request` doesn't attempt to read the response body because + WebSocket handshake responses don't have one. If the response contains a + body, it may be read from ``stream`` after this coroutine returns. + + Args: + stream: Input to read the response from. + + Raises: + EOFError: If the connection is closed without a full HTTP response. + SecurityError: If the response exceeds a security limit. + ValueError: If the response isn't well formatted. + + """ + # https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2 + + # As in read_request, parsing is simple because a fixed value is expected + # for version, status_code is a 3-digit number, and reason can be ignored. + + try: + status_line = await read_line(stream) + except EOFError as exc: + raise EOFError("connection closed while reading HTTP status line") from exc + + try: + version, raw_status_code, raw_reason = status_line.split(b" ", 2) + except ValueError: # not enough values to unpack (expected 3, got 1-2) + raise ValueError(f"invalid HTTP status line: {d(status_line)}") from None + + if version != b"HTTP/1.1": + raise ValueError(f"unsupported HTTP version: {d(version)}") + try: + status_code = int(raw_status_code) + except ValueError: # invalid literal for int() with base 10 + raise ValueError(f"invalid HTTP status code: {d(raw_status_code)}") from None + if not 100 <= status_code < 1000: + raise ValueError(f"unsupported HTTP status code: {d(raw_status_code)}") + if not _value_re.fullmatch(raw_reason): + raise ValueError(f"invalid HTTP reason phrase: {d(raw_reason)}") + reason = raw_reason.decode() + + headers = await read_headers(stream) + + return status_code, reason, headers + + +async def read_headers(stream: asyncio.StreamReader) -> Headers: + """ + Read HTTP headers from ``stream``. + + Non-ASCII characters are represented with surrogate escapes. + + """ + # https://datatracker.ietf.org/doc/html/rfc7230#section-3.2 + + # We don't attempt to support obsolete line folding. + + headers = Headers() + for _ in range(MAX_NUM_HEADERS + 1): + try: + line = await read_line(stream) + except EOFError as exc: + raise EOFError("connection closed while reading HTTP headers") from exc + if line == b"": + break + + try: + raw_name, raw_value = line.split(b":", 1) + except ValueError: # not enough values to unpack (expected 2, got 1) + raise ValueError(f"invalid HTTP header line: {d(line)}") from None + if not _token_re.fullmatch(raw_name): + raise ValueError(f"invalid HTTP header name: {d(raw_name)}") + raw_value = raw_value.strip(b" \t") + if not _value_re.fullmatch(raw_value): + raise ValueError(f"invalid HTTP header value: {d(raw_value)}") + + name = raw_name.decode("ascii") # guaranteed to be ASCII at this point + value = raw_value.decode("ascii", "surrogateescape") + headers[name] = value + + else: + raise SecurityError("too many HTTP headers") + + return headers + + +async def read_line(stream: asyncio.StreamReader) -> bytes: + """ + Read a single line from ``stream``. + + CRLF is stripped from the return value. + + """ + # Security: this is bounded by the StreamReader's limit (default = 32 KiB). + line = await stream.readline() + # Security: this guarantees header values are small (hard-coded = 8 KiB) + if len(line) > MAX_LINE_LENGTH: + raise SecurityError("line too long") + # Not mandatory but safe - https://datatracker.ietf.org/doc/html/rfc7230#section-3.5 + if not line.endswith(b"\r\n"): + raise EOFError("line without CRLF") + return line[:-2] diff --git a/gestao_raul/Lib/site-packages/websockets/legacy/protocol.py b/gestao_raul/Lib/site-packages/websockets/legacy/protocol.py new file mode 100644 index 0000000..db126c0 --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/legacy/protocol.py @@ -0,0 +1,1641 @@ +from __future__ import annotations + +import asyncio +import codecs +import collections +import logging +import random +import ssl +import struct +import sys +import time +import traceback +import uuid +import warnings +from collections.abc import AsyncIterable, AsyncIterator, Awaitable, Iterable, Mapping +from typing import Any, Callable, Deque, cast + +from ..asyncio.compatibility import asyncio_timeout +from ..datastructures import Headers +from ..exceptions import ( + ConnectionClosed, + ConnectionClosedError, + ConnectionClosedOK, + InvalidState, + PayloadTooBig, + ProtocolError, +) +from ..extensions import Extension +from ..frames import ( + OK_CLOSE_CODES, + OP_BINARY, + OP_CLOSE, + OP_CONT, + OP_PING, + OP_PONG, + OP_TEXT, + Close, + CloseCode, + Opcode, +) +from ..protocol import State +from ..typing import Data, LoggerLike, Subprotocol +from .framing import Frame, prepare_ctrl, prepare_data + + +__all__ = ["WebSocketCommonProtocol"] + + +# In order to ensure consistency, the code always checks the current value of +# WebSocketCommonProtocol.state before assigning a new value and never yields +# between the check and the assignment. + + +class WebSocketCommonProtocol(asyncio.Protocol): + """ + WebSocket connection. + + :class:`WebSocketCommonProtocol` provides APIs shared between WebSocket + servers and clients. You shouldn't use it directly. Instead, use + :class:`~websockets.legacy.client.WebSocketClientProtocol` or + :class:`~websockets.legacy.server.WebSocketServerProtocol`. + + This documentation focuses on low-level details that aren't covered in the + documentation of :class:`~websockets.legacy.client.WebSocketClientProtocol` + and :class:`~websockets.legacy.server.WebSocketServerProtocol` for the sake + of simplicity. + + Once the connection is open, a Ping_ frame is sent every ``ping_interval`` + seconds. This serves as a keepalive. It helps keeping the connection open, + especially in the presence of proxies with short timeouts on inactive + connections. Set ``ping_interval`` to :obj:`None` to disable this behavior. + + .. _Ping: https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.2 + + If the corresponding Pong_ frame isn't received within ``ping_timeout`` + seconds, the connection is considered unusable and is closed with code 1011. + This ensures that the remote endpoint remains responsive. Set + ``ping_timeout`` to :obj:`None` to disable this behavior. + + .. _Pong: https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.3 + + See the discussion of :doc:`keepalive <../../topics/keepalive>` for details. + + The ``close_timeout`` parameter defines a maximum wait time for completing + the closing handshake and terminating the TCP connection. For legacy + reasons, :meth:`close` completes in at most ``5 * close_timeout`` seconds + for clients and ``4 * close_timeout`` for servers. + + ``close_timeout`` is a parameter of the protocol because websockets usually + calls :meth:`close` implicitly upon exit: + + * on the client side, when using :func:`~websockets.legacy.client.connect` + as a context manager; + * on the server side, when the connection handler terminates. + + To apply a timeout to any other API, wrap it in :func:`~asyncio.timeout` or + :func:`~asyncio.wait_for`. + + The ``max_size`` parameter enforces the maximum size for incoming messages + in bytes. The default value is 1 MiB. If a larger message is received, + :meth:`recv` will raise :exc:`~websockets.exceptions.ConnectionClosedError` + and the connection will be closed with code 1009. + + The ``max_queue`` parameter sets the maximum length of the queue that + holds incoming messages. The default value is ``32``. Messages are added + to an in-memory queue when they're received; then :meth:`recv` pops from + that queue. In order to prevent excessive memory consumption when + messages are received faster than they can be processed, the queue must + be bounded. If the queue fills up, the protocol stops processing incoming + data until :meth:`recv` is called. In this situation, various receive + buffers (at least in :mod:`asyncio` and in the OS) will fill up, then the + TCP receive window will shrink, slowing down transmission to avoid packet + loss. + + Since Python can use up to 4 bytes of memory to represent a single + character, each connection may use up to ``4 * max_size * max_queue`` + bytes of memory to store incoming messages. By default, this is 128 MiB. + You may want to lower the limits, depending on your application's + requirements. + + The ``read_limit`` argument sets the high-water limit of the buffer for + incoming bytes. The low-water limit is half the high-water limit. The + default value is 64 KiB, half of asyncio's default (based on the current + implementation of :class:`~asyncio.StreamReader`). + + The ``write_limit`` argument sets the high-water limit of the buffer for + outgoing bytes. The low-water limit is a quarter of the high-water limit. + The default value is 64 KiB, equal to asyncio's default (based on the + current implementation of ``FlowControlMixin``). + + See the discussion of :doc:`memory usage <../../topics/memory>` for details. + + Args: + logger: Logger for this server. + It defaults to ``logging.getLogger("websockets.protocol")``. + See the :doc:`logging guide <../../topics/logging>` for details. + ping_interval: Interval between keepalive pings in seconds. + :obj:`None` disables keepalive. + ping_timeout: Timeout for keepalive pings in seconds. + :obj:`None` disables timeouts. + close_timeout: Timeout for closing the connection in seconds. + For legacy reasons, the actual timeout is 4 or 5 times larger. + max_size: Maximum size of incoming messages in bytes. + :obj:`None` disables the limit. + max_queue: Maximum number of incoming messages in receive buffer. + :obj:`None` disables the limit. + read_limit: High-water mark of read buffer in bytes. + write_limit: High-water mark of write buffer in bytes. + + """ + + # There are only two differences between the client-side and server-side + # behavior: masking the payload and closing the underlying TCP connection. + # Set is_client = True/False and side = "client"/"server" to pick a side. + is_client: bool + side: str = "undefined" + + def __init__( + self, + *, + logger: LoggerLike | None = None, + ping_interval: float | None = 20, + ping_timeout: float | None = 20, + close_timeout: float | None = None, + max_size: int | None = 2**20, + max_queue: int | None = 2**5, + read_limit: int = 2**16, + write_limit: int = 2**16, + # The following arguments are kept only for backwards compatibility. + host: str | None = None, + port: int | None = None, + secure: bool | None = None, + legacy_recv: bool = False, + loop: asyncio.AbstractEventLoop | None = None, + timeout: float | None = None, + ) -> None: + if legacy_recv: # pragma: no cover + warnings.warn("legacy_recv is deprecated", DeprecationWarning) + + # Backwards compatibility: close_timeout used to be called timeout. + if timeout is None: + timeout = 10 + else: + warnings.warn("rename timeout to close_timeout", DeprecationWarning) + # If both are specified, timeout is ignored. + if close_timeout is None: + close_timeout = timeout + + # Backwards compatibility: the loop parameter used to be supported. + if loop is None: + loop = asyncio.get_event_loop() + else: + warnings.warn("remove loop argument", DeprecationWarning) + + self.ping_interval = ping_interval + self.ping_timeout = ping_timeout + self.close_timeout = close_timeout + self.max_size = max_size + self.max_queue = max_queue + self.read_limit = read_limit + self.write_limit = write_limit + + # Unique identifier. For logs. + self.id: uuid.UUID = uuid.uuid4() + """Unique identifier of the connection. Useful in logs.""" + + # Logger or LoggerAdapter for this connection. + if logger is None: + logger = logging.getLogger("websockets.protocol") + self.logger: LoggerLike = logging.LoggerAdapter(logger, {"websocket": self}) + """Logger for this connection.""" + + # Track if DEBUG is enabled. Shortcut logging calls if it isn't. + self.debug = logger.isEnabledFor(logging.DEBUG) + + self.loop = loop + + self._host = host + self._port = port + self._secure = secure + self.legacy_recv = legacy_recv + + # Configure read buffer limits. The high-water limit is defined by + # ``self.read_limit``. The ``limit`` argument controls the line length + # limit and half the buffer limit of :class:`~asyncio.StreamReader`. + # That's why it must be set to half of ``self.read_limit``. + self.reader = asyncio.StreamReader(limit=read_limit // 2, loop=loop) + + # Copied from asyncio.FlowControlMixin + self._paused = False + self._drain_waiter: asyncio.Future[None] | None = None + + self._drain_lock = asyncio.Lock() + + # This class implements the data transfer and closing handshake, which + # are shared between the client-side and the server-side. + # Subclasses implement the opening handshake and, on success, execute + # :meth:`connection_open` to change the state to OPEN. + self.state = State.CONNECTING + if self.debug: + self.logger.debug("= connection is CONNECTING") + + # HTTP protocol parameters. + self.path: str + """Path of the opening handshake request.""" + self.request_headers: Headers + """Opening handshake request headers.""" + self.response_headers: Headers + """Opening handshake response headers.""" + + # WebSocket protocol parameters. + self.extensions: list[Extension] = [] + self.subprotocol: Subprotocol | None = None + """Subprotocol, if one was negotiated.""" + + # Close code and reason, set when a close frame is sent or received. + self.close_rcvd: Close | None = None + self.close_sent: Close | None = None + self.close_rcvd_then_sent: bool | None = None + + # Completed when the connection state becomes CLOSED. Translates the + # :meth:`connection_lost` callback to a :class:`~asyncio.Future` + # that can be awaited. (Other :class:`~asyncio.Protocol` callbacks are + # translated by ``self.stream_reader``). + self.connection_lost_waiter: asyncio.Future[None] = loop.create_future() + + # Queue of received messages. + self.messages: Deque[Data] = collections.deque() + self._pop_message_waiter: asyncio.Future[None] | None = None + self._put_message_waiter: asyncio.Future[None] | None = None + + # Protect sending fragmented messages. + self._fragmented_message_waiter: asyncio.Future[None] | None = None + + # Mapping of ping IDs to pong waiters, in chronological order. + self.pings: dict[bytes, tuple[asyncio.Future[float], float]] = {} + + self.latency: float = 0 + """ + Latency of the connection, in seconds. + + Latency is defined as the round-trip time of the connection. It is + measured by sending a Ping frame and waiting for a matching Pong frame. + Before the first measurement, :attr:`latency` is ``0``. + + By default, websockets enables a :ref:`keepalive ` mechanism + that sends Ping frames automatically at regular intervals. You can also + send Ping frames and measure latency with :meth:`ping`. + """ + + # Task running the data transfer. + self.transfer_data_task: asyncio.Task[None] + + # Exception that occurred during data transfer, if any. + self.transfer_data_exc: BaseException | None = None + + # Task sending keepalive pings. + self.keepalive_ping_task: asyncio.Task[None] + + # Task closing the TCP connection. + self.close_connection_task: asyncio.Task[None] + + # Copied from asyncio.FlowControlMixin + async def _drain_helper(self) -> None: # pragma: no cover + if self.connection_lost_waiter.done(): + raise ConnectionResetError("Connection lost") + if not self._paused: + return + waiter = self._drain_waiter + assert waiter is None or waiter.cancelled() + waiter = self.loop.create_future() + self._drain_waiter = waiter + await waiter + + # Copied from asyncio.StreamWriter + async def _drain(self) -> None: # pragma: no cover + if self.reader is not None: + exc = self.reader.exception() + if exc is not None: + raise exc + if self.transport is not None: + if self.transport.is_closing(): + # Yield to the event loop so connection_lost() may be + # called. Without this, _drain_helper() would return + # immediately, and code that calls + # write(...); yield from drain() + # in a loop would never call connection_lost(), so it + # would not see an error when the socket is closed. + await asyncio.sleep(0) + await self._drain_helper() + + def connection_open(self) -> None: + """ + Callback when the WebSocket opening handshake completes. + + Enter the OPEN state and start the data transfer phase. + + """ + # 4.1. The WebSocket Connection is Established. + assert self.state is State.CONNECTING + self.state = State.OPEN + if self.debug: + self.logger.debug("= connection is OPEN") + # Start the task that receives incoming WebSocket messages. + self.transfer_data_task = self.loop.create_task(self.transfer_data()) + # Start the task that sends pings at regular intervals. + self.keepalive_ping_task = self.loop.create_task(self.keepalive_ping()) + # Start the task that eventually closes the TCP connection. + self.close_connection_task = self.loop.create_task(self.close_connection()) + + @property + def host(self) -> str | None: + alternative = "remote_address" if self.is_client else "local_address" + warnings.warn(f"use {alternative}[0] instead of host", DeprecationWarning) + return self._host + + @property + def port(self) -> int | None: + alternative = "remote_address" if self.is_client else "local_address" + warnings.warn(f"use {alternative}[1] instead of port", DeprecationWarning) + return self._port + + @property + def secure(self) -> bool | None: + warnings.warn("don't use secure", DeprecationWarning) + return self._secure + + # Public API + + @property + def local_address(self) -> Any: + """ + Local address of the connection. + + For IPv4 connections, this is a ``(host, port)`` tuple. + + The format of the address depends on the address family; + see :meth:`~socket.socket.getsockname`. + + :obj:`None` if the TCP connection isn't established yet. + + """ + try: + transport = self.transport + except AttributeError: + return None + else: + return transport.get_extra_info("sockname") + + @property + def remote_address(self) -> Any: + """ + Remote address of the connection. + + For IPv4 connections, this is a ``(host, port)`` tuple. + + The format of the address depends on the address family; + see :meth:`~socket.socket.getpeername`. + + :obj:`None` if the TCP connection isn't established yet. + + """ + try: + transport = self.transport + except AttributeError: + return None + else: + return transport.get_extra_info("peername") + + @property + def open(self) -> bool: + """ + :obj:`True` when the connection is open; :obj:`False` otherwise. + + This attribute may be used to detect disconnections. However, this + approach is discouraged per the EAFP_ principle. Instead, you should + handle :exc:`~websockets.exceptions.ConnectionClosed` exceptions. + + .. _EAFP: https://docs.python.org/3/glossary.html#term-eafp + + """ + return self.state is State.OPEN and not self.transfer_data_task.done() + + @property + def closed(self) -> bool: + """ + :obj:`True` when the connection is closed; :obj:`False` otherwise. + + Be aware that both :attr:`open` and :attr:`closed` are :obj:`False` + during the opening and closing sequences. + + """ + return self.state is State.CLOSED + + @property + def close_code(self) -> int | None: + """ + WebSocket close code, defined in `section 7.1.5 of RFC 6455`_. + + .. _section 7.1.5 of RFC 6455: + https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.5 + + :obj:`None` if the connection isn't closed yet. + + """ + if self.state is not State.CLOSED: + return None + elif self.close_rcvd is None: + return CloseCode.ABNORMAL_CLOSURE + else: + return self.close_rcvd.code + + @property + def close_reason(self) -> str | None: + """ + WebSocket close reason, defined in `section 7.1.6 of RFC 6455`_. + + .. _section 7.1.6 of RFC 6455: + https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.6 + + :obj:`None` if the connection isn't closed yet. + + """ + if self.state is not State.CLOSED: + return None + elif self.close_rcvd is None: + return "" + else: + return self.close_rcvd.reason + + async def __aiter__(self) -> AsyncIterator[Data]: + """ + Iterate on incoming messages. + + The iterator exits normally when the connection is closed with the close + code 1000 (OK) or 1001 (going away) or without a close code. + + It raises a :exc:`~websockets.exceptions.ConnectionClosedError` + exception when the connection is closed with any other code. + + """ + try: + while True: + yield await self.recv() + except ConnectionClosedOK: + return + + async def recv(self) -> Data: + """ + Receive the next message. + + When the connection is closed, :meth:`recv` raises + :exc:`~websockets.exceptions.ConnectionClosed`. Specifically, it raises + :exc:`~websockets.exceptions.ConnectionClosedOK` after a normal + connection closure and + :exc:`~websockets.exceptions.ConnectionClosedError` after a protocol + error or a network failure. This is how you detect the end of the + message stream. + + Canceling :meth:`recv` is safe. There's no risk of losing the next + message. The next invocation of :meth:`recv` will return it. + + This makes it possible to enforce a timeout by wrapping :meth:`recv` in + :func:`~asyncio.timeout` or :func:`~asyncio.wait_for`. + + Returns: + A string (:class:`str`) for a Text_ frame. A bytestring + (:class:`bytes`) for a Binary_ frame. + + .. _Text: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 + .. _Binary: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 + + Raises: + ConnectionClosed: When the connection is closed. + RuntimeError: If two coroutines call :meth:`recv` concurrently. + + """ + if self._pop_message_waiter is not None: + raise RuntimeError( + "cannot call recv while another coroutine " + "is already waiting for the next message" + ) + + # Don't await self.ensure_open() here: + # - messages could be available in the queue even if the connection + # is closed; + # - messages could be received before the closing frame even if the + # connection is closing. + + # Wait until there's a message in the queue (if necessary) or the + # connection is closed. + while len(self.messages) <= 0: + pop_message_waiter: asyncio.Future[None] = self.loop.create_future() + self._pop_message_waiter = pop_message_waiter + try: + # If asyncio.wait() is canceled, it doesn't cancel + # pop_message_waiter and self.transfer_data_task. + await asyncio.wait( + [pop_message_waiter, self.transfer_data_task], + return_when=asyncio.FIRST_COMPLETED, + ) + finally: + self._pop_message_waiter = None + + # If asyncio.wait(...) exited because self.transfer_data_task + # completed before receiving a new message, raise a suitable + # exception (or return None if legacy_recv is enabled). + if not pop_message_waiter.done(): + if self.legacy_recv: + return None # type: ignore + else: + # Wait until the connection is closed to raise + # ConnectionClosed with the correct code and reason. + await self.ensure_open() + + # Pop a message from the queue. + message = self.messages.popleft() + + # Notify transfer_data(). + if self._put_message_waiter is not None: + self._put_message_waiter.set_result(None) + self._put_message_waiter = None + + return message + + async def send( + self, + message: Data | Iterable[Data] | AsyncIterable[Data], + ) -> None: + """ + Send a message. + + A string (:class:`str`) is sent as a Text_ frame. A bytestring or + bytes-like object (:class:`bytes`, :class:`bytearray`, or + :class:`memoryview`) is sent as a Binary_ frame. + + .. _Text: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 + .. _Binary: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 + + :meth:`send` also accepts an iterable or an asynchronous iterable of + strings, bytestrings, or bytes-like objects to enable fragmentation_. + Each item is treated as a message fragment and sent in its own frame. + All items must be of the same type, or else :meth:`send` will raise a + :exc:`TypeError` and the connection will be closed. + + .. _fragmentation: https://datatracker.ietf.org/doc/html/rfc6455#section-5.4 + + :meth:`send` rejects dict-like objects because this is often an error. + (If you want to send the keys of a dict-like object as fragments, call + its :meth:`~dict.keys` method and pass the result to :meth:`send`.) + + Canceling :meth:`send` is discouraged. Instead, you should close the + connection with :meth:`close`. Indeed, there are only two situations + where :meth:`send` may yield control to the event loop and then get + canceled; in both cases, :meth:`close` has the same effect and is + more clear: + + 1. The write buffer is full. If you don't want to wait until enough + data is sent, your only alternative is to close the connection. + :meth:`close` will likely time out then abort the TCP connection. + 2. ``message`` is an asynchronous iterator that yields control. + Stopping in the middle of a fragmented message will cause a + protocol error and the connection will be closed. + + When the connection is closed, :meth:`send` raises + :exc:`~websockets.exceptions.ConnectionClosed`. Specifically, it + raises :exc:`~websockets.exceptions.ConnectionClosedOK` after a normal + connection closure and + :exc:`~websockets.exceptions.ConnectionClosedError` after a protocol + error or a network failure. + + Args: + message: Message to send. + + Raises: + ConnectionClosed: When the connection is closed. + TypeError: If ``message`` doesn't have a supported type. + + """ + await self.ensure_open() + + # While sending a fragmented message, prevent sending other messages + # until all fragments are sent. + while self._fragmented_message_waiter is not None: + await asyncio.shield(self._fragmented_message_waiter) + + # Unfragmented message -- this case must be handled first because + # strings and bytes-like objects are iterable. + + if isinstance(message, (str, bytes, bytearray, memoryview)): + opcode, data = prepare_data(message) + await self.write_frame(True, opcode, data) + + # Catch a common mistake -- passing a dict to send(). + + elif isinstance(message, Mapping): + raise TypeError("data is a dict-like object") + + # Fragmented message -- regular iterator. + + elif isinstance(message, Iterable): + # Work around https://github.com/python/mypy/issues/6227 + message = cast(Iterable[Data], message) + + iter_message = iter(message) + try: + fragment = next(iter_message) + except StopIteration: + return + opcode, data = prepare_data(fragment) + + self._fragmented_message_waiter = self.loop.create_future() + try: + # First fragment. + await self.write_frame(False, opcode, data) + + # Other fragments. + for fragment in iter_message: + confirm_opcode, data = prepare_data(fragment) + if confirm_opcode != opcode: + raise TypeError("data contains inconsistent types") + await self.write_frame(False, OP_CONT, data) + + # Final fragment. + await self.write_frame(True, OP_CONT, b"") + + except (Exception, asyncio.CancelledError): + # We're half-way through a fragmented message and we can't + # complete it. This makes the connection unusable. + self.fail_connection(CloseCode.INTERNAL_ERROR) + raise + + finally: + self._fragmented_message_waiter.set_result(None) + self._fragmented_message_waiter = None + + # Fragmented message -- asynchronous iterator + + elif isinstance(message, AsyncIterable): + # Implement aiter_message = aiter(message) without aiter + # Work around https://github.com/python/mypy/issues/5738 + aiter_message = cast( + Callable[[AsyncIterable[Data]], AsyncIterator[Data]], + type(message).__aiter__, + )(message) + try: + # Implement fragment = anext(aiter_message) without anext + # Work around https://github.com/python/mypy/issues/5738 + fragment = await cast( + Callable[[AsyncIterator[Data]], Awaitable[Data]], + type(aiter_message).__anext__, + )(aiter_message) + except StopAsyncIteration: + return + opcode, data = prepare_data(fragment) + + self._fragmented_message_waiter = self.loop.create_future() + try: + # First fragment. + await self.write_frame(False, opcode, data) + + # Other fragments. + async for fragment in aiter_message: + confirm_opcode, data = prepare_data(fragment) + if confirm_opcode != opcode: + raise TypeError("data contains inconsistent types") + await self.write_frame(False, OP_CONT, data) + + # Final fragment. + await self.write_frame(True, OP_CONT, b"") + + except (Exception, asyncio.CancelledError): + # We're half-way through a fragmented message and we can't + # complete it. This makes the connection unusable. + self.fail_connection(CloseCode.INTERNAL_ERROR) + raise + + finally: + self._fragmented_message_waiter.set_result(None) + self._fragmented_message_waiter = None + + else: + raise TypeError("data must be str, bytes-like, or iterable") + + async def close( + self, + code: int = CloseCode.NORMAL_CLOSURE, + reason: str = "", + ) -> None: + """ + Perform the closing handshake. + + :meth:`close` waits for the other end to complete the handshake and + for the TCP connection to terminate. As a consequence, there's no need + to await :meth:`wait_closed` after :meth:`close`. + + :meth:`close` is idempotent: it doesn't do anything once the + connection is closed. + + Wrapping :func:`close` in :func:`~asyncio.create_task` is safe, given + that errors during connection termination aren't particularly useful. + + Canceling :meth:`close` is discouraged. If it takes too long, you can + set a shorter ``close_timeout``. If you don't want to wait, let the + Python process exit, then the OS will take care of closing the TCP + connection. + + Args: + code: WebSocket close code. + reason: WebSocket close reason. + + """ + try: + async with asyncio_timeout(self.close_timeout): + await self.write_close_frame(Close(code, reason)) + except asyncio.TimeoutError: + # If the close frame cannot be sent because the send buffers + # are full, the closing handshake won't complete anyway. + # Fail the connection to shut down faster. + self.fail_connection() + + # If no close frame is received within the timeout, asyncio_timeout() + # cancels the data transfer task and raises TimeoutError. + + # If close() is called multiple times concurrently and one of these + # calls hits the timeout, the data transfer task will be canceled. + # Other calls will receive a CancelledError here. + + try: + # If close() is canceled during the wait, self.transfer_data_task + # is canceled before the timeout elapses. + async with asyncio_timeout(self.close_timeout): + await self.transfer_data_task + except (asyncio.TimeoutError, asyncio.CancelledError): + pass + + # Wait for the close connection task to close the TCP connection. + await asyncio.shield(self.close_connection_task) + + async def wait_closed(self) -> None: + """ + Wait until the connection is closed. + + This coroutine is identical to the :attr:`closed` attribute, except it + can be awaited. + + This can make it easier to detect connection termination, regardless + of its cause, in tasks that interact with the WebSocket connection. + + """ + await asyncio.shield(self.connection_lost_waiter) + + async def ping(self, data: Data | None = None) -> Awaitable[float]: + """ + Send a Ping_. + + .. _Ping: https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.2 + + A ping may serve as a keepalive, as a check that the remote endpoint + received all messages up to this point, or to measure :attr:`latency`. + + Canceling :meth:`ping` is discouraged. If :meth:`ping` doesn't return + immediately, it means the write buffer is full. If you don't want to + wait, you should close the connection. + + Canceling the :class:`~asyncio.Future` returned by :meth:`ping` has no + effect. + + Args: + data: Payload of the ping. A string will be encoded to UTF-8. + If ``data`` is :obj:`None`, the payload is four random bytes. + + Returns: + A future that will be completed when the corresponding pong is + received. You can ignore it if you don't intend to wait. The result + of the future is the latency of the connection in seconds. + + :: + + pong_waiter = await ws.ping() + # only if you want to wait for the corresponding pong + latency = await pong_waiter + + Raises: + ConnectionClosed: When the connection is closed. + RuntimeError: If another ping was sent with the same data and + the corresponding pong wasn't received yet. + + """ + await self.ensure_open() + + if data is not None: + data = prepare_ctrl(data) + + # Protect against duplicates if a payload is explicitly set. + if data in self.pings: + raise RuntimeError("already waiting for a pong with the same data") + + # Generate a unique random payload otherwise. + while data is None or data in self.pings: + data = struct.pack("!I", random.getrandbits(32)) + + pong_waiter = self.loop.create_future() + # Resolution of time.monotonic() may be too low on Windows. + ping_timestamp = time.perf_counter() + self.pings[data] = (pong_waiter, ping_timestamp) + + await self.write_frame(True, OP_PING, data) + + return asyncio.shield(pong_waiter) + + async def pong(self, data: Data = b"") -> None: + """ + Send a Pong_. + + .. _Pong: https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.3 + + An unsolicited pong may serve as a unidirectional heartbeat. + + Canceling :meth:`pong` is discouraged. If :meth:`pong` doesn't return + immediately, it means the write buffer is full. If you don't want to + wait, you should close the connection. + + Args: + data: Payload of the pong. A string will be encoded to UTF-8. + + Raises: + ConnectionClosed: When the connection is closed. + + """ + await self.ensure_open() + + data = prepare_ctrl(data) + + await self.write_frame(True, OP_PONG, data) + + # Private methods - no guarantees. + + def connection_closed_exc(self) -> ConnectionClosed: + exc: ConnectionClosed + if ( + self.close_rcvd is not None + and self.close_rcvd.code in OK_CLOSE_CODES + and self.close_sent is not None + and self.close_sent.code in OK_CLOSE_CODES + ): + exc = ConnectionClosedOK( + self.close_rcvd, + self.close_sent, + self.close_rcvd_then_sent, + ) + else: + exc = ConnectionClosedError( + self.close_rcvd, + self.close_sent, + self.close_rcvd_then_sent, + ) + # Chain to the exception that terminated data transfer, if any. + exc.__cause__ = self.transfer_data_exc + return exc + + async def ensure_open(self) -> None: + """ + Check that the WebSocket connection is open. + + Raise :exc:`~websockets.exceptions.ConnectionClosed` if it isn't. + + """ + # Handle cases from most common to least common for performance. + if self.state is State.OPEN: + # If self.transfer_data_task exited without a closing handshake, + # self.close_connection_task may be closing the connection, going + # straight from OPEN to CLOSED. + if self.transfer_data_task.done(): + await asyncio.shield(self.close_connection_task) + raise self.connection_closed_exc() + else: + return + + if self.state is State.CLOSED: + raise self.connection_closed_exc() + + if self.state is State.CLOSING: + # If we started the closing handshake, wait for its completion to + # get the proper close code and reason. self.close_connection_task + # will complete within 4 or 5 * close_timeout after close(). The + # CLOSING state also occurs when failing the connection. In that + # case self.close_connection_task will complete even faster. + await asyncio.shield(self.close_connection_task) + raise self.connection_closed_exc() + + # Control may only reach this point in buggy third-party subclasses. + assert self.state is State.CONNECTING + raise InvalidState("WebSocket connection isn't established yet") + + async def transfer_data(self) -> None: + """ + Read incoming messages and put them in a queue. + + This coroutine runs in a task until the closing handshake is started. + + """ + try: + while True: + message = await self.read_message() + + # Exit the loop when receiving a close frame. + if message is None: + break + + # Wait until there's room in the queue (if necessary). + if self.max_queue is not None: + while len(self.messages) >= self.max_queue: + self._put_message_waiter = self.loop.create_future() + try: + await asyncio.shield(self._put_message_waiter) + finally: + self._put_message_waiter = None + + # Put the message in the queue. + self.messages.append(message) + + # Notify recv(). + if self._pop_message_waiter is not None: + self._pop_message_waiter.set_result(None) + self._pop_message_waiter = None + + except asyncio.CancelledError as exc: + self.transfer_data_exc = exc + # If fail_connection() cancels this task, avoid logging the error + # twice and failing the connection again. + raise + + except ProtocolError as exc: + self.transfer_data_exc = exc + self.fail_connection(CloseCode.PROTOCOL_ERROR) + + except (ConnectionError, TimeoutError, EOFError, ssl.SSLError) as exc: + # Reading data with self.reader.readexactly may raise: + # - most subclasses of ConnectionError if the TCP connection + # breaks, is reset, or is aborted; + # - TimeoutError if the TCP connection times out; + # - IncompleteReadError, a subclass of EOFError, if fewer + # bytes are available than requested; + # - ssl.SSLError if the other side infringes the TLS protocol. + self.transfer_data_exc = exc + self.fail_connection(CloseCode.ABNORMAL_CLOSURE) + + except UnicodeDecodeError as exc: + self.transfer_data_exc = exc + self.fail_connection(CloseCode.INVALID_DATA) + + except PayloadTooBig as exc: + self.transfer_data_exc = exc + self.fail_connection(CloseCode.MESSAGE_TOO_BIG) + + except Exception as exc: + # This shouldn't happen often because exceptions expected under + # regular circumstances are handled above. If it does, consider + # catching and handling more exceptions. + self.logger.error("data transfer failed", exc_info=True) + + self.transfer_data_exc = exc + self.fail_connection(CloseCode.INTERNAL_ERROR) + + async def read_message(self) -> Data | None: + """ + Read a single message from the connection. + + Re-assemble data frames if the message is fragmented. + + Return :obj:`None` when the closing handshake is started. + + """ + frame = await self.read_data_frame(max_size=self.max_size) + + # A close frame was received. + if frame is None: + return None + + if frame.opcode == OP_TEXT: + text = True + elif frame.opcode == OP_BINARY: + text = False + else: # frame.opcode == OP_CONT + raise ProtocolError("unexpected opcode") + + # Shortcut for the common case - no fragmentation + if frame.fin: + return frame.data.decode() if text else frame.data + + # 5.4. Fragmentation + fragments: list[Data] = [] + max_size = self.max_size + if text: + decoder_factory = codecs.getincrementaldecoder("utf-8") + decoder = decoder_factory(errors="strict") + if max_size is None: + + def append(frame: Frame) -> None: + nonlocal fragments + fragments.append(decoder.decode(frame.data, frame.fin)) + + else: + + def append(frame: Frame) -> None: + nonlocal fragments, max_size + fragments.append(decoder.decode(frame.data, frame.fin)) + assert isinstance(max_size, int) + max_size -= len(frame.data) + + else: + if max_size is None: + + def append(frame: Frame) -> None: + nonlocal fragments + fragments.append(frame.data) + + else: + + def append(frame: Frame) -> None: + nonlocal fragments, max_size + fragments.append(frame.data) + assert isinstance(max_size, int) + max_size -= len(frame.data) + + append(frame) + + while not frame.fin: + frame = await self.read_data_frame(max_size=max_size) + if frame is None: + raise ProtocolError("incomplete fragmented message") + if frame.opcode != OP_CONT: + raise ProtocolError("unexpected opcode") + append(frame) + + return ("" if text else b"").join(fragments) + + async def read_data_frame(self, max_size: int | None) -> Frame | None: + """ + Read a single data frame from the connection. + + Process control frames received before the next data frame. + + Return :obj:`None` if a close frame is encountered before any data frame. + + """ + # 6.2. Receiving Data + while True: + frame = await self.read_frame(max_size) + + # 5.5. Control Frames + if frame.opcode == OP_CLOSE: + # 7.1.5. The WebSocket Connection Close Code + # 7.1.6. The WebSocket Connection Close Reason + self.close_rcvd = Close.parse(frame.data) + if self.close_sent is not None: + self.close_rcvd_then_sent = False + try: + # Echo the original data instead of re-serializing it with + # Close.serialize() because that fails when the close frame + # is empty and Close.parse() synthesizes a 1005 close code. + await self.write_close_frame(self.close_rcvd, frame.data) + except ConnectionClosed: + # Connection closed before we could echo the close frame. + pass + return None + + elif frame.opcode == OP_PING: + # Answer pings, unless connection is CLOSING. + if self.state is State.OPEN: + try: + await self.pong(frame.data) + except ConnectionClosed: + # Connection closed while draining write buffer. + pass + + elif frame.opcode == OP_PONG: + if frame.data in self.pings: + pong_timestamp = time.perf_counter() + # Sending a pong for only the most recent ping is legal. + # Acknowledge all previous pings too in that case. + ping_id = None + ping_ids = [] + for ping_id, (pong_waiter, ping_timestamp) in self.pings.items(): + ping_ids.append(ping_id) + if not pong_waiter.done(): + pong_waiter.set_result(pong_timestamp - ping_timestamp) + if ping_id == frame.data: + self.latency = pong_timestamp - ping_timestamp + break + else: + raise AssertionError("solicited pong not found in pings") + # Remove acknowledged pings from self.pings. + for ping_id in ping_ids: + del self.pings[ping_id] + + # 5.6. Data Frames + else: + return frame + + async def read_frame(self, max_size: int | None) -> Frame: + """ + Read a single frame from the connection. + + """ + frame = await Frame.read( + self.reader.readexactly, + mask=not self.is_client, + max_size=max_size, + extensions=self.extensions, + ) + if self.debug: + self.logger.debug("< %s", frame) + return frame + + def write_frame_sync(self, fin: bool, opcode: int, data: bytes) -> None: + frame = Frame(fin, Opcode(opcode), data) + if self.debug: + self.logger.debug("> %s", frame) + frame.write( + self.transport.write, + mask=self.is_client, + extensions=self.extensions, + ) + + async def drain(self) -> None: + try: + # drain() cannot be called concurrently by multiple coroutines. + # See https://github.com/python/cpython/issues/74116 for details. + # This workaround can be removed when dropping Python < 3.10. + async with self._drain_lock: + # Handle flow control automatically. + await self._drain() + except ConnectionError: + # Terminate the connection if the socket died. + self.fail_connection() + # Wait until the connection is closed to raise ConnectionClosed + # with the correct code and reason. + await self.ensure_open() + + async def write_frame( + self, fin: bool, opcode: int, data: bytes, *, _state: int = State.OPEN + ) -> None: + # Defensive assertion for protocol compliance. + if self.state is not _state: # pragma: no cover + raise InvalidState( + f"Cannot write to a WebSocket in the {self.state.name} state" + ) + self.write_frame_sync(fin, opcode, data) + await self.drain() + + async def write_close_frame(self, close: Close, data: bytes | None = None) -> None: + """ + Write a close frame if and only if the connection state is OPEN. + + This dedicated coroutine must be used for writing close frames to + ensure that at most one close frame is sent on a given connection. + + """ + # Test and set the connection state before sending the close frame to + # avoid sending two frames in case of concurrent calls. + if self.state is State.OPEN: + # 7.1.3. The WebSocket Closing Handshake is Started + self.state = State.CLOSING + if self.debug: + self.logger.debug("= connection is CLOSING") + + self.close_sent = close + if self.close_rcvd is not None: + self.close_rcvd_then_sent = True + if data is None: + data = close.serialize() + + # 7.1.2. Start the WebSocket Closing Handshake + await self.write_frame(True, OP_CLOSE, data, _state=State.CLOSING) + + async def keepalive_ping(self) -> None: + """ + Send a Ping frame and wait for a Pong frame at regular intervals. + + This coroutine exits when the connection terminates and one of the + following happens: + + - :meth:`ping` raises :exc:`ConnectionClosed`, or + - :meth:`close_connection` cancels :attr:`keepalive_ping_task`. + + """ + if self.ping_interval is None: + return + + try: + while True: + await asyncio.sleep(self.ping_interval) + + self.logger.debug("% sending keepalive ping") + pong_waiter = await self.ping() + + if self.ping_timeout is not None: + try: + async with asyncio_timeout(self.ping_timeout): + # Raises CancelledError if the connection is closed, + # when close_connection() cancels keepalive_ping(). + # Raises ConnectionClosed if the connection is lost, + # when connection_lost() calls abort_pings(). + await pong_waiter + self.logger.debug("% received keepalive pong") + except asyncio.TimeoutError: + if self.debug: + self.logger.debug("- timed out waiting for keepalive pong") + self.fail_connection( + CloseCode.INTERNAL_ERROR, + "keepalive ping timeout", + ) + break + + except ConnectionClosed: + pass + + except Exception: + self.logger.error("keepalive ping failed", exc_info=True) + + async def close_connection(self) -> None: + """ + 7.1.1. Close the WebSocket Connection + + When the opening handshake succeeds, :meth:`connection_open` starts + this coroutine in a task. It waits for the data transfer phase to + complete then it closes the TCP connection cleanly. + + When the opening handshake fails, :meth:`fail_connection` does the + same. There's no data transfer phase in that case. + + """ + try: + # Wait for the data transfer phase to complete. + if hasattr(self, "transfer_data_task"): + try: + await self.transfer_data_task + except asyncio.CancelledError: + pass + + # Cancel the keepalive ping task. + if hasattr(self, "keepalive_ping_task"): + self.keepalive_ping_task.cancel() + + # A client should wait for a TCP close from the server. + if self.is_client and hasattr(self, "transfer_data_task"): + if await self.wait_for_connection_lost(): + return + if self.debug: + self.logger.debug("- timed out waiting for TCP close") + + # Half-close the TCP connection if possible (when there's no TLS). + if self.transport.can_write_eof(): + if self.debug: + self.logger.debug("x half-closing TCP connection") + # write_eof() doesn't document which exceptions it raises. + # "[Errno 107] Transport endpoint is not connected" happens + # but it isn't completely clear under which circumstances. + # uvloop can raise RuntimeError here. + try: + self.transport.write_eof() + except (OSError, RuntimeError): # pragma: no cover + pass + + if await self.wait_for_connection_lost(): + return + if self.debug: + self.logger.debug("- timed out waiting for TCP close") + + finally: + # The try/finally ensures that the transport never remains open, + # even if this coroutine is canceled (for example). + await self.close_transport() + + async def close_transport(self) -> None: + """ + Close the TCP connection. + + """ + # If connection_lost() was called, the TCP connection is closed. + # However, if TLS is enabled, the transport still needs closing. + # Else asyncio complains: ResourceWarning: unclosed transport. + if self.connection_lost_waiter.done() and self.transport.is_closing(): + return + + # Close the TCP connection. Buffers are flushed asynchronously. + if self.debug: + self.logger.debug("x closing TCP connection") + self.transport.close() + + if await self.wait_for_connection_lost(): + return + if self.debug: + self.logger.debug("- timed out waiting for TCP close") + + # Abort the TCP connection. Buffers are discarded. + if self.debug: + self.logger.debug("x aborting TCP connection") + self.transport.abort() + + # connection_lost() is called quickly after aborting. + await self.wait_for_connection_lost() + + async def wait_for_connection_lost(self) -> bool: + """ + Wait until the TCP connection is closed or ``self.close_timeout`` elapses. + + Return :obj:`True` if the connection is closed and :obj:`False` + otherwise. + + """ + if not self.connection_lost_waiter.done(): + try: + async with asyncio_timeout(self.close_timeout): + await asyncio.shield(self.connection_lost_waiter) + except asyncio.TimeoutError: + pass + # Re-check self.connection_lost_waiter.done() synchronously because + # connection_lost() could run between the moment the timeout occurs + # and the moment this coroutine resumes running. + return self.connection_lost_waiter.done() + + def fail_connection( + self, + code: int = CloseCode.ABNORMAL_CLOSURE, + reason: str = "", + ) -> None: + """ + 7.1.7. Fail the WebSocket Connection + + This requires: + + 1. Stopping all processing of incoming data, which means cancelling + :attr:`transfer_data_task`. The close code will be 1006 unless a + close frame was received earlier. + + 2. Sending a close frame with an appropriate code if the opening + handshake succeeded and the other side is likely to process it. + + 3. Closing the connection. :meth:`close_connection` takes care of + this once :attr:`transfer_data_task` exits after being canceled. + + (The specification describes these steps in the opposite order.) + + """ + if self.debug: + self.logger.debug("! failing connection with code %d", code) + + # Cancel transfer_data_task if the opening handshake succeeded. + # cancel() is idempotent and ignored if the task is done already. + if hasattr(self, "transfer_data_task"): + self.transfer_data_task.cancel() + + # Send a close frame when the state is OPEN (a close frame was already + # sent if it's CLOSING), except when failing the connection because of + # an error reading from or writing to the network. + # Don't send a close frame if the connection is broken. + if code != CloseCode.ABNORMAL_CLOSURE and self.state is State.OPEN: + close = Close(code, reason) + + # Write the close frame without draining the write buffer. + + # Keeping fail_connection() synchronous guarantees it can't + # get stuck and simplifies the implementation of the callers. + # Not drainig the write buffer is acceptable in this context. + + # This duplicates a few lines of code from write_close_frame(). + + self.state = State.CLOSING + if self.debug: + self.logger.debug("= connection is CLOSING") + + # If self.close_rcvd was set, the connection state would be + # CLOSING. Therefore self.close_rcvd isn't set and we don't + # have to set self.close_rcvd_then_sent. + assert self.close_rcvd is None + self.close_sent = close + + self.write_frame_sync(True, OP_CLOSE, close.serialize()) + + # Start close_connection_task if the opening handshake didn't succeed. + if not hasattr(self, "close_connection_task"): + self.close_connection_task = self.loop.create_task(self.close_connection()) + + def abort_pings(self) -> None: + """ + Raise ConnectionClosed in pending keepalive pings. + + They'll never receive a pong once the connection is closed. + + """ + assert self.state is State.CLOSED + exc = self.connection_closed_exc() + + for pong_waiter, _ping_timestamp in self.pings.values(): + pong_waiter.set_exception(exc) + # If the exception is never retrieved, it will be logged when ping + # is garbage-collected. This is confusing for users. + # Given that ping is done (with an exception), canceling it does + # nothing, but it prevents logging the exception. + pong_waiter.cancel() + + # asyncio.Protocol methods + + def connection_made(self, transport: asyncio.BaseTransport) -> None: + """ + Configure write buffer limits. + + The high-water limit is defined by ``self.write_limit``. + + The low-water limit currently defaults to ``self.write_limit // 4`` in + :meth:`~asyncio.WriteTransport.set_write_buffer_limits`, which should + be all right for reasonable use cases of this library. + + This is the earliest point where we can get hold of the transport, + which means it's the best point for configuring it. + + """ + transport = cast(asyncio.Transport, transport) + transport.set_write_buffer_limits(self.write_limit) + self.transport = transport + + # Copied from asyncio.StreamReaderProtocol + self.reader.set_transport(transport) + + def connection_lost(self, exc: Exception | None) -> None: + """ + 7.1.4. The WebSocket Connection is Closed. + + """ + self.state = State.CLOSED + self.logger.debug("= connection is CLOSED") + + self.abort_pings() + + # If self.connection_lost_waiter isn't pending, that's a bug, because: + # - it's set only here in connection_lost() which is called only once; + # - it must never be canceled. + self.connection_lost_waiter.set_result(None) + + if True: # pragma: no cover + # Copied from asyncio.StreamReaderProtocol + if self.reader is not None: + if exc is None: + self.reader.feed_eof() + else: + self.reader.set_exception(exc) + + # Copied from asyncio.FlowControlMixin + # Wake up the writer if currently paused. + if not self._paused: + return + waiter = self._drain_waiter + if waiter is None: + return + self._drain_waiter = None + if waiter.done(): + return + if exc is None: + waiter.set_result(None) + else: + waiter.set_exception(exc) + + def pause_writing(self) -> None: # pragma: no cover + assert not self._paused + self._paused = True + + def resume_writing(self) -> None: # pragma: no cover + assert self._paused + self._paused = False + + waiter = self._drain_waiter + if waiter is not None: + self._drain_waiter = None + if not waiter.done(): + waiter.set_result(None) + + def data_received(self, data: bytes) -> None: + self.reader.feed_data(data) + + def eof_received(self) -> None: + """ + Close the transport after receiving EOF. + + The WebSocket protocol has its own closing handshake: endpoints close + the TCP or TLS connection after sending and receiving a close frame. + + As a consequence, they never need to write after receiving EOF, so + there's no reason to keep the transport open by returning :obj:`True`. + + Besides, that doesn't work on TLS connections. + + """ + self.reader.feed_eof() + + +# broadcast() is defined in the protocol module even though it's primarily +# used by servers and documented in the server module because it works with +# client connections too and because it's easier to test together with the +# WebSocketCommonProtocol class. + + +def broadcast( + websockets: Iterable[WebSocketCommonProtocol], + message: Data, + raise_exceptions: bool = False, +) -> None: + """ + Broadcast a message to several WebSocket connections. + + A string (:class:`str`) is sent as a Text_ frame. A bytestring or bytes-like + object (:class:`bytes`, :class:`bytearray`, or :class:`memoryview`) is sent + as a Binary_ frame. + + .. _Text: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 + .. _Binary: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 + + :func:`broadcast` pushes the message synchronously to all connections even + if their write buffers are overflowing. There's no backpressure. + + If you broadcast messages faster than a connection can handle them, messages + will pile up in its write buffer until the connection times out. Keep + ``ping_interval`` and ``ping_timeout`` low to prevent excessive memory usage + from slow connections. + + Unlike :meth:`~websockets.legacy.protocol.WebSocketCommonProtocol.send`, + :func:`broadcast` doesn't support sending fragmented messages. Indeed, + fragmentation is useful for sending large messages without buffering them in + memory, while :func:`broadcast` buffers one copy per connection as fast as + possible. + + :func:`broadcast` skips connections that aren't open in order to avoid + errors on connections where the closing handshake is in progress. + + :func:`broadcast` ignores failures to write the message on some connections. + It continues writing to other connections. On Python 3.11 and above, you may + set ``raise_exceptions`` to :obj:`True` to record failures and raise all + exceptions in a :pep:`654` :exc:`ExceptionGroup`. + + While :func:`broadcast` makes more sense for servers, it works identically + with clients, if you have a use case for opening connections to many servers + and broadcasting a message to them. + + Args: + websockets: WebSocket connections to which the message will be sent. + message: Message to send. + raise_exceptions: Whether to raise an exception in case of failures. + + Raises: + TypeError: If ``message`` doesn't have a supported type. + + """ + if not isinstance(message, (str, bytes, bytearray, memoryview)): + raise TypeError("data must be str or bytes-like") + + if raise_exceptions: + if sys.version_info[:2] < (3, 11): # pragma: no cover + raise ValueError("raise_exceptions requires at least Python 3.11") + exceptions = [] + + opcode, data = prepare_data(message) + + for websocket in websockets: + if websocket.state is not State.OPEN: + continue + + if websocket._fragmented_message_waiter is not None: + if raise_exceptions: + exception = RuntimeError("sending a fragmented message") + exceptions.append(exception) + else: + websocket.logger.warning( + "skipped broadcast: sending a fragmented message", + ) + continue + + try: + websocket.write_frame_sync(True, opcode, data) + except Exception as write_exception: + if raise_exceptions: + exception = RuntimeError("failed to write message") + exception.__cause__ = write_exception + exceptions.append(exception) + else: + websocket.logger.warning( + "skipped broadcast: failed to write message: %s", + traceback.format_exception_only( + # Remove first argument when dropping Python 3.9. + type(write_exception), + write_exception, + )[0].strip(), + ) + + if raise_exceptions and exceptions: + raise ExceptionGroup("skipped broadcast", exceptions) + + +# Pretend that broadcast is actually defined in the server module. +broadcast.__module__ = "websockets.legacy.server" diff --git a/gestao_raul/Lib/site-packages/websockets/legacy/server.py b/gestao_raul/Lib/site-packages/websockets/legacy/server.py new file mode 100644 index 0000000..f9d57cb --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/legacy/server.py @@ -0,0 +1,1191 @@ +from __future__ import annotations + +import asyncio +import email.utils +import functools +import http +import inspect +import logging +import socket +import warnings +from collections.abc import Awaitable, Generator, Iterable, Sequence +from types import TracebackType +from typing import Any, Callable, Union, cast + +from ..asyncio.compatibility import asyncio_timeout +from ..datastructures import Headers, HeadersLike, MultipleValuesError +from ..exceptions import ( + InvalidHandshake, + InvalidHeader, + InvalidMessage, + InvalidOrigin, + InvalidUpgrade, + NegotiationError, +) +from ..extensions import Extension, ServerExtensionFactory +from ..extensions.permessage_deflate import enable_server_permessage_deflate +from ..headers import ( + build_extension, + parse_extension, + parse_subprotocol, + validate_subprotocols, +) +from ..http11 import SERVER +from ..protocol import State +from ..typing import ExtensionHeader, LoggerLike, Origin, StatusLike, Subprotocol +from .exceptions import AbortHandshake +from .handshake import build_response, check_request +from .http import read_request +from .protocol import WebSocketCommonProtocol, broadcast + + +__all__ = [ + "broadcast", + "serve", + "unix_serve", + "WebSocketServerProtocol", + "WebSocketServer", +] + + +# Change to HeadersLike | ... when dropping Python < 3.10. +HeadersLikeOrCallable = Union[HeadersLike, Callable[[str, Headers], HeadersLike]] + +HTTPResponse = tuple[StatusLike, HeadersLike, bytes] + + +class WebSocketServerProtocol(WebSocketCommonProtocol): + """ + WebSocket server connection. + + :class:`WebSocketServerProtocol` provides :meth:`recv` and :meth:`send` + coroutines for receiving and sending messages. + + It supports asynchronous iteration to receive messages:: + + async for message in websocket: + await process(message) + + The iterator exits normally when the connection is closed with close code + 1000 (OK) or 1001 (going away) or without a close code. It raises + a :exc:`~websockets.exceptions.ConnectionClosedError` when the connection + is closed with any other code. + + You may customize the opening handshake in a subclass by + overriding :meth:`process_request` or :meth:`select_subprotocol`. + + Args: + ws_server: WebSocket server that created this connection. + + See :func:`serve` for the documentation of ``ws_handler``, ``logger``, ``origins``, + ``extensions``, ``subprotocols``, ``extra_headers``, and ``server_header``. + + See :class:`~websockets.legacy.protocol.WebSocketCommonProtocol` for the + documentation of ``ping_interval``, ``ping_timeout``, ``close_timeout``, + ``max_size``, ``max_queue``, ``read_limit``, and ``write_limit``. + + """ + + is_client = False + side = "server" + + def __init__( + self, + # The version that accepts the path in the second argument is deprecated. + ws_handler: ( + Callable[[WebSocketServerProtocol], Awaitable[Any]] + | Callable[[WebSocketServerProtocol, str], Awaitable[Any]] + ), + ws_server: WebSocketServer, + *, + logger: LoggerLike | None = None, + origins: Sequence[Origin | None] | None = None, + extensions: Sequence[ServerExtensionFactory] | None = None, + subprotocols: Sequence[Subprotocol] | None = None, + extra_headers: HeadersLikeOrCallable | None = None, + server_header: str | None = SERVER, + process_request: ( + Callable[[str, Headers], Awaitable[HTTPResponse | None]] | None + ) = None, + select_subprotocol: ( + Callable[[Sequence[Subprotocol], Sequence[Subprotocol]], Subprotocol] | None + ) = None, + open_timeout: float | None = 10, + **kwargs: Any, + ) -> None: + if logger is None: + logger = logging.getLogger("websockets.server") + super().__init__(logger=logger, **kwargs) + # For backwards compatibility with 6.0 or earlier. + if origins is not None and "" in origins: + warnings.warn("use None instead of '' in origins", DeprecationWarning) + origins = [None if origin == "" else origin for origin in origins] + # For backwards compatibility with 10.0 or earlier. Done here in + # addition to serve to trigger the deprecation warning on direct + # use of WebSocketServerProtocol. + self.ws_handler = remove_path_argument(ws_handler) + self.ws_server = ws_server + self.origins = origins + self.available_extensions = extensions + self.available_subprotocols = subprotocols + self.extra_headers = extra_headers + self.server_header = server_header + self._process_request = process_request + self._select_subprotocol = select_subprotocol + self.open_timeout = open_timeout + + def connection_made(self, transport: asyncio.BaseTransport) -> None: + """ + Register connection and initialize a task to handle it. + + """ + super().connection_made(transport) + # Register the connection with the server before creating the handler + # task. Registering at the beginning of the handler coroutine would + # create a race condition between the creation of the task, which + # schedules its execution, and the moment the handler starts running. + self.ws_server.register(self) + self.handler_task = self.loop.create_task(self.handler()) + + async def handler(self) -> None: + """ + Handle the lifecycle of a WebSocket connection. + + Since this method doesn't have a caller able to handle exceptions, it + attempts to log relevant ones and guarantees that the TCP connection is + closed before exiting. + + """ + try: + try: + async with asyncio_timeout(self.open_timeout): + await self.handshake( + origins=self.origins, + available_extensions=self.available_extensions, + available_subprotocols=self.available_subprotocols, + extra_headers=self.extra_headers, + ) + except asyncio.TimeoutError: # pragma: no cover + raise + except ConnectionError: + raise + except Exception as exc: + if isinstance(exc, AbortHandshake): + status, headers, body = exc.status, exc.headers, exc.body + elif isinstance(exc, InvalidOrigin): + if self.debug: + self.logger.debug("! invalid origin", exc_info=True) + status, headers, body = ( + http.HTTPStatus.FORBIDDEN, + Headers(), + f"Failed to open a WebSocket connection: {exc}.\n".encode(), + ) + elif isinstance(exc, InvalidUpgrade): + if self.debug: + self.logger.debug("! invalid upgrade", exc_info=True) + status, headers, body = ( + http.HTTPStatus.UPGRADE_REQUIRED, + Headers([("Upgrade", "websocket")]), + ( + f"Failed to open a WebSocket connection: {exc}.\n" + f"\n" + f"You cannot access a WebSocket server directly " + f"with a browser. You need a WebSocket client.\n" + ).encode(), + ) + elif isinstance(exc, InvalidHandshake): + if self.debug: + self.logger.debug("! invalid handshake", exc_info=True) + exc_chain = cast(BaseException, exc) + exc_str = f"{exc_chain}" + while exc_chain.__cause__ is not None: + exc_chain = exc_chain.__cause__ + exc_str += f"; {exc_chain}" + status, headers, body = ( + http.HTTPStatus.BAD_REQUEST, + Headers(), + f"Failed to open a WebSocket connection: {exc_str}.\n".encode(), + ) + else: + self.logger.error("opening handshake failed", exc_info=True) + status, headers, body = ( + http.HTTPStatus.INTERNAL_SERVER_ERROR, + Headers(), + ( + b"Failed to open a WebSocket connection.\n" + b"See server log for more information.\n" + ), + ) + + headers.setdefault("Date", email.utils.formatdate(usegmt=True)) + if self.server_header: + headers.setdefault("Server", self.server_header) + + headers.setdefault("Content-Length", str(len(body))) + headers.setdefault("Content-Type", "text/plain") + headers.setdefault("Connection", "close") + + self.write_http_response(status, headers, body) + self.logger.info( + "connection rejected (%d %s)", status.value, status.phrase + ) + await self.close_transport() + return + + try: + await self.ws_handler(self) + except Exception: + self.logger.error("connection handler failed", exc_info=True) + if not self.closed: + self.fail_connection(1011) + raise + + try: + await self.close() + except ConnectionError: + raise + except Exception: + self.logger.error("closing handshake failed", exc_info=True) + raise + + except Exception: + # Last-ditch attempt to avoid leaking connections on errors. + try: + self.transport.close() + except Exception: # pragma: no cover + pass + + finally: + # Unregister the connection with the server when the handler task + # terminates. Registration is tied to the lifecycle of the handler + # task because the server waits for tasks attached to registered + # connections before terminating. + self.ws_server.unregister(self) + self.logger.info("connection closed") + + async def read_http_request(self) -> tuple[str, Headers]: + """ + Read request line and headers from the HTTP request. + + If the request contains a body, it may be read from ``self.reader`` + after this coroutine returns. + + Raises: + InvalidMessage: If the HTTP message is malformed or isn't an + HTTP/1.1 GET request. + + """ + try: + path, headers = await read_request(self.reader) + except asyncio.CancelledError: # pragma: no cover + raise + except Exception as exc: + raise InvalidMessage("did not receive a valid HTTP request") from exc + + if self.debug: + self.logger.debug("< GET %s HTTP/1.1", path) + for key, value in headers.raw_items(): + self.logger.debug("< %s: %s", key, value) + + self.path = path + self.request_headers = headers + + return path, headers + + def write_http_response( + self, status: http.HTTPStatus, headers: Headers, body: bytes | None = None + ) -> None: + """ + Write status line and headers to the HTTP response. + + This coroutine is also able to write a response body. + + """ + self.response_headers = headers + + if self.debug: + self.logger.debug("> HTTP/1.1 %d %s", status.value, status.phrase) + for key, value in headers.raw_items(): + self.logger.debug("> %s: %s", key, value) + if body is not None: + self.logger.debug("> [body] (%d bytes)", len(body)) + + # Since the status line and headers only contain ASCII characters, + # we can keep this simple. + response = f"HTTP/1.1 {status.value} {status.phrase}\r\n" + response += str(headers) + + self.transport.write(response.encode()) + + if body is not None: + self.transport.write(body) + + async def process_request( + self, path: str, request_headers: Headers + ) -> HTTPResponse | None: + """ + Intercept the HTTP request and return an HTTP response if appropriate. + + You may override this method in a :class:`WebSocketServerProtocol` + subclass, for example: + + * to return an HTTP 200 OK response on a given path; then a load + balancer can use this path for a health check; + * to authenticate the request and return an HTTP 401 Unauthorized or an + HTTP 403 Forbidden when authentication fails. + + You may also override this method with the ``process_request`` + argument of :func:`serve` and :class:`WebSocketServerProtocol`. This + is equivalent, except ``process_request`` won't have access to the + protocol instance, so it can't store information for later use. + + :meth:`process_request` is expected to complete quickly. If it may run + for a long time, then it should await :meth:`wait_closed` and exit if + :meth:`wait_closed` completes, or else it could prevent the server + from shutting down. + + Args: + path: Request path, including optional query string. + request_headers: Request headers. + + Returns: + tuple[StatusLike, HeadersLike, bytes] | None: :obj:`None` to + continue the WebSocket handshake normally. + + An HTTP response, represented by a 3-uple of the response status, + headers, and body, to abort the WebSocket handshake and return + that HTTP response instead. + + """ + if self._process_request is not None: + response = self._process_request(path, request_headers) + if isinstance(response, Awaitable): + return await response + else: + # For backwards compatibility with 7.0. + warnings.warn( + "declare process_request as a coroutine", DeprecationWarning + ) + return response + return None + + @staticmethod + def process_origin( + headers: Headers, origins: Sequence[Origin | None] | None = None + ) -> Origin | None: + """ + Handle the Origin HTTP request header. + + Args: + headers: Request headers. + origins: Optional list of acceptable origins. + + Raises: + InvalidOrigin: If the origin isn't acceptable. + + """ + # "The user agent MUST NOT include more than one Origin header field" + # per https://datatracker.ietf.org/doc/html/rfc6454#section-7.3. + try: + origin = headers.get("Origin") + except MultipleValuesError as exc: + raise InvalidHeader("Origin", "multiple values") from exc + if origin is not None: + origin = cast(Origin, origin) + if origins is not None: + if origin not in origins: + raise InvalidOrigin(origin) + return origin + + @staticmethod + def process_extensions( + headers: Headers, + available_extensions: Sequence[ServerExtensionFactory] | None, + ) -> tuple[str | None, list[Extension]]: + """ + Handle the Sec-WebSocket-Extensions HTTP request header. + + Accept or reject each extension proposed in the client request. + Negotiate parameters for accepted extensions. + + Return the Sec-WebSocket-Extensions HTTP response header and the list + of accepted extensions. + + :rfc:`6455` leaves the rules up to the specification of each + :extension. + + To provide this level of flexibility, for each extension proposed by + the client, we check for a match with each extension available in the + server configuration. If no match is found, the extension is ignored. + + If several variants of the same extension are proposed by the client, + it may be accepted several times, which won't make sense in general. + Extensions must implement their own requirements. For this purpose, + the list of previously accepted extensions is provided. + + This process doesn't allow the server to reorder extensions. It can + only select a subset of the extensions proposed by the client. + + Other requirements, for example related to mandatory extensions or the + order of extensions, may be implemented by overriding this method. + + Args: + headers: Request headers. + extensions: Optional list of supported extensions. + + Raises: + InvalidHandshake: To abort the handshake with an HTTP 400 error. + + """ + response_header_value: str | None = None + + extension_headers: list[ExtensionHeader] = [] + accepted_extensions: list[Extension] = [] + + header_values = headers.get_all("Sec-WebSocket-Extensions") + + if header_values and available_extensions: + parsed_header_values: list[ExtensionHeader] = sum( + [parse_extension(header_value) for header_value in header_values], [] + ) + + for name, request_params in parsed_header_values: + for ext_factory in available_extensions: + # Skip non-matching extensions based on their name. + if ext_factory.name != name: + continue + + # Skip non-matching extensions based on their params. + try: + response_params, extension = ext_factory.process_request_params( + request_params, accepted_extensions + ) + except NegotiationError: + continue + + # Add matching extension to the final list. + extension_headers.append((name, response_params)) + accepted_extensions.append(extension) + + # Break out of the loop once we have a match. + break + + # If we didn't break from the loop, no extension in our list + # matched what the client sent. The extension is declined. + + # Serialize extension header. + if extension_headers: + response_header_value = build_extension(extension_headers) + + return response_header_value, accepted_extensions + + # Not @staticmethod because it calls self.select_subprotocol() + def process_subprotocol( + self, headers: Headers, available_subprotocols: Sequence[Subprotocol] | None + ) -> Subprotocol | None: + """ + Handle the Sec-WebSocket-Protocol HTTP request header. + + Return Sec-WebSocket-Protocol HTTP response header, which is the same + as the selected subprotocol. + + Args: + headers: Request headers. + available_subprotocols: Optional list of supported subprotocols. + + Raises: + InvalidHandshake: To abort the handshake with an HTTP 400 error. + + """ + subprotocol: Subprotocol | None = None + + header_values = headers.get_all("Sec-WebSocket-Protocol") + + if header_values and available_subprotocols: + parsed_header_values: list[Subprotocol] = sum( + [parse_subprotocol(header_value) for header_value in header_values], [] + ) + + subprotocol = self.select_subprotocol( + parsed_header_values, available_subprotocols + ) + + return subprotocol + + def select_subprotocol( + self, + client_subprotocols: Sequence[Subprotocol], + server_subprotocols: Sequence[Subprotocol], + ) -> Subprotocol | None: + """ + Pick a subprotocol among those supported by the client and the server. + + If several subprotocols are available, select the preferred subprotocol + by giving equal weight to the preferences of the client and the server. + + If no subprotocol is available, proceed without a subprotocol. + + You may provide a ``select_subprotocol`` argument to :func:`serve` or + :class:`WebSocketServerProtocol` to override this logic. For example, + you could reject the handshake if the client doesn't support a + particular subprotocol, rather than accept the handshake without that + subprotocol. + + Args: + client_subprotocols: List of subprotocols offered by the client. + server_subprotocols: List of subprotocols available on the server. + + Returns: + Selected subprotocol, if a common subprotocol was found. + + :obj:`None` to continue without a subprotocol. + + """ + if self._select_subprotocol is not None: + return self._select_subprotocol(client_subprotocols, server_subprotocols) + + subprotocols = set(client_subprotocols) & set(server_subprotocols) + if not subprotocols: + return None + return sorted( + subprotocols, + key=lambda p: client_subprotocols.index(p) + server_subprotocols.index(p), + )[0] + + async def handshake( + self, + origins: Sequence[Origin | None] | None = None, + available_extensions: Sequence[ServerExtensionFactory] | None = None, + available_subprotocols: Sequence[Subprotocol] | None = None, + extra_headers: HeadersLikeOrCallable | None = None, + ) -> str: + """ + Perform the server side of the opening handshake. + + Args: + origins: List of acceptable values of the Origin HTTP header; + include :obj:`None` if the lack of an origin is acceptable. + extensions: List of supported extensions, in order in which they + should be tried. + subprotocols: List of supported subprotocols, in order of + decreasing preference. + extra_headers: Arbitrary HTTP headers to add to the response when + the handshake succeeds. + + Returns: + path of the URI of the request. + + Raises: + InvalidHandshake: If the handshake fails. + + """ + path, request_headers = await self.read_http_request() + + # Hook for customizing request handling, for example checking + # authentication or treating some paths as plain HTTP endpoints. + early_response_awaitable = self.process_request(path, request_headers) + if isinstance(early_response_awaitable, Awaitable): + early_response = await early_response_awaitable + else: + # For backwards compatibility with 7.0. + warnings.warn("declare process_request as a coroutine", DeprecationWarning) + early_response = early_response_awaitable + + # The connection may drop while process_request is running. + if self.state is State.CLOSED: + # This subclass of ConnectionError is silently ignored in handler(). + raise BrokenPipeError("connection closed during opening handshake") + + # Change the response to a 503 error if the server is shutting down. + if not self.ws_server.is_serving(): + early_response = ( + http.HTTPStatus.SERVICE_UNAVAILABLE, + [], + b"Server is shutting down.\n", + ) + + if early_response is not None: + raise AbortHandshake(*early_response) + + key = check_request(request_headers) + + self.origin = self.process_origin(request_headers, origins) + + extensions_header, self.extensions = self.process_extensions( + request_headers, available_extensions + ) + + protocol_header = self.subprotocol = self.process_subprotocol( + request_headers, available_subprotocols + ) + + response_headers = Headers() + + build_response(response_headers, key) + + if extensions_header is not None: + response_headers["Sec-WebSocket-Extensions"] = extensions_header + + if protocol_header is not None: + response_headers["Sec-WebSocket-Protocol"] = protocol_header + + if callable(extra_headers): + extra_headers = extra_headers(path, self.request_headers) + if extra_headers is not None: + response_headers.update(extra_headers) + + response_headers.setdefault("Date", email.utils.formatdate(usegmt=True)) + if self.server_header is not None: + response_headers.setdefault("Server", self.server_header) + + self.write_http_response(http.HTTPStatus.SWITCHING_PROTOCOLS, response_headers) + + self.logger.info("connection open") + + self.connection_open() + + return path + + +class WebSocketServer: + """ + WebSocket server returned by :func:`serve`. + + This class mirrors the API of :class:`~asyncio.Server`. + + It keeps track of WebSocket connections in order to close them properly + when shutting down. + + Args: + logger: Logger for this server. + It defaults to ``logging.getLogger("websockets.server")``. + See the :doc:`logging guide <../../topics/logging>` for details. + + """ + + def __init__(self, logger: LoggerLike | None = None) -> None: + if logger is None: + logger = logging.getLogger("websockets.server") + self.logger = logger + + # Keep track of active connections. + self.websockets: set[WebSocketServerProtocol] = set() + + # Task responsible for closing the server and terminating connections. + self.close_task: asyncio.Task[None] | None = None + + # Completed when the server is closed and connections are terminated. + self.closed_waiter: asyncio.Future[None] + + def wrap(self, server: asyncio.base_events.Server) -> None: + """ + Attach to a given :class:`~asyncio.Server`. + + Since :meth:`~asyncio.loop.create_server` doesn't support injecting a + custom ``Server`` class, the easiest solution that doesn't rely on + private :mod:`asyncio` APIs is to: + + - instantiate a :class:`WebSocketServer` + - give the protocol factory a reference to that instance + - call :meth:`~asyncio.loop.create_server` with the factory + - attach the resulting :class:`~asyncio.Server` with this method + + """ + self.server = server + for sock in server.sockets: + if sock.family == socket.AF_INET: + name = "%s:%d" % sock.getsockname() + elif sock.family == socket.AF_INET6: + name = "[%s]:%d" % sock.getsockname()[:2] + elif sock.family == socket.AF_UNIX: + name = sock.getsockname() + # In the unlikely event that someone runs websockets over a + # protocol other than IP or Unix sockets, avoid crashing. + else: # pragma: no cover + name = str(sock.getsockname()) + self.logger.info("server listening on %s", name) + + # Initialized here because we need a reference to the event loop. + # This should be moved back to __init__ when dropping Python < 3.10. + self.closed_waiter = server.get_loop().create_future() + + def register(self, protocol: WebSocketServerProtocol) -> None: + """ + Register a connection with this server. + + """ + self.websockets.add(protocol) + + def unregister(self, protocol: WebSocketServerProtocol) -> None: + """ + Unregister a connection with this server. + + """ + self.websockets.remove(protocol) + + def close(self, close_connections: bool = True) -> None: + """ + Close the server. + + * Close the underlying :class:`~asyncio.Server`. + * When ``close_connections`` is :obj:`True`, which is the default, + close existing connections. Specifically: + + * Reject opening WebSocket connections with an HTTP 503 (service + unavailable) error. This happens when the server accepted the TCP + connection but didn't complete the opening handshake before closing. + * Close open WebSocket connections with close code 1001 (going away). + + * Wait until all connection handlers terminate. + + :meth:`close` is idempotent. + + """ + if self.close_task is None: + self.close_task = self.get_loop().create_task( + self._close(close_connections) + ) + + async def _close(self, close_connections: bool) -> None: + """ + Implementation of :meth:`close`. + + This calls :meth:`~asyncio.Server.close` on the underlying + :class:`~asyncio.Server` object to stop accepting new connections and + then closes open connections with close code 1001. + + """ + self.logger.info("server closing") + + # Stop accepting new connections. + self.server.close() + + # Wait until all accepted connections reach connection_made() and call + # register(). See https://github.com/python/cpython/issues/79033 for + # details. This workaround can be removed when dropping Python < 3.11. + await asyncio.sleep(0) + + if close_connections: + # Close OPEN connections with close code 1001. After server.close(), + # handshake() closes OPENING connections with an HTTP 503 error. + close_tasks = [ + asyncio.create_task(websocket.close(1001)) + for websocket in self.websockets + if websocket.state is not State.CONNECTING + ] + # asyncio.wait doesn't accept an empty first argument. + if close_tasks: + await asyncio.wait(close_tasks) + + # Wait until all TCP connections are closed. + await self.server.wait_closed() + + # Wait until all connection handlers terminate. + # asyncio.wait doesn't accept an empty first argument. + if self.websockets: + await asyncio.wait( + [websocket.handler_task for websocket in self.websockets] + ) + + # Tell wait_closed() to return. + self.closed_waiter.set_result(None) + + self.logger.info("server closed") + + async def wait_closed(self) -> None: + """ + Wait until the server is closed. + + When :meth:`wait_closed` returns, all TCP connections are closed and + all connection handlers have returned. + + To ensure a fast shutdown, a connection handler should always be + awaiting at least one of: + + * :meth:`~WebSocketServerProtocol.recv`: when the connection is closed, + it raises :exc:`~websockets.exceptions.ConnectionClosedOK`; + * :meth:`~WebSocketServerProtocol.wait_closed`: when the connection is + closed, it returns. + + Then the connection handler is immediately notified of the shutdown; + it can clean up and exit. + + """ + await asyncio.shield(self.closed_waiter) + + def get_loop(self) -> asyncio.AbstractEventLoop: + """ + See :meth:`asyncio.Server.get_loop`. + + """ + return self.server.get_loop() + + def is_serving(self) -> bool: + """ + See :meth:`asyncio.Server.is_serving`. + + """ + return self.server.is_serving() + + async def start_serving(self) -> None: # pragma: no cover + """ + See :meth:`asyncio.Server.start_serving`. + + Typical use:: + + server = await serve(..., start_serving=False) + # perform additional setup here... + # ... then start the server + await server.start_serving() + + """ + await self.server.start_serving() + + async def serve_forever(self) -> None: # pragma: no cover + """ + See :meth:`asyncio.Server.serve_forever`. + + Typical use:: + + server = await serve(...) + # this coroutine doesn't return + # canceling it stops the server + await server.serve_forever() + + This is an alternative to using :func:`serve` as an asynchronous context + manager. Shutdown is triggered by canceling :meth:`serve_forever` + instead of exiting a :func:`serve` context. + + """ + await self.server.serve_forever() + + @property + def sockets(self) -> Iterable[socket.socket]: + """ + See :attr:`asyncio.Server.sockets`. + + """ + return self.server.sockets + + async def __aenter__(self) -> WebSocketServer: # pragma: no cover + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + ) -> None: # pragma: no cover + self.close() + await self.wait_closed() + + +class Serve: + """ + Start a WebSocket server listening on ``host`` and ``port``. + + Whenever a client connects, the server creates a + :class:`WebSocketServerProtocol`, performs the opening handshake, and + delegates to the connection handler, ``ws_handler``. + + The handler receives the :class:`WebSocketServerProtocol` and uses it to + send and receive messages. + + Once the handler completes, either normally or with an exception, the + server performs the closing handshake and closes the connection. + + Awaiting :func:`serve` yields a :class:`WebSocketServer`. This object + provides a :meth:`~WebSocketServer.close` method to shut down the server:: + + # set this future to exit the server + stop = asyncio.get_running_loop().create_future() + + server = await serve(...) + await stop + server.close() + await server.wait_closed() + + :func:`serve` can be used as an asynchronous context manager. Then, the + server is shut down automatically when exiting the context:: + + # set this future to exit the server + stop = asyncio.get_running_loop().create_future() + + async with serve(...): + await stop + + Args: + ws_handler: Connection handler. It receives the WebSocket connection, + which is a :class:`WebSocketServerProtocol`, in argument. + host: Network interfaces the server binds to. + See :meth:`~asyncio.loop.create_server` for details. + port: TCP port the server listens on. + See :meth:`~asyncio.loop.create_server` for details. + create_protocol: Factory for the :class:`asyncio.Protocol` managing + the connection. It defaults to :class:`WebSocketServerProtocol`. + Set it to a wrapper or a subclass to customize connection handling. + logger: Logger for this server. + It defaults to ``logging.getLogger("websockets.server")``. + See the :doc:`logging guide <../../topics/logging>` for details. + compression: The "permessage-deflate" extension is enabled by default. + Set ``compression`` to :obj:`None` to disable it. See the + :doc:`compression guide <../../topics/compression>` for details. + origins: Acceptable values of the ``Origin`` header, for defending + against Cross-Site WebSocket Hijacking attacks. Include :obj:`None` + in the list if the lack of an origin is acceptable. + extensions: List of supported extensions, in order in which they + should be negotiated and run. + subprotocols: List of supported subprotocols, in order of decreasing + preference. + extra_headers (HeadersLike | Callable[[str, Headers] | HeadersLike]): + Arbitrary HTTP headers to add to the response. This can be + a :data:`~websockets.datastructures.HeadersLike` or a callable + taking the request path and headers in arguments and returning + a :data:`~websockets.datastructures.HeadersLike`. + server_header: Value of the ``Server`` response header. + It defaults to ``"Python/x.y.z websockets/X.Y"``. + Setting it to :obj:`None` removes the header. + process_request (Callable[[str, Headers], \ + Awaitable[tuple[StatusLike, HeadersLike, bytes] | None]] | None): + Intercept HTTP request before the opening handshake. + See :meth:`~WebSocketServerProtocol.process_request` for details. + select_subprotocol: Select a subprotocol supported by the client. + See :meth:`~WebSocketServerProtocol.select_subprotocol` for details. + open_timeout: Timeout for opening connections in seconds. + :obj:`None` disables the timeout. + + See :class:`~websockets.legacy.protocol.WebSocketCommonProtocol` for the + documentation of ``ping_interval``, ``ping_timeout``, ``close_timeout``, + ``max_size``, ``max_queue``, ``read_limit``, and ``write_limit``. + + Any other keyword arguments are passed the event loop's + :meth:`~asyncio.loop.create_server` method. + + For example: + + * You can set ``ssl`` to a :class:`~ssl.SSLContext` to enable TLS. + + * You can set ``sock`` to a :obj:`~socket.socket` that you created + outside of websockets. + + Returns: + WebSocket server. + + """ + + def __init__( + self, + # The version that accepts the path in the second argument is deprecated. + ws_handler: ( + Callable[[WebSocketServerProtocol], Awaitable[Any]] + | Callable[[WebSocketServerProtocol, str], Awaitable[Any]] + ), + host: str | Sequence[str] | None = None, + port: int | None = None, + *, + create_protocol: Callable[..., WebSocketServerProtocol] | None = None, + logger: LoggerLike | None = None, + compression: str | None = "deflate", + origins: Sequence[Origin | None] | None = None, + extensions: Sequence[ServerExtensionFactory] | None = None, + subprotocols: Sequence[Subprotocol] | None = None, + extra_headers: HeadersLikeOrCallable | None = None, + server_header: str | None = SERVER, + process_request: ( + Callable[[str, Headers], Awaitable[HTTPResponse | None]] | None + ) = None, + select_subprotocol: ( + Callable[[Sequence[Subprotocol], Sequence[Subprotocol]], Subprotocol] | None + ) = None, + open_timeout: float | None = 10, + ping_interval: float | None = 20, + ping_timeout: float | None = 20, + close_timeout: float | None = None, + max_size: int | None = 2**20, + max_queue: int | None = 2**5, + read_limit: int = 2**16, + write_limit: int = 2**16, + **kwargs: Any, + ) -> None: + # Backwards compatibility: close_timeout used to be called timeout. + timeout: float | None = kwargs.pop("timeout", None) + if timeout is None: + timeout = 10 + else: + warnings.warn("rename timeout to close_timeout", DeprecationWarning) + # If both are specified, timeout is ignored. + if close_timeout is None: + close_timeout = timeout + + # Backwards compatibility: create_protocol used to be called klass. + klass: type[WebSocketServerProtocol] | None = kwargs.pop("klass", None) + if klass is None: + klass = WebSocketServerProtocol + else: + warnings.warn("rename klass to create_protocol", DeprecationWarning) + # If both are specified, klass is ignored. + if create_protocol is None: + create_protocol = klass + + # Backwards compatibility: recv() used to return None on closed connections + legacy_recv: bool = kwargs.pop("legacy_recv", False) + + # Backwards compatibility: the loop parameter used to be supported. + _loop: asyncio.AbstractEventLoop | None = kwargs.pop("loop", None) + if _loop is None: + loop = asyncio.get_event_loop() + else: + loop = _loop + warnings.warn("remove loop argument", DeprecationWarning) + + ws_server = WebSocketServer(logger=logger) + + secure = kwargs.get("ssl") is not None + + if compression == "deflate": + extensions = enable_server_permessage_deflate(extensions) + elif compression is not None: + raise ValueError(f"unsupported compression: {compression}") + + if subprotocols is not None: + validate_subprotocols(subprotocols) + + # Help mypy and avoid this error: "type[WebSocketServerProtocol] | + # Callable[..., WebSocketServerProtocol]" not callable [misc] + create_protocol = cast(Callable[..., WebSocketServerProtocol], create_protocol) + factory = functools.partial( + create_protocol, + # For backwards compatibility with 10.0 or earlier. Done here in + # addition to WebSocketServerProtocol to trigger the deprecation + # warning once per serve() call rather than once per connection. + remove_path_argument(ws_handler), + ws_server, + host=host, + port=port, + secure=secure, + open_timeout=open_timeout, + ping_interval=ping_interval, + ping_timeout=ping_timeout, + close_timeout=close_timeout, + max_size=max_size, + max_queue=max_queue, + read_limit=read_limit, + write_limit=write_limit, + loop=_loop, + legacy_recv=legacy_recv, + origins=origins, + extensions=extensions, + subprotocols=subprotocols, + extra_headers=extra_headers, + server_header=server_header, + process_request=process_request, + select_subprotocol=select_subprotocol, + logger=logger, + ) + + if kwargs.pop("unix", False): + path: str | None = kwargs.pop("path", None) + # unix_serve(path) must not specify host and port parameters. + assert host is None and port is None + create_server = functools.partial( + loop.create_unix_server, factory, path, **kwargs + ) + else: + create_server = functools.partial( + loop.create_server, factory, host, port, **kwargs + ) + + # This is a coroutine function. + self._create_server = create_server + self.ws_server = ws_server + + # async with serve(...) + + async def __aenter__(self) -> WebSocketServer: + return await self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + ) -> None: + self.ws_server.close() + await self.ws_server.wait_closed() + + # await serve(...) + + def __await__(self) -> Generator[Any, None, WebSocketServer]: + # Create a suitable iterator by calling __await__ on a coroutine. + return self.__await_impl__().__await__() + + async def __await_impl__(self) -> WebSocketServer: + server = await self._create_server() + self.ws_server.wrap(server) + return self.ws_server + + # yield from serve(...) - remove when dropping Python < 3.10 + + __iter__ = __await__ + + +serve = Serve + + +def unix_serve( + # The version that accepts the path in the second argument is deprecated. + ws_handler: ( + Callable[[WebSocketServerProtocol], Awaitable[Any]] + | Callable[[WebSocketServerProtocol, str], Awaitable[Any]] + ), + path: str | None = None, + **kwargs: Any, +) -> Serve: + """ + Start a WebSocket server listening on a Unix socket. + + This function is identical to :func:`serve`, except the ``host`` and + ``port`` arguments are replaced by ``path``. It is only available on Unix. + + Unrecognized keyword arguments are passed the event loop's + :meth:`~asyncio.loop.create_unix_server` method. + + It's useful for deploying a server behind a reverse proxy such as nginx. + + Args: + path: File system path to the Unix socket. + + """ + return serve(ws_handler, path=path, unix=True, **kwargs) + + +def remove_path_argument( + ws_handler: ( + Callable[[WebSocketServerProtocol], Awaitable[Any]] + | Callable[[WebSocketServerProtocol, str], Awaitable[Any]] + ), +) -> Callable[[WebSocketServerProtocol], Awaitable[Any]]: + try: + inspect.signature(ws_handler).bind(None) + except TypeError: + try: + inspect.signature(ws_handler).bind(None, "") + except TypeError: # pragma: no cover + # ws_handler accepts neither one nor two arguments; leave it alone. + pass + else: + # ws_handler accepts two arguments; activate backwards compatibility. + warnings.warn("remove second argument of ws_handler", DeprecationWarning) + + async def _ws_handler(websocket: WebSocketServerProtocol) -> Any: + return await cast( + Callable[[WebSocketServerProtocol, str], Awaitable[Any]], + ws_handler, + )(websocket, websocket.path) + + return _ws_handler + + return cast( + Callable[[WebSocketServerProtocol], Awaitable[Any]], + ws_handler, + ) diff --git a/gestao_raul/Lib/site-packages/websockets/protocol.py b/gestao_raul/Lib/site-packages/websockets/protocol.py new file mode 100644 index 0000000..bc64a21 --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/protocol.py @@ -0,0 +1,758 @@ +from __future__ import annotations + +import enum +import logging +import uuid +from collections.abc import Generator +from typing import Union + +from .exceptions import ( + ConnectionClosed, + ConnectionClosedError, + ConnectionClosedOK, + InvalidState, + PayloadTooBig, + ProtocolError, +) +from .extensions import Extension +from .frames import ( + OK_CLOSE_CODES, + OP_BINARY, + OP_CLOSE, + OP_CONT, + OP_PING, + OP_PONG, + OP_TEXT, + Close, + CloseCode, + Frame, +) +from .http11 import Request, Response +from .streams import StreamReader +from .typing import LoggerLike, Origin, Subprotocol + + +__all__ = [ + "Protocol", + "Side", + "State", + "SEND_EOF", +] + +# Change to Request | Response | Frame when dropping Python < 3.10. +Event = Union[Request, Response, Frame] +"""Events that :meth:`~Protocol.events_received` may return.""" + + +class Side(enum.IntEnum): + """A WebSocket connection is either a server or a client.""" + + SERVER, CLIENT = range(2) + + +SERVER = Side.SERVER +CLIENT = Side.CLIENT + + +class State(enum.IntEnum): + """A WebSocket connection is in one of these four states.""" + + CONNECTING, OPEN, CLOSING, CLOSED = range(4) + + +CONNECTING = State.CONNECTING +OPEN = State.OPEN +CLOSING = State.CLOSING +CLOSED = State.CLOSED + + +SEND_EOF = b"" +"""Sentinel signaling that the TCP connection must be half-closed.""" + + +class Protocol: + """ + Sans-I/O implementation of a WebSocket connection. + + Args: + side: :attr:`~Side.CLIENT` or :attr:`~Side.SERVER`. + state: Initial state of the WebSocket connection. + max_size: Maximum size of incoming messages in bytes; + :obj:`None` disables the limit. + logger: Logger for this connection; depending on ``side``, + defaults to ``logging.getLogger("websockets.client")`` + or ``logging.getLogger("websockets.server")``; + see the :doc:`logging guide <../../topics/logging>` for details. + + """ + + def __init__( + self, + side: Side, + *, + state: State = OPEN, + max_size: int | None = 2**20, + logger: LoggerLike | None = None, + ) -> None: + # Unique identifier. For logs. + self.id: uuid.UUID = uuid.uuid4() + """Unique identifier of the connection. Useful in logs.""" + + # Logger or LoggerAdapter for this connection. + if logger is None: + logger = logging.getLogger(f"websockets.{side.name.lower()}") + self.logger: LoggerLike = logger + """Logger for this connection.""" + + # Track if DEBUG is enabled. Shortcut logging calls if it isn't. + self.debug = logger.isEnabledFor(logging.DEBUG) + + # Connection side. CLIENT or SERVER. + self.side = side + + # Connection state. Initially OPEN because subclasses handle CONNECTING. + self.state = state + + # Maximum size of incoming messages in bytes. + self.max_size = max_size + + # Current size of incoming message in bytes. Only set while reading a + # fragmented message i.e. a data frames with the FIN bit not set. + self.cur_size: int | None = None + + # True while sending a fragmented message i.e. a data frames with the + # FIN bit not set. + self.expect_continuation_frame = False + + # WebSocket protocol parameters. + self.origin: Origin | None = None + self.extensions: list[Extension] = [] + self.subprotocol: Subprotocol | None = None + + # Close code and reason, set when a close frame is sent or received. + self.close_rcvd: Close | None = None + self.close_sent: Close | None = None + self.close_rcvd_then_sent: bool | None = None + + # Track if an exception happened during the handshake. + self.handshake_exc: Exception | None = None + """ + Exception to raise if the opening handshake failed. + + :obj:`None` if the opening handshake succeeded. + + """ + + # Track if send_eof() was called. + self.eof_sent = False + + # Parser state. + self.reader = StreamReader() + self.events: list[Event] = [] + self.writes: list[bytes] = [] + self.parser = self.parse() + next(self.parser) # start coroutine + self.parser_exc: Exception | None = None + + @property + def state(self) -> State: + """ + State of the WebSocket connection. + + Defined in 4.1_, 4.2_, 7.1.3_, and 7.1.4_ of :rfc:`6455`. + + .. _4.1: https://datatracker.ietf.org/doc/html/rfc6455#section-4.1 + .. _4.2: https://datatracker.ietf.org/doc/html/rfc6455#section-4.2 + .. _7.1.3: https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.3 + .. _7.1.4: https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.4 + + """ + return self._state + + @state.setter + def state(self, state: State) -> None: + if self.debug: + self.logger.debug("= connection is %s", state.name) + self._state = state + + @property + def close_code(self) -> int | None: + """ + WebSocket close code received from the remote endpoint. + + Defined in 7.1.5_ of :rfc:`6455`. + + .. _7.1.5: https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.5 + + :obj:`None` if the connection isn't closed yet. + + """ + if self.state is not CLOSED: + return None + elif self.close_rcvd is None: + return CloseCode.ABNORMAL_CLOSURE + else: + return self.close_rcvd.code + + @property + def close_reason(self) -> str | None: + """ + WebSocket close reason received from the remote endpoint. + + Defined in 7.1.6_ of :rfc:`6455`. + + .. _7.1.6: https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.6 + + :obj:`None` if the connection isn't closed yet. + + """ + if self.state is not CLOSED: + return None + elif self.close_rcvd is None: + return "" + else: + return self.close_rcvd.reason + + @property + def close_exc(self) -> ConnectionClosed: + """ + Exception to raise when trying to interact with a closed connection. + + Don't raise this exception while the connection :attr:`state` + is :attr:`~websockets.protocol.State.CLOSING`; wait until + it's :attr:`~websockets.protocol.State.CLOSED`. + + Indeed, the exception includes the close code and reason, which are + known only once the connection is closed. + + Raises: + AssertionError: If the connection isn't closed yet. + + """ + assert self.state is CLOSED, "connection isn't closed yet" + exc_type: type[ConnectionClosed] + if ( + self.close_rcvd is not None + and self.close_sent is not None + and self.close_rcvd.code in OK_CLOSE_CODES + and self.close_sent.code in OK_CLOSE_CODES + ): + exc_type = ConnectionClosedOK + else: + exc_type = ConnectionClosedError + exc: ConnectionClosed = exc_type( + self.close_rcvd, + self.close_sent, + self.close_rcvd_then_sent, + ) + # Chain to the exception raised in the parser, if any. + exc.__cause__ = self.parser_exc + return exc + + # Public methods for receiving data. + + def receive_data(self, data: bytes) -> None: + """ + Receive data from the network. + + After calling this method: + + - You must call :meth:`data_to_send` and send this data to the network. + - You should call :meth:`events_received` and process resulting events. + + Raises: + EOFError: If :meth:`receive_eof` was called earlier. + + """ + self.reader.feed_data(data) + next(self.parser) + + def receive_eof(self) -> None: + """ + Receive the end of the data stream from the network. + + After calling this method: + + - You must call :meth:`data_to_send` and send this data to the network; + it will return ``[b""]``, signaling the end of the stream, or ``[]``. + - You aren't expected to call :meth:`events_received`; it won't return + any new events. + + :meth:`receive_eof` is idempotent. + + """ + if self.reader.eof: + return + self.reader.feed_eof() + next(self.parser) + + # Public methods for sending events. + + def send_continuation(self, data: bytes, fin: bool) -> None: + """ + Send a `Continuation frame`_. + + .. _Continuation frame: + https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 + + Parameters: + data: payload containing the same kind of data + as the initial frame. + fin: FIN bit; set it to :obj:`True` if this is the last frame + of a fragmented message and to :obj:`False` otherwise. + + Raises: + ProtocolError: If a fragmented message isn't in progress. + + """ + if not self.expect_continuation_frame: + raise ProtocolError("unexpected continuation frame") + if self._state is not OPEN: + raise InvalidState(f"connection is {self.state.name.lower()}") + self.expect_continuation_frame = not fin + self.send_frame(Frame(OP_CONT, data, fin)) + + def send_text(self, data: bytes, fin: bool = True) -> None: + """ + Send a `Text frame`_. + + .. _Text frame: + https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 + + Parameters: + data: payload containing text encoded with UTF-8. + fin: FIN bit; set it to :obj:`False` if this is the first frame of + a fragmented message. + + Raises: + ProtocolError: If a fragmented message is in progress. + + """ + if self.expect_continuation_frame: + raise ProtocolError("expected a continuation frame") + if self._state is not OPEN: + raise InvalidState(f"connection is {self.state.name.lower()}") + self.expect_continuation_frame = not fin + self.send_frame(Frame(OP_TEXT, data, fin)) + + def send_binary(self, data: bytes, fin: bool = True) -> None: + """ + Send a `Binary frame`_. + + .. _Binary frame: + https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 + + Parameters: + data: payload containing arbitrary binary data. + fin: FIN bit; set it to :obj:`False` if this is the first frame of + a fragmented message. + + Raises: + ProtocolError: If a fragmented message is in progress. + + """ + if self.expect_continuation_frame: + raise ProtocolError("expected a continuation frame") + if self._state is not OPEN: + raise InvalidState(f"connection is {self.state.name.lower()}") + self.expect_continuation_frame = not fin + self.send_frame(Frame(OP_BINARY, data, fin)) + + def send_close(self, code: int | None = None, reason: str = "") -> None: + """ + Send a `Close frame`_. + + .. _Close frame: + https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.1 + + Parameters: + code: close code. + reason: close reason. + + Raises: + ProtocolError: If the code isn't valid or if a reason is provided + without a code. + + """ + # While RFC 6455 doesn't rule out sending more than one close Frame, + # websockets is conservative in what it sends and doesn't allow that. + if self._state is not OPEN: + raise InvalidState(f"connection is {self.state.name.lower()}") + if code is None: + if reason != "": + raise ProtocolError("cannot send a reason without a code") + close = Close(CloseCode.NO_STATUS_RCVD, "") + data = b"" + else: + close = Close(code, reason) + data = close.serialize() + # 7.1.3. The WebSocket Closing Handshake is Started + self.send_frame(Frame(OP_CLOSE, data)) + # Since the state is OPEN, no close frame was received yet. + # As a consequence, self.close_rcvd_then_sent remains None. + assert self.close_rcvd is None + self.close_sent = close + self.state = CLOSING + + def send_ping(self, data: bytes) -> None: + """ + Send a `Ping frame`_. + + .. _Ping frame: + https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.2 + + Parameters: + data: payload containing arbitrary binary data. + + """ + # RFC 6455 allows control frames after starting the closing handshake. + if self._state is not OPEN and self._state is not CLOSING: + raise InvalidState(f"connection is {self.state.name.lower()}") + self.send_frame(Frame(OP_PING, data)) + + def send_pong(self, data: bytes) -> None: + """ + Send a `Pong frame`_. + + .. _Pong frame: + https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.3 + + Parameters: + data: payload containing arbitrary binary data. + + """ + # RFC 6455 allows control frames after starting the closing handshake. + if self._state is not OPEN and self._state is not CLOSING: + raise InvalidState(f"connection is {self.state.name.lower()}") + self.send_frame(Frame(OP_PONG, data)) + + def fail(self, code: int, reason: str = "") -> None: + """ + `Fail the WebSocket connection`_. + + .. _Fail the WebSocket connection: + https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.7 + + Parameters: + code: close code + reason: close reason + + Raises: + ProtocolError: If the code isn't valid. + """ + # 7.1.7. Fail the WebSocket Connection + + # Send a close frame when the state is OPEN (a close frame was already + # sent if it's CLOSING), except when failing the connection because + # of an error reading from or writing to the network. + if self.state is OPEN: + if code != CloseCode.ABNORMAL_CLOSURE: + close = Close(code, reason) + data = close.serialize() + self.send_frame(Frame(OP_CLOSE, data)) + self.close_sent = close + # If recv_messages() raised an exception upon receiving a close + # frame but before echoing it, then close_rcvd is not None even + # though the state is OPEN. This happens when the connection is + # closed while receiving a fragmented message. + if self.close_rcvd is not None: + self.close_rcvd_then_sent = True + self.state = CLOSING + + # When failing the connection, a server closes the TCP connection + # without waiting for the client to complete the handshake, while a + # client waits for the server to close the TCP connection, possibly + # after sending a close frame that the client will ignore. + if self.side is SERVER and not self.eof_sent: + self.send_eof() + + # 7.1.7. Fail the WebSocket Connection "An endpoint MUST NOT continue + # to attempt to process data(including a responding Close frame) from + # the remote endpoint after being instructed to _Fail the WebSocket + # Connection_." + self.parser = self.discard() + next(self.parser) # start coroutine + + # Public method for getting incoming events after receiving data. + + def events_received(self) -> list[Event]: + """ + Fetch events generated from data received from the network. + + Call this method immediately after any of the ``receive_*()`` methods. + + Process resulting events, likely by passing them to the application. + + Returns: + Events read from the connection. + """ + events, self.events = self.events, [] + return events + + # Public method for getting outgoing data after receiving data or sending events. + + def data_to_send(self) -> list[bytes]: + """ + Obtain data to send to the network. + + Call this method immediately after any of the ``receive_*()``, + ``send_*()``, or :meth:`fail` methods. + + Write resulting data to the connection. + + The empty bytestring :data:`~websockets.protocol.SEND_EOF` signals + the end of the data stream. When you receive it, half-close the TCP + connection. + + Returns: + Data to write to the connection. + + """ + writes, self.writes = self.writes, [] + return writes + + def close_expected(self) -> bool: + """ + Tell if the TCP connection is expected to close soon. + + Call this method immediately after any of the ``receive_*()``, + ``send_close()``, or :meth:`fail` methods. + + If it returns :obj:`True`, schedule closing the TCP connection after a + short timeout if the other side hasn't already closed it. + + Returns: + Whether the TCP connection is expected to close soon. + + """ + # During the opening handshake, when our state is CONNECTING, we expect + # a TCP close if and only if the hansdake fails. When it does, we start + # the TCP closing handshake by sending EOF with send_eof(). + + # Once the opening handshake completes successfully, we expect a TCP + # close if and only if we sent a close frame, meaning that our state + # progressed to CLOSING: + + # * Normal closure: once we send a close frame, we expect a TCP close: + # server waits for client to complete the TCP closing handshake; + # client waits for server to initiate the TCP closing handshake. + + # * Abnormal closure: we always send a close frame and the same logic + # applies, except on EOFError where we don't send a close frame + # because we already received the TCP close, so we don't expect it. + + # If our state is CLOSED, we already received a TCP close so we don't + # expect it anymore. + + # Micro-optimization: put the most common case first + if self.state is OPEN: + return False + if self.state is CLOSING: + return True + if self.state is CLOSED: + return False + assert self.state is CONNECTING + return self.eof_sent + + # Private methods for receiving data. + + def parse(self) -> Generator[None]: + """ + Parse incoming data into frames. + + :meth:`receive_data` and :meth:`receive_eof` run this generator + coroutine until it needs more data or reaches EOF. + + :meth:`parse` never raises an exception. Instead, it sets the + :attr:`parser_exc` and yields control. + + """ + try: + while True: + if (yield from self.reader.at_eof()): + if self.debug: + self.logger.debug("< EOF") + # If the WebSocket connection is closed cleanly, with a + # closing handhshake, recv_frame() substitutes parse() + # with discard(). This branch is reached only when the + # connection isn't closed cleanly. + raise EOFError("unexpected end of stream") + + if self.max_size is None: + max_size = None + elif self.cur_size is None: + max_size = self.max_size + else: + max_size = self.max_size - self.cur_size + + # During a normal closure, execution ends here on the next + # iteration of the loop after receiving a close frame. At + # this point, recv_frame() replaced parse() by discard(). + frame = yield from Frame.parse( + self.reader.read_exact, + mask=self.side is SERVER, + max_size=max_size, + extensions=self.extensions, + ) + + if self.debug: + self.logger.debug("< %s", frame) + + self.recv_frame(frame) + + except ProtocolError as exc: + self.fail(CloseCode.PROTOCOL_ERROR, str(exc)) + self.parser_exc = exc + + except EOFError as exc: + self.fail(CloseCode.ABNORMAL_CLOSURE, str(exc)) + self.parser_exc = exc + + except UnicodeDecodeError as exc: + self.fail(CloseCode.INVALID_DATA, f"{exc.reason} at position {exc.start}") + self.parser_exc = exc + + except PayloadTooBig as exc: + exc.set_current_size(self.cur_size) + self.fail(CloseCode.MESSAGE_TOO_BIG, str(exc)) + self.parser_exc = exc + + except Exception as exc: + self.logger.error("parser failed", exc_info=True) + # Don't include exception details, which may be security-sensitive. + self.fail(CloseCode.INTERNAL_ERROR) + self.parser_exc = exc + + # During an abnormal closure, execution ends here after catching an + # exception. At this point, fail() replaced parse() by discard(). + yield + raise AssertionError("parse() shouldn't step after error") + + def discard(self) -> Generator[None]: + """ + Discard incoming data. + + This coroutine replaces :meth:`parse`: + + - after receiving a close frame, during a normal closure (1.4); + - after sending a close frame, during an abnormal closure (7.1.7). + + """ + # After the opening handshake completes, the server closes the TCP + # connection in the same circumstances where discard() replaces parse(). + # The client closes it when it receives EOF from the server or times + # out. (The latter case cannot be handled in this Sans-I/O layer.) + assert (self.side is SERVER or self.state is CONNECTING) == (self.eof_sent) + while not (yield from self.reader.at_eof()): + self.reader.discard() + if self.debug: + self.logger.debug("< EOF") + # A server closes the TCP connection immediately, while a client + # waits for the server to close the TCP connection. + if self.side is CLIENT and self.state is not CONNECTING: + self.send_eof() + self.state = CLOSED + # If discard() completes normally, execution ends here. + yield + # Once the reader reaches EOF, its feed_data/eof() methods raise an + # error, so our receive_data/eof() methods don't step the generator. + raise AssertionError("discard() shouldn't step after EOF") + + def recv_frame(self, frame: Frame) -> None: + """ + Process an incoming frame. + + """ + if frame.opcode is OP_TEXT or frame.opcode is OP_BINARY: + if self.cur_size is not None: + raise ProtocolError("expected a continuation frame") + if not frame.fin: + self.cur_size = len(frame.data) + + elif frame.opcode is OP_CONT: + if self.cur_size is None: + raise ProtocolError("unexpected continuation frame") + if frame.fin: + self.cur_size = None + else: + self.cur_size += len(frame.data) + + elif frame.opcode is OP_PING: + # 5.5.2. Ping: "Upon receipt of a Ping frame, an endpoint MUST + # send a Pong frame in response" + pong_frame = Frame(OP_PONG, frame.data) + self.send_frame(pong_frame) + + elif frame.opcode is OP_PONG: + # 5.5.3 Pong: "A response to an unsolicited Pong frame is not + # expected." + pass + + elif frame.opcode is OP_CLOSE: + # 7.1.5. The WebSocket Connection Close Code + # 7.1.6. The WebSocket Connection Close Reason + self.close_rcvd = Close.parse(frame.data) + if self.state is CLOSING: + assert self.close_sent is not None + self.close_rcvd_then_sent = False + + if self.cur_size is not None: + raise ProtocolError("incomplete fragmented message") + + # 5.5.1 Close: "If an endpoint receives a Close frame and did + # not previously send a Close frame, the endpoint MUST send a + # Close frame in response. (When sending a Close frame in + # response, the endpoint typically echos the status code it + # received.)" + + if self.state is OPEN: + # Echo the original data instead of re-serializing it with + # Close.serialize() because that fails when the close frame + # is empty and Close.parse() synthesizes a 1005 close code. + # The rest is identical to send_close(). + self.send_frame(Frame(OP_CLOSE, frame.data)) + self.close_sent = self.close_rcvd + self.close_rcvd_then_sent = True + self.state = CLOSING + + # 7.1.2. Start the WebSocket Closing Handshake: "Once an + # endpoint has both sent and received a Close control frame, + # that endpoint SHOULD _Close the WebSocket Connection_" + + # A server closes the TCP connection immediately, while a client + # waits for the server to close the TCP connection. + if self.side is SERVER: + self.send_eof() + + # 1.4. Closing Handshake: "after receiving a control frame + # indicating the connection should be closed, a peer discards + # any further data received." + # RFC 6455 allows reading Ping and Pong frames after a Close frame. + # However, that doesn't seem useful; websockets doesn't support it. + self.parser = self.discard() + next(self.parser) # start coroutine + + else: + # This can't happen because Frame.parse() validates opcodes. + raise AssertionError(f"unexpected opcode: {frame.opcode:02x}") + + self.events.append(frame) + + # Private methods for sending events. + + def send_frame(self, frame: Frame) -> None: + if self.debug: + self.logger.debug("> %s", frame) + self.writes.append( + frame.serialize( + mask=self.side is CLIENT, + extensions=self.extensions, + ) + ) + + def send_eof(self) -> None: + assert not self.eof_sent + self.eof_sent = True + if self.debug: + self.logger.debug("> EOF") + self.writes.append(SEND_EOF) diff --git a/gestao_raul/Lib/site-packages/websockets/py.typed b/gestao_raul/Lib/site-packages/websockets/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/gestao_raul/Lib/site-packages/websockets/server.py b/gestao_raul/Lib/site-packages/websockets/server.py new file mode 100644 index 0000000..1744412 --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/server.py @@ -0,0 +1,587 @@ +from __future__ import annotations + +import base64 +import binascii +import email.utils +import http +import re +import warnings +from collections.abc import Generator, Sequence +from typing import Any, Callable, cast + +from .datastructures import Headers, MultipleValuesError +from .exceptions import ( + InvalidHandshake, + InvalidHeader, + InvalidHeaderValue, + InvalidMessage, + InvalidOrigin, + InvalidUpgrade, + NegotiationError, +) +from .extensions import Extension, ServerExtensionFactory +from .headers import ( + build_extension, + parse_connection, + parse_extension, + parse_subprotocol, + parse_upgrade, +) +from .http11 import Request, Response +from .imports import lazy_import +from .protocol import CONNECTING, OPEN, SERVER, Protocol, State +from .typing import ( + ConnectionOption, + ExtensionHeader, + LoggerLike, + Origin, + StatusLike, + Subprotocol, + UpgradeProtocol, +) +from .utils import accept_key + + +__all__ = ["ServerProtocol"] + + +class ServerProtocol(Protocol): + """ + Sans-I/O implementation of a WebSocket server connection. + + Args: + origins: Acceptable values of the ``Origin`` header. Values can be + :class:`str` to test for an exact match or regular expressions + compiled by :func:`re.compile` to test against a pattern. Include + :obj:`None` in the list if the lack of an origin is acceptable. + This is useful for defending against Cross-Site WebSocket + Hijacking attacks. + extensions: List of supported extensions, in order in which they + should be tried. + subprotocols: List of supported subprotocols, in order of decreasing + preference. + select_subprotocol: Callback for selecting a subprotocol among + those supported by the client and the server. It has the same + signature as the :meth:`select_subprotocol` method, including a + :class:`ServerProtocol` instance as first argument. + state: Initial state of the WebSocket connection. + max_size: Maximum size of incoming messages in bytes; + :obj:`None` disables the limit. + logger: Logger for this connection; + defaults to ``logging.getLogger("websockets.server")``; + see the :doc:`logging guide <../../topics/logging>` for details. + + """ + + def __init__( + self, + *, + origins: Sequence[Origin | re.Pattern[str] | None] | None = None, + extensions: Sequence[ServerExtensionFactory] | None = None, + subprotocols: Sequence[Subprotocol] | None = None, + select_subprotocol: ( + Callable[ + [ServerProtocol, Sequence[Subprotocol]], + Subprotocol | None, + ] + | None + ) = None, + state: State = CONNECTING, + max_size: int | None = 2**20, + logger: LoggerLike | None = None, + ) -> None: + super().__init__( + side=SERVER, + state=state, + max_size=max_size, + logger=logger, + ) + self.origins = origins + self.available_extensions = extensions + self.available_subprotocols = subprotocols + if select_subprotocol is not None: + # Bind select_subprotocol then shadow self.select_subprotocol. + # Use setattr to work around https://github.com/python/mypy/issues/2427. + setattr( + self, + "select_subprotocol", + select_subprotocol.__get__(self, self.__class__), + ) + + def accept(self, request: Request) -> Response: + """ + Create a handshake response to accept the connection. + + If the handshake request is valid and the handshake successful, + :meth:`accept` returns an HTTP response with status code 101. + + Else, it returns an HTTP response with another status code. This rejects + the connection, like :meth:`reject` would. + + You must send the handshake response with :meth:`send_response`. + + You may modify the response before sending it, typically by adding HTTP + headers. + + Args: + request: WebSocket handshake request received from the client. + + Returns: + WebSocket handshake response or HTTP response to send to the client. + + """ + try: + ( + accept_header, + extensions_header, + protocol_header, + ) = self.process_request(request) + except InvalidOrigin as exc: + request._exception = exc + self.handshake_exc = exc + if self.debug: + self.logger.debug("! invalid origin", exc_info=True) + return self.reject( + http.HTTPStatus.FORBIDDEN, + f"Failed to open a WebSocket connection: {exc}.\n", + ) + except InvalidUpgrade as exc: + request._exception = exc + self.handshake_exc = exc + if self.debug: + self.logger.debug("! invalid upgrade", exc_info=True) + response = self.reject( + http.HTTPStatus.UPGRADE_REQUIRED, + ( + f"Failed to open a WebSocket connection: {exc}.\n" + f"\n" + f"You cannot access a WebSocket server directly " + f"with a browser. You need a WebSocket client.\n" + ), + ) + response.headers["Upgrade"] = "websocket" + return response + except InvalidHandshake as exc: + request._exception = exc + self.handshake_exc = exc + if self.debug: + self.logger.debug("! invalid handshake", exc_info=True) + exc_chain = cast(BaseException, exc) + exc_str = f"{exc_chain}" + while exc_chain.__cause__ is not None: + exc_chain = exc_chain.__cause__ + exc_str += f"; {exc_chain}" + return self.reject( + http.HTTPStatus.BAD_REQUEST, + f"Failed to open a WebSocket connection: {exc_str}.\n", + ) + except Exception as exc: + # Handle exceptions raised by user-provided select_subprotocol and + # unexpected errors. + request._exception = exc + self.handshake_exc = exc + self.logger.error("opening handshake failed", exc_info=True) + return self.reject( + http.HTTPStatus.INTERNAL_SERVER_ERROR, + ( + "Failed to open a WebSocket connection.\n" + "See server log for more information.\n" + ), + ) + + headers = Headers() + headers["Date"] = email.utils.formatdate(usegmt=True) + headers["Upgrade"] = "websocket" + headers["Connection"] = "Upgrade" + headers["Sec-WebSocket-Accept"] = accept_header + if extensions_header is not None: + headers["Sec-WebSocket-Extensions"] = extensions_header + if protocol_header is not None: + headers["Sec-WebSocket-Protocol"] = protocol_header + return Response(101, "Switching Protocols", headers) + + def process_request( + self, + request: Request, + ) -> tuple[str, str | None, str | None]: + """ + Check a handshake request and negotiate extensions and subprotocol. + + This function doesn't verify that the request is an HTTP/1.1 or higher + GET request and doesn't check the ``Host`` header. These controls are + usually performed earlier in the HTTP request handling code. They're + the responsibility of the caller. + + Args: + request: WebSocket handshake request received from the client. + + Returns: + ``Sec-WebSocket-Accept``, ``Sec-WebSocket-Extensions``, and + ``Sec-WebSocket-Protocol`` headers for the handshake response. + + Raises: + InvalidHandshake: If the handshake request is invalid; + then the server must return 400 Bad Request error. + + """ + headers = request.headers + + connection: list[ConnectionOption] = sum( + [parse_connection(value) for value in headers.get_all("Connection")], [] + ) + if not any(value.lower() == "upgrade" for value in connection): + raise InvalidUpgrade( + "Connection", ", ".join(connection) if connection else None + ) + + upgrade: list[UpgradeProtocol] = sum( + [parse_upgrade(value) for value in headers.get_all("Upgrade")], [] + ) + # For compatibility with non-strict implementations, ignore case when + # checking the Upgrade header. The RFC always uses "websocket", except + # in section 11.2. (IANA registration) where it uses "WebSocket". + if not (len(upgrade) == 1 and upgrade[0].lower() == "websocket"): + raise InvalidUpgrade("Upgrade", ", ".join(upgrade) if upgrade else None) + + try: + key = headers["Sec-WebSocket-Key"] + except KeyError: + raise InvalidHeader("Sec-WebSocket-Key") from None + except MultipleValuesError: + raise InvalidHeader("Sec-WebSocket-Key", "multiple values") from None + try: + raw_key = base64.b64decode(key.encode(), validate=True) + except binascii.Error as exc: + raise InvalidHeaderValue("Sec-WebSocket-Key", key) from exc + if len(raw_key) != 16: + raise InvalidHeaderValue("Sec-WebSocket-Key", key) + accept_header = accept_key(key) + + try: + version = headers["Sec-WebSocket-Version"] + except KeyError: + raise InvalidHeader("Sec-WebSocket-Version") from None + except MultipleValuesError: + raise InvalidHeader("Sec-WebSocket-Version", "multiple values") from None + if version != "13": + raise InvalidHeaderValue("Sec-WebSocket-Version", version) + + self.origin = self.process_origin(headers) + extensions_header, self.extensions = self.process_extensions(headers) + protocol_header = self.subprotocol = self.process_subprotocol(headers) + + return (accept_header, extensions_header, protocol_header) + + def process_origin(self, headers: Headers) -> Origin | None: + """ + Handle the Origin HTTP request header. + + Args: + headers: WebSocket handshake request headers. + + Returns: + origin, if it is acceptable. + + Raises: + InvalidHandshake: If the Origin header is invalid. + InvalidOrigin: If the origin isn't acceptable. + + """ + # "The user agent MUST NOT include more than one Origin header field" + # per https://datatracker.ietf.org/doc/html/rfc6454#section-7.3. + try: + origin = headers.get("Origin") + except MultipleValuesError: + raise InvalidHeader("Origin", "multiple values") from None + if origin is not None: + origin = cast(Origin, origin) + if self.origins is not None: + for origin_or_regex in self.origins: + if origin_or_regex == origin or ( + isinstance(origin_or_regex, re.Pattern) + and origin is not None + and origin_or_regex.fullmatch(origin) is not None + ): + break + else: + raise InvalidOrigin(origin) + return origin + + def process_extensions( + self, + headers: Headers, + ) -> tuple[str | None, list[Extension]]: + """ + Handle the Sec-WebSocket-Extensions HTTP request header. + + Accept or reject each extension proposed in the client request. + Negotiate parameters for accepted extensions. + + Per :rfc:`6455`, negotiation rules are defined by the specification of + each extension. + + To provide this level of flexibility, for each extension proposed by + the client, we check for a match with each extension available in the + server configuration. If no match is found, the extension is ignored. + + If several variants of the same extension are proposed by the client, + it may be accepted several times, which won't make sense in general. + Extensions must implement their own requirements. For this purpose, + the list of previously accepted extensions is provided. + + This process doesn't allow the server to reorder extensions. It can + only select a subset of the extensions proposed by the client. + + Other requirements, for example related to mandatory extensions or the + order of extensions, may be implemented by overriding this method. + + Args: + headers: WebSocket handshake request headers. + + Returns: + ``Sec-WebSocket-Extensions`` HTTP response header and list of + accepted extensions. + + Raises: + InvalidHandshake: If the Sec-WebSocket-Extensions header is invalid. + + """ + response_header_value: str | None = None + + extension_headers: list[ExtensionHeader] = [] + accepted_extensions: list[Extension] = [] + + header_values = headers.get_all("Sec-WebSocket-Extensions") + + if header_values and self.available_extensions: + parsed_header_values: list[ExtensionHeader] = sum( + [parse_extension(header_value) for header_value in header_values], [] + ) + + for name, request_params in parsed_header_values: + for ext_factory in self.available_extensions: + # Skip non-matching extensions based on their name. + if ext_factory.name != name: + continue + + # Skip non-matching extensions based on their params. + try: + response_params, extension = ext_factory.process_request_params( + request_params, accepted_extensions + ) + except NegotiationError: + continue + + # Add matching extension to the final list. + extension_headers.append((name, response_params)) + accepted_extensions.append(extension) + + # Break out of the loop once we have a match. + break + + # If we didn't break from the loop, no extension in our list + # matched what the client sent. The extension is declined. + + # Serialize extension header. + if extension_headers: + response_header_value = build_extension(extension_headers) + + return response_header_value, accepted_extensions + + def process_subprotocol(self, headers: Headers) -> Subprotocol | None: + """ + Handle the Sec-WebSocket-Protocol HTTP request header. + + Args: + headers: WebSocket handshake request headers. + + Returns: + Subprotocol, if one was selected; this is also the value of the + ``Sec-WebSocket-Protocol`` response header. + + Raises: + InvalidHandshake: If the Sec-WebSocket-Subprotocol header is invalid. + + """ + subprotocols: Sequence[Subprotocol] = sum( + [ + parse_subprotocol(header_value) + for header_value in headers.get_all("Sec-WebSocket-Protocol") + ], + [], + ) + return self.select_subprotocol(subprotocols) + + def select_subprotocol( + self, + subprotocols: Sequence[Subprotocol], + ) -> Subprotocol | None: + """ + Pick a subprotocol among those offered by the client. + + If several subprotocols are supported by both the client and the server, + pick the first one in the list declared the server. + + If the server doesn't support any subprotocols, continue without a + subprotocol, regardless of what the client offers. + + If the server supports at least one subprotocol and the client doesn't + offer any, abort the handshake with an HTTP 400 error. + + You provide a ``select_subprotocol`` argument to :class:`ServerProtocol` + to override this logic. For example, you could accept the connection + even if client doesn't offer a subprotocol, rather than reject it. + + Here's how to negotiate the ``chat`` subprotocol if the client supports + it and continue without a subprotocol otherwise:: + + def select_subprotocol(protocol, subprotocols): + if "chat" in subprotocols: + return "chat" + + Args: + subprotocols: List of subprotocols offered by the client. + + Returns: + Selected subprotocol, if a common subprotocol was found. + + :obj:`None` to continue without a subprotocol. + + Raises: + NegotiationError: Custom implementations may raise this exception + to abort the handshake with an HTTP 400 error. + + """ + # Server doesn't offer any subprotocols. + if not self.available_subprotocols: # None or empty list + return None + + # Server offers at least one subprotocol but client doesn't offer any. + if not subprotocols: + raise NegotiationError("missing subprotocol") + + # Server and client both offer subprotocols. Look for a shared one. + proposed_subprotocols = set(subprotocols) + for subprotocol in self.available_subprotocols: + if subprotocol in proposed_subprotocols: + return subprotocol + + # No common subprotocol was found. + raise NegotiationError( + "invalid subprotocol; expected one of " + + ", ".join(self.available_subprotocols) + ) + + def reject(self, status: StatusLike, text: str) -> Response: + """ + Create a handshake response to reject the connection. + + A short plain text response is the best fallback when failing to + establish a WebSocket connection. + + You must send the handshake response with :meth:`send_response`. + + You may modify the response before sending it, for example by changing + HTTP headers. + + Args: + status: HTTP status code. + text: HTTP response body; it will be encoded to UTF-8. + + Returns: + HTTP response to send to the client. + + """ + # If status is an int instead of an HTTPStatus, fix it automatically. + status = http.HTTPStatus(status) + body = text.encode() + headers = Headers( + [ + ("Date", email.utils.formatdate(usegmt=True)), + ("Connection", "close"), + ("Content-Length", str(len(body))), + ("Content-Type", "text/plain; charset=utf-8"), + ] + ) + return Response(status.value, status.phrase, headers, body) + + def send_response(self, response: Response) -> None: + """ + Send a handshake response to the client. + + Args: + response: WebSocket handshake response event to send. + + """ + if self.debug: + code, phrase = response.status_code, response.reason_phrase + self.logger.debug("> HTTP/1.1 %d %s", code, phrase) + for key, value in response.headers.raw_items(): + self.logger.debug("> %s: %s", key, value) + if response.body: + self.logger.debug("> [body] (%d bytes)", len(response.body)) + + self.writes.append(response.serialize()) + + if response.status_code == 101: + assert self.state is CONNECTING + self.state = OPEN + self.logger.info("connection open") + + else: + self.logger.info( + "connection rejected (%d %s)", + response.status_code, + response.reason_phrase, + ) + + self.send_eof() + self.parser = self.discard() + next(self.parser) # start coroutine + + def parse(self) -> Generator[None]: + if self.state is CONNECTING: + try: + request = yield from Request.parse( + self.reader.read_line, + ) + except Exception as exc: + self.handshake_exc = InvalidMessage( + "did not receive a valid HTTP request" + ) + self.handshake_exc.__cause__ = exc + self.send_eof() + self.parser = self.discard() + next(self.parser) # start coroutine + yield + + if self.debug: + self.logger.debug("< GET %s HTTP/1.1", request.path) + for key, value in request.headers.raw_items(): + self.logger.debug("< %s: %s", key, value) + + self.events.append(request) + + yield from super().parse() + + +class ServerConnection(ServerProtocol): + def __init__(self, *args: Any, **kwargs: Any) -> None: + warnings.warn( # deprecated in 11.0 - 2023-04-02 + "ServerConnection was renamed to ServerProtocol", + DeprecationWarning, + ) + super().__init__(*args, **kwargs) + + +lazy_import( + globals(), + deprecated_aliases={ + # deprecated in 14.0 - 2024-11-09 + "WebSocketServer": ".legacy.server", + "WebSocketServerProtocol": ".legacy.server", + "broadcast": ".legacy.server", + "serve": ".legacy.server", + "unix_serve": ".legacy.server", + }, +) diff --git a/gestao_raul/Lib/site-packages/websockets/speedups.c b/gestao_raul/Lib/site-packages/websockets/speedups.c new file mode 100644 index 0000000..cb10ded --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/speedups.c @@ -0,0 +1,222 @@ +/* C implementation of performance sensitive functions. */ + +#define PY_SSIZE_T_CLEAN +#include +#include /* uint8_t, uint32_t, uint64_t */ + +#if __ARM_NEON +#include +#elif __SSE2__ +#include +#endif + +static const Py_ssize_t MASK_LEN = 4; + +/* Similar to PyBytes_AsStringAndSize, but accepts more types */ + +static int +_PyBytesLike_AsStringAndSize(PyObject *obj, PyObject **tmp, char **buffer, Py_ssize_t *length) +{ + // This supports bytes, bytearrays, and memoryview objects, + // which are common data structures for handling byte streams. + // If *tmp isn't NULL, the caller gets a new reference. + if (PyBytes_Check(obj)) + { + *tmp = NULL; + *buffer = PyBytes_AS_STRING(obj); + *length = PyBytes_GET_SIZE(obj); + } + else if (PyByteArray_Check(obj)) + { + *tmp = NULL; + *buffer = PyByteArray_AS_STRING(obj); + *length = PyByteArray_GET_SIZE(obj); + } + else if (PyMemoryView_Check(obj)) + { + *tmp = PyMemoryView_GetContiguous(obj, PyBUF_READ, 'C'); + if (*tmp == NULL) + { + return -1; + } + Py_buffer *mv_buf; + mv_buf = PyMemoryView_GET_BUFFER(*tmp); + *buffer = mv_buf->buf; + *length = mv_buf->len; + } + else + { + PyErr_Format( + PyExc_TypeError, + "expected a bytes-like object, %.200s found", + Py_TYPE(obj)->tp_name); + return -1; + } + + return 0; +} + +/* C implementation of websockets.utils.apply_mask */ + +static PyObject * +apply_mask(PyObject *self, PyObject *args, PyObject *kwds) +{ + + // In order to support various bytes-like types, accept any Python object. + + static char *kwlist[] = {"data", "mask", NULL}; + PyObject *input_obj; + PyObject *mask_obj; + + // A pointer to a char * + length will be extracted from the data and mask + // arguments, possibly via a Py_buffer. + + PyObject *input_tmp = NULL; + char *input; + Py_ssize_t input_len; + PyObject *mask_tmp = NULL; + char *mask; + Py_ssize_t mask_len; + + // Initialize a PyBytesObject then get a pointer to the underlying char * + // in order to avoid an extra memory copy in PyBytes_FromStringAndSize. + + PyObject *result = NULL; + char *output; + + // Other variables. + + Py_ssize_t i = 0; + + // Parse inputs. + + if (!PyArg_ParseTupleAndKeywords( + args, kwds, "OO", kwlist, &input_obj, &mask_obj)) + { + goto exit; + } + + if (_PyBytesLike_AsStringAndSize(input_obj, &input_tmp, &input, &input_len) == -1) + { + goto exit; + } + + if (_PyBytesLike_AsStringAndSize(mask_obj, &mask_tmp, &mask, &mask_len) == -1) + { + goto exit; + } + + if (mask_len != MASK_LEN) + { + PyErr_SetString(PyExc_ValueError, "mask must contain 4 bytes"); + goto exit; + } + + // Create output. + + result = PyBytes_FromStringAndSize(NULL, input_len); + if (result == NULL) + { + goto exit; + } + + // Since we just created result, we don't need error checks. + output = PyBytes_AS_STRING(result); + + // Perform the masking operation. + + // Apparently GCC cannot figure out the following optimizations by itself. + + // We need a new scope for MSVC 2010 (non C99 friendly) + { +#if __ARM_NEON + + // With NEON support, XOR by blocks of 16 bytes = 128 bits. + + Py_ssize_t input_len_128 = input_len & ~15; + uint8x16_t mask_128 = vreinterpretq_u8_u32(vdupq_n_u32(*(uint32_t *)mask)); + + for (; i < input_len_128; i += 16) + { + uint8x16_t in_128 = vld1q_u8((uint8_t *)(input + i)); + uint8x16_t out_128 = veorq_u8(in_128, mask_128); + vst1q_u8((uint8_t *)(output + i), out_128); + } + +#elif __SSE2__ + + // With SSE2 support, XOR by blocks of 16 bytes = 128 bits. + + // Since we cannot control the 16-bytes alignment of input and output + // buffers, we rely on loadu/storeu rather than load/store. + + Py_ssize_t input_len_128 = input_len & ~15; + __m128i mask_128 = _mm_set1_epi32(*(uint32_t *)mask); + + for (; i < input_len_128; i += 16) + { + __m128i in_128 = _mm_loadu_si128((__m128i *)(input + i)); + __m128i out_128 = _mm_xor_si128(in_128, mask_128); + _mm_storeu_si128((__m128i *)(output + i), out_128); + } + +#else + + // Without SSE2 support, XOR by blocks of 8 bytes = 64 bits. + + // We assume the memory allocator aligns everything on 8 bytes boundaries. + + Py_ssize_t input_len_64 = input_len & ~7; + uint32_t mask_32 = *(uint32_t *)mask; + uint64_t mask_64 = ((uint64_t)mask_32 << 32) | (uint64_t)mask_32; + + for (; i < input_len_64; i += 8) + { + *(uint64_t *)(output + i) = *(uint64_t *)(input + i) ^ mask_64; + } + +#endif + } + + // XOR the remainder of the input byte by byte. + + for (; i < input_len; i++) + { + output[i] = input[i] ^ mask[i & (MASK_LEN - 1)]; + } + +exit: + Py_XDECREF(input_tmp); + Py_XDECREF(mask_tmp); + return result; + +} + +static PyMethodDef speedups_methods[] = { + { + "apply_mask", + (PyCFunction)apply_mask, + METH_VARARGS | METH_KEYWORDS, + "Apply masking to the data of a WebSocket message.", + }, + {NULL, NULL, 0, NULL}, /* Sentinel */ +}; + +static struct PyModuleDef speedups_module = { + PyModuleDef_HEAD_INIT, + "websocket.speedups", /* m_name */ + "C implementation of performance sensitive functions.", + /* m_doc */ + -1, /* m_size */ + speedups_methods, /* m_methods */ + NULL, + NULL, + NULL, + NULL +}; + +PyMODINIT_FUNC +PyInit_speedups(void) +{ + return PyModule_Create(&speedups_module); +} diff --git a/gestao_raul/Lib/site-packages/websockets/speedups.cp310-win_amd64.pyd b/gestao_raul/Lib/site-packages/websockets/speedups.cp310-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..6d7a53068e9eebc836a32dfc4e7a2c894cd9b2f2 GIT binary patch literal 11776 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 WliD-7hwUxdTfX;i-GzV6 bytes: ... diff --git a/gestao_raul/Lib/site-packages/websockets/streams.py b/gestao_raul/Lib/site-packages/websockets/streams.py new file mode 100644 index 0000000..f52e619 --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/streams.py @@ -0,0 +1,151 @@ +from __future__ import annotations + +from collections.abc import Generator + + +class StreamReader: + """ + Generator-based stream reader. + + This class doesn't support concurrent calls to :meth:`read_line`, + :meth:`read_exact`, or :meth:`read_to_eof`. Make sure calls are + serialized. + + """ + + def __init__(self) -> None: + self.buffer = bytearray() + self.eof = False + + def read_line(self, m: int) -> Generator[None, None, bytes]: + """ + Read a LF-terminated line from the stream. + + This is a generator-based coroutine. + + The return value includes the LF character. + + Args: + m: Maximum number bytes to read; this is a security limit. + + Raises: + EOFError: If the stream ends without a LF. + RuntimeError: If the stream ends in more than ``m`` bytes. + + """ + n = 0 # number of bytes to read + p = 0 # number of bytes without a newline + while True: + n = self.buffer.find(b"\n", p) + 1 + if n > 0: + break + p = len(self.buffer) + if p > m: + raise RuntimeError(f"read {p} bytes, expected no more than {m} bytes") + if self.eof: + raise EOFError(f"stream ends after {p} bytes, before end of line") + yield + if n > m: + raise RuntimeError(f"read {n} bytes, expected no more than {m} bytes") + r = self.buffer[:n] + del self.buffer[:n] + return r + + def read_exact(self, n: int) -> Generator[None, None, bytes]: + """ + Read a given number of bytes from the stream. + + This is a generator-based coroutine. + + Args: + n: How many bytes to read. + + Raises: + EOFError: If the stream ends in less than ``n`` bytes. + + """ + assert n >= 0 + while len(self.buffer) < n: + if self.eof: + p = len(self.buffer) + raise EOFError(f"stream ends after {p} bytes, expected {n} bytes") + yield + r = self.buffer[:n] + del self.buffer[:n] + return r + + def read_to_eof(self, m: int) -> Generator[None, None, bytes]: + """ + Read all bytes from the stream. + + This is a generator-based coroutine. + + Args: + m: Maximum number bytes to read; this is a security limit. + + Raises: + RuntimeError: If the stream ends in more than ``m`` bytes. + + """ + while not self.eof: + p = len(self.buffer) + if p > m: + raise RuntimeError(f"read {p} bytes, expected no more than {m} bytes") + yield + r = self.buffer[:] + del self.buffer[:] + return r + + def at_eof(self) -> Generator[None, None, bool]: + """ + Tell whether the stream has ended and all data was read. + + This is a generator-based coroutine. + + """ + while True: + if self.buffer: + return False + if self.eof: + return True + # When all data was read but the stream hasn't ended, we can't + # tell if until either feed_data() or feed_eof() is called. + yield + + def feed_data(self, data: bytes) -> None: + """ + Write data to the stream. + + :meth:`feed_data` cannot be called after :meth:`feed_eof`. + + Args: + data: Data to write. + + Raises: + EOFError: If the stream has ended. + + """ + if self.eof: + raise EOFError("stream ended") + self.buffer += data + + def feed_eof(self) -> None: + """ + End the stream. + + :meth:`feed_eof` cannot be called more than once. + + Raises: + EOFError: If the stream has ended. + + """ + if self.eof: + raise EOFError("stream ended") + self.eof = True + + def discard(self) -> None: + """ + Discard all buffered data, but don't end the stream. + + """ + del self.buffer[:] diff --git a/gestao_raul/Lib/site-packages/websockets/sync/__init__.py b/gestao_raul/Lib/site-packages/websockets/sync/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gestao_raul/Lib/site-packages/websockets/sync/__pycache__/__init__.cpython-310.pyc b/gestao_raul/Lib/site-packages/websockets/sync/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fde3f062cced399504b77e3f2ac5eefbbde88196 GIT binary patch literal 181 zcmd1j<>g`kg3~g`(n0iN5P=LBfgA@QE@lA|DGb33nv8xc8Hzx{2;x_SvsFxJacWU< zOnGWfNq&q=esXDUYF+oetc14X-QRXW=X1UL1J=tB2WgX wC#g6;IXktaSiiV3FIhi6J~J<~BtBlRpz;=nO>TZlX-=vg$i`wOAi=@_0ObfTMgRZ+ literal 0 HcmV?d00001 diff --git a/gestao_raul/Lib/site-packages/websockets/sync/__pycache__/client.cpython-310.pyc b/gestao_raul/Lib/site-packages/websockets/sync/__pycache__/client.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..90f261170fa398e0d9d41626e28890419573ae80 GIT binary patch literal 17113 zcmbt*TW}m#dR})=&lLj@1Of0)HMx|?(Gno3m9)E9Sr)~MM2i9$0$MIx)Xog18^C~b zfll`j#MEFdi%PBSioEN@b~&*&0b}PuRe6i!B$cGHxm4m*lB%Tg;7TVCNx3RjOr%oSUnUxnuRbo3D?% z9r@4*^RyQk{~w@`oHeO}5YYhSOQanID}+`0O^J6}KRo~@sA&!KLwH&r`df5Cl0 z^80GvsK4mGXc}vVH~lwF_oX)sZ^oPb$na+U7ak+PCWq?gHjDykDq&2kU#q zH2j5)pNqe*;`deWtxs(C=P}1M`SR&CZ{1t+mOrxH3%88Mgl+h*Z~q_D;0HDD=$hL` z!`w13Yt`HEZh7CstQWnzp6k7fewX4s-1#Ko)x7t-?_-UZ{c*4Oz;v(Rx8#p`W&g^m zbyD=2RIs;);Zdj4vy9CFfwE3InHE0$Z~dQ(-~ zvg6`PIjB~moUU4L24OTKD?^uJ&|2Ay_Yz<;xp$T>-z;8ueTwC)M_aCLMtLj>{5sfYNCjhXMvezho z%U@Y)R_^-YJ2$UJ*)=~bViotdqp{7h3jFvXs~=jb`pDGK&s}Ub8h(Z6i1{;@FmtWi z;KtJxT@&S7jq3ek_i0i?v)`>kj2Qe&>IXBpgN$#ure}cCm~KMwFX7o<@)O%T@k3+P z^rpOhA5DP%O#4Z1dNtvF4Y%nqur}qLl=@@dGau#MwD+t$Io@$RWqxSbhMNU8KBrD$ zit_n{VdNbAE!3OdLMdDa483Y&t>jeen>D`<*vhdv%~hxD^Z@1PV|U`60RrmGJH`y%a;MsGw)~YqkYGXPWefgc z*%cu6>3GJ1?!Ub5#}i=?|9%zoHkzuAYrE}it^1f84f?=wssSL@41CYos)p-2k9yCS zxz3+Ecg{I|{jGw7(eQBIIlb1DEpL^#rI5Xvtq?~b!${Y82isQVY5G+_3;JglpQWQOHdZZH6$b#U7)0a%%l6g0tR+5gp`{DXQn?Y#T|Ilf z+qaM8kEU^T9#MXHG>PO>I~pGXLX?-sc(>8`(B`9TZ^Kbauk!J@{+-u9Q3gmSi%^_H zok8+AzglaSL#N{`HXFX0WBELjv#fj`;E${D6Si=3-|ZKj0nED9??ox)hb`5JYz|bx z^_M>zX&T=Qz9=(;=aK1vL59Yz`4G9H6($}S8+OO+Se-=2?j$=Y&-^$=W(;y-Evm1h z2Z(C6M?=&BltkkyCfr3;x#vYmTvT60nIcf88WxL%bYus9ZB?B^WAy?P?ssH|pPEs& z2wnr)QY?OIJpS&*g*)%y=z=?2el2X?xzwy^yt#Ap=IfU)-dXd5u-q)Fa;tXdT6N`4 z0ID#zS+3j#+hd>jst0F-?MCISCT8=S+iC$*zl?9d6Si|XGi6!>pWNg2!BNUK-`&Y# zG*$MzDsNl4R$Lc86g-|9;KT6|MSELVdJ#)$gmJsaxgb>C>U4}a6&_0Wk9i)>MIO#B zG96>X2u&P;`J*!?pRC=492Abovs$J)fHHd{xtr>k4^6ay9EQ@9acSC1bgYL~+%v;c zwz>uB)&`GwkT8u#=DZQSw1rZBZsUtq+1*?x@epEhIOf?8%nwbJSsQs7dr{{<#QZ>s zWcz&YR^9Lw$ZcINbaT^}ySxq(5UiK)`tkkc?JJn>gnxe%Ox5>_IKt3he4OJ!&s`w2 z+VcdB=a3X`tygQlvsDJb!4OEydjo>($Fjn>nLXZLegpnR1ZmPb;jyC6w^2?gU2`P2Qd-E4Tb_=%p4kvq!$?Hrm zF?qF@{5*0|w%6w^JO+o5Oyu%r#+<-+5}zZF^O=m9Pi0K|%l4rsaIe=rO8S(8f3k(8P#JJ zWtf7cLAc14T)KuH0jF*vS;IFVh0LWUa8W1B$wd3u6CfHIvO>|V`^S8b-krundptC- zVbD8X1R^u9lmxz2ym=_mKJvug2SzMRXbO8C?ez`l@fP(BmI+Id zobVONk-4ZBnY_e=l(CS}D1Ctig4du(F>*_veoI@)$&m{T^tT4`Ir4!97<{o9<%&g! zpe?8r#VB8dL{P59HR)o}YgURyq0Wkmxq6!kaX>90iN<5zQ1NRuwCi6oftnx;rQIX= z${ouXwrL&Hlzr+?to_X8Kbtr*J0VhWl#X?nWmVuD#Eq(9Od5(@u`mse1xYbV1VJs@ zqXmc}H5slqNhbiD3=;Dq z1=EP}_y`sgAI|*J(zT14n0A}oK@%lN@E#kFYh9~b`+b(3>T0;}Y7`ySC*SL~F3NPh z)&uHVuSp3_Mp3ZLZih4rjWyaH1l|0fK03qc4v-Kcefza28#S!FuzBFC}JGLmm~<(-wftKqrs&~oA-HE zXXYM$J2SiB$PdjOD>Qd4FA*llvqQUMJTP}=JF^?f;Dq!s)rl|_b7b~`b?KqCvtRnB zJrnr@o&D;~&H)+!703vj=N0tH2p4C$iF%gf7v)(ghmeQo8)3SWdSF5VNPD@vmbx8g zI!UY`*%(6!TX^V!{O=e)v83OAweFcaxp=&WdAu^4uI{1m^U%Pud2Uyy; z)5*!2{zo{~$zg^=okJV@JkuNhi?q*7VEz9zobDXz%yte6eD)AN-sH#jj@fuMZo607 zPND68my&&V6Y7sTV>0d^VXxENWjLcpSxt1Zov}{doB1TsnfX2Q*G!CbFr4+UPjJ?m z-TmJFc-1_;{Kf&C+(CQ~ty-&Q)#&!&?ywfZdH;{iU=q)V(fSC!N4@+*$YeZmz~(KD zC&(QQkL^D5(Aa@cPAH<}S&qGv7=aX5&f0HxtPKaeKH(ks81QV2hsU)@kLSeB;qV0J z%6ASwh+(Z)6MhY_5|O{Nx3kYX_;G6INM{=Tj&vqEdpeVyDen-5V&WgFqiADi2X zG9%V2_=I`i$9kS6GyTxmGTLLEqr1;_GJu*sOYCf*?~miYM|V%LN9X9q>D@xNFM2+& zOCgUOF8{ZQ*nkn=J?eKHih4hS{`DVXhd~&>g8=`#dpvf-8ef4G$mD3G@u}; zA%g|QioWhLPu*p{BK9|0YheB8s;Ye>hc-88vUGZWe!jq2dSmp}We6e;s{499))AAk z0pkTMdCfZPL6yFtgT@aEezC!WYmY}6*2e+)1qb#u8j_xNK7F~8d}zVBp?nM-$k@vl zZ#Xe`!WlAGDmhK4?Me(v9FFW)%UJ+JPd4MOL>TLyymK2zMwoL4T{}WN^b;JigaaKK zO1Hov1+8jG>!&lYvR+Ft%LND6xLRFnDQ!HXb|UstLTdTi5@hBsz+xZ|Y=4;BFF0>< zYZNU+$>C->LQnFaCrF0-luEtD5CTtulpc=w)P+p1hZbKJobQ%vEx&tOrIP0AfVBE;EjZV3nj9~-)B)l9wKP${_yx-~Gx~O6r`x?%aUTIp zU^S1|rQxqN!z$#N_^!2Jeq;;&4T@S%n{=>#U|KB9^LfC~P}X{baX^S_tG=RrzCX!; z1h?QU<48{Q4d8RJ@OYxXKVBhiFURt?F_9>i{oy#xl1~CPFfd>Zg0j$D*;pviRwIvI zH4wxDkkLadWJ8NxfTT>zSWm!o)>^nE&MWitXYn64H>;K4>_GjiB{@mY56jgW^b#(G ze@~h+oYPO5G71aM1+`KQRatF2Z!Rz2h|vQm0W+JYO?-MlhedK#yL!K147Bh5q)*sI z2GCIYmP&nN0N@krUz=QbgrN_wK z{b~&0{CW-$ml(8wIQ0r`te486O`%nn7Rd60?I85)WEy>1)k7;sqmrHiSQQKnHH=Se zRP}?e!X+7l8qJ@VF6v49ENBG6#SwbRaSSq z9CxL)3a&%<3|Q)_0{e$_h-@rZdi^8eyd%W!{L68dd0|eq=GNd0!(_3yl%e~5_N?M`qu@;`j2nUV@TkzY)DLPIi$Y4M%M7#b8U0)I#zH}rwDAy0{Wp!9Kif4MWE6DV^b;*2B zICn;R#ynwovL}0y9D<9tx~kM-G)5B~F9l@g_OXEnct8RW1@a|<9;(`NNP6I7EuXtvcUo_IU^oy1}R*LY&2MJ&(y0hOhQfJQ*V&=Y5j<6%wy@5Ynn7Tyt)l@ zR{LP9(HHXvq)ia=M~rcR8xX?;(L~T}sfu5W1sAw~aE>AUChkS0af#SG@nl z7sMMA<@)4iz<(jG3!d;V;T!x-n6YraTsRIjKyzGT!^-1 zY>ma^$#d@UM25Xk_PLeR`$?IZ&nJ=` ziKQ8oD#2~SaLA>ZKmpxeVl=;mj5Zmo{T)ji%W3LVGx7viwSMww+_k*qjtQrE20c<8 z!%M?lYtdW_Q)0FpnNOM$p9~doYOrHy4>qhgW#=6b&#rZ*kM}6c zY&R_=`=xbF8-kbOf)~qi!HZX~_vBZR2@xB6x=&f{vfqucM`i94^ck5SvXnXu4s>U>XKhMF9$dSCB#}oJNy_y>4DwTvEauNp0ltqjW4X zs%tDd$K*Ge>_JjUDd8Y;N^(F%4|hxsH|D(w#E6U{%&(x;-zUPHKnCO%q+5TGdpw!3 z5{Jxb5J!^P%m>z&itUF^+-5&~<;2NSLxgeA1%>#m_S~k}zvg5WyhX9WR>VetVE7P# zpkr6XsUWfef)t*zrKfG+KXL32#ldl*c z`ib8G+5ZVp{GrjYiQ@YizLr^BhU~eoTtmQRfXK8;t>q!`m*S62`@6b~mA3Z@M$KIq zjAw3V;$UQ_Jth{=v*+i}kLLN|5bcMw32~z5*e}K=(f`6zm_>`(WXhZsPM-tfSj>HSz*e7f z7}gJQ)-(!8G30QjFa+aa=z7NCSh)WdD24@K$A{Do+Q+g0KSP9e)3Hm|0QIopu(mt4 zdX7}2gC{s;(Q`LP$6YwqNzj%2K3#?R?X{Rr%!T8fY{C*>i|aAS{+ z_@%q1y5^gN7?1uCkgFmV&uE{f zggzLi))nsPC~Im%`6j7<`y5_3KP7_Q`IB#TCo-GF)- zjW5AXaFy>JP&H6D*eQuswS1_d@|FN3dbn!fd5tNb&_E%7<0vbnwX{vj$8b}=kPxM0 zsd#^ZQ>x!(*XvA1@Oeho9D@tUtmF9p7qR(GGzSNbKbD#L%uZ=yXj7Jj+-v=8A~Oj& z^njU3On#BiW&R}d0VQPOJwA|GtuIJ8^GWsloO=gJk2odpK$6L?&`&n$q##j}7a}I? zA$VcPTM+Zj*ma*0cR%@O%CitI@Crx`LbDK#ktUJBJs@#1RDu0=lASbegdPt#Dx5Wq z76~}x)2!3DiyZiB2jmZT1u`u7Xa@Hq-9fmX#HOW%_fBlz);t&O_wa7fxn8grwPx7C z2GkFb6eb6_m$0feR`ocbW{AJevcJqkaOJNc7p3XXhO1Q53jvxO^KYRNkzi%E?sGXI zRXq(`)FF)t!-uRWi|hr92u>n1nLlLaY*A4}g&=pEg7N{SCYk?CRD>_2M`SKX=9PIc zrsPVq)$l6lJoHEI_Zr#CGBU$2Pq=oLFG!s9^4>UJkw|*adi%U-yx5cSp2KTG2a!vA zr}!!oavATmchozET-Gai$GsEC<-7#g#PjMmFmzkLnLruC59exl!Nh@+4l%2-TuSQ+ zT(oo5#vFcP@$)(*^zO?I;xDDu3v5?Bf_(!b>>6;PcHh15VdoVYxr%)miyE2|!WUv8 zk7xQy_dsBl)^IPPK|j>)iCALtVKgxV9@;>VcQ@P{!XDy5h1>&iEqoJnhUxezk zD+s@lA!>F5zy;N^ig*-d--eq^ZNGl?dXy3K5P%Y8V9}Bn8??26f(Q%&8LuA@AIi-duGt`&8q`U4F31j8i3c!QAqYsh2~t+j)pm#-A^`gkS*f?Ke=-vn_M`3_>TF1e); zDzD8!{|Ngbwy{{o4d zrKKMIA{V`NHU$u%FLj|lTqe+xqn9VX@x>Mz&gc|5eUKy-?iBAh<7VvQ%UVQ)|C zBZJ%l+W{kj7ymapAn0BF6hG>pF!@(ZOZ1ZsVFBfK>z!zvev3gQKSv|xNN-YCxUot*57ooEL6g7Hz{~Eb&i1kxo&(|^< z-Zoc5z)YGWt6=TW0w~pI@s$PeE&$6dO7IWNAN?HS(Gt558y}iKdIoYmu zh))C7ny(k^C|4I+b+77gi5)UZ07uk+Ks^*W!C`_2)<|$2=mTsQWvST|C7UYOSuuK+ zye%CpDYgPm(~Y2zGzPmooLKuA%8&&RiO1xB4z@G0@*w=fGUs@ki~lz>ffV) zZ+^lqQ4SH!%e&e=*Z+k4$ikwrVli%7EdH>2ym_;IU}#ALjr0te5$2=*0mtqK|Gvb> zmpOMz-}!&e0$%)n?5iRH1rZnA|97mCUc8AY6(nR{m$LrJQklV4vM~Zmw=Dxw?OfNUr%gy(4K69H)`f>fMe0-mYh=hu{fC(Y0VS5TW(6>d%d-qr-#mi;lG zc34kYVBsoXQCMx^ zl`<>}q(1J^fzp1UTK7$D?SguP15skqh6|#-o1(Mm{CWM(1;MJ{9NEv@0VL25!gQRewfJ-CW0e!0LS?F3=@aRaVF#t>T5`%WGh7InH*q*cRiqP0KUXJZU27`Ie1RQ zc%@xh>Jt|29W0VySoIT@r^IykQvCa(`s*C&A(LNW@_S4^W%Bz>ewE1|F!_5-cFIqVWIbT@E*xOEM*){ch#S(=0k_1VYvLte43bbO&v0*C_#fME>0vQ6bq7{|J0{4Pg za3A2gcL`ww%CsmseoJZGbR0Vk*iPK2or!1aOwyOz#A!0lWHO!cOkcN|&a`QoYBK%H z9@mB~vcKPV?rQ;%qHHHKO$rJJ=bn4+d3@*lzVp4#QFmmdl)=v*kN@GhU;c6?^JjcW z|77v-SSDk5_ZgXtlW~kzrft*=gL$)M)=c@Et!3q}RkP%8u9lO(`C4B74%LR_Z=qI@ zzr|V+f3vMpd$=~-F4xNKk=lr~X|*ct9km^j&$UL|J8L`JW3{pNZMEA_mUo6)yV|>J zyW4wed)j+zd)xbJ`=q?ky1jiz?T+@HwL9D6wej{{wY%DP*Y1{g#nwG-yJold*Y?YE zsdaDrZMCn5AGg3+Vh&U{ZPw==(bTq!ihl8!si4v)6Fp6di-n@di8I2bvtx_)=Q+mer}xuAUP z$jp)YsnbVK9Xmc96dzypUGItJc{eCZD@VJI8{|%?M%xYYr-j<9 zjO#69hAvvlAARD~^zmbXb?Wr-sUUa!qT4}*sJ|dL4QOy?*0F})2ue?M&z*CXbTvHP zn_CXY^*S((@GBniJhu&aDLfR8xece)?6^TGnbSNWo&L$;;W1+CpP~&nQ!|_lFw_8c zn!wB~{#j1W$$!am`p#|6u9xz)oSS!k-r3{qeaX6F)Q03~pL4rB74Wp?-06(VQxQ*> zoV%TSRfSt(s_^6-HE4{ocE=kUdG!;@tnhW@V5AV=VjFRsezJ@JHOx@iAo$kJ?L6sB;psMa*V1n1)Pm`J#+i0z zP_n1^{^C>4XPwVE&pK!Eyw`cfdDZzjr;hwS=NFwhXC5=V-T6C?FN(?;%Y_nsx&-S2e1WH_I9E;#C?JVtge-g}OZ_iuClu5-~@ zLGC`3J@2d{cfa#~P}wi5{aEY9Ph*`+Hhw1CU1xIEUsOO7(9^8lY%jOmHV{w<(C#kS z4f|<#Zn`^v-u3PIm{KN6`uq7-!}BI*6Xcq;fshxQj_cV+PM`E_Z?U0V$DVWj71!;g z+VWfl()8>`$C0MyTTOK9g$+N`?cvjIujO0x%&%|F!Ro0yhiralY@*aW>~Uf%_1zRs=S^-&~X zFC%12^=1d$=OWloSswjn+wJ!JU}T>Br5-&N+l}YzU7*)VDh7c=@j@})3eM!xKzygpLIu8F^u zV#eRGK6;h;opEl=zYX6G;dg9lm!!KL^GoLEIO09>-Clup79bt=i%KLGD z$I@N+@^13D&vK6U;OU-q`zlu0$$)BxrfS6)uc~G(x7+~U5rAH=?VeENyfd$vLEdku zbFLrcorc@)cB=bT6>rqrnQ-lr)e1^YuL&G(bmrY4i&c^33x-y`Z8I*NHZhQP1r-!16jeIEVf{AbJ;g zztZ5g>tP<)v9Q4gqvu_BxzTD~1l9|a2@2A(`}};6pV1}5x`ex)7mTGIqEB*wlk%%W zf#tcag&V?DK=@ilox+qhYDXu}J_Qo?&aSvEzkBvrcOFZH9?qUQ^Z4aBMO)tjD;{~Gq}?7_vZ=O40{yI7IgSM z30El?X%P_hu8?L5$2CaZ@UWmt){-tTM#Ic>8!cHi>q6^N! zJm(L2^1PcO|H?QhD*1GyyAf! z;<*q6L6NnDMQ!XFs1aB2Bm2t6t^rd^1Vj!z`e8ncU$SlQK4jKlN`OKDQGY!XiPOyc z#*%r}(Bie4M@5)7asp$u6Hh_qjf&WnU`=|KW|il*tPFP@jLh(;Vtp*rAEIKyS$qU) z!G)}aIjoKu$QzkwVAPw=tY8w2qiNFCJ-8HknjLH-m)4~(RK#rkE|IRxuxyi;zXe@ko zM)ln1f-E>htPZ19!RS~RA&OHe6s#5Gdo8PxQ{j#Bm<-~jT1MITSneX}??_QAC%93G zxp!3YCes{ z1TmYdJ0oK5Jcj4&>bQRPvyO4aaLgA?lv=B&jm)L2k?B{#402a9uk1A_o95SHdUlhc1!j1{(6 zULU!NT5Nl%f^^4=X4Pu^FL0%pK+m}IE;MVQ?~ePtA905iBY65|^nYlO&1eP?xBiyM ze|Ey2h8)mbplsY)g`p{8C_h|#r#=jcS96e%O9{^clZe|RSA0uj3|UmvY{Zb_25-Wc zpw@zG;=jf2ioFWcqyv2bQV%&Rr1Mb!h+7J|v<*h$HrlD>IJscY&W7w~7V;_>eImXd zvge?Bp0|4)zX@$UT20mhpWsS6P$L%NUN*Jlxlqe#fU$eaw%;A-$OEIJ?oD+jX&9B} z5_m>^h^C=UgIL8wwhN}DH-`}1GuoP|!O0P&yNXALQP0l41CvCTkielRU33rGp;|<% zas;GBi0E@P4JQL=(Hy-RTZXBp-1>kL-3BA$0!1BRL<_2Mj#eNSmSYVn4Qps&(JSnL z3iAyCekh&~wFHho}ONjf;%jn(v; z0YT%%T50-=vW&B{dbvSKFfNM?b2343PWC}Tgs9%5B1ND!@WE+%be!5x&764XJsNGe z8bE{;?1Av12ZoL5pOD(pOOmW%xC}6b2vcm+SOURu*;QirgGGB_qP7-7Wfttc>CvOp zab1U0NRH$_-=Kws>U)}A!Lop5okXG3WBbtbsVN~o@Zkd>)gBD#q#7x2HRlwt0`0&6 z={`@@6!S}A)*vNq0v+W)4LJ;Jeml4gAl>GXaYu?Fw=L#}Fyp$E`$9tl)F`t2AlpgA&ZFYxp0sZ&91H>q9}hUqC&~69 zyaUkW)zBzXmMrkHK|@xERcUdf4)|bi#L~^u)~p++YcIj={;7Eh1<-A?QD9ONc+x<8 zz#^0j-8c8gMz~|BUPlZ?QW==Vze!J18zZn&z;q2`Kd9WUj;f;&2ZJnqHihG-PRN#~ zC@ThIPj#A94vvY6ATNTlcs+=g0J=7tSyWxL%33}-^^qe_oIF-%TmXGTVk&qNjj1Qt zqP5g*c7mcdBtYv{Cs7(Hwz2LIWcg|v1)>bu)_V}2nC}=h%7$f>;Wfa&>t@z`qhi6s zfU;2|e{KBwuFK~2Qn74ojA#WEDlVY?Tr9k!5pf$(9RL6}s{yZ>TA$fQl?h?nGrD0*Kf7&9S@c09n5V+dlmc^gPbAQXs>6Q$4AsGe>R^Pb1IB#`3c(Fj2QyS1 z%mLNG7*HKfY*8JIOXdqER0sKcQ`O<~Fo?vA`OFPehj2TdTh*BqH43w*U#1I7x`)lg zYo4Q3oeUgE0r8YW(@?Vjhy4T7VBowioVPw1CL?c>+IQ=LsF|ZWvtXN{n0Ub$YT-r=Xo0VtOc{jfU-b6q~>+ zLc2df8S>P5zZglLLUa zMNkW6qMNP4wt|u0-PBkYKCX7N@1|wVn@E>~BFtZzthYT@My$$9iFr`xB<5+;wElLB zc~kvoHp_Vl%li=}KDCD3I~4P31&DcdvJ>?JN=3|5A7$<`6M8|_$C%IopFu%?uf0Nzl%3MoG-p{#Z-^MQfb0c`H)x>vudYr(W3bxte=hv=gi#@ zZF1}RHDi6~YKBgk&Zv|Wk`jnIaCm4Nrjwnf=riyPtLA&tq6zCr|3&n_7WNOXC)&-b zpGoy^q5rc{Z_+nyoiZ9eZSH6DGy3gq+iwZ|zU&V>InGl?QodroYN$W;N7gX+OWA(j zudHQ}v!2d$N)Y09to~Xg#Bua1nOBW|8DClg!~Yh};qBoZz8cLz;>ofhB>#Y8U&^mp zgrs9KnhelnoLg?GFj$(NiCBgI^T0f;OS#VD(mNoCaU&v4*6?81|F^dE4@jaOqT6&| zaqqNs%b22)fu4&G<126PK;I_%wg9^!!IA8-%yaXXtOye5%i)Y6T8HhQPq%-Q*}Sl&?O~@NbI~K-EiGb{ zP7!gA> zkEA4`bij%1&=0i`#BReZO#}BP+Z#Lvp`DOb8%i}+A>F3z7t!lBVt~}@#iqLg`D|by z0~VCjEPk9OyC1W;?$%9pLTfbH6BguOk&(g+oxy=-LecRdEpipBwnY1g&-!R z+8V%#8yfSpfR#4PVLd~GOR6z#V?@v=j3&2c*V7g|ac+wxFV)zMZF(>^I=y-NsZ%(d z2zmoxPFX@jIBQWY!D_8P(3ft9c$pf6Sk<-_OsVq@*yyySanMSaYUxewb%T*!Ew5`g z=Be3XnxO_nGj5^*!Bs>bvDnHHpP3@SEx02ZuiFtjaHO3})LI?tF|}<-L~W8N?Qx8| z8bFfBcM^Rm^1N^r0F40#-IY#gDzJ}0O=e?=Qh-`c11AOyyomuJDl21AIAZNr8kl&Z zXk$IJij8PHSB!74euMp`TT2vat)Zho9oDdksYFAUN&oQX`*$UV$$>#Dmy8kyk>{-bIAP{`s|{Q#_dVrr&;2i<=h_f{IJ;+PwPsQL$V z6~CiaH5%)t8=RkQw`LhX##+6t-uebQFMF420( z*o8q-RpnT!yVbkUlIk(J%;Y!|Dtn@})`mIFx^@u-dA$YKic&~%LoqZ;<&Xr$cr{d( z&ldX#EC!DRBh!9&`6TgP``|S4ED0JB{Qj;$GbhoF4Md`{+;}AnI|y9*7NX{j3Fw}ynHv_6ioJ3LK}OG zyRTW7$Fj_iNxoo}vZFZ9aqwEjGP4!4gi{?np^?8W|_|D~c9zlvpuS>I{x$d#>utdVrSZRE8+ zr6HRxtmKG1(E%phv$TT_z#z|x0_dg1xFzlTP4DP4M^C5Bv zy*v?TQacD*khYvuA6xl3kpf=VxE}%XkhoJ}Phv9rN z{ro39z;vJuT_xWX#~K)YbXvaV(>86B9j9HVEbaUX$pp*bKAs(Tz%N3+Gy z#f*B;FE}s?!HmJW8SkC(o8x|Qz2v|wvR-y_S3#ARw$$<7>5r^e&=k8ZmaRkI{dK{$Oe_;XRBfmWBq;O;am9i`9JNJ>c3gYD|-y8un>`rJh0pD!VNE z;YHg=CVnC%hS)r^DxoQ&;%M_he2kB~pcoW%B$t@3N$~nW4};;B;pR@9a*^jw=r-K@ zVG@@JH4%(;^>~t7sfH*sX`gPa@)UolQpgx-$B66=RODhcAXGxh6htc>q%Kk!ByPmZ zpkat1pxuEWJkDl0><#Z0x;;2JAsTkuS`HX&EsedCk*bW(=&E4YchtD3{5(kqryWl^ z>cJx5ce9TJ-YPiv5ZQIp(CrBXZ!i=>K-jq0Z6X8%=56fNTSL1%zlW$o@@For@KD4+ zAS;U}N>>MC!nz`Oj6r%}fIM7^1EHHETx=b{$9CeCmm@&Yx(xC}4}pv=K$`a&0Mc9h zIcTnn2zClgjMr)CVe!mPgf@o&dccga9?*mdy`+a2XSkyLnR7X?EsXfgwxMaAa2*mL zY=t{+iQv&%k#q%OSk7o|l z%!MnAeTPI4M!AV;fCM?9gb@<_(d)itHP8%!8?A`F>ZUHDL~WMEMxPN zXSiG6i>jpuN|1C*kZ21AWM#8pj8W2tyuAxDuW8)(?EzVq->T2yyDech@%GD8INNN$ zm2I|P+!7Faoigj(c5zn?x5jmB%b}+y7GNpRZWdqVj+ks?E-k1=8zN7hr)WwxM3ns~ z8{#_?`Z&@-M7Gz(VNXC9Rughg2JoeJN2q=JY6`G?9JF49W!#^GH7zx$Tiw^t`dir7 zVutWzA>z0-^=nwyscP|7@SH+rsB986QSg}H=N4>4h0hZkNjjw+AvOXNE5^Oo2XPNI zgPbrxp^GgjNGVV>s1G6&p`bS;E+U*Q^RtlVs7G1rxlra}X?|%44##_;Zx#IF8ZM-` zBFIN+OC&C{|9lLD)2a(QGj{S0_<3}COGcDh)*0INlBlRK0Na6`4+=kyhl;lZ)@q0% zRD5K|cDL20rmDjl++&zfknhTkL23sc!XU-vp094hW6e5!YU%^QPB4>BcctYz=iE9; z)lb+%i7xeFe)9?e;2bO_cAm#+W zg$IB;^U9Nb&VyaYKP1_hY#+k|@ny;I5rg0rLf>wd${N;j z7@s+dUujsruE@CXvj|Xv&JO@ z!4bo-8Rb`Tb(^*s=~{9`ZAiTbUl-Pg97_l_vs4rfdj$)|poijBLp_PVL+XjJ9yDhV z?SoSGk+3YUzg-`OP=NEaD8L>L$+4j;- z2du#qY;A7UK3j$#rv0!jS-`V`Pf9rp%tLMuvaF5%9Vzy) z>5~Bn!M>D4jeQD>gQ%yU>VLnO>61t^ujuF-oPLGjY%fZ%dm|NwL9zwu#ZtROG3`$i zrqss`VM`!!VI_}%M2A)}fG7-psi2PFeQ^n9H^*=+k}94{ui#co2x-GGcW>9rOuCow z1TDkh6;9CNuY|!HP7cySv$H7e;h+Nkg0Y0cAoCTP>iW2$Y<&k;9jxx4jPJ|g_{Q-? zLH#09jxV2#FBzAd-9?WBa5$VA1^XGjno+-lRtQDp{GG^;U4hB54)(eUe!rKV*;*Dm z;Sgn{_1o4gLgK31^ecWmCKV`%T*YHfH}W&K^-r4~O$VfOdytsxQ{OHj2>?|)V3or9SRq%r?)~T7P{`mgzQUvYROvFvjQ~eGZ!sn1| z+%Z@W78H?kSZm|<9-MeQ?gkZ=)+XHF)Y7e1_o~O(#4#pSBmr#xuoTMXq2%DvGE2)c zvAN!YhX`xo?5WLw0xZuz`E@;goKX_Rk00@5a-D;~9i4KC8|%n%D!tXGC=jlKE21-7L%}Og&AThFs%qt%irob%@ zUJUg@AMzY;Edh~NLFDi(E|{yfff6Gl!HYTi1TW=qHi=g#LQ4ia#+9#R$I`Y1K|IG$ zSz&m&7jm5spfCIlPaGNR1u(f8$GXH#wMa(iO;M!xDF+Sz~=;q)?^qDjpS~Jx*qj9M(aYUE$9bOq(T&jS%4MEby zHn4=tCD%e}6#1Q)(KpvDnagj@k`i&W1e>u{yyF(N0T@G_#%|9C@mktps=BnTgz^g+hg<-OMiEz~?|$6E{NyeI3dG{+@#5xU!#{=<2Jeuu@s6(d@unN{ zL-8SSs4#s%L_G(W&^eoIfQAr^J8n!<_ht*Y7+*E(Obzv5b?`u|jUTB5t}iz;Psqdw zs)M1YX+pb#4p!eZmWhUBPC;g%p%t_7!HGun8qWb!5>}N=9D|T3hc&)q?SKq_dHkE!Bec?2&=RzvYytO)a!(n*Zso*s zZ&*JVxM3_!&;#N<*hvO4lR2ucYG-eS*p5Dyy+O@HvInflXbf}wC2>NQs1 zggnIM?+|(D_o(+M%lw4V64oEO^z!uAe=I>Do1=QTPE;Iug z)J_I8)M0&%4c^IpGShH6YuV)ac`$I^rRamf`kVY}KineWCVx%&e~iKD^HZUbn$p~6 z*pPOCO|T0hL(_DLX6f{$dTCK~0Octn@hgD@+ZIFx()&Pmji?=(in7V&Z5>0*SsB_M zWmC?*qR)unR;REezXXQ?gqkV^0c`cLNlLWdcMfdz(9fclXw=g7gsRzpJt2Y+Q$%Oc zdxzc#C+wpV6BKS=P)Wk8{8Q+D6v!q_l{G{07>?g_y$0NgzDs8Vo#`nl6K|u<#^rJB zj)TiLl5pX{nsPUV?ET_L#)cjajn&W+_BNEsH&Hmp1Y8pbtEnN9mZ)i{KS2@{g&fWt zd-{y}cPwl%5sSF&0)N2A#N>)~%c$RGvW@*i`1xKU28qTjMuY;}v_$odUn}Lx*#Sh+ zOg+>hDtcHCvU%zJcILzkbBH-oj*^^gH{Tr?D%}~9L`-bXu^?PjdXOPJC z{@9WHRrM6hewIm%$yp|KCUZ<2CLJahm=u{%XIGzO^7okV&}ywRb?VHMxHFUgKar=- z9M_cg8+`qbnS6!GZ!!`35_99%`1oNYK?zbCWoKNW17%(3+l>zHLsx1qYh7mY`%K85 zwP%I1*vAt9#sImkcd)GzKSF&Uo=y}C4P?$Xi`B_|U< zmFp0wkd`g;MDhlD_mT1&nU9X`H}6IK?;GX!mF_H6O8e2qfzpW8G_BD$O6}5~%5e2z zE{MMW0g&eoss00#cQg5iOsY(-GI^BAuOkVpUJn=T@4)};r3H%$9C6-6V{TqTrWKe^ z7)+gGoVus-{34G$n6g~at_rNeMG8s~AHxe8w2WKks}Uv@zR!!>!P9QuQ5JI@)ff}; z_2zkh)x!^~T`bwnMA+6I=44R>v4x8XstWdx^X*+of^>{R7Pd z{h8)W|6ucA|4{Rg>@R@E;pSn~ZC3P-^p7@=qF!22o5#4e^%N^_tIgx@D6GOt_tAe( zYo1`*HKkttidJ7&v`_7$7B25h>Kh;3PfCZuScC; zYzLom;RP=HN=t~%TnYTnKnU)4Hs^&1MBRvs?|;y`{O;nVtDnRLRIj|hxFjn}^B*t8 z=7Mnh9Lq4~+{pb(O~}}D++|+R=W+2;81gYhEgPD zn5}}*8vb-3FeSLt@G?MJBv2a zEn;Pt*(|kCzrtQ(FJomTHpgCJucB5)?KSp8)GDa`h`o+lRUYWPI0Oc`zXc0R4t^Qq zoIVdjcZG*eR|I_&;CDD%l5-jz&ke&yJDV%o4*zVOccRb+}aeb8hS8hNaA~$FWci`Q4ueW?7ghroTcRRO8Qt-@j7<6v&D10&8^gAzRA~d@W({RCa z`^(I|_$=P|ko4YAhKI%yG@A`Sa^d1IE%nIMdM7Iv$7tXw6wguRIEvVyBi$+W{Ky$O zivi4n4hF}j6p)>RnXS5h{pJ}5*6U_$CCsIJG!TAl&_f-v6xomJIxga5_)AnGWlP;w zMoLSI^escy%n@wKKo!SEFx~?Nca^Cpl{=d3SHx-Augbf6M7g7;^BZYD-L+eacqOu! zeoy@z<0EP}(1wLc+M0%U7I4`k9c6K3pe!*HWm%RLSyt(!>(=5|bn1d+VE7CP8R?(1 z43!ITf<0&e6pP;;xSkUQ&X9{>SeN>c&0B%YlV(J#u0J&w#1`tcxEQUbS`i!XK?Gv6 zKZy8ev3YyI2Rtrc>-E%#C8i5dQq!&8k9pSO2v{sW7+DAnp`&SwpvrI z{r{_CEefH&UtcLZ1c!8#nI>tJVP&`95bH2YG!5YlJzaUROt}G=CP29dN z&d@_dH#Vi5>^{jFNrTTni&}E{&oNV;r=@G^A)H@H)xOsB8GS~b(Tuw_^$6OwW~+ue zJU+2D(?sMVcMfEb<@y3SyPH$zN%~X7CC~F8P@(hu@|>!yK(U9FqB7F9RPY2Sg`u4!A=NS8CeH`4a$ef}euY6E}wbf7b+ z^2o_gBE%6$9B=5x!X-r=qM0F!!Go4M- zk}Ar?Ihr?d2V_sEZ5gVDKjLgnLmm9BsdQ;ypZ?F`(FvDxPsbMH zoq+M3Y6=3cFNp!a8S4lv$o#fK(6!8@Q{Y9Kqy~?_%V)REV{niM4M-sE_A`dU!9=N0?12 zvSp0)HQ;b&l1klymxJ0>i0YQL4MEvM>CHT)k+D{Y?12(4%36_Xf1!}MV>&$ZN7*=h zv$3MyG0`rCKOdI&z88kN3S};D4ie?VjLOI))%{$7N;wdUB0gRj(V}0DZ-)U&0E`Hy^sR%(D`s_;p{m_&Y`dcQ5c|E%6*)=&!%V*#Y`f0 z9ehfkEO*<%<~sWk-$QJ<=er_hzI@#t@+43t-~$c}H!100Yaxex#d!Kbd*V+qHm>Fmx>Bn#OH@UQ^%K{OgVV&LZl zi&SS1GbX2-${pUFWIiHqbNY?>^Ul_sV5P^3+@9BFBDkN(ZJ38kVrGo zf`Gdo!TU|xp${2aT3o2B%^5)l)#`OyiO92u@BvDPykGVsNK98q@e)CbzlE*5X;I9x zru+o<`!-^vYe`r~*&NCo)U{ZfeQ94jQoOLjVbIpa#a|Oq0%h9WBg$}`wGgvdH)6@4 z0~ddBc09d)P$DDoJf0AgkrM~0I8TKHo0Y!1xy)Nx02r*3(14caFy8H$|xAI-MaL%Iz} zd+O`sfUqSBO#pKZ)a}s><_I7e;I6h)m3S73x9iZAX*5em zLdSFkx7{F1OLnmjzKZQkDEvn46 zJyG88PpAC*jPe+}p#yk3!7?3^+6!P5NiA?khGr^{K{F?&yuGreZ&UO?!3f5_$mO-E zr?fRQvValhM^r0NO`-)=B9}$ki*eEV&GKd9OKXUNZ-7Zsl%fG~^c#n#Sb1Va4o=XsxNCI=|<^GChk*Cb^4brDjo*^zI zawGP$_WzCvA=@0j#cB0~23SE(eu?UX?$e>VmnE*vd(m@PnDq`#cQxTzp&0E^G64s;#k!;?Ub8Z>PgDY!h z__WLnAb*|2_4HJB)%D@)AJ9h~$;u5uMwvbl$+VP2r;qxGGrPD1R3?+lPh(NBMX60B zF=bNHydCzC>aJrzW}P3X2~&0fkdH9_JofjnhDu2{)S7lox73Pi|1qEI%5nEw;tYKu z0B6KwICE8gYkCxG9`$W$77AWSGVa6~@i7$=pngI%$&~L>tx3f$mXHm29V`*oQ5hba zWZCi{dJh-I3V0V!k$imlYN~)UMatu-V>P3*mw6)0(!?p0P)nAoB{(w6=$pq_R(3zT zyA72+Kc%vuUpI#TI#!-}MD3)dyJN~67@&_uIS((tK(q-5jDt64qZh5_5I{Sh07;L{s$6e}QfReU&}p^AS)wGptPCn<%EN$&aA)J^wJSkoIRQ zNYKw{@}pMe50z(2$FyS=RnzD~!alt!n)c457v}zA=|rhotJY7_vbzK12eE8H79L;U z5E*yE_aq=_?oRGYv z=<6Cun literal 0 HcmV?d00001 diff --git a/gestao_raul/Lib/site-packages/websockets/sync/__pycache__/router.cpython-310.pyc b/gestao_raul/Lib/site-packages/websockets/sync/__pycache__/router.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cee3e6ced0d4417031cd276d1349542b971f9731 GIT binary patch literal 6339 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 PnH`gvtnqg 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 literal 0 HcmV?d00001 diff --git a/gestao_raul/Lib/site-packages/websockets/sync/__pycache__/utils.cpython-310.pyc b/gestao_raul/Lib/site-packages/websockets/sync/__pycache__/utils.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7fc459b94ad17853dccefacd3f83a311329fc913 GIT binary patch literal 1442 zcmbVM&5jf|5VpH}XL@I6e^?;_QVu@SQLvp24iUJ=ku3 zR)Z9UJ@N=Sz%eh9OWvTbobn2sQq}XL0Yy2)QrqQn*H`7QzHYm_+aoak`tHx^uL&Wa zakDvM*xVz8`X9iML{dXaI;0fs$V9_P##1_s9}}6#_>#!@eKbsDbV{L>b39QcT=CY7Dg^Za`qHr znRq3%5o3tZ74xdFmGsOymfP`*{A^$=b;k6Bh3Cq_E1p%B8C~kIgl@V^xG(*Qu_CZJ z!+@x|z zYm;E+X1laNx~+7PMNQ(BnKj_sz%)F9rAA*`>Y^^CMm^g677UR>>a~q5~c>!t! za=Mb9wz~y2*U7tRPTlo+B%^a!LkyavztK5rG7(PzY~Au>VT#&_0PSA2pG}-CVY{Mp z*-sS6*K$G6x5ek|m*)y4!Gc{JvRu6>@-vha$nlYL)}5`>BLt3xj@BvJf{BgHCrHwU zR5551GG{-vQv5H7R(Jn`!Hk-O|0i-RW8knm7uzU0$>5Gz;;`>-8z=m?H2uv|v)Jw6 z3AVXaWUI%;w7c@%0K=nBd;1C9qkYg&LN5;13c_TNY5=)V)NTu>4o+Q|z}uo9ti5=i zbx@s^C>7^TkMq*X+MwO%{ADf7@}$eTv<2sG52IUjEaclNQHprOR^utCyq8|>W?Kk6 rso@I;V259GOG5*o!WeL2*alRKjJIymJBxbmBAYE}pQKSIK8*hXvIt`a literal 0 HcmV?d00001 diff --git a/gestao_raul/Lib/site-packages/websockets/sync/client.py b/gestao_raul/Lib/site-packages/websockets/sync/client.py new file mode 100644 index 0000000..58cb847 --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/sync/client.py @@ -0,0 +1,648 @@ +from __future__ import annotations + +import socket +import ssl as ssl_module +import threading +import warnings +from collections.abc import Sequence +from typing import Any, Callable, Literal, TypeVar, cast + +from ..client import ClientProtocol +from ..datastructures import Headers, HeadersLike +from ..exceptions import InvalidProxyMessage, InvalidProxyStatus, ProxyError +from ..extensions.base import ClientExtensionFactory +from ..extensions.permessage_deflate import enable_client_permessage_deflate +from ..headers import build_authorization_basic, build_host, validate_subprotocols +from ..http11 import USER_AGENT, Response +from ..protocol import CONNECTING, Event +from ..streams import StreamReader +from ..typing import LoggerLike, Origin, Subprotocol +from ..uri import Proxy, WebSocketURI, get_proxy, parse_proxy, parse_uri +from .connection import Connection +from .utils import Deadline + + +__all__ = ["connect", "unix_connect", "ClientConnection"] + + +class ClientConnection(Connection): + """ + :mod:`threading` implementation of a WebSocket client connection. + + :class:`ClientConnection` provides :meth:`recv` and :meth:`send` methods for + receiving and sending messages. + + It supports iteration to receive messages:: + + for message in websocket: + process(message) + + The iterator exits normally when the connection is closed with close code + 1000 (OK) or 1001 (going away) or without a close code. It raises a + :exc:`~websockets.exceptions.ConnectionClosedError` when the connection is + closed with any other code. + + The ``ping_interval``, ``ping_timeout``, ``close_timeout``, and + ``max_queue`` arguments have the same meaning as in :func:`connect`. + + Args: + socket: Socket connected to a WebSocket server. + protocol: Sans-I/O connection. + + """ + + def __init__( + self, + socket: socket.socket, + protocol: ClientProtocol, + *, + ping_interval: float | None = 20, + ping_timeout: float | None = 20, + close_timeout: float | None = 10, + max_queue: int | None | tuple[int | None, int | None] = 16, + ) -> None: + self.protocol: ClientProtocol + self.response_rcvd = threading.Event() + super().__init__( + socket, + protocol, + ping_interval=ping_interval, + ping_timeout=ping_timeout, + close_timeout=close_timeout, + max_queue=max_queue, + ) + + def handshake( + self, + additional_headers: HeadersLike | None = None, + user_agent_header: str | None = USER_AGENT, + timeout: float | None = None, + ) -> None: + """ + Perform the opening handshake. + + """ + with self.send_context(expected_state=CONNECTING): + self.request = self.protocol.connect() + if additional_headers is not None: + self.request.headers.update(additional_headers) + if user_agent_header is not None: + self.request.headers.setdefault("User-Agent", user_agent_header) + self.protocol.send_request(self.request) + + if not self.response_rcvd.wait(timeout): + raise TimeoutError("timed out while waiting for handshake response") + + # self.protocol.handshake_exc is set when the connection is lost before + # receiving a response, when the response cannot be parsed, or when the + # response fails the handshake. + + if self.protocol.handshake_exc is not None: + raise self.protocol.handshake_exc + + def process_event(self, event: Event) -> None: + """ + Process one incoming event. + + """ + # First event - handshake response. + if self.response is None: + assert isinstance(event, Response) + self.response = event + self.response_rcvd.set() + # Later events - frames. + else: + super().process_event(event) + + def recv_events(self) -> None: + """ + Read incoming data from the socket and process events. + + """ + try: + super().recv_events() + finally: + # If the connection is closed during the handshake, unblock it. + self.response_rcvd.set() + + +def connect( + uri: str, + *, + # TCP/TLS + sock: socket.socket | None = None, + ssl: ssl_module.SSLContext | None = None, + server_hostname: str | None = None, + # WebSocket + origin: Origin | None = None, + extensions: Sequence[ClientExtensionFactory] | None = None, + subprotocols: Sequence[Subprotocol] | None = None, + compression: str | None = "deflate", + # HTTP + additional_headers: HeadersLike | None = None, + user_agent_header: str | None = USER_AGENT, + proxy: str | Literal[True] | None = True, + proxy_ssl: ssl_module.SSLContext | None = None, + proxy_server_hostname: str | None = None, + # Timeouts + open_timeout: float | None = 10, + ping_interval: float | None = 20, + ping_timeout: float | None = 20, + close_timeout: float | None = 10, + # Limits + max_size: int | None = 2**20, + max_queue: int | None | tuple[int | None, int | None] = 16, + # Logging + logger: LoggerLike | None = None, + # Escape hatch for advanced customization + create_connection: type[ClientConnection] | None = None, + **kwargs: Any, +) -> ClientConnection: + """ + Connect to the WebSocket server at ``uri``. + + This function returns a :class:`ClientConnection` instance, which you can + use to send and receive messages. + + :func:`connect` may be used as a context manager:: + + from websockets.sync.client import connect + + with connect(...) as websocket: + ... + + The connection is closed automatically when exiting the context. + + Args: + uri: URI of the WebSocket server. + sock: Preexisting TCP socket. ``sock`` overrides the host and port + from ``uri``. You may call :func:`socket.create_connection` to + create a suitable TCP socket. + ssl: Configuration for enabling TLS on the connection. + server_hostname: Host name for the TLS handshake. ``server_hostname`` + overrides the host name from ``uri``. + origin: Value of the ``Origin`` header, for servers that require it. + extensions: List of supported extensions, in order in which they + should be negotiated and run. + subprotocols: List of supported subprotocols, in order of decreasing + preference. + compression: The "permessage-deflate" extension is enabled by default. + Set ``compression`` to :obj:`None` to disable it. See the + :doc:`compression guide <../../topics/compression>` for details. + additional_headers (HeadersLike | None): Arbitrary HTTP headers to add + to the handshake request. + user_agent_header: Value of the ``User-Agent`` request header. + It defaults to ``"Python/x.y.z websockets/X.Y"``. + Setting it to :obj:`None` removes the header. + proxy: If a proxy is configured, it is used by default. Set ``proxy`` + to :obj:`None` to disable the proxy or to the address of a proxy + to override the system configuration. See the :doc:`proxy docs + <../../topics/proxies>` for details. + proxy_ssl: Configuration for enabling TLS on the proxy connection. + proxy_server_hostname: Host name for the TLS handshake with the proxy. + ``proxy_server_hostname`` overrides the host name from ``proxy``. + open_timeout: Timeout for opening the connection in seconds. + :obj:`None` disables the timeout. + ping_interval: Interval between keepalive pings in seconds. + :obj:`None` disables keepalive. + ping_timeout: Timeout for keepalive pings in seconds. + :obj:`None` disables timeouts. + close_timeout: Timeout for closing the connection in seconds. + :obj:`None` disables the timeout. + max_size: Maximum size of incoming messages in bytes. + :obj:`None` disables the limit. + max_queue: High-water mark of the buffer where frames are received. + It defaults to 16 frames. The low-water mark defaults to ``max_queue + // 4``. You may pass a ``(high, low)`` tuple to set the high-water + and low-water marks. If you want to disable flow control entirely, + you may set it to ``None``, although that's a bad idea. + logger: Logger for this client. + It defaults to ``logging.getLogger("websockets.client")``. + See the :doc:`logging guide <../../topics/logging>` for details. + create_connection: Factory for the :class:`ClientConnection` managing + the connection. Set it to a wrapper or a subclass to customize + connection handling. + + Any other keyword arguments are passed to :func:`~socket.create_connection`. + + Raises: + InvalidURI: If ``uri`` isn't a valid WebSocket URI. + OSError: If the TCP connection fails. + InvalidHandshake: If the opening handshake fails. + TimeoutError: If the opening handshake times out. + + """ + + # Process parameters + + # Backwards compatibility: ssl used to be called ssl_context. + if ssl is None and "ssl_context" in kwargs: + ssl = kwargs.pop("ssl_context") + warnings.warn( # deprecated in 13.0 - 2024-08-20 + "ssl_context was renamed to ssl", + DeprecationWarning, + ) + + ws_uri = parse_uri(uri) + if not ws_uri.secure and ssl is not None: + raise ValueError("ssl argument is incompatible with a ws:// URI") + + # Private APIs for unix_connect() + unix: bool = kwargs.pop("unix", False) + path: str | None = kwargs.pop("path", None) + + if unix: + if path is None and sock is None: + raise ValueError("missing path argument") + elif path is not None and sock is not None: + raise ValueError("path and sock arguments are incompatible") + + if subprotocols is not None: + validate_subprotocols(subprotocols) + + if compression == "deflate": + extensions = enable_client_permessage_deflate(extensions) + elif compression is not None: + raise ValueError(f"unsupported compression: {compression}") + + if unix: + proxy = None + if sock is not None: + proxy = None + if proxy is True: + proxy = get_proxy(ws_uri) + + # Calculate timeouts on the TCP, TLS, and WebSocket handshakes. + # The TCP and TLS timeouts must be set on the socket, then removed + # to avoid conflicting with the WebSocket timeout in handshake(). + deadline = Deadline(open_timeout) + + if create_connection is None: + create_connection = ClientConnection + + try: + # Connect socket + + if sock is None: + if unix: + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + sock.settimeout(deadline.timeout()) + assert path is not None # mypy cannot figure this out + sock.connect(path) + elif proxy is not None: + proxy_parsed = parse_proxy(proxy) + if proxy_parsed.scheme[:5] == "socks": + # Connect to the server through the proxy. + sock = connect_socks_proxy( + proxy_parsed, + ws_uri, + deadline, + # websockets is consistent with the socket module while + # python_socks is consistent across implementations. + local_addr=kwargs.pop("source_address", None), + ) + elif proxy_parsed.scheme[:4] == "http": + # Validate the proxy_ssl argument. + if proxy_parsed.scheme != "https" and proxy_ssl is not None: + raise ValueError( + "proxy_ssl argument is incompatible with an http:// proxy" + ) + # Connect to the server through the proxy. + sock = connect_http_proxy( + proxy_parsed, + ws_uri, + deadline, + user_agent_header=user_agent_header, + ssl=proxy_ssl, + server_hostname=proxy_server_hostname, + **kwargs, + ) + else: + raise AssertionError("unsupported proxy") + else: + kwargs.setdefault("timeout", deadline.timeout()) + sock = socket.create_connection( + (ws_uri.host, ws_uri.port), + **kwargs, + ) + sock.settimeout(None) + + # Disable Nagle algorithm + + if not unix: + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, True) + + # Initialize TLS wrapper and perform TLS handshake + + if ws_uri.secure: + if ssl is None: + ssl = ssl_module.create_default_context() + if server_hostname is None: + server_hostname = ws_uri.host + sock.settimeout(deadline.timeout()) + if proxy_ssl is None: + sock = ssl.wrap_socket(sock, server_hostname=server_hostname) + else: + sock_2 = SSLSSLSocket(sock, ssl, server_hostname=server_hostname) + # Let's pretend that sock is a socket, even though it isn't. + sock = cast(socket.socket, sock_2) + sock.settimeout(None) + + # Initialize WebSocket protocol + + protocol = ClientProtocol( + ws_uri, + origin=origin, + extensions=extensions, + subprotocols=subprotocols, + max_size=max_size, + logger=logger, + ) + + # Initialize WebSocket connection + + connection = create_connection( + sock, + protocol, + ping_interval=ping_interval, + ping_timeout=ping_timeout, + close_timeout=close_timeout, + max_queue=max_queue, + ) + except Exception: + if sock is not None: + sock.close() + raise + + try: + connection.handshake( + additional_headers, + user_agent_header, + deadline.timeout(), + ) + except Exception: + connection.close_socket() + connection.recv_events_thread.join() + raise + + connection.start_keepalive() + return connection + + +def unix_connect( + path: str | None = None, + uri: str | None = None, + **kwargs: Any, +) -> ClientConnection: + """ + Connect to a WebSocket server listening on a Unix socket. + + This function accepts the same keyword arguments as :func:`connect`. + + It's only available on Unix. + + It's mainly useful for debugging servers listening on Unix sockets. + + Args: + path: File system path to the Unix socket. + uri: URI of the WebSocket server. ``uri`` defaults to + ``ws://localhost/`` or, when a ``ssl`` is provided, to + ``wss://localhost/``. + + """ + if uri is None: + # Backwards compatibility: ssl used to be called ssl_context. + if kwargs.get("ssl") is None and kwargs.get("ssl_context") is None: + uri = "ws://localhost/" + else: + uri = "wss://localhost/" + return connect(uri=uri, unix=True, path=path, **kwargs) + + +try: + from python_socks import ProxyType + from python_socks.sync import Proxy as SocksProxy + + SOCKS_PROXY_TYPES = { + "socks5h": ProxyType.SOCKS5, + "socks5": ProxyType.SOCKS5, + "socks4a": ProxyType.SOCKS4, + "socks4": ProxyType.SOCKS4, + } + + SOCKS_PROXY_RDNS = { + "socks5h": True, + "socks5": False, + "socks4a": True, + "socks4": False, + } + + def connect_socks_proxy( + proxy: Proxy, + ws_uri: WebSocketURI, + deadline: Deadline, + **kwargs: Any, + ) -> socket.socket: + """Connect via a SOCKS proxy and return the socket.""" + socks_proxy = SocksProxy( + SOCKS_PROXY_TYPES[proxy.scheme], + proxy.host, + proxy.port, + proxy.username, + proxy.password, + SOCKS_PROXY_RDNS[proxy.scheme], + ) + kwargs.setdefault("timeout", deadline.timeout()) + # connect() is documented to raise OSError and TimeoutError. + # Wrap other exceptions in ProxyError, a subclass of InvalidHandshake. + try: + return socks_proxy.connect(ws_uri.host, ws_uri.port, **kwargs) + except (OSError, TimeoutError, socket.timeout): + raise + except Exception as exc: + raise ProxyError("failed to connect to SOCKS proxy") from exc + +except ImportError: + + def connect_socks_proxy( + proxy: Proxy, + ws_uri: WebSocketURI, + deadline: Deadline, + **kwargs: Any, + ) -> socket.socket: + raise ImportError("python-socks is required to use a SOCKS proxy") + + +def prepare_connect_request( + proxy: Proxy, + ws_uri: WebSocketURI, + user_agent_header: str | None = None, +) -> bytes: + host = build_host(ws_uri.host, ws_uri.port, ws_uri.secure, always_include_port=True) + headers = Headers() + headers["Host"] = build_host(ws_uri.host, ws_uri.port, ws_uri.secure) + if user_agent_header is not None: + headers["User-Agent"] = user_agent_header + if proxy.username is not None: + assert proxy.password is not None # enforced by parse_proxy() + headers["Proxy-Authorization"] = build_authorization_basic( + proxy.username, proxy.password + ) + # We cannot use the Request class because it supports only GET requests. + return f"CONNECT {host} HTTP/1.1\r\n".encode() + headers.serialize() + + +def read_connect_response(sock: socket.socket, deadline: Deadline) -> Response: + reader = StreamReader() + parser = Response.parse( + reader.read_line, + reader.read_exact, + reader.read_to_eof, + include_body=False, + ) + try: + while True: + sock.settimeout(deadline.timeout()) + data = sock.recv(4096) + if data: + reader.feed_data(data) + else: + reader.feed_eof() + next(parser) + except StopIteration as exc: + assert isinstance(exc.value, Response) # help mypy + response = exc.value + if 200 <= response.status_code < 300: + return response + else: + raise InvalidProxyStatus(response) + except socket.timeout: + raise TimeoutError("timed out while connecting to HTTP proxy") + except Exception as exc: + raise InvalidProxyMessage( + "did not receive a valid HTTP response from proxy" + ) from exc + finally: + sock.settimeout(None) + + +def connect_http_proxy( + proxy: Proxy, + ws_uri: WebSocketURI, + deadline: Deadline, + *, + user_agent_header: str | None = None, + ssl: ssl_module.SSLContext | None = None, + server_hostname: str | None = None, + **kwargs: Any, +) -> socket.socket: + # Connect socket + + kwargs.setdefault("timeout", deadline.timeout()) + sock = socket.create_connection((proxy.host, proxy.port), **kwargs) + + # Initialize TLS wrapper and perform TLS handshake + + if proxy.scheme == "https": + if ssl is None: + ssl = ssl_module.create_default_context() + if server_hostname is None: + server_hostname = proxy.host + sock.settimeout(deadline.timeout()) + sock = ssl.wrap_socket(sock, server_hostname=server_hostname) + sock.settimeout(None) + + # Send CONNECT request to the proxy and read response. + + sock.sendall(prepare_connect_request(proxy, ws_uri, user_agent_header)) + try: + read_connect_response(sock, deadline) + except Exception: + sock.close() + raise + + return sock + + +T = TypeVar("T") +F = TypeVar("F", bound=Callable[..., T]) + + +class SSLSSLSocket: + """ + Socket-like object providing TLS-in-TLS. + + Only methods that are used by websockets are implemented. + + """ + + recv_bufsize = 65536 + + def __init__( + self, + sock: socket.socket, + ssl_context: ssl_module.SSLContext, + server_hostname: str | None = None, + ) -> None: + self.incoming = ssl_module.MemoryBIO() + self.outgoing = ssl_module.MemoryBIO() + self.ssl_socket = sock + self.ssl_object = ssl_context.wrap_bio( + self.incoming, + self.outgoing, + server_hostname=server_hostname, + ) + self.run_io(self.ssl_object.do_handshake) + + def run_io(self, func: Callable[..., T], *args: Any) -> T: + while True: + want_read = False + want_write = False + try: + result = func(*args) + except ssl_module.SSLWantReadError: + want_read = True + except ssl_module.SSLWantWriteError: # pragma: no cover + want_write = True + + # Write outgoing data in all cases. + data = self.outgoing.read() + if data: + self.ssl_socket.sendall(data) + + # Read incoming data and retry on SSLWantReadError. + if want_read: + data = self.ssl_socket.recv(self.recv_bufsize) + if data: + self.incoming.write(data) + else: + self.incoming.write_eof() + continue + # Retry after writing outgoing data on SSLWantWriteError. + if want_write: # pragma: no cover + continue + # Return result if no error happened. + return result + + def recv(self, buflen: int) -> bytes: + try: + return self.run_io(self.ssl_object.read, buflen) + except ssl_module.SSLEOFError: + return b"" # always ignore ragged EOFs + + def send(self, data: bytes) -> int: + return self.run_io(self.ssl_object.write, data) + + def sendall(self, data: bytes) -> None: + # adapted from ssl_module.SSLSocket.sendall() + count = 0 + with memoryview(data) as view, view.cast("B") as byte_view: + amount = len(byte_view) + while count < amount: + count += self.send(byte_view[count:]) + + # recv_into(), recvfrom(), recvfrom_into(), sendto(), unwrap(), and the + # flags argument aren't implemented because websockets doesn't need them. + + def __getattr__(self, name: str) -> Any: + return getattr(self.ssl_socket, name) diff --git a/gestao_raul/Lib/site-packages/websockets/sync/connection.py b/gestao_raul/Lib/site-packages/websockets/sync/connection.py new file mode 100644 index 0000000..8b9e062 --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/sync/connection.py @@ -0,0 +1,1072 @@ +from __future__ import annotations + +import contextlib +import logging +import random +import socket +import struct +import threading +import time +import uuid +from collections.abc import Iterable, Iterator, Mapping +from types import TracebackType +from typing import Any, Literal, overload + +from ..exceptions import ( + ConcurrencyError, + ConnectionClosed, + ConnectionClosedOK, + ProtocolError, +) +from ..frames import DATA_OPCODES, BytesLike, CloseCode, Frame, Opcode +from ..http11 import Request, Response +from ..protocol import CLOSED, OPEN, Event, Protocol, State +from ..typing import Data, LoggerLike, Subprotocol +from .messages import Assembler +from .utils import Deadline + + +__all__ = ["Connection"] + + +class Connection: + """ + :mod:`threading` implementation of a WebSocket connection. + + :class:`Connection` provides APIs shared between WebSocket servers and + clients. + + You shouldn't use it directly. Instead, use + :class:`~websockets.sync.client.ClientConnection` or + :class:`~websockets.sync.server.ServerConnection`. + + """ + + recv_bufsize = 65536 + + def __init__( + self, + socket: socket.socket, + protocol: Protocol, + *, + ping_interval: float | None = 20, + ping_timeout: float | None = 20, + close_timeout: float | None = 10, + max_queue: int | None | tuple[int | None, int | None] = 16, + ) -> None: + self.socket = socket + self.protocol = protocol + self.ping_interval = ping_interval + self.ping_timeout = ping_timeout + self.close_timeout = close_timeout + if isinstance(max_queue, int) or max_queue is None: + max_queue = (max_queue, None) + self.max_queue = max_queue + + # Inject reference to this instance in the protocol's logger. + self.protocol.logger = logging.LoggerAdapter( + self.protocol.logger, + {"websocket": self}, + ) + + # Copy attributes from the protocol for convenience. + self.id: uuid.UUID = self.protocol.id + """Unique identifier of the connection. Useful in logs.""" + self.logger: LoggerLike = self.protocol.logger + """Logger for this connection.""" + self.debug = self.protocol.debug + + # HTTP handshake request and response. + self.request: Request | None = None + """Opening handshake request.""" + self.response: Response | None = None + """Opening handshake response.""" + + # Mutex serializing interactions with the protocol. + self.protocol_mutex = threading.Lock() + + # Lock stopping reads when the assembler buffer is full. + self.recv_flow_control = threading.Lock() + + # Assembler turning frames into messages and serializing reads. + self.recv_messages = Assembler( + *self.max_queue, + pause=self.recv_flow_control.acquire, + resume=self.recv_flow_control.release, + ) + + # Deadline for the closing handshake. + self.close_deadline: Deadline | None = None + + # Whether we are busy sending a fragmented message. + self.send_in_progress = False + + # Mapping of ping IDs to pong waiters, in chronological order. + self.pong_waiters: dict[bytes, tuple[threading.Event, float, bool]] = {} + + self.latency: float = 0 + """ + Latency of the connection, in seconds. + + Latency is defined as the round-trip time of the connection. It is + measured by sending a Ping frame and waiting for a matching Pong frame. + Before the first measurement, :attr:`latency` is ``0``. + + By default, websockets enables a :ref:`keepalive ` mechanism + that sends Ping frames automatically at regular intervals. You can also + send Ping frames and measure latency with :meth:`ping`. + """ + + # Thread that sends keepalive pings. None when ping_interval is None. + self.keepalive_thread: threading.Thread | None = None + + # Exception raised in recv_events, to be chained to ConnectionClosed + # in the user thread in order to show why the TCP connection dropped. + self.recv_exc: BaseException | None = None + + # Receiving events from the socket. This thread is marked as daemon to + # allow creating a connection in a non-daemon thread and using it in a + # daemon thread. This mustn't prevent the interpreter from exiting. + self.recv_events_thread = threading.Thread( + target=self.recv_events, + daemon=True, + ) + + # Start recv_events only after all attributes are initialized. + self.recv_events_thread.start() + + # Public attributes + + @property + def local_address(self) -> Any: + """ + Local address of the connection. + + For IPv4 connections, this is a ``(host, port)`` tuple. + + The format of the address depends on the address family. + See :meth:`~socket.socket.getsockname`. + + """ + return self.socket.getsockname() + + @property + def remote_address(self) -> Any: + """ + Remote address of the connection. + + For IPv4 connections, this is a ``(host, port)`` tuple. + + The format of the address depends on the address family. + See :meth:`~socket.socket.getpeername`. + + """ + return self.socket.getpeername() + + @property + def state(self) -> State: + """ + State of the WebSocket connection, defined in :rfc:`6455`. + + This attribute is provided for completeness. Typical applications + shouldn't check its value. Instead, they should call :meth:`~recv` or + :meth:`send` and handle :exc:`~websockets.exceptions.ConnectionClosed` + exceptions. + + """ + return self.protocol.state + + @property + def subprotocol(self) -> Subprotocol | None: + """ + Subprotocol negotiated during the opening handshake. + + :obj:`None` if no subprotocol was negotiated. + + """ + return self.protocol.subprotocol + + @property + def close_code(self) -> int | None: + """ + State of the WebSocket connection, defined in :rfc:`6455`. + + This attribute is provided for completeness. Typical applications + shouldn't check its value. Instead, they should inspect attributes + of :exc:`~websockets.exceptions.ConnectionClosed` exceptions. + + """ + return self.protocol.close_code + + @property + def close_reason(self) -> str | None: + """ + State of the WebSocket connection, defined in :rfc:`6455`. + + This attribute is provided for completeness. Typical applications + shouldn't check its value. Instead, they should inspect attributes + of :exc:`~websockets.exceptions.ConnectionClosed` exceptions. + + """ + return self.protocol.close_reason + + # Public methods + + def __enter__(self) -> Connection: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + ) -> None: + if exc_type is None: + self.close() + else: + self.close(CloseCode.INTERNAL_ERROR) + + def __iter__(self) -> Iterator[Data]: + """ + Iterate on incoming messages. + + The iterator calls :meth:`recv` and yields messages in an infinite loop. + + It exits when the connection is closed normally. It raises a + :exc:`~websockets.exceptions.ConnectionClosedError` exception after a + protocol error or a network failure. + + """ + try: + while True: + yield self.recv() + except ConnectionClosedOK: + return + + # This overload structure is required to avoid the error: + # "parameter without a default follows parameter with a default" + + @overload + def recv(self, timeout: float | None, decode: Literal[True]) -> str: ... + + @overload + def recv(self, timeout: float | None, decode: Literal[False]) -> bytes: ... + + @overload + def recv(self, timeout: float | None = None, *, decode: Literal[True]) -> str: ... + + @overload + def recv( + self, timeout: float | None = None, *, decode: Literal[False] + ) -> bytes: ... + + @overload + def recv( + self, timeout: float | None = None, decode: bool | None = None + ) -> Data: ... + + def recv(self, timeout: float | None = None, decode: bool | None = None) -> Data: + """ + Receive the next message. + + When the connection is closed, :meth:`recv` raises + :exc:`~websockets.exceptions.ConnectionClosed`. Specifically, it raises + :exc:`~websockets.exceptions.ConnectionClosedOK` after a normal closure + and :exc:`~websockets.exceptions.ConnectionClosedError` after a protocol + error or a network failure. This is how you detect the end of the + message stream. + + If ``timeout`` is :obj:`None`, block until a message is received. If + ``timeout`` is set, wait up to ``timeout`` seconds for a message to be + received and return it, else raise :exc:`TimeoutError`. If ``timeout`` + is ``0`` or negative, check if a message has been received already and + return it, else raise :exc:`TimeoutError`. + + If the message is fragmented, wait until all fragments are received, + reassemble them, and return the whole message. + + Args: + timeout: Timeout for receiving a message in seconds. + decode: Set this flag to override the default behavior of returning + :class:`str` or :class:`bytes`. See below for details. + + Returns: + A string (:class:`str`) for a Text_ frame or a bytestring + (:class:`bytes`) for a Binary_ frame. + + .. _Text: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 + .. _Binary: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 + + You may override this behavior with the ``decode`` argument: + + * Set ``decode=False`` to disable UTF-8 decoding of Text_ frames and + return a bytestring (:class:`bytes`). This improves performance + when decoding isn't needed, for example if the message contains + JSON and you're using a JSON library that expects a bytestring. + * Set ``decode=True`` to force UTF-8 decoding of Binary_ frames + and return a string (:class:`str`). This may be useful for + servers that send binary frames instead of text frames. + + Raises: + ConnectionClosed: When the connection is closed. + ConcurrencyError: If two threads call :meth:`recv` or + :meth:`recv_streaming` concurrently. + + """ + try: + return self.recv_messages.get(timeout, decode) + except EOFError: + pass + # fallthrough + except ConcurrencyError: + raise ConcurrencyError( + "cannot call recv while another thread " + "is already running recv or recv_streaming" + ) from None + except UnicodeDecodeError as exc: + with self.send_context(): + self.protocol.fail( + CloseCode.INVALID_DATA, + f"{exc.reason} at position {exc.start}", + ) + # fallthrough + + # Wait for the protocol state to be CLOSED before accessing close_exc. + self.recv_events_thread.join() + raise self.protocol.close_exc from self.recv_exc + + @overload + def recv_streaming(self, decode: Literal[True]) -> Iterator[str]: ... + + @overload + def recv_streaming(self, decode: Literal[False]) -> Iterator[bytes]: ... + + @overload + def recv_streaming(self, decode: bool | None = None) -> Iterator[Data]: ... + + def recv_streaming(self, decode: bool | None = None) -> Iterator[Data]: + """ + Receive the next message frame by frame. + + This method is designed for receiving fragmented messages. It returns an + iterator that yields each fragment as it is received. This iterator must + be fully consumed. Else, future calls to :meth:`recv` or + :meth:`recv_streaming` will raise + :exc:`~websockets.exceptions.ConcurrencyError`, making the connection + unusable. + + :meth:`recv_streaming` raises the same exceptions as :meth:`recv`. + + Args: + decode: Set this flag to override the default behavior of returning + :class:`str` or :class:`bytes`. See below for details. + + Returns: + An iterator of strings (:class:`str`) for a Text_ frame or + bytestrings (:class:`bytes`) for a Binary_ frame. + + .. _Text: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 + .. _Binary: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 + + You may override this behavior with the ``decode`` argument: + + * Set ``decode=False`` to disable UTF-8 decoding of Text_ frames + and return bytestrings (:class:`bytes`). This may be useful to + optimize performance when decoding isn't needed. + * Set ``decode=True`` to force UTF-8 decoding of Binary_ frames + and return strings (:class:`str`). This is useful for servers + that send binary frames instead of text frames. + + Raises: + ConnectionClosed: When the connection is closed. + ConcurrencyError: If two threads call :meth:`recv` or + :meth:`recv_streaming` concurrently. + + """ + try: + yield from self.recv_messages.get_iter(decode) + return + except EOFError: + pass + # fallthrough + except ConcurrencyError: + raise ConcurrencyError( + "cannot call recv_streaming while another thread " + "is already running recv or recv_streaming" + ) from None + except UnicodeDecodeError as exc: + with self.send_context(): + self.protocol.fail( + CloseCode.INVALID_DATA, + f"{exc.reason} at position {exc.start}", + ) + # fallthrough + + # Wait for the protocol state to be CLOSED before accessing close_exc. + self.recv_events_thread.join() + raise self.protocol.close_exc from self.recv_exc + + def send( + self, + message: Data | Iterable[Data], + text: bool | None = None, + ) -> None: + """ + Send a message. + + A string (:class:`str`) is sent as a Text_ frame. A bytestring or + bytes-like object (:class:`bytes`, :class:`bytearray`, or + :class:`memoryview`) is sent as a Binary_ frame. + + .. _Text: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 + .. _Binary: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 + + You may override this behavior with the ``text`` argument: + + * Set ``text=True`` to send a bytestring or bytes-like object + (:class:`bytes`, :class:`bytearray`, or :class:`memoryview`) as a + Text_ frame. This improves performance when the message is already + UTF-8 encoded, for example if the message contains JSON and you're + using a JSON library that produces a bytestring. + * Set ``text=False`` to send a string (:class:`str`) in a Binary_ + frame. This may be useful for servers that expect binary frames + instead of text frames. + + :meth:`send` also accepts an iterable of strings, bytestrings, or + bytes-like objects to enable fragmentation_. Each item is treated as a + message fragment and sent in its own frame. All items must be of the + same type, or else :meth:`send` will raise a :exc:`TypeError` and the + connection will be closed. + + .. _fragmentation: https://datatracker.ietf.org/doc/html/rfc6455#section-5.4 + + :meth:`send` rejects dict-like objects because this is often an error. + (If you really want to send the keys of a dict-like object as fragments, + call its :meth:`~dict.keys` method and pass the result to :meth:`send`.) + + When the connection is closed, :meth:`send` raises + :exc:`~websockets.exceptions.ConnectionClosed`. Specifically, it + raises :exc:`~websockets.exceptions.ConnectionClosedOK` after a normal + connection closure and + :exc:`~websockets.exceptions.ConnectionClosedError` after a protocol + error or a network failure. + + Args: + message: Message to send. + + Raises: + ConnectionClosed: When the connection is closed. + ConcurrencyError: If the connection is sending a fragmented message. + TypeError: If ``message`` doesn't have a supported type. + + """ + # Unfragmented message -- this case must be handled first because + # strings and bytes-like objects are iterable. + + if isinstance(message, str): + with self.send_context(): + if self.send_in_progress: + raise ConcurrencyError( + "cannot call send while another thread is already running send" + ) + if text is False: + self.protocol.send_binary(message.encode()) + else: + self.protocol.send_text(message.encode()) + + elif isinstance(message, BytesLike): + with self.send_context(): + if self.send_in_progress: + raise ConcurrencyError( + "cannot call send while another thread is already running send" + ) + if text is True: + self.protocol.send_text(message) + else: + self.protocol.send_binary(message) + + # Catch a common mistake -- passing a dict to send(). + + elif isinstance(message, Mapping): + raise TypeError("data is a dict-like object") + + # Fragmented message -- regular iterator. + + elif isinstance(message, Iterable): + chunks = iter(message) + try: + chunk = next(chunks) + except StopIteration: + return + + try: + # First fragment. + if isinstance(chunk, str): + with self.send_context(): + if self.send_in_progress: + raise ConcurrencyError( + "cannot call send while another thread " + "is already running send" + ) + self.send_in_progress = True + if text is False: + self.protocol.send_binary(chunk.encode(), fin=False) + else: + self.protocol.send_text(chunk.encode(), fin=False) + encode = True + elif isinstance(chunk, BytesLike): + with self.send_context(): + if self.send_in_progress: + raise ConcurrencyError( + "cannot call send while another thread " + "is already running send" + ) + self.send_in_progress = True + if text is True: + self.protocol.send_text(chunk, fin=False) + else: + self.protocol.send_binary(chunk, fin=False) + encode = False + else: + raise TypeError("data iterable must contain bytes or str") + + # Other fragments + for chunk in chunks: + if isinstance(chunk, str) and encode: + with self.send_context(): + assert self.send_in_progress + self.protocol.send_continuation(chunk.encode(), fin=False) + elif isinstance(chunk, BytesLike) and not encode: + with self.send_context(): + assert self.send_in_progress + self.protocol.send_continuation(chunk, fin=False) + else: + raise TypeError("data iterable must contain uniform types") + + # Final fragment. + with self.send_context(): + self.protocol.send_continuation(b"", fin=True) + self.send_in_progress = False + + except ConcurrencyError: + # We didn't start sending a fragmented message. + # The connection is still usable. + raise + + except Exception: + # We're half-way through a fragmented message and we can't + # complete it. This makes the connection unusable. + with self.send_context(): + self.protocol.fail( + CloseCode.INTERNAL_ERROR, + "error in fragmented message", + ) + raise + + else: + raise TypeError("data must be str, bytes, or iterable") + + def close(self, code: int = CloseCode.NORMAL_CLOSURE, reason: str = "") -> None: + """ + Perform the closing handshake. + + :meth:`close` waits for the other end to complete the handshake, for the + TCP connection to terminate, and for all incoming messages to be read + with :meth:`recv`. + + :meth:`close` is idempotent: it doesn't do anything once the + connection is closed. + + Args: + code: WebSocket close code. + reason: WebSocket close reason. + + """ + try: + # The context manager takes care of waiting for the TCP connection + # to terminate after calling a method that sends a close frame. + with self.send_context(): + if self.send_in_progress: + self.protocol.fail( + CloseCode.INTERNAL_ERROR, + "close during fragmented message", + ) + else: + self.protocol.send_close(code, reason) + except ConnectionClosed: + # Ignore ConnectionClosed exceptions raised from send_context(). + # They mean that the connection is closed, which was the goal. + pass + + def ping( + self, + data: Data | None = None, + ack_on_close: bool = False, + ) -> threading.Event: + """ + Send a Ping_. + + .. _Ping: https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.2 + + A ping may serve as a keepalive or as a check that the remote endpoint + received all messages up to this point + + Args: + data: Payload of the ping. A :class:`str` will be encoded to UTF-8. + If ``data`` is :obj:`None`, the payload is four random bytes. + ack_on_close: when this option is :obj:`True`, the event will also + be set when the connection is closed. While this avoids getting + stuck waiting for a pong that will never arrive, it requires + checking that the state of the connection is still ``OPEN`` to + confirm that a pong was received, rather than the connection + being closed. + + Returns: + An event that will be set when the corresponding pong is received. + You can ignore it if you don't intend to wait. + + :: + + pong_event = ws.ping() + pong_event.wait() # only if you want to wait for the pong + + Raises: + ConnectionClosed: When the connection is closed. + ConcurrencyError: If another ping was sent with the same data and + the corresponding pong wasn't received yet. + + """ + if isinstance(data, BytesLike): + data = bytes(data) + elif isinstance(data, str): + data = data.encode() + elif data is not None: + raise TypeError("data must be str or bytes-like") + + with self.send_context(): + # Protect against duplicates if a payload is explicitly set. + if data in self.pong_waiters: + raise ConcurrencyError("already waiting for a pong with the same data") + + # Generate a unique random payload otherwise. + while data is None or data in self.pong_waiters: + data = struct.pack("!I", random.getrandbits(32)) + + pong_waiter = threading.Event() + self.pong_waiters[data] = (pong_waiter, time.monotonic(), ack_on_close) + self.protocol.send_ping(data) + return pong_waiter + + def pong(self, data: Data = b"") -> None: + """ + Send a Pong_. + + .. _Pong: https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.3 + + An unsolicited pong may serve as a unidirectional heartbeat. + + Args: + data: Payload of the pong. A :class:`str` will be encoded to UTF-8. + + Raises: + ConnectionClosed: When the connection is closed. + + """ + if isinstance(data, BytesLike): + data = bytes(data) + elif isinstance(data, str): + data = data.encode() + else: + raise TypeError("data must be str or bytes-like") + + with self.send_context(): + self.protocol.send_pong(data) + + # Private methods + + def process_event(self, event: Event) -> None: + """ + Process one incoming event. + + This method is overridden in subclasses to handle the handshake. + + """ + assert isinstance(event, Frame) + if event.opcode in DATA_OPCODES: + self.recv_messages.put(event) + + if event.opcode is Opcode.PONG: + self.acknowledge_pings(bytes(event.data)) + + def acknowledge_pings(self, data: bytes) -> None: + """ + Acknowledge pings when receiving a pong. + + """ + with self.protocol_mutex: + # Ignore unsolicited pong. + if data not in self.pong_waiters: + return + + pong_timestamp = time.monotonic() + + # Sending a pong for only the most recent ping is legal. + # Acknowledge all previous pings too in that case. + ping_id = None + ping_ids = [] + for ping_id, ( + pong_waiter, + ping_timestamp, + _ack_on_close, + ) in self.pong_waiters.items(): + ping_ids.append(ping_id) + pong_waiter.set() + if ping_id == data: + self.latency = pong_timestamp - ping_timestamp + break + else: + raise AssertionError("solicited pong not found in pings") + + # Remove acknowledged pings from self.pong_waiters. + for ping_id in ping_ids: + del self.pong_waiters[ping_id] + + def acknowledge_pending_pings(self) -> None: + """ + Acknowledge pending pings when the connection is closed. + + """ + assert self.protocol.state is CLOSED + + for pong_waiter, _ping_timestamp, ack_on_close in self.pong_waiters.values(): + if ack_on_close: + pong_waiter.set() + + self.pong_waiters.clear() + + def keepalive(self) -> None: + """ + Send a Ping frame and wait for a Pong frame at regular intervals. + + """ + assert self.ping_interval is not None + try: + while True: + # If self.ping_timeout > self.latency > self.ping_interval, + # pings will be sent immediately after receiving pongs. + # The period will be longer than self.ping_interval. + self.recv_events_thread.join(self.ping_interval - self.latency) + if not self.recv_events_thread.is_alive(): + break + + try: + pong_waiter = self.ping(ack_on_close=True) + except ConnectionClosed: + break + if self.debug: + self.logger.debug("% sent keepalive ping") + + if self.ping_timeout is not None: + # + if pong_waiter.wait(self.ping_timeout): + if self.debug: + self.logger.debug("% received keepalive pong") + else: + if self.debug: + self.logger.debug("- timed out waiting for keepalive pong") + with self.send_context(): + self.protocol.fail( + CloseCode.INTERNAL_ERROR, + "keepalive ping timeout", + ) + break + except Exception: + self.logger.error("keepalive ping failed", exc_info=True) + + def start_keepalive(self) -> None: + """ + Run :meth:`keepalive` in a thread, unless keepalive is disabled. + + """ + if self.ping_interval is not None: + # This thread is marked as daemon like self.recv_events_thread. + self.keepalive_thread = threading.Thread( + target=self.keepalive, + daemon=True, + ) + self.keepalive_thread.start() + + def recv_events(self) -> None: + """ + Read incoming data from the socket and process events. + + Run this method in a thread as long as the connection is alive. + + ``recv_events()`` exits immediately when the ``self.socket`` is closed. + + """ + try: + while True: + try: + with self.recv_flow_control: + if self.close_deadline is not None: + self.socket.settimeout(self.close_deadline.timeout()) + data = self.socket.recv(self.recv_bufsize) + except Exception as exc: + if self.debug: + self.logger.debug( + "! error while receiving data", + exc_info=True, + ) + # When the closing handshake is initiated by our side, + # recv() may block until send_context() closes the socket. + # In that case, send_context() already set recv_exc. + # Calling set_recv_exc() avoids overwriting it. + with self.protocol_mutex: + self.set_recv_exc(exc) + break + + if data == b"": + break + + # Acquire the connection lock. + with self.protocol_mutex: + # Feed incoming data to the protocol. + self.protocol.receive_data(data) + + # This isn't expected to raise an exception. + events = self.protocol.events_received() + + # Write outgoing data to the socket. + try: + self.send_data() + except Exception as exc: + if self.debug: + self.logger.debug( + "! error while sending data", + exc_info=True, + ) + # Similarly to the above, avoid overriding an exception + # set by send_context(), in case of a race condition + # i.e. send_context() closes the socket after recv() + # returns above but before send_data() calls send(). + self.set_recv_exc(exc) + break + + if self.protocol.close_expected(): + # If the connection is expected to close soon, set the + # close deadline based on the close timeout. + if self.close_deadline is None: + self.close_deadline = Deadline(self.close_timeout) + + # Unlock conn_mutex before processing events. Else, the + # application can't send messages in response to events. + + # If self.send_data raised an exception, then events are lost. + # Given that automatic responses write small amounts of data, + # this should be uncommon, so we don't handle the edge case. + + for event in events: + # This isn't expected to raise an exception. + self.process_event(event) + + # Breaking out of the while True: ... loop means that we believe + # that the socket doesn't work anymore. + with self.protocol_mutex: + # Feed the end of the data stream to the protocol. + self.protocol.receive_eof() + + # This isn't expected to raise an exception. + events = self.protocol.events_received() + + # There is no error handling because send_data() can only write + # the end of the data stream here and it handles errors itself. + self.send_data() + + # This code path is triggered when receiving an HTTP response + # without a Content-Length header. This is the only case where + # reading until EOF generates an event; all other events have + # a known length. Ignore for coverage measurement because tests + # are in test_client.py rather than test_connection.py. + for event in events: # pragma: no cover + # This isn't expected to raise an exception. + self.process_event(event) + + except Exception as exc: + # This branch should never run. It's a safety net in case of bugs. + self.logger.error("unexpected internal error", exc_info=True) + with self.protocol_mutex: + self.set_recv_exc(exc) + finally: + # This isn't expected to raise an exception. + self.close_socket() + + @contextlib.contextmanager + def send_context( + self, + *, + expected_state: State = OPEN, # CONNECTING during the opening handshake + ) -> Iterator[None]: + """ + Create a context for writing to the connection from user code. + + On entry, :meth:`send_context` acquires the connection lock and checks + that the connection is open; on exit, it writes outgoing data to the + socket:: + + with self.send_context(): + self.protocol.send_text(message.encode()) + + When the connection isn't open on entry, when the connection is expected + to close on exit, or when an unexpected error happens, terminating the + connection, :meth:`send_context` waits until the connection is closed + then raises :exc:`~websockets.exceptions.ConnectionClosed`. + + """ + # Should we wait until the connection is closed? + wait_for_close = False + # Should we close the socket and raise ConnectionClosed? + raise_close_exc = False + # What exception should we chain ConnectionClosed to? + original_exc: BaseException | None = None + + # Acquire the protocol lock. + with self.protocol_mutex: + if self.protocol.state is expected_state: + # Let the caller interact with the protocol. + try: + yield + except (ProtocolError, ConcurrencyError): + # The protocol state wasn't changed. Exit immediately. + raise + except Exception as exc: + self.logger.error("unexpected internal error", exc_info=True) + # This branch should never run. It's a safety net in case of + # bugs. Since we don't know what happened, we will close the + # connection and raise the exception to the caller. + wait_for_close = False + raise_close_exc = True + original_exc = exc + else: + # Check if the connection is expected to close soon. + if self.protocol.close_expected(): + wait_for_close = True + # If the connection is expected to close soon, set the + # close deadline based on the close timeout. + # Since we tested earlier that protocol.state was OPEN + # (or CONNECTING) and we didn't release protocol_mutex, + # it is certain that self.close_deadline is still None. + assert self.close_deadline is None + self.close_deadline = Deadline(self.close_timeout) + # Write outgoing data to the socket. + try: + self.send_data() + except Exception as exc: + if self.debug: + self.logger.debug( + "! error while sending data", + exc_info=True, + ) + # While the only expected exception here is OSError, + # other exceptions would be treated identically. + wait_for_close = False + raise_close_exc = True + original_exc = exc + + else: # self.protocol.state is not expected_state + # Minor layering violation: we assume that the connection + # will be closing soon if it isn't in the expected state. + wait_for_close = True + raise_close_exc = True + + # To avoid a deadlock, release the connection lock by exiting the + # context manager before waiting for recv_events() to terminate. + + # If the connection is expected to close soon and the close timeout + # elapses, close the socket to terminate the connection. + if wait_for_close: + if self.close_deadline is None: + timeout = self.close_timeout + else: + # Thread.join() returns immediately if timeout is negative. + timeout = self.close_deadline.timeout(raise_if_elapsed=False) + self.recv_events_thread.join(timeout) + + if self.recv_events_thread.is_alive(): + # There's no risk to overwrite another error because + # original_exc is never set when wait_for_close is True. + assert original_exc is None + original_exc = TimeoutError("timed out while closing connection") + # Set recv_exc before closing the socket in order to get + # proper exception reporting. + raise_close_exc = True + with self.protocol_mutex: + self.set_recv_exc(original_exc) + + # If an error occurred, close the socket to terminate the connection and + # raise an exception. + if raise_close_exc: + self.close_socket() + # Wait for the protocol state to be CLOSED before accessing close_exc. + self.recv_events_thread.join() + raise self.protocol.close_exc from original_exc + + def send_data(self) -> None: + """ + Send outgoing data. + + This method requires holding protocol_mutex. + + Raises: + OSError: When a socket operations fails. + + """ + assert self.protocol_mutex.locked() + for data in self.protocol.data_to_send(): + if data: + if self.close_deadline is not None: + self.socket.settimeout(self.close_deadline.timeout()) + self.socket.sendall(data) + else: + try: + self.socket.shutdown(socket.SHUT_WR) + except OSError: # socket already closed + pass + + def set_recv_exc(self, exc: BaseException | None) -> None: + """ + Set recv_exc, if not set yet. + + This method requires holding protocol_mutex. + + """ + assert self.protocol_mutex.locked() + if self.recv_exc is None: # pragma: no branch + self.recv_exc = exc + + def close_socket(self) -> None: + """ + Shutdown and close socket. Close message assembler. + + Calling close_socket() guarantees that recv_events() terminates. Indeed, + recv_events() may block only on socket.recv() or on recv_messages.put(). + + """ + # shutdown() is required to interrupt recv() on Linux. + try: + self.socket.shutdown(socket.SHUT_RDWR) + except OSError: + pass # socket is already closed + self.socket.close() + + # Calling protocol.receive_eof() is safe because it's idempotent. + # This guarantees that the protocol state becomes CLOSED. + with self.protocol_mutex: + self.protocol.receive_eof() + assert self.protocol.state is CLOSED + + # Abort recv() with a ConnectionClosed exception. + self.recv_messages.close() + + # Acknowledge pings sent with the ack_on_close option. + self.acknowledge_pending_pings() diff --git a/gestao_raul/Lib/site-packages/websockets/sync/messages.py b/gestao_raul/Lib/site-packages/websockets/sync/messages.py new file mode 100644 index 0000000..c619e78 --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/sync/messages.py @@ -0,0 +1,345 @@ +from __future__ import annotations + +import codecs +import queue +import threading +from typing import Any, Callable, Iterable, Iterator, Literal, overload + +from ..exceptions import ConcurrencyError +from ..frames import OP_BINARY, OP_CONT, OP_TEXT, Frame +from ..typing import Data +from .utils import Deadline + + +__all__ = ["Assembler"] + +UTF8Decoder = codecs.getincrementaldecoder("utf-8") + + +class Assembler: + """ + Assemble messages from frames. + + :class:`Assembler` expects only data frames. The stream of frames must + respect the protocol; if it doesn't, the behavior is undefined. + + Args: + pause: Called when the buffer of frames goes above the high water mark; + should pause reading from the network. + resume: Called when the buffer of frames goes below the low water mark; + should resume reading from the network. + + """ + + def __init__( + self, + high: int | None = None, + low: int | None = None, + pause: Callable[[], Any] = lambda: None, + resume: Callable[[], Any] = lambda: None, + ) -> None: + # Serialize reads and writes -- except for reads via synchronization + # primitives provided by the threading and queue modules. + self.mutex = threading.Lock() + + # Queue of incoming frames. + self.frames: queue.SimpleQueue[Frame | None] = queue.SimpleQueue() + + # We cannot put a hard limit on the size of the queue because a single + # call to Protocol.data_received() could produce thousands of frames, + # which must be buffered. Instead, we pause reading when the buffer goes + # above the high limit and we resume when it goes under the low limit. + if high is not None and low is None: + low = high // 4 + if high is None and low is not None: + high = low * 4 + if high is not None and low is not None: + if low < 0: + raise ValueError("low must be positive or equal to zero") + if high < low: + raise ValueError("high must be greater than or equal to low") + self.high, self.low = high, low + self.pause = pause + self.resume = resume + self.paused = False + + # This flag prevents concurrent calls to get() by user code. + self.get_in_progress = False + + # This flag marks the end of the connection. + self.closed = False + + def get_next_frame(self, timeout: float | None = None) -> Frame: + # Helper to factor out the logic for getting the next frame from the + # queue, while handling timeouts and reaching the end of the stream. + if self.closed: + try: + frame = self.frames.get(block=False) + except queue.Empty: + raise EOFError("stream of frames ended") from None + else: + try: + # Check for a frame that's already received if timeout <= 0. + # SimpleQueue.get() doesn't support negative timeout values. + if timeout is not None and timeout <= 0: + frame = self.frames.get(block=False) + else: + frame = self.frames.get(block=True, timeout=timeout) + except queue.Empty: + raise TimeoutError(f"timed out in {timeout:.1f}s") from None + if frame is None: + raise EOFError("stream of frames ended") + return frame + + def reset_queue(self, frames: Iterable[Frame]) -> None: + # Helper to put frames back into the queue after they were fetched. + # This happens only when the queue is empty. However, by the time + # we acquire self.mutex, put() may have added items in the queue. + # Therefore, we must handle the case where the queue is not empty. + frame: Frame | None + with self.mutex: + queued = [] + try: + while True: + queued.append(self.frames.get(block=False)) + except queue.Empty: + pass + for frame in frames: + self.frames.put(frame) + # This loop runs only when a race condition occurs. + for frame in queued: # pragma: no cover + self.frames.put(frame) + + # This overload structure is required to avoid the error: + # "parameter without a default follows parameter with a default" + + @overload + def get(self, timeout: float | None, decode: Literal[True]) -> str: ... + + @overload + def get(self, timeout: float | None, decode: Literal[False]) -> bytes: ... + + @overload + def get(self, timeout: float | None = None, *, decode: Literal[True]) -> str: ... + + @overload + def get(self, timeout: float | None = None, *, decode: Literal[False]) -> bytes: ... + + @overload + def get(self, timeout: float | None = None, decode: bool | None = None) -> Data: ... + + def get(self, timeout: float | None = None, decode: bool | None = None) -> Data: + """ + Read the next message. + + :meth:`get` returns a single :class:`str` or :class:`bytes`. + + If the message is fragmented, :meth:`get` waits until the last frame is + received, then it reassembles the message and returns it. To receive + messages frame by frame, use :meth:`get_iter` instead. + + Args: + timeout: If a timeout is provided and elapses before a complete + message is received, :meth:`get` raises :exc:`TimeoutError`. + decode: :obj:`False` disables UTF-8 decoding of text frames and + returns :class:`bytes`. :obj:`True` forces UTF-8 decoding of + binary frames and returns :class:`str`. + + Raises: + EOFError: If the stream of frames has ended. + UnicodeDecodeError: If a text frame contains invalid UTF-8. + ConcurrencyError: If two coroutines run :meth:`get` or + :meth:`get_iter` concurrently. + TimeoutError: If a timeout is provided and elapses before a + complete message is received. + + """ + with self.mutex: + if self.get_in_progress: + raise ConcurrencyError("get() or get_iter() is already running") + self.get_in_progress = True + + # Locking with get_in_progress prevents concurrent execution + # until get() fetches a complete message or times out. + + try: + deadline = Deadline(timeout) + + # First frame + frame = self.get_next_frame(deadline.timeout(raise_if_elapsed=False)) + with self.mutex: + self.maybe_resume() + assert frame.opcode is OP_TEXT or frame.opcode is OP_BINARY + if decode is None: + decode = frame.opcode is OP_TEXT + frames = [frame] + + # Following frames, for fragmented messages + while not frame.fin: + try: + frame = self.get_next_frame( + deadline.timeout(raise_if_elapsed=False) + ) + except TimeoutError: + # Put frames already received back into the queue + # so that future calls to get() can return them. + self.reset_queue(frames) + raise + with self.mutex: + self.maybe_resume() + assert frame.opcode is OP_CONT + frames.append(frame) + + finally: + self.get_in_progress = False + + data = b"".join(frame.data for frame in frames) + if decode: + return data.decode() + else: + return data + + @overload + def get_iter(self, decode: Literal[True]) -> Iterator[str]: ... + + @overload + def get_iter(self, decode: Literal[False]) -> Iterator[bytes]: ... + + @overload + def get_iter(self, decode: bool | None = None) -> Iterator[Data]: ... + + def get_iter(self, decode: bool | None = None) -> Iterator[Data]: + """ + Stream the next message. + + Iterating the return value of :meth:`get_iter` yields a :class:`str` or + :class:`bytes` for each frame in the message. + + The iterator must be fully consumed before calling :meth:`get_iter` or + :meth:`get` again. Else, :exc:`ConcurrencyError` is raised. + + This method only makes sense for fragmented messages. If messages aren't + fragmented, use :meth:`get` instead. + + Args: + decode: :obj:`False` disables UTF-8 decoding of text frames and + returns :class:`bytes`. :obj:`True` forces UTF-8 decoding of + binary frames and returns :class:`str`. + + Raises: + EOFError: If the stream of frames has ended. + UnicodeDecodeError: If a text frame contains invalid UTF-8. + ConcurrencyError: If two coroutines run :meth:`get` or + :meth:`get_iter` concurrently. + + """ + with self.mutex: + if self.get_in_progress: + raise ConcurrencyError("get() or get_iter() is already running") + self.get_in_progress = True + + # Locking with get_in_progress prevents concurrent execution + # until get_iter() fetches a complete message or times out. + + # If get_iter() raises an exception e.g. in decoder.decode(), + # get_in_progress remains set and the connection becomes unusable. + + # First frame + frame = self.get_next_frame() + with self.mutex: + self.maybe_resume() + assert frame.opcode is OP_TEXT or frame.opcode is OP_BINARY + if decode is None: + decode = frame.opcode is OP_TEXT + if decode: + decoder = UTF8Decoder() + yield decoder.decode(frame.data, frame.fin) + else: + yield frame.data + + # Following frames, for fragmented messages + while not frame.fin: + frame = self.get_next_frame() + with self.mutex: + self.maybe_resume() + assert frame.opcode is OP_CONT + if decode: + yield decoder.decode(frame.data, frame.fin) + else: + yield frame.data + + self.get_in_progress = False + + def put(self, frame: Frame) -> None: + """ + Add ``frame`` to the next message. + + Raises: + EOFError: If the stream of frames has ended. + + """ + with self.mutex: + if self.closed: + raise EOFError("stream of frames ended") + + self.frames.put(frame) + self.maybe_pause() + + # put() and get/get_iter() call maybe_pause() and maybe_resume() while + # holding self.mutex. This guarantees that the calls interleave properly. + # Specifically, it prevents a race condition where maybe_resume() would + # run before maybe_pause(), leaving the connection incorrectly paused. + + # A race condition is possible when get/get_iter() call self.frames.get() + # without holding self.mutex. However, it's harmless — and even beneficial! + # It can only result in popping an item from the queue before maybe_resume() + # runs and skipping a pause() - resume() cycle that would otherwise occur. + + def maybe_pause(self) -> None: + """Pause the writer if queue is above the high water mark.""" + # Skip if flow control is disabled + if self.high is None: + return + + assert self.mutex.locked() + + # Check for "> high" to support high = 0 + if self.frames.qsize() > self.high and not self.paused: + self.paused = True + self.pause() + + def maybe_resume(self) -> None: + """Resume the writer if queue is below the low water mark.""" + # Skip if flow control is disabled + if self.low is None: + return + + assert self.mutex.locked() + + # Check for "<= low" to support low = 0 + if self.frames.qsize() <= self.low and self.paused: + self.paused = False + self.resume() + + def close(self) -> None: + """ + End the stream of frames. + + Calling :meth:`close` concurrently with :meth:`get`, :meth:`get_iter`, + or :meth:`put` is safe. They will raise :exc:`EOFError`. + + """ + with self.mutex: + if self.closed: + return + + self.closed = True + + if self.get_in_progress: + # Unblock get() or get_iter(). + self.frames.put(None) + + if self.paused: + # Unblock recv_events(). + self.paused = False + self.resume() diff --git a/gestao_raul/Lib/site-packages/websockets/sync/router.py b/gestao_raul/Lib/site-packages/websockets/sync/router.py new file mode 100644 index 0000000..5572c42 --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/sync/router.py @@ -0,0 +1,192 @@ +from __future__ import annotations + +import http +import ssl as ssl_module +import urllib.parse +from typing import Any, Callable, Literal + +from werkzeug.exceptions import NotFound +from werkzeug.routing import Map, RequestRedirect + +from ..http11 import Request, Response +from .server import Server, ServerConnection, serve + + +__all__ = ["route", "unix_route", "Router"] + + +class Router: + """WebSocket router supporting :func:`route`.""" + + def __init__( + self, + url_map: Map, + server_name: str | None = None, + url_scheme: str = "ws", + ) -> None: + self.url_map = url_map + self.server_name = server_name + self.url_scheme = url_scheme + for rule in self.url_map.iter_rules(): + rule.websocket = True + + def get_server_name(self, connection: ServerConnection, request: Request) -> str: + if self.server_name is None: + return request.headers["Host"] + else: + return self.server_name + + def redirect(self, connection: ServerConnection, url: str) -> Response: + response = connection.respond(http.HTTPStatus.FOUND, f"Found at {url}") + response.headers["Location"] = url + return response + + def not_found(self, connection: ServerConnection) -> Response: + return connection.respond(http.HTTPStatus.NOT_FOUND, "Not Found") + + def route_request( + self, connection: ServerConnection, request: Request + ) -> Response | None: + """Route incoming request.""" + url_map_adapter = self.url_map.bind( + server_name=self.get_server_name(connection, request), + url_scheme=self.url_scheme, + ) + try: + parsed = urllib.parse.urlparse(request.path) + handler, kwargs = url_map_adapter.match( + path_info=parsed.path, + query_args=parsed.query, + ) + except RequestRedirect as redirect: + return self.redirect(connection, redirect.new_url) + except NotFound: + return self.not_found(connection) + connection.handler, connection.handler_kwargs = handler, kwargs + return None + + def handler(self, connection: ServerConnection) -> None: + """Handle a connection.""" + return connection.handler(connection, **connection.handler_kwargs) + + +def route( + url_map: Map, + *args: Any, + server_name: str | None = None, + ssl: ssl_module.SSLContext | Literal[True] | None = None, + create_router: type[Router] | None = None, + **kwargs: Any, +) -> Server: + """ + Create a WebSocket server dispatching connections to different handlers. + + This feature requires the third-party library `werkzeug`_: + + .. code-block:: console + + $ pip install werkzeug + + .. _werkzeug: https://werkzeug.palletsprojects.com/ + + :func:`route` accepts the same arguments as + :func:`~websockets.sync.server.serve`, except as described below. + + The first argument is a :class:`werkzeug.routing.Map` that maps URL patterns + to connection handlers. In addition to the connection, handlers receive + parameters captured in the URL as keyword arguments. + + Here's an example:: + + + from websockets.sync.router import route + from werkzeug.routing import Map, Rule + + def channel_handler(websocket, channel_id): + ... + + url_map = Map([ + Rule("/channel/", endpoint=channel_handler), + ... + ]) + + with route(url_map, ...) as server: + server.serve_forever() + + Refer to the documentation of :mod:`werkzeug.routing` for details. + + If you define redirects with ``Rule(..., redirect_to=...)`` in the URL map, + when the server runs behind a reverse proxy that modifies the ``Host`` + header or terminates TLS, you need additional configuration: + + * Set ``server_name`` to the name of the server as seen by clients. When not + provided, websockets uses the value of the ``Host`` header. + + * Set ``ssl=True`` to generate ``wss://`` URIs without actually enabling + TLS. Under the hood, this bind the URL map with a ``url_scheme`` of + ``wss://`` instead of ``ws://``. + + There is no need to specify ``websocket=True`` in each rule. It is added + automatically. + + Args: + url_map: Mapping of URL patterns to connection handlers. + server_name: Name of the server as seen by clients. If :obj:`None`, + websockets uses the value of the ``Host`` header. + ssl: Configuration for enabling TLS on the connection. Set it to + :obj:`True` if a reverse proxy terminates TLS connections. + create_router: Factory for the :class:`Router` dispatching requests to + handlers. Set it to a wrapper or a subclass to customize routing. + + """ + url_scheme = "ws" if ssl is None else "wss" + if ssl is not True and ssl is not None: + kwargs["ssl"] = ssl + + if create_router is None: + create_router = Router + + router = create_router(url_map, server_name, url_scheme) + + _process_request: ( + Callable[ + [ServerConnection, Request], + Response | None, + ] + | None + ) = kwargs.pop("process_request", None) + if _process_request is None: + process_request: Callable[ + [ServerConnection, Request], + Response | None, + ] = router.route_request + else: + + def process_request( + connection: ServerConnection, request: Request + ) -> Response | None: + response = _process_request(connection, request) + if response is not None: + return response + return router.route_request(connection, request) + + return serve(router.handler, *args, process_request=process_request, **kwargs) + + +def unix_route( + url_map: Map, + path: str | None = None, + **kwargs: Any, +) -> Server: + """ + Create a WebSocket Unix server dispatching connections to different handlers. + + :func:`unix_route` combines the behaviors of :func:`route` and + :func:`~websockets.sync.server.unix_serve`. + + Args: + url_map: Mapping of URL patterns to connection handlers. + path: File system path to the Unix socket. + + """ + return route(url_map, unix=True, path=path, **kwargs) diff --git a/gestao_raul/Lib/site-packages/websockets/sync/server.py b/gestao_raul/Lib/site-packages/websockets/sync/server.py new file mode 100644 index 0000000..efb40a7 --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/sync/server.py @@ -0,0 +1,763 @@ +from __future__ import annotations + +import hmac +import http +import logging +import os +import re +import selectors +import socket +import ssl as ssl_module +import sys +import threading +import warnings +from collections.abc import Iterable, Sequence +from types import TracebackType +from typing import Any, Callable, Mapping, cast + +from ..exceptions import InvalidHeader +from ..extensions.base import ServerExtensionFactory +from ..extensions.permessage_deflate import enable_server_permessage_deflate +from ..frames import CloseCode +from ..headers import ( + build_www_authenticate_basic, + parse_authorization_basic, + validate_subprotocols, +) +from ..http11 import SERVER, Request, Response +from ..protocol import CONNECTING, OPEN, Event +from ..server import ServerProtocol +from ..typing import LoggerLike, Origin, StatusLike, Subprotocol +from .connection import Connection +from .utils import Deadline + + +__all__ = ["serve", "unix_serve", "ServerConnection", "Server", "basic_auth"] + + +class ServerConnection(Connection): + """ + :mod:`threading` implementation of a WebSocket server connection. + + :class:`ServerConnection` provides :meth:`recv` and :meth:`send` methods for + receiving and sending messages. + + It supports iteration to receive messages:: + + for message in websocket: + process(message) + + The iterator exits normally when the connection is closed with close code + 1000 (OK) or 1001 (going away) or without a close code. It raises a + :exc:`~websockets.exceptions.ConnectionClosedError` when the connection is + closed with any other code. + + The ``ping_interval``, ``ping_timeout``, ``close_timeout``, and + ``max_queue`` arguments have the same meaning as in :func:`serve`. + + Args: + socket: Socket connected to a WebSocket client. + protocol: Sans-I/O connection. + + """ + + def __init__( + self, + socket: socket.socket, + protocol: ServerProtocol, + *, + ping_interval: float | None = 20, + ping_timeout: float | None = 20, + close_timeout: float | None = 10, + max_queue: int | None | tuple[int | None, int | None] = 16, + ) -> None: + self.protocol: ServerProtocol + self.request_rcvd = threading.Event() + super().__init__( + socket, + protocol, + ping_interval=ping_interval, + ping_timeout=ping_timeout, + close_timeout=close_timeout, + max_queue=max_queue, + ) + self.username: str # see basic_auth() + self.handler: Callable[[ServerConnection], None] # see route() + self.handler_kwargs: Mapping[str, Any] # see route() + + def respond(self, status: StatusLike, text: str) -> Response: + """ + Create a plain text HTTP response. + + ``process_request`` and ``process_response`` may call this method to + return an HTTP response instead of performing the WebSocket opening + handshake. + + You can modify the response before returning it, for example by changing + HTTP headers. + + Args: + status: HTTP status code. + text: HTTP response body; it will be encoded to UTF-8. + + Returns: + HTTP response to send to the client. + + """ + return self.protocol.reject(status, text) + + def handshake( + self, + process_request: ( + Callable[ + [ServerConnection, Request], + Response | None, + ] + | None + ) = None, + process_response: ( + Callable[ + [ServerConnection, Request, Response], + Response | None, + ] + | None + ) = None, + server_header: str | None = SERVER, + timeout: float | None = None, + ) -> None: + """ + Perform the opening handshake. + + """ + if not self.request_rcvd.wait(timeout): + raise TimeoutError("timed out while waiting for handshake request") + + if self.request is not None: + with self.send_context(expected_state=CONNECTING): + response = None + + if process_request is not None: + try: + response = process_request(self, self.request) + except Exception as exc: + self.protocol.handshake_exc = exc + response = self.protocol.reject( + http.HTTPStatus.INTERNAL_SERVER_ERROR, + ( + "Failed to open a WebSocket connection.\n" + "See server log for more information.\n" + ), + ) + + if response is None: + self.response = self.protocol.accept(self.request) + else: + self.response = response + + if server_header: + self.response.headers["Server"] = server_header + + response = None + + if process_response is not None: + try: + response = process_response(self, self.request, self.response) + except Exception as exc: + self.protocol.handshake_exc = exc + response = self.protocol.reject( + http.HTTPStatus.INTERNAL_SERVER_ERROR, + ( + "Failed to open a WebSocket connection.\n" + "See server log for more information.\n" + ), + ) + + if response is not None: + self.response = response + + self.protocol.send_response(self.response) + + # self.protocol.handshake_exc is set when the connection is lost before + # receiving a request, when the request cannot be parsed, or when the + # handshake fails, including when process_request or process_response + # raises an exception. + + # It isn't set when process_request or process_response sends an HTTP + # response that rejects the handshake. + + if self.protocol.handshake_exc is not None: + raise self.protocol.handshake_exc + + def process_event(self, event: Event) -> None: + """ + Process one incoming event. + + """ + # First event - handshake request. + if self.request is None: + assert isinstance(event, Request) + self.request = event + self.request_rcvd.set() + # Later events - frames. + else: + super().process_event(event) + + def recv_events(self) -> None: + """ + Read incoming data from the socket and process events. + + """ + try: + super().recv_events() + finally: + # If the connection is closed during the handshake, unblock it. + self.request_rcvd.set() + + +class Server: + """ + WebSocket server returned by :func:`serve`. + + This class mirrors the API of :class:`~socketserver.BaseServer`, notably the + :meth:`~socketserver.BaseServer.serve_forever` and + :meth:`~socketserver.BaseServer.shutdown` methods, as well as the context + manager protocol. + + Args: + socket: Server socket listening for new connections. + handler: Handler for one connection. Receives the socket and address + returned by :meth:`~socket.socket.accept`. + logger: Logger for this server. + It defaults to ``logging.getLogger("websockets.server")``. + See the :doc:`logging guide <../../topics/logging>` for details. + + """ + + def __init__( + self, + socket: socket.socket, + handler: Callable[[socket.socket, Any], None], + logger: LoggerLike | None = None, + ) -> None: + self.socket = socket + self.handler = handler + if logger is None: + logger = logging.getLogger("websockets.server") + self.logger = logger + if sys.platform != "win32": + self.shutdown_watcher, self.shutdown_notifier = os.pipe() + + def serve_forever(self) -> None: + """ + See :meth:`socketserver.BaseServer.serve_forever`. + + This method doesn't return. Calling :meth:`shutdown` from another thread + stops the server. + + Typical use:: + + with serve(...) as server: + server.serve_forever() + + """ + poller = selectors.DefaultSelector() + try: + poller.register(self.socket, selectors.EVENT_READ) + except ValueError: # pragma: no cover + # If shutdown() is called before poller.register(), + # the socket is closed and poller.register() raises + # ValueError: Invalid file descriptor: -1 + return + if sys.platform != "win32": + poller.register(self.shutdown_watcher, selectors.EVENT_READ) + + while True: + poller.select() + try: + # If the socket is closed, this will raise an exception and exit + # the loop. So we don't need to check the return value of select(). + sock, addr = self.socket.accept() + except OSError: + break + # Since there isn't a mechanism for tracking connections and waiting + # for them to terminate, we cannot use daemon threads, or else all + # connections would be terminate brutally when closing the server. + thread = threading.Thread(target=self.handler, args=(sock, addr)) + thread.start() + + def shutdown(self) -> None: + """ + See :meth:`socketserver.BaseServer.shutdown`. + + """ + self.socket.close() + if sys.platform != "win32": + os.write(self.shutdown_notifier, b"x") + + def fileno(self) -> int: + """ + See :meth:`socketserver.BaseServer.fileno`. + + """ + return self.socket.fileno() + + def __enter__(self) -> Server: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + ) -> None: + self.shutdown() + + +def __getattr__(name: str) -> Any: + if name == "WebSocketServer": + warnings.warn( # deprecated in 13.0 - 2024-08-20 + "WebSocketServer was renamed to Server", + DeprecationWarning, + ) + return Server + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + +def serve( + handler: Callable[[ServerConnection], None], + host: str | None = None, + port: int | None = None, + *, + # TCP/TLS + sock: socket.socket | None = None, + ssl: ssl_module.SSLContext | None = None, + # WebSocket + origins: Sequence[Origin | re.Pattern[str] | None] | None = None, + extensions: Sequence[ServerExtensionFactory] | None = None, + subprotocols: Sequence[Subprotocol] | None = None, + select_subprotocol: ( + Callable[ + [ServerConnection, Sequence[Subprotocol]], + Subprotocol | None, + ] + | None + ) = None, + compression: str | None = "deflate", + # HTTP + process_request: ( + Callable[ + [ServerConnection, Request], + Response | None, + ] + | None + ) = None, + process_response: ( + Callable[ + [ServerConnection, Request, Response], + Response | None, + ] + | None + ) = None, + server_header: str | None = SERVER, + # Timeouts + open_timeout: float | None = 10, + ping_interval: float | None = 20, + ping_timeout: float | None = 20, + close_timeout: float | None = 10, + # Limits + max_size: int | None = 2**20, + max_queue: int | None | tuple[int | None, int | None] = 16, + # Logging + logger: LoggerLike | None = None, + # Escape hatch for advanced customization + create_connection: type[ServerConnection] | None = None, + **kwargs: Any, +) -> Server: + """ + Create a WebSocket server listening on ``host`` and ``port``. + + Whenever a client connects, the server creates a :class:`ServerConnection`, + performs the opening handshake, and delegates to the ``handler``. + + The handler receives the :class:`ServerConnection` instance, which you can + use to send and receive messages. + + Once the handler completes, either normally or with an exception, the server + performs the closing handshake and closes the connection. + + This function returns a :class:`Server` whose API mirrors + :class:`~socketserver.BaseServer`. Treat it as a context manager to ensure + that it will be closed and call :meth:`~Server.serve_forever` to serve + requests:: + + from websockets.sync.server import serve + + def handler(websocket): + ... + + with serve(handler, ...) as server: + server.serve_forever() + + Args: + handler: Connection handler. It receives the WebSocket connection, + which is a :class:`ServerConnection`, in argument. + host: Network interfaces the server binds to. + See :func:`~socket.create_server` for details. + port: TCP port the server listens on. + See :func:`~socket.create_server` for details. + sock: Preexisting TCP socket. ``sock`` replaces ``host`` and ``port``. + You may call :func:`socket.create_server` to create a suitable TCP + socket. + ssl: Configuration for enabling TLS on the connection. + origins: Acceptable values of the ``Origin`` header, for defending + against Cross-Site WebSocket Hijacking attacks. Values can be + :class:`str` to test for an exact match or regular expressions + compiled by :func:`re.compile` to test against a pattern. Include + :obj:`None` in the list if the lack of an origin is acceptable. + extensions: List of supported extensions, in order in which they + should be negotiated and run. + subprotocols: List of supported subprotocols, in order of decreasing + preference. + select_subprotocol: Callback for selecting a subprotocol among + those supported by the client and the server. It receives a + :class:`ServerConnection` (not a + :class:`~websockets.server.ServerProtocol`!) instance and a list of + subprotocols offered by the client. Other than the first argument, + it has the same behavior as the + :meth:`ServerProtocol.select_subprotocol + ` method. + compression: The "permessage-deflate" extension is enabled by default. + Set ``compression`` to :obj:`None` to disable it. See the + :doc:`compression guide <../../topics/compression>` for details. + process_request: Intercept the request during the opening handshake. + Return an HTTP response to force the response. Return :obj:`None` to + continue normally. When you force an HTTP 101 Continue response, the + handshake is successful. Else, the connection is aborted. + process_response: Intercept the response during the opening handshake. + Modify the response or return a new HTTP response to force the + response. Return :obj:`None` to continue normally. When you force an + HTTP 101 Continue response, the handshake is successful. Else, the + connection is aborted. + server_header: Value of the ``Server`` response header. + It defaults to ``"Python/x.y.z websockets/X.Y"``. Setting it to + :obj:`None` removes the header. + open_timeout: Timeout for opening connections in seconds. + :obj:`None` disables the timeout. + ping_interval: Interval between keepalive pings in seconds. + :obj:`None` disables keepalive. + ping_timeout: Timeout for keepalive pings in seconds. + :obj:`None` disables timeouts. + close_timeout: Timeout for closing connections in seconds. + :obj:`None` disables the timeout. + max_size: Maximum size of incoming messages in bytes. + :obj:`None` disables the limit. + max_queue: High-water mark of the buffer where frames are received. + It defaults to 16 frames. The low-water mark defaults to ``max_queue + // 4``. You may pass a ``(high, low)`` tuple to set the high-water + and low-water marks. If you want to disable flow control entirely, + you may set it to ``None``, although that's a bad idea. + logger: Logger for this server. + It defaults to ``logging.getLogger("websockets.server")``. See the + :doc:`logging guide <../../topics/logging>` for details. + create_connection: Factory for the :class:`ServerConnection` managing + the connection. Set it to a wrapper or a subclass to customize + connection handling. + + Any other keyword arguments are passed to :func:`~socket.create_server`. + + """ + + # Process parameters + + # Backwards compatibility: ssl used to be called ssl_context. + if ssl is None and "ssl_context" in kwargs: + ssl = kwargs.pop("ssl_context") + warnings.warn( # deprecated in 13.0 - 2024-08-20 + "ssl_context was renamed to ssl", + DeprecationWarning, + ) + + if subprotocols is not None: + validate_subprotocols(subprotocols) + + if compression == "deflate": + extensions = enable_server_permessage_deflate(extensions) + elif compression is not None: + raise ValueError(f"unsupported compression: {compression}") + + if create_connection is None: + create_connection = ServerConnection + + # Bind socket and listen + + # Private APIs for unix_connect() + unix: bool = kwargs.pop("unix", False) + path: str | None = kwargs.pop("path", None) + + if sock is None: + if unix: + if path is None: + raise ValueError("missing path argument") + kwargs.setdefault("family", socket.AF_UNIX) + sock = socket.create_server(path, **kwargs) + else: + sock = socket.create_server((host, port), **kwargs) + else: + if path is not None: + raise ValueError("path and sock arguments are incompatible") + + # Initialize TLS wrapper + + if ssl is not None: + sock = ssl.wrap_socket( + sock, + server_side=True, + # Delay TLS handshake until after we set a timeout on the socket. + do_handshake_on_connect=False, + ) + + # Define request handler + + def conn_handler(sock: socket.socket, addr: Any) -> None: + # Calculate timeouts on the TLS and WebSocket handshakes. + # The TLS timeout must be set on the socket, then removed + # to avoid conflicting with the WebSocket timeout in handshake(). + deadline = Deadline(open_timeout) + + try: + # Disable Nagle algorithm + + if not unix: + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, True) + + # Perform TLS handshake + + if ssl is not None: + sock.settimeout(deadline.timeout()) + # mypy cannot figure this out + assert isinstance(sock, ssl_module.SSLSocket) + sock.do_handshake() + sock.settimeout(None) + + # Create a closure to give select_subprotocol access to connection. + protocol_select_subprotocol: ( + Callable[ + [ServerProtocol, Sequence[Subprotocol]], + Subprotocol | None, + ] + | None + ) = None + if select_subprotocol is not None: + + def protocol_select_subprotocol( + protocol: ServerProtocol, + subprotocols: Sequence[Subprotocol], + ) -> Subprotocol | None: + # mypy doesn't know that select_subprotocol is immutable. + assert select_subprotocol is not None + # Ensure this function is only used in the intended context. + assert protocol is connection.protocol + return select_subprotocol(connection, subprotocols) + + # Initialize WebSocket protocol + + protocol = ServerProtocol( + origins=origins, + extensions=extensions, + subprotocols=subprotocols, + select_subprotocol=protocol_select_subprotocol, + max_size=max_size, + logger=logger, + ) + + # Initialize WebSocket connection + + assert create_connection is not None # help mypy + connection = create_connection( + sock, + protocol, + ping_interval=ping_interval, + ping_timeout=ping_timeout, + close_timeout=close_timeout, + max_queue=max_queue, + ) + except Exception: + sock.close() + return + + try: + try: + connection.handshake( + process_request, + process_response, + server_header, + deadline.timeout(), + ) + except TimeoutError: + connection.close_socket() + connection.recv_events_thread.join() + return + except Exception: + connection.logger.error("opening handshake failed", exc_info=True) + connection.close_socket() + connection.recv_events_thread.join() + return + + assert connection.protocol.state is OPEN + try: + connection.start_keepalive() + handler(connection) + except Exception: + connection.logger.error("connection handler failed", exc_info=True) + connection.close(CloseCode.INTERNAL_ERROR) + else: + connection.close() + + except Exception: # pragma: no cover + # Don't leak sockets on unexpected errors. + sock.close() + + # Initialize server + + return Server(sock, conn_handler, logger) + + +def unix_serve( + handler: Callable[[ServerConnection], None], + path: str | None = None, + **kwargs: Any, +) -> Server: + """ + Create a WebSocket server listening on a Unix socket. + + This function accepts the same keyword arguments as :func:`serve`. + + It's only available on Unix. + + It's useful for deploying a server behind a reverse proxy such as nginx. + + Args: + handler: Connection handler. It receives the WebSocket connection, + which is a :class:`ServerConnection`, in argument. + path: File system path to the Unix socket. + + """ + return serve(handler, unix=True, path=path, **kwargs) + + +def is_credentials(credentials: Any) -> bool: + try: + username, password = credentials + except (TypeError, ValueError): + return False + else: + return isinstance(username, str) and isinstance(password, str) + + +def basic_auth( + realm: str = "", + credentials: tuple[str, str] | Iterable[tuple[str, str]] | None = None, + check_credentials: Callable[[str, str], bool] | None = None, +) -> Callable[[ServerConnection, Request], Response | None]: + """ + Factory for ``process_request`` to enforce HTTP Basic Authentication. + + :func:`basic_auth` is designed to integrate with :func:`serve` as follows:: + + from websockets.sync.server import basic_auth, serve + + with serve( + ..., + process_request=basic_auth( + realm="my dev server", + credentials=("hello", "iloveyou"), + ), + ): + + If authentication succeeds, the connection's ``username`` attribute is set. + If it fails, the server responds with an HTTP 401 Unauthorized status. + + One of ``credentials`` or ``check_credentials`` must be provided; not both. + + Args: + realm: Scope of protection. It should contain only ASCII characters + because the encoding of non-ASCII characters is undefined. Refer to + section 2.2 of :rfc:`7235` for details. + credentials: Hard coded authorized credentials. It can be a + ``(username, password)`` pair or a list of such pairs. + check_credentials: Function that verifies credentials. + It receives ``username`` and ``password`` arguments and returns + whether they're valid. + Raises: + TypeError: If ``credentials`` or ``check_credentials`` is wrong. + ValueError: If ``credentials`` and ``check_credentials`` are both + provided or both not provided. + + """ + if (credentials is None) == (check_credentials is None): + raise ValueError("provide either credentials or check_credentials") + + if credentials is not None: + if is_credentials(credentials): + credentials_list = [cast(tuple[str, str], credentials)] + elif isinstance(credentials, Iterable): + credentials_list = list(cast(Iterable[tuple[str, str]], credentials)) + if not all(is_credentials(item) for item in credentials_list): + raise TypeError(f"invalid credentials argument: {credentials}") + else: + raise TypeError(f"invalid credentials argument: {credentials}") + + credentials_dict = dict(credentials_list) + + def check_credentials(username: str, password: str) -> bool: + try: + expected_password = credentials_dict[username] + except KeyError: + return False + return hmac.compare_digest(expected_password, password) + + assert check_credentials is not None # help mypy + + def process_request( + connection: ServerConnection, + request: Request, + ) -> Response | None: + """ + Perform HTTP Basic Authentication. + + If it succeeds, set the connection's ``username`` attribute and return + :obj:`None`. If it fails, return an HTTP 401 Unauthorized responss. + + """ + try: + authorization = request.headers["Authorization"] + except KeyError: + response = connection.respond( + http.HTTPStatus.UNAUTHORIZED, + "Missing credentials\n", + ) + response.headers["WWW-Authenticate"] = build_www_authenticate_basic(realm) + return response + + try: + username, password = parse_authorization_basic(authorization) + except InvalidHeader: + response = connection.respond( + http.HTTPStatus.UNAUTHORIZED, + "Unsupported credentials\n", + ) + response.headers["WWW-Authenticate"] = build_www_authenticate_basic(realm) + return response + + if not check_credentials(username, password): + response = connection.respond( + http.HTTPStatus.UNAUTHORIZED, + "Invalid credentials\n", + ) + response.headers["WWW-Authenticate"] = build_www_authenticate_basic(realm) + return response + + connection.username = username + return None + + return process_request diff --git a/gestao_raul/Lib/site-packages/websockets/sync/utils.py b/gestao_raul/Lib/site-packages/websockets/sync/utils.py new file mode 100644 index 0000000..00bce2c --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/sync/utils.py @@ -0,0 +1,45 @@ +from __future__ import annotations + +import time + + +__all__ = ["Deadline"] + + +class Deadline: + """ + Manage timeouts across multiple steps. + + Args: + timeout: Time available in seconds or :obj:`None` if there is no limit. + + """ + + def __init__(self, timeout: float | None) -> None: + self.deadline: float | None + if timeout is None: + self.deadline = None + else: + self.deadline = time.monotonic() + timeout + + def timeout(self, *, raise_if_elapsed: bool = True) -> float | None: + """ + Calculate a timeout from a deadline. + + Args: + raise_if_elapsed: Whether to raise :exc:`TimeoutError` + if the deadline lapsed. + + Raises: + TimeoutError: If the deadline lapsed. + + Returns: + Time left in seconds or :obj:`None` if there is no limit. + + """ + if self.deadline is None: + return None + timeout = self.deadline - time.monotonic() + if raise_if_elapsed and timeout <= 0: + raise TimeoutError("timed out") + return timeout diff --git a/gestao_raul/Lib/site-packages/websockets/typing.py b/gestao_raul/Lib/site-packages/websockets/typing.py new file mode 100644 index 0000000..ab7ddd3 --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/typing.py @@ -0,0 +1,74 @@ +from __future__ import annotations + +import http +import logging +from typing import TYPE_CHECKING, Any, NewType, Optional, Sequence, Union + + +__all__ = [ + "Data", + "LoggerLike", + "StatusLike", + "Origin", + "Subprotocol", + "ExtensionName", + "ExtensionParameter", +] + + +# Public types used in the signature of public APIs + +# Change to str | bytes when dropping Python < 3.10. +Data = Union[str, bytes] +"""Types supported in a WebSocket message: +:class:`str` for a Text_ frame, :class:`bytes` for a Binary_. + +.. _Text: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 +.. _Binary : https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 + +""" + + +# Change to logging.Logger | ... when dropping Python < 3.10. +if TYPE_CHECKING: + LoggerLike = Union[logging.Logger, logging.LoggerAdapter[Any]] + """Types accepted where a :class:`~logging.Logger` is expected.""" +else: # remove this branch when dropping support for Python < 3.11 + LoggerLike = Union[logging.Logger, logging.LoggerAdapter] + """Types accepted where a :class:`~logging.Logger` is expected.""" + + +# Change to http.HTTPStatus | int when dropping Python < 3.10. +StatusLike = Union[http.HTTPStatus, int] +""" +Types accepted where an :class:`~http.HTTPStatus` is expected.""" + + +Origin = NewType("Origin", str) +"""Value of a ``Origin`` header.""" + + +Subprotocol = NewType("Subprotocol", str) +"""Subprotocol in a ``Sec-WebSocket-Protocol`` header.""" + + +ExtensionName = NewType("ExtensionName", str) +"""Name of a WebSocket extension.""" + +# Change to tuple[str, str | None] when dropping Python < 3.10. +ExtensionParameter = tuple[str, Optional[str]] +"""Parameter of a WebSocket extension.""" + + +# Private types + +ExtensionHeader = tuple[ExtensionName, Sequence[ExtensionParameter]] +"""Extension in a ``Sec-WebSocket-Extensions`` header.""" + + +ConnectionOption = NewType("ConnectionOption", str) +"""Connection option in a ``Connection`` header.""" + + +UpgradeProtocol = NewType("UpgradeProtocol", str) +"""Upgrade protocol in an ``Upgrade`` header.""" diff --git a/gestao_raul/Lib/site-packages/websockets/uri.py b/gestao_raul/Lib/site-packages/websockets/uri.py new file mode 100644 index 0000000..b925b99 --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/uri.py @@ -0,0 +1,225 @@ +from __future__ import annotations + +import dataclasses +import urllib.parse +import urllib.request + +from .exceptions import InvalidProxy, InvalidURI + + +__all__ = ["parse_uri", "WebSocketURI"] + + +# All characters from the gen-delims and sub-delims sets in RFC 3987. +DELIMS = ":/?#[]@!$&'()*+,;=" + + +@dataclasses.dataclass +class WebSocketURI: + """ + WebSocket URI. + + Attributes: + secure: :obj:`True` for a ``wss`` URI, :obj:`False` for a ``ws`` URI. + host: Normalized to lower case. + port: Always set even if it's the default. + path: May be empty. + query: May be empty if the URI doesn't include a query component. + username: Available when the URI contains `User Information`_. + password: Available when the URI contains `User Information`_. + + .. _User Information: https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.1 + + """ + + secure: bool + host: str + port: int + path: str + query: str + username: str | None = None + password: str | None = None + + @property + def resource_name(self) -> str: + if self.path: + resource_name = self.path + else: + resource_name = "/" + if self.query: + resource_name += "?" + self.query + return resource_name + + @property + def user_info(self) -> tuple[str, str] | None: + if self.username is None: + return None + assert self.password is not None + return (self.username, self.password) + + +def parse_uri(uri: str) -> WebSocketURI: + """ + Parse and validate a WebSocket URI. + + Args: + uri: WebSocket URI. + + Returns: + Parsed WebSocket URI. + + Raises: + InvalidURI: If ``uri`` isn't a valid WebSocket URI. + + """ + parsed = urllib.parse.urlparse(uri) + if parsed.scheme not in ["ws", "wss"]: + raise InvalidURI(uri, "scheme isn't ws or wss") + if parsed.hostname is None: + raise InvalidURI(uri, "hostname isn't provided") + if parsed.fragment != "": + raise InvalidURI(uri, "fragment identifier is meaningless") + + secure = parsed.scheme == "wss" + host = parsed.hostname + port = parsed.port or (443 if secure else 80) + path = parsed.path + query = parsed.query + username = parsed.username + password = parsed.password + # urllib.parse.urlparse accepts URLs with a username but without a + # password. This doesn't make sense for HTTP Basic Auth credentials. + if username is not None and password is None: + raise InvalidURI(uri, "username provided without password") + + try: + uri.encode("ascii") + except UnicodeEncodeError: + # Input contains non-ASCII characters. + # It must be an IRI. Convert it to a URI. + host = host.encode("idna").decode() + path = urllib.parse.quote(path, safe=DELIMS) + query = urllib.parse.quote(query, safe=DELIMS) + if username is not None: + assert password is not None + username = urllib.parse.quote(username, safe=DELIMS) + password = urllib.parse.quote(password, safe=DELIMS) + + return WebSocketURI(secure, host, port, path, query, username, password) + + +@dataclasses.dataclass +class Proxy: + """ + Proxy. + + Attributes: + scheme: ``"socks5h"``, ``"socks5"``, ``"socks4a"``, ``"socks4"``, + ``"https"``, or ``"http"``. + host: Normalized to lower case. + port: Always set even if it's the default. + username: Available when the proxy address contains `User Information`_. + password: Available when the proxy address contains `User Information`_. + + .. _User Information: https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.1 + + """ + + scheme: str + host: str + port: int + username: str | None = None + password: str | None = None + + @property + def user_info(self) -> tuple[str, str] | None: + if self.username is None: + return None + assert self.password is not None + return (self.username, self.password) + + +def parse_proxy(proxy: str) -> Proxy: + """ + Parse and validate a proxy. + + Args: + proxy: proxy. + + Returns: + Parsed proxy. + + Raises: + InvalidProxy: If ``proxy`` isn't a valid proxy. + + """ + parsed = urllib.parse.urlparse(proxy) + if parsed.scheme not in ["socks5h", "socks5", "socks4a", "socks4", "https", "http"]: + raise InvalidProxy(proxy, f"scheme {parsed.scheme} isn't supported") + if parsed.hostname is None: + raise InvalidProxy(proxy, "hostname isn't provided") + if parsed.path not in ["", "/"]: + raise InvalidProxy(proxy, "path is meaningless") + if parsed.query != "": + raise InvalidProxy(proxy, "query is meaningless") + if parsed.fragment != "": + raise InvalidProxy(proxy, "fragment is meaningless") + + scheme = parsed.scheme + host = parsed.hostname + port = parsed.port or (443 if parsed.scheme == "https" else 80) + username = parsed.username + password = parsed.password + # urllib.parse.urlparse accepts URLs with a username but without a + # password. This doesn't make sense for HTTP Basic Auth credentials. + if username is not None and password is None: + raise InvalidProxy(proxy, "username provided without password") + + try: + proxy.encode("ascii") + except UnicodeEncodeError: + # Input contains non-ASCII characters. + # It must be an IRI. Convert it to a URI. + host = host.encode("idna").decode() + if username is not None: + assert password is not None + username = urllib.parse.quote(username, safe=DELIMS) + password = urllib.parse.quote(password, safe=DELIMS) + + return Proxy(scheme, host, port, username, password) + + +def get_proxy(uri: WebSocketURI) -> str | None: + """ + Return the proxy to use for connecting to the given WebSocket URI, if any. + + """ + if urllib.request.proxy_bypass(f"{uri.host}:{uri.port}"): + return None + + # According to the _Proxy Usage_ section of RFC 6455, use a SOCKS5 proxy if + # available, else favor the proxy for HTTPS connections over the proxy for + # HTTP connections. + + # The priority of a proxy for WebSocket connections is unspecified. We give + # it the highest priority. This makes it easy to configure a specific proxy + # for websockets. + + # getproxies() may return SOCKS proxies as {"socks": "http://host:port"} or + # as {"https": "socks5h://host:port"} depending on whether they're declared + # in the operating system or in environment variables. + + proxies = urllib.request.getproxies() + if uri.secure: + schemes = ["wss", "socks", "https"] + else: + schemes = ["ws", "socks", "https", "http"] + + for scheme in schemes: + proxy = proxies.get(scheme) + if proxy is not None: + if scheme == "socks" and proxy.startswith("http://"): + proxy = "socks5h://" + proxy[7:] + return proxy + else: + return None diff --git a/gestao_raul/Lib/site-packages/websockets/utils.py b/gestao_raul/Lib/site-packages/websockets/utils.py new file mode 100644 index 0000000..62d2dc1 --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/utils.py @@ -0,0 +1,51 @@ +from __future__ import annotations + +import base64 +import hashlib +import secrets +import sys + + +__all__ = ["accept_key", "apply_mask"] + + +GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" + + +def generate_key() -> str: + """ + Generate a random key for the Sec-WebSocket-Key header. + + """ + key = secrets.token_bytes(16) + return base64.b64encode(key).decode() + + +def accept_key(key: str) -> str: + """ + Compute the value of the Sec-WebSocket-Accept header. + + Args: + key: Value of the Sec-WebSocket-Key header. + + """ + sha1 = hashlib.sha1((key + GUID).encode()).digest() + return base64.b64encode(sha1).decode() + + +def apply_mask(data: bytes, mask: bytes) -> bytes: + """ + Apply masking to the data of a WebSocket message. + + Args: + data: Data to mask. + mask: 4-bytes mask. + + """ + if len(mask) != 4: + raise ValueError("mask must contain 4 bytes") + + data_int = int.from_bytes(data, sys.byteorder) + mask_repeated = mask * (len(data) // 4) + mask[: len(data) % 4] + mask_int = int.from_bytes(mask_repeated, sys.byteorder) + return (data_int ^ mask_int).to_bytes(len(data), sys.byteorder) diff --git a/gestao_raul/Lib/site-packages/websockets/version.py b/gestao_raul/Lib/site-packages/websockets/version.py new file mode 100644 index 0000000..738f2ca --- /dev/null +++ b/gestao_raul/Lib/site-packages/websockets/version.py @@ -0,0 +1,92 @@ +from __future__ import annotations + +import importlib.metadata + + +__all__ = ["tag", "version", "commit"] + + +# ========= =========== =================== +# release development +# ========= =========== =================== +# tag X.Y X.Y (upcoming) +# version X.Y X.Y.dev1+g5678cde +# commit X.Y 5678cde +# ========= =========== =================== + + +# When tagging a release, set `released = True`. +# After tagging a release, set `released = False` and increment `tag`. + +released = True + +tag = version = commit = "15.0" + + +if not released: # pragma: no cover + import pathlib + import re + import subprocess + + def get_version(tag: str) -> str: + # Since setup.py executes the contents of src/websockets/version.py, + # __file__ can point to either of these two files. + file_path = pathlib.Path(__file__) + root_dir = file_path.parents[0 if file_path.name == "setup.py" else 2] + + # Read version from package metadata if it is installed. + try: + version = importlib.metadata.version("websockets") + except ImportError: + pass + else: + # Check that this file belongs to the installed package. + files = importlib.metadata.files("websockets") + if files: + version_files = [f for f in files if f.name == file_path.name] + if version_files: + version_file = version_files[0] + if version_file.locate() == file_path: + return version + + # Read version from git if available. + try: + description = subprocess.run( + ["git", "describe", "--dirty", "--tags", "--long"], + capture_output=True, + cwd=root_dir, + timeout=1, + check=True, + text=True, + ).stdout.strip() + # subprocess.run raises FileNotFoundError if git isn't on $PATH. + except ( + FileNotFoundError, + subprocess.CalledProcessError, + subprocess.TimeoutExpired, + ): + pass + else: + description_re = r"[0-9.]+-([0-9]+)-(g[0-9a-f]{7,}(?:-dirty)?)" + match = re.fullmatch(description_re, description) + if match is None: + raise ValueError(f"Unexpected git description: {description}") + distance, remainder = match.groups() + remainder = remainder.replace("-", ".") # required by PEP 440 + return f"{tag}.dev{distance}+{remainder}" + + # Avoid crashing if the development version cannot be determined. + return f"{tag}.dev0+gunknown" + + version = get_version(tag) + + def get_commit(tag: str, version: str) -> str: + # Extract commit from version, falling back to tag if not available. + version_re = r"[0-9.]+\.dev[0-9]+\+g([0-9a-f]{7,}|unknown)(?:\.dirty)?" + match = re.fullmatch(version_re, version) + if match is None: + raise ValueError(f"Unexpected version: {version}") + (commit,) = match.groups() + return tag if commit == "unknown" else commit + + commit = get_commit(tag, version)