From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 19F9D62A19 for ; Mon, 21 Dec 2020 14:56:27 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 17C97208FE for ; Mon, 21 Dec 2020 14:56:27 +0100 (CET) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [212.186.127.180]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS id 14ACF208C8 for ; Mon, 21 Dec 2020 14:56:21 +0100 (CET) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id D0101453A8 for ; Mon, 21 Dec 2020 14:56:20 +0100 (CET) From: Stefan Reiter To: pbs-devel@lists.proxmox.com Date: Mon, 21 Dec 2020 14:56:11 +0100 Message-Id: <20201221135611.14456-3-s.reiter@proxmox.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20201221135611.14456-1-s.reiter@proxmox.com> References: <20201221135611.14456-1-s.reiter@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL -0.033 Adjusted score from AWL reputation of From: address KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Subject: [pbs-devel] [RFC proxmox-backup 2/2] http_client: add timeouts for critical connects X-BeenThere: pbs-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox Backup Server development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 21 Dec 2020 13:56:27 -0000 Use timeout futures for sections that might hang in certain error conditions. This is mostly intended to be used as a safeguard, not a first line of defense - i.e. best-effort avoidance of total hangs. Not every future used for the HttpClient/H2Client is changed, only those where a quick response is to be expected. For example, the response reading futures are left alone, so data transfer is never capped with timeout, only the initial server connect. It is also used for upgrading to H2 connections, as that can take a long time on overloaded servers. Signed-off-by: Stefan Reiter --- src/client/http_client.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/client/http_client.rs b/src/client/http_client.rs index a9b9c06c..92df9572 100644 --- a/src/client/http_client.rs +++ b/src/client/http_client.rs @@ -18,6 +18,7 @@ use proxmox::{ api::error::HttpError, sys::linux::tty, tools::fs::{file_get_json, replace_file, CreateOptions}, + tools::future::TimeoutFutureExt, }; use super::pipe_to_stream::PipeToSendStream; @@ -29,6 +30,10 @@ use crate::tools::{ http::HttpsConnector, }; +/// Timeout used for several HTTP operations that are expected to finish quickly but may block in +/// certain error conditions. +const HTTP_TIMEOUT: Duration = Duration::from_secs(20); + #[derive(Clone)] pub struct AuthInfo { pub auth_id: Authid, @@ -557,7 +562,10 @@ impl HttpClient { let enc_ticket = format!("PBSAuthCookie={}", percent_encode(auth.ticket.as_bytes(), DEFAULT_ENCODE_SET)); req.headers_mut().insert("Cookie", HeaderValue::from_str(&enc_ticket).unwrap()); - let resp = client.request(req).await?; + let resp = client + .request(req) + .or_timeout_err(HTTP_TIMEOUT, format_err!("http download request timed out")) + .await?; let status = resp.status(); if !status.is_success() { HttpClient::api_response(resp) @@ -624,7 +632,10 @@ impl HttpClient { req.headers_mut().insert("UPGRADE", HeaderValue::from_str(&protocol_name).unwrap()); - let resp = client.request(req).await?; + let resp = client + .request(req) + .or_timeout_err(HTTP_TIMEOUT, format_err!("http upgrade request timed out")) + .await?; let status = resp.status(); if status != http::StatusCode::SWITCHING_PROTOCOLS { @@ -705,7 +716,7 @@ impl HttpClient { ) -> Result { client.request(req) - .map_err(Error::from) + .or_timeout_err(HTTP_TIMEOUT, format_err!("http request timed out")) .and_then(Self::api_response) .await } -- 2.20.1