From 70831c27b3e17ff824d8aa2ef28b223c5f80e6c2 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Sun, 14 Jun 2026 21:50:38 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=94=AF=E6=8C=81=E6=A0=87=E5=87=86?= =?UTF-8?q?=E4=BB=A3=E7=90=86=E7=8E=AF=E5=A2=83=E5=8F=98=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/core/config.py | 29 +++++++++++++-- tests/test_proxy_config.py | 75 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 3 deletions(-) create mode 100644 tests/test_proxy_config.py diff --git a/app/core/config.py b/app/core/config.py index 41a37daf..5948f50a 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -982,12 +982,35 @@ class Settings(BaseSettings, ConfigModel, LogConfigModel): ) @property - def PROXY(self): + def PROXY(self) -> Optional[Dict[str, str]]: + """ + 获取 requests 兼容的系统代理配置。 + """ if self.PROXY_HOST and self.PROXY_HOST.strip(): + proxy_host = self.PROXY_HOST.strip() return { - "http": self.PROXY_HOST, - "https": self.PROXY_HOST, + "http": proxy_host, + "https": proxy_host, } + https_proxy = self._get_env_proxy("HTTPS_PROXY", "https_proxy") + http_proxy = self._get_env_proxy("HTTP_PROXY", "http_proxy") + proxy_host = https_proxy or http_proxy + if proxy_host: + return { + "http": http_proxy or proxy_host, + "https": https_proxy or proxy_host, + } + return None + + @staticmethod + def _get_env_proxy(*names: str) -> Optional[str]: + """ + 按顺序读取非空代理环境变量。 + """ + for name in names: + proxy_host = os.environ.get(name) + if proxy_host and proxy_host.strip(): + return proxy_host.strip() return None @property diff --git a/tests/test_proxy_config.py b/tests/test_proxy_config.py new file mode 100644 index 00000000..eb3ca923 --- /dev/null +++ b/tests/test_proxy_config.py @@ -0,0 +1,75 @@ +from app.core.config import Settings + + +PROXY_ENV_NAMES = ( + "PROXY_HOST", + "HTTP_PROXY", + "HTTPS_PROXY", + "http_proxy", + "https_proxy", +) + + +def clear_proxy_env(monkeypatch) -> None: + """ + 清理测试进程中的代理环境变量。 + """ + for name in PROXY_ENV_NAMES: + monkeypatch.delenv(name, raising=False) + + +def test_proxy_prefers_proxy_host_over_standard_env(monkeypatch) -> None: + """ + PROXY_HOST 应优先于标准代理环境变量。 + """ + clear_proxy_env(monkeypatch) + monkeypatch.setenv("HTTPS_PROXY", "http://env-proxy.example.com:7890") + + settings = Settings(PROXY_HOST=" http://custom-proxy.example.com:7890 ") + + assert settings.PROXY == { + "http": "http://custom-proxy.example.com:7890", + "https": "http://custom-proxy.example.com:7890", + } + + +def test_proxy_falls_back_to_standard_proxy_env(monkeypatch) -> None: + """ + 未配置 PROXY_HOST 时应读取 HTTP_PROXY 和 HTTPS_PROXY。 + """ + clear_proxy_env(monkeypatch) + monkeypatch.setenv("HTTP_PROXY", "http://http-proxy.example.com:7890") + monkeypatch.setenv("HTTPS_PROXY", "http://https-proxy.example.com:7890") + + settings = Settings(PROXY_HOST=None) + + assert settings.PROXY == { + "http": "http://http-proxy.example.com:7890", + "https": "http://https-proxy.example.com:7890", + } + + +def test_proxy_reuses_single_standard_env_for_both_schemes(monkeypatch) -> None: + """ + 只配置单个标准代理环境变量时应同时用于 http 和 https。 + """ + clear_proxy_env(monkeypatch) + monkeypatch.setenv("HTTP_PROXY", "http://http-proxy.example.com:7890") + + settings = Settings(PROXY_HOST=None) + + assert settings.PROXY == { + "http": "http://http-proxy.example.com:7890", + "https": "http://http-proxy.example.com:7890", + } + + +def test_proxy_returns_none_without_any_proxy(monkeypatch) -> None: + """ + 未配置任何代理时应返回 None。 + """ + clear_proxy_env(monkeypatch) + + settings = Settings(PROXY_HOST=None) + + assert settings.PROXY is None