meta.rs (6566B)
1 //! The `meta` module is an optional feature which stores the MIME type value from the 2 //! `Content-Type` header of a client `PUT` request. 3 //! 4 //! The MIME type is stored on the server under the same file identifier as the content but with a 5 //! postfix '.meta'. 6 //! 7 //! A check is performed to validate that the specified value is a valid MIME type string. However, 8 //! no further check is performed to attempt to verify whether the declared MIME type correctly 9 //! describes the file contents. 10 //! 11 //! For subsequent `GET` requests for the same content, the stored MIME type will be used as the 12 //! `Content-Type` header. 13 //! 14 //! If no MIME type was specified for the content, or if the feature is not enabled, the 15 //! `Content-Type` header will always be `application/octet-stream` 16 //! 17 //! Any subsequent `PUT` for the same content specifying a `Content-Type` header will _overwrite_ 18 //! the previously stored MIME type. 19 //! 20 //! There is no feature to _delete_ the MIME type association from the server. However, setting the MIME 21 //! type explicitly to `application/octet-stream` will achieve the same result as for records that 22 //! do not have a MIME type association. 23 use std::fs::{ 24 File, 25 read, 26 }; 27 use std::path::{ 28 Path, 29 PathBuf, 30 }; 31 use std::io::Write; 32 use std::str::FromStr; 33 use mime::Mime; 34 use hex; 35 36 use log::{debug, error}; 37 38 39 fn meta_path(path: &Path, digest: &Vec<u8>) -> Result<PathBuf, std::io::Error> { 40 let digest_hex = hex::encode(digest); 41 let fp = path.join(digest_hex); 42 43 let mut path_canon = match fp.canonicalize() { 44 Ok(v) => { 45 v 46 }, 47 Err(e) => { 48 debug!("err {:?} {:?}", e, fp); 49 return Err(e); 50 } 51 }; 52 53 path_canon.set_extension("meta"); 54 Ok(path_canon) 55 } 56 57 fn filename_path(path: &Path, digest: &Vec<u8>) -> Result<PathBuf, std::io::Error> { 58 let digest_hex = hex::encode(digest); 59 let fp = path.join(digest_hex); 60 61 let mut path_canon = match fp.canonicalize() { 62 Ok(v) => { 63 v 64 }, 65 Err(e) => { 66 return Err(e); 67 } 68 }; 69 70 path_canon.set_extension("filename"); 71 Ok(path_canon) 72 } 73 74 75 /// Set a MIME type for the specified content. 76 /// 77 /// # Arguments 78 /// 79 /// * `path` - Absolute path to storage diectory. 80 /// * `digest` - Immutable reference to content. 81 /// * `typ` - MIME type to store for the content. 82 pub fn register_type(path: &Path, digest: &Vec<u8>, typ: Mime) -> Result<(), std::io::Error> { 83 match meta_path(path, digest) { 84 Ok(v) => { 85 match File::create(v) { 86 Ok(mut f) => { 87 f.write(typ.as_ref().as_bytes()); 88 } 89 Err(e) => { 90 return Err(e); 91 } 92 }; 93 }, 94 _ => {}, 95 }; 96 Ok(()) 97 } 98 99 100 /// Set a MIME type for the specified content. 101 /// 102 /// # Arguments 103 /// 104 /// * `path` - Absolute path to storage diectory. 105 /// * `digest` - Immutable reference to content. 106 /// * `typ` - MIME type to store for the content. 107 pub fn register_filename(path: &Path, digest: &Vec<u8>, name: String) -> Result<(), std::io::Error> { 108 match filename_path(path, digest) { 109 Ok(v) => { 110 match File::create(v) { 111 Ok(mut f) => { 112 f.write(name.as_str().as_bytes()); 113 debug!("wrote to {:?}", f); 114 } 115 Err(e) => { 116 return Err(e); 117 } 118 }; 119 }, 120 _ => {}, 121 }; 122 Ok(()) 123 } 124 125 /// Retrieve the MIME type for the specified content. 126 /// 127 /// # Arguments 128 /// 129 /// * `path` - Absolute path to storage diectory. 130 /// * `digest` - Immutable reference to content. 131 pub fn get_type(path: &Path, digest: &Vec<u8>) -> Option<Mime> { 132 let digest_hex = hex::encode(digest); 133 match meta_path(path, digest) { 134 Ok(v) => { 135 match read(v) { 136 Ok(r) => { 137 let mime_str = String::from_utf8(r).unwrap(); 138 debug!("content type {} retrieved for {}", &mime_str, &digest_hex); 139 let mime = Mime::from_str(mime_str.as_str()).unwrap(); 140 return Some(mime); 141 }, 142 Err(e) => { 143 debug!("meta type file not found for {}: {}", &digest_hex, e); 144 }, 145 }; 146 }, 147 _ => {}, 148 }; 149 None 150 } 151 152 153 /// Retrieve the alternate filename for the specified content. 154 /// 155 /// # Arguments 156 /// 157 /// * `path` - Absolute path to storage diectory. 158 /// * `digest` - Immutable reference to content. 159 pub fn get_filename(path: &Path, digest: &Vec<u8>) -> Option<String> { 160 let digest_hex = hex::encode(digest); 161 match filename_path(path, digest) { 162 Ok(v) => { 163 match read(v) { 164 Ok(r) => { 165 let filename_str = String::from_utf8(r).unwrap(); 166 debug!("filename {} retrieved for {}", &filename_str, &digest_hex); 167 return Some(filename_str); 168 }, 169 Err(e) => { 170 debug!("filename file not found for {}: {}", &digest_hex, e); 171 }, 172 }; 173 }, 174 _ => {}, 175 }; 176 None 177 } 178 179 #[cfg(test)] 180 mod tests { 181 use hex; 182 use std::str::FromStr; 183 use tempfile::tempdir; 184 use std::path::Path; 185 use std::fs::{ 186 write, 187 File, 188 }; 189 190 use mime::Mime; 191 192 use env_logger; 193 use log::{debug, info, error}; 194 195 use super::{ 196 register_type, 197 register_filename, 198 get_type, 199 get_filename, 200 }; 201 202 #[test] 203 fn test_meta_mime() { 204 let d = tempdir().unwrap(); 205 let dp = d.path(); 206 let url = "deadbeef"; 207 let digest = hex::decode(&url).unwrap(); 208 let mime_type = Mime::from_str("application/zip").unwrap(); 209 210 let fp = dp.join(&url); 211 write(&fp, b"foo"); 212 213 register_type(&dp, &digest, mime_type.clone()); 214 let mime_type_recovered = get_type(&dp, &digest).unwrap(); 215 assert_eq!(mime_type_recovered, mime_type); 216 } 217 218 #[test] 219 fn test_meta_filename() { 220 let d = tempdir().unwrap(); 221 let dp = d.path(); 222 let url = "deadbeef"; 223 let digest = hex::decode(&url).unwrap(); 224 let filename = "foo.zip"; 225 226 let fp = dp.join(&url); 227 write(&fp, b"foo"); 228 229 register_filename(&dp, &digest, String::from(filename)); 230 let filename_recovered = get_filename(&dp, &digest).unwrap(); 231 assert_eq!(filename_recovered, filename); 232 } 233 }