~mort/coffeepaste

35551150dde6d30855952c8e06d95795e8c25ef5 — Martin Dørum 3 months ago 35edf07
load config at runtime rather than compile time
4 files changed, 73 insertions(+), 165 deletions(-)

M Cargo.lock
M Cargo.toml
D build.rs
M src/main.rs
M Cargo.lock => Cargo.lock +15 -133
@@ 3,21 3,6 @@
version = 3

[[package]]
name = "addr2line"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7a2e47a1fbe209ee101dd6d61285226744c6c8d3c21c8dc878ba6cb9f467f3a"
dependencies = [
 "gimli",
]

[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"

[[package]]
name = "aho-corasick"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 44,21 29,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"

[[package]]
name = "backtrace"
version = "0.3.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7815ea54e4d821e791162e078acbebfd6d8c8939cd559c9335dceb1c8ca7282"
dependencies = [
 "addr2line",
 "cc",
 "cfg-if",
 "libc",
 "miniz_oxide",
 "object",
 "rustc-demangle",
]

[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 71,12 41,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040"

[[package]]
name = "cc"
version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787"

[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 101,28 65,19 @@ version = "1.5.1"
dependencies = [
 "bytes",
 "chrono",
 "config_struct",
 "env_logger",
 "futures",
 "hyper",
 "libc",
 "log",
 "mime_guess",
 "once_cell",
 "rand",
 "rexiv2",
 "serde",
 "serde_derive",
 "tokio",
 "tokio-util",
]

[[package]]
name = "config_struct"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6aefd4a90b45fed157c31d107f8fdd9f8aaee79f0df625cefb773a9a1a10059f"
dependencies = [
 "failure",
 "linear-map",
 "quote",
 "toml",
]



@@ 140,28 95,6 @@ dependencies = [
]

[[package]]
name = "failure"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86"
dependencies = [
 "backtrace",
 "failure_derive",
]

[[package]]
name = "failure_derive"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
 "synstructure",
]

[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 283,12 216,6 @@ dependencies = [
]

[[package]]
name = "gimli"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e4075386626662786ddb0ec9081e7c7eeb1ba31951f447ca780ef9f5d568189"

[[package]]
name = "h2"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 424,16 351,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6"

[[package]]
name = "linear-map"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfae20f6b19ad527b550c223fddc3077a547fc70cda94b9b566575423fd303ee"
dependencies = [
 "serde",
 "serde_test",
]

[[package]]
name = "lock_api"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 474,16 391,6 @@ dependencies = [
]

[[package]]
name = "miniz_oxide"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
dependencies = [
 "adler",
 "autocfg",
]

[[package]]
name = "mio"
version = "0.7.11"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 555,19 462,10 @@ dependencies = [
]

[[package]]
name = "object"
version = "0.25.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a38f2be3697a57b4060074ff41b44c16870d916ad7877c17696e063257482bc7"
dependencies = [
 "memchr",
]

[[package]]
name = "once_cell"
version = "1.7.2"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3"
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"

[[package]]
name = "parking_lot"


@@ 726,12 624,6 @@ dependencies = [
]

[[package]]
name = "rustc-demangle"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "410f7acf3cb3a44527c5d9546bad4bf4e6c460915d5f9f2fc524498bfe8f70ce"

[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 739,17 631,19 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"

[[package]]
name = "serde"
version = "1.0.126"
version = "1.0.127"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03"
checksum = "f03b9878abf6d14e6779d3f24f07b2cfa90352cfec4acc5aab8f1ac7f146fae8"

[[package]]
name = "serde_test"
version = "1.0.126"
name = "serde_derive"
version = "1.0.127"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd1055d1c20532080b9da5040ec8e27425f4d4573d8e29eb19ba4ff1e4b9da2d"
checksum = "a024926d3432516606328597e0f224a51355a493b49fdd67e9209187cbe55ecc"
dependencies = [
 "serde",
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]


@@ 795,18 689,6 @@ dependencies = [
]

[[package]]
name = "synstructure"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
 "unicode-xid",
]

[[package]]
name = "termcolor"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 872,9 754,9 @@ dependencies = [

[[package]]
name = "toml"
version = "0.4.10"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f"
checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
dependencies = [
 "serde",
]

M Cargo.toml => Cargo.toml +4 -4
@@ 17,7 17,7 @@ log = "0.4"
env_logger = "0.8"
rexiv2 = "0.9"
bytes = "1.0"

[build-dependencies.config_struct]
version = "0.5"
features = ["toml-parsing"]
toml = "0.5"
serde_derive = "1.0"
serde = "1.0"
once_cell = "1.8"

D build.rs => build.rs +0 -7
@@ 1,7 0,0 @@
use config_struct::{Error, StructOptions};

fn main() -> Result<(), Error> {
    config_struct::create_struct(
        "config.toml", "src/config.rs",
        &StructOptions::default())
}

M src/main.rs => src/main.rs +54 -21
@@ 1,10 1,10 @@
mod config;
mod purge;
mod exif;
mod mime;

use serde_derive::Deserialize;
use log::{debug, info, error};
use config::CONFIG;
use once_cell::sync::OnceCell;
use std::borrow::Cow::Borrowed;
use std::convert::Infallible;
use std::fs;


@@ 18,6 18,7 @@ use std::error::Error;
use std::time::Duration;
use std::os::unix::io::AsRawFd;
use std::thread;
use toml;
use tokio;
use tokio::io::AsyncSeekExt;
use tokio_util::codec::{BytesCodec, FramedRead};


@@ 39,6 40,29 @@ static ERR_404_HTML: &'static [u8] = include_bytes!("../web/404.html");
static ERR_413_HTML: &'static [u8] = include_bytes!("../web/413.html");
static ERR_500_HTML: &'static [u8] = include_bytes!("../web/500.html");

static CONFIG: OnceCell<Config> = OnceCell::new();

#[derive(Deserialize, Debug)]
struct Config {
    data: String,
    expiration_days: i64,
    listen: String,
    max_file_size: i64,
    url: String,
}

impl std::default::Default for Config {
    fn default() -> Self {
        Self {
            data: "./data".into(),
            expiration_days: 30,
            listen: "127.0.0.1:8080".into(),
            max_file_size: 10000000,
            url: "http://localhost:8080".into(),
        }
    }
}

fn parse_range(range: &str) -> Result<(u64, Option<u64>), Box<dyn Error>> {
    let range = range.split("bytes=").nth(1).ok_or("Missing 'bytes='")?;



@@ 211,7 235,7 @@ fn respond_500() -> Result<Response<Body>, Infallible> {
       .body(Body::from(ERR_500_HTML)).unwrap())
}

async fn on_get(req: Request<Body>) -> Result<Response<Body>, Box<dyn Error>> {
async fn on_get(config: &Config, req: Request<Body>) -> Result<Response<Body>, Box<dyn Error>> {
    let path = req.uri().path();
    if path == "/" || path == "/index.html" {
        return Ok(Response::builder()


@@ 271,12 295,12 @@ async fn on_get(req: Request<Body>) -> Result<Response<Body>, Box<dyn Error>> {
    }

    // Serve the actual file
    let mut pathbuf = Path::new(&CONFIG.data as &str).join(&name);
    let mut pathbuf = Path::new(&config.data as &str).join(&name);
    pathbuf.set_extension("");
    return serve_file(&pathbuf, mime.unwrap_or_else(|| "text/plain"), &req).await;
}

async fn on_put(req: Request<Body>) -> Result<Response<Body>, Infallible> {
async fn on_put(config: &Config, req: Request<Body>) -> Result<Response<Body>, Infallible> {
    let (parts, mut reqbody) = req.into_parts();
    let pathstr = parts.uri.path();
    let path = Path::new(pathstr);


@@ 292,7 316,7 @@ async fn on_put(req: Request<Body>) -> Result<Response<Body>, Infallible> {
        None => None,
    };

    let (mut file, name) = match create_random_file(&CONFIG.data) {
    let (mut file, name) = match create_random_file(&config.data) {
        Err(err) => {
            error!("Failed to create random file: {}", err);
            return Ok(Response::builder()


@@ 303,7 327,7 @@ async fn on_put(req: Request<Body>) -> Result<Response<Body>, Infallible> {
        Ok((file, name)) => (file, name),
    };

    info!("Created file '{}/{}'", CONFIG.data, name);
    info!("Created file '{}/{}'", config.data, name);

    let mut bodysize: usize = 0;
    let mut first = true;


@@ 312,7 336,7 @@ async fn on_put(req: Request<Body>) -> Result<Response<Body>, Infallible> {
            Some(chunkres) => match chunkres {
                Err(err) => {
                    error!("Upload file transfer error: {}", err);
                    delete_file(&CONFIG.data, &name);
                    delete_file(&config.data, &name);
                    return respond_500();
                },
                Ok(chunk) => chunk,


@@ 330,21 354,21 @@ async fn on_put(req: Request<Body>) -> Result<Response<Body>, Infallible> {
            first = false;
        }

        if bodysize + chunk.len() > CONFIG.max_file_size as usize {
            error!("Uploaded file exceeds max size ({} bytes)", CONFIG.max_file_size);
            delete_file(&CONFIG.data, &name);
        if bodysize + chunk.len() > config.max_file_size as usize {
            error!("Uploaded file exceeds max size ({} bytes)", config.max_file_size);
            delete_file(&config.data, &name);
            return respond_413();
        }

        bodysize += chunk.len();
        if let Err(err) = file.write(&chunk) {
            error!("File write error: {}", err);
            delete_file(&CONFIG.data, &name);
            delete_file(&config.data, &name);
            return respond_500();
        }
    };

    let mut url = CONFIG.url.to_string() + "/" + &name;
    let mut url = config.url.to_string() + "/" + &name;
    if let Some(e) = result_ext {
        url.push_str(".");
        url.push_str(&e);


@@ 352,7 376,7 @@ async fn on_put(req: Request<Body>) -> Result<Response<Body>, Infallible> {

    // Privacy
    if guess == Some("image/jpeg") {
        exif::strip(&CONFIG.data, &name);
        exif::strip(&config.data, &name);
    }

    return Ok(Response::builder().status(201)


@@ 361,12 385,12 @@ async fn on_put(req: Request<Body>) -> Result<Response<Body>, Infallible> {
       .body(Body::from(url + "\n")).unwrap());
}

async fn on_request(req: Request<Body>) -> Result<Response<Body>, Infallible> {
async fn on_request(config: &Config, req: Request<Body>) -> Result<Response<Body>, Infallible> {
    debug!("{} {}", req.method(), req.uri());

    let res = match req.method() {
        &Method::GET | &Method::HEAD => on_get(req).await,
        &Method::PUT => Ok(on_put(req).await?),
        &Method::GET | &Method::HEAD => on_get(&config, req).await,
        &Method::PUT => Ok(on_put(&config, req).await?),
        _ => Ok(respond_404()?),
    };



@@ 383,20 407,28 @@ async fn main() {
    let _ = env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
    debug!("Logger initialized");

    let addr: SocketAddr = CONFIG.listen.parse()
    if let Ok(confstr) = fs::read_to_string("config.toml") {
        CONFIG.set(toml::from_str(&confstr).unwrap()).unwrap();
    } else {
        CONFIG.set(Config::default()).unwrap();
    }

    let addr: SocketAddr = CONFIG.get().unwrap().listen.parse()
        .expect("Unable to parse socket address");

    let make_svc = make_service_fn(|_conn| async {
        return Ok::<_, Infallible>(service_fn(on_request));
        return Ok::<_, Infallible>(service_fn(
                |req: Request<Body>| on_request(CONFIG.get().unwrap(), req)));
    });

    // Purge thread
    let _purge_thread = thread::spawn(|| {
        let conf = CONFIG.get().unwrap();
        let expiration = Duration::from_secs(
            ((CONFIG.expiration_days as f64) * ((60 * 60 * 24) as f64)) as u64);
            ((conf.expiration_days as f64) * ((60 * 60 * 24) as f64)) as u64);

        loop {
            purge::purge(&CONFIG.data, expiration);
            purge::purge(&conf.data, expiration);
            thread::sleep(Duration::from_secs(10 * 60));
        }
    });


@@ 407,5 439,6 @@ async fn main() {

    if let Err(e) = server.await {
        error!("Server error: {}", e);
        std::process::exit(1);
    }
}