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();
}
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 :)
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.
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.
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
.
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.