llhttp HTTP Parsing Engine, Keep-Alive Connections, Socket Pooling
- Last updated
- Prerequisites:
- Node.js HTTP client/server basics
- Understanding of TCP connections and HTTP parsing
- nodejs
- llhttp
- http-parser
- keep-alive
- socket-pooling
- http
Read at your depth
The practical view
Node's HTTP parsing is done by llhttp, a C++ HTTP parser (TypeScript-compiled-to-C, low-level). It parses incoming HTTP requests/responses quickly with minimal allocation. Keep-alive: HTTP/1.1 connections stay open after a response (Connection: keep-alive), so multiple requests reuse one TCP connection — avoiding TCP handshake + TLS overhead per request. Node's http.Agent implements connection pooling for clients: it keeps free sockets in a pool, reuses them for subsequent requests to the same host, and creates new ones as needed (maxSockets, keepAlive, maxFreeSockets options). Server-side, Node also keeps connections alive for reuse.
The same idea in other frameworks
N/A — Node.js runtime concern.N/A — Node.js concern.N/A — Node.js concern.N/A — Node.js concern.Legacy vs modern
New connection per request vs keep-alive + Agent pooling
Opening a fresh TCP connection per request pays handshake costs; keep-alive and the Agent's socket pool reuse connections.
http.get(url, cb); // ❌ OLD: fresh TCP+TLS handshake per requestconst agent = new http.Agent({ keepAlive: true, maxSockets: 10 }); // ✅ NEWInterview gotchas
Context
This tests the parser design — interviewers want the state-machine/no-allocation rationale and why HTTP parsing is a hot path worth optimizing in native code.
The mechanical answer
llhttp is a state-machine parser written in TypeScript and compiled to C (it generates a compact C state table). It walks the input buffer byte-by-byte with a state machine, emitting callbacks as it recognizes the request line, headers, and body boundaries. Unlike a regex-based or JS-level parser, it avoids per-parse allocations, uses no backtracking, and is small enough to stay in cache. Since HTTP parsing runs for EVERY request, the parser is a critical hot path: a fast, allocation-light parser directly reduces per-request CPU and GC pressure. The state machine also handles streaming input correctly (partial headers, pipelined requests) by remembering its position between chunks.
Trap
The naive answer is 'the parser is fast because it's in C'. The speed comes from the design (state machine, no allocation, no backtracking), and it's compiled from a high-level spec (TypeScript) — the C is generated. Another trap: thinking parsing overhead is negligible — for small requests (typical API calls), parsing can be a meaningful fraction of total request CPU; Node's investment in llhttp is precisely because it matters. Candidates who describe the state-machine design show real understanding.
Context
This probes the connection-management trade-offs — interviewers want the pool reuse benefit, the maxSockets queueing behavior, and the failure modes of misconfiguration.
The mechanical answer
Keep-alive lets a client reuse an open connection for multiple requests, eliminating repeated TCP+TLS handshakes — the main latency win for many small requests. The Agent pools free sockets per origin; when a request arrives, it uses a free socket or opens a new one up to maxSockets. If all sockets are busy and maxSockets is reached, further requests QUEUE until a socket frees up — so a low maxSockets with slow responses creates a bottleneck. Risks: keepAlive without maxFreeSockets caps keeps too many idle sockets open (holding file descriptors and server resources); a high maxSockets can exhaust server connections or trigger per-host limits; and pooling is per-Agent, so creating a new Agent per request defeats the pooling entirely. The tuning trade-off is between reuse (fewer handshakes) and resource usage (open idle sockets).
Trap
The naive answer is 'keepAlive is always better'. It holds idle sockets open and, if misconfigured (no maxFreeSockets cap), leaks descriptors on the client and keeps server connections occupied. Another trap: creating a new Agent per request — this bypasses pooling and re-introduces handshake costs. Candidates who describe the pool lifecycle and the queueing behavior show real depth.