socket up.
A production-grade HTTP/1.1 web server written in Modern C++ using WinSock. No framework. No abstraction borrowed. Served, explained, and demonstrated right here.
What is Lothal?
Lothal is a production-grade HTTP/1.1 web server built entirely from scratch in Modern C++, communicating directly with the Windows networking stack through WinSock. No framework. No borrowed abstraction. Every layer — from the TCP socket that listens for connections, to the request parser that decodes raw bytes into an HTTP message, to the middleware pipeline that processes each request through its stages — was implemented by hand.
It is an educational project with production ambitions. The architecture mirrors what you would find in a real-world server — a thread pool, an in-memory file cache, ETag-based caching, Gzip compression, chunked transfer encoding, byte range support, and a full middleware pipeline. The goal: understand how the modern web actually works by building it.
The project is named after Lothal — the ancient Indus Valley port city, excavated in Gujarat. It was one of the world's first planned dockyards, where ships arrived, goods were inspected, sorted, and dispatched through carefully engineered channels. An HTTP server works the same way. A request arrives. It is inspected. It passes through channels. It is sorted. A response departs.
"The best way to understand a port city is to watch a ship arrive."
What's implemented
TCP Server
Raw WinSock socket, bind, listen, accept — all from scratch
HTTP Parser
Parses request line, headers, query params, body, route params
Middleware Pipeline
Composable middleware: Logger, Auth, CORS, Rate Limiter, Exception
Thread Pool
Multi-threaded request handling with a configurable pool
RAM File Cache
In-memory caching with ETag and Last-Modified support
Gzip Compression
Content-Encoding: gzip when client sends Accept-Encoding
Chunked Transfer
Transfer-Encoding: chunked for streaming responses
Byte Ranges
206 Partial Content — enables video streaming
Dynamic Router
Compiled route patterns, named params, priority-based matching
How a request travels through Lothal
When a browser sends an HTTP request, it is like a merchant ship entering the ancient port. Watch every step — from the TCP handshake at the harbor gates to the response departing back across the water.
// Click "Send a Live Request" to see the actual bytes...
// Response will appear here...
Anatomy of an HTTP Request
GET /api/hello HTTP/1.1 ← Request Line: method + path + version Host: lothal.local ← Required header: target server Accept: text/plain, */* ← What formats the client understands Accept-Encoding: gzip, deflate ← Compression support Connection: keep-alive ← Reuse the TCP connection Authorization: Bearer lothal-demo ← Auth token for /api/* routes ← Empty line marks end of headers ← Body follows (empty for GET)
TCP Handshake
SYN → SYN-ACK → ACK. Three packets before a single byte of HTTP is sent.
Socket Accept
accept() returns a client socket. The thread pool picks it up immediately.
recv() Loop
Raw bytes arrive. Lothal reads until the \r\n\r\n header boundary.
Request Parsing
Method, path, version, headers, query string, body — all extracted in a single pass.
Pipeline → Router
Middleware runs in order. If all pass, the router matches the path to a handler.
send() Response
The built HTTP response is written back to the socket. Keep-Alive? Stay open.
The Middleware Pipeline
The Harappan dockyard used a system of channels to route water and goods. Lothal's middleware pipeline works the same way. Each request flows through a series of gates. Each gate can inspect, modify, or stop the request.
ExceptionMiddleware
Catches unhandled exceptions. Prevents crashes from reaching the socket.
LoggerMiddleware
Records every request: method, path, status, duration.
CorsMiddleware
Adds CORS headers. Handles OPTIONS preflight automatically.
AuthMiddleware
Guards /api/* routes. Requires Bearer token.
RateLimitMiddleware
Sliding window. Max 500 requests per 10 seconds per client.
StaticFileMiddleware
Serves /public/ files with MIME detection and caching.
The Dynamic Router
Ancient trade routes were memorized patterns — routes from the dockyard to the workshops, to the granary, to the city gates. Lothal's router compiles your path patterns at startup and matches incoming requests in microseconds.
Route Compilation
/users/:id into a regex
C++
// RouteCompiler.cpp — pattern → regex conversion // Pattern: /users/:id/posts/:postId // Becomes: ^/users/([^/]+)/posts/([^/]+)$ // Params: {0: "id", 1: "postId"} string RouteCompiler::compile(const Route& route) { string pattern = "^"; for (auto& segment : route.segments) { if (segment.starts_with(':')) { pattern += "([^/]+)"; // Named capture } else { pattern += segment; } pattern += '/'; } pattern += "$"; return pattern; }
Route Priority
Static Exact
/api/hello — highest priority. No wildcards.
Named Param
/users/:id — one variable segment.
Wildcard
/files/* — matches anything. Lowest priority.
HTTP Methods Playground
The workshops of Lothal processed goods by type — copper, ceramics, beads. HTTP methods define the type of operation. Try every method. Watch Lothal respond.
Bearer lothal-demo
/api/* routes — already included below
Retrieve
Read data. Safe, idempotent. The most common method.
Create
Send data to create a resource. Not idempotent.
Replace
Replace a resource entirely. Idempotent.
Update
Partial update. Only the changed fields.
Delete
Remove a resource. Idempotent.
Headers Only
Same as GET but no body. Check if resource exists.
CORS Preflight
Ask the server what methods it allows. Used by browsers.
Custom Method
Lothal's own extension. A GET with a structured query body.
Caching System
The Harappan granary stored grain so the city didn't have to import it every day. Lothal's RAM cache stores files in memory — so the disk doesn't get touched on every request. ETag and Last-Modified tell the browser when grain is still fresh.
◈ Cold Request (Cache Miss)
First request — file read from disk, full 200 response
⊕ Warm Request (Cache Hit)
Second request — server returns 304, zero bytes body
ETag
A fingerprint of the file. If it matches If-None-Match, server returns 304.
Last-Modified
File modification date. Browser sends If-Modified-Since next time.
Cache-Control
max-age, no-cache, private — fine-grained cache policy.
304 Not Modified
The most elegant response: headers only, no body. Zero bandwidth for unchanged files.
Performance Features
The Harappan engineers built the world's first known dockyard, water channels, and drainage systems. Lothal's engineers built compression, streaming, partial content, and concurrent request handling.
Request the same content with and without Accept-Encoding: gzip. Watch the size difference.
Without Gzip
With Gzip
Lothal's /stream endpoint sends 100 messages using Transfer-Encoding: chunked. Each chunk arrives independently — no Content-Length needed.
HTTP Range: bytes=X-Y requests. Lothal returns only the requested portion with status 206 Partial Content.
Lothal uses a thread pool to handle concurrent requests. The /slow endpoint takes 5 seconds. Fire multiple simultaneously — they run in parallel.
With Connection: keep-alive, the TCP connection stays open between requests. Watch multiple requests flow through a single connection.
Security & Logging
The watchtower guarded the dockyard. Every arriving ship was inspected. Suspicious vessels turned away. Records kept. Lothal's security layer works the same way.
Lothal limits /api/* to 500 requests per 10 seconds per client. Click rapidly to trigger the 429.
All /api/* routes require a Bearer token. Try with and without it.
Without Auth
With Auth
Lothal's CORS middleware adds the appropriate headers to every response. Try an OPTIONS preflight.
The /crash endpoint throws an intentional C++ exception. The ExceptionMiddleware catches it and returns a clean 500.
All requests you make on this page are logged by Lothal's LoggerMiddleware. Tracked live.
Architecture & Source
Source File Map
server.cpp
TCP socket lifecycle: bind, listen, accept, thread dispatch
HttpRequest.cpp
HTTP/1.1 request parser: request line, headers, body, params
HttpResponse.cpp
Response builder: status, headers, body, chunked, gzip
router.cpp
Route registration, priority sorting, and dispatch
RouteCompiler.cpp
Converts /users/:id patterns to compiled regex
RouteMatcher.cpp
Matches incoming path against compiled routes, extracts params
MiddlewarePipeline.cpp
Composes the middleware chain, manages the next() call
ThreadPool.cpp
Work queue + worker threads. N configurable worker threads.
FileCache.cpp
RAM-based file cache. Stores file bytes + metadata in memory
Compression.cpp
Gzip compression using zlib. Applied before sending
ChunkedResponse.cpp
Encodes body into HTTP chunked transfer encoding
ETag.cpp
Generates ETag fingerprint. Handles If-None-Match → 304
StaticFileMiddleware.cpp
Serves /public/ with MIME detection, caching, ranges
AuthMiddleware.cpp
Guards /api/* paths. Checks Authorization header
RateLimitMiddleware.cpp
Per-IP sliding window counter. Returns 429 when exceeded
CorsMiddleware.cpp
Adds CORS headers. Responds to OPTIONS preflight
Logger.cpp
File + console logger. Thread-safe. Timestamps every entry
MimeTypes.cpp
Extension → MIME type mapping. ~30 types including binary
lothal.conf
Config: port, threads, document root, keep-alive, gzip
HttpRange.cpp
Parses Range: header, validates ranges, serves 206 partial
Why these choices?
Thread Pool instead of one thread per connection
Creating a thread for every connection is expensive. A pool with N threads handles N concurrent requests with zero allocation overhead per request. Lothal's pool is configured via lothal.conf.
RAM Cache instead of disk reads per request
Disk I/O is orders of magnitude slower than RAM. Static files (HTML, CSS, JS, images) are loaded once into an in-memory map. Subsequent requests are served in nanoseconds.
Custom Router instead of a regex library
The router compiles patterns at startup into a sorted, prioritized list. At request time, matching is a sequential scan through pre-compiled patterns — fast, predictable, and controllable.
WinSock instead of a cross-platform socket library
The goal was to understand sockets at the OS level. WinSock is the Windows native API — the actual layer between your code and the network driver. No abstraction was used intentionally.
Lothal
"This website is served by Lothal itself."
Named after Lothal, the ancient Indus Valley port city excavated in Gujarat, India (2400 BCE).
Port Architect · Shantanu Gopal Vispute