From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id 4BFA81FF189 for ; Fri, 8 Aug 2025 13:39:56 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 5184615DCA; Fri, 8 Aug 2025 13:41:30 +0200 (CEST) From: Christian Ebner To: pbs-devel@lists.proxmox.com Date: Fri, 8 Aug 2025 13:41:16 +0200 Message-ID: <20250808114117.393536-2-c.ebner@proxmox.com> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250808114117.393536-1-c.ebner@proxmox.com> References: <20250808114117.393536-1-c.ebner@proxmox.com> MIME-Version: 1.0 X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1754653262974 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.042 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment RCVD_IN_VALIDITY_CERTIFIED_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. RCVD_IN_VALIDITY_RPBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. RCVD_IN_VALIDITY_SAFE_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record URIBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [proxmox.com] Subject: [pbs-devel] [PATCH proxmox 1/2] s3-client: fix non-optional date header for api response parsing 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: , Reply-To: Proxmox Backup Server development discussion Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: pbs-devel-bounces@lists.proxmox.com Sender: "pbs-devel" Fixes an implementation oversight for the http date header response parsing. This header is most commonly present for all responses, but it is documented as optional in the AWS reference documentation [0]. While introduced during development to possibly detect time shifts between the local s3 client and the api for e.g. last modified time checks, it is currently not actively used by any codepath, and can therefore be adapted without further modifications. [0] https://docs.aws.amazon.com/AmazonS3/latest/API/RESTCommonResponseHeaders.html Fixes: https://forum.proxmox.com/threads/169395/ Signed-off-by: Christian Ebner --- proxmox-s3-client/src/response_reader.rs | 33 +++++++++++++++++------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/proxmox-s3-client/src/response_reader.rs b/proxmox-s3-client/src/response_reader.rs index bfd71205..f200e15a 100644 --- a/proxmox-s3-client/src/response_reader.rs +++ b/proxmox-s3-client/src/response_reader.rs @@ -9,8 +9,7 @@ use hyper::http::StatusCode; use hyper::{HeaderMap, Response}; use serde::Deserialize; -use crate::S3ObjectKey; -use crate::{HttpDate, LastModifiedTimestamp}; +use crate::{HttpDate, LastModifiedTimestamp, S3ObjectKey}; /// Response reader to check S3 api response status codes and parse response body, if any. pub(crate) struct ResponseReader { @@ -21,7 +20,7 @@ pub(crate) struct ResponseReader { /// Response contents of list objects v2 api calls. pub struct ListObjectsV2Response { /// Parsed http date header from response. - pub date: HttpDate, + pub date: Option, /// Bucket name. pub name: String, /// Requested key prefix. @@ -64,7 +63,7 @@ struct ListObjectsV2ResponseBody { } impl ListObjectsV2ResponseBody { - fn with_date(self, date: HttpDate) -> ListObjectsV2Response { + fn with_optional_date(self, date: Option) -> ListObjectsV2Response { ListObjectsV2Response { date, name: self.name, @@ -105,7 +104,7 @@ pub struct HeadObjectResponse { /// Content type header. pub content_type: String, /// Http date header. - pub date: HttpDate, + pub date: Option, /// Entity tag header. pub e_tag: String, /// Last modified http header. @@ -120,7 +119,7 @@ pub struct GetObjectResponse { /// Content type header. pub content_type: String, /// Http date header. - pub date: HttpDate, + pub date: Option, /// Entity tag header. pub e_tag: String, /// Last modified http header. @@ -272,12 +271,12 @@ impl ResponseReader { let body = String::from_utf8(body.to_vec())?; - let date: HttpDate = Self::parse_header(header::DATE, &parts.headers)?; + let date = Self::parse_optional_date_header(&parts.headers)?; let response: ListObjectsV2ResponseBody = serde_xml_rs::from_str(&body).context("failed to parse response body")?; - Ok(response.with_date(date)) + Ok(response.with_optional_date(date)) } /// Read and parse the head object response. @@ -303,7 +302,7 @@ impl ResponseReader { let content_length: u64 = Self::parse_header(header::CONTENT_LENGTH, &parts.headers)?; let content_type = Self::parse_header(header::CONTENT_TYPE, &parts.headers)?; let e_tag = Self::parse_header(header::ETAG, &parts.headers)?; - let date = Self::parse_header(header::DATE, &parts.headers)?; + let date = Self::parse_optional_date_header(&parts.headers)?; let last_modified = Self::parse_header(header::LAST_MODIFIED, &parts.headers)?; Ok(Some(HeadObjectResponse { @@ -335,7 +334,7 @@ impl ResponseReader { let content_length: u64 = Self::parse_header(header::CONTENT_LENGTH, &parts.headers)?; let content_type = Self::parse_header(header::CONTENT_TYPE, &parts.headers)?; let e_tag = Self::parse_header(header::ETAG, &parts.headers)?; - let date = Self::parse_header(header::DATE, &parts.headers)?; + let date = Self::parse_optional_date_header(&parts.headers)?; let last_modified = Self::parse_header(header::LAST_MODIFIED, &parts.headers)?; Ok(Some(GetObjectResponse { @@ -509,6 +508,20 @@ impl ResponseReader { .with_context(|| format!("failed to parse header '{name}'"))?; Ok(value) } + + fn parse_optional_date_header(headers: &HeaderMap) -> Result, Error> { + let header_value = match headers.get(header::DATE) { + Some(value) => value, + None => return Ok(None), + }; + let header_str = header_value + .to_str() + .with_context(|| format!("non UTF-8 header '{}'", header::DATE))?; + let date: HttpDate = header_str + .parse() + .with_context(|| format!("failed to parse header '{}'", header::DATE))?; + Ok(Some(date)) + } } #[test] -- 2.47.2 _______________________________________________ pbs-devel mailing list pbs-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel