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
use md5::compute as compute_md5;
use tempfile::NamedTempFile;
use tiny_http::{Method, Request, Response};

use std::fs::{create_dir_all, remove_file, File};
use std::io::{Error, ErrorKind, Write};
use std::net::SocketAddr;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::thread;

fn req_handler(data_dir: &str, mut req: Request) {
    let path = format!("{:x}", compute_md5(req.url().as_bytes()));
    let mut body = String::new();
    let _ = req.as_reader().read_to_string(&mut body);

    let mut dest_path = PathBuf::from(data_dir);
    dest_path.push(path.get(0..1).unwrap());
    dest_path.push(path.get(1..2).unwrap());
    dest_path.push(path.get(2..).unwrap());

    let _ = match *req.method() {
        Method::Get => {
            let file = File::open(dest_path);

            match file {
                Ok(file) => req.respond(Response::from_file(file)),
                Err(_) => req.respond(resp!("Server Error", 500)),
            }
        }
        Method::Post | Method::Put => {
            let tmpdir = Path::new(data_dir).join("tmp");

            match NamedTempFile::new_in(tmpdir) {
                Ok(mut tmpfile) => {
                    match tmpfile
                        .write(body.as_bytes())
                        .and(create_dir_all(dest_path.parent().unwrap()))
                        .and(
                            tmpfile
                                .persist(dest_path)
                                .map_err(|_| Error::new(ErrorKind::Other, "")),
                        ) {
                        Ok(_) => req.respond(resp!("Inserted", 201)),
                        _ => req.respond(resp!("Unable to create file", 500)),
                    }
                }
                Err(_) => req.respond(resp!("Unable to create file", 500)),
            }
        }
        Method::Delete => match remove_file(dest_path) {
            Ok(_) => req.respond(resp!("Deleted", 204)),
            Err(_) => req.respond(resp!("Unable to delete file", 500)),
        },
        _ => req.respond(resp!("Method not allowed", 405)),
    };
}

pub fn start(port: u16, data_dir: String, threads: u16) {
    let addr: SocketAddr = ([0, 0, 0, 0], port).into();
    let server = Arc::new(tiny_http::Server::http(addr).unwrap());
    let mut handles = Vec::new();

    // create data directory. files are initially created in tmp dir then moved to corresponding path
    if create_dir_all(Path::new(&data_dir).join("tmp")).is_err() {
        panic!("Could not create data dir. exiting\n");
    }

    let data_dir = Arc::new(data_dir);

    for _ in 0..threads {
        let server = server.clone();
        let data_dir = data_dir.clone();

        handles.push(thread::spawn(move || {
            for rq in server.incoming_requests() {
                req_handler(&data_dir, rq);
            }
        }));
    }

    for h in handles {
        h.join().unwrap();
    }
}