This project implements a simple, multi-threaded HTTP/1.1 server in Python as part of a Computer Networks (CN) assignment. The server is designed to handle concurrent client requests efficiently using a fixed-size thread pool. It serves static files from the resources directory, including HTML pages, text files, and images (PNG, JPG, JPEG). Additionally, it supports POST requests to the /upload endpoint for receiving and storing JSON data in an uploads subdirectory.
Key features include:
- Support for GET requests to serve static content.
- POST handling for JSON uploads with file persistence.
- Keep-alive connections for persistent sockets.
- Basic logging for requests, responses, and server events.
- Security validations to prevent common web vulnerabilities.
The server runs on localhost (127.0.0.1) by default on port 8080, with configurable host, port, and thread count via command-line arguments. It uses Python's standard libraries: socket for networking, threading and queue for concurrency, os and json for file operations, and argparse for CLI parsing.
The project structure includes:
server.py: Core server implementation.resources/: Static files directory.- HTML files:
index.html,about.html,contact.html. - Text:
sample.txt. - Images:
logo.png,photo.jpg. uploads/: Directory for uploaded JSON files (created dynamically).
- HTML files:
To run: python server.py [port] [host] [threads]. Example: python server.py 8080 127.0.0.1 10.
The server's architecture follows a classic multi-threaded design with a thread pool to manage incoming connections. Below is a text-based flowchart illustrating the request handling flow:
+-------------------+ +-------------------+ +-------------------+
| Main Loop | | Thread Pool | | Worker Thread |
| (Accept Socket) | ----> | (Queue Connection)| ----> | (serve_client) |
+-------------------+ +-------------------+ +-------------------+
| | |
v v v
+-------------------+ +-------------------+ +-------------------+
| Listen on Port | | Enqueue if Pool | | Parse HTTP Request|
| (bind/listen) | | Not Saturated | | (method, path, |
+-------------------+ +-------------------+ | headers) |
| | +-------------------+
| | |
v v v
+-------------------+ +-------------------+ +-------------------+
| Accept Connection | <---- | If Saturated: | | Validate Host & |
| from Client | | Send 503 & Close | | Path (Security) |
+-------------------+ +-------------------+ +-------------------+
| | |
v v v
| (Pool Monitor: Log Status Every 30s) |
| | |
+-----------------------------------------------------------+
|
v
+-------------------+ +-------------------+
| Handle Request | ----> | Send Response |
| - GET: Serve File | | (Headers + Body) |
| - POST: Upload | +-------------------+
| JSON to /upload | |
+-------------------+ v
| +-------------------+
| | Close if Timeout |
| | or Max Requests |
| +-------------------+
|
v
+-------------------+
| Log Response & |
| Connection Policy |
+-------------------+
- Main Loop: Continuously accepts new connections on the listening socket.
- Thread Pool: Queues accepted connections to worker threads. If all threads are busy, it queues up to a max size or rejects with 503.
- Worker Thread (serve_client): Handles keep-alive loops, parsing requests, validation, and dispatching to handlers.
- Handlers:
handle_getfor static files,handle_postfor uploads. - Pool Monitor: Background thread logs pool status periodically.
This design ensures scalability for moderate loads while preventing resource exhaustion.
The server handles binary files like images and text by sending them in chunks to avoid loading everything into memory at once. It checks the file path for security, ensures only allowed file types are served, and sets headers to prompt downloads for non-HTML files. This keeps things efficient and safe for files up to a few MB.
- Uses a fixed-size thread pool (default 10 workers) to manage concurrent connections.
- Queues incoming requests to prevent overloading the system.
- If the queue is full, sends a 503 error to clients.
- Includes a monitor thread that logs pool status every 30 seconds.
+-------------+ +-------------+ +-------------+
| Main Loop | --> | Queue | --> | Worker |
| Accepts | | Connections | | Threads |
| Connections | +-------------+ +-------------+
+-------------+ | |
| v v
| +-------------+ +-------------+
| | If Full: | | Process |
| | 503 Error | | Requests |
+-------+-------------+ +-------------+
To keep things safe, the server checks incoming requests carefully. It validates the host header to prevent spoofing, blocks path traversal attacks to stop unauthorized file access, and only allows specific file types like HTML, text, and images. For uploads, it restricts to JSON data under 5MB and enforces proper headers. There are also limits on request sizes, connection timeouts, and logging of suspicious activity. This makes it decent for local testing, but real-world use would need HTTPS and more.
- No HTTPS/TLS support, leaving data unencrypted and vulnerable to interception.
- Limited to GET and POST methods; no support for PUT, DELETE, or other HTTP verbs.
- Thread pool can saturate under high load, leading to 503 errors.
- File uploads restricted to JSON under 5MB; no multipart/form-data or other formats.
- No authentication or authorization; anyone can access or upload files.
- Static files only; no dynamic content generation.
- Basic error handling; uncaught errors may close connections silently.