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

Rust

Table of contents

  1. Why Rust?
  2. Resources
  3. Project structure
  4. MIME type parsing
  5. Asynchronous functions

In this part, you will familiarize yourself with Rust programming as well as the structure of the code. If you are using your local computer rather than your Docker workspace, please make sure to install Rust.

Why Rust?

Rust is a systems programming language that, unlike C, is memory safe (no more segfaults!) and has high-level language features alongside low-level memory control. Rust also has other cool features like compile-time concurrency safety, a rich type system, and good performance.

Rust has been voted the most loved language on the StackOverflow Developer Survey for seven years in a row and is starting to find use in a variety of places. These are not needed to complete the homework, but if you would like to learn more, here are some interesting articles on areas where Rust is being used:

Resources

Here are some excellent resources for learning Rust. If you are new to Rust, we recommend skimming over them to get a sense of what’s available, and reading relevant sections in more detail if you think they will be useful. We will link relevant resources in each section of the homework as well.

Here are some other resources that exist, though they are not officially endorsed by Rust:

Async functions

Throughout this assignment, you will see functions labeled async. As a general rule, you must always add a .await when calling such functions. If you don’t, it will be as if you didn’t call the function! The details of how async is implemented in Rust is fairly advanced and not required for this assignment, but if you are curious, here are some resources you can read:

Project structure

.
├── Cargo.lock
├── Cargo.toml
├── Makefile
├── src
│   ├── args.rs
│   ├── http.rs
│   ├── main.rs
│   ├── server.rs
│   ├── stats.rs
│   └── tests
│       ├── args.rs
│       ├── http.rs
│       ├── mod.rs
│       └── stats.rs
└── www
    ├── index.html
    └── my_documents
        ├── WEB_SCALE.jpg
        ├── contributors.txt
        ├── credit.txt
        ├── http-meme.png
        └── wholesome_facts.txt

The skeleton code, as is typical in Rust projects, has a Cargo.lock and Cargo.toml file for managing dependencies. The Makefile provides an easy way to build and test the module, although we recommend looking through what each command does to familiarize yourself with cargo commands.

All Rust code resides in the src/ directory, with unit tests residing in src/tests/. Your final submission should only modify src/http.rs, src/server.rs, and src/stats.rs.

Binaries created by cargo build and cargo run are stored in the target/ directory. The www/ directory has files that can be used to test your HTTP server’s functionality.

The unit tests provided in src/tests/ are not comprehensive, and the autograder will run additional tests when you submit your code.

MIME type parsing

As a warmup, start by implementing the http::get_mime_type function in src/http.rs. Let’s first take a look at the function signature:

pub fn get_mime_type(path: &str) -> &'static str {
    todo!("TODO: Part 1")
}

This function should take in a path to a file, find the file extension, and return the corresponding MIME type as a string literal. You should support the following mappings from file extension to MIME type (there are many, many more, but for simplicity our server will only support these):

ExtensionMIME Type
.html/.htmtext/html
.jpg/.jpegimage/jpeg
.pngimage/png
.csstext/css
.jsapplication/javascript
.pdfapplication/pdf
Other/no extensiontext/plain

You may find the http::file_extension function helpful. We also recommend reading about Rust Options, match expressions, and if let statements.

After you complete your implementation of get_mime_type, the http::tests::test_mime_type test should be passing. You can run the tests using cargo test.

Asynchronous functions

Now that you’ve implemented a basic Rust function, it is time to move onto a more advanced function. Take a look at the http::end_headers function in src/http.rs:

pub async fn end_headers<T>(s: &mut T) -> Result<()>
where
    T: AsyncWriteExt + Unpin,
{
    todo!("TODO: Part 1")
}

The function signature here is more complicated than before, but don’t worry if you don’t understand all of the syntax. Here are the main things to take away:

  • The async fn syntax tells us that the function is asynchronous (i.e. does not immediately return to the caller).
  • the argument s is of a type that implements the AsyncWriteExt “trait”, which you can think of as the Rust equivalent of an interface (this basically means that s can be written to like a file).
  • The function returns a Result, which tells the caller whether or not the function executed successfully.

Implement this function to write a CRLF (in Rust, this would be the byte literal b"\r\n") to s, handling errors appropriately. It may help to take a look at the other async functions in src/http.rs, as well as Rust’s documentation for error handling and asynchronous functions. We are using Rust’s tokio crate, which is a library for asynchronous I/O — don’t worry too much about reading up on it now; we will link the relevant documentation when it is needed.

Although this part can be done by pattern matching with the functions provided in the skeleton code, please take the time to make sure you understand the code – later parts of this assignment will not be as easy to pattern match.

Check that the http::tests::test_end_headers test passes by running cargo test.