kutil_http/transcoding/
response.rs

1use super::{super::headers::*, body::*};
2
3use {
4    ::bytes::*,
5    http::{header::*, *},
6    http_body::*,
7    kutil_std::error::*,
8    kutil_transcoding::*,
9};
10
11//
12// IntoTranscodingResponse
13//
14
15/// Into a [Response] with a [TranscodingBody].
16pub trait IntoTranscodingResponse<BodyT>: Sized
17where
18    BodyT: Body,
19    BodyT::Error: Into<CapturedError>,
20{
21    /// Into a [Response] with a passthrough [TranscodingBody].
22    fn with_transcoding_body_passthrough(self) -> Response<TranscodingBody<BodyT>> {
23        self.with_transcoding_body_passthrough_with_first_bytes(None)
24    }
25
26    /// Into a [Response] with a passthrough [TranscodingBody].
27    fn with_transcoding_body_passthrough_with_first_bytes(
28        self,
29        first_bytes: Option<Bytes>,
30    ) -> Response<TranscodingBody<BodyT>>;
31
32    /// Into a [Response] with an encoding [TranscodingBody].
33    fn with_transcoding_body(
34        self,
35        encoding: &Encoding,
36        encodable_by_default: bool,
37    ) -> Response<TranscodingBody<BodyT>> {
38        self.with_transcoding_body_with_first_bytes(None, encoding, encodable_by_default)
39    }
40
41    /// Into a [Response] with an encoding [TranscodingBody].
42    fn with_transcoding_body_with_first_bytes(
43        self,
44        first_bytes: Option<Bytes>,
45        encoding: &Encoding,
46        encodable_by_default: bool,
47    ) -> Response<TranscodingBody<BodyT>>;
48}
49
50impl<BodyT> IntoTranscodingResponse<BodyT> for Response<BodyT>
51where
52    BodyT: Body,
53    BodyT::Error: Into<CapturedError>,
54{
55    fn with_transcoding_body_passthrough_with_first_bytes(
56        self,
57        first_bytes: Option<Bytes>,
58    ) -> Response<TranscodingBody<BodyT>> {
59        let (mut parts, body) = self.into_parts();
60        parts.headers.remove(XX_ENCODE);
61        parts.headers.remove(XX_CACHE);
62        Response::from_parts(parts, body.into_transcoding_passthrough_with_first_bytes(first_bytes))
63    }
64
65    fn with_transcoding_body_with_first_bytes(
66        self,
67        first_bytes: Option<Bytes>,
68        encoding: &Encoding,
69        encodable_by_default: bool,
70    ) -> Response<TranscodingBody<BodyT>> {
71        if *encoding == Encoding::Identity {
72            return self.with_transcoding_body_passthrough_with_first_bytes(first_bytes);
73        }
74
75        let (mut parts, body) = self.into_parts();
76
77        let encode = parts.headers.xx_encode(encodable_by_default);
78        parts.headers.remove(XX_CACHE);
79        parts.headers.remove(XX_ENCODE);
80
81        if !encode {
82            tracing::debug!("not encoding to {} ({}=false)", encoding, XX_ENCODE);
83            return Response::from_parts(parts, body.into_transcoding_passthrough_with_first_bytes(first_bytes));
84        }
85
86        let current_encoding = parts.headers.content_encoding().into();
87
88        if *encoding == current_encoding {
89            tracing::debug!("already encoded as {}", encoding);
90            return Response::from_parts(parts, body.into_transcoding_passthrough_with_first_bytes(first_bytes));
91        }
92
93        if current_encoding != Encoding::Identity {
94            tracing::debug!("not reencoding from {} to {})", current_encoding, encoding);
95            return Response::from_parts(parts, body.into_transcoding_passthrough_with_first_bytes(first_bytes));
96
97            // We intentionally don't reencode because it would be computationally wasteful!
98            // Also, it would be hard to program this in our current generics-based design.
99            // The wrapping would have to look something like this:
100            //
101            //   BodyReader ->
102            //     decoding TranscodingReader ->
103            //       encoding TranscodingReader
104            //
105            // Also note that we are *not* checking that the client can accept current_encoding.
106            // We just have to trust that the body was generated with respect to the request's
107            // `Accept-Encoding`.
108        }
109
110        parts.headers.set_into_header_value(CONTENT_ENCODING, encoding.clone());
111
112        // We don't know what the final content length will be
113        parts.headers.remove(CONTENT_LENGTH);
114
115        // We don't know what the final digest will be
116        parts.headers.remove(CONTENT_DIGEST);
117
118        Response::from_parts(parts, body.into_encoding_with_first_bytes(first_bytes, encoding))
119    }
120}
121
122/// [Response] with an empty [TranscodingBody] and [StatusCode::INTERNAL_SERVER_ERROR].
123pub fn error_transcoding_response<BodyT>() -> Response<TranscodingBody<BodyT>>
124where
125    BodyT: Body + From<Bytes>,
126    BodyT::Error: Into<CapturedError>,
127{
128    let mut response = Response::new(Bytes::new().into()).with_transcoding_body_passthrough_with_first_bytes(None);
129    *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
130    response
131}
132
133/// [Response] with an empty [TranscodingBody] and [StatusCode::NOT_MODIFIED].
134pub fn not_modified_transcoding_response<BodyT>() -> Response<TranscodingBody<BodyT>>
135where
136    BodyT: Body + From<Bytes>,
137    BodyT::Error: Into<CapturedError>,
138{
139    let mut response = Response::new(Bytes::new().into()).with_transcoding_body_passthrough_with_first_bytes(None);
140    *response.status_mut() = StatusCode::NOT_MODIFIED;
141    response
142}