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.00
# HTTP Parser JS
1
2
HTTP 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.
3
4
## Package Information
5
6
- **Package Name**: http-parser-js
7
- **Package Type**: npm
8
- **Language**: JavaScript with TypeScript definitions
9
- **Installation**: `npm install http-parser-js`
10
11
## Core Imports
12
13
```javascript
14
const { HTTPParser } = require("http-parser-js");
15
```
16
17
For accessing the methods array:
18
19
```javascript
20
const { HTTPParser, methods } = require("http-parser-js");
21
```
22
23
## Basic Usage
24
25
### Monkey-patching Node.js HTTP Parser
26
27
The primary intended use is to replace Node.js's built-in HTTP parser:
28
29
```javascript
30
// Monkey patch before requiring http for the first time
31
process.binding('http_parser').HTTPParser = require('http-parser-js').HTTPParser;
32
33
const http = require('http');
34
// Now Node.js will use the JavaScript parser
35
```
36
37
### Standalone Usage
38
39
```javascript
40
const { HTTPParser } = require('http-parser-js');
41
42
// Parse an HTTP request
43
const parser = new HTTPParser(HTTPParser.REQUEST);
44
let requestData = {};
45
46
parser[HTTPParser.kOnHeadersComplete] = function(req) {
47
requestData.method = HTTPParser.methods[req.method];
48
requestData.url = req.url;
49
requestData.headers = req.headers;
50
requestData.versionMajor = req.versionMajor;
51
requestData.versionMinor = req.versionMinor;
52
};
53
54
parser[HTTPParser.kOnBody] = function(chunk, offset, length) {
55
console.log('Body chunk:', chunk.slice(offset, offset + length));
56
};
57
58
parser[HTTPParser.kOnMessageComplete] = function() {
59
console.log('Parsing complete');
60
};
61
62
// Execute parsing
63
const httpMessage = Buffer.from('GET / HTTP/1.1\\r\\nHost: example.com\\r\\n\\r\\n');
64
parser.execute(httpMessage);
65
parser.finish();
66
```
67
68
## Architecture
69
70
HTTP Parser JS is designed around a single main class with callback-based event handling:
71
72
- **HTTPParser Class**: Main parser that handles both REQUEST and RESPONSE parsing modes
73
- **Event-Driven Architecture**: Uses callback functions for different parsing events (headers, body, completion)
74
- **State Machine**: Internal state management for parsing different parts of HTTP messages
75
- **Compatibility Layer**: Maintains API compatibility with Node.js's native http_parser
76
- **Error Tolerant**: More lenient parsing compared to strict HTTP specification compliance
77
78
## Capabilities
79
80
### HTTPParser Constructor
81
82
Creates a new HTTP parser instance for parsing requests or responses.
83
84
```javascript { .api }
85
/**
86
* Creates a new HTTP parser instance
87
* @param {string} [type] - Parser type: HTTPParser.REQUEST or HTTPParser.RESPONSE
88
*/
89
function HTTPParser(type) {}
90
91
// Static constants
92
HTTPParser.REQUEST = 'REQUEST';
93
HTTPParser.RESPONSE = 'RESPONSE';
94
HTTPParser.encoding = 'ascii';
95
HTTPParser.maxHeaderSize = 81920; // 80KB default
96
97
// Event constants
98
HTTPParser.kOnHeaders = 1;
99
HTTPParser.kOnHeadersComplete = 2;
100
HTTPParser.kOnBody = 3;
101
HTTPParser.kOnMessageComplete = 4;
102
```
103
104
### Parser Initialization
105
106
Initialize or reinitialize the parser with a specific type.
107
108
```javascript { .api }
109
/**
110
* Initialize parser with type and optional async resource
111
* @param {string} type - HTTPParser.REQUEST or HTTPParser.RESPONSE
112
* @param {*} [async_resource] - Optional async resource for tracking
113
*/
114
initialize(type, async_resource) {}
115
116
/**
117
* Reinitialize parser (alias to constructor)
118
*/
119
reinitialize(type) {}
120
```
121
122
### HTTP Message Parsing
123
124
Execute parsing on HTTP data and signal completion.
125
126
```javascript { .api }
127
/**
128
* Parse HTTP data from buffer
129
* @param {Buffer} chunk - Buffer containing HTTP data
130
* @param {number} [start=0] - Start offset in buffer
131
* @param {number} [length] - Length to parse, defaults to chunk.length
132
* @returns {number|Error} Number of bytes parsed or Error object on failure
133
*/
134
execute(chunk, start, length) {}
135
136
/**
137
* Signal end of HTTP message
138
* @returns {void|Error} Error object if called in invalid state
139
*/
140
finish() {}
141
```
142
143
### Event Handlers
144
145
Set callback functions for parsing events.
146
147
```javascript { .api }
148
// Event handler properties (assign functions to these)
149
parser[HTTPParser.kOnHeaders] = function(headers, url) {
150
// Called for trailer headers in chunked encoding
151
};
152
153
parser[HTTPParser.kOnHeadersComplete] = function(info) {
154
// Called when all headers are parsed
155
// info contains: versionMajor, versionMinor, headers, method, url,
156
// statusCode, statusMessage, upgrade, shouldKeepAlive
157
};
158
159
parser[HTTPParser.kOnBody] = function(chunk, offset, length) {
160
// Called for each body data chunk
161
};
162
163
parser[HTTPParser.kOnMessageComplete] = function() {
164
// Called when entire HTTP message is parsed
165
};
166
```
167
168
### Header and Connection Analysis
169
170
Utilities for analyzing parsed HTTP data.
171
172
```javascript { .api }
173
/**
174
* Parse individual header line
175
* @param {string} line - Header line to parse
176
* @param {string[]} headers - Array to append parsed header to
177
*/
178
parseHeader(line, headers) {}
179
180
/**
181
* Check if connection should be kept alive
182
* @returns {boolean} True if connection should persist
183
*/
184
shouldKeepAlive() {}
185
186
/**
187
* Complete current message and prepare for next request
188
*/
189
nextRequest() {}
190
191
/**
192
* Consume a line from the input buffer (internal method)
193
* @returns {string|void} Parsed line or undefined if incomplete
194
*/
195
consumeLine() {}
196
```
197
198
### Instance Properties and Configuration
199
200
Parser configuration and state properties.
201
202
```javascript { .api }
203
// Instance properties
204
parser.maxHeaderSize = 81920; // Max header size in bytes
205
206
// Compatibility stub methods (no-ops)
207
parser.close = function() {};
208
parser.pause = function() {};
209
parser.resume = function() {};
210
parser.remove = function() {};
211
parser.free = function() {};
212
parser.consume = function() {};
213
parser.unconsume = function() {};
214
parser.getCurrentBuffer = function() {};
215
parser.getAsyncId = function() { return 0; };
216
```
217
218
### HTTP Methods Array
219
220
Array of all supported HTTP methods.
221
222
```javascript { .api }
223
/**
224
* Array of supported HTTP method strings
225
*/
226
const methods = [
227
'DELETE', 'GET', 'HEAD', 'POST', 'PUT', 'CONNECT', 'OPTIONS', 'TRACE',
228
'COPY', 'LOCK', 'MKCOL', 'MOVE', 'PROPFIND', 'PROPPATCH', 'SEARCH', 'UNLOCK',
229
'BIND', 'REBIND', 'UNBIND', 'ACL', 'REPORT', 'MKACTIVITY', 'CHECKOUT', 'MERGE',
230
'M-SEARCH', 'NOTIFY', 'SUBSCRIBE', 'UNSUBSCRIBE', 'PATCH', 'PURGE', 'MKCALENDAR',
231
'LINK', 'UNLINK', 'SOURCE'
232
];
233
234
// Also available as HTTPParser.methods
235
```
236
237
### Error Utilities
238
239
Internal error handling utilities.
240
241
```javascript { .api }
242
/**
243
* Create a standardized parse error with error code
244
* @param {string} code - Error code (HPE_LF_EXPECTED, HPE_INVALID_CONSTANT, etc.)
245
* @returns {Error} Error object with code property
246
*/
247
function parseErrorCode(code) {}
248
```
249
250
### Internal Parsing Methods
251
252
Internal state machine methods used during HTTP parsing. These methods are called automatically by the parser and generally should not be called directly.
253
254
```javascript { .api }
255
/**
256
* Parse HTTP request line (internal state method)
257
*/
258
REQUEST_LINE() {}
259
260
/**
261
* Parse HTTP response line (internal state method)
262
*/
263
RESPONSE_LINE() {}
264
265
/**
266
* Parse header lines (internal state method)
267
* @returns {void|boolean} May return true to indicate upgrade request
268
*/
269
HEADER() {}
270
271
/**
272
* Parse chunked transfer encoding header (internal state method)
273
*/
274
BODY_CHUNKHEAD() {}
275
276
/**
277
* Parse chunked transfer encoding body (internal state method)
278
*/
279
BODY_CHUNK() {}
280
281
/**
282
* Parse empty line after chunk (internal state method)
283
*/
284
BODY_CHUNKEMPTYLINE() {}
285
286
/**
287
* Parse chunked transfer encoding trailers (internal state method)
288
*/
289
BODY_CHUNKTRAILERS() {}
290
291
/**
292
* Parse raw body data (internal state method)
293
*/
294
BODY_RAW() {}
295
296
/**
297
* Parse sized body data (internal state method)
298
*/
299
BODY_SIZED() {}
300
```
301
302
### Legacy Compatibility
303
304
Getter/setter properties for older Node.js versions.
305
306
```javascript { .api }
307
// Legacy callback properties (Node.js < 0.11.6)
308
parser.onHeaders = function(headers, url) {};
309
parser.onHeadersComplete = function(info) {};
310
parser.onBody = function(chunk, offset, length) {};
311
parser.onMessageComplete = function() {};
312
```
313
314
## Types
315
316
```typescript { .api }
317
type ParserType = 'REQUEST' | 'RESPONSE';
318
319
type RequestMethod =
320
| 'DELETE' | 'GET' | 'HEAD' | 'POST' | 'PUT' | 'CONNECT' | 'OPTIONS' | 'TRACE'
321
| 'COPY' | 'LOCK' | 'MKCOL' | 'MOVE' | 'PROPFIND' | 'PROPPATCH' | 'SEARCH' | 'UNLOCK'
322
| 'BIND' | 'REBIND' | 'UNBIND' | 'ACL' | 'REPORT' | 'MKACTIVITY' | 'CHECKOUT' | 'MERGE'
323
| 'M-SEARCH' | 'NOTIFY' | 'SUBSCRIBE' | 'UNSUBSCRIBE' | 'PATCH' | 'PURGE' | 'MKCALENDAR'
324
| 'LINK' | 'UNLINK' | 'SOURCE'
325
| string;
326
327
type HeaderObject = Array<string>;
328
329
interface HeaderInfo {
330
versionMajor: number;
331
versionMinor: number;
332
headers: HeaderObject;
333
method: number;
334
url: string;
335
statusCode: number;
336
statusMessage: string;
337
upgrade: boolean;
338
shouldKeepAlive: boolean;
339
}
340
341
type OnHeadersCompleteParser = (info: HeaderInfo) => number | void;
342
type OnBodyParser = (chunk: Buffer, offset: number, length: number) => void;
343
type OnHeadersParser = (headers: string[], url: string) => void;
344
type OnMessageCompleteParser = () => void;
345
346
interface HTTPParserConstructor {
347
new(type?: ParserType): HTTPParser;
348
(type?: ParserType): void;
349
350
readonly REQUEST: 'REQUEST';
351
readonly RESPONSE: 'RESPONSE';
352
readonly methods: RequestMethod[];
353
354
encoding: string;
355
maxHeaderSize: number;
356
357
readonly kOnHeaders: 1;
358
readonly kOnHeadersComplete: 2;
359
readonly kOnBody: 3;
360
readonly kOnMessageComplete: 4;
361
}
362
363
interface HTTPParser {
364
initialize(type: ParserType, async_resource?: unknown): void;
365
execute(chunk: Buffer, start?: number, length?: number): number | Error;
366
finish(): void | Error;
367
368
maxHeaderSize: number;
369
370
[HTTPParser.kOnHeaders]: OnHeadersParser;
371
[HTTPParser.kOnHeadersComplete]: OnHeadersCompleteParser;
372
[HTTPParser.kOnBody]: OnBodyParser;
373
[HTTPParser.kOnMessageComplete]: OnMessageCompleteParser;
374
375
// Legacy compatibility
376
onHeaders: OnHeadersParser;
377
onHeadersComplete: OnHeadersCompleteParser;
378
onBody: OnBodyParser;
379
onMessageComplete: OnMessageCompleteParser;
380
381
// Utility methods
382
parseHeader(line: string, headers: string[]): void;
383
shouldKeepAlive(): boolean;
384
userCall<T>(): (ret?: T) => T;
385
nextRequest(): void;
386
consumeLine(): string | void;
387
388
// Internal properties
389
_compatMode0_11: boolean;
390
391
// Stub methods
392
reinitialize(type: ParserType): void;
393
close(): void;
394
pause(): void;
395
resume(): void;
396
remove(): void;
397
free(): void;
398
consume(): void;
399
unconsume(): void;
400
getCurrentBuffer(): void;
401
getAsyncId(): number;
402
}
403
```
404
405
## Usage Examples
406
407
### Parsing HTTP Request
408
409
```javascript
410
const { HTTPParser } = require('http-parser-js');
411
412
function parseRequest(input) {
413
const parser = new HTTPParser(HTTPParser.REQUEST);
414
let complete = false;
415
let shouldKeepAlive, method, url, headers = [], bodyChunks = [];
416
417
parser[HTTPParser.kOnHeadersComplete] = function(req) {
418
shouldKeepAlive = req.shouldKeepAlive;
419
method = HTTPParser.methods[req.method];
420
url = req.url;
421
headers = req.headers;
422
};
423
424
parser[HTTPParser.kOnBody] = function(chunk, offset, length) {
425
bodyChunks.push(chunk.slice(offset, offset + length));
426
};
427
428
parser[HTTPParser.kOnMessageComplete] = function() {
429
complete = true;
430
};
431
432
parser.execute(input);
433
parser.finish();
434
435
if (!complete) {
436
throw new Error('Could not parse request');
437
}
438
439
return {
440
shouldKeepAlive,
441
method,
442
url,
443
headers,
444
body: Buffer.concat(bodyChunks)
445
};
446
}
447
448
// Usage
449
const request = Buffer.from('GET /path HTTP/1.1\\r\\nHost: example.com\\r\\n\\r\\n');
450
const parsed = parseRequest(request);
451
console.log(parsed.method); // 'GET'
452
console.log(parsed.url); // '/path'
453
```
454
455
### Parsing HTTP Response
456
457
```javascript
458
function parseResponse(input) {
459
const parser = new HTTPParser(HTTPParser.RESPONSE);
460
let complete = false;
461
let statusCode, statusMessage, headers = [], bodyChunks = [];
462
463
parser[HTTPParser.kOnHeadersComplete] = function(res) {
464
statusCode = res.statusCode;
465
statusMessage = res.statusMessage;
466
headers = res.headers;
467
};
468
469
parser[HTTPParser.kOnBody] = function(chunk, offset, length) {
470
bodyChunks.push(chunk.slice(offset, offset + length));
471
};
472
473
parser[HTTPParser.kOnMessageComplete] = function() {
474
complete = true;
475
};
476
477
parser.execute(input);
478
parser.finish();
479
480
return {
481
statusCode,
482
statusMessage,
483
headers,
484
body: Buffer.concat(bodyChunks)
485
};
486
}
487
488
// Usage
489
const response = Buffer.from('HTTP/1.1 200 OK\\r\\nContent-Length: 5\\r\\n\\r\\nHello');
490
const parsed = parseResponse(response);
491
console.log(parsed.statusCode); // 200
492
console.log(parsed.statusMessage); // 'OK'
493
console.log(parsed.body.toString()); // 'Hello'
494
```
495
496
## Error Handling
497
498
The parser returns Error objects for various parsing failures:
499
500
```javascript
501
const result = parser.execute(malformedData);
502
if (result instanceof Error) {
503
console.error('Parse error:', result.message);
504
if (result.code) {
505
console.error('Error code:', result.code);
506
}
507
}
508
509
// Common error codes
510
// HPE_LF_EXPECTED - Line feed expected
511
// HPE_INVALID_CONSTANT - Invalid HTTP constant
512
// HPE_UNEXPECTED_CONTENT_LENGTH - Duplicate/conflicting Content-Length headers
513
```
514
515
## Compatibility Notes
516
517
- **Node.js Support**: Works with Node.js v6-v11, v13-v14 via monkey-patching
518
- **Node.js v12**: Requires `--http-parser=legacy` flag for monkey-patching
519
- **Tolerant Parsing**: More lenient than Node's strict parser for malformed HTTP data
520
- **Header Size Limits**: Configurable via `maxHeaderSize` property (default 80KB)
521
- **Chunked Transfer Encoding**: Full support including trailer headers
522
- **Connection Management**: Automatic keep-alive detection based on HTTP version and headers