88import os .path
99from typing import Any
1010from collections .abc import Callable
11-
1211import httpx
1312from httpx import AsyncClient , BasicAuth , DigestAuth
1413from zeep .cache import SqliteCache
2726from .settings import DEFAULT_SETTINGS
2827from .transport import ASYNC_TRANSPORT
2928from .types import FastDateTime , ForgivingTime
30- from .util import create_no_verify_ssl_context , normalize_url , path_isfile , utcnow
29+ from .util import (
30+ create_no_verify_ssl_context ,
31+ normalize_url ,
32+ path_isfile ,
33+ utcnow ,
34+ strip_user_pass_url ,
35+ obscure_user_pass_url ,
36+ )
3137from .wrappers import retry_connection_error # noqa: F401
3238from .wsa import WsAddressingIfMissingPlugin
3339
@@ -573,12 +579,16 @@ async def get_snapshot(
573579 else :
574580 auth = DigestAuth (self .user , self .passwd )
575581
576- try :
577- response = await self ._snapshot_client .get (uri , auth = auth )
578- except httpx .TimeoutException as error :
579- raise ONVIFTimeoutError (f"Timed out fetching { uri } : { error } " ) from error
580- except httpx .RequestError as error :
581- raise ONVIFError (f"Error fetching { uri } : { error } " ) from error
582+ response = await self ._try_snapshot_uri (uri , auth )
583+
584+ # If the request fails with a 401, make sure to strip any
585+ # sample user/pass from the URL and try again
586+ if (
587+ response .status_code == 401
588+ and (stripped_uri := strip_user_pass_url (uri ))
589+ and stripped_uri != uri
590+ ):
591+ response = await self ._try_snapshot_uri (stripped_uri , auth )
582592
583593 if response .status_code == 401 :
584594 raise ONVIFAuthError (f"Failed to authenticate to { uri } " )
@@ -588,6 +598,20 @@ async def get_snapshot(
588598
589599 return None
590600
601+ async def _try_snapshot_uri (
602+ self , uri : str , auth : BasicAuth | DigestAuth | None
603+ ) -> httpx .Response :
604+ try :
605+ return await self ._snapshot_client .get (uri , auth = auth )
606+ except httpx .TimeoutException as error :
607+ raise ONVIFTimeoutError (
608+ f"Timed out fetching { obscure_user_pass_url (uri )} : { error } "
609+ ) from error
610+ except httpx .RequestError as error :
611+ raise ONVIFError (
612+ f"Error fetching { obscure_user_pass_url (uri )} : { error } "
613+ ) from error
614+
591615 def get_definition (
592616 self , name : str , port_type : str | None = None
593617 ) -> tuple [str , str , str ]:
0 commit comments