Skip to content

Zingzy/http-server

Repository files navigation

Multi-Threaded HTTP Server

Welcome to the Multi-Threaded HTTP Server project. This repository explores how to build a production-inspired HTTP server in pure Python, focusing on concurrency, request validation, and static/dynamic payload handling. The code demonstrates GET/POST processing, keep-alive semantics, a cooperative thread pool, and basic safeguards against common mistakes such as directory traversal and Host header spoofing.


🧭 Architecture Overview

  • http_server.py sets up the listening socket, manages the worker pool, and routes requests to verb-specific handlers.
  • GetHandler validates resource paths, enforces allowed extensions, and streams either text or binary bodies.
  • PostHandler validates JSON uploads, persists them into resources/uploads/, and returns structured JSON responses.
  • Builder classes (HtmlBuilder, HttpResponseBuilder) encapsulate HTML/error generation and HTTP response headers to keep handlers focused on business logic.
  • Utility modules (client.py, client_udp.py, server.py, server_udp.py, simple_server.py) showcase auxiliary experiments (HTTP client interactions, UDP echoing) and can be used for manual testing.
flowchart TD
    A[Client] -->|HTTP Request| B(Socket Listener)
    B -->|Accept| C{Thread Pool Queue}
    C -->|Dispatch| D[Worker Thread]
    D --> E[parse_http_request]
    E --> F{Method Handler}
    F -->|GET| G[GetHandler]
    F -->|POST| H[PostHandler]
    G --> I[Static/Binary Response]
    H --> J[JSON Persistence]
    I --> K[HttpResponseBuilder]
    J --> K[HttpResponseBuilder]
    K --> L{Connection Directive}
    L -->|Keep-Alive| D
    L -->|Close| M[Socket Close]
Loading

🗂️ Project Structure

multi-threaded-http-server/
├─ http_server.py            # Main multi-threaded HTTP server
|─ tests/                    # Automated test suite using pytest
├─ resources/                # Static site assets served by the HTTP server
│  ├─ index.html
│  ├─ about.html
│  ├─ contact.html
│  ├─ hello.txt
│  └─ uploads/               # JSON uploads generated by POST /upload
├─ pyproject.toml            # Project metadata and tooling dependencies
└─ README.md

📦 Binary Transfer Implementation

  • GetHandler.serve_file automatically detects non-HTML extensions and routes them through HttpResponseBuilder.add_disposible_file.
  • Binary payloads are read as raw bytes and returned with Content-Disposition: attachment, retaining the original filename.
  • Content-Length is calculated from the file system to prevent short reads and keep streaming simple.
  • HTML files are served as UTF-8 text; other allowed extensions (txt, png, jpg, jpeg) are streamed as bytes.
  • The handler logs transfer size and type to help diagnose performance issues.

🧵 Thread Pool Design

sequenceDiagram
    participant Client
    participant Listener
    participant Queue
    participant Worker
    Client->>Listener: TCP SYN
    Listener-->>Client: TCP SYN/ACK (handshake)
    Listener->>Queue: Enqueue (conn, addr)
    Worker->>Queue: get()
    Queue-->>Worker: (conn, addr)
    Worker->>Worker: handle_persistent_connection()
    Worker->>Client: Response bytes
    alt Connection Keep-Alive
        Worker->>Queue: Reuse worker loop
    else Close
        Worker->>Client: FIN/ACK
    end
Loading
  • A global queue.Queue buffers accepted sockets while workers are busy.
  • MAX_THREADS worker threads run thread_worker, blocking on the queue with a one-second timeout so they can exit cleanly on shutdown.
  • active_threads and thread_lock track current load and allow the listener to respond with 503 Service Unavailable when both the queue and workers are saturated.
  • Each worker processes up to MAX_REQUESTS_PER_CONNECTION requests per socket with per-request connection-type negotiation.

🛡️ Security Measures

  • Host Header Whitelist: validate_http_request ensures the Host header matches INTERFACE/PORT combinations returned by get_allowed_hosts.
  • Path Normalisation: GetHandler.validate_resource_path rejects absolute paths, traversal attempts (..), and backslash delimiters.
  • Extension Allow-List: Only html, txt, png, jpg, and jpeg can be served.
  • Uploads Segregation: JSON POST bodies are stored beneath resources/uploads/ with timestamped filenames to prevent collisions.
  • Graceful Overload Handling: The listener returns HTTP 503 instead of dropping sockets when the queue is full, protecting the process from resource exhaustion.

⚠️ Known Limitations & Future Ideas

  • HTTP parsing is line-oriented and does not support chunked transfer encoding or multi-part uploads.
  • No TLS support; all traffic is plaintext.
  • MIME detection is extension-based; a dedicated library (e.g., mimetypes) would improve accuracy.
  • Requests larger than the 8 KB buffer are truncated; a streaming parser would be safer for large uploads.

🛠️ Build and Tooling

  • The project targets Python >= 3.9 as declared in pyproject.toml.
  • Use uv for isolated virtual environments and dependency management.
  • Optional developer tooling: pytest and ruff are listed under the dev dependency group.
# Install uv if you have not already
python -m pip install uv

# Create a virtual environment and install the project (editable)
uv sync

🚀 Running the Server

# Syntax: uv run http_server.py <port> <host> <max-threads>
uv run .\http_server.py 8080 127.0.0.1 10

# Examples
uv run .\http_server.py           # defaults to port 8080, host 127.0.0.1, 10 threads
uv run .\http_server.py 8000      # change port only
uv run .\http_server.py 8000 0.0.0.0 50
  • Static files live under resources/. Visiting http://127.0.0.1:8080/ serves index.html.
  • Upload JSON with a POST request to /upload using Content-Type: application/json. The server saves the file and returns the path.
  • Keep-alive is enabled by default for HTTP/1.1 clients unless they request Connection: close.

🧪 Manual cURL Tests

# Fetch landing page (static HTML)
curl http://127.0.0.1:8080/

# Download binary asset and verify size
curl http://127.0.0.1:8080/logo.png --output logo.png
Get-FileHash .\logo.png

# Upload JSON payload (saved under resources/uploads/)
curl -X POST ^
  -H "Content-Type: application/json" ^
  -d '{"message":"hello"}' ^
  http://127.0.0.1:8080/upload

# Trigger error handling (expected 404)
curl -i http://127.0.0.1:8080/nonexistent.png

# Demonstrate traversal defence (expected 403)
curl -i http://127.0.0.1:8080/../etc/passwd

✅ Testing and Verification

  • Automated pytest suite: Run uv run pytest -vv to start an integration-focused suite that boots the server, hits core GET/POST paths, and asserts binary integrity via checksums.
  • Logging review: Monitor console output for timeout warnings, host validation results, and file transfer summaries.


© zingzy . 2025

All Rights Reserved

About

A multi-threaded HTTP Server in Python

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published