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
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(); }
threaded - newest
Awesome. I don’t know how, but I’m thinking the unsafe block isn’t necessary? What was causing borrow checker issues?
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.
Look no further than AtomicUsize in the standard library.
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.
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.
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.
static mut
has so many safety requirements to use it soundly that you should just not use it. Use anAtomicUsize
instead, and just make itstatic
.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 anio::Result
, so you can match on that and then match again on theerror.kind()
. One of theErrorKind
variants isNotFound
, which is when you can respond to the user with “File not found”.Thank you for the suggestion, I will update the code locally :)
Is there a reason why axum needs to be exposed on every network interface? Because that seems like a potential security concern
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 to0.0.0.0
rather than axum to bind to localhost.