February 12, 2016 · node.js http parsing

Using internal HTTPParser for fun and tests

If you wanna use Node's http(s) library but don't want run/simulate http server - read on :)

So, for using native http parser, which in a nutshell are C library binded to Node with a bunch of internal helpers and local functions we need just a several lines of code.

/**
 * using internal .binding method
 */

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

/**
 * specify that wee wanna parse request
 *  HTTPParser.REQUEST == 1
 *  HTTPParser.RESPONSE == 0
 */

var parser = new HTTPParser(HTTPParser.REQUEST);

//closure, just for convenience
var log = console.log;

/**
 * creates new Buffer and pass it to parser
 * @param {Buffer} buffer
 */

function parse(buffer) {  
  var _data = new Buffer(buffer);
  //this method is described below
  parser.execute(_data, 0, _data.length);
}

/**
 * set event emitters
 */

parser.onHeadersComplete = function(headers) {  
    log(".onHeadersComplete, arguments: \n%s\n", JSON.stringify(arguments));
}
parser.onMessageComplete = function() {  
    log(".onMessageComplete invoked");
}
parser.onBody = function(body, start, len) {  
    log(".onBody, arguments: \n%s\n", JSON.stringify(arguments));
    log(body.toString());
}

/**
 * Make tests with proper requests
 */

//single string
parse("GET / HTTP/1.1\r\nhost:google.com\r\ncontent-length: 0\r\n\r\n");

//multiline
parse("PUT /api/v1/spot HTTP/1.1\r\n");  
parse("content-type: application/json\r\n");  
parse("host: example.com\r\ncontent-length:31\r\n\r\n");  
parse("{\"stock\": \"AAPL\", \"price\": 643}");

Results:
single string

.onHeadersComplete, arguments: 
{"0":{"headers":["host","google.com","content-length","0"],"url":"/","method":"GET","versionMajor":1,"versionMinor":1,"shouldKeepAlive":true,"upgrade":false}}

.onMessageComplete invoked

and multiline one

.onHeadersComplete, arguments: 
{"0":{"headers":["content-type","application/json","host","example.com","content-length","31"],"url":"/api/v1/spot","method":"PUT","versionMajor":1,"versionMinor":1,"shouldKeepAlive":true,"upgrade":false}}

.onBody, arguments: 
{"0":[123,34,115,116,111,99,107,34,58,32,34,65,65,80,76,34,44,32,34,112,114,105,99,101,34,58,32,54,52,51,125],"1":0,"2":31}

{"stock": "AAPL", "price": 643}
.onMessageComplete invoked

Cool, we have headers, request line and body buffer.


Btw, .execute method needs data, start and end positions
let's take a peek to http.js:2167

function socketOnData(d, start, end) {  
  var socket = this;
  var req = this._httpMessage;
  var parser = this.parser;

  var ret = parser.execute(d, start, end - start);
  if (ret instanceof Error) {
    debug('parse error');
    freeParser(parser, req);
    socket.destroy();
    req.emit('error', ret);
    req._hadError = true;
  } else if (parser.incoming && parser.incoming.upgrade) {
    // Upgrade or CONNECT
    var bytesParsed = ret; 
    var res = parser.incoming;
    req.res = res;
    ...

Also, here is npm module with this stuff.

  • LinkedIn
  • Tumblr
  • Reddit
  • Google+
  • Pinterest
  • Pocket
Comments powered by Disqus