Hasty Server is a lightweight, educational HTTP server framework built from scratch using Node.js raw TCP sockets. It provides Express.js-like functionality without any third-party dependencies, making it perfect for learning HTTP internals and server architecture.
The server is organized into distinct modules, each with a specific responsibility:
hasty-server/
├── server/
│ ├── index.js # Main server class and routing logic
│ └── response.js # HTTP response handling
├── lib/
│ ├── httpParser.js # HTTP request parsing
│ ├── cors.js # CORS utilities
│ ├── utils.js # General utilities
│ └── mimeDb.js # MIME type database
└── test-server.js # Example usage
Incoming TCP Connection
↓
Socket Handler
↓
HTTP Parser (lib/httpParser.js)
↓
Route Matcher (server/index.js)
↓
Route Handler (user-defined)
↓
Response Builder (server/response.js)
↓
CORS Headers (lib/cors.js)
↓
TCP Socket Response
Purpose: Main server orchestration and routing
Key Features:
- TCP server creation and management
- Route registration and matching
- Request lifecycle management
- Error handling and timeouts
Architecture:
class Hasty extends Server {
// Route registration methods (get, post, put, delete, etc.)
// CORS configuration
// Static file serving
// Server lifecycle management
}Purpose: HTTP response construction and sending
Key Features:
- Chainable API (
res.status(200).json(data)) - Multiple response types (JSON, files, streams)
- Automatic content-type detection
- CORS header integration
Architecture:
class Response {
// Status code management
// Header manipulation
// Content sending (send, json, sendFile, download)
// CORS integration
}Purpose: Raw HTTP request parsing
Key Features:
- Request line parsing (method, path, version)
- Header parsing with validation
- Body parsing (JSON, form-urlencoded)
- Query string parsing
- Safe fallbacks for malformed requests
Purpose: Centralized CORS handling
Key Features:
- Default CORS headers
- Preflight OPTIONS handling
- Configurable CORS policies
// 1. TCP connection established
socket.on('data', (chunk) => {
// 2. Accumulate request data
requestData = Buffer.concat([requestData, chunk]);
// 3. Check for complete request
if (requestData.includes('\r\n\r\n')) {
processRequest(socket, requestData, context);
}
});async function processRequest(socket, requestData, context) {
// 1. Parse HTTP request
const req = await httpParser(requestData.toString());
// 2. Handle CORS preflight
if (req.method === 'OPTIONS' && context.enableCors) {
return handlePreflight(res);
}
// 3. Find matching route
const route = findMatchingRoute(req.method, req.path, context.routes);
// 4. Extract parameters and query
req.params = extractParams(route.path, req.path);
req.query = parseQuery(req.path);
// 5. Execute route handler
await route.callback(req, res);
}The server uses a two-phase matching strategy:
- Exact Match: Direct path comparison
- Parameter Match: Pattern matching with
:paramsyntax
// Route: /users/:id
// Path: /users/123
// Result: { params: { id: '123' } }class Response {
send(data) {
// 1. Apply CORS headers if enabled
this._applyCorsHeaders();
// 2. Set content headers
this.setHeader('Content-Type', contentType);
this.setHeader('Content-Length', contentLength);
// 3. Send HTTP response
this.socket.write(`HTTP/1.1 ${statusCode} ${statusText}\r\n${headers}\r\n\r\n`);
this.socket.write(data);
}
}The entire codebase uses JSDoc annotations for TypeScript compatibility:
/**
* @typedef {Object} Route
* @property {string} method - HTTP method
* @property {string} path - URL path pattern
* @property {Function} callback - Route handler
*/
/**
* Registers a route with the server
* @param {string} method - HTTP method
* @param {string} path - URL path pattern
* @param {Function} callback - Route handler
*/
_registerRoute(method, path, callback) { ... }- Socket timeouts (30 seconds)
- Connection error handling
- Resource cleanup
- Malformed HTTP request handling
- Invalid route handling (404)
- Route handler errors (500)
- Header validation
- Stream error handling
- File serving errors
- HTTP method validation
- Path normalization
- Parameter URL decoding
- Directory traversal prevention
- Safe path joining
- MIME type validation
- Connection timeouts
- Memory-efficient streaming
- Proper socket cleanup
- Single-pass HTTP parsing
- Minimal memory allocation
- Stream-based file serving
- Exact match priority
- Efficient parameter extraction
- Route caching potential
- Non-blocking I/O
- Connection pooling ready
- Graceful shutdown support
const Hasty = require('./server/index.js');
const app = new Hasty();
app.cors(true);
app.get('/', (req, res) => {
res.json({ message: 'Hello World!' });
});
app.listen(3000);// Parameterized routes
app.get('/users/:id', (req, res) => {
res.json({ userId: req.params.id });
});
// Static file serving
app.static('./public', { prefix: '/assets' });
// Custom CORS handling
app.options('/api/*', (req, res) => {
res.status(200).end();
});This architecture demonstrates:
- Raw TCP socket programming
- HTTP protocol implementation
- Asynchronous JavaScript patterns
- Modular code organization
- Type-safe JavaScript development
- Security best practices
- Performance optimization techniques
The codebase serves as an excellent learning resource for understanding how modern web frameworks work under the hood while maintaining production-ready code quality.