bbox_core/
tile_response.rs1use actix_web::http::header::{
2 self, HeaderMap, HeaderValue, TryIntoHeaderPair, TryIntoHeaderValue,
3};
4use flate2::{read::GzDecoder, read::GzEncoder, Compression as GzCompression};
5use std::io::{Cursor, Read};
6
7#[derive(Clone, PartialEq, Debug)]
9pub enum Compression {
10 None,
12 Gzip,
13 }
16
17pub struct TileResponse {
19 headers: HeaderMap,
20 pub(crate) body: Box<dyn Read + Send + Sync>,
21}
22
23pub struct TileResponseData {
25 headers: HeaderMap,
26 pub body: Vec<u8>,
27}
28
29impl TileResponse {
30 pub fn new() -> Self {
31 TileResponse {
32 headers: HeaderMap::new(),
33 body: Box::new(std::io::empty()),
34 }
35 }
36 pub fn set_content_type<V: TryIntoHeaderValue>(&mut self, value: V) -> &mut Self {
38 if let Ok(value) = value.try_into_value() {
39 self.headers.insert(header::CONTENT_TYPE, value);
40 }
41 self
42 }
43 pub fn insert_header(&mut self, header: impl TryIntoHeaderPair) -> &mut Self {
45 if let Ok((key, value)) = header.try_into_pair() {
46 self.headers.insert(key, value);
47 }
48 self
49 }
50 pub fn set_headers(&mut self, headers: &HeaderMap) -> &mut Self {
51 for (key, value) in headers {
52 self.insert_header((key, value));
53 }
54 self
55 }
56 pub fn with_body(mut self, body: Box<dyn Read + Send + Sync>) -> TileResponse {
57 self.body = body;
58 self
59 }
60 pub fn with_compression(mut self, compression: &Compression) -> TileResponse {
62 match (self.compression(), compression) {
63 (Compression::None, Compression::Gzip) => {
64 let gz = GzEncoder::new(self.body, GzCompression::fast());
65 self.body = Box::new(gz);
66 self.insert_header(("Content-Encoding", "gzip"));
67 }
68 (Compression::Gzip, Compression::None) => {
69 let gz = GzDecoder::new(self.body);
70 self.body = Box::new(gz);
71 self.headers.remove(header::CONTENT_ENCODING);
72 }
73 _ => {}
74 }
75 self
76 }
77 pub fn content_type(&self) -> Option<&HeaderValue> {
78 self.headers.get(header::CONTENT_TYPE)
79 }
80 pub fn compression(&self) -> Compression {
81 match self.headers.get(header::CONTENT_ENCODING) {
82 Some(v) if v == HeaderValue::from_static("gzip") => Compression::Gzip,
83 _ => Compression::None,
84 }
85 }
86 pub fn headers(&self) -> &HeaderMap {
87 &self.headers
88 }
89 pub fn read_bytes(
91 mut self,
92 compression: &Compression,
93 ) -> Result<TileResponseData, std::io::Error> {
94 let mut response = TileResponseData {
95 headers: self.headers,
96 body: Vec::new(),
97 };
98 match compression {
99 Compression::Gzip => {
100 let mut gz = GzEncoder::new(self.body, GzCompression::fast());
101 gz.read_to_end(&mut response.body)?;
102 response.insert_header(("Content-Encoding", "gzip"));
103 }
104 Compression::None => {
105 self.body.read_to_end(&mut response.body)?;
106 }
107 }
108 Ok(response)
109 }
110}
111
112impl Default for TileResponse {
113 fn default() -> Self {
114 Self::new()
115 }
116}
117
118impl TileResponseData {
119 pub fn insert_header(&mut self, header: impl TryIntoHeaderPair) -> &mut Self {
121 if let Ok((key, value)) = header.try_into_pair() {
122 self.headers.insert(key, value);
123 }
124 self
125 }
126 pub fn compression(&self) -> Compression {
127 match self.headers.get(header::CONTENT_ENCODING) {
128 Some(v) if v == HeaderValue::from_static("gzip") => Compression::Gzip,
129 _ => Compression::None,
130 }
131 }
132 pub fn as_response(self, compression: &Compression) -> TileResponse {
134 let mut response = TileResponse::new();
135 response.set_headers(&self.headers);
136 match (self.compression(), compression) {
137 (Compression::None, Compression::Gzip) => {
138 let gz = GzEncoder::new(Cursor::new(self.body), GzCompression::fast());
139 response.body = Box::new(gz);
140 response.insert_header(("Content-Encoding", "gzip"));
141 }
142 (Compression::Gzip, Compression::None) => {
143 let gz = GzDecoder::new(Cursor::new(self.body));
144 response.body = Box::new(gz);
145 response.headers.remove(header::CONTENT_ENCODING);
146 }
147 _ => response.body = Box::new(Cursor::new(self.body)),
148 }
149 response
150 }
151}