CS 350: Operating Systems & Systems Programming
Assignment 3: Multi-Threaded HTTP Web Server
In this assignment you will build a multi-threaded HTTP/1.0 web server in C that can serve static files to multiple clients concurrently. This assignment builds directly on the socket-programming concepts from Lecture 12 and the POSIX threading material from Lecture 14.
Learning Objectives
- Use the Berkeley sockets API (
socket,bind,listen,accept) to build a TCP server. - Spawn a worker thread per connection using
pthread_create, and manage thread lifecycle withpthread_join/pthread_detach. - Protect shared state (connection counters, logging buffer) with
pthread_mutex_tto avoid race conditions. - Parse a minimal HTTP/1.0 request line and respond with correct status codes (200, 404, 400).
Requirements
- Your server must accept a port number as a command-line argument:
./httpserver <port>. - Each incoming connection must be handled on its own thread — no forking, no single-threaded blocking loops.
- A global request counter must be incremented on every request, guarded by a mutex. We will stress-test this with
ab(Apache Bench) at 200 concurrent connections; any lost updates will fail the correctness tests. - Implement a bounded thread pool (max 16 concurrent workers) using a condition variable to block additional connections until a worker frees up — do not spawn unbounded threads.
- Serve files relative to a
./wwwdirectory. Requests outside that directory (path traversal via../) must return400 Bad Request. - Gracefully handle
SIGINTby joining all outstanding threads before exit — no dangling connections in valgrind's output.
Race condition warning: the #1 point deduction last semester was an unguarded increment on the shared request-count variable. Run your submission under
helgrind or ThreadSanitizer (-fsanitize=thread) before submitting — a clean run is required for full credit on Requirement 3.Starter Code
Starter code is provided in hw3_starter.tar.gz on the course Modules page. The socket setup boilerplate is provided; you are responsible for the threading and request-handling logic:
// server.c — provided scaffold, fill in handle_connection()
int main(int argc, char *argv[]) {
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
int opt = 1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
struct sockaddr_in addr = {
.sin_family = AF_INET,
.sin_port = htons(atoi(argv[1])),
.sin_addr.s_addr = INADDR_ANY
};
bind(server_fd, (struct sockaddr *)&addr, sizeof(addr));
listen(server_fd, SOMAXCONN);
while (1) {
int *client_fd = malloc(sizeof(int));
*client_fd = accept(server_fd, NULL, NULL);
pthread_t tid;
pthread_create(&tid, NULL, handle_connection, client_fd);
pthread_detach(tid); // TODO: replace with bounded pool
}
}
Submission Guidelines
- Submit a single
.tar.gzcontainingserver.c, any additional.c/.hfiles, and yourMakefile. - Alternatively, upload individual
.c/.hfiles directly using the file upload box below. - Include a short
README(as text entry or plain file) describing your thread-pool design and any known limitations. - Late submissions: −10% per day, up to 3 days, per the syllabus late policy.
Submit Assignment
Drag a file here, or browse your computer
Accepted file types: c, cpp, h