pastebin code is reduced to 42 lines :D
from kionite231@lemmy.ca to rust@programming.dev on 09 Jan 16:38
https://lemmy.ca/post/36783053

Hello, last time I shared my dirty code of pastebin and people suggested me a lot of things so I have implemented those. now the code is reduced to only 42 lines of code :D

last post: lemmy.ca/post/36410861

here is the code:

use axum::{extract::Path, routing::get, routing::post, Router};
use std::fs::{read_to_string, File};
use std::io::prelude::*;
use std::sync::atomic::{AtomicUsize, Ordering};

const MAX_FILE_SIZE: usize = 1024 * 1024 * 10;
static mut FILE_COUNT: AtomicUsize = AtomicUsize::new(0);

async fn handle(Path(id): Path<String>) -> String {
    if let Ok(content) = read_to_string(id) {
        return content;
    }
    return String::from("ERROR: File not found");
}

async fn submit_handle(bytes: String) -> String {
    dbg!(&bytes);
    if bytes.len() > MAX_FILE_SIZE {
        // Don't store the file if it exceeds max size
        return String::from("ERROR: max size exceeded");
    }
    unsafe {
        let path = FILE_COUNT.load(Ordering::Relaxed);
        FILE_COUNT.store(path+1, Ordering::Relaxed);
        let mut output = File::create(path.to_string()).unwrap();
        write!(output, "{}", bytes).unwrap();
        let mut url = String::from("http://localhost:3000/");
        url.push_str(&path.to_string());
        return url;
    }
}

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/", get(|| async { "Paste something in pastebin! use curl -X POST http://localhost:3000/submit -d 'this is some data'" }))
        .route("/{id}", get(handle))
        .route("/submit", post(submit_handle));

    let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

#rust

threaded - newest

beeb@lemm.ee on 09 Jan 18:43 next collapse

I will probably post an improved version (if you like) but the main point is that you do not need the atomic to be mut, and so you don’t need unsafe. Have a look at doc.rust-lang.org/std/…/struct.AtomicUsize.html#m… too

kionite231@lemmy.ca on 10 Jan 05:34 next collapse

yeah, you are right, I don’t need mut. here is the improved version:

use axum::{extract::Path, routing::get, routing::post, Router};
use std::fs::{read_to_string, File};
use std::io::prelude::*;
use std::sync::atomic::{AtomicUsize, Ordering};

const MAX_FILE_SIZE: usize = 1024 * 1024 * 10;
static FILE_COUNT: AtomicUsize = AtomicUsize::new(0);

async fn handle(Path(id): Path<String>) -> String {
    match read_to_string(id) {
        Ok(content) => content,
        Err(e) => e.to_string(),
    }
}

async fn submit_handle(bytes: String) -> String {
    dbg!(&bytes);
    if bytes.len() > MAX_FILE_SIZE {
        // Don't store the file if it exceeds max size
        return String::from("ERROR: max size exceeded");
    }
    let path = FILE_COUNT.load(Ordering::Relaxed);
    FILE_COUNT.fetch_add(1, Ordering::SeqCst);
    let mut output = File::create(path.to_string()).unwrap();
    write!(output, "{}", bytes).unwrap();
    let mut url = String::from("http://localhost:3000/");
    url.push_str(&path.to_string());
    return url;
}

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/", get(|| async { "Paste something in pastebin! use curl -X POST http://localhost:3000/submit -d 'this is some data'" }))
        .route("/{id}", get(handle))
        .route("/submit", post(submit_handle));

    let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
        .await
        .unwrap();
    axum::serve(listener, app).await.unwrap();
}

beeb@lemm.ee on 10 Jan 05:49 collapse

Fetch add will return the old value before updating it so you don’t need the “.load” call above it!

kionite231@lemmy.ca on 10 Jan 06:12 next collapse

wow, now it reduced to only 41 lines of code, that’s nice :D

kionite231@lemmy.ca on 10 Jan 06:13 collapse

Thank you very much!

beeb@lemm.ee on 10 Jan 08:06 collapse

Here’s a slightly more idiomatic version:

use std::{
    fs,
    sync::atomic::{AtomicUsize, Ordering},
};

use axum::{extract::Path, http::StatusCode, routing::get, routing::post, Router};

const MAX_FILE_SIZE: usize = 1024 * 1024 * 10;
static FILE_COUNT: AtomicUsize = AtomicUsize::new(0);

async fn handle(Path(id): Path<String>) -> (StatusCode, String) {
    match fs::read_to_string(id) {
        Ok(content) => (StatusCode::OK, content),
        Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()),
    }
}

async fn submit_handle(bytes: String) -> (StatusCode, String) {
    dbg!(&bytes);
    if bytes.len() > MAX_FILE_SIZE {
        // Don't store the file if it exceeds max size
        return (
            StatusCode::BAD_REQUEST,
            "ERROR: max size exceeded".to_string(),
        );
    }
    let path = FILE_COUNT.fetch_add(1, Ordering::SeqCst);
    if let Err(e) = fs::write(path.
beeb@lemm.ee on 10 Jan 08:12 collapse

Note that there are many security concerns with this, notably the fact that there is no input validation on the id path segment which means you can get the content of any file (e.g. http://localhost:3000/src%2Fmain.rs). It’s also very easy to scrape the content of all the files because the IDs are easy to predict. When the server reboots, you will overwrite previously written files because the counter starts back at zero. Using a UUID would probably mostly solve both these issues.

kionite231@lemmy.ca on 10 Jan 13:31 collapse

it shouldn’t be an issue because I will be running it inside a chroot. I might use UUID though.

ExperimentalGuy@programming.dev on 10 Jan 15:43 collapse

I love seeing you make these posts, I proving each time. Could you write a blog post about your iterations and what you’ve learned?

kionite231@lemmy.ca on 10 Jan 16:29 collapse

yeah sure! I will make a blog post on Lemmy or somewhere else once I get some free time :)