From 6593c52a470e10abdb8df305c3cc177317a97592 Mon Sep 17 00:00:00 2001 From: RA <70325462+RAprogramm@users.noreply.github.com> Date: Thu, 11 Sep 2025 13:08:26 +0700 Subject: [PATCH] propagate reqwest timeout error message --- Cargo.lock | 12 +++++++++++ Cargo.toml | 1 + src/convert/reqwest.rs | 45 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 5f6bff5..81a06b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2290,9 +2290,21 @@ dependencies = [ "signal-hook-registry", "slab", "socket2 0.6.0", + "tokio-macros", "windows-sys 0.59.0", ] +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tokio-util" version = "0.7.16" diff --git a/Cargo.toml b/Cargo.toml index afa66cd..be4f344 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,6 +55,7 @@ reqwest = { version = "0.12", optional = true, default-features = false } [dev-dependencies] serde_json = "1" +tokio = { version = "1", features = ["macros", "rt-multi-thread", "net", "time"], default-features = false } [lib] crate-type = ["cdylib", "rlib"] diff --git a/src/convert/reqwest.rs b/src/convert/reqwest.rs index 454bb89..4a632bc 100644 --- a/src/convert/reqwest.rs +++ b/src/convert/reqwest.rs @@ -56,7 +56,7 @@ use crate::AppError; impl From for AppError { fn from(err: ReqwestError) -> Self { if err.is_timeout() { - AppError::timeout("Request timeout") + AppError::timeout(format!("Request timeout: {err}")) } else if err.is_connect() || err.is_request() { AppError::network(format!("Network error: {err}")) } else if err.is_status() { @@ -66,3 +66,46 @@ impl From for AppError { } } } + +#[cfg(all(test, feature = "reqwest", feature = "tokio"))] +mod tests { + use std::time::Duration; + + use reqwest::Client; + use tokio::{net::TcpListener, time::sleep}; + + use super::*; + + #[tokio::test] + async fn timeout_message_includes_original_error() { + let listener = TcpListener::bind("127.0.0.1:0") + .await + .expect("bind listener"); + let addr = listener.local_addr().expect("listener addr"); + + let server = tokio::spawn(async move { + let (_socket, _) = listener.accept().await.expect("accept"); + sleep(Duration::from_secs(5)).await; + }); + + let client = Client::builder() + .timeout(Duration::from_millis(50)) + .build() + .expect("client"); + + let err = client + .get(format!("http://{addr}")) + .send() + .await + .expect_err("expected timeout"); + + assert!(err.is_timeout()); + + let err_str = err.to_string(); + let app_err: AppError = err.into(); + let msg = app_err.message.expect("app error message"); + assert!(msg.contains(&err_str), "{msg} does not contain {err_str}"); + + server.abort(); + } +}