21

I have a raw HTTP request string from which I need to create an object representation.

Instead of reinvent the wheel I was thinking about use the internal http parser to get an instance of http.IncomingMessage

Is it possible?

I think so because string is not so different from a complete stream.

How to do it?

I had a look on source code and they get a request parser as follow

var HTTPParser = process.binding('http_parser').HTTPParser;
var parser = new HTTPParser(HTTPParser.REQUEST)

Edit

Some progress from a node.js test

var request = Buffer(raw);
var parser = new HTTPParser(HTTPParser.REQUEST);

parser.execute(request, 0, request.length);

Edit 2

Some eventHandlers were missing (all of them)

parser.onHeadersComplete = function(res) {
    console.log('onHeadersComplete');
    console.log(res);
};

parser.onBody = function(body) {
    console.log('body done');
    console.log(body.toString());
}

parser.onMessageComplete = function(res) {
    console.log('done');
};

Thanks

12
  • I updated my question. I have a "GET / HTTP/1.1\nHost: localhost\n\n" string, which I need to parse to create an object. The resulting object must be instance of http.IncomingMessage Commented Oct 3, 2014 at 14:52
  • 2
    So what exactly is the question? Commented Oct 3, 2014 at 16:21
  • The complete workflow until get a IncomingMessage object. Commented Oct 3, 2014 at 17:15
  • 1
    Why don't you use the http module? require('http').createServer(function(req,res){ ... }) The function gets called each time there is a request. req is already an instance of http.IncomingMessage Commented Oct 3, 2014 at 21:19
  • I don't need an HTTP server to serve requests. I just beed a parser for HTTP messages. Commented Oct 4, 2014 at 18:57

3 Answers 3

3

Apparently, the http_parser module is a low-level callback-based parser. It will send whatever parts of the string it can parse to you via those callbacks, and it is up to you to make an IncomingMessage or whatever else you need from them.

I believe something like that might be what you are looking for:

var HTTPParser = process.binding('http_parser').HTTPParser;

function parseMessage(request) {
    var _message = {};
    var _parser = new HTTPParser(HTTPParser.REQUEST);

    _parser.onHeadersComplete = function(headers) { 
        _message = headers; 
    }

    _parser.onBody = function(body, start, len) {
        _message.data = body.slice(start, start+len);
    }

    var _result = _parser.execute(request, 0, request.length);

    if (_result != request.length) { 
        _message.error = _result; 
    }
    else {
        _message.error = false;
    }
    return _message;
}

var request = Buffer("GET / HTTP/1.1\nHost: localhost\nContent-Length: 2\n\nHi\n\n");
result = parseMessage(request);

Note that the particular IncomingMessage class is parameterized with a socket and generally built around the idea of it being used within a server. The code for parsing it is somewhat messy to be reused as-is (to my taste).

Sign up to request clarification or add additional context in comments.

1 Comment

Recent Node versions changed interface, now callbacks are assigned like parser[HTTPParser.kOnHeaders] = function() {...}
2

Old topic, but I'll say something.

Exporting HTTPParser in own stuff (module or app) is not as simple, 'cause http library uses many internal local functions and constructors. Besides, HTTPParser itself is binded from C library and some helpers.

As I know http.parsers was removed from Node > 4, so the only way is to import all necessary stuff from http library.

For node 0.x there are easy way to import:

var parser = require("http").parsers.alloc();

parser.onIncoming = function(response) {
  console.log(response);
};

function parse(data) {
    var buffer = new Buffer(data);
    parser.execute(buffer, 0, buffer.length);
}
/**
 * tests
 */
parse("DELETE / HTTP/1.1\r\n");
parse("user-agent: curl\r\n");
parse("x-pingback:");
parse("12023\r\n");
parse("\r\n");

//response
{ _readableState: 
   { highWaterMark: 16384,
     buffer: [],
     length: 0,
     pipes: null,
     pipesCount: 0,
     flowing: false,
     ended: false,
     endEmitted: false,
     reading: false,
     calledRead: false,
     sync: true,
     needReadable: false,
     emittedReadable: false,
     readableListening: false,
     objectMode: false,
     defaultEncoding: 'utf8',
     ranOut: false,
     awaitDrain: 0,
     readingMore: false,
     decoder: null,
     encoding: null },
  readable: true,
  domain: null,
  _events: {},
  _maxListeners: 10,
  socket: undefined,
  connection: undefined,
  httpVersion: '1.1',
  complete: false,
  headers: { 'user-agent': 'curl', 'x-pingback': '12023' },
  trailers: {},
  _pendings: [],
  _pendingIndex: 0,
  url: '/',
  method: 'DELETE',
  statusCode: null,
  client: undefined,
  _consuming: false,
  _dumped: false,
  httpVersionMajor: 1,
  httpVersionMinor: 1,
  upgrade: false }

More info here

Also, thanks to @KT for elegant solution

1 Comment

Is there a solution for a newer node versions, like current LTS (v16)? require("http").parsers is undefined.
0

How about parsing it manually? The following is a sample of parsing http request from buffer. Need to convert the string to buffer firstly.

const data = Buffer.from([80, 79, 83, 84, 32, 104, 116, 116, 112, 58, 47, 47, 108, 111, 99, 97, 108, 104, 111, 115, 116, 58, 49, 50, 51, 52, 47, 104, 101, 108, 108, 111, 32, 72, 84, 84, 80, 47, 49, 46, 49, 13, 10, 72, 111, 115, 116, 58, 32, 108, 111, 99, 97, 108, 104, 111, 115, 116, 58, 49, 50, 51, 52, 13, 10, 85, 115, 101, 114, 45, 65, 103, 101, 110, 116, 58, 32, 99, 117, 114, 108, 47, 55, 46, 55, 56, 46, 48, 13, 10, 65, 99, 99, 101, 112, 116, 58, 32, 42, 47, 42, 13, 10, 80, 114, 111, 120, 121, 45, 67, 111, 110, 110, 101, 99, 116, 105, 111, 110, 58, 32, 75, 101, 101, 112, 45, 65, 108, 105, 118, 101, 13, 10, 67, 111, 110, 116, 101, 110, 116, 45, 76, 101, 110, 103, 116, 104, 58, 32, 55, 13, 10, 67, 111, 110, 116, 101, 110, 116, 45, 84, 121, 112, 101, 58, 32, 97, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 47, 120, 45, 119, 119, 119, 45, 102, 111, 114, 109, 45, 117, 114, 108, 101, 110, 99, 111, 100, 101, 100, 13,10, 13, 10, 97, 61, 49, 38, 98, 61, 50]);


const delimIndex = data.indexOf('\r\n\r\n');
const lines = data.slice(0, delimIndex).toString('utf8').split('\r\n');
const startLine = lines[0];
const headers = lines.slice(1);
const body=data.slice(delimIndex + 4);

console.log(`${startLine}\n\n${headers.join('\n')}\n\n${body}`);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.