cratestack_cose/call/
payload.rs1use std::borrow::Cow;
9
10use cratestack_core::{
11 Binding, CratestackError, DEFAULT_PAYLOAD_MEDIA_TYPE, PAYLOAD_ACCEPT_HEADER,
12 PAYLOAD_TYPE_HEADER, ResponseBinding, is_sealable_payload_type, parse_payload_accept,
13 parse_payload_type, request_digest,
14};
15
16use super::CallBinding;
17
18impl CallBinding {
19 pub fn request_payload_type(&self) -> &str {
22 self.payload_type
23 .as_deref()
24 .unwrap_or(DEFAULT_PAYLOAD_MEDIA_TYPE)
25 }
26
27 pub fn accepted_payload_types(&self) -> Vec<&str> {
29 match &self.payload_accept {
30 Some(types) => types.iter().map(String::as_str).collect(),
31 None => vec![DEFAULT_PAYLOAD_MEDIA_TYPE],
32 }
33 }
34
35 pub fn validate(&self) -> Result<(), CratestackError> {
40 let payload_type = self.request_payload_type();
41 if !is_sealable_payload_type(payload_type) {
42 return Err(CratestackError::Validation(format!(
43 "a COSE envelope cannot wrap a payload of type {payload_type:?}: a payload type \
44 is a lowercase type/subtype without parameters, and never an envelope, a \
45 stream or a multipart body"
46 )));
47 }
48 let joined = self.accepted_payload_types().join(", ");
49 let accept = parse_payload_accept(joined.as_bytes())
50 .map_err(|error| CratestackError::Validation(error.to_string()))?;
51 match accept.iter().find(|entry| !is_sealable_payload_type(entry)) {
52 Some(entry) => Err(CratestackError::Validation(format!(
53 "a COSE envelope cannot carry an answer of type {entry:?}"
54 ))),
55 None => Ok(()),
56 }
57 }
58
59 pub fn selector_headers(&self) -> Vec<(&'static str, String)> {
66 let mut headers = Vec::new();
67 if self.request_payload_type() != DEFAULT_PAYLOAD_MEDIA_TYPE {
68 headers.push((PAYLOAD_TYPE_HEADER, self.request_payload_type().to_owned()));
69 }
70 let accept = self.accepted_payload_types();
71 if accept != [DEFAULT_PAYLOAD_MEDIA_TYPE] {
72 headers.push((PAYLOAD_ACCEPT_HEADER, accept.join(", ")));
73 }
74 headers
75 }
76
77 pub fn response_for(
87 &self,
88 sealed_request: &[u8],
89 status: u16,
90 response_type: Option<&str>,
91 ) -> Result<Binding<'_>, CratestackError> {
92 let named = match response_type {
93 None => DEFAULT_PAYLOAD_MEDIA_TYPE,
94 Some(text) => parse_payload_type(text.as_bytes()).map_err(|_| unexpected())?,
95 };
96 let accepted = self.accepted_payload_types();
97 let Some(&payload) = accepted.iter().find(|entry| **entry == named) else {
98 return Err(unexpected());
99 };
100 let mut binding = self.request();
101 binding.payload_media_type = Cow::Owned(payload.to_owned());
102 binding.response = Some(ResponseBinding {
103 request: request_digest(sealed_request),
104 status,
105 });
106 Ok(binding)
107 }
108}
109
110fn unexpected() -> CratestackError {
113 CratestackError::Unauthorized(crate::UNAUTHENTICATED.to_owned())
114}