http_encoding/
encode.rs

1//! Stream encoders.
2
3use futures_core::Stream;
4use http::{header, Response, StatusCode, Version};
5
6use super::{
7    coder::{Coder, FeaturedCode},
8    coding::ContentEncoding,
9};
10
11/// Construct from headers and stream body. Use for encoding.
12pub fn encoder<S, T, E>(response: Response<S>, mut encoding: ContentEncoding) -> Response<Coder<S, FeaturedCode>>
13where
14    S: Stream<Item = Result<T, E>>,
15    T: AsRef<[u8]> + 'static,
16{
17    #[allow(unused_mut)]
18    let (mut parts, body) = response.into_parts();
19
20    if parts.headers.contains_key(&header::CONTENT_ENCODING)
21        || parts.status == StatusCode::SWITCHING_PROTOCOLS
22        || parts.status == StatusCode::NO_CONTENT
23    {
24        encoding = ContentEncoding::NoOp
25    }
26
27    let encoder = {
28        match encoding {
29            #[cfg(feature = "de")]
30            ContentEncoding::Deflate => {
31                update_header(&mut parts.headers, "deflate", parts.version);
32                FeaturedCode::EncodeDe(super::deflate::Encoder::new(
33                    super::writer::BytesMutWriter::new(),
34                    flate2::Compression::fast(),
35                ))
36            }
37            #[cfg(feature = "gz")]
38            ContentEncoding::Gzip => {
39                update_header(&mut parts.headers, "gzip", parts.version);
40                FeaturedCode::EncodeGz(super::gzip::Encoder::new(
41                    super::writer::BytesMutWriter::new(),
42                    flate2::Compression::fast(),
43                ))
44            }
45            #[cfg(feature = "br")]
46            ContentEncoding::Br => {
47                update_header(&mut parts.headers, "br", parts.version);
48                FeaturedCode::EncodeBr(super::brotli::Encoder::new(3))
49            }
50            _ => FeaturedCode::default(),
51        }
52    };
53
54    let body = Coder::new(body, encoder);
55    Response::from_parts(parts, body)
56}
57
58#[cfg(any(feature = "br", feature = "gz", feature = "de"))]
59fn update_header(headers: &mut header::HeaderMap, value: &'static str, version: Version) {
60    headers.insert(header::CONTENT_ENCODING, header::HeaderValue::from_static(value));
61    headers.remove(header::CONTENT_LENGTH);
62
63    // Connection specific headers are not allowed in HTTP/2 and later versions.
64    // see https://datatracker.ietf.org/doc/html/rfc7540#section-8.1.2.2
65    if version < Version::HTTP_2 {
66        headers.insert(header::TRANSFER_ENCODING, header::HeaderValue::from_static("chunked"));
67    }
68}