Skip to main content

tower_http_cache_plus/cache/
body.rs

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