var assert = require('assert'); var util = require('util'); var Buffer = require('buffer').Buffer; // Node.js version var mode = /^v0\.8\./.test(process.version) ? 'rusty' : /^v0\.(9|10)\./.test(process.version) ? 'old' : /^v0\.12\./.test(process.version) ? 'normal' : 'modern'; var HTTPParser; var methods; var reverseMethods; var kOnHeaders; var kOnHeadersComplete; var kOnMessageComplete; var kOnBody; if (mode === 'normal' || mode === 'modern') { HTTPParser = process.binding('http_parser').HTTPParser; methods = HTTPParser.methods; // v6 if (!methods) methods = process.binding('http_parser').methods; reverseMethods = {}; methods.forEach(function(method, index) { reverseMethods[method] = index; }); kOnHeaders = HTTPParser.kOnHeaders | 0; kOnHeadersComplete = HTTPParser.kOnHeadersComplete | 0; kOnMessageComplete = HTTPParser.kOnMessageComplete | 0; kOnBody = HTTPParser.kOnBody | 0; } else { kOnHeaders = 'onHeaders'; kOnHeadersComplete = 'onHeadersComplete'; kOnMessageComplete = 'onMessageComplete'; kOnBody = 'onBody'; } function Deceiver(socket, options) { this.socket = socket; this.options = options || {}; this.isClient = this.options.isClient; } module.exports = Deceiver; Deceiver.create = function create(stream, options) { return new Deceiver(stream, options); }; Deceiver.prototype._toHeaderList = function _toHeaderList(object) { var out = []; var keys = Object.keys(object); for (var i = 0; i < keys.length; i++) out.push(keys[i], object[keys[i]]); return out; }; Deceiver.prototype._isUpgrade = function _isUpgrade(request) { return request.method === 'CONNECT' || request.headers.upgrade || request.headers.connection && /(^|\W)upgrade(\W|$)/i.test(request.headers.connection); }; // TODO(indutny): support CONNECT if (mode === 'modern') { /* function parserOnHeadersComplete(versionMajor, versionMinor, headers, method, url, statusCode, statusMessage, upgrade, shouldKeepAlive) { */ Deceiver.prototype.emitRequest = function emitRequest(request) { var parser = this.socket.parser; assert(parser, 'No parser present'); parser.execute = null; var self = this; var method = reverseMethods[request.method]; parser.execute = function execute() { self._skipExecute(this); this[kOnHeadersComplete](1, 1, self._toHeaderList(request.headers), method, request.path, 0, '', self._isUpgrade(request), true); return 0; }; this._emitEmpty(); }; Deceiver.prototype.emitResponse = function emitResponse(response) { var parser = this.socket.parser; assert(parser, 'No parser present'); parser.execute = null; var self = this; parser.execute = function execute() { self._skipExecute(this); this[kOnHeadersComplete](1, 1, self._toHeaderList(response.headers), response.path, response.code, response.status, response.reason || '', self._isUpgrade(response), true); return 0; }; this._emitEmpty(); }; } else { /* `function parserOnHeadersComplete(info) {` info = { .versionMajor, .versionMinor, .url, .headers, .method, .statusCode, .statusMessage, .upgrade, .shouldKeepAlive } */ Deceiver.prototype.emitRequest = function emitRequest(request) { var parser = this.socket.parser; assert(parser, 'No parser present'); var method = request.method; if (reverseMethods) method = reverseMethods[method]; var info = { versionMajor: 1, versionMinor: 1, url: request.path, headers: this._toHeaderList(request.headers), method: method, statusCode: 0, statusMessage: '', upgrade: this._isUpgrade(request), shouldKeepAlive: true }; var self = this; parser.execute = function execute() { self._skipExecute(this); this[kOnHeadersComplete](info); return 0; }; this._emitEmpty(); }; Deceiver.prototype.emitResponse = function emitResponse(response) { var parser = this.socket.parser; assert(parser, 'No parser present'); var info = { versionMajor: 1, versionMinor: 1, url: response.path, headers: this._toHeaderList(response.headers), method: false, statusCode: response.status, statusMessage: response.reason || '', upgrade: this._isUpgrade(response), shouldKeepAlive: true }; var self = this; parser.execute = function execute() { self._skipExecute(this); this[kOnHeadersComplete](info); return 0; }; this._emitEmpty(); }; } Deceiver.prototype._skipExecute = function _skipExecute(parser) { var self = this; var oldExecute = parser.constructor.prototype.execute; var oldFinish = parser.constructor.prototype.finish; parser.execute = null; parser.finish = null; parser.execute = function execute(buffer, start, len) { // Parser reuse if (this.socket !== self.socket) { this.execute = oldExecute; this.finish = oldFinish; return this.execute(buffer, start, len); } if (start !== undefined) buffer = buffer.slice(start, start + len); self.emitBody(buffer); return len; }; parser.finish = function finish() { // Parser reuse if (this.socket !== self.socket) { this.execute = oldExecute; this.finish = oldFinish; return this.finish(); } this.execute = oldExecute; this.finish = oldFinish; self.emitMessageComplete(); }; }; Deceiver.prototype.emitBody = function emitBody(buffer) { var parser = this.socket.parser; assert(parser, 'No parser present'); parser[kOnBody](buffer, 0, buffer.length); }; Deceiver.prototype._emitEmpty = function _emitEmpty() { // Emit data to force out handling of UPGRADE var empty = new Buffer(0); if (this.socket.ondata) this.socket.ondata(empty, 0, 0); else this.socket.emit('data', empty); }; Deceiver.prototype.emitMessageComplete = function emitMessageComplete() { var parser = this.socket.parser; assert(parser, 'No parser present'); parser[kOnMessageComplete](); };