A pure JavaScript HTTP parser for Node.js, compatible replacement for http_parser.c
npx @tessl/cli install tessl/npm-http-parser-js@0.5.0HTTP Parser JS is a pure JavaScript HTTP protocol parser for Node.js that serves as a compatible replacement for the native C++ http_parser.c. It provides more flexible and tolerant parsing of HTTP messages, making it ideal for working with legacy services that don't meet strict HTTP parsing standards.
npm install http-parser-jsconst { HTTPParser } = require("http-parser-js");For accessing the methods array:
const { HTTPParser, methods } = require("http-parser-js");The primary intended use is to replace Node.js's built-in HTTP parser:
// Monkey patch before requiring http for the first time
process.binding('http_parser').HTTPParser = require('http-parser-js').HTTPParser;
const http = require('http');
// Now Node.js will use the JavaScript parserconst { HTTPParser } = require('http-parser-js');
// Parse an HTTP request
const parser = new HTTPParser(HTTPParser.REQUEST);
let requestData = {};
parser[HTTPParser.kOnHeadersComplete] = function(req) {
requestData.method = HTTPParser.methods[req.method];
requestData.url = req.url;
requestData.headers = req.headers;
requestData.versionMajor = req.versionMajor;
requestData.versionMinor = req.versionMinor;
};
parser[HTTPParser.kOnBody] = function(chunk, offset, length) {
console.log('Body chunk:', chunk.slice(offset, offset + length));
};
parser[HTTPParser.kOnMessageComplete] = function() {
console.log('Parsing complete');
};
// Execute parsing
const httpMessage = Buffer.from('GET / HTTP/1.1\\r\\nHost: example.com\\r\\n\\r\\n');
parser.execute(httpMessage);
parser.finish();HTTP Parser JS is designed around a single main class with callback-based event handling:
Creates a new HTTP parser instance for parsing requests or responses.
/**
* Creates a new HTTP parser instance
* @param {string} [type] - Parser type: HTTPParser.REQUEST or HTTPParser.RESPONSE
*/
function HTTPParser(type) {}
// Static constants
HTTPParser.REQUEST = 'REQUEST';
HTTPParser.RESPONSE = 'RESPONSE';
HTTPParser.encoding = 'ascii';
HTTPParser.maxHeaderSize = 81920; // 80KB default
// Event constants
HTTPParser.kOnHeaders = 1;
HTTPParser.kOnHeadersComplete = 2;
HTTPParser.kOnBody = 3;
HTTPParser.kOnMessageComplete = 4;Initialize or reinitialize the parser with a specific type.
/**
* Initialize parser with type and optional async resource
* @param {string} type - HTTPParser.REQUEST or HTTPParser.RESPONSE
* @param {*} [async_resource] - Optional async resource for tracking
*/
initialize(type, async_resource) {}
/**
* Reinitialize parser (alias to constructor)
*/
reinitialize(type) {}Execute parsing on HTTP data and signal completion.
/**
* Parse HTTP data from buffer
* @param {Buffer} chunk - Buffer containing HTTP data
* @param {number} [start=0] - Start offset in buffer
* @param {number} [length] - Length to parse, defaults to chunk.length
* @returns {number|Error} Number of bytes parsed or Error object on failure
*/
execute(chunk, start, length) {}
/**
* Signal end of HTTP message
* @returns {void|Error} Error object if called in invalid state
*/
finish() {}Set callback functions for parsing events.
// Event handler properties (assign functions to these)
parser[HTTPParser.kOnHeaders] = function(headers, url) {
// Called for trailer headers in chunked encoding
};
parser[HTTPParser.kOnHeadersComplete] = function(info) {
// Called when all headers are parsed
// info contains: versionMajor, versionMinor, headers, method, url,
// statusCode, statusMessage, upgrade, shouldKeepAlive
};
parser[HTTPParser.kOnBody] = function(chunk, offset, length) {
// Called for each body data chunk
};
parser[HTTPParser.kOnMessageComplete] = function() {
// Called when entire HTTP message is parsed
};Utilities for analyzing parsed HTTP data.
/**
* Parse individual header line
* @param {string} line - Header line to parse
* @param {string[]} headers - Array to append parsed header to
*/
parseHeader(line, headers) {}
/**
* Check if connection should be kept alive
* @returns {boolean} True if connection should persist
*/
shouldKeepAlive() {}
/**
* Complete current message and prepare for next request
*/
nextRequest() {}
/**
* Consume a line from the input buffer (internal method)
* @returns {string|void} Parsed line or undefined if incomplete
*/
consumeLine() {}Parser configuration and state properties.
// Instance properties
parser.maxHeaderSize = 81920; // Max header size in bytes
// Compatibility stub methods (no-ops)
parser.close = function() {};
parser.pause = function() {};
parser.resume = function() {};
parser.remove = function() {};
parser.free = function() {};
parser.consume = function() {};
parser.unconsume = function() {};
parser.getCurrentBuffer = function() {};
parser.getAsyncId = function() { return 0; };Array of all supported HTTP methods.
/**
* Array of supported HTTP method strings
*/
const methods = [
'DELETE', 'GET', 'HEAD', 'POST', 'PUT', 'CONNECT', 'OPTIONS', 'TRACE',
'COPY', 'LOCK', 'MKCOL', 'MOVE', 'PROPFIND', 'PROPPATCH', 'SEARCH', 'UNLOCK',
'BIND', 'REBIND', 'UNBIND', 'ACL', 'REPORT', 'MKACTIVITY', 'CHECKOUT', 'MERGE',
'M-SEARCH', 'NOTIFY', 'SUBSCRIBE', 'UNSUBSCRIBE', 'PATCH', 'PURGE', 'MKCALENDAR',
'LINK', 'UNLINK', 'SOURCE'
];
// Also available as HTTPParser.methodsInternal error handling utilities.
/**
* Create a standardized parse error with error code
* @param {string} code - Error code (HPE_LF_EXPECTED, HPE_INVALID_CONSTANT, etc.)
* @returns {Error} Error object with code property
*/
function parseErrorCode(code) {}Internal state machine methods used during HTTP parsing. These methods are called automatically by the parser and generally should not be called directly.
/**
* Parse HTTP request line (internal state method)
*/
REQUEST_LINE() {}
/**
* Parse HTTP response line (internal state method)
*/
RESPONSE_LINE() {}
/**
* Parse header lines (internal state method)
* @returns {void|boolean} May return true to indicate upgrade request
*/
HEADER() {}
/**
* Parse chunked transfer encoding header (internal state method)
*/
BODY_CHUNKHEAD() {}
/**
* Parse chunked transfer encoding body (internal state method)
*/
BODY_CHUNK() {}
/**
* Parse empty line after chunk (internal state method)
*/
BODY_CHUNKEMPTYLINE() {}
/**
* Parse chunked transfer encoding trailers (internal state method)
*/
BODY_CHUNKTRAILERS() {}
/**
* Parse raw body data (internal state method)
*/
BODY_RAW() {}
/**
* Parse sized body data (internal state method)
*/
BODY_SIZED() {}Getter/setter properties for older Node.js versions.
// Legacy callback properties (Node.js < 0.11.6)
parser.onHeaders = function(headers, url) {};
parser.onHeadersComplete = function(info) {};
parser.onBody = function(chunk, offset, length) {};
parser.onMessageComplete = function() {};type ParserType = 'REQUEST' | 'RESPONSE';
type RequestMethod =
| 'DELETE' | 'GET' | 'HEAD' | 'POST' | 'PUT' | 'CONNECT' | 'OPTIONS' | 'TRACE'
| 'COPY' | 'LOCK' | 'MKCOL' | 'MOVE' | 'PROPFIND' | 'PROPPATCH' | 'SEARCH' | 'UNLOCK'
| 'BIND' | 'REBIND' | 'UNBIND' | 'ACL' | 'REPORT' | 'MKACTIVITY' | 'CHECKOUT' | 'MERGE'
| 'M-SEARCH' | 'NOTIFY' | 'SUBSCRIBE' | 'UNSUBSCRIBE' | 'PATCH' | 'PURGE' | 'MKCALENDAR'
| 'LINK' | 'UNLINK' | 'SOURCE'
| string;
type HeaderObject = Array<string>;
interface HeaderInfo {
versionMajor: number;
versionMinor: number;
headers: HeaderObject;
method: number;
url: string;
statusCode: number;
statusMessage: string;
upgrade: boolean;
shouldKeepAlive: boolean;
}
type OnHeadersCompleteParser = (info: HeaderInfo) => number | void;
type OnBodyParser = (chunk: Buffer, offset: number, length: number) => void;
type OnHeadersParser = (headers: string[], url: string) => void;
type OnMessageCompleteParser = () => void;
interface HTTPParserConstructor {
new(type?: ParserType): HTTPParser;
(type?: ParserType): void;
readonly REQUEST: 'REQUEST';
readonly RESPONSE: 'RESPONSE';
readonly methods: RequestMethod[];
encoding: string;
maxHeaderSize: number;
readonly kOnHeaders: 1;
readonly kOnHeadersComplete: 2;
readonly kOnBody: 3;
readonly kOnMessageComplete: 4;
}
interface HTTPParser {
initialize(type: ParserType, async_resource?: unknown): void;
execute(chunk: Buffer, start?: number, length?: number): number | Error;
finish(): void | Error;
maxHeaderSize: number;
[HTTPParser.kOnHeaders]: OnHeadersParser;
[HTTPParser.kOnHeadersComplete]: OnHeadersCompleteParser;
[HTTPParser.kOnBody]: OnBodyParser;
[HTTPParser.kOnMessageComplete]: OnMessageCompleteParser;
// Legacy compatibility
onHeaders: OnHeadersParser;
onHeadersComplete: OnHeadersCompleteParser;
onBody: OnBodyParser;
onMessageComplete: OnMessageCompleteParser;
// Utility methods
parseHeader(line: string, headers: string[]): void;
shouldKeepAlive(): boolean;
userCall<T>(): (ret?: T) => T;
nextRequest(): void;
consumeLine(): string | void;
// Internal properties
_compatMode0_11: boolean;
// Stub methods
reinitialize(type: ParserType): void;
close(): void;
pause(): void;
resume(): void;
remove(): void;
free(): void;
consume(): void;
unconsume(): void;
getCurrentBuffer(): void;
getAsyncId(): number;
}const { HTTPParser } = require('http-parser-js');
function parseRequest(input) {
const parser = new HTTPParser(HTTPParser.REQUEST);
let complete = false;
let shouldKeepAlive, method, url, headers = [], bodyChunks = [];
parser[HTTPParser.kOnHeadersComplete] = function(req) {
shouldKeepAlive = req.shouldKeepAlive;
method = HTTPParser.methods[req.method];
url = req.url;
headers = req.headers;
};
parser[HTTPParser.kOnBody] = function(chunk, offset, length) {
bodyChunks.push(chunk.slice(offset, offset + length));
};
parser[HTTPParser.kOnMessageComplete] = function() {
complete = true;
};
parser.execute(input);
parser.finish();
if (!complete) {
throw new Error('Could not parse request');
}
return {
shouldKeepAlive,
method,
url,
headers,
body: Buffer.concat(bodyChunks)
};
}
// Usage
const request = Buffer.from('GET /path HTTP/1.1\\r\\nHost: example.com\\r\\n\\r\\n');
const parsed = parseRequest(request);
console.log(parsed.method); // 'GET'
console.log(parsed.url); // '/path'function parseResponse(input) {
const parser = new HTTPParser(HTTPParser.RESPONSE);
let complete = false;
let statusCode, statusMessage, headers = [], bodyChunks = [];
parser[HTTPParser.kOnHeadersComplete] = function(res) {
statusCode = res.statusCode;
statusMessage = res.statusMessage;
headers = res.headers;
};
parser[HTTPParser.kOnBody] = function(chunk, offset, length) {
bodyChunks.push(chunk.slice(offset, offset + length));
};
parser[HTTPParser.kOnMessageComplete] = function() {
complete = true;
};
parser.execute(input);
parser.finish();
return {
statusCode,
statusMessage,
headers,
body: Buffer.concat(bodyChunks)
};
}
// Usage
const response = Buffer.from('HTTP/1.1 200 OK\\r\\nContent-Length: 5\\r\\n\\r\\nHello');
const parsed = parseResponse(response);
console.log(parsed.statusCode); // 200
console.log(parsed.statusMessage); // 'OK'
console.log(parsed.body.toString()); // 'Hello'The parser returns Error objects for various parsing failures:
const result = parser.execute(malformedData);
if (result instanceof Error) {
console.error('Parse error:', result.message);
if (result.code) {
console.error('Error code:', result.code);
}
}
// Common error codes
// HPE_LF_EXPECTED - Line feed expected
// HPE_INVALID_CONSTANT - Invalid HTTP constant
// HPE_UNEXPECTED_CONTENT_LENGTH - Duplicate/conflicting Content-Length headers--http-parser=legacymaxHeaderSize