| '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; |