@@ -318,61 +318,84 @@ async def request(
318318 url , method , data , headers , actual_timeout , ** kwargs
319319 )
320320 )
321- if response .status_code == http_client .UNAUTHORIZED :
322- if getattr (self , "is_mtls" , False ) and any (
323- prefix in url for prefix in MTLS_URL_PREFIXES
324- ):
325- # Snapshot the stale certificate state BEFORE acquiring the lock.
326- # This represents the cert that caused the 401 rejection.
327- stale_cert = self ._cached_cert
328-
329- # Wait in line to acquire the lock
330- async with self ._mtls_rotation_lock :
331- # Check Did another coroutine already reconfigure mTLS
332- if self ._cached_cert != stale_cert :
333- # Yes! Another request already updated the channel
334- pass
335- else :
336- try :
337- (
338- call_cert_bytes ,
339- call_key_bytes ,
340- cached_fingerprint ,
341- current_cert_fingerprint ,
342- ) = await mtls ._run_in_executor (
343- google .auth .transport ._mtls_helper .check_parameters_for_unauthorized_response ,
344- self ._cached_cert ,
345- )
346- if cached_fingerprint != current_cert_fingerprint :
347- try :
348- _LOGGER .info (
349- "Client certificate has changed, reconfiguring mTLS "
350- "channel."
351- )
352- if self ._mtls_init_task and self ._mtls_init_task .done ():
353- self ._mtls_init_task = None
354- await self .configure_mtls_channel (
355- lambda : (call_cert_bytes , call_key_bytes )
356- )
357- continue
358- except Exception as e :
359- _LOGGER .error ("Failed to reconfigure mTLS channel: %s" , e )
360- raise exceptions .MutualTLSChannelError (
361- "Failed to reconfigure mTLS channel"
362- ) from e
363- else :
364- _LOGGER .info (
365- "Skipping reconfiguration of mTLS channel because the client"
366- " certificate has not changed."
367- )
368- except Exception as e :
369- _LOGGER .warning (
370- "Failed to check client certificate parameters: %s. Proceeding with original response." ,
371- e ,
372- )
373321
374322 if response .status_code not in transport .DEFAULT_RETRYABLE_STATUS_CODES :
375323 break
324+
325+ if response .status_code == http_client .UNAUTHORIZED :
326+ _auth_retry_count = kwargs .pop ("_auth_retry_count" , 0 )
327+ if _auth_retry_count < 2 :
328+ is_streaming = data is not None and isinstance (data , (collections .abc .Iterator , collections .abc .AsyncIterable )) or hasattr (data , "read" )
329+ if getattr (self , "is_mtls" , False ) and any (
330+ prefix in url for prefix in MTLS_URL_PREFIXES
331+ ):
332+ # Snapshot the stale certificate state BEFORE acquiring the lock.
333+ # This represents the cert that caused the 401 rejection.
334+ stale_cert = self ._cached_cert
335+
336+ # Wait in line to acquire the lock
337+ async with self ._mtls_rotation_lock :
338+ # Check Did another coroutine already reconfigure mTLS
339+ if self ._cached_cert != stale_cert :
340+ # Yes! Another request already updated the channel
341+ pass
342+ else :
343+ try :
344+ (
345+ call_cert_bytes ,
346+ call_key_bytes ,
347+ cached_fingerprint ,
348+ current_cert_fingerprint ,
349+ ) = await mtls ._run_in_executor (
350+ google .auth .transport ._mtls_helper .check_parameters_for_unauthorized_response ,
351+ self ._cached_cert ,
352+ )
353+ if cached_fingerprint != current_cert_fingerprint :
354+ try :
355+ _LOGGER .info (
356+ "Client certificate has changed, reconfiguring mTLS "
357+ "channel."
358+ )
359+ if self ._mtls_init_task and self ._mtls_init_task .done ():
360+ self ._mtls_init_task = None
361+ await self .configure_mtls_channel (
362+ lambda : (call_cert_bytes , call_key_bytes )
363+ )
364+ continue
365+ except Exception as e :
366+ _LOGGER .error ("Failed to reconfigure mTLS channel: %s" , e )
367+ raise exceptions .MutualTLSChannelError (
368+ "Failed to reconfigure mTLS channel"
369+ ) from e
370+ else :
371+ _LOGGER .info (
372+ "Skipping reconfiguration of mTLS channel because the client"
373+ " certificate has not changed."
374+ )
375+ except Exception as e :
376+ _LOGGER .warning (
377+ "Failed to check client certificate parameters: %s. Proceeding with original response." ,
378+ e ,
379+ )
380+ if is_streaming :
381+ return response
382+ if hasattr (response , "close" ):
383+ if asyncio .iscoroutinefunction (response .close ):
384+ await response .close ()
385+ else :
386+ response .close ()
387+ await self ._credentials .refresh (self ._auth_request )
388+ kwargs ["_auth_retry_count" ] = _auth_retry_count + 1
389+ return await self .request (
390+ method ,
391+ url ,
392+ data = data ,
393+ headers = headers ,
394+ max_allowed_time = max_allowed_time ,
395+ timeout = timeout ,
396+ total_attempts = total_attempts ,
397+ ** kwargs
398+ )
376399 return response
377400
378401 @functools .wraps (request )
0 commit comments