water_http/server/encoding/
mod.rs

1use std::borrow::Cow;
2use std::future::Future;
3use std::io::Write;
4use std::pin::Pin;
5use flate2::write::{GzEncoder,DeflateEncoder};
6/// for providing encoding configurations for response data
7#[derive(Debug)]
8pub struct EncodingConfigurations {
9    pub(crate) logic:EncodingLogic,
10    /// - this framework support encoding with all encoding algorithms
11    /// ['zstd,Gzip,Deflate,Brotli'] so the response will be compressed with one of these
12    /// algorithms depending on the threshold of the data you need to send
13    /// so the default value is 4000000 which is approximately [4 MB ]
14    /// so if your server is very close to your clients leave this value as default but
15    /// if your server is a little far from your client then try to decrease this threshold
16    /// to get the best response latency
17    /// also notice that when you send a custom headers you should implement this encoding manually
18    pub threshold_for_encoding_response:usize,
19
20}
21
22impl EncodingConfigurations {
23
24    pub (crate) fn is_not_none(&self)->bool {self.logic.is_not_none()}
25
26    /// creating new [EncodingConfigurations] with default values
27    /// logic : [EncodingLogic::None]
28    /// threshold: 4_000_000
29    pub fn default()->Self{
30        EncodingConfigurations {
31            logic:EncodingLogic::None,
32            threshold_for_encoding_response:4_000_000
33        }
34    }
35
36
37    /// for setting new logic [EncodingLogic]
38    pub fn set_logic(&mut self,logic:EncodingLogic){
39        self.logic = logic;
40    }
41
42
43    /// for setting new threshold
44    pub fn set_threshold(&mut self,max:usize){
45        self.threshold_for_encoding_response = max;
46    }
47
48
49    pub (crate) async fn encode(&self,accept_encoding:Cow<'_, str>,data:&[u8])->
50                                                                              Option<EncodedData> {
51        let accept_encoding = accept_encoding.to_lowercase();
52        match self.logic {
53            EncodingLogic::All => {return self.default_encode(accept_encoding,data).await}
54            EncodingLogic::Default => {return self.default_encode(accept_encoding,data).await}
55            EncodingLogic::Zstd => {
56                if accept_encoding.contains("zstd") {
57                    let data = zstd_encode(data);
58                    if let Some(data) = data {
59                        return Some(
60                            EncodedData::new(
61                                EncodingLogic::Zstd,
62                                data
63                            )
64                        )
65                    }
66                }
67            }
68            EncodingLogic::Brotli => {
69                if accept_encoding.contains("br") {
70                    let data = brotli_encode(data);
71                    if let Some(data) = data {
72                        return Some(
73                            EncodedData::new(
74                                EncodingLogic::Brotli,
75                                data
76                            )
77                        )
78                    }
79                }
80            }
81            EncodingLogic::Gzip => {
82                if accept_encoding.contains("gzip") {
83                    let data = gzip_encode(data);
84                    if let Some(data) = data {
85                        return Some(
86                            EncodedData::new(
87                                EncodingLogic::Gzip,
88                                data
89                            )
90                        )
91                    }
92                }
93            }
94            EncodingLogic::Deflate => {
95                if accept_encoding.contains("deflate") {
96                    let data = deflate_encode(data);
97                    if let Some(data) = data {
98                        return Some(
99                            EncodedData::new(
100                                EncodingLogic::Default,
101                                data
102                            )
103                        )
104                    }
105                }
106            }
107            EncodingLogic::Lz4 => {
108                if accept_encoding.contains("lz4") {
109                    let data = lz4_encode(data);
110                    if let Some(data) = data {
111                        return Some(
112                            EncodedData::new(
113                                EncodingLogic::Lz4,
114                                data
115                            )
116                        )
117                    }
118                }
119            }
120            EncodingLogic::Bzip2 => {
121                if accept_encoding.contains("bzip2") {
122                    let data = b2zip_encode(data);
123                    if let Some(data) = data {
124                        return Some(
125                            EncodedData::new(
126                                EncodingLogic::Bzip2,
127                                data
128                            )
129                        )
130                    }
131                }
132            }
133            EncodingLogic::Snappy => {
134                if accept_encoding.contains("snappy") {
135                    let data = b2zip_encode(data);
136                    if let Some(data) = data {
137                        return Some(
138                            EncodedData::new(
139                                EncodingLogic::Snappy,
140                                data
141                            )
142                        )
143                    }
144                }
145            }
146            EncodingLogic::None => {return None}
147            EncodingLogic::Custom(callback) => {
148                if let Ok(res) = callback(&accept_encoding,data).await {
149                    return Some(res)
150                }
151                return None;
152            }
153        }
154        None
155    }
156    pub (crate) async fn default_encode(&self,accept_encoding:String,data:&[u8])->Option<EncodedData>{
157        let logic = match Self::pick_best_encoding(&accept_encoding) {
158            Some(d)=>{d}
159            _ => { return None}
160        };
161         match logic {
162            EncodingLogic::Zstd => {
163                if accept_encoding.contains("zstd") {
164                    let data = zstd_encode(data);
165                    if let Some(data) = data {
166                        return Some(
167                            EncodedData::new(
168                                EncodingLogic::Zstd,
169                                data
170                            )
171                        )
172                    }
173                }
174            }
175            EncodingLogic::Brotli => {
176                if accept_encoding.contains("br") {
177                    let data = brotli_encode(data);
178                    if let Some(data) = data {
179                        return Some(
180                            EncodedData::new(
181                                EncodingLogic::Brotli,
182                                data
183                            )
184                        )
185                    }
186                }
187            }
188            EncodingLogic::Gzip => {
189                if accept_encoding.contains("gzip") {
190                    let data = gzip_encode(data);
191                    if let Some(data) = data {
192                        return Some(
193                            EncodedData::new(
194                                EncodingLogic::Gzip,
195                                data
196                            )
197                        )
198                    }
199                }
200            }
201            EncodingLogic::Deflate => {
202                if accept_encoding.contains("deflate") {
203                    let data = deflate_encode(data);
204                    if let Some(data) = data {
205                        return Some(
206                            EncodedData::new(
207                                EncodingLogic::Default,
208                                data
209                            )
210                        )
211                    }
212                }
213            }
214            EncodingLogic::Lz4 => {
215                if accept_encoding.contains("lz4") {
216                    let data = lz4_encode(data);
217                    if let Some(data) = data {
218                        return Some(
219                            EncodedData::new(
220                                EncodingLogic::Lz4,
221                                data
222                            )
223                        )
224                    }
225                }
226            }
227            EncodingLogic::Bzip2 => {
228                if accept_encoding.contains("bzip2") {
229                    let data = b2zip_encode(data);
230                    if let Some(data) = data {
231                        return Some(
232                            EncodedData::new(
233                                EncodingLogic::Bzip2,
234                                data
235                            )
236                        )
237                    }
238                }
239            }
240            EncodingLogic::Snappy => {
241                if accept_encoding.contains("snappy") {
242                    let data = snappy_encode(data);
243                    if let Some(data) = data {
244                        return Some(
245                            EncodedData::new(
246                                EncodingLogic::Snappy,
247                                data
248                            )
249                        )
250                    }
251                }
252            }
253            _ => {}
254
255        }
256        None
257    }
258    fn pick_best_encoding(header: &str) -> Option<EncodingLogic> {
259        let supported_encodings = vec!["br", "zstd", "gzip", "deflate", "lz4", "bzip2", "snappy"];
260        for encoding in supported_encodings {
261            if header.contains(encoding) {
262                return match encoding {
263                    "br" => Some(EncodingLogic::Brotli),
264                    "zstd" => Some(EncodingLogic::Zstd),
265                    "gzip" => Some(EncodingLogic::Gzip),
266                    "deflate" => Some(EncodingLogic::Deflate),
267                    "lz4" => Some(EncodingLogic::Lz4),
268                    "bzip2" => Some(EncodingLogic::Bzip2),
269                    "snappy" => Some(EncodingLogic::Snappy),
270                    _ => None,
271                };
272            }
273        }
274        None
275    }
276
277}
278
279/// for encoding configurations
280#[derive(Debug)]
281pub enum EncodingLogic{
282    /// to support all encoding algorithms
283    All,
284    /// works as well as [`EncodingLogic::All`] option
285    Default,
286    /// for providing zstd encoding only
287    Zstd,
288    /// for providing brotli encoding only
289    Brotli,
290    /// for providing gzip encoding only
291    Gzip,
292    /// for providing deflate encoding only
293    Deflate,
294    /// for providing Snappy encoding only
295    Snappy,
296    /// for providing Bzip2 encoding only
297    Bzip2,
298    /// for providing Lz4 encoding only
299    Lz4,
300    /// to never encode response
301    None,
302    /// when custom creating encoding logic
303    Custom(fn (&str,&[u8])->Pin<Box<dyn Future<Output=Result<EncodedData,()>> + Send>>)
304}
305
306
307impl EncodingLogic {
308
309    pub (crate) fn is_not_none(&self)->bool{
310        if let EncodingLogic::None = self {return false}
311        return  true
312    }
313
314
315    pub (crate) fn content_encoding(&self)->Option<&str>{
316        match self {
317
318            EncodingLogic::Zstd => {Some("zstd")}
319            EncodingLogic::Brotli => {Some("br")}
320            EncodingLogic::Gzip => {Some("gzip")}
321            EncodingLogic::Deflate => {Some("deflate")}
322            EncodingLogic::Snappy => {Some("snappy")}
323            EncodingLogic::Bzip2 => {Some("bzip2")}
324            EncodingLogic::Lz4 => {Some("lz4")}
325            _ =>{None}
326        }
327
328    }
329
330}
331
332
333/// for providing encoding generic use cases
334pub struct EncodedData {
335    pub(crate) logic:String,
336    pub(crate) data:Vec<u8>
337}
338
339
340impl EncodedData {
341
342    pub (crate) fn new(logic:EncodingLogic,data:Vec<u8>)->Self{
343        Self {
344            logic:logic.content_encoding().unwrap_or("").to_owned(),
345            data
346        }
347    }
348}
349unsafe impl  Send for EncodedData {}
350unsafe impl Send for EncodingLogic {}
351unsafe impl Send for EncodingConfigurations {}
352
353fn gzip_encode(data:&[u8])->Option<Vec<u8>>{
354    let mut encoder = GzEncoder::new(Vec::new(),flate2::Compression::best());
355    if encoder.write_all(data).is_err() {return  None;}
356    if let Ok(res) = encoder.finish() { return  Some(res)}
357    None
358}
359fn deflate_encode(data:&[u8])->Option<Vec<u8>>{
360    let mut encoder = DeflateEncoder::new(Vec::new(),flate2::Compression::best());
361    if encoder.write_all(data).is_err() {return  None;}
362    if let Ok(res) = encoder.finish() { return  Some(res)}
363    None
364}
365fn zstd_encode(data:&[u8])->Option<Vec<u8>>{
366    let mut encoder =
367    match  zstd::stream::Encoder::new(Vec::new(),3) {
368        Ok(r) => {r}
369        Err(_) => { return None}
370    };
371    if encoder.write_all(data).is_err() {return  None}
372    if let Ok(res) = encoder.finish() {
373        return Some(res)
374    }
375    None
376}
377fn brotli_encode(data:&[u8])->Option<Vec<u8>>{
378    let mut encoder = brotli::CompressorWriter::new(Vec::new(), 4096, 5, 22);
379    if encoder.write_all(data).is_err() {return None}
380    Some(encoder.into_inner())
381 }
382fn snappy_encode(data:&[u8])->Option<Vec<u8>> {
383    let mut encoder = snap::write::FrameEncoder::new(Vec::new());
384    if encoder.write_all(data).is_err() { return None}
385    if let Ok(res) = encoder.into_inner() {
386        return Some(res)
387    }
388    return None;
389}
390fn lz4_encode(data: &[u8]) -> Option<Vec<u8>> {
391    let mut encoder = lz4::EncoderBuilder::new().build(Vec::new()).expect("Failed to create LZ4 encoder");
392    if let Err(_) = encoder.write_all(data) { return None}
393    let (compressed, result) = encoder.finish();
394    if let Err(_) = result { return None}
395    Some(compressed)
396}
397
398fn b2zip_encode(data:&[u8])->Option<Vec<u8>>{
399    let mut encoder = bzip2::write::BzEncoder::new(Vec::new(),bzip2::Compression::best());
400    if encoder.write_all(data).is_err() { return None}
401    if let Ok(res) = encoder.finish() {
402        return  Some(res);
403    }
404    None
405}