An asynchronous server

This part builds a proxy server similar to the one you have been using for this lab.

This part may take a little more time to do, but we are talking about some 60-70 lines of code top. Do not worry if you don't terminate in the allotted time.

The project

We will use the axum and axum-server crates to build a HTTP server. We will accept requests with a path like "/wikipedia/LANGUAGE", and forward them to "https://en.wikipedia.org/wiki/LANGUAGE_(programming_language)". When the response arrives, we will forward it to the caller, along with the status code and most headers.

Exercise 1.a: Create a new cargo binary project server, with dependencies on crates axum, axum-server, tokio with feature flag full, and reqwest.

Answering a simple HTTP request

Exercise 1.b: Create a axum server, listening on port 3000, accepting a route of /wikipedia/:language and redirecting it to a wikipedia() function. Return just the request name as a String.

async fn wikipedia(Path(language): Path<String>) -> String

Returning the Wikipedia page

Exercise 1.c: Change the signature of the wikipedia() function to return a tuple of StatusCode, HeaderMap and a String, or just a StatusCode if there is an internal error:

async fn wikipedia(Path(language): Path<String>) -> Result<(StatusCode, HeaderMap, String), StatusCode>

In this function, query the right Wikipedia page using reqwest, and return the status code, headers and body from the wikipedia response. The "content-length" header must be removed from the headers as it will be recomputed by HTTP middleware used by axum1.

If there is any error, remap it (using .map_err() to StatusCode::INTERNAL_ERROR) and exit prematurely from the function.

Exercise 1.d: Run your server, it should listen on port 3000. Modify your client so that it uses http://localhost:3000/wikipedia/LANGUAGE as a reference, and check that everything works well.

If it does, it means that your server is properly running things in parallel. However it probably answers too fast and does not add the extra delay.

Slowing down the proxy server

Exercise 1.e:

Before returning the answer, sleep for 2 seconds. Of course this asynchronous sleep function must be used, using std::thread::sleep() would block the whole server thread and prevent other requests from progressing in parallel.

Sharing resources

The same Client can be used to perform concurrent requests. Using the same client to do multiple to the same site (here Wikipedia) will automatically reuse the connection and reduce the load on the server.

The Extension let you add a layer to your application. This layer will be available in any request handler.

Exercise 1.f: Add a Arc<Client> extension to your application, and use this client in your wikikpedia() function.

Adding some cache

Every time you want to access a programming language page, you reload it from Wikipedia. Even though you reduced the load by sharing the client through an extension, if a client requests the same page several times you may end up requesting a page from Wikipedia several times.

You can add a cache, whose type would be:

type Cache = HashMap<String, (StatusCode, HeaderMap, String)>;

The idea here is to share a mutable Cache object and look for the result here first. If it is not available, Wikipedia will be queried, and the result will be stored in the cache then returned to the client.

You will need some synchronization here. Rust's RwLock seems like a good candidate.

Exercise 1.g: Share a Arc<RwLock<Cache>> resource, in order to avoid doing multiple identical requests to Wikipedia.

Be cautious though: you must never keep a resource locked (be it in read or write mode) accross a await point. Limit locking the cache to the shortest possible period.

1

The communication with Wikipedia server through reqwest uses content negotiation and retrieves compressed pages. The Content-Length corresponds to the compressed data, which is neither the original data size nor necessarily the size of the data we would obtain if we used compression ourselves (unless we used the very same algorithm with the very same parameters). It is best to leave the Content-Length computation to the web framework.