Skip to main content

rialo_types/
headers.rs

1// Copyright (c) Subzero Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::fmt;
5
6use borsh::{BorshDeserialize, BorshSerialize};
7use serde::{Deserialize, Serialize};
8use thiserror::Error;
9
10use crate::RexValue;
11
12type HeadersInnerType = std::collections::BTreeMap<String, RexValue>;
13
14/// A structure representing the headers of an HTTP request.
15#[derive(
16    Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default, BorshSerialize, BorshDeserialize,
17)]
18pub struct Headers(HeadersInnerType);
19
20#[derive(Error, Debug)]
21pub enum HeadersError {
22    #[error("Failed to decode {encoding} for parameter '{param}': {reason}")]
23    DecodeError {
24        param: String,
25        encoding: String,
26        reason: String,
27    },
28
29    #[error("JSON parsing error: {reason}")]
30    JsonError { reason: String },
31}
32
33impl Headers {
34    /// Creates a new Headers instance from the provided HeadersType map.
35    ///
36    /// # Arguments
37    /// * `headers` - A HeadersInnerType containing header key-value pairs
38    ///
39    /// # Returns
40    /// A new Headers instance wrapping the provided map
41    pub fn new(headers: HeadersInnerType) -> Self {
42        Headers(headers)
43    }
44
45    /// Inserts a plain text header value into the headers map.
46    ///
47    /// This method adds a header with a plain (unencrypted) value to the headers collection.
48    ///
49    /// # Arguments
50    /// * `key` - The header name
51    /// * `value` - The plain text header value
52    pub fn insert_plain(&mut self, key: String, value: String) {
53        self.0.insert(key, RexValue::Plain(value.into_bytes()));
54    }
55
56    /// Inserts an encrypted header value into the headers map.
57    ///
58    /// This method adds a header with an encrypted value to the headers collection.
59    /// The encrypted value should be base64-encoded ciphertext that can be decrypted
60    /// within the TEE using the stored secret key.
61    ///
62    /// # Arguments
63    /// * `key` - The header name
64    /// * `encrypted_value` - The base64-encoded encrypted header value
65    pub fn insert_encrypted(&mut self, key: String, encrypted_value: String) {
66        self.0
67            .insert(key, RexValue::Encrypted(encrypted_value.into_bytes()));
68    }
69
70    /// Inserts encrypted raw bytes as a header value.
71    ///
72    /// Use this when you have raw ciphertext bytes (e.g., from HPKE encryption)
73    /// rather than a base64-encoded string. The bytes will be internally base64-encoded
74    /// to match the expected format for TEE decryption.
75    ///
76    /// # Arguments
77    /// * `key` - The header name
78    /// * `ciphertext` - The raw encrypted bytes
79    #[cfg(feature = "non-pdk")]
80    pub fn insert_encrypted_bytes(&mut self, key: String, ciphertext: Vec<u8>) {
81        use fastcrypto::encoding::{Base64, Encoding};
82        // Base64-encode the raw ciphertext so it can be decoded by the TEE's decode_headers
83        let encoded = Base64::encode(&ciphertext);
84        self.0
85            .insert(key, RexValue::Encrypted(encoded.into_bytes()));
86    }
87
88    /// Provide a public accessor to move out the inner map.
89    /// This enables other crates to consume the headers safely.
90    ///
91    /// # Returns
92    /// The inner HeadersInnerType map, consuming the Headers instance
93    pub fn into_inner(self) -> HeadersInnerType {
94        self.0
95    }
96
97    /// Parses base64-encoded headers string into a Headers instance.
98    ///
99    /// This method decodes a base64-encoded string containing JSON headers data
100    /// and deserializes it into a Headers structure.
101    ///
102    /// # Arguments
103    /// * `headers_b64` - Base64-encoded string containing JSON headers
104    ///
105    /// # Returns
106    /// * `Ok(Headers)` - Successfully parsed headers
107    /// * `Err(HeadersError)` - If base64 decoding, UTF-8 conversion, or JSON parsing fails
108    #[cfg(feature = "non-pdk")]
109    pub fn parse_b64(headers_b64: &str) -> Result<Self, HeadersError> {
110        use fastcrypto::{encoding, encoding::Encoding};
111        let headers_bytes =
112            encoding::Base64::decode(headers_b64).map_err(|err| HeadersError::DecodeError {
113                param: "headers".to_string(),
114                encoding: "base64".to_string(),
115                reason: err.to_string(),
116            })?;
117
118        let headers_str =
119            std::str::from_utf8(&headers_bytes).map_err(|err| HeadersError::DecodeError {
120                param: "headers".to_string(),
121                encoding: "utf-8".to_string(),
122                reason: err.to_string(),
123            })?;
124
125        let parsed_headers: Headers =
126            serde_json::from_str(headers_str).map_err(|error| HeadersError::JsonError {
127                reason: format!("Invalid headers JSON: {error}"),
128            })?;
129
130        Ok(parsed_headers)
131    }
132}
133
134impl std::str::FromStr for Headers {
135    type Err = String;
136
137    fn from_str(s: &str) -> Result<Self, Self::Err> {
138        let headers: HeadersInnerType = serde_json::from_str(s).map_err(|e| e.to_string())?;
139        Ok(Headers(headers))
140    }
141}
142
143impl fmt::Display for Headers {
144    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145        write!(f, "{}", serde_json::to_string(&self.0).unwrap())
146    }
147}
148
149#[cfg(test)]
150mod tests {
151    use std::str::FromStr;
152
153    use super::*;
154
155    #[test]
156    fn test_headers_from_str_json() {
157        let json = r#"{"A": {"Plain":[66]}}"#;
158        let headers = Headers::from_str(json).unwrap();
159        let got = headers.0.into_iter().collect::<Vec<(_, RexValue)>>();
160        let expected = vec![("A".to_string(), RexValue::plain_string("B"))];
161        assert_eq!(got, expected);
162    }
163
164    #[test]
165    fn test_headers_from_str_with_string_values() {
166        let json = r#"{"Mynonce": {"Plain":"abc123"}}"#;
167        let headers = Headers::from_str(json).unwrap();
168        let got = headers.0.into_iter().collect::<Vec<(_, RexValue)>>();
169        let expected = vec![("Mynonce".to_string(), RexValue::plain_string("abc123"))];
170        assert_eq!(got, expected);
171    }
172}