From 985566a93bb99fd7e5cd3a01f2ee2430f5a2abfc Mon Sep 17 00:00:00 2001 From: Jun Kurihara Date: Tue, 25 Nov 2025 14:10:10 +0900 Subject: [PATCH] deps: sfv-0.14 --- httpsig-hyper/Cargo.toml | 2 +- httpsig-hyper/src/hyper_content_digest.rs | 11 +++--- httpsig/Cargo.toml | 2 +- httpsig/src/message_component/component_id.rs | 12 +++++-- .../src/message_component/component_name.rs | 2 +- .../src/message_component/component_param.rs | 36 ++++++++++--------- httpsig/src/signature_base.rs | 20 +++++++---- httpsig/src/signature_params.rs | 26 ++++++++------ 8 files changed, 65 insertions(+), 46 deletions(-) diff --git a/httpsig-hyper/Cargo.toml b/httpsig-hyper/Cargo.toml index 9ada008..aeeec7b 100644 --- a/httpsig-hyper/Cargo.toml +++ b/httpsig-hyper/Cargo.toml @@ -25,7 +25,7 @@ indexmap = { version = "2.11.1" } # content digest with rfc8941 structured field values sha2 = { version = "0.10.9", default-features = false } -sfv = { version = "0.10.4" } +sfv = { version = "0.14.0" } # encoding base64 = { version = "0.22.1" } diff --git a/httpsig-hyper/src/hyper_content_digest.rs b/httpsig-hyper/src/hyper_content_digest.rs index 721c641..3c3a9bb 100644 --- a/httpsig-hyper/src/hyper_content_digest.rs +++ b/httpsig-hyper/src/hyper_content_digest.rs @@ -5,9 +5,9 @@ use bytes::Bytes; use http::{Request, Response}; use http_body::Body; use http_body_util::{combinators::BoxBody, BodyExt, Full}; -use sfv::FromStr; use sha2::Digest; use std::future::Future; +use std::str::FromStr; // hyper's http specific extension to generate and verify http signature @@ -209,7 +209,8 @@ async fn extract_content_digest(header_map: &http::HeaderMap) -> HyperDigestResu .get(CONTENT_DIGEST_HEADER) .ok_or(HyperDigestError::NoDigestHeader("No content-digest header".to_string()))? .to_str()?; - let indexmap = sfv::Parser::parse_dictionary(content_digest_header.as_bytes()) + let indexmap = sfv::Parser::new(content_digest_header) + .parse::() .map_err(|e| HyperDigestError::InvalidHeaderValue(e.to_string()))?; if indexmap.len() != 1 { return Err(HyperDigestError::InvalidHeaderValue( @@ -217,12 +218,12 @@ async fn extract_content_digest(header_map: &http::HeaderMap) -> HyperDigestResu )); }; let (cd_type, cd) = indexmap.iter().next().unwrap(); - let cd_type = ContentDigestType::from_str(cd_type) + let cd_type = ContentDigestType::from_str(cd_type.as_str()) .map_err(|e| HyperDigestError::InvalidHeaderValue(format!("Invalid Content-Digest type: {e}")))?; if !matches!( cd, sfv::ListEntry::Item(sfv::Item { - bare_item: sfv::BareItem::ByteSeq(_), + bare_item: sfv::BareItem::ByteSequence(_), .. }) ) { @@ -233,7 +234,7 @@ async fn extract_content_digest(header_map: &http::HeaderMap) -> HyperDigestResu let cd = match cd { sfv::ListEntry::Item(sfv::Item { - bare_item: sfv::BareItem::ByteSeq(cd), + bare_item: sfv::BareItem::ByteSequence(cd), .. }) => cd, _ => unreachable!(), diff --git a/httpsig/Cargo.toml b/httpsig/Cargo.toml index b6de663..e82a4f6 100644 --- a/httpsig/Cargo.toml +++ b/httpsig/Cargo.toml @@ -45,7 +45,7 @@ bytes = { version = "1.10.1" } base64 = { version = "0.22.1" } # for rfc8941 structured field values -sfv = { version = "0.10.4" } +sfv = { version = "0.14.0" } [dev-dependencies] rand-085 = { package = "rand", version = "0.8.5" } # testing only diff --git a/httpsig/src/message_component/component_id.rs b/httpsig/src/message_component/component_id.rs index c8bebac..c55168c 100644 --- a/httpsig/src/message_component/component_id.rs +++ b/httpsig/src/message_component/component_id.rs @@ -35,11 +35,17 @@ impl TryFrom<&str> for HttpMessageComponentId { /// But accept string in the form of `` (without double quotations) when no param is given fn try_from(val: &str) -> HttpSigResult { let val = val.trim(); - let item = if !val.starts_with('"') && !val.ends_with('"') && !val.is_empty() && !val.contains('"') { + let item: sfv::Item = if !val.starts_with('"') && !val.ends_with('"') && !val.is_empty() && !val.contains('"') { // maybe insufficient, but it's enough for now - Parser::parse_item(format!("\"{val}\"").as_bytes()).map_err(|e| HttpSigError::ParseSFVError(e.to_string()))? + Parser::new(format!("\"{val}\"").as_str()) + .parse() + .map_err(|e| HttpSigError::ParseSFVError(e.to_string()))? + // Parser::parse_item(format!("\"{val}\"").as_bytes()).map_err(|e| HttpSigError::ParseSFVError(e.to_string()))? } else { - Parser::parse_item(val.as_bytes()).map_err(|e| HttpSigError::ParseSFVError(e.to_string()))? + Parser::new(val) + .parse() + .map_err(|e| HttpSigError::ParseSFVError(e.to_string()))? + // Parser::parse_item(val.as_bytes()).map_err(|e| HttpSigError::ParseSFVError(e.to_string()))? }; let res = Self { diff --git a/httpsig/src/message_component/component_name.rs b/httpsig/src/message_component/component_name.rs index 916f399..7164157 100644 --- a/httpsig/src/message_component/component_name.rs +++ b/httpsig/src/message_component/component_name.rs @@ -16,7 +16,7 @@ impl TryFrom<&BareItem> for HttpMessageComponentName { fn try_from(value: &BareItem) -> HttpSigResult { match value { BareItem::String(name) => { - if name.starts_with('@') { + if name.as_str().starts_with('@') { Ok(Self::Derived(DerivedComponentName::from(name.as_str()))) } else { Ok(Self::HttpField(name.to_string())) diff --git a/httpsig/src/message_component/component_param.rs b/httpsig/src/message_component/component_param.rs index 831b603..04717ca 100644 --- a/httpsig/src/message_component/component_param.rs +++ b/httpsig/src/message_component/component_param.rs @@ -1,5 +1,5 @@ use crate::error::{HttpSigError, HttpSigResult}; -use sfv::{Parser, SerializeValue}; +use sfv::{FieldType, Parser}; type IndexSet = indexmap::IndexSet; @@ -46,13 +46,13 @@ impl TryFrom<(&str, &sfv::BareItem)> for HttpMessageComponentParam { "tr" => Ok(Self::Tr), "req" => Ok(Self::Req), "name" => { - let name = val.as_str().ok_or(HttpSigError::InvalidComponentParam( + let name = val.as_string().ok_or(HttpSigError::InvalidComponentParam( "Invalid http field param: name".to_string(), ))?; Ok(Self::Name(name.to_string())) } "key" => { - let key = val.as_str().ok_or(HttpSigError::InvalidComponentParam( + let key = val.as_string().ok_or(HttpSigError::InvalidComponentParam( "Invalid http field param: key".to_string(), ))?; Ok(Self::Key(key.to_string())) @@ -106,10 +106,10 @@ pub(super) fn handle_params_sf(field_values: &mut [String]) -> HttpSigResult<()> let parsed_list = field_values .iter() .map(|v| { - if let Ok(list) = Parser::parse_list(v.as_bytes()) { - list.serialize_value() - } else if let Ok(dict) = Parser::parse_dictionary(v.as_bytes()) { - dict.serialize_value() + if let Ok(list) = Parser::new(v).parse::() { + list.serialize().ok_or("Failed to serialize structured field value for sf") + } else if let Ok(dict) = Parser::new(v).parse::() { + dict.serialize().ok_or("Failed to serialize structured field value for sf") } else { Err("invalid structured field value for sf") } @@ -129,7 +129,8 @@ pub(super) fn handle_params_sf(field_values: &mut [String]) -> HttpSigResult<()> pub(super) fn handle_params_key_into(field_values: &[String], key: &str) -> HttpSigResult> { let dicts = field_values .iter() - .map(|v| Parser::parse_dictionary(v.as_bytes())) + .map(|v| Parser::new(v.as_str()).parse() as Result) + // Parser::parse_dictionary(v.as_bytes())) .collect::, _>>() .map_err(|e| HttpSigError::InvalidComponentParam(format!("Failed to parse structured field value: {e}")))?; @@ -138,11 +139,12 @@ pub(super) fn handle_params_key_into(field_values: &[String], key: &str) -> Http .filter_map(|dict| { dict.get(key).map(|v| { let sfvalue: sfv::List = vec![v.clone()]; - sfvalue.serialize_value() + // sfvalue.serialize_value() + sfvalue.serialize() }) }) - .collect::, _>>() - .map_err(|e| HttpSigError::InvalidComponentParam(format!("Failed to serialize structured field value: {e}")))?; + .collect::>>() + .ok_or_else(|| HttpSigError::InvalidComponentParam(format!("Failed to serialize structured field value")))?; Ok(found_entries) } @@ -157,19 +159,19 @@ mod tests { fn parser_test() { // Parsing structured field value of Item type. let item_header_input = "12.445;foo=bar"; - let item = Parser::parse_item(item_header_input.as_bytes()).unwrap(); - assert_eq!(item.serialize_value().unwrap(), item_header_input); + let item = Parser::new(item_header_input).parse::().unwrap(); + assert_eq!(item.serialize(), item_header_input); // Parsing structured field value of List type. let list_header_input = " 1; a=tok, (\"foo\" \"bar\" );baz, ( )"; - let list = Parser::parse_list(list_header_input.as_bytes()).unwrap(); - assert_eq!(list.serialize_value().unwrap(), "1;a=tok, (\"foo\" \"bar\");baz, ()"); + let list = Parser::new(list_header_input).parse::().unwrap(); + assert_eq!(list.serialize().unwrap(), "1;a=tok, (\"foo\" \"bar\");baz, ()"); // Parsing structured field value of Dictionary type. let dict_header_input = "a=?0, b, c; foo=bar, rating=1.5, fruits=(apple pear), d"; - let dict = Parser::parse_dictionary(dict_header_input.as_bytes()).unwrap(); + let dict = Parser::new(dict_header_input).parse::().unwrap(); assert_eq!( - dict.serialize_value().unwrap(), + dict.serialize().unwrap(), "a=?0, b, c;foo=bar, rating=1.5, fruits=(apple pear), d" ); } diff --git a/httpsig/src/signature_base.rs b/httpsig/src/signature_base.rs index 0e21a76..125fcea 100644 --- a/httpsig/src/signature_base.rs +++ b/httpsig/src/signature_base.rs @@ -6,8 +6,8 @@ use crate::{ signature_params::HttpSignatureParams, }; use base64::{engine::general_purpose, Engine as _}; -use rustc_hash::FxBuildHasher; use indexmap::IndexMap; +use rustc_hash::FxBuildHasher; use sfv::{BareItem, Item, ListEntry, Parser}; /// IndexMap of signature name and HttpSignatureHeaders @@ -30,10 +30,16 @@ pub struct HttpSignatureHeaders { impl HttpSignatureHeaders { /// Generates (possibly multiple) HttpSignatureHeaders from signature and signature-input header values pub fn try_parse(signature_header: &str, signature_input_header: &str) -> HttpSigResult { - let signature_input = - Parser::parse_dictionary(signature_input_header.as_bytes()).map_err(|e| HttpSigError::ParseSFVError(e.to_string()))?; - let signature = - Parser::parse_dictionary(signature_header.as_bytes()).map_err(|e| HttpSigError::ParseSFVError(e.to_string()))?; + let signature_input: sfv::Dictionary = Parser::new(signature_input_header) + .parse() + .map_err(|e| HttpSigError::ParseSFVError(e.to_string()))?; + let signature: sfv::Dictionary = Parser::new(signature_header) + .parse() + .map_err(|e| HttpSigError::ParseSFVError(e.to_string()))?; + // let signature_input = + // Parser::parse_dictionary(signature_input_header.as_bytes()).map_err(|e| HttpSigError::ParseSFVError(e.to_string()))?; + // let signature = + // Parser::parse_dictionary(signature_header.as_bytes()).map_err(|e| HttpSigError::ParseSFVError(e.to_string()))?; if signature.len() != signature_input.len() { return Err(HttpSigError::BuildSignatureHeaderError( @@ -50,7 +56,7 @@ impl HttpSignatureHeaders { matches!( v, ListEntry::Item(Item { - bare_item: BareItem::ByteSeq(_), + bare_item: BareItem::ByteSequence(_), .. }) ) @@ -73,7 +79,7 @@ impl HttpSignatureHeaders { let signature_bytes = match signature.get(k) { Some(ListEntry::Item(Item { - bare_item: BareItem::ByteSeq(v), + bare_item: BareItem::ByteSequence(v), .. })) => v, _ => unreachable!(), diff --git a/httpsig/src/signature_params.rs b/httpsig/src/signature_params.rs index c2827ae..892df06 100644 --- a/httpsig/src/signature_params.rs +++ b/httpsig/src/signature_params.rs @@ -7,7 +7,7 @@ use crate::{ }; use base64::{engine::general_purpose, Engine as _}; use rand::Rng; -use sfv::{ListEntry, Parser, SerializeValue}; +use sfv::{FieldType, ListEntry, Parser}; use std::time::{SystemTime, UNIX_EPOCH}; const DEFAULT_DURATION: u64 = 300; @@ -167,9 +167,10 @@ impl TryFrom<&ListEntry> for HttpSignatureParams { .items .iter() .map(|v| { - v.serialize_value() - .map_err(|e| HttpSigError::ParseSFVError(e.to_string())) - .and_then(|v| HttpMessageComponentId::try_from(v.as_str())) + HttpMessageComponentId::try_from(v.serialize().as_str()) + // v.serialize_value() + // .map_err(|e| HttpSigError::ParseSFVError(e.to_string())) + // .and_then(|v| HttpMessageComponentId::try_from(v.as_str())) }) .collect::, _>>()?; @@ -193,12 +194,12 @@ impl TryFrom<&ListEntry> for HttpSignatureParams { .params .iter() .for_each(|(key, bare_item)| match key.as_str() { - "created" => params.created = bare_item.as_int().map(|v| v as u64), - "expires" => params.expires = bare_item.as_int().map(|v| v as u64), - "nonce" => params.nonce = bare_item.as_str().map(|v| v.to_string()), - "alg" => params.alg = bare_item.as_str().map(|v| v.to_string()), - "keyid" => params.keyid = bare_item.as_str().map(|v| v.to_string()), - "tag" => params.tag = bare_item.as_str().map(|v| v.to_string()), + "created" => params.created = bare_item.as_integer().map(|v| v.try_into().unwrap()), + "expires" => params.expires = bare_item.as_integer().map(|v| v.try_into().unwrap()), + "nonce" => params.nonce = bare_item.as_string().map(|v| v.to_string()), + "alg" => params.alg = bare_item.as_string().map(|v| v.to_string()), + "keyid" => params.keyid = bare_item.as_string().map(|v| v.to_string()), + "tag" => params.tag = bare_item.as_string().map(|v| v.to_string()), _ => { error!("Ignore invalid signature parameter: {}", key) } @@ -211,7 +212,10 @@ impl TryFrom<&str> for HttpSignatureParams { type Error = HttpSigError; /// Convert from string to HttpSignatureParams fn try_from(value: &str) -> HttpSigResult { - let sfv_parsed = Parser::parse_list(value.as_bytes()).map_err(|e| HttpSigError::ParseSFVError(e.to_string()))?; + let sfv_parsed: sfv::List = Parser::new(value) + .parse() + .map_err(|e| HttpSigError::ParseSFVError(e.to_string()))?; + // let sfv_parsed = Parser::parse_list(value.as_bytes()).map_err(|e| HttpSigError::ParseSFVError(e.to_string()))?; if sfv_parsed.len() != 1 || !matches!(sfv_parsed[0], ListEntry::InnerList(_)) { return Err(HttpSigError::InvalidSignatureParams("Invalid signature params".to_string())); }