tomdemuyt
tomdemuyt

Reputation: 4592

TcpStream hangs after parsing \u{d}\u{a}

I am trying to extend the example from the simple HTTP parser;

https://doc.rust-lang.org/book/ch20-01-single-threaded.html

And when I parse the request, the server always hangs on the line after the last head line. I added some logging for each line, and write out the '2' character \d\a line;

1:20:GET /data HTTP/1.1
2:22:Host: localhost:7878
3:25:User-Agent: curl/7.64.0
4:13:Accept: */*
5:20:Content-Length: 27
6:49:Content-Type: application/x-www-form-urlencoded
7:2:
\u{d}
\u{a}

Why does it do that, you can test this by running the code and then running curl http://localhost:7878

You should see it hang and when you press Control C, you will see the rust log show

Natural end of HTTP request reached
We made it out!

This is the code where things go wrong;

use std::fs;
use std::io::BufReader;
use std::io::prelude::*;
use std::net::TcpStream;
use std::net::TcpListener;

enum LogLevel {
  DEBUG=0,
  NORMAL=1,
  PROD=2
}

const LOG_LEVEL : LogLevel = LogLevel::DEBUG;

const GET:i8 = 1;
const OOPS:i8 = -1;

fn main() {
    let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
    log("Server started".to_string(), LogLevel::PROD);

    for stream in listener.incoming() {
        handle_connection(stream.unwrap());
    }
}

fn validate_request_verb(verb: String)->i8 {
  if verb.eq("GET") {
    return GET;
  }
  return OOPS;
}

fn handle_connection(mut stream: TcpStream) {

    let mut line_count = 0;
    let mut reader = BufReader::new(stream.try_clone().unwrap());
    let mut request_verb:i8 = OOPS;

    loop {
        line_count = line_count + 1;
        let mut line = String::new();
        let line_size  = reader.read_line(&mut line).unwrap();
        if line_size > 0 {
            log(format!("{}:{}:{}", line_count, line_size, line), LogLevel::DEBUG);
            if line_count == 1 {
                let mut token_counter = 1;
                for token in line.split_whitespace(){
                    if token_counter == 1 {
                      request_verb = validate_request_verb(token.to_string());
                    }
                    token_counter = token_counter + 1;
                }
            } else { //line_count > 2
                if request_verb == GET && !line.contains(": ") {
                    //spell_out(line);
                    //TODO: this only works for GET
                    //break;

                }
            }
        } else { //Count == 0
            log("Natural end of HTTP request reached".into(), LogLevel::NORMAL);
            break;
        }
        if line_count > 100 {
            log("HTTP request had more than 100 lines".into(), LogLevel::NORMAL);
            break;
        }
    }

    println!("We made it out!");

    let contents = fs::read_to_string("hello.html").unwrap();

    let response = format!(
        "HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n{}",
        contents.len(),
        contents
    );

    stream.write(response.as_bytes()).unwrap();
    stream.flush().unwrap();
}

fn log(message: String , maximum_log_level:LogLevel){
  if LOG_LEVEL as i8 <= maximum_log_level as i8 {
      if message.ends_with("\n") {
          print!("{}", message);
      } else {
          println!("{}", message);
      }
  }
}

fn spell_out(s: String){
    for c in s.chars(){
        println!("{}", c.escape_unicode());
    }
}

On a final note, according to

Reading from a TcpStream with Read::read_to_string hangs until the connection is closed by the remote end

this should be fixed by using read_to_string, but no luck..

Upvotes: 0

Views: 393

Answers (1)

kmdreko
kmdreko

Reputation: 60702

Check the documentation for read_line. It returns the number of bytes read, which includes the newline itself. So it will never be 0 unless the stream is closed, and thus your else { // Count == 0 won't be reached to break out of the loop naturally.

Upvotes: 2

Related Questions