This project is STRICTLY solo. You may not collaborate with other students at all.
Setup
Register for the project using grinch
: https://grinch.caltech.edu/register and clone the repository as explained in the setup instructions.
You MUST work solo on this project!
You may not collaborate at all with anyone else! Please use the SSH link found on the registration page and in the email sent to you,
it should look something like this git@gitlab.caltech.edu:cs3-24sp/solo03-blank.git
.
Introduction
In the last project, you wrote code to parse requests…but then you just ignored the contents of the request and said hello! Real servers obviously don’t do that.
This project will be focused on handling the “path” part of the request which is used to specify the specific resource requested from the server. For example, on the
grinch.caltech.edu
web server (which you’ve used extensively!), there are many paths that all respond differently. For example:
- The URL https://grinch.caltech.edu/cs2 has a path of
/cs2
, and the “handler” for that path gathers and displays your CS 2 grades in HTML. - The URL https://grinch.caltech.edu/cs2/grades has a path of
/cs2/grades
, and the “handler” for that path gathers and returns your CS 2 grades as a JSON object. - The URL https://grinch.caltech.edu/cs3 has a path of
/cs3
, and the “handler” for that path gathers and returns your CS 3 diagnostic grades. - The URL https://grinch.caltech.edu/register has a path of
/register
, and the “handler” for that path sends you a form and possible projects to register for.
The part of the server that figures out how to handle any given path is called the “router”. The router selects a “handler” (which is a function pointer) to handle the request and return an answer based on the path passed in in the request.
Routing
To implement a structure similar to the grinch one above, we would need four “handlers”:
char *cs2_handler(request_t *request) { ... }
char *cs2_grades_handler(request_t *request) { ... }
char *cs3_handler(request_t *request) { ... }
char *reg_handler(request_t *request) { ... }
Then, we could “install” those handlers into our router by calling the router_register
function four times:
router_register(router, "/cs2", cs2_handler);
router_register(router, "/cs2/grades", cs2_grades_handler);
router_register(router, "/cs3", cs3_handler);
router_register(router, "/register", reg_handler);
Finally, the client would call router_dispatch
on each request made to the web server. router_dispatch
would then determine which of the registered routes is the one requested and call
the appropriate handler for that path!
Initialization
Task 0.
Bring over strarray.c
, mystr.c
, ll.c
, http_request.c
, and web_server.c
. When copying over web_server.c
, make sure to place it in the server
directory.
If your server uses nu_try_read_str
, this should also be changed to nu_try_read_header
. nu_send_str
may remain nu_send_str
.
DO NOT copy over http_response.c
, as we’ve provided you with a better (but more complicated) version of this module.
You can run make task0
to run the previous projects’ tests to make sure you’ve brought everything over correctly.
Now that you have that out of the way, it’s time to get started.
Task 1.
Implement the router_init
and router_free
functions in router.c
. As usual, consult router.h
for details of their behavior.
Both of these functions require working with all the constructs we’ve learned in CS 3 so far but are otherwise simple and should be less than 10 lines each.
Run make task1
to run a basic check.
Once you’ve done that, you can get to the interesting stuff: registering and dispatching routes.
Registering routes represents telling the router that a given path should be handled by a given handler. Typically, you would register all your routes all at once when the server starts.
Dispatching routes represents analyzing a request (specifically, its path) to figure out what code should run to respond to this request. This is done each time a request comes in.
We’ll start with dispatching, since this will let us test the fallback handler behavior, though we won’t be able to fully test your dispatch until you implement registering.
Task 2a.
Implement router_dispatch
.
Now, implement registration.
Task 2b.
Implement router_register
.
Run make test
once finished to test registration (and more fully test dispatch).
You’re all done (for now…). Next time, you will use your router in web-server.c
to register some routes that will be useful later!
Push your code to complete the project.