HTTP Server and Request Handling
Creating HTTP servers and handling requests/responses efficiently
Challenge
Section titled “Challenge”You need to build an HTTP server that handles multiple concurrent requests, properly manages timeouts, and efficiently streams large responses. How do you do this correctly?
Explanation
Section titled “Explanation”Node.js http module provides low-level APIs for creating HTTP servers. Understanding request/response objects, headers, and streaming is essential for building robust servers.
Basic HTTP Server
Section titled “Basic HTTP Server”const http = require('http');
const server = http.createServer((req, res) => { // Set response headers res.writeHead(200, { 'Content-Type': 'application/json' });
// Send response res.write(JSON.stringify({ message: 'Hello' })); res.end();});
server.listen(3000, 'localhost', () => { console.log('Server running on http://localhost:3000');});
// Handle errorsserver.on('error', (err) => { console.error('Server error:', err);});
// Graceful shutdownprocess.on('SIGTERM', () => { server.close(() => { console.log('Server closed'); process.exit(0); });});Solution: Request Routing and Response Streaming
Section titled “Solution: Request Routing and Response Streaming”const http = require('http');const fs = require('fs');const url = require('url');
const server = http.createServer(async (req, res) => { // Parse URL and query parameters const parsedUrl = url.parse(req.url, true); const pathname = parsedUrl.pathname; const query = parsedUrl.query;
try { // Route handling if (pathname === '/' && req.method === 'GET') { handleHome(req, res); } else if (pathname === '/api/users' && req.method === 'GET') { handleGetUsers(req, res, query); } else if (pathname === '/api/users' && req.method === 'POST') { handleCreateUser(req, res); } else if (pathname.match(/^\/file\//) && req.method === 'GET') { handleFileDownload(req, res, pathname); } else { res.writeHead(404); res.end(JSON.stringify({ error: 'Not Found' })); } } catch (err) { res.writeHead(500); res.end(JSON.stringify({ error: 'Internal Server Error' })); }});
function handleHome(req, res) { res.writeHead(200, { 'Content-Type': 'text/html' }); res.end('<h1>Welcome</h1>');}
function handleGetUsers(req, res, query) { const limit = query.limit || 10; const offset = query.offset || 0;
res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ users: [], pagination: { limit, offset } }));}
function handleCreateUser(req, res) { let body = '';
// Collect request body req.on('data', chunk => { body += chunk.toString(); });
req.on('end', () => { try { const user = JSON.parse(body); res.writeHead(201, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ id: 1, ...user })); } catch (err) { res.writeHead(400); res.end(JSON.stringify({ error: 'Invalid JSON' })); } });}
function handleFileDownload(req, res, pathname) { const filePath = `./files${pathname.replace(/^\/file/, '')}`;
// Stream file response const stream = fs.createReadStream(filePath);
res.writeHead(200, { 'Content-Type': 'application/octet-stream', 'Content-Disposition': 'attachment; filename="file"' });
stream.pipe(res);
stream.on('error', (err) => { res.writeHead(404); res.end('File not found'); });}
server.listen(3000);Request Body Handling
Section titled “Request Body Handling”const http = require('http');
const server = http.createServer((req, res) => { if (req.method === 'POST') { let body = '';
// Handle data event req.on('data', chunk => { body += chunk.toString();
// Prevent large uploads if (body.length > 1e6) { req.destroy(); res.writeHead(413); // Payload Too Large res.end(); } });
req.on('end', () => { try { const data = JSON.parse(body); res.writeHead(200); res.end(JSON.stringify({ received: data })); } catch (err) { res.writeHead(400); res.end('Invalid JSON'); } });
req.on('error', (err) => { res.writeHead(400); res.end('Request error'); }); }});
server.listen(3000);Headers and Cookies
Section titled “Headers and Cookies”const http = require('http');
const server = http.createServer((req, res) => { // Read request headers console.log(req.headers); // { // 'user-agent': 'curl/7.64.1', // 'accept': '*/*', // 'host': 'localhost:3000', // 'cookie': 'session=abc123' // }
// Parse cookies const cookies = {}; if (req.headers.cookie) { req.headers.cookie.split('; ').forEach(cookie => { const [key, value] = cookie.split('='); cookies[key] = value; }); }
// Set response headers res.setHeader('Content-Type', 'application/json'); res.setHeader('Set-Cookie', 'session=xyz789; Path=/; HttpOnly'); res.setHeader('Cache-Control', 'no-cache'); res.setHeader('Access-Control-Allow-Origin', '*');
res.writeHead(200); res.end(JSON.stringify({ cookies }));});
server.listen(3000);Performance and Optimization
Section titled “Performance and Optimization”const http = require('http');const zlib = require('zlib');
const server = http.createServer((req, res) => { // Compression based on client support const acceptEncoding = req.headers['accept-encoding'] || '';
const data = JSON.stringify({ message: 'Hello' * 10000 });
if (acceptEncoding.includes('gzip')) { res.writeHead(200, { 'Content-Type': 'application/json', 'Content-Encoding': 'gzip' }); res.pipe(zlib.createGzip()).end(data); } else { res.writeHead(200); res.end(data); }});
// Keep-alive connection for better performanceconst server2 = http.createServer((req, res) => { res.writeHead(200, { 'Connection': 'keep-alive', 'Keep-Alive': 'timeout=5, max=100' }); res.end('OK');});
// Connection poolingserver.maxConnections = 200;
server.listen(3000);Advanced Patterns
Section titled “Advanced Patterns”// Middleware pattern for request processingfunction createServer(middlewares = []) { return http.createServer(async (req, res) => { // Execute middlewares in sequence for (const middleware of middlewares) { await new Promise((resolve) => { middleware(req, res, resolve); });
if (res.headersSent) return; // Middleware sent response } });}
const server = createServer([ // Logging middleware (req, res, next) => { console.log(`${req.method} ${req.url}`); next(); },
// Authentication middleware (req, res, next) => { if (req.headers.authorization) { next(); } else { res.writeHead(401); res.end('Unauthorized'); } },
// Main handler (req, res, next) => { res.writeHead(200); res.end('OK'); }]);
server.listen(3000);