Assignment 6
This assignment is about updating your web server to adhere to the new standards we decided on as a class. In a future assignment, students from other teams will be building a new request handler for your web server. In order for them to do this, you must:
- Make your code adhere to the common API
- Write a trivial 404 handler with the common API
- Document how one can contribute a new request handler to your code base
Each student should submit this assignment by 11:59PM on November 10, 2026 into the submission form.
Table of contents
Refactor your code
Based on the API presentations for Assignment 5, we chose a common API for all teams to use. Now, you must refactor your code to use the common API. As a result of this, you should be able to use a configuration file or request handler written by a different team with your web server. Configuration files should be able to be shared and parsed with minimal modification, just enough to account for differences in property names. Request handlers could require slight modification, if you have slight differences in field names or header file location. The important thing is to adhere to the spirit and structure of the common API.
Write contributor documentation
Because everyone will be using similar APIs, students from other teams should be able to contribute to your project with a minimal learning curve. Potential contributors need to know things like:
- How the source code is laid out
- How to build, test, and run the code
- How to add a request handler, including:
- a well-documented example of an existing handler
- well-documented header files
Create a README file with the above instructions on how to work with your code. This should be sufficient for people not intimately familiar with the history of your source code to contribute to it. Ideally they can find all the information they need in the docs without having to ask you any other questions. That’s the standard to aim for. (Feel free to link to general CS130 documentation to clarify specific instructions.)
In a future assignment, you may be partially graded on the experience someone has working in your code base, so try to put yourself in a contributor’s shoes now and make things as easy as possible for them later!
Write a 404 handler
Write a new request handler using the common API that always returns an HTTP 404 (not found) error code. Configure this handler to / so that it handles any request that does not reach one of the other configured handlers.
Grading Criteria
Team grading criteria include those features described above and refactoring according to class-chosen API description below:
- documentation
- a 404 handler
- a common API for all handlers
- a mechanism to dispatch from an incoming uri to a specific handler
Individual Contributor criteria includes:
- Code submitted for review (follows existing style, readable, testable)
- Addressed and resolved all comments from TL
Tech Lead criteria includes:
- Kept assignment tracker complete and up to date
- Maintained comprehensive meeting notes
- Gave multiple thoughtful (more than just an LGTM) reviews in Gerrit
General criteria includes how well your team follows the class project protocol. Additional criteria may be considered at the discretion of graders or instructors.
Submit your assignment
Everyone should fill out the submission form before 11:59PM on the due date. We will only review code that was submitted to the main branch of the team repository before the time of the TL’s submission. You may update your submission by re-submitting the form, which will update the submission time considered for grading purposes if you are the TL.
Late hours will acrue based on the submission time, so TL’s should avoid re-submitting the form after the due date unless they want to use late hours.
Common API
Based on prior experience in our own server up to this point and after seeing other team’s design choices, we discussed the options for the config file, handler API, and dispatch, and voted (as a class) on each one in lecture. The decisions are summarized below.
Config File Format
- The config file follows the Nginx-style “block based” format: statements end in
;, and blocks are wrapped in{ ... }. - Server-level parameters are top-level directives. There is no enclosing
serverblock. The port is set with theportdirective. - Handlers use the “location-major” format: the keyword
location, followed by a serving path, and a block{ ... }containing everything needed to handle requests under that path. This includes the handler type, given with thehandlerkeyword. - Handler arguments are typed key-value pairs inside the
locationblock, rather than positional arguments. Each handler type defines which keys it accepts.
port 80; # port my server listens on
location /echo {
handler echo; # no arguments
}
location /static {
handler static;
root ./files; # supports relative paths
}
Request Handlers
- Request handlers are long-lived. One handler object is created for each
locationblock when the server starts up and reads the config file, and it serves every request routed to that location for the life of the server. Handlers should not keep per-request state in member variables. - Handlers are created via a factory, not by the dispatcher directly. The dispatcher hands the factory a handler type and its config block, and gets back a handler. Only the factory knows which concrete handler classes exist and how to construct them, so adding a new handler type does not require changing the dispatcher.
- Configuration is passed through the handler’s constructor. There is no
Init()method. A handler is fully configured and ready to use as soon as it is constructed. The factory is responsible for reading the typed arguments from the{ ... }block and passing them to the constructor, so handlers never parse the Nginx config object themselves.
// Example constructor signature
StaticHandler(const std::string& location, const std::string& root);
Dispatching
- The server dispatches an incoming request by matching the request URL against the paths of the
locationblocks in the config. - The request is routed to the handler with the longest matching prefix. For example, with handlers configured at
/and/static, a request for/static/images/cat.jpggoes to the/statichandler, and a request for/echogoes to the/handler.
// Built once at startup from the config: location -> handler.
std::map<std::string, std::unique_ptr<RequestHandler>> handlers;
std::string location = longest_prefix_match(handlers, request.target());
Response response = handlers[location]->handle_request(request);
RequestHandler Interface
After deciding which handler to call, the server passes a typed Request object to the handler. The server core is responsible for parsing the raw request text into this object. Teams can define their own Request/Response types, and applying a typedef to the default Boost objects is permitted. The Request object should not include the raw text of the request.
The handler builds a typed Response object and returns it by value. The server is responsible for serializing it into the HTTP response text. The method must be named handle_request().
class RequestHandler {
public:
virtual ~RequestHandler() = default;
virtual Response handle_request(const Request& request) = 0;
};
Handlers do not return a separate error value. If something goes wrong while handling a request (e.g. a file isn’t found), the handler returns a Response with the appropriate HTTP status code.
Additional Boost Info
You are not required to use Boost objects. Information below is FYI only.
The boost::beast::http::request object (documentation) encapsulates the request from the client and contains fields for the url path (target), the HTML method (method) and version (version), headers (base) that hold metadata and context, and a body (body).
The handler fills out a boost::beast::http::response object (documentation) and returns a copy of this object to the server. The response object consists of headers that hold metadata associated with the content and session, a body of content, and a code for delivery to the requester.
See here for an example of using the boost::beast::http objects.