I made a minimal pastebin in 60 lines of Rust
from kionite231@lemmy.ca to rust@programming.dev on 03 Jan 23:26
https://lemmy.ca/post/36410861

the code I have written isn’t very idiomatic or efficient. I am still new to Rust so I am learning things. I am amazed that I can write a pastebin in just 60 lines of Rust code. It’s awesome. I am thinking about deploying it on my server.

any suggestions would be appreciated :)

code:

use axum::{extract::Path, routing::get, Router};
use std::fs::{read_to_string, File};
use std::io::prelude::*;
use std::net::{TcpListener, TcpStream};
use std::str;

const MAX_FILE_SIZE: usize = 1024 * 1024 * 10;
static mut FILE_COUNT: usize = 0;

fn handle_client(stream: &mut TcpStream) -> std::io::Result<()> {
    let mut buf = vec![0; 1024];
    unsafe {
        let file_name = FILE_COUNT.to_string();
        FILE_COUNT += 1;
        let mut file = File::create(file_name)?;
        let mut size: usize = 0;
        loop {
            let read_data = stream.read(&mut buf).unwrap();
            size += read_data;
            if size >= MAX_FILE_SIZE {
                return Ok(())
            }
            if read_data == 0 {
                return Ok(());
            }
            stream.write_all(&buf[..read_data]).unwrap();
            write!(file, "{}", str::from_utf8(&buf[..read_data]).unwrap())?;
        }
    }
}

async fn upload_handle() -> std::io::Result<()> {
    let listener = TcpListener::bind("127.0.0.1:8080")?;

    // accept connections and process them serially
    for stream in listener.incoming() {
        handle_client(&mut stream?)?;
    }

    Ok(())
}

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");
}

#[tokio::main]
async fn main() {
    tokio::spawn(upload_handle());

    let app = Router::new()
        .route("/", get(|| async { "Paste something in pastebin!" }))
        .route("/{id}", get(handle));

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

#rust

threaded - newest

maegul@lemmy.ml on 03 Jan 23:52 next collapse

Awesome. I don’t know how, but I’m thinking the unsafe block isn’t necessary? What was causing borrow checker issues?

kionite231@lemmy.ca on 04 Jan 00:08 collapse

It’s because I am changing the value of a static variable, there could be more than one thread trying to change the value of FILE_COUNT which could lead to race condition.

aurele@sh.itjust.works on 04 Jan 00:55 collapse

Look no further than AtomicUsize in the standard library.

deadcream@sopuli.xyz on 05 Jan 14:06 collapse

Atomics are not free, and there is no need to make access to FILE_COUNT thread-safe in this case. Though of course this code has many other issues.

asudox@discuss.tchncs.de on 04 Jan 00:18 next collapse

A mutable static is rare and sometimes used in embedded environments. This is very unneccessary for a simple pastebin clone.

I am also not very sure why you’re using axum and then handling uploading the actual text without axum. You could have made this much more simpler.

kionite231@lemmy.ca on 04 Jan 00:22 collapse

This is very unneccessary for a simple pastebin clone.

It might be but as I said I am very new to Rust and don’t know how to do some stuff in Rust. someone on IRC said I should use Arc to get around this issue but I didn’t know much about Arc so I went with unsafe.

TehPers@beehaw.org on 04 Jan 00:28 collapse

static mut has so many safety requirements to use it soundly that you should just not use it. Use an AtomicUsize instead, and just make it static.

Ephera@lemmy.ml on 04 Jan 01:48 next collapse

In your handle-function, you kind of just assume that an error while reading the file means that the file isn’t there. But it could also have the wrong permissions, for example.

The fs::read_to_string() function returns an io::Result, so you can match on that and then match again on the error.kind(). One of the ErrorKind variants is NotFound, which is when you can respond to the user with “File not found”.

kionite231@lemmy.ca on 04 Jan 01:56 collapse

Thank you for the suggestion, I will update the code locally :)

BaumGeist@lemmy.ml on 04 Jan 04:01 collapse

Is there a reason why axum needs to be exposed on every network interface? Because that seems like a potential security concern

alienscience@programming.dev on 04 Jan 12:19 collapse

If you deploy with Docker you need to attach to the external interface – I bound to localhost in a Docker container once and its painful enough to debug that it is something I never forget.

I expect that upload_handle() would need to change to 0.0.0.0 rather than axum to bind to localhost.