'use strict';
|
|
|
|
var NodeHTTPParser = require('http-parser-js').HTTPParser,
|
|
Buffer = require('safe-buffer').Buffer;
|
|
|
|
var TYPES = {
|
|
request: NodeHTTPParser.REQUEST || 'request',
|
|
response: NodeHTTPParser.RESPONSE || 'response'
|
|
};
|
|
|
|
var HttpParser = function(type) {
|
|
this._type = type;
|
|
this._parser = new NodeHTTPParser(TYPES[type]);
|
|
this._complete = false;
|
|
this.headers = {};
|
|
|
|
var current = null,
|
|
self = this;
|
|
|
|
this._parser.onHeaderField = function(b, start, length) {
|
|
current = b.toString('utf8', start, start + length).toLowerCase();
|
|
};
|
|
|
|
this._parser.onHeaderValue = function(b, start, length) {
|
|
var value = b.toString('utf8', start, start + length);
|
|
|
|
if (self.headers.hasOwnProperty(current))
|
|
self.headers[current] += ', ' + value;
|
|
else
|
|
self.headers[current] = value;
|
|
};
|
|
|
|
this._parser.onHeadersComplete = this._parser[NodeHTTPParser.kOnHeadersComplete] =
|
|
function(majorVersion, minorVersion, headers, method, pathname, statusCode) {
|
|
var info = arguments[0];
|
|
|
|
if (typeof info === 'object') {
|
|
method = info.method;
|
|
pathname = info.url;
|
|
statusCode = info.statusCode;
|
|
headers = info.headers;
|
|
}
|
|
|
|
self.method = (typeof method === 'number') ? HttpParser.METHODS[method] : method;
|
|
self.statusCode = statusCode;
|
|
self.url = pathname;
|
|
|
|
if (!headers) return;
|
|
|
|
for (var i = 0, n = headers.length, key, value; i < n; i += 2) {
|
|
key = headers[i].toLowerCase();
|
|
value = headers[i+1];
|
|
if (self.headers.hasOwnProperty(key))
|
|
self.headers[key] += ', ' + value;
|
|
else
|
|
self.headers[key] = value;
|
|
}
|
|
|
|
self._complete = true;
|
|
};
|
|
};
|
|
|
|
HttpParser.METHODS = {
|
|
0: 'DELETE',
|
|
1: 'GET',
|
|
2: 'HEAD',
|
|
3: 'POST',
|
|
4: 'PUT',
|
|
5: 'CONNECT',
|
|
6: 'OPTIONS',
|
|
7: 'TRACE',
|
|
8: 'COPY',
|
|
9: 'LOCK',
|
|
10: 'MKCOL',
|
|
11: 'MOVE',
|
|
12: 'PROPFIND',
|
|
13: 'PROPPATCH',
|
|
14: 'SEARCH',
|
|
15: 'UNLOCK',
|
|
16: 'BIND',
|
|
17: 'REBIND',
|
|
18: 'UNBIND',
|
|
19: 'ACL',
|
|
20: 'REPORT',
|
|
21: 'MKACTIVITY',
|
|
22: 'CHECKOUT',
|
|
23: 'MERGE',
|
|
24: 'M-SEARCH',
|
|
25: 'NOTIFY',
|
|
26: 'SUBSCRIBE',
|
|
27: 'UNSUBSCRIBE',
|
|
28: 'PATCH',
|
|
29: 'PURGE',
|
|
30: 'MKCALENDAR',
|
|
31: 'LINK',
|
|
32: 'UNLINK'
|
|
};
|
|
|
|
var VERSION = (process.version || '')
|
|
.match(/[0-9]+/g)
|
|
.map(function(n) { return parseInt(n, 10) });
|
|
|
|
if (VERSION[0] === 0 && VERSION[1] === 12) {
|
|
HttpParser.METHODS[16] = 'REPORT';
|
|
HttpParser.METHODS[17] = 'MKACTIVITY';
|
|
HttpParser.METHODS[18] = 'CHECKOUT';
|
|
HttpParser.METHODS[19] = 'MERGE';
|
|
HttpParser.METHODS[20] = 'M-SEARCH';
|
|
HttpParser.METHODS[21] = 'NOTIFY';
|
|
HttpParser.METHODS[22] = 'SUBSCRIBE';
|
|
HttpParser.METHODS[23] = 'UNSUBSCRIBE';
|
|
HttpParser.METHODS[24] = 'PATCH';
|
|
HttpParser.METHODS[25] = 'PURGE';
|
|
}
|
|
|
|
HttpParser.prototype.isComplete = function() {
|
|
return this._complete;
|
|
};
|
|
|
|
HttpParser.prototype.parse = function(chunk) {
|
|
var consumed = this._parser.execute(chunk, 0, chunk.length);
|
|
|
|
if (typeof consumed !== 'number') {
|
|
this.error = consumed;
|
|
this._complete = true;
|
|
return;
|
|
}
|
|
|
|
if (this._complete)
|
|
this.body = (consumed < chunk.length)
|
|
? chunk.slice(consumed)
|
|
: Buffer.alloc(0);
|
|
};
|
|
|
|
module.exports = HttpParser;
|