1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
rama_utils::macros::enums::enum_builder! {
/// Directive for the [`TransferEncoding`] header.
@String
pub enum TransferEncodingDirective {
/// A format using the [Lempel-Ziv-Welch](https://en.wikipedia.org/wiki/LZW) (LZW) algorithm.
///
/// The value name was taken from the UNIX compress program,
/// which implemented this algorithm. Like the compress program,
/// which has disappeared from most UNIX distributions,
/// this content-encoding is used by almost no browsers today,
/// partly because of a patent issue (which expired in 2003).
Compress => "compress",
/// Using the zlib structure (defined in [RFC 1950](https://datatracker.ietf.org/doc/html/rfc1950))
/// with the [deflate](https://en.wikipedia.org/wiki/Deflate) compression algorithm
/// (defined in [RFC 1951](https://datatracker.ietf.org/doc/html/rfc1951)).
Deflate => "deflate",
/// A format using the [Lempel-Ziv coding](https://en.wikipedia.org/wiki/LZ77_and_LZ78#LZ77) (LZ77),
/// with a 32-bit CRC. This is the original format of the UNIX gzip program.
///
/// The HTTP/1.1 standard also recommends that the servers supporting this
/// content-encoding should recognize x-gzip as an alias, for compatibility purposes.
Gzip => "gzip" | "x-gzip",
/// Data is sent in a series of chunks.
///
/// Content can be sent in streams of unknown size to be transferred as a sequence of
/// length-delimited buffers, so the sender can keep a connection open,
/// and let the recipient know when it has received the entire message.
/// The Content-Length header must be omitted, and at the beginning of each chunk,
/// a string of hex digits indicate the size of the chunk-data in octets,
/// followed by `\r\n` and then the chunk itself, followed by another `\r\n`.
/// The terminating chunk is a zero-length chunk.
Chunked => "chunked",
}
}
derive_non_empty_flat_csv_header! {
#[header(name = TRANSFER_ENCODING, sep = Comma)]
#[derive(Clone, Debug)]
/// `Transfer-Encoding` header, defined in
/// [RFC7230](https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.1)
///
/// The `Transfer-Encoding` header field lists the transfer coding names
/// corresponding to the sequence of transfer codings that have been (or
/// will be) applied to the payload body in order to form the message
/// body.
///
/// Note that setting this header will *remove* any previously set
/// `Content-Length` header, in accordance with
/// [RFC7230](https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2):
///
/// > A sender MUST NOT send a Content-Length header field in any message
/// > that contains a Transfer-Encoding header field.
///
/// # ABNF
///
/// ```text
/// Transfer-Encoding = 1#transfer-coding
/// ```
///
/// # Example values
///
/// * `chunked`
/// * `gzip, chunked`
///
/// # Example
///
/// ```
/// use rama_http_headers::TransferEncoding;
///
/// let transfer = TransferEncoding::chunked();
/// ```
pub struct TransferEncoding(pub NonEmptySmallVec<2, TransferEncodingDirective>);
}
impl TransferEncoding {
/// Constructor for the most common Transfer-Encoding, `chunked`.
#[must_use]
#[inline(always)]
pub fn chunked() -> Self {
Self::new(TransferEncodingDirective::Chunked)
}
/// Returns whether this ends with the `chunked` encoding.
#[must_use]
pub fn is_chunked(&self) -> bool {
self.0.last().eq(&TransferEncodingDirective::Chunked)
}
}
#[cfg(test)]
mod tests {
use rama_utils::collections::non_empty_smallvec;
use super::super::{test_decode, test_encode};
use super::{TransferEncoding, TransferEncodingDirective};
#[test]
fn chunked_is_chunked() {
assert!(TransferEncoding::chunked().is_chunked());
}
#[test]
fn decode_gzip_chunked_is_chunked() {
let te = test_decode::<TransferEncoding>(&["gzip, chunked"]).unwrap();
assert!(te.is_chunked());
}
#[test]
fn decode_chunked_gzip_is_not_chunked() {
let te = test_decode::<TransferEncoding>(&["chunked, gzip"]).unwrap();
assert!(!te.is_chunked());
}
#[test]
fn decode_notchunked_is_not_chunked() {
let te = test_decode::<TransferEncoding>(&["notchunked"]).unwrap();
assert!(!te.is_chunked());
}
#[test]
fn decode_multiple_is_chunked() {
let te = test_decode::<TransferEncoding>(&["gzip", "chunked"]).unwrap();
assert!(te.is_chunked());
}
#[test]
fn encode_single() {
let allow = TransferEncoding::new(TransferEncodingDirective::Gzip);
let headers = test_encode(allow);
assert_eq!(headers["transfer-encoding"], "gzip");
}
#[test]
fn encode_multi() {
let allow = TransferEncoding(non_empty_smallvec![
TransferEncodingDirective::Deflate,
TransferEncodingDirective::Chunked,
]);
let headers = test_encode(allow);
assert_eq!(headers["transfer-encoding"], "deflate, chunked");
}
}