Rust web server not working with requests from Chrome?

Hi!
I'm trying to build a simple web server in rust using the sample code in the book. It work's fine in Firefox and Opera, but not in chrome. Why is that? And how would I make it work for chrome users too?

Here is the example code I'm running, from the book:

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

fn main() {
    let listener = TcpListener::bind("127.0.0.1:7878").unwrap();

    for stream in listener.incoming() {
        let stream = stream.unwrap();

        handle_connection(stream);
    }
}

fn handle_connection(mut stream: TcpStream) {
    let mut buffer = [0; 512];
    stream.read(&mut buffer).unwrap();

    let get = b"GET / HTTP/1.1\r\n";

    let (status_line, filename) = if buffer.starts_with(get) {
        ("HTTP/1.1 200 OK\r\n\r\n", "hello.html")
    } else {
        ("HTTP/1.1 404 NOT FOUND\r\n\r\n", "404.html")
    };

    let contents = fs::read_to_string(filename).unwrap();

    let response = format!("{}{}", status_line, contents);

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

When you say it doesn't work, what's the behavior you see? Do you get a 404?

I ask because your code to check the HTTP request line is very particular, and it's not unusual for browsers speaking HTTP/1.1 to send the full URL after GET instead of just a slash. Chrome might be doing this. You can check, in Chrome, by opening the developer tools (F12 on Win/Linux at least), going to the Network tab, and reloading the page. Clicking on the individual resources will show you the precise request and response that was sent.

(You might also consider logging requests using println!.)

That sample code is very fragile and IMO should not be promoted in "the book" like this.

You cannot just use stream.read() once and expect the buffer to contain the entire request. The number of bytes read by the read method can be anything ≥ 1 depending on many factors, such as internal buffer sizes or the number of bytes sent at a time by the client.
Also, the request size might be larger than 512 bytes.

The client (Chrome in this case) usually expects the whole request to be read by the server and might not even start reading the response until all of the request was sent.

To correctly read a HTTP request you have to read the header line by line until you reach an empty line and then read the body according to the relevant header fields (Content-Length, Transfer-Encoding). Everything else will not work in all cases.

EDIT:
The same is true for stream.write(), which is not required to write the entire buffer to the stream. There's the write_all method for that.

3 Likes

using read_line should help already in this case

1 Like

Thanks a lot!

Is there anything else in the book I have to look out for, like other bad practices?

I would not say it was a bad practice as such. The intention there is to teach some Rust not the intricacies of HTTP. I'm guessing the target audience for that writing was people familiar with web things already so that would not be required.

Still, I guess it ought to work !

I'm curious to know why it does not. I went through those exercises a while back and all was OK from Chrome on Win 10.

Quoting from the book itself:

Before that, more generally:

@Qoutroy, without saying how it's not working, I can't help you. It works in Chrome for me, on Windows 10.

1 Like

The buffer only being 512 bytes is only one of the problems with that code (and a minor one).
Using just one single read and write call without even checking the number of bytes returned makes it seem ok to use those functions like that, which it isn't (in any case).

I understand that you don't want to write a production grade HTTP server in the book, but with a few reasonable assumptions (e.g. that the request has not body) it can easily be made much more correct.
Using BufRead.read_line in a loop until the first empty line is only maybe 2 or 3 more lines of code.
And using write_all instead of write is really a no-brainer.

If that's too complex, then maybe building a HTTP server is not the best example.

It doesn't help you learn if you learn it wrong.

2 Likes

make a pull request.

1 Like

Hi @steveklabnik, thanks for all your amazing work for the Rust community.
It also does not work for me on Chrome 79.0.3945.117 on MacOS with a 512 bytes buffer. I have updated my code to have a 1024 bytes buffer and now works without any problem. For some reason Chrome needs a bigger buffer size than Firefox (which works all the time). Maybe to be safe we could increase the buffer size on the book so everyone has a seamless experience.
Again, thanks for all the work you've done.

1 Like

I'd like to weigh in and relate that I am new to both Rust and web programming. I also use chrome for my browser and I was very confused when this "Return Real HTML" section was not working for me, and thought I must be doing something wrong. It was just luck that I stumbled onto this thread, so I too would vote for updating the tutorial.

changed to 1024 length works

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.