我正在用 quart 编写一个 restapi,由 Hypercorn 提供服务,使用由BenMorel 的剧本。我的单个 python 文件的结构如下:
import asyncio
from quart import Quart, request, abort, make_response, Response
from hypercorn.config import Config
from hypercorn.asyncio import serve
(...)
app = Quart(__name__)
(...)
@app.route(URL_BASE_PATH + "/get-stats", methods=['GET'])
async def getStats():
(...)
return {
"query-able-first-date":firstDate,
"query-able-last-date":lastDate,
"free-space":freeSpace,
}
(...)
if __name__ == '__main__':
try:
config = Config()
config.bind = ["0.0.0.0:" + str(SERVER_PORT)]
if TLS_ENABLED:
if os.path.isfile(TLS_CERT) and os.path.isfile(TLS_PRIVKEY):
config.certfile = TLS_CERT
config.keyfile = TLS_PRIVKEY
else:
print("TLS private key or certificate is missing, cannot use TLS")
asyncio.run(serve(app, config))
except Exception as e:
print("Some error message")
sys.exit(1)
如果我不使用 TLS,这一切都很好。但是,如果我使用 TLS,并在浏览器(Firefox 或 Chrome)中查找此页面,我会收到“一些”未处理的错误:
Task exception was never retrieved
future: <Task finished name='Task-6' coro=<worker_serve.<locals>._server_callback() done, defined at /home/sga/.local/lib/python3.9/site-packages/hypercorn/asyncio/run.py:94> exception=SSLError(1, '[SSL: APPLICATION_DATA_AFTER_CLOSE_NOTIFY] application data after close notify (_ssl.c:2745)')>
Traceback (most recent call last):
File "/home/sga/.local/lib/python3.9/site-packages/hypercorn/asyncio/tcp_server.py", line 70, in run
await self._read_data()
File "/home/sga/.local/lib/python3.9/site-packages/hypercorn/asyncio/tcp_server.py", line 106, in _read_data
await self.protocol.handle(RawData(data))
File "/home/sga/.local/lib/python3.9/site-packages/hypercorn/protocol/__init__.py", line 62, in handle
return await self.protocol.handle(event)
File "/home/sga/.local/lib/python3.9/site-packages/hypercorn/protocol/h2.py", line 188, in handle
await self._handle_events(events)
File "/home/sga/.local/lib/python3.9/site-packages/hypercorn/protocol/h2.py", line 255, in _handle_events
await self.streams[event.stream_id].handle(EndBody(stream_id=event.stream_id))
KeyError: 15
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/sga/.local/lib/python3.9/site-packages/hypercorn/asyncio/tcp_server.py", line 70, in run
await self._read_data()
File "/home/sga/.local/lib/python3.9/site-packages/hypercorn/asyncio/task_group.py", line 82, in __aexit__
await task
File "/home/sga/.local/lib/python3.9/site-packages/hypercorn/asyncio/task_group.py", line 78, in __aexit__
await task
asyncio.exceptions.CancelledError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/sga/.local/lib/python3.9/site-packages/hypercorn/asyncio/run.py", line 96, in _server_callback
await TCPServer(app, loop, config, context, reader, writer)
File "/home/sga/.local/lib/python3.9/site-packages/hypercorn/asyncio/tcp_server.py", line 74, in run
await self._close()
File "/home/sga/.local/lib/python3.9/site-packages/hypercorn/asyncio/tcp_server.py", line 118, in _close
await self.writer.wait_closed()
File "/usr/lib/python3.9/asyncio/streams.py", line 359, in wait_closed
await self._protocol._get_close_waiter(self)
File "/usr/lib/python3.9/asyncio/sslproto.py", line 528, in data_received
ssldata, appdata = self._sslpipe.feed_ssldata(data)
File "/usr/lib/python3.9/asyncio/sslproto.py", line 206, in feed_ssldata
self._sslobj.unwrap()
File "/usr/lib/python3.9/ssl.py", line 948, in unwrap
return self._sslobj.shutdown()
ssl.SSLError: [SSL: APPLICATION_DATA_AFTER_CLOSE_NOTIFY] application data after close notify (_ssl.c:2745)
因为我所做的只是在 asyncio 内部调用 hypercorn.serve,所以我对未处理的错误无能为力...在 Firefox 中,我导入了假的自签名证书并得到了 Keyerror: 15,而在 chrome 中我没有(只是接受了非常有害的后果)我得到了 Keyerror: 1。除此之外,作为客户端,两种浏览器都会发生相同的错误。
我知道这个 SSL 错误意味着服务器在关闭 SSL 连接后才收到数据,但我不知道为什么会发生这种情况。我还看到 asyncio 失败,因此给出“CancelledError”消息……尽管应用程序和 restapi 服务器继续运行,我甚至在浏览器中得到了结果。我不明白如何解决这个问题,或者从哪里开始调试它。
答案1
看起来这个问题只发生在网络浏览器上。使用最新的 curl(支持 HTTP/2),如果我询问页面(curl -kv --http https://IP:port/resource
),它工作正常……