fix(upload): 测速时单条线路失败不再中断整体测速 (#1674)
Probe::probe 对每条线路的 ping 使用 ? 传播错误,任意一条线路 DNS/连接/读取失败都会让整个测速立即返回错误,即使后续线路 完全可用,调用方也会因 unwrap_or_default 直接退回默认线路。 现在单条线路网络错误或异常状态码只记录警告并跳过该线路, 继续测速其余候选;全部线路失败时返回统一错误。
This commit is contained in:
1 file changed
+84
-12
@@ -13,7 +13,7 @@ use crate::error::Kind::{Custom, RateLimit};
|
||||
use crate::uploader::bilibili::{BiliBili, Video};
|
||||
use crate::uploader::line::upos::{Upos, UposPart};
|
||||
use std::time::Instant;
|
||||
use tracing::info;
|
||||
use tracing::{info, warn};
|
||||
|
||||
pub mod upos;
|
||||
|
||||
@@ -87,22 +87,44 @@ impl Probe {
|
||||
.await?
|
||||
.json()
|
||||
.await?;
|
||||
// let client = res.ping(client);
|
||||
Self::select_line(client, &res.probe, res.lines).await
|
||||
}
|
||||
|
||||
/// 逐条线路测速并选出耗时最短者。
|
||||
/// 单条线路网络错误或异常状态码只跳过该线路,全部失败才返回错误。
|
||||
async fn select_line(
|
||||
client: &reqwest::Client,
|
||||
probe: &serde_json::Value,
|
||||
lines: Vec<Line>,
|
||||
) -> Result<Line> {
|
||||
let mut choice_line: Line = Default::default();
|
||||
for mut line in res.lines {
|
||||
for mut line in lines {
|
||||
let instant = Instant::now();
|
||||
if Probe::ping(&res.probe, &format!("https:{}", line.probe_url), client)
|
||||
match Probe::ping(probe, &format!("https:{}", line.probe_url), client)
|
||||
.send()
|
||||
.await?
|
||||
.status()
|
||||
.is_success()
|
||||
.await
|
||||
{
|
||||
line.cost = instant.elapsed().as_millis();
|
||||
info!("{}: {}", line.query, line.cost);
|
||||
if choice_line.cost > line.cost {
|
||||
choice_line = line
|
||||
Ok(response) if response.status().is_success() => {
|
||||
line.cost = instant.elapsed().as_millis();
|
||||
info!("{}: {}", line.query, line.cost);
|
||||
if choice_line.cost > line.cost {
|
||||
choice_line = line
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(response) => {
|
||||
warn!(
|
||||
"{} 测速返回异常状态码 {},跳过该线路",
|
||||
line.query,
|
||||
response.status()
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("{} 测速失败,跳过该线路: {e}", line.query);
|
||||
}
|
||||
}
|
||||
}
|
||||
if choice_line.cost == u128::MAX {
|
||||
return Err(Custom("所有上传线路测速均失败".to_string()));
|
||||
}
|
||||
Ok(choice_line)
|
||||
}
|
||||
@@ -498,3 +520,53 @@ pub fn akbd() -> Line {
|
||||
cost: 0,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn broken_line(query: &str) -> Line {
|
||||
Line {
|
||||
os: Uploader::Upos,
|
||||
// "https:" 拼接后不是合法 URL,send() 直接报错且不产生网络请求
|
||||
probe_url: String::new(),
|
||||
query: query.into(),
|
||||
cost: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// 单条线路 send 失败不应中断测速,全部失败时返回统一错误而不是首条线路的网络错误。
|
||||
#[tokio::test]
|
||||
async fn select_line_skips_failed_lines_and_reports_all_failed() {
|
||||
let client = reqwest::Client::new();
|
||||
let result = Probe::select_line(
|
||||
&client,
|
||||
&serde_json::json!({"get": {}}),
|
||||
vec![broken_line("a"), broken_line("b")],
|
||||
)
|
||||
.await;
|
||||
match result {
|
||||
Err(Custom(message)) => assert_eq!(message, "所有上传线路测速均失败"),
|
||||
other => panic!("期望所有线路失败的统一错误,实际为 {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
/// 无候选线路时同样返回错误,而不是把未测速的默认线路当作结果。
|
||||
#[tokio::test]
|
||||
async fn select_line_rejects_empty_lines() {
|
||||
let client = reqwest::Client::new();
|
||||
let result = Probe::select_line(&client, &serde_json::json!({"get": {}}), Vec::new()).await;
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
/// 真实网络测速仍能选出可用线路。默认忽略,本地验证:
|
||||
/// `cargo test -p biliup probe_selects_line_over_network -- --ignored --nocapture`
|
||||
#[tokio::test]
|
||||
#[ignore = "requires network access to member.bilibili.com"]
|
||||
async fn probe_selects_line_over_network() {
|
||||
let client = reqwest::Client::new();
|
||||
let line = Probe::probe(&client).await.expect("测速应选出可用线路");
|
||||
assert_ne!(line.cost, u128::MAX);
|
||||
println!("selected line: {} cost={}ms", line.query, line.cost);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user