How I Built a Simple HTTP Server from Scratch using C
Tags: programming, web, tutorial, c
Published: 2023-05-11 15:08:00

You might wonder how web servers "serve" files, and how they deliver files that piece together a website that shows on your screen. Well, knowing how a web server is implemented is certainly a good way to "learn by building".

Although implementing a web server in C sounds very basic and detailed at a ground level, but it might help you better understand how HTTP works, and how servers actually interacts with clients. Let's get started!

## How Socket Works

Before building the web server, you need to know how a "socket" works. 

If a program (or process) running on a host is like a house, a socket is like a door that lets mails go in and out. When a person in the house receives or delivers a letter, he or she can be agnostic to how the mail is being delivered in the outside world. 

![Hosts using sockets to communicate](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3wokm1drbw00ax60o4ns.png)

Using the socket network interface, our web server can use a set of functions from C standard package `<sys/socket.h>` and let our server "talk to" clients over the internet. Those clients are also using sockets to talk to us, so socket is basically like a consesus on how to talk with each other over the internet.

![Client send requests and receive response from servers, both using sockets](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qbtgkb5i8msewgalxnnk.png)


## Set up Server Socket

To kick things off, I started by creating and configuring a socket for the server. I configured a few things:

- AF_INET: use IPv4 (vs IPv6)
- SOCK_STREAM: use TCP (vs UDP)
- INADDR_ANY: the server accepts connections from any network interface

![create & configure socket](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wc03s7cnu4c2h6cj4q72.png)

`bind()` binds the socket to a port (I'm using 8080 here) means the socket will listen to any clients trying to connect to the port 8080. `listen()` takes the maximum number of pending connections (I set it to 10). 

Now, my server is all set up and ready to accept incoming client connections.

## Handle Client Connections

With the server up and running, the next step is to handle incoming client connections. I used an infinite loop to continuously listen for new clients.

When a client connects, the server accepts the connection and creates a new thread to handle the client's HTTP request. This way, the server can handle multiple clients concurrently.

![handle client connections](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t0o9awyx69tnnak6em0q.png)

I defined a function `handle_client()` that handles an incoming request from a client. I only implemented GET here since it shows a good case on how server "serve" files to clients.

`handle_client()` receives the request data, extracts the requested file name, and decodes the URL (e.g. convert %20 to space). Then, it identifies the file extension, builds an HTTP response containing the requested file, and sends it back to the client.

![handle_client()](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xjlb2jbbqz2s0n5vfkzk.png)

## Build HTTP Response

Inside `handle_client()`, I defined another function `build_http_response()` that constructs an HTTP response, containing a header and the actual file.

![build_http_response()](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6skvxhk3jp6m6i3v1g80.png)

The function starts by building an HTTP header with the appropriate MIME type based on the file extension (e.g. if requesting.jpg, MIME type is image/jpeg). If the file doesn't exist, the function creates a 404 Not Found response. Otherwise, it retrieves the file's size and appends the 200 OK header to the response buffer.

![404 cat](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t20u6q0h8gzgtbmt7f4k.png)


Next, it reads the file content and appends it to the response buffer. The response buffer returned back to `handle_client()` is sent back to the client .I set BUFFER_SIZE to 1MB, meaning the server can handle any HTTP response with size up to 1MB.

## A Working HTTP Server!

{% embed https://www.loom.com/share/46d4105777cc40c48f05c1ec90c3843e %}


To wrap up, I hope this tutorial helps you better understand how servers actually interacts with clients.

Here is the [github repo](https://github.com/JeffreytheCoder/Simple-HTTP-Server) for the full code.

---

### 🌟 Special Giveaway

CodeCrafters has a [coding challenge](https://app.codecrafters.io/courses/http-server/overview) that guides you to build a HTTP server step-by-step in any language. Join using my [referral link](https://app.codecrafters.io/join?via=jeffreythecoder) to get 40% off 🚀