| 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](); |
| }; |