fix: TLSSession guards unbuilt session in _is_closed/close; document setup() is not auto-invoked
_is_closed() and close() called self.session unconditionally, routing through the lazy session property and building a real backend client even when the session was never used - including during __del__ on GC of a constructed-but-unused TLSSession, silently building (and leaking) a backend client nothing ever closes. Mirrors aioweb.ExtendedSession's own _session is None guards. Also corrects setup()'s docstring, which claimed TLSSession invokes a backend's setup() lazily before the first request - it never does; only Noble self-invokes it from its own raw_request. Signed-off-by: disqualifier <dev@disqualifier.me>
This commit is contained in:
@@ -38,8 +38,10 @@ class TLSSession(ExtendedSession):
|
||||
async def setup(self) -> None:
|
||||
"""run the backend's one-time setup if it has one (idempotent, e.g. noble's Go lib fetch)
|
||||
|
||||
optional to call: also invoked lazily before the first request, but calling
|
||||
it upfront lets callers pre-warm at startup.
|
||||
optional to call upfront to pre-warm at startup. TLSSession itself never calls
|
||||
this automatically - a backend needing lazy setup must self-invoke it from its
|
||||
own raw_request, as Noble does; a custom backend that skips this will never run
|
||||
setup() unless the caller calls TLSSession.setup() explicitly.
|
||||
"""
|
||||
setup = getattr(self.backend, "setup", None)
|
||||
if setup is not None:
|
||||
@@ -64,9 +66,14 @@ class TLSSession(ExtendedSession):
|
||||
return cookies_for_url(self.session, url)
|
||||
|
||||
def _is_closed(self) -> bool:
|
||||
"""closed if explicitly closed here or the backend reports it"""
|
||||
"""closed if explicitly closed here, never built, or the backend reports it
|
||||
|
||||
an unbuilt session counts as closed: nothing was opened, nothing to leak.
|
||||
"""
|
||||
if self._closed:
|
||||
return True
|
||||
if self._session is None:
|
||||
return True
|
||||
return self.backend.is_closed(self.session)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
@@ -104,10 +111,12 @@ class TLSSession(ExtendedSession):
|
||||
# lifecycle - backends close differently, so route through the backend
|
||||
|
||||
async def close(self) -> None:
|
||||
"""close via the backend's close (falls back to session.close)"""
|
||||
"""close via the backend's close (falls back to session.close); no-op if never built"""
|
||||
self._closed = True
|
||||
if self._session is None:
|
||||
return
|
||||
close = getattr(self.backend, "close", None)
|
||||
if close is not None:
|
||||
await close(self.session)
|
||||
await close(self._session)
|
||||
else:
|
||||
await self.session.close()
|
||||
await self._session.close()
|
||||
|
||||
Reference in New Issue
Block a user