\$\begingroup\$

Learn to love rustfmt: Spaces go between comma-separated items: -use std::io::{Read, Write,BufReader, BufRead}; +use std::io::{Read, Write, BufReader, BufRead}; -let response = format!("{}{}{}{}","HTTP/1.1 ",status," OK



",res); +let response = format!("{}{}{}{}", "HTTP/1.1 ", status, " OK



", res); Spaces go before braces: -fn main(){ +fn main() { -loop{ +loop { Colons attach on the left side of type definitions: -fn send_response(mut stream: TcpStream, res :&str, status :u32){ +fn send_response(mut stream: TcpStream, res: &str, status: u32) { Pay attention to compiler warnings. One of the entire purposes of using a compiled language is to have the compiler watch your back. If you ignore it, then what good is it? warning: unused result which must be used, #[warn(unused_must_use)] on by default --> src/main.rs:19:5 | 19 | reader.read_line(&mut lines); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused result which must be used, #[warn(unused_must_use)] on by default --> src/main.rs:35:9 | 35 | file.read_to_string(&mut response); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Introduce more functions to break up the distinct parts of read_request . From my inspection, there's a part that parses the request, a part that handles the request by reading a file, and a part that responds (which already exists). Note that doing this contrains the number of lines of code a variable is mutable and avoids the need to recover the inner TcpStream from the BufReader . vec_line is poorly named; it's not a Vec . I am happy to see your use of nth ; many people would collect into a Vec , which is inefficient. There's no reason to convert the result of nth into a String , you can call replace on a &str equally as well. Avoid using mut just to be able to reassign a variable (e.g. the result of replace ). Instead, just bind the variable a second time. This reduces the amount and scope of mutability. requested_page == "" should be requested_page.is_empty() Why would you convert a String to a String using to_string ? Just take a reference to the existing string. There's no need to create a string to write to the stream; just write! directly to the stream. The code always prints "OK", regardless of the status. Checking if a file exists before reading it introduces a race where the file can be deleted between the check and the read. It's better to open the file and check any resulting error. Instead of creating mutable variables ( response , status ) with defaults, consider only setting them in the appropriate branches. Again, this reduces the number of mutable variables and the scopes of mutability. Once response and status are moved to a function, you can just return them directly. read_request does more than just read, so it should be renamed. The current code doesn't require a TcpStream except in the outermost loop. It only needs anything that implements Read and Write . Using generics will allow you to more easily test those functions in isolation.

use std::io::{self, Read, Write, BufReader, BufRead}; use std::net::TcpListener; use std::fs::File; fn main() { loop { let listener = TcpListener::bind("localhost:8000").unwrap(); let stream = listener.accept().unwrap().0; handle_request(stream); } } fn handle_request<S>(mut stream: S) where S: Read + Write { let requested_page = parse_request(&mut stream); let (response, status) = read_file(&requested_page); send_response(stream, &response, status); } fn parse_request<R>(stream: R) -> String where R: Read { let mut line = String::new(); let mut reader = BufReader::new(stream); reader.read_line(&mut line).unwrap(); let mut words = line.split_whitespace(); let requested_page = words.nth(1).unwrap(); let requested_page = requested_page.replace("/", ""); if requested_page.is_empty() { String::from("index.html") } else { requested_page } } fn read_file(requested_page: &str) -> (String, u32) { match File::open(&requested_page) { Ok(mut file) => { let mut s = String::new(); file.read_to_string(&mut s).unwrap(); (s, 200) } Err(ref e) if e.kind() == io::ErrorKind::NotFound => (String::from("Not Found!"), 404), Err(e) => panic!("Unable to open file: {}", e), } } fn send_response<W>(mut stream: W, res: &str, status: u32) where W: Write { write!(&mut stream, "{}{}{}{}", "HTTP/1.1 ", status, " OK



", res).unwrap(); }

There are way too many uses of unwrap . At the very least, use expect so you have some clue about what caused the program to terminate. Even better, use Result for error handling.

As a basic step, returning a Box<Error> is extremely simple and allows you to consolidate your error handling: