From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) by lore.proxmox.com (Postfix) with ESMTPS id 42E7F1FF139 for ; Tue, 24 Feb 2026 12:43:45 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 492E06F5A; Tue, 24 Feb 2026 12:44:41 +0100 (CET) Date: Tue, 24 Feb 2026 12:44:33 +0100 From: Fabian =?iso-8859-1?q?Gr=FCnbichler?= Subject: partially-applied: [pbs-devel] [PATCH proxmox 1/3] s3-client: factor out optional response header parsing To: Proxmox Backup Server development discussion References: <20260127122712.505774-1-c.ebner@proxmox.com> <20260127122712.505774-2-c.ebner@proxmox.com> In-Reply-To: <20260127122712.505774-2-c.ebner@proxmox.com> MIME-Version: 1.0 User-Agent: astroid/0.17.0 (https://github.com/astroidmail/astroid) Message-Id: <1771933001.xrrl8zqvkr.astroid@yuna.none> Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1771933461307 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.047 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 SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: 4WY6OSIQ74Q4T5DW63JPWVDZJXXMSS4E X-Message-ID-Hash: 4WY6OSIQ74Q4T5DW63JPWVDZJXXMSS4E X-MailFrom: f.gruenbichler@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox Backup Server development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: applied this one, thanks! On January 27, 2026 1:27 pm, Christian Ebner wrote: > Introduce an associated function to parse optional headers, currently > only present for the date header. Further, reduce code duplication > by using the same function also for the parsing of required headers. >=20 > Will be used to parse optional headers set in the delete object > response so they can be mapped to look like a delete objects response > when adding a provider quirk to perform delete objects via individual > delete object calls. >=20 > Signed-off-by: Christian Ebner > --- > proxmox-s3-client/src/response_reader.rs | 47 +++++++++++++----------- > 1 file changed, 25 insertions(+), 22 deletions(-) >=20 > diff --git a/proxmox-s3-client/src/response_reader.rs b/proxmox-s3-client= /src/response_reader.rs > index e03b3bb0..7066c33b 100644 > --- a/proxmox-s3-client/src/response_reader.rs > +++ b/proxmox-s3-client/src/response_reader.rs > @@ -251,7 +251,7 @@ impl ResponseReader { > =20 > let body =3D String::from_utf8(body.to_vec())?; > =20 > - let date =3D Self::parse_optional_date_header(&parts.headers)?; > + let date =3D Self::parse_optional_header(header::DATE, &parts.he= aders)?; > =20 > let response: ListObjectsV2ResponseBody =3D > serde_xml_rs::from_str(&body).context("failed to parse respo= nse body")?; > @@ -282,7 +282,7 @@ impl ResponseReader { > let content_length: u64 =3D Self::parse_header(header::CONTENT_L= ENGTH, &parts.headers)?; > let content_type =3D Self::parse_header(header::CONTENT_TYPE, &p= arts.headers)?; > let e_tag =3D Self::parse_header(header::ETAG, &parts.headers)?; > - let date =3D Self::parse_optional_date_header(&parts.headers)?; > + let date =3D Self::parse_optional_header(header::DATE, &parts.he= aders)?; > let last_modified =3D Self::parse_header(header::LAST_MODIFIED, = &parts.headers)?; > =20 > Ok(Some(HeadObjectResponse { > @@ -314,7 +314,7 @@ impl ResponseReader { > let content_length: u64 =3D Self::parse_header(header::CONTENT_L= ENGTH, &parts.headers)?; > let content_type =3D Self::parse_header(header::CONTENT_TYPE, &p= arts.headers)?; > let e_tag =3D Self::parse_header(header::ETAG, &parts.headers)?; > - let date =3D Self::parse_optional_date_header(&parts.headers)?; > + let date =3D Self::parse_optional_header(header::DATE, &parts.he= aders)?; > let last_modified =3D Self::parse_header(header::LAST_MODIFIED, = &parts.headers)?; > =20 > Ok(Some(GetObjectResponse { > @@ -477,30 +477,30 @@ impl ResponseReader { > ::Err: Send + Sync + 'static, > Result::Err>: Context::Err>, > { > - let header_value =3D headers > - .get(&name) > + let value =3D Self::parse_optional_header(name.clone(), headers)= ? > .ok_or_else(|| anyhow!("missing header '{name}'"))?; > - let header_str =3D header_value > - .to_str() > - .with_context(|| format!("non UTF-8 header '{name}'"))?; > - let value =3D header_str > - .parse() > - .with_context(|| format!("failed to parse header '{name}'"))= ?; > Ok(value) > } > =20 > - fn parse_optional_date_header(headers: &HeaderMap) -> Result, Error> { > - let header_value =3D match headers.get(header::DATE) { > + fn parse_optional_header( > + name: HeaderName, > + headers: &HeaderMap, > + ) -> Result, Error> > + where > + ::Err: Send + Sync + 'static, > + Result::Err>: Context::Err>, > + { > + let header_value =3D match headers.get(&name) { > Some(value) =3D> value, > None =3D> return Ok(None), > }; > let header_str =3D header_value > .to_str() > - .with_context(|| format!("non UTF-8 header '{}'", header::DA= TE))?; > - let date: HttpDate =3D header_str > + .with_context(|| format!("non UTF-8 header '{name}'"))?; > + let value =3D header_str > .parse() > - .with_context(|| format!("failed to parse header '{}'", head= er::DATE))?; > - Ok(Some(date)) > + .with_context(|| format!("failed to parse header '{name}'"))= ?; > + Ok(Some(value)) > } > } > =20 > @@ -615,7 +615,8 @@ fn test_optional_date_header_parsing() { > =20 > let expected_date =3D "Wed, 12 Oct 2009 17:50:00 GMT"; > header_map.insert(header::DATE, expected_date.parse().unwrap()); > - let parsed_date =3D ResponseReader::parse_optional_date_header(&head= er_map).unwrap(); > + let parsed_date: Option =3D > + ResponseReader::parse_optional_header(header::DATE, &header_map)= .unwrap(); > assert!(parsed_date.is_some()); > assert_eq!( > parsed_date.unwrap(), > @@ -625,10 +626,12 @@ fn test_optional_date_header_parsing() { > header_map.clear(); > let invalid_date_format =3D "2019-11-10"; > header_map.insert(header::DATE, invalid_date_format.parse().unwrap()= ); > - assert!(ResponseReader::parse_optional_date_header(&header_map).is_e= rr()); > + assert!(ResponseReader::parse_optional_header::(header::DA= TE, &header_map).is_err()); > =20 > header_map.clear(); > - assert!(ResponseReader::parse_optional_date_header(&header_map) > - .unwrap() > - .is_none()); > + assert!( > + ResponseReader::parse_optional_header::(header::DATE, = &header_map) > + .unwrap() > + .is_none() > + ); > } > --=20 > 2.47.3 >=20 >=20 >=20 > _______________________________________________ > pbs-devel mailing list > pbs-devel@lists.proxmox.com > https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel >=20 >=20 >=20