actix_http/
http_message.rs1use std::{
2 cell::{Ref, RefMut},
3 str,
4};
5
6use encoding_rs::{Encoding, UTF_8};
7use http::header;
8use mime::Mime;
9
10use crate::{
11 error::{ContentTypeError, ParseError},
12 header::{Header, HeaderMap},
13 payload::Payload,
14 Extensions,
15};
16
17pub trait HttpMessage: Sized {
19 type Stream;
21
22 fn headers(&self) -> &HeaderMap;
24
25 fn take_payload(&mut self) -> Payload<Self::Stream>;
27
28 fn extensions(&self) -> Ref<'_, Extensions>;
30
31 fn extensions_mut(&self) -> RefMut<'_, Extensions>;
33
34 #[doc(hidden)]
36 fn get_header<H: Header>(&self) -> Option<H>
37 where
38 Self: Sized,
39 {
40 if self.headers().contains_key(H::name()) {
41 H::parse(self).ok()
42 } else {
43 None
44 }
45 }
46
47 fn content_type(&self) -> &str {
50 if let Some(content_type) = self.headers().get(header::CONTENT_TYPE) {
51 if let Ok(content_type) = content_type.to_str() {
52 return content_type.split(';').next().unwrap().trim();
53 }
54 }
55 ""
56 }
57
58 fn encoding(&self) -> Result<&'static Encoding, ContentTypeError> {
62 if let Some(mime_type) = self.mime_type()? {
63 if let Some(charset) = mime_type.get_param("charset") {
64 if let Some(enc) = Encoding::for_label_no_replacement(charset.as_str().as_bytes()) {
65 Ok(enc)
66 } else {
67 Err(ContentTypeError::UnknownEncoding)
68 }
69 } else {
70 Ok(UTF_8)
71 }
72 } else {
73 Ok(UTF_8)
74 }
75 }
76
77 fn mime_type(&self) -> Result<Option<Mime>, ContentTypeError> {
79 if let Some(content_type) = self.headers().get(header::CONTENT_TYPE) {
80 if let Ok(content_type) = content_type.to_str() {
81 return match content_type.parse() {
82 Ok(mt) => Ok(Some(mt)),
83 Err(_) => Err(ContentTypeError::ParseError),
84 };
85 } else {
86 return Err(ContentTypeError::ParseError);
87 }
88 }
89 Ok(None)
90 }
91
92 fn chunked(&self) -> Result<bool, ParseError> {
94 if let Some(encodings) = self.headers().get(header::TRANSFER_ENCODING) {
95 if let Ok(s) = encodings.to_str() {
96 Ok(s.rsplit(',')
97 .next()
98 .map(|token| token.trim().eq_ignore_ascii_case("chunked"))
99 .unwrap_or(false))
100 } else {
101 Err(ParseError::Header)
102 }
103 } else {
104 Ok(false)
105 }
106 }
107}
108
109impl<T> HttpMessage for &mut T
110where
111 T: HttpMessage,
112{
113 type Stream = T::Stream;
114
115 fn headers(&self) -> &HeaderMap {
116 (**self).headers()
117 }
118
119 fn take_payload(&mut self) -> Payload<Self::Stream> {
121 (**self).take_payload()
122 }
123
124 fn extensions(&self) -> Ref<'_, Extensions> {
126 (**self).extensions()
127 }
128
129 fn extensions_mut(&self) -> RefMut<'_, Extensions> {
131 (**self).extensions_mut()
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use bytes::Bytes;
138 use encoding_rs::ISO_8859_2;
139
140 use super::*;
141 use crate::test::TestRequest;
142
143 #[test]
144 fn test_content_type() {
145 let req = TestRequest::default()
146 .insert_header(("content-type", "text/plain"))
147 .finish();
148 assert_eq!(req.content_type(), "text/plain");
149 let req = TestRequest::default()
150 .insert_header(("content-type", "application/json; charset=utf-8"))
151 .finish();
152 assert_eq!(req.content_type(), "application/json");
153 let req = TestRequest::default().finish();
154 assert_eq!(req.content_type(), "");
155 }
156
157 #[test]
158 fn test_mime_type() {
159 let req = TestRequest::default()
160 .insert_header(("content-type", "application/json"))
161 .finish();
162 assert_eq!(req.mime_type().unwrap(), Some(mime::APPLICATION_JSON));
163 let req = TestRequest::default().finish();
164 assert_eq!(req.mime_type().unwrap(), None);
165 let req = TestRequest::default()
166 .insert_header(("content-type", "application/json; charset=utf-8"))
167 .finish();
168 let mt = req.mime_type().unwrap().unwrap();
169 assert_eq!(mt.get_param(mime::CHARSET), Some(mime::UTF_8));
170 assert_eq!(mt.type_(), mime::APPLICATION);
171 assert_eq!(mt.subtype(), mime::JSON);
172 }
173
174 #[test]
175 fn test_mime_type_error() {
176 let req = TestRequest::default()
177 .insert_header(("content-type", "applicationadfadsfasdflknadsfklnadsfjson"))
178 .finish();
179 assert_eq!(Err(ContentTypeError::ParseError), req.mime_type());
180 }
181
182 #[test]
183 fn test_encoding() {
184 let req = TestRequest::default().finish();
185 assert_eq!(UTF_8.name(), req.encoding().unwrap().name());
186
187 let req = TestRequest::default()
188 .insert_header(("content-type", "application/json"))
189 .finish();
190 assert_eq!(UTF_8.name(), req.encoding().unwrap().name());
191
192 let req = TestRequest::default()
193 .insert_header(("content-type", "application/json; charset=ISO-8859-2"))
194 .finish();
195 assert_eq!(ISO_8859_2, req.encoding().unwrap());
196 }
197
198 #[test]
199 fn test_encoding_error() {
200 let req = TestRequest::default()
201 .insert_header(("content-type", "applicatjson"))
202 .finish();
203 assert_eq!(Some(ContentTypeError::ParseError), req.encoding().err());
204
205 let req = TestRequest::default()
206 .insert_header(("content-type", "application/json; charset=kkkttktk"))
207 .finish();
208 assert_eq!(
209 Some(ContentTypeError::UnknownEncoding),
210 req.encoding().err()
211 );
212 }
213
214 #[test]
215 fn test_chunked() {
216 let req = TestRequest::default().finish();
217 assert!(!req.chunked().unwrap());
218
219 let req = TestRequest::default()
220 .insert_header((header::TRANSFER_ENCODING, "chunked"))
221 .finish();
222 assert!(req.chunked().unwrap());
223
224 let req = TestRequest::default()
225 .insert_header((header::TRANSFER_ENCODING, "gzip, chunked"))
226 .finish();
227 assert!(req.chunked().unwrap());
228
229 let req = TestRequest::default()
230 .insert_header((header::TRANSFER_ENCODING, "chunked, gzip"))
231 .finish();
232 assert!(!req.chunked().unwrap());
233
234 let req = TestRequest::default()
235 .insert_header((header::TRANSFER_ENCODING, "not-chunked"))
236 .finish();
237 assert!(!req.chunked().unwrap());
238
239 let req = TestRequest::default()
240 .insert_header((header::TRANSFER_ENCODING, "chunked-fake"))
241 .finish();
242 assert!(!req.chunked().unwrap());
243
244 let req = TestRequest::default()
245 .insert_header((
246 header::TRANSFER_ENCODING,
247 Bytes::from_static(b"some va\xadscc\xacas0xsdasdlue"),
248 ))
249 .finish();
250 assert!(req.chunked().is_err());
251 }
252}