httpsig/message_component/
component_param.rs

1use crate::error::{HttpSigError, HttpSigResult};
2use sfv::{FieldType, Parser};
3
4type IndexSet<K> = indexmap::IndexSet<K, rustc_hash::FxBuildHasher>;
5
6/* ---------------------------------------------------------------- */
7#[derive(PartialEq, Eq, Hash, Debug, Clone)]
8/// Http message component parameters that appends with `;` in the signature input
9/// https://datatracker.ietf.org/doc/html/rfc9421#secion-2.1
10pub enum HttpMessageComponentParam {
11  /// sf: https://datatracker.ietf.org/doc/html/rfc9421#section-2.1.1
12  Sf,
13  /// key: https://datatracker.ietf.org/doc/html/rfc9421#section-2.1.2
14  /// This will be encoded to `;key="..."` in the signature input
15  Key(String),
16  /// bs: https://datatracker.ietf.org/doc/html/rfc9421#section-2.1.3
17  Bs,
18  // tr: https://datatracker.ietf.org/doc/html/rfc9421#section-2.1.4
19  Tr,
20  // req: https://datatracker.ietf.org/doc/html/rfc9421#section-2.4
21  Req,
22  // name: https://datatracker.ietf.org/doc/html/rfc9421#name-query-parameters
23  /// This will be encoded to `;name="..."` in the signature input
24  Name(String),
25}
26
27impl From<HttpMessageComponentParam> for String {
28  fn from(val: HttpMessageComponentParam) -> Self {
29    match val {
30      HttpMessageComponentParam::Sf => "sf".to_string(),
31      HttpMessageComponentParam::Key(val) => format!("key=\"{val}\""),
32      HttpMessageComponentParam::Bs => "bs".to_string(),
33      HttpMessageComponentParam::Tr => "tr".to_string(),
34      HttpMessageComponentParam::Req => "req".to_string(),
35      HttpMessageComponentParam::Name(v) => format!("name=\"{v}\""),
36    }
37  }
38}
39
40impl TryFrom<(&str, &sfv::BareItem)> for HttpMessageComponentParam {
41  type Error = HttpSigError;
42  fn try_from((key, val): (&str, &sfv::BareItem)) -> Result<Self, Self::Error> {
43    match key {
44      "sf" => Ok(Self::Sf),
45      "bs" => Ok(Self::Bs),
46      "tr" => Ok(Self::Tr),
47      "req" => Ok(Self::Req),
48      "name" => {
49        let name = val.as_string().ok_or(HttpSigError::InvalidComponentParam(
50          "Invalid http field param: name".to_string(),
51        ))?;
52        Ok(Self::Name(name.to_string()))
53      }
54      "key" => {
55        let key = val.as_string().ok_or(HttpSigError::InvalidComponentParam(
56          "Invalid http field param: key".to_string(),
57        ))?;
58        Ok(Self::Key(key.to_string()))
59      }
60      _ => Err(HttpSigError::InvalidComponentParam(format!(
61        "Invalid http field param: {key}"
62      ))),
63    }
64  }
65}
66
67#[derive(PartialEq, Eq, Debug, Clone)]
68/// Http message component parameters
69pub struct HttpMessageComponentParams(pub IndexSet<HttpMessageComponentParam>);
70
71impl std::hash::Hash for HttpMessageComponentParams {
72  fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
73    let mut params = self.0.iter().map(|v| v.clone().into()).collect::<Vec<String>>();
74    params.sort();
75    params.hash(state);
76  }
77}
78
79impl TryFrom<&sfv::Parameters> for HttpMessageComponentParams {
80  type Error = HttpSigError;
81  fn try_from(val: &sfv::Parameters) -> Result<Self, Self::Error> {
82    let hs = val
83      .iter()
84      .map(|(k, v)| HttpMessageComponentParam::try_from((k.as_str(), v)))
85      .collect::<Result<IndexSet<_>, _>>()?;
86    Ok(Self(hs))
87  }
88}
89impl std::fmt::Display for HttpMessageComponentParams {
90  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91    if !self.0.is_empty() {
92      write!(
93        f,
94        ";{}",
95        self.0.iter().map(|v| v.clone().into()).collect::<Vec<String>>().join(";")
96      )
97    } else {
98      Ok(())
99    }
100  }
101}
102
103/* ---------------------------------------------------------------- */
104/// Handle `sf` parameter
105pub(super) fn handle_params_sf(field_values: &mut [String]) -> HttpSigResult<()> {
106  let parsed_list = field_values
107    .iter()
108    .map(|v| {
109      if let Ok(list) = Parser::new(v).parse::<sfv::List>() {
110        list.serialize().ok_or("Failed to serialize structured field value for sf")
111      } else if let Ok(dict) = Parser::new(v).parse::<sfv::Dictionary>() {
112        dict.serialize().ok_or("Failed to serialize structured field value for sf")
113      } else {
114        Err("invalid structured field value for sf")
115      }
116    })
117    .collect::<Result<Vec<_>, _>>()
118    .map_err(|e| HttpSigError::InvalidComponentParam(format!("Failed to parse structured field value: {e}")))?;
119
120  field_values.iter_mut().zip(parsed_list).for_each(|(v, p)| {
121    *v = p;
122  });
123
124  Ok(())
125}
126
127/* ---------------------------------------------------------------- */
128/// Handle `key` parameter, returns new field values
129pub(super) fn handle_params_key_into(field_values: &[String], key: &str) -> HttpSigResult<Vec<String>> {
130  let dicts = field_values
131    .iter()
132    .map(|v| Parser::new(v.as_str()).parse() as Result<sfv::Dictionary, _>)
133    // Parser::parse_dictionary(v.as_bytes()))
134    .collect::<Result<Vec<_>, _>>()
135    .map_err(|e| HttpSigError::InvalidComponentParam(format!("Failed to parse structured field value: {e}")))?;
136
137  let found_entries = dicts
138    .into_iter()
139    .filter_map(|dict| {
140      dict.get(key).map(|v| {
141        let sfvalue: sfv::List = vec![v.clone()];
142        // sfvalue.serialize_value()
143        sfvalue.serialize()
144      })
145    })
146    .collect::<Option<Vec<_>>>()
147    .ok_or_else(|| HttpSigError::InvalidComponentParam(format!("Failed to serialize structured field value")))?;
148
149  Ok(found_entries)
150}
151
152/* ---------------------------------------------------------------- */
153
154mod tests {
155  #[allow(unused)]
156  use super::*;
157
158  #[test]
159  fn parser_test() {
160    // Parsing structured field value of Item type.
161    let item_header_input = "12.445;foo=bar";
162    let item = Parser::new(item_header_input).parse::<sfv::Item>().unwrap();
163    assert_eq!(item.serialize(), item_header_input);
164
165    // Parsing structured field value of List type.
166    let list_header_input = "  1; a=tok, (\"foo\"   \"bar\" );baz, (  )";
167    let list = Parser::new(list_header_input).parse::<sfv::List>().unwrap();
168    assert_eq!(list.serialize().unwrap(), "1;a=tok, (\"foo\" \"bar\");baz, ()");
169
170    // Parsing structured field value of Dictionary type.
171    let dict_header_input = "a=?0, b, c; foo=bar, rating=1.5, fruits=(apple pear), d";
172    let dict = Parser::new(dict_header_input).parse::<sfv::Dictionary>().unwrap();
173    assert_eq!(
174      dict.serialize().unwrap(),
175      "a=?0, b, c;foo=bar, rating=1.5, fruits=(apple pear), d"
176    );
177  }
178}