一键导入
salvo-timeout
Configure request timeouts to prevent slow requests from blocking resources. Use for protecting APIs from long-running operations.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Configure request timeouts to prevent slow requests from blocking resources. Use for protecting APIs from long-running operations.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Implement authentication and authorization using JWT, Basic Auth, or custom schemes. Use for securing API endpoints and user management.
Create basic Salvo web applications with handlers, routers, and server setup. Use when starting a new Salvo project or adding basic HTTP endpoints.
Implement caching strategies for improved performance. Use for reducing database load and speeding up responses.
Compress HTTP responses using gzip, brotli, zstd, or deflate. Use for reducing bandwidth and improving load times.
Limit concurrent requests to protect resources. Use for file uploads, expensive operations, and preventing resource exhaustion.
Configure Cross-Origin Resource Sharing (CORS) and security headers. Use for APIs accessed from browsers on different domains.
| name | salvo-timeout |
| description | Configure request timeouts to prevent slow requests from blocking resources. Use for protecting APIs from long-running operations. |
| version | 0.94.0 |
| tags | ["performance","timeout","request-timeout"] |
Timeout lives in salvo_extra behind a feature flag — it is NOT on by default.
[dependencies]
salvo = { version = "0.94.0", features = ["timeout"] }
Timeout is re-exported via salvo::prelude::Timeout.
use std::time::Duration;
use salvo::prelude::*;
#[handler]
async fn slow() -> &'static str {
tokio::time::sleep(Duration::from_secs(10)).await;
"done"
}
#[tokio::main]
async fn main() {
let router = Router::new()
.hoop(Timeout::new(Duration::from_secs(5)))
.push(Router::with_path("slow").get(slow));
let acceptor = TcpListener::new("0.0.0.0:8080").bind().await;
Server::new(acceptor).serve(router).await;
}
GOTCHA: on timeout Salvo returns 503 Service Unavailable, not 408 Request Timeout. This is intentional — some browsers retry 408 automatically. The response also includes Connection: close.
Override the error via .error(|| StatusError):
use salvo::http::StatusError;
let timeout = Timeout::new(Duration::from_secs(5))
.error(|| StatusError::gateway_timeout().brief("Upstream too slow."));
let router = Router::new()
.push(Router::with_path("quick")
.hoop(Timeout::new(Duration::from_secs(2)))
.get(quick_handler))
.push(Router::with_path("upload")
.hoop(Timeout::new(Duration::from_secs(120)))
.post(upload_handler))
.push(Router::with_path("report")
.hoop(Timeout::new(Duration::from_secs(300)))
.post(report_handler));
Inner hoop runs after the outer one; a longer timeout on an inner route effectively overrides the shorter global timeout because the first handler to complete (or expire) wins the tokio::select! race.
let router = Router::new()
.hoop(Timeout::new(Duration::from_secs(30))) // default
.push(Router::with_path("health").get(health))
.push(
Router::with_path("reports/generate")
.hoop(Timeout::new(Duration::from_secs(300)))
.post(generate_report),
);
A Catcher can customize the 503 body:
use salvo::catcher::Catcher;
#[handler]
async fn on_timeout(res: &mut Response, ctrl: &mut FlowCtrl) {
if res.status_code == Some(StatusCode::SERVICE_UNAVAILABLE) {
res.render(Json(serde_json::json!({
"error": "timeout",
"message": "The request took too long to process",
})));
ctrl.skip_rest();
}
}
let service = Service::new(router).catcher(Catcher::default().hoop(on_timeout));
let router = Router::new()
.hoop(rate_limiter) // 429 on abuse
.hoop(Timeout::new(Duration::from_secs(30))) // 503 on stall
.push(Router::with_path("api/{**rest}").get(api_handler));
Do NOT wrap long-lived endpoints (WebSocket upgrade, SSE) with Timeout — the middleware aborts any handler that runs longer than the duration, which will terminate these connections.