Skip to content

Instantly share code, notes, and snippets.

@0x24a
Created July 14, 2024 14:22
Show Gist options
  • Save 0x24a/79ed9822bbd84d7a60d23bba1101475b to your computer and use it in GitHub Desktop.
Save 0x24a/79ed9822bbd84d7a60d23bba1101475b to your computer and use it in GitHub Desktop.
Simple Rust HTTP Fileserver
use std::{io::{BufRead, BufReader, Write}, net::{TcpListener, TcpStream}, fs};
use dict::{ Dict, DictIface};
fn main(){
let listener = TcpListener::bind("127.0.0.1:10086").unwrap();
println!("Listening for HTTP requests on {}", "http://127.0.0.1:10086");
for stream in listener.incoming(){
let stream = stream.unwrap();
println!("Connected with {}!", stream.peer_addr().unwrap());
handle_connection(stream);
}
}
fn construct_response(status_code: i32, status_text: String, headers: Dict<String>, content: String) -> String{
let mut response: String = String::new();
response += "HTTP/1.1 ";
response.push_str(status_code.to_string().as_str());
response += " ";
response.push_str(status_text.as_str());
if !headers.is_empty(){
response += "\r\n";
}
for header in headers {
response.push_str(header.key.as_str());
response.push_str(": ");
response.push_str(header.val.as_str());
}
response += "\r\n\r\n";
response.push_str(content.as_str());
return response;
}
fn handle_connection(mut stream: TcpStream) {
let buf_reader = BufReader::new(&mut stream);
let http_request: Vec<_> = buf_reader
.lines()
.map(|result| result.unwrap())
.take_while(|line| !line.is_empty())
.collect();
// Parse the request
let headline = http_request.first();
if headline.is_none(){
let response = construct_response(400, "Bad Request".to_string(), Dict::new(), "".to_string());
let result = stream.write(response.as_bytes());
if !result.err().is_none(){
stream.write(b"").unwrap();
}
return;
}
let headline = headline.unwrap();
let method = headline
.as_str()
.split(" ")
.collect::<Vec<&str>>();
if method.len() != 3{
let response = construct_response(400, "Bad Request".to_string(), Dict::new(), "".to_string());
let result = stream.write(response.as_bytes());
if !result.err().is_none(){
stream.write(b"").unwrap();
}
return;
}
let path = method[1];
if path.contains("/../") || !path.starts_with("/"){
match stream.write(
construct_response(
403,
"Forbidden".to_string(),
Dict::new(),
"403 Forbidden".to_string())
.as_bytes()
) {
Ok(_) => {return;},
Err(_) => {return;}
}
}
let mut file_path = String::new();
file_path.push_str("./files/");
file_path.push_str(path);
match fs::read_to_string(file_path.as_str()){
Ok(result) => {
let mut headers = Dict::<String>::new();
headers.add("Content-Type".to_string(),
"application/octet-stream".to_string());
let response = construct_response(200, "OK".to_string(), headers, result);
match stream.write(response.as_bytes()) {
Ok(_) => {return;},
Err(_) => {return;}
}
},
Err(_) => {
match stream.write(
construct_response(200, "Not found".to_string(), Dict::new(), "404 Not Found".to_string()).as_bytes()
) {
Ok(_) => {return;},
Err(_) => {return;}
}
}
}
}
@0x24a
Copy link
Author

0x24a commented Jul 14, 2024

I'm new to Rust, so this may be weird.. qwq

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment