wala-rust

Content-adressed HTTP file server
Info | Log | Files | Refs | README | LICENSE

meta.rs (7177B)


      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 /// TODO: return none on write meta error
     83 pub fn register_type(path: &Path, digest: &Vec<u8>, typ: Mime) -> Result<(), std::io::Error> {
     84     match meta_path(path, digest) {
     85         Ok(v) => {
     86             match File::create(v) {
     87                 Ok(mut f) => {
     88                     match f.write(typ.as_ref().as_bytes()) {
     89                         Ok(_) => {
     90                             debug!("wrote meta to {:?}", f);
     91                         },
     92                         Err(e) => {
     93                             error!("could not write meta to {:?} ({})", f, e);
     94                         },
     95                     };
     96                 }
     97                 Err(e) => {
     98                     return Err(e);
     99                 }
    100             };
    101         },
    102         _ => {},
    103     };
    104     Ok(())
    105 }
    106 
    107 
    108 /// Set a MIME type for the specified content.
    109 ///
    110 /// # Arguments
    111 ///
    112 /// * `path` - Absolute path to storage diectory.
    113 /// * `digest` - Immutable reference to content.
    114 /// * `typ` - MIME type to store for the content.
    115 /// TODO: return none on write error
    116 pub fn register_filename(path: &Path, digest: &Vec<u8>, name: String) -> Result<(), std::io::Error> {
    117     match filename_path(path, digest) {
    118         Ok(v) => {
    119             match File::create(v) {
    120                 Ok(mut f) => {
    121                     match f.write(name.as_str().as_bytes()) {
    122                         Ok(_) => {
    123                             debug!("wrote to {:?}", f);
    124                         },
    125                         Err(e) => {
    126                             error!("could not write to {:?} ({})", f, e);
    127                         },
    128                     };
    129                 }
    130                 Err(e) => {
    131                     return Err(e);
    132                 }
    133             };
    134         },
    135         _ => {},
    136     };
    137     Ok(())
    138 }
    139 
    140 /// Retrieve the MIME type for the specified content.
    141 ///
    142 /// # Arguments
    143 ///
    144 /// * `path` - Absolute path to storage diectory.
    145 /// * `digest` - Immutable reference to content.
    146 pub fn get_type(path: &Path, digest: &Vec<u8>) -> Option<Mime> {
    147     let digest_hex = hex::encode(digest);
    148     match meta_path(path, digest) {
    149         Ok(v) => {
    150             match read(v) {
    151                 Ok(r) => {
    152                     let mime_str = String::from_utf8(r).unwrap();
    153                     debug!("content type {} retrieved for {}", &mime_str, &digest_hex);
    154                     let mime = Mime::from_str(mime_str.as_str()).unwrap();
    155                     return Some(mime);
    156                 },
    157                 Err(e) => {
    158                     debug!("meta type file not found for {}: {}", &digest_hex, e);
    159                 },
    160             };
    161         },
    162         _ => {},
    163     };
    164     None
    165 }
    166 
    167 
    168 /// Retrieve the alternate filename for the specified content.
    169 ///
    170 /// # Arguments
    171 ///
    172 /// * `path` - Absolute path to storage diectory.
    173 /// * `digest` - Immutable reference to content.
    174 pub fn get_filename(path: &Path, digest: &Vec<u8>) -> Option<String> {
    175     let digest_hex = hex::encode(digest);
    176     match filename_path(path, digest) {
    177         Ok(v) => {
    178             match read(v) {
    179                 Ok(r) => {
    180                     let filename_str = String::from_utf8(r).unwrap();
    181                     debug!("filename {} retrieved for {}", &filename_str, &digest_hex);
    182                     return Some(filename_str);
    183                 },
    184                 Err(e) => {
    185                     debug!("filename file not found for {}: {}", &digest_hex, e);
    186                 },
    187             };
    188         },
    189         _ => {},
    190     };
    191     None
    192 }
    193 
    194 #[cfg(test)]
    195 mod tests {
    196     use hex;
    197     use std::str::FromStr;
    198     use tempfile::tempdir;
    199     use std::path::Path;
    200     use std::fs::{
    201         write,
    202         File,
    203     };
    204 
    205     use mime::Mime;
    206 
    207     use env_logger;
    208     use log::{debug, info, error};
    209 
    210     use super::{
    211         register_type,
    212         register_filename,
    213         get_type,
    214         get_filename,
    215     };
    216 
    217     #[test]
    218     fn test_meta_mime() {
    219         let d = tempdir().unwrap();
    220         let dp = d.path();
    221         let url = "deadbeef";
    222         let digest = hex::decode(&url).unwrap();
    223         let mime_type = Mime::from_str("application/zip").unwrap();
    224 
    225         let fp = dp.join(&url);
    226         write(&fp, b"foo");
    227 
    228         register_type(&dp, &digest, mime_type.clone());
    229         let mime_type_recovered = get_type(&dp, &digest).unwrap();
    230         assert_eq!(mime_type_recovered, mime_type);
    231     }
    232 
    233     #[test]
    234     fn test_meta_filename() {
    235         let d = tempdir().unwrap();
    236         let dp = d.path();
    237         let url = "deadbeef";
    238         let digest = hex::decode(&url).unwrap();
    239         let filename = "foo.zip";
    240 
    241         let fp = dp.join(&url);
    242         write(&fp, b"foo");
    243 
    244         register_filename(&dp, &digest, String::from(filename));
    245         let filename_recovered = get_filename(&dp, &digest).unwrap();
    246         assert_eq!(filename_recovered, filename);
    247     }
    248 }