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 axum
1.
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.
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.