A multi-threaded HTTP server built from scratch in Java — no frameworks, no Netty, no Spring. Raw sockets, manual HTTP parsing, thread pool concurrency.
- Accepts TCP connections on a configurable port
- Parses raw HTTP/1.1 requests byte-by-byte from the socket
InputStream - Routes
HEAD``GETandPOSTrequests to static files or template-injected HTML - Serves files from a configurable webroot directory
- Returns structured HTTP responses with correct status codes, headers, and body
- Handles malformed requests, missing files, and server errors gracefully
The server is structured in two clear zones separated by the syscall boundary.
The kernel owns everything below your Java code:
- TCP/IP stack — reassembles inbound packets into a byte stream, segments outbound bytes into packets
- Accept queue — holds completed TCP connections (after 3-way handshake) until your code calls
accept() - Socket send/recv buffers —
OutputStream.write()copies bytes here; the kernel handles NIC timing - VFS / Filesystem — services
FileInputStream.read()syscalls, reads from SSD - NIC driver — hardware interface to the network wire
Your Java code never talks to the NIC or SSD directly. It makes syscalls and the kernel handles the rest.
ServerListenerThread
└── calls accept() in a loop
└── submits each Socket to the thread pool
ThreadPool (Executors.newFixedThreadPool(500))
└── selects an idle thread
└── executes HttpConnectionWorkerThread
HttpConnectionWorkerThread
├── HttpParser
│ ├── parseRequestLine() → method, target, HTTP version
│ ├── parseHeaders() → name:value pairs
│ └── parseBody() → form-urlencoded or JSON
├── HttpRequest → structured result
├── Router
│ ├── GET → static file or template injection
│ ├── POST → body params
│ └── HEAD/DELETE → 501 Not Implemented
├── WebRootHandler → reads files from webroot (SSD via syscall)
├── HttpResponse.build() → assembles status line + headers + body bytes
└── OutputStream.write() → copies to kernel send buffer → TCP → NIC → wire
HTTP is constructed twice and parsed twice — symmetrically:
| Side | Outbound | Inbound |
|---|---|---|
| Client | Browser builds GET /index.html HTTP/1.1\r\n... |
Browser parses response headers + body |
| Server | HttpResponse.build() assembles response bytes |
HttpParser reconstructs request from raw bytes |
Between those two moments it is just a TCP byte stream. The kernel is completely blind to HTTP.
The container wraps only the JVM. The OS kernel is the host kernel — shared, not virtualized. Syscalls from inside the container go directly to the host kernel. There is no container kernel.
┌─ Server machine ──────────────────────────┐
│ ┌─ Docker container ──────────────────┐ │
│ │ JVM — your Java server code │ │
│ └─────────────────────────────────────┘ │
│ │
│ OS kernel (host, shared) │
│ TCP/IP · Sockets · VFS · NIC driver │
│ │
│ Hardware: NIC · SSD │
└────────────────────────────────────────────┘
Edit src/main/resources/http.json:
{
"port": 8080,
"webroot": "/path/to/your/webroot"
}ConfigurationManager reads this at startup and passes port to ServerSocket and webroot to WebRootHandler.
# Build
mvn clean package
# Run directly
java -jar target/httpserver.jar
# Run in Docker
docker build -t httpserver .
docker run -p 8080:8080 httpserver| Method | Status |
|---|---|
| GET | Supported — static files + template injection |
| POST | Supported — application/json and application/x-www-form-urlencoded |
| HEAD | 501 Not Implemented |
| DELETE | 501 Not Implemented |
| PATCH | 501 Not Implemented |
| Code | Condition |
|---|---|
| 400 Bad Request | Malformed request line, headers, or body |
| 404 Not Found | File not found in webroot |
| 500 Internal Server Error | File read failure |
| 501 Not Implemented | Unsupported HTTP method |
| 505 HTTP Version Not Supported | Non HTTP/1.1 request |
readAllBytes()loads entire file into heap — not suitable for large file serving- No HTTPS / TLS
- No persistent connections (HTTP keep-alive)
- No chunked transfer encoding
WebRootHandleris shared across all 500 threads — must remain stateless
src/main/java/tech/fasih/
├── http/
│ ├── HttpParser # raw stream → HttpRequest
│ ├── HttpRequest # parsed request model
│ ├── HttpResponse # response builder
│ ├── HttpMessage # base message type
│ ├── HttpHeaders # header map
│ ├── HttpMethod # enum: GET, POST, HEAD …
│ ├── HttpVersion # enum: HTTP/1.1
│ ├── HttpStatusCode # enum: 200, 400, 404 …
│ ├── HttpParsingException # bad request signal
│ └── BadHttpVersionException # 505 signal
└── httpserver/
├── config/ # ConfigurationManager, http.json model
├── core/ # ServerListenerThread, WorkerThread, WebRootHandler
├── utils/ # Json helper
└── Httpserver.java # entry point
