aiohttp TLS websocket fails for continously sending packages on ESP32
まだ誰も着手していません。
評価
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 初心者へのやさしさ
- 25/100
- issue の種類
- バグ
- 明瞭さ
- 説明が足りない
- 活発さ
- 停滞
- 技術スタック
- python
- 領域
- networking
調査の方向性
提供されたESP32の再現環境から始め、aiohttp_ws.pyを通じてwebsocket操作を追跡します。特にreceive、send_str、close、_read_frameを確認し、その後asyncio/stream.pyのエラーを調べます。TLSのシャットダウン動作とメモリ増加を含め、リモートとローカルの失敗を比較します。再現可能な原因を特定し、継続的な2–4 kBのTLS websocketメッセージに対する検証済みの修正を定義できれば、完了とします。
索引モデルが issue の本文から書いたものです。
説明
@Carglglz
I have an application i want to send TLS / SSL packages over websockets using aiohttp on an ESP32. The problem is that the websockets fail after a short while when using packages that have a little bigger size around 2kB to 4kB.
Here is a simple test script:
import aiohttp
import asyncio
import gc
# allocate and prepare fixed block of data to be sent
b = bytearray(10_000)
for k in range(100):
p = k*100
b[p:p] = b'X'*96 + f'{k:3}' + '\n'
mv = memoryview(b)
URL = "wss://somewebsocketserver/echo"
sslctx = False
if URL.startswith("wss:"):
try:
import ssl
sslctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
sslctx.verify_mode = ssl.CERT_NONE
except Exception:
pass
async def ws_receive(ws : ClientWebSocketResponse):
try:
async for msg in ws:
if msg.type != aiohttp.WSMsgType.TEXT:
print('type:', msg.type, repr(msg.data))
except TypeError as e:
print('ws_receive', repr(e))
async def ws_test_echo(session):
n = 1
while True:
ws_receive_task = None
async with session.ws_connect(URL, ssl=sslctx) as ws:
ws_receive_task = asyncio.create_task(ws_receive(ws))
try:
while True:
gc.collect()
print('-------------------', n, '------------------------')
await ws.send_str(b[0:100*n].decode())
n += 1
await asyncio.sleep_ms(100)
except KeyboardInterrupt:
pass
finally:
await ws.close()
async def main():
async with aiohttp.ClientSession() as session:
print('session')
await ws_test_echo(session)
if __name__ == "__main__":
asyncio.run(main())
remote
I'll get the following exception after a while. Just dealing with the exception is not satisfying since it takes a while and the applicaiton is blocked in that time.
Task exception wasn't retrieved
future: <Task> coro= <generator object 'ws_receive' at 3ffec850>
Traceback (most recent call last):
File "asyncio/core.py", line 1, in run_until_complete
File "<stdin>", line 29, in ws_receive
File "/lib/aiohttp/aiohttp_ws.py", line 226, in __anext__
File "/lib/aiohttp/aiohttp_ws.py", line 171, in receive
File "/lib/aiohttp/aiohttp_ws.py", line 198, in _read_frame
File "asyncio/stream.py", line 1, in read
OSError: -113
Traceback (most recent call last):
File "<stdin>", line 66, in <module>
File "asyncio/core.py", line 1, in run
File "asyncio/core.py", line 1, in run_until_complete
File "asyncio/core.py", line 1, in run_until_complete
File "<stdin>", line 62, in main
File "<stdin>", line 56, in ws_test_echo
File "/lib/aiohttp/aiohttp_ws.py", line 233, in close
File "/lib/aiohttp/aiohttp_ws.py", line 194, in close
File "/lib/aiohttp/aiohttp_ws.py", line 189, in send
File "asyncio/stream.py", line 1, in drain
OSError: -113
(OSError: 113 = ECONNABORTED)
Noteworthy is that there are always some packages that micropyhon thinks are sent already but don't reach the other side.
I also do not receive a websocket close package.
This was tested on a remote Websocket server that i don't control.
local
When i try it on a local server, i see different problems.
Traceback (most recent call last):
File "<stdin>", line 64, in <module>
File "asyncio/core.py", line 1, in run
File "asyncio/core.py", line 1, in run_until_complete
File "asyncio/core.py", line 1, in run_until_complete
File "<stdin>", line 60, in main
File "<stdin>", line 45, in ws_test_echo
File "/lib/aiohttp/aiohttp_ws.py", line 239, in send_str
File "/lib/aiohttp/aiohttp_ws.py", line 187, in send
File "asyncio/stream.py", line 1, in write
OSError: -104
(OSError: 104 = ECONNRESET)
The servers seems to not receive the complete message / only a corrupted message and closes the connection:
...
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 37
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 38
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 39
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
b'\xe9\x82\xc3+\xe9\x82\xbbG\x81\xd0\xc3+\xe9\x82\xc3+\xe9\x82\xc3+\xe9\x82\xc3+\xe9\x82\xc3+\xe9\x82\xc3+\xe9\x82\xc3+\xe9\x82\xc3+\xe9\x82\xc3'
Unhandled exception in client_connected_cb
transport: <asyncio.sslproto._SSLProtocolTransport object at 0x7f18b780ecf0>
Traceback (most recent call last):
File "/home/username/Documents/projects/rfid/websockets_python/microdot/examples/tls/microdot/microdot.py", line 1224, in serve
await self.handle_request(reader, writer)
File "/home/username/Documents/projects/rfid/websockets_python/microdot/examples/tls/microdot/microdot.py", line 1338, in handle_request
await writer.aclose()
File "/home/username/Documents/projects/rfid/websockets_python/microdot/examples/tls/microdot/microdot.py", line 1218, in aclose
await self.wait_closed()
File "/usr/lib/python3.11/asyncio/streams.py", line 364, in wait_closed
await self._protocol._get_close_waiter(self)
File "/usr/lib/python3.11/asyncio/sslproto.py", line 648, in _do_shutdown
self._sslobj.unwrap()
File "/usr/lib/python3.11/ssl.py", line 983, in unwrap
return self._sslobj.shutdown()
^^^^^^^^^^^^^^^^^^^^^^^
ssl.SSLError: [SSL: APPLICATION_DATA_AFTER_CLOSE_NOTIFY] application data after close notify (_ssl.c:2706)
The closing of the websocket is also not handled properly by the micropython application / aiohttp. The close package is received but the connection is not closed automatically.
Another problem is that even if i deal with the exceptions and reconnect automatically, my application will eventually crash because of a run out of memory.
This is really problematic to me since i only send messages of size 2k or so.
I guess this is because of the not so memory economical design of aiohttp. For sending and receiving there is always a lot of new memory allocation involved.
The use of preallocation and memoryview seems reasonable here.
This is the local python websocketserver code:
import ssl
import sys
from microdot import Microdot
from microdot.websocket import with_websocket
app = Microdot()
html = '''<!DOCTYPE html>
<html>
<head>
<title>Microdot Example Page</title>
<meta charset="UTF-8">
</head>
<body>
<div>
<h1>Microdot Example Page</h1>
<p>Hello from Microdot!</p>
<p><a href="/shutdown">Click to shutdown the server</a></p>
</div>
</body>
</html>
'''
@app.route('/')
async def hello(request):
return html, 200, {'Content-Type': 'text/html'}
@app.route('/echo')
@with_websocket
async def echo(request, ws):
while True:
data = await ws.receive()
print(data)
await ws.send(data)
@app.route('/shutdown')
async def shutdown(request):
request.app.shutdown()
return 'The server is shutting down...'
ext = 'der' if sys.implementation.name == 'micropython' else 'pem'
folder_name = "/home/username/Documents/projects/rfid/websockets_python/"
ssl_cert = "cert.pem"
ssl_key = "key.pem"
sslctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
sslctx.load_cert_chain(ssl_cert, ssl_key)
app.run(port=4443, debug=True, ssl=sslctx)
MicroPython v1.23.0-preview.344.gb1ac266bb.dirty on 2024-04-29; Generic ESP32 module with ESP32
- 主要言語
- Python
- スター
- 2.9k
- フォーク
- 1.1k
- 平均マージ
- 4時間 17分
- マージ済み PR(30日)
- 1
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートあり
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
micropython/micropython-lib のほかの issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 62/100
micropython/micropython-lib#1074 ·
-
needs-info
難易度 1/5 1時間未満 初心者へのやさしさ 72/100
micropython/micropython-lib#943 · コメント 1 件 ·
-
"requests" does not handle redirection with relative path correctly再び着手できるかも このイシューのプルリクエストはマージされずにクローズされました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
micropython/micropython-lib#931 · コメント 1 件 ·
-
難易度 1/5 1時間未満 初心者へのやさしさ 62/100
micropython/micropython-lib#795 · コメント 2 件 ·
-
webrepl: Support LAN as well as WLAN boards.対応中かも @Romaric-RILLET が 797 日前に担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 62/100
micropython/micropython-lib#762 ·
micropython/micropython-lib の issue をすべて見る
似ている issue
-
fuzz fuzz-unconfirmed
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
メンテナーはふだん 1 日以内に返信
-
Switch to ruffオープン
難易度 2/5 1〜3時間 初心者へのやさしさ 65/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 67/100
メンテナーはふだん 1 日以内に返信
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 65/100
Qiskit/qiskit-aer#2533 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 64/100
etesync/etesync-dav#376 ·