Created
July 14, 2024 14:22
-
-
Save 0x24a/79ed9822bbd84d7a60d23bba1101475b to your computer and use it in GitHub Desktop.
Simple Rust HTTP Fileserver
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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;} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I'm new to Rust, so this may be weird.. qwq