wala-rust

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

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 }