diff --git a/sygnal/gcmpushkin.py b/sygnal/gcmpushkin.py index 0f83f1ff..911882ef 100644 --- a/sygnal/gcmpushkin.py +++ b/sygnal/gcmpushkin.py @@ -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 @@ -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 @@ -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]: diff --git a/tests/test_gcm.py b/tests/test_gcm.py index 86e23da3..ff82d6bc 100644 --- a/tests/test_gcm.py +++ b/tests/test_gcm.py @@ -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)