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.to_lowercase().contains("chunked"))
97 } else {
98 Err(ParseError::Header)
99 }
100 } else {
101 Ok(false)
102 }
103 }
104}
105
106impl<T> HttpMessage for &mut T
107where
108 T: HttpMessage,
109{
110 type Stream = T::Stream;
111
112 fn headers(&self) -> &HeaderMap {
113 (**self).headers()
114 }
115
116 fn take_payload(&mut self) -> Payload<Self::Stream> {
118 (**self).take_payload()
119 }
120
121 fn extensions(&self) -> Ref<'_, Extensions> {
123 (**self).extensions()
124 }
125
126 fn extensions_mut(&self) -> RefMut<'_, Extensions> {
128 (**self).extensions_mut()
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use bytes::Bytes;
135 use encoding_rs::ISO_8859_2;
136
137 use super::*;
138 use crate::test::TestRequest;
139
140 #[test]
141 fn test_content_type() {
142 let req = TestRequest::default()
143 .insert_header(("content-type", "text/plain"))
144 .finish();
145 assert_eq!(req.content_type(), "text/plain");
146 let req = TestRequest::default()
147 .insert_header(("content-type", "application/json; charset=utf-8"))
148 .finish();
149 assert_eq!(req.content_type(), "application/json");
150 let req = TestRequest::default().finish();
151 assert_eq!(req.content_type(), "");
152 }
153
154 #[test]
155 fn test_mime_type() {
156 let req = TestRequest::default()
157 .insert_header(("content-type", "application/json"))
158 .finish();
159 assert_eq!(req.mime_type().unwrap(), Some(mime::APPLICATION_JSON));
160 let req = TestRequest::default().finish();
161 assert_eq!(req.mime_type().unwrap(), None);
162 let req = TestRequest::default()
163 .insert_header(("content-type", "application/json; charset=utf-8"))
164 .finish();
165 let mt = req.mime_type().unwrap().unwrap();
166 assert_eq!(mt.get_param(mime::CHARSET), Some(mime::UTF_8));
167 assert_eq!(mt.type_(), mime::APPLICATION);
168 assert_eq!(mt.subtype(), mime::JSON);
169 }
170
171 #[test]
172 fn test_mime_type_error() {
173 let req = TestRequest::default()
174 .insert_header(("content-type", "applicationadfadsfasdflknadsfklnadsfjson"))
175 .finish();
176 assert_eq!(Err(ContentTypeError::ParseError), req.mime_type());
177 }
178
179 #[test]
180 fn test_encoding() {
181 let req = TestRequest::default().finish();
182 assert_eq!(UTF_8.name(), req.encoding().unwrap().name());
183
184 let req = TestRequest::default()
185 .insert_header(("content-type", "application/json"))
186 .finish();
187 assert_eq!(UTF_8.name(), req.encoding().unwrap().name());
188
189 let req = TestRequest::default()
190 .insert_header(("content-type", "application/json; charset=ISO-8859-2"))
191 .finish();
192 assert_eq!(ISO_8859_2, req.encoding().unwrap());
193 }
194
195 #[test]
196 fn test_encoding_error() {
197 let req = TestRequest::default()
198 .insert_header(("content-type", "applicatjson"))
199 .finish();
200 assert_eq!(Some(ContentTypeError::ParseError), req.encoding().err());
201
202 let req = TestRequest::default()
203 .insert_header(("content-type", "application/json; charset=kkkttktk"))
204 .finish();
205 assert_eq!(
206 Some(ContentTypeError::UnknownEncoding),
207 req.encoding().err()
208 );
209 }
210
211 #[test]
212 fn test_chunked() {
213 let req = TestRequest::default().finish();
214 assert!(!req.chunked().unwrap());
215
216 let req = TestRequest::default()
217 .insert_header((header::TRANSFER_ENCODING, "chunked"))
218 .finish();
219 assert!(req.chunked().unwrap());
220
221 let req = TestRequest::default()
222 .insert_header((
223 header::TRANSFER_ENCODING,
224 Bytes::from_static(b"some va\xadscc\xacas0xsdasdlue"),
225 ))
226 .finish();
227 assert!(req.chunked().is_err());
228 }
229}