diff --git a/lib/https/index.js b/lib/https/index.js index 198b361ba..4c1be66f5 100644 --- a/lib/https/index.js +++ b/lib/https/index.js @@ -12,6 +12,8 @@ var pluginMgr = require('../plugins'); var socketMgr = require('../socket-mgr'); var serverAgent = require('./util').serverAgent; var logger = require('../util/logger'); +var parseReq = require('hparser').parse; + var LOCALHOST = '127.0.0.1'; var tunnelTmplData = {}; var proxy; @@ -295,7 +297,7 @@ function handleWebsocket(socket, clientIp, clientPort, callback, wss) { reqSocket.write(socket.getBuffer((isInternalProxy || reqHeaders || plugin || matchedUrl || _rules.hostname || util.hasHeaderRules(headers)) ? headers : null, matchedUrl ? options.path : null)); - util.parseReq(reqSocket, function(err, res) { + parseReq(reqSocket, function(err, res) { if (err) { return execCallback(err); } @@ -410,7 +412,7 @@ function handleTlsSocket(socket) { return socket.on('error', destroy).on('close', destroy); } - util.parseReq(socket, function(err, socket) { + parseReq(socket, function(err, socket) { if (err) { return destroy(err); } @@ -471,7 +473,7 @@ module.exports = function dispatch(socket, hostname, callback) { } if (/upgrade\s*:\s*websocket/i.test(chunk.toString())) { //ws - util.parseReq(socket, function(err, socket) { + parseReq(socket, function(err, socket) { if (err) { return destroy(err); } diff --git a/lib/inspectors/req.js b/lib/inspectors/req.js index e85d7fd01..9bb4727e2 100644 --- a/lib/inspectors/req.js +++ b/lib/inspectors/req.js @@ -8,7 +8,10 @@ var WhistleTransform = util.WhistleTransform; var ReplacePatternTransform = util.ReplacePatternTransform; var ReplaceStringTransform = util.ReplaceStringTransform; var FileWriterTransform = util.FileWriterTransform; -var MultiPartParser = util.MultiPartParser; +var hparser = require('hparser'); +var toMultiparts = hparser.toMultiparts; +var MultiPartParser = hparser.MultiPartParser; + var MAX_REQ_SIZE = 256 * 1024; var REQ_TYPE = { urlencoded: 'application/x-www-form-urlencoded', @@ -175,7 +178,7 @@ function handleParams(req, params) { data = this.parser.transform(chunk.slice(0, index)); result = result && data ? Buffer.concat([result, data]) : (result || data); if (!sep) { - data = util.toMultiparts(_params, boundaryStr); + data = toMultiparts(_params, boundaryStr); result = result ? Buffer.concat([result, data]) : data; } sep = sep ? sepBoundary : endBoudary; diff --git a/lib/inspectors/res.js b/lib/inspectors/res.js index de709603d..87cfd92d0 100644 --- a/lib/inspectors/res.js +++ b/lib/inspectors/res.js @@ -13,6 +13,10 @@ var ReplaceStringTransform = util.ReplaceStringTransform; var FileWriterTransform = util.FileWriterTransform; var rules = require('../rules'); var pluginMgr = require('../plugins'); +var hparser = require('hparser'); +var formatHeaders = hparser.formatHeaders; +var getRawHeaderNames = hparser.getRawHeaderNames; + var LOCALHOST = '127.0.0.1'; var SCRIPT_START = util.toBuffer(''); @@ -353,7 +357,7 @@ module.exports = function(req, res, next) { delete req.headers[prop]; }); - options.headers = util.formatHeaders(options.headers, req.rawHeaderNames); + options.headers = formatHeaders(options.headers, req.rawHeaderNames); _request().on('error', function(err) { if (responsed) { return; @@ -390,7 +394,7 @@ module.exports = function(req, res, next) { res.rawHeaderNames = _res.rawHeaderNames; } else { res.rawHeaderNames = _res.rawHeaderNames = Array.isArray(_res.rawHeaders) ? - util.getRawHeaderNames(_res.rawHeaders) : {}; + getRawHeaderNames(_res.rawHeaders) : {}; } pluginMgr.getResRules(req, _res, function(resRulesMgr) { util.mergeRules(req, resRulesMgr && resRulesMgr.resolveRules(req.fullUrl)); @@ -639,7 +643,7 @@ getWriterFile(util.getRuleFile(resRules.resWriteRaw), _res.statusCode)], functio }); try { - res.writeHead(_res.statusCode, util.formatHeaders(_res.headers, res.rawHeaderNames)); + res.writeHead(_res.statusCode, formatHeaders(_res.headers, res.rawHeaderNames)); _res.trailers && res.addTrailers(_res.trailers); } catch(e) { e._resError = true; diff --git a/lib/inspectors/rules.js b/lib/inspectors/rules.js index 7dd892bdc..b2cea1549 100644 --- a/lib/inspectors/rules.js +++ b/lib/inspectors/rules.js @@ -5,6 +5,7 @@ var qs = require('querystring'); var rules = require('../rules'); var util = require('../util'); var pluginMgr = require('../plugins'); +var getRawHeaderNames = require('hparser').getRawHeaderNames; function resolveRules(req, callback, rules) { if (!rules) { @@ -94,7 +95,7 @@ module.exports = function(req, res, next) { req.options.href = req.fullUrl; } req.rawHeaderNames = Array.isArray(req.rawHeaders) ? - util.getRawHeaderNames(req.rawHeaders) : {}; + getRawHeaderNames(req.rawHeaders) : {}; if (!req.rawHeaderNames.connection) { req.rawHeaderNames.connection = 'Connection'; } diff --git a/lib/upgrade.js b/lib/upgrade.js index 574de4ef7..a87fa7210 100644 --- a/lib/upgrade.js +++ b/lib/upgrade.js @@ -1,5 +1,9 @@ var util = require('./util'); var handleWebsocket = require('./https').handleWebsocket; +var hparser = require('hparser'); +var formatHeaders = hparser.formatHeaders; +var getRawHeaderNames = hparser.getRawHeaderNames; +var getRawHeaders = hparser.getRawHeaders; function upgradeHandler(req, socket) { var reqSocket; @@ -14,12 +18,12 @@ function upgradeHandler(req, socket) { } socket.on('error', destroy); socket.headers = req.headers; - socket.rawHeaderNames = util.getRawHeaderNames(req.rawHeaders); + socket.rawHeaderNames = getRawHeaderNames(req.rawHeaders); socket.url = req.url; socket.getBuffer = function(headers, path) { var rawData = ['GET' + ' ' + (path || req.url) + ' ' + 'HTTP/1.1']; - headers = util.formatHeaders(headers || req.headers, req.rawHeaders); - rawData.push(util.getRawHeaders(headers)); + headers = formatHeaders(headers || req.headers, req.rawHeaders); + rawData.push(getRawHeaders(headers)); return new Buffer(rawData.join('\r\n') + '\r\n\r\n'); }; handleWebsocket(socket, null, null, function(err, svrSocket) { diff --git a/lib/util/index.js b/lib/util/index.js index de9dc5ddf..b9a276b91 100644 --- a/lib/util/index.js +++ b/lib/util/index.js @@ -31,12 +31,7 @@ exports.ReplacePatternTransform = require('./replace-pattern-transform'); exports.ReplaceStringTransform = require('./replace-string-transform'); exports.SpeedTransform = require('./speed-transform'); exports.FileWriterTransform = require('./file-writer-transform'); -exports.MultiPartParser = require('./multipart-parser'); -exports.parseReq = require('./parse-req'); exports.getServer = require('./get-server'); -exports.formatHeaders = exports.parseReq.formatHeaders; -exports.getRawHeaderNames = exports.parseReq.getRawHeaderNames; -exports.getRawHeaders = exports.parseReq.getRawHeaders; function noop() {} @@ -142,31 +137,6 @@ function execScriptSync(script, context) { exports.execScriptSync = execScriptSync; -function toMultipart(name, value) { - if (value == null) { - value = ''; - } - if (typeof value == 'object') { - var filename = value.filename || value.name; - filename = filename == null ? '' : filename + ''; - value = value.content || value.value || ''; - return toBuffer('Content-Disposition: form-data; name="' + name + '"; filename="' + filename -+ '"\r\nContent-Type: ' + mime.lookup(filename) + '\r\n\r\n' + value); - } - return toBuffer('Content-Disposition: form-data; name="' + name + '"\r\n\r\n' + value); -} - -exports.toMultipart = toMultipart; - -function toMultiparts(params, boundary) { - var content = Object.keys(params).map(function(name) { - return boundary + '\r\n' + toMultipart(name, params[name]); - }).join('\r\n'); - return toBuffer(content ? '\r\n' + content : ''); -} - -exports.toMultiparts = toMultiparts; - function getFileWriter(file, callback) { if (!file || fileWriterCache[file]) { return callback(); diff --git a/lib/util/multipart-parser.js b/lib/util/multipart-parser.js deleted file mode 100644 index a95ffe4a0..000000000 --- a/lib/util/multipart-parser.js +++ /dev/null @@ -1,47 +0,0 @@ -var util = require('./index'); -var CRLF = new Buffer('\r\n'); -var MAX_SIZE = 5120; -var NAME_RE = /name=(?:"([^"]+)"|([^;]+))/i; - -function MultipartParser(params) { - this._params = params; -} - -function getName(firstLine) { - return NAME_RE.test(firstLine) ? (RegExp.$1 || RegExp.$2 || '') : null; -} - -var proto = MultipartParser.prototype; -proto.transform = function(chunk) { - if (this._notMatched) { - return chunk; - } - - if (this._ended) { - return; - } - - chunk = this._buffer ? Buffer.concat([this._buffer, chunk]) : chunk; - this._buffer = null; - var index = util.indexOfList(chunk, CRLF); - if (index != -1) { - var name = getName(chunk.slice(0, index) + ''); - if (name == null || !(name in this._params)) { - this._notMatched = true; - } else { - this._ended = true; - chunk = util.toMultipart(name, this._params[name]); - delete this._params[name]; - } - } else if (chunk.length > MAX_SIZE) { - this._notMatched = true; - } else { - this._buffer = chunk; - chunk = null; - } - - return chunk; -}; - -module.exports = MultipartParser; - diff --git a/lib/util/parse-req.js b/lib/util/parse-req.js deleted file mode 100644 index c59af05b2..000000000 --- a/lib/util/parse-req.js +++ /dev/null @@ -1,189 +0,0 @@ -var PassThrough = require('stream').PassThrough; -var TIMEOUT = 36000; -var MAX_BYTES = 1024 * 256; -var CRLF_BUF = toBuffer('\r\n'); - -function toBuffer(buf) { - return new Buffer(buf); -} - -function parsePairs(pairs, buffer) { - var rawHeaders = []; - for (var i = 0, len = buffer.length; i < len; i++) { - var pair = getPair(buffer[i], rawHeaders); - if (pair) { - var value = pairs[pair.key]; - if (value) { - if (!Array.isArray(value)) { - value = [value]; - } - value.push(pair.value); - } - pairs[pair.key] = value || pair.value; - } - } - - return rawHeaders; -} - -function getPair(line, rawHeaders) { - if (!(line = line && line.trim())) { - return; - } - - var index = line.indexOf(':'); - var key = line.substring(0, index).trim(); - var value = line.substring(index + 1).trim(); - rawHeaders.push(key, value); - return index != -1 ? { - key: key.toLowerCase(), - value: value - } : null; -} - -function endIndexOf(buffer, start, end) { - start = start || 0; - end = end || buffer.length; - for (; start < end; start++) { - if (buffer[start] == CRLF_BUF[0] && buffer[start + 1] == CRLF_BUF[1] -&& buffer[start + 2] == CRLF_BUF[0] && buffer[start + 3] == CRLF_BUF[1]) { - return start; - } - } - - return -1; -} - -function formatHeaders(headers, rawHeaders) { - if (!rawHeaders) { - return headers; - } - var newHeaders = {}; - if (Array.isArray(rawHeaders)) { - var rawHeadersMap = {}; - for (var i = 0, len = rawHeaders.length; i < len; i += 2) { - var name = rawHeaders[i]; - if (typeof name === 'string') { - rawHeadersMap[name.toLowerCase()] = name; - } - } - rawHeaders = rawHeadersMap; - } - Object.keys(headers).forEach(function(name) { - newHeaders[rawHeaders[name] || name] = headers[name]; - }); - return newHeaders; -} - -function getRawHeaderNames(rawHeaders) { - var rawHeaderNames = {}; - for (var i = 0, len = rawHeaders.length; i < len; i += 2) { - var name = rawHeaders[i]; - if (typeof name === 'string') { - rawHeaderNames[name.toLowerCase()] = name; - } - } - return rawHeaderNames; -} - -function getRawHeaders(headers) { - var rawData = []; - Object.keys(headers).forEach(function(key) { - var value = headers[key]; - if (Array.isArray(value)) { - value.forEach(function(val) { - rawData.push(key + ':' + (val || '')); - }); - } else { - rawData.push(key + ':' + (value || '')); - } - }); - return rawData.join('\r\n'); -} - -module.exports = function parseReq(socket, callback, readBuffer, neadModifyHeaders) { - if (typeof readBuffer == 'boolean') { - var temp = neadModifyHeaders; - neadModifyHeaders = readBuffer; - readBuffer = temp; - } - - var timeoutId, buffer, done; - var execCallback = function(err, req) { - if (done) { - return; - } - done = true; - if (typeof callback === 'function') { - callback(err, req); - } - }; - - socket.on('error', execCallback); - socket.on('data', parseData); - readBuffer && parseData(readBuffer); - - function parseData(data) { - clearTimeout(timeoutId); - buffer = buffer ? Buffer.concat([buffer, data]) : data; - var endIndex = endIndexOf(buffer); - if (endIndex == -1) { - if (buffer.length > MAX_BYTES) { - execCallback(new Error('Parse error')); - } else { - timeoutId = setTimeout(showTimeout, TIMEOUT); - } - return; - } - - socket.removeListener('data', parseData); - var req = neadModifyHeaders ? socket : new PassThrough(); - var headers = {}; - req.body = buffer.slice(endIndex + 4).toString(); - var rawHeaders = buffer.slice(0, endIndex).toString().trim().split(/\r\n/g); - var firstLine = rawHeaders.shift(); - var status = firstLine.split(/\s/g); - - req.rawHeaderNames = getRawHeaderNames(parsePairs(headers, rawHeaders)); - req.method = status[0] = status[0] || 'GET'; - req.url = status[1] || '/'; - req.statusCode = status[1] || 0; - req.httpVersion = status[2] && status[2].split('/')[1] || '1.1'; - req.headers = headers; - - if (neadModifyHeaders) { - req.getBuffer = function(headers, path) { - if (!headers) { - return buffer; - } - if (path) { - status[1] = path; - path = status.join(' '); - } - headers = formatHeaders(headers, req.rawHeaderNames); - var rawData = [path || firstLine, getRawHeaders(headers)]; - return Buffer.concat([toBuffer(rawData.join('\r\n')), buffer.slice(endIndex)]); - }; - } else { - req.on('error', function () { - socket.destroy(); - }); - req.destroy = function() { - socket.destroy(); - }; - req.socket = socket; - req.write(buffer); - socket.pipe(req); - } - - execCallback(null, req); - } - - function showTimeout() { - execCallback(new Error('Timeout')); - } -}; - -module.exports.formatHeaders = formatHeaders; -module.exports.getRawHeaderNames = getRawHeaderNames; -module.exports.getRawHeaders = getRawHeaders; diff --git a/package.json b/package.json index f166833e0..abf6b459b 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "cookie": "^0.3.1", "express": "4.12.3", "fs-extra": "0.22.1", + "hparser": "^0.1.0", "iconv-lite": "0.4.7", "lru-cache": "^4.1.1", "mime": "1.3.4",