Skip to content

Commit

Permalink
Only call aiohttp.ClientSession from async func
Browse files Browse the repository at this point in the history
Since `aiohttp==3.10.0`, instantiating `aiohttp.ClientSession` must be
done from an async function. Calling this from a non-async function
relied on deprecated behaviour in Python, which broke in recent
releases.
aio-libs/aiohttp#8555 (comment)
explains the situation.

Rather than pin `aiohttp` to an older version, let's just fix the
behaviour.
  • Loading branch information
anoadragon453 committed Oct 2, 2024
1 parent 345aa61 commit c7a280e
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 12 deletions.
36 changes: 24 additions & 12 deletions sygnal/gcmpushkin.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,13 +155,13 @@ def __init__(self, name: str, sygnal: "Sygnal", config: Dict[str, Any]) -> None:
tls_client_options_factory = ClientTLSOptionsFactory()

# use the Sygnal global proxy configuration
proxy_url = sygnal.config.get("proxy")
self.proxy_url = sygnal.config.get("proxy")

self.http_agent = ProxyAgent(
reactor=sygnal.reactor,
pool=self.http_pool,
contextFactory=tls_client_options_factory,
proxy_url_str=proxy_url,
proxy_url_str=self.proxy_url,
)

self.api_version = APIVersion.Legacy
Expand Down Expand Up @@ -209,16 +209,10 @@ def __init__(self, name: str, sygnal: "Sygnal", config: Dict[str, Any]) -> None:
f"`service_account_file` must be valid: {str(e)}",
)

session = None
if proxy_url:
# `ClientSession` can't directly take the proxy URL, so we need to
# set the usual env var and use `trust_env=True`
os.environ["HTTPS_PROXY"] = proxy_url
session = aiohttp.ClientSession(trust_env=True, auto_decompress=False)

self.google_auth_request = google.auth.transport._aiohttp_requests.Request(
session=session
)
# This is built in `_refresh_credentials`, and must be done in an async function.
self.google_auth_request: Optional[
google.auth.transport._aiohttp_requests.Request
] = None

# Use the fcm_options config dictionary as a foundation for the body;
# this lets the Sygnal admin choose custom FCM options
Expand Down Expand Up @@ -513,12 +507,30 @@ async def _get_auth_header(self) -> str:
async def _refresh_credentials(self) -> None:
assert self.credentials is not None
if not self.credentials.valid:
# Setting up a ClientSession must be done from an async function.
# Lazily build `self.google_auth_request` instead of doing so in `__init__`.
if self.google_auth_request is None:
self.google_auth_request = await self._build_google_auth_request()

await Deferred.fromFuture(
asyncio.ensure_future(
self.credentials.refresh(self.google_auth_request)
)
)

async def _build_google_auth_request(
self,
) -> google.auth.transport._aiohttp_requests.Request:
"""Build a google auth request with an HTTP proxy, if configured."""
session = None
if self.proxy_url:
# `ClientSession` can't directly take the proxy URL, so we need to
# set the usual env var and use `trust_env=True`
os.environ["HTTPS_PROXY"] = self.proxy_url
session = aiohttp.ClientSession(trust_env=True, auto_decompress=False)

return google.auth.transport._aiohttp_requests.Request(session=session)

async def _dispatch_notification_unlimited(
self, n: Notification, device: Device, context: NotificationContext
) -> List[str]:
Expand Down
2 changes: 2 additions & 0 deletions tests/test_gcm.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ async def _perform_http_request( # type: ignore[override]
async def _refresh_credentials(self) -> None:
assert self.credentials is not None
if not self.credentials.valid:
if self.google_auth_request is None:
self.google_auth_request = await self._build_google_auth_request()
await self.credentials.refresh(self.google_auth_request)


Expand Down

0 comments on commit c7a280e

Please sign in to comment.