diff --git a/src/uipath/_cli/_auth/_oidc_utils.py b/src/uipath/_cli/_auth/_oidc_utils.py index b6acdd6a5..07f4c4218 100644 --- a/src/uipath/_cli/_auth/_oidc_utils.py +++ b/src/uipath/_cli/_auth/_oidc_utils.py @@ -6,7 +6,7 @@ import httpx -from ..._utils._ssl_context import get_httpx_client_kwargs +from ..._utils._http_clients import get_httpx_client_kwargs from .._utils._console import ConsoleLogger from ._models import AuthConfig from ._url_utils import build_service_url diff --git a/src/uipath/_cli/_auth/_portal_service.py b/src/uipath/_cli/_auth/_portal_service.py index d50b3a38d..ab270ba76 100644 --- a/src/uipath/_cli/_auth/_portal_service.py +++ b/src/uipath/_cli/_auth/_portal_service.py @@ -9,7 +9,7 @@ ) from ..._utils._auth import update_env_file -from ..._utils._ssl_context import get_httpx_client_kwargs +from ..._utils._http_clients import get_httpx_client_kwargs from ...platform.common import TokenData from .._utils._console import ConsoleLogger from ._models import OrganizationInfo, TenantInfo, TenantsAndOrganizationInfoResponse diff --git a/src/uipath/_cli/_utils/_processes.py b/src/uipath/_cli/_utils/_processes.py index 610da5ec4..44c30e620 100644 --- a/src/uipath/_cli/_utils/_processes.py +++ b/src/uipath/_cli/_utils/_processes.py @@ -4,7 +4,7 @@ import httpx -from ..._utils._ssl_context import get_httpx_client_kwargs +from ..._utils._http_clients import get_httpx_client_kwargs from ._console import ConsoleLogger console = ConsoleLogger() diff --git a/src/uipath/_cli/cli_invoke.py b/src/uipath/_cli/cli_invoke.py index 3e5381650..65e547813 100644 --- a/src/uipath/_cli/cli_invoke.py +++ b/src/uipath/_cli/cli_invoke.py @@ -6,7 +6,7 @@ import click import httpx -from .._utils._ssl_context import get_httpx_client_kwargs +from .._utils._http_clients import get_httpx_client_kwargs from ._utils._common import get_env_vars from ._utils._console import ConsoleLogger from ._utils._folders import get_personal_workspace_info_async diff --git a/src/uipath/_cli/cli_publish.py b/src/uipath/_cli/cli_publish.py index 9e7c64afc..e9c82279d 100644 --- a/src/uipath/_cli/cli_publish.py +++ b/src/uipath/_cli/cli_publish.py @@ -5,7 +5,7 @@ import click import httpx -from .._utils._ssl_context import get_httpx_client_kwargs +from .._utils._http_clients import get_httpx_client_kwargs from ._utils._common import get_env_vars from ._utils._console import ConsoleLogger from ._utils._folders import get_personal_workspace_info_async diff --git a/src/uipath/_utils/_ssl_context.py b/src/uipath/_utils/_ssl_context.py index 7e6dc692b..6d2fd9f2a 100644 --- a/src/uipath/_utils/_ssl_context.py +++ b/src/uipath/_utils/_ssl_context.py @@ -1,6 +1,5 @@ import os import ssl -from typing import Any, Dict def expand_path(path): @@ -32,23 +31,3 @@ def create_ssl_context(): cafile=ssl_cert_file or requests_ca_bundle or certifi.where(), capath=ssl_cert_dir, ) - - -def get_httpx_client_kwargs() -> Dict[str, Any]: - """Get standardized httpx client configuration.""" - client_kwargs: Dict[str, Any] = {"follow_redirects": True, "timeout": 30.0} - - # Check environment variable to disable SSL verification - disable_ssl_env = os.environ.get("UIPATH_DISABLE_SSL_VERIFY", "").lower() - disable_ssl_from_env = disable_ssl_env in ("1", "true", "yes", "on") - - if disable_ssl_from_env: - client_kwargs["verify"] = False - else: - # Use system certificates with truststore fallback - client_kwargs["verify"] = create_ssl_context() - - # Auto-detect proxy from environment variables (httpx handles this automatically) - # HTTP_PROXY, HTTPS_PROXY, NO_PROXY are read by httpx by default - - return client_kwargs diff --git a/src/uipath/_utils/_url.py b/src/uipath/_utils/_url.py index 4776120cd..4ecfbc13c 100644 --- a/src/uipath/_utils/_url.py +++ b/src/uipath/_utils/_url.py @@ -1,6 +1,8 @@ from typing import Literal from urllib.parse import urlparse +from ._service_url_overrides import get_service_override + class UiPathUrl: """A class that represents a UiPath URL. @@ -61,6 +63,11 @@ def scope_url(self, url: str, scoped: Literal["org", "tenant"] = "tenant") -> st if not self._is_relative_url(url): return url + # Check for service-specific URL override + override_url = self._resolve_service_override(url) + if override_url is not None: + return override_url + parts = [self.org_name] if scoped == "tenant": parts.append(self.tenant_name) @@ -68,6 +75,29 @@ def scope_url(self, url: str, scoped: Literal["org", "tenant"] = "tenant") -> st return "/".join(parts) + def _resolve_service_override(self, url: str) -> str | None: + """Return an absolute URL if a service override is configured for *url*. + + Extracts the first path segment. If it ends with ``_`` (the UiPath + service-prefix convention), look up an override. When found, strip the + prefix and return an absolute URL pointing at the override host. + """ + stripped = url.strip("/") + if not stripped: + return None + + first_segment, _, rest = stripped.partition("/") + if not first_segment.endswith("_"): + return None + + override_base = get_service_override(first_segment) + if override_base is None: + return None + + if rest: + return f"{override_base}/{rest}" + return override_base + @property def _org_tenant_names(self): parsed = urlparse(self._url) diff --git a/src/uipath/_utils/constants.py b/src/uipath/_utils/constants.py index bb0f08aba..0aa623471 100644 --- a/src/uipath/_utils/constants.py +++ b/src/uipath/_utils/constants.py @@ -19,6 +19,7 @@ ENV_UIPATH_PROCESS_UUID = "UIPATH_PROCESS_UUID" ENV_UIPATH_TRACE_ID = "UIPATH_TRACE_ID" ENV_UIPATH_PROCESS_VERSION = "UIPATH_PROCESS_VERSION" +ENV_LICENSE_TRANSACTION_ID = "LICENSE_TRANSACTION_ID" # Headers HEADER_FOLDER_KEY = "x-uipath-folderkey" @@ -28,6 +29,7 @@ HEADER_INTERNAL_TENANT_ID = "x-uipath-internal-tenantid" HEADER_JOB_KEY = "x-uipath-jobkey" HEADER_SW_LOCK_KEY = "x-uipath-sw-lockkey" +HEADER_LICENSING_TRANSACTION_ID = "X-UiPath-Licensing-TransactionId" # Data sources (request types) ORCHESTRATOR_STORAGE_BUCKET_DATA_SOURCE_REQUEST = ( diff --git a/src/uipath/platform/common/_base_service.py b/src/uipath/platform/common/_base_service.py index 65977d506..586a9dab9 100644 --- a/src/uipath/platform/common/_base_service.py +++ b/src/uipath/platform/common/_base_service.py @@ -20,7 +20,7 @@ ) from ..._utils import UiPathUrl, user_agent_value -from ..._utils._ssl_context import get_httpx_client_kwargs +from ..._utils._http_clients import get_httpx_client_kwargs from ..._utils.constants import HEADER_USER_AGENT from ..errors import EnrichedException from ._config import UiPathApiConfig @@ -46,11 +46,12 @@ def __init__( self._url = UiPathUrl(self._config.base_url) default_client_kwargs = get_httpx_client_kwargs() + base_headers = default_client_kwargs.pop("headers", {}) client_kwargs = { **default_client_kwargs, # SSL, proxy, timeout, redirects "base_url": self._url.base_url, - "headers": Headers(self.default_headers), + "headers": Headers({**base_headers, **self.default_headers}), } self._client = Client(**client_kwargs) @@ -103,6 +104,8 @@ def request( kwargs["headers"][HEADER_USER_AGENT] = user_agent_value(specific_component) scoped_url = self._url.scope_url(str(url), scoped) + if scoped_url.startswith(("http://", "https://")): + self._logger.debug(f"Service URL override active: {scoped_url}") response = self._client.request(method, scoped_url, **kwargs) @@ -140,6 +143,8 @@ async def request_async( ) scoped_url = self._url.scope_url(str(url), scoped) + if scoped_url.startswith(("http://", "https://")): + self._logger.debug(f"Service URL override active: {scoped_url}") response = await self._client_async.request(method, scoped_url, **kwargs) diff --git a/src/uipath/platform/common/_external_application_service.py b/src/uipath/platform/common/_external_application_service.py index ae4e543e4..ecf6cfd52 100644 --- a/src/uipath/platform/common/_external_application_service.py +++ b/src/uipath/platform/common/_external_application_service.py @@ -5,7 +5,7 @@ import httpx from httpx import HTTPStatusError, Request -from ..._utils._ssl_context import get_httpx_client_kwargs +from ..._utils._http_clients import get_httpx_client_kwargs from ..._utils.constants import ENV_BASE_URL from ..errors import EnrichedException from .auth import TokenData diff --git a/src/uipath/platform/context_grounding/_context_grounding_service.py b/src/uipath/platform/context_grounding/_context_grounding_service.py index 11ad14f30..00b75a32f 100644 --- a/src/uipath/platform/context_grounding/_context_grounding_service.py +++ b/src/uipath/platform/context_grounding/_context_grounding_service.py @@ -5,7 +5,7 @@ from pydantic import Field, TypeAdapter from ..._utils import Endpoint, RequestSpec, header_folder, resource_override -from ..._utils._ssl_context import get_httpx_client_kwargs +from ..._utils._http_clients import get_httpx_client_kwargs from ..._utils.constants import ( LLMV4_REQUEST, ORCHESTRATOR_STORAGE_BUCKET_DATA_SOURCE, diff --git a/src/uipath/platform/orchestrator/_attachments_service.py b/src/uipath/platform/orchestrator/_attachments_service.py index fac8a1658..129a0f201 100644 --- a/src/uipath/platform/orchestrator/_attachments_service.py +++ b/src/uipath/platform/orchestrator/_attachments_service.py @@ -12,7 +12,7 @@ from httpx._types import RequestContent from ..._utils import Endpoint, RequestSpec, header_folder -from ..._utils._ssl_context import get_httpx_client_kwargs +from ..._utils._http_clients import get_httpx_client_kwargs from ..._utils.constants import TEMP_ATTACHMENTS_FOLDER from ...tracing import traced from ..attachments import Attachment, AttachmentMode, BlobFileAccessInfo diff --git a/src/uipath/platform/orchestrator/_buckets_service.py b/src/uipath/platform/orchestrator/_buckets_service.py index ebb6bcbe3..98df46bd1 100644 --- a/src/uipath/platform/orchestrator/_buckets_service.py +++ b/src/uipath/platform/orchestrator/_buckets_service.py @@ -7,7 +7,7 @@ import httpx from ..._utils import Endpoint, RequestSpec, header_folder, resource_override -from ..._utils._ssl_context import get_httpx_client_kwargs +from ..._utils._http_clients import get_httpx_client_kwargs from ..._utils.validation import validate_pagination_params from ...tracing import traced from ..common import BaseService, FolderContext, UiPathApiConfig, UiPathExecutionContext diff --git a/src/uipath/tracing/_otel_exporters.py b/src/uipath/tracing/_otel_exporters.py index 02fe8cdb0..92ebd7867 100644 --- a/src/uipath/tracing/_otel_exporters.py +++ b/src/uipath/tracing/_otel_exporters.py @@ -11,7 +11,7 @@ SpanExportResult, ) -from uipath._utils._ssl_context import get_httpx_client_kwargs +from uipath._utils._http_clients import get_httpx_client_kwargs from ._utils import _SpanUtils @@ -117,13 +117,15 @@ def __init__( super().__init__() self.base_url = self._get_base_url() self.auth_token = os.environ.get("UIPATH_ACCESS_TOKEN") + client_kwargs = get_httpx_client_kwargs() + base_headers = client_kwargs.pop("headers", {}) + self.headers = { + **base_headers, "Content-Type": "application/json", "Authorization": f"Bearer {self.auth_token}", } - client_kwargs = get_httpx_client_kwargs() - self.http_client = httpx.Client(**client_kwargs, headers=self.headers) self.trace_id = trace_id diff --git a/src/uipath/utils/_endpoints_manager.py b/src/uipath/utils/_endpoints_manager.py index 3b2e22ce0..96d90116e 100644 --- a/src/uipath/utils/_endpoints_manager.py +++ b/src/uipath/utils/_endpoints_manager.py @@ -4,7 +4,7 @@ import httpx -from uipath._utils._ssl_context import get_httpx_client_kwargs +from uipath._utils._http_clients import get_httpx_client_kwargs loggger = logging.getLogger(__name__)