Architecture Overview
Glass Box follows a modular, layered architecture designed for transparency and educational value. The codebase is split into two packages: com.ns.tcpframework (the reusable HTTP server framework) and com.ns.webserver (the application layer with handlers and models).
Request Lifecycle
Every HTTP request passes through these stages:
- TCPServer accepts a raw TCP connection and submits the socket to a virtual thread pool.
- HTTPHandler receives the socket and delegates parsing to
HTTPRequestParser. - HTTPRequestParser reads the raw byte stream, detects the
\r\n\r\nheader boundary, and constructs anHTTPRequestwith the method, path, headers, and body stream. - VirtualHostManager resolves the
Hostheader to the correctVirtualHostConfig. - RouterConfig matches the request path against registered patterns (exact match first, then wildcard patterns like
/api/todos/*). If no match is found, theStaticFileHandleris used as the default. - RequestHandler processes the request and returns an
HTTPResponse. - The response is serialized and sent back through the socket.
If any stage throws an exception, HTTPErrorHandler catches it and maps it to the appropriate HTTP error response (400, 404, 500, or 501).
Core Components
TCPServer
The entry point of the server. Extends Thread and runs an accept loop on a ServerSocket. Each incoming connection is submitted to an ExecutorService backed by virtual threads.
while (true) {
var clientSocket = serverSocket.accept();
handler.handle(clientSocket, pool);
}
HTTPHandler
Coordinates the full request-response cycle. Receives a socket from TCPServer, delegates parsing, resolves the virtual host, finds the matching handler, and sends the response. Also increments the Stats request counter and logs the request via ServerLogger.
HTTPRequestParser
A stateless utility class that reads HTTP/1.1 from raw input streams. The parser uses a state machine to detect the \r\n\r\n header terminator, then extracts:
- Request line — method, path, HTTP version
- Headers — parsed into a
LinkedHashMap<String, String[]>(case-insensitive keys, multi-value support) - Body — wrapped in a
FixedLengthInputStreamconstrained by theContent-Lengthheader
HTTPRequest and HTTPResponse
Simple data classes. HTTPRequest is immutable after construction. HTTPResponse provides a builder-style API:
HTTPResponse response = new HTTPResponse(200, "OK");
response.setHeader("X-Custom", "value");
response.setBody(jsonBytes, "application/json");
response.send(socket);
Handler Hierarchy
Glass Box provides three base classes for implementing request handlers, each offering a different level of abstraction:
| Base Class | Use Case | Pattern |
|---|---|---|
RequestHandler | Full control over request and socket | Direct override |
MethodeBasedHandler | Handle requests by HTTP method | Template Method |
RouteBasedHandler | Handle requests by path pattern | Command / Strategy |
See Custom Handlers for implementation details.
Configuration and Handler Discovery
Configuration is loaded from YAML files by ConfigLoader, which uses Jackson for deserialization. The HandlerFactory uses the Reflections library to scan the classpath for all RequestHandler subclasses at startup. This means handlers are resolved by simple class name in the YAML config:
routes:
/api/todos/*: ToDoHandler # Resolves to com.ns.webserver.handlers.ToDoHandler
/api/sse: SSEHandler # Resolves to com.ns.tcpframework.reqeustHandlers.sse.SSEHandler
No manual registration or factory wiring is needed — just add a handler class anywhere under the com.ns package and reference it by name.
Threading Model
Glass Box uses Java 25 virtual threads (Executors.newVirtualThreadPerTaskExecutor()). Each incoming connection gets its own lightweight virtual thread, enabling thousands of concurrent connections without the overhead of platform threads.
Key threading details:
- TCPServer runs on its own platform thread (extends
Thread) - ServerLogger runs on a dedicated virtual thread, processing log entries from a
BlockingQueue - Stats runs on a platform thread for system metrics collection (CPU, memory via OSHI)
- SSE connections keep their virtual thread alive in a sleep loop, broadcasting heartbeats every second
Error Handling
Exceptions are mapped to HTTP status codes by HTTPErrorHandler:
| Exception | Status Code | Response |
|---|---|---|
BadRequestException | 400 | Bad Request |
NotFoundException | 404 | Not Found |
InternalServerErrorException | 500 | Internal Server Error |
NotImplementedException | 501 | Not Implemented |
Any other Exception | 500 | Internal Server Error |
All errors are also logged via ServerLogger and broadcast to SSE clients.