-
-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
tests - add test for ResourceWarning: unclosed transport <asyncio.sslproto._SSLProtocolTransport object … #5146
base: master
Are you sure you want to change the base?
Changes from 16 commits
6aa9192
f0523a8
57326c0
ffc1f7d
5dd8dc7
0239556
af54de5
a210f61
b2ee18c
34d7f96
0392b86
bd9a15e
2a32cea
4a822a1
1ec87be
711c5c3
a409329
5b4a9c0
4b5a9c5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
import asyncio | ||
import platform | ||
import ssl | ||
import sys | ||
Check notice Code scanning / CodeQL Unused import Note test
Import of 'sys' is not used.
|
||
import warnings | ||
from typing import Any, Tuple, cast | ||
|
||
import pytest | ||
|
||
from aiohttp import TCPConnector, web | ||
from aiohttp.client import ServerDisconnectedError | ||
from aiohttp.pytest_plugin import AiohttpClient, AiohttpServer | ||
from aiohttp.test_utils import TestClient, TestServer | ||
|
||
|
||
async def handle_root(request: web.Request) -> web.Response: | ||
"""respond after server was restarted, forcing connection_lost""" | ||
cq = request.app["cq"] | ||
dq = request.app["dq"] | ||
|
||
await dq.put(0) | ||
|
||
await cq.get() | ||
cq.task_done() | ||
|
||
return web.Response(text="") | ||
|
||
|
||
async def _prepare( | ||
aiohttp_server: Any, | ||
aiohttp_client: Any, | ||
ssl_ctx: ssl.SSLContext, | ||
client_ssl_ctx: ssl.SSLContext, | ||
cq: "asyncio.Queue[int]", | ||
dq: "asyncio.Queue[int]", | ||
) -> Tuple[TestServer, TestClient]: | ||
commonism marked this conversation as resolved.
Show resolved
Hide resolved
|
||
app = web.Application() | ||
app["cq"] = cq | ||
app["dq"] = dq | ||
app.router.add_get("/", handle_root) | ||
server = await aiohttp_server(app, port=0, ssl=ssl_ctx) | ||
|
||
session = await aiohttp_client(server, connector=TCPConnector(ssl=client_ssl_ctx)) | ||
|
||
# replace SockSite … it is different and blocks | ||
await dq.put(0) | ||
await _restart(server.runner, ssl_ctx, session.port, cq, dq) | ||
await cq.get() | ||
|
||
return server, session | ||
|
||
|
||
async def _restart( | ||
runner: web.BaseRunner, | ||
ssl_ctx: ssl.SSLContext, | ||
port: int, | ||
cq: "asyncio.Queue[int]", | ||
dq: "asyncio.Queue[int]", | ||
) -> None: | ||
"""restart service to force connection_lost""" | ||
await dq.get() | ||
dq.task_done() | ||
site = next(iter(runner.sites)) | ||
await site.stop() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In 3.12 it hangs here - you do not get
as you get in 3.11 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't see anything suspicious in there, unless the asyncio server is not closing: Everything else looks like it has a 60s timeout, so would fail before the 5 min timeout we're seeing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If it is the asyncio server not closing, then we should get a cpython bug filed for the regression. |
||
await cq.put(0) | ||
site = web.TCPSite( | ||
runner, "127.0.0.1", port=port, ssl_context=ssl_ctx, shutdown_timeout=1.0 | ||
Dreamsorcerer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
) | ||
await site.start() | ||
|
||
|
||
def _ssl_resource_warnings(w: warnings.WarningMessage) -> bool: | ||
unclosed_transport_msg = ( | ||
"unclosed transport <asyncio.sslproto._SSLProtocolTransport object at" | ||
) | ||
return ( | ||
w.category == ResourceWarning | ||
and w.filename.endswith("sslproto.py") | ||
and cast(Warning, w.message).args[0].startswith(unclosed_transport_msg) | ||
) | ||
|
||
|
||
async def _run( | ||
aiohttp_server: AiohttpServer, | ||
aiohttp_client: AiohttpClient, | ||
recwarn: pytest.WarningsRecorder, | ||
ssl_ctx: ssl.SSLContext, | ||
client_ssl_ctx: ssl.SSLContext, | ||
cq: "asyncio.Queue[int]", | ||
dq: "asyncio.Queue[int]", | ||
) -> None: | ||
"""run for two processed client requests""" | ||
server, session = await _prepare( | ||
aiohttp_server, aiohttp_client, ssl_ctx, client_ssl_ctx, cq, dq | ||
) | ||
assert server.runner is not None and session.port is not None | ||
for i in range(3): | ||
try: | ||
jobs: Any = [] | ||
jobs.append(session.get("/")) | ||
jobs.append(_restart(server.runner, ssl_ctx, session.port, cq, dq)) | ||
await asyncio.gather(*jobs) | ||
except ServerDisconnectedError: | ||
# Restarting the service will cause the client connections to fail | ||
# this is expected and not a failure. | ||
pass | ||
|
||
finally: | ||
await asyncio.sleep(0.1) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess it's the 300s ClientTimeout
300.26s call tests/test_ssl_transport_close.py::test_unclosed_transport_asyncio_sslproto_SSLProtocolTransport[pyloop] increasing the sleep to something larger to have to server restart in time? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not seeing how the sleep relates to it, but we can try it if you think that's the problem. |
||
|
||
assert not len( | ||
list(filter(_ssl_resource_warnings, recwarn)) | ||
), "unclosed transport" | ||
|
||
|
||
@pytest.mark.xfail( | ||
sys.version_info < (3, 11) and platform.python_implementation() != "PyPy", | ||
reason="Working on 3.11+ and Pypy.", | ||
) | ||
Dreamsorcerer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
def test_unclosed_transport_asyncio_sslproto_SSLProtocolTransport( | ||
loop: asyncio.AbstractEventLoop, | ||
aiohttp_server: AiohttpServer, | ||
aiohttp_client: AiohttpClient, | ||
recwarn: pytest.WarningsRecorder, | ||
ssl_ctx: ssl.SSLContext, | ||
client_ssl_ctx: ssl.SSLContext, | ||
) -> None: | ||
cq: "asyncio.Queue[int]" = asyncio.Queue() | ||
dq: "asyncio.Queue[int]" = asyncio.Queue() | ||
loop.set_debug(True) | ||
loop.run_until_complete( | ||
_run( | ||
aiohttp_server, | ||
aiohttp_client, | ||
recwarn, | ||
ssl_ctx, | ||
client_ssl_ctx, | ||
cq, | ||
dq, | ||
) | ||
) |
Check notice
Code scanning / CodeQL
Unused import Note test