Error: Expected: HTTP-version, Received: "" in http-server rust

The rust code developed seems to be compiling correctly however its failing the tests once pushed here is the code

#[allow(unused_imports)]
use std::net::{ TcpStream, TcpListener};
use std::io::{ Write, BufReader, BufRead };
use std::{env, fs};
use std::path::Path;
use std::fs::File;

enum StatusCode {
    Success,
    NotFound,
    SuccessBody{content_len: u8, content: String},
    OctateSuccess{content_len: usize, content: String},
    Created
}
fn main() {
    // You can use print statements as follows for debugging, they'll be visible when running tests.
    println!("Logs from your program will appear here!");

    // Uncomment this block to pass the first stage
    //
    let listener = TcpListener::bind("127.0.0.1:4221").unwrap();
    //
    for stream in listener.incoming() {
         match stream {
             Ok(stream) => {
                 println!("accepted new connection");
                 std::thread::spawn(|| process_stream(stream));
             }
             Err(e) => {
                 println!("error: {}", e);
             }
         }
     }
}

fn handle_connection (stream: &mut TcpStream) -> StatusCode {
    let buffer = BufReader::new(stream);
    let http_request: Vec<String> = buffer.lines().map(|line| line.unwrap()).collect();
    let request_line: Vec<String> = http_request[0].split(" ").map(|item| item.to_string()).collect();

    if request_line[0].starts_with("POST") {
        let content:Vec<String> = request_line[1].split("/").map(|item| item.to_string()).collect();
        let file_name = content[content.len() - 1].clone();
        let env_args: Vec<String> = env::args().collect();
        let dir = env_args[2].clone();
        let file_path = Path::new(&dir).join(file_name);
        let prefix = file_path.parent().unwrap();
        std::fs::create_dir_all(prefix).unwrap();
        let content = http_request[http_request.len() - 1].clone();
        let mut f = File::create_new(&file_path).unwrap();
        f.write(content.as_bytes()).unwrap();
        println!("{:?}", content);
        StatusCode::Created
        
    } else if request_line[1] == "/" {
        StatusCode::Success
    } else if request_line[1].starts_with("/echo") {
        
        let content:Vec<String> = request_line[1].split("/").map(|item| item.to_string()).collect();
        let response_body = content[content.len() - 1].clone();
        StatusCode::SuccessBody {
            content_len: response_body.len() as u8,
            content: response_body as String
        }
    } else if request_line[1].starts_with("/user-agent") {
        let content:Vec<String> = http_request[http_request.len() - 1].split(" ").map(|item| item.to_string()).collect();
        let response_body = content[content.len() - 1].clone();
        StatusCode::SuccessBody {
            content_len: response_body.len() as u8,
            content: response_body as String
        }
    } else if request_line[1].starts_with("/files") {
        let content:Vec<String> = request_line[1].split("/").map(|item| item.to_string()).collect();
        let files = content[content.len() - 1].clone();
        let env_args: Vec<String> = env::args().collect();
        let mut dir = env_args[2].clone();
        dir.push_str(&files);
        let file = fs::read(dir);
        match file {
            Ok(fc) => {
                StatusCode::OctateSuccess {
                    content_len: fc.len(),
                    content: String::from_utf8(fc).expect("file content")
                }
            },
            Err(..) => {
                StatusCode::NotFound
            }
        }
    } else {
        StatusCode::NotFound
    }
}

fn process_stream (mut stream: TcpStream) {
    let status_code = handle_connection(&mut stream);
    match status_code {
        StatusCode::Success => {
            stream.write("HTTP/1.1 200 OK\r\n\r\n".as_bytes()).unwrap();
        },
        StatusCode::NotFound => {
            stream.write("HTTP/1.1 404 Not Found\r\n\r\n".as_bytes()).unwrap();
        },
        StatusCode::SuccessBody{content_len, content} => {
            let response = format!("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: {}\r\n\r\n{}",content_len, content);
            stream.write(response.as_bytes()).unwrap();
        },
        StatusCode::OctateSuccess{content_len, content} => {
            let response = format!("HTTP/1.1 200 OK\r\nContent-Type: application/octet-stream\r\nContent-Length: {}\r\n\r\n{}",content_len, content);
            stream.write(response.as_bytes()).unwrap();
        },
        StatusCode::Created => {
            stream.write("HTTP/1.1 201 Created\r\n\r\n".as_bytes()).unwrap();
        }
    }
}

while the implementation is bit simple my expectation is that it shuld work for the challenge “Read Request Body”
error i get is Error: Expected: HTTP-version, Received: “”
the print statements added indicates that the code is coming till the StatusCode::Created path in final match statement.
Any small hint is appreciated.

Thanks

Hi @chinmay-khadilkar, this is rather difficult to read. :sweat_smile:

Could you upload your code to GitHub and share the link? It will be much easier to read and debug if I can run it directly.

https://git.codecrafters.io/2c2a30b81d9c3f3b

The tests so far (until post challenge) has worked.
When I try this code in thunder client however it does not give any response.
this is the correct link

Thanks for the sharing the link!

Could you please also clean up the original post a bit? It’s quite difficult to read as of now. :handshake:

the code when run with cargo run command and tested in thunderclient seems to work, but not quite. What happens is that when the request is hit the response is not receiedd by the thunderclient and indicates that the process is ongoing.
Now here comes the interesting part, only after aborting the request from thunderclient manually the logs are appering, and the code is doing stuff like saving file with content in directory, even here the response is not being generated.

Thanks for the detailed explanation! I’ll take a look by the end of the week.

@chinmay-khadilkar, I added two logs like this:

fn handle_connection(stream: &mut TcpStream) -> StatusCode {
    // let mut buffer = BufReader::new(stream);
    let mut data: Vec<u8> = Vec::new();

    println!("⛳ before");
    stream.read_to_end(&mut data).unwrap();
    println!("⛳ after");

When your server hangs, only “:golf: before” is printed, indicating that the issue occurs during the read_to_end() call.

The problem with read_to_end() is that it will keep reading until the connection is closed. For HTTP connections that stay open, this causes it to hang.

Consider reading line by line from stream instead of using read_to_end().

You might find the Building a Single-Threaded Web Server chapter from the the Rust book helpful.

Closing this thread due to inactivity. If you still need assistance, feel free to reopen or start a new discussion!