Rust
Table of contents
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 howasync
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):
Extension | MIME Type |
---|---|
.html/.htm | text/html |
.jpg/.jpeg | image/jpeg |
.png | image/png |
.css | text/css |
.js | application/javascript |
application/pdf | |
Other/no extension | text/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 theAsyncWriteExt
“trait”, which you can think of as the Rust equivalent of an interface (this basically means thats
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
.