Skip to main content Link Menu Expand (external link) Document Search Copy Copied

GET requests

Table of contents

  1. Serving files
  2. Serving directories

In this part, you will implement the remainder of the basic HTTP server in src/server.rs by supporting GET requests for files and directories.

Although there are many types of HTTP requests such as GET, POST, and HEAD, you are only expected to handle GET requests. In other words, you can ignore the request type in the parsed request and safely assume that your server will only receive GET requests.

Serving files

Implement server::handle_socket to handle HTTP GET requests for files. You should be able to handle requests to files in subdirectories of the files directory (e.g. GET /images/hero.jpg).

  • If the file denoted by path exists, serve the file. Read the contents of the file and write it to the client socket.
    • Make sure to set the Content-Type header to be the MIME type indicated by the file extension.
    • Also make sure to correctly set the Content-Length HTTP header. The value of this header should be the size of the HTTP response body, measured in bytes. For example, Content-Length: 7810.
    • No more than 1024 bytes of the file should be loaded into memory at any given time. You will likely want to alternate between reading the file into a buffer and writing the buffer contents to the socket.
    • The request path cannot be used directly as the file path. To access local files, prepend a “.” to the request path.
  • Otherwise, if the file does not exist, serve a 404 Not Found response (the HTTP headers and body are optional) to the client. There are many things that can go wrong during an HTTP request, but we only expect you to support the 404 Not Found error message for a non-existent file.
  • If you encounter any other errors, use log::warn! to print out the error and continue serving requests. We will not verify that you are logging errors, but we will be testing that your server does not panic upon an internal error.

You may find the http::parse_request function helpful here. It takes in a mutable reference to an object with type T, where T implements the traits AsyncReadExt and Unpin. You do not need to understand all of the syntax here or what the traits do, but implementing these traits essentially means that T must have a certain set of methods. If you would like to get a deeper understanding of what is going on here, we recommend taking a look at the docs on traits, generics, and bounds.

Since we are using asynchronous functions, make sure to use the tokio::fs::File API when reading files (do not use std::fs::File). The documention for tokio::io::AsyncReadExt and tokio::io::AsyncWriteExt may be useful as well. When testing the files on your local browser, be sure to “hard refresh” pages as they can often become cached and may misrepresent your requests.

After finishing this part, curling for /index.html should output the contents of the file index.html. You should also be passing all of the Basic Server tests on the autograder other than the ones related to directories, as well as the Thread Server test.

Serving directories

Update server::handle_socket to handle HTTP GET requests for both files and directories.

  • You will now need to determine if path in handle files request refers to a file or a directory. The tokio::fs::metadata function will be useful for this purpose.

    Do not use errors from File::open to determine whether a file is a directory or not. While trying to open a directory with File::open will result in an error on Windows, it will not do so on Linux or Mac (and hence you will get different behavior on the autograder).

  • If the directory contains an index.html file, respond with a 200 OK and the full contents of the index.html file. You may not assume that directory requests will have a trailing slash in the query string.
    • The http::format_index function may be useful.
  • If the directory does not contain an index.html file, respond with an HTML page containing links to all of the immediate children of the directory (similar to ls -1), as well as a link to the parent directory.
    • The http::format_href function may be useful.
    • To list the contents of a directory, a good function to use is tokio::fs::read_dir.
    • Be sure to set the Content-Type HTTP header. You may omit the Content-Length HTTP header, but it is an interesting exercise to try including one that gives the length of bytes you are sending in your response.
    • For retrieving relative paths (such as for the parent directory), it may be helpful to use Rust’s Path and PathBuf API. You may also find the following code snippet useful for converting a Path or PathBuf to a string:

      // Converts `Path` to `&str`.
      let path = Path::new("./foo");
      let path_str = path.as_os_str().into_str().unwrap();
      
      // Converts `PathBuf` to `String`.
      let path_buf = PathBuf::from("./foo");
      let path_buf_str = path_buf.into_os_string().into_string().unwrap();
      
  • If the directory does not exist, serve a 404 Not Found response to the client.
  • You don’t need to worry about extra slashes in your links (e.g. //files///a.jpg is perfectly fine). Both the file system and your web browser are tolerant of it.
  • You do not need to handle file system objects other than files and directories (i.e. you do not need to handle symbolic links, pipes, or special files).
  • Make helper functions to re-use similar code when you can. It will make your code easier to debug!

Directly using format! to manipulate URLs can result in some nasty URLs with extra slashes and periods. Though these URLs may be valid, we suggest that you use Paths and PathBufs instead of raw strings for path manipulations.

After finishing this part, curling for the root directory / should output the contents of the file index.html. All Basic Server and Thread Server tests should pass on the autograder.