Skip to content

Corg-Labs/httpserver

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 

HTTP in C

A minimal HTTP/1.1 static file server in a single C file. It accepts TCP connections, parses GET request lines, maps URL paths to the filesystem, and streams file contents back with correct headers. A live access log prints every request with status code, byte count, and response time. Concurrent requests are handled via fork().

Part of the Corg-Labs collection of single-file C programs.


Features

  • TCP socket lifecycle: socketbindlistenaccept
  • GET and HEAD method support; 405 for everything else
  • %xx URL decoding and directory-traversal (..) protection
  • Automatic index.html serving for directory paths
  • MIME type table covering HTML, CSS, JS, JSON, PNG, JPEG, SVG, WASM, and more
  • fork()-per-connection concurrency; zombie reaping via SIGCHLD

Tutorial

1. TCP Socket Setup

int lfd = socket(AF_INET, SOCK_STREAM, 0);

int yes = 1;
setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));

struct sockaddr_in addr = {
    .sin_family      = AF_INET,
    .sin_port        = htons((uint16_t)port),
    .sin_addr.s_addr = INADDR_ANY,
};
bind(lfd, (struct sockaddr *)&addr, sizeof(addr));
listen(lfd, 64);

SO_REUSEADDR lets the port be rebound immediately after restart.

2. Fork-per-Connection Concurrency

pid_t pid = fork();
if (pid == 0) {
    close(lfd);               /* child doesn't need listener */
    handle_client(cfd, root);
    close(cfd);
    exit(0);
}
close(cfd);                   /* parent closes its copy */

SIGCHLD is caught by a handler that calls waitpid(-1, NULL, WNOHANG) to reap zombies.

3. HTTP Request Parsing

char method[16], raw_path[1024], proto[16];
sscanf(req, "%15s %1023s %15s", method, raw_path, proto);

URL percent-encoding is decoded: %xx hex sequences are converted to their byte values. Any path containing .. is rejected with 403.

4. MIME Type Resolution

static const mime_entry_t MIME_TABLE[] = {
    { "html", "text/html; charset=utf-8" },
    { "js",   "application/javascript"   },
    { "png",  "image/png"                },
    { NULL,   "application/octet-stream" },   /* fallback */
};

strrchr finds the file extension; the table is scanned with strcasecmp.

5. File Streaming

snprintf(hdr, sizeof(hdr),
    "HTTP/1.1 200 OK\r\nContent-Type: %s\r\n"
    "Content-Length: %lld\r\nConnection: close\r\n\r\n",
    mime, (long long)st.st_size);
send_all(cfd, hdr, hlen);

char buf[8192];
while ((n = read(ffd, buf, sizeof(buf))) > 0)
    send_all(cfd, buf, n);

send_all loops until every byte is written, handling short writes.


Build

gcc http.c -o http

Run

./http                 # port 8080, serve current directory
./http 9090 /var/www   # custom port and root

Open http://localhost:8080/ in a browser. The terminal shows a live access log.


Concepts Practiced

  • TCP socket lifecycle (socket, bind, listen, accept)
  • HTTP/1.1 request and response format
  • fork()-per-connection concurrency and zombie reaping
  • URL percent-decoding and path sanitisation
  • MIME type resolution from file extensions
  • Reliable write loops for streaming file content

Dependencies

Standard C library + POSIX (sys/socket.h, sys/stat.h, sys/wait.h). No external libraries.

About

▶ A minimal HTTP/1.1 static file server in a single C file

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages