MP 5: HTTP Client-Server
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.
MP Overview Session
An MP Overview will be held on Thursday, Oct. 6 at 05:00pm in 1304 SC.
Initial Files
In your CS 340 directory, merge the initial starting files with the following commands:
git fetch release
git merge release/mp5 --allow-unrelated-histories -m "Merging initial files"
Implementation
We’ve split up the implementation into three parts:
- 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
excepthttprequest_read
. - Reading an HTTP request over a socket (the
httprequest_read
function). - Building a web server using your
httprequest
code that can be used with Chrome or other web browsers.
Modifiable Files
In your solution, you must only modify the following files. Modifications of other files may break things:
http.c
http.h
server.c
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
), andhttprequest_destroy
, to free any memory stored by anHTTPRequest
struct
While working on this part:
- You can (and will have to) add to the
HTTPRequest
struct inhttp.h
(possibly also creating other structs in there, too). - You must populate the
action
,path
,version
fields inHTTPRequest
while parsing the packet as part ofhttprequest_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 thepayload
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 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).
Part 3: Building a Web Server
In Lecture, 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 in server.c
:
- You must read an HTTP request from the
fd
(use yourhttprequest_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 a404 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
, theContent-Type
header must be set toimage/png
. - If the file name ends in
.html
, theContent-Type
header must be set totext/html
.
- If the requested path is
- 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.
- Compile your server with
make
- Launch your server using
./server 34000
(or any other port number) - Visit http://localhost:34000/
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.
Memory Correctness
For full credit, your MP must run “valgrind clean”. This means that you must:
- Compile all test cases on the command line using
make test
, - Run all test cases using
valgrind --leak-check=full --show-leak-kinds=all ./test
, - This must report the following output:
All heap blocks were freed -- no leaks are possible
macOS Specific Information
Sadly,valgrind
does not work on macOS. However, it does work in Docker running on a Mac:
# Build a light-weight docker: docker build -t cs340 . # Run make clean, make, and run valgrind: docker run --rm -it -v `pwd`:/mp5 cs340 "make clean" docker run --rm -it -v `pwd`:/mp5 cs340 make docker run --rm -it -v `pwd`:/mp5 cs340 "valgrind ./test"
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. :)