http_msgsign/components/
derive.rs1use std::fmt::Display;
2
3use http::{Request, Response};
4
5use crate::components::NameType;
6use crate::components::params::Serializer;
7use crate::errors::{HttpComponentError, InvalidDerivedComponent, InvalidFormat};
8
9#[derive(Debug, Clone, Eq, PartialEq, Hash)]
11pub enum Derive {
12 Method,
13 TargetUri,
14 Authority,
15 Scheme,
16 RequestTarget,
17 Path,
18 Query,
19 QueryParam,
20 Status,
21}
22
23impl AsRef<str> for Derive {
24 fn as_ref(&self) -> &str {
25 match self {
26 Derive::Method => "@method",
27 Derive::TargetUri => "@target-uri",
28 Derive::Authority => "@authority",
29 Derive::Scheme => "@scheme",
30 Derive::RequestTarget => "@request-target",
31 Derive::Path => "@path",
32 Derive::Query => "@query",
33 Derive::QueryParam => "@query-param",
34 Derive::Status => "@status",
35 }
36 }
37}
38
39impl Display for Derive {
41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42 write!(f, "\"{}\"", self.as_ref())
43 }
44}
45
46impl From<Derive> for NameType {
47 fn from(derived: Derive) -> Self {
48 NameType::Derived(derived)
49 }
50}
51
52impl Derive {
53 pub fn seek_request<B>(
55 &self,
56 request: &Request<B>,
57 params: &Serializer,
58 ) -> Result<Option<String>, HttpComponentError> {
59 Ok(match self {
60 Derive::Method => Some(request.method().to_string()),
61 Derive::TargetUri => Some(request.uri().to_string()),
62 Derive::Authority => request
63 .uri()
64 .authority()
65 .map(|authority| authority.to_string()),
66 Derive::Scheme => request.uri().scheme().map(|scheme| scheme.to_string()),
67 Derive::RequestTarget => request
68 .uri()
69 .path_and_query()
70 .map(|req_target| req_target.to_string()),
71 Derive::Path => Some(request.uri().path().to_string()),
72 Derive::Query => request.uri().query().map(|query| query.to_string()),
73 Derive::QueryParam => extract_query_value(params, request)?.map(|val| val.to_string()),
74 _ => {
75 return Err(HttpComponentError::UnsupportedComponent(self.to_string()));
76 }
77 })
78 }
79
80 pub fn seek_response<B>(
81 &self,
82 response: &Response<B>,
83 ) -> Result<Option<String>, HttpComponentError> {
84 Ok(match self {
85 Derive::Status => Some(response.status().as_u16().to_string()),
86 _ => {
87 return Err(HttpComponentError::UnsupportedComponent(self.to_string()));
88 }
89 })
90 }
91}
92
93impl TryFrom<sfv::BareItem> for Derive {
94 type Error = InvalidFormat;
95
96 fn try_from(item: sfv::BareItem) -> Result<Self, Self::Error> {
97 let sfv::BareItem::String(ident) = item else {
98 return Err(InvalidFormat::String);
99 };
100
101 Ok(match ident.as_str() {
102 "@method" => Derive::Method,
103 "@target-uri" => Derive::TargetUri,
104 "@authority" => Derive::Authority,
105 "@scheme" => Derive::Scheme,
106 "@request-target" => Derive::RequestTarget,
107 "@path" => Derive::Path,
108 "@query" => Derive::Query,
109 "@query-param" => Derive::QueryParam,
110 "@status" => Derive::Status,
111 _ => return Err(InvalidDerivedComponent)?,
112 })
113 }
114}
115
116impl Derive {
117 pub fn contains(other: &sfv::BareItem) -> bool {
118 if let sfv::BareItem::String(ident) = other {
119 let ident = ident.as_str();
120 ident == "@method"
121 || ident == "@target-uri"
122 || ident == "@authority"
123 || ident == "@scheme"
124 || ident == "@request-target"
125 || ident == "@path"
126 || ident == "@query"
127 || ident == "@query-param"
128 || ident == "@status"
129 } else {
130 false
131 }
132 }
133}
134
135fn extract_query_value<'a, B>(
136 params: &'a Serializer,
137 req: &'a Request<B>,
138) -> Result<Option<&'a str>, HttpComponentError> {
139 let Some(require) = params.name() else {
140 return Err(HttpComponentError::UnmetRequirement {
141 reason: "`@query-param` must have a `name` parameter. https://datatracker.ietf.org/doc/html/rfc9421#section-2.2.8-1",
142 });
143 };
144
145 let Some(query) = req.uri().query().map(|params| {
146 params
147 .split("&")
148 .map(|kv| kv.split("=").collect::<Vec<_>>())
149 .collect::<Vec<_>>()
150 }) else {
151 return Ok(None);
152 };
153 let value = query.iter().find(|kv| kv[0] == require).map(|kv| kv[1]);
154 Ok(value.to_owned())
155}