Statistics
Table of contents
When you are running a server, it is often useful to collect information on response time, requests per second, error codes, and other metrics that tell you about the performance of your server.
In this part, you will implement a very simplified statistics collection module for your HTTP server. This will do two things:
- Record the number of requests resulting in a given status code from the time the server was started. For example, there may be 15 requests resulting in a
200 OK
response, 2 requests resulting in a403 Forbidden
response, and 4 requests resulting in a404 Not Found
response. - Display these counts at the URL
/stats
, overriding any files/directories stored at that location.
Data structures
The src/stats.rs
file contains a Stats
struct that holds a HashMap
mapping status codes to counts (the number of requests that resulted in that status code):
pub struct Stats {
statuses: HashMap<StatusCode, usize>,
}
To start, fill in the implementation of stats::Stats::incr
in the impl Stats
block. The stats::Stats::incr
method takes in a status code and simply adds one to the count for that status code. Do not worry about thread safety at this stage.
You may find it helpful to reference the documentation for Rust’s HashMap
. Hint: Look at the HashMap::entry
method.
After you are done, you should be passing the incr_items
, incr_items_four
, and items_sorted
tests in tests/stats.rs
.
Thread safety
Your web server may be handling several requests at a time, so it is important that you update the counters in Stats
in a thread-safe manner. The stats::Stats::incr
method you wrote in the previous section should not have been thread-safe. In other words, concurrent execution of your previous stats::Stats::incr
method would result in undefined behavior.
To ensure that only one thread can modify the counters at a time, we’ll wrap our Stats
struct in a tokio::sync::RwLock
(not to be confused with std::sync::RwLock
). When we acquire the lock, we’ll be able to modify the contents of the inner Stats
struct. Rust will ensure that we do not modify the Stats
struct unless we have acquired the lock.
Since multiple threads need to have ownership of the RwLock
wrapping Stats
, we need to wrap both of these in an Arc
(atomic reference count). See here for a longer discussion on why this is necessary.
To avoid having to think about all this repeatedly, we define a new type, which we call StatsPtr
:
pub type StatsPtr = Arc<RwLock<Stats>>;
A StatsPtr
is an atomically reference counted read-write lock guarding a single Stats
struct. Using an instance of StatsPtr
, we can modify the inner Stats
struct safely from multiple threads.
This specific example from the documentation may come in handy for understanding how an Arc<RwLock<_>>
can be used. Keep in mind that this example isn’t intended to tell you which method(s) to call, but rather to show you how to interact with an Arc<RwLock<_>>
. You will need to look through the documentation for the appropriate method(s) yourself.
Fill in the stats::incr
method in src/stats.rs
(this is the one not inside the impl
block). Your implementation should call the stats::Stats::incr
method you wrote previously.
After you finish implementing stats::incr
, the incr_stats_ptr
test in tests/stats.rs
should pass.
Integration (optional)
This part is optional and not worth any additional credit.
Collecting statistics
Now integrate your stats collection routines into your file/directory serving logic. How to do this depends on how your code is structured, but you will likely want to do the following:
- Initialize a new
StatsPtr
instance inserver::main
. - Pass an
Arc::clone
‘dStatsPtr
to your file/directory serving routines. - Increment the appropriate status code in your file/directory serving routines whenever a response is returned.
If you prefer to use global variables instead of passing around references, you may use the lazy_static
crate.
Displaying statistics
The last step is to display all the statistics you’ve collected! Modify your web server so that when someone visits the /stats
URL, you display all non-zero counts next to the corresponding HTTP messages. For example, your statistics page should show:
OK: 7
Not Found: 2
Do not include to requests to /stats
in your statistics. That is, a visit to /stats
should not change the counts for any status code. You should use the stats::Stats::items
method provided to you. This will make our autograder happy by ordering response codes consistently. Note: you won’t be able to call this directly, since your Stats
instance should be guarded by the lock in StatsPtr
.
Set the Content-Type
header to text/plain
and return a 200 OK
status code (unless there was some sort of error). You do not need to provide the Content-Length
header. Each line should be formatted as {response_message}: {count}
. To get the response message from the status code, you may find the http::response_message
function useful.