devserver_lib/
lib.rs

1/// A local host only for serving static files.
2/// Simple and easy, but not robust or tested.
3
4#[cfg(feature = "https")]
5use native_tls::{Identity, TlsAcceptor};
6#[cfg(feature = "https")]
7use std::sync::Arc;
8
9use std::ffi::OsStr;
10use std::fs;
11use std::io::BufRead;
12use std::io::{Read, Write};
13use std::net::TcpListener;
14use std::path::Path;
15use std::str;
16use std::thread;
17
18#[cfg(feature = "reload")]
19mod reload;
20
21pub fn read_header<T: Read + Write>(stream: &mut T) -> Vec<u8> {
22    let mut buffer = Vec::new();
23    let mut reader = std::io::BufReader::new(stream);
24    loop {
25        reader.read_until(b'\n', &mut buffer).unwrap();
26        // Read until end of header.
27        if buffer.ends_with(b"\r\n\r\n") {
28            break;
29        }
30    }
31    buffer
32}
33
34#[allow(unused)]
35fn handle_client<T: Read + Write>(mut stream: T, root_path: &str, reload: bool, headers: &str) {
36    let buffer = read_header(&mut stream);
37    let request_string = str::from_utf8(&buffer).unwrap();
38
39    if request_string.is_empty() {
40        return;
41    }
42
43    // Split the request into different parts.
44    let mut parts = request_string.split(' ');
45
46    let _method = parts.next().unwrap().trim();
47    let mut path = parts.next().unwrap().trim();
48    let _http_version = parts.next().unwrap().trim();
49
50    // Trim parameters from URL
51    if let Some(parameters_index) = path.find('?') {
52        path = &path[..parameters_index];
53    }
54
55    // Replace white space characters with proper whitespace and remove any paths that refer to the parent.
56    let path = path.replace("../", "").replace("%20", " ");
57    let path = if path.ends_with("/") {
58        Path::new(root_path).join(Path::new(&format!(
59            "{}{}",
60            path.trim_start_matches('/'),
61            "index.html"
62        )))
63    } else {
64        Path::new(root_path).join(path.trim_matches('/'))
65    };
66
67    let extension = path.extension().and_then(OsStr::to_str);
68
69    let (file_contents, extension) = if extension != None {
70        (fs::read(&path), extension)
71    } else {
72        // If the request has no extension look first for a matching file without an extension
73        if let Ok(file_contents) = fs::read(&path) {
74            println!("WARNING: Serving file without extension: [ {} ] with media type 'application/octet-stream'", &path.to_str().unwrap());
75            (Ok(file_contents), None)
76        } else {
77            // If no file without an extension is found see if there's a file with a ".html" extension
78            // This enables "pretty URLs" without a trailing `/` like: `example.com/blog-post`
79            let file = fs::read(&path.with_extension("html"));
80            (file, Some("html"))
81        }
82    };
83
84    if let Ok(mut file_contents) = file_contents {
85        // Pair the file extension to a media (also known as MIME) type.
86        let content_type = extension_to_mime_impl(extension);
87
88        #[allow(unused_mut)]
89        let mut content_length = file_contents.len();
90
91        // Prepare to inject code into HTML if reload is enabled.
92        #[cfg(feature = "reload")]
93        let reload_append = include_bytes!("reload.html");
94        #[cfg(feature = "reload")]
95        {
96            if extension == Some("html") && reload {
97                content_length += reload_append.len();
98            }
99        }
100
101        let response = format!(
102            "HTTP/1.1 200 OK\r\nContent-type: {}\r\nContent-Length: {}{}\r\n\r\n",
103            content_type, content_length, headers
104        );
105
106        let mut bytes = response.as_bytes().to_vec();
107        bytes.append(&mut file_contents);
108        stream.write_all(&bytes).unwrap();
109
110        // Inject code into HTML if reload is enabled
111        #[cfg(feature = "reload")]
112        {
113            if extension == Some("html") && reload {
114                // Insert javascript for reloading
115                stream.write_all(reload_append).unwrap();
116            }
117        }
118
119        stream.flush().unwrap();
120    } else {
121        println!("Could not find file: {}", path.to_str().unwrap());
122        let response = "HTTP/1.1 404 NOT FOUND\r\n\r\n";
123        stream.write_all(response.as_bytes()).unwrap();
124        stream.flush().unwrap();
125    }
126}
127
128pub fn run(address: &str, port: u32, path: &str, reload: bool, headers: &str) {
129    #[cfg(feature = "https")]
130    let acceptor = {
131        // Hard coded certificate generated with the following commands:
132        // openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 36500 -nodes -subj "/"
133        // openssl pkcs12 -export -out identity.pfx -inkey key.pem -in cert.pem
134        // password for second command: 'debug'
135        let bytes = include_bytes!("identity.pfx");
136        let identity = Identity::from_pkcs12(bytes, "debug").unwrap();
137        Arc::new(TlsAcceptor::new(identity).unwrap())
138    };
139
140    #[cfg(feature = "reload")]
141    {
142        if reload {
143            let address = address.to_owned();
144            let path = path.to_owned();
145            thread::spawn(move || {
146                reload::watch_for_reloads(&address, &path);
147            });
148        }
149    }
150
151    let address_with_port = format!("{}:{:?}", address, port);
152    let listener = TcpListener::bind(address_with_port).unwrap();
153    for stream in listener.incoming() {
154        if let Ok(stream) = stream {
155            #[cfg(feature = "https")]
156            let acceptor = acceptor.clone();
157
158            let path = path.to_owned();
159            let headers = headers.to_owned();
160            thread::spawn(move || {
161                // HTTP requests always begin with a verb like 'GET'.
162                // HTTPS requests begin with a number, so peeking and checking for a number
163                // is used to determine if a request is HTTPS or HTTP
164                let mut buf = [0; 2];
165                stream.peek(&mut buf).expect("peek failed");
166
167                #[cfg(feature = "https")]
168                let is_https =
169                    !((buf[0] as char).is_alphabetic() && (buf[1] as char).is_alphabetic());
170
171                #[cfg(not(feature = "https"))]
172                let is_https = false;
173
174                if is_https {
175                    // acceptor.accept will block indefinitely if called with an HTTP stream.
176                    #[cfg(feature = "https")]
177                    if let Ok(stream) = acceptor.accept(stream) {
178                        handle_client(stream, &path, reload, &headers);
179                    }
180                } else {
181                    handle_client(stream, &path, reload, &headers);
182                }
183            });
184        }
185    }
186}
187
188/// Taken from Rouille:
189/// https://github.com/tomaka/rouille/blob/master/src/assets.rs
190/// Returns the mime type of a file based on its extension.
191fn extension_to_mime_impl(extension: Option<&str>) -> &'static str {
192    // List taken from https://github.com/cybergeek94/mime_guess/blob/master/src/mime_types.rs,
193    // itself taken from a dead link.
194    match extension {
195        Some("323") => "text/h323; charset=utf8",
196        Some("3g2") => "video/3gpp2",
197        Some("3gp") => "video/3gpp",
198        Some("3gp2") => "video/3gpp2",
199        Some("3gpp") => "video/3gpp",
200        Some("7z") => "application/x-7z-compressed",
201        Some("aa") => "audio/audible",
202        Some("aac") => "audio/aac",
203        Some("aaf") => "application/octet-stream",
204        Some("aax") => "audio/vnd.audible.aax",
205        Some("ac3") => "audio/ac3",
206        Some("aca") => "application/octet-stream",
207        Some("accda") => "application/msaccess.addin",
208        Some("accdb") => "application/msaccess",
209        Some("accdc") => "application/msaccess.cab",
210        Some("accde") => "application/msaccess",
211        Some("accdr") => "application/msaccess.runtime",
212        Some("accdt") => "application/msaccess",
213        Some("accdw") => "application/msaccess.webapplication",
214        Some("accft") => "application/msaccess.ftemplate",
215        Some("acx") => "application/internet-property-stream",
216        Some("addin") => "application/xml",
217        Some("ade") => "application/msaccess",
218        Some("adobebridge") => "application/x-bridge-url",
219        Some("adp") => "application/msaccess",
220        Some("adt") => "audio/vnd.dlna.adts",
221        Some("adts") => "audio/aac",
222        Some("afm") => "application/octet-stream",
223        Some("ai") => "application/postscript",
224        Some("aif") => "audio/x-aiff",
225        Some("aifc") => "audio/aiff",
226        Some("aiff") => "audio/aiff",
227        Some("air") => "application/vnd.adobe.air-application-installer-package+zip",
228        Some("amc") => "application/x-mpeg",
229        Some("application") => "application/x-ms-application",
230        Some("art") => "image/x-jg",
231        Some("asa") => "application/xml",
232        Some("asax") => "application/xml",
233        Some("ascx") => "application/xml",
234        Some("asd") => "application/octet-stream",
235        Some("asf") => "video/x-ms-asf",
236        Some("ashx") => "application/xml",
237        Some("asi") => "application/octet-stream",
238        Some("asm") => "text/plain; charset=utf8",
239        Some("asmx") => "application/xml",
240        Some("aspx") => "application/xml",
241        Some("asr") => "video/x-ms-asf",
242        Some("asx") => "video/x-ms-asf",
243        Some("atom") => "application/atom+xml",
244        Some("au") => "audio/basic",
245        Some("avi") => "video/x-msvideo",
246        Some("axs") => "application/olescript",
247        Some("bas") => "text/plain; charset=utf8",
248        Some("bcpio") => "application/x-bcpio",
249        Some("bin") => "application/octet-stream",
250        Some("bmp") => "image/bmp",
251        Some("c") => "text/plain; charset=utf8",
252        Some("cab") => "application/octet-stream",
253        Some("caf") => "audio/x-caf",
254        Some("calx") => "application/vnd.ms-office.calx",
255        Some("cat") => "application/vnd.ms-pki.seccat",
256        Some("cc") => "text/plain; charset=utf8",
257        Some("cd") => "text/plain; charset=utf8",
258        Some("cdda") => "audio/aiff",
259        Some("cdf") => "application/x-cdf",
260        Some("cer") => "application/x-x509-ca-cert",
261        Some("chm") => "application/octet-stream",
262        Some("class") => "application/x-java-applet",
263        Some("clp") => "application/x-msclip",
264        Some("cmx") => "image/x-cmx",
265        Some("cnf") => "text/plain; charset=utf8",
266        Some("cod") => "image/cis-cod",
267        Some("config") => "application/xml",
268        Some("contact") => "text/x-ms-contact; charset=utf8",
269        Some("coverage") => "application/xml",
270        Some("cpio") => "application/x-cpio",
271        Some("cpp") => "text/plain; charset=utf8",
272        Some("crd") => "application/x-mscardfile",
273        Some("crl") => "application/pkix-crl",
274        Some("crt") => "application/x-x509-ca-cert",
275        Some("cs") => "text/plain; charset=utf8",
276        Some("csdproj") => "text/plain; charset=utf8",
277        Some("csh") => "application/x-csh",
278        Some("csproj") => "text/plain; charset=utf8",
279        Some("css") => "text/css; charset=utf8",
280        Some("csv") => "text/csv; charset=utf8",
281        Some("cur") => "application/octet-stream",
282        Some("cxx") => "text/plain; charset=utf8",
283        Some("dat") => "application/octet-stream",
284        Some("datasource") => "application/xml",
285        Some("dbproj") => "text/plain; charset=utf8",
286        Some("dcr") => "application/x-director",
287        Some("def") => "text/plain; charset=utf8",
288        Some("deploy") => "application/octet-stream",
289        Some("der") => "application/x-x509-ca-cert",
290        Some("dgml") => "application/xml",
291        Some("dib") => "image/bmp",
292        Some("dif") => "video/x-dv",
293        Some("dir") => "application/x-director",
294        Some("disco") => "application/xml",
295        Some("dll") => "application/x-msdownload",
296        Some("dll.config") => "application/xml",
297        Some("dlm") => "text/dlm; charset=utf8",
298        Some("doc") => "application/msword",
299        Some("docm") => "application/vnd.ms-word.document.macroEnabled.12",
300        Some("docx") => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
301        Some("dot") => "application/msword",
302        Some("dotm") => "application/vnd.ms-word.template.macroEnabled.12",
303        Some("dotx") => "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
304        Some("dsp") => "application/octet-stream",
305        Some("dsw") => "text/plain; charset=utf8",
306        Some("dtd") => "application/xml",
307        Some("dtsConfig") => "application/xml",
308        Some("dv") => "video/x-dv",
309        Some("dvi") => "application/x-dvi",
310        Some("dwf") => "drawing/x-dwf",
311        Some("dwp") => "application/octet-stream",
312        Some("dxr") => "application/x-director",
313        Some("eml") => "message/rfc822",
314        Some("emz") => "application/octet-stream",
315        Some("eot") => "application/vnd.ms-fontobject",
316        Some("eps") => "application/postscript",
317        Some("etl") => "application/etl",
318        Some("etx") => "text/x-setext; charset=utf8",
319        Some("evy") => "application/envoy",
320        Some("exe") => "application/octet-stream",
321        Some("exe.config") => "application/xml",
322        Some("fdf") => "application/vnd.fdf",
323        Some("fif") => "application/fractals",
324        Some("filters") => "Application/xml",
325        Some("fla") => "application/octet-stream",
326        Some("flr") => "x-world/x-vrml",
327        Some("flv") => "video/x-flv",
328        Some("fsscript") => "application/fsharp-script",
329        Some("fsx") => "application/fsharp-script",
330        Some("generictest") => "application/xml",
331        Some("gif") => "image/gif",
332        Some("group") => "text/x-ms-group; charset=utf8",
333        Some("gsm") => "audio/x-gsm",
334        Some("gtar") => "application/x-gtar",
335        Some("gz") => "application/x-gzip",
336        Some("h") => "text/plain; charset=utf8",
337        Some("hdf") => "application/x-hdf",
338        Some("hdml") => "text/x-hdml; charset=utf8",
339        Some("hhc") => "application/x-oleobject",
340        Some("hhk") => "application/octet-stream",
341        Some("hhp") => "application/octet-stream",
342        Some("hlp") => "application/winhlp",
343        Some("hpp") => "text/plain; charset=utf8",
344        Some("hqx") => "application/mac-binhex40",
345        Some("hta") => "application/hta",
346        Some("htc") => "text/x-component; charset=utf8",
347        Some("htm") => "text/html; charset=utf8",
348        Some("html") => "text/html; charset=utf8",
349        Some("htt") => "text/webviewhtml; charset=utf8",
350        Some("hxa") => "application/xml",
351        Some("hxc") => "application/xml",
352        Some("hxd") => "application/octet-stream",
353        Some("hxe") => "application/xml",
354        Some("hxf") => "application/xml",
355        Some("hxh") => "application/octet-stream",
356        Some("hxi") => "application/octet-stream",
357        Some("hxk") => "application/xml",
358        Some("hxq") => "application/octet-stream",
359        Some("hxr") => "application/octet-stream",
360        Some("hxs") => "application/octet-stream",
361        Some("hxt") => "text/html; charset=utf8",
362        Some("hxv") => "application/xml",
363        Some("hxw") => "application/octet-stream",
364        Some("hxx") => "text/plain; charset=utf8",
365        Some("i") => "text/plain; charset=utf8",
366        Some("ico") => "image/x-icon",
367        Some("ics") => "application/octet-stream",
368        Some("idl") => "text/plain; charset=utf8",
369        Some("ief") => "image/ief",
370        Some("iii") => "application/x-iphone",
371        Some("inc") => "text/plain; charset=utf8",
372        Some("inf") => "application/octet-stream",
373        Some("inl") => "text/plain; charset=utf8",
374        Some("ins") => "application/x-internet-signup",
375        Some("ipa") => "application/x-itunes-ipa",
376        Some("ipg") => "application/x-itunes-ipg",
377        Some("ipproj") => "text/plain; charset=utf8",
378        Some("ipsw") => "application/x-itunes-ipsw",
379        Some("iqy") => "text/x-ms-iqy; charset=utf8",
380        Some("isp") => "application/x-internet-signup",
381        Some("ite") => "application/x-itunes-ite",
382        Some("itlp") => "application/x-itunes-itlp",
383        Some("itms") => "application/x-itunes-itms",
384        Some("itpc") => "application/x-itunes-itpc",
385        Some("ivf") => "video/x-ivf",
386        Some("jar") => "application/java-archive",
387        Some("java") => "application/octet-stream",
388        Some("jck") => "application/liquidmotion",
389        Some("jcz") => "application/liquidmotion",
390        Some("jfif") => "image/pjpeg",
391        Some("jnlp") => "application/x-java-jnlp-file",
392        Some("jpb") => "application/octet-stream",
393        Some("jpe") => "image/jpeg",
394        Some("jpeg") => "image/jpeg",
395        Some("jpg") => "image/jpeg",
396        Some("js") => "application/javascript",
397        Some("json") => "application/json",
398        Some("jsx") => "text/jscript; charset=utf8",
399        Some("jsxbin") => "text/plain; charset=utf8",
400        Some("latex") => "application/x-latex",
401        Some("library-ms") => "application/windows-library+xml",
402        Some("lit") => "application/x-ms-reader",
403        Some("loadtest") => "application/xml",
404        Some("lpk") => "application/octet-stream",
405        Some("lsf") => "video/x-la-asf",
406        Some("lst") => "text/plain; charset=utf8",
407        Some("lsx") => "video/x-la-asf",
408        Some("lzh") => "application/octet-stream",
409        Some("m13") => "application/x-msmediaview",
410        Some("m14") => "application/x-msmediaview",
411        Some("m1v") => "video/mpeg",
412        Some("m2t") => "video/vnd.dlna.mpeg-tts",
413        Some("m2ts") => "video/vnd.dlna.mpeg-tts",
414        Some("m2v") => "video/mpeg",
415        Some("m3u") => "audio/x-mpegurl",
416        Some("m3u8") => "audio/x-mpegurl",
417        Some("m4a") => "audio/m4a",
418        Some("m4b") => "audio/m4b",
419        Some("m4p") => "audio/m4p",
420        Some("m4r") => "audio/x-m4r",
421        Some("m4v") => "video/x-m4v",
422        Some("mac") => "image/x-macpaint",
423        Some("mak") => "text/plain; charset=utf8",
424        Some("man") => "application/x-troff-man",
425        Some("manifest") => "application/x-ms-manifest",
426        Some("map") => "text/plain; charset=utf8",
427        Some("master") => "application/xml",
428        Some("mda") => "application/msaccess",
429        Some("mdb") => "application/x-msaccess",
430        Some("mde") => "application/msaccess",
431        Some("mdp") => "application/octet-stream",
432        Some("me") => "application/x-troff-me",
433        Some("mfp") => "application/x-shockwave-flash",
434        Some("mht") => "message/rfc822",
435        Some("mhtml") => "message/rfc822",
436        Some("mid") => "audio/mid",
437        Some("midi") => "audio/mid",
438        Some("mix") => "application/octet-stream",
439        Some("mjs") => "application/javascript",
440        Some("mk") => "text/plain; charset=utf8",
441        Some("mmf") => "application/x-smaf",
442        Some("mno") => "application/xml",
443        Some("mny") => "application/x-msmoney",
444        Some("mod") => "video/mpeg",
445        Some("mov") => "video/quicktime",
446        Some("movie") => "video/x-sgi-movie",
447        Some("mp2") => "video/mpeg",
448        Some("mp2v") => "video/mpeg",
449        Some("mp3") => "audio/mpeg",
450        Some("mp4") => "video/mp4",
451        Some("mp4v") => "video/mp4",
452        Some("mpa") => "video/mpeg",
453        Some("mpe") => "video/mpeg",
454        Some("mpeg") => "video/mpeg",
455        Some("mpf") => "application/vnd.ms-mediapackage",
456        Some("mpg") => "video/mpeg",
457        Some("mpp") => "application/vnd.ms-project",
458        Some("mpv2") => "video/mpeg",
459        Some("mqv") => "video/quicktime",
460        Some("ms") => "application/x-troff-ms",
461        Some("msi") => "application/octet-stream",
462        Some("mso") => "application/octet-stream",
463        Some("mts") => "video/vnd.dlna.mpeg-tts",
464        Some("mtx") => "application/xml",
465        Some("mvb") => "application/x-msmediaview",
466        Some("mvc") => "application/x-miva-compiled",
467        Some("mxp") => "application/x-mmxp",
468        Some("nc") => "application/x-netcdf",
469        Some("nsc") => "video/x-ms-asf",
470        Some("nws") => "message/rfc822",
471        Some("ocx") => "application/octet-stream",
472        Some("oda") => "application/oda",
473        Some("odc") => "text/x-ms-odc; charset=utf8",
474        Some("odh") => "text/plain; charset=utf8",
475        Some("odl") => "text/plain; charset=utf8",
476        Some("odp") => "application/vnd.oasis.opendocument.presentation",
477        Some("ods") => "application/oleobject",
478        Some("odt") => "application/vnd.oasis.opendocument.text",
479        Some("ogg") => "application/ogg",
480        Some("one") => "application/onenote",
481        Some("onea") => "application/onenote",
482        Some("onepkg") => "application/onenote",
483        Some("onetmp") => "application/onenote",
484        Some("onetoc") => "application/onenote",
485        Some("onetoc2") => "application/onenote",
486        Some("orderedtest") => "application/xml",
487        Some("osdx") => "application/opensearchdescription+xml",
488        Some("otf") => "application/x-font-opentype",
489        Some("p10") => "application/pkcs10",
490        Some("p12") => "application/x-pkcs12",
491        Some("p7b") => "application/x-pkcs7-certificates",
492        Some("p7c") => "application/pkcs7-mime",
493        Some("p7m") => "application/pkcs7-mime",
494        Some("p7r") => "application/x-pkcs7-certreqresp",
495        Some("p7s") => "application/pkcs7-signature",
496        Some("pbm") => "image/x-portable-bitmap",
497        Some("pcast") => "application/x-podcast",
498        Some("pct") => "image/pict",
499        Some("pcx") => "application/octet-stream",
500        Some("pcz") => "application/octet-stream",
501        Some("pdf") => "application/pdf",
502        Some("pfb") => "application/octet-stream",
503        Some("pfm") => "application/octet-stream",
504        Some("pfx") => "application/x-pkcs12",
505        Some("pgm") => "image/x-portable-graymap",
506        Some("pic") => "image/pict",
507        Some("pict") => "image/pict",
508        Some("pkgdef") => "text/plain; charset=utf8",
509        Some("pkgundef") => "text/plain; charset=utf8",
510        Some("pko") => "application/vnd.ms-pki.pko",
511        Some("pls") => "audio/scpls",
512        Some("pma") => "application/x-perfmon",
513        Some("pmc") => "application/x-perfmon",
514        Some("pml") => "application/x-perfmon",
515        Some("pmr") => "application/x-perfmon",
516        Some("pmw") => "application/x-perfmon",
517        Some("png") => "image/png",
518        Some("pnm") => "image/x-portable-anymap",
519        Some("pnt") => "image/x-macpaint",
520        Some("pntg") => "image/x-macpaint",
521        Some("pnz") => "image/png",
522        Some("pot") => "application/vnd.ms-powerpoint",
523        Some("potm") => "application/vnd.ms-powerpoint.template.macroEnabled.12",
524        Some("potx") => "application/vnd.openxmlformats-officedocument.presentationml.template",
525        Some("ppa") => "application/vnd.ms-powerpoint",
526        Some("ppam") => "application/vnd.ms-powerpoint.addin.macroEnabled.12",
527        Some("ppm") => "image/x-portable-pixmap",
528        Some("pps") => "application/vnd.ms-powerpoint",
529        Some("ppsm") => "application/vnd.ms-powerpoint.slideshow.macroEnabled.12",
530        Some("ppsx") => "application/vnd.openxmlformats-officedocument.presentationml.slideshow",
531        Some("ppt") => "application/vnd.ms-powerpoint",
532        Some("pptm") => "application/vnd.ms-powerpoint.presentation.macroEnabled.12",
533        Some("pptx") => "application/vnd.openxmlformats-officedocument.presentationml.presentation",
534        Some("prf") => "application/pics-rules",
535        Some("prm") => "application/octet-stream",
536        Some("prx") => "application/octet-stream",
537        Some("ps") => "application/postscript",
538        Some("psc1") => "application/PowerShell",
539        Some("psd") => "application/octet-stream",
540        Some("psess") => "application/xml",
541        Some("psm") => "application/octet-stream",
542        Some("psp") => "application/octet-stream",
543        Some("pub") => "application/x-mspublisher",
544        Some("pwz") => "application/vnd.ms-powerpoint",
545        Some("qht") => "text/x-html-insertion; charset=utf8",
546        Some("qhtm") => "text/x-html-insertion; charset=utf8",
547        Some("qt") => "video/quicktime",
548        Some("qti") => "image/x-quicktime",
549        Some("qtif") => "image/x-quicktime",
550        Some("qtl") => "application/x-quicktimeplayer",
551        Some("qxd") => "application/octet-stream",
552        Some("ra") => "audio/x-pn-realaudio",
553        Some("ram") => "audio/x-pn-realaudio",
554        Some("rar") => "application/octet-stream",
555        Some("ras") => "image/x-cmu-raster",
556        Some("rat") => "application/rat-file",
557        Some("rc") => "text/plain; charset=utf8",
558        Some("rc2") => "text/plain; charset=utf8",
559        Some("rct") => "text/plain; charset=utf8",
560        Some("rdlc") => "application/xml",
561        Some("resx") => "application/xml",
562        Some("rf") => "image/vnd.rn-realflash",
563        Some("rgb") => "image/x-rgb",
564        Some("rgs") => "text/plain; charset=utf8",
565        Some("rm") => "application/vnd.rn-realmedia",
566        Some("rmi") => "audio/mid",
567        Some("rmp") => "application/vnd.rn-rn_music_package",
568        Some("roff") => "application/x-troff",
569        Some("rpm") => "audio/x-pn-realaudio-plugin",
570        Some("rqy") => "text/x-ms-rqy; charset=utf8",
571        Some("rtf") => "application/rtf",
572        Some("rtx") => "text/richtext; charset=utf8",
573        Some("ruleset") => "application/xml",
574        Some("s") => "text/plain; charset=utf8",
575        Some("safariextz") => "application/x-safari-safariextz",
576        Some("scd") => "application/x-msschedule",
577        Some("sct") => "text/scriptlet; charset=utf8",
578        Some("sd2") => "audio/x-sd2",
579        Some("sdp") => "application/sdp",
580        Some("sea") => "application/octet-stream",
581        Some("searchConnector-ms") => "application/windows-search-connector+xml",
582        Some("setpay") => "application/set-payment-initiation",
583        Some("setreg") => "application/set-registration-initiation",
584        Some("settings") => "application/xml",
585        Some("sfnt") => "application/font-sfnt",
586        Some("sgimb") => "application/x-sgimb",
587        Some("sgml") => "text/sgml; charset=utf8",
588        Some("sh") => "application/x-sh",
589        Some("shar") => "application/x-shar",
590        Some("shtml") => "text/html; charset=utf8",
591        Some("sit") => "application/x-stuffit",
592        Some("sitemap") => "application/xml",
593        Some("skin") => "application/xml",
594        Some("sldm") => "application/vnd.ms-powerpoint.slide.macroEnabled.12",
595        Some("sldx") => "application/vnd.openxmlformats-officedocument.presentationml.slide",
596        Some("slk") => "application/vnd.ms-excel",
597        Some("sln") => "text/plain; charset=utf8",
598        Some("slupkg-ms") => "application/x-ms-license",
599        Some("smd") => "audio/x-smd",
600        Some("smi") => "application/octet-stream",
601        Some("smx") => "audio/x-smd",
602        Some("smz") => "audio/x-smd",
603        Some("snd") => "audio/basic",
604        Some("snippet") => "application/xml",
605        Some("snp") => "application/octet-stream",
606        Some("sol") => "text/plain; charset=utf8",
607        Some("sor") => "text/plain; charset=utf8",
608        Some("spc") => "application/x-pkcs7-certificates",
609        Some("spl") => "application/futuresplash",
610        Some("src") => "application/x-wais-source",
611        Some("srf") => "text/plain; charset=utf8",
612        Some("ssisdeploymentmanifest") => "application/xml",
613        Some("ssm") => "application/streamingmedia",
614        Some("sst") => "application/vnd.ms-pki.certstore",
615        Some("stl") => "application/vnd.ms-pki.stl",
616        Some("sv4cpio") => "application/x-sv4cpio",
617        Some("sv4crc") => "application/x-sv4crc",
618        Some("svc") => "application/xml",
619        Some("svg") => "image/svg+xml",
620        Some("swf") => "application/x-shockwave-flash",
621        Some("t") => "application/x-troff",
622        Some("tar") => "application/x-tar",
623        Some("tcl") => "application/x-tcl",
624        Some("testrunconfig") => "application/xml",
625        Some("testsettings") => "application/xml",
626        Some("tex") => "application/x-tex",
627        Some("texi") => "application/x-texinfo",
628        Some("texinfo") => "application/x-texinfo",
629        Some("tgz") => "application/x-compressed",
630        Some("thmx") => "application/vnd.ms-officetheme",
631        Some("thn") => "application/octet-stream",
632        Some("tif") => "image/tiff",
633        Some("tiff") => "image/tiff",
634        Some("tlh") => "text/plain; charset=utf8",
635        Some("tli") => "text/plain; charset=utf8",
636        Some("toc") => "application/octet-stream",
637        Some("tr") => "application/x-troff",
638        Some("trm") => "application/x-msterminal",
639        Some("trx") => "application/xml",
640        Some("ts") => "video/vnd.dlna.mpeg-tts",
641        Some("tsv") => "text/tab-separated-values; charset=utf8",
642        Some("ttf") => "application/x-font-ttf",
643        Some("tts") => "video/vnd.dlna.mpeg-tts",
644        Some("txt") => "text/plain; charset=utf8",
645        Some("u32") => "application/octet-stream",
646        Some("uls") => "text/iuls; charset=utf8",
647        Some("user") => "text/plain; charset=utf8",
648        Some("ustar") => "application/x-ustar",
649        Some("vb") => "text/plain; charset=utf8",
650        Some("vbdproj") => "text/plain; charset=utf8",
651        Some("vbk") => "video/mpeg",
652        Some("vbproj") => "text/plain; charset=utf8",
653        Some("vbs") => "text/vbscript; charset=utf8",
654        Some("vcf") => "text/x-vcard; charset=utf8",
655        Some("vcproj") => "Application/xml",
656        Some("vcs") => "text/plain; charset=utf8",
657        Some("vcxproj") => "Application/xml",
658        Some("vddproj") => "text/plain; charset=utf8",
659        Some("vdp") => "text/plain; charset=utf8",
660        Some("vdproj") => "text/plain; charset=utf8",
661        Some("vdx") => "application/vnd.ms-visio.viewer",
662        Some("vml") => "application/xml",
663        Some("vscontent") => "application/xml",
664        Some("vsct") => "application/xml",
665        Some("vsd") => "application/vnd.visio",
666        Some("vsi") => "application/ms-vsi",
667        Some("vsix") => "application/vsix",
668        Some("vsixlangpack") => "application/xml",
669        Some("vsixmanifest") => "application/xml",
670        Some("vsmdi") => "application/xml",
671        Some("vspscc") => "text/plain; charset=utf8",
672        Some("vss") => "application/vnd.visio",
673        Some("vsscc") => "text/plain; charset=utf8",
674        Some("vssettings") => "application/xml",
675        Some("vssscc") => "text/plain; charset=utf8",
676        Some("vst") => "application/vnd.visio",
677        Some("vstemplate") => "application/xml",
678        Some("vsto") => "application/x-ms-vsto",
679        Some("vsw") => "application/vnd.visio",
680        Some("vsx") => "application/vnd.visio",
681        Some("vtx") => "application/vnd.visio",
682        Some("wasm") => "application/wasm",
683        Some("wav") => "audio/wav",
684        Some("wave") => "audio/wav",
685        Some("wax") => "audio/x-ms-wax",
686        Some("wbk") => "application/msword",
687        Some("wbmp") => "image/vnd.wap.wbmp",
688        Some("wcm") => "application/vnd.ms-works",
689        Some("wdb") => "application/vnd.ms-works",
690        Some("wdp") => "image/vnd.ms-photo",
691        Some("webarchive") => "application/x-safari-webarchive",
692        Some("webtest") => "application/xml",
693        Some("wiq") => "application/xml",
694        Some("wiz") => "application/msword",
695        Some("wks") => "application/vnd.ms-works",
696        Some("wlmp") => "application/wlmoviemaker",
697        Some("wlpginstall") => "application/x-wlpg-detect",
698        Some("wlpginstall3") => "application/x-wlpg3-detect",
699        Some("wm") => "video/x-ms-wm",
700        Some("wma") => "audio/x-ms-wma",
701        Some("wmd") => "application/x-ms-wmd",
702        Some("wmf") => "application/x-msmetafile",
703        Some("wml") => "text/vnd.wap.wml; charset=utf8",
704        Some("wmlc") => "application/vnd.wap.wmlc",
705        Some("wmls") => "text/vnd.wap.wmlscript; charset=utf8",
706        Some("wmlsc") => "application/vnd.wap.wmlscriptc",
707        Some("wmp") => "video/x-ms-wmp",
708        Some("wmv") => "video/x-ms-wmv",
709        Some("wmx") => "video/x-ms-wmx",
710        Some("wmz") => "application/x-ms-wmz",
711        Some("woff") => "application/font-woff",
712        Some("woff2") => "application/font-woff2",
713        Some("wpl") => "application/vnd.ms-wpl",
714        Some("wps") => "application/vnd.ms-works",
715        Some("wri") => "application/x-mswrite",
716        Some("wrl") => "x-world/x-vrml",
717        Some("wrz") => "x-world/x-vrml",
718        Some("wsc") => "text/scriptlet; charset=utf8",
719        Some("wsdl") => "application/xml",
720        Some("wvx") => "video/x-ms-wvx",
721        Some("x") => "application/directx",
722        Some("xaf") => "x-world/x-vrml",
723        Some("xaml") => "application/xaml+xml",
724        Some("xap") => "application/x-silverlight-app",
725        Some("xbap") => "application/x-ms-xbap",
726        Some("xbm") => "image/x-xbitmap",
727        Some("xdr") => "text/plain; charset=utf8",
728        Some("xht") => "application/xhtml+xml",
729        Some("xhtml") => "application/xhtml+xml",
730        Some("xla") => "application/vnd.ms-excel",
731        Some("xlam") => "application/vnd.ms-excel.addin.macroEnabled.12",
732        Some("xlc") => "application/vnd.ms-excel",
733        Some("xld") => "application/vnd.ms-excel",
734        Some("xlk") => "application/vnd.ms-excel",
735        Some("xll") => "application/vnd.ms-excel",
736        Some("xlm") => "application/vnd.ms-excel",
737        Some("xls") => "application/vnd.ms-excel",
738        Some("xlsb") => "application/vnd.ms-excel.sheet.binary.macroEnabled.12",
739        Some("xlsm") => "application/vnd.ms-excel.sheet.macroEnabled.12",
740        Some("xlsx") => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
741        Some("xlt") => "application/vnd.ms-excel",
742        Some("xltm") => "application/vnd.ms-excel.template.macroEnabled.12",
743        Some("xltx") => "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
744        Some("xlw") => "application/vnd.ms-excel",
745        Some("xml") => "application/xml",
746        Some("xmta") => "application/xml",
747        Some("xof") => "x-world/x-vrml",
748        Some("xoml") => "text/plain; charset=utf8",
749        Some("xpm") => "image/x-xpixmap",
750        Some("xps") => "application/vnd.ms-xpsdocument",
751        Some("xrm-ms") => "application/xml",
752        Some("xsc") => "application/xml",
753        Some("xsd") => "application/xml",
754        Some("xsf") => "application/xml",
755        Some("xsl") => "application/xml",
756        Some("xslt") => "application/xslt+xml",
757        Some("xsn") => "application/octet-stream",
758        Some("xss") => "application/xml",
759        Some("xtp") => "application/octet-stream",
760        Some("xwd") => "image/x-xwindowdump",
761        Some("z") => "application/x-compress",
762        Some("zip") => "application/zip",
763        _ => "application/octet-stream",
764    }
765}