kutil_http/cache/
body.rs

1use super::{configuration::*, weight::*};
2
3use {
4    ::bytes::*,
5    kutil_std::collections::*,
6    kutil_transcoding::{bytes::*, *},
7    std::io,
8};
9
10//
11// CachedBody
12//
13
14/// Cached HTTP response body.
15#[derive(Clone, Debug)]
16pub struct CachedBody {
17    /// Representations.
18    pub representations: FastHashMap<Encoding, Bytes>,
19}
20
21impl CachedBody {
22    /// Constructor.
23    pub fn new() -> Self {
24        Self { representations: FastHashMap::new() }
25    }
26
27    /// Constructor with an initial representation.
28    ///
29    /// If the `preferred_encoding` is different from the `encoding` then we will reencode.
30    ///
31    /// If an [Identity](Encoding::Identity) is created during this reencoding then it will also be
32    /// stored if `keep_identity_encoding` is true.
33    pub async fn new_with(
34        bytes: Bytes,
35        encoding: Encoding,
36        preferred_encoding: Encoding,
37        configuration: &EncodingConfiguration,
38    ) -> io::Result<Self> {
39        let mut representations = FastHashMap::new();
40
41        if preferred_encoding == encoding {
42            // It's already in the preferred encoding
43            representations.insert(preferred_encoding, bytes);
44        } else if encoding == Encoding::Identity {
45            tracing::debug!("encoding to {}", preferred_encoding);
46
47            let encoded_bytes = bytes.encode(&preferred_encoding).await?;
48
49            representations.insert(preferred_encoding, encoded_bytes);
50            if configuration.keep_identity_encoding {
51                representations.insert(Encoding::Identity, bytes);
52            }
53        } else if preferred_encoding == Encoding::Identity {
54            tracing::debug!("decoding from {}", encoding);
55
56            let identity_bytes = bytes.decode(&encoding).await?;
57
58            representations.insert(Encoding::Identity, identity_bytes);
59        } else {
60            tracing::debug!("reencoding from {} to {}", encoding, preferred_encoding);
61
62            let identity_bytes = bytes.decode(&encoding).await?;
63            let encoded_bytes = identity_bytes.encode(&preferred_encoding).await?;
64
65            representations.insert(preferred_encoding, encoded_bytes);
66            if configuration.keep_identity_encoding {
67                representations.insert(Encoding::Identity, identity_bytes);
68            }
69        }
70
71        Ok(Self { representations })
72    }
73
74    /// Returns the body [Bytes] in the specified encoding.
75    ///
76    /// If we don't have the specified encoding then we will reencode from another encoding,
77    /// storing the result so that we won't have to encode it again.
78    ///
79    /// If an [Identity](Encoding::Identity) is created during this reencoding then it will also be
80    /// stored if `keep_identity_encoding` is true.
81    ///
82    /// Returns a modified clone if reencoding caused a new encoding to be stored. Note that
83    /// cloning should be cheap due to our use of [Bytes].
84    pub async fn get(
85        &self,
86        encoding: &Encoding,
87        configuration: &EncodingConfiguration,
88    ) -> io::Result<(Bytes, Option<Self>)> {
89        match self.representations.get(encoding) {
90            Some(bytes) => Ok((bytes.clone(), None)),
91
92            None => match encoding {
93                Encoding::Identity => {
94                    // Decode
95                    for from_encoding in ENCODINGS_BY_DECODING_COST {
96                        if let Some(bytes) = self.representations.get(from_encoding) {
97                            tracing::debug!("decoding from {}", from_encoding);
98
99                            let identity_bytes = bytes.decode(from_encoding).await?;
100
101                            let mut modified = self.clone();
102                            modified.representations.insert(Encoding::Identity, identity_bytes.clone());
103
104                            return Ok((identity_bytes, Some(modified)));
105                        }
106                    }
107
108                    // This should never happen (but we don't want to panic here!)
109                    tracing::error!("no encodings");
110                    Ok((Bytes::new(), None))
111                }
112
113                to_encoding => {
114                    // Reencode
115                    if let Some(identity_bytes) = self.representations.get(&Encoding::Identity) {
116                        tracing::debug!("encoding to {}", to_encoding);
117
118                        let bytes = identity_bytes.encode(to_encoding).await?;
119
120                        let mut modified = self.clone();
121                        modified.representations.insert(to_encoding.clone(), bytes.clone());
122
123                        Ok((bytes, Some(modified)))
124                    } else {
125                        for from_encoding in ENCODINGS_BY_DECODING_COST {
126                            if let Some(bytes) = self.representations.get(from_encoding) {
127                                tracing::debug!("reencoding from {} to {}", from_encoding, to_encoding);
128
129                                let identity_bytes = bytes.decode(from_encoding).await?;
130                                let bytes = identity_bytes.encode(to_encoding).await?;
131
132                                let mut modified = self.clone();
133                                if configuration.keep_identity_encoding {
134                                    modified.representations.insert(Encoding::Identity, identity_bytes);
135                                }
136                                modified.representations.insert(to_encoding.clone(), bytes.clone());
137
138                                return Ok((bytes, Some(modified)));
139                            }
140                        }
141
142                        // This should never happen (but we don't want to panic here!)
143                        tracing::error!("no encodings");
144                        Ok((Bytes::new(), None))
145                    }
146                }
147            },
148        }
149    }
150}
151
152impl CacheWeight for CachedBody {
153    fn cache_weight(&self) -> usize {
154        const SELF_SIZE: usize = size_of::<CachedBody>();
155        const ENTRY_SIZE: usize = size_of::<Encoding>() + size_of::<Bytes>();
156
157        let mut size = SELF_SIZE;
158
159        for bytes in self.representations.values() {
160            size += ENTRY_SIZE + bytes.len();
161        }
162
163        size
164    }
165}