修复AGC运行时测试与本地请求回归
将平台生成 5xx 提交统一归类为结果未知,保留对账账本 本地 loopback Provider 请求绕过 HTTP 代理,恢复 transport 错误分类 修正流式与画布 mock 服务读取完整请求体 放宽 macOS 系统临时目录路径测试前提并保留项目链接安全校验
This commit is contained in:
@@ -3074,7 +3074,7 @@ mod tests {
|
||||
assert!(resolve_direct_codex_game_workspace(&project_root).is_err());
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[cfg(all(unix, not(target_os = "macos")))]
|
||||
#[test]
|
||||
fn direct_project_grant_root_comparison_remains_case_sensitive() {
|
||||
let temp = tempfile::tempdir().expect("temp dir");
|
||||
@@ -3162,7 +3162,7 @@ mod tests {
|
||||
assert_ne!(first_identity, second_identity);
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[cfg(all(unix, not(target_os = "macos")))]
|
||||
#[test]
|
||||
fn direct_project_pool_identity_hashes_non_utf8_canonical_path_bytes_losslessly() {
|
||||
use std::os::unix::ffi::OsStringExt;
|
||||
|
||||
@@ -963,7 +963,7 @@ pub(crate) async fn submit_external_generation_request(
|
||||
}
|
||||
let request_body_json = serde_json::to_string(&request_body)
|
||||
.map_err(|error| format!("序列化平台图片生成请求失败:{error}"))?;
|
||||
client
|
||||
let response = client
|
||||
.post(format!(
|
||||
"{api_base_url}{}",
|
||||
resolve_platform_editor_api_route(endpoint)
|
||||
@@ -978,7 +978,14 @@ pub(crate) async fn submit_external_generation_request(
|
||||
format!(
|
||||
"{EXTERNAL_GENERATION_RESULT_UNKNOWN_PREFIX} 请求平台图片生成后未取得确定响应:{error}"
|
||||
)
|
||||
})
|
||||
})?;
|
||||
if response.status().is_server_error() {
|
||||
return Err(format!(
|
||||
"{EXTERNAL_GENERATION_RESULT_UNKNOWN_PREFIX} 请求平台图片生成后收到 HTTP {},服务端是否已产生副作用未知",
|
||||
response.status().as_u16()
|
||||
));
|
||||
}
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
async fn resume_prepared_external_generation_at(
|
||||
|
||||
@@ -459,10 +459,19 @@ fn validate_repository_root(root: &Path) -> Result<PathBuf, String> {
|
||||
return Err("Repository root must not contain parent-directory components".to_string());
|
||||
}
|
||||
|
||||
let canonical_temp_root = fs::canonicalize(std::env::temp_dir()).ok();
|
||||
let raw_temp_root = std::env::temp_dir();
|
||||
let allow_system_temp_symlink = canonical_temp_root.as_ref().is_some_and(|temp| {
|
||||
fs::canonicalize(root).is_ok_and(|canonical| canonical.starts_with(temp))
|
||||
}) && root.starts_with(&raw_temp_root);
|
||||
for ancestor in root.ancestors() {
|
||||
let metadata = fs::symlink_metadata(ancestor)
|
||||
.map_err(|error| format!("Unable to inspect repository root ancestor: {error}"))?;
|
||||
if metadata_is_symlink_like(&metadata) {
|
||||
let is_system_temp_ancestor = allow_system_temp_symlink
|
||||
&& raw_temp_root
|
||||
.ancestors()
|
||||
.any(|temp_ancestor| temp_ancestor == ancestor);
|
||||
if metadata_is_symlink_like(&metadata) && !is_system_temp_ancestor {
|
||||
return Err("Repository root and its ancestors must not be symbolic links".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2446,10 +2446,10 @@ fn spawn_mock_llm_stream_server_with_capture(
|
||||
let base_url = format!("http://{}", listener.local_addr().expect("mock llm addr"));
|
||||
std::thread::spawn(move || {
|
||||
let (mut stream, _) = listener.accept().expect("mock stream llm accept");
|
||||
let mut request_buffer = [0_u8; 8192];
|
||||
let read_len = stream.read(&mut request_buffer).unwrap_or(0);
|
||||
if let Some(sender) = request_sender.as_ref() {
|
||||
let _ = sender.send(String::from_utf8_lossy(&request_buffer[..read_len]).into_owned());
|
||||
let _ = sender.send(read_mock_http_request(&mut stream));
|
||||
} else {
|
||||
let _ = read_mock_http_request(&mut stream);
|
||||
}
|
||||
let response = format!(
|
||||
"HTTP/1.1 200 OK\r\nContent-Type: text/event-stream; charset=utf-8\r\nContent-Length: {}\r\nx-request-id: req_role_agent_stream\r\nConnection: close\r\n\r\n{}",
|
||||
@@ -3028,11 +3028,9 @@ fn spawn_mock_external_canvas_api_server_with_capture_and_generation_gate(
|
||||
let mut generation_poll_index = 0_u8;
|
||||
for _ in 0..expected_requests {
|
||||
let (mut stream, _) = listener.accept().expect("mock canvas api accept");
|
||||
let mut request_buffer = [0_u8; 8192];
|
||||
let read_len = stream.read(&mut request_buffer).unwrap_or(0);
|
||||
let request = String::from_utf8_lossy(&request_buffer[..read_len]);
|
||||
let request = read_mock_http_request(&mut stream);
|
||||
if let Some(sender) = request_sender.as_ref() {
|
||||
let _ = sender.send(request.to_string());
|
||||
let _ = sender.send(request.clone());
|
||||
}
|
||||
let normalized_request = request.to_ascii_lowercase();
|
||||
let (status, content_type, body) = if request
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::{
|
||||
env,
|
||||
error::Error,
|
||||
fmt, fs,
|
||||
net::IpAddr,
|
||||
path::PathBuf,
|
||||
str as std_str,
|
||||
sync::atomic::{AtomicU64, Ordering},
|
||||
@@ -1496,7 +1497,13 @@ impl LlmError {
|
||||
|
||||
impl LlmClient {
|
||||
pub fn new(config: LlmConfig) -> Result<Self, LlmError> {
|
||||
let http_client = Client::builder()
|
||||
let mut builder = Client::builder();
|
||||
if llm_base_url_is_loopback(config.base_url()) {
|
||||
// 本地 mock/sidecar 必须直连。否则开发机 HTTP_PROXY 可能把已断开的
|
||||
// loopback 连接改写成 HTTP 502,破坏 Provider transport 错误分类。
|
||||
builder = builder.no_proxy();
|
||||
}
|
||||
let http_client = builder
|
||||
.use_rustls_tls()
|
||||
.http1_only()
|
||||
.build()
|
||||
@@ -1511,7 +1518,11 @@ impl LlmClient {
|
||||
}
|
||||
|
||||
pub fn new_without_redirects(config: LlmConfig) -> Result<Self, LlmError> {
|
||||
let http_client = Client::builder()
|
||||
let mut builder = Client::builder();
|
||||
if llm_base_url_is_loopback(config.base_url()) {
|
||||
builder = builder.no_proxy();
|
||||
}
|
||||
let http_client = builder
|
||||
.use_rustls_tls()
|
||||
.http1_only()
|
||||
.redirect(Policy::none())
|
||||
@@ -2040,6 +2051,17 @@ impl LlmClient {
|
||||
}
|
||||
}
|
||||
|
||||
fn llm_base_url_is_loopback(base_url: &str) -> bool {
|
||||
let Ok(url) = reqwest::Url::parse(base_url) else {
|
||||
return false;
|
||||
};
|
||||
match url.host_str() {
|
||||
Some("localhost") => true,
|
||||
Some(host) => host.parse::<IpAddr>().is_ok_and(|ip| ip.is_loopback()),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
impl OpenAiCompatibleSseParser {
|
||||
fn new(api_kind: LlmApiKind) -> Self {
|
||||
Self {
|
||||
|
||||
Reference in New Issue
Block a user