A small educational HTTP server implemented in Python using raw sockets and a simple thread pool. It serves static files from the resources/ folder, accepts JSON uploads, and can stream a ZIP bundle of non-HTML resources on demand.
This repo is intended for learning and light testing — not production use.
- Language: Python 3
- Entry point:
server.py - Static content directory:
resources/ - Uploads are stored under
resources/uploads/ - Default host:
127.0.0.1, default port:8080 - Thread pool size: configurable (default 10)
Key design points:
- Simple socket-based HTTP parser and handlers in
http_handler.py. - Streaming ZIP creation for the
/download_allendpoint to avoid large memory usage. - Minimal dependencies (standard library only).
server.py— main server; reads command-line args (port, host, max_threads), accepts connections and dispatches them to the thread pool.http_handler.py— request parser and request handlers (handle_get,handle_post). Handles static files, downloads, and uploads.thread_pool.py— tiny thread pool implementation used by the server.utils.py— small helpers (logging, safe path resolution, date formatting, upload filename generation).resources/— HTML, images, text samples and other static files.resources/uploads/contains JSON files saved via POST.
- Make sure you are running Python 3.8+.
- From the project root, run:
python3 server.pyOptional arguments:
python3 server.py 9090— start on port 9090 instead of 8080.python3 server.py 8080 0.0.0.0— specify port and bind host.python3 server.py 8080 127.0.0.1 20— port, host, max threads.
Server logs are printed to stdout. Use Ctrl+C to stop.
If you see OSError: [Errno 48] Address already in use when binding, another process is using that port. To find/stop it on macOS:
lsof -i :8080
kill -9 <PID>
# Or run the server on a different port:
python3 server.py 9090Note: the server accepts basic HTTP methods (GET, HEAD, POST). Responses include standard headers and status codes.
- Serve static files (HTML, images, text)
- GET
/or/index.html— servesresources/index.html. - GET
/about.html,/contact.html, or any other file underresources/— returns the file when permitted.
Example (download an image):
curl -I http://127.0.0.1:8080/mikasa.jpeg # HEAD to inspect headers
curl http://127.0.0.1:8080/mikasa.jpeg -o mikasa.jpegSupported binary extensions (configured in http_handler.py): .png, .jpg, .jpeg, .txt (these are served as attachment with Content-Disposition by default).
- Download all non-HTML resources as a ZIP
- GET
/download_allor/download-all— server creates a temporary zip (containing non-HTML files fromresources/), streams it to the client, and removes the temporary file after streaming. - HEAD
/download_all— returns the response headers only (and removes the temporary file).
Example:
# inspect headers
curl -I http://127.0.0.1:8080/download_all
# download the zip
curl http://127.0.0.1:8080/download_all -o all_resources.zip
unzip -l all_resources.zipNotes:
- The server dynamically includes non-HTML files from
resources/(excluding theuploads/folder). - Temporary zip files are created on disk and removed after successful streaming; this avoids keeping large archives in memory.
- Upload JSON via POST
- POST JSON (Content-Type: application/json) to any path (the server does not strictly require a specific upload path) — the JSON body will be parsed and saved to
resources/uploads/with a generated filename.
Example:
curl -v -X POST http://127.0.0.1:8080/upload \
-H "Content-Type: application/json" \
-d '{"name":"alice","message":"hello"}'
# Example response (JSON):
# {"status":"success","message":"File created successfully","filepath":"/uploads/upload-2025-10-10-...json"}- The server requires
Content-Type: application/json. Other content types return415 Unsupported Media Type. - Upload files are written to
resources/uploads/— directory is created if missing.
- The HTTP parser in
http_handler.pyis intentionally simple and not fully RFC-compliant. It works for basic requests used in this project, but edge cases (chunked transfer, multipart/form-data, very long headers) are not supported. - The server sends HTML responses with
Content-Type: text/html; charset=utf-8and binary responses asapplication/octet-streamwithContent-Disposition: attachment. - For
/download_all, the server streams a temporary ZIP to avoid high memory usage. If an error occurs during ZIP creation, the server returns500 Internal Server Error.
- Port in use: see the bind error section above (use
lsofto locate and kill). - Large downloads:
/download_alluses temporary files — if the server crashes while streaming, temporary files may remain in/tmp/(or system temp). They are removed automatically after successful transfers but can be manually inspected/removed if needed. - If static files return 403 or 404, check that
resources/contains the file and paths are correct. The server protects against directory traversal; malformed path requests will return403 Forbidden.
- Make visual/style changes by editing the HTML files in
resources/(they use inline<style>blocks in this repository). - If you want to consolidate CSS into a single file for maintainability, move the shared rules to
resources/styles.cssand link it from the HTML head.
- Replace the simple HTTP parser with a library (or a more complete parser) for robustness.
- Add proper routing so uploads require a dedicated
/uploadendpoint and static serving is clearly separated. - Add tests for the handlers (unit tests for parsing and endpoints).
- Add graceful shutdown and active-request draining on server stop.
If you'd like, I can:
- Run the server and exercise the endpoints (download a zip, POST a JSON) and paste the responses/logs here.
- Add an example script in
examples/that performs the common curl calls automatically.
What would you like me to do next?