CS 3x (Spring 2026) Webserver 02: HTTP Requests and Servers, Oh my!

In this project, we will use the libraries from the last project to parse HTTP requests.

Setup

Register for the project using the registration tool 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 at the picture after clicking the link in the registration email:

img

Introduction

When you open a URL in your browser, what exactly is happening? We will answer this question over the next few webserver projects as we attempt to “respond” to a browser’s request for a website.

URLs consist of three parts: (1) the protocol, (2) the remote server, and (3) the path. For example, the website you requested to read this document, https://sof.tware.design/25sp/projects/webserver/02, uses the “https” protocol, the “sof.tware.design” remote server, and the path “25sp/projects/webserver/02”.

While, nowadays, you will mostly see the “https” protocol, there’s others such as “http”, “file”, and “mailto”. The only difference between “https” and “http” is that “https” is secured using a certificate which your browser checks.

In practice, HTTP has been more or less phased out in favor of HTTPS for its security, but in this project we’ll be working with HTTP because it turns out that security makes things much more complicated.

After you complete this project, you’ll be able to run make server to start your own web server and connect to it from your own browser.

Hypertext Transfer Protocol (“HTTP”)

After you type a URL into your browser and hit enter, your browser constructs a “request” to the remote server specified in the URL.

HTTP requests are strings consisting of five parts:

What’s in a Web Server?

On the other side (the one being “requested from”) is a “web server”. A “web server” is a fancy name for a computer that is waiting (in a loop) for other computers to make requests using the HTTP(S) protocol. Since we know the format of an HTTP request, this more or less boils down to waiting and processing connections, one at a time. The general shell of a web server looks like the following:

Writing the Web Server

First things first, we need to port over your code from webserver01 and datastructures00. We’re going to change the code you wrote in webserver01 to use a packed_strarray_t instead of a strarray_t, so we’ve provided you with a script to copy over the files and make some necessary changes.

Now, we’re going to update the webserver code so that it uses a packed_strarray_t instead of a strarray_t. In webserver01, we wrote two functions that return a strarray_t: mystr_split and ll_get_keys. If you take a look at their function headers in the new project, you’ll notice that we’ve changed them to return a packed_strarray_t now. We will need to implement functionality that converts the original return value of strarray_t to the new return value of packed_strarray_t. To avoid code duplication, we are going to create the method packed_strarray_from_strarray in packed_strarray.c, and then utilize it in the two updated methods.

Whenever we extend the functionality of a library like this, it is important to check for new edge cases that may arise. In our original implementation of packed_strarray_t, we never considered the creation of an array with 0 elements, as that would lead to an empty data array. However, now that we’re connecting our new packed array with mystr_split and ll_get_keys, our packed_strarray_t should be able to handle being asked to create an array with 0 elements; whenever we are asked to do so, we should return NULL to indicate that no data can be stored. This also means some other details of our implementation will need to change…

Next, let’s start sketching out the web server itself. It’s not going to do much yet, but you’ll be able to build on it. At any point, you can run make server to run your server.

There aren’t any tests for the server code until the end of this project, but you should test it yourself by connecting to it via the link printed by make server.

Before you implement the parsing, we need to do some bookkeeping utilities.

The Format of an HTTP Request

An HTTP request will look like

[METHOD] [PATH] [VERSION STRING]\r\n
[KEY 1]: [VALUE 1]\r\n
[KEY 2]: [VALUE 2]\r\n
...
[KEY n]: [VALUE n]\r\n
\r\n

where each of [METHOD], [PATH], and [VERSION STRING] is guaranteed not to contain any spaces, \n, or \r, and each [KEY #] is guaranteed not to contain any :’s, \n, or \r. Each [VALUE #] is guaranteed not to contain any \n or \r (but may contain :’s).

The method, path, and version are space-separated and each line ends in \r\n. The key-value pairs are each on a separate line with the key and the value separated by “: ” (a colon followed by a space). The request header is terminated by a \r\n. The request may also include a body after the final \r\n, but you will not be parsing that in this project and may assume it doesn’t exist.

Parsing the HTTP Request

Now it’s time to implement the parsing.

At this point, your server is capable of parsing requests! Unfortunately, the server is not able to respond to them yet. We’ve done most of the work by writing the function response_type_format for you, but you need to finish it.

Responding to the HTTP Request

A response is formatted as follows:

[VERSION STRING] [RESPONSE CODE] [RESPONSE BRIEF]\r\n
[KEY 1]: [VALUE 1]\r\n
...
[KEY n]: [VALUE n]\r\n
\r\n
[BODY]

You’re only going to be supporting [VERSION STRING] = HTTP/1.1 and a single key-value header pair, Content-Type: text/html.

One of the primary components of the response is the “response status code” which represents a brief summary to the computer of the status of the response. This is things like “200” for “everything ok” or the ever-familiar “404” for “not found.”

The [response brief] is a short human-readable summary of the status code. For 200, this is "OK", or for 404 this is "Not Found".

See the documentation of the response_code_t type in include/http_response.h for details on what status codes you should support or see Mozilla’s documentation if you’re curious about other status codes.

response_code_t is an “enum” of status codes. Before you continue, you’ll want to familiarize yourself with enums by reading our explanation of enums and switch statements.

Now, go ahead and look at response_type_format. Since this function is mostly nothing new, we’ve done most of the work for you. However, you do need to fill out the status_brief function. You’ll want to use a switch statement.

Run make test to make sure you’ve finished everything.

Push your code to GitLab to finish the project.