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 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
#![allow(trivial_casts)] #![cfg_attr(test, deny(warnings))] extern crate conduit; extern crate conduit_mime_types as mime; extern crate time; extern crate filetime; #[cfg(test)] extern crate tempdir; use std::collections::HashMap; use std::error::Error; use std::io; use std::io::prelude::*; use std::path::{PathBuf, Path}; use std::fs::File; use conduit::{Request, Response, Handler}; use filetime::FileTime; pub struct Static { path: PathBuf, types: mime::Types } impl Static { pub fn new<P: AsRef<Path>>(path: P) -> Static { Static { path: path.as_ref().to_path_buf(), types: mime::Types::new() .ok().expect("Couldn't load mime-types") } } } impl Handler for Static { #[allow(deprecated)] fn call(&self, request: &mut Request) -> Result<Response, Box<Error+Send>> { let request_path = &request.path()[1..]; if request_path.contains("..") { return Ok(not_found()) } let path = self.path.join(request_path); let mime = self.types.mime_for_path(&path); let file = match File::open(&path) { Ok(f) => f, Err(..) => return Ok(not_found()), }; let data = try!(file.metadata().map_err(|e| Box::new(e) as Box<Error+Send>)); if data.is_dir() { return Ok(not_found()) } let mtime = FileTime::from_last_modification_time(&data); let ts = time::Timespec { sec: (mtime.seconds_relative_to_1970() as i64) / 1000, nsec: (((mtime.nanoseconds() as u32) % 1000) as i32) * 1000000 }; let tm = time::at(ts).to_utc(); let mut headers = HashMap::new(); headers.insert("Content-Type".to_string(), vec![mime.to_string()]); headers.insert("Content-Length".to_string(), vec![data.len().to_string()]); headers.insert("Last-Modified".to_string(), vec![tm.strftime("%a, %d %b %Y %T GMT").unwrap() .to_string()]); Ok(Response { status: (200, "OK"), headers: headers, body: Box::new(file), }) } } fn not_found() -> Response { Response { status: (404, "Not Found"), headers: HashMap::new(), body: Box::new(io::empty()), } } #[cfg(test)] mod tests { extern crate conduit_test as test; use std::fs::{self, File}; use std::io::prelude::*; use tempdir::TempDir; use conduit::{Handler, Method}; use Static; #[test] fn test_static() { let td = TempDir::new("conduit-static").unwrap(); let root = td.path(); let handler = Static::new(root.clone()); File::create(&root.join("Cargo.toml")).unwrap() .write_all(b"[package]").unwrap(); let mut req = test::MockRequest::new(Method::Get, "/Cargo.toml"); let mut res = handler.call(&mut req).ok().expect("No response"); let mut body = String::new(); res.body.read_to_string(&mut body).unwrap(); assert_eq!(body, "[package]"); assert_eq!(res.headers.get("Content-Type"), Some(&vec!("text/plain".to_string()))); assert_eq!(res.headers.get("Content-Length"), Some(&vec!["9".to_string()])); } #[test] fn test_mime_types() { let td = TempDir::new("conduit-static").unwrap(); let root = td.path(); fs::create_dir(&root.join("src")).unwrap(); File::create(&root.join("src/fixture.css")).unwrap(); let handler = Static::new(root.clone()); let mut req = test::MockRequest::new(Method::Get, "/src/fixture.css"); let res = handler.call(&mut req).ok().expect("No response"); assert_eq!(res.headers.get("Content-Type"), Some(&vec!("text/css".to_string()))); assert_eq!(res.headers.get("Content-Length"), Some(&vec!["0".to_string()])); } #[test] fn test_missing() { let td = TempDir::new("conduit-static").unwrap(); let root = td.path(); let handler = Static::new(root.clone()); let mut req = test::MockRequest::new(Method::Get, "/nope"); let res = handler.call(&mut req).ok().expect("No response"); assert_eq!(res.status.0, 404); } #[test] fn test_dir() { let td = TempDir::new("conduit-static").unwrap(); let root = td.path(); fs::create_dir(&root.join("foo")).unwrap(); let handler = Static::new(root.clone()); let mut req = test::MockRequest::new(Method::Get, "/foo"); let res = handler.call(&mut req).ok().expect("No response"); assert_eq!(res.status.0, 404); } #[test] fn last_modified() { let td = TempDir::new("conduit-static").unwrap(); let root = td.path(); File::create(&root.join("test")).unwrap(); let handler = Static::new(root.clone()); let mut req = test::MockRequest::new(Method::Get, "/test"); let res = handler.call(&mut req).ok().expect("No response"); assert_eq!(res.status.0, 200); assert!(res.headers.get("Last-Modified").is_some()); } }