express-http-proxy
Version:
http proxy middleware for express
156 lines (116 loc) • 3.96 kB
JavaScript
var assert = require('assert');
var util = require('util');
var url = require('url');
var http = require('http');
var is = require('type-is');
var getRawBody = require('raw-body');
require('buffer');
module.exports = function proxy(host, options) {
assert(host, 'Host should not be empty');
options = options || {};
var port = 80;
if (typeof host == 'string') {
var mc = host.match(/^(https?:\/\/)/);
if (mc) {
host = host.substring(mc[1].length);
}
var h = host.split(':');
host = h[0];
port = h[1] || 80;
}
/**
* intercept(data, res, req, function(err, json));
*/
var intercept = options.intercept;
var decorateRequest = options.decorateRequest;
var forwardPath = options.forwardPath;
var filter = options.filter;
return function handleProxy(req, res, next) {
if (filter && !filter(req, res)) next();
var headers = options.headers || {};
var path;
path = forwardPath ? forwardPath(req, res) : url.parse(req.url).path;
var hds = extend(headers, req.headers, ['connection', 'host', 'content-length']);
hds.connection = 'close';
// var hasRequestBody = 'content-type' in req.headers || 'transfer-encoding' in req.headers;
getRawBody(req, {
length: req.headers['content-length'],
limit: '1mb' // TODO let options do here?
}, function(err, bodyContent) {
if (err) return next(err);
var reqOpt = {
hostname: (typeof host == 'function') ? host(req) : host.toString(),
port: port,
headers: hds,
method: req.method,
path: path,
bodyContent: bodyContent
};
if (decorateRequest)
reqOpt = decorateRequest(reqOpt) || reqOpt;
bodyContent = reqOpt.bodyContent;
delete reqOpt.bodyContent;
if (typeof bodyContent == 'string')
reqOpt.headers['content-length'] = Buffer.byteLength(bodyContent);
else if (Buffer.isBuffer(bodyContent)) // Buffer
reqOpt.headers['content-length'] = bodyContent.length;
var chunks = [];
var realRequest = http.request(reqOpt, function(rsp) {
var rspData = null;
rsp.on('data', function(chunk) {
chunks.push(chunk);
});
rsp.on('end', function() {
var totalLength = chunks.reduce(function(len, buf) {
return len + buf.length;
}, 0);
var rspData = Buffer.concat(chunks, totalLength);
if (intercept) {
intercept(rspData, req, res, function(err, rsp, sent) {
if (err) {
return next(err);
}
if (typeof rsp == 'string')
rsp = new Buffer(rsp, 'utf8');
if (!Buffer.isBuffer(rsp)) {
next(new Error("intercept should return string or buffer as data"));
}
if (!res.headersSent)
res.set('content-length', rsp.length);
else if (rsp.length != rspData.length) {
next(new Error("'Content-Length' is already sent, the length of response data can not be changed"));
}
if (!sent)
res.send(rsp);
});
} else
res.send(rspData);
});
rsp.on('error', function(e) {
next(e);
});
if (!res.headersSent) { // if header is not set yet
res.status(rsp.statusCode);
for (var p in rsp.headers) {
res.set(p, rsp.headers[p]);
}
}
});
realRequest.on('error', function(e) {
next(e);
});
if (bodyContent.length) {
realRequest.write(bodyContent);
}
realRequest.end();
});
};
};
function extend(obj, source, skips) {
if (!source) return obj;
for (var prop in source) {
if (skips.indexOf(prop) == -1)
obj[prop] = source[prop];
}
return obj;
}