Skip to Content
Course content

223: Building a Basic HTTP Server in C

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Think of an HTTP server like a fast-food drive-thru. First, the restaurant has to install a speaker box at a specific spot in the parking lot so customers know where to go. That's your socket and your port. Then, the employee inside just sits there waiting for the "ding" that signifies someone has pulled up. That's your listen call. When the ding happens, the employee opens a communication channel to hear the order. That's accept. Finally, the customer says, "I want a burger," the employee checks if they have burgers, and then hands a bag of food through the window. That's your read (the request) and write (the HTTP response).

In C, we don't have a high-level "Web Server" library in the standard toolkit. We have to build this plumbing manually using the Berkeley sockets API. It's a bit tedious, but once you do it, you'll realize that HTTP is really just a specific way of formatting strings sent over a TCP connection.

Setting Up the Socket Pipeline

To get this running, we need to tell the OS to reserve a port for us. I usually use port 8080 for local testing because ports below 1024 usually require root privileges, and I hate running my experimental code as root.

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

int main() {
    int server_fd, new_socket;
    struct sockaddr_in address;
    int addrlen = sizeof(address);

    // Create the socket file descriptor
    server_fd = socket(AF_INET, SOCK_STREAM, 0);
    
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY; // Listen on all available interfaces
    address.sin_port = htons(8080);       // Convert port to network byte order

    // Bind the socket to the address and port
    bind(server_fd, (struct sockaddr *)&address, sizeof(address));

    // Start listening for incoming connections
    listen(server_fd, 3);
    printf("Waiting for a connection on port 8080...\n");
}

Notice htons(). I see people forget this all the time. Computers disagree on whether the most significant byte comes first (Endianness). Network protocols demand Big-Endian, so we use "host-to-network short" to make sure the port number is readable by the network hardware.

Speaking the Language of the Browser

Once we accept() a connection, the browser sends us a block of text. It looks like GET /index.html HTTP/1.1. We don't actually need to parse the whole thing for a basic server; we just need to send back a response that the browser understands. If you send back random text, the browser will likely give you a "Malformed Response" error.

An HTTP response requires a header and a body, separated by a double newline. Here is how we'd handle a single request to serve a "System Status" page:

while(1) {
    new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen);
    
    char buffer[1024] = {0};
    read(new_socket, buffer, 1024);
    printf("Request received:\n%s\n", buffer);

    // This is the "Bag of Food" - the HTTP Response
    char *http_response = 
        "HTTP/1.1 200 OK\n"
        "Content-Type: text/html\n"
        "Content-Length: 52\n"
        "\n"
        "

System Status: Online

"; write(new_socket, http_response, strlen(http_response)); close(new_socket); }

I'm using a while loop here because a server that handles one request and then quits isn't much of a server. Just keep in mind that this specific implementation is "blocking." If one client has a slow connection, the whole server hangs until that write finishes. In a real-world production server, you'd use fork() or threads to handle each connection in parallel, but let's master the handshake first.

Closing the Connection Gracefully

In the code above, close(new_socket) is non-negotiable. If you forget to close the socket after sending the response, the browser will keep the connection open, waiting for more data, and you'll eventually leak all your available file descriptors. Your OS will eventually stop letting you open new sockets, and your server will crash. Always clean up your descriptors.




📋 Practical Task

Build a Custom Maintenance Page Server

Modify the server code from this lesson to implement a "Maintenance Mode" feature. Your server should do the following:

  • Listen on port 8081.
  • Instead of a "System Status: Online" message, return an HTTP 503 Service Unavailable status code.
  • The HTML body should contain a heading that says "Under Maintenance" and a paragraph stating "We'll be back online at 12:00 PM UTC."
  • Ensure the Content-Length header accurately reflects the size of your new HTML body.

Test your server by navigating to http://localhost:8081 in your web browser and verifying that the browser recognizes the 503 error status while displaying your custom message.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.