HTTPHaven is a multithreaded, HTTP/1.1-compliant web server built directly on TCP sockets to explore how the protocol actually works under the hood - request parsing, method semantics, content negotiation, caching, and connection management, all implemented by hand from RFC 2616.
Every response header below is generated by hand - no library fills them in:
$ curl -i http://127.0.0.1:12001/
HTTP/1.1 200 OK
Etag: e4cd62ee7fbcdccd9bb8f4f39f33ecbd
Server: http-server/1.2.4 (Ubuntu)
Accept-Ranges: bytes
Set-Cookie: client_cookie:=UbgCdi
Date: Sat, 18 Jul 2026 04:19:25 UTC
Content-Encoding: identity
Content-Type: text/html
Content-Length: 10933
$ curl -o /dev/null -w "%{http_code}\n" -H "Range: bytes=0-9" http://127.0.0.1:12001/sample.txt
206
$ curl -o /dev/null -w "%{http_code}\n" -X PUT -H "Content-Type: text/plain" -d "hello" http://127.0.0.1:12001/demo.txt
201
$ curl -sD - -o /dev/null -H "Accept-Encoding: gzip" http://127.0.0.1:12001/ | grep -i encoding
Content-Encoding: gzipAnd serving a page in the browser:
- HTTP methods -
GET,POST,PUT,DELETE,HEAD, with correct semantics and per-method routing - 15+ status codes -
200,201,204,206,304,400,403,404,405,406,412,415,416,501,505, each with proper error pages - Caching -
ETaggeneration andIf-None-Match/If-Matchvalidation with304 Not Modified/412 Precondition Failed - Range requests - partial content (
206) withAccept-Rangesand416for unsatisfiable ranges - Content negotiation -
Accept-Encodinghonored with gzip, brotli, deflate, and LZW compression - Persistent connections - configurable keep-alive with max-request and timeout limits
- Concurrency - one thread per connection on top of a plain
socketaccept loop - Multipart form data - file uploads and form submissions parsed by hand
- Cookies - set and retrieve cookies for basic session tracking
- Configurable - document root, ports, timeouts, log paths, and connection limits via a
.conffile - Access & error logs - Apache-style logging of every request and failure
# 1. Install dependencies (compression codecs + test client)
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
# 2. Start the server (reads abhishekS.conf, defaults to port 12001)
bash start_server.sh
# 3. Talk to it
curl -i http://127.0.0.1:12001/
# 4. Stop or restart it
bash stop_server.sh
bash restart_server.shtesting.py fires 61 checks at a live server - every method, conditional headers, byte ranges, content negotiation, multipart uploads, malformed raw-socket requests, and concurrent clients - and prints one pass/fail line per request with the RFC reason for each expected status:
python testing.py # one line per check + summary, exits non-zero on failure
VERBOSE=1 python testing.py # also dump full request/response headers per checkThe server is configured through abhishekS.conf:
[DOCUMENTROOT]
DocumentRoot = httpfiles/
[POSTROOT]
PostRoot = POST/
[PUTROOT]
PutRoot = PUT/
[PIDFILE]
pidfile = pid.txt
[TIMEOUT]
TimeOut = 300
[KEEP_ALIVE]
KeepAlive = Off
[MAX_KEEP_ALIVE_REQUESTS]
MaxKeepAliveRequests = 5
[KEEP_ALIVE_TIMEOUT]
KeepAliveTimeout = 20
[ACCESSLOG]
AccessLog = logs/access.log
[ERRORLOG]
ErrorLog = logs/error.log
[MAX_SIMULTANEOUS_CONNECTION]
MaxSimultaneousConnection = 5
[PORT_NUMBER]
PortNumber = 12001sequenceDiagram
participant C as Client
participant M as Main thread<br/>(socket accept loop)
participant W as Worker thread
participant FS as Filesystem
C->>M: TCP connect
M->>W: spawn thread per connection
C->>W: GET /index.html HTTP/1.1
W->>W: parse request line + headers
W->>FS: resolve path under DocumentRoot
W->>W: ETag check → 304? · Range → 206?<br/>negotiate Accept-Encoding
W->>C: status + headers + (compressed) body
W->>W: keep-alive? loop : close
Note over W: access.log / error.log entry written
Every request is recorded in an Apache-style access log:
127.0.0.1:50126 - [Sun, 14 Nov 2021 17:23:09 GMT] "GET / HTTP/1.1" 200 3148 python-requests/2.22.0
Fields: client IP and port, timestamp, request line, status code, response size in bytes, and the client's User-Agent.
Failures get their own log with an error class appended:
127.0.0.1:50128 - [Sun, 14 Nov 2021 17:23:09 GMT] "GET /index23.html HTTP/1.1" 404 Not Found python-requests/2.22.0 Client-Error:4xx
Fields: client IP and port, timestamp, request line, status code and reason, User-Agent, and the error category (Client-Error:4xx / Server-Error:5xx).
Both paths are set in the config file (logs/access.log and logs/error.log by default).
HTTPHaven/
├── http-server.py # Entry point: socket setup + accept loop, thread spawning
├── methods.py # The heart: request parsing, method handlers, caching,
│ # ranges, compression, keep-alive
├── functions.py # Helpers: headers, dates, error pages
├── logger.py # Access / error log writers
├── testing.py # Regression test suite (run against a live server)
├── abhishekS.conf # Server configuration
├── httpfiles/ # Document root (GET)
├── POST/ # POST upload destination
├── PUT/ # PUT upload destination
└── logs/ # access.log + error.log
- RFC 2616 - the foundational HTTP/1.1 specification this server is built against
- MDN Web Docs - comprehensive documentation on HTTP and web technologies

