A lightweight, high-performance HTTP/1.1 server written from scratch in C. This project implements the core of the HTTP protocol, supporting concurrent connections, file handling, and data compression.
The project demonstrates low-level systems programming concepts including TCP sockets, process management (fork), manual HTTP request parsing, and GZIP compression using zlib.
- TCP & Sockets: Raw implementation of server/client sockets.
- Concurrency: Handles multiple simultaneous clients using
fork()(multi-process architecture). - HTTP 1.1 Parsing: Manually parses HTTP verbs, paths, and headers.
- File Handling:
GET: Reads and serves files directly from the disk.POST: Creates and writes content to new files.
- GZIP Compression: Automatically compresses responses using
zlibif the client sends theAccept-Encoding: gzipheader. - Persistent Connections: Implicit support for
Connection: keep-alive.
The server follows a multi-process arhitecture to ensure stability and performance. The main process handles initial TCP handshake and immediately forks a child process for each new client connection.
sequenceDiagram
participant Client
participant Server (Main)
participant Server (Child)
Note over Server (Main): Socket Create -> Bind -> Listen
Client->>Server (Main): Connect (TCP Handshake)
Server (Main)->>Server (Main): Accept Connection
Server (Main)->>Server (Child): FORK Process
rect rgb(20, 20, 20)
Note right of Server (Child): Handle Request in Isolation
Client->>Server (Child): HTTP Request (GET /file)
Server (Child)-->>Client: HTTP Response (200 OK + Data)
Server (Child)->>Server (Child): Exit Process
end
Note over Server (Main): Continue listening for new clients...
- GCC (GNU Compiler Collection)
- Zlib (Required for compression. On Ubuntu/Debian:
sudo apt install zlib1g-dev)
Compile the server using gcc. It is essential to include the -lz flag to link the zlib library.
gcc main.c -o server -lzStart the server by executing the generated binary:
./serverThe server will listen on port 4221. Note the server is configured to serve and save files by default in the HOME directory of the current user.
Here is a complete list of supported routes and how to test them using curl.
Checks if the server is online
- Request:
GET / - Response:
200 OK
curl -v http://localhost:4221/Return the string provided in the URL as the response body. Useful for testing parsing logic.
curl -v http://localhost:4221/echo/hello-worldReads the User-Agent header sent by the client and returns it to the body.
- Request:
GET /user-agent - Response:
200 OK, Example Body:curl/8.5.0
curl -v http://localhost:4221/user-agentLooks for specific files in the user's HOME directory and sends the content back to the user. The Dir header can be of any form (Dir: 1, Dir: 12123), just needs to be specified for file reading.
- Request:
GET /test.txt - Response:
-
- If found:
200 OK, Content-Type:application/octet-stream, Body: file content
- If found:
-
- Else:
404 Not found
- Else:
curl -v -H "Dir: 1" http://localhost:4221/test.txtCreates (or overwrites) a file in the HOME directory with the content sent in the request body.
- Request:
POST /test.txt(Body: "Hello, World!") - Response:
201 Created
curl -v -d "Hello, World!" http://localhost:4221/test.txt The server automatically detects if the clients support compression.
- Request: Any request that includes the
Accept-Encoding: gzipheader. - Response: Includes
Content-Encoding: gzipheader and compressed body.
curl -v -H "Accept-Encoding: gzip" http://localhost:4221/echo/long_stringThis project was built as a self-directed educational implementation of the HTTP standard, inspired by the "Build Your Own HTTP Server" challenge from CodeCrafters.
I undertook this project to deconstruct the "black box" of web servers. My goal was to step away from high-level frameworks and understand the raw mechanics of the HTTP protocol and systems programming in C.
Building this server from scratch pushed me to understand:
- The Unix Philosophy: Working directly with file descriptors, system calls, and the TCP/IP stack without abstraction layers.
- Concurrency Models Implementing a multi-process architecture using
fork()to handle simultaneous connections, learning how the OS schedules and isolates processes.
This project served as a deep dive into the fundamentals of software engineering, bridging the gap between writing code and understanding how the machine executes it.