MP4
HTTP Server
Due dates may be found on the schedule.

The Hyper-Text Transfer Protocol (HTTP) – defined in RFC 2616 and other later specifications – is the fundamental protocol for transferring data on the World Wide Web (i.e. Internet). It is an application-level protocol which through the use of its request methods, error codes and headers is widely used for various different applications.

HTTP is a Client-Server protocol, where the Client sends a request to the Server which processes the request and returns appropriate response. In this MP, you will:

1 Initial Files

In your CS 340 directory, merge the initial starting files with the following commands:

git fetch release
git merge release/mp4 --allow-unrelated-histories -m "Merging initial files"

Debugging the code

If you can’t run the debugger on this code, you might need to make one or both of the following changes depending on your system setup:

2 Implementation

To help you organize the parts of building a web server, we’ve split up the implementation into three parts:

  1. Parsing an HTTP request packet for the request and headers (as a string, without needing to worry about the socket code). This will include all of http.c except httprequest_read.
  2. Reading an HTTP request over a socket (the httprequest_read function).
  3. Building a web server using your httprequest code that can be used with Chrome or other web browsers.

2.1 Modifiable Files

In your solution, you must only modify the following files. Modifications of other files may break things:

2.2 Webserver overview

HTTP sends messages over a socket (or, for some of our tests, other file-like objects). Messages have 3 parts:

  1. A start line that explains what type and version of message it is
  2. Zero or more header lines, each a key-value pair
  3. Optionally, a message body (also called a payload)

A web server (which is what you’re making in this MP) needs to receive and parse HTTP messages of the various Request types and construct and sent HTTP messages of the various Response types.

We’ve split these tasks into several functions. In order of calling, they are

  1. Either server.c’s main (which is complete and needs no changes from you) or a tester file establishes a connection to a client.

  2. Either server.c’s client_thread (which you’ll write in part 3) or a tester file manages that connection and calls httprequest_read with the open connection as an argument.

  3. http.c’s httprequest_read (which you’ll write in part 2) assembles an HTTP message from one or more packets, parses it, and stores it in an HTTPRequet structure defined in http.h by:

    1. using http.c’s httprequest_parse_headers to parse the start line and header lines; and
    2. using the parsed headers to read the message body.

    This information gets stored in the HTTPRequet structure, which you may add to if you wish. Because you may add to it, getter functions are used to access its key details rather than using direct field access, and a destructor function is used to deallocate any resources it has used.

  4. server.c’s client_thread uses the parsed request message to decide what response message to send.

The details of each of these pieces (e.g. how you know that you’ve passed the end of the headers) are defined in RFC 2616.

2.3 Part 1: Parsing an HTTP request

Implement the API provided in http.{c,h}, excluding httprequest_read. The main function in this part is httprequest_parse_headers, which must populate the provided HTTPRequest *req data structure with the contents from char *buffer.

The other functions include:

While working on this part:

2.3.1 Testing Part 1

We have provided a simple test suite to test the correctness of your parsing logic:

  • In your terminal, type make test to compile the test suite.
  • Run ./test "[part=1]" to run the tests that have been tagged with [part=1] (covering this portion of the MP).

2.4 Part 2: Reading an HTTP request from a socket

Complete httprequest_read. This function is called with a file descriptor where you must read to read the contents of the request.

To pass some of the tests you will need to use read not recv when reading from the socket.

2.4.1 Testing Part 2

We have provided a simple test suite to test the correctness of your parsing logic. The first five tests are identical to part 1, except that they’re now delivered via the sockfd file descriptor instead of as a string. The final test tests if your code can read the payload of a requests.

  • In your terminal, type make test to compile the test suite.
  • Run ./test "[part=2]" to run the tests that have been tagged with [part=2] (covering this portion of the MP).

2.5 Part 3: Building a Web Server

We have provided a partial threaded socket-based web server in server.c. You need to finish implementing the client_thread function.

Our code has a main thread in the function main and spawns worker threads to run client_thread. The main thread is fully complete (no edits needed) and does the following:

  1. Accepts a port number as a command-line argument
  2. Asks the OS to let it listen for incoming messages by asking for a socket, then binding it to that port and asking the OS to listen for incoming connections
  3. Repeatedly
    1. accept an incoming connection, which opens a new socket to talk over
    2. create a worker thread to speak with the client over that connection
    3. detach the worker thread, meaning ignore it’s final results and let it retire when its job is done

You will write the client_thread function in server.c:

Web clients may send several HTTP requests over the same connection. They may also send one, wait for the reply without closing the connection, then send the next. Make sure you process each HTTP request as soon as you have the entire request, not waiting for the connection to close.

2.5.1 Testing Part 3

You will test Part 3 using your favorite web browser.

If you can see the pages and images, you just made your first static web server! 🎉

There are a few tests ./test "[part=3]" to verify it works by code – but that’s not really the point of this part.

3 Submission and Grading

3.1 Memory Correctness

For full credit, your MP must run valgrind clean.

Running valgrind via Docker (required on macOS) The first time only, you need to build the docker when you’re in your mp4 directory:
docker build -t cs340  .
After the the container is built, run the container in your mp4 directory. This new container now provides you a bash prompt.
docker run -p 34000:34000 --rm -it -v "`pwd`:/mp4" cs340
Once you’re into the container, you will need to cd mp4 and then you can run the terminal commands directly! :)
cd mp4
make clean
make
make test
./test

3.2 Submission

Once you have locally passed all the tests, you will need to submit and grade your code. First commit using the standard git commands:

git add -A
git commit -m "MP submission"
git push

3.3 Grading

The initial grading is done via a manual GitHub Action. You MUST complete this step before the deadline to earn any points for the MP:

3.4 Points

2/3 of points are for the autograder part of the GitHub Action

1/3 of points are for the valgrind part of the GitHub Action