1 // Copyright (c) 2023 Huawei Device Co., Ltd.
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13
14 use core::str;
15 use std::path::Path;
16
17 use crate::error::{ErrorKind, HttpError};
18
19 /// A type that defines the general structure of the `MIME` media typing system.
20 ///
21 /// A `MIME` type most-commonly consists of just two parts:
22 ///
23 /// - `Type`
24 /// - `Subtype`
25 ///
26 /// `Type` and `SubType` are separated by a slash (/) — with no whitespace
27 /// between:
28 ///
29 /// ```type/subtype```
30 ///
31 /// It is case-insensitive but are traditionally written in lowercase, such as:
32 /// ```application/octet-stream```.
33 ///
34 /// # Examples
35 ///
36 /// ```
37 /// use ylong_http::body::MimeType;
38 ///
39 /// let mime_type = MimeType::from_bytes(b"application/octet-stream").unwrap();
40 /// assert!(mime_type.is_application());
41 /// ```
42 #[derive(Debug, Eq, PartialEq)]
43 pub struct MimeType<'a> {
44 tag: MimeTypeTag,
45 bytes: &'a [u8],
46 // Index of '/'.
47 slash: usize,
48 }
49
50 impl<'a> MimeType<'a> {
51 /// Creates a `MimeType` from a bytes slice.
52 ///
53 /// # Examples
54 ///
55 /// ```
56 /// use ylong_http::body::MimeType;
57 ///
58 /// let mime_type = MimeType::from_bytes(b"application/octet-stream").unwrap();
59 /// assert_eq!(mime_type.main_type(), "application");
60 /// assert_eq!(mime_type.sub_type(), "octet-stream");
61 /// ```
from_bytes(bytes: &'a [u8]) -> Result<Self, HttpError>62 pub fn from_bytes(bytes: &'a [u8]) -> Result<Self, HttpError> {
63 // From [RFC6838](http://tools.ietf.org/html/rfc6838#section-4.2):
64 // <type-name> and <subtype-name> SHOULD be limited to 64 characters.
65 // Both top-level type and subtype names are case-insensitive.
66
67 let (slash, _) = bytes
68 .iter()
69 .enumerate()
70 .find(|(_, &b)| b == b'/')
71 .ok_or_else(|| HttpError::from(ErrorKind::InvalidInput))?;
72
73 let tag = MimeTypeTag::from_bytes(&bytes[..slash])?;
74
75 let sub_type = &bytes[slash + 1..];
76 if sub_type.len() > 64 || !is_valid(sub_type) {
77 return Err(ErrorKind::InvalidInput.into());
78 }
79
80 Ok(MimeType { tag, bytes, slash })
81 }
82
83 /// Creates a new `MimeType` from a file path. The extension of the file
84 /// will be used to create it.
85 ///
86 /// # Examples
87 ///
88 /// ```
89 /// use std::path::Path;
90 ///
91 /// use ylong_http::body::MimeType;
92 ///
93 /// let path = Path::new("./foo/bar.pdf");
94 /// let mime_type = MimeType::from_path(path).unwrap();
95 /// assert_eq!(mime_type.main_type(), "application");
96 /// assert_eq!(mime_type.sub_type(), "pdf");
97 /// ```
from_path(path: &'a Path) -> Result<Self, HttpError>98 pub fn from_path(path: &'a Path) -> Result<Self, HttpError> {
99 let str = path
100 .extension()
101 .and_then(|ext| ext.to_str())
102 .ok_or_else(|| HttpError::from(ErrorKind::InvalidInput))?;
103 Self::from_extension(str)
104 }
105
106 /// Returns a `&str` which represents the `MimeType`.
107 ///
108 /// # Examples
109 ///
110 /// ```
111 /// use ylong_http::body::MimeType;
112 ///
113 /// let mime_type = MimeType::from_bytes(b"application/pdf").unwrap();
114 /// assert_eq!(mime_type.as_str(), "application/pdf");
115 /// ```
as_str(&self) -> &str116 pub fn as_str(&self) -> &str {
117 // Safety: The input byte slice is checked, so it can be directly
118 // converted to `&str` here.
119 unsafe { str::from_utf8_unchecked(self.bytes) }
120 }
121
122 /// Returns main type string, such as `text` of `text/plain`.
123 ///
124 /// # Examples
125 ///
126 /// ```
127 /// use ylong_http::body::MimeType;
128 ///
129 /// let mime_type = MimeType::from_bytes(b"application/pdf").unwrap();
130 /// assert_eq!(mime_type.main_type(), "application");
131 /// ```
main_type(&self) -> &str132 pub fn main_type(&self) -> &str {
133 // Safety: The input byte slice is checked, so it can be directly
134 // converted to `&str` here.
135 unsafe { str::from_utf8_unchecked(&self.bytes[..self.slash]) }
136 }
137
138 /// Returns sub type string, such as `plain` of `text/plain`.
139 ///
140 /// # Examples
141 ///
142 /// ```
143 /// use ylong_http::body::MimeType;
144 ///
145 /// let mime_type = MimeType::from_bytes(b"application/pdf").unwrap();
146 /// assert_eq!(mime_type.sub_type(), "pdf");
147 /// ```
sub_type(&self) -> &str148 pub fn sub_type(&self) -> &str {
149 // Safety: The input byte slice is checked, so it can be directly
150 // converted to `&str` here.
151 unsafe { str::from_utf8_unchecked(&self.bytes[self.slash + 1..]) }
152 }
153
154 /// Checks whether the main type is `application`.
155 ///
156 /// # Examples
157 ///
158 /// ```
159 /// use ylong_http::body::MimeType;
160 ///
161 /// let mime_type = MimeType::from_bytes(b"application/pdf").unwrap();
162 /// assert!(mime_type.is_application());
163 /// ```
is_application(&self) -> bool164 pub fn is_application(&self) -> bool {
165 matches!(self.tag, MimeTypeTag::Application)
166 }
167
168 /// Checks whether the main type is `audio`.
169 ///
170 /// # Examples
171 ///
172 /// ```
173 /// use ylong_http::body::MimeType;
174 ///
175 /// let mime_type = MimeType::from_bytes(b"audio/basic").unwrap();
176 /// assert!(mime_type.is_audio());
177 /// ```
is_audio(&self) -> bool178 pub fn is_audio(&self) -> bool {
179 matches!(self.tag, MimeTypeTag::Audio)
180 }
181
182 /// Checks whether the main type is `font`.
183 ///
184 /// # Examples
185 ///
186 /// ```
187 /// use ylong_http::body::MimeType;
188 ///
189 /// let mime_type = MimeType::from_bytes(b"font/collection").unwrap();
190 /// assert!(mime_type.is_font());
191 /// ```
is_font(&self) -> bool192 pub fn is_font(&self) -> bool {
193 matches!(self.tag, MimeTypeTag::Font)
194 }
195
196 /// Checks whether the main type is `image`.
197 ///
198 /// # Examples
199 ///
200 /// ```
201 /// use ylong_http::body::MimeType;
202 ///
203 /// let mime_type = MimeType::from_bytes(b"image/gif").unwrap();
204 /// assert!(mime_type.is_image());
205 /// ```
is_image(&self) -> bool206 pub fn is_image(&self) -> bool {
207 matches!(self.tag, MimeTypeTag::Image)
208 }
209
210 /// Checks whether the main type is `message`.
211 ///
212 /// # Examples
213 ///
214 /// ```
215 /// use ylong_http::body::MimeType;
216 ///
217 /// let mime_type = MimeType::from_bytes(b"message/rfc822").unwrap();
218 /// assert!(mime_type.is_message());
219 /// ```
is_message(&self) -> bool220 pub fn is_message(&self) -> bool {
221 matches!(self.tag, MimeTypeTag::Message)
222 }
223
224 /// Checks whether the main type is `model`.
225 ///
226 /// # Examples
227 ///
228 /// ```
229 /// use ylong_http::body::MimeType;
230 ///
231 /// let mime_type = MimeType::from_bytes(b"model/e57").unwrap();
232 /// assert!(mime_type.is_model());
233 /// ```
is_model(&self) -> bool234 pub fn is_model(&self) -> bool {
235 matches!(self.tag, MimeTypeTag::Model)
236 }
237
238 /// Checks whether the main type is `multipart`.
239 ///
240 /// # Examples
241 ///
242 /// ```
243 /// use ylong_http::body::MimeType;
244 ///
245 /// let mime_type = MimeType::from_bytes(b"multipart/form-data").unwrap();
246 /// assert!(mime_type.is_multipart());
247 /// ```
is_multipart(&self) -> bool248 pub fn is_multipart(&self) -> bool {
249 matches!(self.tag, MimeTypeTag::Multipart)
250 }
251
252 /// Checks whether the main type is `text`.
253 ///
254 /// # Examples
255 ///
256 /// ```
257 /// use ylong_http::body::MimeType;
258 ///
259 /// let mime_type = MimeType::from_bytes(b"text/richtext").unwrap();
260 /// assert!(mime_type.is_text());
261 /// ```
is_text(&self) -> bool262 pub fn is_text(&self) -> bool {
263 matches!(self.tag, MimeTypeTag::Text)
264 }
265
266 /// Checks whether the main type is `video`.
267 ///
268 /// # Examples
269 ///
270 /// ```
271 /// use ylong_http::body::MimeType;
272 ///
273 /// let mime_type = MimeType::from_bytes(b"video/mpeg").unwrap();
274 /// assert!(mime_type.is_video());
275 /// ```
is_video(&self) -> bool276 pub fn is_video(&self) -> bool {
277 matches!(self.tag, MimeTypeTag::Video)
278 }
279
280 /// Checks whether the main type is non-standard type `x-`.
281 ///
282 /// # Examples
283 ///
284 /// ```
285 /// use ylong_http::body::MimeType;
286 ///
287 /// let mime_type = MimeType::from_bytes(b"x-world/x-vrml").unwrap();
288 /// assert!(mime_type.is_xnew());
289 /// ```
is_xnew(&self) -> bool290 pub fn is_xnew(&self) -> bool {
291 matches!(self.tag, MimeTypeTag::Xnew)
292 }
293 }
294
295 impl<'a> Default for MimeType<'a> {
default() -> Self296 fn default() -> Self {
297 Self {
298 tag: MimeTypeTag::Application,
299 bytes: b"application/octet-stream",
300 slash: 11,
301 }
302 }
303 }
304
305 macro_rules! mime {
306 ($($ext: expr, $str: expr, $slash: expr, $tag: expr$(;)?)*) => {
307 impl MimeType<'_> {
308 /// Creates a new `MimeType` from file extension.
309 ///
310 /// Returns `application/octet-stream` if extension is not discerned.
311 ///
312 /// # Examples
313 ///
314 /// ```
315 /// use ylong_http::body::MimeType;
316 ///
317 /// let mime_type = MimeType::from_extension("pdf").unwrap();
318 /// assert_eq!(mime_type.main_type(), "application");
319 /// assert_eq!(mime_type.sub_type(), "pdf");
320 /// ```
321 pub fn from_extension(s: &str) -> Result<Self, HttpError> {
322 Ok(match s {
323 $(
324 $ext => MimeType {
325 tag: $tag,
326 bytes: $str.as_bytes(),
327 slash: $slash,
328 },
329 )*
330 _=> MimeType {
331 tag: MimeTypeTag::Application,
332 bytes: b"application/octet-stream",
333 slash: 11,
334 }
335 })
336 }
337 }
338
339 /// UT test cases for `ut_mime_type_from_extension`.
340 ///
341 /// # Brief
342 /// 1. Creates a `MimeType` from file extension.
343 /// 2. Checks if the test results are correct.
344 #[test]
345 fn ut_mime_type_from_extension() {
346 $(
347 let mime_type = MimeType::from_extension($ext).unwrap();
348 assert_eq!(mime_type.tag, $tag);
349 assert_eq!(mime_type.bytes, $str.as_bytes());
350 assert_eq!(mime_type.slash, $slash);
351 )*
352 }
353 };
354 }
355
356 mime!(
357 "evy", "application/envoy", 11, MimeTypeTag::Application;
358 "fif", "application/fractals", 11, MimeTypeTag::Application;
359 "spl", "application/futuresplash", 11, MimeTypeTag::Application;
360 "hta", "application/hta", 11, MimeTypeTag::Application;
361 "acx", "application/internet-property-stream", 11, MimeTypeTag::Application;
362 "hqx", "application/mac-binhex40", 11, MimeTypeTag::Application;
363 "doc", "application/msword", 11, MimeTypeTag::Application;
364 "dot", "application/msword", 11, MimeTypeTag::Application;
365 "*", "application/octet-stream", 11, MimeTypeTag::Application;
366 "bin", "application/octet-stream", 11, MimeTypeTag::Application;
367 "class", "application/octet-stream", 11, MimeTypeTag::Application;
368 "dms", "application/octet-stream", 11, MimeTypeTag::Application;
369 "exe", "application/octet-stream", 11, MimeTypeTag::Application;
370 "lha", "application/octet-stream", 11, MimeTypeTag::Application;
371 "lzh", "application/octet-stream", 11, MimeTypeTag::Application;
372 "oda", "application/oda", 11, MimeTypeTag::Application;
373 "axs", "application/olescript", 11, MimeTypeTag::Application;
374 "pdf", "application/pdf", 11, MimeTypeTag::Application;
375 "prf", "application/pics-rules", 11, MimeTypeTag::Application;
376 "p10", "application/pkcs10", 11, MimeTypeTag::Application;
377 "crl", "application/pkix-crl", 11, MimeTypeTag::Application;
378 "ai", "application/postscript", 11, MimeTypeTag::Application;
379 "eps", "application/postscript", 11, MimeTypeTag::Application;
380 "ps", "application/postscript", 11, MimeTypeTag::Application;
381 "rtf", "application/rtf", 11, MimeTypeTag::Application;
382 "setpay", "application/set-payment-initiation", 11, MimeTypeTag::Application;
383 "setreg", "application/set-registration-initiation", 11, MimeTypeTag::Application;
384 "xla", "application/vnd.ms-excel", 11, MimeTypeTag::Application;
385 "xlc", "application/vnd.ms-excel", 11, MimeTypeTag::Application;
386 "xlm", "application/vnd.ms-excel", 11, MimeTypeTag::Application;
387 "xls", "application/vnd.ms-excel", 11, MimeTypeTag::Application;
388 "xlt", "application/vnd.ms-excel", 11, MimeTypeTag::Application;
389 "xlw", "application/vnd.ms-excel", 11, MimeTypeTag::Application;
390 "msg", "application/vnd.ms-outlook", 11, MimeTypeTag::Application;
391 "sst", "application/vnd.ms-pkicertstore", 11, MimeTypeTag::Application;
392 "cat", "application/vnd.ms-pkiseccat", 11, MimeTypeTag::Application;
393 "stl", "application/vnd.ms-pkistl", 11, MimeTypeTag::Application;
394 "pot", "application/vnd.ms-powerpoint", 11, MimeTypeTag::Application;
395 "pps", "application/vnd.ms-powerpoint", 11, MimeTypeTag::Application;
396 "ppt", "application/vnd.ms-powerpoint", 11, MimeTypeTag::Application;
397 "mpp", "application/vnd.ms-project", 11, MimeTypeTag::Application;
398 "wcm", "application/vnd.ms-works", 11, MimeTypeTag::Application;
399 "wdb", "application/vnd.ms-works", 11, MimeTypeTag::Application;
400 "wks", "application/vnd.ms-works", 11, MimeTypeTag::Application;
401 "wps", "application/vnd.ms-works", 11, MimeTypeTag::Application;
402 "hlp", "application/winhlp", 11, MimeTypeTag::Application;
403 "bcpio", "application/x-bcpio", 11, MimeTypeTag::Application;
404 // "cdf" also can be "application/x-netcdf"
405 "cdf", "application/x-cdf", 11, MimeTypeTag::Application;
406 "z", "application/x-compress", 11, MimeTypeTag::Application;
407 "tgz", "application/x-compressed", 11, MimeTypeTag::Application;
408 "cpio", "application/x-cpio", 11, MimeTypeTag::Application;
409 "csh", "application/x-csh", 11, MimeTypeTag::Application;
410 "dcr", "application/x-director", 11, MimeTypeTag::Application;
411 "dir", "application/x-director", 11, MimeTypeTag::Application;
412 "dxr", "application/x-director", 11, MimeTypeTag::Application;
413 "dvi", "application/x-dvi", 11, MimeTypeTag::Application;
414 "gtar", "application/x-gtar", 11, MimeTypeTag::Application;
415 "gz", "application/x-gzip", 11, MimeTypeTag::Application;
416 "hdf", "application/x-hdf", 11, MimeTypeTag::Application;
417 "ins", "application/x-internet-signup", 11, MimeTypeTag::Application;
418 "isp", "application/x-internet-signup", 11, MimeTypeTag::Application;
419 "iii", "application/x-iphone", 11, MimeTypeTag::Application;
420 "js", "application/x-javascript", 11, MimeTypeTag::Application;
421 "latex", "application/x-latex", 11, MimeTypeTag::Application;
422 "mdb", "application/x-msaccess", 11, MimeTypeTag::Application;
423 "crd", "application/x-mscardfile", 11, MimeTypeTag::Application;
424 "clp", "application/x-msclip", 11, MimeTypeTag::Application;
425 "dll", "application/x-msdownload", 11, MimeTypeTag::Application;
426 "m13", "application/x-msmediaview", 11, MimeTypeTag::Application;
427 "m14", "application/x-msmediaview", 11, MimeTypeTag::Application;
428 "mvb", "application/x-msmediaview", 11, MimeTypeTag::Application;
429 "wmf", "application/x-msmetafile", 11, MimeTypeTag::Application;
430 "mny", "application/x-msmoney", 11, MimeTypeTag::Application;
431 "pub", "application/x-mspublisher", 11, MimeTypeTag::Application;
432 "scd", "application/x-msschedule", 11, MimeTypeTag::Application;
433 "trm", "application/x-msterminal", 11, MimeTypeTag::Application;
434 "wri", "application/x-mswrite", 11, MimeTypeTag::Application;
435 "nc", "application/x-netcdf", 11, MimeTypeTag::Application;
436 "pma", "application/x-perfmon", 11, MimeTypeTag::Application;
437 "pmc", "application/x-perfmon", 11, MimeTypeTag::Application;
438 "pml", "application/x-perfmon", 11, MimeTypeTag::Application;
439 "pmr", "application/x-perfmon", 11, MimeTypeTag::Application;
440 "pmw", "application/x-perfmon", 11, MimeTypeTag::Application;
441 "p12", "application/x-pkcs12", 11, MimeTypeTag::Application;
442 "pfx", "application/x-pkcs12", 11, MimeTypeTag::Application;
443 "p7b", "application/x-pkcs7-certificates", 11, MimeTypeTag::Application;
444 "spc", "application/x-pkcs7-certificates", 11, MimeTypeTag::Application;
445 "p7r", "application/x-pkcs7-certificates", 11, MimeTypeTag::Application;
446 "p7c", "application/x-pkcs7-mime", 11, MimeTypeTag::Application;
447 "p7m", "application/x-pkcs7-mime", 11, MimeTypeTag::Application;
448 "p7s", "application/x-pkcs7-signature", 11, MimeTypeTag::Application;
449 "sh", "application/x-sh", 11, MimeTypeTag::Application;
450 "shar", "application/x-shar", 11, MimeTypeTag::Application;
451 "swf", "application/x-shockwave-flash", 11, MimeTypeTag::Application;
452 "sit", "application/x-stuffit", 11, MimeTypeTag::Application;
453 "sv4cpio", "application/x-sv4cpio", 11, MimeTypeTag::Application;
454 "sv4crc", "application/x-sv4crc", 11, MimeTypeTag::Application;
455 "tar", "application/x-tar", 11, MimeTypeTag::Application;
456 "tcl", "application/x-tcl", 11, MimeTypeTag::Application;
457 "tex", "application/x-tex", 11, MimeTypeTag::Application;
458 "texi", "application/x-texinfo", 11, MimeTypeTag::Application;
459 "texinfo", "application/x-texinfo", 11, MimeTypeTag::Application;
460 "roff", "application/x-troff", 11, MimeTypeTag::Application;
461 "t", "application/x-troff", 11, MimeTypeTag::Application;
462 "tr", "application/x-troff", 11, MimeTypeTag::Application;
463 "man", "application/x-troff-man", 11, MimeTypeTag::Application;
464 "me", "application/x-troff-me", 11, MimeTypeTag::Application;
465 "ms", "application/x-troff-ms", 11, MimeTypeTag::Application;
466 "ustar", "application/x-ustar", 11, MimeTypeTag::Application;
467 "src", "application/x-wais-source", 11, MimeTypeTag::Application;
468 "cer", "application/x-x509-ca-cert", 11, MimeTypeTag::Application;
469 "crt", "application/x-x509-ca-cert", 11, MimeTypeTag::Application;
470 "der", "application/x-x509-ca-cert", 11, MimeTypeTag::Application;
471 "pko", "application/ynd.ms-pkipko", 11, MimeTypeTag::Application;
472 "zip", "application/zip", 11, MimeTypeTag::Application;
473 "au", "audio/basic", 5, MimeTypeTag::Audio;
474 "snd", "audio/basic", 5, MimeTypeTag::Audio;
475 "mid", "audio/mid", 5, MimeTypeTag::Audio;
476 "rmi", "audio/mid", 5, MimeTypeTag::Audio;
477 "mp3", "audio/mpeg", 5, MimeTypeTag::Audio;
478 "aif", "audio/x-aiff", 5, MimeTypeTag::Audio;
479 "aifc", "audio/x-aiff", 5, MimeTypeTag::Audio;
480 "aiff", "audio/x-aiff", 5, MimeTypeTag::Audio;
481 "m3u", "audio/x-mpegurl", 5, MimeTypeTag::Audio;
482 "ra", "audio/x-pn-realaudio", 5, MimeTypeTag::Audio;
483 "ram", "audio/x-pn-realaudio", 5, MimeTypeTag::Audio;
484 "wav", "audio/x-wav", 5, MimeTypeTag::Audio;
485 "bmp", "image/bmp", 5, MimeTypeTag::Image;
486 "cod", "image/cis-cod", 5, MimeTypeTag::Image;
487 "gif", "image/gif", 5, MimeTypeTag::Image;
488 "ief", "image/ief", 5, MimeTypeTag::Image;
489 "jpe", "image/jpeg", 5, MimeTypeTag::Image;
490 "jpeg", "image/jpeg", 5, MimeTypeTag::Image;
491 "jpg", "image/jpeg", 5, MimeTypeTag::Image;
492 "jfif", "image/pipeg", 5, MimeTypeTag::Image;
493 "svg", "image/svg+xml", 5, MimeTypeTag::Image;
494 "tif", "image/tiff", 5, MimeTypeTag::Image;
495 "tiff", "image/tiff", 5, MimeTypeTag::Image;
496 "ras", "image/x-cmu-raster", 5, MimeTypeTag::Image;
497 "cmx", "image/x-cmx", 5, MimeTypeTag::Image;
498 "ico", "image/x-icon", 5, MimeTypeTag::Image;
499 "pnm", "image/x-portable-anymap", 5, MimeTypeTag::Image;
500 "pbm", "image/x-portable-bitmap", 5, MimeTypeTag::Image;
501 "pgm", "image/x-portable-graymap", 5, MimeTypeTag::Image;
502 "ppm", "image/x-portable-pixmap", 5, MimeTypeTag::Image;
503 "rgb", "image/x-rgb", 5, MimeTypeTag::Image;
504 "xbm", "image/x-xbitmap", 5, MimeTypeTag::Image;
505 "xpm", "image/x-xpixmap", 5, MimeTypeTag::Image;
506 "xwd", "image/x-xwindowdump", 5, MimeTypeTag::Image;
507 "mht", "message/rfc822", 7, MimeTypeTag::Message;
508 "mhtml", "message/rfc822", 7, MimeTypeTag::Message;
509 "nws", "message/rfc822", 7, MimeTypeTag::Message;
510 "css", "text/css", 4, MimeTypeTag::Text;
511 "323", "text/h323", 4, MimeTypeTag::Text;
512 "htm", "text/html", 4, MimeTypeTag::Text;
513 "html", "text/html", 4, MimeTypeTag::Text;
514 "stm", "text/html", 4, MimeTypeTag::Text;
515 "uls", "text/iuls", 4, MimeTypeTag::Text;
516 "bas", "text/plain", 4, MimeTypeTag::Text;
517 "c", "text/plain", 4, MimeTypeTag::Text;
518 "h", "text/plain", 4, MimeTypeTag::Text;
519 "txt", "text/plain", 4, MimeTypeTag::Text;
520 "rtx", "text/richtext", 4, MimeTypeTag::Text;
521 "sct", "text/scriptlet", 4, MimeTypeTag::Text;
522 "tsv", "text/tab-separated-values", 4, MimeTypeTag::Text;
523 "htt", "text/webviewhtml", 4, MimeTypeTag::Text;
524 "htc", "text/x-component", 4, MimeTypeTag::Text;
525 "etx", "text/x-setext", 4, MimeTypeTag::Text;
526 "vcf", "text/x-vcard", 4, MimeTypeTag::Text;
527 "mp2", "video/mpeg", 5, MimeTypeTag::Video;
528 "mpa", "video/mpeg", 5, MimeTypeTag::Video;
529 "mpe", "video/mpeg", 5, MimeTypeTag::Video;
530 "mpeg", "video/mpeg", 5, MimeTypeTag::Video;
531 "mpg", "video/mpeg", 5, MimeTypeTag::Video;
532 "mpv2", "video/mpeg", 5, MimeTypeTag::Video;
533 "mov", "video/quicktime", 5, MimeTypeTag::Video;
534 "qt", "video/quicktime", 5, MimeTypeTag::Video;
535 "lsf", "video/x-la-asf", 5, MimeTypeTag::Video;
536 "lsx", "video/x-la-asf", 5, MimeTypeTag::Video;
537 "asf", "video/x-ms-asf", 5, MimeTypeTag::Video;
538 "asr", "video/x-ms-asf", 5, MimeTypeTag::Video;
539 "asx", "video/x-ms-asf", 5, MimeTypeTag::Video;
540 "avi", "video/x-msvideo", 5, MimeTypeTag::Video;
541 "movie", "video/x-sgi-movie", 5, MimeTypeTag::Video;
542 "flr", "x-world/x-vrml", 7, MimeTypeTag::Xnew;
543 "vrml", "x-world/x-vrml", 7, MimeTypeTag::Xnew;
544 "wrl", "x-world/x-vrml", 7, MimeTypeTag::Xnew;
545 "wrz", "x-world/x-vrml", 7, MimeTypeTag::Xnew;
546 "xaf", "x-world/x-vrml", 7, MimeTypeTag::Xnew;
547 "xof", "x-world/x-vrml", 7, MimeTypeTag::Xnew;
548 );
549
550 /// `MIME` main type.
551 #[derive(Debug, PartialEq, Eq)]
552 enum MimeTypeTag {
553 Application,
554 Audio,
555 Font,
556 Image,
557 Message,
558 Model,
559 Multipart,
560 Text,
561 Video,
562 // A type not included in the standard, beginning with `x-`
563 Xnew,
564 }
565
566 impl MimeTypeTag {
from_bytes(b: &[u8]) -> Result<Self, HttpError>567 fn from_bytes(b: &[u8]) -> Result<Self, HttpError> {
568 // From [RFC6838](http://tools.ietf.org/html/rfc6838#section-4.2)
569 // <type-name> and <subtype-name> SHOULD be limited to 64 characters.
570 // Both top-level type and subtype names are case-insensitive.
571
572 if b.len() > 64 || b.len() < 2 {
573 return Err(ErrorKind::InvalidInput.into());
574 }
575
576 match b[0].to_ascii_lowercase() {
577 // beginning with "x-"
578 b'x' => {
579 if b[1] == b'-' && is_valid(&b[2..]) {
580 return Ok(Self::Xnew);
581 }
582 }
583 b'a' => {
584 return Self::mime_byte_a(b);
585 }
586 b'f' => {
587 // font
588 if b[1..].eq_ignore_ascii_case(b"ont") {
589 return Ok(Self::Font);
590 }
591 }
592 b'i' => {
593 // image
594 if b[1..].eq_ignore_ascii_case(b"mage") {
595 return Ok(Self::Image);
596 }
597 }
598 b'm' => {
599 return Self::mime_byte_m(b);
600 }
601 b't' => {
602 // text
603 if b[1..].eq_ignore_ascii_case(b"ext") {
604 return Ok(Self::Text);
605 }
606 }
607 b'v' => {
608 // video
609 if b[1..].eq_ignore_ascii_case(b"ideo") {
610 return Ok(Self::Video);
611 }
612 }
613 _ => return Err(ErrorKind::InvalidInput.into()),
614 };
615
616 Err(ErrorKind::InvalidInput.into())
617 }
618
mime_byte_a(b: &[u8]) -> Result<MimeTypeTag, HttpError>619 fn mime_byte_a(b: &[u8]) -> Result<MimeTypeTag, HttpError> {
620 // application
621 if b[1..].eq_ignore_ascii_case(b"pplication") {
622 Ok(Self::Application)
623 // audio
624 } else if b[1..].eq_ignore_ascii_case(b"udio") {
625 Ok(Self::Audio)
626 } else {
627 Err(ErrorKind::InvalidInput.into())
628 }
629 }
mime_byte_m(b: &[u8]) -> Result<MimeTypeTag, HttpError>630 fn mime_byte_m(b: &[u8]) -> Result<MimeTypeTag, HttpError> {
631 // message
632 if b[1..].eq_ignore_ascii_case(b"essage") {
633 Ok(Self::Message)
634 // model
635 } else if b[1..].eq_ignore_ascii_case(b"odel") {
636 Ok(Self::Model)
637 // multipart
638 } else if b[1..].eq_ignore_ascii_case(b"ultipart") {
639 Ok(Self::Multipart)
640 } else {
641 Err(ErrorKind::InvalidInput.into())
642 }
643 }
644 }
645
646 // From [RFC6838](http://tools.ietf.org/html/rfc6838#section-4.2):
647 //
648 // All registered media types MUST be assigned top-level type and
649 // subtype names. The combination of these names serves to uniquely
650 // identify the media type, and the subtype name facet (or the absence
651 // of one) identifies the registration tree. Both top-level type and
652 // subtype names are case-insensitive.
653 //
654 // Type and subtype names MUST conform to the following ABNF:
655 //
656 // type-name = restricted-name
657 // subtype-name = restricted-name
658 //
659 // restricted-name = restricted-name-first *126restricted-name-chars
660 // restricted-name-first = ALPHA / DIGIT
661 // restricted-name-chars = ALPHA / DIGIT / "!" / "#" /
662 // "$" / "&" / "-" / "^" / "_"
663 // restricted-name-chars =/ "." ; Characters before first dot always
664 // ; specify a facet name
665 // restricted-name-chars =/ "+" ; Characters after last plus always
666 // ; specify a structured syntax suffix
667 #[rustfmt::skip]
668 static MEDIA_TYPE_VALID_BYTES: [bool; 256] = {
669 const __: bool = false;
670 const TT: bool = true;
671 [
672 // \0 HT LF CR
673 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
674 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
675 // \w ! " # $ % & ' ( ) * + , - . /
676 __, TT, __, TT, TT, __, TT, __, __, __, __, TT, __, TT, TT, __,
677 // 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
678 TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, __, __, __, __, __, __,
679 // @ A B C D E F G H I J K L M N O
680 __, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT,
681 // P Q R S T U V W X Y Z [ \ ] ^ _
682 TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, __, __, __, TT, TT,
683 // ` a b c d e f g h i j k l m n o
684 __, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT,
685 // p q r s t u v w x y z { | } ~ del
686 TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, TT, __, __, __, __, __,
687 // Expand ascii
688 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
689 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
690 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
691 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
692 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
693 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
694 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
695 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
696 ]
697 };
698
is_valid(v: &[u8]) -> bool699 fn is_valid(v: &[u8]) -> bool {
700 v.iter().all(|b| MEDIA_TYPE_VALID_BYTES[*b as usize])
701 }
702
703 #[cfg(test)]
704 mod ut_mime {
705 use super::{MimeType, MimeTypeTag};
706 use crate::error::{ErrorKind, HttpError};
707
708 /// UT test cases for `MimeTypeTag::from_bytes`.
709 ///
710 /// # Brief
711 /// 1. Creates a `MimeTypeTag` from `&[u8]`.
712 /// 2. Checks if the test results are correct.
713 #[test]
ut_mime_type_tag_from_bytes()714 fn ut_mime_type_tag_from_bytes() {
715 assert_eq!(
716 MimeTypeTag::from_bytes(b"application"),
717 Ok(MimeTypeTag::Application)
718 );
719 assert_eq!(MimeTypeTag::from_bytes(b"audio"), Ok(MimeTypeTag::Audio));
720 assert_eq!(MimeTypeTag::from_bytes(b"font"), Ok(MimeTypeTag::Font));
721 assert_eq!(MimeTypeTag::from_bytes(b"image"), Ok(MimeTypeTag::Image));
722 assert_eq!(
723 MimeTypeTag::from_bytes(b"message"),
724 Ok(MimeTypeTag::Message)
725 );
726 assert_eq!(MimeTypeTag::from_bytes(b"model"), Ok(MimeTypeTag::Model));
727 assert_eq!(
728 MimeTypeTag::from_bytes(b"multipart"),
729 Ok(MimeTypeTag::Multipart)
730 );
731 assert_eq!(MimeTypeTag::from_bytes(b"text"), Ok(MimeTypeTag::Text));
732 assert_eq!(MimeTypeTag::from_bytes(b"video"), Ok(MimeTypeTag::Video));
733 assert_eq!(MimeTypeTag::from_bytes(b"x-world"), Ok(MimeTypeTag::Xnew));
734 assert_eq!(
735 MimeTypeTag::from_bytes(b"APPLICATION"),
736 Ok(MimeTypeTag::Application)
737 );
738 assert_eq!(
739 MimeTypeTag::from_bytes(b"x-ab/cd"),
740 Err(HttpError::from(ErrorKind::InvalidInput))
741 );
742 assert_eq!(
743 MimeTypeTag::from_bytes(b"notype"),
744 Err(HttpError::from(ErrorKind::InvalidInput))
745 );
746 }
747
748 /// UT test cases for `MimeType::from_bytes`.
749 ///
750 /// # Brief
751 /// 1. Creates a `MimeType` from `&[u8]`.
752 /// 2. Checks if the test results are correct.
753 #[test]
ut_mime_type_from_bytes()754 fn ut_mime_type_from_bytes() {
755 assert_eq!(
756 MimeType::from_bytes(b"application/octet-stream"),
757 Ok(MimeType {
758 tag: MimeTypeTag::Application,
759 bytes: b"application/octet-stream",
760 slash: 11,
761 })
762 );
763
764 assert_eq!(
765 MimeType::from_bytes(b"TEXT/PLAIN"),
766 Ok(MimeType {
767 tag: MimeTypeTag::Text,
768 bytes: b"TEXT/PLAIN",
769 slash: 4,
770 })
771 );
772
773 assert_eq!(
774 MimeType::from_bytes(b"TEXT/~PLAIN"),
775 Err(HttpError::from(ErrorKind::InvalidInput))
776 );
777
778 assert_eq!(
779 MimeType::from_bytes(b"application/octet/stream"),
780 Err(HttpError::from(ErrorKind::InvalidInput))
781 );
782 }
783
784 /// UT test cases for `MimeType::from_path`.
785 ///
786 /// # Brief
787 /// 1. Creates a `MimeType` from path.
788 /// 2. Checks if the test results are correct.
789 #[test]
ut_mime_type_from_path()790 fn ut_mime_type_from_path() {
791 use std::path::Path;
792
793 use crate::error::HttpError;
794
795 assert_eq!(
796 MimeType::from_path(Path::new("./foo/bar.evy")),
797 MimeType::from_bytes(b"application/envoy")
798 );
799 assert_eq!(
800 MimeType::from_path(Path::new("foo.*")),
801 MimeType::from_bytes(b"application/octet-stream")
802 );
803 assert_eq!(
804 MimeType::from_path(Path::new("")),
805 Err(HttpError::from(ErrorKind::InvalidInput))
806 );
807 assert_eq!(
808 MimeType::from_path(Path::new(".txt")),
809 Err(HttpError::from(ErrorKind::InvalidInput))
810 );
811 assert_eq!(
812 MimeType::from_path(Path::new("./foo/bar")),
813 Err(HttpError::from(ErrorKind::InvalidInput))
814 );
815 }
816
817 /// UT test cases for `MimeType::main_type`.
818 ///
819 /// # Brief
820 /// 1. Gets main type string by `main_type`.
821 /// 2. Checks if the test results are correct.
822 #[test]
ut_mime_type_main_type()823 fn ut_mime_type_main_type() {
824 let mime_type = MimeType::from_bytes(b"application/octet-stream").unwrap();
825 assert_eq!(mime_type.main_type(), "application");
826
827 let mime_type = MimeType::from_bytes(b"TeXT/PLAIN").unwrap();
828 assert_eq!(mime_type.main_type(), "TeXT");
829 }
830
831 /// UT test cases for `MimeType::sub_type`.
832 ///
833 /// # Brief
834 /// 1. Gets subtype type string by `sub_type`.
835 /// 2. Checks if the test results are correct.
836 #[test]
ut_mimetype_sub_type()837 fn ut_mimetype_sub_type() {
838 let mime_type = MimeType::from_bytes(b"application/octet-stream").unwrap();
839 assert_eq!(mime_type.sub_type(), "octet-stream");
840 }
841
842 /// UT test cases for `MimeType::as_str`.
843 ///
844 /// # Brief
845 /// 1. Gets string from `MimeType` by `as_str`.
846 /// 2. Checks if the test results are correct.
847 #[test]
ut_mimetype_as_str()848 fn ut_mimetype_as_str() {
849 let mime_type = MimeType::from_bytes(b"application/pdf").unwrap();
850 assert_eq!(mime_type.as_str(), "application/pdf");
851
852 let mime_type = MimeType::from_bytes(b"application/octet-stream").unwrap();
853 assert_eq!(mime_type.as_str(), "application/octet-stream");
854 }
855
856 /// UT test cases for `Mimetype::eq`.
857 ///
858 /// # Brief
859 /// 1. Creates some `MimeType`, and check if they are equal.
860 /// 2. Checks if the test results are correct.
861 #[test]
ut_mime_type_eq()862 fn ut_mime_type_eq() {
863 assert_eq!(
864 MimeType {
865 tag: MimeTypeTag::Application,
866 bytes: b"application/octet-stream",
867 slash: 11,
868 },
869 MimeType {
870 tag: MimeTypeTag::Application,
871 bytes: b"application/octet-stream",
872 slash: 11,
873 }
874 );
875
876 assert_ne!(
877 MimeType::from_bytes(b"application/octet-stream"),
878 MimeType::from_bytes(b"application/pdf")
879 );
880
881 assert_eq!(
882 MimeType::from_extension("pdf"),
883 MimeType::from_bytes(b"application/pdf")
884 );
885 }
886
887 /// UT test cases for `MimeType::is_application`.
888 ///
889 /// # Brief
890 /// 1. Creates `MimeType` from `&[u8]` by `MimeType::from_bytes`.
891 /// 2. Checks whether the main types are correct.
892 #[test]
ut_mimetype_is_application()893 fn ut_mimetype_is_application() {
894 assert!(MimeType::from_bytes(b"application/pdf")
895 .unwrap()
896 .is_application());
897 assert!(!MimeType::from_bytes(b"audio/basic")
898 .unwrap()
899 .is_application());
900 }
901
902 /// UT test cases for `MimeType::is_audio`.
903 ///
904 /// # Brief
905 /// 1. Creates `MimeType` from `&[u8]` by `MimeType::from_bytes`.
906 /// 2. Checks whether the main types are correct.
907 #[test]
ut_mimetype_is_audio()908 fn ut_mimetype_is_audio() {
909 assert!(MimeType::from_bytes(b"audio/basic").unwrap().is_audio());
910 assert!(!MimeType::from_bytes(b"application/pdf").unwrap().is_audio());
911 }
912
913 /// UT test cases for `MimeType::is_font`.
914 ///
915 /// # Brief
916 /// 1. Creates `MimeType` from `&[u8]` by `MimeType::from_bytes`.
917 /// 2. Checks whether the main types are correct.
918 #[test]
ut_mime_type_is_font()919 fn ut_mime_type_is_font() {
920 assert!(MimeType::from_bytes(b"font/collection").unwrap().is_font());
921 assert!(!MimeType::from_bytes(b"application/pdf").unwrap().is_font());
922 }
923
924 /// UT test cases for `MimeType::is_image`.
925 ///
926 /// # Brief
927 /// 1. Creates `MimeType` from `&[u8]` by `MimeType::from_bytes`.
928 /// 2. Checks whether the main types are correct.
929 #[test]
ut_mimetype_is_image()930 fn ut_mimetype_is_image() {
931 assert!(MimeType::from_bytes(b"image/bmp").unwrap().is_image());
932 assert!(!MimeType::from_bytes(b"application/pdf").unwrap().is_image());
933 }
934
935 /// UT test cases for `MimeType::is_message`.
936 ///
937 /// # Brief
938 /// 1. Creates `MimeType` from `&[u8]` by `MimeType::from_bytes`.
939 /// 2. Checks whether the main types are correct.
940 #[test]
ut_mimetype_is_message()941 fn ut_mimetype_is_message() {
942 assert!(MimeType::from_bytes(b"message/example")
943 .unwrap()
944 .is_message());
945 assert!(!MimeType::from_bytes(b"application/pdf")
946 .unwrap()
947 .is_message());
948 }
949
950 /// UT test cases for `MimeType::is_model`.
951 ///
952 /// # Brief
953 /// 1. Creates `MimeType` from `&[u8]` by `MimeType::from_bytes`.
954 /// 2. Checks whether the main types are correct.
955 #[test]
ut_mimetype_is_model()956 fn ut_mimetype_is_model() {
957 assert!(MimeType::from_bytes(b"model/e57").unwrap().is_model());
958 assert!(!MimeType::from_bytes(b"application/pdf").unwrap().is_model());
959 }
960
961 /// UT test cases for `MimeType::is_multipart`.
962 ///
963 /// # Brief
964 /// 1. Creates `MimeType` from `&[u8]` by `MimeType::from_bytes`.
965 /// 2. Checks whether the main types are correct.
966 #[test]
ut_mime_type_is_multipart()967 fn ut_mime_type_is_multipart() {
968 assert!(MimeType::from_bytes(b"multipart/form-data")
969 .unwrap()
970 .is_multipart());
971 assert!(!MimeType::from_bytes(b"application/pdf")
972 .unwrap()
973 .is_multipart());
974 }
975
976 /// UT test cases for `MimeType::is_text`.
977 ///
978 /// # Brief
979 /// 1. Creates `MimeType` from `&[u8]` by `MimeType::from_bytes`.
980 /// 2. Checks whether the main types are correct.
981 #[test]
ut_mime_type_is_text()982 fn ut_mime_type_is_text() {
983 assert!(MimeType::from_bytes(b"text/csv").unwrap().is_text());
984 assert!(!MimeType::from_bytes(b"application/pdf").unwrap().is_text());
985 }
986
987 /// UT test cases for `MimeType::is_video`.
988 ///
989 /// # Brief
990 /// 1. Creates `MimeType` from `&[u8]` by `MimeType::from_bytes`.
991 /// 2. Checks whether the main types are correct.
992 #[test]
ut_mimetype_is_video()993 fn ut_mimetype_is_video() {
994 assert!(MimeType::from_bytes(b"video/mpeg").unwrap().is_video());
995 assert!(!MimeType::from_bytes(b"application/pdf").unwrap().is_video());
996 }
997
998 /// UT test cases for `MimeType::is_xnew`.
999 ///
1000 /// # Brief
1001 /// 1. Creates `MimeType` from `&[u8]` by `MimeType::from_bytes`.
1002 /// 2. Checks whether the main types are correct.
1003 #[test]
ut_mime_type_is_xnew()1004 fn ut_mime_type_is_xnew() {
1005 assert!(MimeType::from_bytes(b"x-world/x-vrml").unwrap().is_xnew());
1006 assert!(!MimeType::from_bytes(b"application/pdf").unwrap().is_xnew());
1007 }
1008 }
1009