From b54ff06bc7960122f47a9f2dd485412ac50a9eca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Thu, 24 Sep 2026 17:57:31 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B9=B3=E5=8F=B0=E5=B1=82=20Tripo=20=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E6=8A=8A=20base=5Furl=20=E6=A0=A1=E9=AA=8C=E6=88=90?= =?UTF-8?q?=E7=BB=9D=E5=AF=B9=20http(s)=20=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - server-rs/crates/platform-tripo/src/common/config.rs:validate 用 url::Url 解析 base_url,缺 scheme(openapi.tripo3d.com)、非 http(s)(ftp://)与无 host 的值都在构造点按 Configuration 失败,不再拖到请求期变成传输层报错 - server-rs/crates/platform-tripo/src/common/config.rs:补用例覆盖缺 scheme / 非 http(s) / 非法 URL 被拒、前后带空白的合法地址放行 --- .../platform-tripo/src/common/config.rs | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/server-rs/crates/platform-tripo/src/common/config.rs b/server-rs/crates/platform-tripo/src/common/config.rs index 66bc0548b..0fa12c18f 100644 --- a/server-rs/crates/platform-tripo/src/common/config.rs +++ b/server-rs/crates/platform-tripo/src/common/config.rs @@ -51,6 +51,9 @@ impl TripoSettings { /// 校验配置自洽。 /// + /// `base_url` 必须是绝对的 `http(s)` 地址:只会「非空」挡不住 `example.com` 这类少了 + /// scheme 的写法,那种值要等到真正发请求时才失败,报错指向传输层而不是配置本身。 + /// /// 字段都是公开的,调用方可以用结构体字面量绕过 [`TripoSettings::new`]; /// [`crate::TripoProviderClient::new`] 会再校验一次,保证真正发起请求的那一端 /// 拿到的永远是合法配置。 @@ -67,6 +70,23 @@ impl TripoSettings { message: "base_url must not be blank".to_string(), }); } + let base_url = + url::Url::parse(self.base_url.trim()).map_err(|error| TripoError::Configuration { + field: "base_url", + message: format!("base_url must be an absolute URL: {error}"), + })?; + if !matches!(base_url.scheme(), "http" | "https") { + return Err(TripoError::Configuration { + field: "base_url", + message: "base_url must use http or https".to_string(), + }); + } + if base_url.host_str().is_none() { + return Err(TripoError::Configuration { + field: "base_url", + message: "base_url must include a host".to_string(), + }); + } if self.request_timeout.is_zero() { return Err(TripoError::Configuration { field: "request_timeout", @@ -127,6 +147,29 @@ mod tests { } } + #[test] + fn base_url_must_be_an_absolute_http_url() { + for (label, base_url) in [ + ("缺 scheme", "openapi.tripo3d.com/v3"), + ("非 http(s)", "ftp://openapi.tripo3d.com/v3"), + ("不是 URL", "not-a-url"), + ] { + let error = settings(Duration::from_secs(1), "key", base_url) + .validate() + .expect_err(&format!("{label} 必须被拒绝")); + assert!(matches!(error, TripoError::Configuration { .. }), "{error}"); + } + + // 前后带空白的合法地址仍然放行:先 trim 再校验,与取值口径一致。 + settings( + Duration::from_secs(1), + "key", + " https://openapi.tripo3d.com/v3 ", + ) + .validate() + .expect("带空白的合法地址应可校验通过"); + } + #[test] fn new_returns_ready_to_use_settings() { let settings = TripoSettings::new(