hyper_fast/server/
http_response.rs1use anyhow::Context;
2use futures::{Stream, TryStreamExt};
3use http::{header, Response};
4use http::{HeaderValue, StatusCode};
5use hyper::Body;
6use serde::Serialize;
7
8use crate::server::{HttpResult, HttpRoute};
9use crate::server::commons::get_hostname_header;
10
11use super::commons::{BR_CONTENT_ENCODING, DEFLATE_CONTENT_ENCODING, GZIP_CONTENT_ENCODING};
12
13pub struct HttpResponse;
14
15lazy_static! {
16 static ref BR_HEADER_VALUE: HeaderValue = HeaderValue::from_static("br");
17 static ref DEFLATE_HEADER_VALUE: HeaderValue = HeaderValue::from_static("deflate");
18 static ref GZIP_HEADER_VALUE: HeaderValue = HeaderValue::from_static("gzip");
19}
20
21impl HttpResponse {
22 pub fn internal_server_error(error: anyhow::Error) -> HttpResult {
23 let body = Body::from(format!("Error in serving request ==> {:?}", error));
24
25 HttpResponse::build_response(StatusCode::INTERNAL_SERVER_ERROR, body)
26 }
27
28 pub fn not_found(reason: &str) -> HttpResult {
29 let body = Body::from(format!("Not found: {}", reason));
30
31 HttpResponse::build_response(StatusCode::NOT_FOUND, body)
32 }
33
34 pub fn forbidden(reason: &str) -> HttpResult {
35 let body = Body::from(format!("Forbidden: {}", reason));
36
37 HttpResponse::build_response(StatusCode::FORBIDDEN, body)
38 }
39
40 pub fn bad_request(error: anyhow::Error) -> HttpResult {
41 let body = Body::from(format!("Bad Request: {:?}", error));
42
43 HttpResponse::build_response(StatusCode::BAD_REQUEST, body)
44 }
45
46 pub fn no_content(reason: &str) -> HttpResult {
47 let body = Body::from(format!("No Content: {}", reason));
48
49 HttpResponse::build_response(StatusCode::NO_CONTENT, body)
50 }
51
52 fn build_response(code: StatusCode, body: Body) -> HttpResult {
53 let response = Response::builder()
54 .status(code)
55 .header(header::HOST, get_hostname_header().clone())
56 .body(body)
57 .with_context(|| "Error in building HttpResponse")?;
58
59 Ok(response)
60 }
61
62 pub fn ok(route: &HttpRoute<'_>, body: Body) -> HttpResult {
63 let response = Response::builder()
64 .status(StatusCode::OK)
65 .header(header::HOST, get_hostname_header().clone())
66 .body(body)
67 .with_context(|| "Error in building HttpResponse")?;
68
69 Ok(Self::compress_response(route, response))
70 }
71
72 pub fn string(route: &HttpRoute<'_>, body: String) -> HttpResult {
73 let response = Response::builder()
74 .status(StatusCode::OK)
75 .header(header::HOST, get_hostname_header().clone())
76 .body(Body::from(body))
77 .with_context(|| "Error in building HttpResponse")?;
78
79 Ok(Self::compress_response(route, response))
80 }
81
82 pub fn str(route: &HttpRoute<'_>, body: &'static str) -> HttpResult {
83 let response = Response::builder()
84 .status(StatusCode::OK)
85 .header(header::HOST, get_hostname_header().clone())
86 .body(Body::from(body))
87 .with_context(|| "Error in building HttpResponse")?;
88
89 Ok(Self::compress_response(route, response))
90 }
91
92 pub fn json<S>(route: &HttpRoute<'_>, body: &S) -> HttpResult
93 where
94 S: Serialize,
95 {
96 let body = serde_json::to_vec(body).with_context(|| "Error in serialising")?;
97 let body = Body::from(body);
98
99 let response = Response::builder()
100 .status(StatusCode::OK)
101 .header(header::CONTENT_TYPE, "application/json")
102 .header(header::HOST, get_hostname_header().clone())
103 .body(body)
104 .with_context(|| "Error in building HttpResponse")?;
105
106 Ok(Self::compress_response(route, response))
107 }
108
109 pub fn proto_binary(route: &HttpRoute<'_>, body: Vec<u8>) -> HttpResult
110 {
111 let body = Body::from(body);
112
113 let response = Response::builder()
114 .status(StatusCode::OK)
115 .header(header::CONTENT_TYPE, "proto/bytes")
116 .header(header::HOST, get_hostname_header().clone())
117 .body(body)
118 .with_context(|| "Error in building HttpResponse")?;
119
120 Ok(Self::compress_response(route, response))
121 }
122
123 pub fn binary_or_json<S>(route: &HttpRoute<'_>, body: &S) -> HttpResult
125 where
126 S: Serialize,
127 {
128 let body = serde_json::to_vec(body).with_context(|| "Error in serialising")?;
129 let body = Body::from(body);
130
131 let response = Response::builder()
132 .status(StatusCode::OK)
133 .header(header::CONTENT_TYPE, "application/json")
134 .header(header::HOST, get_hostname_header().clone())
135 .body(body)
136 .with_context(|| "Error in building HttpResponse")?;
137
138 Ok(Self::compress_response(route, response))
139 }
140
141 pub fn from_vec<S>(route: &HttpRoute<'_>, body: Vec<u8>) -> HttpResult
142 where
143 S: Serialize,
144 {
145 let body = Body::from(body);
147
148 let response = Response::builder()
149 .status(StatusCode::OK)
150 .header(header::CONTENT_TYPE, "application/json")
151 .header(header::HOST, get_hostname_header().clone())
152 .body(body)
153 .with_context(|| "Error in building HttpResponse")?;
154
155 Ok(Self::compress_response(route, response))
156 }
157
158 pub fn compress_response(
212 route: &HttpRoute<'_>,
213 mut response: Response<Body>,
214 ) -> Response<Body> {
215 use std::io::{Error as IOError, ErrorKind as IOErrorKind};
216
217 if let Some(accept_encoding) = route.accept_encoding {
219 match accept_encoding {
220 BR_CONTENT_ENCODING => {
221 response
222 .headers_mut()
223 .insert(header::CONTENT_ENCODING, BR_HEADER_VALUE.clone());
224 response = response.map(|body| {
225 Body::wrap_stream(brotli_encode(
226 body.map_err(|_| IOError::from(IOErrorKind::InvalidData)),
227 ))
228 });
229 }
230 DEFLATE_CONTENT_ENCODING => {
231 response
232 .headers_mut()
233 .insert(header::CONTENT_ENCODING, DEFLATE_HEADER_VALUE.clone());
234 response = response.map(|body| {
235 Body::wrap_stream(deflate_encode(
236 body.map_err(|_| IOError::from(IOErrorKind::InvalidData)),
237 ))
238 });
239 }
240 GZIP_CONTENT_ENCODING => {
241 response
242 .headers_mut()
243 .insert(header::CONTENT_ENCODING, GZIP_HEADER_VALUE.clone());
244 response = response.map(|body| {
245 Body::wrap_stream(gzip_encode(
246 body.map_err(|_| IOError::from(IOErrorKind::InvalidData)),
247 ))
248 });
249 }
250 _ => {
251 }
253 }
254 }
255
256 response
257 }
258}
259
260fn gzip_encode(
261 input: impl Stream<Item=std::io::Result<bytes::Bytes>>,
262) -> impl Stream<Item=std::io::Result<bytes::Bytes>> {
263 tokio_util::io::ReaderStream::new(async_compression::tokio::bufread::GzipEncoder::new(
264 tokio_util::io::StreamReader::new(input),
265 ))
266}
267
268fn brotli_encode(
269 input: impl Stream<Item=std::io::Result<bytes::Bytes>>,
270) -> impl Stream<Item=std::io::Result<bytes::Bytes>> {
271 tokio_util::io::ReaderStream::new(async_compression::tokio::bufread::BrotliEncoder::new(
272 tokio_util::io::StreamReader::new(input),
273 ))
274}
275
276fn deflate_encode(
277 input: impl Stream<Item=std::io::Result<bytes::Bytes>>,
278) -> impl Stream<Item=std::io::Result<bytes::Bytes>> {
279 tokio_util::io::ReaderStream::new(async_compression::tokio::bufread::DeflateEncoder::new(
280 tokio_util::io::StreamReader::new(input),
281 ))
282}