1#![warn(missing_docs)]
4
5use std::cell::RefCell;
6use std::fmt;
7use std::io::prelude::*;
8
9use afire::{
10 middleware::{MiddleResponse, Middleware},
11 Header, Request, Response, Server,
12};
13
14use brotli2;
15use flate2;
16use libflate::deflate;
17
18#[derive(Debug, Clone, Copy)]
20pub enum CompressType {
21 Gzip(u32),
25
26 Deflate,
28
29 Brotli(u32),
33}
34
35#[derive(Debug, Clone, Copy)]
37pub struct Compress {
38 compression: CompressType,
39 threshold: usize,
40}
41
42impl Middleware for Compress {
43 fn post(&mut self, req: Request, res: Response) -> MiddleResponse {
44 if res.data.len() <= self.threshold {
46 return MiddleResponse::Continue;
47 }
48
49 match req.header("Accept-Encoding") {
51 Some(i) => {
52 if !i
53 .split(",")
54 .map(|x| x.trim().to_owned())
55 .collect::<Vec<_>>()
56 .contains(&self.compression.to_string())
57 {
58 return MiddleResponse::Continue;
59 }
60 }
61 None => return MiddleResponse::Continue,
62 }
63
64 let new = match self.compression {
66 CompressType::Gzip(level) => {
67 let mut encoder =
68 flate2::write::GzEncoder::new(Vec::new(), flate2::Compression::new(level));
69 encoder.write_all(&res.data).unwrap();
70 (encoder.finish().unwrap(), "gzip")
71 }
72
73 CompressType::Deflate => {
74 let mut encoder = deflate::Encoder::new(Vec::new());
75 encoder.write_all(&res.data).unwrap();
76 (encoder.finish().into_result().unwrap(), "deflate")
77 }
78
79 CompressType::Brotli(level) => {
80 let encoder = brotli2::read::BrotliEncoder::new(&*res.data, level);
81 (encoder.into_inner().to_vec(), "br")
82 }
83 };
84
85 MiddleResponse::Add(
86 res.bytes(new.0)
87 .header(Header::new("Content-Encoding", new.1))
88 .content(afire::Content::TXT),
89 )
90 }
91}
92
93impl Compress {
94 pub fn new() -> Self {
96 Compress {
97 compression: CompressType::Gzip(6),
98 threshold: 1024,
99 }
100 }
101
102 pub fn threshold(self, threshold: usize) -> Self {
107 Compress { threshold, ..self }
108 }
109
110 pub fn compression(self, compression: CompressType) -> Self {
114 Compress {
115 compression,
116 ..self
117 }
118 }
119
120 pub fn attach(self, server: &mut Server)
122 where
123 Self: Sized + 'static,
124 {
125 server.middleware.push(Box::new(RefCell::new(self)));
126 }
127}
128
129impl fmt::Display for CompressType {
130 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
131 write!(
132 f,
133 "{}",
134 match self {
135 CompressType::Gzip(_) => "gzip",
136 CompressType::Deflate => "deflate",
137 CompressType::Brotli(_) => "br",
138 }
139 )
140 }
141}