MP 5: HTTP Client-Server

Due Date: Completed and turned in via git before October 8, 2021 at 11:59pm
Points: MP 5 is worth 50 points

Overview

The Hyper-Text Transfer Protocol (HTTP) – defined in RFC 2616 – 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:

  • Dive into the HTTP protocol to understand the technical design of HTTP request and response packets.
  • Write an HTTP server that responds to HTTP requests (from web browsers like Chrome, command line utilities like curl, and anything else that “speaks” HTTP).
  • Parse HTTP headers into key-value pairs.
  • Have a foundational understanding of how libraries parse HTTP requests for building web services in the future.

Initial Files

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

git fetch release
git merge release/mp5 -m "Merging initial files"

Implementation

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.

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:

  • httprequest_get_action, to get the action verb (ex: GET) from the HTTP request,
  • httprequest_get_path, to get the path (ex: /) from the HTTP request,
  • httprequest_get_header, to get a value for a specific header (ex: Host -> localhost), and
  • httprequest_destroy, to free any memory stored by an HTTPRequest struct

While working on this part:

  • You can (and will have to) add to the HTTPRequest struct in http.h (possibly also creating other structs in there, too).
  • You must populate the action, path, version fields in HTTPRequest while parsing the packet as part of httprequest_parse_headers.

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).

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.

  • You should use your httprequest_parse_headers to parse the headers of the request.
  • The Content-Length header is a special HTTP header that will help you out to read the payload of the request.

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 tests 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).

Part 3: Building a Web Server

In Lecture #10, you saw a simple socket-based web server. We’ve extended the server to launch a new thread for each incoming connection (client_thread).

In the client_thread function:

  • You must read an HTTP request from the fd (use your httprequest_read).
  • You must create an HTTP response to respond to the request.
    • If the requested path is /, you should process that request as if the path is /index.html.
    • If the file does not exist in your static directory (excluding the /), you must respond with a 404 Not Found response.
    • If the file requested does exist, you will respond with a 200 OK packet and:
      • Return the contents of the file as the payload,
      • If the file name ends in .png, the Content-Type header must be set to image/png.
      • If the file name ends in .html, the Content-Type header must be set to text/html.
  • You can (and probably should, to make it easier for you) close(fd) after responding to the request. This will ensure the browser opens a new socket (and you will have a new thread) when making another request. (You can continue to re-use this same socket and keep the connection alive, but this is not required.)

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.

Submission

You will submit via git using the usual commands:

git add -u
git commit -m "MP5 submission"
git push origin master

You can verify your code was successfully submitted by viewing your git repo on github.com. :)