diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 17863bc067..6998f9ea25 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -5,12 +5,14 @@ import time import uuid import email +import os import asyncio import inspect import logging import platform import warnings import email.utils +from contextlib import contextmanager from types import TracebackType from random import random from typing import ( @@ -79,6 +81,30 @@ OVERRIDE_CAST_TO_HEADER, DEFAULT_CONNECTION_LIMITS, ) + + +def _normalize_no_proxy_value(value: str) -> str: + entries = [entry.strip() for entry in value.replace("\r", ",").replace("\n", ",").split(",")] + return ",".join(entry for entry in entries if entry) + + +@contextmanager +def _sanitized_proxy_env() -> Generator[None, None, None]: + original: dict[str, str] = {} + + try: + for key in ("NO_PROXY", "no_proxy"): + value = os.environ.get(key) + if value is None or ("\n" not in value and "\r" not in value): + continue + + original[key] = value + os.environ[key] = _normalize_no_proxy_value(value) + + yield + finally: + for key, value in original.items(): + os.environ[key] = value from ._streaming import Stream, SSEDecoder, AsyncStream, SSEBytesDecoder from ._exceptions import ( APIStatusError, @@ -814,7 +840,8 @@ def __init__(self, **kwargs: Any) -> None: kwargs.setdefault("timeout", DEFAULT_TIMEOUT) kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) kwargs.setdefault("follow_redirects", True) - super().__init__(**kwargs) + with _sanitized_proxy_env(): + super().__init__(**kwargs) if TYPE_CHECKING: @@ -1388,7 +1415,8 @@ def __init__(self, **kwargs: Any) -> None: kwargs.setdefault("timeout", DEFAULT_TIMEOUT) kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) kwargs.setdefault("follow_redirects", True) - super().__init__(**kwargs) + with _sanitized_proxy_env(): + super().__init__(**kwargs) try: @@ -1406,7 +1434,8 @@ def __init__(self, **kwargs: Any) -> None: kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) kwargs.setdefault("follow_redirects", True) - super().__init__(**kwargs) + with _sanitized_proxy_env(): + super().__init__(**kwargs) if TYPE_CHECKING: diff --git a/tests/test_client.py b/tests/test_client.py index 396f6dea99..413463bd1b 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -414,6 +414,12 @@ def test_validate_headers(self) -> None: client2 = OpenAI(base_url=base_url, api_key=None, _strict_response_validation=True) _ = client2 + def test_no_proxy_with_newlines_is_sanitized_for_default_http_client(self) -> None: + with update_env(NO_PROXY="localhost\n127.0.0.1"): + client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + assert os.environ["NO_PROXY"] == "localhost\n127.0.0.1" + client.close() + def test_default_query_option(self) -> None: client = OpenAI( base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"query_param": "bar"} @@ -1438,6 +1444,12 @@ async def test_validate_headers(self) -> None: client2 = AsyncOpenAI(base_url=base_url, api_key=None, _strict_response_validation=True) _ = client2 + async def test_no_proxy_with_newlines_is_sanitized_for_default_http_client(self) -> None: + with update_env(NO_PROXY="localhost\n127.0.0.1"): + client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + assert os.environ["NO_PROXY"] == "localhost\n127.0.0.1" + await client.close() + async def test_default_query_option(self) -> None: client = AsyncOpenAI( base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"query_param": "bar"}